diff --git a/.github/DISCUSSION_TEMPLATE/help.yml b/.github/DISCUSSION_TEMPLATE/help.yml new file mode 100644 index 00000000..6d32d10a --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/help.yml @@ -0,0 +1,20 @@ +body: + - type: textarea + attributes: + label: Summary + description: What do you need help with? + validations: + required: true + - type: textarea + attributes: + label: Additional information + description: Any code snippets, error messages, or dependency details that may be related? + render: js + validations: + required: false + - type: input + attributes: + label: Example + description: A link to a minimal reproduction is helpful for collaborative debugging! + validations: + required: false diff --git a/.github/DISCUSSION_TEMPLATE/ideas.yml b/.github/DISCUSSION_TEMPLATE/ideas.yml new file mode 100644 index 00000000..3c81f93e --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/ideas.yml @@ -0,0 +1,23 @@ +body: + - type: textarea + attributes: + label: Goals + description: Short list of what the feature request aims to address? + value: | + 1. + 2. + 3. + validations: + required: true + - type: textarea + attributes: + label: Background + description: Discuss prior art, why do you think this feature is needed? Are there current alternatives? + validations: + required: true + - type: textarea + attributes: + label: Proposal + description: How should this feature be implemented? Are you interested in contributing? + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/1.bug_report.yml b/.github/ISSUE_TEMPLATE/1.bug_report.yml new file mode 100644 index 00000000..74854dc2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/1.bug_report.yml @@ -0,0 +1,39 @@ +name: Bug Report +description: Create a bug report for the Node SDK +labels: ["Type: Bug"] +body: + - type: textarea + attributes: + label: Describe the Bug + description: A clear and concise description of what the bug is. + validations: + required: true + - type: input + attributes: + label: Link to the code that reproduces this issue + description: | + A link to a GitHub repository minimal reproduction. A minimal reproduction code is really helpful to understand the issue. + validations: + required: true + - type: textarea + attributes: + label: To Reproduce + description: Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below. If using code blocks, make sure that [syntax highlighting is correct](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) and double check that the rendered preview is not broken. + validations: + required: true + - type: textarea + attributes: + label: Expected Behavior + description: A clear and concise description of what you expected to happen. + validations: + required: true + - type: markdown + attributes: + value: Before posting the issue go through the steps you've written down to make sure the steps provided are detailed and clear. + - type: markdown + attributes: + value: Contributors should be able to follow the steps provided in order to reproduce the bug. + - type: input + attributes: + label: What's your node version? (if relevant) + description: "Please specify the exact version." diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..09b49e5b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Ask a question + url: https://github.com/resend/resend-node/discussions + about: Ask questions and discuss with other community members + - name: Feature request + url: https://github.com/resend/resend-node/discussions/new?category=ideas + about: Feature requests should be opened as discussions diff --git a/.github/scripts/check-pinned-dependencies.mjs b/.github/scripts/check-pinned-dependencies.mjs new file mode 100644 index 00000000..00798940 --- /dev/null +++ b/.github/scripts/check-pinned-dependencies.mjs @@ -0,0 +1,27 @@ +import fs from 'node:fs'; + +const pkg = JSON.parse(await fs.promises.readFile('package.json', 'utf8')); +const errors = []; + +function isPinned(version) { + return /^\d+\.\d+\.\d+(-canary\.\d+)?$/.test(version); +} + +for (const [dep, version] of Object.entries(pkg.dependencies || {})) { + if (!isPinned(version)) { + errors.push(`Dependency "${dep}" is not pinned: "${version}"`); + } +} + +for (const [dep, version] of Object.entries(pkg.devDependencies || {})) { + if (!isPinned(version)) { + errors.push(`Dev dependency "${dep}" is not pinned: "${version}"`); + } +} + +if (errors.length > 0) { + console.error(`\n${errors.join('\n')}\n`); + process.exit(1); +} else { + console.log('All dependencies are pinned.'); +} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 3f4d9c24..c66c74c7 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -4,24 +4,22 @@ on: branches: - main pull_request: +permissions: + contents: read + pull-requests: read jobs: lint: runs-on: buildjet-4vcpu-ubuntu-2204 container: - image: node:20 - credentials: - username: ${{ vars.DOCKER_HUB_USERNAME }} - password: ${{ secrets.DOCKER_HUB_API_KEY }} + image: node:22 + # credentials: + # username: ${{ vars.DOCKER_HUB_USERNAME || '' }} + # password: ${{ secrets.DOCKER_HUB_API_KEY || '' }} steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 - name: pnpm setup - uses: pnpm/action-setup@v4 - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: "pnpm" + uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda - name: Install packages run: pnpm install - name: Run Lint diff --git a/.github/workflows/pin-dependencies-check.yml b/.github/workflows/pin-dependencies-check.yml index 47b6ad5c..ef87c499 100644 --- a/.github/workflows/pin-dependencies-check.yml +++ b/.github/workflows/pin-dependencies-check.yml @@ -4,44 +4,20 @@ on: branches: - main pull_request: +permissions: + contents: read + pull-requests: read jobs: pin-dependencies-check: runs-on: buildjet-4vcpu-ubuntu-2204 container: - image: node:20 - credentials: - username: ${{ vars.DOCKER_HUB_USERNAME }} - password: ${{ secrets.DOCKER_HUB_API_KEY }} + image: node:22 + # credentials: + # username: ${{ vars.DOCKER_HUB_USERNAME || '' }} + # password: ${{ secrets.DOCKER_HUB_API_KEY || '' }} steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 - name: Check for pinned dependencies run: | - node -e ' - const fs = require("fs"); - const pkg = JSON.parse(fs.readFileSync("package.json", "utf8")); - const errors = []; - - function isPinned(version) { - return /^\d+\.\d+\.\d+(-canary\.\d+)?$/.test(version); - } - - for (const [dep, version] of Object.entries(pkg.dependencies || {})) { - if (!isPinned(version)) { - errors.push(`Dependency "${dep}" is not pinned: "${version}"`); - } - } - - for (const [dep, version] of Object.entries(pkg.devDependencies || {})) { - if (!isPinned(version)) { - errors.push(`Dev dependency "${dep}" is not pinned: "${version}"`); - } - } - - if (errors.length > 0) { - console.error(`\n${errors.join("\n")}\n`); - process.exit(1); - } else { - console.log("All dependencies are pinned."); - } - ' + node .github/scripts/check-pinned-dependencies.mjs diff --git a/.github/workflows/pr-title-check.yml b/.github/workflows/pr-title-check.yml index 16b8902b..a5a59a6b 100644 --- a/.github/workflows/pr-title-check.yml +++ b/.github/workflows/pr-title-check.yml @@ -2,17 +2,19 @@ name: PR Title Check on: pull_request: types: [opened, edited, synchronize] +permissions: + pull-requests: read jobs: pr-title-check: runs-on: buildjet-4vcpu-ubuntu-2204 container: - image: node:20 - credentials: - username: ${{ vars.DOCKER_HUB_USERNAME }} - password: ${{ secrets.DOCKER_HUB_API_KEY }} + image: node:22 + # credentials: + # username: ${{ vars.DOCKER_HUB_USERNAME || '' }} + # password: ${{ secrets.DOCKER_HUB_API_KEY || '' }} steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 - name: Run PR title check run: | node .github/scripts/pr-title-check.js diff --git a/.github/workflows/preview-release.yml b/.github/workflows/preview-release.yml new file mode 100644 index 00000000..d09bb7bc --- /dev/null +++ b/.github/workflows/preview-release.yml @@ -0,0 +1,40 @@ + +name: Preview Release +on: + push: + branches: + - canary + - preview-* + pull_request: +permissions: + contents: read + pull-requests: write +concurrency: ${{ github.workflow }}-${{ github.ref }} +jobs: + preview-release: + runs-on: buildjet-4vcpu-ubuntu-2204 + permissions: + contents: write + pull-requests: write + container: + image: node:22 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: pnpm setup + uses: pnpm/action-setup@v4 + - name: Install packages + run: pnpm install --frozen-lockfile + - name: Run Build + run: pnpm build + - name: Find changed files + id: changed_files + if: github.event_name == 'pull_request' + uses: tj-actions/changed-files@2036da178f85576f1940fedb74bb93a36cd89ab7 + with: + files: src/**,pnpm-lock.yaml + dir_names: true + dir_names_max_depth: 2 + - name: Publish changed packages to pkg.pr.new + if: github.event_name == 'push' || steps.changed_files.outputs.all_changed_and_modified_files != '' + run: pnpm dlx pkg-pr-new publish diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index db186314..b4557cd0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -3,27 +3,24 @@ on: push: branches: - main + - canary pull_request: +permissions: + contents: read + pull-requests: read jobs: tests: runs-on: buildjet-4vcpu-ubuntu-2204 container: - image: node:20 - credentials: - username: ${{ vars.DOCKER_HUB_USERNAME }} - password: ${{ secrets.DOCKER_HUB_API_KEY }} + image: node:22 + # credentials: + # username: ${{ vars.DOCKER_HUB_USERNAME || '' }} + # password: ${{ secrets.DOCKER_HUB_API_KEY || '' }} steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 - name: pnpm setup - uses: pnpm/action-setup@v4 - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: "pnpm" - - name: Install Doppler CLI - uses: dopplerhq/cli-action@v3 + uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda - name: Install packages run: pnpm install - name: Run Tests diff --git a/biome.jsonc b/biome.jsonc index 39f4d297..35164366 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -7,9 +7,7 @@ // only run on changed files by default (faster, but happy to remove) "defaultBranch": "main" }, - "organizeImports": { - "enabled": true - }, + "assist": { "actions": { "source": { "organizeImports": "on" } } }, "formatter": { "indentStyle": "space", "indentWidth": 2, @@ -24,10 +22,22 @@ "linter": { "enabled": true, "rules": { - "recommended": true + "recommended": true, + "style": { + "noParameterAssign": "error", + "useAsConstAssertion": "error", + "useDefaultParameterLast": "error", + "useEnumInitializers": "error", + "useSelfClosingElements": "error", + "useSingleVarDeclarator": "error", + "noUnusedTemplateLiteral": "error", + "useNumberNamespace": "error", + "noInferrableTypes": "error", + "noUselessElse": "error" + } } }, "files": { - "ignore": ["pnpm-lock.yaml"] + "includes": ["**", "!**/pnpm-lock.yaml", "!dist"] } } diff --git a/jest.config.ts b/jest.config.ts deleted file mode 100644 index 5f1618a9..00000000 --- a/jest.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { JestConfigWithTsJest } from 'ts-jest/'; - -const config: JestConfigWithTsJest = { - preset: 'ts-jest', - clearMocks: true, - restoreMocks: true, - verbose: true, - testEnvironment: 'node', - setupFiles: ['./jest.setup.ts'], - prettierPath: null, -}; - -export default config; diff --git a/jest.setup.ts b/jest.setup.ts deleted file mode 100644 index 03ba3a28..00000000 --- a/jest.setup.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { enableFetchMocks } from 'jest-fetch-mock'; - -enableFetchMocks(); diff --git a/package.json b/package.json index 574b37a2..7f709be8 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,13 @@ { "name": "resend", - "version": "6.0.1", + "version": "6.1.0-canary.1", "description": "Node.js library for the Resend API", "main": "./dist/index.js", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", - "files": ["dist/**"], + "files": [ + "dist/**" + ], "engines": { "node": ">=18" }, @@ -23,13 +25,13 @@ }, "scripts": { "build": "tsup src/index.ts --format esm,cjs --dts", - "test": "jest", - "test:watch": "jest --watch", + "format": "biome format --write .", "format:apply": "biome check --write .", "format:check": "biome format .", - "format": "biome format --write .", "lint": "biome check .", - "prepublishOnly": "pnpm run build" + "prepublishOnly": "pnpm run build", + "test": "vitest run", + "test:watch": "vitest" }, "repository": { "type": "git", @@ -50,16 +52,14 @@ } }, "devDependencies": { - "@biomejs/biome": "1.9.4", - "@types/jest": "29.5.14", - "@types/node": "18.19.86", - "@types/react": "19.1.2", - "jest": "29.7.0", - "jest-fetch-mock": "3.0.3", - "ts-jest": "29.3.4", - "ts-node": "10.9.2", - "tsup": "7.2.0", - "typescript": "5.8.3" + "@biomejs/biome": "2.2.0", + "@types/node": "22.17.2", + "@types/react": "19.1.10", + "pkg-pr-new": "0.0.56", + "tsup": "8.5.0", + "typescript": "5.9.2", + "vitest": "3.2.4", + "vitest-fetch-mock": "0.4.5" }, - "packageManager": "pnpm@10.14.0" + "packageManager": "pnpm@10.15.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d15e9666..dfeba2b2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,395 +13,238 @@ importers: version: 1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) devDependencies: '@biomejs/biome': - specifier: 1.9.4 - version: 1.9.4 - '@types/jest': - specifier: 29.5.14 - version: 29.5.14 + specifier: 2.2.0 + version: 2.2.0 '@types/node': - specifier: 18.19.86 - version: 18.19.86 + specifier: 22.17.2 + version: 22.17.2 '@types/react': - specifier: 19.1.2 - version: 19.1.2 - jest: - specifier: 29.7.0 - version: 29.7.0(@types/node@18.19.86)(ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.3)) - jest-fetch-mock: - specifier: 3.0.3 - version: 3.0.3 - ts-jest: - specifier: 29.3.4 - version: 29.3.4(@babel/core@7.24.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(esbuild@0.18.20)(jest@29.7.0(@types/node@18.19.86)(ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.3)))(typescript@5.8.3) - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@18.19.86)(typescript@5.8.3) + specifier: 19.1.10 + version: 19.1.10 + pkg-pr-new: + specifier: 0.0.56 + version: 0.0.56 tsup: - specifier: 7.2.0 - version: 7.2.0(ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.3))(typescript@5.8.3) + specifier: 8.5.0 + version: 8.5.0(postcss@8.5.6)(typescript@5.9.2)(yaml@2.4.5) typescript: - specifier: 5.8.3 - version: 5.8.3 + specifier: 5.9.2 + version: 5.9.2 + vitest: + specifier: 3.2.4 + version: 3.2.4(@types/node@22.17.2)(yaml@2.4.5) + vitest-fetch-mock: + specifier: 0.4.5 + version: 0.4.5(vitest@3.2.4(@types/node@22.17.2)(yaml@2.4.5)) packages: - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - - '@babel/code-frame@7.24.7': - resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} - engines: {node: '>=6.9.0'} - - '@babel/compat-data@7.24.7': - resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==} - engines: {node: '>=6.9.0'} - - '@babel/core@7.24.7': - resolution: {integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==} - engines: {node: '>=6.9.0'} - - '@babel/generator@7.24.7': - resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-compilation-targets@7.24.7': - resolution: {integrity: sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-environment-visitor@7.24.7': - resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-function-name@7.24.7': - resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-hoist-variables@7.24.7': - resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-imports@7.24.7': - resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-transforms@7.24.7': - resolution: {integrity: sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-plugin-utils@7.24.7': - resolution: {integrity: sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-simple-access@7.24.7': - resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-split-export-declaration@7.24.7': - resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-string-parser@7.24.7': - resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-identifier@7.24.7': - resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-option@7.24.7': - resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==} - engines: {node: '>=6.9.0'} - - '@babel/helpers@7.24.7': - resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==} - engines: {node: '>=6.9.0'} - - '@babel/highlight@7.24.7': - resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} - engines: {node: '>=6.9.0'} - - '@babel/parser@7.24.7': - resolution: {integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==} - engines: {node: '>=6.0.0'} - hasBin: true - - '@babel/plugin-syntax-async-generators@7.8.4': - resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-bigint@7.8.3': - resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-class-properties@7.12.13': - resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-meta@7.10.4': - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-json-strings@7.8.3': - resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-jsx@7.24.7': - resolution: {integrity: sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-logical-assignment-operators@7.10.4': - resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': - resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-numeric-separator@7.10.4': - resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-object-rest-spread@7.8.3': - resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-optional-catch-binding@7.8.3': - resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-optional-chaining@7.8.3': - resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-top-level-await@7.14.5': - resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-typescript@7.24.7': - resolution: {integrity: sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/template@7.24.7': - resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} - engines: {node: '>=6.9.0'} - - '@babel/traverse@7.24.7': - resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.24.7': - resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==} - engines: {node: '>=6.9.0'} - - '@bcoe/v8-coverage@0.2.3': - resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - - '@biomejs/biome@1.9.4': - resolution: {integrity: sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==} + '@biomejs/biome@2.2.0': + resolution: {integrity: sha512-3On3RSYLsX+n9KnoSgfoYlckYBoU6VRM22cw1gB4Y0OuUVSYd/O/2saOJMrA4HFfA1Ff0eacOvMN1yAAvHtzIw==} engines: {node: '>=14.21.3'} hasBin: true - '@biomejs/cli-darwin-arm64@1.9.4': - resolution: {integrity: sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==} + '@biomejs/cli-darwin-arm64@2.2.0': + resolution: {integrity: sha512-zKbwUUh+9uFmWfS8IFxmVD6XwqFcENjZvEyfOxHs1epjdH3wyyMQG80FGDsmauPwS2r5kXdEM0v/+dTIA9FXAg==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [darwin] - '@biomejs/cli-darwin-x64@1.9.4': - resolution: {integrity: sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==} + '@biomejs/cli-darwin-x64@2.2.0': + resolution: {integrity: sha512-+OmT4dsX2eTfhD5crUOPw3RPhaR+SKVspvGVmSdZ9y9O/AgL8pla6T4hOn1q+VAFBHuHhsdxDRJgFCSC7RaMOw==} engines: {node: '>=14.21.3'} cpu: [x64] os: [darwin] - '@biomejs/cli-linux-arm64-musl@1.9.4': - resolution: {integrity: sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==} + '@biomejs/cli-linux-arm64-musl@2.2.0': + resolution: {integrity: sha512-egKpOa+4FL9YO+SMUMLUvf543cprjevNc3CAgDNFLcjknuNMcZ0GLJYa3EGTCR2xIkIUJDVneBV3O9OcIlCEZQ==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] - '@biomejs/cli-linux-arm64@1.9.4': - resolution: {integrity: sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==} + '@biomejs/cli-linux-arm64@2.2.0': + resolution: {integrity: sha512-6eoRdF2yW5FnW9Lpeivh7Mayhq0KDdaDMYOJnH9aT02KuSIX5V1HmWJCQQPwIQbhDh68Zrcpl8inRlTEan0SXw==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] - '@biomejs/cli-linux-x64-musl@1.9.4': - resolution: {integrity: sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==} + '@biomejs/cli-linux-x64-musl@2.2.0': + resolution: {integrity: sha512-I5J85yWwUWpgJyC1CcytNSGusu2p9HjDnOPAFG4Y515hwRD0jpR9sT9/T1cKHtuCvEQ/sBvx+6zhz9l9wEJGAg==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] - '@biomejs/cli-linux-x64@1.9.4': - resolution: {integrity: sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==} + '@biomejs/cli-linux-x64@2.2.0': + resolution: {integrity: sha512-5UmQx/OZAfJfi25zAnAGHUMuOd+LOsliIt119x2soA2gLggQYrVPA+2kMUxR6Mw5M1deUF/AWWP2qpxgH7Nyfw==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] - '@biomejs/cli-win32-arm64@1.9.4': - resolution: {integrity: sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==} + '@biomejs/cli-win32-arm64@2.2.0': + resolution: {integrity: sha512-n9a1/f2CwIDmNMNkFs+JI0ZjFnMO0jdOyGNtihgUNFnlmd84yIYY2KMTBmMV58ZlVHjgmY5Y6E1hVTnSRieggA==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [win32] - '@biomejs/cli-win32-x64@1.9.4': - resolution: {integrity: sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==} + '@biomejs/cli-win32-x64@2.2.0': + resolution: {integrity: sha512-Nawu5nHjP/zPKTIryh2AavzTc/KEg4um/MxWdXW0A6P/RZOyIpa7+QSjeXwAwX/utJGaCoXRPWtF3m5U/bB3Ww==} engines: {node: '>=14.21.3'} cpu: [x64] os: [win32] - '@cspotcode/source-map-support@0.8.1': - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} + '@esbuild/aix-ppc64@0.25.8': + resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] - '@esbuild/android-arm64@0.18.20': - resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} - engines: {node: '>=12'} + '@esbuild/android-arm64@0.25.8': + resolution: {integrity: sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==} + engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.18.20': - resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} - engines: {node: '>=12'} + '@esbuild/android-arm@0.25.8': + resolution: {integrity: sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==} + engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.18.20': - resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} - engines: {node: '>=12'} + '@esbuild/android-x64@0.25.8': + resolution: {integrity: sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==} + engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.18.20': - resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} - engines: {node: '>=12'} + '@esbuild/darwin-arm64@0.25.8': + resolution: {integrity: sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.18.20': - resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} - engines: {node: '>=12'} + '@esbuild/darwin-x64@0.25.8': + resolution: {integrity: sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.18.20': - resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} - engines: {node: '>=12'} + '@esbuild/freebsd-arm64@0.25.8': + resolution: {integrity: sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.18.20': - resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} - engines: {node: '>=12'} + '@esbuild/freebsd-x64@0.25.8': + resolution: {integrity: sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.18.20': - resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} - engines: {node: '>=12'} + '@esbuild/linux-arm64@0.25.8': + resolution: {integrity: sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.18.20': - resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} - engines: {node: '>=12'} + '@esbuild/linux-arm@0.25.8': + resolution: {integrity: sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==} + engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.18.20': - resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} - engines: {node: '>=12'} + '@esbuild/linux-ia32@0.25.8': + resolution: {integrity: sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.18.20': - resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} - engines: {node: '>=12'} + '@esbuild/linux-loong64@0.25.8': + resolution: {integrity: sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.18.20': - resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} - engines: {node: '>=12'} + '@esbuild/linux-mips64el@0.25.8': + resolution: {integrity: sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.18.20': - resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} - engines: {node: '>=12'} + '@esbuild/linux-ppc64@0.25.8': + resolution: {integrity: sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.18.20': - resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} - engines: {node: '>=12'} + '@esbuild/linux-riscv64@0.25.8': + resolution: {integrity: sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.18.20': - resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} - engines: {node: '>=12'} + '@esbuild/linux-s390x@0.25.8': + resolution: {integrity: sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.18.20': - resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} - engines: {node: '>=12'} + '@esbuild/linux-x64@0.25.8': + resolution: {integrity: sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==} + engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-x64@0.18.20': - resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} - engines: {node: '>=12'} + '@esbuild/netbsd-arm64@0.25.8': + resolution: {integrity: sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.8': + resolution: {integrity: sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-x64@0.18.20': - resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} - engines: {node: '>=12'} + '@esbuild/openbsd-arm64@0.25.8': + resolution: {integrity: sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.8': + resolution: {integrity: sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.18.20': - resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} - engines: {node: '>=12'} + '@esbuild/openharmony-arm64@0.25.8': + resolution: {integrity: sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.8': + resolution: {integrity: sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.18.20': - resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} - engines: {node: '>=12'} + '@esbuild/win32-arm64@0.25.8': + resolution: {integrity: sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.18.20': - resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} - engines: {node: '>=12'} + '@esbuild/win32-ia32@0.25.8': + resolution: {integrity: sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.18.20': - resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} - engines: {node: '>=12'} + '@esbuild/win32-x64@0.25.8': + resolution: {integrity: sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==} + engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -409,80 +252,6 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - '@istanbuljs/load-nyc-config@1.1.0': - resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} - engines: {node: '>=8'} - - '@istanbuljs/schema@0.1.3': - resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} - engines: {node: '>=8'} - - '@jest/console@29.7.0': - resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/core@29.7.0': - resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} - 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 - - '@jest/environment@29.7.0': - resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/expect-utils@29.7.0': - resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/expect@29.7.0': - resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/fake-timers@29.7.0': - resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/globals@29.7.0': - resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/reporters@29.7.0': - resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} - 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 - - '@jest/schemas@29.6.3': - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/source-map@29.6.3': - resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/test-result@29.7.0': - resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/test-sequencer@29.7.0': - resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/transform@29.7.0': - resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/types@29.6.3': - resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jridgewell/gen-mapping@0.3.5': resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} engines: {node: '>=6.0.0'} @@ -495,26 +264,71 @@ packages: resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.4.15': - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@jridgewell/sourcemap-codec@1.5.4': + resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@jridgewell/trace-mapping@0.3.9': - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@jsdevtools/ez-spawn@3.0.4': + resolution: {integrity: sha512-f5DRIOZf7wxogefH03RjMPMdBF7ADTWUMoOs9kaJo06EfwF+aFhMZMDZxHg/Xe12hptN9xoZjGso2fdjapBRIA==} + engines: {node: '>=10'} - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} + '@octokit/action@6.1.0': + resolution: {integrity: sha512-lo+nHx8kAV86bxvOVOI3vFjX3gXPd/L7guAUbvs3pUvnR2KC+R7yjBkA1uACt4gYhs4LcWP3AXSGQzsbeN2XXw==} + engines: {node: '>= 18'} - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} + '@octokit/auth-action@4.1.0': + resolution: {integrity: sha512-m+3t7K46IYyMk7Bl6/lF4Rv09GqDZjYmNg8IWycJ2Fa3YE3DE7vQcV6G2hUPmR9NDqenefNJwVtlisMjzymPiQ==} + engines: {node: '>= 18'} - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} + '@octokit/auth-token@4.0.0': + resolution: {integrity: sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==} + engines: {node: '>= 18'} + + '@octokit/core@5.2.2': + resolution: {integrity: sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==} + engines: {node: '>= 18'} + + '@octokit/endpoint@9.0.6': + resolution: {integrity: sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==} + engines: {node: '>= 18'} + + '@octokit/graphql@7.1.1': + resolution: {integrity: sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==} + engines: {node: '>= 18'} + + '@octokit/openapi-types@20.0.0': + resolution: {integrity: sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==} + + '@octokit/openapi-types@24.2.0': + resolution: {integrity: sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==} + + '@octokit/plugin-paginate-rest@9.2.2': + resolution: {integrity: sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '5' + + '@octokit/plugin-rest-endpoint-methods@10.4.1': + resolution: {integrity: sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '5' + + '@octokit/request-error@5.1.1': + resolution: {integrity: sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==} + engines: {node: '>= 18'} + + '@octokit/request@8.4.1': + resolution: {integrity: sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==} + engines: {node: '>= 18'} + + '@octokit/types@12.6.0': + resolution: {integrity: sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==} + + '@octokit/types@13.10.0': + resolution: {integrity: sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==} '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} @@ -527,85 +341,158 @@ packages: react: ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^18.0 || ^19.0 || ^19.0.0-rc - '@selderee/plugin-htmlparser2@0.11.0': - resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} + '@rollup/rollup-android-arm-eabi@4.46.2': + resolution: {integrity: sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.46.2': + resolution: {integrity: sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.46.2': + resolution: {integrity: sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.46.2': + resolution: {integrity: sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.46.2': + resolution: {integrity: sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.46.2': + resolution: {integrity: sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.46.2': + resolution: {integrity: sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.46.2': + resolution: {integrity: sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.46.2': + resolution: {integrity: sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.46.2': + resolution: {integrity: sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==} + cpu: [arm64] + os: [linux] - '@sinclair/typebox@0.27.8': - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + '@rollup/rollup-linux-loongarch64-gnu@4.46.2': + resolution: {integrity: sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.46.2': + resolution: {integrity: sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==} + cpu: [ppc64] + os: [linux] - '@sinonjs/commons@3.0.1': - resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + '@rollup/rollup-linux-riscv64-gnu@4.46.2': + resolution: {integrity: sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==} + cpu: [riscv64] + os: [linux] - '@sinonjs/fake-timers@10.3.0': - resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + '@rollup/rollup-linux-riscv64-musl@4.46.2': + resolution: {integrity: sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==} + cpu: [riscv64] + os: [linux] - '@tsconfig/node10@1.0.11': - resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + '@rollup/rollup-linux-s390x-gnu@4.46.2': + resolution: {integrity: sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==} + cpu: [s390x] + os: [linux] - '@tsconfig/node12@1.0.11': - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + '@rollup/rollup-linux-x64-gnu@4.46.2': + resolution: {integrity: sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==} + cpu: [x64] + os: [linux] - '@tsconfig/node14@1.0.3': - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + '@rollup/rollup-linux-x64-musl@4.46.2': + resolution: {integrity: sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==} + cpu: [x64] + os: [linux] - '@tsconfig/node16@1.0.4': - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + '@rollup/rollup-win32-arm64-msvc@4.46.2': + resolution: {integrity: sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==} + cpu: [arm64] + os: [win32] - '@types/babel__core@7.20.5': - resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + '@rollup/rollup-win32-ia32-msvc@4.46.2': + resolution: {integrity: sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==} + cpu: [ia32] + os: [win32] - '@types/babel__generator@7.6.8': - resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} + '@rollup/rollup-win32-x64-msvc@4.46.2': + resolution: {integrity: sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==} + cpu: [x64] + os: [win32] - '@types/babel__template@7.4.4': - resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + '@selderee/plugin-htmlparser2@0.11.0': + resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} - '@types/babel__traverse@7.20.6': - resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + '@types/chai@5.2.2': + resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} - '@types/graceful-fs@4.1.9': - resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - '@types/istanbul-lib-coverage@2.0.6': - resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - '@types/istanbul-lib-report@3.0.3': - resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + '@types/node@22.17.2': + resolution: {integrity: sha512-gL6z5N9Jm9mhY+U2KXZpteb+09zyffliRkZyZOHODGATyC5B1Jt/7TzuuiLkFsSUMLbS1OLmlj/E+/3KF4Q/4w==} - '@types/istanbul-reports@3.0.4': - resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + '@types/react@19.1.10': + resolution: {integrity: sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg==} - '@types/jest@29.5.14': - resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} - '@types/node@18.19.86': - resolution: {integrity: sha512-fifKayi175wLyKyc5qUfyENhQ1dCNI1UNjp653d8kuYcPQN5JhX3dGuP/XmvPTg/xRBn1VTLpbmi+H/Mr7tLfQ==} + '@vitest/mocker@3.2.4': + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true - '@types/react@19.1.2': - resolution: {integrity: sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==} + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} - '@types/stack-utils@2.0.3': - resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + '@vitest/runner@3.2.4': + resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} - '@types/yargs-parser@21.0.3': - resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + '@vitest/snapshot@3.2.4': + resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} - '@types/yargs@17.0.32': - resolution: {integrity: sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==} + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} - acorn-walk@8.3.2: - resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} - engines: {node: '>=0.4.0'} + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} - acorn@8.11.3: - resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} hasBin: true - ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -614,18 +501,10 @@ packages: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} engines: {node: '>=12'} - ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} - ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} @@ -633,149 +512,48 @@ packages: any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - - arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - - argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - - async@3.2.5: - resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} - - babel-jest@29.7.0: - resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.8.0 - - babel-plugin-istanbul@6.1.1: - resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} - engines: {node: '>=8'} - - babel-plugin-jest-hoist@29.6.3: - resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - babel-preset-current-node-syntax@1.0.1: - resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} - peerDependencies: - '@babel/core': ^7.0.0 - - babel-preset-jest@29.6.3: - resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.0.0 + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - - brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + before-after-hook@2.2.3: + resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==} 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.23.1: - resolution: {integrity: sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - - bs-logger@0.2.6: - resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} - engines: {node: '>= 6'} - - bser@2.1.1: - resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} - - buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - - bundle-require@4.2.1: - resolution: {integrity: sha512-7Q/6vkyYAwOmQNRw75x+4yRtZCZJXUDmHHlFdkiV0wgv/reNjtJwpu1jPJ0w2kbEpIM0uoKI3S4/f39dU7AjSA==} + bundle-require@5.1.0: + resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: - esbuild: '>=0.17' + esbuild: '>=0.18' 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'} - - camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - - camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - - caniuse-lite@1.0.30001632: - resolution: {integrity: sha512-udx3o7yHJfUxMLkGohMlVHCvFvWmirKh9JAH/d7WOLPetlH+LTL5cocMZ0t7oZx/mdlOWXti97xLZWc8uURRHg==} - - chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} - - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - - char-regex@1.0.2: - resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} - engines: {node: '>=10'} - - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} - - ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} - engines: {node: '>=8'} - - cjs-module-lexer@1.3.1: - resolution: {integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==} + call-me-maybe@1.0.2: + resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} - cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - - co@4.6.0: - resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} - engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + chai@5.2.1: + resolution: {integrity: sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==} + engines: {node: '>=18'} - collect-v8-coverage@1.0.2: - resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} - color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} - color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} @@ -783,22 +561,12 @@ packages: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - - convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} - create-jest@29.7.0: - resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - - create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - - cross-fetch@3.1.8: - resolution: {integrity: sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==} + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} @@ -807,8 +575,8 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - debug@4.3.5: - resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -816,33 +584,20 @@ packages: supports-color: optional: true - dedent@1.5.3: - resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} - peerDependencies: - babel-plugin-macros: ^3.1.0 - peerDependenciesMeta: - babel-plugin-macros: - optional: true + decode-uri-component@0.4.1: + resolution: {integrity: sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==} + engines: {node: '>=14.16'} + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} - detect-newline@3.1.0: - resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} - engines: {node: '>=8'} - - diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} - - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} + deprecation@2.3.1: + resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} @@ -860,18 +615,6 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - ejs@3.1.10: - resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} - engines: {node: '>=0.10.0'} - hasBin: true - - electron-to-chromium@1.4.797: - resolution: {integrity: sha512-RWMYymqyWwIdCEb7Psag5zyAHirYnB354ZREoF8c5QOHbt8AodF7lwVxGUnu5gzBVjzDo9R3XeTwy7pbvubxGw==} - - emittery@0.13.1: - resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} - engines: {node: '>=12'} - emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -882,140 +625,53 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} - error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - - esbuild@0.18.20: - resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} - engines: {node: '>=12'} - hasBin: true - - escalade@3.1.2: - resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} - engines: {node: '>=6'} - - escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} - - escape-string-regexp@2.0.0: - resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} - engines: {node: '>=8'} + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} + esbuild@0.25.8: + resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==} + engines: {node: '>=18'} hasBin: true - execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - - exit@0.1.2: - resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} - engines: {node: '>= 0.8.0'} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - expect@29.7.0: - resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + expect-type@1.2.2: + resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + engines: {node: '>=12.0.0'} fast-deep-equal@2.0.1: resolution: {integrity: sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==} - fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} - engines: {node: '>=8.6.0'} - - fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - - fastq@1.17.1: - resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} - - fb-watchman@2.0.2: - resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} - - filelist@1.0.4: - resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + fdir@6.4.6: + resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} + filter-obj@5.1.0: + resolution: {integrity: sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==} + engines: {node: '>=14.16'} - find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} + fix-dts-default-cjs-exports@1.0.1: + resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} foreground-child@3.1.1: resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} engines: {node: '>=14'} - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - 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==} - - gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - - get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - - get-package-type@0.1.0: - resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} - engines: {node: '>=8.0.0'} - - get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - glob@10.4.1: resolution: {integrity: sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==} engines: {node: '>=16 || 14 >=14.18'} hasBin: true - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported - - globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} - - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - - 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'} - - html-escaper@2.0.2: - resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - html-to-text@9.0.5: resolution: {integrity: sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==} engines: {node: '>=14'} @@ -1023,232 +679,25 @@ packages: htmlparser2@8.0.2: resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} - human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - ignore@5.3.1: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} - import-local@3.1.0: - resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} - engines: {node: '>=8'} - hasBin: true - - imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - - inflight@1.0.6: - resolution: {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. - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - - is-core-module@2.13.1: - resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} - - 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-generator-fn@2.1.0: - resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} - engines: {node: '>=6'} - - is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} + isbinaryfile@5.0.4: + resolution: {integrity: sha512-YKBKVkKhty7s8rxddb40oOkuP0NbaeXrQvLin6QMHL7Ypiy2RW9LwOVrVgZRyOrhQlayMd9t+D8yDy8MKFTSDQ==} + engines: {node: '>= 18.0.0'} 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-instrument@5.2.1: - resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} - engines: {node: '>=8'} - - istanbul-lib-instrument@6.0.2: - resolution: {integrity: sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==} - engines: {node: '>=10'} - - istanbul-lib-report@3.0.1: - resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} - engines: {node: '>=10'} - - istanbul-lib-source-maps@4.0.1: - resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} - engines: {node: '>=10'} - - istanbul-reports@3.1.7: - resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} - engines: {node: '>=8'} - jackspeak@3.4.0: resolution: {integrity: sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==} engines: {node: '>=14'} - jake@10.9.2: - resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==} - engines: {node: '>=10'} - hasBin: true - - jest-changed-files@29.7.0: - resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-circus@29.7.0: - resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-cli@29.7.0: - resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - - jest-config@29.7.0: - resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} - 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 - - jest-diff@29.7.0: - resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-docblock@29.7.0: - resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-each@29.7.0: - resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-environment-node@29.7.0: - resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-fetch-mock@3.0.3: - resolution: {integrity: sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==} - - jest-get-type@29.6.3: - resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-haste-map@29.7.0: - resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-leak-detector@29.7.0: - resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-matcher-utils@29.7.0: - resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-message-util@29.7.0: - resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-mock@29.7.0: - resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-pnp-resolver@1.2.3: - resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} - engines: {node: '>=6'} - peerDependencies: - jest-resolve: '*' - peerDependenciesMeta: - jest-resolve: - optional: true - - jest-regex-util@29.6.3: - resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-resolve-dependencies@29.7.0: - resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-resolve@29.7.0: - resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-runner@29.7.0: - resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-runtime@29.7.0: - resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-snapshot@29.7.0: - resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-util@29.7.0: - resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-validate@29.7.0: - resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-watcher@29.7.0: - resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-worker@29.7.0: - resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest@29.7.0: - resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - joycon@3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} @@ -1256,34 +705,12 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true - - jsesc@2.5.2: - resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} - engines: {node: '>=4'} - hasBin: true - - json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - - kleur@3.0.3: - resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} - engines: {node: '>=6'} + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} leac@0.6.0: resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==} - leven@3.1.0: - resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} - engines: {node: '>=6'} - lilconfig@3.1.2: resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} engines: {node: '>=14'} @@ -1295,13 +722,6 @@ packages: resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - - lodash.memoize@4.1.2: - resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} - lodash.sortby@4.7.0: resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} @@ -1309,44 +729,15 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + loupe@3.2.0: + resolution: {integrity: sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==} + lru-cache@10.2.2: resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==} engines: {node: 14 || >=16.14} - lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - - make-dir@4.0.0: - resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} - engines: {node: '>=10'} - - make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - - makeerror@1.0.12: - resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - - mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - - minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} minimatch@9.0.4: resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} @@ -1356,37 +747,19 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} - ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + mlly@1.7.4: + resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - - node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - - node-int64@0.4.0: - resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - - node-releases@2.0.14: - resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} - - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - - npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} @@ -1395,120 +768,93 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - - p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - - p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - - p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - - p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - - parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - parseley@0.12.1: resolution: {integrity: sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==} - path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - 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'} - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} peberminta@0.9.0: resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==} - picocolors@1.0.1: - resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + 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.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} pirates@4.0.6: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} - pkg-dir@4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} - engines: {node: '>=8'} + pkg-pr-new@0.0.56: + resolution: {integrity: sha512-QUdWJ5m0IUNIujAIwc03wJ3L1BVCpSK8jm7yIz4F2pkvQ8afVltyl+/tJTPLKs5jcd2gkQZPl71NYwMYplRvaA==} + hasBin: true - postcss-load-config@4.0.2: - resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} - engines: {node: '>= 14'} + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} peerDependencies: + jiti: '>=1.21.0' postcss: '>=8.0.9' - ts-node: '>=9.0.0' + tsx: ^4.8.1 + yaml: ^2.4.2 peerDependenciesMeta: + jiti: + optional: true postcss: optional: true - ts-node: + tsx: optional: true + yaml: + optional: true + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} prettier@3.5.3: resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} engines: {node: '>=14'} hasBin: true - pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - promise-polyfill@8.3.0: - resolution: {integrity: sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==} - - prompts@2.4.2: - resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} - engines: {node: '>= 6'} - punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - pure-rand@6.1.0: - resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + query-registry@3.0.1: + resolution: {integrity: sha512-M9RxRITi2mHMVPU5zysNjctUT8bAPx6ltEXo/ir9+qmiM47Y7f0Ir3+OxUO5OjYAWdicBQRew7RtHtqUXydqlg==} + engines: {node: '>=20'} - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + query-string@9.2.2: + resolution: {integrity: sha512-pDSIZJ9sFuOp6VnD+5IkakSVf+rICAuuU88Hcsr6AKL0QtxSIfVuKiVP2oahFI7tk3CRSexwV+Ya6MOoTxzg9g==} + engines: {node: '>=18'} + + quick-lru@7.1.0: + resolution: {integrity: sha512-Pzd/4IFnTb8E+I1P5rbLQoqpUHcXKg48qTYKi4EANg+sTPwGFEMOcYGiiZz6xuQcOMZP7MPsrdAPx+16Q8qahg==} + engines: {node: '>=18'} react-dom@18.3.1: resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} peerDependencies: react: ^18.3.1 - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - react-promise-suspense@0.3.4: resolution: {integrity: sha512-I42jl7L3Ze6kZaq+7zXWSunBa3b1on5yfvUW6Eo/3fFOj6dZ5Bqmcd264nJbTK/gn1HjjILAjSwnZbV4RpSaNQ==} @@ -1516,62 +862,25 @@ packages: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - - require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - - resolve-cwd@3.0.0: - resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} - engines: {node: '>=8'} + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} - resolve.exports@2.0.2: - resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} - engines: {node: '>=10'} - - resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} - hasBin: true - - reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - - rollup@3.29.5: - resolution: {integrity: sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==} - engines: {node: '>=14.18.0', npm: '>=8.0.0'} + rollup@4.46.2: + resolution: {integrity: sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} selderee@0.11.0: resolution: {integrity: sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==} - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - - semver@7.7.1: - resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} - engines: {node: '>=10'} - hasBin: true - - 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'} @@ -1580,41 +889,35 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - - source-map-support@0.5.13: - resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} - - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} source-map@0.8.0-beta.0: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} engines: {node: '>= 8'} + deprecated: The work that was done in this beta branch won't be included in future versions - sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + split-on-first@3.0.0: + resolution: {integrity: sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==} + engines: {node: '>=12'} - stack-utils@2.0.6: - resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} - engines: {node: '>=10'} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - string-length@4.0.2: - resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} - engines: {node: '>=10'} + 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==} @@ -1632,43 +935,14 @@ packages: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} engines: {node: '>=12'} - strip-bom@4.0.0: - resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} - engines: {node: '>=8'} - - strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - - strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} + strip-literal@3.0.0: + resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} sucrase@3.35.0: resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} engines: {node: '>=16 || 14 >=14.17'} hasBin: true - supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - - supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - - test-exclude@6.0.0: - resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} - engines: {node: '>=8'} - thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} @@ -1676,19 +950,27 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - tmpl@1.0.5: - resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - to-fast-properties@2.0.0: - resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} - engines: {node: '>=4'} + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + engines: {node: '>=12.0.0'} - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} - tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@4.0.3: + resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==} + engines: {node: '>=14.0.0'} tr46@1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} @@ -1700,53 +982,18 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - ts-jest@29.3.4: - resolution: {integrity: sha512-Iqbrm8IXOmV+ggWHOTEbjwyCf2xZlUMv5npExksXohL+tk8va4Fjhb+X2+Rt9NBmgO7bJ8WpnMLOwih/DnMlFA==} - engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@babel/core': '>=7.0.0-beta.0 <8' - '@jest/transform': ^29.0.0 - '@jest/types': ^29.0.0 - babel-jest: ^29.0.0 - esbuild: '*' - 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 - - ts-node@10.9.2: - resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true - - tsup@7.2.0: - resolution: {integrity: sha512-vDHlczXbgUvY3rWvqFEbSqmC1L7woozbzngMqTtL2PGBODTtWlRwGDDawhvWzr5c1QjKe4OAKqJGfE1xeXUvtQ==} - engines: {node: '>=16.14'} + tsup@8.5.0: + resolution: {integrity: sha512-VmBp77lWNQq6PfuMqCHD3xWl22vEoWsKajkF8t+yMBawlUS8JzEI+vOVMeuNZIuMML8qXRizFKi9oD5glKQVcQ==} + engines: {node: '>=18'} hasBin: true peerDependencies: + '@microsoft/api-extractor': ^7.36.0 '@swc/core': ^1 postcss: ^8.4.12 - typescript: '>=4.1.0' + typescript: '>=4.5.0' peerDependenciesMeta: + '@microsoft/api-extractor': + optional: true '@swc/core': optional: true postcss: @@ -1754,51 +1001,118 @@ packages: typescript: optional: true - type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + type-detect@4.1.0: + resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} engines: {node: '>=4'} - type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - - type-fest@4.41.0: - resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} - engines: {node: '>=16'} - - typescript@5.8.3: - resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + typescript@5.9.2: + resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} engines: {node: '>=14.17'} hasBin: true - undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + ufo@1.6.1: + resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} - update-browserslist-db@1.0.16: - resolution: {integrity: sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + undici@6.21.3: + resolution: {integrity: sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==} + engines: {node: '>=18.17'} + + universal-user-agent@6.0.1: + resolution: {integrity: sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==} - v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + url-join@5.0.0: + resolution: {integrity: sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + validate-npm-package-name@5.0.1: + resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + vite-node@3.2.4: + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true - v8-to-istanbul@9.2.0: - resolution: {integrity: sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==} - engines: {node: '>=10.12.0'} + vite@7.1.1: + resolution: {integrity: sha512-yJ+Mp7OyV+4S+afWo+QyoL9jFWD11QFH0i5i7JypnfTcA1rmgxCbiA8WwAICDEtZ1Z1hzrVhN8R8rGTqkTY8ZQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + 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 - walker@1.0.8: - resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + vitest-fetch-mock@0.4.5: + resolution: {integrity: sha512-nhWdCQIGtaSEUVl96pMm0WggyDGPDv5FUy/Q9Hx3cs2RGmh3Q/uRsLClGbdG3kXBkJ3br5yTUjB2MeW25TwdOA==} + engines: {node: '>=18.0.0'} + peerDependencies: + vitest: '>=2.0.0' - webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + vitest@3.2.4: + resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} + 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.2.4 + '@vitest/ui': 3.2.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 webidl-conversions@4.0.2: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} - whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - whatwg-url@7.1.0: resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} @@ -1807,6 +1121,11 @@ packages: engines: {node: '>= 8'} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -1818,355 +1137,131 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - write-file-atomic@4.0.2: - resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - - y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - - yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yaml@2.4.5: resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} engines: {node: '>= 14'} hasBin: true - yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - - yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} + zod-package-json@1.2.0: + resolution: {integrity: sha512-tamtgPM3MkP+obfO2dLr/G+nYoYkpJKmuHdYEy6IXRKfLybruoJ5NUj0lM0LxwOpC9PpoGLbll1ecoeyj43Wsg==} + engines: {node: '>=20'} - yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} snapshots: - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - - '@babel/code-frame@7.24.7': - dependencies: - '@babel/highlight': 7.24.7 - picocolors: 1.0.1 - - '@babel/compat-data@7.24.7': {} - - '@babel/core@7.24.7': - dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.24.7 - '@babel/generator': 7.24.7 - '@babel/helper-compilation-targets': 7.24.7 - '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7) - '@babel/helpers': 7.24.7 - '@babel/parser': 7.24.7 - '@babel/template': 7.24.7 - '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 - convert-source-map: 2.0.0 - debug: 4.3.5 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/generator@7.24.7': - dependencies: - '@babel/types': 7.24.7 - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - jsesc: 2.5.2 - - '@babel/helper-compilation-targets@7.24.7': - dependencies: - '@babel/compat-data': 7.24.7 - '@babel/helper-validator-option': 7.24.7 - browserslist: 4.23.1 - lru-cache: 5.1.1 - semver: 6.3.1 - - '@babel/helper-environment-visitor@7.24.7': - dependencies: - '@babel/types': 7.24.7 - - '@babel/helper-function-name@7.24.7': - dependencies: - '@babel/template': 7.24.7 - '@babel/types': 7.24.7 - - '@babel/helper-hoist-variables@7.24.7': - dependencies: - '@babel/types': 7.24.7 - - '@babel/helper-module-imports@7.24.7': - dependencies: - '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-module-imports': 7.24.7 - '@babel/helper-simple-access': 7.24.7 - '@babel/helper-split-export-declaration': 7.24.7 - '@babel/helper-validator-identifier': 7.24.7 - transitivePeerDependencies: - - supports-color - - '@babel/helper-plugin-utils@7.24.7': {} - - '@babel/helper-simple-access@7.24.7': - dependencies: - '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 - transitivePeerDependencies: - - supports-color - - '@babel/helper-split-export-declaration@7.24.7': - dependencies: - '@babel/types': 7.24.7 - - '@babel/helper-string-parser@7.24.7': {} - - '@babel/helper-validator-identifier@7.24.7': {} - - '@babel/helper-validator-option@7.24.7': {} - - '@babel/helpers@7.24.7': - dependencies: - '@babel/template': 7.24.7 - '@babel/types': 7.24.7 - - '@babel/highlight@7.24.7': - dependencies: - '@babel/helper-validator-identifier': 7.24.7 - chalk: 2.4.2 - js-tokens: 4.0.0 - picocolors: 1.0.1 - - '@babel/parser@7.24.7': - dependencies: - '@babel/types': 7.24.7 - - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/plugin-syntax-typescript@7.24.7(@babel/core@7.24.7)': - dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - - '@babel/template@7.24.7': - dependencies: - '@babel/code-frame': 7.24.7 - '@babel/parser': 7.24.7 - '@babel/types': 7.24.7 - - '@babel/traverse@7.24.7': - dependencies: - '@babel/code-frame': 7.24.7 - '@babel/generator': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-function-name': 7.24.7 - '@babel/helper-hoist-variables': 7.24.7 - '@babel/helper-split-export-declaration': 7.24.7 - '@babel/parser': 7.24.7 - '@babel/types': 7.24.7 - debug: 4.3.5 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color + '@biomejs/biome@2.2.0': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 2.2.0 + '@biomejs/cli-darwin-x64': 2.2.0 + '@biomejs/cli-linux-arm64': 2.2.0 + '@biomejs/cli-linux-arm64-musl': 2.2.0 + '@biomejs/cli-linux-x64': 2.2.0 + '@biomejs/cli-linux-x64-musl': 2.2.0 + '@biomejs/cli-win32-arm64': 2.2.0 + '@biomejs/cli-win32-x64': 2.2.0 + + '@biomejs/cli-darwin-arm64@2.2.0': + optional: true - '@babel/types@7.24.7': - dependencies: - '@babel/helper-string-parser': 7.24.7 - '@babel/helper-validator-identifier': 7.24.7 - to-fast-properties: 2.0.0 + '@biomejs/cli-darwin-x64@2.2.0': + optional: true - '@bcoe/v8-coverage@0.2.3': {} + '@biomejs/cli-linux-arm64-musl@2.2.0': + optional: true - '@biomejs/biome@1.9.4': - optionalDependencies: - '@biomejs/cli-darwin-arm64': 1.9.4 - '@biomejs/cli-darwin-x64': 1.9.4 - '@biomejs/cli-linux-arm64': 1.9.4 - '@biomejs/cli-linux-arm64-musl': 1.9.4 - '@biomejs/cli-linux-x64': 1.9.4 - '@biomejs/cli-linux-x64-musl': 1.9.4 - '@biomejs/cli-win32-arm64': 1.9.4 - '@biomejs/cli-win32-x64': 1.9.4 - - '@biomejs/cli-darwin-arm64@1.9.4': + '@biomejs/cli-linux-arm64@2.2.0': optional: true - '@biomejs/cli-darwin-x64@1.9.4': + '@biomejs/cli-linux-x64-musl@2.2.0': optional: true - '@biomejs/cli-linux-arm64-musl@1.9.4': + '@biomejs/cli-linux-x64@2.2.0': optional: true - '@biomejs/cli-linux-arm64@1.9.4': + '@biomejs/cli-win32-arm64@2.2.0': optional: true - '@biomejs/cli-linux-x64-musl@1.9.4': + '@biomejs/cli-win32-x64@2.2.0': optional: true - '@biomejs/cli-linux-x64@1.9.4': + '@esbuild/aix-ppc64@0.25.8': optional: true - '@biomejs/cli-win32-arm64@1.9.4': + '@esbuild/android-arm64@0.25.8': optional: true - '@biomejs/cli-win32-x64@1.9.4': + '@esbuild/android-arm@0.25.8': optional: true - '@cspotcode/source-map-support@0.8.1': - dependencies: - '@jridgewell/trace-mapping': 0.3.9 + '@esbuild/android-x64@0.25.8': + optional: true - '@esbuild/android-arm64@0.18.20': + '@esbuild/darwin-arm64@0.25.8': optional: true - '@esbuild/android-arm@0.18.20': + '@esbuild/darwin-x64@0.25.8': optional: true - '@esbuild/android-x64@0.18.20': + '@esbuild/freebsd-arm64@0.25.8': optional: true - '@esbuild/darwin-arm64@0.18.20': + '@esbuild/freebsd-x64@0.25.8': optional: true - '@esbuild/darwin-x64@0.18.20': + '@esbuild/linux-arm64@0.25.8': optional: true - '@esbuild/freebsd-arm64@0.18.20': + '@esbuild/linux-arm@0.25.8': optional: true - '@esbuild/freebsd-x64@0.18.20': + '@esbuild/linux-ia32@0.25.8': optional: true - '@esbuild/linux-arm64@0.18.20': + '@esbuild/linux-loong64@0.25.8': optional: true - '@esbuild/linux-arm@0.18.20': + '@esbuild/linux-mips64el@0.25.8': optional: true - '@esbuild/linux-ia32@0.18.20': + '@esbuild/linux-ppc64@0.25.8': optional: true - '@esbuild/linux-loong64@0.18.20': + '@esbuild/linux-riscv64@0.25.8': optional: true - '@esbuild/linux-mips64el@0.18.20': + '@esbuild/linux-s390x@0.25.8': optional: true - '@esbuild/linux-ppc64@0.18.20': + '@esbuild/linux-x64@0.25.8': optional: true - '@esbuild/linux-riscv64@0.18.20': + '@esbuild/netbsd-arm64@0.25.8': optional: true - '@esbuild/linux-s390x@0.18.20': + '@esbuild/netbsd-x64@0.25.8': optional: true - '@esbuild/linux-x64@0.18.20': + '@esbuild/openbsd-arm64@0.25.8': optional: true - '@esbuild/netbsd-x64@0.18.20': + '@esbuild/openbsd-x64@0.25.8': optional: true - '@esbuild/openbsd-x64@0.18.20': + '@esbuild/openharmony-arm64@0.25.8': optional: true - '@esbuild/sunos-x64@0.18.20': + '@esbuild/sunos-x64@0.25.8': optional: true - '@esbuild/win32-arm64@0.18.20': + '@esbuild/win32-arm64@0.25.8': optional: true - '@esbuild/win32-ia32@0.18.20': + '@esbuild/win32-ia32@0.25.8': optional: true - '@esbuild/win32-x64@0.18.20': + '@esbuild/win32-x64@0.25.8': optional: true '@isaacs/cliui@8.0.2': @@ -2178,1242 +1273,464 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@istanbuljs/load-nyc-config@1.1.0': - dependencies: - camelcase: 5.3.1 - find-up: 4.1.0 - get-package-type: 0.1.0 - js-yaml: 3.14.1 - resolve-from: 5.0.0 - - '@istanbuljs/schema@0.1.3': {} - - '@jest/console@29.7.0': - dependencies: - '@jest/types': 29.6.3 - '@types/node': 18.19.86 - chalk: 4.1.2 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - slash: 3.0.0 - - '@jest/core@29.7.0(ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.3))': - 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': 18.19.86 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.9.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@18.19.86)(ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.3)) - 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.8 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - ts-node - - '@jest/environment@29.7.0': - dependencies: - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 18.19.86 - jest-mock: 29.7.0 - - '@jest/expect-utils@29.7.0': - dependencies: - jest-get-type: 29.6.3 - - '@jest/expect@29.7.0': - dependencies: - expect: 29.7.0 - jest-snapshot: 29.7.0 - transitivePeerDependencies: - - supports-color - - '@jest/fake-timers@29.7.0': - dependencies: - '@jest/types': 29.6.3 - '@sinonjs/fake-timers': 10.3.0 - '@types/node': 18.19.86 - jest-message-util: 29.7.0 - jest-mock: 29.7.0 - jest-util: 29.7.0 - - '@jest/globals@29.7.0': - dependencies: - '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 - '@jest/types': 29.6.3 - jest-mock: 29.7.0 - transitivePeerDependencies: - - supports-color - - '@jest/reporters@29.7.0': - 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.25 - '@types/node': 18.19.86 - chalk: 4.1.2 - collect-v8-coverage: 1.0.2 - exit: 0.1.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - istanbul-lib-coverage: 3.2.2 - istanbul-lib-instrument: 6.0.2 - istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.7 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - jest-worker: 29.7.0 - slash: 3.0.0 - string-length: 4.0.2 - strip-ansi: 6.0.1 - v8-to-istanbul: 9.2.0 - transitivePeerDependencies: - - supports-color - - '@jest/schemas@29.6.3': - dependencies: - '@sinclair/typebox': 0.27.8 - - '@jest/source-map@29.6.3': - dependencies: - '@jridgewell/trace-mapping': 0.3.25 - callsites: 3.1.0 - graceful-fs: 4.2.11 - - '@jest/test-result@29.7.0': - dependencies: - '@jest/console': 29.7.0 - '@jest/types': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.6 - collect-v8-coverage: 1.0.2 - - '@jest/test-sequencer@29.7.0': - dependencies: - '@jest/test-result': 29.7.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - slash: 3.0.0 - - '@jest/transform@29.7.0': - dependencies: - '@babel/core': 7.24.7 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.25 - babel-plugin-istanbul: 6.1.1 - chalk: 4.1.2 - convert-source-map: 2.0.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - micromatch: 4.0.8 - pirates: 4.0.6 - slash: 3.0.0 - write-file-atomic: 4.0.2 - transitivePeerDependencies: - - supports-color - - '@jest/types@29.6.3': - dependencies: - '@jest/schemas': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.6 - '@types/istanbul-reports': 3.0.4 - '@types/node': 18.19.86 - '@types/yargs': 17.0.32 - chalk: 4.1.2 - '@jridgewell/gen-mapping@0.3.5': dependencies: '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.4 '@jridgewell/trace-mapping': 0.3.25 '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/set-array@1.2.1': {} - '@jridgewell/sourcemap-codec@1.4.15': {} + '@jridgewell/sourcemap-codec@1.5.4': {} '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 - - '@jridgewell/trace-mapping@0.3.9': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 - - '@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.17.1 - - '@pkgjs/parseargs@0.11.0': - optional: true - - '@react-email/render@1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - html-to-text: 9.0.5 - prettier: 3.5.3 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-promise-suspense: 0.3.4 - - '@selderee/plugin-htmlparser2@0.11.0': - dependencies: - domhandler: 5.0.3 - selderee: 0.11.0 - - '@sinclair/typebox@0.27.8': {} - - '@sinonjs/commons@3.0.1': - dependencies: - type-detect: 4.0.8 - - '@sinonjs/fake-timers@10.3.0': - dependencies: - '@sinonjs/commons': 3.0.1 - - '@tsconfig/node10@1.0.11': {} - - '@tsconfig/node12@1.0.11': {} - - '@tsconfig/node14@1.0.3': {} - - '@tsconfig/node16@1.0.4': {} - - '@types/babel__core@7.20.5': - dependencies: - '@babel/parser': 7.24.7 - '@babel/types': 7.24.7 - '@types/babel__generator': 7.6.8 - '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.20.6 - - '@types/babel__generator@7.6.8': - dependencies: - '@babel/types': 7.24.7 - - '@types/babel__template@7.4.4': - dependencies: - '@babel/parser': 7.24.7 - '@babel/types': 7.24.7 - - '@types/babel__traverse@7.20.6': - dependencies: - '@babel/types': 7.24.7 + '@jridgewell/sourcemap-codec': 1.5.4 - '@types/graceful-fs@4.1.9': + '@jsdevtools/ez-spawn@3.0.4': dependencies: - '@types/node': 18.19.86 - - '@types/istanbul-lib-coverage@2.0.6': {} - - '@types/istanbul-lib-report@3.0.3': - dependencies: - '@types/istanbul-lib-coverage': 2.0.6 - - '@types/istanbul-reports@3.0.4': - dependencies: - '@types/istanbul-lib-report': 3.0.3 - - '@types/jest@29.5.14': - dependencies: - expect: 29.7.0 - pretty-format: 29.7.0 - - '@types/node@18.19.86': - dependencies: - undici-types: 5.26.5 - - '@types/react@19.1.2': - dependencies: - csstype: 3.1.3 - - '@types/stack-utils@2.0.3': {} - - '@types/yargs-parser@21.0.3': {} - - '@types/yargs@17.0.32': - dependencies: - '@types/yargs-parser': 21.0.3 - - acorn-walk@8.3.2: {} - - acorn@8.11.3: {} - - ansi-escapes@4.3.2: - dependencies: - type-fest: 0.21.3 - - ansi-regex@5.0.1: {} - - ansi-regex@6.0.1: {} - - ansi-styles@3.2.1: - dependencies: - color-convert: 1.9.3 - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - ansi-styles@5.2.0: {} - - ansi-styles@6.2.1: {} - - any-promise@1.3.0: {} - - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - - arg@4.1.3: {} - - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 - - array-union@2.1.0: {} - - async@3.2.5: {} - - babel-jest@29.7.0(@babel/core@7.24.7): - dependencies: - '@babel/core': 7.24.7 - '@jest/transform': 29.7.0 - '@types/babel__core': 7.20.5 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.24.7) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - - babel-plugin-istanbul@6.1.1: - dependencies: - '@babel/helper-plugin-utils': 7.24.7 - '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-instrument: 5.2.1 - test-exclude: 6.0.0 - transitivePeerDependencies: - - supports-color - - babel-plugin-jest-hoist@29.6.3: - dependencies: - '@babel/template': 7.24.7 - '@babel/types': 7.24.7 - '@types/babel__core': 7.20.5 - '@types/babel__traverse': 7.20.6 - - babel-preset-current-node-syntax@1.0.1(@babel/core@7.24.7): - dependencies: - '@babel/core': 7.24.7 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.7) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.7) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.7) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.7) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.7) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.7) - - babel-preset-jest@29.6.3(@babel/core@7.24.7): - dependencies: - '@babel/core': 7.24.7 - babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.7) - - balanced-match@1.0.2: {} - - binary-extensions@2.3.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.23.1: - dependencies: - caniuse-lite: 1.0.30001632 - electron-to-chromium: 1.4.797 - node-releases: 2.0.14 - update-browserslist-db: 1.0.16(browserslist@4.23.1) - - bs-logger@0.2.6: - dependencies: - fast-json-stable-stringify: 2.1.0 - - bser@2.1.1: - dependencies: - node-int64: 0.4.0 - - buffer-from@1.1.2: {} - - bundle-require@4.2.1(esbuild@0.18.20): - dependencies: - esbuild: 0.18.20 - load-tsconfig: 0.2.5 - - cac@6.7.14: {} - - callsites@3.1.0: {} - - camelcase@5.3.1: {} - - camelcase@6.3.0: {} - - caniuse-lite@1.0.30001632: {} + call-me-maybe: 1.0.2 + cross-spawn: 7.0.6 + string-argv: 0.3.2 + type-detect: 4.1.0 - chalk@2.4.2: + '@octokit/action@6.1.0': dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 + '@octokit/auth-action': 4.1.0 + '@octokit/core': 5.2.2 + '@octokit/plugin-paginate-rest': 9.2.2(@octokit/core@5.2.2) + '@octokit/plugin-rest-endpoint-methods': 10.4.1(@octokit/core@5.2.2) + '@octokit/types': 12.6.0 + undici: 6.21.3 - chalk@4.1.2: + '@octokit/auth-action@4.1.0': dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - char-regex@1.0.2: {} - - chokidar@3.6.0: - dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 + '@octokit/auth-token': 4.0.0 + '@octokit/types': 13.10.0 - ci-info@3.9.0: {} + '@octokit/auth-token@4.0.0': {} - cjs-module-lexer@1.3.1: {} - - cliui@8.0.1: + '@octokit/core@5.2.2': dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - - co@4.6.0: {} + '@octokit/auth-token': 4.0.0 + '@octokit/graphql': 7.1.1 + '@octokit/request': 8.4.1 + '@octokit/request-error': 5.1.1 + '@octokit/types': 13.10.0 + before-after-hook: 2.2.3 + universal-user-agent: 6.0.1 - collect-v8-coverage@1.0.2: {} - - color-convert@1.9.3: + '@octokit/endpoint@9.0.6': dependencies: - color-name: 1.1.3 + '@octokit/types': 13.10.0 + universal-user-agent: 6.0.1 - color-convert@2.0.1: + '@octokit/graphql@7.1.1': dependencies: - color-name: 1.1.4 + '@octokit/request': 8.4.1 + '@octokit/types': 13.10.0 + universal-user-agent: 6.0.1 - color-name@1.1.3: {} + '@octokit/openapi-types@20.0.0': {} - color-name@1.1.4: {} - - commander@4.1.1: {} - - concat-map@0.0.1: {} - - convert-source-map@2.0.0: {} - - create-jest@29.7.0(@types/node@18.19.86)(ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.3)): - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@18.19.86)(ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.3)) - jest-util: 29.7.0 - prompts: 2.4.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - - create-require@1.1.1: {} - - cross-fetch@3.1.8: - dependencies: - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding + '@octokit/openapi-types@24.2.0': {} - cross-spawn@7.0.6: + '@octokit/plugin-paginate-rest@9.2.2(@octokit/core@5.2.2)': dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - csstype@3.1.3: {} + '@octokit/core': 5.2.2 + '@octokit/types': 12.6.0 - debug@4.3.5: + '@octokit/plugin-rest-endpoint-methods@10.4.1(@octokit/core@5.2.2)': dependencies: - ms: 2.1.2 + '@octokit/core': 5.2.2 + '@octokit/types': 12.6.0 - dedent@1.5.3: {} - - deepmerge@4.3.1: {} - - detect-newline@3.1.0: {} - - diff-sequences@29.6.3: {} - - diff@4.0.2: {} - - dir-glob@3.0.1: + '@octokit/request-error@5.1.1': dependencies: - path-type: 4.0.0 + '@octokit/types': 13.10.0 + deprecation: 2.3.1 + once: 1.4.0 - dom-serializer@2.0.0: + '@octokit/request@8.4.1': dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - entities: 4.5.0 + '@octokit/endpoint': 9.0.6 + '@octokit/request-error': 5.1.1 + '@octokit/types': 13.10.0 + universal-user-agent: 6.0.1 - domelementtype@2.3.0: {} - - domhandler@5.0.3: + '@octokit/types@12.6.0': dependencies: - domelementtype: 2.3.0 + '@octokit/openapi-types': 20.0.0 - domutils@3.1.0: + '@octokit/types@13.10.0': dependencies: - dom-serializer: 2.0.0 - domelementtype: 2.3.0 - domhandler: 5.0.3 + '@octokit/openapi-types': 24.2.0 - eastasianwidth@0.2.0: {} + '@pkgjs/parseargs@0.11.0': + optional: true - ejs@3.1.10: + '@react-email/render@1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - jake: 10.9.2 - - electron-to-chromium@1.4.797: {} - - emittery@0.13.1: {} - - emoji-regex@8.0.0: {} - - emoji-regex@9.2.2: {} - - entities@4.5.0: {} + html-to-text: 9.0.5 + prettier: 3.5.3 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-promise-suspense: 0.3.4 - error-ex@1.3.2: - dependencies: - is-arrayish: 0.2.1 + '@rollup/rollup-android-arm-eabi@4.46.2': + optional: true - esbuild@0.18.20: - optionalDependencies: - '@esbuild/android-arm': 0.18.20 - '@esbuild/android-arm64': 0.18.20 - '@esbuild/android-x64': 0.18.20 - '@esbuild/darwin-arm64': 0.18.20 - '@esbuild/darwin-x64': 0.18.20 - '@esbuild/freebsd-arm64': 0.18.20 - '@esbuild/freebsd-x64': 0.18.20 - '@esbuild/linux-arm': 0.18.20 - '@esbuild/linux-arm64': 0.18.20 - '@esbuild/linux-ia32': 0.18.20 - '@esbuild/linux-loong64': 0.18.20 - '@esbuild/linux-mips64el': 0.18.20 - '@esbuild/linux-ppc64': 0.18.20 - '@esbuild/linux-riscv64': 0.18.20 - '@esbuild/linux-s390x': 0.18.20 - '@esbuild/linux-x64': 0.18.20 - '@esbuild/netbsd-x64': 0.18.20 - '@esbuild/openbsd-x64': 0.18.20 - '@esbuild/sunos-x64': 0.18.20 - '@esbuild/win32-arm64': 0.18.20 - '@esbuild/win32-ia32': 0.18.20 - '@esbuild/win32-x64': 0.18.20 - - escalade@3.1.2: {} - - escape-string-regexp@1.0.5: {} - - escape-string-regexp@2.0.0: {} - - esprima@4.0.1: {} - - execa@5.1.1: - dependencies: - cross-spawn: 7.0.6 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 + '@rollup/rollup-android-arm64@4.46.2': + optional: true - exit@0.1.2: {} + '@rollup/rollup-darwin-arm64@4.46.2': + optional: true - expect@29.7.0: - 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 + '@rollup/rollup-darwin-x64@4.46.2': + optional: true - fast-deep-equal@2.0.1: {} + '@rollup/rollup-freebsd-arm64@4.46.2': + optional: true - fast-glob@3.3.2: - 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 + '@rollup/rollup-freebsd-x64@4.46.2': + optional: true - fast-json-stable-stringify@2.1.0: {} + '@rollup/rollup-linux-arm-gnueabihf@4.46.2': + optional: true - fastq@1.17.1: - dependencies: - reusify: 1.0.4 + '@rollup/rollup-linux-arm-musleabihf@4.46.2': + optional: true - fb-watchman@2.0.2: - dependencies: - bser: 2.1.1 + '@rollup/rollup-linux-arm64-gnu@4.46.2': + optional: true - filelist@1.0.4: - dependencies: - minimatch: 5.1.6 + '@rollup/rollup-linux-arm64-musl@4.46.2': + optional: true - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 + '@rollup/rollup-linux-loongarch64-gnu@4.46.2': + optional: true - find-up@4.1.0: - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 + '@rollup/rollup-linux-ppc64-gnu@4.46.2': + optional: true - foreground-child@3.1.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 + '@rollup/rollup-linux-riscv64-gnu@4.46.2': + optional: true - fs.realpath@1.0.0: {} + '@rollup/rollup-linux-riscv64-musl@4.46.2': + optional: true - fsevents@2.3.3: + '@rollup/rollup-linux-s390x-gnu@4.46.2': optional: true - function-bind@1.1.2: {} + '@rollup/rollup-linux-x64-gnu@4.46.2': + optional: true - gensync@1.0.0-beta.2: {} + '@rollup/rollup-linux-x64-musl@4.46.2': + optional: true - get-caller-file@2.0.5: {} + '@rollup/rollup-win32-arm64-msvc@4.46.2': + optional: true - get-package-type@0.1.0: {} + '@rollup/rollup-win32-ia32-msvc@4.46.2': + optional: true - get-stream@6.0.1: {} + '@rollup/rollup-win32-x64-msvc@4.46.2': + optional: true - glob-parent@5.1.2: + '@selderee/plugin-htmlparser2@0.11.0': dependencies: - is-glob: 4.0.3 + domhandler: 5.0.3 + selderee: 0.11.0 - glob@10.4.1: + '@types/chai@5.2.2': dependencies: - foreground-child: 3.1.1 - jackspeak: 3.4.0 - minimatch: 9.0.4 - minipass: 7.1.2 - path-scurry: 1.11.1 + '@types/deep-eql': 4.0.2 - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 + '@types/deep-eql@4.0.2': {} - globals@11.12.0: {} + '@types/estree@1.0.8': {} - globby@11.1.0: + '@types/node@22.17.2': dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.2 - ignore: 5.3.1 - merge2: 1.4.1 - slash: 3.0.0 + undici-types: 6.21.0 - graceful-fs@4.2.11: {} - - has-flag@3.0.0: {} - - has-flag@4.0.0: {} - - hasown@2.0.2: + '@types/react@19.1.10': dependencies: - function-bind: 1.1.2 - - html-escaper@2.0.2: {} + csstype: 3.1.3 - html-to-text@9.0.5: + '@vitest/expect@3.2.4': dependencies: - '@selderee/plugin-htmlparser2': 0.11.0 - deepmerge: 4.3.1 - dom-serializer: 2.0.0 - htmlparser2: 8.0.2 - selderee: 0.11.0 + '@types/chai': 5.2.2 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.2.1 + tinyrainbow: 2.0.0 - htmlparser2@8.0.2: + '@vitest/mocker@3.2.4(vite@7.1.1(@types/node@22.17.2)(yaml@2.4.5))': dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - domutils: 3.1.0 - entities: 4.5.0 - - human-signals@2.1.0: {} - - ignore@5.3.1: {} + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 7.1.1(@types/node@22.17.2)(yaml@2.4.5) - import-local@3.1.0: + '@vitest/pretty-format@3.2.4': dependencies: - pkg-dir: 4.2.0 - resolve-cwd: 3.0.0 + tinyrainbow: 2.0.0 - imurmurhash@0.1.4: {} - - inflight@1.0.6: + '@vitest/runner@3.2.4': dependencies: - once: 1.4.0 - wrappy: 1.0.2 - - inherits@2.0.4: {} + '@vitest/utils': 3.2.4 + pathe: 2.0.3 + strip-literal: 3.0.0 - is-arrayish@0.2.1: {} + '@vitest/snapshot@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + magic-string: 0.30.17 + pathe: 2.0.3 - is-binary-path@2.1.0: + '@vitest/spy@3.2.4': dependencies: - binary-extensions: 2.3.0 + tinyspy: 4.0.3 - is-core-module@2.13.1: + '@vitest/utils@3.2.4': dependencies: - hasown: 2.0.2 + '@vitest/pretty-format': 3.2.4 + loupe: 3.2.0 + tinyrainbow: 2.0.0 - is-extglob@2.1.1: {} + acorn@8.15.0: {} - is-fullwidth-code-point@3.0.0: {} + ansi-regex@5.0.1: {} - is-generator-fn@2.1.0: {} + ansi-regex@6.0.1: {} - is-glob@4.0.3: + ansi-styles@4.3.0: dependencies: - is-extglob: 2.1.1 + color-convert: 2.0.1 - is-number@7.0.0: {} + ansi-styles@6.2.1: {} - is-stream@2.0.1: {} + any-promise@1.3.0: {} - isexe@2.0.0: {} + assertion-error@2.0.1: {} - istanbul-lib-coverage@3.2.2: {} + balanced-match@1.0.2: {} - istanbul-lib-instrument@5.2.1: - dependencies: - '@babel/core': 7.24.7 - '@babel/parser': 7.24.7 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.2 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color + before-after-hook@2.2.3: {} - istanbul-lib-instrument@6.0.2: + brace-expansion@2.0.1: dependencies: - '@babel/core': 7.24.7 - '@babel/parser': 7.24.7 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.2 - semver: 7.7.1 - transitivePeerDependencies: - - supports-color + balanced-match: 1.0.2 - istanbul-lib-report@3.0.1: + bundle-require@5.1.0(esbuild@0.25.8): dependencies: - istanbul-lib-coverage: 3.2.2 - make-dir: 4.0.0 - supports-color: 7.2.0 + esbuild: 0.25.8 + load-tsconfig: 0.2.5 + + cac@6.7.14: {} - istanbul-lib-source-maps@4.0.1: + call-me-maybe@1.0.2: {} + + chai@5.2.1: dependencies: - debug: 4.3.5 - istanbul-lib-coverage: 3.2.2 - source-map: 0.6.1 - transitivePeerDependencies: - - supports-color + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.2.0 + pathval: 2.0.1 + + check-error@2.1.1: {} - istanbul-reports@3.1.7: + chokidar@4.0.3: dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.1 + readdirp: 4.1.2 - jackspeak@3.4.0: + color-convert@2.0.1: dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 + color-name: 1.1.4 - jake@10.9.2: - dependencies: - async: 3.2.5 - chalk: 4.1.2 - filelist: 1.0.4 - minimatch: 3.1.2 - - jest-changed-files@29.7.0: - dependencies: - execa: 5.1.1 - jest-util: 29.7.0 - p-limit: 3.1.0 - - jest-circus@29.7.0: - dependencies: - '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 18.19.86 - chalk: 4.1.2 - co: 4.6.0 - dedent: 1.5.3 - is-generator-fn: 2.1.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.1.0 - slash: 3.0.0 - stack-utils: 2.0.6 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color + color-name@1.1.4: {} - jest-cli@29.7.0(@types/node@18.19.86)(ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.3)): - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.3)) - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0(@types/node@18.19.86)(ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.3)) - exit: 0.1.2 - import-local: 3.1.0 - jest-config: 29.7.0(@types/node@18.19.86)(ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.3)) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node + commander@4.1.1: {} - jest-config@29.7.0(@types/node@18.19.86)(ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.3)): - dependencies: - '@babel/core': 7.24.7 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.24.7) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - 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.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 18.19.86 - ts-node: 10.9.2(@types/node@18.19.86)(typescript@5.8.3) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color + confbox@0.1.8: {} - jest-diff@29.7.0: - dependencies: - chalk: 4.1.2 - diff-sequences: 29.6.3 - jest-get-type: 29.6.3 - pretty-format: 29.7.0 + consola@3.4.2: {} - jest-docblock@29.7.0: + cross-spawn@7.0.6: dependencies: - detect-newline: 3.1.0 + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 - jest-each@29.7.0: - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - jest-get-type: 29.6.3 - jest-util: 29.7.0 - pretty-format: 29.7.0 + csstype@3.1.3: {} - jest-environment-node@29.7.0: + debug@4.4.1: dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 18.19.86 - jest-mock: 29.7.0 - jest-util: 29.7.0 + ms: 2.1.3 - jest-fetch-mock@3.0.3: - dependencies: - cross-fetch: 3.1.8 - promise-polyfill: 8.3.0 - transitivePeerDependencies: - - encoding - - jest-get-type@29.6.3: {} - - jest-haste-map@29.7.0: - dependencies: - '@jest/types': 29.6.3 - '@types/graceful-fs': 4.1.9 - '@types/node': 18.19.86 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - jest-worker: 29.7.0 - micromatch: 4.0.8 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.3 + decode-uri-component@0.4.1: {} - jest-leak-detector@29.7.0: - dependencies: - jest-get-type: 29.6.3 - pretty-format: 29.7.0 + deep-eql@5.0.2: {} + + deepmerge@4.3.1: {} - jest-matcher-utils@29.7.0: + deprecation@2.3.1: {} + + dom-serializer@2.0.0: dependencies: - chalk: 4.1.2 - jest-diff: 29.7.0 - jest-get-type: 29.6.3 - pretty-format: 29.7.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} - jest-message-util@29.7.0: + domhandler@5.0.3: dependencies: - '@babel/code-frame': 7.24.7 - '@jest/types': 29.6.3 - '@types/stack-utils': 2.0.3 - chalk: 4.1.2 - graceful-fs: 4.2.11 - micromatch: 4.0.8 - pretty-format: 29.7.0 - slash: 3.0.0 - stack-utils: 2.0.6 + domelementtype: 2.3.0 - jest-mock@29.7.0: + domutils@3.1.0: dependencies: - '@jest/types': 29.6.3 - '@types/node': 18.19.86 - jest-util: 29.7.0 + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 - jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): - optionalDependencies: - jest-resolve: 29.7.0 + eastasianwidth@0.2.0: {} - jest-regex-util@29.6.3: {} + emoji-regex@8.0.0: {} - jest-resolve-dependencies@29.7.0: - dependencies: - jest-regex-util: 29.6.3 - jest-snapshot: 29.7.0 - transitivePeerDependencies: - - supports-color + emoji-regex@9.2.2: {} - jest-resolve@29.7.0: - dependencies: - chalk: 4.1.2 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) - jest-util: 29.7.0 - jest-validate: 29.7.0 - resolve: 1.22.8 - resolve.exports: 2.0.2 - slash: 3.0.0 - - jest-runner@29.7.0: - 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': 18.19.86 - chalk: 4.1.2 - emittery: 0.13.1 - graceful-fs: 4.2.11 - 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 - transitivePeerDependencies: - - supports-color + entities@4.5.0: {} - jest-runtime@29.7.0: - 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': 18.19.86 - chalk: 4.1.2 - cjs-module-lexer: 1.3.1 - collect-v8-coverage: 1.0.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - 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 - transitivePeerDependencies: - - supports-color + es-module-lexer@1.7.0: {} - jest-snapshot@29.7.0: - dependencies: - '@babel/core': 7.24.7 - '@babel/generator': 7.24.7 - '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-syntax-typescript': 7.24.7(@babel/core@7.24.7) - '@babel/types': 7.24.7 - '@jest/expect-utils': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.7) - chalk: 4.1.2 - expect: 29.7.0 - graceful-fs: 4.2.11 - 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.7.1 - transitivePeerDependencies: - - supports-color + esbuild@0.25.8: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.8 + '@esbuild/android-arm': 0.25.8 + '@esbuild/android-arm64': 0.25.8 + '@esbuild/android-x64': 0.25.8 + '@esbuild/darwin-arm64': 0.25.8 + '@esbuild/darwin-x64': 0.25.8 + '@esbuild/freebsd-arm64': 0.25.8 + '@esbuild/freebsd-x64': 0.25.8 + '@esbuild/linux-arm': 0.25.8 + '@esbuild/linux-arm64': 0.25.8 + '@esbuild/linux-ia32': 0.25.8 + '@esbuild/linux-loong64': 0.25.8 + '@esbuild/linux-mips64el': 0.25.8 + '@esbuild/linux-ppc64': 0.25.8 + '@esbuild/linux-riscv64': 0.25.8 + '@esbuild/linux-s390x': 0.25.8 + '@esbuild/linux-x64': 0.25.8 + '@esbuild/netbsd-arm64': 0.25.8 + '@esbuild/netbsd-x64': 0.25.8 + '@esbuild/openbsd-arm64': 0.25.8 + '@esbuild/openbsd-x64': 0.25.8 + '@esbuild/openharmony-arm64': 0.25.8 + '@esbuild/sunos-x64': 0.25.8 + '@esbuild/win32-arm64': 0.25.8 + '@esbuild/win32-ia32': 0.25.8 + '@esbuild/win32-x64': 0.25.8 + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + expect-type@1.2.2: {} - jest-util@29.7.0: + fast-deep-equal@2.0.1: {} + + fdir@6.4.6(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + filter-obj@5.1.0: {} + + fix-dts-default-cjs-exports@1.0.1: dependencies: - '@jest/types': 29.6.3 - '@types/node': 18.19.86 - chalk: 4.1.2 - ci-info: 3.9.0 - graceful-fs: 4.2.11 - picomatch: 2.3.1 + magic-string: 0.30.17 + mlly: 1.7.4 + rollup: 4.46.2 - jest-validate@29.7.0: + foreground-child@3.1.1: dependencies: - '@jest/types': 29.6.3 - camelcase: 6.3.0 - chalk: 4.1.2 - jest-get-type: 29.6.3 - leven: 3.1.0 - pretty-format: 29.7.0 + cross-spawn: 7.0.6 + signal-exit: 4.1.0 - jest-watcher@29.7.0: + fsevents@2.3.3: + optional: true + + glob@10.4.1: dependencies: - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 18.19.86 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - emittery: 0.13.1 - jest-util: 29.7.0 - string-length: 4.0.2 + foreground-child: 3.1.1 + jackspeak: 3.4.0 + minimatch: 9.0.4 + minipass: 7.1.2 + path-scurry: 1.11.1 - jest-worker@29.7.0: + html-to-text@9.0.5: dependencies: - '@types/node': 18.19.86 - jest-util: 29.7.0 - merge-stream: 2.0.0 - supports-color: 8.1.1 + '@selderee/plugin-htmlparser2': 0.11.0 + deepmerge: 4.3.1 + dom-serializer: 2.0.0 + htmlparser2: 8.0.2 + selderee: 0.11.0 - jest@29.7.0(@types/node@18.19.86)(ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.3)): + htmlparser2@8.0.2: dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.3)) - '@jest/types': 29.6.3 - import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@18.19.86)(ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.3)) - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.1.0 + entities: 4.5.0 - joycon@3.1.1: {} + ignore@5.3.1: {} - js-tokens@4.0.0: {} + is-fullwidth-code-point@3.0.0: {} - js-yaml@3.14.1: - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 + isbinaryfile@5.0.4: {} - jsesc@2.5.2: {} + isexe@2.0.0: {} + + jackspeak@3.4.0: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 - json-parse-even-better-errors@2.3.1: {} + joycon@3.1.1: {} - json5@2.2.3: {} + js-tokens@4.0.0: {} - kleur@3.0.3: {} + js-tokens@9.0.1: {} leac@0.6.0: {} - leven@3.1.0: {} - lilconfig@3.1.2: {} lines-and-columns@1.2.4: {} load-tsconfig@0.2.5: {} - locate-path@5.0.0: - dependencies: - p-locate: 4.1.0 - - lodash.memoize@4.1.2: {} - lodash.sortby@4.7.0: {} loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 - lru-cache@10.2.2: {} - - lru-cache@5.1.1: - dependencies: - yallist: 3.1.1 - - make-dir@4.0.0: - dependencies: - semver: 7.7.1 - - make-error@1.3.6: {} - - makeerror@1.0.12: - dependencies: - tmpl: 1.0.5 - - merge-stream@2.0.0: {} - - merge2@1.4.1: {} - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.1 - - mimic-fn@2.1.0: {} + loupe@3.2.0: {} - minimatch@3.1.2: - dependencies: - brace-expansion: 1.1.11 + lru-cache@10.2.2: {} - minimatch@5.1.6: + magic-string@0.30.17: dependencies: - brace-expansion: 2.0.1 + '@jridgewell/sourcemap-codec': 1.5.4 minimatch@9.0.4: dependencies: @@ -3421,7 +1738,14 @@ snapshots: minipass@7.1.2: {} - ms@2.1.2: {} + mlly@1.7.4: + dependencies: + acorn: 8.15.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.1 + + ms@2.1.3: {} mz@2.7.0: dependencies: @@ -3429,21 +1753,7 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - natural-compare@1.4.0: {} - - node-fetch@2.7.0: - dependencies: - whatwg-url: 5.0.0 - - node-int64@0.4.0: {} - - node-releases@2.0.14: {} - - normalize-path@3.0.0: {} - - npm-run-path@4.0.1: - dependencies: - path-key: 3.1.1 + nanoid@3.3.11: {} object-assign@4.1.1: {} @@ -3451,90 +1761,79 @@ snapshots: dependencies: wrappy: 1.0.2 - onetime@5.1.2: - dependencies: - mimic-fn: 2.1.0 - - p-limit@2.3.0: - dependencies: - p-try: 2.2.0 - - p-limit@3.1.0: - dependencies: - yocto-queue: 0.1.0 - - p-locate@4.1.0: - dependencies: - p-limit: 2.3.0 - - p-try@2.2.0: {} - - parse-json@5.2.0: - dependencies: - '@babel/code-frame': 7.24.7 - error-ex: 1.3.2 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - parseley@0.12.1: dependencies: leac: 0.6.0 peberminta: 0.9.0 - path-exists@4.0.0: {} - - path-is-absolute@1.0.1: {} - path-key@3.1.1: {} - path-parse@1.0.7: {} - path-scurry@1.11.1: dependencies: lru-cache: 10.2.2 minipass: 7.1.2 - path-type@4.0.0: {} + pathe@2.0.3: {} + + pathval@2.0.1: {} peberminta@0.9.0: {} - picocolors@1.0.1: {} + picocolors@1.1.1: {} - picomatch@2.3.1: {} + picomatch@4.0.3: {} pirates@4.0.6: {} - pkg-dir@4.2.0: + pkg-pr-new@0.0.56: + dependencies: + '@jsdevtools/ez-spawn': 3.0.4 + '@octokit/action': 6.1.0 + ignore: 5.3.1 + isbinaryfile: 5.0.4 + pkg-types: 1.3.1 + query-registry: 3.0.1 + tinyglobby: 0.2.14 + + pkg-types@1.3.1: dependencies: - find-up: 4.1.0 + confbox: 0.1.8 + mlly: 1.7.4 + pathe: 2.0.3 - postcss-load-config@4.0.2(ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.3)): + postcss-load-config@6.0.1(postcss@8.5.6)(yaml@2.4.5): dependencies: lilconfig: 3.1.2 - yaml: 2.4.5 optionalDependencies: - ts-node: 10.9.2(@types/node@18.19.86)(typescript@5.8.3) - - prettier@3.5.3: {} + postcss: 8.5.6 + yaml: 2.4.5 - pretty-format@29.7.0: + postcss@8.5.6: dependencies: - '@jest/schemas': 29.6.3 - ansi-styles: 5.2.0 - react-is: 18.3.1 + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 - promise-polyfill@8.3.0: {} - - prompts@2.4.2: - dependencies: - kleur: 3.0.3 - sisteransi: 1.0.5 + prettier@3.5.3: {} punycode@2.3.1: {} - pure-rand@6.1.0: {} + query-registry@3.0.1: + dependencies: + query-string: 9.2.2 + quick-lru: 7.1.0 + url-join: 5.0.0 + validate-npm-package-name: 5.0.1 + zod: 3.25.76 + zod-package-json: 1.2.0 + + query-string@9.2.2: + dependencies: + decode-uri-component: 0.4.1 + filter-obj: 5.1.0 + split-on-first: 3.0.0 - queue-microtask@1.2.3: {} + quick-lru@7.1.0: {} react-dom@18.3.1(react@18.3.1): dependencies: @@ -3542,8 +1841,6 @@ snapshots: react: 18.3.1 scheduler: 0.23.2 - react-is@18.3.1: {} - react-promise-suspense@0.3.4: dependencies: fast-deep-equal: 2.0.1 @@ -3552,36 +1849,36 @@ snapshots: dependencies: loose-envify: 1.4.0 - readdirp@3.6.0: - dependencies: - picomatch: 2.3.1 - - require-directory@2.1.1: {} - - resolve-cwd@3.0.0: - dependencies: - resolve-from: 5.0.0 + readdirp@4.1.2: {} resolve-from@5.0.0: {} - resolve.exports@2.0.2: {} - - resolve@1.22.8: + rollup@4.46.2: dependencies: - is-core-module: 2.13.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - - reusify@1.0.4: {} - - rollup@3.29.5: + '@types/estree': 1.0.8 optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.46.2 + '@rollup/rollup-android-arm64': 4.46.2 + '@rollup/rollup-darwin-arm64': 4.46.2 + '@rollup/rollup-darwin-x64': 4.46.2 + '@rollup/rollup-freebsd-arm64': 4.46.2 + '@rollup/rollup-freebsd-x64': 4.46.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.46.2 + '@rollup/rollup-linux-arm-musleabihf': 4.46.2 + '@rollup/rollup-linux-arm64-gnu': 4.46.2 + '@rollup/rollup-linux-arm64-musl': 4.46.2 + '@rollup/rollup-linux-loongarch64-gnu': 4.46.2 + '@rollup/rollup-linux-ppc64-gnu': 4.46.2 + '@rollup/rollup-linux-riscv64-gnu': 4.46.2 + '@rollup/rollup-linux-riscv64-musl': 4.46.2 + '@rollup/rollup-linux-s390x-gnu': 4.46.2 + '@rollup/rollup-linux-x64-gnu': 4.46.2 + '@rollup/rollup-linux-x64-musl': 4.46.2 + '@rollup/rollup-win32-arm64-msvc': 4.46.2 + '@rollup/rollup-win32-ia32-msvc': 4.46.2 + '@rollup/rollup-win32-x64-msvc': 4.46.2 fsevents: 2.3.3 - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - scheduler@0.23.2: dependencies: loose-envify: 1.4.0 @@ -3590,47 +1887,29 @@ snapshots: dependencies: parseley: 0.12.1 - semver@6.3.1: {} - - semver@7.7.1: {} - - semver@7.7.2: {} - shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 shebang-regex@3.0.0: {} - signal-exit@3.0.7: {} + siginfo@2.0.0: {} signal-exit@4.1.0: {} - sisteransi@1.0.5: {} - - slash@3.0.0: {} - - source-map-support@0.5.13: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - - source-map@0.6.1: {} + source-map-js@1.2.1: {} source-map@0.8.0-beta.0: dependencies: whatwg-url: 7.1.0 - sprintf-js@1.0.3: {} + split-on-first@3.0.0: {} - stack-utils@2.0.6: - dependencies: - escape-string-regexp: 2.0.0 + stackback@0.0.2: {} - string-length@4.0.2: - dependencies: - char-regex: 1.0.2 - strip-ansi: 6.0.1 + std-env@3.9.0: {} + + string-argv@0.3.2: {} string-width@4.2.3: dependencies: @@ -3652,11 +1931,9 @@ snapshots: dependencies: ansi-regex: 6.0.1 - strip-bom@4.0.0: {} - - strip-final-newline@2.0.0: {} - - strip-json-comments@3.1.1: {} + strip-literal@3.0.0: + dependencies: + js-tokens: 9.0.1 sucrase@3.35.0: dependencies: @@ -3668,26 +1945,6 @@ snapshots: pirates: 4.0.6 ts-interface-checker: 0.1.13 - supports-color@5.5.0: - dependencies: - has-flag: 3.0.0 - - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 - - supports-color@8.1.1: - dependencies: - has-flag: 4.0.0 - - supports-preserve-symlinks-flag@1.0.0: {} - - test-exclude@6.0.0: - dependencies: - '@istanbuljs/schema': 0.1.3 - glob: 7.2.3 - minimatch: 3.1.2 - thenify-all@1.6.0: dependencies: thenify: 3.3.1 @@ -3696,15 +1953,20 @@ snapshots: dependencies: any-promise: 1.3.0 - tmpl@1.0.5: {} + tinybench@2.9.0: {} - to-fast-properties@2.0.0: {} + tinyexec@0.3.2: {} - to-regex-range@5.0.1: + tinyglobby@0.2.14: dependencies: - is-number: 7.0.0 + fdir: 6.4.6(picomatch@4.0.3) + picomatch: 4.0.3 + + tinypool@1.1.1: {} + + tinyrainbow@2.0.0: {} - tr46@0.0.3: {} + tinyspy@4.0.3: {} tr46@1.0.1: dependencies: @@ -3714,104 +1976,131 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-jest@29.3.4(@babel/core@7.24.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(esbuild@0.18.20)(jest@29.7.0(@types/node@18.19.86)(ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.3)))(typescript@5.8.3): - dependencies: - bs-logger: 0.2.6 - ejs: 3.1.10 - fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@18.19.86)(ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.3)) - jest-util: 29.7.0 - json5: 2.2.3 - lodash.memoize: 4.1.2 - make-error: 1.3.6 - semver: 7.7.2 - type-fest: 4.41.0 - typescript: 5.8.3 - yargs-parser: 21.1.1 - optionalDependencies: - '@babel/core': 7.24.7 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.24.7) - esbuild: 0.18.20 - - ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.3): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.11 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 18.19.86 - acorn: 8.11.3 - acorn-walk: 8.3.2 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.8.3 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - - tsup@7.2.0(ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.3))(typescript@5.8.3): - dependencies: - bundle-require: 4.2.1(esbuild@0.18.20) + tsup@8.5.0(postcss@8.5.6)(typescript@5.9.2)(yaml@2.4.5): + dependencies: + bundle-require: 5.1.0(esbuild@0.25.8) cac: 6.7.14 - chokidar: 3.6.0 - debug: 4.3.5 - esbuild: 0.18.20 - execa: 5.1.1 - globby: 11.1.0 + chokidar: 4.0.3 + consola: 3.4.2 + debug: 4.4.1 + esbuild: 0.25.8 + fix-dts-default-cjs-exports: 1.0.1 joycon: 3.1.1 - postcss-load-config: 4.0.2(ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.3)) + picocolors: 1.1.1 + postcss-load-config: 6.0.1(postcss@8.5.6)(yaml@2.4.5) resolve-from: 5.0.0 - rollup: 3.29.5 + rollup: 4.46.2 source-map: 0.8.0-beta.0 sucrase: 3.35.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.14 tree-kill: 1.2.2 optionalDependencies: - typescript: 5.8.3 + postcss: 8.5.6 + typescript: 5.9.2 transitivePeerDependencies: + - jiti - supports-color - - ts-node + - tsx + - yaml - type-detect@4.0.8: {} + type-detect@4.1.0: {} - type-fest@0.21.3: {} + typescript@5.9.2: {} - type-fest@4.41.0: {} + ufo@1.6.1: {} - typescript@5.8.3: {} + undici-types@6.21.0: {} - undici-types@5.26.5: {} + undici@6.21.3: {} - update-browserslist-db@1.0.16(browserslist@4.23.1): - dependencies: - browserslist: 4.23.1 - escalade: 3.1.2 - picocolors: 1.0.1 + universal-user-agent@6.0.1: {} - v8-compile-cache-lib@3.0.1: {} + url-join@5.0.0: {} - v8-to-istanbul@9.2.0: - dependencies: - '@jridgewell/trace-mapping': 0.3.25 - '@types/istanbul-lib-coverage': 2.0.6 - convert-source-map: 2.0.0 + validate-npm-package-name@5.0.1: {} - walker@1.0.8: + vite-node@3.2.4(@types/node@22.17.2)(yaml@2.4.5): dependencies: - makeerror: 1.0.12 + cac: 6.7.14 + debug: 4.4.1 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 7.1.1(@types/node@22.17.2)(yaml@2.4.5) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite@7.1.1(@types/node@22.17.2)(yaml@2.4.5): + dependencies: + esbuild: 0.25.8 + fdir: 6.4.6(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.46.2 + tinyglobby: 0.2.14 + optionalDependencies: + '@types/node': 22.17.2 + fsevents: 2.3.3 + yaml: 2.4.5 - webidl-conversions@3.0.1: {} + vitest-fetch-mock@0.4.5(vitest@3.2.4(@types/node@22.17.2)(yaml@2.4.5)): + dependencies: + vitest: 3.2.4(@types/node@22.17.2)(yaml@2.4.5) + + vitest@3.2.4(@types/node@22.17.2)(yaml@2.4.5): + dependencies: + '@types/chai': 5.2.2 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@7.1.1(@types/node@22.17.2)(yaml@2.4.5)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.2.1 + debug: 4.4.1 + expect-type: 1.2.2 + magic-string: 0.30.17 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.9.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.14 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 7.1.1(@types/node@22.17.2)(yaml@2.4.5) + vite-node: 3.2.4(@types/node@22.17.2)(yaml@2.4.5) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.17.2 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml webidl-conversions@4.0.2: {} - whatwg-url@5.0.0: - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - whatwg-url@7.1.0: dependencies: lodash.sortby: 4.7.0 @@ -3822,6 +2111,11 @@ snapshots: dependencies: isexe: 2.0.0 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -3836,29 +2130,11 @@ snapshots: wrappy@1.0.2: {} - write-file-atomic@4.0.2: - dependencies: - imurmurhash: 0.1.4 - signal-exit: 3.0.7 - - y18n@5.0.8: {} - - yallist@3.1.1: {} - - yaml@2.4.5: {} - - yargs-parser@21.1.1: {} + yaml@2.4.5: + optional: true - yargs@17.7.2: + zod-package-json@1.2.0: dependencies: - cliui: 8.0.1 - escalade: 3.1.2 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - - yn@3.1.1: {} + zod: 3.25.76 - yocto-queue@0.1.0: {} + zod@3.25.76: {} diff --git a/readme.md b/readme.md index 146e3d16..e65cda10 100644 --- a/readme.md +++ b/readme.md @@ -55,13 +55,15 @@ const resend = new Resend('re_xxxx...xxxxxx'); Send your first email: ```js -await resend.emails.send({ +const { data } = await resend.emails.send({ from: 'you@example.com', to: 'user@gmail.com', replyTo: 'you@example.com', subject: 'hello world', text: 'it works!', }); + +console.log(`Email ${data.id} has been sent`); ``` > [!NOTE] @@ -72,13 +74,15 @@ await resend.emails.send({ Send an email custom HTML content: ```js -await resend.emails.send({ +const { data } = await resend.emails.send({ from: 'you@example.com', to: 'user@gmail.com', replyTo: 'you@example.com', subject: 'hello world', html: 'it works!', }); + +console.log(`Emaill ${data.id} with customer HTML content has been sent.`); ``` ## Send email using React @@ -103,17 +107,19 @@ Then import the template component and pass it to the `react` property. ```jsx import EmailTemplate from '../components/EmailTemplate'; -await resend.emails.send({ +const { data } = await resend.emails.send({ from: 'you@example.com', to: 'user@gmail.com', replyTo: 'you@example.com', subject: 'hello world', react: , }); + +console.log(`Email ${data.id} with a React template has been sent`); ``` > [!NOTE] -> If your endpoint is a JS/TS file, render the template (i.e., pass `EmailTemplate({firstName="John", product="MyApp"})` instead of the component). +> If your endpoint is a JS/TS file, render the template (i.e., pass `EmailTemplate({ firstName: "John", product: "MyApp" })` instead of the component). ## License diff --git a/src/api-keys/api-keys.spec.ts b/src/api-keys/api-keys.spec.ts index ad9f30f5..7764f2bf 100644 --- a/src/api-keys/api-keys.spec.ts +++ b/src/api-keys/api-keys.spec.ts @@ -1,6 +1,9 @@ -import { enableFetchMocks } from 'jest-fetch-mock'; import type { ErrorResponse } from '../interfaces'; import { Resend } from '../resend'; +import { + mockErrorResponse, + mockSuccessResponse, +} from '../test-utils/mock-fetch'; import type { CreateApiKeyOptions, CreateApiKeyResponseSuccess, @@ -8,11 +11,7 @@ import type { import type { ListApiKeysResponseSuccess } from './interfaces/list-api-keys.interface'; import type { RemoveApiKeyResponseSuccess } from './interfaces/remove-api-keys.interface'; -enableFetchMocks(); - describe('API Keys', () => { - afterEach(() => fetchMock.resetMocks()); - describe('create', () => { it('creates an api key', async () => { const payload: CreateApiKeyOptions = { @@ -23,12 +22,8 @@ describe('API Keys', () => { id: '430eed87-632a-4ea6-90db-0aace67ec228', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 201, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -42,6 +37,11 @@ describe('API Keys', () => { "token": "re_PKr4RCko_Lhm9ost2YjNCctnPjbLw8Nqk", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -55,12 +55,8 @@ describe('API Keys', () => { name: 'validation_error', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 422, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockErrorResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -74,6 +70,11 @@ describe('API Keys', () => { "message": "String must contain at least 1 character(s)", "name": "validation_error", }, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -90,12 +91,8 @@ describe('API Keys', () => { id: '430eed87-632a-4ea6-90db-0aace67ec228', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 201, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -109,6 +106,11 @@ describe('API Keys', () => { "token": "re_PKr4RCko_Lhm9ost2YjNCctnPjbLw8Nqk", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -123,12 +125,8 @@ describe('API Keys', () => { id: '430eed87-632a-4ea6-90db-0aace67ec228', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 201, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -142,6 +140,11 @@ describe('API Keys', () => { "token": "re_PKr4RCko_Lhm9ost2YjNCctnPjbLw8Nqk", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -152,12 +155,8 @@ describe('API Keys', () => { message: 'Access must be "full_access" | "sending_access"', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 422, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockErrorResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -176,6 +175,11 @@ describe('API Keys', () => { "message": "Access must be "full_access" | "sending_access"", "name": "invalid_access", }, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -271,12 +275,8 @@ describe('API Keys', () => { created_at: '2023-04-06T23:09:49.093947+00:00', }, ]; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -296,6 +296,11 @@ describe('API Keys', () => { }, ], "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -306,12 +311,8 @@ describe('API Keys', () => { const response: RemoveApiKeyResponseSuccess = {}; it('removes an api key', async () => { - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -320,6 +321,11 @@ describe('API Keys', () => { { "data": {}, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -330,12 +336,8 @@ describe('API Keys', () => { message: 'Something went wrong', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 500, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockErrorResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -349,6 +351,11 @@ describe('API Keys', () => { "message": "Something went wrong", "name": "application_error", }, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -359,12 +366,8 @@ describe('API Keys', () => { message: 'API key not found', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 404, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockErrorResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -380,6 +383,11 @@ describe('API Keys', () => { "message": "API key not found", "name": "not_found", }, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); diff --git a/src/api-keys/interfaces/create-api-key-options.interface.ts b/src/api-keys/interfaces/create-api-key-options.interface.ts index 628600a8..59274575 100644 --- a/src/api-keys/interfaces/create-api-key-options.interface.ts +++ b/src/api-keys/interfaces/create-api-key-options.interface.ts @@ -1,5 +1,5 @@ import type { PostOptions } from '../../common/interfaces'; -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; export interface CreateApiKeyOptions { name: string; @@ -14,12 +14,4 @@ export interface CreateApiKeyResponseSuccess { id: string; } -export type CreateApiKeyResponse = - | { - data: CreateApiKeyResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type CreateApiKeyResponse = Response; diff --git a/src/api-keys/interfaces/list-api-keys.interface.ts b/src/api-keys/interfaces/list-api-keys.interface.ts index 59a14b79..a4696d5a 100644 --- a/src/api-keys/interfaces/list-api-keys.interface.ts +++ b/src/api-keys/interfaces/list-api-keys.interface.ts @@ -1,4 +1,4 @@ -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; import type { ApiKey } from './api-key'; export type ListApiKeysResponseSuccess = Pick< @@ -6,12 +6,4 @@ export type ListApiKeysResponseSuccess = Pick< 'name' | 'id' | 'created_at' >[]; -export type ListApiKeysResponse = - | { - data: ListApiKeysResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type ListApiKeysResponse = Response; diff --git a/src/api-keys/interfaces/remove-api-keys.interface.ts b/src/api-keys/interfaces/remove-api-keys.interface.ts index c299238b..3b6b77dc 100644 --- a/src/api-keys/interfaces/remove-api-keys.interface.ts +++ b/src/api-keys/interfaces/remove-api-keys.interface.ts @@ -1,14 +1,6 @@ -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; -// biome-ignore lint/complexity/noBannedTypes: +// biome-ignore lint/complexity/noBannedTypes: allow empty types export type RemoveApiKeyResponseSuccess = {}; -export type RemoveApiKeyResponse = - | { - data: RemoveApiKeyResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type RemoveApiKeyResponse = Response; diff --git a/src/audiences/audiences.spec.ts b/src/audiences/audiences.spec.ts index cf42f416..f548e0fe 100644 --- a/src/audiences/audiences.spec.ts +++ b/src/audiences/audiences.spec.ts @@ -1,6 +1,9 @@ -import { enableFetchMocks } from 'jest-fetch-mock'; import type { ErrorResponse } from '../interfaces'; import { Resend } from '../resend'; +import { + mockErrorResponse, + mockSuccessResponse, +} from '../test-utils/mock-fetch'; import type { CreateAudienceOptions, CreateAudienceResponseSuccess, @@ -9,8 +12,6 @@ import type { GetAudienceResponseSuccess } from './interfaces/get-audience.inter import type { ListAudiencesResponseSuccess } from './interfaces/list-audiences.interface'; import type { RemoveAudiencesResponseSuccess } from './interfaces/remove-audience.interface'; -enableFetchMocks(); - describe('Audiences', () => { afterEach(() => fetchMock.resetMocks()); @@ -23,12 +24,8 @@ describe('Audiences', () => { object: 'audience', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -42,6 +39,11 @@ describe('Audiences', () => { "object": "audience", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -53,12 +55,8 @@ describe('Audiences', () => { message: 'Missing "name" field', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 422, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockErrorResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -72,6 +70,11 @@ describe('Audiences', () => { "message": "Missing "name" field", "name": "missing_required_field", }, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -94,12 +97,8 @@ describe('Audiences', () => { }, ], }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -122,6 +121,11 @@ describe('Audiences', () => { "object": "list", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -135,12 +139,8 @@ describe('Audiences', () => { message: 'Audience not found', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 404, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockErrorResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -154,6 +154,11 @@ describe('Audiences', () => { "message": "Audience not found", "name": "not_found", }, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -167,12 +172,8 @@ describe('Audiences', () => { created_at: '2023-06-21T06:10:36.144Z', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -188,6 +189,11 @@ describe('Audiences', () => { "object": "audience", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -201,12 +207,8 @@ describe('Audiences', () => { id, deleted: true, }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -219,6 +221,11 @@ describe('Audiences', () => { "object": "audience", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); diff --git a/src/audiences/interfaces/create-audience-options.interface.ts b/src/audiences/interfaces/create-audience-options.interface.ts index f8775dba..4bd6ae88 100644 --- a/src/audiences/interfaces/create-audience-options.interface.ts +++ b/src/audiences/interfaces/create-audience-options.interface.ts @@ -1,5 +1,5 @@ import type { PostOptions } from '../../common/interfaces'; -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; import type { Audience } from './audience'; export interface CreateAudienceOptions { @@ -13,12 +13,4 @@ export interface CreateAudienceResponseSuccess object: 'audience'; } -export type CreateAudienceResponse = - | { - data: CreateAudienceResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type CreateAudienceResponse = Response; diff --git a/src/audiences/interfaces/get-audience.interface.ts b/src/audiences/interfaces/get-audience.interface.ts index 9c08efac..f229a6f9 100644 --- a/src/audiences/interfaces/get-audience.interface.ts +++ b/src/audiences/interfaces/get-audience.interface.ts @@ -1,4 +1,4 @@ -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; import type { Audience } from './audience'; export interface GetAudienceResponseSuccess @@ -6,12 +6,4 @@ export interface GetAudienceResponseSuccess object: 'audience'; } -export type GetAudienceResponse = - | { - data: GetAudienceResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type GetAudienceResponse = Response; diff --git a/src/audiences/interfaces/list-audiences.interface.ts b/src/audiences/interfaces/list-audiences.interface.ts index 86a92bc2..12e1c95f 100644 --- a/src/audiences/interfaces/list-audiences.interface.ts +++ b/src/audiences/interfaces/list-audiences.interface.ts @@ -1,4 +1,4 @@ -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; import type { Audience } from './audience'; export type ListAudiencesResponseSuccess = { @@ -6,12 +6,4 @@ export type ListAudiencesResponseSuccess = { data: Audience[]; }; -export type ListAudiencesResponse = - | { - data: ListAudiencesResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type ListAudiencesResponse = Response; diff --git a/src/audiences/interfaces/remove-audience.interface.ts b/src/audiences/interfaces/remove-audience.interface.ts index e82e0b39..97de36a4 100644 --- a/src/audiences/interfaces/remove-audience.interface.ts +++ b/src/audiences/interfaces/remove-audience.interface.ts @@ -1,4 +1,4 @@ -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; import type { Audience } from './audience'; export interface RemoveAudiencesResponseSuccess extends Pick { @@ -6,12 +6,4 @@ export interface RemoveAudiencesResponseSuccess extends Pick { deleted: boolean; } -export type RemoveAudiencesResponse = - | { - data: RemoveAudiencesResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type RemoveAudiencesResponse = Response; diff --git a/src/batch/batch.spec.ts b/src/batch/batch.spec.ts index 6fa7fecc..6fa90d89 100644 --- a/src/batch/batch.spec.ts +++ b/src/batch/batch.spec.ts @@ -1,11 +1,9 @@ -import { enableFetchMocks } from 'jest-fetch-mock'; import { Resend } from '../resend'; -import type { - CreateBatchOptions, - CreateBatchSuccessResponse, -} from './interfaces/create-batch-options.interface'; - -enableFetchMocks(); +import { + mockSuccessResponse, + mockSuccessWithStatusCode, +} from '../test-utils/mock-fetch'; +import type { CreateBatchOptions } from './interfaces/create-batch-options.interface'; const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -34,20 +32,20 @@ describe('Batch', () => { html: '

