From 0e75594b46b2d43bd3a2a12078fb8de8ddf6fb96 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 11 Aug 2026 09:27:48 +0000 Subject: [PATCH 1/4] Add Playground e2e tests and a "try in Playground" button on PRs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There were no tests. The filter blocks are almost entirely server-side behaviour driven by the query string, which is exactly the kind of thing that is cheap to break and expensive to notice. Follow the hm-query-loop harness: Playwright driving WordPress Playground via @wp-playground/cli, rather than wp-env and Docker. Playground boots in process, so there is no environment to start or stop, and the PHP and WordPress versions are just parameters — CI runs a PHP 8.2/8.3 x WP 6.8/latest matrix off the same blueprint developers use locally. blueprint.json describes the environment once and is shared between the test run and `npm run playground:start`. Fixture content is seeded by tests/seed.php, and tests/mu-plugins/register-test-content.php registers the post types and taxonomies the tests filter against, including deliberately private ones so the suite can prove they stay invisible. 16 tests covering the taxonomy filter (select, checkbox, multi-term, URL round-tripping), the post type filter, the search block, and the query string validation added in the previous commit. Also add a Playground preview button to every PR. build/ is gitignored, so pr-playground-preview.yml builds the PR and pushes the result to a throwaway pr--built branch that Playground installs from; pr-cleanup deletes the branch when the PR closes. Unlike the hm-query-loop version this skips fork PRs, whose read-only token cannot push that branch, and keeps the PR title out of the shell — it is attacker controlled. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_015J9z6XLV1hT1FB1BSXPczo --- .github/workflows/playwright-tests.yml | 83 + .github/workflows/pr-cleanup.yml | 33 + .github/workflows/pr-playground-preview.yml | 136 ++ .gitignore | 6 + README.md | 21 + blueprint.json | 71 + global-setup.js | 93 + global-teardown.js | 14 + package-lock.json | 1779 +++++++++++++++++-- package.json | 10 +- playwright.config.js | 48 + tests/e2e/README.md | 89 + tests/e2e/fixtures.js | 88 + tests/e2e/post-type-filter.spec.js | 85 + tests/e2e/search-filter.spec.js | 35 + tests/e2e/taxonomy-filter.spec.js | 123 ++ tests/mu-plugins/register-test-content.php | 50 + tests/seed.php | 131 ++ 18 files changed, 2794 insertions(+), 101 deletions(-) create mode 100644 .github/workflows/playwright-tests.yml create mode 100644 .github/workflows/pr-cleanup.yml create mode 100644 .github/workflows/pr-playground-preview.yml create mode 100644 blueprint.json create mode 100644 global-setup.js create mode 100644 global-teardown.js create mode 100644 playwright.config.js create mode 100644 tests/e2e/README.md create mode 100644 tests/e2e/fixtures.js create mode 100644 tests/e2e/post-type-filter.spec.js create mode 100644 tests/e2e/search-filter.spec.js create mode 100644 tests/e2e/taxonomy-filter.spec.js create mode 100644 tests/mu-plugins/register-test-content.php create mode 100644 tests/seed.php diff --git a/.github/workflows/playwright-tests.yml b/.github/workflows/playwright-tests.yml new file mode 100644 index 0000000..4d0986e --- /dev/null +++ b/.github/workflows/playwright-tests.yml @@ -0,0 +1,83 @@ +name: Playwright Tests + +on: + push: + branches: + - main + pull_request: + types: + - opened + - reopened + - synchronize + - ready_for_review + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + name: Test PHP ${{ matrix.php }} / WP ${{ matrix.wordpress }} + timeout-minutes: 30 + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + + strategy: + fail-fast: false + matrix: + php: [ '8.2', '8.3' ] + wordpress: [ '6.8', 'latest' ] + + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Set up Node + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 22 + cache: 'npm' + + - name: Install dependencies + run: npm ci + + # Tests run against the built assets, not src/. + - name: Build plugin + run: npm run build + + - name: Install Playwright browsers + run: npx playwright install --with-deps chromium + + - name: Run Playwright tests + id: tests + continue-on-error: true + run: npm run test:e2e + env: + CI: true + WP_PLAYGROUND_PHP: ${{ matrix.php }} + WP_PLAYGROUND_WP: ${{ matrix.wordpress }} + WP_PLAYGROUND_PORT: 9400 + + - name: Upload test results + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + if: always() + with: + name: playwright-report-php${{ matrix.php }}-wp${{ matrix.wordpress }} + path: | + test-results/ + playwright-report/ + retention-days: 7 + if-no-files-found: ignore + + - name: Comment PR with test results + uses: daun/playwright-report-comment@31ed947ec7a623d18a66cfd20ef546dc66ef8063 # v4.1.0 + if: always() && github.event_name == 'pull_request' + with: + report-file: test-results/results.json + comment-title: 'Playwright — PHP ${{ matrix.php }} / WP ${{ matrix.wordpress }}' + + - name: Fail if tests failed + if: steps.tests.outcome == 'failure' + run: exit 1 diff --git a/.github/workflows/pr-cleanup.yml b/.github/workflows/pr-cleanup.yml new file mode 100644 index 0000000..325c5e7 --- /dev/null +++ b/.github/workflows/pr-cleanup.yml @@ -0,0 +1,33 @@ +name: PR Cleanup + +# Deletes the `pr--built` branch created by pr-playground-preview.yml once +# the pull request is closed or merged. + +on: + pull_request: + types: [ closed ] + +permissions: + contents: write + +jobs: + cleanup: + name: Delete the preview branch + runs-on: ubuntu-latest + + steps: + - name: Delete PR preview branch + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + REPO: ${{ github.repository }} + run: | + BRANCH_NAME="pr-${PR_NUMBER}-built" + REMOTE="https://x-access-token:${GH_TOKEN}@github.com/${REPO}.git" + + if git ls-remote --heads "$REMOTE" "$BRANCH_NAME" | grep -q "$BRANCH_NAME"; then + echo "Deleting $BRANCH_NAME" + git push "$REMOTE" --delete "$BRANCH_NAME" + else + echo "Branch $BRANCH_NAME does not exist. Nothing to clean up." + fi diff --git a/.github/workflows/pr-playground-preview.yml b/.github/workflows/pr-playground-preview.yml new file mode 100644 index 0000000..087c4aa --- /dev/null +++ b/.github/workflows/pr-playground-preview.yml @@ -0,0 +1,136 @@ +name: PR Playground Preview + +# Builds the PR's assets, pushes them to a throwaway `pr--built` branch, and +# adds a "Preview in WordPress Playground" button to the PR description that +# boots a WordPress instance with that build installed and demo content seeded. +# +# `build/` is gitignored, so the preview needs a branch that carries the +# compiled assets — Playground installs the plugin from that branch's zip. +# `pr-cleanup.yml` deletes the branch when the PR closes. + +on: + pull_request: + types: [ opened, synchronize, reopened, edited ] + +permissions: + contents: write + pull-requests: write + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + build-and-preview: + name: Build and publish preview + runs-on: ubuntu-latest + # Fork PRs get a read-only token, so the branch push would fail. Skip + # rather than failing the check. + if: github.event.pull_request.head.repo.full_name == github.repository + + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Set up Node + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 22 + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Label the build with the PR number + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + # PR_NUMBER is numeric, so this is safe to interpolate. Never + # interpolate the PR title — it is attacker controlled. + sed -i "s|^ \* Version:.*| * Version: 0.0.0-pr${PR_NUMBER}|" query-filter.php + grep -n "Version:" query-filter.php + + - name: Build plugin + run: npm run build + + - name: Push built files to a preview branch + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + + BRANCH_NAME="pr-${PR_NUMBER}-built" + git checkout -B "$BRANCH_NAME" + + # build/ is gitignored in source; force-add it so the branch zip is + # a self-contained, installable plugin. + git add -f build + git add -A + + if git diff --staged --quiet; then + echo "No changes to commit" + else + git commit -m "Build assets for PR #${PR_NUMBER} (commit: ${HEAD_SHA})" + git push origin "$BRANCH_NAME" --force + fi + + - name: Add the Playground preview button to the PR + uses: WordPress/action-wp-playground-pr-preview@43fc435558bc6cee69f5b214d6b8ca4f9f80c31d # v3 + with: + mode: 'append-to-description' + github-token: ${{ secrets.GITHUB_TOKEN }} + blueprint: | + { + "landingPage": "/query-filter-demo/", + "preferredVersions": { + "php": "8.2", + "wp": "6.8" + }, + "features": { "networking": true }, + "extraLibraries": [ "wp-cli" ], + "steps": [ + { + "step": "login", + "username": "admin", + "password": "password" + }, + { + "step": "installTheme", + "themeData": { + "resource": "wordpress.org/themes", + "slug": "twentytwentyfive" + }, + "options": { "activate": true } + }, + { + "step": "installPlugin", + "pluginData": { + "resource": "wordpress.org/plugins", + "slug": "advanced-query-loop" + }, + "options": { "activate": true } + }, + { + "step": "installPlugin", + "pluginData": { + "resource": "url", + "url": "https://github.com/${{ github.repository }}/archive/refs/heads/pr-${{ github.event.pull_request.number }}-built.zip" + }, + "options": { "activate": true } + }, + { + "step": "runPHP", + "code": " 'alpha', 'Beta' => 'beta', 'Gamma' => 'gamma' ] as $name => $slug ) { wp_insert_term( $name, 'category', [ 'slug' => $slug ] ); } $i = 0; foreach ( [ 'alpha', 'alpha', 'beta', 'beta', 'gamma', '' ] as $slug ) { $i++; $id = wp_insert_post( [ 'post_title' => 'Demo Post ' . $i, 'post_name' => 'demo-post-' . $i, 'post_status' => 'publish', 'post_type' => 'post', 'post_author' => 1, 'post_content' => 'Demo content for post ' . $i . '.' ] ); if ( $slug ) { wp_set_object_terms( $id, [ $slug ], 'category' ); } } wp_insert_post( [ 'post_title' => 'Query Filter Demo', 'post_name' => 'query-filter-demo', 'post_status' => 'publish', 'post_type' => 'page', 'post_author' => 1, 'post_content' => '
' ] ); echo 'Seeded demo content.';" + }, + { + "step": "wp-cli", + "command": "wp rewrite structure '/%postname%/'" + }, + { + "step": "wp-cli", + "command": "wp rewrite flush" + } + ] + } diff --git a/.gitignore b/.gitignore index 9136884..cb9d9be 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,9 @@ node_modules /build/ /vendor/ + +# Playwright / Playground test artefacts +/.auth/ +/dist/ +/playwright-report/ +/test-results/ diff --git a/README.md b/README.md index 305b906..179c57d 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,27 @@ Command | Purpose This command deliberately filters out GET/OPTIONS/HEAD/POST/PUT access log entries +## Testing + +End-to-end tests run against [WordPress Playground](https://wordpress.org/playground/), which boots in process — there is no environment to start or stop: + +```bash +npm ci +npm run build # tests run against build/, not src/ +npm run test:e2e +``` + +Command | Purpose +---- | ---- +`npm run test:e2e` | Run the Playwright suite +`npm run test:e2e:debug` | Step through tests in the Playwright inspector +`npm run test:e2e:watch` | Open the Playwright UI, rerunning on change +`npm run playground:start` | Boot the same environment for manual testing, without running tests + +The environment is described by [blueprint.json](./blueprint.json) and is shared between the test run and manual use. See [tests/e2e/README.md](./tests/e2e/README.md) for the fixture content and how to write tests. + +Every pull request also gets a **Preview in WordPress Playground** button added to its description, which boots that PR's build with demo content already in place — no local checkout needed to try a change. + ## Release Process Merges to `main` automatically [build](https://github.com/humanmade/query-filter/actions/workflows/build-release-branch.yml) to the `release` branch. A project may track the `release` branch using [Composer](https://getcomposer.org/) to pull in the latest built beta version. diff --git a/blueprint.json b/blueprint.json new file mode 100644 index 0000000..5d74720 --- /dev/null +++ b/blueprint.json @@ -0,0 +1,71 @@ +{ + "$schema": "https://playground.wordpress.net/blueprint-schema.json", + "landingPage": "/taxonomy-filter/", + "preferredVersions": { + "php": "8.2", + "wp": "6.8" + }, + "features": { + "networking": true + }, + "extraLibraries": [ "wp-cli" ], + "steps": [ + { + "step": "login" + }, + { + "step": "installTheme", + "themeData": { + "resource": "wordpress.org/themes", + "slug": "twentytwentyfive" + }, + "options": { + "activate": true + } + }, + { + "step": "installPlugin", + "pluginData": { + "resource": "wordpress.org/plugins", + "slug": "advanced-query-loop" + }, + "options": { + "activate": true + } + }, + { + "step": "defineWpConfigConsts", + "consts": { + "WP_DEBUG": true, + "WP_DEBUG_LOG": true, + "WP_DEBUG_DISPLAY": false, + "SCRIPT_DEBUG": true + } + }, + { + "step": "wp-cli", + "command": "wp plugin activate query-filter" + }, + { + "step": "mkdir", + "path": "/wordpress/wp-content/mu-plugins" + }, + { + "step": "cp", + "fromPath": "/wordpress/wp-content/plugins/query-filter/tests/mu-plugins/register-test-content.php", + "toPath": "/wordpress/wp-content/mu-plugins/register-test-content.php" + }, + { + "step": "runPHP", + "code": " { + // WP_BASE_URL means someone else is running Playground (e.g. via + // `npm run playground:start`). Use theirs rather than booting a second. + if ( process.env.WP_BASE_URL ) { + return; + } + + const blueprintPath = process.env.WP_BLUEPRINT_PATH + ? path.resolve( process.env.WP_BLUEPRINT_PATH ) + : path.resolve( process.cwd(), 'blueprint.json' ); + const blueprint = JSON.parse( fs.readFileSync( blueprintPath, 'utf8' ) ); + const port = resolvePort(); + + console.log( `Starting WordPress Playground on port ${ port }...` ); + + const cli = await runCLI( { + command: 'server', + port, + php: process.env.WP_PLAYGROUND_PHP || undefined, + wp: process.env.WP_PLAYGROUND_WP || undefined, + mount: [ + { + hostPath: process.cwd(), + vfsPath: '/wordpress/wp-content/plugins/query-filter', + }, + ], + blueprint, + } ); + + // runCLI returns { playground, server } — derive the URL from the socket + // rather than the requested port, which may have been taken. + const addr = cli.server.address(); + const serverUrl = `http://127.0.0.1:${ addr.port }`; + + process.env.WP_BASE_URL = serverUrl; + globalThis.__wpPlayground = cli; + + console.log( `Playground running at ${ serverUrl }` ); + + // Log in once and save the authentication state for all tests. The filter + // blocks are front end, but the storage state keeps admin-side checks and + // any future editor tests cheap. + const browser = await chromium.launch(); + const page = await browser.newPage( { + baseURL: serverUrl, + extraHTTPHeaders: { 'Accept-Encoding': 'identity' }, + } ); + + await page.goto( `${ serverUrl }/wp-login.php` ); + await page.fill( '#user_login', 'admin' ); + await page.fill( '#user_pass', 'password' ); + await page.click( '#wp-submit' ); + + try { + await Promise.race( [ + page.waitForURL( '**/wp-admin/', { timeout: 30000 } ), + page.waitForSelector( '#wpadminbar', { timeout: 30000 } ), + ] ); + } catch ( error ) { + console.error( 'Failed to log in to WordPress Playground' ); + await browser.close(); + throw error; + } + + fs.mkdirSync( '.auth', { recursive: true } ); + await page.context().storageState( { path: '.auth/wordpress.json' } ); + await browser.close(); + + console.log( 'WordPress authentication state saved.' ); +}; diff --git a/global-teardown.js b/global-teardown.js new file mode 100644 index 0000000..7289917 --- /dev/null +++ b/global-teardown.js @@ -0,0 +1,14 @@ +/* global globalThis */ +module.exports = async () => { + const cli = globalThis.__wpPlayground; + + if ( ! cli ) { + return; + } + + if ( typeof cli[ Symbol.asyncDispose ] === 'function' ) { + await cli[ Symbol.asyncDispose ](); + } else { + await cli.server?.close(); + } +}; diff --git a/package-lock.json b/package-lock.json index 6e6203b..1d30327 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,11 @@ "license": "GPL-2.0-or-later", "dependencies": { "@wordpress/scripts": "^28.6.0" + }, + "devDependencies": { + "@playwright/test": "^1.58.0", + "@types/node": "^24.10.1", + "@wp-playground/cli": "^1.0.0" } }, "node_modules/@ampproject/remapping": { @@ -2490,6 +2495,789 @@ "node": ">= 8" } }, + "node_modules/@octokit/app": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/app/-/app-14.1.0.tgz", + "integrity": "sha512-g3uEsGOQCBl1+W1rgfwoRFUIR6PtvB2T1E4RpygeUU5LrLvlOqcxrt5lfykIeRpUPpupreGJUYl70fqMDXdTpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-app": "^6.0.0", + "@octokit/auth-unauthenticated": "^5.0.0", + "@octokit/core": "^5.0.0", + "@octokit/oauth-app": "^6.0.0", + "@octokit/plugin-paginate-rest": "^9.0.0", + "@octokit/types": "^12.0.0", + "@octokit/webhooks": "^12.0.4" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/auth-app": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-6.1.4.tgz", + "integrity": "sha512-QkXkSOHZK4dA5oUqY5Dk3S+5pN2s1igPjEASNQV8/vgJgW034fQWR16u7VsNOK/EljA00eyjYF5mWNxWKWhHRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-oauth-app": "^7.1.0", + "@octokit/auth-oauth-user": "^4.1.0", + "@octokit/request": "^8.3.1", + "@octokit/request-error": "^5.1.0", + "@octokit/types": "^13.1.0", + "deprecation": "^2.3.1", + "lru-cache": "npm:@wolfy1339/lru-cache@^11.0.2-patch.1", + "universal-github-app-jwt": "^1.1.2", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/auth-app/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/auth-app/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/auth-app/node_modules/lru-cache": { + "name": "@wolfy1339/lru-cache", + "version": "11.0.2-patch.1", + "resolved": "https://registry.npmjs.org/@wolfy1339/lru-cache/-/lru-cache-11.0.2-patch.1.tgz", + "integrity": "sha512-BgYZfL2ADCXKOw2wJtkM3slhHotawWkgIRRxq4wEybnZQPjvAp71SPX35xepMykTw8gXlzWcWPTY31hlbnRsDA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "18 >=18.20 || 20 || >=22" + } + }, + "node_modules/@octokit/auth-oauth-app": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-7.1.0.tgz", + "integrity": "sha512-w+SyJN/b0l/HEb4EOPRudo7uUOSW51jcK1jwLa+4r7PA8FPFpoxEnHBHMITqCsc/3Vo2qqFjgQfz/xUUvsSQnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-oauth-device": "^6.1.0", + "@octokit/auth-oauth-user": "^4.1.0", + "@octokit/request": "^8.3.1", + "@octokit/types": "^13.0.0", + "@types/btoa-lite": "^1.0.0", + "btoa-lite": "^1.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/auth-oauth-app/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/auth-oauth-app/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/auth-oauth-device": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-6.1.0.tgz", + "integrity": "sha512-FNQ7cb8kASufd6Ej4gnJ3f1QB5vJitkoV1O0/g6e6lUsQ7+VsSNRHRmFScN2tV4IgKA12frrr/cegUs0t+0/Lw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/oauth-methods": "^4.1.0", + "@octokit/request": "^8.3.1", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/auth-oauth-device/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/auth-oauth-device/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/auth-oauth-user": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-4.1.0.tgz", + "integrity": "sha512-FrEp8mtFuS/BrJyjpur+4GARteUCrPeR/tZJzD8YourzoVhRics7u7we/aDcKv+yywRNwNi/P4fRi631rG/OyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-oauth-device": "^6.1.0", + "@octokit/oauth-methods": "^4.1.0", + "@octokit/request": "^8.3.1", + "@octokit/types": "^13.0.0", + "btoa-lite": "^1.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/auth-oauth-user/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/auth-oauth-user/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/auth-token": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", + "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/auth-unauthenticated": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-unauthenticated/-/auth-unauthenticated-5.0.1.tgz", + "integrity": "sha512-oxeWzmBFxWd+XolxKTc4zr+h3mt+yofn4r7OfoIkR/Cj/o70eEGmPsFbueyJE2iBAGpjgTnEOKM3pnuEGVmiqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.2.tgz", + "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^4.0.0", + "@octokit/graphql": "^7.1.0", + "@octokit/request": "^8.4.1", + "@octokit/request-error": "^5.1.1", + "@octokit/types": "^13.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/core/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/endpoint": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz", + "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/endpoint/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/graphql": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz", + "integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request": "^8.4.1", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/graphql/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/oauth-app": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@octokit/oauth-app/-/oauth-app-6.1.0.tgz", + "integrity": "sha512-nIn/8eUJ/BKUVzxUXd5vpzl1rwaVxMyYbQkNZjHrF7Vk/yu98/YDF/N2KeWO7uZ0g3b5EyiFXFkZI8rJ+DH1/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-oauth-app": "^7.0.0", + "@octokit/auth-oauth-user": "^4.0.0", + "@octokit/auth-unauthenticated": "^5.0.0", + "@octokit/core": "^5.0.0", + "@octokit/oauth-authorization-url": "^6.0.2", + "@octokit/oauth-methods": "^4.0.0", + "@types/aws-lambda": "^8.10.83", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/oauth-authorization-url": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-6.0.2.tgz", + "integrity": "sha512-CdoJukjXXxqLNK4y/VOiVzQVjibqoj/xHgInekviUJV73y/BSIcwvJ/4aNHPBPKcPWFnd4/lO9uqRV65jXhcLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/oauth-methods": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-4.1.0.tgz", + "integrity": "sha512-4tuKnCRecJ6CG6gr0XcEXdZtkTDbfbnD5oaHBmLERTjTMZNi2CbfEHZxPU41xXLDG4DfKf+sonu00zvKI9NSbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/oauth-authorization-url": "^6.0.2", + "@octokit/request": "^8.3.1", + "@octokit/request-error": "^5.1.0", + "@octokit/types": "^13.0.0", + "btoa-lite": "^1.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/oauth-methods/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/oauth-methods/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-graphql": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-4.0.1.tgz", + "integrity": "sha512-R8ZQNmrIKKpHWC6V2gum4x9LG2qF1RxRjo27gjQcG3j+vf2tLsEfE7I/wRWEPzYMaenr1M+qDAtNcwZve1ce1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=5" + } + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.2.tgz", + "integrity": "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^12.6.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.4.1.tgz", + "integrity": "sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^12.6.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-retry": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-6.1.0.tgz", + "integrity": "sha512-WrO3bvq4E1Xh1r2mT9w6SDFg01gFmP81nIG77+p/MqW1JeXXgL++6umim3t6x0Zj5pZm3rXAN+0HEjmmdhIRig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^13.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-retry/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-retry/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/plugin-throttling": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-8.2.0.tgz", + "integrity": "sha512-nOpWtLayKFpgqmgD0y3GqXafMFuKcA4tRPZIfu7BArd2lEZeb1988nhWhwx4aZWmjDmUfdgVf7W+Tt4AmvRmMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^12.2.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "^5.0.0" + } + }, + "node_modules/@octokit/request": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz", + "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^9.0.6", + "@octokit/request-error": "^5.1.1", + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz", + "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/request-error/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/request/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/request/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/types": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^20.0.0" + } + }, + "node_modules/@octokit/webhooks": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-12.3.2.tgz", + "integrity": "sha512-exj1MzVXoP7xnAcAB3jZ97pTvVPkQF9y6GA/dvYC47HV7vLv+24XRS6b/v/XnyikpEuvMhugEXdGtAlU086WkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request-error": "^5.0.0", + "@octokit/webhooks-methods": "^4.1.0", + "@octokit/webhooks-types": "7.6.1", + "aggregate-error": "^3.1.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/webhooks-methods": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-4.1.0.tgz", + "integrity": "sha512-zoQyKw8h9STNPqtm28UGOYFE7O6D4Il8VJwhAtMHFt2C4L0VQT1qGKLeefUOqHNs1mNRYSadVv7x0z8U2yyeWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/webhooks-types": { + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-7.6.1.tgz", + "integrity": "sha512-S8u2cJzklBC0FgTwWVLaM8tMrDuDMVE4xiTK4EYXM9GntyvrdbSoxqDQa+Fh57CCNApyIpyeqPhhFEmHPfrXgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@php-wasm/fs-journal": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@php-wasm/fs-journal/-/fs-journal-1.2.3.tgz", + "integrity": "sha512-ObBx2C6oD6WgveULVyhwhL66hPTXbi54GEPw6qSswinRBZHkOMTs1JorHLT3gUmFNk3olwmar5WM3WA7Lqx6PA==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/logger": "1.2.3", + "@php-wasm/node": "1.2.3", + "@php-wasm/universal": "1.2.3", + "@php-wasm/util": "1.2.3", + "express": "4.21.2", + "fs-ext": "2.1.1", + "ini": "4.1.2", + "wasm-feature-detect": "1.8.0", + "ws": "8.18.1", + "yargs": "17.7.2" + }, + "engines": { + "node": ">=20.18.3", + "npm": ">=10.1.0" + } + }, + "node_modules/@php-wasm/fs-journal/node_modules/ini": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.2.tgz", + "integrity": "sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@php-wasm/logger": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@php-wasm/logger/-/logger-1.2.3.tgz", + "integrity": "sha512-ulJofBSE7G0Qx35UXCRrPl7tabkwtfUXeELzEYy5YRwH/ZVupl8qwj6fVZnxnK2fHt+f/ypksPTCZpp6vJtPfA==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/node-polyfills": "1.2.3" + }, + "engines": { + "node": ">=20.18.3", + "npm": ">=10.1.0" + } + }, + "node_modules/@php-wasm/node": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@php-wasm/node/-/node-1.2.3.tgz", + "integrity": "sha512-9jw3jvhZ+DpMtwpjx+GmyOy/12wdtoaZ4L3e9CHZ/9g0RihhrbJqHxz2RSixq/ui/3HO3eArmqAvJh+iB705eA==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/logger": "1.2.3", + "@php-wasm/node-polyfills": "1.2.3", + "@php-wasm/universal": "1.2.3", + "@php-wasm/util": "1.2.3", + "@wp-playground/common": "1.2.3", + "express": "4.21.2", + "fs-ext": "2.1.1", + "ini": "4.1.2", + "wasm-feature-detect": "1.8.0", + "ws": "8.18.1", + "yargs": "17.7.2" + }, + "engines": { + "node": ">=20.18.3", + "npm": ">=10.1.0" + } + }, + "node_modules/@php-wasm/node-polyfills": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@php-wasm/node-polyfills/-/node-polyfills-1.2.3.tgz", + "integrity": "sha512-1WXiiNs/tM82CFYArZ+zP9+CHLegpR6AvmP/5TjjyMaMONG+hspWdjtLj4+a/TuUFcNBxLigXG5EB6x4lIvPFw==", + "dev": true, + "license": "GPL-2.0-or-later" + }, + "node_modules/@php-wasm/node/node_modules/ini": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.2.tgz", + "integrity": "sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@php-wasm/progress": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@php-wasm/progress/-/progress-1.2.3.tgz", + "integrity": "sha512-C38pne3d55i3rxGJHcm6GBcE8Xd2UDT78XQ543Twe8jaWD79jvvX94BAIKHWg+5EcGjs8lqOseIWEhVm2VPd+A==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/logger": "1.2.3", + "@php-wasm/node-polyfills": "1.2.3" + }, + "engines": { + "node": ">=20.18.3", + "npm": ">=10.1.0" + } + }, + "node_modules/@php-wasm/scopes": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@php-wasm/scopes/-/scopes-1.2.3.tgz", + "integrity": "sha512-1MJWCEiwpbtStn43l8634QRo3luEiS+2OhXJeToTGftP1k19MoCDPuWljlCus8JXomUmpo1sjQ8unCt04lVLsQ==", + "dev": true, + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=20.18.3", + "npm": ">=10.1.0" + } + }, + "node_modules/@php-wasm/stream-compression": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@php-wasm/stream-compression/-/stream-compression-1.2.3.tgz", + "integrity": "sha512-f9J8qNm24iwsxoRKdcPneqltDdu0Rs14BC88kSmZbtiiDr9LV5TYdpeV0YMxNC/Kg1tB4n3LWlR8+nVxeWrZlw==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/node-polyfills": "1.2.3", + "@php-wasm/util": "1.2.3" + } + }, + "node_modules/@php-wasm/universal": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@php-wasm/universal/-/universal-1.2.3.tgz", + "integrity": "sha512-RKjqVPM7jpgJ2BUC7N8XeqrQaghvxaQYKiFMVwBX2ubvjD020+Yhok6NipjRW+98Y4PTEt6r7ArroGqMVa2RlQ==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/logger": "1.2.3", + "@php-wasm/node-polyfills": "1.2.3", + "@php-wasm/progress": "1.2.3", + "@php-wasm/stream-compression": "1.2.3", + "@php-wasm/util": "1.2.3", + "ini": "4.1.2" + }, + "engines": { + "node": ">=20.18.3", + "npm": ">=10.1.0" + } + }, + "node_modules/@php-wasm/universal/node_modules/ini": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.2.tgz", + "integrity": "sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@php-wasm/util": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@php-wasm/util/-/util-1.2.3.tgz", + "integrity": "sha512-0LwozUhdbC5AuWD9TslZN7b4ci+CqvStMJeBu7MQy9iyq8r5qdXJOc+fy3a/QtMF/Hf+IMgZm9R2l0CY3Pn/Yg==", + "dev": true, + "engines": { + "node": ">=20.18.3", + "npm": ">=10.1.0" + } + }, + "node_modules/@php-wasm/web": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@php-wasm/web/-/web-1.2.3.tgz", + "integrity": "sha512-MQSALqjqsmlQwvJefRKKw9SidwE5ffaG4UA2Gjkpl6xo757m85EYjFUdJlNtu25f+K38883bsqdyRYxNgq2uDA==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/fs-journal": "1.2.3", + "@php-wasm/logger": "1.2.3", + "@php-wasm/universal": "1.2.3", + "@php-wasm/util": "1.2.3", + "@php-wasm/web-service-worker": "1.2.3", + "express": "4.21.2", + "fs-ext": "2.1.1", + "ini": "4.1.2", + "wasm-feature-detect": "1.8.0", + "ws": "8.18.1", + "yargs": "17.7.2" + }, + "engines": { + "node": ">=20.18.3", + "npm": ">=10.1.0" + } + }, + "node_modules/@php-wasm/web-service-worker": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@php-wasm/web-service-worker/-/web-service-worker-1.2.3.tgz", + "integrity": "sha512-eekKpGQ3IAwDAlzC+Kh2g3uhWxlCmQzqMd3inX90PRnH7//sUioYEOrDu1gaYNomScmdn62zYn7U41J230bZaA==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/scopes": "1.2.3" + }, + "engines": { + "node": ">=20.18.3", + "npm": ">=10.1.0" + } + }, + "node_modules/@php-wasm/web/node_modules/ini": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.2.tgz", + "integrity": "sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/@pkgr/core": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", @@ -2502,18 +3290,18 @@ } }, "node_modules/@playwright/test": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.0.tgz", - "integrity": "sha512-SgAdlSwYVpToI4e/IH19IHHWvoijAYH5hu2MWSXptRypLSnzj51PcGD+rsOXFayde4P9ZLi+loXVwArg6IUkCA==", - "peer": true, + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.62.1.tgz", + "integrity": "sha512-DTcUc8qii+cpHvtOwggMtBRMjKZHXYWdw8syRYu2vtzuq4Wxphqq4NfCs5Zt44L6mA8rfDfj+PHnxFc/FeK6mQ==", + "license": "Apache-2.0", "dependencies": { - "playwright": "1.47.0" + "playwright": "1.62.1" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=18" + "node": ">=20" } }, "node_modules/@pmmmwh/react-refresh-webpack-plugin": { @@ -3066,6 +3854,13 @@ "node": ">=10.13.0" } }, + "node_modules/@types/aws-lambda": { + "version": "8.10.162", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.162.tgz", + "integrity": "sha512-Fn658grtLOci1oxi1391vvDWJRKNGWRSqfxRkmN/Iy3c0tQH1USMKEXcPYHLvope+ZgTFocx9FRQJx1muBL6qw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -3120,6 +3915,13 @@ "@types/node": "*" } }, + "node_modules/@types/btoa-lite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/btoa-lite/-/btoa-lite-1.0.2.tgz", + "integrity": "sha512-ZYbcE2x7yrvNFJiU7xJGrpF/ihpkM7zKgw8bha3LNJSesvTtUNxbpzaT7WXBIryf6jovisrxTBvymxMeLLj1Mg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -3235,6 +4037,17 @@ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -3250,12 +4063,20 @@ "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==" }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { - "version": "22.5.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", - "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", + "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~7.18.0" } }, "node_modules/@types/node-forge": { @@ -4130,38 +4951,345 @@ "wp-scripts": "bin/wp-scripts.js" }, "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" - }, - "peerDependencies": { - "@playwright/test": "^1.46.0", - "react": "^18.0.0", - "react-dom": "^18.0.0" + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "@playwright/test": "^1.46.0", + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@wordpress/stylelint-config": { + "version": "22.7.0", + "resolved": "https://registry.npmjs.org/@wordpress/stylelint-config/-/stylelint-config-22.7.0.tgz", + "integrity": "sha512-nzFUC1urqtLklCke38VTH7yYlF3ihHu+ULRYwzhJNqWzttwz4srj60M3etEa2pdd2U6yywotQAJzcDbHw01qvQ==", + "dependencies": { + "stylelint-config-recommended": "^6.0.0", + "stylelint-config-recommended-scss": "^5.0.2" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "stylelint": "^14.2" + } + }, + "node_modules/@wordpress/warning": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@wordpress/warning/-/warning-3.7.0.tgz", + "integrity": "sha512-wGbQfPlf8YV6gGhcGPYWUhHORct4xaBQSaDTJrwzlgHYyrrJUVXXgZxaM4+Aa23zQoA13nvFQHvfssOkwdh65g==", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wp-playground/blueprints": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@wp-playground/blueprints/-/blueprints-1.2.3.tgz", + "integrity": "sha512-4K7oUonNtk2xDKQIgA99P5PrYEHmGBLmc4XCE9yh9dqCMVW71pZLSuT16/ifN2fV1ossRdNs5xa0e6BP66vwnQ==", + "dev": true, + "dependencies": { + "@php-wasm/logger": "1.2.3", + "@php-wasm/node": "1.2.3", + "@php-wasm/node-polyfills": "1.2.3", + "@php-wasm/progress": "1.2.3", + "@php-wasm/stream-compression": "1.2.3", + "@php-wasm/universal": "1.2.3", + "@php-wasm/util": "1.2.3", + "@php-wasm/web": "1.2.3", + "@wp-playground/common": "1.2.3", + "@wp-playground/storage": "1.2.3", + "@wp-playground/wordpress": "1.2.3", + "@zip.js/zip.js": "2.7.57", + "ajv": "8.12.0", + "async-lock": "1.4.1", + "clean-git-ref": "2.0.1", + "crc-32": "1.2.2", + "diff3": "0.0.4", + "express": "4.21.2", + "fs-ext": "2.1.1", + "ignore": "5.3.2", + "ini": "4.1.2", + "minimisted": "2.0.1", + "octokit": "3.1.2", + "pako": "1.0.10", + "pify": "2.3.0", + "readable-stream": "3.6.2", + "sha.js": "2.4.11", + "simple-get": "4.0.1", + "wasm-feature-detect": "1.8.0", + "ws": "8.18.1", + "yargs": "17.7.2" + }, + "engines": { + "node": ">=20.18.3", + "npm": ">=10.1.0" + } + }, + "node_modules/@wp-playground/blueprints/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@wp-playground/blueprints/node_modules/ini": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.2.tgz", + "integrity": "sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@wp-playground/blueprints/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@wp-playground/blueprints/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@wp-playground/cli": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@wp-playground/cli/-/cli-1.2.3.tgz", + "integrity": "sha512-XicbMXq1uHCVdObsApGbtZF3P9KxFMyOU2RVjCf/HNHvK/YRbQTbqYAxabV2lctatzWdcPZY3ab5hpTW7hgauA==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/logger": "1.2.3", + "@php-wasm/node": "1.2.3", + "@php-wasm/progress": "1.2.3", + "@php-wasm/universal": "1.2.3", + "@php-wasm/util": "1.2.3", + "@wp-playground/blueprints": "1.2.3", + "@wp-playground/common": "1.2.3", + "@wp-playground/storage": "1.2.3", + "@wp-playground/wordpress": "1.2.3", + "@zip.js/zip.js": "2.7.57", + "ajv": "8.12.0", + "async-lock": "1.4.1", + "clean-git-ref": "2.0.1", + "crc-32": "1.2.2", + "diff3": "0.0.4", + "express": "4.21.2", + "fs-ext": "2.1.1", + "fs-extra": "11.1.1", + "ignore": "5.3.2", + "ini": "4.1.2", + "minimisted": "2.0.1", + "octokit": "3.1.2", + "pako": "1.0.10", + "pify": "2.3.0", + "readable-stream": "3.6.2", + "sha.js": "2.4.11", + "simple-get": "4.0.1", + "wasm-feature-detect": "1.8.0", + "ws": "8.18.1", + "yargs": "17.7.2" + }, + "bin": { + "cli": "wp-playground.js" + } + }, + "node_modules/@wp-playground/cli/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@wp-playground/cli/node_modules/fs-extra": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@wp-playground/cli/node_modules/ini": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.2.tgz", + "integrity": "sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@wp-playground/cli/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@wp-playground/cli/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/@wordpress/stylelint-config": { - "version": "22.7.0", - "resolved": "https://registry.npmjs.org/@wordpress/stylelint-config/-/stylelint-config-22.7.0.tgz", - "integrity": "sha512-nzFUC1urqtLklCke38VTH7yYlF3ihHu+ULRYwzhJNqWzttwz4srj60M3etEa2pdd2U6yywotQAJzcDbHw01qvQ==", + "node_modules/@wp-playground/cli/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@wp-playground/common": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@wp-playground/common/-/common-1.2.3.tgz", + "integrity": "sha512-cAxssn9FbKGJv5fXorxwhSzJ47W2paFjRQcgHgv4uUND5iTyfyL3Gx8l3uoEGdGCf5mNOB22TZwJLvRsvi7Ftw==", + "dev": true, + "license": "GPL-2.0-or-later", "dependencies": { - "stylelint-config-recommended": "^6.0.0", - "stylelint-config-recommended-scss": "^5.0.2" + "@php-wasm/universal": "1.2.3", + "@php-wasm/util": "1.2.3", + "ini": "4.1.2" }, "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" + "node": ">=20.18.3", + "npm": ">=10.1.0" + } + }, + "node_modules/@wp-playground/common/node_modules/ini": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.2.tgz", + "integrity": "sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@wp-playground/storage": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@wp-playground/storage/-/storage-1.2.3.tgz", + "integrity": "sha512-EIK1hYd+RPuYJ/u1N9kHHZ0+QAjVT/2AMYvR9WIL7JzVVmuObbJisBiXxnpRLIKVzYpBS16B7UI5TpDBUecBDg==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/stream-compression": "1.2.3", + "@php-wasm/universal": "1.2.3", + "@php-wasm/util": "1.2.3", + "@php-wasm/web": "1.2.3", + "@zip.js/zip.js": "2.7.57", + "async-lock": "^1.4.1", + "clean-git-ref": "^2.0.1", + "crc-32": "^1.2.0", + "diff3": "0.0.3", + "express": "4.21.2", + "fs-ext": "2.1.1", + "ignore": "^5.1.4", + "ini": "4.1.2", + "minimisted": "^2.0.0", + "octokit": "3.1.2", + "pako": "^1.0.10", + "pify": "^4.0.1", + "readable-stream": "^3.4.0", + "sha.js": "^2.4.9", + "simple-get": "^4.0.1", + "wasm-feature-detect": "1.8.0", + "ws": "8.18.1", + "yargs": "17.7.2" + } + }, + "node_modules/@wp-playground/storage/node_modules/diff3": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/diff3/-/diff3-0.0.3.tgz", + "integrity": "sha512-iSq8ngPOt0K53A6eVr4d5Kn6GNrM2nQZtC740pzIriHtn4pOQ2lyzEXQMBeVcWERN0ye7fhBsk9PbLLQOnUx/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@wp-playground/storage/node_modules/ini": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.2.tgz", + "integrity": "sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@wp-playground/wordpress": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@wp-playground/wordpress/-/wordpress-1.2.3.tgz", + "integrity": "sha512-jsoWEpuWijQpP0+fhUEbk4TmV/QqlsZj6UBtdPNgJBlFFJZzEX+N8/9ZfXHxb44fQVSa9yR/YqVQyVIKk6tK+A==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/logger": "1.2.3", + "@php-wasm/node": "1.2.3", + "@php-wasm/universal": "1.2.3", + "@php-wasm/util": "1.2.3", + "@wp-playground/common": "1.2.3", + "express": "4.21.2", + "fs-ext": "2.1.1", + "ini": "4.1.2", + "wasm-feature-detect": "1.8.0", + "ws": "8.18.1", + "yargs": "17.7.2" }, - "peerDependencies": { - "stylelint": "^14.2" + "engines": { + "node": ">=20.18.3", + "npm": ">=10.1.0" } }, - "node_modules/@wordpress/warning": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@wordpress/warning/-/warning-3.7.0.tgz", - "integrity": "sha512-wGbQfPlf8YV6gGhcGPYWUhHORct4xaBQSaDTJrwzlgHYyrrJUVXXgZxaM4+Aa23zQoA13nvFQHvfssOkwdh65g==", + "node_modules/@wp-playground/wordpress/node_modules/ini": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.2.tgz", + "integrity": "sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==", + "dev": true, + "license": "ISC", "engines": { - "node": ">=18.12.0", - "npm": ">=8.19.2" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/@xtuc/ieee754": { @@ -4174,6 +5302,18 @@ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" }, + "node_modules/@zip.js/zip.js": { + "version": "2.7.57", + "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.57.tgz", + "integrity": "sha512-BtonQ1/jDnGiMed6OkV6rZYW78gLmLswkHOzyMrMb+CAR7CZO8phOHO6c2qw6qb1g1betN7kwEHhhZk30dv+NA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "bun": ">=0.7.0", + "deno": ">=1.0.0", + "node": ">=16.5.0" + } + }, "node_modules/abab": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", @@ -4258,6 +5398,20 @@ "node": ">= 6.0.0" } }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -4630,6 +5784,13 @@ "node": ">=8" } }, + "node_modules/async-lock": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", + "dev": true, + "license": "MIT" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -4940,6 +6101,13 @@ "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -4970,9 +6138,10 @@ } }, "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -4982,7 +6151,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.11.0", + "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -4996,6 +6165,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -5004,6 +6174,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -5012,6 +6183,7 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -5022,7 +6194,8 @@ "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/bonjour-service": { "version": "1.2.1", @@ -5038,6 +6211,13 @@ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, + "node_modules/bottleneck": { + "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", + "dev": true, + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -5096,6 +6276,13 @@ "node-int64": "^0.4.0" } }, + "node_modules/btoa-lite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", + "integrity": "sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA==", + "dev": true, + "license": "MIT" + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -5127,6 +6314,13 @@ "node": "*" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -5417,6 +6611,23 @@ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.0.tgz", "integrity": "sha512-N1NGmowPlGBLsOZLPvm48StN04V4YvQRL0i6b7ctrVY3epjP/ct7hFLOItz6pDIvRjwpfPxi52a2UWV2ziir8g==" }, + "node_modules/clean-git-ref": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/clean-git-ref/-/clean-git-ref-2.0.1.tgz", + "integrity": "sha512-bLSptAy2P0s6hU4PzuIMKmMJJSE6gLXGH1cntDu7bWJUksvuM+7ReOK61mozULErYvP6a15rnYl0zFDef+pyPw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/clean-webpack-plugin": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-3.0.0.tgz", @@ -5643,6 +6854,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -5807,6 +7019,19 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/create-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", @@ -6252,6 +7477,22 @@ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/dedent": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", @@ -6444,14 +7685,23 @@ "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/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true, + "license": "ISC" + }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" @@ -6483,6 +7733,13 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/diff3": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/diff3/-/diff3-0.0.4.tgz", + "integrity": "sha512-f1rQ7jXDn/3i37hdwRk9ohqcvLRH3+gEIgmA6qEM280WUOh7cOr3GXV8Jm5sPwUs46Nzl48SE8YNLGJoaLuodg==", + "dev": true, + "license": "MIT" + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -6604,10 +7861,21 @@ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" }, "node_modules/electron-to-chromium": { "version": "1.5.17", @@ -6639,9 +7907,10 @@ } }, "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -7760,6 +9029,7 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -7885,36 +9155,37 @@ "integrity": "sha512-6Ey4Xy2xvmuQu7z7YQtMsaMV0EHJRpVxIDOd5GRrm04/I3nkTKIutELfECsLp6le+b3SSa3cXhPiw6PgqzxYWA==" }, "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "0.19.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -7923,12 +9194,17 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express/node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -8125,12 +9401,13 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -8145,6 +9422,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -8152,7 +9430,8 @@ "node_modules/finalhandler/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/find-cache-dir": { "version": "3.3.2", @@ -8347,6 +9626,7 @@ "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -8364,6 +9644,19 @@ "node": ">=0.10.0" } }, + "node_modules/fs-ext": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fs-ext/-/fs-ext-2.1.1.tgz", + "integrity": "sha512-/TrISPOFhCkbgIRWK9lzscRzwPCu0PqtCcvMc9jsHKBgZGoqA0VzhspVht5Zu8lxaXjIYIBWILHpRotYkCCcQA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "nan": "^2.21.0" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/fs-extra": { "version": "11.2.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", @@ -8938,6 +10231,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", @@ -10660,6 +11954,42 @@ "node": ">= 10.0.0" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "dev": true, + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -10674,6 +12004,29 @@ "node": ">=4.0" } }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -11065,6 +12418,48 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -11075,6 +12470,13 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -11322,6 +12724,7 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -11395,9 +12798,13 @@ } }, "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/merge-stream": { "version": "2.0.0", @@ -11475,6 +12882,19 @@ "node": ">=6" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -11558,6 +12978,16 @@ "node": ">=0.10.0" } }, + "node_modules/minimisted": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minimisted/-/minimisted-2.0.1.tgz", + "integrity": "sha512-1oPjfuLQa2caorJUM8HV8lGgWCc0qqAO1MNv/k05G4qslmsndV/5WdNZrqCiyqiz3wohia2Ij2B7w2Dr7/IyrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5" + } + }, "node_modules/mitt": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz", @@ -11613,6 +13043,13 @@ "multicast-dns": "cli.js" } }, + "node_modules/nan": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.28.0.tgz", + "integrity": "sha512-fTsDz99OTq2sVePhGdp4qQhggZFtKr64ZNVyVajRKtMOkJxYekplBh577PiJB12v/D3s2E5cGtOI45LWp6rnLQ==", + "dev": true, + "license": "MIT" + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -12005,10 +13442,33 @@ "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" }, + "node_modules/octokit": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/octokit/-/octokit-3.1.2.tgz", + "integrity": "sha512-MG5qmrTL5y8KYwFgE1A4JWmgfQBaIETE/lOlfwNYx1QOtCQHGVxkRJmdUJltFc1HVn73d61TlMhMyNTOtMl+ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/app": "^14.0.2", + "@octokit/core": "^5.0.0", + "@octokit/oauth-app": "^6.0.0", + "@octokit/plugin-paginate-graphql": "^4.0.0", + "@octokit/plugin-paginate-rest": "^9.0.0", + "@octokit/plugin-rest-endpoint-methods": "^10.0.0", + "@octokit/plugin-retry": "^6.0.0", + "@octokit/plugin-throttling": "^8.0.0", + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -12226,6 +13686,13 @@ "node": ">= 14" } }, + "node_modules/pako": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", + "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", @@ -12348,9 +13815,10 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" }, "node_modules/path-type": { "version": "4.0.0", @@ -12428,33 +13896,33 @@ } }, "node_modules/playwright": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.0.tgz", - "integrity": "sha512-jOWiRq2pdNAX/mwLiwFYnPHpEZ4rM+fRSQpRHwEwZlP2PUANvL3+aJOF/bvISMhFD30rqMxUB4RJx9aQbfh4Ww==", - "peer": true, + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.62.1.tgz", + "integrity": "sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==", + "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.47.0" + "playwright-core": "1.62.1" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=18" + "node": ">=20" }, "optionalDependencies": { "fsevents": "2.3.2" } }, "node_modules/playwright-core": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.0.tgz", - "integrity": "sha512-1DyHT8OqkcfCkYUD9zzUTfg7EfTd+6a8MkD/NWOvjo0u/SCNd5YmY/lJwFvUZOxJbWNds+ei7ic2+R/cRz/PDg==", - "peer": true, + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.62.1.tgz", + "integrity": "sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==", + "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" }, "engines": { - "node": ">=18" + "node": ">=20" } }, "node_modules/playwright/node_modules/fsevents": { @@ -12462,11 +13930,11 @@ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" ], - "peer": true, "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } @@ -13390,11 +14858,12 @@ ] }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -13460,6 +14929,7 @@ "version": "2.5.2", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -13474,6 +14944,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -13482,6 +14953,7 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -14196,9 +15668,10 @@ } }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -14222,6 +15695,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -14229,12 +15703,23 @@ "node_modules/send/node_modules/debug/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } }, "node_modules/send/node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -14331,14 +15816,15 @@ } }, "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" @@ -14377,7 +15863,22 @@ "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } }, "node_modules/shallow-clone": { "version": "0.1.2", @@ -14461,6 +15962,53 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "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/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "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": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/sirv": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", @@ -14755,6 +16303,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -15518,6 +17067,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", "engines": { "node": ">=0.6" } @@ -15689,6 +17239,7 @@ "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" @@ -15816,9 +17367,10 @@ } }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", @@ -15867,6 +17419,24 @@ "node": ">=8" } }, + "node_modules/universal-github-app-jwt": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-1.2.0.tgz", + "integrity": "sha512-dncpMpnsKBk0eetwfN8D8OUHGfiDhhJ+mtsbMl+7PfW7mYjiH8LIcqRmYMtzYLgSh47HjfdBtrBwIQ/gizKR3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/jsonwebtoken": "^9.0.0", + "jsonwebtoken": "^9.0.2" + } + }, + "node_modules/universal-user-agent": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", + "dev": true, + "license": "ISC" + }, "node_modules/universalify": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", @@ -15879,6 +17449,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -16098,6 +17669,13 @@ "makeerror": "1.0.12" } }, + "node_modules/wasm-feature-detect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz", + "integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/watchpack": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", @@ -16677,9 +18255,10 @@ } }, "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", + "license": "MIT", "engines": { "node": ">=10.0.0" }, diff --git a/package.json b/package.json index 575332c..f7e6100 100644 --- a/package.json +++ b/package.json @@ -15,11 +15,19 @@ "lint:css": "wp-scripts lint-style --fix", "lint:js": "wp-scripts lint-js", "lint:php": "composer phpcs", - "test": "echo \"Error: no test specified\" && exit 1" + "playground:start": "npx @wp-playground/cli server --blueprint=blueprint.json --auto-mount", + "test:e2e": "playwright test", + "test:e2e:debug": "playwright test --debug", + "test:e2e:watch": "playwright test --ui" }, "author": "Human Made Limited", "license": "GPL-2.0-or-later", "dependencies": { "@wordpress/scripts": "^28.6.0" + }, + "devDependencies": { + "@playwright/test": "^1.58.0", + "@types/node": "^24.10.1", + "@wp-playground/cli": "^1.0.0" } } diff --git a/playwright.config.js b/playwright.config.js new file mode 100644 index 0000000..80fb979 --- /dev/null +++ b/playwright.config.js @@ -0,0 +1,48 @@ +const { defineConfig, devices } = require( '@playwright/test' ); + +/** + * Playground is booted by global-setup.js, which writes the server URL to + * process.env.WP_BASE_URL. Set WP_BASE_URL directly to skip global-setup and + * use an externally managed Playground (e.g. `npm run playground:start`). + * + * @see https://playwright.dev/docs/test-configuration + */ +module.exports = defineConfig( { + testDir: './tests/e2e', + globalSetup: require.resolve( './global-setup' ), + globalTeardown: require.resolve( './global-teardown' ), + /* Playground boot is slow — 120s gives headroom */ + timeout: 120000, + fullyParallel: false, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !! process.env.CI, + retries: process.env.CI ? 2 : 0, + /* Single worker — Playground is one shared instance with shared fixtures */ + workers: 1, + reporter: [ + [ 'list' ], + [ 'html', { open: process.env.CI ? 'never' : 'on-failure' } ], + [ 'json', { outputFile: 'test-results/results.json' } ], + ], + use: { + baseURL: process.env.WP_BASE_URL, + // Tell the server not to compress responses. Playground's WASM PHP can + // set Content-Encoding: gzip without producing a valid gzip body, which + // causes ERR_CONTENT_DECODING_FAILED. Sending 'identity' prevents PHP's + // zlib output compression from activating entirely. + extraHTTPHeaders: { 'Accept-Encoding': 'identity' }, + trace: 'on-first-retry', + screenshot: 'only-on-failure', + video: 'retain-on-failure', + actionTimeout: 15000, + navigationTimeout: 30000, + storageState: '.auth/wordpress.json', + }, + + projects: [ + { + name: 'chromium', + use: { ...devices[ 'Desktop Chrome' ] }, + }, + ], +} ); diff --git a/tests/e2e/README.md b/tests/e2e/README.md new file mode 100644 index 0000000..0a8ad6f --- /dev/null +++ b/tests/e2e/README.md @@ -0,0 +1,89 @@ +# End-to-End Tests + +Playwright tests for the query filter blocks, run against +[WordPress Playground](https://wordpress.org/playground/) rather than a Docker +based environment. There is nothing to start or stop: Playground boots in +process and is torn down when the run finishes. + +## Running the tests + +```bash +npm ci +npm run build # tests run against build/, not src/ +npm run test:e2e +``` + +Other commands: + +- `npm run test:e2e:debug` — step through tests in the Playwright inspector. +- `npm run test:e2e:watch` — the Playwright UI, reruns on change. +- `npm run playground:start` — boot the same environment for manual poking, + without running any tests. + +## How the environment is put together + +`blueprint.json` describes the whole environment and is shared between the test +run and `npm run playground:start`: + +1. Logs in as `admin` / `password`. +2. Installs and activates Twenty Twenty-Five and the Advanced Query Loop + plugin. +3. Activates this plugin, mounted from the working tree. +4. Copies `tests/mu-plugins/register-test-content.php` into `mu-plugins`, which + registers the post types and taxonomies the tests filter against — including + deliberately private ones. +5. Runs `tests/seed.php`, which creates the fixture terms, posts and the demo + pages the specs visit. + +Seeding is idempotent, guarded by the `query_filter_e2e_seeded` option. + +### Fixture content + +| Page | Query ID | Contains | +| ---- | -------- | -------- | +| `/taxonomy-filter/` | 1 | Taxonomy filter (select) | +| `/taxonomy-checkboxes/` | 2 | Taxonomy filter (checkboxes) | +| `/post-type-filter/` | 3 | Post type filter and core search block | + +Posts: `Alpha One` and `Alpha Two` in the `alpha` category, `Beta One` in +`beta`, and `Unfiled Post` in neither — so an active filter is always +distinguishable from no filter. `Doc One` and `Doc Two` are in the public +`qf_doc` post type. `Secret One` is published in the private `qf_secret` post +type and must never appear on the front end. + +## Configuration + +| Variable | Purpose | +| -------- | ------- | +| `WP_BASE_URL` | Use an already running Playground and skip booting one. | +| `WP_PLAYGROUND_PORT` | Pin the port. Otherwise derived from a hash of the working directory, so worktrees don't collide. | +| `WP_PLAYGROUND_PHP` | PHP version, e.g. `8.3`. | +| `WP_PLAYGROUND_WP` | WordPress version, e.g. `6.8` or `latest`. | +| `WP_BLUEPRINT_PATH` | Use a different blueprint. | + +## Writing tests + +Import from `./fixtures` rather than `@playwright/test` directly. It provides +the `loop` fixture for reading a rendered query loop, and `POSTS` for the +fixture titles: + +```js +const { test, expect, POSTS } = require( './fixtures' ); + +test( 'filters the loop', async ( { page, loop } ) => { + await page.goto( '/taxonomy-filter/' ); + await loop.taxonomySelect().selectOption( { label: 'Alpha' } ); + await page.waitForURL( /query-1-category=alpha/ ); + await loop.expectTitles( POSTS.alpha ); +} ); +``` + +Filtering navigates through the Interactivity API router, which swaps the loop +region in without a document navigation. Use `loop.expectTitles()`, which polls +the rendered titles, rather than waiting on a load event. + +## CI + +`.github/workflows/playwright-tests.yml` runs the suite on pushes to `main` and +on pull requests, across a PHP 8.2/8.3 × WordPress 6.8/latest matrix, and +comments the results on the PR. diff --git a/tests/e2e/fixtures.js b/tests/e2e/fixtures.js new file mode 100644 index 0000000..0242ead --- /dev/null +++ b/tests/e2e/fixtures.js @@ -0,0 +1,88 @@ +const { test: base, expect } = require( '@playwright/test' ); + +/** + * Fixture titles seeded by tests/seed.php, grouped for readability in + * assertions. Keep in step with that file. + */ +const POSTS = { + alpha: [ 'Alpha One', 'Alpha Two' ], + beta: [ 'Beta One' ], + unfiled: [ 'Unfiled Post' ], + docs: [ 'Doc One', 'Doc Two' ], + secret: [ 'Secret One' ], +}; + +POSTS.all = [ ...POSTS.alpha, ...POSTS.beta, ...POSTS.unfiled ]; + +/** + * Helpers for reading a rendered query loop. + */ +const test = base.extend( { + /** + * Query loop utilities bound to the current page. + * + * @param {Object} root0 Fixture arguments. + * @param {import('@playwright/test').Page} root0.page Page under test. + * @param {Function} use Playwright fixture callback. + */ + loop: async ( { page }, use ) => { + const loop = { + /** + * Titles currently rendered by the query loop, in document order. + * + * @return {Promise} Rendered post titles. + */ + async titles() { + const titles = await page + .locator( '.wp-block-post-template .wp-block-post-title' ) + .allInnerTexts(); + return titles.map( ( title ) => title.trim() ); + }, + + /** + * Wait until the loop renders exactly the given titles. + * + * The interactivity router swaps the region in without a document + * navigation, so polling the rendered titles is the reliable + * signal that the update has landed. + * + * @param {string[]} expected Titles in the order they should appear. + */ + async expectTitles( expected ) { + await expect + .poll( () => loop.titles(), { + message: `waiting for loop to render ${ JSON.stringify( + expected + ) }`, + } ) + .toEqual( expected ); + }, + + /** + * The taxonomy filter's select control. + * + * @return {import('@playwright/test').Locator} Select locator. + */ + taxonomySelect() { + return page.locator( + '.wp-block-query-filter-taxonomy__select' + ); + }, + + /** + * The post type filter's select control. + * + * @return {import('@playwright/test').Locator} Select locator. + */ + postTypeSelect() { + return page.locator( + '.wp-block-query-filter-post-type__select' + ); + }, + }; + + await use( loop ); + }, +} ); + +module.exports = { test, expect, POSTS }; diff --git a/tests/e2e/post-type-filter.spec.js b/tests/e2e/post-type-filter.spec.js new file mode 100644 index 0000000..dc47e1e --- /dev/null +++ b/tests/e2e/post-type-filter.spec.js @@ -0,0 +1,85 @@ +const { test, expect, POSTS } = require( './fixtures' ); + +test.describe( 'Post type filter', () => { + test( 'renders a select for the post types the loop queries', async ( { + page, + loop, + } ) => { + await page.goto( '/post-type-filter/' ); + + const select = loop.postTypeSelect(); + await expect( select ).toBeVisible(); + await expect( select.locator( 'option' ) ).toHaveText( [ + 'All', + 'Posts', + ] ); + + await loop.expectTitles( POSTS.all ); + } ); + + test( 'a public post type in the URL switches the loop', async ( { + page, + loop, + } ) => { + await page.goto( '/post-type-filter/?query-3-post_type=qf_doc' ); + + await loop.expectTitles( POSTS.docs ); + } ); + + test( 'a private post type in the URL is ignored', async ( { + page, + loop, + } ) => { + // qf_secret is registered with public and publicly_queryable false. + // Its posts are published, so without a viewability check the loop + // would happily list them. + const response = await page.goto( + '/post-type-filter/?query-3-post_type=qf_secret' + ); + + expect( response.status() ).toBe( 200 ); + await loop.expectTitles( POSTS.all ); + await expect( page.locator( 'body' ) ).not.toContainText( + 'Secret One' + ); + } ); + + test( 'an unregistered post type in the URL is ignored', async ( { + page, + loop, + } ) => { + await page.goto( + '/post-type-filter/?query-3-post_type=not_a_post_type' + ); + + await loop.expectTitles( POSTS.all ); + } ); + + test( 'a mixed list keeps only the public post types', async ( { + page, + loop, + } ) => { + await page.goto( + '/post-type-filter/?query-3-post_type=qf_doc,qf_secret' + ); + + await loop.expectTitles( POSTS.docs ); + await expect( page.locator( 'body' ) ).not.toContainText( + 'Secret One' + ); + } ); + + test( 'an array shaped post type value does not error', async ( { + page, + loop, + } ) => { + // ?query-3-post_type[]=x reached urldecode() as an array, which is an + // uncaught TypeError on PHP 8. + const response = await page.goto( + '/post-type-filter/?query-3-post_type%5B%5D=qf_secret' + ); + + expect( response.status() ).toBe( 200 ); + await loop.expectTitles( POSTS.all ); + } ); +} ); diff --git a/tests/e2e/search-filter.spec.js b/tests/e2e/search-filter.spec.js new file mode 100644 index 0000000..503c63f --- /dev/null +++ b/tests/e2e/search-filter.spec.js @@ -0,0 +1,35 @@ +const { test, expect, POSTS } = require( './fixtures' ); + +test.describe( 'Search filter', () => { + test( 'the core search block narrows the loop it sits in', async ( { + page, + loop, + } ) => { + await page.goto( '/post-type-filter/' ); + await loop.expectTitles( POSTS.all ); + + const input = page.locator( '.wp-block-search__input' ); + await expect( input ).toBeVisible(); + + // The search block is rewritten to post into the loop's own query var + // so several searchable loops can coexist on one page. + await expect( input ).toHaveAttribute( 'name', 'query-3-s' ); + + await input.fill( 'Alpha' ); + + await page.waitForURL( /query-3-s=Alpha/ ); + await loop.expectTitles( POSTS.alpha ); + } ); + + test( 'a search term in the URL is reflected in the input', async ( { + page, + loop, + } ) => { + await page.goto( '/post-type-filter/?query-3-s=Beta' ); + + await loop.expectTitles( POSTS.beta ); + await expect( page.locator( '.wp-block-search__input' ) ).toHaveValue( + 'Beta' + ); + } ); +} ); diff --git a/tests/e2e/taxonomy-filter.spec.js b/tests/e2e/taxonomy-filter.spec.js new file mode 100644 index 0000000..37930de --- /dev/null +++ b/tests/e2e/taxonomy-filter.spec.js @@ -0,0 +1,123 @@ +const { test, expect, POSTS } = require( './fixtures' ); + +test.describe( 'Taxonomy filter', () => { + test( 'renders a select listing every non-empty term', async ( { + page, + loop, + } ) => { + await page.goto( '/taxonomy-filter/' ); + + const select = loop.taxonomySelect(); + await expect( select ).toBeVisible(); + await expect( select.locator( 'option' ) ).toHaveText( [ + 'All', + 'Alpha', + 'Beta', + ] ); + + // Unfiltered, the loop shows every post. + await loop.expectTitles( POSTS.all ); + } ); + + test( 'selecting a term filters the loop and updates the URL', async ( { + page, + loop, + } ) => { + await page.goto( '/taxonomy-filter/' ); + await loop.expectTitles( POSTS.all ); + + await loop.taxonomySelect().selectOption( { label: 'Alpha' } ); + + await page.waitForURL( /query-1-category=alpha/ ); + await loop.expectTitles( POSTS.alpha ); + } ); + + test( 'a term in the URL filters the loop on first render', async ( { + page, + loop, + } ) => { + await page.goto( '/taxonomy-filter/?query-1-category=beta' ); + + await loop.expectTitles( POSTS.beta ); + + // The control reflects the active filter rather than resetting to All. + await expect( loop.taxonomySelect() ).toHaveValue( + /query-1-category=beta/ + ); + } ); + + test( 'returning to All clears the filter', async ( { page, loop } ) => { + await page.goto( '/taxonomy-filter/?query-1-category=alpha' ); + await loop.expectTitles( POSTS.alpha ); + + await loop.taxonomySelect().selectOption( { label: 'All' } ); + + await page.waitForURL( + ( url ) => ! url.search.includes( 'query-1-category' ) + ); + await loop.expectTitles( POSTS.all ); + } ); + + test( 'an unknown term slug returns nothing rather than everything', async ( { + page, + loop, + } ) => { + await page.goto( '/taxonomy-filter/?query-1-category=does-not-exist' ); + + await loop.expectTitles( [] ); + } ); + + test( 'checkbox mode selects several terms at once', async ( { + page, + loop, + } ) => { + await page.goto( '/taxonomy-checkboxes/' ); + await loop.expectTitles( POSTS.all ); + + const group = page.locator( + '.wp-block-query-filter-taxonomy__checkbox-group' + ); + await expect( group.locator( 'label' ) ).toHaveText( [ + 'Alpha', + 'Beta', + ] ); + + await group + .locator( 'label', { hasText: 'Alpha' } ) + .locator( 'input' ) + .check(); + await page.waitForURL( /query-2-category=alpha/ ); + await loop.expectTitles( POSTS.alpha ); + + await group + .locator( 'label', { hasText: 'Beta' } ) + .locator( 'input' ) + .check(); + await page.waitForURL( + /query-2-category=alpha%2Cbeta|query-2-category=alpha,beta/ + ); + await loop.expectTitles( [ ...POSTS.alpha, ...POSTS.beta ] ); + } ); + + test( 'a private taxonomy named in the URL is ignored', async ( { + page, + loop, + } ) => { + // qf_hidden is registered non-public, and only Alpha One carries the + // "classified" term. If the filter were honoured the loop would narrow + // to that single post, turning the front end into an oracle for a + // private grouping. + await page.goto( '/taxonomy-filter/?query-1-qf_hidden=classified' ); + + await loop.expectTitles( POSTS.all ); + } ); + + test( 'an unregistered taxonomy named in the URL is ignored', async ( { + page, + loop, + } ) => { + await page.goto( '/taxonomy-filter/?query-1-not_a_taxonomy=whatever' ); + + await loop.expectTitles( POSTS.all ); + } ); +} ); diff --git a/tests/mu-plugins/register-test-content.php b/tests/mu-plugins/register-test-content.php new file mode 100644 index 0000000..d2fc39b --- /dev/null +++ b/tests/mu-plugins/register-test-content.php @@ -0,0 +1,50 @@ + 'Docs', + 'public' => true, + 'publicly_queryable' => true, + 'show_in_rest' => true, + 'has_archive' => true, + 'supports' => [ 'title', 'editor', 'excerpt' ], + ] ); + + // Private post type. Nothing on the front end may ever surface these, + // however the query string asks. + register_post_type( 'qf_secret', [ + 'label' => 'Secrets', + 'public' => false, + 'publicly_queryable' => false, + 'show_in_rest' => false, + 'supports' => [ 'title', 'editor' ], + ] ); + + // Private taxonomy, for the same reason. + register_taxonomy( 'qf_hidden', [ 'post' ], [ + 'label' => 'Hidden', + 'public' => false, + 'publicly_queryable' => false, + 'show_in_rest' => false, + 'hierarchical' => false, + ] ); +} diff --git a/tests/seed.php b/tests/seed.php new file mode 100644 index 0000000..99c7e93 --- /dev/null +++ b/tests/seed.php @@ -0,0 +1,131 @@ + $title, + 'post_name' => sanitize_title( $title ), + 'post_status' => 'publish', + 'post_type' => $type, + 'post_author' => 1, + 'post_content' => sprintf( 'Fixture content for %s.', $title ), + ], $extra ) ); +} + +// Categories. "Unfiled Post" deliberately belongs to neither, so an active +// filter is distinguishable from no filter at all. +wp_insert_term( 'Alpha', 'category', [ 'slug' => 'alpha' ] ); +wp_insert_term( 'Beta', 'category', [ 'slug' => 'beta' ] ); + +$alpha_one = seed_post( 'Alpha One' ); +$alpha_two = seed_post( 'Alpha Two' ); +$beta_one = seed_post( 'Beta One' ); +seed_post( 'Unfiled Post' ); + +wp_set_object_terms( $alpha_one, [ 'alpha' ], 'category' ); +wp_set_object_terms( $alpha_two, [ 'alpha' ], 'category' ); +wp_set_object_terms( $beta_one, [ 'beta' ], 'category' ); + +// Private taxonomy term on a post that is otherwise public, so a query string +// filter naming it would visibly narrow the results if it were honoured. +wp_insert_term( 'Classified', 'qf_hidden', [ 'slug' => 'classified' ] ); +wp_set_object_terms( $alpha_one, [ 'classified' ], 'qf_hidden' ); + +// Second public post type. +seed_post( 'Doc One', 'qf_doc' ); +seed_post( 'Doc Two', 'qf_doc' ); + +// Published, but in a post type that is not publicly queryable. If this title +// ever appears on the front end, something has gone wrong. +seed_post( 'Secret One', 'qf_secret' ); + +/** + * Build the block markup for a query loop carrying filter blocks. + * + * @param int $query_id Query ID for the loop. + * @param string $filters Serialized filter block markup to place in the loop. + * @return string Block markup. + */ +function query_loop_markup( int $query_id, string $filters ) : string { + $query = wp_json_encode( [ + 'queryId' => $query_id, + 'query' => [ + 'perPage' => 10, + 'pages' => 0, + 'offset' => 0, + 'postType' => 'post', + 'order' => 'asc', + 'orderBy' => 'title', + 'author' => '', + 'search' => '', + 'exclude' => [], + 'sticky' => '', + 'inherit' => false, + ], + ] ); + + return << +
+{$filters} + + + +
+ +HTML; +} + +// Page 1: taxonomy filter as a select. +seed_post( 'Taxonomy Filter', 'page', [ + 'post_name' => 'taxonomy-filter', + 'post_content' => query_loop_markup( + 1, + '' + ), +] ); + +// Page 2: taxonomy filter as checkboxes, for multi-term selection. +seed_post( 'Taxonomy Checkboxes', 'page', [ + 'post_name' => 'taxonomy-checkboxes', + 'post_content' => query_loop_markup( + 2, + '' + ), +] ); + +// Page 3: post type filter and the core search block. +seed_post( 'Post Type Filter', 'page', [ + 'post_name' => 'post-type-filter', + 'post_content' => query_loop_markup( + 3, + '' . "\n" . '' + ), +] ); + +update_option( 'query_filter_e2e_seeded', 1 ); + +echo "Seeded query filter e2e fixtures.\n"; From 62ae389f2ec8d009bcaa9bb17103b68a469162fc Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 11 Aug 2026 09:39:01 +0000 Subject: [PATCH 2/4] Clear WordPress default content before seeding e2e fixtures Every unfiltered assertion failed on the same diff: WordPress ships with a "Hello world!" post, so the loop returned five titles where the fixtures define four. It also sat in Uncategorized, which put a third option in the taxonomy filter's derived term list. Delete all pre-existing posts and pages before seeding so the fixtures are the only content the loops can return. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_015J9z6XLV1hT1FB1BSXPczo --- tests/seed.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/seed.php b/tests/seed.php index 99c7e93..d526b48 100644 --- a/tests/seed.php +++ b/tests/seed.php @@ -35,6 +35,21 @@ function seed_post( string $title, string $type = 'post', array $extra = [] ) : ], $extra ) ); } +// WordPress ships with a "Hello world!" post and a sample page. Remove all +// pre-existing content so the fixtures below are the only thing the loops can +// return, and so the default Uncategorized category stays empty and therefore +// out of the taxonomy filter's derived term list. +$existing = get_posts( [ + 'post_type' => [ 'post', 'page' ], + 'post_status' => 'any', + 'numberposts' => -1, + 'fields' => 'ids', +] ); + +foreach ( $existing as $existing_id ) { + wp_delete_post( $existing_id, true ); +} + // Categories. "Unfiled Post" deliberately belongs to neither, so an active // filter is distinguishable from no filter at all. wp_insert_term( 'Alpha', 'category', [ 'slug' => 'alpha' ] ); From 1caf181b308f446ada9ed584e3e7615badfe514c Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 11 Aug 2026 09:45:29 +0000 Subject: [PATCH 3/4] Keep Uncategorized out of the term list, and normalize label text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two things kept the taxonomy term list assertions red. wp_insert_post() assigns the default category when a post is created without one, so "Unfiled Post" — whose whole purpose is to belong to no category — landed in Uncategorized. That made Uncategorized non-empty and put a third option in the filter's derived term list. Clear it explicitly. The checkbox labels wrap their text across lines in the template, and toHaveText compares textContent verbatim, so the tabs and newlines came through. Compare innerText instead. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_015J9z6XLV1hT1FB1BSXPczo --- tests/e2e/taxonomy-filter.spec.js | 10 ++++++---- tests/seed.php | 7 ++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/tests/e2e/taxonomy-filter.spec.js b/tests/e2e/taxonomy-filter.spec.js index 37930de..b185967 100644 --- a/tests/e2e/taxonomy-filter.spec.js +++ b/tests/e2e/taxonomy-filter.spec.js @@ -77,10 +77,12 @@ test.describe( 'Taxonomy filter', () => { const group = page.locator( '.wp-block-query-filter-taxonomy__checkbox-group' ); - await expect( group.locator( 'label' ) ).toHaveText( [ - 'Alpha', - 'Beta', - ] ); + // useInnerText, because the label markup wraps its text across lines + // and toHaveText compares textContent verbatim. + await expect( group.locator( 'label' ) ).toHaveText( + [ 'Alpha', 'Beta' ], + { useInnerText: true } + ); await group .locator( 'label', { hasText: 'Alpha' } ) diff --git a/tests/seed.php b/tests/seed.php index d526b48..db7f31f 100644 --- a/tests/seed.php +++ b/tests/seed.php @@ -58,12 +58,17 @@ function seed_post( string $title, string $type = 'post', array $extra = [] ) : $alpha_one = seed_post( 'Alpha One' ); $alpha_two = seed_post( 'Alpha Two' ); $beta_one = seed_post( 'Beta One' ); -seed_post( 'Unfiled Post' ); +$unfiled = seed_post( 'Unfiled Post' ); wp_set_object_terms( $alpha_one, [ 'alpha' ], 'category' ); wp_set_object_terms( $alpha_two, [ 'alpha' ], 'category' ); wp_set_object_terms( $beta_one, [ 'beta' ], 'category' ); +// wp_insert_post() assigns the default category when a post is created without +// one. The point of this post is to belong to no category, and clearing it also +// leaves Uncategorized empty so it stays out of the filter's derived term list. +wp_set_object_terms( $unfiled, [], 'category' ); + // Private taxonomy term on a post that is otherwise public, so a query string // filter naming it would visibly narrow the results if it were honoured. wp_insert_term( 'Classified', 'qf_hidden', [ 'slug' => 'classified' ] ); From 1bb708e9956cc0130fd0a984cdbe6af99cc277a3 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 12 Aug 2026 16:42:00 +0000 Subject: [PATCH 4/4] Keep the Playground test harness out of the release archive The e2e harness landed after .gitattributes, so its four root files were not covered by any export-ignore rule and would have shipped in the distribution ZIP. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_015J9z6XLV1hT1FB1BSXPczo --- .gitattributes | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitattributes b/.gitattributes index 9a36fab..1338c42 100644 --- a/.gitattributes +++ b/.gitattributes @@ -13,7 +13,11 @@ /.gitattributes export-ignore /.phpcs.xml export-ignore /.wp-env.json export-ignore +/blueprint.json export-ignore /composer.lock export-ignore +/global-setup.js export-ignore +/global-teardown.js export-ignore +/playwright.config.js export-ignore /package.json export-ignore /package-lock.json export-ignore /CONTRIBUTING.md export-ignore