From 11d6553dec6360c0d4f9d141daab2c46360e8fbe Mon Sep 17 00:00:00 2001 From: Serhii Shpak Date: Wed, 14 Jan 2026 15:04:49 +0200 Subject: [PATCH] Revert "PP-2572" --- .cursor/rules/pr-message-format.mdc | 36 - .cursorrules | 95 -- .dockerignore | 112 -- .github/workflows/ci.yml | 1 + .github/workflows/release-beta.yml | 54 - .github/workflows/release.yml | 32 +- .gitignore | 39 - .releaserc.json | 29 +- Dockerfile | 26 - e2e/config-watcher/watcher.debounce.spec.ts | 61 - e2e/config-watcher/watcher.reload.spec.ts | 74 - e2e/toolbar/toolbar.minimize.spec.ts | 130 -- e2e/toolbar/toolbar.popups.spec.ts | 200 --- e2e/toolbar/toolbar.sync.spec.ts | 119 -- e2e/toolbar/toolbar.visibility.spec.ts | 90 - package-lock.json | 1503 +++++++---------- package.json | 18 +- run-tests.sh | 31 - scripts/run-tests.js | 306 ---- src/plugins/mi-topbar-plugin.ts | 6 +- tests/README.md | 92 - tests/TESTING.md | 184 -- .../middleware/pp-redirect.spec.ts | 201 --- .../middleware/proxy-cache.spec.ts | 162 -- .../middleware/rewrite-response.spec.ts | 135 -- tests/setup.ts | 38 - tests/test-commonjs/package-lock.json | 8 +- tests/test-commonjs/package.json | 4 +- tests/test-nextjs-cjs/package.json | 8 +- tests/test-nextjs/package.json | 4 +- tests/tsconfig.json | 31 - tests/unit/config/config.loader.spec.ts | 238 --- tests/unit/config/config.validation.spec.ts | 58 - tests/unit/helpers/formatting.spec.ts | 93 - tests/unit/lib/color.helper.spec.ts | 49 - tests/unit/lib/url.helper.spec.ts | 140 -- tests/unit/plugin/plugin.normalize.spec.ts | 349 ---- vitest.config.ts | 28 - vitest.integration.config.ts | 29 - 39 files changed, 654 insertions(+), 4159 deletions(-) delete mode 100644 .cursor/rules/pr-message-format.mdc delete mode 100644 .cursorrules delete mode 100644 .dockerignore delete mode 100644 .github/workflows/release-beta.yml delete mode 100644 .gitignore delete mode 100644 Dockerfile delete mode 100644 e2e/config-watcher/watcher.debounce.spec.ts delete mode 100644 e2e/config-watcher/watcher.reload.spec.ts delete mode 100644 e2e/toolbar/toolbar.minimize.spec.ts delete mode 100644 e2e/toolbar/toolbar.popups.spec.ts delete mode 100644 e2e/toolbar/toolbar.sync.spec.ts delete mode 100644 e2e/toolbar/toolbar.visibility.spec.ts delete mode 100644 run-tests.sh delete mode 100644 scripts/run-tests.js delete mode 100644 tests/README.md delete mode 100644 tests/TESTING.md delete mode 100644 tests/integration/middleware/pp-redirect.spec.ts delete mode 100644 tests/integration/middleware/proxy-cache.spec.ts delete mode 100644 tests/integration/middleware/rewrite-response.spec.ts delete mode 100644 tests/setup.ts delete mode 100644 tests/tsconfig.json delete mode 100644 tests/unit/config/config.loader.spec.ts delete mode 100644 tests/unit/config/config.validation.spec.ts delete mode 100644 tests/unit/helpers/formatting.spec.ts delete mode 100644 tests/unit/lib/color.helper.spec.ts delete mode 100644 tests/unit/lib/url.helper.spec.ts delete mode 100644 tests/unit/plugin/plugin.normalize.spec.ts delete mode 100644 vitest.config.ts delete mode 100644 vitest.integration.config.ts diff --git a/.cursor/rules/pr-message-format.mdc b/.cursor/rules/pr-message-format.mdc deleted file mode 100644 index eab1aeb..0000000 --- a/.cursor/rules/pr-message-format.mdc +++ /dev/null @@ -1,36 +0,0 @@ -๏ปฟ--- -description: Generate PR messages in markdown format for easy copy-paste to GitHub -globs: *.md,*.mdc ---- - -# PR Message Generation Rules - -When generating PR messages, always follow these formatting guidelines for optimal GitHub compatibility. - -## Emoji Guidelines -- ๐Ÿš€ for new features -- ๐Ÿ”ง for fixes/improvements -- โšก for performance optimizations -- ๐Ÿ” for security/auth features -- ๐Ÿงน for cleanup/maintenance -- ๐Ÿ“Š for metrics/analytics -- ๐Ÿงช for testing -- ๐Ÿ”„ for breaking changes -- โœ… for completion/readiness - -## Content Requirements -- Always include a clear summary -- List specific technical improvements -- Mention file counts and code changes -- Include commit references -- Specify breaking changes or compatibility -- Use bullet points for readability -- Keep language professional but engaging - -## GitHub Compatibility -- Use proper markdown headers (##, ###) -- Include horizontal rules (---) for separation -- Use bold text (**text**) for emphasis -- Use code blocks for technical details -- Ensure proper line breaks for readability -- Use checkboxes for status indicators diff --git a/.cursorrules b/.cursorrules deleted file mode 100644 index d76556d..0000000 --- a/.cursorrules +++ /dev/null @@ -1,95 +0,0 @@ -# pp-dev - Cursor Rules - -## Commit Message Guidelines - -### Format: `(): ` -- Use imperative mood ("add" not "added") -- Don't capitalize first letter -- No period at the end -- Keep subject concise (50 chars or less) - -### Commit Types (choose one): -- **`feat`** - A new feature (triggers MINOR version bump) -- **`fix`** - A bug fix (triggers PATCH version bump) -- **`docs`** - Documentation only changes (NO version bump) -- **`style`** - Code style changes, formatting, etc (NO version bump) -- **`refactor`** - Code refactoring, no functional changes (NO version bump) -- **`perf`** - Performance improvements (NO version bump) -- **`test`** - Adding or updating tests (NO version bump) -- **`chore`** - Maintenance tasks, dependencies, etc (NO version bump) -- **`ci`** - CI/CD configuration changes (NO version bump) -- **`build`** - Build system or external dependency changes (NO version bump) - -### Scopes (optional, but recommended): -- **`api`** - API-related changes -- **`auth`** - Authentication/authorization changes -- **`cli`** - CLI-related changes -- **`ui`** - User interface changes (client toolbar) -- **`build`** - Build system changes -- **`ci`** - CI/CD changes -- **`deps`** - Dependency updates -- **`docs`** - Documentation changes -- **`test`** - Testing changes -- **`utils`** - Utility functions -- **`config`** - Configuration changes -- **`plugin`** - Vite plugin changes -- **`middleware`** - Middleware changes - -### Examples: -```bash -feat(api): add new authentication middleware -fix(cli): resolve memory leak in data processing -docs(readme): update installation instructions -style: format code according to prettier rules -refactor(utils): extract common validation functions -perf(build): optimize bundle size -test(e2e): add comprehensive login test suite -chore(deps): update all dependencies to latest versions -ci(github): add semantic-release workflow -build(rollup): optimize bundle configuration -``` - -### Breaking Changes: -```bash -feat(api): change response format structure - -BREAKING CHANGE: API now returns data in different structure. -All existing clients must be updated to handle new format. -``` - -### Body and Footer Guidelines: -- **Body**: Explain WHAT and WHY, not HOW -- **Breaking Changes**: Start footer with "BREAKING CHANGE: " - -### Important Notes: -- Only `feat`, `fix`, and `BREAKING CHANGE` trigger version bumps -- Use scopes to indicate which area is affected -- Be descriptive but concise -- Reference related issues when possible -- Follow the [Conventional Commits](https://www.conventionalcommits.org/) specification - -## General Development Guidelines - -### Code Quality: -- Always aim for clean, maintainable architecture -- Focus on areas of the code relevant to the task -- Do not touch code unrelated to the task -- Avoid making major changes to working patterns unless explicitly instructed -- Think about what other methods and areas might be affected by changes - -### Semantic Release: -- This project uses semantic-release for automated versioning -- All releases go through Pull Requests (no direct pushes to main) -- Commit messages determine version bumps automatically -- Follow conventional commit format strictly for proper releases - -### Project Structure: -- `src/` - Source code -- `src/api/` - MI API integration -- `src/cli.ts` - CLI entry point -- `src/client/` - Browser toolbar client -- `src/lib/` - Core library functions -- `src/plugins/` - Vite plugins -- `bin/` - CLI binary -- `tests/` - E2E tests -- `dist/` - Build output diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 2c546be..0000000 --- a/.dockerignore +++ /dev/null @@ -1,112 +0,0 @@ -# Node modules -node_modules/ -*/node_modules/ -**/node_modules/ - -# Build outputs -dist/ -build/ -.next/ -out/ - -# Logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Coverage directory used by tools like istanbul -coverage/ -*.lcov - -# nyc test coverage -.nyc_output - -# Dependency directories -jspm_packages/ - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' (except the one we need for tests) -*.tgz -!metricinsights-pp-dev-latest.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env -.env.test -.env.local -.env.development.local -.env.test.local -.env.production.local - -# parcel-bundler cache (https://parceljs.org/) -.cache -.parcel-cache - -# next.js build output -.next - -# nuxt.js build output -.nuxt - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test - -# IDE files -.vscode/ -.idea/ -*.swp -*.swo -*~ - -# OS generated files -.DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes -ehthumbs.db -Thumbs.db - -# Git -.git/ -.gitignore - -# Docker -Dockerfile* -docker-compose* -.dockerignore - -# Test results -test-results/ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3e3b351..b32e550 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,3 +58,4 @@ jobs: - name: Run E2E tests run: npx playwright test continue-on-error: true + diff --git a/.github/workflows/release-beta.yml b/.github/workflows/release-beta.yml deleted file mode 100644 index 37b7839..0000000 --- a/.github/workflows/release-beta.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: Beta Release - -on: - push: - branches: [develop] - -permissions: - contents: write - issues: write - pull-requests: write - -jobs: - release-beta: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - persist-credentials: false - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 22 - cache: 'npm' - - - name: Install dependencies - run: npm ci - - - name: Build - run: npm run build - - - name: Verify build output - run: | - if [[ ! -d "dist" ]]; then - echo "Build failed: dist directory not found" - exit 1 - fi - echo "Build verification passed" - - - name: Configure Git - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git remote set-url origin https://x-access-token:${GITHUB_TOKEN}@github.com/${{ github.repository }}.git - git fetch --all --tags - - - name: Release Beta - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: npx semantic-release diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index db56858..b218ac4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,12 +1,13 @@ -name: Production Release +name: Release on: push: - tags: - - 'v*' + branches: [main, develop] permissions: contents: write + issues: write + pull-requests: write jobs: release: @@ -16,6 +17,7 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 + persist-credentials: false - name: Setup Node.js uses: actions/setup-node@v4 @@ -24,13 +26,6 @@ jobs: registry-url: https://registry.npmjs.org/ cache: 'npm' - - name: Extract version from tag - id: version - run: | - VERSION=${GITHUB_REF_NAME#v} - echo "version=$VERSION" >> $GITHUB_OUTPUT - echo "Releasing version: $VERSION" - - name: Install dependencies run: npm ci @@ -45,19 +40,10 @@ jobs: fi echo "Build verification passed" - - name: Update package version - run: npm version ${{ steps.version.outputs.version }} --no-git-tag-version - - - name: Publish to npm + - name: Release env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - run: npm publish --access public + run: npx semantic-release - - name: Create GitHub Release - uses: softprops/action-gh-release@v1 - with: - tag_name: ${{ github.ref_name }} - name: Release ${{ github.ref_name }} - generate_release_notes: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 396282d..0000000 --- a/.gitignore +++ /dev/null @@ -1,39 +0,0 @@ -# IDE -.idea -.vscode - -# Build outputs -dist -dist-zip - -# Dependencies -node_modules - -# Pack files -*-pp-dev-*.*.*.tgz -*-pp-dev-latest.tgz - -# Backups -backups - -# Environment -.env -!.env.example - -# PP Dev metadata -.pp-dev-meta/ - -# Playwright -/test-results/ -/playwright-report/ -/blob-report/ -/playwright/.cache/ - -# Logs -*.log -npm-debug.log* - -# OS files -.DS_Store -Thumbs.db - diff --git a/.releaserc.json b/.releaserc.json index 6c02caa..6fa12e1 100644 --- a/.releaserc.json +++ b/.releaserc.json @@ -12,48 +12,29 @@ [ "@semantic-release/commit-analyzer", { - "preset": "angular", - "presetConfig": { - "issuePrefixes": [], - "issueFormat": false, - "issueReferences": false - } + "preset": "angular" } ], [ "@semantic-release/release-notes-generator", { - "preset": "angular", - "presetConfig": { - "issuePrefixes": [], - "commitTypes": ["feat", "fix", "docs", "style", "refactor", "perf", "test", "chore", "ci", "build"], - "issueFormat": false, - "issueReferences": false - } + "preset": "angular" } ], "@semantic-release/changelog", [ "@semantic-release/npm", { - "npmPublish": false + "npmPublish": true } ], [ "@semantic-release/git", { "assets": ["package.json", "package-lock.json", "CHANGELOG.md"], - "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" + "message": "chore(release): ${nextRelease.version} [skip ci]" } ], - [ - "@semantic-release/github", - { - "assets": ["package.json", "package-lock.json", "CHANGELOG.md"], - "releasedLabels": false, - "failCommentCondition": false, - "successCommentCondition": false - } - ] + "@semantic-release/github" ] } diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 03a984f..0000000 --- a/Dockerfile +++ /dev/null @@ -1,26 +0,0 @@ -# Use official Node.js image -FROM node:22-alpine - -# Set working directory -WORKDIR /app - -# Copy the built pp-dev package -COPY metricinsights-pp-dev-latest.tgz ./ - -# Copy test folders (without node_modules - they will be installed) -COPY tests/ ./tests/ - -# Copy and setup script for running tests -COPY run-tests.sh /usr/local/bin/run-tests.sh - -# Fix line endings (Windows -> Unix) -RUN sed -i 's/\r$//' /usr/local/bin/run-tests.sh - -# Make script executable -RUN chmod +x /usr/local/bin/run-tests.sh - -# Set entry point -ENTRYPOINT ["/usr/local/bin/run-tests.sh"] - -# Default command runs commonjs tests -CMD ["dev-commonjs"] diff --git a/e2e/config-watcher/watcher.debounce.spec.ts b/e2e/config-watcher/watcher.debounce.spec.ts deleted file mode 100644 index a085b50..0000000 --- a/e2e/config-watcher/watcher.debounce.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { test, expect } from '@playwright/test'; - -const testType = process.env.TEST_TYPE; -const baseURL = process.env.BASE_URL ?? ''; - -test.describe('Config Watcher Debounce Behavior', () => { - test.skip(testType !== 'config', 'Only run for config test type'); - - test('should not restart immediately on config change', async ({ page }) => { - // The debounce is set to 500ms in the CLI - // This test verifies server doesn't restart too quickly - - await page.goto(baseURL); - await expect(page).toHaveURL(/\/p[tl]?\//); - - // Record initial time - const startTime = Date.now(); - - // Make multiple rapid requests - for (let i = 0; i < 5; i++) { - const response = await page.request.get(baseURL); - expect(response.ok()).toBe(true); - } - - const endTime = Date.now(); - - // All requests should complete relatively quickly - // If debounce wasn't working, server restarts would cause delays - expect(endTime - startTime).toBeLessThan(10000); - }); - - test('should coalesce multiple rapid config changes', async ({ page }) => { - await page.goto(baseURL); - - // Verify server is stable - await page.waitForTimeout(1000); - - // Make several requests to verify stability - const requests = []; - for (let i = 0; i < 3; i++) { - requests.push(page.request.get(baseURL)); - } - - const responses = await Promise.all(requests); - responses.forEach((response) => { - expect(response.ok()).toBe(true); - }); - }); - - test('should remain responsive during config reload', async ({ page }) => { - await page.goto(baseURL); - - // The page should remain usable even during config watch - const toolbar = page.locator('.pp-dev-info'); - - // For Vite projects, toolbar should be visible - if (testType === 'config' || testType?.includes('commonjs')) { - await expect(toolbar).toBeVisible({ timeout: 10000 }); - } - }); -}); diff --git a/e2e/config-watcher/watcher.reload.spec.ts b/e2e/config-watcher/watcher.reload.spec.ts deleted file mode 100644 index b44a1c9..0000000 --- a/e2e/config-watcher/watcher.reload.spec.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { test, expect } from '@playwright/test'; - -const testType = process.env.TEST_TYPE; -const baseURL = process.env.BASE_URL ?? ''; - -// Only run config watcher tests for the config test type -test.describe('Config File Watcher - Reload', () => { - test.skip(testType !== 'config', 'Only run for config test type'); - - test('should detect config file changes', async ({ page }) => { - // This test verifies the config watcher detects changes - // In the Docker environment, we can modify config files - - // Navigate to verify server is running - await page.goto(baseURL); - await expect(page).toHaveURL(/\/p[tl]?\//); - - // The actual file modification and server restart is tested - // in the Docker environment setup - // Here we just verify the server responds correctly - const title = await page.title(); - expect(title).toBeDefined(); - }); - - test('should maintain server functionality after config change', async ({ page }) => { - await page.goto(baseURL); - - // Verify redirect still works - await expect(page).toHaveURL(/\/p[tl]?\//); - - // Verify page loads correctly - const body = await page.locator('body'); - await expect(body).toBeVisible(); - }); - - test('should handle rapid config changes gracefully (debounce)', async ({ page }) => { - // This test verifies the debounce mechanism works - // The server should not restart multiple times for rapid changes - - await page.goto(baseURL); - - // Wait a bit for server to stabilize - await page.waitForTimeout(2000); - - // Verify server is responsive - const response = await page.request.get(baseURL); - expect(response.status()).toBe(200); - }); -}); - -test.describe('Config File Watcher - File Types', () => { - test.skip(testType !== 'config', 'Only run for config test type'); - - test('should watch pp-dev.config.ts files', async ({ page }) => { - // Verify the server watches TypeScript config files - await page.goto(baseURL); - await expect(page).toHaveURL(/\/p[tl]?\//); - }); - - test('should watch package.json for pp-dev field', async ({ page }) => { - // Verify the server watches package.json - await page.goto(baseURL); - await expect(page).toHaveURL(/\/p[tl]?\//); - }); - - test('should watch .env files', async ({ page }) => { - // Verify the server watches .env files - await page.goto(baseURL); - - // Server should be running and responsive - const response = await page.request.get(baseURL); - expect(response.ok()).toBe(true); - }); -}); diff --git a/e2e/toolbar/toolbar.minimize.spec.ts b/e2e/toolbar/toolbar.minimize.spec.ts deleted file mode 100644 index d416276..0000000 --- a/e2e/toolbar/toolbar.minimize.spec.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { test, expect } from '@playwright/test'; - -const testType = process.env.TEST_TYPE; - -test.describe('Toolbar Minimize Functionality', () => { - // Skip toolbar tests for Next.js - test.skip(testType?.includes('nextjs') ?? false, 'Skipping toolbar tests for Next.js'); - - test('should minimize toolbar when minimize button is clicked', async ({ page, baseURL }) => { - if (!baseURL) { - throw new Error('baseURL is not set'); - } - - await page.goto(baseURL); - - const toolbar = page.locator('.pp-dev-info'); - const minimizeBtn = page.locator('.pp-dev-info__wrap-btn'); - - // Wait for toolbar to be visible - await expect(toolbar).toBeVisible(); - - // Ensure toolbar is not minimized initially (unless localStorage has previous state) - // Clear localStorage to ensure fresh state - await page.evaluate(() => localStorage.removeItem('pp-dev-info-closed')); - await page.reload(); - - await expect(toolbar).toBeVisible(); - - // Click minimize button - await minimizeBtn.click(); - - // Toolbar should have 'closed' class - await expect(toolbar).toHaveClass(/closed/); - }); - - test('should restore toolbar when minimize button is clicked again', async ({ page, baseURL }) => { - if (!baseURL) { - throw new Error('baseURL is not set'); - } - - await page.goto(baseURL); - - // Clear localStorage to ensure fresh state - await page.evaluate(() => localStorage.removeItem('pp-dev-info-closed')); - await page.reload(); - - const toolbar = page.locator('.pp-dev-info'); - const minimizeBtn = page.locator('.pp-dev-info__wrap-btn'); - - await expect(toolbar).toBeVisible(); - - // Minimize - await minimizeBtn.click(); - await expect(toolbar).toHaveClass(/closed/); - - // Restore - await minimizeBtn.click(); - await expect(toolbar).not.toHaveClass(/closed/); - }); - - test('should persist minimize state in localStorage', async ({ page, baseURL }) => { - if (!baseURL) { - throw new Error('baseURL is not set'); - } - - await page.goto(baseURL); - - // Clear previous state - await page.evaluate(() => localStorage.removeItem('pp-dev-info-closed')); - await page.reload(); - - const toolbar = page.locator('.pp-dev-info'); - const minimizeBtn = page.locator('.pp-dev-info__wrap-btn'); - - // Minimize toolbar - await minimizeBtn.click(); - await expect(toolbar).toHaveClass(/closed/); - - // Check localStorage value - const storageValue = await page.evaluate(() => localStorage.getItem('pp-dev-info-closed')); - expect(storageValue).toBe('true'); - - // Reload page - await page.reload(); - - // Toolbar should still be minimized - await expect(toolbar).toHaveClass(/closed/); - }); - - test('should restore state from localStorage on page load', async ({ page, baseURL }) => { - if (!baseURL) { - throw new Error('baseURL is not set'); - } - - // Set localStorage before navigating - await page.goto(baseURL); - await page.evaluate(() => localStorage.setItem('pp-dev-info-closed', 'true')); - await page.reload(); - - const toolbar = page.locator('.pp-dev-info'); - - // Should be minimized based on localStorage - await expect(toolbar).toHaveClass(/closed/); - }); - - test('should toggle SVG icon class when minimizing', async ({ page, baseURL }) => { - if (!baseURL) { - throw new Error('baseURL is not set'); - } - - await page.goto(baseURL); - await page.evaluate(() => localStorage.removeItem('pp-dev-info-closed')); - await page.reload(); - - const minimizeBtn = page.locator('.pp-dev-info__wrap-btn'); - const minimizeSvg = minimizeBtn.locator('svg'); - - // Click to minimize - await minimizeBtn.click(); - - // SVG should have 'closed' class - await expect(minimizeSvg).toHaveClass(/closed/); - - // Click to restore - await minimizeBtn.click(); - - // SVG should not have 'closed' class - await expect(minimizeSvg).not.toHaveClass(/closed/); - }); -}); diff --git a/e2e/toolbar/toolbar.popups.spec.ts b/e2e/toolbar/toolbar.popups.spec.ts deleted file mode 100644 index f7231dc..0000000 --- a/e2e/toolbar/toolbar.popups.spec.ts +++ /dev/null @@ -1,200 +0,0 @@ -import { test, expect } from '@playwright/test'; - -const testType = process.env.TEST_TYPE; - -test.describe('Toolbar Popup Notifications', () => { - // Skip toolbar tests for Next.js - test.skip(testType?.includes('nextjs') ?? false, 'Skipping toolbar tests for Next.js'); - - test('should show popup with correct structure when triggered', async ({ page, baseURL }) => { - if (!baseURL) { - throw new Error('baseURL is not set'); - } - - await page.goto(baseURL); - - // Inject a test popup using the internal mechanism - await page.evaluate(() => { - // Access the hot module to send a test event - if ((window as any).__vite_plugin_react_preamble_installed__) { - // For testing, we'll check the popup structure exists - } - }); - - // Trigger sync to potentially show a popup (if sync is available) - const syncButton = page.locator('#sync-template'); - const isDisabled = await syncButton.evaluate((el: HTMLButtonElement) => el.disabled); - - if (!isDisabled) { - await syncButton.click(); - - // Wait for potential popup - await page.waitForTimeout(5000); - - // Check if any popup appeared - const popup = page.locator('.pp-dev-info__popup'); - const popupCount = await popup.count(); - - if (popupCount > 0) { - // Verify popup structure - await expect(popup.first().locator('.pp-dev-info__popup-title')).toBeVisible(); - await expect(popup.first().locator('.pp-dev-info__popup-content')).toBeVisible(); - await expect(popup.first().locator('.pp-dev-info__popup-progress')).toBeAttached(); - } - } - }); - - test('should position multiple popups correctly', async ({ page, baseURL }) => { - if (!baseURL) { - throw new Error('baseURL is not set'); - } - - await page.goto(baseURL); - - // Check that the toolbar doesn't overlap with popup positioning logic - const toolbar = page.locator('.pp-dev-info'); - await expect(toolbar).toBeVisible(); - - // Verify the dev panel stays at bottom - const toolbarBottom = await toolbar.evaluate((el) => { - const styles = window.getComputedStyle(el); - return styles.bottom; - }); - - expect(toolbarBottom).toBe('0px'); - }); - - test('should have close button on popups', async ({ page, baseURL }) => { - if (!baseURL) { - throw new Error('baseURL is not set'); - } - - await page.goto(baseURL); - - // Trigger an action that creates a popup - const syncButton = page.locator('#sync-template'); - const isDisabled = await syncButton.evaluate((el: HTMLButtonElement) => el.disabled); - - if (!isDisabled) { - await syncButton.click(); - - // Wait for popup - const popup = page.locator('.pp-dev-info__popup').first(); - - // Wait for popup with timeout - try { - await popup.waitFor({ state: 'visible', timeout: 10000 }); - - // Check for close button - const closeButton = popup.locator('.pp-dev-info__popup-title-close'); - await expect(closeButton).toBeVisible(); - - // Close button should contain SVG - const closeSvg = closeButton.locator('svg'); - await expect(closeSvg).toBeVisible(); - } catch { - // Popup didn't appear, which is fine for this test - test.skip(); - } - } else { - test.skip(); - } - }); - - test('should remove popup when close button is clicked', async ({ page, baseURL }) => { - if (!baseURL) { - throw new Error('baseURL is not set'); - } - - await page.goto(baseURL); - - const syncButton = page.locator('#sync-template'); - const isDisabled = await syncButton.evaluate((el: HTMLButtonElement) => el.disabled); - - if (!isDisabled) { - await syncButton.click(); - - const popup = page.locator('.pp-dev-info__popup').first(); - - try { - await popup.waitFor({ state: 'visible', timeout: 10000 }); - - // Click close button - const closeButton = popup.locator('.pp-dev-info__popup-title-close'); - await closeButton.click(); - - // Popup should be removed (with animation) - await expect(popup).not.toBeVisible({ timeout: 5000 }); - } catch { - test.skip(); - } - } else { - test.skip(); - } - }); - - test('should auto-dismiss popup after duration', async ({ page, baseURL }) => { - if (!baseURL) { - throw new Error('baseURL is not set'); - } - - await page.goto(baseURL); - - const syncButton = page.locator('#sync-template'); - const isDisabled = await syncButton.evaluate((el: HTMLButtonElement) => el.disabled); - - if (!isDisabled) { - await syncButton.click(); - - const popup = page.locator('.pp-dev-info__popup').first(); - - try { - await popup.waitFor({ state: 'visible', timeout: 10000 }); - - // Wait for auto-dismiss (default duration is 10000ms) - await expect(popup).not.toBeVisible({ timeout: 15000 }); - } catch { - // Popup may have been dismissed already or didn't appear - test.skip(); - } - } else { - test.skip(); - } - }); - - test('should show progress bar animation on popup', async ({ page, baseURL }) => { - if (!baseURL) { - throw new Error('baseURL is not set'); - } - - await page.goto(baseURL); - - const syncButton = page.locator('#sync-template'); - const isDisabled = await syncButton.evaluate((el: HTMLButtonElement) => el.disabled); - - if (!isDisabled) { - await syncButton.click(); - - const popup = page.locator('.pp-dev-info__popup').first(); - - try { - await popup.waitFor({ state: 'visible', timeout: 10000 }); - - const progressBar = popup.locator('.pp-dev-info__popup-progress'); - await expect(progressBar).toBeAttached(); - - // Check that progress bar has width style (animated) - const hasWidthStyle = await progressBar.evaluate((el) => { - const style = window.getComputedStyle(el); - return style.width !== '0px' && style.width !== ''; - }); - - expect(hasWidthStyle).toBe(true); - } catch { - test.skip(); - } - } else { - test.skip(); - } - }); -}); diff --git a/e2e/toolbar/toolbar.sync.spec.ts b/e2e/toolbar/toolbar.sync.spec.ts deleted file mode 100644 index fc4ca8d..0000000 --- a/e2e/toolbar/toolbar.sync.spec.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { test, expect } from '@playwright/test'; - -const testType = process.env.TEST_TYPE; - -test.describe('Toolbar Sync Functionality', () => { - // Skip toolbar tests for Next.js - test.skip(testType?.includes('nextjs') ?? false, 'Skipping toolbar tests for Next.js'); - - test('should have sync button in toolbar', async ({ page, baseURL }) => { - if (!baseURL) { - throw new Error('baseURL is not set'); - } - - await page.goto(baseURL); - - const syncButton = page.locator('#sync-template'); - await expect(syncButton).toBeAttached(); - }); - - test('should add syncing class when sync button is clicked', async ({ page, baseURL }) => { - if (!baseURL) { - throw new Error('baseURL is not set'); - } - - await page.goto(baseURL); - - const syncButton = page.locator('#sync-template'); - - // Check if button is not disabled (sync is available) - const isDisabled = await syncButton.evaluate((el: HTMLButtonElement) => el.disabled); - - if (!isDisabled) { - await syncButton.click(); - - // Button should have 'syncing' class while syncing - await expect(syncButton).toHaveClass(/syncing/, { timeout: 5000 }); - } else { - // Skip test if sync is disabled - test.skip(); - } - }); - - test('should display sync button with correct title when enabled', async ({ page, baseURL }) => { - if (!baseURL) { - throw new Error('baseURL is not set'); - } - - await page.goto(baseURL); - - const syncButton = page.locator('#sync-template'); - - // Wait for config update that might enable/disable sync - await page.waitForTimeout(2000); - - const isDisabled = await syncButton.evaluate((el: HTMLButtonElement) => el.disabled); - - if (!isDisabled) { - const title = await syncButton.getAttribute('title'); - expect(title).toBe('Sync template'); - } else { - const title = await syncButton.getAttribute('title'); - expect(title).toContain('unavailable'); - } - }); - - test('should update sync button state based on config', async ({ page, baseURL }) => { - if (!baseURL) { - throw new Error('baseURL is not set'); - } - - await page.goto(baseURL); - - const syncButton = page.locator('#sync-template'); - - // Wait for potential config update - await page.waitForTimeout(3000); - - // Check initial state - const initialDisabled = await syncButton.evaluate((el: HTMLButtonElement) => el.disabled); - const initialClass = await syncButton.getAttribute('class'); - - // If disabled, should have 'disabled' class - if (initialDisabled) { - expect(initialClass).toContain('disabled'); - } - }); - - test('should prevent multiple simultaneous sync requests', async ({ page, baseURL }) => { - if (!baseURL) { - throw new Error('baseURL is not set'); - } - - await page.goto(baseURL); - - const syncButton = page.locator('#sync-template'); - - const isDisabled = await syncButton.evaluate((el: HTMLButtonElement) => el.disabled); - - if (!isDisabled) { - // Click sync button - await syncButton.click(); - - // While syncing, button should be in syncing state - const hasSyncingClass = await syncButton.evaluate((el) => el.classList.contains('syncing')); - - // If syncing is in progress, attempting another click shouldn't change state - if (hasSyncingClass) { - const classBeforeSecondClick = await syncButton.getAttribute('class'); - await syncButton.click(); - const classAfterSecondClick = await syncButton.getAttribute('class'); - - // Class should remain the same (syncing should still be present) - expect(classAfterSecondClick).toContain('syncing'); - } - } else { - test.skip(); - } - }); -}); diff --git a/e2e/toolbar/toolbar.visibility.spec.ts b/e2e/toolbar/toolbar.visibility.spec.ts deleted file mode 100644 index 39fecb2..0000000 --- a/e2e/toolbar/toolbar.visibility.spec.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { test, expect } from '@playwright/test'; - -const testType = process.env.TEST_TYPE; - -test.describe('Toolbar Visibility', () => { - // Skip toolbar tests for Next.js as it doesn't inject the toolbar in the same way - test.skip(testType?.includes('nextjs') ?? false, 'Skipping toolbar tests for Next.js'); - - test('should display pp-dev toolbar on page load', async ({ page, baseURL }) => { - if (!baseURL) { - throw new Error('baseURL is not set'); - } - - await page.goto(baseURL); - - // Wait for the toolbar to be visible - const toolbar = page.locator('.pp-dev-info'); - await expect(toolbar).toBeVisible({ timeout: 10000 }); - }); - - test('should have the pp-dev-info-namespace class', async ({ page, baseURL }) => { - if (!baseURL) { - throw new Error('baseURL is not set'); - } - - await page.goto(baseURL); - - const namespace = page.locator('.pp-dev-info-namespace'); - await expect(namespace).toBeVisible(); - }); - - test('should display minimize button', async ({ page, baseURL }) => { - if (!baseURL) { - throw new Error('baseURL is not set'); - } - - await page.goto(baseURL); - - const minimizeBtn = page.locator('.pp-dev-info__wrap-btn'); - await expect(minimizeBtn).toBeVisible(); - }); - - test('should display sync button when available', async ({ page, baseURL }) => { - if (!baseURL) { - throw new Error('baseURL is not set'); - } - - await page.goto(baseURL); - - const syncButton = page.locator('#sync-template'); - // Sync button may be disabled but should exist - await expect(syncButton).toBeAttached(); - }); - - test('should have correct toolbar structure', async ({ page, baseURL }) => { - if (!baseURL) { - throw new Error('baseURL is not set'); - } - - await page.goto(baseURL); - - // Check for main toolbar elements - const toolbar = page.locator('.pp-dev-info'); - await expect(toolbar).toBeVisible(); - - // Check that toolbar has SVG icons - const svgIcons = toolbar.locator('svg'); - expect(await svgIcons.count()).toBeGreaterThan(0); - }); - - test('should be positioned at the bottom of the page', async ({ page, baseURL }) => { - if (!baseURL) { - throw new Error('baseURL is not set'); - } - - await page.goto(baseURL); - - const toolbar = page.locator('.pp-dev-info'); - await expect(toolbar).toBeVisible(); - - // Check CSS positioning - const bottom = await toolbar.evaluate((el) => { - const styles = window.getComputedStyle(el); - return styles.bottom; - }); - - // Should be positioned at the bottom - expect(bottom).toBe('0px'); - }); -}); diff --git a/package-lock.json b/package-lock.json index 563a98f..ce2ca7a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,7 +41,6 @@ "pp-dev": "bin/pp-dev.js" }, "devDependencies": { - "@playwright/test": "^1.57.0", "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^12.1.1", "@rollup/plugin-url": "^8.0.2", @@ -54,7 +53,6 @@ "@types/jsdom": "^21.1.7", "@types/memory-cache": "^0.2.6", "@types/node": "^22.15.0", - "@vitest/coverage-v8": "^3.0.0", "esbuild": "^0.25.8", "prettier": "^3.3.3", "rimraf": "^6.0.1", @@ -62,10 +60,9 @@ "rollup-plugin-scss": "^4.0.1", "rollup-plugin-visualizer": "^5.12.0", "semantic-release": "^25.0.2", + "semantic-release-monorepo": "^8.0.2", "tslib": "^2.8.1", - "tsx": "^4.19.2", - "vitest": "^3.0.0", - "yargs": "^17.7.2" + "tsx": "^4.19.2" }, "engines": { "node": ">=22.14" @@ -132,20 +129,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@asamuzakjp/css-color": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.1.4.tgz", @@ -174,66 +157,16 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", - "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.5" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/types": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", - "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@bcoe/v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", - "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -1261,16 +1194,6 @@ "node": ">=12" } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", @@ -1321,9 +1244,9 @@ "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1474,6 +1397,44 @@ "node": ">= 10" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@npmcli/agent": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", @@ -1976,22 +1937,6 @@ "node": ">=14" } }, - "node_modules/@playwright/test": { - "version": "1.57.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.57.0.tgz", - "integrity": "sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "playwright": "1.57.0" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@pnpm/config.env-replace": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", @@ -3034,17 +2979,6 @@ "@types/node": "*" } }, - "node_modules/@types/chai": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", - "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/deep-eql": "*", - "assertion-error": "^2.0.1" - } - }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -3055,13 +2989,6 @@ "@types/node": "*" } }, - "node_modules/@types/deep-eql": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", - "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/diff": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@types/diff/-/diff-6.0.0.tgz", @@ -3241,179 +3168,6 @@ "@types/node": "*" } }, - "node_modules/@vitest/coverage-v8": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz", - "integrity": "sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.3.0", - "@bcoe/v8-coverage": "^1.0.2", - "ast-v8-to-istanbul": "^0.3.3", - "debug": "^4.4.1", - "istanbul-lib-coverage": "^3.2.2", - "istanbul-lib-report": "^3.0.1", - "istanbul-lib-source-maps": "^5.0.6", - "istanbul-reports": "^3.1.7", - "magic-string": "^0.30.17", - "magicast": "^0.3.5", - "std-env": "^3.9.0", - "test-exclude": "^7.0.1", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@vitest/browser": "3.2.4", - "vitest": "3.2.4" - }, - "peerDependenciesMeta": { - "@vitest/browser": { - "optional": true - } - } - }, - "node_modules/@vitest/expect": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", - "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/mocker": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", - "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "3.2.4", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.17" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@vitest/mocker/node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/@vitest/pretty-format": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", - "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", - "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "3.2.4", - "pathe": "^2.0.3", - "strip-literal": "^3.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@vitest/snapshot": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", - "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.2.4", - "magic-string": "^0.30.17", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/snapshot/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@vitest/spy": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", - "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyspy": "^4.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", - "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.2.4", - "loupe": "^3.1.4", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, "node_modules/@xmldom/xmldom": { "version": "0.7.13", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.13.tgz", @@ -3608,49 +3362,20 @@ "dev": true, "license": "MIT" }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "license": "MIT" - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" - } - }, - "node_modules/ast-v8-to-istanbul": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.10.tgz", - "integrity": "sha512-p4K7vMz2ZSk3wN8l5o3y2bJAoZXT3VuJI5OLTATY/01CYWumWvwkUw0SqDBnNq6IiTO3qDa1eSQDibAV8g7XOQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.31", - "estree-walker": "^3.0.3", - "js-tokens": "^9.0.1" - } - }, - "node_modules/ast-v8-to-istanbul/node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" + "node": ">=8" } }, - "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", - "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", - "dev": true, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "license": "MIT" }, "node_modules/async": { @@ -4050,23 +3775,6 @@ "optional": true, "peer": true }, - "node_modules/chai": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", - "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -4093,16 +3801,6 @@ "node": ">=10" } }, - "node_modules/check-error": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", - "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, "node_modules/cheerio": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", @@ -4920,9 +4618,9 @@ } }, "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -4942,16 +4640,6 @@ "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", "license": "MIT" }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -4981,23 +4669,142 @@ "node": ">=8" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "node_modules/del": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", + "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", + "dev": true, "license": "MIT", + "dependencies": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, "engines": { - "node": ">=0.4.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } + "node_modules/del/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/del/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/del/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/del/node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/del/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/del/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } }, "node_modules/destroy": { "version": "1.2.0", @@ -5493,13 +5300,6 @@ "node": ">= 0.4" } }, - "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "dev": true, - "license": "MIT" - }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -5671,16 +5471,6 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/expect-type": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", - "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/exponential-backoff": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz", @@ -5800,6 +5590,33 @@ ], "license": "MIT" }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -5923,6 +5740,16 @@ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", "license": "MIT" }, + "node_modules/file-url": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/file-url/-/file-url-3.0.0.tgz", + "integrity": "sha512-g872QGsHexznxkIAdK8UiZRe7SkE6kvylShU4Nsj8NvfvZag7S0QuQ4IgvPDkk75HxgjIVDwycFTDAgIiO4nDA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -5998,6 +5825,19 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/find-up-simple": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", @@ -6175,6 +6015,13 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -6338,9 +6185,8 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "devOptional": true, "license": "ISC", - "optional": true, - "peer": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -6509,13 +6355,6 @@ "node": ">=18" } }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, "node_modules/htmlparser2": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", @@ -6775,6 +6614,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -6935,6 +6786,26 @@ "node": ">=8" } }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-obj": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", @@ -7042,89 +6913,6 @@ "node": "^18.17 || >=20.6.1" } }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report/node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/istanbul-lib-report/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", - "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", - "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jackspeak": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", @@ -7362,6 +7150,20 @@ "node": ">=4" } }, + "node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -7442,13 +7244,6 @@ "node": ">=0.1.90" } }, - "node_modules/loupe": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", - "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", - "dev": true, - "license": "MIT" - }, "node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", @@ -7465,18 +7260,6 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, - "node_modules/magicast": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", - "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.25.4", - "@babel/types": "^7.25.4", - "source-map-js": "^1.2.0" - } - }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -7623,6 +7406,16 @@ "dev": true, "license": "MIT" }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -11044,29 +10837,68 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-map": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", - "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", + "node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=18" + "dependencies": { + "p-limit": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=6" } }, - "node_modules/p-reduce": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", - "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==", + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-reduce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", + "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -11184,6 +11016,16 @@ "node": ">=4" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -11247,16 +11089,6 @@ "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", "license": "MIT" }, - "node_modules/pathval": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", - "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.16" - } - }, "node_modules/peek-readable": { "version": "5.4.2", "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.4.2.tgz", @@ -11390,51 +11222,17 @@ "node": ">=4" } }, - "node_modules/playwright": { - "version": "1.57.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.57.0.tgz", - "integrity": "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "playwright-core": "1.57.0" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/playwright-core": { - "version": "1.57.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.57.0.tgz", - "integrity": "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==", - "devOptional": true, - "license": "Apache-2.0", - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/playwright/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "node_modules/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "find-up": "^3.0.0" + }, "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=8" } }, "node_modules/postcss": { @@ -11585,6 +11383,34 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/ramda": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.2.tgz", + "integrity": "sha512-SbiLPU40JuJniHexQSAgad32hfwd+DRUdwF2PlVuI5RZD0/vahUco7R8vD86J/tcEKKF9vZrUVwgtmGCqlCKyA==", + "dev": true, + "license": "MIT" + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -11841,6 +11667,17 @@ "node": ">= 4" } }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rimraf": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", @@ -11997,6 +11834,30 @@ "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", "license": "MIT" }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -12122,6 +11983,191 @@ "node": "^22.14.0 || >= 24.10.0" } }, + "node_modules/semantic-release-monorepo": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/semantic-release-monorepo/-/semantic-release-monorepo-8.0.2.tgz", + "integrity": "sha512-TQC6KKIA0ATjii1OT0ZmQqcVzBJoaetJaJBC8FmKkg1IbDR4wBsuX6gl6UHDdijRDl8YyXqahj2hkJNyV6m9Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "execa": "^5.1.1", + "file-url": "^3.0.0", + "fs-extra": "^10.0.1", + "get-stream": "^6.0.1", + "git-log-parser": "^1.2.0", + "p-each-series": "^2.1.0", + "p-limit": "^3.1.0", + "pkg-up": "^3.1.0", + "ramda": "^0.27.2", + "read-pkg": "^5.2.0", + "semantic-release-plugin-decorators": "^4.0.0", + "tempy": "1.0.1" + }, + "peerDependencies": { + "semantic-release": ">=22.0.7" + } + }, + "node_modules/semantic-release-monorepo/node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/semantic-release-monorepo/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/semantic-release-monorepo/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true, + "license": "ISC" + }, + "node_modules/semantic-release-monorepo/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/semantic-release-monorepo/node_modules/p-each-series": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", + "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release-monorepo/node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/semantic-release-monorepo/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/semantic-release-monorepo/node_modules/temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/semantic-release-monorepo/node_modules/tempy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-1.0.1.tgz", + "integrity": "sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "del": "^6.0.0", + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release-monorepo/node_modules/tempy/node_modules/type-fest": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release-monorepo/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/semantic-release-monorepo/node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/semantic-release-plugin-decorators": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/semantic-release-plugin-decorators/-/semantic-release-plugin-decorators-4.0.0.tgz", + "integrity": "sha512-5eqaITbgGJu7AWCqY/ZwDh3TCS84Q9i470AImwP9vw3YcFRyR8sEb499Zbnqa076bv02yFUn88GtloQMXQsBrg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "semantic-release": ">20" + } + }, "node_modules/semantic-release/node_modules/@semantic-release/error": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", @@ -12729,13 +12775,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true, - "license": "ISC" - }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -13030,13 +13069,6 @@ "node": "*" } }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true, - "license": "MIT" - }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -13046,13 +13078,6 @@ "node": ">= 0.8" } }, - "node_modules/std-env": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", - "dev": true, - "license": "MIT" - }, "node_modules/stream-combiner2": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", @@ -13205,26 +13230,6 @@ "node": ">=0.10.0" } }, - "node_modules/strip-literal": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", - "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "js-tokens": "^9.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/strip-literal/node_modules/js-tokens": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", - "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", - "dev": true, - "license": "MIT" - }, "node_modules/strtok3": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-9.1.1.tgz", @@ -13813,101 +13818,6 @@ "devOptional": true, "license": "MIT" }, - "node_modules/test-exclude": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", - "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^10.4.1", - "minimatch": "^9.0.4" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/test-exclude/node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/test-exclude/node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/test-exclude/node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", @@ -13962,20 +13872,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true, - "license": "MIT" - }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -13992,36 +13888,6 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinypool": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", - "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, - "node_modules/tinyrainbow": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", - "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/tldts": { "version": "6.1.86", "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", @@ -14527,36 +14393,6 @@ } } }, - "node_modules/vite-node": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", - "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.4.1", - "es-module-lexer": "^1.7.0", - "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vite-node/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, "node_modules/vite-plugin-image-optimizer": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/vite-plugin-image-optimizer/-/vite-plugin-image-optimizer-1.1.8.tgz", @@ -14585,86 +14421,6 @@ "vite": ">=2.x" } }, - "node_modules/vitest": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", - "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/expect": "3.2.4", - "@vitest/mocker": "3.2.4", - "@vitest/pretty-format": "^3.2.4", - "@vitest/runner": "3.2.4", - "@vitest/snapshot": "3.2.4", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "debug": "^4.4.1", - "expect-type": "^1.2.1", - "magic-string": "^0.30.17", - "pathe": "^2.0.3", - "picomatch": "^4.0.2", - "std-env": "^3.9.0", - "tinybench": "^2.9.0", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.14", - "tinypool": "^1.1.1", - "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", - "vite-node": "3.2.4", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/debug": "^4.1.12", - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.2.4", - "@vitest/ui": "3.2.4", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/debug": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, - "node_modules/vitest/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", @@ -14756,23 +14512,6 @@ "node": ">= 8" } }, - "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, - "bin": { - "why-is-node-running": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/winston": { "version": "3.17.0", "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", diff --git a/package.json b/package.json index 11b8dde..cbb4616 100644 --- a/package.json +++ b/package.json @@ -22,17 +22,9 @@ "postbuild": "npm pack && node scripts/rename-pack.js", "startup:optimize": "tsx scripts/startup-optimizer.js", "startup:profile": "node --prof bin/pp-dev.js", + "test": "cd test && npm run build", "release": "semantic-release", - "version": "npm version --commit-hooks false --git-tag-version false", - "test": "npm run test:all", - "test:unit": "vitest run --config vitest.config.ts", - "test:unit:watch": "vitest --config vitest.config.ts", - "test:unit:coverage": "vitest run --config vitest.config.ts --coverage", - "test:integration": "vitest run --config vitest.integration.config.ts", - "test:integration:watch": "vitest --config vitest.integration.config.ts", - "test:e2e": "node scripts/run-tests.js", - "test:e2e:ui": "npx playwright test --ui", - "test:all": "npm run test:unit && npm run test:integration" + "version": "npm version --commit-hooks false --git-tag-version false" }, "exports": { ".": { @@ -104,7 +96,6 @@ "winston": "^3.17.0" }, "devDependencies": { - "@playwright/test": "^1.57.0", "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^12.1.1", "@rollup/plugin-url": "^8.0.2", @@ -117,7 +108,6 @@ "@types/jsdom": "^21.1.7", "@types/memory-cache": "^0.2.6", "@types/node": "^22.15.0", - "@vitest/coverage-v8": "^3.0.0", "esbuild": "^0.25.8", "prettier": "^3.3.3", "rimraf": "^6.0.1", @@ -126,9 +116,7 @@ "rollup-plugin-visualizer": "^5.12.0", "semantic-release": "^25.0.2", "tslib": "^2.8.1", - "tsx": "^4.19.2", - "vitest": "^3.0.0", - "yargs": "^17.7.2" + "tsx": "^4.19.2" }, "files": [ "bin", diff --git a/run-tests.sh b/run-tests.sh deleted file mode 100644 index 0d1d24a..0000000 --- a/run-tests.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh -case "$1" in - "dev-commonjs") - echo "Starting CommonJS dev server..." - cd /app/tests/test-commonjs - npm run reinstall - npm i --force - npm run dev -- --host - ;; - "dev-nextjs") - echo "Starting Next.js dev server..." - cd /app/tests/test-nextjs - npm run reinstall - npm i --force - npm run dev -- --host 0.0.0.0 - ;; - "dev-nextjs-cjs") - echo "Starting Next.js CJS dev server..." - cd /app/tests/test-nextjs-cjs - npm run reinstall - npm i --force - npm run dev -- --host 0.0.0.0 - ;; - *) - echo "Usage: docker run [dev-commonjs|dev-nextjs|dev-nextjs-cjs]" - echo " dev-commonjs - Start CommonJS dev server" - echo " dev-nextjs - Start Next.js dev server" - echo " dev-nextjs-cjs - Start Next.js CJS dev server" - exit 1 - ;; -esac diff --git a/scripts/run-tests.js b/scripts/run-tests.js deleted file mode 100644 index 7154514..0000000 --- a/scripts/run-tests.js +++ /dev/null @@ -1,306 +0,0 @@ -import { spawn } from 'child_process'; -import path from 'path'; -import { promisify } from 'util'; -import { exec } from 'child_process'; -import yargs from 'yargs'; -import { hideBin } from 'yargs/helpers'; - -// Parse command line arguments using yargs -const argv = yargs(hideBin(process.argv)) - .option('test-types', { - alias: 't', - type: 'array', - description: 'Test types to run (dev-commonjs, dev-nextjs, dev-nextjs-cjs)', - default: ['dev-commonjs', 'dev-nextjs', 'dev-nextjs-cjs'], - }) - .option('all', { - alias: 'a', - type: 'boolean', - description: 'Run all test types', - default: false, - }) - .option('help', { - alias: 'h', - type: 'boolean', - description: 'Show help information', - }) - .option('interactive', { - alias: 'i', - type: 'boolean', - description: 'Run in interactive mode', - default: false, - }) - .help().argv; - -// Determine test types to run -let testTypes = []; - -if (argv.all) { - testTypes = ['dev-commonjs', 'dev-nextjs', 'dev-nextjs-cjs']; -} else if (argv.testTypes && argv.testTypes.length > 0) { - testTypes = argv.testTypes; -} else { - // Fallback to positional arguments for backward compatibility - const positionalArgs = argv._; - - if (positionalArgs.includes('all')) { - testTypes = ['dev-commonjs', 'dev-nextjs', 'dev-nextjs-cjs']; - } else if (positionalArgs.length > 0) { - testTypes = positionalArgs; - } else { - testTypes = ['dev-commonjs', 'dev-nextjs', 'dev-nextjs-cjs']; - } -} - -const testFolders = new Map(testTypes.map((type) => [type, `test-${type.replace(/^dev-/, '')}`])); - -let port = 3005; -let runningContainers = []; - -const runCommand = (command, args = [], options = {}) => { - return new Promise((resolve, reject) => { - console.log(`Running: ${command} ${args.join(' ')}`); - - const child = spawn(command, args, { - stdio: 'inherit', - shell: true, - ...options, - }); - - child.on('close', (code) => { - if (code === 0) { - resolve(); - } else if (code === 137) { - // SIGKILL - not an error, container was killed - console.log(`โœ… Command ${command} was SIGKILLed (exit code 137)`); - resolve(); - } else if (code === 139) { - // SIGSEGV - not an error, container segfaulted - console.log(`โœ… Command ${command} had SIGSEGV (exit code 139)`); - resolve(); - } else { - reject(new Error(`Command failed with exit code ${code}`)); - } - }); - - child.on('error', (error) => { - reject(error); - }); - }); -}; - -// Run command with input -const runCommandChild = (command, args = [], options = {}, callback) => { - const child = spawn(command, args, { - stdio: ['pipe', 'inherit', 'inherit'], - shell: true, - ...options, - }); - - if (callback) { - child.on('close', (code) => { - if (code === 0) { - callback(null, { exitCode: code }); - } else if (code === 137) { - // SIGKILL - not an error, container was killed - console.log(`โœ… Command ${command} was SIGKILLed (exit code 137)`); - callback(null, { exitCode: code }); - } else if (code === 139) { - // SIGSEGV - not an error, container segfaulted - console.log(`โœ… Command ${command} had SIGSEGV (exit code 139)`); - callback(null, { exitCode: code }); - } else { - callback(new Error(`Command failed with exit code ${code}`), { exitCode: code }); - } - }); - - child.on('error', (error) => { - callback(error, null); - }); - } - - return child; -}; - -// Run command as shell - -let hasCleanup = false; - -const cleanup = async () => { - hasCleanup = true; - - console.log('\n๐Ÿ›‘ Received interrupt signal. Cleaning up...'); - - for (const container of runningContainers) { - try { - console.log(`Stopping container: ${container}`); - await runCommand('docker', ['stop', container]); - console.log(`Removed container: ${container}`); - } catch (error) { - console.log(`Container ${container} may already be stopped`); - } - } - - try { - console.log('Removing Docker image...'); - await runCommand('docker', ['rmi', 'pp-dev-tests', '--force']); - } catch (error) { - console.log('Docker image may already be removed'); - } - - console.log('โœ… Cleanup completed'); - process.exit(0); -}; - -// Handle Ctrl+C and other termination signals -process.on('SIGINT', cleanup); -process.on('SIGTERM', cleanup); -process.on('SIGQUIT', cleanup); - -const main = async () => { - try { - await runCommand('docker', ['build', '-f', './Dockerfile', '-t', 'pp-dev-tests', '.']); - - const configPort = 3004; - - // Test helper configuration - runCommand('docker', [ - 'run', - '--rm', - '-it', - '-p', - `${configPort}:3000`, - '-v', - `${path.resolve(process.cwd(), 'tests')}:/app/tests`, - '-v', - '/app/tests/test-commonjs', - '--name', - 'pp-dev-tests', - '--entrypoint', - 'sh', - 'pp-dev-tests', - ]).catch((error) => { - runCommand('docker', ['rmi', 'pp-dev-tests', '--force']); - process.exit(1); - }); - - await new Promise((resolve) => { - setTimeout(resolve, 1000); - }); - - await runCommand('docker', ['exec', '-it', '-w', '/app/tests/test-commonjs', 'pp-dev-tests', 'npm run reinstall']); - - // Run playwright tests - await runCommand('npx', ['playwright', 'test', 'e2e/config.spec.ts'], { - env: { ...process.env, BASE_URL: `http://localhost:${configPort}`, TEST_TYPE: 'config' }, - }).catch(async (error) => { - await runCommand('docker', ['stop', 'pp-dev-tests']); - await runCommand('docker', ['rmi', 'pp-dev-tests', '--force']); - - console.error('โŒ Error:', error.message); - - process.exit(1); - }); - - await runCommand('docker', ['stop', 'pp-dev-tests']); - - for (const folder of testTypes) { - console.log(`\n๐Ÿš€ Starting tests for ${folder}...`); - - // Start container in detached mode - runCommand('docker', [ - 'run', - '--rm', - '-p', - `${port}:3000`, - '-v', - `${path.resolve(process.cwd(), 'tests', testFolders.get(folder))}:/app/tests/${testFolders.get(folder)}`, - '--name', - testFolders.get(folder), - 'pp-dev-tests', - folder, - ]); - console.log(`โœ… Container ${testFolders.get(folder)} started`); - runningContainers.push(testFolders.get(folder)); - - await new Promise((resolve) => { - setTimeout(resolve, 1000); - }); - - // Wait for server to start in container - await new Promise((resolve, reject) => { - const maxAttempts = 240; // 240 seconds timeout - let attempts = 0; - console.log(`Checking server on port ${port}`); - - const checkServer = async () => { - // check if container is running - const container = await promisify(exec)(`docker ps --filter name=${testFolders.get(folder)}`); - - if (!container.stdout.includes(testFolders.get(folder))) { - reject(new Error('Container not running')); - - return; - } - - if (hasCleanup) { - reject(new Error('Cleanup signal received')); - - return; - } - - try { - const response = await fetch(`http://localhost:${port}`); - - if (response.ok) { - console.log(`โœ… Server is ready on port ${port}`); - - resolve(); - } else { - throw new Error(`Server responded with status ${response.status}`); - } - } catch (error) { - attempts++; - - if (attempts >= maxAttempts) { - reject(new Error(`Server failed to start within ${maxAttempts} seconds`)); - - return; - } - - console.log(`โณ Waiting for server... (attempt ${attempts}/${maxAttempts})`); - setTimeout(checkServer, 1000); - } - }; - - checkServer(); - }); - - // Run playwright tests - await runCommand('npx', ['playwright', 'test'], { - env: { ...process.env, BASE_URL: `http://localhost:${port}`, TEST_TYPE: folder }, - }); - - await runCommand('docker', ['stop', testFolders.get(folder)]); - // Clean up this container - await runCommand('docker', ['rmi', testFolders.get(folder), '--force']); - - // Remove from running containers list - runningContainers = runningContainers.filter((c) => c !== testFolders.get(folder)); - - console.log(`โœ… Tests completed for ${testFolders.get(folder)}`); - port++; - } - - // Final cleanup - await runCommand('docker', ['rmi', 'pp-dev-tests', '--force']); - console.log('\n๐ŸŽ‰ All tests completed successfully!'); - } catch (error) { - console.error('โŒ Error:', error.message); - await cleanup(); - } -}; - -main().finally(() => { - cleanup(); -}); diff --git a/src/plugins/mi-topbar-plugin.ts b/src/plugins/mi-topbar-plugin.ts index ad1f1f1..9e16194 100644 --- a/src/plugins/mi-topbar-plugin.ts +++ b/src/plugins/mi-topbar-plugin.ts @@ -7,17 +7,17 @@ export function miTopBarPlugin(): Plugin { return [ { tag: "script", - injectTo: "head-prepend", + injectTo: "body", attrs: { src: "/auth/info.js" }, }, { tag: "script", - injectTo: "head-prepend", + injectTo: "body", attrs: { src: "/js/main.js", defer: "defer" }, }, { tag: "link", - injectTo: "head-prepend", + injectTo: "head", attrs: { href: "/css/main.css", rel: "stylesheet" }, }, { diff --git a/tests/README.md b/tests/README.md deleted file mode 100644 index a330209..0000000 --- a/tests/README.md +++ /dev/null @@ -1,92 +0,0 @@ -# Docker Testing Environment - -This testing setup allows running e2e tests for pp-dev in an isolated Docker environment. - -## Test Structure - -- `test-commonjs` - Tests for CommonJS/Vite modules -- `test-nextjs` - Tests for Next.js applications (ESM) -- `test-nextjs-cjs` - Tests for Next.js applications (CommonJS) - -## Usage - -### 1. Build the package first - -```bash -# From project root directory -npm run build -``` - -This creates `metricinsights-pp-dev-latest.tgz` which is used by the test projects. - -### 2. Running Tests - -#### Run All Tests -```bash -npm test -# or -npm test -- --all -``` - -#### Run Specific Tests -```bash -# Only CommonJS tests -npm test -- -t dev-commonjs - -# Only Next.js tests -npm test -- -t dev-nextjs - -# Only Next.js CJS tests -npm test -- -t dev-nextjs-cjs -``` - -### 3. Manual Docker Commands - -#### Building Docker Image -```bash -docker build -f Dockerfile -t pp-dev-tests . -``` - -#### Run Dev Servers in Docker -```bash -# CommonJS dev server -docker run --rm -p 3000:3000 pp-dev-tests dev-commonjs - -# Next.js dev server -docker run --rm -p 3000:3000 pp-dev-tests dev-nextjs - -# Next.js CJS dev server -docker run --rm -p 3000:3000 pp-dev-tests dev-nextjs-cjs -``` - -## Available Test Types - -### Development Servers -- `dev-commonjs` - Start CommonJS/Vite dev server (port 3000) -- `dev-nextjs` - Start Next.js dev server (port 3000) -- `dev-nextjs-cjs` - Start Next.js CJS dev server (port 3000) - -## Docker Image Structure - -``` -/app/ -โ”œโ”€โ”€ metricinsights-pp-dev-latest.tgz # Built pp-dev package -โ””โ”€โ”€ tests/ - โ”œโ”€โ”€ test-commonjs/ # CommonJS/Vite tests - โ”œโ”€โ”€ test-nextjs/ # Next.js ESM tests - โ””โ”€โ”€ test-nextjs-cjs/ # Next.js CJS tests -``` - -## Configuration - -Dockerfile automatically: -1. Uses Node.js 22 Alpine -2. Copies the built pp-dev package -3. Copies test folders -4. Installs dependencies and runs dev servers - -## Requirements - -- Docker -- Node.js 22+ (for local development) -- Playwright (`npx playwright install` for local browser testing) diff --git a/tests/TESTING.md b/tests/TESTING.md deleted file mode 100644 index 7447225..0000000 --- a/tests/TESTING.md +++ /dev/null @@ -1,184 +0,0 @@ -# pp-dev Testing Guide - -This document describes the comprehensive testing setup for the pp-dev package. - -## Test Architecture - -The testing suite is organized into three layers: - -### 1. Unit Tests (`tests/unit/`) - -Unit tests verify isolated functions and modules without external dependencies. - -``` -tests/unit/ -โ”œโ”€โ”€ config/ -โ”‚ โ”œโ”€โ”€ config.loader.spec.ts # Config file loading & caching -โ”‚ โ””โ”€โ”€ config.validation.spec.ts # Config file name patterns -โ”œโ”€โ”€ plugin/ -โ”‚ โ””โ”€โ”€ plugin.normalize.spec.ts # normalizeVitePPDevConfig function -โ”œโ”€โ”€ lib/ -โ”‚ โ”œโ”€โ”€ url.helper.spec.ts # URL replacement utilities -โ”‚ โ””โ”€โ”€ color.helper.spec.ts # Terminal color utilities -โ””โ”€โ”€ helpers/ - โ””โ”€โ”€ formatting.spec.ts # Template literal helpers -``` - -**Run unit tests:** -```bash -npm run test:unit # Run once -npm run test:unit:watch # Watch mode -npm run test:unit:coverage # With coverage report -``` - -### 2. Integration Tests (`tests/integration/`) - -Integration tests verify middleware and server components working together. - -``` -tests/integration/ -โ””โ”€โ”€ middleware/ - โ”œโ”€โ”€ pp-redirect.spec.ts # PP redirect middleware - โ”œโ”€โ”€ proxy-cache.spec.ts # Proxy cache middleware - โ””โ”€โ”€ rewrite-response.spec.ts # Response rewriting middleware -``` - -**Run integration tests:** -```bash -npm run test:integration # Run once -npm run test:integration:watch # Watch mode -``` - -### 3. E2E Tests (`e2e/`) - -End-to-end tests using Playwright verify complete user workflows. - -``` -e2e/ -โ”œโ”€โ”€ server.spec.ts # Basic server functionality -โ”œโ”€โ”€ config.spec.ts # Config file watcher -โ”œโ”€โ”€ toolbar/ -โ”‚ โ”œโ”€โ”€ toolbar.visibility.spec.ts # Toolbar display -โ”‚ โ”œโ”€โ”€ toolbar.minimize.spec.ts # Minimize functionality -โ”‚ โ”œโ”€โ”€ toolbar.sync.spec.ts # Sync button -โ”‚ โ””โ”€โ”€ toolbar.popups.spec.ts # Popup notifications -โ””โ”€โ”€ config-watcher/ - โ”œโ”€โ”€ watcher.reload.spec.ts # Config change detection - โ””โ”€โ”€ watcher.debounce.spec.ts # Debounce behavior -``` - -**Run E2E tests:** -```bash -npm run test:e2e # Run all E2E tests via Docker -npm run test:e2e:ui # Run with Playwright UI -``` - -## Running All Tests - -```bash -npm run test:all # Runs unit, integration, and E2E tests -``` - -## Test Configuration Files - -| File | Purpose | -|------|---------| -| `vitest.config.ts` | Unit test configuration | -| `vitest.integration.config.ts` | Integration test configuration | -| `playwright.config.ts` | E2E test configuration | -| `tests/tsconfig.json` | TypeScript config for tests | -| `tests/setup.ts` | Global test setup | - -## Test Environment Variables - -For E2E tests: -- `TEST_TYPE` - Type of test (dev-commonjs, dev-nextjs, dev-nextjs-cjs, config) -- `BASE_URL` - Base URL for the test server - -## Writing Tests - -### Unit Test Example - -```typescript -import { describe, it, expect, vi } from 'vitest'; - -describe('MyFunction', () => { - it('should do something', () => { - const result = myFunction('input'); - expect(result).toBe('expected'); - }); -}); -``` - -### Integration Test Example - -```typescript -import { describe, it, expect } from 'vitest'; -import { initPPRedirect } from '../../../src/lib/pp-redirect.middleware.js'; - -describe('PP Redirect Middleware', () => { - it('should redirect root to base path', async () => { - const middleware = initPPRedirect('/p/template/', 'template'); - // Test middleware behavior... - }); -}); -``` - -### E2E Test Example - -```typescript -import { test, expect } from '@playwright/test'; - -test('should display toolbar', async ({ page, baseURL }) => { - await page.goto(baseURL!); - await expect(page.locator('.pp-dev-info')).toBeVisible(); -}); -``` - -## Coverage Reports - -Coverage reports are generated in: -- `coverage/unit/` - Unit test coverage -- `coverage/integration/` - Integration test coverage -- `playwright-report/` - E2E test reports - -## Docker Testing - -E2E tests run in Docker containers for isolation: - -```bash -# Build test image -docker build -f Dockerfile -t pp-dev-tests . - -# Run specific test type -docker run --rm -p 3000:3000 pp-dev-tests dev-commonjs -``` - -See `tests/README.md` for more details on Docker testing. - -## Debugging Tests - -### Unit/Integration Tests -```bash -npm run test:unit -- --reporter=verbose -npm run test:integration -- --reporter=verbose -``` - -### E2E Tests -```bash -npx playwright test --debug -npx playwright test --ui -``` - -## Test Fixtures - -Test fixtures and mock data are located in: -- `tests/test-commonjs/` - CommonJS/Vite test project -- `tests/test-nextjs/` - Next.js ESM test project -- `tests/test-nextjs-cjs/` - Next.js CJS test project - -## Known Issues - -1. **Config normalization spread bug**: The `normalizeVitePPDevConfig` function has a bug where `...config` at the end overwrites normalized values. Tests document this behavior with comments. - -2. **Next.js toolbar tests**: Toolbar E2E tests are skipped for Next.js as the toolbar injection works differently. diff --git a/tests/integration/middleware/pp-redirect.spec.ts b/tests/integration/middleware/pp-redirect.spec.ts deleted file mode 100644 index f5502b5..0000000 --- a/tests/integration/middleware/pp-redirect.spec.ts +++ /dev/null @@ -1,201 +0,0 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { createServer, type Server } from 'http'; -import { initPPRedirect } from '../../../src/lib/pp-redirect.middleware.js'; - -// Simple test server helper -function createTestServer(middleware: any): Promise<{ server: Server; port: number }> { - return new Promise((resolve) => { - const server = createServer((req, res) => { - const next = () => { - res.statusCode = 200; - res.end('OK'); - }; - middleware(req, res, next); - }); - - server.listen(0, () => { - const address = server.address(); - const port = typeof address === 'object' ? address?.port ?? 0 : 0; - - resolve({ server, port }); - }); - }); -} - -async function makeRequest(port: number, path: string): Promise<{ status: number; location?: string; body: string }> { - return new Promise((resolve) => { - const http = require('http'); - const req = http.request( - { - hostname: 'localhost', - port, - path, - method: 'GET', - }, - (res: any) => { - let body = ''; - res.on('data', (chunk: string) => (body += chunk)); - res.on('end', () => { - resolve({ - status: res.statusCode, - location: res.headers.location, - body, - }); - }); - } - ); - req.end(); - }); -} - -describe('PP Redirect Middleware', () => { - describe('Root Path Redirection', () => { - it('should redirect root path to base path', async () => { - const middleware = initPPRedirect('/p/my-template/', 'my-template'); - const { server, port } = await createTestServer(middleware); - - try { - const response = await makeRequest(port, '/'); - - expect(response.status).toBe(302); - expect(response.location).toBe('/p/my-template/'); - } finally { - server.close(); - } - }); - - it('should not redirect requests to base path', async () => { - const middleware = initPPRedirect('/p/my-template/', 'my-template'); - const { server, port } = await createTestServer(middleware); - - try { - const response = await makeRequest(port, '/p/my-template/'); - - expect(response.status).toBe(200); - expect(response.body).toBe('OK'); - } finally { - server.close(); - } - }); - - it('should not redirect requests to sub-paths under base', async () => { - const middleware = initPPRedirect('/p/my-template/', 'my-template'); - const { server, port } = await createTestServer(middleware); - - try { - const response = await makeRequest(port, '/p/my-template/page'); - - expect(response.status).toBe(200); - expect(response.body).toBe('OK'); - } finally { - server.close(); - } - }); - }); - - describe('V7 Features Path', () => { - it('should handle /pl/ prefix for v7 features', async () => { - const middleware = initPPRedirect('/pl/my-template/', 'my-template'); - const { server, port } = await createTestServer(middleware); - - try { - const response = await makeRequest(port, '/'); - - expect(response.status).toBe(302); - expect(response.location).toBe('/pl/my-template/'); - } finally { - server.close(); - } - }); - - it('should not redirect /pl/ path requests', async () => { - const middleware = initPPRedirect('/pl/my-template/', 'my-template'); - const { server, port } = await createTestServer(middleware); - - try { - const response = await makeRequest(port, '/pl/my-template/'); - - expect(response.status).toBe(200); - } finally { - server.close(); - } - }); - }); - - describe('Base Path Without Trailing Slash', () => { - it('should handle base path without trailing slash', async () => { - const middleware = initPPRedirect('/p/my-template', 'my-template'); - const { server, port } = await createTestServer(middleware); - - try { - const response = await makeRequest(port, '/'); - - expect(response.status).toBe(302); - // Should still redirect properly - expect(response.location).toContain('/p/my-template'); - } finally { - server.close(); - } - }); - }); - - describe('Special Characters in Template Name', () => { - it('should handle template names with hyphens', async () => { - const middleware = initPPRedirect('/p/my-cool-template/', 'my-cool-template'); - const { server, port } = await createTestServer(middleware); - - try { - const response = await makeRequest(port, '/'); - - expect(response.status).toBe(302); - expect(response.location).toBe('/p/my-cool-template/'); - } finally { - server.close(); - } - }); - - it('should handle template names with underscores', async () => { - const middleware = initPPRedirect('/p/my_template/', 'my_template'); - const { server, port } = await createTestServer(middleware); - - try { - const response = await makeRequest(port, '/'); - - expect(response.status).toBe(302); - expect(response.location).toBe('/p/my_template/'); - } finally { - server.close(); - } - }); - }); - - describe('Pass Through Behavior', () => { - it('should pass through Vite internal requests', async () => { - const middleware = initPPRedirect('/p/template/', 'template'); - const { server, port } = await createTestServer(middleware); - - try { - const response = await makeRequest(port, '/@vite/client'); - - expect(response.status).toBe(200); - expect(response.body).toBe('OK'); - } finally { - server.close(); - } - }); - - it('should pass through other paths', async () => { - const middleware = initPPRedirect('/p/template/', 'template'); - const { server, port } = await createTestServer(middleware); - - try { - const response = await makeRequest(port, '/api/data'); - - expect(response.status).toBe(200); - expect(response.body).toBe('OK'); - } finally { - server.close(); - } - }); - }); -}); diff --git a/tests/integration/middleware/proxy-cache.spec.ts b/tests/integration/middleware/proxy-cache.spec.ts deleted file mode 100644 index c678f5e..0000000 --- a/tests/integration/middleware/proxy-cache.spec.ts +++ /dev/null @@ -1,162 +0,0 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { initProxyCache } from '../../../src/lib/proxy-cache.middleware.js'; - -describe('Proxy Cache Middleware', () => { - let mockDevServer: any; - let mockReq: any; - let mockRes: any; - let nextFn: ReturnType; - - beforeEach(() => { - mockDevServer = { - middlewares: { use: vi.fn() }, - config: { - logger: { - info: vi.fn(), - warn: vi.fn(), - error: vi.fn(), - }, - }, - }; - - mockReq = { - method: 'GET', - url: '/api/test', - headers: {}, - }; - - mockRes = { - statusCode: 200, - setHeader: vi.fn(), - getHeader: vi.fn(), - write: vi.fn(), - end: vi.fn(), - on: vi.fn(), - }; - - nextFn = vi.fn(); - }); - - afterEach(() => { - vi.clearAllMocks(); - }); - - describe('Initialization', () => { - it('should create middleware function', () => { - const middleware = initProxyCache({ devServer: mockDevServer, ttl: 5000 }); - - expect(typeof middleware).toBe('function'); - }); - - it('should accept custom TTL', () => { - const middleware = initProxyCache({ devServer: mockDevServer, ttl: 10000 }); - - expect(typeof middleware).toBe('function'); - }); - }); - - describe('Request Handling', () => { - it('should call next for GET requests', () => { - const middleware = initProxyCache({ devServer: mockDevServer, ttl: 5000 }); - - middleware(mockReq, mockRes, nextFn); - - expect(nextFn).toHaveBeenCalled(); - }); - - it('should call next for POST requests', () => { - mockReq.method = 'POST'; - const middleware = initProxyCache({ devServer: mockDevServer, ttl: 5000 }); - - middleware(mockReq, mockRes, nextFn); - - expect(nextFn).toHaveBeenCalled(); - }); - - it('should handle requests with query parameters', () => { - mockReq.url = '/api/test?param=value'; - const middleware = initProxyCache({ devServer: mockDevServer, ttl: 5000 }); - - middleware(mockReq, mockRes, nextFn); - - expect(nextFn).toHaveBeenCalled(); - }); - }); - - describe('Cache Behavior', () => { - it('should not cache POST requests', () => { - mockReq.method = 'POST'; - const middleware = initProxyCache({ devServer: mockDevServer, ttl: 5000 }); - - // First request - middleware(mockReq, mockRes, nextFn); - - // Second request - middleware(mockReq, mockRes, nextFn); - - // Both should call next (no caching for POST) - expect(nextFn).toHaveBeenCalledTimes(2); - }); - - it('should not cache PUT requests', () => { - mockReq.method = 'PUT'; - const middleware = initProxyCache({ devServer: mockDevServer, ttl: 5000 }); - - middleware(mockReq, mockRes, nextFn); - - expect(nextFn).toHaveBeenCalled(); - }); - - it('should not cache DELETE requests', () => { - mockReq.method = 'DELETE'; - const middleware = initProxyCache({ devServer: mockDevServer, ttl: 5000 }); - - middleware(mockReq, mockRes, nextFn); - - expect(nextFn).toHaveBeenCalled(); - }); - }); - - describe('URL Patterns', () => { - it('should handle root URL', () => { - mockReq.url = '/'; - const middleware = initProxyCache({ devServer: mockDevServer, ttl: 5000 }); - - middleware(mockReq, mockRes, nextFn); - - expect(nextFn).toHaveBeenCalled(); - }); - - it('should handle nested paths', () => { - mockReq.url = '/api/v1/users/123/profile'; - const middleware = initProxyCache({ devServer: mockDevServer, ttl: 5000 }); - - middleware(mockReq, mockRes, nextFn); - - expect(nextFn).toHaveBeenCalled(); - }); - - it('should handle URLs with special characters', () => { - mockReq.url = '/api/search?q=hello%20world&filter=name'; - const middleware = initProxyCache({ devServer: mockDevServer, ttl: 5000 }); - - middleware(mockReq, mockRes, nextFn); - - expect(nextFn).toHaveBeenCalled(); - }); - }); - - describe('TTL Configuration', () => { - it('should accept very short TTL', () => { - const middleware = initProxyCache({ devServer: mockDevServer, ttl: 100 }); - - expect(typeof middleware).toBe('function'); - }); - - it('should accept very long TTL', () => { - const middleware = initProxyCache({ devServer: mockDevServer, ttl: 3600000 }); // 1 hour - - expect(typeof middleware).toBe('function'); - }); - }); -}); diff --git a/tests/integration/middleware/rewrite-response.spec.ts b/tests/integration/middleware/rewrite-response.spec.ts deleted file mode 100644 index 45cd5a0..0000000 --- a/tests/integration/middleware/rewrite-response.spec.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { initRewriteResponse } from '../../../src/lib/rewrite-response.middleware.js'; - -describe('Rewrite Response Middleware', () => { - let mockReq: any; - let mockRes: any; - let nextFn: ReturnType; - - beforeEach(() => { - mockReq = { - url: '/test', - headers: { - host: 'localhost:3000', - }, - }; - - // Create a more complete mock response - mockRes = { - statusCode: 200, - _headers: {} as Record, - setHeader: vi.fn((name: string, value: string) => { - mockRes._headers[name.toLowerCase()] = value; - }), - getHeader: vi.fn((name: string) => mockRes._headers[name.toLowerCase()]), - write: vi.fn(), - end: vi.fn(), - on: vi.fn(), - once: vi.fn(), - emit: vi.fn(), - removeListener: vi.fn(), - }; - - nextFn = vi.fn(); - }); - - describe('Initialization', () => { - it('should create middleware function with url matcher and transformer', () => { - const urlMatcher = (url: string) => url.endsWith('.html'); - const transformer = (body: string) => Buffer.from(body.toUpperCase()); - - const middleware = initRewriteResponse(urlMatcher, transformer); - - expect(typeof middleware).toBe('function'); - }); - }); - - describe('URL Matching', () => { - it('should apply transformer when URL matches', () => { - // The actual middleware passes url, req, and res to the matcher - const urlMatcher = vi.fn((url: string, _req: any, _res: any) => url.endsWith('index.html')); - const transformer = vi.fn((body: string) => Buffer.from(body)); - - const middleware = initRewriteResponse(urlMatcher, transformer); - - mockReq.url = '/p/template/index.html'; - - middleware(mockReq, mockRes, nextFn); - - // Verify the URL matcher was called with the URL as first argument - expect(urlMatcher).toHaveBeenCalled(); - expect(urlMatcher.mock.calls[0][0]).toBe('/p/template/index.html'); - }); - - it('should not apply transformer when URL does not match', () => { - const urlMatcher = vi.fn((url: string) => url.endsWith('index.html')); - const transformer = vi.fn((body: string) => Buffer.from(body)); - - const middleware = initRewriteResponse(urlMatcher, transformer); - - mockReq.url = '/api/data.json'; - - middleware(mockReq, mockRes, nextFn); - - // Verify the URL matcher was called with the URL as first argument - expect(urlMatcher).toHaveBeenCalled(); - expect(urlMatcher.mock.calls[0][0]).toBe('/api/data.json'); - }); - }); - - describe('URL Patterns', () => { - it('should handle index.html detection', () => { - const urlMatcher = (url: string) => url.split('?')[0].endsWith('index.html'); - - expect(urlMatcher('/p/template/index.html')).toBe(true); - expect(urlMatcher('/p/template/index.html?v=123')).toBe(true); - expect(urlMatcher('/p/template/other.html')).toBe(false); - expect(urlMatcher('/api/data')).toBe(false); - }); - - it('should handle query string stripping', () => { - const urlMatcher = (url: string) => { - const path = url.split('?')[0]; - return path.endsWith('index.html'); - }; - - expect(urlMatcher('/index.html?cache=false')).toBe(true); - expect(urlMatcher('/page?file=index.html')).toBe(false); - }); - }); - - describe('Transformer Function', () => { - it('should support Buffer return from transformer', () => { - const urlMatcher = () => true; - const transformer = (body: string) => Buffer.from(body.replace('old', 'new')); - - const middleware = initRewriteResponse(urlMatcher, transformer); - - expect(typeof middleware).toBe('function'); - }); - }); - - describe('Middleware Behavior', () => { - it('should be a function that accepts req, res, and next', () => { - const middleware = initRewriteResponse( - () => false, - (body) => Buffer.from(body) - ); - - expect(typeof middleware).toBe('function'); - expect(middleware.length).toBeGreaterThanOrEqual(2); // At least req and res - }); - - it('should process requests without throwing', () => { - const middleware = initRewriteResponse( - (url) => url.includes('special'), - (body) => Buffer.from(body) - ); - - mockReq.url = '/normal/path'; - - // Should not throw - expect(() => middleware(mockReq, mockRes, nextFn)).not.toThrow(); - }); - }); -}); diff --git a/tests/setup.ts b/tests/setup.ts deleted file mode 100644 index aa949ff..0000000 --- a/tests/setup.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { vi, beforeEach, afterEach } from 'vitest'; - -// Global test setup - -// Reset all mocks before each test -beforeEach(() => { - vi.clearAllMocks(); -}); - -// Clean up after each test -afterEach(() => { - vi.restoreAllMocks(); -}); - -// Mock console methods to reduce noise in tests (optional) -// Uncomment if you want cleaner test output -// vi.spyOn(console, 'log').mockImplementation(() => {}); -// vi.spyOn(console, 'info').mockImplementation(() => {}); -// vi.spyOn(console, 'warn').mockImplementation(() => {}); - -// Global test utilities -export const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); - -// Helper to create temporary directories for tests -export const createTempDir = async (prefix: string = 'pp-dev-test') => { - const { mkdtemp } = await import('fs/promises'); - const { tmpdir } = await import('os'); - const { join } = await import('path'); - - return mkdtemp(join(tmpdir(), `${prefix}-`)); -}; - -// Helper to clean up temporary directories -export const cleanupTempDir = async (dir: string) => { - const { rm } = await import('fs/promises'); - - await rm(dir, { recursive: true, force: true }); -}; diff --git a/tests/test-commonjs/package-lock.json b/tests/test-commonjs/package-lock.json index 11afc07..ddafe2b 100644 --- a/tests/test-commonjs/package-lock.json +++ b/tests/test-commonjs/package-lock.json @@ -8,7 +8,7 @@ "name": "test-commonjs", "version": "1.0.0", "dependencies": { - "@metricinsights/pp-dev": "file:../../metricinsights-pp-dev-latest.tgz", + "@metricinsights/pp-dev": "file:../../packages/pp-dev/metricinsights-pp-dev-latest.tgz", "luxon": "^3.3.0" }, "devDependencies": { @@ -1081,9 +1081,9 @@ } }, "node_modules/@metricinsights/pp-dev": { - "version": "0.12.3-beta.1", - "resolved": "file:../../metricinsights-pp-dev-latest.tgz", - "integrity": "sha512-qh0UoDfSiceuTbmZYleg2tzOolx6APuXhN5C3pvGHKnq/oSmIiQey0gWBhOWTlxp47+Im9soFdt+3CBBmb6AIw==", + "version": "0.12.2", + "resolved": "file:../../packages/pp-dev/metricinsights-pp-dev-latest.tgz", + "integrity": "sha512-sKvzMMpf4yiX0qpkTg8A/ye10FXGL4H5gd2ZS1u8dfR7zevLeLr90zLg0MNrVmhYIZEavxhiwXgyHGC2daqjTw==", "license": "ISC", "dependencies": { "axios": "^1.7.8", diff --git a/tests/test-commonjs/package.json b/tests/test-commonjs/package.json index 1e29e4c..f58091d 100644 --- a/tests/test-commonjs/package.json +++ b/tests/test-commonjs/package.json @@ -6,10 +6,10 @@ "build": "pp-dev build --changelog", "changelog": "pp-dev changelog backups/test-changelog.zip dist --destination dist-zip", "dev": "pp-dev", - "reinstall": "npm i ../../metricinsights-pp-dev-latest.tgz" + "reinstall": "npm i ../../packages/pp-dev/metricinsights-pp-dev-latest.tgz" }, "dependencies": { - "@metricinsights/pp-dev": "file:../../metricinsights-pp-dev-latest.tgz", + "@metricinsights/pp-dev": "file:../../packages/pp-dev/metricinsights-pp-dev-latest.tgz", "luxon": "^3.3.0" }, "devDependencies": { diff --git a/tests/test-nextjs-cjs/package.json b/tests/test-nextjs-cjs/package.json index 506ec5b..2fa4ad5 100644 --- a/tests/test-nextjs-cjs/package.json +++ b/tests/test-nextjs-cjs/package.json @@ -1,16 +1,16 @@ { - "name": "test-nextjs-cjs", + "name": "test-nextjs", "version": "0.1.0", "private": true, "scripts": { - "dev": "pp-dev next", + "dev": "npx pp-dev next", "build": "next build", "start": "next start", - "reinstall": "npm i ../../metricinsights-pp-dev-latest.tgz", + "reinstall": "npm i ../../packages/pp-dev/metricinsights-pp-dev-latest.tgz", "lint": "next lint" }, "dependencies": { - "@metricinsights/pp-dev": "file:../../metricinsights-pp-dev-latest.tgz", + "@metricinsights/pp-dev": "file:../../packages/pp-dev/metricinsights-pp-dev-latest.tgz", "next": "14.0.3", "react": "^18", "react-dom": "^18", diff --git a/tests/test-nextjs/package.json b/tests/test-nextjs/package.json index 9f55ec7..c0f2654 100644 --- a/tests/test-nextjs/package.json +++ b/tests/test-nextjs/package.json @@ -7,11 +7,11 @@ "dev": "pp-dev next", "build": "next build", "start": "next start", - "reinstall": "npm i ../../metricinsights-pp-dev-latest.tgz", + "reinstall": "npm i ../../packages/pp-dev/metricinsights-pp-dev-latest.tgz", "lint": "next lint" }, "dependencies": { - "@metricinsights/pp-dev": "file:../../metricinsights-pp-dev-latest.tgz", + "@metricinsights/pp-dev": "file:../../packages/pp-dev/metricinsights-pp-dev-latest.tgz", "axios": "^1.7.8", "cac": "^6.7.14", "deepmerge-ts": "^7.1.3", diff --git a/tests/tsconfig.json b/tests/tsconfig.json deleted file mode 100644 index 6bd107e..0000000 --- a/tests/tsconfig.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "moduleResolution": "bundler", - "esModuleInterop": true, - "strict": true, - "skipLibCheck": true, - "resolveJsonModule": true, - "declaration": false, - "declarationMap": false, - "sourceMap": true, - "outDir": "./dist", - "rootDir": ".", - "baseUrl": ".", - "paths": { - "@/*": ["../src/*"] - }, - "types": ["node"] - }, - "include": [ - "**/*.ts", - "**/*.spec.ts" - ], - "exclude": [ - "node_modules", - "test-commonjs", - "test-nextjs", - "test-nextjs-cjs" - ] -} diff --git a/tests/unit/config/config.loader.spec.ts b/tests/unit/config/config.loader.spec.ts deleted file mode 100644 index bf849ce..0000000 --- a/tests/unit/config/config.loader.spec.ts +++ /dev/null @@ -1,238 +0,0 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { writeFileSync, mkdirSync, rmSync, existsSync } from 'fs'; -import { join } from 'path'; -import { tmpdir } from 'os'; - -describe('Config Loader', () => { - let testDir: string; - let originalCwd: string; - - beforeEach(() => { - // Save original cwd - originalCwd = process.cwd(); - - // Create a unique test directory - testDir = join(tmpdir(), `pp-dev-config-test-${Date.now()}-${Math.random().toString(36).slice(2)}`); - mkdirSync(testDir, { recursive: true }); - - // Change to test directory - process.chdir(testDir); - }); - - afterEach(async () => { - // Restore original cwd - process.chdir(originalCwd); - - // Clean up test directory - if (existsSync(testDir)) { - rmSync(testDir, { recursive: true, force: true }); - } - - // Clear module cache to ensure fresh imports - vi.resetModules(); - }); - - describe('getConfig', () => { - it('should return empty config when no config files exist', async () => { - // Create minimal package.json - writeFileSync(join(testDir, 'package.json'), JSON.stringify({ name: 'test' })); - - const { getConfig, clearConfigCache } = await import('../../../src/config.js'); - clearConfigCache(); - - const config = await getConfig(); - - expect(config).toEqual({}); - }); - - it('should load config from package.json pp-dev field', async () => { - writeFileSync( - join(testDir, 'package.json'), - JSON.stringify({ - name: 'test-project', - 'pp-dev': { - backendBaseURL: 'https://pkg.example.com', - portalPageId: 456, - }, - }) - ); - - const { getConfig, clearConfigCache } = await import('../../../src/config.js'); - clearConfigCache(); - - const config = await getConfig(); - - expect(config.backendBaseURL).toBe('https://pkg.example.com'); - expect(config.portalPageId).toBe(456); - }); - - it('should load config from .pp-watch.config.json (legacy format)', async () => { - writeFileSync(join(testDir, 'package.json'), JSON.stringify({ name: 'test' })); - writeFileSync( - join(testDir, '.pp-watch.config.json'), - JSON.stringify({ - baseURL: 'https://watch.example.com', - portalPageId: 789, - }) - ); - - const { getConfig, clearConfigCache } = await import('../../../src/config.js'); - clearConfigCache(); - - const config = await getConfig(); - - expect(config.backendBaseURL).toBe('https://watch.example.com'); - expect(config.portalPageId).toBe(789); - }); - - it('should load config from pp-dev.config.json', async () => { - writeFileSync(join(testDir, 'package.json'), JSON.stringify({ name: 'test' })); - writeFileSync( - join(testDir, 'pp-dev.config.json'), - JSON.stringify({ - backendBaseURL: 'https://json.example.com', - portalPageId: 111, - templateLess: true, - }) - ); - - const { getConfig, clearConfigCache } = await import('../../../src/config.js'); - clearConfigCache(); - - const config = await getConfig(); - - expect(config.backendBaseURL).toBe('https://json.example.com'); - expect(config.portalPageId).toBe(111); - expect(config.templateLess).toBe(true); - }); - - it('should prioritize pp-dev config over pp-watch config', async () => { - writeFileSync(join(testDir, 'package.json'), JSON.stringify({ name: 'test' })); - - // Create both configs - writeFileSync( - join(testDir, 'pp-dev.config.json'), - JSON.stringify({ - backendBaseURL: 'https://pp-dev.example.com', - portalPageId: 100, - }) - ); - writeFileSync( - join(testDir, '.pp-watch.config.json'), - JSON.stringify({ - baseURL: 'https://pp-watch.example.com', - portalPageId: 200, - }) - ); - - const { getConfig, clearConfigCache } = await import('../../../src/config.js'); - clearConfigCache(); - - const config = await getConfig(); - - // pp-dev config should take priority - expect(config.backendBaseURL).toBe('https://pp-dev.example.com'); - expect(config.portalPageId).toBe(100); - }); - - it('should prioritize config file over package.json pp-dev field', async () => { - writeFileSync( - join(testDir, 'package.json'), - JSON.stringify({ - name: 'test', - 'pp-dev': { - backendBaseURL: 'https://pkg.example.com', - portalPageId: 1, - }, - }) - ); - writeFileSync( - join(testDir, 'pp-dev.config.json'), - JSON.stringify({ - backendBaseURL: 'https://file.example.com', - portalPageId: 2, - }) - ); - - const { getConfig, clearConfigCache } = await import('../../../src/config.js'); - clearConfigCache(); - - const config = await getConfig(); - - // Config file should take priority - expect(config.backendBaseURL).toBe('https://file.example.com'); - expect(config.portalPageId).toBe(2); - }); - }); - - describe('getPkg', () => { - it('should return package.json contents', async () => { - writeFileSync( - join(testDir, 'package.json'), - JSON.stringify({ - name: 'test-package', - version: '1.0.0', - description: 'Test package', - }) - ); - - const { getPkg, clearConfigCache } = await import('../../../src/config.js'); - clearConfigCache(); - - const pkg = getPkg(); - - expect(pkg.name).toBe('test-package'); - expect(pkg.version).toBe('1.0.0'); - }); - - it('should return empty object when package.json does not exist', async () => { - // Don't create package.json - - const { getPkg, clearConfigCache } = await import('../../../src/config.js'); - clearConfigCache(); - - const pkg = getPkg(); - - expect(pkg).toEqual({}); - }); - }); - - describe('Config Caching', () => { - it('should cache package.json reads', async () => { - writeFileSync( - join(testDir, 'package.json'), - JSON.stringify({ name: 'test', version: '1.0.0' }) - ); - - const { getPkg, clearConfigCache, getConfigCacheStats } = await import('../../../src/config.js'); - clearConfigCache(); - - // First call - getPkg(); - const stats1 = getConfigCacheStats(); - - // Second call (should use cache) - getPkg(); - const stats2 = getConfigCacheStats(); - - expect(stats1.packageJsonCached).toBe(true); - expect(stats2.packageJsonCached).toBe(true); - }); - - it('should clear cache when clearConfigCache is called', async () => { - writeFileSync(join(testDir, 'package.json'), JSON.stringify({ name: 'test' })); - - const { getPkg, clearConfigCache, getConfigCacheStats } = await import('../../../src/config.js'); - - // Populate cache - getPkg(); - expect(getConfigCacheStats().packageJsonCached).toBe(true); - - // Clear cache - clearConfigCache(); - expect(getConfigCacheStats().packageJsonCached).toBe(false); - expect(getConfigCacheStats().configEntries).toBe(0); - expect(getConfigCacheStats().dirContentCached).toBe(false); - }); - }); -}); diff --git a/tests/unit/config/config.validation.spec.ts b/tests/unit/config/config.validation.spec.ts deleted file mode 100644 index b49bdaf..0000000 --- a/tests/unit/config/config.validation.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { describe, it, expect } from 'vitest'; - -// Import the constants to test config file name patterns -import { PP_DEV_CONFIG_NAMES, PP_WATCH_CONFIG_NAMES } from '../../../src/constants.js'; - -describe('Config Validation', () => { - describe('PP_DEV_CONFIG_NAMES', () => { - it('should include all supported config file names', () => { - expect(PP_DEV_CONFIG_NAMES).toContain('pp-dev.config.ts'); - expect(PP_DEV_CONFIG_NAMES).toContain('pp-dev.config.js'); - expect(PP_DEV_CONFIG_NAMES).toContain('pp-dev.config.json'); - expect(PP_DEV_CONFIG_NAMES).toContain('pp-dev.config.cjs'); - expect(PP_DEV_CONFIG_NAMES).toContain('pp-dev.config.mjs'); - expect(PP_DEV_CONFIG_NAMES).toContain('pp-dev.config.cts'); - expect(PP_DEV_CONFIG_NAMES).toContain('pp-dev.config.mts'); - }); - - it('should include dotfile config variants', () => { - expect(PP_DEV_CONFIG_NAMES).toContain('.pp-dev.config.ts'); - expect(PP_DEV_CONFIG_NAMES).toContain('.pp-dev.config.js'); - expect(PP_DEV_CONFIG_NAMES).toContain('.pp-dev.config.json'); - expect(PP_DEV_CONFIG_NAMES).toContain('.pp-dev.config.cjs'); - expect(PP_DEV_CONFIG_NAMES).toContain('.pp-dev.config.mjs'); - expect(PP_DEV_CONFIG_NAMES).toContain('.pp-dev.config.cts'); - expect(PP_DEV_CONFIG_NAMES).toContain('.pp-dev.config.mts'); - }); - - it('should have dotfile variants before regular variants (priority)', () => { - const dotfileIndex = PP_DEV_CONFIG_NAMES.indexOf('.pp-dev.config.js'); - const regularIndex = PP_DEV_CONFIG_NAMES.indexOf('pp-dev.config.js'); - - expect(dotfileIndex).toBeLessThan(regularIndex); - }); - }); - - describe('PP_WATCH_CONFIG_NAMES', () => { - it('should include legacy pp-watch config file names', () => { - expect(PP_WATCH_CONFIG_NAMES).toContain('.pp-watch.config.js'); - expect(PP_WATCH_CONFIG_NAMES).toContain('.pp-watch.config.ts'); - expect(PP_WATCH_CONFIG_NAMES).toContain('.pp-watch.config.json'); - }); - - it('should only have 3 legacy config names', () => { - expect(PP_WATCH_CONFIG_NAMES).toHaveLength(3); - }); - }); - - describe('Config File Extension Patterns', () => { - const supportedExtensions = ['.ts', '.js', '.json', '.cjs', '.mjs', '.cts', '.mts']; - - it('should support all modern JavaScript/TypeScript extensions', () => { - for (const ext of supportedExtensions) { - const hasExtension = PP_DEV_CONFIG_NAMES.some((name) => name.endsWith(ext)); - expect(hasExtension, `Should support ${ext} extension`).toBe(true); - } - }); - }); -}); diff --git a/tests/unit/helpers/formatting.spec.ts b/tests/unit/helpers/formatting.spec.ts deleted file mode 100644 index a22f84a..0000000 --- a/tests/unit/helpers/formatting.spec.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { templateFunction, html, css } from '../../../src/helpers/formatting.js'; - -describe('Formatting Helpers', () => { - describe('templateFunction', () => { - it('should return the string content from template literal', () => { - const result = templateFunction`Hello World`; - expect(result).toBe('Hello World'); - }); - - it('should handle template literals with values', () => { - const name = 'Test'; - const result = templateFunction`Hello ${name}!`; - expect(result).toBe('Hello Test!'); - }); - - it('should handle multiple values', () => { - const a = 1; - const b = 2; - const result = templateFunction`Sum of ${a} and ${b} is ${a + b}`; - expect(result).toBe('Sum of 1 and 2 is 3'); - }); - - it('should preserve whitespace', () => { - const result = templateFunction` spaces `; - expect(result).toBe(' spaces '); - }); - - it('should handle empty template', () => { - const result = templateFunction``; - expect(result).toBe(''); - }); - - it('should handle newlines', () => { - const result = templateFunction`line1 -line2`; - expect(result).toContain('line1'); - expect(result).toContain('line2'); - }); - }); - - describe('html', () => { - it('should be an alias for templateFunction', () => { - expect(html).toBe(templateFunction); - }); - - it('should work with HTML content', () => { - const className = 'container'; - const result = html`
Content
`; - expect(result).toBe('
Content
'); - }); - - it('should handle complex HTML structures', () => { - const items = ['a', 'b', 'c']; - const result = html` -
    - ${items.map((item) => `
  • ${item}
  • `).join('')} -