Hi there

', }, ]; - const response: CreateBatchSuccessResponse = { - data: [ - { id: 'aabeeefc-bd13-474a-a440-0ee139b3a4cc' }, - { id: 'aebe1c6e-30ad-4257-993b-519f5affa626' }, - { id: 'b2bc2598-f98b-4da4-86c9-7b32881ef394' }, - ], - }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', + mockSuccessResponse( + { + data: [ + { id: 'aabeeefc-bd13-474a-a440-0ee139b3a4cc' }, + { id: 'aebe1c6e-30ad-4257-993b-519f5affa626' }, + { id: 'b2bc2598-f98b-4da4-86c9-7b32881ef394' }, + ], }, - }); + { + headers: { + Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', + }, + }, + ); const data = await resend.batch.create(payload); expect(data).toMatchInlineSnapshot(` @@ -66,26 +64,30 @@ describe('Batch', () => { ], }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); it('does not send the Idempotency-Key header when idempotencyKey is not provided', async () => { - const response: CreateBatchSuccessResponse = { - data: [ - { - id: 'not-idempotent-123', + mockSuccessResponse( + { + data: [ + { + id: 'not-idempotent-123', + }, + ], + }, + { + headers: { + Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', }, - ], - }; - - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', }, - }); + ); const payload: CreateBatchOptions = [ { @@ -102,31 +104,28 @@ describe('Batch', () => { const lastCall = fetchMock.mock.calls[0]; expect(lastCall).toBeDefined(); - //@ts-ignore - const hasIdempotencyKey = lastCall[1]?.headers.has('Idempotency-Key'); - expect(hasIdempotencyKey).toBeFalsy(); + const request = lastCall[1]; + expect(request).toBeDefined(); - //@ts-ignore - const usedIdempotencyKey = lastCall[1]?.headers.get('Idempotency-Key'); - expect(usedIdempotencyKey).toBeNull(); + const headers = new Headers(request?.headers); + expect(headers.has('Idempotency-Key')).toBeFalsy(); }); it('sends the Idempotency-Key header when idempotencyKey is provided', async () => { - const response: CreateBatchSuccessResponse = { - data: [ - { - id: 'idempotent-123', + mockSuccessResponse( + { + data: [ + { + id: 'idempotent-123', + }, + ], + }, + { + headers: { + Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', }, - ], - }; - - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', }, - }); + ); const payload: CreateBatchOptions = [ { @@ -148,11 +147,11 @@ describe('Batch', () => { // In the mock, headers is an object with key-value pairs expect(fetchMock.mock.calls[0][1]?.headers).toBeDefined(); - //@ts-ignore + //@ts-expect-error const hasIdempotencyKey = lastCall[1]?.headers.has('Idempotency-Key'); expect(hasIdempotencyKey).toBeTruthy(); - //@ts-ignore + //@ts-expect-error const usedIdempotencyKey = lastCall[1]?.headers.get('Idempotency-Key'); expect(usedIdempotencyKey).toBe(idempotencyKey); }); @@ -180,21 +179,21 @@ describe('Batch', () => { html: '

