diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b8b583..7619b6e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,34 +1,80 @@ name: CI on: + push: + branches: [main] pull_request: branches: [main] concurrency: - group: ci-${{ github.head_ref }} + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: + contents: read + jobs: - quality: - name: Quality Gates + lint: + name: Lint runs-on: ubuntu-latest + timeout-minutes: 15 steps: - - uses: actions/checkout@v4 - - - uses: pnpm/action-setup@v4 - - - uses: actions/setup-node@v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 + - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version-file: .node-version cache: pnpm - - run: pnpm install --frozen-lockfile + - run: pnpm lint - - name: Lint - run: pnpm lint + typecheck: + name: Typecheck + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 + - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 + with: + node-version-file: .node-version + cache: pnpm + - run: pnpm install --frozen-lockfile + - run: pnpm typecheck:ci - - name: Typecheck - run: pnpm exec tsc --noEmit + test: + name: Test + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 + - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 + with: + node-version-file: .node-version + cache: pnpm + - run: pnpm install --frozen-lockfile + # Enforces the 100% coverage gate (thresholds live in vitest.config.ts). + - run: pnpm test:coverage - - name: Test - run: pnpm test:once + build: + name: Build + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 + - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 + with: + node-version-file: .node-version + cache: pnpm + - run: pnpm install --frozen-lockfile + - run: pnpm build diff --git a/.github/workflows/leak-gate.yml b/.github/workflows/leak-gate.yml index f955f93..036671e 100644 --- a/.github/workflows/leak-gate.yml +++ b/.github/workflows/leak-gate.yml @@ -8,4 +8,5 @@ permissions: jobs: leak-gate: uses: vllnt/.github/.github/workflows/leak-gate.yml@main - secrets: inherit + secrets: + LEAK_GATE_EXTRA_PATTERNS: ${{ secrets.LEAK_GATE_EXTRA_PATTERNS }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8665a52..0109949 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,186 +1,143 @@ name: Publish +# Canary: every push to main touching the package, or a manual run on main. +# Stable (@latest): only a pushed vX.Y.Z tag whose commit is on main and matches +# package.json and a CHANGELOG.md entry. Both use npm trusted +# publishing (OIDC) with provenance; this workflow holds no npm token. + on: push: branches: [main] + tags: ["v*.*.*"] + # Not evaluated for tag pushes. paths: - "src/**" - "package.json" + - "tsup.config.ts" - "pnpm-lock.yaml" - ".github/workflows/publish.yml" workflow_dispatch: - inputs: - bump: - description: "Version bump type" - required: true - type: choice - options: - - patch - - minor - - major concurrency: - group: publish-${{ github.event_name }} - cancel-in-progress: true + group: publish-${{ github.ref }} + cancel-in-progress: false permissions: - contents: write - id-token: write + contents: read jobs: quality: name: Quality Gates runs-on: ubuntu-latest + timeout-minutes: 15 steps: - - uses: actions/checkout@v4 - - - uses: pnpm/action-setup@v4 - - - uses: actions/setup-node@v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 + - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version-file: .node-version cache: pnpm - - run: pnpm install --frozen-lockfile - - - name: Lint - run: pnpm lint - - - name: Typecheck - run: pnpm exec tsc --noEmit - - - name: Test - run: pnpm test:once + - run: pnpm lint + - run: pnpm typecheck:ci + - run: pnpm test:coverage + - run: pnpm build canary: name: Publish Canary needs: quality - if: github.event_name == 'push' + if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest + timeout-minutes: 10 permissions: - id-token: write contents: read + id-token: write steps: - - uses: actions/checkout@v4 - - - uses: pnpm/action-setup@v4 - - - uses: actions/setup-node@v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 + - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version-file: .node-version cache: pnpm registry-url: https://registry.npmjs.org - - run: pnpm install --frozen-lockfile - - - name: Upgrade npm for OIDC support - run: npm install -g npm@latest - + - run: pnpm build - name: Publish canary run: | + set -euo pipefail sed -i '/_authToken/d' "$NPM_CONFIG_USERCONFIG" unset NODE_AUTH_TOKEN + + PKG=$(node -p "require('./package.json').name") BASE_VERSION=$(node -p "require('./package.json').version") - SHORT_SHA=$(echo "$GITHUB_SHA" | cut -c1-7) - CANARY_VERSION="${BASE_VERSION}-canary.${SHORT_SHA}" - npm version "$CANARY_VERSION" --no-git-tag-version - TARBALL=$(pnpm pack --pack-destination /tmp | tail -1) - npm publish "$TARBALL" --tag canary --provenance --access public + # A canary of an already-published version is work toward the next patch, so it + # must sort above that release. + if [ -n "$(npm view "${PKG}@${BASE_VERSION}" version 2>/dev/null || true)" ]; then + BASE_VERSION=$(node -e "const v=require('./package.json').version.split('.').map(Number); console.log(v[0]+'.'+v[1]+'.'+(v[2]+1))") + fi + CANARY_VERSION="${BASE_VERSION}-canary.${GITHUB_SHA::7}" + npm version "$CANARY_VERSION" --no-git-tag-version --ignore-scripts + npm publish --tag canary --provenance --access public --ignore-scripts release: name: Publish Release needs: quality - if: github.event_name == 'workflow_dispatch' + if: startsWith(github.ref, 'refs/tags/v') runs-on: ubuntu-latest + timeout-minutes: 10 permissions: contents: write id-token: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 - token: ${{ secrets.RELEASE_PAT }} - - - uses: pnpm/action-setup@v4 - - - uses: actions/setup-node@v4 + persist-credentials: false + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 + - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version-file: .node-version cache: pnpm registry-url: https://registry.npmjs.org - - run: pnpm install --frozen-lockfile - - - name: Bump version - id: version + - name: Verify release tag + env: + TAG: ${{ github.ref_name }} run: | - npm version ${{ inputs.bump }} --no-git-tag-version + set -euo pipefail VERSION=$(node -p "require('./package.json').version") - echo "version=$VERSION" >> "$GITHUB_OUTPUT" - - - name: Generate changelog - id: changelog - run: | - LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") - if [ -z "$LAST_TAG" ]; then - RANGE="HEAD" - else - RANGE="${LAST_TAG}..HEAD" - fi - - { - echo "body</dev/null || true) - if [ -n "$FEATS" ]; then - echo "### Features" - echo "$FEATS" | sed 's/^/- /' - echo "" - fi + [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo "::error::package.json version $VERSION is not a stable semver"; exit 1; } + [ "$TAG" = "v$VERSION" ] || { echo "::error::tag $TAG does not match package.json version $VERSION"; exit 1; } + git merge-base --is-ancestor "$GITHUB_SHA" origin/main || { echo "::error::$TAG is not on main"; exit 1; } + grep -qF "## [$VERSION]" CHANGELOG.md || { echo "::error::CHANGELOG.md has no [$VERSION] entry"; exit 1; } + awk -v v="$VERSION" '$0 ~ "^## \\[" v "\\]" {f=1; next} /^## \[/ {f=0} f' CHANGELOG.md > "$RUNNER_TEMP/release-notes.md" - FIXES=$(git log "$RANGE" --pretty=format:"%s" --grep="^fix" 2>/dev/null || true) - if [ -n "$FIXES" ]; then - echo "### Bug Fixes" - echo "$FIXES" | sed 's/^/- /' - echo "" - fi - - OTHERS=$(git log "$RANGE" --pretty=format:"%s" --invert-grep --grep="^feat" --grep="^fix" 2>/dev/null || true) - if [ -n "$OTHERS" ]; then - echo "### Other Changes" - echo "$OTHERS" | sed 's/^/- /' - echo "" - fi - - echo "CHANGELOG_EOF" - } >> "$GITHUB_OUTPUT" - - - name: Commit and tag - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git add package.json - git commit -m "chore(release): v${{ steps.version.outputs.version }}" - git tag -a "v${{ steps.version.outputs.version }}" -m "v${{ steps.version.outputs.version }}" - git push origin main --follow-tags - - - name: Upgrade npm for OIDC support - run: npm install -g npm@latest + - run: pnpm install --frozen-lockfile + - run: pnpm build - name: Publish to npm run: | + set -euo pipefail sed -i '/_authToken/d' "$NPM_CONFIG_USERCONFIG" unset NODE_AUTH_TOKEN - TARBALL=$(pnpm pack --pack-destination /tmp | tail -1) - npm publish "$TARBALL" --tag latest --provenance --access public + PKG=$(node -p "require('./package.json').name") + VERSION=$(node -p "require('./package.json').version") + # Re-runs after a later step failed must not try to republish. + if [ -n "$(npm view "${PKG}@${VERSION}" version 2>/dev/null || true)" ]; then + echo "${PKG}@${VERSION} is already published" + else + npm publish --tag latest --provenance --access public --ignore-scripts + fi - name: Create GitHub Release env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ github.token }} + TAG: ${{ github.ref_name }} run: | - cat <<'NOTES_EOF' > /tmp/release-notes.md - ${{ steps.changelog.outputs.body }} - NOTES_EOF - gh release create "v${{ steps.version.outputs.version }}" \ - --title "v${{ steps.version.outputs.version }}" \ - --notes-file /tmp/release-notes.md + PKG=$(node -p "require('./package.json').name") + gh release view "$TAG" >/dev/null 2>&1 \ + || gh release create "$TAG" --verify-tag --title "$PKG $TAG" --notes-file "$RUNNER_TEMP/release-notes.md" diff --git a/.gitignore b/.gitignore index 07d6d5e..33f0edc 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules/ coverage/ *.tsbuildinfo .mcp.json +dist/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..2797e8b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,40 @@ +# Changelog + +All notable changes to this project are documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project +adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.2.0] - Unreleased + +### Changed + +- The package now ships compiled output in `dist` (ESM, CommonJS and type declarations) instead of + raw TypeScript sources, so it works without transpiling `node_modules`. The `.` and `./react` + export subpaths and the `react` peer range are unchanged. `./package.json` is now exported too. +- `track()` ignores events whose name is empty or not a string, and `identify()` ignores an empty + or non-string user id, instead of recording them. + +### Fixed + +- `getConsentFromCookie()` no longer matches look-alike cookie names that merely end with + `vllnt-consent` (for example `evil-vllnt-consent=1`). + +### Security + +- Releases are published from CI with npm trusted publishing and provenance. `@latest` is published + only from a `vX.Y.Z` tag on `main` that matches `package.json` and this changelog. + +## [0.1.1] - 2026-03-10 + +### Added + +- CI and publish workflows. + +## [0.1.0] - 2026-03-05 + +### Added + +- Initial release: event tracking with a pre-initialization queue, two-category consent management + (localStorage and SSR cookie), versioned consent re-prompts, Do Not Track support, tutorial + tracking helpers, and React integration (`AnalyticsProvider`, `useAnalytics`, `useConsent`). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..3e330da --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,48 @@ +# Contributing to @vllnt/analytics + +Thanks for your interest in contributing! + +## Development Setup + +```bash +pnpm install +pnpm lint +pnpm typecheck +pnpm test:coverage +pnpm build +``` + +## Testing + +Tests use [Vitest](https://vitest.dev) with `jsdom`: + +```bash +pnpm test:once # single run +pnpm test # watch mode +pnpm test:coverage # enforced 100% on covered files +``` + +Coverage is gated at 100% (`vitest.config.ts`). Every public function needs a happy-path test and +at least one adversarial test (invalid input, SSR, storage unavailable). + +## Pull Requests + +- Target `main` +- One logical change per PR +- Include tests for new behavior or bug fixes +- Ensure all checks pass: `pnpm lint && pnpm typecheck && pnpm test:coverage && pnpm build` + +## Releases + +Maintainers only. Every publish runs in `.github/workflows/publish.yml` with npm trusted +publishing and provenance; nothing is published from a local machine. + +- Canary (`@canary`): every push to `main` that touches the package publishes one; you can + also run the workflow manually on `main`. +- Stable (`@latest`): merge a PR that sets `package.json` `version` and adds a matching + `## [X.Y.Z]` entry to `CHANGELOG.md`, then push the tag `vX.Y.Z` on that `main` commit. + +## Reporting Issues + +Use [GitHub Issues](https://github.com/vllnt/analytics/issues). For security vulnerabilities, use +[GitHub Security Advisories](https://github.com/vllnt/analytics/security/advisories/new) instead of a public issue. diff --git a/README.md b/README.md index 0506c1f..0d9f6f3 100644 --- a/README.md +++ b/README.md @@ -19,31 +19,33 @@ pnpm add @vllnt/analytics Peer dependency: `react ^18.0.0 || ^19.0.0` +Ships ESM and CommonJS builds with type declarations; no bundler transpilation of `node_modules` is needed. + ## Usage ### React Provider ```tsx -import { AnalyticsProvider } from '@vllnt/analytics/react' +import { AnalyticsProvider } from "@vllnt/analytics/react"; function App() { return ( - + {children} - ) + ); } ``` ### Consent Banner ```tsx -import { useConsent } from '@vllnt/analytics/react' +import { useConsent } from "@vllnt/analytics/react"; function CookieBanner() { - const { hasResponded, isLoading, acceptAll, declineAnalytics } = useConsent() + const { hasResponded, isLoading, acceptAll, declineAnalytics } = useConsent(); - if (isLoading || hasResponded) return null + if (isLoading || hasResponded) return null; return (
@@ -51,20 +53,20 @@ function CookieBanner() {
- ) + ); } ``` ### Track Events ```tsx -import { track, trackPage } from '@vllnt/analytics' +import { track, trackPage } from "@vllnt/analytics"; // Generic event -track('button_click', { id: 'cta-hero' }) +track("button_click", { id: "cta-hero" }); // Page view -trackPage({ path: '/about' }) +trackPage({ path: "/about" }); ``` ### Tutorial Tracking @@ -79,35 +81,35 @@ import { trackTutorialComplete, trackTutorialNavigation, trackTimeSpent, -} from '@vllnt/analytics' +} from "@vllnt/analytics"; trackTutorialStart({ - tutorial_id: 'getting-started', - tutorial_title: 'Getting Started', - locale: 'en', + tutorial_id: "getting-started", + tutorial_title: "Getting Started", + locale: "en", total_sections: 5, -}) +}); trackSectionView({ - tutorial_id: 'getting-started', - tutorial_title: 'Getting Started', - locale: 'en', - section_id: 'intro', + tutorial_id: "getting-started", + tutorial_title: "Getting Started", + locale: "en", + section_id: "intro", section_index: 0, total_sections: 5, -}) +}); ``` ### Identify Users ```tsx -import { identify, resetAnalytics } from '@vllnt/analytics' +import { identify, resetAnalytics } from "@vllnt/analytics"; // After login -identify('user-123', { plan: 'pro' }) +identify("user-123", { plan: "pro" }); // After logout -resetAnalytics() +resetAnalytics(); ``` ### Consent Utilities (non-React) @@ -122,15 +124,15 @@ import { getConsentFromCookie, isDoNotTrackEnabled, needsRePrompt, -} from '@vllnt/analytics' +} from "@vllnt/analytics"; // SSR: read consent from cookie header -const analyticsAllowed = getConsentFromCookie(request.headers.cookie) +const analyticsAllowed = getConsentFromCookie(request.headers.cookie); // Check if re-prompt needed after version bump -const consent = loadConsent() +const consent = loadConsent(); if (consent && needsRePrompt(consent, 2)) { - clearConsent() + clearConsent(); } ``` @@ -138,38 +140,38 @@ if (consent && needsRePrompt(consent, 2)) { ### Core -| Function | Description | -|---|---| -| `initAnalytics(config)` | Initialize analytics singleton | -| `track(name, properties?)` | Track a custom event | -| `trackPage(properties?)` | Track a page view | -| `identify(userId, traits?)` | Identify a user | -| `resetAnalytics()` | Clear user identity (logout) | -| `enableAnalytics(config)` | Re-enable after consent granted | -| `disableAnalytics()` | Disable after consent revoked | -| `isAnalyticsEnabled()` | Check if tracking is active | +| Function | Description | +| --------------------------- | ---------------------------------------------- | +| `initAnalytics(config)` | Initialize analytics singleton | +| `track(name, properties?)` | Track a custom event (empty names are ignored) | +| `trackPage(properties?)` | Track a page view | +| `identify(userId, traits?)` | Identify a user (empty ids are ignored) | +| `resetAnalytics()` | Clear user identity (logout) | +| `enableAnalytics(config)` | Re-enable after consent granted | +| `disableAnalytics()` | Disable after consent revoked | +| `isAnalyticsEnabled()` | Check if tracking is active | ### Consent -| Function | Description | -|---|---| -| `loadConsent()` | Load consent from localStorage | -| `saveConsent(state)` | Persist consent to localStorage + cookie | -| `clearConsent()` | Remove consent (triggers re-prompt) | -| `createAcceptAllConsent()` | Create accept-all consent state | -| `createDeclineAnalyticsConsent()` | Create analytics-declined state | -| `updateConsentCategory(state, category, value)` | Update a single category | -| `needsRePrompt(consent, version)` | Check if version requires re-prompt | -| `getConsentFromCookie(cookieString)` | Parse consent from cookie (SSR) | -| `isDoNotTrackEnabled()` | Check browser DNT setting | +| Function | Description | +| ----------------------------------------------- | ---------------------------------------- | +| `loadConsent()` | Load consent from localStorage | +| `saveConsent(state)` | Persist consent to localStorage + cookie | +| `clearConsent()` | Remove consent (triggers re-prompt) | +| `createAcceptAllConsent()` | Create accept-all consent state | +| `createDeclineAnalyticsConsent()` | Create analytics-declined state | +| `updateConsentCategory(state, category, value)` | Update a single category | +| `needsRePrompt(consent, version)` | Check if version requires re-prompt | +| `getConsentFromCookie(cookieString)` | Parse consent from cookie (SSR) | +| `isDoNotTrackEnabled()` | Check browser DNT setting | ### React (`@vllnt/analytics/react`) -| Export | Description | -|---|---| -| `AnalyticsProvider` | Context provider — wires consent + analytics init | -| `useAnalytics()` | Access full analytics context | -| `useConsent(options?)` | Standalone consent hook for banner UIs | +| Export | Description | +| ---------------------- | ------------------------------------------------- | +| `AnalyticsProvider` | Context provider — wires consent + analytics init | +| `useAnalytics()` | Access full analytics context | +| `useConsent(options?)` | Standalone consent hook for banner UIs | ## How It Works @@ -196,6 +198,8 @@ pnpm test:once # Run tests once pnpm test:coverage # Run with coverage pnpm lint # Lint pnpm lint:fix # Lint + autofix +pnpm typecheck # Type-check +pnpm build # Build dist (ESM, CJS, types) ``` ## License diff --git a/eslint.config.js b/eslint.config.js index cb653e6..89ff99c 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -2,7 +2,7 @@ import { react } from '@vllnt/eslint-config' export default [ { - ignores: ['node_modules/**', 'eslint.config.js', 'coverage/**'], + ignores: ['node_modules/**', 'eslint.config.js', 'coverage/**', 'dist/**'], }, ...react, { diff --git a/package.json b/package.json index 12b8b7f..a9d4da3 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "name": "@vllnt/analytics", - "version": "0.1.1", + "version": "0.2.0", "description": "Lightweight, privacy-first analytics with consent management and React integration", "type": "module", - "packageManager": "pnpm@9.15.4", + "packageManager": "pnpm@10.34.8", "license": "MIT", "repository": { "type": "git", @@ -19,39 +19,49 @@ ], "sideEffects": false, "files": [ - "src", - "!src/__tests__", - "README.md", - "LICENSE" + "dist" ], - "main": "./src/index.ts", - "module": "./src/index.ts", - "types": "./src/index.ts", + "main": "./dist/index.cjs", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", "exports": { ".": { - "types": "./src/index.ts", - "import": "./src/index.ts", - "require": "./src/index.ts" + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } }, "./react": { - "types": "./src/react.tsx", - "import": "./src/react.tsx", - "require": "./src/react.tsx" - } + "import": { + "types": "./dist/react.d.ts", + "default": "./dist/react.js" + }, + "require": { + "types": "./dist/react.d.cts", + "default": "./dist/react.cjs" + } + }, + "./package.json": "./package.json" }, "scripts": { - "clean": "rm -rf node_modules", - "lint": "eslint .", - "lint:fix": "eslint . --fix", + "build": "tsup", + "clean": "rm -rf node_modules dist", + "lint": "eslint --cache --cache-location node_modules/.cache/eslint/ .", + "lint:fix": "eslint --cache --cache-location node_modules/.cache/eslint/ . --fix", "test": "vitest", "test:once": "vitest run", - "test:coverage": "vitest run --coverage" + "test:coverage": "vitest run --coverage", + "typecheck": "tsc --noEmit --incremental --tsBuildInfoFile node_modules/.cache/tsc/typecheck.tsbuildinfo", + "typecheck:ci": "tsc --noEmit --incremental --tsBuildInfoFile node_modules/.cache/tsc/typecheck-ci.tsbuildinfo" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0" }, "devDependencies": { - "eslint": "^9.39.5", "@testing-library/jest-dom": "^7.0.1", "@testing-library/react": "^16.3.3", "@types/node": "^24.19.2", @@ -59,15 +69,24 @@ "@vitest/coverage-v8": "^5.0.5", "@vllnt/eslint-config": "^2.0.0", "@vllnt/typescript": "^2.0.0", + "eslint": "^9.39.5", "jsdom": "^30.1.3", "react": "^19.3.1", + "tsup": "^9.0.0", "typescript": "^6.0.3", "vitest": "^5.0.5" }, "pnpm": { + "minimumReleaseAge": 10080, + "minimumReleaseAgeExclude": [ + "@vllnt/*" + ], "overrides": { "deepmerge-ts@<8.0.0": "8.0.0", "handlebars@<4.7.10": "4.7.10" - } + }, + "ignoredBuiltDependencies": [ + "esbuild" + ] } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bf8b870..4dd1a4e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,12 +42,15 @@ importers: react: specifier: ^19.3.1 version: 19.3.1 + tsup: + specifier: ^9.0.0 + version: 9.0.0(postcss@8.5.31)(typescript@6.0.3) typescript: specifier: ^6.0.3 version: 6.0.3 vitest: specifier: ^5.0.5 - version: 5.0.5(@types/node@24.19.2)(@vitest/coverage-v8@5.0.5)(jsdom@30.1.3)(vite@8.3.5(@types/node@24.19.2)(esbuild@0.27.0)) + version: 5.0.5(@types/node@24.19.2)(@vitest/coverage-v8@5.0.5)(jsdom@30.1.3)(vite@8.3.5(@types/node@24.19.2)(esbuild@0.28.2)) packages: @@ -195,156 +198,312 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.28.2': + resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.27.0': resolution: {integrity: sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==} engines: {node: '>=18'} cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.28.2': + resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.27.0': resolution: {integrity: sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==} engines: {node: '>=18'} cpu: [arm] os: [android] + '@esbuild/android-arm@0.28.2': + resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.27.0': resolution: {integrity: sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==} engines: {node: '>=18'} cpu: [x64] os: [android] + '@esbuild/android-x64@0.28.2': + resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.27.0': resolution: {integrity: sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.28.2': + resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.27.0': resolution: {integrity: sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==} engines: {node: '>=18'} cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.28.2': + resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.27.0': resolution: {integrity: sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.28.2': + resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.27.0': resolution: {integrity: sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.28.2': + resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.27.0': resolution: {integrity: sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==} engines: {node: '>=18'} cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.28.2': + resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.27.0': resolution: {integrity: sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==} engines: {node: '>=18'} cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.28.2': + resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.27.0': resolution: {integrity: sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==} engines: {node: '>=18'} cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.28.2': + resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.27.0': resolution: {integrity: sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.28.2': + resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.27.0': resolution: {integrity: sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.28.2': + resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.27.0': resolution: {integrity: sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.28.2': + resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.27.0': resolution: {integrity: sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.28.2': + resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.27.0': resolution: {integrity: sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==} engines: {node: '>=18'} cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.28.2': + resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.27.0': resolution: {integrity: sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==} engines: {node: '>=18'} cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.28.2': + resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/netbsd-arm64@0.27.0': resolution: {integrity: sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-arm64@0.28.2': + resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.27.0': resolution: {integrity: sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.28.2': + resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/openbsd-arm64@0.27.0': resolution: {integrity: sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.28.2': + resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.27.0': resolution: {integrity: sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.28.2': + resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openharmony-arm64@0.27.0': resolution: {integrity: sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] + '@esbuild/openharmony-arm64@0.28.2': + resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/sunos-x64@0.27.0': resolution: {integrity: sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.28.2': + resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.27.0': resolution: {integrity: sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==} engines: {node: '>=18'} cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.28.2': + resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.27.0': resolution: {integrity: sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.28.2': + resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.27.0': resolution: {integrity: sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==} engines: {node: '>=18'} cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.28.2': + resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.10.1': resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -434,6 +593,13 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + resolution: {integrity: sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==} + engines: {node: ^22.20 || ^24.12 || >=25} + cpu: [x64] + os: [linux] + libc: [glibc] + '@next/eslint-plugin-next@16.4.3': resolution: {integrity: sha512-oxuC06wt/71h6ACYCV9GbKOfnf5a7VlRe/p80YHTut15TNgDEmynDnaq8t/xhy5rlvk7dhItIPs6H3rvIM4kyw==} @@ -497,36 +663,42 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-arm64-musl@1.2.15': resolution: {integrity: sha512-uN26XOnVR7rqQupG3V9oAG2EGGatWjcJHOeDOHUFCh4TBWmP2tHTFoVN13aYbwfm0aHqJAEmcwhAu6sooIjfqQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [musl] '@rolldown/binding-linux-ppc64-gnu@1.2.15': resolution: {integrity: sha512-9nWkcdd+H+XhaA3fQIwpHnKIrfx/PQ6rO0pivr78Og07tXlUlctBBJoEYLPfhTmCR/QxEVdn6uPjHV5SymPhdw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-s390x-gnu@1.2.15': resolution: {integrity: sha512-Ggh0ciFjLwIusLSLz7LZYEaGdxOAUJXI+Qu5JCbhPJJHEt8vP+IjRATTJheiJsdzzTYgUiHUiw3X1Ea3ZpMBdA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] + libc: [glibc] '@rolldown/binding-linux-x64-gnu@1.2.15': resolution: {integrity: sha512-FHg60rS6DBePAYZ9X9F67x+JObFdmgn6tupA1C/a3B+CufFmTD4mX+e8M75tCiAO3foR/6xwVBI0KQiI7sn/rw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-x64-musl@1.2.15': resolution: {integrity: sha512-/cvV/7v5VGz/moiz0XKRj2dE8DzsKyhIu1KpOvJGPjRywg3yscON889UGvAe8zfiJLHup7VLjBduoGWi3HusWQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [musl] '@rolldown/binding-openharmony-arm64@1.2.15': resolution: {integrity: sha512-MyzC5Hrv/sm+Z7TSGKP/j8001RO2xQm1zOpCNVL4j0cY/VhBZviludA2YFQ7i4lH6ZAXxQm0lcMdBzM+99mE7w==} @@ -549,6 +721,144 @@ packages: '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + '@rollup/rollup-android-arm-eabi@4.65.0': + resolution: {integrity: sha512-t2AnVba25baFdq7gczzS13JyVcdubLVbFX1X04mxrggDrTQPUXNv3XM0k9H1hr5tqvg7AVvtQ720QHAj0iYqhg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.65.0': + resolution: {integrity: sha512-dNONUaEwPGhl/nuO2KV8VABWlcjYyU6KtZGN8dQv5OS5JU7JSCxPijVNKXbRJd0iuTzTiJ4TjPt/4bbvDRNiMQ==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.65.0': + resolution: {integrity: sha512-zWQqUH+9zavr2Bxuzfpuv2SIF3X31cr58TvSQC9o1GhpOvkqRwr60GkZdQcudyT/BEgdpdSafjgd2I13MbTL7g==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.65.0': + resolution: {integrity: sha512-RQYreBFWtO4//hIuOaZkYUnF/lPU4lz35dveDimDshWCJvVisVtJ/shmT9bkXK8wFJY+FJF89X6F3Cq1qI3LCw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.65.0': + resolution: {integrity: sha512-A0NfMj6Cuh/9BE5k3awJKv6HoxFVXIcLAb6SZqAMoxUHPA1ZNNK4RsGuUPD/vL7nEAm9LG+ZzXWWTrnC0XtkpA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.65.0': + resolution: {integrity: sha512-BI6/H7q3aP0I7S7GOrjyxTjgEJlSnovXE38+bdxBpuKN6z+d//uF+T+VQ3Q7ltJL/71p17ufartARX9Ja5gOGQ==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.65.0': + resolution: {integrity: sha512-W1vUprf09RWaVWnsFP1vlUkEWJzTZlpObOEAYZXt9Fq0UVAOYEkeGr7wBX7McKqqIQskXdBnpiOMgwPujordgA==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.65.0': + resolution: {integrity: sha512-s8QTcZWgrl9haFzEGIqyE5BWyo89pFDGLMBFZ752WYxk8DJwNU/O1X6pZgPw829d9/sm6CCHyrMP3aurt76p1Q==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.65.0': + resolution: {integrity: sha512-ke1f6dLRzkqveyjjF2fkEwQYZvxHtNNwjegY8nJrwcO0Bdqr8CzFO/pKE0Bns13cx0qz2Ba2Y0Hs3dSwMdnHxA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.65.0': + resolution: {integrity: sha512-926SMeUcy4y18Rz/w2aJiUKOnyAnm6czg72bXkQEaeemsll+Wr0hnsjb2BGRpHvWgwAMYy5ACJCotbvfLCfj0A==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.65.0': + resolution: {integrity: sha512-6T7JNRP1hc4hqF2dUsEoVLOt9K8tmODoCLldtQRYnrddGGQY8Gqe2pM/qMMUo7EMowpP7oa2N95k0/8sYK/ipw==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.65.0': + resolution: {integrity: sha512-SImGFl1Je5kpyAgOCPONVT7h6ASV8mAUwGRjY7eZvVjPg//PfHOXgI+P241RF/+HqsA4PP3qD527ysWFKNQyhQ==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.65.0': + resolution: {integrity: sha512-XeDTbmPF1Yu5asfXGPrMkWoxTDhgryQbmw5Q1+R582O51VNCk23J6f2pDrsAp5YkneBVBwtExx6ZGOb9D2aQ6A==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.65.0': + resolution: {integrity: sha512-VS55JUI/1nsljmV/jfqNkLZXFw8uMO8nNlfoHmSevNf6fO3neuhyDvx9dv3c/Ytt8r/431SL8Qax1dpYvOMZTg==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.65.0': + resolution: {integrity: sha512-Vblw84TbX7RS4la1Y4+yQ7aore/oSyaNtFbppuBr5Vs/4LfBeXKq9dIfoExw6zOvRERqd2rRAjnZRaMhH/0x5w==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.65.0': + resolution: {integrity: sha512-7+gHjtONvKgwR9InTKFaLq53f6YzBJkgeMRBENdWTsQr+spahGqQm8HF5aXpq8i4JaRrOqYyv6Zn1WltDTxaZQ==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.65.0': + resolution: {integrity: sha512-KvNDC0J6s4vVHhtJ6KrDXU0Er0au/hmRIBq/+1maCV8TseGupeKizq0bWcrbp2HE1T8xoknvN0Q/KZ/YF0qX3w==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.65.0': + resolution: {integrity: sha512-GExdD15WPvu/x/drELjVam7vX8EsPgvTMOa/iJL/p/4m/oOqzbb7JXZg8fXR4hlXtg/sF2zyTZ3bR0AEtEMlhQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.65.0': + resolution: {integrity: sha512-8sT9PemIZfIMED4Vd4KGoOXVDj5CMSWNcGdbheRUx9TYS03yOWdT2Q0lXPyEyNBB9YTilwewIQKNIBk6Nxi0Kg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.65.0': + resolution: {integrity: sha512-pO6Ehi0jo5yE0R6PltKhJhv9mlIiyzBJk5N5NUF2OF6xtVKI+1bdXxj/rb0jGXy3hck1ELVT0QPY3tzkAm360w==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.65.0': + resolution: {integrity: sha512-vo7N3/+cjjPDYr8vtxC+qSNgWa9sDvJ7g8N8R5SWqFY4WiNH1ULcAU70SKVyHeiIZOYYwNP9pTipmhN5sl1NPw==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.65.0': + resolution: {integrity: sha512-ArwvgFAbjKcDM7qqotZ4OVYf+UMT9xH2YzkCzK4+AuZSDihMlz1Ekoe4INwBVXzn2mnJzuIYOcSx07iTBijGRQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.65.0': + resolution: {integrity: sha512-QGMmK4/cxKE/36FoLMSHQBCyymv/eFTnsCvvsBF0rgmOvVbvX78VWZRerL3dHa77f4pM6Xcc2iGDhJ10w/svKQ==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.65.0': + resolution: {integrity: sha512-XfKYFWHIPs4HPODIMsGF1IKlS3H1e3WokUZIgpmn4+uNnZBlnRDDHEylNyazbQXwhMKZre68OjtRYbKqb5+j8w==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.65.0': + resolution: {integrity: sha512-wPWi++K4Bs1VR4XihOhfGi4IGX6Cub7FlerTf8X2v+2BtDHddgSqWCXwGD+h2yAe4/niIhNDwAltioOC+wEogQ==} + cpu: [x64] + os: [win32] + '@testing-library/dom@10.4.2': resolution: {integrity: sha512-yzr2S9HyAIdhz2/6qHgbs665Q7PKVcDF05vsOlHPxG1mo36gKVesdYVeDLnXgfjJ03CrKRk08knc6+E/9m8v2Q==} engines: {node: '>=18'} @@ -802,6 +1112,9 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -901,6 +1214,16 @@ packages: resolution: {integrity: sha512-JCWSCdun+4Ovd9BhM/Vtlmwb7oD6Wl4YiPRXXwhAuwlPhW1un/MKgXn7GZoFm53ginnoNzus69V8JWx0K393jg==} engines: {node: '>=18.20'} + bundle-require@5.1.0: + resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + peerDependencies: + esbuild: '>=0.18' + + cac@7.1.0: + resolution: {integrity: sha512-LMDU/U2yA4IPIY8KT0NsNPWEYZvAMkbgyspxJ6btJaYV+WMDOHAZCmB0lsTObiHaRS1V/ztKAl7+TSkTqXyeoA==} + engines: {node: '>=20.19.0'} + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -931,6 +1254,10 @@ packages: change-case@5.4.4: resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + ci-info@4.4.0: resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} engines: {node: '>=8'} @@ -949,9 +1276,20 @@ packages: commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -1129,6 +1467,11 @@ packages: engines: {node: '>=18'} hasBin: true + esbuild@0.28.2: + resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -1357,6 +1700,9 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} + fix-dts-default-cjs-exports@1.0.1: + resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} + flat-cache@4.0.1: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} @@ -1633,6 +1979,10 @@ packages: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + js-tokens@10.0.0: resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} @@ -1724,24 +2074,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.33.0: resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.33.0: resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.33.0: resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.33.0: resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} @@ -1759,6 +2113,17 @@ packages: resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} engines: {node: '>= 12.0.0'} + lilconfig@3.1.4: + resolution: {integrity: sha512-zJZyBmQhArDRefuWTypZxwDpFDIYmrj8ypSBvwGD5P13Y95s6DjzKgu4DVdGbeQa5PDEsAOCAPNk4X+QOR006w==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + load-tsconfig@0.2.5: + resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -1784,6 +2149,9 @@ packages: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magic-string@1.4.3: resolution: {integrity: sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==} @@ -1819,9 +2187,15 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + mlly@1.8.2: + resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} + 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.20: resolution: {integrity: sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -1918,6 +2292,9 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1929,6 +2306,13 @@ packages: resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} engines: {node: '>=12'} + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} @@ -1937,6 +2321,24 @@ packages: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} + peerDependencies: + jiti: '>=1.21.0' + postcss: '>=8.0.9' + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + postcss@8.5.31: resolution: {integrity: sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==} engines: {node: ^10 || ^12 || >=14} @@ -1983,6 +2385,10 @@ packages: resolution: {integrity: sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw==} engines: {node: '>=0.10.0'} + readdirp@5.1.1: + resolution: {integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==} + engines: {node: '>= 20.19.0'} + redent@3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} @@ -2015,6 +2421,10 @@ packages: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + resolve@1.22.13: resolution: {integrity: sha512-Dj/cW9zBV2aRj6XzCxuGIs3ESZKv02jixne3q6jFZYZ3/Tg09gHHviSMDL4kObh9pqSahdRHEnDoaP2gn8YLhA==} engines: {node: '>= 0.4'} @@ -2034,6 +2444,11 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + rollup@4.65.0: + resolution: {integrity: sha512-QVCvQUTGDN6JRwi3jVGCY3H4aTHvi9rX0LyoBMkGUvdh/Z6O51CzcLz85BWZaTCDGhl6ySGqYZs+xI1jmPqGcw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -2151,6 +2566,11 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + sucrase@3.35.1: + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -2163,6 +2583,13 @@ packages: resolution: {integrity: sha512-U+/AXT6LWsxnj2vz9HeUuPWBNgdWxjyIEjEPdbJARC9txmgPzo2VEdqnFo4hHaNyMPtycyVJrSaQYeQv/PoyyA==} engines: {node: ^14.18.0 || >=16.0.0} + 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==} + tinybench@6.2.2: resolution: {integrity: sha512-nIZ2N2dFpjBsg2hgitKu7mA+FvsIJuGJOwysRngmVRRmWgR/UyPjwJj8c/fT/dhm8EkHnF5EOogRf+0Z2qrimA==} engines: {node: '>=20.0.0'} @@ -2202,6 +2629,10 @@ packages: resolution: {integrity: sha512-BcDSDW+SIGWVOns1Zm6Yg91q9AEj7eaID0QDY9oIqJD1A8dscNwMcl9Sms2DeuWCNov1X0/oXanHGYQBSLwZsg==} engines: {node: ^22.14.0 || >=24.0.0} + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + ts-api-utils@2.5.0: resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} engines: {node: '>=18.12'} @@ -2213,6 +2644,28 @@ packages: peerDependencies: typescript: '>=4.0.0' + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + tsup@9.0.0: + resolution: {integrity: sha512-DHgb0bfo2gFKybOa8dRFMiHSqzWWhMkfNK0f902VVquWQtDBWExNgidHszjjDKaFd12pWPgzumpWf9nGr3gzFg==} + engines: {node: ^22.18.0 || ^24.11.0 || >=26.0.0} + hasBin: true + peerDependencies: + '@microsoft/api-extractor': ^7.36.0 + '@swc/core': ^1 + postcss: ^8.4.12 + typescript: '>=4.5.0' + peerDependenciesMeta: + '@microsoft/api-extractor': + optional: true + '@swc/core': + optional: true + postcss: + optional: true + typescript: + optional: true + turbo@2.11.10: resolution: {integrity: sha512-3GJYwig8ygEI89oaOZdduJX8/aw6O9eRPYHoIt6T3FZl9hkKjiyIspufJoVgqXihPoJu8M5CSV0DlZvzfohBCw==} hasBin: true @@ -2249,6 +2702,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + uglify-js@3.19.3: resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} engines: {node: '>=0.8.0'} @@ -2630,81 +3086,159 @@ snapshots: '@esbuild/aix-ppc64@0.27.0': optional: true + '@esbuild/aix-ppc64@0.28.2': + optional: true + '@esbuild/android-arm64@0.27.0': optional: true + '@esbuild/android-arm64@0.28.2': + optional: true + '@esbuild/android-arm@0.27.0': optional: true + '@esbuild/android-arm@0.28.2': + optional: true + '@esbuild/android-x64@0.27.0': optional: true + '@esbuild/android-x64@0.28.2': + optional: true + '@esbuild/darwin-arm64@0.27.0': optional: true + '@esbuild/darwin-arm64@0.28.2': + optional: true + '@esbuild/darwin-x64@0.27.0': optional: true + '@esbuild/darwin-x64@0.28.2': + optional: true + '@esbuild/freebsd-arm64@0.27.0': optional: true + '@esbuild/freebsd-arm64@0.28.2': + optional: true + '@esbuild/freebsd-x64@0.27.0': optional: true + '@esbuild/freebsd-x64@0.28.2': + optional: true + '@esbuild/linux-arm64@0.27.0': optional: true + '@esbuild/linux-arm64@0.28.2': + optional: true + '@esbuild/linux-arm@0.27.0': optional: true + '@esbuild/linux-arm@0.28.2': + optional: true + '@esbuild/linux-ia32@0.27.0': optional: true + '@esbuild/linux-ia32@0.28.2': + optional: true + '@esbuild/linux-loong64@0.27.0': optional: true + '@esbuild/linux-loong64@0.28.2': + optional: true + '@esbuild/linux-mips64el@0.27.0': optional: true + '@esbuild/linux-mips64el@0.28.2': + optional: true + '@esbuild/linux-ppc64@0.27.0': optional: true + '@esbuild/linux-ppc64@0.28.2': + optional: true + '@esbuild/linux-riscv64@0.27.0': optional: true + '@esbuild/linux-riscv64@0.28.2': + optional: true + '@esbuild/linux-s390x@0.27.0': optional: true + '@esbuild/linux-s390x@0.28.2': + optional: true + '@esbuild/linux-x64@0.27.0': optional: true + '@esbuild/linux-x64@0.28.2': + optional: true + '@esbuild/netbsd-arm64@0.27.0': optional: true + '@esbuild/netbsd-arm64@0.28.2': + optional: true + '@esbuild/netbsd-x64@0.27.0': optional: true + '@esbuild/netbsd-x64@0.28.2': + optional: true + '@esbuild/openbsd-arm64@0.27.0': optional: true + '@esbuild/openbsd-arm64@0.28.2': + optional: true + '@esbuild/openbsd-x64@0.27.0': optional: true + '@esbuild/openbsd-x64@0.28.2': + optional: true + '@esbuild/openharmony-arm64@0.27.0': optional: true + '@esbuild/openharmony-arm64@0.28.2': + optional: true + '@esbuild/sunos-x64@0.27.0': optional: true + '@esbuild/sunos-x64@0.28.2': + optional: true + '@esbuild/win32-arm64@0.27.0': optional: true + '@esbuild/win32-arm64@0.28.2': + optional: true + '@esbuild/win32-ia32@0.27.0': optional: true + '@esbuild/win32-ia32@0.28.2': + optional: true + '@esbuild/win32-x64@0.27.0': optional: true + '@esbuild/win32-x64@0.28.2': + optional: true + '@eslint-community/eslint-utils@4.10.1(eslint@9.39.5)': dependencies: eslint: 9.39.5 @@ -2793,6 +3327,9 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.6.0 + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + optional: true + '@next/eslint-plugin-next@16.4.3(eslint@9.39.5)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5) @@ -2863,6 +3400,81 @@ snapshots: '@rolldown/pluginutils@1.0.1': {} + '@rollup/rollup-android-arm-eabi@4.65.0': + optional: true + + '@rollup/rollup-android-arm64@4.65.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.65.0': + optional: true + + '@rollup/rollup-darwin-x64@4.65.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.65.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.65.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.65.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.65.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.65.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.65.0': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.65.0': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.65.0': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.65.0': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.65.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.65.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.65.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.65.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.65.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.65.0': + optional: true + + '@rollup/rollup-openbsd-x64@4.65.0': + optional: true + + '@rollup/rollup-openharmony-arm64@4.65.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.65.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.65.0': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.65.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.65.0': + optional: true + '@testing-library/dom@10.4.2': dependencies: '@babel/code-frame': 7.29.7 @@ -2884,7 +3496,7 @@ snapshots: picocolors: 1.1.1 redent: 3.0.0 optionalDependencies: - vitest: 5.0.5(@types/node@24.19.2)(@vitest/coverage-v8@5.0.5)(jsdom@30.1.3)(vite@8.3.5(@types/node@24.19.2)(esbuild@0.27.0)) + vitest: 5.0.5(@types/node@24.19.2)(@vitest/coverage-v8@5.0.5)(jsdom@30.1.3)(vite@8.3.5(@types/node@24.19.2)(esbuild@0.28.2)) '@testing-library/react@16.3.3(@testing-library/dom@10.4.2)(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)': dependencies: @@ -3086,7 +3698,7 @@ snapshots: obug: 3.0.0 std-env: 4.3.0 tinyrainbow: 3.2.0 - vitest: 5.0.5(@types/node@24.19.2)(@vitest/coverage-v8@5.0.5)(jsdom@30.1.3)(vite@8.3.5(@types/node@24.19.2)(esbuild@0.27.0)) + vitest: 5.0.5(@types/node@24.19.2)(@vitest/coverage-v8@5.0.5)(jsdom@30.1.3)(vite@8.3.5(@types/node@24.19.2)(esbuild@0.28.2)) '@vitest/istanbul-lib-coverage@1.0.2': {} @@ -3094,14 +3706,14 @@ snapshots: dependencies: '@vitest/istanbul-lib-coverage': 1.0.2 - '@vitest/mocker@5.0.5(vite@8.3.5(@types/node@24.19.2)(esbuild@0.27.0))': + '@vitest/mocker@5.0.5(vite@8.3.5(@types/node@24.19.2)(esbuild@0.28.2))': dependencies: '@jridgewell/trace-mapping': 0.3.31 '@vitest/spy': 5.0.5 estree-walker: 3.0.3 magic-string: 1.4.3 optionalDependencies: - vite: 8.3.5(@types/node@24.19.2)(esbuild@0.27.0) + vite: 8.3.5(@types/node@24.19.2)(esbuild@0.28.2) '@vitest/spy@5.0.5': {} @@ -3164,6 +3776,8 @@ snapshots: ansi-styles@5.2.0: {} + any-promise@1.3.0: {} + argparse@2.0.1: {} aria-query@5.3.0: @@ -3282,6 +3896,13 @@ snapshots: builtin-modules@5.4.0: {} + bundle-require@5.1.0(esbuild@0.28.2): + dependencies: + esbuild: 0.28.2 + load-tsconfig: 0.2.5 + + cac@7.1.0: {} + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -3312,6 +3933,10 @@ snapshots: change-case@5.4.4: {} + chokidar@5.0.0: + dependencies: + readdirp: 5.1.1 + ci-info@4.4.0: {} clean-regexp@1.0.0: @@ -3326,8 +3951,14 @@ snapshots: commander@2.20.3: {} + commander@4.1.1: {} + concat-map@0.0.1: {} + confbox@0.1.8: {} + + consola@3.4.2: {} + convert-source-map@2.0.0: {} convex@1.48.0(react@19.3.1): @@ -3583,6 +4214,35 @@ snapshots: '@esbuild/win32-ia32': 0.27.0 '@esbuild/win32-x64': 0.27.0 + esbuild@0.28.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.2 + '@esbuild/android-arm': 0.28.2 + '@esbuild/android-arm64': 0.28.2 + '@esbuild/android-x64': 0.28.2 + '@esbuild/darwin-arm64': 0.28.2 + '@esbuild/darwin-x64': 0.28.2 + '@esbuild/freebsd-arm64': 0.28.2 + '@esbuild/freebsd-x64': 0.28.2 + '@esbuild/linux-arm': 0.28.2 + '@esbuild/linux-arm64': 0.28.2 + '@esbuild/linux-ia32': 0.28.2 + '@esbuild/linux-loong64': 0.28.2 + '@esbuild/linux-mips64el': 0.28.2 + '@esbuild/linux-ppc64': 0.28.2 + '@esbuild/linux-riscv64': 0.28.2 + '@esbuild/linux-s390x': 0.28.2 + '@esbuild/linux-x64': 0.28.2 + '@esbuild/netbsd-arm64': 0.28.2 + '@esbuild/netbsd-x64': 0.28.2 + '@esbuild/openbsd-arm64': 0.28.2 + '@esbuild/openbsd-x64': 0.28.2 + '@esbuild/openharmony-arm64': 0.28.2 + '@esbuild/sunos-x64': 0.28.2 + '@esbuild/win32-arm64': 0.28.2 + '@esbuild/win32-ia32': 0.28.2 + '@esbuild/win32-x64': 0.28.2 + escalade@3.2.0: {} escape-string-regexp@1.0.5: {} @@ -3869,6 +4529,12 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 + fix-dts-default-cjs-exports@1.0.1: + dependencies: + magic-string: 0.30.21 + mlly: 1.8.2 + rollup: 4.65.0 + flat-cache@4.0.1: dependencies: flatted: 3.4.4 @@ -4159,6 +4825,8 @@ snapshots: has-symbols: 1.1.0 set-function-name: 2.0.2 + joycon@3.1.1: {} + js-tokens@10.0.0: {} js-tokens@4.0.0: {} @@ -4273,6 +4941,12 @@ snapshots: lightningcss-win32-arm64-msvc: 1.33.0 lightningcss-win32-x64-msvc: 1.33.0 + lilconfig@3.1.4: {} + + lines-and-columns@1.2.4: {} + + load-tsconfig@0.2.5: {} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -4293,6 +4967,10 @@ snapshots: lz-string@1.5.0: {} + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + magic-string@1.4.3: dependencies: '@jridgewell/sourcemap-codec': 1.6.0 @@ -4326,8 +5004,21 @@ snapshots: minimist@1.2.8: {} + mlly@1.8.2: + dependencies: + acorn: 8.19.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.4 + 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.20: {} natural-compare@1.4.0: {} @@ -4425,16 +5116,32 @@ snapshots: path-parse@1.0.7: {} + pathe@2.0.3: {} + picocolors@1.1.1: {} picomatch@2.3.2: {} picomatch@4.0.7: {} + pirates@4.0.7: {} + + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.8.2 + pathe: 2.0.3 + pluralize@8.0.0: {} possible-typed-array-names@1.1.0: {} + postcss-load-config@6.0.1(postcss@8.5.31): + dependencies: + lilconfig: 3.1.4 + optionalDependencies: + postcss: 8.5.31 + postcss@8.5.31: dependencies: nanoid: 3.3.20 @@ -4476,6 +5183,8 @@ snapshots: react@19.3.1: {} + readdirp@5.1.1: {} + redent@3.0.0: dependencies: indent-string: 4.0.0 @@ -4513,6 +5222,8 @@ snapshots: resolve-from@4.0.0: {} + resolve-from@5.0.0: {} + resolve@1.22.13: dependencies: es-errors: 1.3.0 @@ -4552,6 +5263,38 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.2.15 '@rolldown/binding-win32-x64-msvc': 1.2.15 + rollup@4.65.0: + dependencies: + '@types/estree': 1.0.9 + optionalDependencies: + '@napi-rs/lzma-linux-x64-gnu': 1.5.1 + '@rollup/rollup-android-arm-eabi': 4.65.0 + '@rollup/rollup-android-arm64': 4.65.0 + '@rollup/rollup-darwin-arm64': 4.65.0 + '@rollup/rollup-darwin-x64': 4.65.0 + '@rollup/rollup-freebsd-arm64': 4.65.0 + '@rollup/rollup-freebsd-x64': 4.65.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.65.0 + '@rollup/rollup-linux-arm-musleabihf': 4.65.0 + '@rollup/rollup-linux-arm64-gnu': 4.65.0 + '@rollup/rollup-linux-arm64-musl': 4.65.0 + '@rollup/rollup-linux-loong64-gnu': 4.65.0 + '@rollup/rollup-linux-loong64-musl': 4.65.0 + '@rollup/rollup-linux-ppc64-gnu': 4.65.0 + '@rollup/rollup-linux-ppc64-musl': 4.65.0 + '@rollup/rollup-linux-riscv64-gnu': 4.65.0 + '@rollup/rollup-linux-riscv64-musl': 4.65.0 + '@rollup/rollup-linux-s390x-gnu': 4.65.0 + '@rollup/rollup-linux-x64-gnu': 4.65.0 + '@rollup/rollup-linux-x64-musl': 4.65.0 + '@rollup/rollup-openbsd-x64': 4.65.0 + '@rollup/rollup-openharmony-arm64': 4.65.0 + '@rollup/rollup-win32-arm64-msvc': 4.65.0 + '@rollup/rollup-win32-ia32-msvc': 4.65.0 + '@rollup/rollup-win32-x64-gnu': 4.65.0 + '@rollup/rollup-win32-x64-msvc': 4.65.0 + fsevents: 2.3.3 + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -4711,6 +5454,16 @@ snapshots: strip-json-comments@3.1.1: {} + sucrase@3.35.1: + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + commander: 4.1.1 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.7 + tinyglobby: 0.2.17 + ts-interface-checker: 0.1.13 + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -4722,6 +5475,14 @@ snapshots: '@dual-bundle/import-meta-resolve': 4.2.1 '@pkgr/core': 0.3.6 + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + tinybench@6.2.2: {} tinyexec@1.3.1: {} @@ -4753,6 +5514,8 @@ snapshots: dependencies: punycode: 2.3.1 + tree-kill@1.2.2: {} + ts-api-utils@2.5.0(typescript@6.0.3): dependencies: typescript: 6.0.3 @@ -4762,6 +5525,35 @@ snapshots: picomatch: 4.0.7 typescript: 6.0.3 + ts-interface-checker@0.1.13: {} + + tsup@9.0.0(postcss@8.5.31)(typescript@6.0.3): + dependencies: + bundle-require: 5.1.0(esbuild@0.28.2) + cac: 7.1.0 + chokidar: 5.0.0 + consola: 3.4.2 + esbuild: 0.28.2 + fix-dts-default-cjs-exports: 1.0.1 + joycon: 3.1.1 + obug: 3.0.0 + picocolors: 1.1.1 + postcss-load-config: 6.0.1(postcss@8.5.31) + resolve-from: 5.0.0 + rollup: 4.65.0 + source-map-js: 1.2.2 + sucrase: 3.35.1 + tinyexec: 1.3.1 + tinyglobby: 0.2.17 + tree-kill: 1.2.2 + optionalDependencies: + postcss: 8.5.31 + typescript: 6.0.3 + transitivePeerDependencies: + - jiti + - tsx + - yaml + turbo@2.11.10: optionalDependencies: '@turbo/darwin-64': 2.11.10 @@ -4820,6 +5612,8 @@ snapshots: typescript@6.0.3: {} + ufo@1.6.4: {} + uglify-js@3.19.3: optional: true @@ -4844,7 +5638,7 @@ snapshots: dependencies: punycode: 2.3.1 - vite@8.3.5(@types/node@24.19.2)(esbuild@0.27.0): + vite@8.3.5(@types/node@24.19.2)(esbuild@0.28.2): dependencies: lightningcss: 1.33.0 picomatch: 4.0.7 @@ -4853,13 +5647,13 @@ snapshots: tinyglobby: 0.2.17 optionalDependencies: '@types/node': 24.19.2 - esbuild: 0.27.0 + esbuild: 0.28.2 fsevents: 2.3.3 - vitest@5.0.5(@types/node@24.19.2)(@vitest/coverage-v8@5.0.5)(jsdom@30.1.3)(vite@8.3.5(@types/node@24.19.2)(esbuild@0.27.0)): + vitest@5.0.5(@types/node@24.19.2)(@vitest/coverage-v8@5.0.5)(jsdom@30.1.3)(vite@8.3.5(@types/node@24.19.2)(esbuild@0.28.2)): dependencies: '@types/chai': 5.2.3 - '@vitest/mocker': 5.0.5(vite@8.3.5(@types/node@24.19.2)(esbuild@0.27.0)) + '@vitest/mocker': 5.0.5(vite@8.3.5(@types/node@24.19.2)(esbuild@0.28.2)) chai: 6.3.0 es-module-lexer: 2.3.2 expect-type: 1.4.0 @@ -4870,7 +5664,7 @@ snapshots: tinybench: 6.2.2 tinyexec: 1.3.1 tinyglobby: 0.2.17 - vite: 8.3.5(@types/node@24.19.2)(esbuild@0.27.0) + vite: 8.3.5(@types/node@24.19.2)(esbuild@0.28.2) why-is-node-running: 3.2.1 optionalDependencies: '@types/node': 24.19.2 diff --git a/src/__tests__/analytics.test.ts b/src/__tests__/analytics.test.ts index 4ba60ea..f2fe067 100644 --- a/src/__tests__/analytics.test.ts +++ b/src/__tests__/analytics.test.ts @@ -124,6 +124,29 @@ describe("analytics", () => { }); describe("track", () => { + it("ignores empty or non-string event names", () => { + const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + + initAnalytics({ ...baseConfig, debug: true }); + logSpy.mockClear(); + track(""); + track(undefined as unknown as string); + + expect(logSpy).not.toHaveBeenCalled(); + }); + + it("does not queue invalid event names before initialization", () => { + const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + + track(""); + initAnalytics({ ...baseConfig, debug: true }); + + expect(logSpy).not.toHaveBeenCalledWith( + "[analytics:track]", + expect.anything(), + ); + }); + it("queues events before initialization", () => { // Track before init - should be queued without error expect(() => { @@ -233,6 +256,14 @@ describe("analytics", () => { }); describe("identify", () => { + it("ignores an empty user id", () => { + initAnalytics(baseConfig); + identify("user-1"); + identify(""); + + expect(getAnalyticsState()?.userId).toBe("user-1"); + }); + it("identifies user after initialization", () => { const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); diff --git a/src/__tests__/consent.test.ts b/src/__tests__/consent.test.ts index 8b6381f..2230d67 100644 --- a/src/__tests__/consent.test.ts +++ b/src/__tests__/consent.test.ts @@ -377,19 +377,20 @@ describe("consent", () => { expect(getConsentFromCookie("")).toBe(null); }); - it.fails( - "rejects substring cookie names once regex is anchored (AC-18)", - () => { - // BUG: getConsentFromCookie regex lacks word-boundary/anchoring, so - // "evil-vllnt-consent=1" matches as if it were "vllnt-consent=1". - // This test documents the correct behavior: should return null. - // It currently fails because the bug exists. When the regex is fixed, - // this test will start passing — remove `.fails` at that point. - expect(getConsentFromCookie(`evil-${CONSENT_COOKIE_NAME}=1`)).toBe( - null, - ); - }, - ); + it("ignores cookies whose name only ends with the consent cookie name (AC-18)", () => { + expect(getConsentFromCookie(`evil-${CONSENT_COOKIE_NAME}=1`)).toBe(null); + expect( + getConsentFromCookie(`a=b; evil-${CONSENT_COOKIE_NAME}=1; c=d`), + ).toBe(null); + }); + + it("prefers the exact cookie over a look-alike", () => { + expect( + getConsentFromCookie( + `evil-${CONSENT_COOKIE_NAME}=1; ${CONSENT_COOKIE_NAME}=0`, + ), + ).toBe(false); + }); }); describe("isStorageAvailable edge cases", () => { diff --git a/src/analytics.ts b/src/analytics.ts index ea9d684..92223ae 100644 --- a/src/analytics.ts +++ b/src/analytics.ts @@ -118,13 +118,14 @@ function processQueue(): void { } /** - * Track a custom event + * Track a custom event. Events with an empty or non-string name are ignored. */ export function track( name: string, properties?: Record, ): void { if (typeof window === "undefined") return; + if (typeof name !== "string" || name === "") return; if (isBlocked) return; @@ -162,13 +163,14 @@ export function trackPage(properties?: Record): void { } /** - * Identify a user + * Identify a user. An empty or non-string user id is ignored. */ export function identify( userId: string, traits?: Record, ): void { if (typeof window === "undefined") return; + if (typeof userId !== "string" || userId === "") return; if (isBlocked) return; if (!isInitialized || !analyticsState) { diff --git a/src/consent.ts b/src/consent.ts index 63ac203..2573f5c 100644 --- a/src/consent.ts +++ b/src/consent.ts @@ -169,7 +169,11 @@ export function isDoNotTrackEnabled(): boolean { * Get consent from cookie (for SSR) */ export function getConsentFromCookie(cookieString: string): boolean | null { - const match = new RegExp(`${CONSENT_COOKIE_NAME}=([^;]+)`).exec(cookieString); + // Anchor to the start of the string or a cookie separator so that + // look-alike names such as "evil-vllnt-consent" are not matched. + const match = new RegExp(`(?:^|;\\s*)${CONSENT_COOKIE_NAME}=([^;]+)`).exec( + cookieString, + ); if (!match) return null; return match[1] === "1"; } diff --git a/tsconfig.json b/tsconfig.json index 24d6370..1e22eb7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,6 @@ "@/*": ["./src/*"] } }, - "include": ["src", "vitest.config.ts"], + "include": ["src", "vitest.config.ts", "tsup.config.ts"], "exclude": ["node_modules"] } diff --git a/tsup.config.ts b/tsup.config.ts new file mode 100644 index 0000000..c6a3685 --- /dev/null +++ b/tsup.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + clean: true, + dts: true, + entry: { index: "src/index.ts", react: "src/react.tsx" }, + external: ["react"], + format: ["esm", "cjs"], + // Shared chunks keep the module-level analytics state a singleton across + // both entry points, in ESM and in CJS. + splitting: true, +}); diff --git a/vitest.config.ts b/vitest.config.ts index 196b33d..0a74ff9 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -23,6 +23,8 @@ export default defineConfig({ environment: "jsdom", globals: true, include: ["src/**/*.test.{ts,tsx}"], + // Shared dev hosts: keep test parallelism low. + maxWorkers: 2, setupFiles: ["./src/__tests__/setup.ts"], }, });