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 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 fea617a..94714cb 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 Releases are cut by a manually-dispatched ["Release" GH Actions workflow](https://github.com/humanmade/query-filter/actions/workflows/release.yml), which builds the plugin, stamps the version into `query-filter.php`, and creates an immutable `vX.Y.Z` tag pointing at the built, versioned code. That tag is published to [Packagist](https://packagist.org/packages/humanmade/query-filter) and attached to a GitHub release as a downloadable ZIP. 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..b185967 --- /dev/null +++ b/tests/e2e/taxonomy-filter.spec.js @@ -0,0 +1,125 @@ +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' + ); + // 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' } ) + .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..db7f31f --- /dev/null +++ b/tests/seed.php @@ -0,0 +1,151 @@ + $title, + 'post_name' => sanitize_title( $title ), + 'post_status' => 'publish', + 'post_type' => $type, + 'post_author' => 1, + 'post_content' => sprintf( 'Fixture content for %s.', $title ), + ], $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' ] ); +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' ); +$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' ] ); +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";