From c17f733da124d4c99e54c120413614125160784a Mon Sep 17 00:00:00 2001 From: Xevion Date: Wed, 22 Oct 2025 02:45:58 -0500 Subject: [PATCH] ci: implement comprehensive CI/CD and workflow automation - Add GitHub Actions workflows for CI, release, and deployment - Configure Renovate for automated dependency updates - Set up Husky pre-commit hooks with lint-staged - Add commitlint for enforcing Conventional Commits - Configure semantic-release for automated versioning - Add Prettier configuration for consistent formatting - Reformat codebase with new formatting rules --- .commitlintrc.json | 26 + .github/renovate.json | 85 + .github/workflows/ci.yml | 165 ++ .github/workflows/release.yml | 71 + .husky/commit-msg | 5 + .husky/pre-commit | 5 + .lintstagedrc.json | 6 + .prettierignore | 25 + .prettierrc.json | 9 + .releaserc.json | 109 + README.md | 8 +- eslint.config.mjs | 108 +- next.config.mjs | 10 +- package.json | 133 +- pnpm-lock.yaml | 2652 +++++++++++++++++++++++- postcss.config.cjs | 6 +- prettier.config.cjs | 4 - src/bootstrap.ts | 28 +- src/components/common/AbstractCard.tsx | 181 +- src/components/common/DynamicDate.tsx | 35 +- src/components/common/ErrorCard.tsx | 78 +- src/components/common/Property.tsx | 28 +- src/components/common/PropertyList.tsx | 38 +- src/components/form/LookupInput.tsx | 571 +++-- src/components/lookup/AutnumCard.tsx | 87 +- src/components/lookup/DomainCard.tsx | 79 +- src/components/lookup/Events.tsx | 32 +- src/components/lookup/Generic.tsx | 66 +- src/components/lookup/IPCard.tsx | 82 +- src/constants.ts | 162 +- src/env/client.mjs | 38 +- src/env/schema.mjs | 8 +- src/env/server.mjs | 18 +- src/helpers.asn.test.ts | 236 +-- src/helpers.test.ts | 283 ++- src/helpers.ts | 167 +- src/hooks/useLookup.tsx | 706 +++---- src/lib/utils.ts | 2 +- src/pages/_app.tsx | 2 +- src/pages/index.tsx | 160 +- src/rdap.integration.test.ts | 64 +- src/rdap.test.ts | 671 +++--- src/rdap.ts | 256 ++- src/schema.ts | 250 ++- src/styles/globals.css | 24 +- src/types.ts | 28 +- tsconfig.json | 48 +- vitest.config.ts | 20 +- 48 files changed, 5453 insertions(+), 2422 deletions(-) create mode 100644 .commitlintrc.json create mode 100644 .github/renovate.json create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml create mode 100644 .husky/commit-msg create mode 100644 .husky/pre-commit create mode 100644 .lintstagedrc.json create mode 100644 .prettierignore create mode 100644 .prettierrc.json create mode 100644 .releaserc.json delete mode 100644 prettier.config.cjs diff --git a/.commitlintrc.json b/.commitlintrc.json new file mode 100644 index 0000000..2cd6ea7 --- /dev/null +++ b/.commitlintrc.json @@ -0,0 +1,26 @@ +{ + "extends": ["@commitlint/config-conventional"], + "rules": { + "type-enum": [ + 2, + "always", + [ + "feat", + "fix", + "docs", + "style", + "refactor", + "perf", + "test", + "build", + "ci", + "chore", + "revert" + ] + ], + "subject-case": [2, "never", ["upper-case"]], + "subject-empty": [2, "never"], + "subject-full-stop": [2, "never", "."], + "header-max-length": [2, "always", 100] + } +} diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 0000000..508192b --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,85 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended", + ":dependencyDashboard", + ":semanticCommits", + ":automergeDigest", + ":automergeMinor" + ], + "schedule": ["after 10pm every weekday", "before 5am every weekday", "every weekend"], + "timezone": "America/Chicago", + "prConcurrentLimit": 3, + "prCreation": "not-pending", + "rebaseWhen": "behind-base-branch", + "semanticCommitScope": "deps", + "vulnerabilityAlerts": { + "labels": ["security"], + "automerge": true, + "schedule": ["at any time"] + }, + "packageRules": [ + { + "description": "Automerge dev dependencies", + "matchDepTypes": ["devDependencies"], + "automerge": true, + "automergeType": "pr", + "minimumReleaseAge": "3 days" + }, + { + "description": "Automerge TypeScript type packages", + "matchPackagePatterns": ["^@types/"], + "automerge": true, + "automergeType": "pr" + }, + { + "description": "Group ESLint packages together", + "matchPackagePatterns": ["^eslint", "^@typescript-eslint/"], + "groupName": "eslint packages", + "automerge": true + }, + { + "description": "Group testing packages together", + "matchPackagePatterns": ["^vitest", "^@vitest/", "^@testing-library/"], + "groupName": "testing packages", + "automerge": true + }, + { + "description": "Group Next.js related packages", + "matchPackageNames": ["next", "eslint-config-next"], + "groupName": "Next.js packages", + "minimumReleaseAge": "7 days" + }, + { + "description": "Group React packages", + "matchPackageNames": ["react", "react-dom", "@types/react", "@types/react-dom"], + "groupName": "React packages", + "minimumReleaseAge": "7 days" + }, + { + "description": "Pin Node.js major versions", + "matchPackageNames": ["node"], + "enabled": false + }, + { + "description": "Group Tailwind CSS packages", + "matchPackagePatterns": [ + "^tailwindcss", + "^@tailwindcss/", + "prettier-plugin-tailwindcss" + ], + "groupName": "Tailwind CSS packages" + }, + { + "description": "Group font packages", + "matchPackagePatterns": ["^@fontsource"], + "groupName": "font packages", + "automerge": true + } + ], + "postUpdateOptions": ["pnpmDedupe"], + "lockFileMaintenance": { + "enabled": true, + "schedule": ["before 5am on monday"] + } +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..5155031 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,165 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + branches: [master] + +env: + NODE_VERSION: "20" + PNPM_VERSION: "9.0.0" + +jobs: + # Code quality checks + quality: + name: Code Quality + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: ${{ env.PNPM_VERSION }} + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: "pnpm" + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run linting + run: pnpm lint + + - name: Type checking + run: pnpm type-check + + - name: Check formatting + run: pnpm prettier --check . + + # Testing + test: + name: Test Suite + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: ${{ env.PNPM_VERSION }} + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: "pnpm" + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run unit tests + run: pnpm test:run + + - name: Run integration tests + run: pnpm test:integration + + - name: Upload coverage + uses: codecov/codecov-action@v4 + if: always() + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./coverage/coverage-final.json + flags: unittests + name: codecov-umbrella + fail_ci_if_error: false + + # Build verification + build: + name: Build Application + needs: [quality, test] + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: ${{ env.PNPM_VERSION }} + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: "pnpm" + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build application + run: pnpm build + env: + NODE_ENV: production + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: next-build + path: | + .next/ + out/ + retention-days: 7 + if-no-files-found: warn + + # Security scanning + security: + name: Security Scan + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: ${{ env.PNPM_VERSION }} + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: "pnpm" + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run security audit + run: pnpm audit --audit-level=moderate + continue-on-error: true + + - name: Check for known vulnerabilities + uses: aquasecurity/trivy-action@master + with: + scan-type: "fs" + scan-ref: "." + format: "sarif" + output: "trivy-results.sarif" + severity: "CRITICAL,HIGH" + exit-code: 0 + + - name: Upload Trivy results + uses: github/codeql-action/upload-sarif@v3 + if: always() + with: + sarif_file: "trivy-results.sarif" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..3f5b413 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,71 @@ +name: Release + +on: + push: + branches: + - master + +permissions: + contents: write + issues: write + pull-requests: write + +jobs: + release: + name: Create Release + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Check for skip ci + id: check_skip + run: | + if git log -1 --pretty=%B | grep -q '\[skip ci\]'; then + echo "skip=true" >> $GITHUB_OUTPUT + else + echo "skip=false" >> $GITHUB_OUTPUT + fi + + - name: Setup pnpm + if: steps.check_skip.outputs.skip == 'false' + uses: pnpm/action-setup@v4 + with: + version: 9.0.0 + + - name: Setup Node.js + if: steps.check_skip.outputs.skip == 'false' + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: "pnpm" + + - name: Install dependencies + if: steps.check_skip.outputs.skip == 'false' + run: pnpm install --frozen-lockfile + + - name: Build application + if: steps.check_skip.outputs.skip == 'false' + run: pnpm build + env: + NODE_ENV: production + + - name: Run semantic release + if: steps.check_skip.outputs.skip == 'false' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: pnpm semantic-release + + - name: Upload release artifacts + if: steps.check_skip.outputs.skip == 'false' + uses: actions/upload-artifact@v4 + with: + name: release-build + path: | + .next/ + out/ + retention-days: 30 diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100644 index 0000000..5443bbb --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,5 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +# Validate commit message format +pnpm commitlint --edit "$1" diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..02a9c75 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,5 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +# Run linting on staged files +pnpm lint-staged diff --git a/.lintstagedrc.json b/.lintstagedrc.json new file mode 100644 index 0000000..4fc8a73 --- /dev/null +++ b/.lintstagedrc.json @@ -0,0 +1,6 @@ +{ + "*.{js,jsx,ts,tsx,mjs}": ["eslint --fix", "prettier --write"], + "*.{json,md,yml,yaml}": ["prettier --write"], + "*.{css,scss}": ["prettier --write"], + "package.json": ["prettier --write"] +} diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..e8c075e --- /dev/null +++ b/.prettierignore @@ -0,0 +1,25 @@ +# Dependencies +node_modules +pnpm-lock.yaml + +# Build outputs +.next +out +dist +build + +# Cache +.cache +.turbo + +# Coverage +coverage + +# Misc +.DS_Store +*.log +*.tsbuildinfo + +# Environment +.env +.env*.local diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..efedcbb --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,9 @@ +{ + "semi": true, + "trailingComma": "es5", + "printWidth": 100, + "tabWidth": 4, + "useTabs": true, + "endOfLine": "lf", + "plugins": ["prettier-plugin-tailwindcss"] +} diff --git a/.releaserc.json b/.releaserc.json new file mode 100644 index 0000000..bf761e2 --- /dev/null +++ b/.releaserc.json @@ -0,0 +1,109 @@ +{ + "branches": [ + "master", + { + "name": "beta", + "prerelease": true + }, + { + "name": "alpha", + "prerelease": true + } + ], + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits", + "releaseRules": [ + { + "type": "docs", + "scope": "README", + "release": "patch" + }, + { + "type": "refactor", + "release": "patch" + }, + { + "type": "style", + "release": "patch" + }, + { + "type": "perf", + "release": "patch" + } + ], + "parserOpts": { + "noteKeywords": ["BREAKING CHANGE", "BREAKING CHANGES"] + } + } + ], + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits", + "presetConfig": { + "types": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "perf", + "section": "Performance Improvements" + }, + { + "type": "refactor", + "section": "Code Refactoring" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "style", + "section": "Styles" + }, + { + "type": "test", + "section": "Tests" + }, + { + "type": "build", + "section": "Build System" + }, + { + "type": "ci", + "section": "Continuous Integration" + } + ] + } + } + ], + [ + "@semantic-release/changelog", + { + "changelogFile": "CHANGELOG.md" + } + ], + [ + "@semantic-release/npm", + { + "npmPublish": false + } + ], + [ + "@semantic-release/git", + { + "assets": ["CHANGELOG.md", "package.json"], + "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" + } + ], + "@semantic-release/github" + ] +} diff --git a/README.md b/README.md index f39bdd4..0076031 100644 --- a/README.md +++ b/README.md @@ -35,11 +35,11 @@ But in all seriousness, my project does intend to have more features: - Whois: RDAP is a newer protocol that is meant to replace Whois, but some servers still use Whois. I might add support for that. - Punycode: Some domain names use punycode, which is a way to represent Unicode characters in ASCII. I might add support for working with these domains. - Better Error Handling: The original RDAP client doesn't handle errors very well, or tell you what went wrong. I want to fix that. - - For example, when querying a TLD that does not have a RDAP server, or one that does not exist, the original client doesn't explain this very well. + - For example, when querying a TLD that does not have a RDAP server, or one that does not exist, the original client doesn't explain this very well. - RDAP Schema Adherence: RDAP servers are supposed to follow a schema, but it appears a large number simply don't. I intend to provide handling for this. - - Essentially, two separate schemas will be available: one for basic type checking, and one for the RFC-compliant schema. - - If the server doesn't follow the RFC-compliant schema, the basic schema will be used instead. - - It's hard to tell at this moment if Typescript can handle this well, but I'll try. + - Essentially, two separate schemas will be available: one for basic type checking, and one for the RFC-compliant schema. + - If the server doesn't follow the RFC-compliant schema, the basic schema will be used instead. + - It's hard to tell at this moment if Typescript can handle this well, but I'll try. [rdap]: https://rdap.xevion.dev [nextjs]: https://nextjs.org diff --git a/eslint.config.mjs b/eslint.config.mjs index 0c2cdff..097a32f 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -8,69 +8,69 @@ import { FlatCompat } from "@eslint/eslintrc"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const compat = new FlatCompat({ - baseDirectory: __dirname, - recommendedConfig: js.configs.recommended, - allConfig: js.configs.all + baseDirectory: __dirname, + recommendedConfig: js.configs.recommended, + allConfig: js.configs.all, }); export default [ - // Base configuration with ignores - { - ignores: [ - ".next/**", - "node_modules/**", - "out/**", - "*.config.mjs", - "*.config.js", - "next-env.d.ts" // Next.js generated file - ], - }, + // Base configuration with ignores + { + ignores: [ + ".next/**", + "node_modules/**", + "out/**", + "*.config.mjs", + "*.config.js", + "next-env.d.ts", // Next.js generated file + ], + }, - // Next.js core web vitals using FlatCompat - ...compat.extends("next/core-web-vitals"), + // Next.js core web vitals using FlatCompat + ...compat.extends("next/core-web-vitals"), - // TypeScript recommended rules - ...compat.extends("plugin:@typescript-eslint/recommended"), + // TypeScript recommended rules + ...compat.extends("plugin:@typescript-eslint/recommended"), - // Base TypeScript configuration - { - plugins: { - "@typescript-eslint": typescriptEslint, - }, + // Base TypeScript configuration + { + plugins: { + "@typescript-eslint": typescriptEslint, + }, - languageOptions: { - parser: tsParser, - ecmaVersion: "latest", - sourceType: "module", - parserOptions: { - project: "./tsconfig.json", - }, - }, + languageOptions: { + parser: tsParser, + ecmaVersion: "latest", + sourceType: "module", + parserOptions: { + project: "./tsconfig.json", + }, + }, - rules: { - "@typescript-eslint/consistent-type-imports": "warn", - }, - }, + rules: { + "@typescript-eslint/consistent-type-imports": "warn", + }, + }, - // Additional strict TypeScript rules for .ts and .tsx files - { - files: ["**/*.ts", "**/*.tsx"], - ...compat.extends("plugin:@typescript-eslint/recommended-requiring-type-checking")[0], + // Additional strict TypeScript rules for .ts and .tsx files + { + files: ["**/*.ts", "**/*.tsx"], + ...compat.extends("plugin:@typescript-eslint/recommended-requiring-type-checking")[0], - languageOptions: { - ecmaVersion: "latest", - sourceType: "module", - parserOptions: { - project: "./tsconfig.json", - }, - }, - }, + languageOptions: { + ecmaVersion: "latest", + sourceType: "module", + parserOptions: { + project: "./tsconfig.json", + }, + }, + }, - // Allow CommonJS require in .cjs files - { - files: ["**/*.cjs"], - rules: { - "@typescript-eslint/no-require-imports": "off", - }, - }, + // Allow CommonJS require in .cjs files + { + files: ["**/*.cjs"], + rules: { + "@typescript-eslint/no-require-imports": "off", + }, + }, ]; diff --git a/next.config.mjs b/next.config.mjs index 4208266..e6835fd 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -7,10 +7,10 @@ /** @type {import("next").NextConfig} */ const config = { - reactStrictMode: true, - i18n: { - locales: ["en"], - defaultLocale: "en", - }, + reactStrictMode: true, + i18n: { + locales: ["en"], + defaultLocale: "en", + }, }; export default config; diff --git a/package.json b/package.json index 9602cda..9b3cec5 100644 --- a/package.json +++ b/package.json @@ -1,62 +1,75 @@ { - "name": "rdap", - "version": "0.2.0", - "private": true, - "scripts": { - "build": "next build", - "dev": "next dev", - "lint": "eslint .", - "start": "next start", - "test": "vitest", - "test:ui": "vitest --ui", - "test:run": "vitest run --exclude '**/*.integration.test.ts'", - "test:integration": "vitest run --include '**/*.integration.test.ts'", - "test:all": "vitest run", - "type-check": "tsc --noEmit" - }, - "dependencies": { - "@fontsource-variable/inter": "^5.2.8", - "@fontsource/ibm-plex-mono": "^5.2.7", - "@headlessui/react": "^2.2.9", - "@heroicons/react": "^2.0.16", - "@swc/helpers": "^0.5.11", - "clsx": "^2.1.1", - "date-fns": "^4.1.0", - "immutability-helper": "^3.1.1", - "next": "^15.5.6", - "react": "19.2.0", - "react-dom": "19.2.0", - "react-hook-form": "^7.42.1", - "react-timeago": "^8.3.0", - "sass": "^1.57.1", - "tailwind-merge": "^3.3.1", - "true-myth": "^9.2.0", - "usehooks-ts": "^3.1.1", - "zod": "^4.1.12" - }, - "devDependencies": { - "@tailwindcss/postcss": "^4.1.15", - "@testing-library/jest-dom": "^6.9.1", - "@testing-library/react": "^16.3.0", - "@types/node": "^24.9.1", - "@types/react": "^19.2.2", - "@types/react-dom": "^19.2.2", - "@typescript-eslint/eslint-plugin": "^8.46.2", - "@typescript-eslint/parser": "^8.46.2", - "@vitest/ui": "^3.2.4", - "eslint": "^9.38.0", - "eslint-config-next": "15.5.6", - "happy-dom": "^20.0.8", - "postcss": "^8.4.14", - "prettier": "^3.6.2", - "prettier-plugin-tailwindcss": "^0.7.1", - "tailwindcss": "^4.1.15", - "type-fest": "^5.1.0", - "typescript": "^5.9.3", - "vitest": "^3.2.4" - }, - "ct3aMetadata": { - "initVersion": "7.2.0" - }, - "packageManager": "pnpm@9.0.0" + "name": "rdap", + "version": "0.2.0", + "private": true, + "scripts": { + "build": "next build", + "dev": "next dev", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "format": "prettier --write .", + "format:check": "prettier --check .", + "start": "next start", + "test": "vitest", + "test:ui": "vitest --ui", + "test:run": "vitest run --exclude '**/*.integration.test.ts'", + "test:integration": "vitest run --include '**/*.integration.test.ts'", + "test:all": "vitest run", + "type-check": "tsc --noEmit", + "prepare": "husky install", + "semantic-release": "semantic-release" + }, + "dependencies": { + "@fontsource-variable/inter": "^5.2.8", + "@fontsource/ibm-plex-mono": "^5.2.7", + "@headlessui/react": "^2.2.9", + "@heroicons/react": "^2.0.16", + "@swc/helpers": "^0.5.11", + "clsx": "^2.1.1", + "date-fns": "^4.1.0", + "immutability-helper": "^3.1.1", + "next": "^15.5.6", + "react": "19.2.0", + "react-dom": "19.2.0", + "react-hook-form": "^7.42.1", + "react-timeago": "^8.3.0", + "sass": "^1.57.1", + "tailwind-merge": "^3.3.1", + "true-myth": "^9.2.0", + "usehooks-ts": "^3.1.1", + "zod": "^4.1.12" + }, + "devDependencies": { + "@commitlint/cli": "^19.0.0", + "@commitlint/config-conventional": "^19.0.0", + "@semantic-release/changelog": "^6.0.3", + "@semantic-release/git": "^10.0.1", + "@tailwindcss/postcss": "^4.1.15", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.0", + "@types/node": "^24.9.1", + "@types/react": "^19.2.2", + "@types/react-dom": "^19.2.2", + "@typescript-eslint/eslint-plugin": "^8.46.2", + "@typescript-eslint/parser": "^8.46.2", + "@vitest/ui": "^3.2.4", + "conventional-changelog-conventionalcommits": "^8.0.0", + "eslint": "^9.38.0", + "eslint-config-next": "15.5.6", + "happy-dom": "^20.0.8", + "husky": "^9.0.0", + "lint-staged": "^15.0.0", + "postcss": "^8.4.14", + "prettier": "^3.6.2", + "prettier-plugin-tailwindcss": "^0.7.1", + "semantic-release": "^24.0.0", + "tailwindcss": "^4.1.15", + "type-fest": "^5.1.0", + "typescript": "^5.9.3", + "vitest": "^3.2.4" + }, + "ct3aMetadata": { + "initVersion": "7.2.0" + }, + "packageManager": "pnpm@9.0.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f30b3be..43c3f6b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -63,6 +63,18 @@ importers: specifier: ^4.1.12 version: 4.1.12 devDependencies: + '@commitlint/cli': + specifier: ^19.0.0 + version: 19.8.1(@types/node@24.9.1)(typescript@5.9.3) + '@commitlint/config-conventional': + specifier: ^19.0.0 + version: 19.8.1 + '@semantic-release/changelog': + specifier: ^6.0.3 + version: 6.0.3(semantic-release@24.2.9(typescript@5.9.3)) + '@semantic-release/git': + specifier: ^10.0.1 + version: 10.0.1(semantic-release@24.2.9(typescript@5.9.3)) '@tailwindcss/postcss': specifier: ^4.1.15 version: 4.1.15 @@ -90,6 +102,9 @@ importers: '@vitest/ui': specifier: ^3.2.4 version: 3.2.4(vitest@3.2.4) + conventional-changelog-conventionalcommits: + specifier: ^8.0.0 + version: 8.0.0 eslint: specifier: ^9.38.0 version: 9.38.0(jiti@2.6.1) @@ -99,6 +114,12 @@ importers: happy-dom: specifier: ^20.0.8 version: 20.0.8 + husky: + specifier: ^9.0.0 + version: 9.1.7 + lint-staged: + specifier: ^15.0.0 + version: 15.5.2 postcss: specifier: ^8.4.14 version: 8.5.6 @@ -108,6 +129,9 @@ importers: prettier-plugin-tailwindcss: specifier: ^0.7.1 version: 0.7.1(prettier@3.6.2) + semantic-release: + specifier: ^24.0.0 + version: 24.2.9(typescript@5.9.3) tailwindcss: specifier: ^4.1.15 version: 4.1.15 @@ -119,7 +143,7 @@ importers: version: 5.9.3 vitest: specifier: ^3.2.4 - version: 3.2.4(@types/node@24.9.1)(@vitest/ui@3.2.4)(happy-dom@20.0.8)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2) + version: 3.2.4(@types/node@24.9.1)(@vitest/ui@3.2.4)(happy-dom@20.0.8)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(yaml@2.8.1) packages: @@ -142,6 +166,79 @@ packages: resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} engines: {node: '>=6.9.0'} + '@colors/colors@1.5.0': + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + + '@commitlint/cli@19.8.1': + resolution: {integrity: sha512-LXUdNIkspyxrlV6VDHWBmCZRtkEVRpBKxi2Gtw3J54cGWhLCTouVD/Q6ZSaSvd2YaDObWK8mDjrz3TIKtaQMAA==} + engines: {node: '>=v18'} + hasBin: true + + '@commitlint/config-conventional@19.8.1': + resolution: {integrity: sha512-/AZHJL6F6B/G959CsMAzrPKKZjeEiAVifRyEwXxcT6qtqbPwGw+iQxmNS+Bu+i09OCtdNRW6pNpBvgPrtMr9EQ==} + engines: {node: '>=v18'} + + '@commitlint/config-validator@19.8.1': + resolution: {integrity: sha512-0jvJ4u+eqGPBIzzSdqKNX1rvdbSU1lPNYlfQQRIFnBgLy26BtC0cFnr7c/AyuzExMxWsMOte6MkTi9I3SQ3iGQ==} + engines: {node: '>=v18'} + + '@commitlint/ensure@19.8.1': + resolution: {integrity: sha512-mXDnlJdvDzSObafjYrOSvZBwkD01cqB4gbnnFuVyNpGUM5ijwU/r/6uqUmBXAAOKRfyEjpkGVZxaDsCVnHAgyw==} + engines: {node: '>=v18'} + + '@commitlint/execute-rule@19.8.1': + resolution: {integrity: sha512-YfJyIqIKWI64Mgvn/sE7FXvVMQER/Cd+s3hZke6cI1xgNT/f6ZAz5heND0QtffH+KbcqAwXDEE1/5niYayYaQA==} + engines: {node: '>=v18'} + + '@commitlint/format@19.8.1': + resolution: {integrity: sha512-kSJj34Rp10ItP+Eh9oCItiuN/HwGQMXBnIRk69jdOwEW9llW9FlyqcWYbHPSGofmjsqeoxa38UaEA5tsbm2JWw==} + engines: {node: '>=v18'} + + '@commitlint/is-ignored@19.8.1': + resolution: {integrity: sha512-AceOhEhekBUQ5dzrVhDDsbMaY5LqtN8s1mqSnT2Kz1ERvVZkNihrs3Sfk1Je/rxRNbXYFzKZSHaPsEJJDJV8dg==} + engines: {node: '>=v18'} + + '@commitlint/lint@19.8.1': + resolution: {integrity: sha512-52PFbsl+1EvMuokZXLRlOsdcLHf10isTPlWwoY1FQIidTsTvjKXVXYb7AvtpWkDzRO2ZsqIgPK7bI98x8LRUEw==} + engines: {node: '>=v18'} + + '@commitlint/load@19.8.1': + resolution: {integrity: sha512-9V99EKG3u7z+FEoe4ikgq7YGRCSukAcvmKQuTtUyiYPnOd9a2/H9Ak1J9nJA1HChRQp9OA/sIKPugGS+FK/k1A==} + engines: {node: '>=v18'} + + '@commitlint/message@19.8.1': + resolution: {integrity: sha512-+PMLQvjRXiU+Ae0Wc+p99EoGEutzSXFVwQfa3jRNUZLNW5odZAyseb92OSBTKCu+9gGZiJASt76Cj3dLTtcTdg==} + engines: {node: '>=v18'} + + '@commitlint/parse@19.8.1': + resolution: {integrity: sha512-mmAHYcMBmAgJDKWdkjIGq50X4yB0pSGpxyOODwYmoexxxiUCy5JJT99t1+PEMK7KtsCtzuWYIAXYAiKR+k+/Jw==} + engines: {node: '>=v18'} + + '@commitlint/read@19.8.1': + resolution: {integrity: sha512-03Jbjb1MqluaVXKHKRuGhcKWtSgh3Jizqy2lJCRbRrnWpcM06MYm8th59Xcns8EqBYvo0Xqb+2DoZFlga97uXQ==} + engines: {node: '>=v18'} + + '@commitlint/resolve-extends@19.8.1': + resolution: {integrity: sha512-GM0mAhFk49I+T/5UCYns5ayGStkTt4XFFrjjf0L4S26xoMTSkdCf9ZRO8en1kuopC4isDFuEm7ZOm/WRVeElVg==} + engines: {node: '>=v18'} + + '@commitlint/rules@19.8.1': + resolution: {integrity: sha512-Hnlhd9DyvGiGwjfjfToMi1dsnw1EXKGJNLTcsuGORHz6SS9swRgkBsou33MQ2n51/boIDrbsg4tIBbRpEWK2kw==} + engines: {node: '>=v18'} + + '@commitlint/to-lines@19.8.1': + resolution: {integrity: sha512-98Mm5inzbWTKuZQr2aW4SReY6WUukdWXuZhrqf1QdKPZBCCsXuG87c+iP0bwtD6DBnmVVQjgp4whoHRVixyPBg==} + engines: {node: '>=v18'} + + '@commitlint/top-level@19.8.1': + resolution: {integrity: sha512-Ph8IN1IOHPSDhURCSXBz44+CIu+60duFwRsg6HqaISFHQHbmBtxVw4ZrFNIYUzEP7WwrNPxa2/5qJ//NK1FGcw==} + engines: {node: '>=v18'} + + '@commitlint/types@19.8.1': + resolution: {integrity: sha512-/yCrWGCoA1SVKOks25EGadP9Pnj0oAIHGpl2wH2M2Y46dPM2ueb8wyCVOD7O3WCTkaJ0IkKvzhl1JY7+uCT2Dw==} + engines: {node: '>=v18'} + '@emnapi/core@1.6.0': resolution: {integrity: sha512-zq/ay+9fNIJJtJiZxdTnXS20PllcYMX3OE23ESc4HK/bdYu3cOWYVhsOhVnXALfU/uqJIxn5NBPd9z4v+SfoSg==} @@ -615,6 +712,54 @@ packages: resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} engines: {node: '>=12.4.0'} + '@octokit/auth-token@6.0.0': + resolution: {integrity: sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==} + engines: {node: '>= 20'} + + '@octokit/core@7.0.5': + resolution: {integrity: sha512-t54CUOsFMappY1Jbzb7fetWeO0n6K0k/4+/ZpkS+3Joz8I4VcvY9OiEBFRYISqaI2fq5sCiPtAjRDOzVYG8m+Q==} + engines: {node: '>= 20'} + + '@octokit/endpoint@11.0.1': + resolution: {integrity: sha512-7P1dRAZxuWAOPI7kXfio88trNi/MegQ0IJD3vfgC3b+LZo1Qe6gRJc2v0mz2USWWJOKrB2h5spXCzGbw+fAdqA==} + engines: {node: '>= 20'} + + '@octokit/graphql@9.0.2': + resolution: {integrity: sha512-iz6KzZ7u95Fzy9Nt2L8cG88lGRMr/qy1Q36ih/XVzMIlPDMYwaNLE/ENhqmIzgPrlNWiYJkwmveEetvxAgFBJw==} + engines: {node: '>= 20'} + + '@octokit/openapi-types@26.0.0': + resolution: {integrity: sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA==} + + '@octokit/plugin-paginate-rest@13.2.1': + resolution: {integrity: sha512-Tj4PkZyIL6eBMYcG/76QGsedF0+dWVeLhYprTmuFVVxzDW7PQh23tM0TP0z+1MvSkxB29YFZwnUX+cXfTiSdyw==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-retry@8.0.2': + resolution: {integrity: sha512-mVPCe77iaD8g1lIX46n9bHPUirFLzc3BfIzsZOpB7bcQh1ecS63YsAgcsyMGqvGa2ARQWKEFTrhMJX2MLJVHVw==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=7' + + '@octokit/plugin-throttling@11.0.2': + resolution: {integrity: sha512-ntNIig4zZhQVOZF4fG9Wt8QCoz9ehb+xnlUwp74Ic2ANChCk8oKmRwV9zDDCtrvU1aERIOvtng8wsalEX7Jk5Q==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': ^7.0.0 + + '@octokit/request-error@7.0.1': + resolution: {integrity: sha512-CZpFwV4+1uBrxu7Cw8E5NCXDWFNf18MSY23TdxCBgjw1tXXHvTrZVsXlW8hgFTOLw8RQR1BBrMvYRtuyaijHMA==} + engines: {node: '>= 20'} + + '@octokit/request@10.0.5': + resolution: {integrity: sha512-TXnouHIYLtgDhKo+N6mXATnDBkV05VwbR0TtMWpgTHIoQdRQfCSzmy/LGqR1AbRMbijq/EckC/E3/ZNcU92NaQ==} + engines: {node: '>= 20'} + + '@octokit/types@15.0.1': + resolution: {integrity: sha512-sdiirM93IYJ9ODDCBgmRPIboLbSkpLa5i+WLuXH8b8Atg+YMLAyLvDDhNWLV4OYd08tlvYfVm/dw88cqHWtw1Q==} + '@parcel/watcher-android-arm64@2.5.1': resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} engines: {node: '>= 10.0.0'} @@ -697,6 +842,18 @@ packages: resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} engines: {node: '>= 10.0.0'} + '@pnpm/config.env-replace@1.1.0': + resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} + engines: {node: '>=12.22.0'} + + '@pnpm/network.ca-file@1.0.2': + resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} + engines: {node: '>=12.22.0'} + + '@pnpm/npm-conf@2.3.1': + resolution: {integrity: sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==} + engines: {node: '>=12'} + '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} @@ -853,6 +1010,61 @@ packages: '@rushstack/eslint-patch@1.14.0': resolution: {integrity: sha512-WJFej426qe4RWOm9MMtP4V3CV4AucXolQty+GRgAWLgQXmpCuwzs7hEpxxhSc/znXUSxum9d/P/32MW0FlAAlA==} + '@sec-ant/readable-stream@0.4.1': + resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + + '@semantic-release/changelog@6.0.3': + resolution: {integrity: sha512-dZuR5qByyfe3Y03TpmCvAxCyTnp7r5XwtHRf/8vD9EAn4ZWbavUX8adMtXYzE86EVh0gyLA7lm5yW4IV30XUag==} + engines: {node: '>=14.17'} + peerDependencies: + semantic-release: '>=18.0.0' + + '@semantic-release/commit-analyzer@13.0.1': + resolution: {integrity: sha512-wdnBPHKkr9HhNhXOhZD5a2LNl91+hs8CC2vsAVYxtZH3y0dV3wKn+uZSN61rdJQZ8EGxzWB3inWocBHV9+u/CQ==} + engines: {node: '>=20.8.1'} + peerDependencies: + semantic-release: '>=20.1.0' + + '@semantic-release/error@3.0.0': + resolution: {integrity: sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==} + engines: {node: '>=14.17'} + + '@semantic-release/error@4.0.0': + resolution: {integrity: sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==} + engines: {node: '>=18'} + + '@semantic-release/git@10.0.1': + resolution: {integrity: sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==} + engines: {node: '>=14.17'} + peerDependencies: + semantic-release: '>=18.0.0' + + '@semantic-release/github@11.0.6': + resolution: {integrity: sha512-ctDzdSMrT3H+pwKBPdyCPty6Y47X8dSrjd3aPZ5KKIKKWTwZBE9De8GtsH3TyAlw3Uyo2stegMx6rJMXKpJwJA==} + engines: {node: '>=20.8.1'} + peerDependencies: + semantic-release: '>=24.1.0' + + '@semantic-release/npm@12.0.2': + resolution: {integrity: sha512-+M9/Lb35IgnlUO6OSJ40Ie+hUsZLuph2fqXC/qrKn0fMvUU/jiCjpoL6zEm69vzcmaZJ8yNKtMBEKHWN49WBbQ==} + engines: {node: '>=20.8.1'} + peerDependencies: + semantic-release: '>=20.1.0' + + '@semantic-release/release-notes-generator@14.1.0': + resolution: {integrity: sha512-CcyDRk7xq+ON/20YNR+1I/jP7BYKICr1uKd1HHpROSnnTdGqOTburi4jcRiTYz0cpfhxSloQO3cGhnoot7IEkA==} + engines: {node: '>=20.8.1'} + peerDependencies: + semantic-release: '>=20.1.0' + + '@sindresorhus/is@4.6.0': + resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} + engines: {node: '>=10'} + + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} @@ -988,6 +1200,9 @@ packages: '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + '@types/conventional-commits-parser@5.0.1': + resolution: {integrity: sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==} + '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} @@ -1006,6 +1221,9 @@ packages: '@types/node@24.9.1': resolution: {integrity: sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==} + '@types/normalize-package-data@2.4.4': + resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} + '@types/react-dom@19.2.2': resolution: {integrity: sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==} peerDependencies: @@ -1205,6 +1423,10 @@ packages: '@vitest/utils@3.2.4': resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + JSONStream@1.3.5: + resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} + hasBin: true + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1215,13 +1437,40 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + aggregate-error@3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + + aggregate-error@5.0.0: + resolution: {integrity: sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==} + engines: {node: '>=18'} + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + ansi-escapes@7.1.1: + resolution: {integrity: sha512-Zhl0ErHcSRUaVfGUeUdDuLgpkEo8KIFjB4Y9uAc46ScOpdDiU1Dbyplh7qWJeJ/ZHpbyMSM26+X3BySgnIz40Q==} + engines: {node: '>=18'} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + 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'} @@ -1230,9 +1479,19 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + argv-formatter@1.0.0: + resolution: {integrity: sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==} + aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} @@ -1244,6 +1503,9 @@ packages: resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} + array-ify@1.0.0: + resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} + array-includes@3.1.9: resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} engines: {node: '>= 0.4'} @@ -1298,6 +1560,12 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + before-after-hook@4.0.0: + resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} + + bottleneck@2.19.5: + resolution: {integrity: sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==} + brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} @@ -1335,10 +1603,22 @@ packages: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} + 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'} + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + check-error@2.1.1: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} @@ -1347,27 +1627,141 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} + clean-stack@2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + + clean-stack@5.3.0: + resolution: {integrity: sha512-9ngPTOhYGQqNVSfeJkYXHmF7AGWp4/nN5D/QqNQs3Dvxd1Kk/WpjHfNujKHYUQ/5CoGyOyFNoWSPk5afzP0QVg==} + engines: {node: '>=14.16'} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-highlight@2.1.11: + resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==} + engines: {node: '>=8.0.0', npm: '>=5.0.0'} + hasBin: true + + cli-table3@0.6.5: + resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} + engines: {node: 10.* || >= 12.*} + + cli-truncate@4.0.0: + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + engines: {node: '>=18'} + client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + 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==} + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + commander@13.1.0: + resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} + engines: {node: '>=18'} + + compare-func@2.0.0: + resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + + conventional-changelog-angular@7.0.0: + resolution: {integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==} + engines: {node: '>=16'} + + conventional-changelog-angular@8.1.0: + resolution: {integrity: sha512-GGf2Nipn1RUCAktxuVauVr1e3r8QrLP/B0lEUsFktmGqc3ddbQkhoJZHJctVU829U1c6mTSWftrVOCHaL85Q3w==} + engines: {node: '>=18'} + + conventional-changelog-conventionalcommits@7.0.2: + resolution: {integrity: sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==} + engines: {node: '>=16'} + + conventional-changelog-conventionalcommits@8.0.0: + resolution: {integrity: sha512-eOvlTO6OcySPyyyk8pKz2dP4jjElYunj9hn9/s0OB+gapTO8zwS9UQWrZ1pmF2hFs3vw1xhonOLGcGjy/zgsuA==} + engines: {node: '>=18'} + + conventional-changelog-writer@8.2.0: + resolution: {integrity: sha512-Y2aW4596l9AEvFJRwFGJGiQjt2sBYTjPD18DdvxX9Vpz0Z7HQ+g1Z+6iYDAm1vR3QOJrDBkRHixHK/+FhkR6Pw==} + engines: {node: '>=18'} + hasBin: true + + conventional-commits-filter@5.0.0: + resolution: {integrity: sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q==} + engines: {node: '>=18'} + + conventional-commits-parser@5.0.0: + resolution: {integrity: sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==} + engines: {node: '>=16'} + hasBin: true + + conventional-commits-parser@6.2.1: + resolution: {integrity: sha512-20pyHgnO40rvfI0NGF/xiEoFMkXDtkF8FwHvk5BokoFoCuTQRI8vrNCNFWUOfuolKJMm1tPCHc8GgYEtr1XRNA==} + engines: {node: '>=18'} + hasBin: true + + convert-hrtime@5.0.0: + resolution: {integrity: sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==} + engines: {node: '>=12'} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cosmiconfig-typescript-loader@6.2.0: + resolution: {integrity: sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==} + engines: {node: '>=v18'} + peerDependencies: + '@types/node': '*' + cosmiconfig: '>=9' + typescript: '>=5' + + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + crypto-random-string@4.0.0: + resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} + engines: {node: '>=12'} + css.escape@1.5.1: resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} @@ -1377,6 +1771,10 @@ packages: damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + dargs@8.1.0: + resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} + engines: {node: '>=12'} + data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} @@ -1413,6 +1811,10 @@ packages: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -1437,6 +1839,10 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -1447,17 +1853,48 @@ packages: dom-accessibility-api@0.6.3: resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + dot-prop@5.3.0: + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + duplexer2@0.1.4: + resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} + + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + emojilib@2.4.0: + resolution: {integrity: sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==} + enhanced-resolve@5.18.3: resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} engines: {node: '>=10.13.0'} + env-ci@11.2.0: + resolution: {integrity: sha512-D5kWfzkmaOQDioPmiviWAVtKmpPT4/iJmMVQxWxMPJTFyTkdc5JQUfc5iXEeWxcOdsYTKSAiA/Age4NUOqKsRA==} + engines: {node: ^18.17 || >=20.6.1} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + es-abstract@1.24.0: resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} engines: {node: '>= 0.4'} @@ -1498,10 +1935,22 @@ packages: engines: {node: '>=18'} hasBin: true + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + eslint-config-next@15.5.6: resolution: {integrity: sha512-cGr3VQlPsZBEv8rtYp4BpG1KNXDqGvPo9VC1iaCgIA11OfziC/vczng+TnAS3WpRIR3Q5ye/6yl+CRUuZ1fPGg==} peerDependencies: @@ -1621,10 +2070,28 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + + execa@9.6.0: + resolution: {integrity: sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==} + engines: {node: ^18.19.0 || >=20.5.0} + expect-type@1.2.2: resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} engines: {node: '>=12.0.0'} + fast-content-type-parse@3.0.0: + resolution: {integrity: sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -1642,6 +2109,9 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} @@ -1657,6 +2127,14 @@ packages: fflate@0.8.2: resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + figures@2.0.0: + resolution: {integrity: sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==} + engines: {node: '>=4'} + + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -1665,10 +2143,26 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + find-up-simple@1.0.1: + resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==} + engines: {node: '>=18'} + + find-up@2.1.0: + resolution: {integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==} + engines: {node: '>=4'} + find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} + find-up@7.0.0: + resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} + engines: {node: '>=18'} + + find-versions@6.0.0: + resolution: {integrity: sha512-2kCCtc+JvcZ86IGAz3Z2Y0A1baIz9fL31pH/0S1IqZr9Iwnjq8izfPtrCyQKO6TLMPELLsQMre7VDqeIKCsHkA==} + engines: {node: '>=18'} + flat-cache@4.0.1: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} @@ -1680,6 +2174,13 @@ packages: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} + from2@2.3.0: + resolution: {integrity: sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==} + + fs-extra@11.3.2: + resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==} + engines: {node: '>=14.14'} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1688,6 +2189,10 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + function-timeout@1.0.2: + resolution: {integrity: sha512-939eZS4gJ3htTHAldmyyuzlrD58P03fHG49v2JfFXbV6OhvZKRC9j2yAtdHw/zrp2zXHuv05zMIy40F0ge7spA==} + engines: {node: '>=18'} + function.prototype.name@1.1.8: resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} engines: {node: '>= 0.4'} @@ -1699,6 +2204,14 @@ packages: resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} engines: {node: '>= 0.4'} + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-east-asian-width@1.4.0: + resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} + engines: {node: '>=18'} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -1707,6 +2220,22 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + get-stream@7.0.1: + resolution: {integrity: sha512-3M8C1EOFN6r8AMUhwUAACIoXZJEOufDU5+0gFFN5uNs6XYOralD2Pqkl7m046va6x77FwposWXbAhPPIOus7mQ==} + engines: {node: '>=16'} + + get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + + get-stream@9.0.1: + resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} + engines: {node: '>=18'} + get-symbol-description@1.1.0: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} @@ -1714,6 +2243,14 @@ packages: get-tsconfig@4.13.0: resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} + git-log-parser@1.2.1: + resolution: {integrity: sha512-PI+sPDvHXNPl5WNOErAK05s3j0lgwUzMN6o8cyQrDaKfT3qd7TmNJKeXX+SknI5I0QhG5fVPAEwSY4tRGDtYoQ==} + + git-raw-commits@4.0.0: + resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==} + engines: {node: '>=16'} + hasBin: true + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -1722,6 +2259,10 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + global-directory@4.0.1: + resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} + engines: {node: '>=18'} + globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -1734,12 +2275,20 @@ packages: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} + graceful-fs@4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + handlebars@4.7.8: + resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + engines: {node: '>=0.4.7'} + hasBin: true + happy-dom@20.0.8: resolution: {integrity: sha512-TlYaNQNtzsZ97rNMBAm8U+e2cUQXNithgfCizkDgc11lgmN4j9CKMhO3FPGKWQYPwwkFcPpoXYF/CqEPLgzfOg==} engines: {node: '>=20.0.0'} @@ -1748,6 +2297,10 @@ packages: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} + 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'} @@ -1771,6 +2324,46 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + highlight.js@10.7.3: + resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + + hook-std@4.0.0: + resolution: {integrity: sha512-IHI4bEVOt3vRUDJ+bFA9VUJlo7SzvFARPNLw75pqSmAOP2HmTWfFJtPvLBrDrlgjEYXY9zs7SFdHPQaJShkSCQ==} + engines: {node: '>=20'} + + hosted-git-info@7.0.2: + resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} + engines: {node: ^16.14.0 || >=18.0.0} + + hosted-git-info@8.1.0: + resolution: {integrity: sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==} + engines: {node: ^18.17.0 || >=20.5.0} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + + human-signals@8.0.1: + resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} + engines: {node: '>=18.18.0'} + + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} + hasBin: true + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1789,6 +2382,13 @@ packages: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} + import-from-esm@2.0.0: + resolution: {integrity: sha512-YVt14UZCgsX1vZQ3gKjkWVdBdHQ6eu3MPU1TBgL1H5orXe2+jWD006WCPPtOuwlQm10NuzOW5WawiF1Q9veW8g==} + engines: {node: '>=18.20'} + + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -1797,14 +2397,39 @@ packages: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} + indent-string@5.0.0: + resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} + engines: {node: '>=12'} + + index-to-position@1.2.0: + resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==} + engines: {node: '>=18'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + ini@4.1.1: + resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} + into-stream@7.0.0: + resolution: {integrity: sha512-2dYz766i9HprMBasCMvHMuazJ7u4WzhJwo5kb3iPSiW/iRYV6uPari3zHoqZlnuaR7V1bEiNMxikhp37rdBXbw==} + engines: {node: '>=12'} + is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-async-function@2.1.1: resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} engines: {node: '>= 0.4'} @@ -1844,6 +2469,18 @@ packages: resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} engines: {node: '>= 0.4'} + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + + is-fullwidth-code-point@5.1.0: + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} + engines: {node: '>=18'} + is-generator-function@1.1.2: resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} engines: {node: '>= 0.4'} @@ -1868,6 +2505,14 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -1880,6 +2525,18 @@ packages: resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} engines: {node: '>= 0.4'} + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-stream@4.0.1: + resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} + engines: {node: '>=18'} + is-string@1.1.1: resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} engines: {node: '>= 0.4'} @@ -1888,10 +2545,18 @@ packages: resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} engines: {node: '>= 0.4'} + is-text-path@2.0.0: + resolution: {integrity: sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==} + engines: {node: '>=8'} + is-typed-array@1.1.15: resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} engines: {node: '>= 0.4'} + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} @@ -1904,16 +2569,27 @@ packages: resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} engines: {node: '>= 0.4'} + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + issue-parser@7.0.1: + resolution: {integrity: sha512-3YZcUUR2Wt1WsapF+S/WiA2WmlW0cWAoPccMqne7AxEBhCdFeTPjfv/Axb8V2gyCgY3nRw+ksZ3xSUX+R47iAg==} + engines: {node: ^18.17 || >=20.6.1} + iterator.prototype@1.1.5: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} + java-properties@1.0.2: + resolution: {integrity: sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==} + engines: {node: '>= 0.6.0'} + jiti@2.6.1: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true @@ -1931,9 +2607,18 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-parse-better-errors@1.0.2: + resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -1941,6 +2626,13 @@ packages: resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} hasBin: true + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + + jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} @@ -2029,16 +2721,90 @@ packages: resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} engines: {node: '>= 12.0.0'} + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + lint-staged@15.5.2: + resolution: {integrity: sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w==} + engines: {node: '>=18.12.0'} + hasBin: true + + listr2@8.3.3: + resolution: {integrity: sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==} + engines: {node: '>=18.0.0'} + + load-json-file@4.0.0: + resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} + engines: {node: '>=4'} + + locate-path@2.0.0: + resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} + engines: {node: '>=4'} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + lodash.capitalize@4.2.1: + resolution: {integrity: sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==} + lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + lodash.escaperegexp@4.1.2: + resolution: {integrity: sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + + lodash.kebabcase@4.1.1: + resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.mergewith@4.6.2: + resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} + + lodash.snakecase@4.1.1: + resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} + + lodash.startcase@4.4.0: + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + + lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + + lodash.uniqby@4.7.0: + resolution: {integrity: sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==} + + lodash.upperfirst@4.3.1: + resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -2046,6 +2812,9 @@ packages: loupe@3.2.1: resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lz-string@1.5.0: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true @@ -2053,10 +2822,32 @@ packages: magic-string@0.30.19: resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} + marked-terminal@7.3.0: + resolution: {integrity: sha512-t4rBvPsHc57uE/2nJOLmMbZCQ4tgAccAED3ngXQqW6g+TxA488JzJ+FK3lQkzBQOI1mRV/r/Kq+1ZlJ4D0owQw==} + engines: {node: '>=16.0.0'} + peerDependencies: + marked: '>=1 <16' + + marked@15.0.12: + resolution: {integrity: sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==} + engines: {node: '>= 18'} + hasBin: true + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + meow@12.1.1: + resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} + engines: {node: '>=16.10'} + + meow@13.2.0: + resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} + engines: {node: '>=18'} + + 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'} @@ -2065,6 +2856,23 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} + mime@4.1.0: + resolution: {integrity: sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==} + engines: {node: '>=16'} + hasBin: true + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + min-indent@1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} @@ -2086,6 +2894,9 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -2099,6 +2910,12 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + nerf-dart@1.0.0: + resolution: {integrity: sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==} + next@15.5.6: resolution: {integrity: sha512-zTxsnI3LQo3c9HSdSf91O1jMNsEzIXDShXd4wVdg9y5shwLqBXi4ZtUUJyB86KGVSJLZx0PFONvO54aheGX8QQ==} engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} @@ -2123,6 +2940,104 @@ packages: node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-emoji@2.2.0: + resolution: {integrity: sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==} + engines: {node: '>=18'} + + normalize-package-data@6.0.2: + resolution: {integrity: sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==} + engines: {node: ^16.14.0 || >=18.0.0} + + normalize-url@8.1.0: + resolution: {integrity: sha512-X06Mfd/5aKsRHc0O0J5CUedwnPmnDtLF2+nq+KN9KSDlJHkPuh0JUviWjEWMe0SW/9TDdSLVPuk7L5gGTIA1/w==} + engines: {node: '>=14.16'} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + npm-run-path@6.0.0: + resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} + engines: {node: '>=18'} + + npm@10.9.4: + resolution: {integrity: sha512-OnUG836FwboQIbqtefDNlyR0gTHzIfwRfE3DuiNewBvnMnWEpB0VEXwBlFVgqpNzIgYo/MHh3d2Hel/pszapAA==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + bundledDependencies: + - '@isaacs/string-locale-compare' + - '@npmcli/arborist' + - '@npmcli/config' + - '@npmcli/fs' + - '@npmcli/map-workspaces' + - '@npmcli/package-json' + - '@npmcli/promise-spawn' + - '@npmcli/redact' + - '@npmcli/run-script' + - '@sigstore/tuf' + - abbrev + - archy + - cacache + - chalk + - ci-info + - cli-columns + - fastest-levenshtein + - fs-minipass + - glob + - graceful-fs + - hosted-git-info + - ini + - init-package-json + - is-cidr + - json-parse-even-better-errors + - libnpmaccess + - libnpmdiff + - libnpmexec + - libnpmfund + - libnpmhook + - libnpmorg + - libnpmpack + - libnpmpublish + - libnpmsearch + - libnpmteam + - libnpmversion + - make-fetch-happen + - minimatch + - minipass + - minipass-pipeline + - ms + - node-gyp + - nopt + - normalize-package-data + - npm-audit-report + - npm-install-checks + - npm-package-arg + - npm-pick-manifest + - npm-profile + - npm-registry-fetch + - npm-user-validate + - p-map + - pacote + - parse-conflict-json + - proc-log + - qrcode-terminal + - read + - semver + - spdx-expression-parse + - ssri + - supports-color + - tar + - text-table + - tiny-relative-date + - treeverse + - validate-npm-package-name + - which + - write-file-atomic + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -2155,6 +3070,18 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -2163,29 +3090,114 @@ packages: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} + p-each-series@3.0.0: + resolution: {integrity: sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw==} + engines: {node: '>=12'} + + p-filter@4.1.0: + resolution: {integrity: sha512-37/tPdZ3oJwHaS3gNJdenCDB3Tz26i9sjhnguBtvN0vYlRIiDNnvTWkuh+0hETV9rLPdJ3rlL3yVOYPIAnM8rw==} + engines: {node: '>=18'} + + p-is-promise@3.0.0: + resolution: {integrity: sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==} + engines: {node: '>=8'} + + p-limit@1.3.0: + resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} + engines: {node: '>=4'} + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-locate@2.0.0: + resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==} + engines: {node: '>=4'} + p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} + p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-map@7.0.3: + resolution: {integrity: sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==} + engines: {node: '>=18'} + + p-reduce@2.1.0: + resolution: {integrity: sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==} + engines: {node: '>=8'} + + p-reduce@3.0.0: + resolution: {integrity: sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==} + engines: {node: '>=12'} + + p-try@1.0.0: + resolution: {integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==} + engines: {node: '>=4'} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse-json@4.0.0: + resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} + engines: {node: '>=4'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse-json@8.3.0: + resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==} + engines: {node: '>=18'} + + parse-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} + + parse5-htmlparser2-tree-adapter@6.0.1: + resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} + + parse5@5.1.1: + resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==} + + parse5@6.0.1: + resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} + + path-exists@3.0.0: + resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} + engines: {node: '>=4'} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + 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==} @@ -2204,6 +3216,19 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + pidtree@0.6.0: + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + engines: {node: '>=0.10'} + hasBin: true + + pify@3.0.0: + resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} + engines: {node: '>=4'} + + pkg-conf@2.1.0: + resolution: {integrity: sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==} + engines: {node: '>=4'} + possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -2284,9 +3309,19 @@ packages: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + pretty-ms@9.3.0: + resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} + engines: {node: '>=18'} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -2294,6 +3329,10 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + react-dom@19.2.0: resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==} peerDependencies: @@ -2320,6 +3359,17 @@ packages: resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} engines: {node: '>=0.10.0'} + read-package-up@11.0.0: + resolution: {integrity: sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==} + engines: {node: '>=18'} + + read-pkg@9.0.1: + resolution: {integrity: sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==} + engines: {node: '>=18'} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -2336,10 +3386,26 @@ packages: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} + registry-auth-token@5.1.0: + resolution: {integrity: sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==} + engines: {node: '>=14'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} @@ -2352,10 +3418,17 @@ packages: resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + rollup@4.52.5: resolution: {integrity: sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -2368,6 +3441,9 @@ packages: resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safe-push-apply@1.0.0: resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} engines: {node: '>= 0.4'} @@ -2384,6 +3460,20 @@ packages: scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + semantic-release@24.2.9: + resolution: {integrity: sha512-phCkJ6pjDi9ANdhuF5ElS10GGdAKY6R1Pvt9lT3SFhOwM4T7QZE7MLpBDbNruUx/Q3gFD92/UOFringGipRqZA==} + engines: {node: '>=20.8.1'} + hasBin: true + + semver-diff@5.0.0: + resolution: {integrity: sha512-0HbGtOm+S7T6NGQ/pxJSJipJvc4DK3FcRVMRkhsIwJDJ4Jcz5DQC1cPPzB5GhzyHjwttW878HaWQq46CkL3cqg==} + engines: {node: '>=12'} + deprecated: Deprecated as the semver package now supports this built-in. + + semver-regex@4.0.5: + resolution: {integrity: sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==} + engines: {node: '>=12'} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -2436,14 +3526,63 @@ packages: siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + signale@1.4.0: + resolution: {integrity: sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==} + engines: {node: '>=6'} + sirv@3.0.2: resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} engines: {node: '>=18'} + skin-tone@2.0.0: + resolution: {integrity: sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==} + engines: {node: '>=8'} + + slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + + slice-ansi@7.1.2: + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} + engines: {node: '>=18'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + spawn-error-forwarder@1.0.0: + resolution: {integrity: sha512-gRjMgK5uFjbCvdibeGJuy3I5OYz6VLoVdsOJdA6wV0WlfQVLFueoqMxwwYD9RODdgb6oUIvlRlsyFSiQkMKu0g==} + + spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + + spdx-license-ids@3.0.22: + resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} + + split2@1.0.0: + resolution: {integrity: sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg==} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + stable-hash@0.0.5: resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} @@ -2457,6 +3596,21 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} + stream-combiner2@1.1.1: + resolution: {integrity: sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + string.prototype.includes@2.0.1: resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} engines: {node: '>= 0.4'} @@ -2480,14 +3634,41 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} + engines: {node: '>=12'} + strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + + strip-final-newline@4.0.0: + resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} + engines: {node: '>=18'} + strip-indent@3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -2508,10 +3689,22 @@ packages: babel-plugin-macros: optional: true + super-regex@1.0.0: + resolution: {integrity: sha512-CY8u7DtbvucKuquCmOFEKhr9Besln7n9uN8eFbwcoGYWXOMW07u2o8njWaiXt11ylS3qoGF55pILjRmPlbodyg==} + engines: {node: '>=18'} + + 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-hyperlinks@3.2.0: + resolution: {integrity: sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==} + engines: {node: '>=14.18'} + supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -2533,12 +3726,44 @@ packages: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} + temp-dir@3.0.0: + resolution: {integrity: sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==} + engines: {node: '>=14.16'} + + tempy@3.1.0: + resolution: {integrity: sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==} + engines: {node: '>=14.16'} + + text-extensions@2.4.0: + resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==} + engines: {node: '>=8'} + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + through2@2.0.5: + resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + time-span@5.1.0: + resolution: {integrity: sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==} + engines: {node: '>=12'} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinyexec@1.0.1: + resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} + tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} @@ -2563,6 +3788,10 @@ packages: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} + traverse@0.6.8: + resolution: {integrity: sha512-aXJDbk6SnumuaZSANd21XAo15ucCDE38H4fkqiGsc3MhCK+wOlZvLP9cB/TvpHT0mOyWgC4Z8EwRlzqYSUzdsA==} + engines: {node: '>= 0.4'} + true-myth@9.2.0: resolution: {integrity: sha512-I9ITOjtmfXndwXYm0CHIPxoGcqfZyI5qBGtfUcQj6Hj7GWBE7JiMCdeSEPaQSo6mqgLatX2xXOoI3D0gnf3QDQ==} engines: {node: 18.* || >= 20.*} @@ -2583,6 +3812,18 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} + type-fest@1.4.0: + resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} + engines: {node: '>=10'} + + type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + type-fest@5.1.0: resolution: {integrity: sha512-wQ531tuWvB6oK+pchHIu5lHe5f5wpSCqB8Kf4dWQRbOYc9HTge7JL0G4Qd44bh6QuJCccIzL3bugb8GI0MwHrg==} engines: {node: '>=20'} @@ -2608,6 +3849,11 @@ packages: engines: {node: '>=14.17'} hasBin: true + uglify-js@3.19.3: + resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} + engines: {node: '>=0.8.0'} + hasBin: true + unbox-primitive@1.1.0: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} @@ -2618,12 +3864,39 @@ packages: undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + unicode-emoji-modifier-base@1.0.0: + resolution: {integrity: sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==} + engines: {node: '>=4'} + + unicorn-magic@0.1.0: + resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} + engines: {node: '>=18'} + + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + + unique-string@3.0.0: + resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} + engines: {node: '>=12'} + + universal-user-agent@7.0.3: + resolution: {integrity: sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + url-join@5.0.0: + resolution: {integrity: sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + use-sync-external-store@1.6.0: resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} peerDependencies: @@ -2635,6 +3908,12 @@ packages: peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + vite-node@3.2.4: resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -2742,10 +4021,58 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yaml@2.8.1: + resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + yocto-queue@1.2.1: + resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==} + engines: {node: '>=12.20'} + + yoctocolors@2.1.2: + resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} + engines: {node: '>=18'} + zod@4.1.12: resolution: {integrity: sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==} @@ -2765,6 +4092,119 @@ snapshots: '@babel/runtime@7.28.4': {} + '@colors/colors@1.5.0': + optional: true + + '@commitlint/cli@19.8.1(@types/node@24.9.1)(typescript@5.9.3)': + dependencies: + '@commitlint/format': 19.8.1 + '@commitlint/lint': 19.8.1 + '@commitlint/load': 19.8.1(@types/node@24.9.1)(typescript@5.9.3) + '@commitlint/read': 19.8.1 + '@commitlint/types': 19.8.1 + tinyexec: 1.0.1 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - typescript + + '@commitlint/config-conventional@19.8.1': + dependencies: + '@commitlint/types': 19.8.1 + conventional-changelog-conventionalcommits: 7.0.2 + + '@commitlint/config-validator@19.8.1': + dependencies: + '@commitlint/types': 19.8.1 + ajv: 8.17.1 + + '@commitlint/ensure@19.8.1': + dependencies: + '@commitlint/types': 19.8.1 + lodash.camelcase: 4.3.0 + lodash.kebabcase: 4.1.1 + lodash.snakecase: 4.1.1 + lodash.startcase: 4.4.0 + lodash.upperfirst: 4.3.1 + + '@commitlint/execute-rule@19.8.1': {} + + '@commitlint/format@19.8.1': + dependencies: + '@commitlint/types': 19.8.1 + chalk: 5.6.2 + + '@commitlint/is-ignored@19.8.1': + dependencies: + '@commitlint/types': 19.8.1 + semver: 7.7.3 + + '@commitlint/lint@19.8.1': + dependencies: + '@commitlint/is-ignored': 19.8.1 + '@commitlint/parse': 19.8.1 + '@commitlint/rules': 19.8.1 + '@commitlint/types': 19.8.1 + + '@commitlint/load@19.8.1(@types/node@24.9.1)(typescript@5.9.3)': + dependencies: + '@commitlint/config-validator': 19.8.1 + '@commitlint/execute-rule': 19.8.1 + '@commitlint/resolve-extends': 19.8.1 + '@commitlint/types': 19.8.1 + chalk: 5.6.2 + cosmiconfig: 9.0.0(typescript@5.9.3) + cosmiconfig-typescript-loader: 6.2.0(@types/node@24.9.1)(cosmiconfig@9.0.0(typescript@5.9.3))(typescript@5.9.3) + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + lodash.uniq: 4.5.0 + transitivePeerDependencies: + - '@types/node' + - typescript + + '@commitlint/message@19.8.1': {} + + '@commitlint/parse@19.8.1': + dependencies: + '@commitlint/types': 19.8.1 + conventional-changelog-angular: 7.0.0 + conventional-commits-parser: 5.0.0 + + '@commitlint/read@19.8.1': + dependencies: + '@commitlint/top-level': 19.8.1 + '@commitlint/types': 19.8.1 + git-raw-commits: 4.0.0 + minimist: 1.2.8 + tinyexec: 1.0.1 + + '@commitlint/resolve-extends@19.8.1': + dependencies: + '@commitlint/config-validator': 19.8.1 + '@commitlint/types': 19.8.1 + global-directory: 4.0.1 + import-meta-resolve: 4.2.0 + lodash.mergewith: 4.6.2 + resolve-from: 5.0.0 + + '@commitlint/rules@19.8.1': + dependencies: + '@commitlint/ensure': 19.8.1 + '@commitlint/message': 19.8.1 + '@commitlint/to-lines': 19.8.1 + '@commitlint/types': 19.8.1 + + '@commitlint/to-lines@19.8.1': {} + + '@commitlint/top-level@19.8.1': + dependencies: + find-up: 7.0.0 + + '@commitlint/types@19.8.1': + dependencies: + '@types/conventional-commits-parser': 5.0.1 + chalk: 5.6.2 + '@emnapi/core@1.6.0': dependencies: '@emnapi/wasi-threads': 1.1.0 @@ -3118,6 +4558,65 @@ snapshots: '@nolyfill/is-core-module@1.0.39': {} + '@octokit/auth-token@6.0.0': {} + + '@octokit/core@7.0.5': + dependencies: + '@octokit/auth-token': 6.0.0 + '@octokit/graphql': 9.0.2 + '@octokit/request': 10.0.5 + '@octokit/request-error': 7.0.1 + '@octokit/types': 15.0.1 + before-after-hook: 4.0.0 + universal-user-agent: 7.0.3 + + '@octokit/endpoint@11.0.1': + dependencies: + '@octokit/types': 15.0.1 + universal-user-agent: 7.0.3 + + '@octokit/graphql@9.0.2': + dependencies: + '@octokit/request': 10.0.5 + '@octokit/types': 15.0.1 + universal-user-agent: 7.0.3 + + '@octokit/openapi-types@26.0.0': {} + + '@octokit/plugin-paginate-rest@13.2.1(@octokit/core@7.0.5)': + dependencies: + '@octokit/core': 7.0.5 + '@octokit/types': 15.0.1 + + '@octokit/plugin-retry@8.0.2(@octokit/core@7.0.5)': + dependencies: + '@octokit/core': 7.0.5 + '@octokit/request-error': 7.0.1 + '@octokit/types': 15.0.1 + bottleneck: 2.19.5 + + '@octokit/plugin-throttling@11.0.2(@octokit/core@7.0.5)': + dependencies: + '@octokit/core': 7.0.5 + '@octokit/types': 15.0.1 + bottleneck: 2.19.5 + + '@octokit/request-error@7.0.1': + dependencies: + '@octokit/types': 15.0.1 + + '@octokit/request@10.0.5': + dependencies: + '@octokit/endpoint': 11.0.1 + '@octokit/request-error': 7.0.1 + '@octokit/types': 15.0.1 + fast-content-type-parse: 3.0.0 + universal-user-agent: 7.0.3 + + '@octokit/types@15.0.1': + dependencies: + '@octokit/openapi-types': 26.0.0 + '@parcel/watcher-android-arm64@2.5.1': optional: true @@ -3179,6 +4678,18 @@ snapshots: '@parcel/watcher-win32-x64': 2.5.1 optional: true + '@pnpm/config.env-replace@1.1.0': {} + + '@pnpm/network.ca-file@1.0.2': + dependencies: + graceful-fs: 4.2.10 + + '@pnpm/npm-conf@2.3.1': + dependencies: + '@pnpm/config.env-replace': 1.1.0 + '@pnpm/network.ca-file': 1.0.2 + config-chain: 1.1.13 + '@polka/url@1.0.0-next.29': {} '@react-aria/focus@3.21.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': @@ -3300,6 +4811,107 @@ snapshots: '@rushstack/eslint-patch@1.14.0': {} + '@sec-ant/readable-stream@0.4.1': {} + + '@semantic-release/changelog@6.0.3(semantic-release@24.2.9(typescript@5.9.3))': + dependencies: + '@semantic-release/error': 3.0.0 + aggregate-error: 3.1.0 + fs-extra: 11.3.2 + lodash: 4.17.21 + semantic-release: 24.2.9(typescript@5.9.3) + + '@semantic-release/commit-analyzer@13.0.1(semantic-release@24.2.9(typescript@5.9.3))': + dependencies: + conventional-changelog-angular: 8.1.0 + conventional-changelog-writer: 8.2.0 + conventional-commits-filter: 5.0.0 + conventional-commits-parser: 6.2.1 + debug: 4.4.3 + import-from-esm: 2.0.0 + lodash-es: 4.17.21 + micromatch: 4.0.8 + semantic-release: 24.2.9(typescript@5.9.3) + transitivePeerDependencies: + - supports-color + + '@semantic-release/error@3.0.0': {} + + '@semantic-release/error@4.0.0': {} + + '@semantic-release/git@10.0.1(semantic-release@24.2.9(typescript@5.9.3))': + dependencies: + '@semantic-release/error': 3.0.0 + aggregate-error: 3.1.0 + debug: 4.4.3 + dir-glob: 3.0.1 + execa: 5.1.1 + lodash: 4.17.21 + micromatch: 4.0.8 + p-reduce: 2.1.0 + semantic-release: 24.2.9(typescript@5.9.3) + transitivePeerDependencies: + - supports-color + + '@semantic-release/github@11.0.6(semantic-release@24.2.9(typescript@5.9.3))': + dependencies: + '@octokit/core': 7.0.5 + '@octokit/plugin-paginate-rest': 13.2.1(@octokit/core@7.0.5) + '@octokit/plugin-retry': 8.0.2(@octokit/core@7.0.5) + '@octokit/plugin-throttling': 11.0.2(@octokit/core@7.0.5) + '@semantic-release/error': 4.0.0 + aggregate-error: 5.0.0 + debug: 4.4.3 + dir-glob: 3.0.1 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + issue-parser: 7.0.1 + lodash-es: 4.17.21 + mime: 4.1.0 + p-filter: 4.1.0 + semantic-release: 24.2.9(typescript@5.9.3) + tinyglobby: 0.2.15 + url-join: 5.0.0 + transitivePeerDependencies: + - supports-color + + '@semantic-release/npm@12.0.2(semantic-release@24.2.9(typescript@5.9.3))': + dependencies: + '@semantic-release/error': 4.0.0 + aggregate-error: 5.0.0 + execa: 9.6.0 + fs-extra: 11.3.2 + lodash-es: 4.17.21 + nerf-dart: 1.0.0 + normalize-url: 8.1.0 + npm: 10.9.4 + rc: 1.2.8 + read-pkg: 9.0.1 + registry-auth-token: 5.1.0 + semantic-release: 24.2.9(typescript@5.9.3) + semver: 7.7.3 + tempy: 3.1.0 + + '@semantic-release/release-notes-generator@14.1.0(semantic-release@24.2.9(typescript@5.9.3))': + dependencies: + conventional-changelog-angular: 8.1.0 + conventional-changelog-writer: 8.2.0 + conventional-commits-filter: 5.0.0 + conventional-commits-parser: 6.2.1 + debug: 4.4.3 + get-stream: 7.0.1 + import-from-esm: 2.0.0 + into-stream: 7.0.0 + lodash-es: 4.17.21 + read-package-up: 11.0.0 + semantic-release: 24.2.9(typescript@5.9.3) + transitivePeerDependencies: + - supports-color + + '@sindresorhus/is@4.6.0': {} + + '@sindresorhus/merge-streams@4.0.0': {} + '@swc/helpers@0.5.15': dependencies: tslib: 2.8.1 @@ -3427,6 +5039,10 @@ snapshots: '@types/deep-eql': 4.0.2 assertion-error: 2.0.1 + '@types/conventional-commits-parser@5.0.1': + dependencies: + '@types/node': 24.9.1 + '@types/deep-eql@4.0.2': {} '@types/estree@1.0.8': {} @@ -3443,6 +5059,8 @@ snapshots: dependencies: undici-types: 7.16.0 + '@types/normalize-package-data@2.4.4': {} + '@types/react-dom@19.2.2(@types/react@19.2.2)': dependencies: '@types/react': 19.2.2 @@ -3613,13 +5231,13 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2))': + '@vitest/mocker@3.2.4(vite@7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(yaml@2.8.1))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.19 optionalDependencies: - vite: 7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2) + vite: 7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(yaml@2.8.1) '@vitest/pretty-format@3.2.4': dependencies: @@ -3650,7 +5268,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.15 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/node@24.9.1)(@vitest/ui@3.2.4)(happy-dom@20.0.8)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2) + vitest: 3.2.4(@types/node@24.9.1)(@vitest/ui@3.2.4)(happy-dom@20.0.8)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(yaml@2.8.1) '@vitest/utils@3.2.4': dependencies: @@ -3658,12 +5276,29 @@ snapshots: loupe: 3.2.1 tinyrainbow: 2.0.0 + JSONStream@1.3.5: + dependencies: + jsonparse: 1.3.1 + through: 2.3.8 + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 acorn@8.15.0: {} + agent-base@7.1.4: {} + + aggregate-error@3.1.0: + dependencies: + clean-stack: 2.2.0 + indent-string: 4.0.0 + + aggregate-error@5.0.0: + dependencies: + clean-stack: 5.3.0 + indent-string: 5.0.0 + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -3671,16 +5306,39 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-escapes@7.1.1: + dependencies: + environment: 1.1.0 + ansi-regex@5.0.1: {} + ansi-regex@6.2.2: {} + + 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.3: {} + + any-promise@1.3.0: {} + argparse@2.0.1: {} + argv-formatter@1.0.0: {} + aria-query@5.3.0: dependencies: dequal: 2.0.3 @@ -3692,6 +5350,8 @@ snapshots: call-bound: 1.0.4 is-array-buffer: 3.0.5 + array-ify@1.0.0: {} + array-includes@3.1.9: dependencies: call-bind: 1.0.8 @@ -3770,6 +5430,10 @@ snapshots: balanced-match@1.0.2: {} + before-after-hook@4.0.0: {} + + bottleneck@2.19.5: {} + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 @@ -3814,41 +5478,175 @@ snapshots: loupe: 3.2.1 pathval: 2.0.1 + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 + chalk@5.6.2: {} + + char-regex@1.0.2: {} + check-error@2.1.1: {} chokidar@4.0.3: dependencies: readdirp: 4.1.2 + clean-stack@2.2.0: {} + + clean-stack@5.3.0: + dependencies: + escape-string-regexp: 5.0.0 + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-highlight@2.1.11: + dependencies: + chalk: 4.1.2 + highlight.js: 10.7.3 + mz: 2.7.0 + parse5: 5.1.1 + parse5-htmlparser2-tree-adapter: 6.0.1 + yargs: 16.2.0 + + cli-table3@0.6.5: + dependencies: + string-width: 4.2.3 + optionalDependencies: + '@colors/colors': 1.5.0 + + cli-truncate@4.0.0: + dependencies: + slice-ansi: 5.0.0 + string-width: 7.2.0 + client-only@0.0.1: {} + cliui@7.0.4: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + clsx@2.1.1: {} + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + color-convert@2.0.1: dependencies: color-name: 1.1.4 + color-name@1.1.3: {} + color-name@1.1.4: {} + colorette@2.0.20: {} + + commander@13.1.0: {} + + compare-func@2.0.0: + dependencies: + array-ify: 1.0.0 + dot-prop: 5.3.0 + concat-map@0.0.1: {} + config-chain@1.1.13: + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + + conventional-changelog-angular@7.0.0: + dependencies: + compare-func: 2.0.0 + + conventional-changelog-angular@8.1.0: + dependencies: + compare-func: 2.0.0 + + conventional-changelog-conventionalcommits@7.0.2: + dependencies: + compare-func: 2.0.0 + + conventional-changelog-conventionalcommits@8.0.0: + dependencies: + compare-func: 2.0.0 + + conventional-changelog-writer@8.2.0: + dependencies: + conventional-commits-filter: 5.0.0 + handlebars: 4.7.8 + meow: 13.2.0 + semver: 7.7.3 + + conventional-commits-filter@5.0.0: {} + + conventional-commits-parser@5.0.0: + dependencies: + JSONStream: 1.3.5 + is-text-path: 2.0.0 + meow: 12.1.1 + split2: 4.2.0 + + conventional-commits-parser@6.2.1: + dependencies: + meow: 13.2.0 + + convert-hrtime@5.0.0: {} + + core-util-is@1.0.3: {} + + cosmiconfig-typescript-loader@6.2.0(@types/node@24.9.1)(cosmiconfig@9.0.0(typescript@5.9.3))(typescript@5.9.3): + dependencies: + '@types/node': 24.9.1 + cosmiconfig: 9.0.0(typescript@5.9.3) + jiti: 2.6.1 + typescript: 5.9.3 + + cosmiconfig@9.0.0(typescript@5.9.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.9.3 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 + crypto-random-string@4.0.0: + dependencies: + type-fest: 1.4.0 + css.escape@1.5.1: {} csstype@3.1.3: {} damerau-levenshtein@1.0.8: {} + dargs@8.1.0: {} + data-view-buffer@1.0.2: dependencies: call-bound: 1.0.4 @@ -3879,6 +5677,8 @@ snapshots: deep-eql@5.0.2: {} + deep-extend@0.6.0: {} + deep-is@0.1.4: {} define-data-property@1.1.4: @@ -3900,6 +5700,10 @@ snapshots: detect-libc@2.1.2: {} + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + doctrine@2.1.0: dependencies: esutils: 2.0.3 @@ -3908,19 +5712,46 @@ snapshots: dom-accessibility-api@0.6.3: {} + dot-prop@5.3.0: + dependencies: + is-obj: 2.0.0 + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 es-errors: 1.3.0 gopd: 1.2.0 + duplexer2@0.1.4: + dependencies: + readable-stream: 2.3.8 + + emoji-regex@10.6.0: {} + + emoji-regex@8.0.0: {} + emoji-regex@9.2.2: {} + emojilib@2.4.0: {} + enhanced-resolve@5.18.3: dependencies: graceful-fs: 4.2.11 tapable: 2.3.0 + env-ci@11.2.0: + dependencies: + execa: 8.0.1 + java-properties: 1.0.2 + + env-paths@2.2.1: {} + + environment@1.1.0: {} + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + es-abstract@1.24.0: dependencies: array-buffer-byte-length: 1.0.2 @@ -4053,8 +5884,14 @@ snapshots: '@esbuild/win32-ia32': 0.25.11 '@esbuild/win32-x64': 0.25.11 + escalade@3.2.0: {} + + escape-string-regexp@1.0.5: {} + escape-string-regexp@4.0.0: {} + escape-string-regexp@5.0.0: {} + eslint-config-next@15.5.6(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3): dependencies: '@next/eslint-plugin-next': 15.5.6 @@ -4255,8 +6092,51 @@ snapshots: esutils@2.0.3: {} + eventemitter3@5.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 + + execa@8.0.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + + execa@9.6.0: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + cross-spawn: 7.0.6 + figures: 6.1.0 + get-stream: 9.0.1 + human-signals: 8.0.1 + is-plain-obj: 4.1.0 + is-stream: 4.0.1 + npm-run-path: 6.0.0 + pretty-ms: 9.3.0 + signal-exit: 4.1.0 + strip-final-newline: 4.0.0 + yoctocolors: 2.1.2 + expect-type@1.2.2: {} + fast-content-type-parse@3.0.0: {} + fast-deep-equal@3.1.3: {} fast-glob@3.3.1: @@ -4279,6 +6159,8 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-uri@3.1.0: {} + fastq@1.19.1: dependencies: reusify: 1.1.0 @@ -4289,6 +6171,14 @@ snapshots: fflate@0.8.2: {} + figures@2.0.0: + dependencies: + escape-string-regexp: 1.0.5 + + figures@6.1.0: + dependencies: + is-unicode-supported: 2.1.0 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -4297,11 +6187,28 @@ snapshots: dependencies: to-regex-range: 5.0.1 + find-up-simple@1.0.1: {} + + find-up@2.1.0: + dependencies: + locate-path: 2.0.0 + find-up@5.0.0: dependencies: locate-path: 6.0.0 path-exists: 4.0.0 + find-up@7.0.0: + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + unicorn-magic: 0.1.0 + + find-versions@6.0.0: + dependencies: + semver-regex: 4.0.5 + super-regex: 1.0.0 + flat-cache@4.0.1: dependencies: flatted: 3.3.3 @@ -4313,11 +6220,24 @@ snapshots: dependencies: is-callable: 1.2.7 + from2@2.3.0: + dependencies: + inherits: 2.0.4 + readable-stream: 2.3.8 + + fs-extra@11.3.2: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + fsevents@2.3.3: optional: true function-bind@1.1.2: {} + function-timeout@1.0.2: {} + function.prototype.name@1.1.8: dependencies: call-bind: 1.0.8 @@ -4331,6 +6251,10 @@ snapshots: generator-function@2.0.1: {} + get-caller-file@2.0.5: {} + + get-east-asian-width@1.4.0: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -4349,6 +6273,17 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 + get-stream@6.0.1: {} + + get-stream@7.0.1: {} + + get-stream@8.0.1: {} + + get-stream@9.0.1: + dependencies: + '@sec-ant/readable-stream': 0.4.1 + is-stream: 4.0.1 + get-symbol-description@1.1.0: dependencies: call-bound: 1.0.4 @@ -4359,6 +6294,21 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + git-log-parser@1.2.1: + dependencies: + argv-formatter: 1.0.0 + spawn-error-forwarder: 1.0.0 + split2: 1.0.0 + stream-combiner2: 1.1.1 + through2: 2.0.5 + traverse: 0.6.8 + + git-raw-commits@4.0.0: + dependencies: + dargs: 8.1.0 + meow: 12.1.1 + split2: 4.2.0 + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -4367,6 +6317,10 @@ snapshots: dependencies: is-glob: 4.0.3 + global-directory@4.0.1: + dependencies: + ini: 4.1.1 + globals@14.0.0: {} globalthis@1.0.4: @@ -4376,10 +6330,21 @@ snapshots: gopd@1.2.0: {} + graceful-fs@4.2.10: {} + graceful-fs@4.2.11: {} graphemer@1.4.0: {} + handlebars@4.7.8: + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.19.3 + happy-dom@20.0.8: dependencies: '@types/node': 20.19.23 @@ -4388,6 +6353,8 @@ snapshots: has-bigints@1.1.0: {} + has-flag@3.0.0: {} + has-flag@4.0.0: {} has-property-descriptors@1.0.2: @@ -4408,6 +6375,40 @@ snapshots: dependencies: function-bind: 1.1.2 + highlight.js@10.7.3: {} + + hook-std@4.0.0: {} + + hosted-git-info@7.0.2: + dependencies: + lru-cache: 10.4.3 + + hosted-git-info@8.1.0: + dependencies: + lru-cache: 10.4.3 + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + human-signals@2.1.0: {} + + human-signals@5.0.0: {} + + human-signals@8.0.1: {} + + husky@9.1.7: {} + ignore@5.3.2: {} ignore@7.0.5: {} @@ -4421,22 +6422,48 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 + import-from-esm@2.0.0: + dependencies: + debug: 4.4.3 + import-meta-resolve: 4.2.0 + transitivePeerDependencies: + - supports-color + + import-meta-resolve@4.2.0: {} + imurmurhash@0.1.4: {} indent-string@4.0.0: {} + indent-string@5.0.0: {} + + index-to-position@1.2.0: {} + + inherits@2.0.4: {} + + ini@1.3.8: {} + + ini@4.1.1: {} + internal-slot@1.1.0: dependencies: es-errors: 1.3.0 hasown: 2.0.2 side-channel: 1.1.0 + into-stream@7.0.0: + dependencies: + from2: 2.3.0 + p-is-promise: 3.0.0 + is-array-buffer@3.0.5: dependencies: call-bind: 1.0.8 call-bound: 1.0.4 get-intrinsic: 1.3.0 + is-arrayish@0.2.1: {} + is-async-function@2.1.1: dependencies: async-function: 1.0.0 @@ -4481,6 +6508,14 @@ snapshots: dependencies: call-bound: 1.0.4 + is-fullwidth-code-point@3.0.0: {} + + is-fullwidth-code-point@4.0.0: {} + + is-fullwidth-code-point@5.1.0: + dependencies: + get-east-asian-width: 1.4.0 + is-generator-function@1.1.2: dependencies: call-bound: 1.0.4 @@ -4504,6 +6539,10 @@ snapshots: is-number@7.0.0: {} + is-obj@2.0.0: {} + + is-plain-obj@4.1.0: {} + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -4517,6 +6556,12 @@ snapshots: dependencies: call-bound: 1.0.4 + is-stream@2.0.1: {} + + is-stream@3.0.0: {} + + is-stream@4.0.1: {} + is-string@1.1.1: dependencies: call-bound: 1.0.4 @@ -4528,10 +6573,16 @@ snapshots: has-symbols: 1.1.0 safe-regex-test: 1.1.0 + is-text-path@2.0.0: + dependencies: + text-extensions: 2.4.0 + is-typed-array@1.1.15: dependencies: which-typed-array: 1.1.19 + is-unicode-supported@2.1.0: {} + is-weakmap@2.0.2: {} is-weakref@1.1.1: @@ -4543,10 +6594,20 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 + isarray@1.0.0: {} + isarray@2.0.5: {} isexe@2.0.0: {} + issue-parser@7.0.1: + dependencies: + lodash.capitalize: 4.2.1 + lodash.escaperegexp: 4.1.2 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.uniqby: 4.7.0 + iterator.prototype@1.1.5: dependencies: define-data-property: 1.1.4 @@ -4556,6 +6617,8 @@ snapshots: has-symbols: 1.1.0 set-function-name: 2.0.2 + java-properties@1.0.2: {} + jiti@2.6.1: {} js-tokens@4.0.0: {} @@ -4568,14 +6631,28 @@ snapshots: json-buffer@3.0.1: {} + json-parse-better-errors@1.0.2: {} + + json-parse-even-better-errors@2.3.1: {} + json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} json5@1.0.2: dependencies: minimist: 1.2.8 + jsonfile@6.2.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jsonparse@1.3.1: {} + jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.9 @@ -4647,28 +6724,129 @@ snapshots: lightningcss-win32-arm64-msvc: 1.30.2 lightningcss-win32-x64-msvc: 1.30.2 + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + lint-staged@15.5.2: + dependencies: + chalk: 5.6.2 + commander: 13.1.0 + debug: 4.4.3 + execa: 8.0.1 + lilconfig: 3.1.3 + listr2: 8.3.3 + micromatch: 4.0.8 + pidtree: 0.6.0 + string-argv: 0.3.2 + yaml: 2.8.1 + transitivePeerDependencies: + - supports-color + + listr2@8.3.3: + dependencies: + cli-truncate: 4.0.0 + colorette: 2.0.20 + eventemitter3: 5.0.1 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.2 + + load-json-file@4.0.0: + dependencies: + graceful-fs: 4.2.11 + parse-json: 4.0.0 + pify: 3.0.0 + strip-bom: 3.0.0 + + locate-path@2.0.0: + dependencies: + p-locate: 2.0.0 + path-exists: 3.0.0 + locate-path@6.0.0: dependencies: p-locate: 5.0.0 + locate-path@7.2.0: + dependencies: + p-locate: 6.0.0 + + lodash-es@4.17.21: {} + + lodash.camelcase@4.3.0: {} + + lodash.capitalize@4.2.1: {} + lodash.debounce@4.0.8: {} + lodash.escaperegexp@4.1.2: {} + + lodash.isplainobject@4.0.6: {} + + lodash.isstring@4.0.1: {} + + lodash.kebabcase@4.1.1: {} + lodash.merge@4.6.2: {} + lodash.mergewith@4.6.2: {} + + lodash.snakecase@4.1.1: {} + + lodash.startcase@4.4.0: {} + + lodash.uniq@4.5.0: {} + + lodash.uniqby@4.7.0: {} + + lodash.upperfirst@4.3.1: {} + + lodash@4.17.21: {} + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.1.1 + cli-cursor: 5.0.0 + slice-ansi: 7.1.2 + strip-ansi: 7.1.2 + wrap-ansi: 9.0.2 + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 loupe@3.2.1: {} + lru-cache@10.4.3: {} + lz-string@1.5.0: {} magic-string@0.30.19: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + marked-terminal@7.3.0(marked@15.0.12): + dependencies: + ansi-escapes: 7.1.1 + ansi-regex: 6.2.2 + chalk: 5.6.2 + cli-highlight: 2.1.11 + cli-table3: 0.6.5 + marked: 15.0.12 + node-emoji: 2.2.0 + supports-hyperlinks: 3.2.0 + + marked@15.0.12: {} + math-intrinsics@1.1.0: {} + meow@12.1.1: {} + + meow@13.2.0: {} + + merge-stream@2.0.0: {} + merge2@1.4.1: {} micromatch@4.0.8: @@ -4676,6 +6854,14 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + mime@4.1.0: {} + + mimic-fn@2.1.0: {} + + mimic-fn@4.0.0: {} + + mimic-function@5.0.1: {} + min-indent@1.0.1: {} minimatch@3.1.2: @@ -4692,12 +6878,22 @@ snapshots: ms@2.1.3: {} + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + nanoid@3.3.11: {} napi-postinstall@0.3.4: {} natural-compare@1.4.0: {} + neo-async@2.6.2: {} + + nerf-dart@1.0.0: {} + next@15.5.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.93.2): dependencies: '@next/env': 15.5.6 @@ -4725,6 +6921,36 @@ snapshots: node-addon-api@7.1.1: optional: true + node-emoji@2.2.0: + dependencies: + '@sindresorhus/is': 4.6.0 + char-regex: 1.0.2 + emojilib: 2.4.0 + skin-tone: 2.0.0 + + normalize-package-data@6.0.2: + dependencies: + hosted-git-info: 7.0.2 + semver: 7.7.3 + validate-npm-package-license: 3.0.4 + + normalize-url@8.1.0: {} + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + npm-run-path@5.3.0: + dependencies: + path-key: 4.0.0 + + npm-run-path@6.0.0: + dependencies: + path-key: 4.0.0 + unicorn-magic: 0.3.0 + + npm@10.9.4: {} + object-assign@4.1.1: {} object-inspect@1.13.4: {} @@ -4767,6 +6993,18 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + onetime@6.0.0: + dependencies: + mimic-fn: 4.0.0 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -4782,24 +7020,92 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 + p-each-series@3.0.0: {} + + p-filter@4.1.0: + dependencies: + p-map: 7.0.3 + + p-is-promise@3.0.0: {} + + p-limit@1.3.0: + dependencies: + p-try: 1.0.0 + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 + p-limit@4.0.0: + dependencies: + yocto-queue: 1.2.1 + + p-locate@2.0.0: + dependencies: + p-limit: 1.3.0 + p-locate@5.0.0: dependencies: p-limit: 3.1.0 + p-locate@6.0.0: + dependencies: + p-limit: 4.0.0 + + p-map@7.0.3: {} + + p-reduce@2.1.0: {} + + p-reduce@3.0.0: {} + + p-try@1.0.0: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 + parse-json@4.0.0: + dependencies: + error-ex: 1.3.4 + json-parse-better-errors: 1.0.2 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse-json@8.3.0: + dependencies: + '@babel/code-frame': 7.27.1 + index-to-position: 1.2.0 + type-fest: 4.41.0 + + parse-ms@4.0.0: {} + + parse5-htmlparser2-tree-adapter@6.0.1: + dependencies: + parse5: 6.0.1 + + parse5@5.1.1: {} + + parse5@6.0.1: {} + + path-exists@3.0.0: {} + path-exists@4.0.0: {} + path-exists@5.0.0: {} + path-key@3.1.1: {} + path-key@4.0.0: {} + path-parse@1.0.7: {} + path-type@4.0.0: {} + pathe@2.0.3: {} pathval@2.0.1: {} @@ -4810,6 +7116,15 @@ snapshots: picomatch@4.0.3: {} + pidtree@0.6.0: {} + + pify@3.0.0: {} + + pkg-conf@2.1.0: + dependencies: + find-up: 2.1.0 + load-json-file: 4.0.0 + possible-typed-array-names@1.1.0: {} postcss@8.4.31: @@ -4838,16 +7153,31 @@ snapshots: ansi-styles: 5.2.0 react-is: 17.0.2 + pretty-ms@9.3.0: + dependencies: + parse-ms: 4.0.0 + + process-nextick-args@2.0.1: {} + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 object-assign: 4.1.1 react-is: 16.13.1 + proto-list@1.2.4: {} + punycode@2.3.1: {} queue-microtask@1.2.3: {} + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + react-dom@19.2.0(react@19.2.0): dependencies: react: 19.2.0 @@ -4867,6 +7197,30 @@ snapshots: react@19.2.0: {} + read-package-up@11.0.0: + dependencies: + find-up-simple: 1.0.1 + read-pkg: 9.0.1 + type-fest: 4.41.0 + + read-pkg@9.0.1: + dependencies: + '@types/normalize-package-data': 2.4.4 + normalize-package-data: 6.0.2 + parse-json: 8.3.0 + type-fest: 4.41.0 + unicorn-magic: 0.1.0 + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + readdirp@4.1.2: {} redent@3.0.0: @@ -4894,8 +7248,18 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 + registry-auth-token@5.1.0: + dependencies: + '@pnpm/npm-conf': 2.3.1 + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + resolve-from@4.0.0: {} + resolve-from@5.0.0: {} + resolve-pkg-maps@1.0.0: {} resolve@1.22.11: @@ -4910,8 +7274,15 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + reusify@1.1.0: {} + rfdc@1.4.1: {} + rollup@4.52.5: dependencies: '@types/estree': 1.0.8 @@ -4952,6 +7323,8 @@ snapshots: has-symbols: 1.1.0 isarray: 2.0.5 + safe-buffer@5.1.2: {} + safe-push-apply@1.0.0: dependencies: es-errors: 1.3.0 @@ -4973,6 +7346,47 @@ snapshots: scheduler@0.27.0: {} + semantic-release@24.2.9(typescript@5.9.3): + dependencies: + '@semantic-release/commit-analyzer': 13.0.1(semantic-release@24.2.9(typescript@5.9.3)) + '@semantic-release/error': 4.0.0 + '@semantic-release/github': 11.0.6(semantic-release@24.2.9(typescript@5.9.3)) + '@semantic-release/npm': 12.0.2(semantic-release@24.2.9(typescript@5.9.3)) + '@semantic-release/release-notes-generator': 14.1.0(semantic-release@24.2.9(typescript@5.9.3)) + aggregate-error: 5.0.0 + cosmiconfig: 9.0.0(typescript@5.9.3) + debug: 4.4.3 + env-ci: 11.2.0 + execa: 9.6.0 + figures: 6.1.0 + find-versions: 6.0.0 + get-stream: 6.0.1 + git-log-parser: 1.2.1 + hook-std: 4.0.0 + hosted-git-info: 8.1.0 + import-from-esm: 2.0.0 + lodash-es: 4.17.21 + marked: 15.0.12 + marked-terminal: 7.3.0(marked@15.0.12) + micromatch: 4.0.8 + p-each-series: 3.0.0 + p-reduce: 3.0.0 + read-package-up: 11.0.0 + resolve-from: 5.0.0 + semver: 7.7.3 + semver-diff: 5.0.0 + signale: 1.4.0 + yargs: 17.7.2 + transitivePeerDependencies: + - supports-color + - typescript + + semver-diff@5.0.0: + dependencies: + semver: 7.7.3 + + semver-regex@4.0.5: {} + semver@6.3.1: {} semver@7.7.3: {} @@ -5065,14 +7479,62 @@ snapshots: siginfo@2.0.0: {} + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + signale@1.4.0: + dependencies: + chalk: 2.4.2 + figures: 2.0.0 + pkg-conf: 2.1.0 + sirv@3.0.2: dependencies: '@polka/url': 1.0.0-next.29 mrmime: 2.0.1 totalist: 3.0.1 + skin-tone@2.0.0: + dependencies: + unicode-emoji-modifier-base: 1.0.0 + + slice-ansi@5.0.0: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 4.0.0 + + slice-ansi@7.1.2: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + source-map-js@1.2.1: {} + source-map@0.6.1: {} + + spawn-error-forwarder@1.0.0: {} + + spdx-correct@3.2.0: + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.22 + + spdx-exceptions@2.5.0: {} + + spdx-expression-parse@3.0.1: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.22 + + spdx-license-ids@3.0.22: {} + + split2@1.0.0: + dependencies: + through2: 2.0.5 + + split2@4.2.0: {} + stable-hash@0.0.5: {} stackback@0.0.2: {} @@ -5084,6 +7546,25 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 + stream-combiner2@1.1.1: + dependencies: + duplexer2: 0.1.4 + readable-stream: 2.3.8 + + string-argv@0.3.2: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.4.0 + strip-ansi: 7.1.2 + string.prototype.includes@2.0.1: dependencies: call-bind: 1.0.8 @@ -5134,12 +7615,32 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.2: + dependencies: + ansi-regex: 6.2.2 + strip-bom@3.0.0: {} + strip-final-newline@2.0.0: {} + + strip-final-newline@3.0.0: {} + + strip-final-newline@4.0.0: {} + strip-indent@3.0.0: dependencies: min-indent: 1.0.1 + strip-json-comments@2.0.1: {} + strip-json-comments@3.1.1: {} strip-literal@3.1.0: @@ -5151,10 +7652,24 @@ snapshots: client-only: 0.0.1 react: 19.2.0 + super-regex@1.0.0: + dependencies: + function-timeout: 1.0.2 + time-span: 5.1.0 + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + supports-color@7.2.0: dependencies: has-flag: 4.0.0 + supports-hyperlinks@3.2.0: + dependencies: + has-flag: 4.0.0 + supports-color: 7.2.0 + supports-preserve-symlinks-flag@1.0.0: {} tabbable@6.2.0: {} @@ -5167,10 +7682,42 @@ snapshots: tapable@2.3.0: {} + temp-dir@3.0.0: {} + + tempy@3.1.0: + dependencies: + is-stream: 3.0.0 + temp-dir: 3.0.0 + type-fest: 2.19.0 + unique-string: 3.0.0 + + text-extensions@2.4.0: {} + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + through2@2.0.5: + dependencies: + readable-stream: 2.3.8 + xtend: 4.0.2 + + through@2.3.8: {} + + time-span@5.1.0: + dependencies: + convert-hrtime: 5.0.0 + tinybench@2.9.0: {} tinyexec@0.3.2: {} + tinyexec@1.0.1: {} + tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) @@ -5188,6 +7735,8 @@ snapshots: totalist@3.0.1: {} + traverse@0.6.8: {} + true-myth@9.2.0: {} ts-api-utils@2.1.0(typescript@5.9.3): @@ -5207,6 +7756,12 @@ snapshots: dependencies: prelude-ls: 1.2.1 + type-fest@1.4.0: {} + + type-fest@2.19.0: {} + + type-fest@4.41.0: {} + type-fest@5.1.0: dependencies: tagged-tag: 1.0.0 @@ -5246,6 +7801,9 @@ snapshots: typescript@5.9.3: {} + uglify-js@3.19.3: + optional: true + unbox-primitive@1.1.0: dependencies: call-bound: 1.0.4 @@ -5257,6 +7815,20 @@ snapshots: undici-types@7.16.0: {} + unicode-emoji-modifier-base@1.0.0: {} + + unicorn-magic@0.1.0: {} + + unicorn-magic@0.3.0: {} + + unique-string@3.0.0: + dependencies: + crypto-random-string: 4.0.0 + + universal-user-agent@7.0.3: {} + + universalify@2.0.1: {} + unrs-resolver@1.11.1: dependencies: napi-postinstall: 0.3.4 @@ -5285,6 +7857,8 @@ snapshots: dependencies: punycode: 2.3.1 + url-join@5.0.0: {} + use-sync-external-store@1.6.0(react@19.2.0): dependencies: react: 19.2.0 @@ -5294,13 +7868,20 @@ snapshots: lodash.debounce: 4.0.8 react: 19.2.0 - vite-node@3.2.4(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2): + util-deprecate@1.0.2: {} + + validate-npm-package-license@3.0.4: + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + + vite-node@3.2.4(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(yaml@2.8.1): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2) + vite: 7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(yaml@2.8.1) transitivePeerDependencies: - '@types/node' - jiti @@ -5315,7 +7896,7 @@ snapshots: - tsx - yaml - vite@7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2): + vite@7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(yaml@2.8.1): dependencies: esbuild: 0.25.11 fdir: 6.5.0(picomatch@4.0.3) @@ -5329,12 +7910,13 @@ snapshots: jiti: 2.6.1 lightningcss: 1.30.2 sass: 1.93.2 + yaml: 2.8.1 - vitest@3.2.4(@types/node@24.9.1)(@vitest/ui@3.2.4)(happy-dom@20.0.8)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2): + vitest@3.2.4(@types/node@24.9.1)(@vitest/ui@3.2.4)(happy-dom@20.0.8)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(yaml@2.8.1): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)) + '@vitest/mocker': 3.2.4(vite@7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(yaml@2.8.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -5352,8 +7934,8 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2) - vite-node: 3.2.4(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2) + vite: 7.1.11(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(yaml@2.8.1) + vite-node: 3.2.4(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.93.2)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 24.9.1 @@ -5427,6 +8009,54 @@ snapshots: word-wrap@1.2.5: {} + wordwrap@1.0.0: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@9.0.2: + dependencies: + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.1.2 + + xtend@4.0.2: {} + + y18n@5.0.8: {} + + yaml@2.8.1: {} + + yargs-parser@20.2.9: {} + + yargs-parser@21.1.1: {} + + yargs@16.2.0: + dependencies: + cliui: 7.0.4 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + 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 + yocto-queue@0.1.0: {} + yocto-queue@1.2.1: {} + + yoctocolors@2.1.2: {} + zod@4.1.12: {} diff --git a/postcss.config.cjs b/postcss.config.cjs index e564072..769de0b 100644 --- a/postcss.config.cjs +++ b/postcss.config.cjs @@ -1,5 +1,5 @@ module.exports = { - plugins: { - '@tailwindcss/postcss': {}, - }, + plugins: { + "@tailwindcss/postcss": {}, + }, }; diff --git a/prettier.config.cjs b/prettier.config.cjs deleted file mode 100644 index 58b0aee..0000000 --- a/prettier.config.cjs +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import("prettier").Config} */ -module.exports = { - plugins: [require.resolve("prettier-plugin-tailwindcss")], -}; diff --git a/src/bootstrap.ts b/src/bootstrap.ts index 847bf05..4a2536b 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -11,25 +11,25 @@ * @returns {number} The relative position of the value to the range as -1, 0 or 1. */ function compareASN(value: number, range: string): number { - const [start, end] = range.split("-", 2) as [string, string]; - if (value < parseInt(start)) return -1; - if (value > parseInt(end)) return 1; - return 0; + const [start, end] = range.split("-", 2) as [string, string]; + if (value < parseInt(start)) return -1; + if (value > parseInt(end)) return 1; + return 0; } /** * Find the range in which a given ASN exists via binary search. If not found, -1 is used. */ export function findASN(asn: number, ranges: string[]) { - let start = 0; - let end = ranges.length - 1; + let start = 0; + let end = ranges.length - 1; - while (start <= end) { - const mid = Math.floor((start + end) / 2); - const comparison = compareASN(asn, ranges[mid] as string); - if (comparison == 0) return mid; // Success case - if (comparison == -1) end = mid - 1; - else start = mid + 1; - } - return -1; // Failure case + while (start <= end) { + const mid = Math.floor((start + end) / 2); + const comparison = compareASN(asn, ranges[mid] as string); + if (comparison == 0) return mid; // Success case + if (comparison == -1) end = mid - 1; + else start = mid + 1; + } + return -1; // Failure case } diff --git a/src/components/common/AbstractCard.tsx b/src/components/common/AbstractCard.tsx index 4e97a07..2db294b 100644 --- a/src/components/common/AbstractCard.tsx +++ b/src/components/common/AbstractCard.tsx @@ -2,104 +2,107 @@ import type { FunctionComponent, ReactNode } from "react"; import React from "react"; import { useBoolean } from "usehooks-ts"; import { - LinkIcon, - CodeBracketIcon, - DocumentArrowDownIcon, - ClipboardDocumentIcon, + LinkIcon, + CodeBracketIcon, + DocumentArrowDownIcon, + ClipboardDocumentIcon, } from "@heroicons/react/24/outline"; type AbstractCardProps = { - children?: ReactNode; - header?: ReactNode; - footer?: ReactNode; - data?: object; - url?: string; + children?: ReactNode; + header?: ReactNode; + footer?: ReactNode; + data?: object; + url?: string; }; const AbstractCard: FunctionComponent = ({ - url, - children, - header, - footer, - data, + url, + children, + header, + footer, + data, }) => { - const { value: showRaw, toggle: toggleRaw } = useBoolean(false); + const { value: showRaw, toggle: toggleRaw } = useBoolean(false); - return ( -
- {header != undefined || data != undefined ? ( -
-
{header}
- {url != undefined ? ( -
- - - -
- ) : null} - {data != undefined ? ( - <> -
- { - // stringify the JSON object, then begin the async clipboard write - navigator.clipboard - .writeText(JSON.stringify(data, null, 4)) - .then( - () => { - console.log("Copied to clipboard."); - }, - (err) => { - if (err instanceof Error) - console.error( - `Failed to copy to clipboard (${err.toString()}).` - ); - else console.error("Failed to copy to clipboard."); - } - ); - }} - className="h-6 w-6 cursor-pointer" - /> -
-
- { - const file = new Blob([JSON.stringify(data, null, 4)], { - type: "application/json", - }); + return ( +
+ {header != undefined || data != undefined ? ( +
+
{header}
+ {url != undefined ? ( +
+ + + +
+ ) : null} + {data != undefined ? ( + <> +
+ { + // stringify the JSON object, then begin the async clipboard write + navigator.clipboard + .writeText(JSON.stringify(data, null, 4)) + .then( + () => { + console.log("Copied to clipboard."); + }, + (err) => { + if (err instanceof Error) + console.error( + `Failed to copy to clipboard (${err.toString()}).` + ); + else + console.error( + "Failed to copy to clipboard." + ); + } + ); + }} + className="h-6 w-6 cursor-pointer" + /> +
+
+ { + const file = new Blob([JSON.stringify(data, null, 4)], { + type: "application/json", + }); - const anchor = document.createElement("a"); - anchor.href = URL.createObjectURL(file); - anchor.download = "response.json"; - anchor.click(); - }} - className="h-6 w-6 cursor-pointer" - /> -
-
- -
- - ) : null} -
- ) : null} -
- {showRaw ? ( -
-            {JSON.stringify(data, null, 4)}
-          
- ) : ( - children - )} -
- {footer != null ? ( -
{footer}
- ) : null} -
- ); + const anchor = document.createElement("a"); + anchor.href = URL.createObjectURL(file); + anchor.download = "response.json"; + anchor.click(); + }} + className="h-6 w-6 cursor-pointer" + /> +
+
+ +
+ + ) : null} +
+ ) : null} +
+ {showRaw ? ( +
+						{JSON.stringify(data, null, 4)}
+					
+ ) : ( + children + )} +
+ {footer != null ? ( +
{footer}
+ ) : null} +
+ ); }; export default AbstractCard; diff --git a/src/components/common/DynamicDate.tsx b/src/components/common/DynamicDate.tsx index ce666de..07276d3 100644 --- a/src/components/common/DynamicDate.tsx +++ b/src/components/common/DynamicDate.tsx @@ -4,8 +4,8 @@ import { format } from "date-fns"; import TimeAgo from "react-timeago"; type DynamicDateProps = { - value: Date | number; - absoluteFormat?: string; + value: Date | number; + absoluteFormat?: string; }; /** @@ -13,24 +13,21 @@ type DynamicDateProps = { * @param value The date to be displayed, the Date value, or * @param absoluteFormat Optional - the date-fns format string to use for the absolute date rendering. */ -const DynamicDate: FunctionComponent = ({ - value, - absoluteFormat, -}) => { - const { value: showAbsolute, toggle: toggleFormat } = useBoolean(true); +const DynamicDate: FunctionComponent = ({ value, absoluteFormat }) => { + const { value: showAbsolute, toggle: toggleFormat } = useBoolean(true); - const date = new Date(value); - return ( - - ); + const date = new Date(value); + return ( + + ); }; export default DynamicDate; diff --git a/src/components/common/ErrorCard.tsx b/src/components/common/ErrorCard.tsx index ae7058d..3ff37b0 100644 --- a/src/components/common/ErrorCard.tsx +++ b/src/components/common/ErrorCard.tsx @@ -3,49 +3,49 @@ import { XCircleIcon } from "@heroicons/react/20/solid"; import { cn } from "@/lib/utils"; export type ErrorCardProps = { - title: ReactNode; - description?: ReactNode; - issues?: ReactNode[]; - className?: string; + title: ReactNode; + description?: ReactNode; + issues?: ReactNode[]; + className?: string; }; const ErrorCard: FunctionComponent = ({ - title, - description, - issues, - className, + title, + description, + issues, + className, }) => { - return ( -
-
-
-
-
-

{title}

- {description != undefined ? ( -
- {description} -
- ) : null} -
- {issues != undefined ? ( -
    - {issues.map((issueText, index) => ( -
  • {issueText}
  • - ))} -
- ) : null} -
-
-
-
- ); + return ( +
+
+
+
+
+

{title}

+ {description != undefined ? ( +
+ {description} +
+ ) : null} +
+ {issues != undefined ? ( +
    + {issues.map((issueText, index) => ( +
  • {issueText}
  • + ))} +
+ ) : null} +
+
+
+
+ ); }; export default ErrorCard; diff --git a/src/components/common/Property.tsx b/src/components/common/Property.tsx index fa5c940..787b399 100644 --- a/src/components/common/Property.tsx +++ b/src/components/common/Property.tsx @@ -3,24 +3,24 @@ import React from "react"; import { cn } from "@/lib/utils"; type PropertyProps = { - title: string | ReactNode; - children: string | ReactNode; - titleClass?: string; - valueClass?: string; + title: string | ReactNode; + children: string | ReactNode; + titleClass?: string; + valueClass?: string; }; const Property: FunctionComponent = ({ - title, - children, - titleClass, - valueClass, + title, + children, + titleClass, + valueClass, }) => { - return ( - <> -
{title}:
-
{children}
- - ); + return ( + <> +
{title}:
+
{children}
+ + ); }; export default Property; diff --git a/src/components/common/PropertyList.tsx b/src/components/common/PropertyList.tsx index c56f0b2..f93c434 100644 --- a/src/components/common/PropertyList.tsx +++ b/src/components/common/PropertyList.tsx @@ -3,33 +3,33 @@ import React from "react"; import Property from "@/components/common/Property"; const PropertyListItem: FunctionComponent<{ - title: string; - children: string; + title: string; + children: string; }> = ({ title, children }) => { - return ( -
  • - - {children} - -
  • - ); + return ( +
  • + + {children} + +
  • + ); }; type PropertyListProps = { - title: string; - children: ReactNode; + title: string; + children: ReactNode; }; const PropertyList: FunctionComponent & { - Item: typeof PropertyListItem; + Item: typeof PropertyListItem; } = ({ title, children }) => { - return ( - -
      - {children} -
    -
    - ); + return ( + +
      + {children} +
    +
    + ); }; PropertyList.Item = PropertyListItem; diff --git a/src/components/form/LookupInput.tsx b/src/components/form/LookupInput.tsx index 0592d40..5503e96 100644 --- a/src/components/form/LookupInput.tsx +++ b/src/components/form/LookupInput.tsx @@ -4,18 +4,18 @@ import { Fragment, useState } from "react"; import { onPromise, preventDefault } from "@/helpers"; import type { SimplifiedTargetType, SubmitProps, TargetType } from "@/types"; import { - CheckIcon, - ChevronUpDownIcon, - LockClosedIcon, - MagnifyingGlassIcon, - ArrowPathIcon, + CheckIcon, + ChevronUpDownIcon, + LockClosedIcon, + MagnifyingGlassIcon, + ArrowPathIcon, } from "@heroicons/react/20/solid"; import { - Listbox, - ListboxButton, - ListboxOptions, - ListboxOption, - Transition, + Listbox, + ListboxButton, + ListboxOptions, + ListboxOption, + Transition, } from "@headlessui/react"; import { cn } from "@/lib/utils"; import type { Maybe } from "true-myth"; @@ -25,304 +25,289 @@ import { placeholders } from "@/constants"; * Props for the LookupInput component. */ type LookupInputProps = { - isLoading?: boolean; - /** - * Callback function called when a type of registry is detected when a user changes their input. - * @param type - The detected type of registry. - * @returns A promise. - */ - onRegistry?: (type: TargetType) => Promise; - /** - * Callback function called when a user hits submit. - * @param props - The submit props. - * @returns A promise. - */ - onSubmit?: (props: SubmitProps) => Promise; - /** - * Callback function called when a user changes their input (text search) or explicitly changes the type of search. - * @param target - The target object containing the search target and target type. - * @returns Nothing. - */ - onChange?: (target: { - target: string; - targetType: TargetType | null; - }) => Promise; - detectedType: Maybe; + isLoading?: boolean; + /** + * Callback function called when a type of registry is detected when a user changes their input. + * @param type - The detected type of registry. + * @returns A promise. + */ + onRegistry?: (type: TargetType) => Promise; + /** + * Callback function called when a user hits submit. + * @param props - The submit props. + * @returns A promise. + */ + onSubmit?: (props: SubmitProps) => Promise; + /** + * Callback function called when a user changes their input (text search) or explicitly changes the type of search. + * @param target - The target object containing the search target and target type. + * @returns Nothing. + */ + onChange?: (target: { target: string; targetType: TargetType | null }) => Promise; + detectedType: Maybe; }; const LookupInput: FunctionComponent = ({ - isLoading, - onSubmit, - onChange, - detectedType, + isLoading, + onSubmit, + onChange, + detectedType, }: LookupInputProps) => { - const { register, handleSubmit, getValues } = useForm({ - defaultValues: { - target: "", - // Not used at this time. - followReferral: false, - requestJSContact: false, - }, - }); + const { register, handleSubmit, getValues } = useForm({ + defaultValues: { + target: "", + // Not used at this time. + followReferral: false, + requestJSContact: false, + }, + }); - /** - * A mapping of available (simple) target types to their long-form human-readable names. - */ - const objectNames: Record = { - auto: "Autodetect", - domain: "Domain", - ip: "IP/CIDR", // IPv4/IPv6 are combined into this option - tld: "TLD", - autnum: "AS Number", - entity: "Entity Handle", - registrar: "Registrar", - url: "URL", - json: "JSON", - }; + /** + * A mapping of available (simple) target types to their long-form human-readable names. + */ + const objectNames: Record = { + auto: "Autodetect", + domain: "Domain", + ip: "IP/CIDR", // IPv4/IPv6 are combined into this option + tld: "TLD", + autnum: "AS Number", + entity: "Entity Handle", + registrar: "Registrar", + url: "URL", + json: "JSON", + }; - /** - * Mapping of precise target types to their simplified short-form names. - */ - const targetShortNames: Record = { - domain: "Domain", - tld: "TLD", - ip4: "IPv4", - ip6: "IPv6", - autnum: "ASN", - entity: "Entity", - registrar: "Registrar", - url: "URL", - json: "JSON", - }; + /** + * Mapping of precise target types to their simplified short-form names. + */ + const targetShortNames: Record = { + domain: "Domain", + tld: "TLD", + ip4: "IPv4", + ip6: "IPv6", + autnum: "ASN", + entity: "Entity", + registrar: "Registrar", + url: "URL", + json: "JSON", + }; - /** - * Represents the selected value in the LookupInput component. - */ - const [selected, setSelected] = useState( - "auto" - ); + /** + * Represents the selected value in the LookupInput component. + */ + const [selected, setSelected] = useState("auto"); - /** - * Retrieves the target type based on the provided value. - * @param value - The value to retrieve the target type for. - * @returns The target type as ObjectType or null. - */ - function retrieveTargetType(value?: string | null): TargetType | null { - // If the value is null and the selected value is null, return null. - if (value == null) value = selected; + /** + * Retrieves the target type based on the provided value. + * @param value - The value to retrieve the target type for. + * @returns The target type as ObjectType or null. + */ + function retrieveTargetType(value?: string | null): TargetType | null { + // If the value is null and the selected value is null, return null. + if (value == null) value = selected; - // 'auto' means 'do whatever' so we return null. - if (value == "auto") return null; + // 'auto' means 'do whatever' so we return null. + if (value == "auto") return null; - return value as TargetType; - } + return value as TargetType; + } - const searchIcon = ( - <> - - - ); + const searchIcon = ( + <> + + + ); - const searchInput = ( - { - if (onChange != undefined) - void onChange({ - target: getValues("target"), - // dropdown target will be pulled from state anyways, so no need to provide it here - targetType: retrieveTargetType(null), - }); - }, - })} - /> - ); + const searchInput = ( + { + if (onChange != undefined) + void onChange({ + target: getValues("target"), + // dropdown target will be pulled from state anyways, so no need to provide it here + targetType: retrieveTargetType(null), + }); + }, + })} + /> + ); - const dropdown = ( - { - setSelected(value); + const dropdown = ( + { + setSelected(value); - if (onChange != undefined) - void onChange({ - target: getValues("target"), - // we provide the value as the state will not have updated yet for this context - targetType: retrieveTargetType(value), - }); - }} - disabled={isLoading} - > -
    - - {/* Fetch special text for 'auto' mode, otherwise just use the options. */} - - {selected == "auto" ? ( - // If the detected type was provided, then notate which in parentheses. Compact object naming might be better in the future. - detectedType.isJust ? ( - <> - Auto ( - - {targetShortNames[detectedType.value]} - - ) - - ) : ( - objectNames["auto"] - ) - ) : ( - <> - - {objectNames[selected]} - - )} - - - - - - - {Object.entries(objectNames).map(([key, value]) => ( - - cn( - "relative cursor-default select-none py-2 pl-10 pr-4", - focus ? "bg-zinc-800 text-zinc-300" : null - ) - } - value={key} - > - {({ selected }) => ( - <> - - {value} - - {selected ? ( - - - ) : ( - - )} - - )} - - ))} - - -
    -
    - ); + if (onChange != undefined) + void onChange({ + target: getValues("target"), + // we provide the value as the state will not have updated yet for this context + targetType: retrieveTargetType(value), + }); + }} + disabled={isLoading} + > +
    + + {/* Fetch special text for 'auto' mode, otherwise just use the options. */} + + {selected == "auto" ? ( + // If the detected type was provided, then notate which in parentheses. Compact object naming might be better in the future. + detectedType.isJust ? ( + <> + Auto ( + + {targetShortNames[detectedType.value]} + + ) + + ) : ( + objectNames["auto"] + ) + ) : ( + <> + + {objectNames[selected]} + + )} + + + + + + + {Object.entries(objectNames).map(([key, value]) => ( + + cn( + "relative cursor-default py-2 pr-4 pl-10 select-none", + focus ? "bg-zinc-800 text-zinc-300" : null + ) + } + value={key} + > + {({ selected }) => ( + <> + + {value} + + {selected ? ( + + + ) : ( + + )} + + )} + + ))} + + +
    +
    + ); - return ( -
    -
    - -
    - {searchIcon} - {searchInput} - {dropdown} -
    -
    -
    -
    -
    - - -
    -
    - - -
    -
    -
    -
    - ); + return ( +
    +
    + +
    + {searchIcon} + {searchInput} + {dropdown} +
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    +
    +
    + ); }; export default LookupInput; diff --git a/src/components/lookup/AutnumCard.tsx b/src/components/lookup/AutnumCard.tsx index 7d6ace3..625ac47 100644 --- a/src/components/lookup/AutnumCard.tsx +++ b/src/components/lookup/AutnumCard.tsx @@ -7,54 +7,51 @@ import PropertyList from "@/components/common/PropertyList"; import AbstractCard from "@/components/common/AbstractCard"; export type AutnumCardProps = { - data: AutonomousNumber; - url?: string; + data: AutonomousNumber; + url?: string; }; -const AutnumCard: FunctionComponent = ({ - data, - url, -}: AutnumCardProps) => { - const asnRange = - data.startAutnum === data.endAutnum - ? `AS${data.startAutnum}` - : `AS${data.startAutnum}-AS${data.endAutnum}`; +const AutnumCard: FunctionComponent = ({ data, url }: AutnumCardProps) => { + const asnRange = + data.startAutnum === data.endAutnum + ? `AS${data.startAutnum}` + : `AS${data.startAutnum}-AS${data.endAutnum}`; - return ( - - AUTONOMOUS SYSTEM - {asnRange} - ({data.handle}) - - } - > -
    - {data.name} - {data.handle} - - {data.startAutnum === data.endAutnum - ? `AS${data.startAutnum}` - : `AS${data.startAutnum} - AS${data.endAutnum}`} - - {data.type} - {data.country.toUpperCase()} - - - - - {data.status.map((status, index) => ( - - {status} - - ))} - -
    -
    - ); + return ( + + AUTONOMOUS SYSTEM + {asnRange} + ({data.handle}) + + } + > +
    + {data.name} + {data.handle} + + {data.startAutnum === data.endAutnum + ? `AS${data.startAutnum}` + : `AS${data.startAutnum} - AS${data.endAutnum}`} + + {data.type} + {data.country.toUpperCase()} + + + + + {data.status.map((status, index) => ( + + {status} + + ))} + +
    +
    + ); }; export default AutnumCard; diff --git a/src/components/lookup/DomainCard.tsx b/src/components/lookup/DomainCard.tsx index f5bf26e..d36fe3c 100644 --- a/src/components/lookup/DomainCard.tsx +++ b/src/components/lookup/DomainCard.tsx @@ -8,49 +8,46 @@ import PropertyList from "@/components/common/PropertyList"; import AbstractCard from "@/components/common/AbstractCard"; export type DomainProps = { - data: Domain; - url?: string; + data: Domain; + url?: string; }; -const DomainCard: FunctionComponent = ({ - data, - url, -}: DomainProps) => { - return ( - - DOMAIN - - {data.ldhName ?? data.unicodeName} - - ({data.handle}) - - } - > -
    - {data.unicodeName != undefined ? ( - {data.unicodeName} - ) : null} - - {data.ldhName} - - {data.handle} - - - - - {data.status.map((statusKey, index) => ( - - {statusKey} - - ))} - -
    -
    - ); +const DomainCard: FunctionComponent = ({ data, url }: DomainProps) => { + return ( + + DOMAIN + + {data.ldhName ?? data.unicodeName} + + ({data.handle}) + + } + > +
    + {data.unicodeName != undefined ? ( + {data.unicodeName} + ) : null} + + {data.ldhName} + + {data.handle} + + + + + {data.status.map((statusKey, index) => ( + + {statusKey} + + ))} + +
    +
    + ); }; export default DomainCard; diff --git a/src/components/lookup/Events.tsx b/src/components/lookup/Events.tsx index 47dfe15..6bc5792 100644 --- a/src/components/lookup/Events.tsx +++ b/src/components/lookup/Events.tsx @@ -4,24 +4,24 @@ import { Fragment } from "react"; import DynamicDate from "@/components/common/DynamicDate"; export type EventsProps = { - data: Event[]; + data: Event[]; }; const Events: FunctionComponent = ({ data }) => { - return ( -
    - {data.map(({ eventAction, eventDate, eventActor }, index) => { - return ( - -
    {eventAction}:
    -
    - - {eventActor != null ? ` (by ${eventActor})` : null} -
    -
    - ); - })} -
    - ); + return ( +
    + {data.map(({ eventAction, eventDate, eventActor }, index) => { + return ( + +
    {eventAction}:
    +
    + + {eventActor != null ? ` (by ${eventActor})` : null} +
    +
    + ); + })} +
    + ); }; export default Events; diff --git a/src/components/lookup/Generic.tsx b/src/components/lookup/Generic.tsx index 5668b7b..cd2da7c 100644 --- a/src/components/lookup/Generic.tsx +++ b/src/components/lookup/Generic.tsx @@ -2,53 +2,39 @@ import type { FunctionComponent } from "react"; import DomainCard from "@/components/lookup/DomainCard"; import IPCard from "@/components/lookup/IPCard"; import AutnumCard from "@/components/lookup/AutnumCard"; -import type { - Domain, - AutonomousNumber, - Entity, - Nameserver, - IpNetwork, -} from "@/types"; +import type { Domain, AutonomousNumber, Entity, Nameserver, IpNetwork } from "@/types"; import AbstractCard from "@/components/common/AbstractCard"; -export type ParsedGeneric = - | Domain - | Nameserver - | Entity - | AutonomousNumber - | IpNetwork; +export type ParsedGeneric = Domain | Nameserver | Entity | AutonomousNumber | IpNetwork; export type ObjectProps = { - data: ParsedGeneric; - url?: string; + data: ParsedGeneric; + url?: string; }; -const Generic: FunctionComponent = ({ - data, - url, -}: ObjectProps) => { - switch (data.objectClassName) { - case "domain": - return ; - case "ip network": - return ; - case "autnum": - return ; - case "entity": - case "nameserver": - default: - return ( - - Not implemented. (
    {data.objectClassName ?? "null"}
    ) -
    - ); - } +const Generic: FunctionComponent = ({ data, url }: ObjectProps) => { + switch (data.objectClassName) { + case "domain": + return ; + case "ip network": + return ; + case "autnum": + return ; + case "entity": + case "nameserver": + default: + return ( + + Not implemented. (
    {data.objectClassName ?? "null"}
    ) +
    + ); + } - // const title: string = (data.unicodeName ?? data.ldhName ?? data.handle)?.toUpperCase() ?? "Response"; - // return
    - //
    {title}
    - // {objectFragment} - //
    + // const title: string = (data.unicodeName ?? data.ldhName ?? data.handle)?.toUpperCase() ?? "Response"; + // return
    + //
    {title}
    + // {objectFragment} + //
    }; export default Generic; diff --git a/src/components/lookup/IPCard.tsx b/src/components/lookup/IPCard.tsx index 94e86d5..0c8db8e 100644 --- a/src/components/lookup/IPCard.tsx +++ b/src/components/lookup/IPCard.tsx @@ -7,50 +7,50 @@ import PropertyList from "@/components/common/PropertyList"; import AbstractCard from "@/components/common/AbstractCard"; export type IPCardProps = { - data: IpNetwork; - url?: string; + data: IpNetwork; + url?: string; }; const IPCard: FunctionComponent = ({ data, url }: IPCardProps) => { - return ( - - IP NETWORK - - {data.startAddress} - {data.startAddress !== data.endAddress && ` - ${data.endAddress}`} - - ({data.handle}) - - } - > -
    - {data.name} - {data.handle} - {data.ipVersion.toUpperCase()} - {data.startAddress} - {data.endAddress} - {data.type} - {data.country && {data.country}} - {data.parentHandle && ( - {data.parentHandle} - )} - - - - - {data.status.map((status, index) => ( - - {status} - - ))} - -
    -
    - ); + return ( + + IP NETWORK + + {data.startAddress} + {data.startAddress !== data.endAddress && ` - ${data.endAddress}`} + + ({data.handle}) + + } + > +
    + {data.name} + {data.handle} + {data.ipVersion.toUpperCase()} + {data.startAddress} + {data.endAddress} + {data.type} + {data.country && {data.country}} + {data.parentHandle && ( + {data.parentHandle} + )} + + + + + {data.status.map((status, index) => ( + + {status} + + ))} + +
    +
    + ); }; export default IPCard; diff --git a/src/constants.ts b/src/constants.ts index 2446904..f9555da 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,96 +1,88 @@ // see https://www.iana.org/assignments/rdap-json-values -import type { - RdapStatusType, - RootRegistryType, - SimplifiedTargetType, -} from "@/types"; +import type { RdapStatusType, RootRegistryType, SimplifiedTargetType } from "@/types"; export const rdapStatusInfo: Record = { - validated: - "Signifies that the data of the object instance has been found to be accurate. This type of status is usually found on entity object instances to note the validity of identifying contact information.", - "renew prohibited": - "Renewal or reregistration of the object instance is forbidden.", - "update prohibited": "Updates to the object instance are forbidden.", - "transfer prohibited": - "Transfers of the registration from one registrar to another are forbidden. This type of status normally applies to DNR domain names.", - "delete prohibited": - "Deletion of the registration of the object instance is forbidden. This type of status normally applies to DNR domain names.", - proxy: - "The registration of the object instance has been performed by a third party. This is most commonly applied to entities.", - private: - "The information of the object instance is not designated for public consumption. This is most commonly applied to entities.", - removed: - "Some of the information of the object instance has not been made available and has been removed. This is most commonly applied to entities.", - obscured: - "Some of the information of the object instance has been altered for the purposes of not readily revealing the actual information of the object instance. This is most commonly applied to entities.", - associated: - "The object instance is associated with other object instances in the registry. This is most commonly used to signify that a nameserver is associated with a domain or that an entity is associated with a network resource or domain.", - active: - "The object instance is in use. For domain names, it signifies that the domain name is published in DNS. For network and autnum registrations it signifies that they are allocated or assigned for use in operational networks. This maps to the Extensible Provisioning Protocol (EPP) [RFC5730] 'OK' status.", - inactive: "The object instance is not in use. See 'active'.", - locked: - "Changes to the object instance cannot be made, including the association of other object instances.", - "pending create": - "A request has been received for the creation of the object instance but this action is not yet complete.", - "pending renew": - "A request has been received for the renewal of the object instance but this action is not yet complete.", - "pending transfer": - "A request has been received for the transfer of the object instance but this action is not yet complete.", - "pending update": - "A request has been received for the update or modification of the object instance but this action is not yet complete.", - "pending delete": - "A request has been received for the deletion or removal of the object instance but this action is not yet complete. For domains, this might mean that the name is no longer published in DNS but has not yet been purged from the registry database.", - "add period": - "This grace period is provided after the initial registration of the object. If the object is deleted by the client during this period, the server provides a credit to the client for the cost of the registration. This maps to the Domain Registry Grace Period Mapping for the Extensible Provisioning Protocol (EPP) [RFC3915] 'addPeriod' status.", - "auto renew period": - "This grace period is provided after an object registration period expires and is extended (renewed) automatically by the server. If the object is deleted by the client during this period, the server provides a credit to the client for the cost of the auto renewal. This maps to the Domain Registry Grace Period Mapping for the Extensible Provisioning Protocol (EPP) [RFC3915] 'autoRenewPeriod' status.", - "client delete prohibited": - "The client requested that requests to delete the object MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731], Extensible Provisioning Protocol (EPP) Host Mapping [RFC5732], and Extensible Provisioning Protocol (EPP) Contact Mapping [RFC5733] 'clientDeleteProhibited' status.", - "client hold": - "The client requested that the DNS delegation information MUST NOT be published for the object. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731] 'clientHold' status.", - "client renew prohibited": - "The client requested that requests to renew the object MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731] 'clientRenewProhibited' status.", - "client transfer prohibited": - "The client requested that requests to transfer the object MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731] and Extensible Provisioning Protocol (EPP) Contact Mapping [RFC5733] 'clientTransferProhibited' status.", - "client update prohibited": - "The client requested that requests to update the object (other than to remove this status) MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731], Extensible Provisioning Protocol (EPP) Host Mapping [RFC5732], and Extensible Provisioning Protocol (EPP) Contact Mapping [RFC5733] 'clientUpdateProhibited' status.", - "pending restore": - "An object is in the process of being restored after being in the redemption period state. This maps to the Domain Registry Grace Period Mapping for the Extensible Provisioning Protocol (EPP) [RFC3915] 'pendingRestore' status.", - "redemption period": - "A delete has been received, but the object has not yet been purged because an opportunity exists to restore the object and abort the deletion process. This maps to the Domain Registry Grace Period Mapping for the Extensible Provisioning Protocol (EPP) [RFC3915] 'redemptionPeriod' status.", - "renew period": - "This grace period is provided after an object registration period is explicitly extended (renewed) by the client. If the object is deleted by the client during this period, the server provides a credit to the client for the cost of the renewal. This maps to the Domain Registry Grace Period Mapping for the Extensible Provisioning Protocol (EPP) [RFC3915] 'renewPeriod' status.", - "server delete prohibited": - "The server set the status so that requests to delete the object MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731], Extensible Provisioning Protocol (EPP) Host Mapping [RFC5732], and Extensible Provisioning Protocol (EPP) Contact Mapping [RFC5733] 'serverDeleteProhibited' status.", - "server renew prohibited": - "The server set the status so that requests to renew the object MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731] 'serverRenewProhibited' status.", - "server transfer prohibited": - "The server set the status so that requests to transfer the object MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731] and Extensible Provisioning Protocol (EPP) Contact Mapping [RFC5733] 'serverTransferProhibited' status.", - "server update prohibited": - "The server set the status so that requests to update the object (other than to remove this status) MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731], Extensible Provisioning Protocol (EPP) Host Mapping [RFC5732], and Extensible Provisioning Protocol (EPP) Contact Mapping [RFC5733] 'serverUpdateProhibited' status.", - "server hold": - "The server set the status so that DNS delegation information MUST NOT be published for the object. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731] 'serverHold' status.", - "transfer period": - "This grace period is provided after the successful transfer of object registration sponsorship from one client to another client. If the object is deleted by the client during this period, the server provides a credit to the client for the cost of the transfer. This maps to the Domain Registry Grace Period Mapping for the Extensible Provisioning Protocol (EPP) [RFC3915] 'transferPeriod' status.", + validated: + "Signifies that the data of the object instance has been found to be accurate. This type of status is usually found on entity object instances to note the validity of identifying contact information.", + "renew prohibited": "Renewal or reregistration of the object instance is forbidden.", + "update prohibited": "Updates to the object instance are forbidden.", + "transfer prohibited": + "Transfers of the registration from one registrar to another are forbidden. This type of status normally applies to DNR domain names.", + "delete prohibited": + "Deletion of the registration of the object instance is forbidden. This type of status normally applies to DNR domain names.", + proxy: "The registration of the object instance has been performed by a third party. This is most commonly applied to entities.", + private: + "The information of the object instance is not designated for public consumption. This is most commonly applied to entities.", + removed: + "Some of the information of the object instance has not been made available and has been removed. This is most commonly applied to entities.", + obscured: + "Some of the information of the object instance has been altered for the purposes of not readily revealing the actual information of the object instance. This is most commonly applied to entities.", + associated: + "The object instance is associated with other object instances in the registry. This is most commonly used to signify that a nameserver is associated with a domain or that an entity is associated with a network resource or domain.", + active: "The object instance is in use. For domain names, it signifies that the domain name is published in DNS. For network and autnum registrations it signifies that they are allocated or assigned for use in operational networks. This maps to the Extensible Provisioning Protocol (EPP) [RFC5730] 'OK' status.", + inactive: "The object instance is not in use. See 'active'.", + locked: "Changes to the object instance cannot be made, including the association of other object instances.", + "pending create": + "A request has been received for the creation of the object instance but this action is not yet complete.", + "pending renew": + "A request has been received for the renewal of the object instance but this action is not yet complete.", + "pending transfer": + "A request has been received for the transfer of the object instance but this action is not yet complete.", + "pending update": + "A request has been received for the update or modification of the object instance but this action is not yet complete.", + "pending delete": + "A request has been received for the deletion or removal of the object instance but this action is not yet complete. For domains, this might mean that the name is no longer published in DNS but has not yet been purged from the registry database.", + "add period": + "This grace period is provided after the initial registration of the object. If the object is deleted by the client during this period, the server provides a credit to the client for the cost of the registration. This maps to the Domain Registry Grace Period Mapping for the Extensible Provisioning Protocol (EPP) [RFC3915] 'addPeriod' status.", + "auto renew period": + "This grace period is provided after an object registration period expires and is extended (renewed) automatically by the server. If the object is deleted by the client during this period, the server provides a credit to the client for the cost of the auto renewal. This maps to the Domain Registry Grace Period Mapping for the Extensible Provisioning Protocol (EPP) [RFC3915] 'autoRenewPeriod' status.", + "client delete prohibited": + "The client requested that requests to delete the object MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731], Extensible Provisioning Protocol (EPP) Host Mapping [RFC5732], and Extensible Provisioning Protocol (EPP) Contact Mapping [RFC5733] 'clientDeleteProhibited' status.", + "client hold": + "The client requested that the DNS delegation information MUST NOT be published for the object. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731] 'clientHold' status.", + "client renew prohibited": + "The client requested that requests to renew the object MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731] 'clientRenewProhibited' status.", + "client transfer prohibited": + "The client requested that requests to transfer the object MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731] and Extensible Provisioning Protocol (EPP) Contact Mapping [RFC5733] 'clientTransferProhibited' status.", + "client update prohibited": + "The client requested that requests to update the object (other than to remove this status) MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731], Extensible Provisioning Protocol (EPP) Host Mapping [RFC5732], and Extensible Provisioning Protocol (EPP) Contact Mapping [RFC5733] 'clientUpdateProhibited' status.", + "pending restore": + "An object is in the process of being restored after being in the redemption period state. This maps to the Domain Registry Grace Period Mapping for the Extensible Provisioning Protocol (EPP) [RFC3915] 'pendingRestore' status.", + "redemption period": + "A delete has been received, but the object has not yet been purged because an opportunity exists to restore the object and abort the deletion process. This maps to the Domain Registry Grace Period Mapping for the Extensible Provisioning Protocol (EPP) [RFC3915] 'redemptionPeriod' status.", + "renew period": + "This grace period is provided after an object registration period is explicitly extended (renewed) by the client. If the object is deleted by the client during this period, the server provides a credit to the client for the cost of the renewal. This maps to the Domain Registry Grace Period Mapping for the Extensible Provisioning Protocol (EPP) [RFC3915] 'renewPeriod' status.", + "server delete prohibited": + "The server set the status so that requests to delete the object MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731], Extensible Provisioning Protocol (EPP) Host Mapping [RFC5732], and Extensible Provisioning Protocol (EPP) Contact Mapping [RFC5733] 'serverDeleteProhibited' status.", + "server renew prohibited": + "The server set the status so that requests to renew the object MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731] 'serverRenewProhibited' status.", + "server transfer prohibited": + "The server set the status so that requests to transfer the object MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731] and Extensible Provisioning Protocol (EPP) Contact Mapping [RFC5733] 'serverTransferProhibited' status.", + "server update prohibited": + "The server set the status so that requests to update the object (other than to remove this status) MUST be rejected. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731], Extensible Provisioning Protocol (EPP) Host Mapping [RFC5732], and Extensible Provisioning Protocol (EPP) Contact Mapping [RFC5733] 'serverUpdateProhibited' status.", + "server hold": + "The server set the status so that DNS delegation information MUST NOT be published for the object. This maps to the Extensible Provisioning Protocol (EPP) Domain Name Mapping [RFC5731] 'serverHold' status.", + "transfer period": + "This grace period is provided after the successful transfer of object registration sponsorship from one client to another client. If the object is deleted by the client during this period, the server provides a credit to the client for the cost of the transfer. This maps to the Domain Registry Grace Period Mapping for the Extensible Provisioning Protocol (EPP) [RFC3915] 'transferPeriod' status.", }; // list of RDAP bootstrap registry URLs export const registryURLs: Record = { - autnum: "https://data.iana.org/rdap/asn.json", - domain: "https://data.iana.org/rdap/dns.json", - ip4: "https://data.iana.org/rdap/ipv4.json", - ip6: "https://data.iana.org/rdap/ipv6.json", - entity: "https://data.iana.org/rdap/object-tags.json", + autnum: "https://data.iana.org/rdap/asn.json", + domain: "https://data.iana.org/rdap/dns.json", + ip4: "https://data.iana.org/rdap/ipv4.json", + ip6: "https://data.iana.org/rdap/ipv6.json", + entity: "https://data.iana.org/rdap/object-tags.json", }; export const placeholders: Record = { - auto: "A domain, an IP address, a TLD, an RDAP URL...", - ip: "192.168.0.1/16 or 2001:db8::/32", - autnum: "AS27594", - entity: "OPS4-RIPE", - url: "https://rdap.org/domain/example.com", - tld: ".dev", - registrar: "9999", - json: `{"objectClassName":"domain", ... }`, - domain: "example.com", + auto: "A domain, an IP address, a TLD, an RDAP URL...", + ip: "192.168.0.1/16 or 2001:db8::/32", + autnum: "AS27594", + entity: "OPS4-RIPE", + url: "https://rdap.org/domain/example.com", + tld: ".dev", + registrar: "9999", + json: `{"objectClassName":"domain", ... }`, + domain: "example.com", }; diff --git a/src/env/client.mjs b/src/env/client.mjs index 76c967c..b1eadce 100644 --- a/src/env/client.mjs +++ b/src/env/client.mjs @@ -4,32 +4,32 @@ import { clientEnv, clientSchema } from "./schema.mjs"; const _clientEnv = clientSchema.safeParse(clientEnv); export const formatErrors = ( - /** @type {import('zod').ZodFormattedError,string>} */ - errors + /** @type {import('zod').ZodFormattedError,string>} */ + errors ) => - Object.entries(errors) - .map(([name, value]) => { - if (value && "_errors" in value) - return `${String(name)}: ${value._errors.join(", ")}\n`; - }) - .filter(Boolean); + Object.entries(errors) + .map(([name, value]) => { + if (value && "_errors" in value) + return `${String(name)}: ${value._errors.join(", ")}\n`; + }) + .filter(Boolean); if (!_clientEnv.success) { - console.error( - "❌ Invalid environment variables:\n", - ...formatErrors(_clientEnv.error.format()) - ); - throw new Error("Invalid environment variables"); + console.error( + "❌ Invalid environment variables:\n", + ...formatErrors(_clientEnv.error.format()) + ); + throw new Error("Invalid environment variables"); } for (let key of Object.keys(_clientEnv.data)) { - if (!key.startsWith("NEXT_PUBLIC_")) { - console.warn( - `❌ Invalid public environment variable name: ${key}. It must begin with 'NEXT_PUBLIC_'` - ); + if (!key.startsWith("NEXT_PUBLIC_")) { + console.warn( + `❌ Invalid public environment variable name: ${key}. It must begin with 'NEXT_PUBLIC_'` + ); - throw new Error("Invalid public environment variable name"); - } + throw new Error("Invalid public environment variable name"); + } } export const env = _clientEnv.data; diff --git a/src/env/schema.mjs b/src/env/schema.mjs index eee6cdf..40dc4ed 100644 --- a/src/env/schema.mjs +++ b/src/env/schema.mjs @@ -6,7 +6,7 @@ import { z } from "zod"; * This way you can ensure the app isn't built with invalid env vars. */ export const serverSchema = z.object({ - NODE_ENV: z.enum(["development", "test", "production"]), + NODE_ENV: z.enum(["development", "test", "production"]), }); /** @@ -15,7 +15,7 @@ export const serverSchema = z.object({ * @type {{ [k in keyof z.infer]: z.infer[k] | undefined }} */ export const serverEnv = { - NODE_ENV: process.env.NODE_ENV, + NODE_ENV: process.env.NODE_ENV, }; /** @@ -24,7 +24,7 @@ export const serverEnv = { * To expose them to the client, prefix them with `NEXT_PUBLIC_`. */ export const clientSchema = z.object({ - // NEXT_PUBLIC_CLIENTVAR: z.string(), + // NEXT_PUBLIC_CLIENTVAR: z.string(), }); /** @@ -34,5 +34,5 @@ export const clientSchema = z.object({ * @type {{ [k in keyof z.infer]: z.infer[k] | undefined }} */ export const clientEnv = { - // NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR, + // NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR, }; diff --git a/src/env/server.mjs b/src/env/server.mjs index c5d66cc..b4a3398 100644 --- a/src/env/server.mjs +++ b/src/env/server.mjs @@ -9,19 +9,19 @@ import { env as clientEnv, formatErrors } from "./client.mjs"; const _serverEnv = serverSchema.safeParse(serverEnv); if (!_serverEnv.success) { - console.error( - "❌ Invalid environment variables:\n", - ...formatErrors(_serverEnv.error.format()) - ); - throw new Error("Invalid environment variables"); + console.error( + "❌ Invalid environment variables:\n", + ...formatErrors(_serverEnv.error.format()) + ); + throw new Error("Invalid environment variables"); } for (let key of Object.keys(_serverEnv.data)) { - if (key.startsWith("NEXT_PUBLIC_")) { - console.warn("❌ You are exposing a server-side env-variable:", key); + if (key.startsWith("NEXT_PUBLIC_")) { + console.warn("❌ You are exposing a server-side env-variable:", key); - throw new Error("You are exposing a server-side env-variable"); - } + throw new Error("You are exposing a server-side env-variable"); + } } export const env = { ..._serverEnv.data, ...clientEnv }; diff --git a/src/helpers.asn.test.ts b/src/helpers.asn.test.ts index 60c2188..2a20ba7 100644 --- a/src/helpers.asn.test.ts +++ b/src/helpers.asn.test.ts @@ -2,143 +2,143 @@ import { describe, it, expect } from "vitest"; import { asnInRange } from "./helpers"; describe("asnInRange", () => { - describe("basic matching", () => { - it("should match ASN in single number range", () => { - expect(asnInRange(100, "100-200")).toBe(true); - expect(asnInRange(150, "100-200")).toBe(true); - expect(asnInRange(200, "100-200")).toBe(true); - }); + describe("basic matching", () => { + it("should match ASN in single number range", () => { + expect(asnInRange(100, "100-200")).toBe(true); + expect(asnInRange(150, "100-200")).toBe(true); + expect(asnInRange(200, "100-200")).toBe(true); + }); - it("should not match ASN outside single number range", () => { - expect(asnInRange(99, "100-200")).toBe(false); - expect(asnInRange(201, "100-200")).toBe(false); - }); + it("should not match ASN outside single number range", () => { + expect(asnInRange(99, "100-200")).toBe(false); + expect(asnInRange(201, "100-200")).toBe(false); + }); - it("should match ASN at boundaries", () => { - expect(asnInRange(1, "1-10")).toBe(true); - expect(asnInRange(10, "1-10")).toBe(true); - }); + it("should match ASN at boundaries", () => { + expect(asnInRange(1, "1-10")).toBe(true); + expect(asnInRange(10, "1-10")).toBe(true); + }); - it("should match single ASN (same start and end)", () => { - expect(asnInRange(12345, "12345-12345")).toBe(true); - }); + it("should match single ASN (same start and end)", () => { + expect(asnInRange(12345, "12345-12345")).toBe(true); + }); - it("should not match single ASN outside", () => { - expect(asnInRange(12346, "12345-12345")).toBe(false); - expect(asnInRange(12344, "12345-12345")).toBe(false); - }); - }); + it("should not match single ASN outside", () => { + expect(asnInRange(12346, "12345-12345")).toBe(false); + expect(asnInRange(12344, "12345-12345")).toBe(false); + }); + }); - describe("real-world ASN ranges from IANA", () => { - // ARIN ranges - it("should match ARIN ASN ranges", () => { - // ARIN typically has ranges like 1-1876, 1902-2042, etc. - expect(asnInRange(100, "1-1876")).toBe(true); - expect(asnInRange(1876, "1-1876")).toBe(true); - expect(asnInRange(2000, "1902-2042")).toBe(true); - }); + describe("real-world ASN ranges from IANA", () => { + // ARIN ranges + it("should match ARIN ASN ranges", () => { + // ARIN typically has ranges like 1-1876, 1902-2042, etc. + expect(asnInRange(100, "1-1876")).toBe(true); + expect(asnInRange(1876, "1-1876")).toBe(true); + expect(asnInRange(2000, "1902-2042")).toBe(true); + }); - // RIPE ranges - it("should match RIPE ASN ranges", () => { - // RIPE has ranges like 1877-1901, 2043-2109, etc. - expect(asnInRange(1900, "1877-1901")).toBe(true); - expect(asnInRange(2100, "2043-2109")).toBe(true); - }); + // RIPE ranges + it("should match RIPE ASN ranges", () => { + // RIPE has ranges like 1877-1901, 2043-2109, etc. + expect(asnInRange(1900, "1877-1901")).toBe(true); + expect(asnInRange(2100, "2043-2109")).toBe(true); + }); - // APNIC ranges - it("should match APNIC ASN ranges", () => { - // APNIC has ranges like 2110-2136, 4608-4864, etc. - expect(asnInRange(2120, "2110-2136")).toBe(true); - expect(asnInRange(4700, "4608-4864")).toBe(true); - }); + // APNIC ranges + it("should match APNIC ASN ranges", () => { + // APNIC has ranges like 2110-2136, 4608-4864, etc. + expect(asnInRange(2120, "2110-2136")).toBe(true); + expect(asnInRange(4700, "4608-4864")).toBe(true); + }); - // Well-known ASNs - it("should match Google ASN (AS15169)", () => { - // Google's ASN 15169 falls in range that includes it - expect(asnInRange(15169, "15000-16000")).toBe(true); - expect(asnInRange(15169, "15169-15169")).toBe(true); - expect(asnInRange(15169, "15360-16383")).toBe(false); // Not in this range - }); + // Well-known ASNs + it("should match Google ASN (AS15169)", () => { + // Google's ASN 15169 falls in range that includes it + expect(asnInRange(15169, "15000-16000")).toBe(true); + expect(asnInRange(15169, "15169-15169")).toBe(true); + expect(asnInRange(15169, "15360-16383")).toBe(false); // Not in this range + }); - it("should match Cloudflare ASN (AS13335)", () => { - // Cloudflare's ASN 13335 should be in ARIN range 13312-18431 - expect(asnInRange(13335, "13312-18431")).toBe(true); - }); + it("should match Cloudflare ASN (AS13335)", () => { + // Cloudflare's ASN 13335 should be in ARIN range 13312-18431 + expect(asnInRange(13335, "13312-18431")).toBe(true); + }); - it("should match Amazon ASN (AS16509)", () => { - // Amazon's ASN 16509 - expect(asnInRange(16509, "15360-16383")).toBe(false); - expect(asnInRange(16509, "16384-18431")).toBe(true); - }); - }); + it("should match Amazon ASN (AS16509)", () => { + // Amazon's ASN 16509 + expect(asnInRange(16509, "15360-16383")).toBe(false); + expect(asnInRange(16509, "16384-18431")).toBe(true); + }); + }); - describe("private ASN ranges", () => { - it("should match 16-bit private ASN range", () => { - // Private range: 64512-65534 - expect(asnInRange(64512, "64512-65534")).toBe(true); - expect(asnInRange(65000, "64512-65534")).toBe(true); - expect(asnInRange(65534, "64512-65534")).toBe(true); - }); + describe("private ASN ranges", () => { + it("should match 16-bit private ASN range", () => { + // Private range: 64512-65534 + expect(asnInRange(64512, "64512-65534")).toBe(true); + expect(asnInRange(65000, "64512-65534")).toBe(true); + expect(asnInRange(65534, "64512-65534")).toBe(true); + }); - it("should not match outside private range", () => { - expect(asnInRange(64511, "64512-65534")).toBe(false); - expect(asnInRange(65535, "64512-65534")).toBe(false); - }); + it("should not match outside private range", () => { + expect(asnInRange(64511, "64512-65534")).toBe(false); + expect(asnInRange(65535, "64512-65534")).toBe(false); + }); - it("should match 32-bit private ASN range", () => { - // Private range: 4200000000-4294967294 - expect(asnInRange(4200000000, "4200000000-4294967294")).toBe(true); - expect(asnInRange(4250000000, "4200000000-4294967294")).toBe(true); - expect(asnInRange(4294967294, "4200000000-4294967294")).toBe(true); - }); - }); + it("should match 32-bit private ASN range", () => { + // Private range: 4200000000-4294967294 + expect(asnInRange(4200000000, "4200000000-4294967294")).toBe(true); + expect(asnInRange(4250000000, "4200000000-4294967294")).toBe(true); + expect(asnInRange(4294967294, "4200000000-4294967294")).toBe(true); + }); + }); - describe("large ASN numbers (32-bit)", () => { - it("should handle large ASN numbers", () => { - expect(asnInRange(4200000000, "4200000000-4294967294")).toBe(true); - expect(asnInRange(4294967295, "4200000000-4294967294")).toBe(false); - }); + describe("large ASN numbers (32-bit)", () => { + it("should handle large ASN numbers", () => { + expect(asnInRange(4200000000, "4200000000-4294967294")).toBe(true); + expect(asnInRange(4294967295, "4200000000-4294967294")).toBe(false); + }); - it("should handle ASNs near 32-bit limit", () => { - const maxAsn = 4294967295; - expect(asnInRange(maxAsn, `${maxAsn}-${maxAsn}`)).toBe(true); - expect(asnInRange(maxAsn - 1, `${maxAsn}-${maxAsn}`)).toBe(false); - }); - }); + it("should handle ASNs near 32-bit limit", () => { + const maxAsn = 4294967295; + expect(asnInRange(maxAsn, `${maxAsn}-${maxAsn}`)).toBe(true); + expect(asnInRange(maxAsn - 1, `${maxAsn}-${maxAsn}`)).toBe(false); + }); + }); - describe("edge cases", () => { - it("should handle invalid range format", () => { - expect(asnInRange(100, "invalid")).toBe(false); - expect(asnInRange(100, "100")).toBe(false); - expect(asnInRange(100, "100-")).toBe(false); - expect(asnInRange(100, "-100")).toBe(false); - }); + describe("edge cases", () => { + it("should handle invalid range format", () => { + expect(asnInRange(100, "invalid")).toBe(false); + expect(asnInRange(100, "100")).toBe(false); + expect(asnInRange(100, "100-")).toBe(false); + expect(asnInRange(100, "-100")).toBe(false); + }); - it("should handle negative numbers gracefully", () => { - expect(asnInRange(-1, "1-100")).toBe(false); - expect(asnInRange(50, "-100-100")).toBe(false); - }); + it("should handle negative numbers gracefully", () => { + expect(asnInRange(-1, "1-100")).toBe(false); + expect(asnInRange(50, "-100-100")).toBe(false); + }); - it("should handle reversed ranges (end < start)", () => { - // Invalid range where end is less than start - expect(asnInRange(150, "200-100")).toBe(false); - }); + it("should handle reversed ranges (end < start)", () => { + // Invalid range where end is less than start + expect(asnInRange(150, "200-100")).toBe(false); + }); - it("should handle zero", () => { - expect(asnInRange(0, "0-100")).toBe(true); - expect(asnInRange(0, "1-100")).toBe(false); - }); - }); + it("should handle zero", () => { + expect(asnInRange(0, "0-100")).toBe(true); + expect(asnInRange(0, "1-100")).toBe(false); + }); + }); - describe("ASN number parsing", () => { - it("should handle number inputs", () => { - expect(asnInRange(12345, "10000-20000")).toBe(true); - }); + describe("ASN number parsing", () => { + it("should handle number inputs", () => { + expect(asnInRange(12345, "10000-20000")).toBe(true); + }); - it("should handle very large numbers", () => { - const largeAsn = 4000000000; - expect(asnInRange(largeAsn, "3000000000-4294967295")).toBe(true); - expect(asnInRange(largeAsn, "1-1000000000")).toBe(false); - }); - }); + it("should handle very large numbers", () => { + const largeAsn = 4000000000; + expect(asnInRange(largeAsn, "3000000000-4294967295")).toBe(true); + expect(asnInRange(largeAsn, "1-1000000000")).toBe(false); + }); + }); }); diff --git a/src/helpers.test.ts b/src/helpers.test.ts index f8e1ebc..10efbe0 100644 --- a/src/helpers.test.ts +++ b/src/helpers.test.ts @@ -2,178 +2,169 @@ import { describe, it, expect } from "vitest"; import { ipv4InCIDR, ipv6InCIDR } from "./helpers"; describe("ipv4InCIDR", () => { - describe("basic matching", () => { - it("should match IP in /8 network", () => { - expect(ipv4InCIDR("8.8.8.8", "8.0.0.0/8")).toBe(true); - expect(ipv4InCIDR("8.255.255.255", "8.0.0.0/8")).toBe(true); - expect(ipv4InCIDR("8.0.0.0", "8.0.0.0/8")).toBe(true); - }); + describe("basic matching", () => { + it("should match IP in /8 network", () => { + expect(ipv4InCIDR("8.8.8.8", "8.0.0.0/8")).toBe(true); + expect(ipv4InCIDR("8.255.255.255", "8.0.0.0/8")).toBe(true); + expect(ipv4InCIDR("8.0.0.0", "8.0.0.0/8")).toBe(true); + }); - it("should not match IP outside /8 network", () => { - expect(ipv4InCIDR("9.0.0.0", "8.0.0.0/8")).toBe(false); - expect(ipv4InCIDR("7.255.255.255", "8.0.0.0/8")).toBe(false); - }); + it("should not match IP outside /8 network", () => { + expect(ipv4InCIDR("9.0.0.0", "8.0.0.0/8")).toBe(false); + expect(ipv4InCIDR("7.255.255.255", "8.0.0.0/8")).toBe(false); + }); - it("should match IP in /16 network", () => { - expect(ipv4InCIDR("192.168.1.1", "192.168.0.0/16")).toBe(true); - expect(ipv4InCIDR("192.168.255.255", "192.168.0.0/16")).toBe(true); - expect(ipv4InCIDR("192.168.0.0", "192.168.0.0/16")).toBe(true); - }); + it("should match IP in /16 network", () => { + expect(ipv4InCIDR("192.168.1.1", "192.168.0.0/16")).toBe(true); + expect(ipv4InCIDR("192.168.255.255", "192.168.0.0/16")).toBe(true); + expect(ipv4InCIDR("192.168.0.0", "192.168.0.0/16")).toBe(true); + }); - it("should not match IP outside /16 network", () => { - expect(ipv4InCIDR("192.169.1.1", "192.168.0.0/16")).toBe(false); - expect(ipv4InCIDR("192.167.1.1", "192.168.0.0/16")).toBe(false); - }); + it("should not match IP outside /16 network", () => { + expect(ipv4InCIDR("192.169.1.1", "192.168.0.0/16")).toBe(false); + expect(ipv4InCIDR("192.167.1.1", "192.168.0.0/16")).toBe(false); + }); - it("should match IP in /24 network", () => { - expect(ipv4InCIDR("192.168.1.1", "192.168.1.0/24")).toBe(true); - expect(ipv4InCIDR("192.168.1.255", "192.168.1.0/24")).toBe(true); - expect(ipv4InCIDR("192.168.1.0", "192.168.1.0/24")).toBe(true); - }); + it("should match IP in /24 network", () => { + expect(ipv4InCIDR("192.168.1.1", "192.168.1.0/24")).toBe(true); + expect(ipv4InCIDR("192.168.1.255", "192.168.1.0/24")).toBe(true); + expect(ipv4InCIDR("192.168.1.0", "192.168.1.0/24")).toBe(true); + }); - it("should not match IP outside /24 network", () => { - expect(ipv4InCIDR("192.168.2.1", "192.168.1.0/24")).toBe(false); - expect(ipv4InCIDR("192.168.0.255", "192.168.1.0/24")).toBe(false); - }); + it("should not match IP outside /24 network", () => { + expect(ipv4InCIDR("192.168.2.1", "192.168.1.0/24")).toBe(false); + expect(ipv4InCIDR("192.168.0.255", "192.168.1.0/24")).toBe(false); + }); - it("should match IP in /32 network (single host)", () => { - expect(ipv4InCIDR("192.168.1.1", "192.168.1.1/32")).toBe(true); - }); + it("should match IP in /32 network (single host)", () => { + expect(ipv4InCIDR("192.168.1.1", "192.168.1.1/32")).toBe(true); + }); - it("should not match different IP in /32 network", () => { - expect(ipv4InCIDR("192.168.1.2", "192.168.1.1/32")).toBe(false); - }); - }); + it("should not match different IP in /32 network", () => { + expect(ipv4InCIDR("192.168.1.2", "192.168.1.1/32")).toBe(false); + }); + }); - describe("real-world RDAP bootstrap ranges", () => { - // ARIN ranges (from IANA bootstrap data) - it("should match Google DNS (8.8.8.8) in ARIN range", () => { - expect(ipv4InCIDR("8.8.8.8", "8.0.0.0/8")).toBe(true); - }); + describe("real-world RDAP bootstrap ranges", () => { + // ARIN ranges (from IANA bootstrap data) + it("should match Google DNS (8.8.8.8) in ARIN range", () => { + expect(ipv4InCIDR("8.8.8.8", "8.0.0.0/8")).toBe(true); + }); - // APNIC ranges - it("should match Cloudflare DNS (1.1.1.1) in APNIC range", () => { - expect(ipv4InCIDR("1.1.1.1", "1.0.0.0/8")).toBe(true); - }); + // APNIC ranges + it("should match Cloudflare DNS (1.1.1.1) in APNIC range", () => { + expect(ipv4InCIDR("1.1.1.1", "1.0.0.0/8")).toBe(true); + }); - // Private ranges - it("should match private IPs in their ranges", () => { - expect(ipv4InCIDR("10.0.0.1", "10.0.0.0/8")).toBe(true); - expect(ipv4InCIDR("172.16.0.1", "172.16.0.0/12")).toBe(true); - expect(ipv4InCIDR("192.168.0.1", "192.168.0.0/16")).toBe(true); - }); - }); + // Private ranges + it("should match private IPs in their ranges", () => { + expect(ipv4InCIDR("10.0.0.1", "10.0.0.0/8")).toBe(true); + expect(ipv4InCIDR("172.16.0.1", "172.16.0.0/12")).toBe(true); + expect(ipv4InCIDR("192.168.0.1", "192.168.0.0/16")).toBe(true); + }); + }); - describe("edge cases", () => { - it("should handle /0 (all IPs)", () => { - expect(ipv4InCIDR("0.0.0.0", "0.0.0.0/0")).toBe(true); - expect(ipv4InCIDR("255.255.255.255", "0.0.0.0/0")).toBe(true); - expect(ipv4InCIDR("192.168.1.1", "0.0.0.0/0")).toBe(true); - }); + describe("edge cases", () => { + it("should handle /0 (all IPs)", () => { + expect(ipv4InCIDR("0.0.0.0", "0.0.0.0/0")).toBe(true); + expect(ipv4InCIDR("255.255.255.255", "0.0.0.0/0")).toBe(true); + expect(ipv4InCIDR("192.168.1.1", "0.0.0.0/0")).toBe(true); + }); - it("should handle invalid CIDR notation", () => { - expect(ipv4InCIDR("192.168.1.1", "invalid")).toBe(false); - expect(ipv4InCIDR("192.168.1.1", "192.168.1.0/-1")).toBe(false); - expect(ipv4InCIDR("192.168.1.1", "192.168.1.0/33")).toBe(false); - }); + it("should handle invalid CIDR notation", () => { + expect(ipv4InCIDR("192.168.1.1", "invalid")).toBe(false); + expect(ipv4InCIDR("192.168.1.1", "192.168.1.0/-1")).toBe(false); + expect(ipv4InCIDR("192.168.1.1", "192.168.1.0/33")).toBe(false); + }); - it("should handle malformed IPs", () => { - expect(ipv4InCIDR("invalid", "192.168.1.0/24")).toBe(false); - }); + it("should handle malformed IPs", () => { + expect(ipv4InCIDR("invalid", "192.168.1.0/24")).toBe(false); + }); - it("should handle partial IPs (wrong number of octets)", () => { - expect(ipv4InCIDR("8.8", "8.0.0.0/8")).toBe(false); - expect(ipv4InCIDR("192.168.1", "192.168.1.0/24")).toBe(false); - expect(ipv4InCIDR("192.168.1.1.1", "192.168.1.0/24")).toBe(false); - }); - }); + it("should handle partial IPs (wrong number of octets)", () => { + expect(ipv4InCIDR("8.8", "8.0.0.0/8")).toBe(false); + expect(ipv4InCIDR("192.168.1", "192.168.1.0/24")).toBe(false); + expect(ipv4InCIDR("192.168.1.1.1", "192.168.1.0/24")).toBe(false); + }); + }); }); describe("ipv6InCIDR", () => { - describe("basic matching", () => { - it("should match IPv6 in /32 network", () => { - expect(ipv6InCIDR("2001:db8::", "2001:db8::/32")).toBe(true); - expect(ipv6InCIDR("2001:db8:1234::", "2001:db8::/32")).toBe(true); - expect( - ipv6InCIDR("2001:db8:ffff:ffff:ffff:ffff:ffff:ffff", "2001:db8::/32") - ).toBe(true); - }); + describe("basic matching", () => { + it("should match IPv6 in /32 network", () => { + expect(ipv6InCIDR("2001:db8::", "2001:db8::/32")).toBe(true); + expect(ipv6InCIDR("2001:db8:1234::", "2001:db8::/32")).toBe(true); + expect(ipv6InCIDR("2001:db8:ffff:ffff:ffff:ffff:ffff:ffff", "2001:db8::/32")).toBe( + true + ); + }); - it("should not match IPv6 outside /32 network", () => { - expect(ipv6InCIDR("2001:db9::", "2001:db8::/32")).toBe(false); - expect(ipv6InCIDR("2001:db7::", "2001:db8::/32")).toBe(false); - }); + it("should not match IPv6 outside /32 network", () => { + expect(ipv6InCIDR("2001:db9::", "2001:db8::/32")).toBe(false); + expect(ipv6InCIDR("2001:db7::", "2001:db8::/32")).toBe(false); + }); - it("should match IPv6 in /64 network", () => { - expect( - ipv6InCIDR("2001:db8:1234:5678::", "2001:db8:1234:5678::/64") - ).toBe(true); - expect( - ipv6InCIDR("2001:db8:1234:5678:abcd::", "2001:db8:1234:5678::/64") - ).toBe(true); - expect( - ipv6InCIDR( - "2001:db8:1234:5678:ffff:ffff:ffff:ffff", - "2001:db8:1234:5678::/64" - ) - ).toBe(true); - }); + it("should match IPv6 in /64 network", () => { + expect(ipv6InCIDR("2001:db8:1234:5678::", "2001:db8:1234:5678::/64")).toBe(true); + expect(ipv6InCIDR("2001:db8:1234:5678:abcd::", "2001:db8:1234:5678::/64")).toBe(true); + expect( + ipv6InCIDR("2001:db8:1234:5678:ffff:ffff:ffff:ffff", "2001:db8:1234:5678::/64") + ).toBe(true); + }); - it("should not match IPv6 outside /64 network", () => { - expect( - ipv6InCIDR("2001:db8:1234:5679::", "2001:db8:1234:5678::/64") - ).toBe(false); - }); + it("should not match IPv6 outside /64 network", () => { + expect(ipv6InCIDR("2001:db8:1234:5679::", "2001:db8:1234:5678::/64")).toBe(false); + }); - it("should match IPv6 in /128 network (single host)", () => { - expect(ipv6InCIDR("2001:db8::1", "2001:db8::1/128")).toBe(true); - }); + it("should match IPv6 in /128 network (single host)", () => { + expect(ipv6InCIDR("2001:db8::1", "2001:db8::1/128")).toBe(true); + }); - it("should not match different IPv6 in /128 network", () => { - expect(ipv6InCIDR("2001:db8::2", "2001:db8::1/128")).toBe(false); - }); - }); + it("should not match different IPv6 in /128 network", () => { + expect(ipv6InCIDR("2001:db8::2", "2001:db8::1/128")).toBe(false); + }); + }); - describe("real-world RDAP bootstrap ranges", () => { - it("should match Google IPv6 DNS in ARIN range", () => { - // Google DNS: 2001:4860:4860::8888 - expect(ipv6InCIDR("2001:4860:4860::8888", "2001:4860::/32")).toBe(true); - }); + describe("real-world RDAP bootstrap ranges", () => { + it("should match Google IPv6 DNS in ARIN range", () => { + // Google DNS: 2001:4860:4860::8888 + expect(ipv6InCIDR("2001:4860:4860::8888", "2001:4860::/32")).toBe(true); + }); - it("should match Cloudflare IPv6 DNS in APNIC range", () => { - // Cloudflare DNS: 2606:4700:4700::1111 - expect(ipv6InCIDR("2606:4700:4700::1111", "2606:4700::/32")).toBe(true); - }); - }); + it("should match Cloudflare IPv6 DNS in APNIC range", () => { + // Cloudflare DNS: 2606:4700:4700::1111 + expect(ipv6InCIDR("2606:4700:4700::1111", "2606:4700::/32")).toBe(true); + }); + }); - describe("IPv6 shorthand notation", () => { - it("should handle :: notation correctly", () => { - expect(ipv6InCIDR("2001:db8::1", "2001:db8::/32")).toBe(true); - expect(ipv6InCIDR("::1", "::1/128")).toBe(true); - expect(ipv6InCIDR("::", "::/128")).toBe(true); - }); + describe("IPv6 shorthand notation", () => { + it("should handle :: notation correctly", () => { + expect(ipv6InCIDR("2001:db8::1", "2001:db8::/32")).toBe(true); + expect(ipv6InCIDR("::1", "::1/128")).toBe(true); + expect(ipv6InCIDR("::", "::/128")).toBe(true); + }); - it("should handle expanded vs compressed notation", () => { - expect( - ipv6InCIDR("2001:0db8:0000:0000:0000:0000:0000:0001", "2001:db8::/32") - ).toBe(true); - expect( - ipv6InCIDR("2001:db8::1", "2001:0db8:0000:0000:0000:0000:0000:0000/32") - ).toBe(true); - }); - }); + it("should handle expanded vs compressed notation", () => { + expect(ipv6InCIDR("2001:0db8:0000:0000:0000:0000:0000:0001", "2001:db8::/32")).toBe( + true + ); + expect(ipv6InCIDR("2001:db8::1", "2001:0db8:0000:0000:0000:0000:0000:0000/32")).toBe( + true + ); + }); + }); - describe("edge cases", () => { - it("should handle invalid CIDR notation", () => { - expect(ipv6InCIDR("2001:db8::1", "invalid")).toBe(false); - expect(ipv6InCIDR("2001:db8::1", "2001:db8::/-1")).toBe(false); - expect(ipv6InCIDR("2001:db8::1", "2001:db8::/129")).toBe(false); - }); + describe("edge cases", () => { + it("should handle invalid CIDR notation", () => { + expect(ipv6InCIDR("2001:db8::1", "invalid")).toBe(false); + expect(ipv6InCIDR("2001:db8::1", "2001:db8::/-1")).toBe(false); + expect(ipv6InCIDR("2001:db8::1", "2001:db8::/129")).toBe(false); + }); - it("should handle malformed IPv6", () => { - expect(ipv6InCIDR("invalid", "2001:db8::/32")).toBe(false); - expect(ipv6InCIDR("zzzz::1", "2001:db8::/32")).toBe(false); - expect(ipv6InCIDR("2001:xyz::1", "2001:db8::/32")).toBe(false); - }); - }); + it("should handle malformed IPv6", () => { + expect(ipv6InCIDR("invalid", "2001:db8::/32")).toBe(false); + expect(ipv6InCIDR("zzzz::1", "2001:db8::/32")).toBe(false); + expect(ipv6InCIDR("2001:xyz::1", "2001:db8::/32")).toBe(false); + }); + }); }); diff --git a/src/helpers.ts b/src/helpers.ts index c49e680..9063ca5 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -2,24 +2,24 @@ import type { SyntheticEvent } from "react"; import type { Entries } from "type-fest"; declare global { - interface ObjectConstructor { - entries(o: T): Entries; - } + interface ObjectConstructor { + entries(o: T): Entries; + } } export function truthy(value: string | null | undefined) { - if (value == undefined) return false; - return value.toLowerCase() == "true" || value == "1"; + if (value == undefined) return false; + return value.toLowerCase() == "true" || value == "1"; } export function onPromise(promise: (event: SyntheticEvent) => Promise) { - return (event: SyntheticEvent) => { - if (promise) { - promise(event).catch((error) => { - console.log("Unexpected error", error); - }); - } - }; + return (event: SyntheticEvent) => { + if (promise) { + promise(event).catch((error) => { + console.log("Unexpected error", error); + }); + } + }; } /** @@ -30,27 +30,24 @@ export function onPromise(promise: (event: SyntheticEvent) => Promise) { * @param ellipsis A string representing what should be placed on the end when the max length is hit. */ export function truncated(input: string, maxLength: number, ellipsis = "...") { - if (maxLength <= 0) return ""; - if (input.length <= maxLength) return input; - return ( - input.substring(0, Math.max(0, maxLength - ellipsis.length)) + ellipsis - ); + if (maxLength <= 0) return ""; + if (input.length <= maxLength) return input; + return input.substring(0, Math.max(0, maxLength - ellipsis.length)) + ellipsis; } export function preventDefault(event: SyntheticEvent | Event) { - event.preventDefault(); + event.preventDefault(); } /** * Convert an IPv4 address string to a 32-bit integer */ function ipv4ToInt(ip: string): number { - const parts = ip.split(".").map(Number); - if (parts.length !== 4) return 0; - const [a, b, c, d] = parts; - if (a === undefined || b === undefined || c === undefined || d === undefined) - return 0; - return ((a << 24) | (b << 16) | (c << 8) | d) >>> 0; + const parts = ip.split(".").map(Number); + if (parts.length !== 4) return 0; + const [a, b, c, d] = parts; + if (a === undefined || b === undefined || c === undefined || d === undefined) return 0; + return ((a << 24) | (b << 16) | (c << 8) | d) >>> 0; } /** @@ -60,57 +57,57 @@ function ipv4ToInt(ip: string): number { * @returns true if the IP is within the CIDR range */ export function ipv4InCIDR(ip: string, cidr: string): boolean { - const [rangeIp, prefixLenStr] = cidr.split("/"); - const prefixLen = parseInt(prefixLenStr ?? "", 10); + const [rangeIp, prefixLenStr] = cidr.split("/"); + const prefixLen = parseInt(prefixLenStr ?? "", 10); - if (!rangeIp || isNaN(prefixLen) || prefixLen < 0 || prefixLen > 32) { - return false; - } + if (!rangeIp || isNaN(prefixLen) || prefixLen < 0 || prefixLen > 32) { + return false; + } - // Special case: /0 matches all IPs - if (prefixLen === 0) { - return true; - } + // Special case: /0 matches all IPs + if (prefixLen === 0) { + return true; + } - const ipInt = ipv4ToInt(ip); - const rangeInt = ipv4ToInt(rangeIp); - const mask = (0xffffffff << (32 - prefixLen)) >>> 0; + const ipInt = ipv4ToInt(ip); + const rangeInt = ipv4ToInt(rangeIp); + const mask = (0xffffffff << (32 - prefixLen)) >>> 0; - return (ipInt & mask) === (rangeInt & mask); + return (ipInt & mask) === (rangeInt & mask); } /** * Convert an IPv6 address to a BigInt representation */ function ipv6ToBigInt(ip: string): bigint { - // Expand :: notation - const expandedIp = expandIPv6(ip); - const parts = expandedIp.split(":"); + // Expand :: notation + const expandedIp = expandIPv6(ip); + const parts = expandedIp.split(":"); - let result = BigInt(0); - for (const part of parts) { - result = (result << BigInt(16)) | BigInt(parseInt(part, 16)); - } - return result; + let result = BigInt(0); + for (const part of parts) { + result = (result << BigInt(16)) | BigInt(parseInt(part, 16)); + } + return result; } /** * Expand IPv6 address shorthand notation */ function expandIPv6(ip: string): string { - if (ip.includes("::")) { - const [left, right] = ip.split("::"); - const leftParts = left ? left.split(":") : []; - const rightParts = right ? right.split(":") : []; - const missingParts = 8 - leftParts.length - rightParts.length; - const middleParts: string[] = Array(missingParts).fill("0") as string[]; - const allParts = [...leftParts, ...middleParts, ...rightParts]; - return allParts.map((p: string) => p.padStart(4, "0")).join(":"); - } - return ip - .split(":") - .map((p: string) => p.padStart(4, "0")) - .join(":"); + if (ip.includes("::")) { + const [left, right] = ip.split("::"); + const leftParts = left ? left.split(":") : []; + const rightParts = right ? right.split(":") : []; + const missingParts = 8 - leftParts.length - rightParts.length; + const middleParts: string[] = Array(missingParts).fill("0") as string[]; + const allParts = [...leftParts, ...middleParts, ...rightParts]; + return allParts.map((p: string) => p.padStart(4, "0")).join(":"); + } + return ip + .split(":") + .map((p: string) => p.padStart(4, "0")) + .join(":"); } /** @@ -120,23 +117,23 @@ function expandIPv6(ip: string): string { * @returns true if the IP is within the CIDR range */ export function ipv6InCIDR(ip: string, cidr: string): boolean { - const [rangeIp, prefixLenStr] = cidr.split("/"); - const prefixLen = parseInt(prefixLenStr ?? "", 10); + const [rangeIp, prefixLenStr] = cidr.split("/"); + const prefixLen = parseInt(prefixLenStr ?? "", 10); - if (!rangeIp || isNaN(prefixLen) || prefixLen < 0 || prefixLen > 128) { - return false; - } + if (!rangeIp || isNaN(prefixLen) || prefixLen < 0 || prefixLen > 128) { + return false; + } - try { - const ipInt = ipv6ToBigInt(ip); - const rangeInt = ipv6ToBigInt(rangeIp); - const maxMask = BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); - const mask = (maxMask << BigInt(128 - prefixLen)) & maxMask; + try { + const ipInt = ipv6ToBigInt(ip); + const rangeInt = ipv6ToBigInt(rangeIp); + const maxMask = BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); + const mask = (maxMask << BigInt(128 - prefixLen)) & maxMask; - return (ipInt & mask) === (rangeInt & mask); - } catch { - return false; - } + return (ipInt & mask) === (rangeInt & mask); + } catch { + return false; + } } /** @@ -146,22 +143,22 @@ export function ipv6InCIDR(ip: string, cidr: string): boolean { * @returns true if the ASN is within the range */ export function asnInRange(asn: number, range: string): boolean { - const parts = range.split("-"); + const parts = range.split("-"); - if (parts.length !== 2) { - return false; - } + if (parts.length !== 2) { + return false; + } - const start = parseInt(parts[0] ?? "", 10); - const end = parseInt(parts[1] ?? "", 10); + const start = parseInt(parts[0] ?? "", 10); + const end = parseInt(parts[1] ?? "", 10); - if (isNaN(start) || isNaN(end) || start < 0 || end < 0 || start > end) { - return false; - } + if (isNaN(start) || isNaN(end) || start < 0 || end < 0 || start > end) { + return false; + } - if (asn < 0) { - return false; - } + if (asn < 0) { + return false; + } - return asn >= start && asn <= end; + return asn >= start && asn <= end; } diff --git a/src/hooks/useLookup.tsx b/src/hooks/useLookup.tsx index b4a4908..bfffc72 100644 --- a/src/hooks/useLookup.tsx +++ b/src/hooks/useLookup.tsx @@ -1,21 +1,21 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { domainMatchPredicate, getBestURL, getType } from "@/rdap"; import type { - AutonomousNumber, - Domain, - IpNetwork, - Register, - RootRegistryType, - SubmitProps, - TargetType, + AutonomousNumber, + Domain, + IpNetwork, + Register, + RootRegistryType, + SubmitProps, + TargetType, } from "@/types"; import { registryURLs } from "@/constants"; import { - AutonomousNumberSchema, - DomainSchema, - IpNetworkSchema, - RegisterSchema, - RootRegistryEnum, + AutonomousNumberSchema, + DomainSchema, + IpNetworkSchema, + RegisterSchema, + RootRegistryEnum, } from "@/schema"; import { truncated, ipv4InCIDR, ipv6InCIDR, asnInRange } from "@/helpers"; import type { ZodSchema } from "zod"; @@ -24,413 +24,383 @@ import { Maybe, Result } from "true-myth"; export type WarningHandler = (warning: { message: string }) => void; export type MetaParsedGeneric = { - data: ParsedGeneric; - url: string; - completeTime: Date; + data: ParsedGeneric; + url: string; + completeTime: Date; }; // An array of schemas to try and parse unknown JSON data with. const schemas = [DomainSchema, AutonomousNumberSchema, IpNetworkSchema]; const useLookup = (warningHandler?: WarningHandler) => { - /** - * A reference to the registry data, which is used to cache the registry data in memory. - * This uses TargetType as the key, meaning v4/v6 IP/CIDR lookups are differentiated. - */ - const registryDataRef = useRef>( - {} as Record - ); + /** + * A reference to the registry data, which is used to cache the registry data in memory. + * This uses TargetType as the key, meaning v4/v6 IP/CIDR lookups are differentiated. + */ + const registryDataRef = useRef>( + {} as Record + ); - const [error, setError] = useState(null); - const [target, setTarget] = useState(""); - const [uriType, setUriType] = useState>(Maybe.nothing()); + const [error, setError] = useState(null); + const [target, setTarget] = useState(""); + const [uriType, setUriType] = useState>(Maybe.nothing()); - // Used by a callback on LookupInput to forcibly set the type of the lookup. - const [currentType, setTargetType] = useState(null); + // Used by a callback on LookupInput to forcibly set the type of the lookup. + const [currentType, setTargetType] = useState(null); - // Used to allow repeatable lookups when weird errors happen. - const repeatableRef = useRef(""); + // Used to allow repeatable lookups when weird errors happen. + const repeatableRef = useRef(""); - useCallback(async () => { - if (currentType != null) return Maybe.just(currentType); - const uri: Maybe = (await getTypeEasy(target)).mapOr( - Maybe.nothing(), - (type) => Maybe.just(type) - ); - setUriType(uri); - }, [target, currentType, getTypeEasy]); + useCallback(async () => { + if (currentType != null) return Maybe.just(currentType); + const uri: Maybe = (await getTypeEasy(target)).mapOr(Maybe.nothing(), (type) => + Maybe.just(type) + ); + setUriType(uri); + }, [target, currentType, getTypeEasy]); - // Fetch & load a specific registry's data into memory. - async function loadBootstrap(type: RootRegistryType, force = false) { - // Early preload exit condition - if (registryDataRef.current[type] != null && !force) return; + // Fetch & load a specific registry's data into memory. + async function loadBootstrap(type: RootRegistryType, force = false) { + // Early preload exit condition + if (registryDataRef.current[type] != null && !force) return; - // Fetch the bootstrapping file from the registry - const response = await fetch(registryURLs[type]); - if (response.status != 200) - throw new Error(`Error: ${response.statusText}`); + // Fetch the bootstrapping file from the registry + const response = await fetch(registryURLs[type]); + if (response.status != 200) throw new Error(`Error: ${response.statusText}`); - // Parse it, so we don't make any false assumptions during development & while maintaining the tool. - const parsedRegister = RegisterSchema.safeParse(await response.json()); - if (!parsedRegister.success) - throw new Error( - `Could not parse IANA bootstrap response (type: ${type}).` - ); + // Parse it, so we don't make any false assumptions during development & while maintaining the tool. + const parsedRegister = RegisterSchema.safeParse(await response.json()); + if (!parsedRegister.success) + throw new Error(`Could not parse IANA bootstrap response (type: ${type}).`); - // Set it in state so we can use it. - registryDataRef.current = { - ...registryDataRef.current, - [type]: parsedRegister.data, - }; - } + // Set it in state so we can use it. + registryDataRef.current = { + ...registryDataRef.current, + [type]: parsedRegister.data, + }; + } - async function getRegistry(type: RootRegistryType): Promise { - if (registryDataRef.current[type] == null) await loadBootstrap(type); - const registry = registryDataRef.current[type]; - if (registry == null) - throw new Error(`Could not load bootstrap data for ${type} registry.`); - return registry; - } + async function getRegistry(type: RootRegistryType): Promise { + if (registryDataRef.current[type] == null) await loadBootstrap(type); + const registry = registryDataRef.current[type]; + if (registry == null) + throw new Error(`Could not load bootstrap data for ${type} registry.`); + return registry; + } - async function getTypeEasy( - target: string - ): Promise> { - return getType(target, getRegistry); - } + async function getTypeEasy(target: string): Promise> { + return getType(target, getRegistry); + } - function getRegistryURL( - type: RootRegistryType, - lookupTarget: string - ): string { - const bootstrap = registryDataRef.current[type]; - if (bootstrap == null) - throw new Error( - `Cannot acquire RDAP URL without bootstrap data for ${type} lookup.` - ); + function getRegistryURL(type: RootRegistryType, lookupTarget: string): string { + const bootstrap = registryDataRef.current[type]; + if (bootstrap == null) + throw new Error(`Cannot acquire RDAP URL without bootstrap data for ${type} lookup.`); - let url: string | null = null; + let url: string | null = null; - typeSwitch: switch (type) { - case "domain": - for (const bootstrapItem of bootstrap.services) { - if (bootstrapItem[0].some(domainMatchPredicate(lookupTarget))) { - // min length of 1 is validated in zod schema - url = getBestURL(bootstrapItem[1] as [string, ...string[]]); - break typeSwitch; - } - } - throw new Error(`No matching domain found.`); - case "ip4": { - // Extract the IP address without CIDR suffix for matching - const ipAddress = lookupTarget.split("/")[0] ?? lookupTarget; - for (const bootstrapItem of bootstrap.services) { - // bootstrapItem[0] contains CIDR ranges like ["1.0.0.0/8", "2.0.0.0/8"] - if (bootstrapItem[0].some((cidr) => ipv4InCIDR(ipAddress, cidr))) { - url = getBestURL(bootstrapItem[1] as [string, ...string[]]); - break typeSwitch; - } - } - throw new Error(`No matching IPv4 registry found for ${lookupTarget}.`); - } - case "ip6": { - // Extract the IP address without CIDR suffix for matching - const ipAddress = lookupTarget.split("/")[0] ?? lookupTarget; - for (const bootstrapItem of bootstrap.services) { - // bootstrapItem[0] contains CIDR ranges like ["2001:0200::/23", "2001:0400::/23"] - if (bootstrapItem[0].some((cidr) => ipv6InCIDR(ipAddress, cidr))) { - url = getBestURL(bootstrapItem[1] as [string, ...string[]]); - break typeSwitch; - } - } - throw new Error(`No matching IPv6 registry found for ${lookupTarget}.`); - } - case "autnum": { - // Extract ASN number from "AS12345" format - const asnMatch = lookupTarget.match(/^AS(\d+)$/i); - if (!asnMatch || !asnMatch[1]) { - throw new Error(`Invalid ASN format: ${lookupTarget}`); - } + typeSwitch: switch (type) { + case "domain": + for (const bootstrapItem of bootstrap.services) { + if (bootstrapItem[0].some(domainMatchPredicate(lookupTarget))) { + // min length of 1 is validated in zod schema + url = getBestURL(bootstrapItem[1] as [string, ...string[]]); + break typeSwitch; + } + } + throw new Error(`No matching domain found.`); + case "ip4": { + // Extract the IP address without CIDR suffix for matching + const ipAddress = lookupTarget.split("/")[0] ?? lookupTarget; + for (const bootstrapItem of bootstrap.services) { + // bootstrapItem[0] contains CIDR ranges like ["1.0.0.0/8", "2.0.0.0/8"] + if (bootstrapItem[0].some((cidr) => ipv4InCIDR(ipAddress, cidr))) { + url = getBestURL(bootstrapItem[1] as [string, ...string[]]); + break typeSwitch; + } + } + throw new Error(`No matching IPv4 registry found for ${lookupTarget}.`); + } + case "ip6": { + // Extract the IP address without CIDR suffix for matching + const ipAddress = lookupTarget.split("/")[0] ?? lookupTarget; + for (const bootstrapItem of bootstrap.services) { + // bootstrapItem[0] contains CIDR ranges like ["2001:0200::/23", "2001:0400::/23"] + if (bootstrapItem[0].some((cidr) => ipv6InCIDR(ipAddress, cidr))) { + url = getBestURL(bootstrapItem[1] as [string, ...string[]]); + break typeSwitch; + } + } + throw new Error(`No matching IPv6 registry found for ${lookupTarget}.`); + } + case "autnum": { + // Extract ASN number from "AS12345" format + const asnMatch = lookupTarget.match(/^AS(\d+)$/i); + if (!asnMatch || !asnMatch[1]) { + throw new Error(`Invalid ASN format: ${lookupTarget}`); + } - const asnNumber = parseInt(asnMatch[1], 10); - if (isNaN(asnNumber)) { - throw new Error(`Invalid ASN number: ${lookupTarget}`); - } + const asnNumber = parseInt(asnMatch[1], 10); + if (isNaN(asnNumber)) { + throw new Error(`Invalid ASN number: ${lookupTarget}`); + } - for (const bootstrapItem of bootstrap.services) { - // bootstrapItem[0] contains ASN ranges like ["64512-65534", "13312-18431"] - if (bootstrapItem[0].some((range) => asnInRange(asnNumber, range))) { - url = getBestURL(bootstrapItem[1] as [string, ...string[]]); - break typeSwitch; - } - } - throw new Error(`No matching registry found for ${lookupTarget}.`); - } - case "entity": - throw new Error(`No matching entity found.`); - default: - throw new Error("Invalid lookup target provided."); - } + for (const bootstrapItem of bootstrap.services) { + // bootstrapItem[0] contains ASN ranges like ["64512-65534", "13312-18431"] + if (bootstrapItem[0].some((range) => asnInRange(asnNumber, range))) { + url = getBestURL(bootstrapItem[1] as [string, ...string[]]); + break typeSwitch; + } + } + throw new Error(`No matching registry found for ${lookupTarget}.`); + } + case "entity": + throw new Error(`No matching entity found.`); + default: + throw new Error("Invalid lookup target provided."); + } - if (url == null) throw new Error("No lookup target was resolved."); + if (url == null) throw new Error("No lookup target was resolved."); - // Map internal types to RDAP endpoint paths - // ip4 and ip6 both use the 'ip' endpoint in RDAP - const rdapPath = type === "ip4" || type === "ip6" ? "ip" : type; + // Map internal types to RDAP endpoint paths + // ip4 and ip6 both use the 'ip' endpoint in RDAP + const rdapPath = type === "ip4" || type === "ip6" ? "ip" : type; - return `${url}${rdapPath}/${lookupTarget}`; - } + return `${url}${rdapPath}/${lookupTarget}`; + } - useEffect(() => { - const preload = async () => { - if (uriType.isNothing) return; + useEffect(() => { + const preload = async () => { + if (uriType.isNothing) return; - const registryUri = RootRegistryEnum.safeParse(uriType.value); - if (!registryUri.success) return; + const registryUri = RootRegistryEnum.safeParse(uriType.value); + if (!registryUri.success) return; - console.log({ - uriType: uriType.value, - registryData: registryDataRef.current, - registryUri: registryUri.data, - }); - if (registryDataRef.current[registryUri.data] != null) return; + console.log({ + uriType: uriType.value, + registryData: registryDataRef.current, + registryUri: registryUri.data, + }); + if (registryDataRef.current[registryUri.data] != null) return; - try { - await loadBootstrap(registryUri.data); - } catch (e) { - if (warningHandler != undefined) { - const message = - e instanceof Error ? `(${truncated(e.message, 15)})` : "."; - warningHandler({ - message: `Failed to preload registry${message}`, - }); - } - } - }; + try { + await loadBootstrap(registryUri.data); + } catch (e) { + if (warningHandler != undefined) { + const message = e instanceof Error ? `(${truncated(e.message, 15)})` : "."; + warningHandler({ + message: `Failed to preload registry${message}`, + }); + } + } + }; - preload().catch(console.error); - }, [target, uriType, warningHandler]); + preload().catch(console.error); + }, [target, uriType, warningHandler]); - async function getAndParse( - url: string, - schema: ZodSchema - ): Promise> { - const response = await fetch(url); + async function getAndParse(url: string, schema: ZodSchema): Promise> { + const response = await fetch(url); - if (response.status == 200) { - const result = schema.safeParse(await response.json()); + if (response.status == 200) { + const result = schema.safeParse(await response.json()); - if (result.success === false) { - // flatten the errors to make them more readable and simple - const flatErrors = result.error.flatten(function (issue) { - const path = issue.path.map((value) => value.toString()).join("."); - return `${path}: ${issue.message}`; - }); + if (result.success === false) { + // flatten the errors to make them more readable and simple + const flatErrors = result.error.flatten(function (issue) { + const path = issue.path.map((value) => value.toString()).join("."); + return `${path}: ${issue.message}`; + }); - console.log(flatErrors); + console.log(flatErrors); - // combine them all, wrap them in a new error, and return it - return Result.err( - new Error( - [ - "Could not parse the response from the registry.", - ...flatErrors.formErrors, - ...Object.values(flatErrors.fieldErrors).flat(), - ].join("\n\t") - ) - ); - } + // combine them all, wrap them in a new error, and return it + return Result.err( + new Error( + [ + "Could not parse the response from the registry.", + ...flatErrors.formErrors, + ...Object.values(flatErrors.fieldErrors).flat(), + ].join("\n\t") + ) + ); + } - return Result.ok(result.data); - } + return Result.ok(result.data); + } - switch (response.status) { - case 302: - return Result.err( - new Error( - "The registry indicated that the resource requested is available at a different location." - ) - ); - case 400: - return Result.err( - new Error( - "The registry indicated that the request was malformed or could not be processed. Check that you typed in the correct information and try again." - ) - ); - case 403: - return Result.err( - new Error( - "The registry indicated that the request was forbidden. This could be due to rate limiting, abusive behavior, or other reasons. Try again later or contact the registry for more information." - ) - ); + switch (response.status) { + case 302: + return Result.err( + new Error( + "The registry indicated that the resource requested is available at a different location." + ) + ); + case 400: + return Result.err( + new Error( + "The registry indicated that the request was malformed or could not be processed. Check that you typed in the correct information and try again." + ) + ); + case 403: + return Result.err( + new Error( + "The registry indicated that the request was forbidden. This could be due to rate limiting, abusive behavior, or other reasons. Try again later or contact the registry for more information." + ) + ); - case 404: - return Result.err( - new Error( - "The registry indicated that the resource requested could not be found; the resource either does not exist, or is something that the registry does not track (i.e. this software queried incorrectly, which is unlikely)." - ) - ); - case 500: - return Result.err( - new Error( - "The registry indicated that an internal server error occurred. This could be due to a misconfiguration, a bug, or other reasons. Try again later or contact the registry for more information." - ) - ); - default: - return Result.err( - new Error( - `The registry did not return an OK status code: ${response.status}.` - ) - ); - } - } + case 404: + return Result.err( + new Error( + "The registry indicated that the resource requested could not be found; the resource either does not exist, or is something that the registry does not track (i.e. this software queried incorrectly, which is unlikely)." + ) + ); + case 500: + return Result.err( + new Error( + "The registry indicated that an internal server error occurred. This could be due to a misconfiguration, a bug, or other reasons. Try again later or contact the registry for more information." + ) + ); + default: + return Result.err( + new Error(`The registry did not return an OK status code: ${response.status}.`) + ); + } + } - async function submitInternal( - target: string - ): Promise> { - if (target == null || target.length == 0) - return Result.err( - new Error("A target must be given in order to execute a lookup.") - ); + async function submitInternal( + target: string + ): Promise> { + if (target == null || target.length == 0) + return Result.err(new Error("A target must be given in order to execute a lookup.")); - const targetType = await getTypeEasy(target); + const targetType = await getTypeEasy(target); - if (targetType.isErr) { - return Result.err( - new Error("Unable to determine type, unable to send query", { - cause: targetType.error, - }) - ); - } + if (targetType.isErr) { + return Result.err( + new Error("Unable to determine type, unable to send query", { + cause: targetType.error, + }) + ); + } - switch (targetType.value) { - // Block scoped case to allow url const reuse - case "ip4": { - await loadBootstrap("ip4"); - const url = getRegistryURL(targetType.value, target); - const result = await getAndParse(url, IpNetworkSchema); - if (result.isErr) return Result.err(result.error); - return Result.ok({ data: result.value, url }); - } - case "ip6": { - await loadBootstrap("ip6"); - const url = getRegistryURL(targetType.value, target); - const result = await getAndParse(url, IpNetworkSchema); - if (result.isErr) return Result.err(result.error); - return Result.ok({ data: result.value, url }); - } - case "domain": { - await loadBootstrap("domain"); - const url = getRegistryURL(targetType.value, target); + switch (targetType.value) { + // Block scoped case to allow url const reuse + case "ip4": { + await loadBootstrap("ip4"); + const url = getRegistryURL(targetType.value, target); + const result = await getAndParse(url, IpNetworkSchema); + if (result.isErr) return Result.err(result.error); + return Result.ok({ data: result.value, url }); + } + case "ip6": { + await loadBootstrap("ip6"); + const url = getRegistryURL(targetType.value, target); + const result = await getAndParse(url, IpNetworkSchema); + if (result.isErr) return Result.err(result.error); + return Result.ok({ data: result.value, url }); + } + case "domain": { + await loadBootstrap("domain"); + const url = getRegistryURL(targetType.value, target); - // HTTP - if (url.startsWith("http://") && url != repeatableRef.current) { - repeatableRef.current = url; - return Result.err( - new Error( - "The registry this domain belongs to uses HTTP, which is not secure. " + - "In order to prevent a cryptic error from appearing due to mixed active content, " + - "or worse, a CORS error, this lookup has been blocked. Try again to force the lookup." - ) - ); - } - const result = await getAndParse(url, DomainSchema); - if (result.isErr) return Result.err(result.error); + // HTTP + if (url.startsWith("http://") && url != repeatableRef.current) { + repeatableRef.current = url; + return Result.err( + new Error( + "The registry this domain belongs to uses HTTP, which is not secure. " + + "In order to prevent a cryptic error from appearing due to mixed active content, " + + "or worse, a CORS error, this lookup has been blocked. Try again to force the lookup." + ) + ); + } + const result = await getAndParse(url, DomainSchema); + if (result.isErr) return Result.err(result.error); - return Result.ok({ data: result.value, url }); - } - case "autnum": { - await loadBootstrap("autnum"); - const url = getRegistryURL(targetType.value, target); - const result = await getAndParse( - url, - AutonomousNumberSchema - ); - if (result.isErr) return Result.err(result.error); - return Result.ok({ data: result.value, url }); - } - case "tld": { - // remove the leading dot - const value = target.startsWith(".") ? target.slice(1) : target; - const url = `https://root.rdap.org/domain/${value}`; - const result = await getAndParse(url, DomainSchema); - if (result.isErr) return Result.err(result.error); - return Result.ok({ data: result.value, url }); - } - case "url": { - const response = await fetch(target); + return Result.ok({ data: result.value, url }); + } + case "autnum": { + await loadBootstrap("autnum"); + const url = getRegistryURL(targetType.value, target); + const result = await getAndParse(url, AutonomousNumberSchema); + if (result.isErr) return Result.err(result.error); + return Result.ok({ data: result.value, url }); + } + case "tld": { + // remove the leading dot + const value = target.startsWith(".") ? target.slice(1) : target; + const url = `https://root.rdap.org/domain/${value}`; + const result = await getAndParse(url, DomainSchema); + if (result.isErr) return Result.err(result.error); + return Result.ok({ data: result.value, url }); + } + case "url": { + const response = await fetch(target); - if (response.status != 200) - return Result.err( - new Error( - `The URL provided returned a non-200 status code: ${response.status}.` - ) - ); + if (response.status != 200) + return Result.err( + new Error( + `The URL provided returned a non-200 status code: ${response.status}.` + ) + ); - const data = await response.json(); + const data = await response.json(); - // Try each schema until one works - for (const schema of schemas) { - const result = schema.safeParse(data); - if (result.success) - return Result.ok({ data: result.data, url: target }); - } + // Try each schema until one works + for (const schema of schemas) { + const result = schema.safeParse(data); + if (result.success) return Result.ok({ data: result.data, url: target }); + } - return Result.err( - new Error("No schema was able to parse the response.") - ); - } - case "json": { - const data = JSON.parse(target); - for (const schema of schemas) { - const result = schema.safeParse(data); - if (result.success) return Result.ok({ data: result.data, url: "" }); - } - } - case "registrar": { - } - default: - return Result.err( - new Error("The type detected has not been implemented.") - ); - } - } + return Result.err(new Error("No schema was able to parse the response.")); + } + case "json": { + const data = JSON.parse(target); + for (const schema of schemas) { + const result = schema.safeParse(data); + if (result.success) return Result.ok({ data: result.data, url: "" }); + } + } + case "registrar": { + } + default: + return Result.err(new Error("The type detected has not been implemented.")); + } + } - async function submit({ - target, - }: SubmitProps): Promise> { - try { - // target is already set in state, but it's also provided by the form callback, so we'll use it. - const response = await submitInternal(target); + async function submit({ target }: SubmitProps): Promise> { + try { + // target is already set in state, but it's also provided by the form callback, so we'll use it. + const response = await submitInternal(target); - if (response.isErr) { - setError(response.error.message); - console.error(response.error); - } else setError(null); + if (response.isErr) { + setError(response.error.message); + console.error(response.error); + } else setError(null); - return response.isOk - ? Maybe.just({ - data: response.value.data, - url: response.value.url, - completeTime: new Date(), - }) - : Maybe.nothing(); - } catch (e) { - if (!(e instanceof Error)) - setError("An unknown, unprocessable error has occurred."); - else setError(e.message); - console.error(e); - return Maybe.nothing(); - } - } + return response.isOk + ? Maybe.just({ + data: response.value.data, + url: response.value.url, + completeTime: new Date(), + }) + : Maybe.nothing(); + } catch (e) { + if (!(e instanceof Error)) setError("An unknown, unprocessable error has occurred."); + else setError(e.message); + console.error(e); + return Maybe.nothing(); + } + } - return { - error, - setTarget, - setTargetType, - submit, - currentType: uriType, - getType: getTypeEasy, - }; + return { + error, + setTarget, + setTargetType, + submit, + currentType: uriType, + getType: getTypeEasy, + }; }; export default useLookup; diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 365058c..ac680b3 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -2,5 +2,5 @@ import { type ClassValue, clsx } from "clsx"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)); + return twMerge(clsx(inputs)); } diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index da2fe14..fe4b9f0 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -6,7 +6,7 @@ import "@fontsource/ibm-plex-mono/400.css"; import "../styles/globals.css"; const MyApp: AppType = ({ Component, pageProps }) => { - return ; + return ; }; export default MyApp; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index dba1080..a19414f 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -10,93 +10,83 @@ import { Maybe } from "true-myth"; import type { TargetType } from "@/types"; const Index: NextPage = () => { - const { error, setTarget, setTargetType, submit, getType } = useLookup(); - const [detectedType, setDetectedType] = useState>( - Maybe.nothing() - ); - const [response, setResponse] = useState>( - Maybe.nothing() - ); - const [isLoading, setLoading] = useState(false); + const { error, setTarget, setTargetType, submit, getType } = useLookup(); + const [detectedType, setDetectedType] = useState>(Maybe.nothing()); + const [response, setResponse] = useState>(Maybe.nothing()); + const [isLoading, setLoading] = useState(false); - return ( - <> - - rdap.xevion.dev - - - - - - - - - - -
    -
    - { - setTarget(target); - setTargetType(targetType); + return ( + <> + + rdap.xevion.dev + + + + + + + + + + +
    +
    + { + setTarget(target); + setTargetType(targetType); - const detectResult = await getType(target); - if (detectResult.isOk) { - setDetectedType(Maybe.just(detectResult.value)); - } else { - setDetectedType(Maybe.nothing()); - } - }} - onSubmit={async function (props) { - try { - setLoading(true); - setResponse(await submit(props)); - setLoading(false); - } catch (e) { - console.error(e); - setResponse(Maybe.nothing()); - setLoading(false); - } - }} - /> - {error != null ? ( - - ) : null} - {response.isJust ? ( - - ) : null} -
    -
    - - ); + const detectResult = await getType(target); + if (detectResult.isOk) { + setDetectedType(Maybe.just(detectResult.value)); + } else { + setDetectedType(Maybe.nothing()); + } + }} + onSubmit={async function (props) { + try { + setLoading(true); + setResponse(await submit(props)); + setLoading(false); + } catch (e) { + console.error(e); + setResponse(Maybe.nothing()); + setLoading(false); + } + }} + /> + {error != null ? ( + + ) : null} + {response.isJust ? ( + + ) : null} +
    +
    + + ); }; export default Index; diff --git a/src/rdap.integration.test.ts b/src/rdap.integration.test.ts index 95b2b36..6b7c37d 100644 --- a/src/rdap.integration.test.ts +++ b/src/rdap.integration.test.ts @@ -11,43 +11,43 @@ import { registryURLs } from "./constants"; const registryCache = new Map(); async function getRealRegistry(type: RootRegistryType): Promise { - if (registryCache.has(type)) { - return registryCache.get(type)!; - } + if (registryCache.has(type)) { + return registryCache.get(type)!; + } - const response = await fetch(registryURLs[type]); - if (!response.ok) { - throw new Error(`Failed to fetch ${type} registry: ${response.statusText}`); - } + const response = await fetch(registryURLs[type]); + if (!response.ok) { + throw new Error(`Failed to fetch ${type} registry: ${response.statusText}`); + } - const data = (await response.json()) as Register; - registryCache.set(type, data); - return data; + const data = (await response.json()) as Register; + registryCache.set(type, data); + return data; } describe("getType - Integration tests with real registries", () => { - it("should detect entity with real entity registry", async () => { - // Test with a known entity tag (RIPE) - const result = await getType("TEST-RIPE", getRealRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("entity"); - } - }, 10000); // Longer timeout for network call + it("should detect entity with real entity registry", async () => { + // Test with a known entity tag (RIPE) + const result = await getType("TEST-RIPE", getRealRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("entity"); + } + }, 10000); // Longer timeout for network call - it("should detect entity with ARIN tag", async () => { - const result = await getType("NET-ARIN", getRealRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("entity"); - } - }, 10000); + it("should detect entity with ARIN tag", async () => { + const result = await getType("NET-ARIN", getRealRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("entity"); + } + }, 10000); - it("should not detect invalid entity tag", async () => { - const result = await getType("INVALID-NOTREAL", getRealRegistry); - // Should either error or detect as something else, but not entity - if (result.isOk) { - expect(result.value).not.toBe("entity"); - } - }, 10000); + it("should not detect invalid entity tag", async () => { + const result = await getType("INVALID-NOTREAL", getRealRegistry); + // Should either error or detect as something else, but not entity + if (result.isOk) { + expect(result.value).not.toBe("entity"); + } + }, 10000); }); diff --git a/src/rdap.test.ts b/src/rdap.test.ts index d2d62fa..6f916a8 100644 --- a/src/rdap.test.ts +++ b/src/rdap.test.ts @@ -4,406 +4,403 @@ import type { Register } from "./types"; // Mock registry getter (matches real IANA structure: [email, tags, urls]) const mockRegistry: Register = { - description: "Test registry", - publication: "2024-01-01", - version: "1.0", - services: [ - [ - ["test@example.com"], // email - ["RIPE", "APNIC"], // tags - ["https://rdap.example.com/"] // urls - ] - ], + description: "Test registry", + publication: "2024-01-01", + version: "1.0", + services: [ + [ + ["test@example.com"], // email + ["RIPE", "APNIC"], // tags + ["https://rdap.example.com/"], // urls + ], + ], }; const mockGetRegistry = vi.fn(() => Promise.resolve(mockRegistry)); describe("getType - IP address detection", () => { - describe("IPv4 detection", () => { - it("should detect standard IPv4 addresses", async () => { - const result = await getType("192.168.1.1", mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("ip4"); - } - }); + describe("IPv4 detection", () => { + it("should detect standard IPv4 addresses", async () => { + const result = await getType("192.168.1.1", mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("ip4"); + } + }); - it("should detect IPv4 with CIDR notation", async () => { - const result = await getType("192.168.1.0/24", mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("ip4"); - } - }); + it("should detect IPv4 with CIDR notation", async () => { + const result = await getType("192.168.1.0/24", mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("ip4"); + } + }); - it("should detect various IPv4 addresses", async () => { - const ips = [ - "8.8.8.8", - "1.1.1.1", - "10.0.0.1", - "172.16.0.1", - "255.255.255.255", - "0.0.0.0", - ]; + it("should detect various IPv4 addresses", async () => { + const ips = [ + "8.8.8.8", + "1.1.1.1", + "10.0.0.1", + "172.16.0.1", + "255.255.255.255", + "0.0.0.0", + ]; - for (const ip of ips) { - const result = await getType(ip, mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("ip4"); - } - } - }); + for (const ip of ips) { + const result = await getType(ip, mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("ip4"); + } + } + }); - it("should detect IPv4 with various CIDR prefixes", async () => { - const cidrs = [ - "192.168.1.0/8", - "10.0.0.0/16", - "172.16.0.0/12", - "8.8.8.0/24", - "1.1.1.1/32", - ]; + it("should detect IPv4 with various CIDR prefixes", async () => { + const cidrs = [ + "192.168.1.0/8", + "10.0.0.0/16", + "172.16.0.0/12", + "8.8.8.0/24", + "1.1.1.1/32", + ]; - for (const cidr of cidrs) { - const result = await getType(cidr, mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("ip4"); - } - } - }); - }); + for (const cidr of cidrs) { + const result = await getType(cidr, mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("ip4"); + } + } + }); + }); - describe("IPv6 detection", () => { - it("should detect standard IPv6 addresses", async () => { - const result = await getType("2001:db8::1", mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("ip6"); - } - }); + describe("IPv6 detection", () => { + it("should detect standard IPv6 addresses", async () => { + const result = await getType("2001:db8::1", mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("ip6"); + } + }); - it("should detect IPv6 with CIDR notation", async () => { - const result = await getType("2001:db8::/32", mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("ip6"); - } - }); + it("should detect IPv6 with CIDR notation", async () => { + const result = await getType("2001:db8::/32", mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("ip6"); + } + }); - it("should detect various IPv6 addresses", async () => { - const ips = [ - "2001:4860:4860::8888", // Google DNS - "2606:4700:4700::1111", // Cloudflare DNS - "::1", // Localhost - "::", // All zeros - "fe80::1", // Link-local - "2001:db8:85a3::8a2e:370:7334", // Full notation - ]; + it("should detect various IPv6 addresses", async () => { + const ips = [ + "2001:4860:4860::8888", // Google DNS + "2606:4700:4700::1111", // Cloudflare DNS + "::1", // Localhost + "::", // All zeros + "fe80::1", // Link-local + "2001:db8:85a3::8a2e:370:7334", // Full notation + ]; - for (const ip of ips) { - const result = await getType(ip, mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("ip6"); - } - } - }); + for (const ip of ips) { + const result = await getType(ip, mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("ip6"); + } + } + }); - it("should detect IPv6 with various CIDR prefixes", async () => { - const cidrs = ["2001:db8::/32", "2001:4860::/32", "fe80::/10", "::1/128"]; + it("should detect IPv6 with various CIDR prefixes", async () => { + const cidrs = ["2001:db8::/32", "2001:4860::/32", "fe80::/10", "::1/128"]; - for (const cidr of cidrs) { - const result = await getType(cidr, mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("ip6"); - } - } - }); - }); + for (const cidr of cidrs) { + const result = await getType(cidr, mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("ip6"); + } + } + }); + }); }); describe("getType - Domain detection", () => { - it("should detect standard domains", async () => { - const result = await getType("example.com", mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("domain"); - } - }); + it("should detect standard domains", async () => { + const result = await getType("example.com", mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("domain"); + } + }); - it("should detect various domain formats", async () => { - const domains = [ - "google.com", - "www.example.com", - "sub.domain.example.com", - "test-domain.com", - "example123.org", - "a.b.c.d.example.net", - ]; + it("should detect various domain formats", async () => { + const domains = [ + "google.com", + "www.example.com", + "sub.domain.example.com", + "test-domain.com", + "example123.org", + "a.b.c.d.example.net", + ]; - for (const domain of domains) { - const result = await getType(domain, mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("domain"); - } - } - }); + for (const domain of domains) { + const result = await getType(domain, mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("domain"); + } + } + }); }); describe("getType - ASN detection", () => { - it("should detect standard ASN format", async () => { - const result = await getType("AS12345", mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("autnum"); - } - }); + it("should detect standard ASN format", async () => { + const result = await getType("AS12345", mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("autnum"); + } + }); - it("should detect various ASN formats", async () => { - const asns = [ - "AS1", - "AS13335", // Cloudflare - "AS15169", // Google - "AS8075", // Microsoft - "AS16509", // Amazon - "AS999999", - ]; + it("should detect various ASN formats", async () => { + const asns = [ + "AS1", + "AS13335", // Cloudflare + "AS15169", // Google + "AS8075", // Microsoft + "AS16509", // Amazon + "AS999999", + ]; - for (const asn of asns) { - const result = await getType(asn, mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("autnum"); - } - } - }); + for (const asn of asns) { + const result = await getType(asn, mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("autnum"); + } + } + }); }); describe("getType - TLD detection", () => { - it("should detect TLD format", async () => { - const result = await getType(".com", mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("tld"); - } - }); + it("should detect TLD format", async () => { + const result = await getType(".com", mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("tld"); + } + }); - it("should detect various TLDs", async () => { - const tlds = [".com", ".org", ".net", ".dev", ".io", ".ai", ".co"]; + it("should detect various TLDs", async () => { + const tlds = [".com", ".org", ".net", ".dev", ".io", ".ai", ".co"]; - for (const tld of tlds) { - const result = await getType(tld, mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("tld"); - } - } - }); + for (const tld of tlds) { + const result = await getType(tld, mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("tld"); + } + } + }); }); describe("getType - URL detection", () => { - it("should detect HTTP URLs", async () => { - const result = await getType("http://example.com", mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("url"); - } - }); + it("should detect HTTP URLs", async () => { + const result = await getType("http://example.com", mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("url"); + } + }); - it("should detect HTTPS URLs", async () => { - const result = await getType("https://example.com", mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("url"); - } - }); + it("should detect HTTPS URLs", async () => { + const result = await getType("https://example.com", mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("url"); + } + }); - it("should detect RDAP URLs", async () => { - const urls = [ - "https://rdap.arin.net/registry/ip/8.8.8.8", - "http://rdap.apnic.net/ip/1.1.1.1", - "https://rdap.org/domain/example.com", - ]; + it("should detect RDAP URLs", async () => { + const urls = [ + "https://rdap.arin.net/registry/ip/8.8.8.8", + "http://rdap.apnic.net/ip/1.1.1.1", + "https://rdap.org/domain/example.com", + ]; - for (const url of urls) { - const result = await getType(url, mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("url"); - } - } - }); + for (const url of urls) { + const result = await getType(url, mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("url"); + } + } + }); }); describe("getType - JSON detection", () => { - it("should detect JSON objects", async () => { - const result = await getType( - '{"objectClassName":"domain"}', - mockGetRegistry - ); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("json"); - } - }); + it("should detect JSON objects", async () => { + const result = await getType('{"objectClassName":"domain"}', mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("json"); + } + }); - it("should detect various JSON formats", async () => { - const jsons = [ - "{}", - '{"key": "value"}', - '{"objectClassName":"ip network"}', - '{"handle":"TEST"}', - ]; + it("should detect various JSON formats", async () => { + const jsons = [ + "{}", + '{"key": "value"}', + '{"objectClassName":"ip network"}', + '{"handle":"TEST"}', + ]; - for (const json of jsons) { - const result = await getType(json, mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("json"); - } - } - }); + for (const json of jsons) { + const result = await getType(json, mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("json"); + } + } + }); }); describe("getType - Invalid inputs", () => { - it("should return error for empty string", async () => { - const result = await getType("", mockGetRegistry); - expect(result.isErr).toBe(true); - }); + it("should return error for empty string", async () => { + const result = await getType("", mockGetRegistry); + expect(result.isErr).toBe(true); + }); - it("should return error for unrecognized format", async () => { - const result = await getType("not-a-valid-input!!@@##", mockGetRegistry); - expect(result.isErr).toBe(true); - }); + it("should return error for unrecognized format", async () => { + const result = await getType("not-a-valid-input!!@@##", mockGetRegistry); + expect(result.isErr).toBe(true); + }); - describe("Invalid IPv4 addresses", () => { - it("should return error for IPv4 with octet > 255", async () => { - const result = await getType("256.1.1.1", mockGetRegistry); - expect(result.isErr).toBe(true); - if (result.isErr) { - expect(result.error.message).toContain("Invalid IPv4 address"); - expect(result.error.message).toContain("octet"); - } - }); + describe("Invalid IPv4 addresses", () => { + it("should return error for IPv4 with octet > 255", async () => { + const result = await getType("256.1.1.1", mockGetRegistry); + expect(result.isErr).toBe(true); + if (result.isErr) { + expect(result.error.message).toContain("Invalid IPv4 address"); + expect(result.error.message).toContain("octet"); + } + }); - it("should return error for IPv4 with octet 999", async () => { - const result = await getType("192.999.1.1", mockGetRegistry); - expect(result.isErr).toBe(true); - if (result.isErr) { - expect(result.error.message).toContain("Invalid IPv4 address"); - } - }); + it("should return error for IPv4 with octet 999", async () => { + const result = await getType("192.999.1.1", mockGetRegistry); + expect(result.isErr).toBe(true); + if (result.isErr) { + expect(result.error.message).toContain("Invalid IPv4 address"); + } + }); - it("should return error for IPv4 with invalid CIDR prefix", async () => { - const result = await getType("192.168.1.1/33", mockGetRegistry); - expect(result.isErr).toBe(true); - if (result.isErr) { - expect(result.error.message).toContain("CIDR prefix"); - } - }); + it("should return error for IPv4 with invalid CIDR prefix", async () => { + const result = await getType("192.168.1.1/33", mockGetRegistry); + expect(result.isErr).toBe(true); + if (result.isErr) { + expect(result.error.message).toContain("CIDR prefix"); + } + }); - it("should return error for IPv4 with negative CIDR", async () => { - const result = await getType("192.168.1.1/-1", mockGetRegistry); - expect(result.isErr).toBe(true); - }); - }); + it("should return error for IPv4 with negative CIDR", async () => { + const result = await getType("192.168.1.1/-1", mockGetRegistry); + expect(result.isErr).toBe(true); + }); + }); - describe("Invalid IPv6 addresses", () => { - it("should return error for IPv6 with multiple ::", async () => { - const result = await getType("2001::db8::1", mockGetRegistry); - expect(result.isErr).toBe(true); - if (result.isErr) { - expect(result.error.message).toContain("::"); - } - }); + describe("Invalid IPv6 addresses", () => { + it("should return error for IPv6 with multiple ::", async () => { + const result = await getType("2001::db8::1", mockGetRegistry); + expect(result.isErr).toBe(true); + if (result.isErr) { + expect(result.error.message).toContain("::"); + } + }); - it("should return error for IPv6 with invalid CIDR prefix", async () => { - const result = await getType("2001:db8::1/129", mockGetRegistry); - expect(result.isErr).toBe(true); - if (result.isErr) { - expect(result.error.message).toContain("CIDR prefix"); - } - }); + it("should return error for IPv6 with invalid CIDR prefix", async () => { + const result = await getType("2001:db8::1/129", mockGetRegistry); + expect(result.isErr).toBe(true); + if (result.isErr) { + expect(result.error.message).toContain("CIDR prefix"); + } + }); - it("should not match completely invalid hex strings as IPv6", async () => { - // "gggg" doesn't match the basic IPv6 pattern, so it won't be detected as IPv6 - const result = await getType("gggg::1", mockGetRegistry); - expect(result.isErr).toBe(true); - // Won't have IPv6-specific error since it didn't match the pattern - if (result.isErr) { - expect(result.error.message).toContain("No patterns matched"); - } - }); - }); + it("should not match completely invalid hex strings as IPv6", async () => { + // "gggg" doesn't match the basic IPv6 pattern, so it won't be detected as IPv6 + const result = await getType("gggg::1", mockGetRegistry); + expect(result.isErr).toBe(true); + // Won't have IPv6-specific error since it didn't match the pattern + if (result.isErr) { + expect(result.error.message).toContain("No patterns matched"); + } + }); + }); }); describe("getType - Type detection priority", () => { - it("should detect URL before domain", async () => { - const result = await getType("https://example.com", mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("url"); - } - }); + it("should detect URL before domain", async () => { + const result = await getType("https://example.com", mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("url"); + } + }); - it("should detect JSON before domain", async () => { - const result = await getType('{"key":"value"}', mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("json"); - } - }); + it("should detect JSON before domain", async () => { + const result = await getType('{"key":"value"}', mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("json"); + } + }); - it("should detect TLD before domain", async () => { - const result = await getType(".com", mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("tld"); - } - }); + it("should detect TLD before domain", async () => { + const result = await getType(".com", mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("tld"); + } + }); - it("should detect IP before domain", async () => { - const result = await getType("8.8.8.8", mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("ip4"); - } - }); + it("should detect IP before domain", async () => { + const result = await getType("8.8.8.8", mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("ip4"); + } + }); }); describe("getType - Case sensitivity", () => { - it("should detect uppercase domains", async () => { - const result = await getType("GOOGLE.COM", mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("domain"); - } - }); + it("should detect uppercase domains", async () => { + const result = await getType("GOOGLE.COM", mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("domain"); + } + }); - it("should detect mixed case domains", async () => { - const result = await getType("GoOgLe.CoM", mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("domain"); - } - }); + it("should detect mixed case domains", async () => { + const result = await getType("GoOgLe.CoM", mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("domain"); + } + }); - it("should detect lowercase ASN", async () => { - const result = await getType("as12345", mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("autnum"); - } - }); + it("should detect lowercase ASN", async () => { + const result = await getType("as12345", mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("autnum"); + } + }); - it("should detect uppercase ASN", async () => { - const result = await getType("AS12345", mockGetRegistry); - expect(result.isOk).toBe(true); - if (result.isOk) { - expect(result.value).toBe("autnum"); - } - }); + it("should detect uppercase ASN", async () => { + const result = await getType("AS12345", mockGetRegistry); + expect(result.isOk).toBe(true); + if (result.isOk) { + expect(result.value).toBe("autnum"); + } + }); }); diff --git a/src/rdap.ts b/src/rdap.ts index 06e80e5..264d9c2 100644 --- a/src/rdap.ts +++ b/src/rdap.ts @@ -10,11 +10,11 @@ import { Result } from "true-myth"; // }; export function domainMatchPredicate(domain: string): (tld: string) => boolean { - return (tld) => domainMatch(tld, domain); + return (tld) => domainMatch(tld, domain); } export function domainMatch(tld: string, domain: string): boolean { - return domain.toUpperCase().endsWith(`.${tld.toUpperCase()}`); + return domain.toUpperCase().endsWith(`.${tld.toUpperCase()}`); } /* @@ -43,10 +43,10 @@ export function ipMatch(prefix: string, ip: string) { // return the first HTTPS url, or the first URL export function getBestURL(urls: [string, ...string[]]): string { - urls.forEach((url) => { - if (url.startsWith("https://")) return url; - }); - return urls[0]; + urls.forEach((url) => { + if (url.startsWith("https://")) return url; + }); + return urls[0]; } // given a URL, injects that URL into the query input, @@ -758,8 +758,8 @@ export function createRDAPLink(url, title) { */ type ValidatorArgs = { - value: string; - getRegistry: (type: RootRegistryType) => Promise; + value: string; + getRegistry: (type: RootRegistryType) => Promise; }; /** @@ -774,128 +774,114 @@ type ValidatorResult = boolean | string; * Type validators in priority order (most specific to most generic). * Order matters: url/json/tld are checked before domain to avoid false matches. */ -const TypeValidators = new Map< - TargetType, - (args: ValidatorArgs) => Promise ->([ - ["url", ({ value }) => Promise.resolve(/^https?:/.test(value))], - ["json", ({ value }) => Promise.resolve(/^{/.test(value))], - ["tld", ({ value }) => Promise.resolve(/^\.\w+$/.test(value))], - [ - "ip4", - ({ value }) => { - // Basic format check - const match = value.match( - /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})(\/\d{1,2})?$/ - ); - if (!match) return Promise.resolve(false); +const TypeValidators = new Map Promise>([ + ["url", ({ value }) => Promise.resolve(/^https?:/.test(value))], + ["json", ({ value }) => Promise.resolve(/^{/.test(value))], + ["tld", ({ value }) => Promise.resolve(/^\.\w+$/.test(value))], + [ + "ip4", + ({ value }) => { + // Basic format check + const match = value.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})(\/\d{1,2})?$/); + if (!match) return Promise.resolve(false); - // Validate each octet is 0-255 - const octets = [match[1], match[2], match[3], match[4]]; - for (let i = 0; i < octets.length; i++) { - const octet = parseInt(octets[i] ?? "", 10); - if (isNaN(octet) || octet < 0 || octet > 255) { - return Promise.resolve( - `Invalid IPv4 address: octet ${i + 1} (${octets[i] ?? 'undefined'}) must be 0-255` - ); - } - } + // Validate each octet is 0-255 + const octets = [match[1], match[2], match[3], match[4]]; + for (let i = 0; i < octets.length; i++) { + const octet = parseInt(octets[i] ?? "", 10); + if (isNaN(octet) || octet < 0 || octet > 255) { + return Promise.resolve( + `Invalid IPv4 address: octet ${i + 1} (${octets[i] ?? "undefined"}) must be 0-255` + ); + } + } - // Validate CIDR prefix if present - if (match[5]) { - const prefix = parseInt(match[5].substring(1), 10); - if (isNaN(prefix) || prefix < 0 || prefix > 32) { - return Promise.resolve( - "Invalid IPv4 address: CIDR prefix must be 0-32" - ); - } - } + // Validate CIDR prefix if present + if (match[5]) { + const prefix = parseInt(match[5].substring(1), 10); + if (isNaN(prefix) || prefix < 0 || prefix > 32) { + return Promise.resolve("Invalid IPv4 address: CIDR prefix must be 0-32"); + } + } - return Promise.resolve(true); - }, - ], - [ - "ip6", - ({ value }) => { - // Basic format check (hex characters, colons, optional CIDR) - const match = value.match(/^([0-9a-fA-F:]+)(\/\d{1,3})?$/); - if (!match) return Promise.resolve(false); + return Promise.resolve(true); + }, + ], + [ + "ip6", + ({ value }) => { + // Basic format check (hex characters, colons, optional CIDR) + const match = value.match(/^([0-9a-fA-F:]+)(\/\d{1,3})?$/); + if (!match) return Promise.resolve(false); - const ipPart = match[1] ?? ""; + const ipPart = match[1] ?? ""; - // Check for invalid characters - if (!/^[0-9a-fA-F:]+$/.test(ipPart)) { - return Promise.resolve( - "Invalid IPv6 address: contains invalid characters" - ); - } + // Check for invalid characters + if (!/^[0-9a-fA-F:]+$/.test(ipPart)) { + return Promise.resolve("Invalid IPv6 address: contains invalid characters"); + } - // Validate double :: only appears once - const doubleColonCount = (ipPart.match(/::/g) || []).length; - if (doubleColonCount > 1) { - return Promise.resolve("Invalid IPv6 address: :: can only appear once"); - } + // Validate double :: only appears once + const doubleColonCount = (ipPart.match(/::/g) || []).length; + if (doubleColonCount > 1) { + return Promise.resolve("Invalid IPv6 address: :: can only appear once"); + } - // Validate CIDR prefix if present - if (match[2]) { - const prefix = parseInt(match[2].substring(1), 10); - if (isNaN(prefix) || prefix < 0 || prefix > 128) { - return Promise.resolve( - "Invalid IPv6 address: CIDR prefix must be 0-128" - ); - } - } + // Validate CIDR prefix if present + if (match[2]) { + const prefix = parseInt(match[2].substring(1), 10); + if (isNaN(prefix) || prefix < 0 || prefix > 128) { + return Promise.resolve("Invalid IPv6 address: CIDR prefix must be 0-128"); + } + } - return Promise.resolve(true); - }, - ], - ["autnum", ({ value }) => Promise.resolve(/^AS\d+$/i.test(value))], - [ - "entity", - async ({ value, getRegistry }) => { - // Ensure the entity handle is in the correct format - const result = value.match(/^\w+-(\w+)$/); - if (result === null || result.length <= 1 || result[1] == undefined) - return false; + return Promise.resolve(true); + }, + ], + ["autnum", ({ value }) => Promise.resolve(/^AS\d+$/i.test(value))], + [ + "entity", + async ({ value, getRegistry }) => { + // Ensure the entity handle is in the correct format + const result = value.match(/^\w+-(\w+)$/); + if (result === null || result.length <= 1 || result[1] == undefined) return false; - // Check if the entity object tag is real - try { - const registry = await getRegistry("entity"); + // Check if the entity object tag is real + try { + const registry = await getRegistry("entity"); - // Check each service to see if tag matches - // Entity registry structure: [email, tags, urls] - for (const service of registry.services) { - const tags = service[1]; // Tags are at index 1 (0=email, 1=tags, 2=urls) - if ( - tags.some( - (tag) => tag.toUpperCase() === (result[1] as string).toUpperCase() - ) - ) - return true; - } + // Check each service to see if tag matches + // Entity registry structure: [email, tags, urls] + for (const service of registry.services) { + const tags = service[1]; // Tags are at index 1 (0=email, 1=tags, 2=urls) + if ( + tags.some( + (tag) => tag.toUpperCase() === (result[1] as string).toUpperCase() + ) + ) + return true; + } - return false; - } catch (e) { - console.error( - new Error("Failed to fetch entity registry", { cause: e }) - ); - return false; - } - }, - ], - [ - "domain", - ({ value }) => { - // Case-insensitive domain matching with support for multiple labels - // Matches: example.com, www.example.com, a.b.c.d.example.net, etc. - return Promise.resolve( - /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+$/i.test( - value - ) - ); - }, - ], - ["registrar", () => Promise.resolve(false)], + return false; + } catch (e) { + console.error(new Error("Failed to fetch entity registry", { cause: e })); + return false; + } + }, + ], + [ + "domain", + ({ value }) => { + // Case-insensitive domain matching with support for multiple labels + // Matches: example.com, www.example.com, a.b.c.d.example.net, etc. + return Promise.resolve( + /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+$/i.test( + value + ) + ); + }, + ], + ["registrar", () => Promise.resolve(false)], ]); /** @@ -910,23 +896,23 @@ const TypeValidators = new Map< * or an `Error` if the value is invalid or no patterns match. */ export async function getType( - value: string, - getRegistry: (type: RootRegistryType) => Promise + value: string, + getRegistry: (type: RootRegistryType) => Promise ): Promise> { - for (const [type, validator] of TypeValidators.entries()) { - const result = await validator({ value, getRegistry }); + for (const [type, validator] of TypeValidators.entries()) { + const result = await validator({ value, getRegistry }); - if (result === false) { - // Didn't match this type, try next validator - continue; - } else if (result === true) { - // Matched and valid - return Result.ok(type); - } else { - // Matched but invalid (result is error message) - return Result.err(new Error(result)); - } - } + if (result === false) { + // Didn't match this type, try next validator + continue; + } else if (result === true) { + // Matched and valid + return Result.ok(type); + } else { + // Matched but invalid (result is error message) + return Result.err(new Error(result)); + } + } - return Result.err(new Error("No patterns matched the input")); + return Result.err(new Error("No patterns matched the input")); } diff --git a/src/schema.ts b/src/schema.ts index c8d66c8..b8c0462 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -1,166 +1,160 @@ import { z } from "zod"; export const TargetTypeEnum = z.enum([ - "autnum", - "domain", - "ip4", - "ip6", - "entity", - "url", - "tld", - "registrar", - "json", + "autnum", + "domain", + "ip4", + "ip6", + "entity", + "url", + "tld", + "registrar", + "json", ]); -export const RootRegistryEnum = z.enum([ - "autnum", - "domain", - "ip4", - "ip6", - "entity", -]); +export const RootRegistryEnum = z.enum(["autnum", "domain", "ip4", "ip6", "entity"]); export const StatusEnum = z.enum([ - "validated", - "renew prohibited", - "update prohibited", - "transfer prohibited", - "delete prohibited", - "proxy", - "private", - "removed", - "obscured", - "associated", - "active", - "inactive", - "locked", - "pending create", - "pending renew", - "pending transfer", - "pending update", - "pending delete", - "add period", - "auto renew period", - "client delete prohibited", - "client hold", - "client renew prohibited", - "client transfer prohibited", - "client update prohibited", - "pending restore", - "redemption period", - "renew period", - "server delete prohibited", - "server renew prohibited", - "server transfer prohibited", - "server update prohibited", - "server hold", - "transfer period", + "validated", + "renew prohibited", + "update prohibited", + "transfer prohibited", + "delete prohibited", + "proxy", + "private", + "removed", + "obscured", + "associated", + "active", + "inactive", + "locked", + "pending create", + "pending renew", + "pending transfer", + "pending update", + "pending delete", + "add period", + "auto renew period", + "client delete prohibited", + "client hold", + "client renew prohibited", + "client transfer prohibited", + "client update prohibited", + "pending restore", + "redemption period", + "renew period", + "server delete prohibited", + "server renew prohibited", + "server transfer prohibited", + "server update prohibited", + "server hold", + "transfer period", ]); export const LinkSchema = z.object({ - value: z.string().optional(), // de-facto optional - rel: z.string().optional(), // de-facto optional - href: z.string(), - hrefLang: z.array(z.string()).optional(), - title: z.string().optional(), - media: z.string().optional(), - type: z.string().optional(), + value: z.string().optional(), // de-facto optional + rel: z.string().optional(), // de-facto optional + href: z.string(), + hrefLang: z.array(z.string()).optional(), + title: z.string().optional(), + media: z.string().optional(), + type: z.string().optional(), }); export const EntitySchema = z.object({ - objectClassName: z.literal("entity"), - handle: z.string().optional(), - roles: z.array(z.string()), - publicIds: z - .array( - z.object({ - type: z.string(), - identifier: z.string(), - }) - ) - .optional(), + objectClassName: z.literal("entity"), + handle: z.string().optional(), + roles: z.array(z.string()), + publicIds: z + .array( + z.object({ + type: z.string(), + identifier: z.string(), + }) + ) + .optional(), }); export const NameserverSchema = z.object({ - objectClassName: z.literal("nameserver"), - ldhName: z.string(), + objectClassName: z.literal("nameserver"), + ldhName: z.string(), }); export const EventSchema = z.object({ - eventAction: z.string(), - eventActor: z.string().optional(), - eventDate: z.string(), + eventAction: z.string(), + eventActor: z.string().optional(), + eventDate: z.string(), }); export const NoticeSchema = z.object({ - description: z.string().array(), // de jure required - title: z.string().optional(), - links: z.array(LinkSchema).optional(), + description: z.string().array(), // de jure required + title: z.string().optional(), + links: z.array(LinkSchema).optional(), }); export type Notice = z.infer; export const IpNetworkSchema = z.object({ - objectClassName: z.literal("ip network"), - handle: z.string(), - startAddress: z.string(), - endAddress: z.string(), - ipVersion: z.enum(["v4", "v6"]), - name: z.string(), - type: z.string(), - country: z.string().optional(), - parentHandle: z.string().optional(), - status: z.string().array(), - entities: z.array(EntitySchema).optional(), - remarks: z.any().optional(), - links: z.any().optional(), - port43: z.any().optional(), - events: z.array(EventSchema), + objectClassName: z.literal("ip network"), + handle: z.string(), + startAddress: z.string(), + endAddress: z.string(), + ipVersion: z.enum(["v4", "v6"]), + name: z.string(), + type: z.string(), + country: z.string().optional(), + parentHandle: z.string().optional(), + status: z.string().array(), + entities: z.array(EntitySchema).optional(), + remarks: z.any().optional(), + links: z.any().optional(), + port43: z.any().optional(), + events: z.array(EventSchema), }); export const AutonomousNumberSchema = z.object({ - objectClassName: z.literal("autnum"), - handle: z.string(), - startAutnum: z.number().positive(), // TODO: 32bit - endAutnum: z.number().positive(), // TODO: 32bit - name: z.string(), - type: z.string(), - status: z.array(z.string()), - country: z.string().length(2), - events: z.array(EventSchema), - entities: z.array(EntitySchema), - roles: z.array(z.string()), - links: z.array(LinkSchema), + objectClassName: z.literal("autnum"), + handle: z.string(), + startAutnum: z.number().positive(), // TODO: 32bit + endAutnum: z.number().positive(), // TODO: 32bit + name: z.string(), + type: z.string(), + status: z.array(z.string()), + country: z.string().length(2), + events: z.array(EventSchema), + entities: z.array(EntitySchema), + roles: z.array(z.string()), + links: z.array(LinkSchema), }); export const DomainSchema = z.object({ - objectClassName: z.literal("domain"), - handle: z.string(), - ldhName: z.string(), - unicodeName: z.string().optional(), - links: z.array(LinkSchema).optional(), - status: z.array(StatusEnum), - entities: z.array(EntitySchema), - events: z.array(EventSchema), - secureDNS: z.any(), // TODO: Complete schema - nameservers: z.array(NameserverSchema), - rdapConformance: z.string().array(), // TODO: Complete - notices: z.array(NoticeSchema), - network: IpNetworkSchema.optional(), + objectClassName: z.literal("domain"), + handle: z.string(), + ldhName: z.string(), + unicodeName: z.string().optional(), + links: z.array(LinkSchema).optional(), + status: z.array(StatusEnum), + entities: z.array(EntitySchema), + events: z.array(EventSchema), + secureDNS: z.any(), // TODO: Complete schema + nameservers: z.array(NameserverSchema), + rdapConformance: z.string().array(), // TODO: Complete + notices: z.array(NoticeSchema), + network: IpNetworkSchema.optional(), }); const RegistrarSchema = z - .tuple([z.array(z.string()).min(1), z.array(z.string()).min(1)]) - .or( - z.tuple([ - z.array(z.string()).min(1), - z.array(z.string()).min(1), - z.array(z.string()).min(1), - ]) - ); + .tuple([z.array(z.string()).min(1), z.array(z.string()).min(1)]) + .or( + z.tuple([ + z.array(z.string()).min(1), + z.array(z.string()).min(1), + z.array(z.string()).min(1), + ]) + ); export const RegisterSchema = z.object({ - description: z.string(), - publication: z.string(), - services: z.array(RegistrarSchema), - version: z.string(), + description: z.string(), + publication: z.string(), + services: z.array(RegistrarSchema), + version: z.string(), }); diff --git a/src/styles/globals.css b/src/styles/globals.css index aa29e4b..74e9e10 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -1,33 +1,37 @@ @import "tailwindcss"; @theme { - --font-sans: "Inter var", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - --color-zinc-850: #1D1D20; + --font-sans: + "Inter var", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", + "Segoe UI Symbol", "Noto Color Emoji"; + --font-mono: + "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", + "Courier New", monospace; + --color-zinc-850: #1d1d20; } dd { - margin: 0.5em 0 1em 2em; + margin: 0.5em 0 1em 2em; } .dashed { - border-bottom: 1px dashed silver; + border-bottom: 1px dashed silver; } body { - color-scheme: dark; - @apply bg-zinc-900 font-sans text-white; + color-scheme: dark; + @apply bg-zinc-900 font-sans text-white; } dd, dl { - white-space: nowrap; + white-space: nowrap; } dl { - margin: 0; + margin: 0; } .scrollbar-thin { - scrollbar-width: thin; + scrollbar-width: thin; } diff --git a/src/types.ts b/src/types.ts index 5535735..0e9afcc 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,16 +1,16 @@ import type { z } from "zod"; import type { - AutonomousNumberSchema, - DomainSchema, - EntitySchema, - EventSchema, - IpNetworkSchema, - LinkSchema, - NameserverSchema, - TargetTypeEnum, - RegisterSchema, - StatusEnum, - RootRegistryEnum, + AutonomousNumberSchema, + DomainSchema, + EntitySchema, + EventSchema, + IpNetworkSchema, + LinkSchema, + NameserverSchema, + TargetTypeEnum, + RegisterSchema, + StatusEnum, + RootRegistryEnum, } from "@/schema"; // All precise target types that can be placed in the search bar. @@ -33,7 +33,7 @@ export type Register = z.infer; export type Domain = z.infer; export type SubmitProps = { - target: string; - requestJSContact: boolean; - followReferral: boolean; + target: string; + requestJSContact: boolean; + followReferral: boolean; }; diff --git a/tsconfig.json b/tsconfig.json index efb7e04..fba2e56 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,26 +1,26 @@ { - "compilerOptions": { - "target": "es2017", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "incremental": true, - "noUncheckedIndexedAccess": true, - "baseUrl": "./src/", - "paths": { - "@/config/*": ["../config/*"], - "@/*": ["./*"] - } - }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.cjs", "**/*.mjs"], - "exclude": ["node_modules"] + "compilerOptions": { + "target": "es2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "noUncheckedIndexedAccess": true, + "baseUrl": "./src/", + "paths": { + "@/config/*": ["../config/*"], + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.cjs", "**/*.mjs"], + "exclude": ["node_modules"] } diff --git a/vitest.config.ts b/vitest.config.ts index c86d489..dc01493 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -2,14 +2,14 @@ import { defineConfig } from "vitest/config"; import path from "path"; export default defineConfig({ - test: { - globals: true, - environment: "happy-dom", - setupFiles: ["./src/test/setup.ts"], - }, - resolve: { - alias: { - "@": path.resolve(__dirname, "./src"), - }, - }, + test: { + globals: true, + environment: "happy-dom", + setupFiles: ["./src/test/setup.ts"], + }, + resolve: { + alias: { + "@": path.resolve(__dirname, "./src"), + }, + }, });