Hi there

', }, ]; - const response: CreateBatchSuccessResponse = { - data: [ - { id: 'aabeeefc-bd13-474a-a440-0ee139b3a4cc' }, - { id: 'aebe1c6e-30ad-4257-993b-519f5affa626' }, - { id: 'b2bc2598-f98b-4da4-86c9-7b32881ef394' }, - ], - }; - - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', + + mockSuccessResponse( + { + data: [ + { id: 'aabeeefc-bd13-474a-a440-0ee139b3a4cc' }, + { id: 'aebe1c6e-30ad-4257-993b-519f5affa626' }, + { id: 'b2bc2598-f98b-4da4-86c9-7b32881ef394' }, + ], }, - }); + { + headers: { + Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', + }, + }, + ); const data = await resend.batch.send(payload); expect(data).toMatchInlineSnapshot(` @@ -213,26 +212,30 @@ describe('Batch', () => { ], }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); it('does not send the Idempotency-Key header when idempotencyKey is not provided', async () => { - const response: CreateBatchSuccessResponse = { - data: [ - { - id: 'not-idempotent-123', + mockSuccessResponse( + { + data: [ + { + id: 'not-idempotent-123', + }, + ], + }, + { + headers: { + Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', }, - ], - }; - - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', }, - }); + ); const payload: CreateBatchOptions = [ { @@ -248,32 +251,27 @@ describe('Batch', () => { // Inspect the last fetch call and body const lastCall = fetchMock.mock.calls[0]; expect(lastCall).toBeDefined(); - - //@ts-ignore - const hasIdempotencyKey = lastCall[1]?.headers.has('Idempotency-Key'); - expect(hasIdempotencyKey).toBeFalsy(); - - //@ts-ignore - const usedIdempotencyKey = lastCall[1]?.headers.get('Idempotency-Key'); - expect(usedIdempotencyKey).toBeNull(); + const request = lastCall[1]; + expect(request).toBeDefined(); + const headers = new Headers(request?.headers); + expect(headers.has('Idempotency-Key')).toBe(false); }); it('sends the Idempotency-Key header when idempotencyKey is provided', async () => { - const response: CreateBatchSuccessResponse = { - data: [ - { - id: 'idempotent-123', + mockSuccessResponse( + { + data: [ + { + id: 'idempotent-123', + }, + ], + }, + { + headers: { + Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', }, - ], - }; - - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', }, - }); + ); const payload: CreateBatchOptions = [ { @@ -290,18 +288,91 @@ describe('Batch', () => { // Inspect the last fetch call and body const lastCall = fetchMock.mock.calls[0]; expect(lastCall).toBeDefined(); + const headers = new Headers(lastCall[1]?.headers); + expect(headers.has('Idempotency-Key')).toBeTruthy(); + expect(headers.get('Idempotency-Key')).toBe(idempotencyKey); + }); + + it('handles batch response with errors field when permissive option is set', async () => { + mockSuccessWithStatusCode( + { + data: [], + errors: [{ index: 2, message: 'Invalid email address' }], + }, + 202, + { + headers: { + Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', + }, + }, + ); + + const payload: CreateBatchOptions = []; + + const result = await resend.batch.create(payload, { + batchValidation: 'permissive', + }); + + // Verify the header was passed correctly + const lastCall = fetchMock.mock.calls[0]; + expect(lastCall).toBeDefined(); + const request = lastCall[1]; + expect(request).toBeDefined(); + const headers = new Headers(request?.headers); + expect(headers.get('x-resend-batch-validation')).toBe('permissive'); + + expect(result.data).toEqual({ + data: [], + errors: [{ index: 2, message: 'Invalid email address' }], + }); + expect(result.error).toBeNull(); + }); - // Check if headers contains Idempotency-Key - // In the mock, headers is an object with key-value pairs - expect(fetchMock.mock.calls[0][1]?.headers).toBeDefined(); + it('removes errors field when permissive header is not set (backward compatibility)', async () => { + mockSuccessResponse( + { + data: [ + { id: 'success-email-1' }, + { id: 'success-email-2' }, + { id: 'success-email-3' }, + ], + }, + { + headers: { + Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', + }, + }, + ); - //@ts-ignore - const hasIdempotencyKey = lastCall[1]?.headers.has('Idempotency-Key'); - expect(hasIdempotencyKey).toBeTruthy(); + const payload: CreateBatchOptions = [ + { + from: 'admin@resend.com', + to: 'user1@example.com', + subject: 'Test 1', + html: '