- `; - expect(result).toContain('
    '); - expect(result).toContain('
  • a
  • '); - expect(result).toContain('
  • b
  • '); - expect(result).toContain('
  • c
  • '); - expect(result).toContain('
'); - }); - }); - - describe('css', () => { - it('should be an alias for templateFunction', () => { - expect(css).toBe(templateFunction); - }); - - it('should work with CSS content', () => { - const color = 'red'; - const result = css`.container { color: ${color}; }`; - expect(result).toBe('.container { color: red; }'); - }); - - it('should handle complex CSS structures', () => { - const primaryColor = '#007bff'; - const fontSize = '16px'; - const result = css` - .button { - background-color: ${primaryColor}; - font-size: ${fontSize}; - padding: 8px 16px; - } - `; - expect(result).toContain(`background-color: ${primaryColor}`); - expect(result).toContain(`font-size: ${fontSize}`); - }); - }); -}); diff --git a/tests/unit/lib/color.helper.spec.ts b/tests/unit/lib/color.helper.spec.ts deleted file mode 100644 index 667a093..0000000 --- a/tests/unit/lib/color.helper.spec.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { colors } from '../../../src/lib/helpers/color.helper.js'; - -describe('Color Helper', () => { - describe('colors object', () => { - it('should have all expected color methods', () => { - expect(typeof colors.red).toBe('function'); - expect(typeof colors.green).toBe('function'); - expect(typeof colors.yellow).toBe('function'); - expect(typeof colors.blue).toBe('function'); - expect(typeof colors.white).toBe('function'); - }); - - it('should have formatting methods', () => { - expect(typeof colors.bold).toBe('function'); - expect(typeof colors.dim).toBe('function'); - expect(typeof colors.reset).toBe('function'); - }); - - it('should return string from color methods', () => { - expect(typeof colors.red('test')).toBe('string'); - expect(typeof colors.green('test')).toBe('string'); - expect(typeof colors.yellow('test')).toBe('string'); - expect(typeof colors.blue('test')).toBe('string'); - }); - - it('should return string from formatting methods', () => { - expect(typeof colors.bold('test')).toBe('string'); - expect(typeof colors.dim('test')).toBe('string'); - expect(typeof colors.reset('test')).toBe('string'); - }); - - it('should handle empty strings', () => { - expect(colors.red('')).toBeDefined(); - expect(colors.bold('')).toBeDefined(); - }); - - it('should handle strings with special characters', () => { - const special = 'Test\n\t"special"'; - expect(() => colors.red(special)).not.toThrow(); - expect(() => colors.bold(special)).not.toThrow(); - }); - - it('should allow chaining of methods', () => { - const result = colors.bold(colors.red('test')); - expect(typeof result).toBe('string'); - }); - }); -}); diff --git a/tests/unit/lib/url.helper.spec.ts b/tests/unit/lib/url.helper.spec.ts deleted file mode 100644 index e471b55..0000000 --- a/tests/unit/lib/url.helper.spec.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { urlReplacer, redirect } from '../../../src/lib/helpers/url.helper.js'; - -describe('URL Helper', () => { - describe('urlReplacer', () => { - it('should replace backend host with local host in HTML content', () => { - const backendHost = 'api.example.com'; - const localHost = 'localhost:3000'; - const html = ` - - - - - - - Link - - - `; - - const result = urlReplacer(backendHost, localHost, html); - - expect(result).toContain('localhost:3000/styles.css'); - expect(result).toContain('localhost:3000/script.js'); - expect(result).toContain('localhost:3000/page'); - expect(result).not.toContain('api.example.com'); - }); - - it('should handle HTTP URLs', () => { - const result = urlReplacer( - 'backend.com', - 'localhost:5000', - 'Link' - ); - - expect(result).toContain('localhost:5000/path'); - }); - - it('should handle HTTPS URLs', () => { - const result = urlReplacer( - 'backend.com', - 'localhost:5000', - 'Link' - ); - - expect(result).toContain('localhost:5000/path'); - }); - - it('should preserve URLs from different hosts', () => { - const result = urlReplacer( - 'api.example.com', - 'localhost:3000', - 'Link' - ); - - expect(result).toContain('https://other.com/path'); - }); - - it('should handle empty content', () => { - const result = urlReplacer('backend.com', 'localhost', ''); - expect(result).toBe(''); - }); - - it('should handle content without URLs', () => { - const content = '
Hello World
'; - const result = urlReplacer('backend.com', 'localhost', content); - expect(result).toBe(content); - }); - - it('should handle multiple occurrences of the same host', () => { - const html = ` - - - - `; - - const result = urlReplacer('api.com', 'localhost', html); - - expect(result).not.toContain('api.com'); - expect(result.match(/localhost/g)?.length).toBe(3); - }); - }); - - describe('redirect', () => { - it('should send redirect response with correct headers', () => { - const mockRes = { - statusCode: 0, - setHeader: vi.fn(), - end: vi.fn(), - }; - - redirect(mockRes as any, '/new-path', 302); - - expect(mockRes.statusCode).toBe(302); - // Note: implementation uses lowercase 'location' - expect(mockRes.setHeader).toHaveBeenCalledWith('location', '/new-path'); - expect(mockRes.end).toHaveBeenCalled(); - }); - - it('should use 302 as default status code', () => { - const mockRes = { - statusCode: 0, - setHeader: vi.fn(), - end: vi.fn(), - }; - - redirect(mockRes as any, '/path'); - - expect(mockRes.statusCode).toBe(302); - }); - - it('should support 301 permanent redirect', () => { - const mockRes = { - statusCode: 0, - setHeader: vi.fn(), - end: vi.fn(), - }; - - redirect(mockRes as any, '/permanent', 301); - - expect(mockRes.statusCode).toBe(301); - }); - - it('should handle absolute URLs', () => { - const mockRes = { - statusCode: 0, - setHeader: vi.fn(), - end: vi.fn(), - }; - - redirect(mockRes as any, 'https://example.com/path', 302); - - // Note: implementation uses lowercase 'location' - expect(mockRes.setHeader).toHaveBeenCalledWith('location', 'https://example.com/path'); - }); - }); -}); - -// Import vi for mocking -import { vi } from 'vitest'; diff --git a/tests/unit/plugin/plugin.normalize.spec.ts b/tests/unit/plugin/plugin.normalize.spec.ts deleted file mode 100644 index 1c791a1..0000000 --- a/tests/unit/plugin/plugin.normalize.spec.ts +++ /dev/null @@ -1,349 +0,0 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { normalizeVitePPDevConfig, type VitePPDevOptions } from '../../../src/plugin.js'; - -describe('normalizeVitePPDevConfig', () => { - const originalEnv = process.env; - - beforeEach(() => { - vi.resetModules(); - process.env = { ...originalEnv }; - }); - - afterEach(() => { - process.env = originalEnv; - }); - - describe('Required Fields', () => { - it('should throw error for missing templateName', () => { - expect(() => normalizeVitePPDevConfig({} as VitePPDevOptions)).toThrow( - 'templateName must be a non-empty string' - ); - }); - - it('should throw error for empty templateName', () => { - expect(() => normalizeVitePPDevConfig({ templateName: '' })).toThrow( - 'templateName must be a non-empty string' - ); - }); - - it('should accept valid templateName', () => { - const config = normalizeVitePPDevConfig({ templateName: 'my-template' }); - expect(config.templateName).toBe('my-template'); - }); - }); - - describe('Default Values', () => { - it('should set default enableProxyCache to true', () => { - const config = normalizeVitePPDevConfig({ templateName: 'test' }); - expect(config.enableProxyCache).toBe(true); - }); - - it('should set default proxyCacheTTL to 600000 (10 minutes)', () => { - const config = normalizeVitePPDevConfig({ templateName: 'test' }); - expect(config.proxyCacheTTL).toBe(600000); - }); - - it('should set default disableSSLValidation to false', () => { - const config = normalizeVitePPDevConfig({ templateName: 'test' }); - expect(config.disableSSLValidation).toBe(false); - }); - - it('should set default miHudLess to false', () => { - const config = normalizeVitePPDevConfig({ templateName: 'test' }); - expect(config.miHudLess).toBe(false); - }); - - it('should set default templateLess to false', () => { - const config = normalizeVitePPDevConfig({ templateName: 'test' }); - expect(config.templateLess).toBe(false); - }); - - it('should set default v7Features to false', () => { - const config = normalizeVitePPDevConfig({ templateName: 'test' }); - expect(config.v7Features).toBe(false); - }); - - it('should set default integrateMiTopBar to false', () => { - const config = normalizeVitePPDevConfig({ templateName: 'test' }); - expect(config.integrateMiTopBar).toBe(false); - }); - - it('should set default outDir to "dist"', () => { - const config = normalizeVitePPDevConfig({ templateName: 'test' }); - expect(config.outDir).toBe('dist'); - }); - - it('should set default syncBackupsDir to "backups"', () => { - const config = normalizeVitePPDevConfig({ templateName: 'test' }); - expect(config.syncBackupsDir).toBe('backups'); - }); - }); - - describe('distZip Normalization', () => { - // Note: The current implementation has a bug where ...config at the end - // overwrites the normalized distZip value. These tests document actual behavior. - it('should preserve distZip: true as passed (note: normalization is overwritten by spread)', () => { - const config = normalizeVitePPDevConfig({ - templateName: 'my-template', - distZip: true, - }); - - // Due to ...config spread at end, the original value is preserved - expect(config.distZip).toBe(true); - }); - - it('should normalize distZip: false to false', () => { - const config = normalizeVitePPDevConfig({ - templateName: 'my-template', - distZip: false, - }); - - expect(config.distZip).toBe(false); - }); - - it('should preserve object with outFileName as passed (spread overwrites normalization)', () => { - const config = normalizeVitePPDevConfig({ - templateName: 'my-app', - distZip: { - outFileName: '[templateName]-build.zip', - }, - }); - - // Due to ...config spread at end, original object is preserved - expect(config.distZip).toEqual({ - outFileName: '[templateName]-build.zip', - }); - }); - - it('should use custom outDir when provided', () => { - const config = normalizeVitePPDevConfig({ - templateName: 'my-template', - distZip: { - outDir: 'custom-output', - }, - }); - - expect((config.distZip as { outDir: string }).outDir).toBe('custom-output'); - }); - - it('should preserve distZip object as passed (spread overwrites normalization)', () => { - const config = normalizeVitePPDevConfig({ - templateName: 'test', - distZip: { - outFileName: 'custom-[templateName].zip', - outDir: 'release', - }, - }); - - // Due to ...config spread at end, original object is preserved - expect(config.distZip).toEqual({ - outFileName: 'custom-[templateName].zip', - outDir: 'release', - }); - }); - }); - - describe('imageOptimizer Normalization', () => { - // Note: The current implementation has a bug where ...config at the end - // overwrites the normalized imageOptimizer value. These tests document actual behavior. - it('should preserve imageOptimizer: true as passed (spread overwrites normalization)', () => { - const config = normalizeVitePPDevConfig({ - templateName: 'test', - imageOptimizer: true, - }); - - // Due to ...config spread at end, the original value is preserved - expect(config.imageOptimizer).toBe(true); - }); - - it('should normalize imageOptimizer: false to false', () => { - const config = normalizeVitePPDevConfig({ - templateName: 'test', - imageOptimizer: false, - }); - - expect(config.imageOptimizer).toBe(false); - }); - - it('should pass through imageOptimizer object options', () => { - const optimizerOptions = { - png: { quality: 80 }, - jpeg: { quality: 75 }, - }; - - const config = normalizeVitePPDevConfig({ - templateName: 'test', - imageOptimizer: optimizerOptions, - }); - - expect(config.imageOptimizer).toEqual(optimizerOptions); - }); - }); - - describe('portalPageId and appId', () => { - // Note: The spread at the end of normalizeVitePPDevConfig causes issues - // when both portalPageId and appId are provided, as portalPageId from - // config.portalPageId in the spread overwrites the computed value. - it('should keep portalPageId from original config when both provided (spread behavior)', () => { - const config = normalizeVitePPDevConfig({ - templateName: 'test', - portalPageId: 100, - appId: 200, - }); - - // Due to ...config spread at end, the original portalPageId is preserved - expect(config.portalPageId).toBe(100); - }); - - it('should use portalPageId when appId is not provided', () => { - const config = normalizeVitePPDevConfig({ - templateName: 'test', - portalPageId: 100, - }); - - expect(config.portalPageId).toBe(100); - }); - - it('should use appId when portalPageId is not provided', () => { - const config = normalizeVitePPDevConfig({ - templateName: 'test', - appId: 200, - }); - - expect(config.portalPageId).toBe(200); - }); - }); - - describe('personalAccessToken', () => { - it('should use provided personalAccessToken', () => { - const config = normalizeVitePPDevConfig({ - templateName: 'test', - personalAccessToken: 'my-token', - }); - - expect(config.personalAccessToken).toBe('my-token'); - }); - - it('should use MI_ACCESS_TOKEN from env when not provided', () => { - process.env.MI_ACCESS_TOKEN = 'env-token'; - - const config = normalizeVitePPDevConfig({ templateName: 'test' }); - - expect(config.personalAccessToken).toBe('env-token'); - }); - - it('should prefer explicit token over env variable', () => { - process.env.MI_ACCESS_TOKEN = 'env-token'; - - const config = normalizeVitePPDevConfig({ - templateName: 'test', - personalAccessToken: 'explicit-token', - }); - - expect(config.personalAccessToken).toBe('explicit-token'); - }); - }); - - describe('Validation Errors', () => { - it('should throw error for invalid backendBaseURL (empty string)', () => { - expect(() => - normalizeVitePPDevConfig({ - templateName: 'test', - backendBaseURL: '', - }) - ).toThrow('backendBaseURL must be a non-empty string if provided'); - }); - - it('should throw error for invalid portalPageId (negative)', () => { - expect(() => - normalizeVitePPDevConfig({ - templateName: 'test', - portalPageId: -1, - }) - ).toThrow('portalPageId must be a positive number if provided'); - }); - - it('should throw error for invalid appId (zero)', () => { - expect(() => - normalizeVitePPDevConfig({ - templateName: 'test', - appId: 0, - }) - ).toThrow('appId must be a positive number if provided'); - }); - - it('should throw error for invalid proxyCacheTTL (negative)', () => { - expect(() => - normalizeVitePPDevConfig({ - templateName: 'test', - proxyCacheTTL: -100, - }) - ).toThrow('proxyCacheTTL must be a positive number if provided'); - }); - - it('should throw error when integrateMiTopBar is true but miHudLess is false', () => { - expect(() => - normalizeVitePPDevConfig({ - templateName: 'test', - integrateMiTopBar: true, - miHudLess: false, - }) - ).toThrow('integrateMiTopBar can only be true when miHudLess is true'); - }); - - it('should allow integrateMiTopBar: true when miHudLess is true', () => { - const config = normalizeVitePPDevConfig({ - templateName: 'test', - integrateMiTopBar: true, - miHudLess: true, - }); - - expect(config.integrateMiTopBar).toBe(true); - expect(config.miHudLess).toBe(true); - }); - }); - - describe('Complete Configuration', () => { - it('should normalize a complete configuration correctly', () => { - const config = normalizeVitePPDevConfig({ - templateName: 'my-app', - backendBaseURL: 'https://api.example.com', - appId: 123, - templateLess: true, - miHudLess: true, - integrateMiTopBar: true, - enableProxyCache: false, - proxyCacheTTL: 300000, - disableSSLValidation: true, - v7Features: true, - outDir: 'build', - distZip: { - outFileName: '[templateName]-v1.zip', - outDir: 'releases', - }, - syncBackupsDir: 'archives', - }); - - // Note: Due to ...config spread at end, some normalized values are overwritten - expect(config).toMatchObject({ - templateName: 'my-app', - backendBaseURL: 'https://api.example.com', - portalPageId: 123, - templateLess: true, - miHudLess: true, - integrateMiTopBar: true, - enableProxyCache: false, - proxyCacheTTL: 300000, - disableSSLValidation: true, - v7Features: true, - outDir: 'build', - // distZip is not normalized due to spread behavior - distZip: { - outFileName: '[templateName]-v1.zip', - outDir: 'releases', - }, - syncBackupsDir: 'archives', - }); - }); - }); -}); diff --git a/vitest.config.ts b/vitest.config.ts deleted file mode 100644 index 31a2398..0000000 --- a/vitest.config.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { defineConfig } from 'vitest/config'; - -export default defineConfig({ - test: { - globals: true, - environment: 'node', - include: ['tests/unit/**/*.spec.ts'], - exclude: ['tests/integration/**', 'tests/test-*/**', 'e2e/**'], - coverage: { - provider: 'v8', - reporter: ['text', 'json', 'html'], - reportsDirectory: './coverage/unit', - include: ['src/**/*.ts'], - exclude: [ - 'src/client/**', - 'src/**/*.d.ts', - 'src/banner/**', - ], - }, - setupFiles: ['tests/setup.ts'], - testTimeout: 10000, - }, - resolve: { - alias: { - '@': '/src', - }, - }, -}); diff --git a/vitest.integration.config.ts b/vitest.integration.config.ts deleted file mode 100644 index 6da123d..0000000 --- a/vitest.integration.config.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { defineConfig } from 'vitest/config'; - -export default defineConfig({ - test: { - globals: true, - environment: 'node', - include: ['tests/integration/**/*.spec.ts'], - exclude: ['tests/unit/**', 'tests/test-*/**', 'e2e/**'], - coverage: { - provider: 'v8', - reporter: ['text', 'json', 'html'], - reportsDirectory: './coverage/integration', - include: ['src/**/*.ts'], - exclude: [ - 'src/client/**', - 'src/**/*.d.ts', - 'src/banner/**', - ], - }, - setupFiles: ['tests/setup.ts'], - testTimeout: 30000, - pool: 'forks', // Better isolation for integration tests - }, - resolve: { - alias: { - '@': '/src', - }, - }, -});