Test 1

', + }, + { + from: 'admin@resend.com', + to: 'user2@example.com', + subject: 'Test 2', + html: '

Test 2

', + }, + { + from: 'admin@resend.com', + to: 'invalid-email', + subject: 'Test 3', + html: '

Test 3

', + }, + ]; - //@ts-ignore - const usedIdempotencyKey = lastCall[1]?.headers.get('Idempotency-Key'); - expect(usedIdempotencyKey).toBe(idempotencyKey); + const response = await resend.batch.create(payload); + // Should not have errors field for backward compatibility + expect(response.data).not.toHaveProperty('errors'); + expect(response.data?.data).toEqual([ + { id: 'success-email-1' }, + { id: 'success-email-2' }, + { id: 'success-email-3' }, + ]); }); }); }); diff --git a/src/batch/batch.ts b/src/batch/batch.ts index 18829170..908934b5 100644 --- a/src/batch/batch.ts +++ b/src/batch/batch.ts @@ -1,4 +1,3 @@ -import type * as React from 'react'; import type { EmailApiOptions } from '../common/interfaces/email-api-options.interface'; import { parseEmailToApiOptions } from '../common/utils/parse-email-to-api-options'; import type { Resend } from '../resend'; @@ -13,17 +12,17 @@ export class Batch { private renderAsync?: (component: React.ReactElement) => Promise; constructor(private readonly resend: Resend) {} - async send( + async send( payload: CreateBatchOptions, - options: CreateBatchRequestOptions = {}, - ): Promise { + options?: Options, + ): Promise> { return this.create(payload, options); } - async create( + async create( payload: CreateBatchOptions, - options: CreateBatchRequestOptions = {}, - ): Promise { + options?: Options, + ): Promise> { const emails: EmailApiOptions[] = []; for (const email of payload) { @@ -32,7 +31,7 @@ export class Batch { try { const { renderAsync } = await import('@react-email/render'); this.renderAsync = renderAsync; - } catch (error) { + } catch { throw new Error( 'Failed to render React component. Make sure to install `@react-email/render`', ); @@ -46,10 +45,16 @@ export class Batch { emails.push(parseEmailToApiOptions(email)); } - const data = await this.resend.post( + const data = await this.resend.post>( '/emails/batch', emails, - options, + { + ...options, + headers: { + 'x-resend-batch-validation': options?.batchValidation ?? 'strict', + ...options?.headers, + }, + }, ); return data; diff --git a/src/batch/interfaces/create-batch-options.interface.ts b/src/batch/interfaces/create-batch-options.interface.ts index d19dbe1c..9a9946db 100644 --- a/src/batch/interfaces/create-batch-options.interface.ts +++ b/src/batch/interfaces/create-batch-options.interface.ts @@ -1,27 +1,43 @@ import type { PostOptions } from '../../common/interfaces'; import type { IdempotentRequest } from '../../common/interfaces/idempotent-request.interface'; import type { CreateEmailOptions } from '../../emails/interfaces/create-email-options.interface'; -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; export type CreateBatchOptions = CreateEmailOptions[]; export interface CreateBatchRequestOptions extends PostOptions, - IdempotentRequest {} + IdempotentRequest { + /** + * @default 'strict' + */ + batchValidation?: 'strict' | 'permissive'; +} -export interface CreateBatchSuccessResponse { +export type CreateBatchSuccessResponse< + Options extends CreateBatchRequestOptions = CreateBatchRequestOptions, +> = { data: { /** The ID of the newly created email. */ id: string; }[]; -} - -export type CreateBatchResponse = - | { - data: CreateBatchSuccessResponse; - error: null; +} & (Options['batchValidation'] extends 'permissive' + ? { + /** + * Only present when header "x-resend-batch-validation" is set to 'permissive'. + */ + errors: { + /** + * The index of the failed email in the batch + */ + index: number; + /** + * The error message for the failed email + */ + message: string; + }[]; // This always being an array depends on us doing https://github.com/resend/resend-api/pull/2025/files#r2303897690 } - | { - data: null; - error: ErrorResponse; - }; + : Record); + +export type CreateBatchResponse = + Response>; diff --git a/src/broadcasts/broadcasts.spec.ts b/src/broadcasts/broadcasts.spec.ts index 5c6a2310..c7fb0d61 100644 --- a/src/broadcasts/broadcasts.spec.ts +++ b/src/broadcasts/broadcasts.spec.ts @@ -1,6 +1,10 @@ -import { enableFetchMocks } from 'jest-fetch-mock'; import type { ErrorResponse } from '../interfaces'; import { Resend } from '../resend'; +import { + mockErrorResponse, + mockFetchWithRateLimit, + mockSuccessResponse, +} from '../test-utils/mock-fetch'; import type { CreateBroadcastOptions, CreateBroadcastResponseSuccess, @@ -10,8 +14,6 @@ import type { ListBroadcastsResponseSuccess } from './interfaces/list-broadcasts import type { RemoveBroadcastResponseSuccess } from './interfaces/remove-broadcast.interface'; import type { UpdateBroadcastResponseSuccess } from './interfaces/update-broadcast.interface'; -enableFetchMocks(); - const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); describe('Broadcasts', () => { @@ -24,10 +26,8 @@ describe('Broadcasts', () => { message: 'Missing `from` field.', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 422, + mockErrorResponse(response, { headers: { - 'content-type': 'application/json', Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', }, }); @@ -40,6 +40,11 @@ describe('Broadcasts', () => { "message": "Missing \`from\` field.", "name": "missing_required_field", }, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -48,10 +53,8 @@ describe('Broadcasts', () => { const response: CreateBroadcastResponseSuccess = { id: '71cdfe68-cf79-473a-a9d7-21f91db6a526', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, + mockSuccessResponse(response, { headers: { - 'content-type': 'application/json', Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', }, }); @@ -70,6 +73,11 @@ describe('Broadcasts', () => { "id": "71cdfe68-cf79-473a-a9d7-21f91db6a526", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -79,12 +87,8 @@ describe('Broadcasts', () => { id: '124dc0f1-e36c-417c-a65c-e33773abc768', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const payload: CreateBroadcastOptions = { @@ -100,6 +104,11 @@ describe('Broadcasts', () => { "id": "124dc0f1-e36c-417c-a65c-e33773abc768", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -109,12 +118,8 @@ describe('Broadcasts', () => { id: '124dc0f1-e36c-417c-a65c-e33773abc768', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const payload: CreateBroadcastOptions = { @@ -132,6 +137,11 @@ describe('Broadcasts', () => { "id": "124dc0f1-e36c-417c-a65c-e33773abc768", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -143,12 +153,8 @@ describe('Broadcasts', () => { 'Invalid `from` field. The email address needs to follow the `email@example.com` or `Name ` format', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 422, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockErrorResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const payload: CreateBroadcastOptions = { @@ -162,14 +168,19 @@ describe('Broadcasts', () => { const result = resend.broadcasts.create(payload); await expect(result).resolves.toMatchInlineSnapshot(` - { - "data": null, - "error": { - "message": "Invalid \`from\` field. The email address needs to follow the \`email@example.com\` or \`Name \` format", - "name": "invalid_parameter", - }, - } - `); +{ + "data": null, + "error": { + "message": "Invalid \`from\` field. The email address needs to follow the \`email@example.com\` or \`Name \` format", + "name": "invalid_parameter", + }, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, +} +`); }); it('returns an error when fetch fails', async () => { @@ -199,7 +210,7 @@ describe('Broadcasts', () => { }); it('returns an error when api responds with text payload', async () => { - fetchMock.mockOnce('local_rate_limited', { + mockFetchWithRateLimit('local_rate_limited', { status: 422, headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823', @@ -233,10 +244,8 @@ describe('Broadcasts', () => { id: randomBroadcastId, }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, + mockSuccessResponse(response, { headers: { - 'content-type': 'application/json', Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', }, }); @@ -249,6 +258,11 @@ describe('Broadcasts', () => { "id": "b01e0de9-7c27-4a53-bf38-2e3f98389a65", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -279,12 +293,8 @@ describe('Broadcasts', () => { }, ], }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -315,6 +325,11 @@ describe('Broadcasts', () => { "object": "list", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -328,12 +343,8 @@ describe('Broadcasts', () => { message: 'Broadcast not found', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 404, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockErrorResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -349,6 +360,11 @@ describe('Broadcasts', () => { "message": "Broadcast not found", "name": "not_found", }, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -370,12 +386,8 @@ describe('Broadcasts', () => { sent_at: null, }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -399,6 +411,11 @@ describe('Broadcasts', () => { "subject": "hello world", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -412,12 +429,8 @@ describe('Broadcasts', () => { id, deleted: true, }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -432,6 +445,11 @@ describe('Broadcasts', () => { "object": "broadcast", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -441,12 +459,8 @@ describe('Broadcasts', () => { it('updates a broadcast', async () => { const id = 'b01e0de9-7c27-4a53-bf38-2e3f98389a65'; const response: UpdateBroadcastResponseSuccess = { id }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -459,6 +473,11 @@ describe('Broadcasts', () => { "id": "b01e0de9-7c27-4a53-bf38-2e3f98389a65", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); diff --git a/src/broadcasts/broadcasts.ts b/src/broadcasts/broadcasts.ts index 90323b06..6c308992 100644 --- a/src/broadcasts/broadcasts.ts +++ b/src/broadcasts/broadcasts.ts @@ -40,7 +40,7 @@ export class Broadcasts { try { const { renderAsync } = await import('@react-email/render'); this.renderAsync = renderAsync; - } catch (error) { + } catch { throw new Error( 'Failed to render React component. Make sure to install `@react-email/render`', ); @@ -106,6 +106,23 @@ export class Broadcasts { id: string, payload: UpdateBroadcastOptions, ): Promise { + if (payload.react) { + if (!this.renderAsync) { + try { + const { renderAsync } = await import('@react-email/render'); + this.renderAsync = renderAsync; + } catch { + throw new Error( + 'Failed to render React component. Make sure to install `@react-email/render`', + ); + } + } + + payload.html = await this.renderAsync( + payload.react as React.ReactElement, + ); + } + const data = await this.resend.patch( `/broadcasts/${id}`, { diff --git a/src/broadcasts/interfaces/create-broadcast-options.interface.ts b/src/broadcasts/interfaces/create-broadcast-options.interface.ts index 69d6940b..6b36357d 100644 --- a/src/broadcasts/interfaces/create-broadcast-options.interface.ts +++ b/src/broadcasts/interfaces/create-broadcast-options.interface.ts @@ -1,7 +1,7 @@ import type * as React from 'react'; import type { PostOptions } from '../../common/interfaces'; import type { RequireAtLeastOne } from '../../common/interfaces/require-at-least-one'; -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; interface EmailRenderOptions { /** @@ -73,12 +73,4 @@ export interface CreateBroadcastResponseSuccess { id: string; } -export type CreateBroadcastResponse = - | { - data: CreateBroadcastResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type CreateBroadcastResponse = Response; diff --git a/src/broadcasts/interfaces/get-broadcast.interface.ts b/src/broadcasts/interfaces/get-broadcast.interface.ts index 2b7b2ac0..e456f200 100644 --- a/src/broadcasts/interfaces/get-broadcast.interface.ts +++ b/src/broadcasts/interfaces/get-broadcast.interface.ts @@ -1,16 +1,8 @@ -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; import type { Broadcast } from './broadcast'; export interface GetBroadcastResponseSuccess extends Broadcast { object: 'broadcast'; } -export type GetBroadcastResponse = - | { - data: GetBroadcastResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type GetBroadcastResponse = Response; diff --git a/src/broadcasts/interfaces/list-broadcasts.interface.ts b/src/broadcasts/interfaces/list-broadcasts.interface.ts index fd91e66b..575a756b 100644 --- a/src/broadcasts/interfaces/list-broadcasts.interface.ts +++ b/src/broadcasts/interfaces/list-broadcasts.interface.ts @@ -1,4 +1,4 @@ -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; import type { Broadcast } from './broadcast'; export type ListBroadcastsResponseSuccess = { @@ -15,12 +15,4 @@ export type ListBroadcastsResponseSuccess = { >[]; }; -export type ListBroadcastsResponse = - | { - data: ListBroadcastsResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type ListBroadcastsResponse = Response; diff --git a/src/broadcasts/interfaces/remove-broadcast.interface.ts b/src/broadcasts/interfaces/remove-broadcast.interface.ts index 438b4884..1b45e8ce 100644 --- a/src/broadcasts/interfaces/remove-broadcast.interface.ts +++ b/src/broadcasts/interfaces/remove-broadcast.interface.ts @@ -1,4 +1,4 @@ -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; import type { Broadcast } from './broadcast'; export interface RemoveBroadcastResponseSuccess extends Pick { @@ -6,12 +6,4 @@ export interface RemoveBroadcastResponseSuccess extends Pick { deleted: boolean; } -export type RemoveBroadcastResponse = - | { - data: RemoveBroadcastResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type RemoveBroadcastResponse = Response; diff --git a/src/broadcasts/interfaces/send-broadcast-options.interface.ts b/src/broadcasts/interfaces/send-broadcast-options.interface.ts index 75f79393..1d92a6ae 100644 --- a/src/broadcasts/interfaces/send-broadcast-options.interface.ts +++ b/src/broadcasts/interfaces/send-broadcast-options.interface.ts @@ -1,5 +1,5 @@ import type { PostOptions } from '../../common/interfaces'; -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; interface SendBroadcastBaseOptions { /** @@ -21,12 +21,4 @@ export interface SendBroadcastResponseSuccess { id: string; } -export type SendBroadcastResponse = - | { - data: SendBroadcastResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type SendBroadcastResponse = Response; diff --git a/src/broadcasts/interfaces/update-broadcast.interface.ts b/src/broadcasts/interfaces/update-broadcast.interface.ts index e458dce2..644be559 100644 --- a/src/broadcasts/interfaces/update-broadcast.interface.ts +++ b/src/broadcasts/interfaces/update-broadcast.interface.ts @@ -1,26 +1,19 @@ -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; export interface UpdateBroadcastResponseSuccess { id: string; } -export interface UpdateBroadcastOptions { +export type UpdateBroadcastOptions = { name?: string; audienceId?: string; from?: string; html?: string; + react?: React.ReactNode; text?: string; subject?: string; replyTo?: string[]; previewText?: string; -} +}; -export type UpdateBroadcastResponse = - | { - data: UpdateBroadcastResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type UpdateBroadcastResponse = Response; diff --git a/src/common/interfaces/get-option.interface.ts b/src/common/interfaces/get-option.interface.ts index ded97ff8..78814b2f 100644 --- a/src/common/interfaces/get-option.interface.ts +++ b/src/common/interfaces/get-option.interface.ts @@ -1,3 +1,4 @@ export interface GetOptions { query?: Record; + headers?: HeadersInit; } diff --git a/src/common/interfaces/patch-option.interface.ts b/src/common/interfaces/patch-option.interface.ts index 0a40d192..2a21c3db 100644 --- a/src/common/interfaces/patch-option.interface.ts +++ b/src/common/interfaces/patch-option.interface.ts @@ -1,3 +1,4 @@ export interface PatchOptions { query?: { [key: string]: unknown }; + headers?: HeadersInit; } diff --git a/src/common/interfaces/post-option.interface.ts b/src/common/interfaces/post-option.interface.ts index 2508405b..e56e6762 100644 --- a/src/common/interfaces/post-option.interface.ts +++ b/src/common/interfaces/post-option.interface.ts @@ -1,3 +1,4 @@ export interface PostOptions { query?: { [key: string]: unknown }; + headers?: HeadersInit; } diff --git a/src/common/interfaces/put-option.interface.ts b/src/common/interfaces/put-option.interface.ts index 92b87a92..506d348e 100644 --- a/src/common/interfaces/put-option.interface.ts +++ b/src/common/interfaces/put-option.interface.ts @@ -1,3 +1,4 @@ export interface PutOptions { query?: { [key: string]: unknown }; + headers?: HeadersInit; } diff --git a/src/common/pagination.spec.ts b/src/common/pagination.spec.ts new file mode 100644 index 00000000..0fa3218f --- /dev/null +++ b/src/common/pagination.spec.ts @@ -0,0 +1,471 @@ +import { + type PageRequestOptions, + type PageResponse, + PaginatedRequest, +} from './pagination'; + +type TestItem = { + id: string; + name: string; + created_at: string; +}; + +describe('PaginatedRequest', () => { + afterEach(() => fetchMock.resetMocks()); + + const createMockFetchPage = ( + pages: Array<{ data: TestItem[]; has_more: boolean }>, + ) => { + let pageIndex = 0; + return vi.fn( + async (_options: PageRequestOptions): Promise> => { + const currentPage = pages[pageIndex] || { data: [], has_more: false }; + pageIndex++; + + return { + data: { + object: 'list', + data: currentPage.data, + has_more: currentPage.has_more, + }, + error: null, + rateLimiting: { + limit: 10, + remainingRequests: 9, + shouldResetAfter: 60, + }, + }; + }, + ); + }; + + const testItems: TestItem[] = [ + { id: '1', name: 'Item 1', created_at: '2023-01-01T00:00:00Z' }, + { id: '2', name: 'Item 2', created_at: '2023-01-02T00:00:00Z' }, + { id: '3', name: 'Item 3', created_at: '2023-01-03T00:00:00Z' }, + { id: '4', name: 'Item 4', created_at: '2023-01-04T00:00:00Z' }, + { id: '5', name: 'Item 5', created_at: '2023-01-05T00:00:00Z' }, + { id: '6', name: 'Item 6', created_at: '2023-01-06T00:00:00Z' }, + ]; + + describe('Promise interface', () => { + it('should work with async/await', async () => { + const mockFetchPage = createMockFetchPage([ + { data: [testItems[0]], has_more: false }, + ]); + + const request = new PaginatedRequest(mockFetchPage, { limit: 1 }); + const result = await request; + + expect(result.data.data).toEqual([testItems[0]]); + expect(result.error).toBeNull(); + }); + + it('should work with try/catch', async () => { + const mockFetchPage = vi + .fn() + .mockRejectedValue(new Error('Network error')); + const request = new PaginatedRequest(mockFetchPage, { limit: 1 }); + + try { + await request; + expect.fail('Should have thrown an error'); + } catch (error) { + expect(error).toBeInstanceOf(Error); + expect((error as Error).message).toBe('Network error'); + } + }); + + it('should work with try/catch/finally', async () => { + const mockFetchPage = createMockFetchPage([ + { data: [testItems[0]], has_more: false }, + ]); + const finallyCallback = vi.fn(); + + const request = new PaginatedRequest(mockFetchPage, { limit: 1 }); + + try { + const result = await request; + expect(result.data.data).toEqual([testItems[0]]); + } finally { + finallyCallback(); + } + + expect(finallyCallback).toHaveBeenCalled(); + }); + + it('should have correct Symbol.toStringTag', () => { + const mockFetchPage = createMockFetchPage([]); + const request = new PaginatedRequest(mockFetchPage, { limit: 1 }); + + expect(request[Symbol.toStringTag]).toBe('PaginatedRequest'); + }); + }); + + describe('AsyncIterable interface', () => { + it('should implement AsyncIterable interface', async () => { + const mockFetchPage = createMockFetchPage([ + { data: [testItems[0], testItems[1]], has_more: true }, + { data: [testItems[2]], has_more: false }, + ]); + + const request = new PaginatedRequest(mockFetchPage, { limit: 2 }); + const pages: PageResponse[] = []; + + for await (const page of request) { + pages.push(page); + } + + expect(pages).toHaveLength(2); + expect(pages[0].data.data).toEqual([testItems[0], testItems[1]]); + expect(pages[1].data.data).toEqual([testItems[2]]); + }); + + it('should stop iteration when has_more is false', async () => { + const mockFetchPage = createMockFetchPage([ + { data: [testItems[0]], has_more: false }, + ]); + + const request = new PaginatedRequest(mockFetchPage, { limit: 1 }); + const pages: PageResponse[] = []; + + for await (const page of request) { + pages.push(page); + } + + expect(pages).toHaveLength(1); + expect(mockFetchPage).toHaveBeenCalledTimes(1); + }); + + it('should stop iteration when page data is empty', async () => { + const mockFetchPage = createMockFetchPage([ + { data: [testItems[0]], has_more: true }, + { data: [], has_more: true }, + ]); + + const request = new PaginatedRequest(mockFetchPage, { limit: 1 }); + const pages: PageResponse[] = []; + + for await (const page of request) { + pages.push(page); + } + + expect(pages).toHaveLength(2); + expect(pages[1].data.data).toEqual([]); + }); + }); + + describe('Pagination options', () => { + it('should use after cursor for forward pagination', async () => { + const mockFetchPage = createMockFetchPage([ + { data: [testItems[0], testItems[1]], has_more: true }, + { data: [testItems[2]], has_more: false }, + ]); + + const request = new PaginatedRequest(mockFetchPage, { + limit: 2, + after: 'initial-cursor', + }); + + const pages: PageResponse[] = []; + for await (const page of request) { + pages.push(page); + } + + expect(mockFetchPage).toHaveBeenCalledTimes(2); + expect(mockFetchPage).toHaveBeenNthCalledWith(1, { + limit: 2, + after: 'initial-cursor', + }); + expect(mockFetchPage).toHaveBeenNthCalledWith(2, { + limit: 2, + after: testItems[1].id, + }); + }); + + it('should use before cursor for backward pagination', async () => { + const mockFetchPage = createMockFetchPage([ + { data: [testItems[2], testItems[1]], has_more: true }, + { data: [testItems[0]], has_more: false }, + ]); + + const request = new PaginatedRequest(mockFetchPage, { + limit: 2, + before: 'initial-cursor', + }); + + const pages: PageResponse[] = []; + for await (const page of request) { + pages.push(page); + } + + expect(mockFetchPage).toHaveBeenCalledTimes(2); + expect(mockFetchPage).toHaveBeenNthCalledWith(1, { + limit: 2, + before: 'initial-cursor', + }); + expect(mockFetchPage).toHaveBeenNthCalledWith(2, { + limit: 2, + before: testItems[2].id, + }); + }); + + it('should default to forward pagination when no cursor specified', async () => { + const mockFetchPage = createMockFetchPage([ + { data: [testItems[0], testItems[1]], has_more: true }, + { data: [testItems[2]], has_more: false }, + ]); + + const request = new PaginatedRequest(mockFetchPage, { limit: 2 }); + const pages: PageResponse[] = []; + + for await (const page of request) { + pages.push(page); + } + + expect(mockFetchPage).toHaveBeenNthCalledWith(2, { + limit: 2, + after: testItems[1].id, + }); + }); + }); + + describe('Rate limiting handling', () => { + it('should handle rate limit on initial page', async () => { + const mockFetchPage = vi + .fn() + .mockResolvedValueOnce({ + data: null, + error: { + name: 'rate_limit_exceeded', + message: 'Rate limit exceeded', + retryAfter: 0.001, // Very short delay for testing + }, + rateLimiting: { + limit: 10, + remainingRequests: 0, + shouldResetAfter: 60, + }, + }) + .mockResolvedValueOnce({ + data: { + object: 'list', + data: [testItems[0]], + has_more: false, + }, + error: null, + rateLimiting: { + limit: 10, + remainingRequests: 9, + shouldResetAfter: 60, + }, + }); + + const request = new PaginatedRequest(mockFetchPage, { limit: 1 }); + const pages: PageResponse[] = []; + + for await (const page of request) { + pages.push(page); + } + + expect(pages).toHaveLength(1); + expect(pages[0].data?.data).toEqual([testItems[0]]); + expect(mockFetchPage).toHaveBeenCalledTimes(2); + }); + + it('should handle rate limit on subsequent pages', async () => { + const mockFetchPage = vi + .fn() + .mockResolvedValueOnce({ + data: { + object: 'list', + data: [testItems[0]], + has_more: true, + }, + error: null, + rateLimiting: { + limit: 10, + remainingRequests: 9, + shouldResetAfter: 60, + }, + }) + .mockResolvedValueOnce({ + data: null, + error: { + name: 'rate_limit_exceeded', + message: 'Rate limit exceeded', + retryAfter: 0.001, // Very short delay for testing + }, + rateLimiting: { + limit: 10, + remainingRequests: 0, + shouldResetAfter: 60, + }, + }) + .mockResolvedValueOnce({ + data: { + object: 'list', + data: [testItems[1]], + has_more: false, + }, + error: null, + rateLimiting: { + limit: 10, + remainingRequests: 9, + shouldResetAfter: 60, + }, + }); + + const request = new PaginatedRequest(mockFetchPage, { limit: 1 }); + const pages: PageResponse[] = []; + + for await (const page of request) { + pages.push(page); + } + + expect(pages).toHaveLength(2); + expect(pages[0].data?.data).toEqual([testItems[0]]); + expect(pages[1].data?.data).toEqual([testItems[1]]); + expect(mockFetchPage).toHaveBeenCalledTimes(3); + }); + + it('should not wait on first call to getPageRetryingRateLimit when firstWait is 0', async () => { + const mockFetchPage = vi.fn().mockResolvedValue({ + data: { object: 'list', data: [testItems[0]], has_more: false }, + error: null, + rateLimiting: { limit: 10, remainingRequests: 9, shouldResetAfter: 60 }, + }); + + const request = new PaginatedRequest(mockFetchPage, { limit: 1 }); + + const pages: PageResponse[] = []; + for await (const page of request) { + pages.push(page); + break; // Only get first page to test this scenario + } + + expect(pages).toHaveLength(1); + expect(mockFetchPage).toHaveBeenCalledTimes(1); + }); + }); + + describe('Error handling', () => { + it('should handle non-rate-limit errors in fetchPage', async () => { + const mockFetchPage = vi.fn().mockResolvedValue({ + data: null, + error: { + name: 'invalid_parameter', + message: 'Invalid parameter provided', + }, + rateLimiting: { limit: 10, remainingRequests: 9, shouldResetAfter: 60 }, + }); + + const request = new PaginatedRequest(mockFetchPage, { limit: 1 }); + const pages: PageResponse[] = []; + + for await (const page of request) { + pages.push(page); + } + + expect(pages).toHaveLength(1); + expect(pages[0].error?.name).toBe('invalid_parameter'); + expect(pages[0].data).toBeNull(); + }); + + it('should handle Promise rejection in fetchPage', async () => { + const mockFetchPage = vi + .fn() + .mockRejectedValue(new Error('Network error')); + const request = new PaginatedRequest(mockFetchPage, { limit: 1 }); + + await expect(async () => { + for await (const _page of request) { + // This should throw + } + }).rejects.toThrow('Network error'); + }); + }); + + describe('Edge cases', () => { + it('should handle empty first page', async () => { + const mockFetchPage = createMockFetchPage([ + { data: [], has_more: false }, + ]); + + const request = new PaginatedRequest(mockFetchPage, { limit: 10 }); + const pages: PageResponse[] = []; + + for await (const page of request) { + pages.push(page); + } + + expect(pages).toHaveLength(1); + expect(pages[0].data?.data).toEqual([]); + expect(pages[0].data?.has_more).toBe(false); + }); + + it('should handle single item across multiple pages', async () => { + const mockFetchPage = createMockFetchPage([ + { data: [testItems[0]], has_more: true }, + { data: [testItems[1]], has_more: true }, + { data: [testItems[2]], has_more: false }, + ]); + + const request = new PaginatedRequest(mockFetchPage, { limit: 1 }); + const pages: PageResponse[] = []; + + for await (const page of request) { + pages.push(page); + } + + expect(pages).toHaveLength(3); + expect(pages[0].data?.data).toEqual([testItems[0]]); + expect(pages[1].data?.data).toEqual([testItems[1]]); + expect(pages[2].data?.data).toEqual([testItems[2]]); + }); + + it('should handle mixed Promise and AsyncIterable usage', async () => { + const mockFetchPage = createMockFetchPage([ + { data: [testItems[0]], has_more: true }, + { data: [testItems[1]], has_more: false }, + ]); + + const request = new PaginatedRequest(mockFetchPage, { limit: 1 }); + + // First use as Promise + const firstPage = await request; + expect(firstPage.data.data).toEqual([testItems[0]]); + + // Then use as AsyncIterable (should start fresh iteration) + const pages: PageResponse[] = []; + for await (const page of request) { + pages.push(page); + } + + expect(pages).toHaveLength(2); + expect(pages[0].data?.data).toEqual([testItems[0]]); + expect(pages[1].data?.data).toEqual([testItems[1]]); + }); + }); + + describe('Type constraints', () => { + it('should work with objects that have id property', async () => { + type CustomItem = { id: string; value: number }; + const customItems: CustomItem[] = [ + { id: 'a1', value: 10 }, + { id: 'b2', value: 20 }, + ]; + + const mockFetchPage = vi.fn().mockResolvedValue({ + data: { object: 'list', data: customItems, has_more: false }, + error: null, + rateLimiting: { limit: 10, remainingRequests: 9, shouldResetAfter: 60 }, + }); + + const request = new PaginatedRequest(mockFetchPage, { limit: 10 }); + const result = await request; + + expect(result.data.data).toEqual(customItems); + }); + }); +}); diff --git a/src/common/pagination.ts b/src/common/pagination.ts new file mode 100644 index 00000000..2b2426ba --- /dev/null +++ b/src/common/pagination.ts @@ -0,0 +1,106 @@ +import type { Response } from '../interfaces'; + +export type PageResponse = Response<{ + object: 'list'; + data: Array; + has_more: boolean; +}>; + +export type PageRequestOptions = { + limit?: number; +} & ({ after?: string; before?: never } | { before?: string; after?: never }); + +export class PaginatedRequest + implements Promise>, AsyncIterable> +{ + private readonly initialPagePromise: Promise>; + + constructor( + private readonly fetchPage: ( + options: PageRequestOptions, + ) => Promise>, + private readonly options: PageRequestOptions, + ) { + this.initialPagePromise = fetchPage(options); + } + + // biome-ignore lint/suspicious/noThenProperty: this class implements Promise + then, TResult2 = never>( + onfulfilled?: + | ((value: PageResponse) => TResult1 | PromiseLike) + | null + | undefined, + onrejected?: + | ((reason: unknown) => TResult2 | PromiseLike) + | null + | undefined, + ): Promise { + return this.initialPagePromise.then(onfulfilled, onrejected); + } + + catch( + onrejected?: + | ((reason: unknown) => TResult | PromiseLike) + | null + | undefined, + ): Promise | TResult> { + return this.initialPagePromise.catch(onrejected); + } + + finally( + onfinally?: (() => void) | null | undefined, + ): Promise> { + return this.initialPagePromise.finally(onfinally); + } + + [Symbol.toStringTag] = 'PaginatedRequest'; + + private async getPageRetryingRateLimit( + firstWait: number, + options: PageRequestOptions, + ): Promise> { + if (firstWait > 0) { + await sleep(firstWait); + } + + let page = await this.fetchPage(options); + while (page.error?.name === 'rate_limit_exceeded') { + const retryAfter = page.error.retryAfter; + await sleep(retryAfter); + page = await this.fetchPage(options); + } + + return page; + } + + private async *iterator(): AsyncIterator> { + let page = await this.initialPagePromise; + if (page.error?.name === 'rate_limit_exceeded') { + page = await this.getPageRetryingRateLimit( + page.error.retryAfter, + this.options, + ); + } + yield page; + + const options = { ...this.options }; + while (page.data?.has_more && page.data.data.length > 0) { + if (options.before) { + options.before = page.data.data[0].id; + } else { + options.after = page.data.data[page.data.data.length - 1].id; + } + + page = await this.getPageRetryingRateLimit(0, options); + yield page; + } + } + + [Symbol.asyncIterator](): AsyncIterator> { + return this.iterator(); + } +} + +async function sleep(seconds: number): Promise { + return new Promise((resolve) => setTimeout(resolve, seconds * 1000)); +} diff --git a/src/contacts/contacts.spec.ts b/src/contacts/contacts.spec.ts index 20d182a5..87355fdb 100644 --- a/src/contacts/contacts.spec.ts +++ b/src/contacts/contacts.spec.ts @@ -1,6 +1,9 @@ -import { enableFetchMocks } from 'jest-fetch-mock'; import type { ErrorResponse } from '../interfaces'; import { Resend } from '../resend'; +import { + mockErrorResponse, + mockSuccessResponse, +} from '../test-utils/mock-fetch'; import type { CreateContactOptions, CreateContactResponseSuccess, @@ -19,8 +22,6 @@ import type { } from './interfaces/remove-contact.interface'; import type { UpdateContactOptions } from './interfaces/update-contact.interface'; -enableFetchMocks(); - describe('Contacts', () => { afterEach(() => fetchMock.resetMocks()); @@ -35,12 +36,8 @@ describe('Contacts', () => { id: '3deaccfb-f47f-440a-8875-ea14b1716b43', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -53,6 +50,11 @@ describe('Contacts', () => { "object": "contact", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -67,12 +69,8 @@ describe('Contacts', () => { message: 'Missing `email` field.', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 422, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockErrorResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -86,6 +84,11 @@ describe('Contacts', () => { "message": "Missing \`email\` field.", "name": "missing_required_field", }, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -117,12 +120,8 @@ describe('Contacts', () => { }, ], }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -153,6 +152,11 @@ describe('Contacts', () => { "object": "list", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -166,12 +170,8 @@ describe('Contacts', () => { message: 'Contact not found', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 404, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockErrorResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -189,6 +189,11 @@ describe('Contacts', () => { "message": "Contact not found", "name": "not_found", }, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -205,12 +210,8 @@ describe('Contacts', () => { unsubscribed: false, }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -232,6 +233,11 @@ describe('Contacts', () => { "unsubscribed": false, }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -247,12 +253,8 @@ describe('Contacts', () => { unsubscribed: false, }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -274,6 +276,11 @@ describe('Contacts', () => { "unsubscribed": false, }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -290,12 +297,8 @@ describe('Contacts', () => { id: '3d4a472d-bc6d-4dd2-aa9d-d3d50ce87223', object: 'contact', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -309,6 +312,11 @@ describe('Contacts', () => { "object": "contact", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -321,12 +329,8 @@ describe('Contacts', () => { object: 'contact', deleted: true, }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -344,6 +348,11 @@ describe('Contacts', () => { "object": "contact", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -354,12 +363,8 @@ describe('Contacts', () => { object: 'contact', deleted: true, }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -370,15 +375,20 @@ describe('Contacts', () => { await expect( resend.contacts.remove(options), ).resolves.toMatchInlineSnapshot(` - { - "data": { - "contact": "acme@example.com", - "deleted": true, - "object": "contact", - }, - "error": null, - } - `); +{ + "data": { + "contact": "acme@example.com", + "deleted": true, + "object": "contact", + }, + "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, +} +`); }); }); }); diff --git a/src/contacts/contacts.ts b/src/contacts/contacts.ts index 5fefa506..b3c21158 100644 --- a/src/contacts/contacts.ts +++ b/src/contacts/contacts.ts @@ -57,6 +57,7 @@ export class Contacts { if (!options.id && !options.email) { return { data: null, + rateLimiting: null, error: { message: 'Missing `id` or `email` field.', name: 'missing_required_field', @@ -70,10 +71,11 @@ export class Contacts { return data; } - async update(payload: UpdateContactOptions): Promise { - if (!payload.id && !payload.email) { + async update(options: UpdateContactOptions): Promise { + if (!options.id && !options.email) { return { data: null, + rateLimiting: null, error: { message: 'Missing `id` or `email` field.', name: 'missing_required_field', @@ -82,11 +84,11 @@ export class Contacts { } const data = await this.resend.patch( - `/audiences/${payload.audienceId}/contacts/${payload?.email ? payload?.email : payload?.id}`, + `/audiences/${options.audienceId}/contacts/${options?.email ? options?.email : options?.id}`, { - unsubscribed: payload.unsubscribed, - first_name: payload.firstName, - last_name: payload.lastName, + unsubscribed: options.unsubscribed, + first_name: options.firstName, + last_name: options.lastName, }, ); return data; @@ -96,6 +98,7 @@ export class Contacts { if (!payload.id && !payload.email) { return { data: null, + rateLimiting: null, error: { message: 'Missing `id` or `email` field.', name: 'missing_required_field', diff --git a/src/contacts/interfaces/contact.ts b/src/contacts/interfaces/contact.ts index 3bcdc521..56ecd1b7 100644 --- a/src/contacts/interfaces/contact.ts +++ b/src/contacts/interfaces/contact.ts @@ -6,3 +6,33 @@ export interface Contact { last_name?: string; unsubscribed: boolean; } + +export type SelectingField = + | { + /** + * The contact id. + * + * @link https://resend.com/docs/api-reference/contacts/delete-contact#body-parameters + */ + id: string; + /** + * The contact email. + * + * @link https://resend.com/docs/api-reference/contacts/delete-contact#body-parameters + */ + email?: undefined | null; + } + | { + /** + * The contact id. + * + * @link https://resend.com/docs/api-reference/contacts/delete-contact#body-parameters + */ + id?: undefined | null; + /** + * The contact email. + * + * @link https://resend.com/docs/api-reference/contacts/delete-contact#body-parameters + */ + email: string; + }; diff --git a/src/contacts/interfaces/create-contact-options.interface.ts b/src/contacts/interfaces/create-contact-options.interface.ts index ff73f25b..49304acd 100644 --- a/src/contacts/interfaces/create-contact-options.interface.ts +++ b/src/contacts/interfaces/create-contact-options.interface.ts @@ -1,5 +1,5 @@ import type { PostOptions } from '../../common/interfaces'; -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; import type { Contact } from './contact'; export interface CreateContactOptions { @@ -16,12 +16,4 @@ export interface CreateContactResponseSuccess extends Pick { object: 'contact'; } -export type CreateContactResponse = - | { - data: CreateContactResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type CreateContactResponse = Response; diff --git a/src/contacts/interfaces/get-contact.interface.ts b/src/contacts/interfaces/get-contact.interface.ts index 95e4cf6a..2ed90a5e 100644 --- a/src/contacts/interfaces/get-contact.interface.ts +++ b/src/contacts/interfaces/get-contact.interface.ts @@ -1,11 +1,9 @@ -import type { ErrorResponse } from '../../interfaces'; -import type { Contact } from './contact'; +import type { Response } from '../../interfaces'; +import type { Contact, SelectingField } from './contact'; -export interface GetContactOptions { +export type GetContactOptions = { audienceId: string; - id?: string; - email?: string; -} +} & SelectingField; export interface GetContactResponseSuccess extends Pick< @@ -15,12 +13,4 @@ export interface GetContactResponseSuccess object: 'contact'; } -export type GetContactResponse = - | { - data: GetContactResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type GetContactResponse = Response; diff --git a/src/contacts/interfaces/list-contacts.interface.ts b/src/contacts/interfaces/list-contacts.interface.ts index a3970786..8b71b435 100644 --- a/src/contacts/interfaces/list-contacts.interface.ts +++ b/src/contacts/interfaces/list-contacts.interface.ts @@ -1,4 +1,4 @@ -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; import type { Contact } from './contact'; export interface ListContactsOptions { @@ -10,12 +10,4 @@ export interface ListContactsResponseSuccess { data: Contact[]; } -export type ListContactsResponse = - | { - data: ListContactsResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type ListContactsResponse = Response; diff --git a/src/contacts/interfaces/remove-contact.interface.ts b/src/contacts/interfaces/remove-contact.interface.ts index aedf40ba..dd7d117f 100644 --- a/src/contacts/interfaces/remove-contact.interface.ts +++ b/src/contacts/interfaces/remove-contact.interface.ts @@ -1,4 +1,5 @@ -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; +import type { SelectingField } from './contact'; export type RemoveContactsResponseSuccess = { object: 'contact'; @@ -6,31 +7,8 @@ export type RemoveContactsResponseSuccess = { contact: string; }; -interface RemoveByOptions { - /** - * The contact id. - * - * @link https://resend.com/docs/api-reference/contacts/delete-contact#body-parameters - */ - id?: string; - /** - * The contact email. - * - * @link https://resend.com/docs/api-reference/contacts/delete-contact#body-parameters - */ - email?: string; -} - -export interface RemoveContactOptions extends RemoveByOptions { +export type RemoveContactOptions = SelectingField & { audienceId: string; -} +}; -export type RemoveContactsResponse = - | { - data: RemoveContactsResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type RemoveContactsResponse = Response; diff --git a/src/contacts/interfaces/update-contact.interface.ts b/src/contacts/interfaces/update-contact.interface.ts index 8a5d98e6..51a903c4 100644 --- a/src/contacts/interfaces/update-contact.interface.ts +++ b/src/contacts/interfaces/update-contact.interface.ts @@ -1,28 +1,15 @@ -import type { ErrorResponse } from '../../interfaces'; -import type { Contact } from './contact'; +import type { Response } from '../../interfaces'; +import type { Contact, SelectingField } from './contact'; -interface UpdateContactBaseOptions { - id?: string; - email?: string; -} - -export interface UpdateContactOptions extends UpdateContactBaseOptions { +export type UpdateContactOptions = { audienceId: string; unsubscribed?: boolean; firstName?: string; lastName?: string; -} +} & SelectingField; export type UpdateContactResponseSuccess = Pick & { object: 'contact'; }; -export type UpdateContactResponse = - | { - data: UpdateContactResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type UpdateContactResponse = Response; diff --git a/src/domains/domains.spec.ts b/src/domains/domains.spec.ts index 369f9c68..083378bb 100644 --- a/src/domains/domains.spec.ts +++ b/src/domains/domains.spec.ts @@ -1,6 +1,9 @@ -import { enableFetchMocks } from 'jest-fetch-mock'; import type { ErrorResponse } from '../interfaces'; import { Resend } from '../resend'; +import { + mockErrorResponse, + mockSuccessResponse, +} from '../test-utils/mock-fetch'; import type { CreateDomainOptions, CreateDomainResponseSuccess, @@ -12,8 +15,6 @@ import type { RemoveDomainsResponseSuccess } from './interfaces/remove-domain.in import type { UpdateDomainsResponseSuccess } from './interfaces/update-domain.interface'; import type { VerifyDomainsResponseSuccess } from './interfaces/verify-domain.interface'; -enableFetchMocks(); - describe('Domains', () => { afterEach(() => fetchMock.resetMocks()); @@ -69,12 +70,8 @@ describe('Domains', () => { ], region: 'us-east-1', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const payload: CreateDomainOptions = { name: 'resend.com' }; @@ -134,6 +131,11 @@ describe('Domains', () => { "status": "not_started", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -144,12 +146,8 @@ describe('Domains', () => { message: 'Missing "name" field', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 422, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockErrorResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const payload: CreateDomainOptions = { @@ -167,6 +165,11 @@ describe('Domains', () => { "message": "Missing "name" field", "name": "missing_required_field", }, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -234,57 +237,12 @@ describe('Domains', () => { resend.domains.create(payload), ).resolves.toMatchInlineSnapshot(` { - "data": { - "created_at": "2023-04-07T22:48:33.420498+00:00", - "id": "3d4a472d-bc6d-4dd2-aa9d-d3d50ce87222", - "name": "resend.com", - "records": [ - { - "name": "bounces", - "priority": 10, - "record": "SPF", - "status": "not_started", - "ttl": "Auto", - "type": "MX", - "value": "feedback-smtp.eu-west-1.com", - }, - { - "name": "bounces", - "record": "SPF", - "status": "not_started", - "ttl": "Auto", - "type": "TXT", - "value": ""v=spf1 include:com ~all"", - }, - { - "name": "nu22pfdfqaxdybogtw3ebaokmalv5mxg._domainkey", - "record": "DKIM", - "status": "not_started", - "ttl": "Auto", - "type": "CNAME", - "value": "nu22pfdfqaxdybogtw3ebaokmalv5mxg.dkim.com.", - }, - { - "name": "qklz5ozk742hhql3vmekdu3pr4f5ggsj._domainkey", - "record": "DKIM", - "status": "not_started", - "ttl": "Auto", - "type": "CNAME", - "value": "qklz5ozk742hhql3vmekdu3pr4f5ggsj.dkim.com.", - }, - { - "name": "eeaemodxoao5hxwjvhywx4bo5mswjw6v._domainkey", - "record": "DKIM", - "status": "not_started", - "ttl": "Auto", - "type": "CNAME", - "value": "eeaemodxoao5hxwjvhywx4bo5mswjw6v.dkim.com.", - }, - ], - "region": "eu-west-1", - "status": "not_started", + "data": null, + "error": { + "message": "Unable to fetch data. The request could not be resolved.", + "name": "application_error", }, - "error": null, + "rateLimiting": null, } `); }); @@ -295,12 +253,8 @@ describe('Domains', () => { message: 'Region must be "us-east-1" | "eu-west-1" | "sa-east-1"', }; - fetchMock.mockOnce(JSON.stringify(errorResponse), { - status: 422, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockErrorResponse(errorResponse, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -317,6 +271,11 @@ describe('Domains', () => { "message": "Region must be "us-east-1" | "eu-west-1" | "sa-east-1"", "name": "invalid_region", }, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -359,12 +318,8 @@ describe('Domains', () => { region: 'us-east-1', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const payload: CreateDomainOptions = { @@ -412,6 +367,11 @@ describe('Domains', () => { "status": "not_started", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -438,12 +398,8 @@ describe('Domains', () => { }, ], }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -469,6 +425,11 @@ describe('Domains', () => { ], }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -482,12 +443,8 @@ describe('Domains', () => { message: 'Domain not found', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 404, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockErrorResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -501,6 +458,11 @@ describe('Domains', () => { "message": "Domain not found", "name": "not_found", }, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -544,12 +506,8 @@ describe('Domains', () => { ], }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -592,6 +550,11 @@ describe('Domains', () => { "status": "not_started", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -605,12 +568,8 @@ describe('Domains', () => { id, }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -627,6 +586,11 @@ describe('Domains', () => { "object": "domain", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -639,12 +603,8 @@ describe('Domains', () => { object: 'domain', id, }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -656,6 +616,11 @@ describe('Domains', () => { "object": "domain", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -669,12 +634,8 @@ describe('Domains', () => { id, deleted: true, }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -687,6 +648,11 @@ describe('Domains', () => { "object": "domain", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); diff --git a/src/domains/interfaces/create-domain-options.interface.ts b/src/domains/interfaces/create-domain-options.interface.ts index 13ba9562..ce278e95 100644 --- a/src/domains/interfaces/create-domain-options.interface.ts +++ b/src/domains/interfaces/create-domain-options.interface.ts @@ -1,5 +1,5 @@ import type { PostOptions } from '../../common/interfaces'; -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; import type { Domain, DomainRecords, DomainRegion } from './domain'; export interface CreateDomainOptions { @@ -15,12 +15,4 @@ export interface CreateDomainResponseSuccess records: DomainRecords[]; } -export type CreateDomainResponse = - | { - data: CreateDomainResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type CreateDomainResponse = Response; diff --git a/src/domains/interfaces/get-domain.interface.ts b/src/domains/interfaces/get-domain.interface.ts index 6c79410f..1b86eab3 100644 --- a/src/domains/interfaces/get-domain.interface.ts +++ b/src/domains/interfaces/get-domain.interface.ts @@ -1,4 +1,4 @@ -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; import type { Domain, DomainRecords } from './domain'; export interface GetDomainResponseSuccess @@ -7,12 +7,4 @@ export interface GetDomainResponseSuccess records: DomainRecords[]; } -export type GetDomainResponse = - | { - data: GetDomainResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type GetDomainResponse = Response; diff --git a/src/domains/interfaces/list-domains.interface.ts b/src/domains/interfaces/list-domains.interface.ts index fcba8ab9..af6a5604 100644 --- a/src/domains/interfaces/list-domains.interface.ts +++ b/src/domains/interfaces/list-domains.interface.ts @@ -1,14 +1,6 @@ -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; import type { Domain } from './domain'; export type ListDomainsResponseSuccess = { data: Domain[] }; -export type ListDomainsResponse = - | { - data: ListDomainsResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type ListDomainsResponse = Response; diff --git a/src/domains/interfaces/remove-domain.interface.ts b/src/domains/interfaces/remove-domain.interface.ts index 7c11f64d..aa1cb266 100644 --- a/src/domains/interfaces/remove-domain.interface.ts +++ b/src/domains/interfaces/remove-domain.interface.ts @@ -1,4 +1,4 @@ -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; import type { Domain } from './domain'; export type RemoveDomainsResponseSuccess = Pick & { @@ -6,12 +6,4 @@ export type RemoveDomainsResponseSuccess = Pick & { deleted: boolean; }; -export type RemoveDomainsResponse = - | { - data: RemoveDomainsResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type RemoveDomainsResponse = Response; diff --git a/src/domains/interfaces/update-domain.interface.ts b/src/domains/interfaces/update-domain.interface.ts index c07333eb..9b36b90e 100644 --- a/src/domains/interfaces/update-domain.interface.ts +++ b/src/domains/interfaces/update-domain.interface.ts @@ -1,4 +1,4 @@ -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; import type { Domain } from './domain'; export interface UpdateDomainsOptions { @@ -12,12 +12,4 @@ export type UpdateDomainsResponseSuccess = Pick & { object: 'domain'; }; -export type UpdateDomainsResponse = - | { - data: UpdateDomainsResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type UpdateDomainsResponse = Response; diff --git a/src/domains/interfaces/verify-domain.interface.ts b/src/domains/interfaces/verify-domain.interface.ts index 069b9193..6bb420b9 100644 --- a/src/domains/interfaces/verify-domain.interface.ts +++ b/src/domains/interfaces/verify-domain.interface.ts @@ -1,16 +1,8 @@ -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; import type { Domain } from './domain'; export type VerifyDomainsResponseSuccess = Pick & { object: 'domain'; }; -export type VerifyDomainsResponse = - | { - data: VerifyDomainsResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type VerifyDomainsResponse = Response; diff --git a/src/emails/emails.spec.ts b/src/emails/emails.spec.ts index c71fe78e..2acf2523 100644 --- a/src/emails/emails.spec.ts +++ b/src/emails/emails.spec.ts @@ -1,13 +1,16 @@ -import { enableFetchMocks } from 'jest-fetch-mock'; import type { ErrorResponse } from '../interfaces'; import { Resend } from '../resend'; +import { + mockErrorResponse, + mockFetchWithRateLimit, + mockSuccessResponse, +} from '../test-utils/mock-fetch'; import type { CreateEmailOptions, CreateEmailResponseSuccess, } from './interfaces/create-email-options.interface'; import type { GetEmailResponseSuccess } from './interfaces/get-email-options.interface'; - -enableFetchMocks(); +import type { ListEmailsResponseSuccess } from './interfaces/list-emails-options.interface'; const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); @@ -21,10 +24,8 @@ describe('Emails', () => { message: 'Missing `from` field.', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 422, + mockErrorResponse(response, { headers: { - 'content-type': 'application/json', Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', }, }); @@ -38,6 +39,11 @@ describe('Emails', () => { "message": "Missing \`from\` field.", "name": "missing_required_field", }, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -47,10 +53,8 @@ describe('Emails', () => { id: 'not-idempotent-123', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, + mockSuccessResponse(response, { headers: { - 'content-type': 'application/json', Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', }, }); @@ -67,15 +71,11 @@ describe('Emails', () => { // Inspect the last fetch call and body const lastCall = fetchMock.mock.calls[0]; expect(lastCall).toBeDefined(); + const request = lastCall[1]; + expect(request).toBeDefined(); - console.log('debug:', lastCall[1]?.headers); - //@ts-ignore - const hasIdempotencyKey = lastCall[1]?.headers.has('Idempotency-Key'); - expect(hasIdempotencyKey).toBeFalsy(); - - //@ts-ignore - const usedIdempotencyKey = lastCall[1]?.headers.get('Idempotency-Key'); - expect(usedIdempotencyKey).toBeNull(); + const headers = new Headers(request?.headers); + expect(headers.has('Idempotency-Key')).toBe(false); }); it('sends the Idempotency-Key header when idempotencyKey is provided', async () => { @@ -83,10 +83,8 @@ describe('Emails', () => { id: 'idempotent-123', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, + mockSuccessResponse(response, { headers: { - 'content-type': 'application/json', Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', }, }); @@ -105,17 +103,9 @@ describe('Emails', () => { const lastCall = fetchMock.mock.calls[0]; expect(lastCall).toBeDefined(); - // Check if headers contains Idempotency-Key - // In the mock, headers is an object with key-value pairs - expect(fetchMock.mock.calls[0][1]?.headers).toBeDefined(); - - //@ts-ignore - const hasIdempotencyKey = lastCall[1]?.headers.has('Idempotency-Key'); - expect(hasIdempotencyKey).toBeTruthy(); - - //@ts-ignore - const usedIdempotencyKey = lastCall[1]?.headers.get('Idempotency-Key'); - expect(usedIdempotencyKey).toBe(idempotencyKey); + const headers = new Headers(lastCall[1]?.headers); + expect(headers.has('Idempotency-Key')).toBe(true); + expect(headers.get('Idempotency-Key')).toBe(idempotencyKey); }); }); @@ -124,10 +114,8 @@ describe('Emails', () => { const response: CreateEmailResponseSuccess = { id: '71cdfe68-cf79-473a-a9d7-21f91db6a526', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, + mockSuccessResponse(response, { headers: { - 'content-type': 'application/json', Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', }, }); @@ -146,6 +134,11 @@ describe('Emails', () => { "id": "71cdfe68-cf79-473a-a9d7-21f91db6a526", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -155,12 +148,8 @@ describe('Emails', () => { id: '124dc0f1-e36c-417c-a65c-e33773abc768', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const payload: CreateEmailOptions = { @@ -176,6 +165,11 @@ describe('Emails', () => { "id": "124dc0f1-e36c-417c-a65c-e33773abc768", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -185,12 +179,8 @@ describe('Emails', () => { id: '124dc0f1-e36c-417c-a65c-e33773abc768', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const payload: CreateEmailOptions = { @@ -208,6 +198,11 @@ describe('Emails', () => { "id": "124dc0f1-e36c-417c-a65c-e33773abc768", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -217,12 +212,8 @@ describe('Emails', () => { id: '124dc0f1-e36c-417c-a65c-e33773abc768', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const payload: CreateEmailOptions = { @@ -240,6 +231,11 @@ describe('Emails', () => { "id": "124dc0f1-e36c-417c-a65c-e33773abc768", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -249,12 +245,8 @@ describe('Emails', () => { id: '124dc0f1-e36c-417c-a65c-e33773abc768', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const payload: CreateEmailOptions = { @@ -272,6 +264,11 @@ describe('Emails', () => { "id": "124dc0f1-e36c-417c-a65c-e33773abc768", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -281,12 +278,8 @@ describe('Emails', () => { id: '124dc0f1-e36c-417c-a65c-e33773abc768', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockSuccessResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const payload: CreateEmailOptions = { @@ -306,6 +299,11 @@ describe('Emails', () => { "id": "124dc0f1-e36c-417c-a65c-e33773abc768", }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -317,12 +315,8 @@ describe('Emails', () => { 'Invalid `from` field. The email address needs to follow the `email@example.com` or `Name ` format', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 422, - headers: { - 'content-type': 'application/json', - Authorization: 'Bearer re_924b3rjh2387fbewf823', - }, + mockErrorResponse(response, { + headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823' }, }); const payload: CreateEmailOptions = { @@ -336,14 +330,19 @@ describe('Emails', () => { const result = resend.emails.send(payload); await expect(result).resolves.toMatchInlineSnapshot(` - { - "data": null, - "error": { - "message": "Invalid \`from\` field. The email address needs to follow the \`email@example.com\` or \`Name \` format", - "name": "invalid_parameter", - }, - } - `); +{ + "data": null, + "error": { + "message": "Invalid \`from\` field. The email address needs to follow the \`email@example.com\` or \`Name \` format", + "name": "invalid_parameter", + }, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, +} +`); }); it('returns an error when fetch fails', async () => { @@ -373,7 +372,7 @@ describe('Emails', () => { }); it('returns an error when api responds with text payload', async () => { - fetchMock.mockOnce('local_rate_limited', { + mockFetchWithRateLimit('local_rate_limited', { status: 422, headers: { Authorization: 'Bearer re_924b3rjh2387fbewf823', @@ -408,10 +407,8 @@ describe('Emails', () => { message: 'Email not found', }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 404, + mockErrorResponse(response, { headers: { - 'content-type': 'application/json', Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', }, }); @@ -427,6 +424,11 @@ describe('Emails', () => { "message": "Email not found", "name": "not_found", }, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -450,10 +452,8 @@ describe('Emails', () => { scheduled_at: null, }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, + mockSuccessResponse(response, { headers: { - 'content-type': 'application/json', Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', }, }); @@ -480,6 +480,11 @@ describe('Emails', () => { ], }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); @@ -501,10 +506,8 @@ describe('Emails', () => { scheduled_at: null, }; - fetchMock.mockOnce(JSON.stringify(response), { - status: 200, + mockSuccessResponse(response, { headers: { - 'content-type': 'application/json', Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', }, }); @@ -534,9 +537,113 @@ describe('Emails', () => { ], }, "error": null, + "rateLimiting": { + "limit": 2, + "remainingRequests": 2, + "shouldResetAfter": 1, + }, } `); }); }); }); + + describe('list', () => { + const response: ListEmailsResponseSuccess = { + object: 'list', + has_more: false, + data: [ + { + id: '67d9bcdb-5a02-42d7-8da9-0d6feea18cff', + to: ['zeno@resend.com'], + from: 'bu@resend.com', + created_at: '2023-04-07T23:13:52.669661+00:00', + subject: 'Test email', + bcc: null, + cc: null, + reply_to: null, + last_event: 'delivered', + scheduled_at: null, + }, + ], + }; + const headers = { + Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', + }; + + describe('when no pagination options provided', () => { + it('calls endpoint without query params and return the response', async () => { + mockSuccessResponse(response, { + headers, + }); + + const result = await resend.emails.list(); + expect(result).toEqual({ + data: response, + error: null, + rateLimiting: { + limit: 2, + remainingRequests: 2, + shouldResetAfter: 1, + }, + }); + expect(fetchMock.mock.calls[0][0]).toBe( + 'https://api.resend.com/emails', + ); + }); + }); + + describe('when pagination options are provided', () => { + it('calls endpoint passing limit param and return the response', async () => { + mockSuccessResponse(response, { headers }); + const result = await resend.emails.list({ limit: 10 }); + expect(result).toEqual({ + data: response, + error: null, + rateLimiting: { + limit: 2, + remainingRequests: 2, + shouldResetAfter: 1, + }, + }); + expect(fetchMock.mock.calls[0][0]).toBe( + 'https://api.resend.com/emails?limit=10', + ); + }); + + it('calls endpoint passing after param and return the response', async () => { + mockSuccessResponse(response, { headers }); + const result = await resend.emails.list({ after: 'cursor123' }); + expect(result).toEqual({ + data: response, + error: null, + rateLimiting: { + limit: 2, + remainingRequests: 2, + shouldResetAfter: 1, + }, + }); + expect(fetchMock.mock.calls[0][0]).toBe( + 'https://api.resend.com/emails?after=cursor123', + ); + }); + + it('calls endpoint passing before param and return the response', async () => { + mockSuccessResponse(response, { headers }); + const result = await resend.emails.list({ before: 'cursor123' }); + expect(result).toEqual({ + data: response, + error: null, + rateLimiting: { + limit: 2, + remainingRequests: 2, + shouldResetAfter: 1, + }, + }); + expect(fetchMock.mock.calls[0][0]).toBe( + 'https://api.resend.com/emails?before=cursor123', + ); + }); + }); + }); }); diff --git a/src/emails/emails.ts b/src/emails/emails.ts index 23c13b0c..0a5c4ce2 100644 --- a/src/emails/emails.ts +++ b/src/emails/emails.ts @@ -1,4 +1,5 @@ import type * as React from 'react'; +import { PaginatedRequest } from '../common/pagination'; import { parseEmailToApiOptions } from '../common/utils/parse-email-to-api-options'; import type { Resend } from '../resend'; import type { @@ -15,6 +16,11 @@ import type { GetEmailResponse, GetEmailResponseSuccess, } from './interfaces/get-email-options.interface'; +import type { + ListEmail, + ListEmailsOptions, + ListEmailsResponseSuccess, +} from './interfaces/list-emails-options.interface'; import type { UpdateEmailOptions, UpdateEmailResponse, @@ -41,7 +47,7 @@ export class Emails { try { const { renderAsync } = await import('@react-email/render'); this.renderAsync = renderAsync; - } catch (error) { + } catch { throw new Error( 'Failed to render React component. Make sure to install `@react-email/render`', ); @@ -70,6 +76,30 @@ export class Emails { return data; } + list(options: ListEmailsOptions = {}): PaginatedRequest { + const fetchPage = async (options: ListEmailsOptions) => { + const searchParams = new URLSearchParams(); + + if (options.limit !== undefined) { + searchParams.set('limit', options.limit.toString()); + } + + if ('after' in options && options.after !== undefined) { + searchParams.set('after', options.after); + } + + if ('before' in options && options.before !== undefined) { + searchParams.set('before', options.before); + } + + const queryString = searchParams.toString(); + const url = queryString ? `/emails?${queryString}` : '/emails'; + return this.resend.get(url); + }; + + return new PaginatedRequest(fetchPage, options); + } + async update(payload: UpdateEmailOptions): Promise { const data = await this.resend.patch( `/emails/${payload.id}`, diff --git a/src/emails/interfaces/cancel-email-options.interface.ts b/src/emails/interfaces/cancel-email-options.interface.ts index c34fbf2d..e7ff6ff8 100644 --- a/src/emails/interfaces/cancel-email-options.interface.ts +++ b/src/emails/interfaces/cancel-email-options.interface.ts @@ -1,16 +1,8 @@ -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; export interface CancelEmailResponseSuccess { object: 'email'; id: string; } -export type CancelEmailResponse = - | { - data: CancelEmailResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type CancelEmailResponse = Response; diff --git a/src/emails/interfaces/create-email-options.interface.ts b/src/emails/interfaces/create-email-options.interface.ts index 11db678a..509ff88f 100644 --- a/src/emails/interfaces/create-email-options.interface.ts +++ b/src/emails/interfaces/create-email-options.interface.ts @@ -2,7 +2,7 @@ import type * as React from 'react'; import type { PostOptions } from '../../common/interfaces'; import type { IdempotentRequest } from '../../common/interfaces/idempotent-request.interface'; import type { RequireAtLeastOne } from '../../common/interfaces/require-at-least-one'; -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; interface EmailRenderOptions { /** @@ -101,15 +101,7 @@ export interface CreateEmailResponseSuccess { id: string; } -export type CreateEmailResponse = - | { - data: CreateEmailResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type CreateEmailResponse = Response; export interface Attachment { /** Content of an attached file. */ diff --git a/src/emails/interfaces/get-email-options.interface.ts b/src/emails/interfaces/get-email-options.interface.ts index 1d83248a..4c840a4a 100644 --- a/src/emails/interfaces/get-email-options.interface.ts +++ b/src/emails/interfaces/get-email-options.interface.ts @@ -1,4 +1,4 @@ -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; export interface GetEmailResponseSuccess { bcc: string[] | null; @@ -28,12 +28,4 @@ export interface GetEmailResponseSuccess { object: 'email'; } -export type GetEmailResponse = - | { - data: GetEmailResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type GetEmailResponse = Response; diff --git a/src/emails/interfaces/list-emails-options.interface.ts b/src/emails/interfaces/list-emails-options.interface.ts new file mode 100644 index 00000000..1adabd6e --- /dev/null +++ b/src/emails/interfaces/list-emails-options.interface.ts @@ -0,0 +1,36 @@ +import type { Response } from '../../interfaces'; +import type { GetEmailResponseSuccess } from './get-email-options.interface'; + +// Pagination options using cursor-based approach +export type ListEmailsOptions = { + /** + * Maximum number of emails to return (1-100, default: 20) + */ + limit?: number; +} & ( + | { + /** + * Get emails after this cursor (cannot be used with 'before') + */ + after?: string; + } + | { + /** + * Get emails before this cursor (cannot be used with 'after') + */ + before?: string; + } +); + +export type ListEmail = Omit< + GetEmailResponseSuccess, + 'html' | 'text' | 'tags' | 'object' +>; + +export type ListEmailsResponseSuccess = { + object: 'list'; + has_more: boolean; + data: ListEmail[]; +}; + +export type ListEmailsResponse = Response; diff --git a/src/emails/interfaces/update-email-options.interface.ts b/src/emails/interfaces/update-email-options.interface.ts index 89d04637..b3184539 100644 --- a/src/emails/interfaces/update-email-options.interface.ts +++ b/src/emails/interfaces/update-email-options.interface.ts @@ -1,4 +1,4 @@ -import type { ErrorResponse } from '../../interfaces'; +import type { Response } from '../../interfaces'; export interface UpdateEmailOptions { id: string; @@ -10,12 +10,4 @@ export interface UpdateEmailResponseSuccess { object: 'email'; } -export type UpdateEmailResponse = - | { - data: UpdateEmailResponseSuccess; - error: null; - } - | { - data: null; - error: ErrorResponse; - }; +export type UpdateEmailResponse = Response; diff --git a/src/index.ts b/src/index.ts index de508132..9cd7c1ed 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,3 @@ -export { Resend } from './resend'; -export { ErrorResponse } from './interfaces'; export * from './api-keys/interfaces/create-api-key-options.interface'; export * from './api-keys/interfaces/list-api-keys.interface'; export * from './api-keys/interfaces/remove-api-keys.interface'; @@ -7,9 +5,9 @@ export * from './audiences/interfaces/create-audience-options.interface'; export * from './audiences/interfaces/get-audience.interface'; export * from './audiences/interfaces/list-audiences.interface'; export * from './audiences/interfaces/remove-audience.interface'; +export * from './batch/interfaces/create-batch-options.interface'; export * from './broadcasts/interfaces/create-broadcast-options.interface'; export * from './broadcasts/interfaces/send-broadcast-options.interface'; -export * from './batch/interfaces/create-batch-options.interface'; export * from './contacts/interfaces/create-contact-options.interface'; export * from './contacts/interfaces/get-contact.interface'; export * from './contacts/interfaces/list-contacts.interface'; @@ -23,3 +21,5 @@ export * from './domains/interfaces/update-domain.interface'; export * from './domains/interfaces/verify-domain.interface'; export * from './emails/interfaces/create-email-options.interface'; export * from './emails/interfaces/get-email-options.interface'; +export { ErrorResponse } from './interfaces'; +export { Resend } from './resend'; diff --git a/src/interfaces.ts b/src/interfaces.ts index f3843d9e..15861c5e 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -1,3 +1,5 @@ +import type { RateLimit } from './rate-limiting'; + export const RESEND_ERROR_CODES_BY_KEY = { missing_required_field: 422, invalid_idempotency_key: 400, @@ -19,9 +21,32 @@ export const RESEND_ERROR_CODES_BY_KEY = { export type RESEND_ERROR_CODE_KEY = keyof typeof RESEND_ERROR_CODES_BY_KEY; -export interface ErrorResponse { +export type RateLimitExceededErrorResponse = { message: string; - name: RESEND_ERROR_CODE_KEY; -} + name: Extract; + /** + * Time in seconds. + */ + retryAfter: number; +}; + +export type ErrorResponse = + | { + message: string; + name: Exclude; + } + | RateLimitExceededErrorResponse; + +export type Response = + | { + data: Data; + rateLimiting: RateLimit; + error: null; + } + | { + data: null; + rateLimiting: RateLimit | null; + error: ErrorResponse; + }; export type Tag = { name: string; value: string }; diff --git a/src/rate-limiting.ts b/src/rate-limiting.ts new file mode 100644 index 00000000..31ee1537 --- /dev/null +++ b/src/rate-limiting.ts @@ -0,0 +1,44 @@ +import type { RateLimitExceededErrorResponse } from './interfaces'; + +export type RateLimit = { + /** + * The maximum amount of requests that can be made in the time window of {@link RateLimit.shouldResetAfter}. + */ + limit: number; + /** + * The amount of requests that can still be made before hitting {@link RateLimit.limit}. + * + * Resets after the seconds in {@link RateLimit.shouldResetAfter} go by. + */ + remainingRequests: number; + /** + * The number of seconds after which the rate limiting will reset, + * and {@link RateLimit.remainingRequests} goes back to the value of + * {@link RateLimit.limit}. + * + * @see {@link RateLimitExceededErrorResponse.retryAfter} + */ + shouldResetAfter: number; +}; + +export function parseRateLimit(headers: Headers): RateLimit { + const limitHeader = headers.get('ratelimit-limit'); + const remainingHeader = headers.get('ratelimit-remaining'); + const resetHeader = headers.get('ratelimit-reset'); + + if (!limitHeader || !remainingHeader || !resetHeader) { + throw new Error( + "The rate limit headers are not present in the response, something must've gone wrong, please email us at support@resend.com", + ); + } + + const limit = Number.parseInt(limitHeader, 10); + const remaining = Number.parseInt(remainingHeader, 10); + const reset = Number.parseInt(resetHeader, 10); + + return { + limit, + remainingRequests: remaining, + shouldResetAfter: reset, + }; +} diff --git a/src/resend.ts b/src/resend.ts index fb547964..9b90c983 100644 --- a/src/resend.ts +++ b/src/resend.ts @@ -9,7 +9,8 @@ import type { PatchOptions } from './common/interfaces/patch-option.interface'; import { Contacts } from './contacts/contacts'; import { Domains } from './domains/domains'; import { Emails } from './emails/emails'; -import type { ErrorResponse } from './interfaces'; +import type { ErrorResponse, Response } from './interfaces'; +import { parseRateLimit } from './rate-limiting'; const defaultBaseUrl = 'https://api.resend.com'; const defaultUserAgent = `resend-node:${version}`; @@ -53,21 +54,28 @@ export class Resend { }); } - async fetchRequest( - path: string, - options = {}, - ): Promise<{ data: T; error: null } | { data: null; error: ErrorResponse }> { + async fetchRequest(path: string, options = {}): Promise> { try { const response = await fetch(`${baseUrl}${path}`, options); + const rateLimiting = parseRateLimit(response.headers); + if (!response.ok) { try { const rawError = await response.text(); - return { data: null, error: JSON.parse(rawError) }; + const error: ErrorResponse = JSON.parse(rawError); + if (error.name === 'rate_limit_exceeded' && response.status === 429) { + const retryAfterHeader = response.headers.get('retry-after'); + if (retryAfterHeader) { + error.retryAfter = Number.parseInt(retryAfterHeader, 10); + } + } + return { data: null, rateLimiting, error }; } catch (err) { if (err instanceof SyntaxError) { return { data: null, + rateLimiting, error: { name: 'application_error', message: @@ -82,18 +90,23 @@ export class Resend { }; if (err instanceof Error) { - return { data: null, error: { ...error, message: err.message } }; + return { + data: null, + rateLimiting: rateLimiting, + error: { ...error, message: err.message }, + }; } - return { data: null, error }; + return { data: null, rateLimiting, error }; } } const data = await response.json(); - return { data, error: null }; - } catch (error) { + return { data, rateLimiting, error: null }; + } catch { return { data: null, + rateLimiting: null, error: { name: 'application_error', message: 'Unable to fetch data. The request could not be resolved.', @@ -108,48 +121,69 @@ export class Resend { options: PostOptions & IdempotentRequest = {}, ) { const headers = new Headers(this.headers); - + if (options.headers) { + for (const [key, value] of new Headers(options.headers).entries()) { + headers.set(key, value); + } + } if (options.idempotencyKey) { headers.set('Idempotency-Key', options.idempotencyKey); } - const requestOptions = { method: 'POST', - headers: headers, body: JSON.stringify(entity), ...options, + headers, }; return this.fetchRequest(path, requestOptions); } async get(path: string, options: GetOptions = {}) { + const headers = new Headers(this.headers); + if (options.headers) { + for (const [key, value] of new Headers(options.headers).entries()) { + headers.set(key, value); + } + } const requestOptions = { method: 'GET', - headers: this.headers, ...options, + headers, }; return this.fetchRequest(path, requestOptions); } async put(path: string, entity: unknown, options: PutOptions = {}) { + const headers = new Headers(this.headers); + if (options.headers) { + for (const [key, value] of new Headers(options.headers).entries()) { + headers.set(key, value); + } + } const requestOptions = { method: 'PUT', - headers: this.headers, body: JSON.stringify(entity), ...options, + headers, }; return this.fetchRequest(path, requestOptions); } async patch(path: string, entity: unknown, options: PatchOptions = {}) { + const headers = new Headers(this.headers); + if (options.headers) { + for (const [key, value] of new Headers(options.headers).entries()) { + headers.set(key, value); + } + } const requestOptions = { method: 'PATCH', - headers: this.headers, body: JSON.stringify(entity), ...options, + headers, }; return this.fetchRequest(path, requestOptions); @@ -158,7 +192,6 @@ export class Resend { async delete(path: string, query?: unknown) { const requestOptions = { method: 'DELETE', - headers: this.headers, body: JSON.stringify(query), }; diff --git a/src/test-utils/mock-fetch.ts b/src/test-utils/mock-fetch.ts new file mode 100644 index 00000000..6a276a46 --- /dev/null +++ b/src/test-utils/mock-fetch.ts @@ -0,0 +1,90 @@ +import type { MockParams } from 'vitest-fetch-mock'; + +export interface MockFetchOptions extends MockParams { + rateLimiting?: { + limit?: number; + remaining?: number; + reset?: number; + }; +} + +/** + * Mock fetch response with rate limiting headers included by default + */ +export function mockFetchWithRateLimit( + body: string, + options: MockFetchOptions = {}, +): void { + const { + rateLimiting = {}, + headers = {}, + status = 200, + ...restOptions + } = options; + + const defaultRateLimit = { + limit: 2, + remaining: 2, + reset: 1, // Fixed timestamp for consistent tests + }; + + const rateLimitHeaders = { + 'ratelimit-limit': String(rateLimiting.limit ?? defaultRateLimit.limit), + 'ratelimit-remaining': String( + rateLimiting.remaining ?? defaultRateLimit.remaining, + ), + 'ratelimit-reset': String(rateLimiting.reset ?? defaultRateLimit.reset), + }; + + const allHeaders = { + 'content-type': 'application/json', + ...rateLimitHeaders, + ...headers, + }; + + fetchMock.mockOnce(body, { + status, + headers: allHeaders, + ...restOptions, + }); +} + +/** + * Mock successful response with rate limiting headers + */ +export function mockSuccessResponse( + data: T, + options: MockFetchOptions = {}, +): void { + mockFetchWithRateLimit(JSON.stringify(data), { + status: 200, + ...options, + }); +} + +/** + * Mock successful response with a custom status code + */ +export function mockSuccessWithStatusCode( + data: T, + status: number, + options: MockFetchOptions = {}, +): void { + mockFetchWithRateLimit(JSON.stringify(data), { + status, + ...options, + }); +} + +/** + * Mock error response with rate limiting headers + */ +export function mockErrorResponse( + error: { name: string; message: string }, + options: MockFetchOptions = {}, +): void { + mockFetchWithRateLimit(JSON.stringify(error), { + status: 422, + ...options, + }); +} diff --git a/tsconfig.json b/tsconfig.json index 3adad82d..ba75ca8f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,18 +2,21 @@ "compilerOptions": { "allowSyntheticDefaultImports": true, "alwaysStrict": true, + "declaration": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, + "module": "commonjs", "moduleResolution": "node", + "noEmit": true, "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, "noUnusedParameters": true, + "skipLibCheck": true, + "outDir": "build", "resolveJsonModule": true, + "strict": true, "target": "es6", - "module": "commonjs", - "declaration": true, - "outDir": "build", - "strict": true + "types": ["vitest/globals"] }, "include": ["src"] } diff --git a/vitest.config.mts b/vitest.config.mts new file mode 100644 index 00000000..d0d7ba2e --- /dev/null +++ b/vitest.config.mts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + setupFiles: ['vitest.setup.mts'], + }, +}); diff --git a/vitest.setup.mts b/vitest.setup.mts new file mode 100644 index 00000000..baac05c8 --- /dev/null +++ b/vitest.setup.mts @@ -0,0 +1,6 @@ +import { vi } from 'vitest'; +import createFetchMock from 'vitest-fetch-mock'; + +const fetchMocker = createFetchMock(vi); + +fetchMocker.enableMocks();