From ae4a32a779e44ed9300a66e5966ca83dcc4f9cd5 Mon Sep 17 00:00:00 2001 From: Steve Jones Date: Fri, 13 Feb 2026 00:07:18 -0500 Subject: [PATCH 01/26] Enhance link validation to trim whitespace and normalize href checks --- src/pageScanner/checks/link-has-valid-href-or-role.js | 11 ++++++----- tests/jest/rules/linkImproper.test.js | 5 +++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/pageScanner/checks/link-has-valid-href-or-role.js b/src/pageScanner/checks/link-has-valid-href-or-role.js index 013ace5c8..0b25e253f 100644 --- a/src/pageScanner/checks/link-has-valid-href-or-role.js +++ b/src/pageScanner/checks/link-has-valid-href-or-role.js @@ -31,21 +31,22 @@ export default { } const trimmedHref = href ? href.trim() : ''; + const normalizedHref = trimmedHref.toLowerCase(); // Fail if href is missing, just '#', or contains invalid protocols if ( ! href || trimmedHref === '#' || - href.toLowerCase().startsWith( 'javascript:' ) || - href.toLowerCase().startsWith( 'data:' ) || - href.toLowerCase().startsWith( 'file:' ) + normalizedHref.startsWith( 'javascript:' ) || + normalizedHref.startsWith( 'data:' ) || + normalizedHref.startsWith( 'file:' ) ) { return false; } // Optionally validate URL format if it's an absolute URL - if ( href.includes( '://' ) ) { + if ( trimmedHref.includes( '://' ) ) { try { - new URL( href ); + new URL( trimmedHref ); } catch ( e ) { return false; // Invalid URL formats } diff --git a/tests/jest/rules/linkImproper.test.js b/tests/jest/rules/linkImproper.test.js index 8ca9a6c15..33fb4e22d 100644 --- a/tests/jest/rules/linkImproper.test.js +++ b/tests/jest/rules/linkImproper.test.js @@ -39,6 +39,11 @@ describe( 'Link Improper Rule', () => { html: 'Bad practice', shouldPass: false, }, + { + name: 'Fails when anchor has javascript: href with leading whitespace', + html: 'Bad practice', + shouldPass: false, + }, { name: 'Fails when anchor has malformed URL', html: 'Invalid URL', From 8aa48bc54361a159f4514ac693ac0058b7cea64c Mon Sep 17 00:00:00 2001 From: Steve Jones Date: Wed, 18 Feb 2026 20:32:54 -0500 Subject: [PATCH 02/26] Fix cached table name reuse in validator --- includes/helper-functions.php | 10 ++-- .../GetValidTableNameTest.php | 53 +++++++++++++++++++ 2 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 tests/phpunit/helper-functions/GetValidTableNameTest.php diff --git a/includes/helper-functions.php b/includes/helper-functions.php index f1a71db42..f54785e7a 100644 --- a/includes/helper-functions.php +++ b/includes/helper-functions.php @@ -238,10 +238,10 @@ function edac_get_post_type_label( string $post_type ): string { */ function edac_get_valid_table_name( $table_name ) { global $wpdb; - static $found_table_name; + static $found_table_names = []; - if ( isset( $found_table_name ) ) { - return $found_table_name; + if ( isset( $found_table_names[ $table_name ] ) ) { + return $found_table_names[ $table_name ]; } // Check if table name only contains alphanumeric characters, underscores, or hyphens. @@ -257,8 +257,8 @@ function edac_get_valid_table_name( $table_name ) { return null; } - $found_table_name = $table_name; - return $found_table_name; + $found_table_names[ $table_name ] = $table_name; + return $table_name; } /** diff --git a/tests/phpunit/helper-functions/GetValidTableNameTest.php b/tests/phpunit/helper-functions/GetValidTableNameTest.php new file mode 100644 index 000000000..a1421b8a5 --- /dev/null +++ b/tests/phpunit/helper-functions/GetValidTableNameTest.php @@ -0,0 +1,53 @@ +get_charset_collate(); + $main_table = $wpdb->prefix . 'accessibility_checker'; + $this->alt_table = $wpdb->posts; + + require_once ABSPATH . 'wp-admin/includes/upgrade.php'; + + $db_schema = "CREATE TABLE %s ( + id bigint(20) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (id) + ) $charset_collate;"; + + dbDelta( sprintf( $db_schema, $main_table ) ); + } + + /** + * Ensures the helper validates different tables without returning cached results. + */ + public function test_returns_requested_table_name_each_time() { + global $wpdb; + + $main_table = $wpdb->prefix . 'accessibility_checker'; + + $this->assertSame( $main_table, edac_get_valid_table_name( $main_table ) ); + $this->assertSame( $this->alt_table, edac_get_valid_table_name( $this->alt_table ) ); + } +} From 183fd07b38012220db52c1e84f8046ff38abe6c0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 17:09:49 +0000 Subject: [PATCH 03/26] Initial plan From 573af34dd3e3fe96dc25a4e552b68db1b372dd47 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Apr 2026 11:51:02 +0000 Subject: [PATCH 04/26] Initial plan From 4c6587842b507f35b508abde5951951951539a77 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Apr 2026 11:55:38 +0000 Subject: [PATCH 05/26] Skip 1x1 tracking pixels with empty alt in img_alt_empty_check Agent-Logs-Url: https://github.com/equalizedigital/accessibility-checker/sessions/5add204d-d436-4544-9c42-3b121f54713e Co-authored-by: pattonwebz <3902039+pattonwebz@users.noreply.github.com> --- src/pageScanner/checks/img-alt-empty-check.js | 32 +++++++++++++++++++ tests/jest/rules/imgAltEmpty.test.js | 22 +++++++++++++ 2 files changed, 54 insertions(+) diff --git a/src/pageScanner/checks/img-alt-empty-check.js b/src/pageScanner/checks/img-alt-empty-check.js index 1d4bbdbcb..c2fa88487 100644 --- a/src/pageScanner/checks/img-alt-empty-check.js +++ b/src/pageScanner/checks/img-alt-empty-check.js @@ -35,6 +35,11 @@ export default { return true; } + // Skip 1x1 tracking pixels (any src or base64) + if ( hasEmptyAlt && isTrackingPixel( node ) ) { + return true; + } + // Return false if alt is empty and none of the exceptions apply return ! hasEmptyAlt; }, @@ -78,6 +83,33 @@ function isInsideValidCaption( node ) { return false; } +/** + * Check if image is a 1x1 tracking pixel. + * Checks both HTML attributes and computed natural dimensions. + * @param {HTMLElement} node - The node to check + * @return {boolean} True if image is a 1x1 tracking pixel + */ +function isTrackingPixel( node ) { + // Check HTML width/height attributes + const widthAttr = node.getAttribute( 'width' ); + const heightAttr = node.getAttribute( 'height' ); + if ( widthAttr === '1' && heightAttr === '1' ) { + return true; + } + + // Check computed natural dimensions (e.g. for base64 or loaded images without explicit attributes) + if ( + typeof node.naturalWidth === 'number' && + typeof node.naturalHeight === 'number' && + node.naturalWidth === 1 && + node.naturalHeight === 1 + ) { + return true; + } + + return false; +} + /** * Check if image should be ignored due to plugin-specific cases * @param {HTMLElement} node - The node to check diff --git a/tests/jest/rules/imgAltEmpty.test.js b/tests/jest/rules/imgAltEmpty.test.js index cadf38fa5..220e75cd1 100644 --- a/tests/jest/rules/imgAltEmpty.test.js +++ b/tests/jest/rules/imgAltEmpty.test.js @@ -95,6 +95,28 @@ describe( 'Image Alt Empty Validation', () => { html: '', shouldPass: true, }, + + // Tracking pixel (1x1) edge cases + { + name: 'should pass for 1x1 tracking pixel with empty alt (gif)', + html: '', + shouldPass: true, + }, + { + name: 'should pass for 1x1 tracking pixel with empty alt (arbitrary src)', + html: '', + shouldPass: true, + }, + { + name: 'should fail for 2x1 image with empty alt (not a tracking pixel)', + html: '', + shouldPass: false, + }, + { + name: 'should fail for img with empty alt and no dimension attributes (cannot confirm tracking pixel without dimensions)', + html: '', + shouldPass: false, + }, ]; testCases.forEach( ( testCase ) => { From fcd70333da488073c7ac666abc9106fc9c427cfe Mon Sep 17 00:00:00 2001 From: Steve Jones Date: Thu, 28 May 2026 22:47:10 -0400 Subject: [PATCH 06/26] Fix: use correct ID when site homepage is set to latest posts When show_on_front=posts, WordPress sets the global $post to the first blog post rather than a page object. Both the frontend highlighter and the admin editor app were passing that post's ID to scan storage, attributing homepage issues to the wrong entry. Frontend: pass null (not the first post's ID) to edac_filter_frontend_highlight_post_id when is_home() && is_front_page() && show_on_front=posts, so the free plugin bails cleanly and Pro can supply a virtual-page ID via the filter. Admin: add edac_filter_admin_post_id so extensions can correct the post ID when the FSE site editor sets $post to the first blog post. Add edac_filter_post_is_latest_posts_home so Pro can flag a virtual homepage page and trigger use of get_home_url() for the scanner iframe instead of an invalid preview URL. Fixes PRO-726 Co-Authored-By: Claude Sonnet 4.6 --- admin/class-enqueue-admin.php | 19 +++++- includes/classes/class-enqueue-frontend.php | 11 +++- tests/phpunit/Admin/EnqueueAdminTest.php | 62 +++++++++++++++++++ .../includes/classes/EnqueueFrontendTest.php | 52 ++++++++++++++++ 4 files changed, 142 insertions(+), 2 deletions(-) diff --git a/admin/class-enqueue-admin.php b/admin/class-enqueue-admin.php index 137374432..4f81bfa1a 100644 --- a/admin/class-enqueue-admin.php +++ b/admin/class-enqueue-admin.php @@ -78,6 +78,14 @@ public static function maybe_enqueue_admin_and_editor_app_scripts() { global $post; $post_id = is_object( $post ) ? $post->ID : null; + + // When the "latest posts" homepage is active (show_on_front=posts), the global + // $post can be set to the first blog post from the main query — for example in + // the FSE site editor while previewing the home template. Apply a filter so that + // extensions can supply the correct post ID (e.g. a Pro virtual-page ID) rather + // than letting the wrong post's ID propagate into scan results. + $post_id = apply_filters( 'edac_filter_admin_post_id', $post_id ); + wp_enqueue_script( 'edac', plugin_dir_url( EDAC_PLUGIN_FILE ) . 'build/admin.bundle.js', [ 'jquery' ], EDAC_VERSION, false ); wp_set_script_translations( 'edac', 'accessibility-checker', plugin_dir_path( EDAC_PLUGIN_FILE ) . 'languages' ); @@ -112,7 +120,16 @@ public static function maybe_enqueue_admin_and_editor_app_scripts() { wp_set_script_translations( 'edac-editor-app', 'accessibility-checker', plugin_dir_path( EDAC_PLUGIN_FILE ) . 'languages' ); // If this is the frontpage or homepage, preview URLs won't work. Use the live URL. - if ( (int) get_option( 'page_on_front' ) === $post_id || (int) get_option( 'page_for_posts' ) === $post_id ) { + // When show_on_front=posts and page_for_posts is not set (0), neither WP option + // will match — allow extensions to flag the current post as the latest-posts + // homepage so we use get_home_url() for the scan rather than a preview link. + $is_latest_posts_home = 'posts' === get_option( 'show_on_front' ) + && 0 === (int) get_option( 'page_for_posts' ) + && apply_filters( 'edac_filter_post_is_latest_posts_home', false, $post_id ); + + if ( $is_latest_posts_home ) { + $scan_url = add_query_arg( 'edac_pageScanner', 1, trailingslashit( get_home_url() ) ); + } elseif ( (int) get_option( 'page_on_front' ) === $post_id || (int) get_option( 'page_for_posts' ) === $post_id ) { $scan_url = add_query_arg( 'edac_pageScanner', 1, get_permalink( $post_id ) ); } else { $post_view_link = apply_filters( diff --git a/includes/classes/class-enqueue-frontend.php b/includes/classes/class-enqueue-frontend.php index 64f349526..9a2ffc8c8 100644 --- a/includes/classes/class-enqueue-frontend.php +++ b/includes/classes/class-enqueue-frontend.php @@ -102,7 +102,16 @@ public static function maybe_enqueue_frontend_highlighter() { // Don't load on the frontend if we don't have a post to work with. global $post; - $post_id = apply_filters( 'edac_filter_frontend_highlight_post_id', is_object( $post ) ? $post->ID : null ); + + // When the homepage displays latest posts (show_on_front=posts), WordPress populates + // the global $post with the first post from the blog query rather than a page object. + // Passing that post's ID would attribute homepage scan results to the wrong entry. + // Pass null instead so the filter can supply a virtual-page ID (Pro) or bail cleanly. + $default_post_id = ( is_home() && is_front_page() && 'posts' === get_option( 'show_on_front' ) ) + ? null + : ( is_object( $post ) ? $post->ID : null ); + + $post_id = apply_filters( 'edac_filter_frontend_highlight_post_id', $default_post_id ); if ( null === $post_id ) { return; diff --git a/tests/phpunit/Admin/EnqueueAdminTest.php b/tests/phpunit/Admin/EnqueueAdminTest.php index 946529035..ce0002c44 100644 --- a/tests/phpunit/Admin/EnqueueAdminTest.php +++ b/tests/phpunit/Admin/EnqueueAdminTest.php @@ -369,6 +369,68 @@ public function testSrOnlyFormatDoesNotEnqueueOnOtherAdminPages() { } + /** + * Test that edac_filter_admin_post_id overrides the post ID localized into edac_script_vars. + * + * @return void + */ + public function testAdminPostIdFilterOverridesLocalizedPostId() { + global $post, $pagenow, $wp_scripts; + + $original_post = $this->factory()->post->create_and_get(); + $alternate_post = $this->factory()->post->create_and_get(); + $post = $original_post; + $pagenow = 'post.php'; + + $filter_callback = static function () use ( $alternate_post ) { + return $alternate_post->ID; + }; + add_filter( 'edac_filter_admin_post_id', $filter_callback ); + + $this->enqueue_admin::maybe_enqueue_admin_and_editor_app_scripts(); + + remove_filter( 'edac_filter_admin_post_id', $filter_callback ); + + $localized_data = $wp_scripts->get_data( 'edac', 'data' ); + $this->assertStringContainsString( (string) $alternate_post->ID, $localized_data ); + } + + /** + * Test that edac_filter_post_is_latest_posts_home causes the scan URL to use the home URL. + * + * When show_on_front=posts and page_for_posts=0, the standard WP options cannot identify a + * virtual homepage post. The filter lets extensions signal this so we use get_home_url() + * for the scanner iframe rather than an invalid preview URL. + * + * @return void + */ + public function testScanUrlUsesHomeUrlWhenLatestPostsHomeFilterReturnsTrue() { + global $post, $pagenow, $wp_scripts; + + $post = $this->factory()->post->create_and_get( [ 'post_type' => 'page' ] ); + $pagenow = 'post.php'; + + update_option( 'show_on_front', 'posts' ); + update_option( 'page_for_posts', 0 ); + + $filter_callback = static function () { + return true; + }; + add_filter( 'edac_filter_post_is_latest_posts_home', $filter_callback ); + + $this->enqueue_admin::maybe_enqueue_admin_and_editor_app_scripts(); + + remove_filter( 'edac_filter_post_is_latest_posts_home', $filter_callback ); + delete_option( 'show_on_front' ); + delete_option( 'page_for_posts' ); + + $localized_data = $wp_scripts->get_data( 'edac-editor-app', 'data' ); + $this->assertStringContainsString( 'edac_pageScanner', $localized_data ); + $this->assertStringNotContainsString( 'preview=true', $localized_data ); + // The scan URL should be based on the home URL, not a preview link. + $this->assertStringContainsString( trailingslashit( get_home_url() ), $localized_data ); + } + /** * Helper to set a mock current screen with block editor context. * diff --git a/tests/phpunit/includes/classes/EnqueueFrontendTest.php b/tests/phpunit/includes/classes/EnqueueFrontendTest.php index c28c34c23..0f2378102 100644 --- a/tests/phpunit/includes/classes/EnqueueFrontendTest.php +++ b/tests/phpunit/includes/classes/EnqueueFrontendTest.php @@ -73,6 +73,58 @@ public function testScannerBundleUrlIncludesVersionQueryString(): void { $this->assertStringContainsString( 'ver=' . EDAC_VERSION, $localized_data ); } + /** + * Highlighter must NOT load on the "latest posts" homepage when no filter overrides the ID. + * + * When show_on_front=posts, the global $post is the first blog post from the main query, not + * the homepage itself. The fix passes null to the filter so the free plugin bails gracefully. + */ + public function testFrontendHighlighterDoesNotLoadOnLatestPostsHomepage(): void { + $admin_id = $this->factory()->user->create( [ 'role' => 'administrator' ] ); + wp_set_current_user( $admin_id ); + + // Create a post so the main query has results. + $this->factory()->post->create_and_get( [ 'post_type' => 'post' ] ); + + update_option( 'show_on_front', 'posts' ); + + // Simulate visiting the homepage so is_home() / is_front_page() return true. + $this->go_to( '/' ); + + Enqueue_Frontend::maybe_enqueue_frontend_highlighter(); + + $this->assertFalse( wp_script_is( 'edac-frontend-highlighter-app', 'enqueued' ) ); + + delete_option( 'show_on_front' ); + } + + /** + * A filter on edac_filter_frontend_highlight_post_id can enable the highlighter on the + * "latest posts" homepage by supplying a valid post ID (e.g. a Pro virtual-page ID). + */ + public function testFrontendHighlighterLoadsOnLatestPostsHomepageWhenFilterProvidesId(): void { + $admin_id = $this->factory()->user->create( [ 'role' => 'administrator' ] ); + wp_set_current_user( $admin_id ); + + $post = $this->factory()->post->create_and_get( [ 'post_type' => 'post' ] ); + + update_option( 'show_on_front', 'posts' ); + + $this->go_to( '/' ); + + $filter_callback = static function () use ( $post ) { + return $post->ID; + }; + $this->added_filters['edac_filter_frontend_highlight_post_id'] = $filter_callback; + add_filter( 'edac_filter_frontend_highlight_post_id', $filter_callback ); + + Enqueue_Frontend::maybe_enqueue_frontend_highlighter(); + + $this->assertTrue( wp_script_is( 'edac-frontend-highlighter-app', 'enqueued' ) ); + + delete_option( 'show_on_front' ); + } + /** * Ensure the highlighter uses the filtered post ID when determining scannable post types. */ From f3637f6d7738c847add31a3433697d065e8d4e30 Mon Sep 17 00:00:00 2001 From: Steve Jones Date: Sun, 31 May 2026 22:20:38 -0400 Subject: [PATCH 07/26] Address code review feedback on PRO-726 homepage scan fix - Remove redundant show_on_front check in frontend highlighter; rely on is_home() && is_front_page() to cover both the explicit "latest posts" setting and the fallback where show_on_front=page with no page_on_front - Broaden $is_latest_posts_home condition in admin enqueue to include the same fallback case (show_on_front=page but page_on_front is empty) - Recompute $active from the filtered $post_id's post type so extensions that override the post ID via edac_filter_admin_post_id get a correct active flag rather than one based on the pre-filter global $post - Add tests covering the fallback homepage case and the $active recomputation Co-Authored-By: Claude Sonnet 4.6 --- admin/class-enqueue-admin.php | 16 +++-- includes/classes/class-enqueue-frontend.php | 12 ++-- tests/phpunit/Admin/EnqueueAdminTest.php | 75 +++++++++++++++++++++ 3 files changed, 92 insertions(+), 11 deletions(-) diff --git a/admin/class-enqueue-admin.php b/admin/class-enqueue-admin.php index 4f81bfa1a..878041f80 100644 --- a/admin/class-enqueue-admin.php +++ b/admin/class-enqueue-admin.php @@ -106,7 +106,10 @@ public static function maybe_enqueue_admin_and_editor_app_scripts() { if ( 'post.php' === $pagenow || 'post-new.php' === $pagenow ) { // Is this posttype setup to be checked? - $active = $is_scannable_post; + // Re-evaluate based on the filtered $post_id, which may have been overridden + // by edac_filter_admin_post_id (e.g. a Pro virtual-page ID for the homepage). + $filtered_post_type = $post_id ? get_post_type( $post_id ) : false; + $active = $filtered_post_type && is_array( $post_types ) && in_array( $filtered_post_type, $post_types, true ); $pro = defined( 'EDACP_VERSION' ) && EDAC_KEY_VALID; @@ -120,11 +123,12 @@ public static function maybe_enqueue_admin_and_editor_app_scripts() { wp_set_script_translations( 'edac-editor-app', 'accessibility-checker', plugin_dir_path( EDAC_PLUGIN_FILE ) . 'languages' ); // If this is the frontpage or homepage, preview URLs won't work. Use the live URL. - // When show_on_front=posts and page_for_posts is not set (0), neither WP option - // will match — allow extensions to flag the current post as the latest-posts - // homepage so we use get_home_url() for the scan rather than a preview link. - $is_latest_posts_home = 'posts' === get_option( 'show_on_front' ) - && 0 === (int) get_option( 'page_for_posts' ) + // Cover both the explicit "latest posts" setting (show_on_front=posts) and the + // fallback where show_on_front=page but no static front page is configured. + // Allow extensions to flag the current post as the latest-posts homepage so we + // use get_home_url() for the scan rather than a preview link. + $show_on_front = get_option( 'show_on_front', 'posts' ); + $is_latest_posts_home = ( 'posts' === $show_on_front || ( 'page' === $show_on_front && ! get_option( 'page_on_front' ) ) ) && apply_filters( 'edac_filter_post_is_latest_posts_home', false, $post_id ); if ( $is_latest_posts_home ) { diff --git a/includes/classes/class-enqueue-frontend.php b/includes/classes/class-enqueue-frontend.php index 9a2ffc8c8..8f919a44a 100644 --- a/includes/classes/class-enqueue-frontend.php +++ b/includes/classes/class-enqueue-frontend.php @@ -103,11 +103,13 @@ public static function maybe_enqueue_frontend_highlighter() { // Don't load on the frontend if we don't have a post to work with. global $post; - // When the homepage displays latest posts (show_on_front=posts), WordPress populates - // the global $post with the first post from the blog query rather than a page object. - // Passing that post's ID would attribute homepage scan results to the wrong entry. - // Pass null instead so the filter can supply a virtual-page ID (Pro) or bail cleanly. - $default_post_id = ( is_home() && is_front_page() && 'posts' === get_option( 'show_on_front' ) ) + // When the homepage displays latest posts, WordPress populates the global $post with the + // first post from the blog query rather than a page object. This happens when + // show_on_front=posts, but also in the fallback case where show_on_front=page but no + // valid static front page is configured. is_home() && is_front_page() covers both. + // Passing the first post's ID would attribute homepage scan results to the wrong entry, + // so pass null and let the filter supply a virtual-page ID (Pro) or bail cleanly. + $default_post_id = ( is_home() && is_front_page() ) ? null : ( is_object( $post ) ? $post->ID : null ); diff --git a/tests/phpunit/Admin/EnqueueAdminTest.php b/tests/phpunit/Admin/EnqueueAdminTest.php index ce0002c44..142a26a0d 100644 --- a/tests/phpunit/Admin/EnqueueAdminTest.php +++ b/tests/phpunit/Admin/EnqueueAdminTest.php @@ -431,6 +431,81 @@ public function testScanUrlUsesHomeUrlWhenLatestPostsHomeFilterReturnsTrue() { $this->assertStringContainsString( trailingslashit( get_home_url() ), $localized_data ); } + /** + * Test that edac_filter_post_is_latest_posts_home causes the scan URL to use the home URL + * when show_on_front=page but no static front page is configured (fallback case). + * + * WordPress falls back to showing latest posts when show_on_front=page but page_on_front + * is empty. The $is_latest_posts_home condition must cover this case so extensions can + * still signal that get_home_url() should be used as the scan URL. + * + * @return void + */ + public function testScanUrlUsesHomeUrlWhenShowOnFrontIsPageWithNoFrontPageConfigured() { + global $post, $pagenow, $wp_scripts; + + $post = $this->factory()->post->create_and_get( [ 'post_type' => 'page' ] ); + $pagenow = 'post.php'; + + update_option( 'show_on_front', 'page' ); + delete_option( 'page_on_front' ); // No static front page configured — WP falls back to latest posts. + + $filter_callback = static function () { + return true; + }; + add_filter( 'edac_filter_post_is_latest_posts_home', $filter_callback ); + + $this->enqueue_admin::maybe_enqueue_admin_and_editor_app_scripts(); + + remove_filter( 'edac_filter_post_is_latest_posts_home', $filter_callback ); + delete_option( 'show_on_front' ); + + $localized_data = $wp_scripts->get_data( 'edac-editor-app', 'data' ); + $this->assertStringContainsString( 'edac_pageScanner', $localized_data ); + $this->assertStringNotContainsString( 'preview=true', $localized_data ); + // The scan URL should be based on the home URL, not a preview link. + $this->assertStringContainsString( trailingslashit( get_home_url() ), $localized_data ); + } + + /** + * Test that the 'active' flag in edac_editor_app reflects the filtered post ID's post type. + * + * Before Fix 3, $active was set to $is_scannable_post which was computed from the global + * $post before the edac_filter_admin_post_id filter ran. If the filter returns a different + * post ID whose type is not scannable, $active must be false so the scanner doesn't run. + * + * @return void + */ + public function testActiveReflectsFilteredPostIdPostType() { + global $post, $pagenow, $wp_scripts; + + // Global $post is a 'post' type (scannable under current option). + $scannable_post = $this->factory()->post->create_and_get( [ 'post_type' => 'post' ] ); + // The filter will return a 'page' ID; make 'page' non-scannable for this test. + $non_scannable_post = $this->factory()->post->create_and_get( [ 'post_type' => 'page' ] ); + $post = $scannable_post; + $pagenow = 'post.php'; + + // Restrict scannable types to 'post' only so 'page' becomes non-scannable. + update_option( 'edac_post_types', [ 'post' ] ); + + $filter_callback = static function () use ( $non_scannable_post ) { + return $non_scannable_post->ID; + }; + add_filter( 'edac_filter_admin_post_id', $filter_callback ); + + $this->enqueue_admin::maybe_enqueue_admin_and_editor_app_scripts(); + + remove_filter( 'edac_filter_admin_post_id', $filter_callback ); + // Restore original scannable post types. + update_option( 'edac_post_types', [ 'post', 'page' ] ); + + $localized_data = $wp_scripts->get_data( 'edac-editor-app', 'data' ); + $this->assertNotEmpty( $localized_data ); + // $active must be false because the filtered post type ('page') is not scannable. + $this->assertMatchesRegularExpression( '/"active"\s*:\s*false/', $localized_data ); + } + /** * Helper to set a mock current screen with block editor context. * From 6f8f7dfcaa0eb006f50adc32a98dff037bc70179 Mon Sep 17 00:00:00 2001 From: Steve Jones Date: Sun, 31 May 2026 22:26:54 -0400 Subject: [PATCH 08/26] Fix test assertion for wp_localize_script boolean serialization wp_localize_script serializes PHP false as an empty string ("") rather than JSON false in current WP versions, so match either form in the regex to keep the test passing across WP releases. Co-Authored-By: Claude Sonnet 4.6 --- tests/phpunit/Admin/EnqueueAdminTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/phpunit/Admin/EnqueueAdminTest.php b/tests/phpunit/Admin/EnqueueAdminTest.php index 142a26a0d..08b844d66 100644 --- a/tests/phpunit/Admin/EnqueueAdminTest.php +++ b/tests/phpunit/Admin/EnqueueAdminTest.php @@ -503,7 +503,9 @@ public function testActiveReflectsFilteredPostIdPostType() { $localized_data = $wp_scripts->get_data( 'edac-editor-app', 'data' ); $this->assertNotEmpty( $localized_data ); // $active must be false because the filtered post type ('page') is not scannable. - $this->assertMatchesRegularExpression( '/"active"\s*:\s*false/', $localized_data ); + // wp_localize_script serializes PHP false as "" (empty string) in older WP versions + // and may serialize it as JSON false in newer ones — accept both forms. + $this->assertMatchesRegularExpression( '/"active"\s*:\s*(false|"")/', $localized_data ); } /** From 1dd16ab543a28a414b18423458bb257fce8f372d Mon Sep 17 00:00:00 2001 From: Steve Jones Date: Sun, 31 May 2026 22:31:35 -0400 Subject: [PATCH 09/26] Fix home URL assertions for WP < 6.9 JSON slash escaping WP < 6.9 escapes forward slashes as \/ in json_encode output, so asserting the literal home URL string fails on those builds. Apply the same version-conditional str_replace pattern already used by the existing permalink tests. Co-Authored-By: Claude Sonnet 4.6 --- tests/phpunit/Admin/EnqueueAdminTest.php | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/tests/phpunit/Admin/EnqueueAdminTest.php b/tests/phpunit/Admin/EnqueueAdminTest.php index 08b844d66..a01883226 100644 --- a/tests/phpunit/Admin/EnqueueAdminTest.php +++ b/tests/phpunit/Admin/EnqueueAdminTest.php @@ -166,7 +166,7 @@ public function testScanUrlUsesPermalinkForFrontpage() { $this->assertStringContainsString( 'edac_pageScanner', $localized_data ); // In WP 6.9 there were changes to the flags passed to wp_json_encode() that made slashes no longer get escaped by default. // We should check for both possibilities here to ensure compatibility across versions. - // See: https://github.com/WordPress/wordpress-develop/pull/9557 for more details. + // See: https://github.com/WordPress/wordpress-develop/pull/9557. for more details. if ( version_compare( get_bloginfo( 'version' ), '6.9', '>=' ) ) { $this->assertStringContainsString( esc_url_raw( get_permalink( $post->ID ) ), $localized_data ); } else { @@ -198,7 +198,7 @@ public function testScanUrlUsesPermalinkForPostsPage() { $this->assertStringContainsString( 'edac_pageScanner', $localized_data ); // In WP 6.9 there were changes to the flags passed to wp_json_encode() that made slashes no longer get escaped by default. // We should check for both possibilities here to ensure compatibility across versions. - // See: https://github.com/WordPress/wordpress-develop/pull/9557 for more details. + // See: https://github.com/WordPress/wordpress-develop/pull/9557. for more details. if ( version_compare( get_bloginfo( 'version' ), '6.9', '>=' ) ) { $this->assertStringContainsString( esc_url_raw( get_permalink( $post->ID ) ), $localized_data ); } else { @@ -428,7 +428,13 @@ public function testScanUrlUsesHomeUrlWhenLatestPostsHomeFilterReturnsTrue() { $this->assertStringContainsString( 'edac_pageScanner', $localized_data ); $this->assertStringNotContainsString( 'preview=true', $localized_data ); // The scan URL should be based on the home URL, not a preview link. - $this->assertStringContainsString( trailingslashit( get_home_url() ), $localized_data ); + // In WP 6.9 forward slashes are no longer escaped in json_encode output; handle both forms. + // See: https://github.com/WordPress/wordpress-develop/pull/9557. + $expected_home = esc_url_raw( trailingslashit( get_home_url() ) ); + if ( version_compare( get_bloginfo( 'version' ), '6.9', '<' ) ) { + $expected_home = str_replace( '/', '\\/', $expected_home ); + } + $this->assertStringContainsString( $expected_home, $localized_data ); } /** @@ -464,7 +470,13 @@ public function testScanUrlUsesHomeUrlWhenShowOnFrontIsPageWithNoFrontPageConfig $this->assertStringContainsString( 'edac_pageScanner', $localized_data ); $this->assertStringNotContainsString( 'preview=true', $localized_data ); // The scan URL should be based on the home URL, not a preview link. - $this->assertStringContainsString( trailingslashit( get_home_url() ), $localized_data ); + // In WP 6.9 forward slashes are no longer escaped in json_encode output; handle both forms. + // See: https://github.com/WordPress/wordpress-develop/pull/9557. + $expected_home = esc_url_raw( trailingslashit( get_home_url() ) ); + if ( version_compare( get_bloginfo( 'version' ), '6.9', '<' ) ) { + $expected_home = str_replace( '/', '\\/', $expected_home ); + } + $this->assertStringContainsString( $expected_home, $localized_data ); } /** From 4771d9e5758670140c2cf214ae5c2f243bc07e5a Mon Sep 17 00:00:00 2001 From: Steve Jones Date: Sun, 31 May 2026 23:45:29 -0400 Subject: [PATCH 10/26] Address code review feedback: use trimmedHref for empty check and add whitespace-only href test Co-Authored-By: Claude Sonnet 4.6 --- src/pageScanner/checks/link-has-valid-href-or-role.js | 2 +- tests/jest/rules/linkImproper.test.js | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pageScanner/checks/link-has-valid-href-or-role.js b/src/pageScanner/checks/link-has-valid-href-or-role.js index 0b25e253f..146739832 100644 --- a/src/pageScanner/checks/link-has-valid-href-or-role.js +++ b/src/pageScanner/checks/link-has-valid-href-or-role.js @@ -34,7 +34,7 @@ export default { const normalizedHref = trimmedHref.toLowerCase(); // Fail if href is missing, just '#', or contains invalid protocols - if ( ! href || + if ( ! trimmedHref || trimmedHref === '#' || normalizedHref.startsWith( 'javascript:' ) || normalizedHref.startsWith( 'data:' ) || diff --git a/tests/jest/rules/linkImproper.test.js b/tests/jest/rules/linkImproper.test.js index 33fb4e22d..2f68f5d65 100644 --- a/tests/jest/rules/linkImproper.test.js +++ b/tests/jest/rules/linkImproper.test.js @@ -44,6 +44,11 @@ describe( 'Link Improper Rule', () => { html: 'Bad practice', shouldPass: false, }, + { + name: 'Fails when anchor href contains only whitespace', + html: 'Whitespace link', + shouldPass: false, + }, { name: 'Fails when anchor has malformed URL', html: 'Invalid URL', From 79f4f9c245f1973c963c6ab0ab7588a63a78d017 Mon Sep 17 00:00:00 2001 From: Steve Jones Date: Sun, 31 May 2026 23:53:49 -0400 Subject: [PATCH 11/26] Update composer.lock to pull in PHPUnit 9.6.34 (fixes CVE-2026-24765) Co-Authored-By: Claude Sonnet 4.6 --- composer.lock | 379 ++++++++++++++++++++++++++++---------------------- 1 file changed, 215 insertions(+), 164 deletions(-) diff --git a/composer.lock b/composer.lock index 27f65ffd2..2b9648a10 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "61b1718f886504a4958c41f3d46bb148", + "content-hash": "dc43f50218d9d0f2eca30fc534d2d330", "packages": [ { "name": "composer/installers", @@ -215,16 +215,16 @@ "packages-dev": [ { "name": "antecedent/patchwork", - "version": "2.2.1", + "version": "2.2.3", "source": { "type": "git", "url": "https://github.com/antecedent/patchwork.git", - "reference": "1bf183a3e1bd094f231a2128b9ecc5363c269245" + "reference": "8b6b235f405af175259c8f56aea5fc23ab9f03ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antecedent/patchwork/zipball/1bf183a3e1bd094f231a2128b9ecc5363c269245", - "reference": "1bf183a3e1bd094f231a2128b9ecc5363c269245", + "url": "https://api.github.com/repos/antecedent/patchwork/zipball/8b6b235f405af175259c8f56aea5fc23ab9f03ce", + "reference": "8b6b235f405af175259c8f56aea5fc23ab9f03ce", "shasum": "" }, "require": { @@ -257,9 +257,9 @@ ], "support": { "issues": "https://github.com/antecedent/patchwork/issues", - "source": "https://github.com/antecedent/patchwork/tree/2.2.1" + "source": "https://github.com/antecedent/patchwork/tree/2.2.3" }, - "time": "2024-12-11T10:19:54+00:00" + "time": "2025-09-17T09:00:56+00:00" }, { "name": "automattic/vipwpcs", @@ -317,27 +317,27 @@ }, { "name": "brain/monkey", - "version": "2.6.2", + "version": "2.7.0", "source": { "type": "git", "url": "https://github.com/Brain-WP/BrainMonkey.git", - "reference": "d95a9d895352c30f47604ad1b825ab8fa9d1a373" + "reference": "ea3aeb3d559ba3c0930b3f4d210b665a4c044d83" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Brain-WP/BrainMonkey/zipball/d95a9d895352c30f47604ad1b825ab8fa9d1a373", - "reference": "d95a9d895352c30f47604ad1b825ab8fa9d1a373", + "url": "https://api.github.com/repos/Brain-WP/BrainMonkey/zipball/ea3aeb3d559ba3c0930b3f4d210b665a4c044d83", + "reference": "ea3aeb3d559ba3c0930b3f4d210b665a4c044d83", "shasum": "" }, "require": { "antecedent/patchwork": "^2.1.17", - "mockery/mockery": "^1.3.5 || ^1.4.4", + "mockery/mockery": "~1.3.6 || ~1.4.4 || ~1.5.1 || ^1.6.10", "php": ">=5.6.0" }, "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.1", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0.0", "phpcompatibility/php-compatibility": "^9.3.0", - "phpunit/phpunit": "^5.7.26 || ^6.0 || ^7.0 || >=8.0 <8.5.12 || ^8.5.14 || ^9.0" + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.49 || ^9.6.30" }, "type": "library", "extra": { @@ -383,7 +383,7 @@ "issues": "https://github.com/Brain-WP/BrainMonkey/issues", "source": "https://github.com/Brain-WP/BrainMonkey" }, - "time": "2024-08-29T20:15:04+00:00" + "time": "2026-02-05T09:22:14+00:00" }, { "name": "dealerdirect/phpcodesniffer-composer-installer", @@ -666,16 +666,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.13.1", + "version": "1.13.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c" + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c", - "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { @@ -714,7 +714,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { @@ -722,20 +722,20 @@ "type": "tidelift" } ], - "time": "2025-04-29T12:36:36+00:00" + "time": "2025-08-01T08:46:24+00:00" }, { "name": "nikic/php-parser", - "version": "v5.5.0", + "version": "v5.7.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "ae59794362fe85e051a58ad36b289443f57be7a9" + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9", - "reference": "ae59794362fe85e051a58ad36b289443f57be7a9", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", "shasum": "" }, "require": { @@ -754,7 +754,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -778,9 +778,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" }, - "time": "2025-05-31T08:24:38+00:00" + "time": "2025-12-06T11:56:16+00:00" }, { "name": "phar-io/manifest", @@ -963,16 +963,16 @@ }, { "name": "php-stubs/wordpress-stubs", - "version": "v6.8.1", + "version": "v6.9.1", "source": { "type": "git", "url": "https://github.com/php-stubs/wordpress-stubs.git", - "reference": "92e444847d94f7c30f88c60004648f507688acd5" + "reference": "f12220f303e0d7c0844c0e5e957b0c3cee48d2f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/92e444847d94f7c30f88c60004648f507688acd5", - "reference": "92e444847d94f7c30f88c60004648f507688acd5", + "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/f12220f303e0d7c0844c0e5e957b0c3cee48d2f7", + "reference": "f12220f303e0d7c0844c0e5e957b0c3cee48d2f7", "shasum": "" }, "conflict": { @@ -980,12 +980,13 @@ }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "^1.0", - "nikic/php-parser": "^5.4", + "nikic/php-parser": "^5.5", "php": "^7.4 || ^8.0", "php-stubs/generator": "^0.8.3", - "phpdocumentor/reflection-docblock": "^5.4.1", + "phpdocumentor/reflection-docblock": "^6.0", "phpstan/phpstan": "^2.1", "phpunit/phpunit": "^9.5", + "symfony/polyfill-php80": "*", "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^1.1.1", "wp-coding-standards/wpcs": "3.1.0 as 2.3.0" }, @@ -1008,9 +1009,9 @@ ], "support": { "issues": "https://github.com/php-stubs/wordpress-stubs/issues", - "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.8.1" + "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.9.1" }, - "time": "2025-05-02T12:33:34+00:00" + "time": "2026-02-03T19:29:21+00:00" }, { "name": "phpcompatibility/php-compatibility", @@ -1076,16 +1077,16 @@ }, { "name": "phpcompatibility/phpcompatibility-paragonie", - "version": "1.3.3", + "version": "1.3.4", "source": { "type": "git", "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git", - "reference": "293975b465e0e709b571cbf0c957c6c0a7b9a2ac" + "reference": "244d7b04fc4bc2117c15f5abe23eb933b5f02bbf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/293975b465e0e709b571cbf0c957c6c0a7b9a2ac", - "reference": "293975b465e0e709b571cbf0c957c6c0a7b9a2ac", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/244d7b04fc4bc2117c15f5abe23eb933b5f02bbf", + "reference": "244d7b04fc4bc2117c15f5abe23eb933b5f02bbf", "shasum": "" }, "require": { @@ -1142,22 +1143,26 @@ { "url": "https://opencollective.com/php_codesniffer", "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcompatibility", + "type": "thanks_dev" } ], - "time": "2024-04-24T21:30:46+00:00" + "time": "2025-09-19T17:43:28+00:00" }, { "name": "phpcompatibility/phpcompatibility-wp", - "version": "2.1.7", + "version": "2.1.8", "source": { "type": "git", "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git", - "reference": "5bfbbfbabb3df2b9a83e601de9153e4a7111962c" + "reference": "7c8d18b4d90dac9e86b0869a608fa09158e168fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/5bfbbfbabb3df2b9a83e601de9153e4a7111962c", - "reference": "5bfbbfbabb3df2b9a83e601de9153e4a7111962c", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/7c8d18b4d90dac9e86b0869a608fa09158e168fa", + "reference": "7c8d18b4d90dac9e86b0869a608fa09158e168fa", "shasum": "" }, "require": { @@ -1219,31 +1224,31 @@ "type": "thanks_dev" } ], - "time": "2025-05-12T16:38:37+00:00" + "time": "2025-10-18T00:05:59+00:00" }, { "name": "phpcsstandards/phpcsextra", - "version": "1.4.0", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHPCSExtra.git", - "reference": "fa4b8d051e278072928e32d817456a7fdb57b6ca" + "reference": "b598aa890815b8df16363271b659d73280129101" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/fa4b8d051e278072928e32d817456a7fdb57b6ca", - "reference": "fa4b8d051e278072928e32d817456a7fdb57b6ca", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/b598aa890815b8df16363271b659d73280129101", + "reference": "b598aa890815b8df16363271b659d73280129101", "shasum": "" }, "require": { "php": ">=5.4", - "phpcsstandards/phpcsutils": "^1.1.0", - "squizlabs/php_codesniffer": "^3.13.0 || ^4.0" + "phpcsstandards/phpcsutils": "^1.2.0", + "squizlabs/php_codesniffer": "^3.13.5 || ^4.0.1" }, "require-dev": { "php-parallel-lint/php-console-highlighter": "^1.0", "php-parallel-lint/php-parallel-lint": "^1.4.0", - "phpcsstandards/phpcsdevcs": "^1.1.6", + "phpcsstandards/phpcsdevcs": "^1.2.0", "phpcsstandards/phpcsdevtools": "^1.2.1", "phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" }, @@ -1301,32 +1306,32 @@ "type": "thanks_dev" } ], - "time": "2025-06-14T07:40:39+00:00" + "time": "2025-11-12T23:06:57+00:00" }, { "name": "phpcsstandards/phpcsutils", - "version": "1.1.0", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHPCSUtils.git", - "reference": "65355670ac17c34cd235cf9d3ceae1b9252c4dad" + "reference": "c216317e96c8b3f5932808f9b0f1f7a14e3bbf55" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/65355670ac17c34cd235cf9d3ceae1b9252c4dad", - "reference": "65355670ac17c34cd235cf9d3ceae1b9252c4dad", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/c216317e96c8b3f5932808f9b0f1f7a14e3bbf55", + "reference": "c216317e96c8b3f5932808f9b0f1f7a14e3bbf55", "shasum": "" }, "require": { "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0", "php": ">=5.4", - "squizlabs/php_codesniffer": "^3.13.0 || ^4.0" + "squizlabs/php_codesniffer": "^3.13.5 || ^4.0.1" }, "require-dev": { "ext-filter": "*", "php-parallel-lint/php-console-highlighter": "^1.0", "php-parallel-lint/php-parallel-lint": "^1.4.0", - "phpcsstandards/phpcsdevcs": "^1.1.6", + "phpcsstandards/phpcsdevcs": "^1.2.0", "yoast/phpunit-polyfills": "^1.1.0 || ^2.0.0 || ^3.0.0" }, "type": "phpcodesniffer-standard", @@ -1394,7 +1399,7 @@ "type": "thanks_dev" } ], - "time": "2025-06-12T04:32:33+00:00" + "time": "2025-12-08T14:27:58+00:00" }, { "name": "phpstan/extension-installer", @@ -1446,16 +1451,11 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.27", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "3a6e423c076ab39dfedc307e2ac627ef579db162" - }, + "version": "1.12.33", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/3a6e423c076ab39dfedc307e2ac627ef579db162", - "reference": "3a6e423c076ab39dfedc307e2ac627ef579db162", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/37982d6fc7cbb746dda7773530cda557cdf119e1", + "reference": "37982d6fc7cbb746dda7773530cda557cdf119e1", "shasum": "" }, "require": { @@ -1500,7 +1500,7 @@ "type": "github" } ], - "time": "2025-05-21T20:51:45+00:00" + "time": "2026-02-28T20:30:03+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1823,16 +1823,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.23", + "version": "9.6.34", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95" + "reference": "b36f02317466907a230d3aa1d34467041271ef4a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", - "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b36f02317466907a230d3aa1d34467041271ef4a", + "reference": "b36f02317466907a230d3aa1d34467041271ef4a", "shasum": "" }, "require": { @@ -1843,7 +1843,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.13.1", + "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=7.3", @@ -1854,11 +1854,11 @@ "phpunit/php-timer": "^5.0.3", "sebastian/cli-parser": "^1.0.2", "sebastian/code-unit": "^1.0.8", - "sebastian/comparator": "^4.0.8", + "sebastian/comparator": "^4.0.10", "sebastian/diff": "^4.0.6", "sebastian/environment": "^5.1.5", - "sebastian/exporter": "^4.0.6", - "sebastian/global-state": "^5.0.7", + "sebastian/exporter": "^4.0.8", + "sebastian/global-state": "^5.0.8", "sebastian/object-enumerator": "^4.0.4", "sebastian/resource-operations": "^3.0.4", "sebastian/type": "^3.2.1", @@ -1906,7 +1906,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.23" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.34" }, "funding": [ { @@ -1930,7 +1930,7 @@ "type": "tidelift" } ], - "time": "2025-05-02T06:40:34+00:00" + "time": "2026-01-27T05:45:00+00:00" }, { "name": "sebastian/cli-parser", @@ -2101,16 +2101,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.8", + "version": "4.0.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e4df00b9b3571187db2831ae9aada2c6efbd715d", + "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d", "shasum": "" }, "require": { @@ -2163,15 +2163,27 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.10" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2026-01-24T09:22:56+00:00" }, { "name": "sebastian/complexity", @@ -2361,16 +2373,16 @@ }, { "name": "sebastian/exporter", - "version": "4.0.6", + "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", - "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/14c6ba52f95a36c3d27c835d65efc7123c446e8c", + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c", "shasum": "" }, "require": { @@ -2426,28 +2438,40 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "time": "2024-03-02T06:33:00+00:00" + "time": "2025-09-24T06:03:27+00:00" }, { "name": "sebastian/global-state", - "version": "5.0.7", + "version": "5.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", - "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b6781316bdcd28260904e7cc18ec983d0d2ef4f6", + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6", "shasum": "" }, "require": { @@ -2490,15 +2514,27 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" } ], - "time": "2024-03-02T06:35:11+00:00" + "time": "2025-08-10T07:10:35+00:00" }, { "name": "sebastian/lines-of-code", @@ -2671,16 +2707,16 @@ }, { "name": "sebastian/recursion-context", - "version": "4.0.5", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/539c6691e0623af6dc6f9c20384c120f963465a0", + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0", "shasum": "" }, "require": { @@ -2722,15 +2758,27 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.6" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2023-02-03T06:07:39+00:00" + "time": "2025-08-10T06:57:39+00:00" }, { "name": "sebastian/resource-operations", @@ -2897,28 +2945,27 @@ }, { "name": "sirbrillig/phpcs-variable-analysis", - "version": "v2.12.0", + "version": "v2.13.0", "source": { "type": "git", "url": "https://github.com/sirbrillig/phpcs-variable-analysis.git", - "reference": "4debf5383d9ade705e0a25121f16c3fecaf433a7" + "reference": "a15e970b8a0bf64cfa5e86d941f5e6b08855f369" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/4debf5383d9ade705e0a25121f16c3fecaf433a7", - "reference": "4debf5383d9ade705e0a25121f16c3fecaf433a7", + "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/a15e970b8a0bf64cfa5e86d941f5e6b08855f369", + "reference": "a15e970b8a0bf64cfa5e86d941f5e6b08855f369", "shasum": "" }, "require": { "php": ">=5.4.0", - "squizlabs/php_codesniffer": "^3.5.6" + "squizlabs/php_codesniffer": "^3.5.7 || ^4.0.0" }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || ^1.0", - "phpcsstandards/phpcsdevcs": "^1.1", - "phpstan/phpstan": "^1.7", + "phpstan/phpstan": "^1.7 || ^2.0", "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.5 || ^7.0 || ^8.0 || ^9.0 || ^10.5.32 || ^11.3.3", - "vimeo/psalm": "^0.2 || ^0.3 || ^1.1 || ^4.24 || ^5.0" + "vimeo/psalm": "^0.2 || ^0.3 || ^1.1 || ^4.24 || ^5.0 || ^6.0 || ^7.0" }, "type": "phpcodesniffer-standard", "autoload": { @@ -2950,20 +2997,20 @@ "source": "https://github.com/sirbrillig/phpcs-variable-analysis", "wiki": "https://github.com/sirbrillig/phpcs-variable-analysis/wiki" }, - "time": "2025-03-17T16:17:38+00:00" + "time": "2025-09-30T22:22:48+00:00" }, { "name": "squizlabs/php_codesniffer", - "version": "3.13.2", + "version": "3.13.5", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "5b5e3821314f947dd040c70f7992a64eac89025c" + "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/5b5e3821314f947dd040c70f7992a64eac89025c", - "reference": "5b5e3821314f947dd040c70f7992a64eac89025c", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0ca86845ce43291e8f5692c7356fccf3bcf02bf4", + "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4", "shasum": "" }, "require": { @@ -2980,11 +3027,6 @@ "bin/phpcs" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" @@ -3034,11 +3076,11 @@ "type": "thanks_dev" } ], - "time": "2025-06-17T22:17:01+00:00" + "time": "2025-11-04T16:30:35+00:00" }, { "name": "symfony/polyfill-php73", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", @@ -3094,7 +3136,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php73/tree/v1.33.0" }, "funding": [ { @@ -3105,6 +3147,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -3177,16 +3223,16 @@ }, { "name": "theseer/tokenizer", - "version": "1.2.3", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", "shasum": "" }, "require": { @@ -3215,7 +3261,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" }, "funding": [ { @@ -3223,20 +3269,20 @@ "type": "github" } ], - "time": "2024-03-03T12:36:25+00:00" + "time": "2025-11-17T20:03:58+00:00" }, { "name": "wp-coding-standards/wpcs", - "version": "3.1.0", + "version": "3.3.0", "source": { "type": "git", "url": "https://github.com/WordPress/WordPress-Coding-Standards.git", - "reference": "9333efcbff231f10dfd9c56bb7b65818b4733ca7" + "reference": "7795ec6fa05663d716a549d0b44e47ffc8b0d4a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/9333efcbff231f10dfd9c56bb7b65818b4733ca7", - "reference": "9333efcbff231f10dfd9c56bb7b65818b4733ca7", + "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/7795ec6fa05663d716a549d0b44e47ffc8b0d4a6", + "reference": "7795ec6fa05663d716a549d0b44e47ffc8b0d4a6", "shasum": "" }, "require": { @@ -3244,17 +3290,17 @@ "ext-libxml": "*", "ext-tokenizer": "*", "ext-xmlreader": "*", - "php": ">=5.4", - "phpcsstandards/phpcsextra": "^1.2.1", - "phpcsstandards/phpcsutils": "^1.0.10", - "squizlabs/php_codesniffer": "^3.9.0" + "php": ">=7.2", + "phpcsstandards/phpcsextra": "^1.5.0", + "phpcsstandards/phpcsutils": "^1.1.0", + "squizlabs/php_codesniffer": "^3.13.4" }, "require-dev": { "php-parallel-lint/php-console-highlighter": "^1.0.0", - "php-parallel-lint/php-parallel-lint": "^1.3.2", - "phpcompatibility/php-compatibility": "^9.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpcompatibility/php-compatibility": "^10.0.0@dev", "phpcsstandards/phpcsdevtools": "^1.2.0", - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^8.0 || ^9.0" }, "suggest": { "ext-iconv": "For improved results", @@ -3289,20 +3335,20 @@ "type": "custom" } ], - "time": "2024-03-25T16:39:00+00:00" + "time": "2025-11-25T12:08:04+00:00" }, { "name": "wp-phpunit/wp-phpunit", - "version": "6.8.1", + "version": "6.9.4", "source": { "type": "git", "url": "https://github.com/wp-phpunit/wp-phpunit.git", - "reference": "a33d328dab5a4a9ddf0c560bcadbabb58b5ee67f" + "reference": "15fd216bf6516670d8d07b938675925bfa5c15b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/wp-phpunit/wp-phpunit/zipball/a33d328dab5a4a9ddf0c560bcadbabb58b5ee67f", - "reference": "a33d328dab5a4a9ddf0c560bcadbabb58b5ee67f", + "url": "https://api.github.com/repos/wp-phpunit/wp-phpunit/zipball/15fd216bf6516670d8d07b938675925bfa5c15b0", + "reference": "15fd216bf6516670d8d07b938675925bfa5c15b0", "shasum": "" }, "type": "library", @@ -3337,20 +3383,20 @@ "issues": "https://github.com/wp-phpunit/issues", "source": "https://github.com/wp-phpunit/wp-phpunit" }, - "time": "2025-04-16T01:40:54+00:00" + "time": "2026-02-04T01:48:23+00:00" }, { "name": "yoast/phpunit-polyfills", - "version": "1.1.4", + "version": "1.1.5", "source": { "type": "git", "url": "https://github.com/Yoast/PHPUnit-Polyfills.git", - "reference": "e6faedf5e34cea4438e341f660e2f719760c531d" + "reference": "41aaac462fbd80feb8dd129e489f4bbc53fe26b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/e6faedf5e34cea4438e341f660e2f719760c531d", - "reference": "e6faedf5e34cea4438e341f660e2f719760c531d", + "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/41aaac462fbd80feb8dd129e489f4bbc53fe26b0", + "reference": "41aaac462fbd80feb8dd129e489f4bbc53fe26b0", "shasum": "" }, "require": { @@ -3360,7 +3406,7 @@ "require-dev": { "php-parallel-lint/php-console-highlighter": "^1.0.0", "php-parallel-lint/php-parallel-lint": "^1.4.0", - "yoast/yoastcs": "^3.1.0" + "yoast/yoastcs": "^3.2.0" }, "type": "library", "extra": { @@ -3400,29 +3446,31 @@ "security": "https://github.com/Yoast/PHPUnit-Polyfills/security/policy", "source": "https://github.com/Yoast/PHPUnit-Polyfills" }, - "time": "2025-02-09T18:13:44+00:00" + "time": "2025-08-10T04:54:36+00:00" }, { "name": "yoast/wp-test-utils", - "version": "1.2.0", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/Yoast/wp-test-utils.git", - "reference": "2e0f62e0281e4859707c5f13b7da1422aa1c8f7b" + "reference": "e1c316f10ff892fff36116349b59ee5a00174ca3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Yoast/wp-test-utils/zipball/2e0f62e0281e4859707c5f13b7da1422aa1c8f7b", - "reference": "2e0f62e0281e4859707c5f13b7da1422aa1c8f7b", + "url": "https://api.github.com/repos/Yoast/wp-test-utils/zipball/e1c316f10ff892fff36116349b59ee5a00174ca3", + "reference": "e1c316f10ff892fff36116349b59ee5a00174ca3", "shasum": "" }, "require": { - "brain/monkey": "^2.6.1", + "brain/monkey": "^2.7.0", "php": ">=5.6", - "yoast/phpunit-polyfills": "^1.1.0" + "yoast/phpunit-polyfills": "^1.1.5" }, "require-dev": { - "yoast/yoastcs": "^2.3.1" + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "yoast/yoastcs": "^3.3.0" }, "type": "library", "extra": { @@ -3467,9 +3515,10 @@ ], "support": { "issues": "https://github.com/Yoast/wp-test-utils/issues", + "security": "https://github.com/Yoast/wp-test-utils/security/policy", "source": "https://github.com/Yoast/wp-test-utils" }, - "time": "2023-09-27T10:25:08+00:00" + "time": "2026-02-05T18:06:16+00:00" } ], "aliases": [], @@ -3480,8 +3529,10 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": ">=7.4" + "php": ">=7.4", + "ext-openssl": "*", + "ext-json": "*" }, "platform-dev": [], - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.2.0" } From bf1952c630e00fa22f36aa073422a33fffa41403 Mon Sep 17 00:00:00 2001 From: Steve Jones Date: Sun, 31 May 2026 23:57:49 -0400 Subject: [PATCH 12/26] Apply suggestions from code review Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- tests/jest/rules/linkImproper.test.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/jest/rules/linkImproper.test.js b/tests/jest/rules/linkImproper.test.js index 2f68f5d65..a2afe3a15 100644 --- a/tests/jest/rules/linkImproper.test.js +++ b/tests/jest/rules/linkImproper.test.js @@ -49,6 +49,11 @@ describe( 'Link Improper Rule', () => { html: 'Whitespace link', shouldPass: false, }, + { + name: 'Fails when anchor href contains only whitespace', + html: 'Whitespace link', + shouldPass: false, + }, { name: 'Fails when anchor has malformed URL', html: 'Invalid URL', From 0e491be8ff15d90c123cd3dc0f9efbc563c5b0bf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 17:12:57 +0000 Subject: [PATCH 13/26] chore: initial plan Co-authored-by: pattonwebz <3902039+pattonwebz@users.noreply.github.com> --- package-lock.json | 1337 +++++++++++++++++++++++++++++---------------- 1 file changed, 869 insertions(+), 468 deletions(-) diff --git a/package-lock.json b/package-lock.json index 059e80434..6338bb589 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "accessibility-checker", - "version": "1.37.0", + "version": "1.42.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "accessibility-checker", - "version": "1.37.0", + "version": "1.42.1", "hasInstallScript": true, "license": "GPL-2.0+", "devDependencies": { @@ -14,8 +14,10 @@ "@babel/preset-env": "^7.22.4", "@babel/preset-react": "^7.22.3", "@floating-ui/dom": "^1.2.9", + "@svgr/webpack": "^8.1.0", "@wordpress/eslint-plugin": "^17.5.0", - "@wordpress/i18n": "^6.11.0", + "@wordpress/i18n": "^6.10.0", + "@wordpress/icons": "^11.5.0", "@wordpress/scripts": "^31.0.0", "axe-core": "^4.8.2", "babel-loader": "^9.1.2", @@ -23,6 +25,7 @@ "css-loader": "^6.8.1", "css-minimizer-webpack-plugin": "^5.0.0", "cypress": "^14.5.0", + "eslint-plugin-react": "^7.37.0", "focus-trap": "^7.4.3", "husky": "^9.0.6", "jest": "^29.7.0", @@ -113,7 +116,6 @@ "integrity": "sha512-yJ474Zv3cwiSOO9nXJuqzvwEeM+chDuQ8GJirw+pZ91sCGCyOZ3dJkVE09fTV0VEVzXyLWhh3G/AolYTPX7Mow==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.25.7", @@ -2044,7 +2046,6 @@ "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@keyv/serialize": "^1.1.1" } @@ -2158,7 +2159,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -2199,7 +2199,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -3474,7 +3473,6 @@ "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=8.0.0" } @@ -3498,7 +3496,6 @@ "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=14" }, @@ -3512,7 +3509,6 @@ "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/semantic-conventions": "1.28.0" }, @@ -3539,7 +3535,6 @@ "integrity": "sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/api-logs": "0.57.2", "@types/shimmer": "^1.2.0", @@ -3996,7 +3991,6 @@ "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" @@ -4024,7 +4018,6 @@ "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/resources": "1.30.1", @@ -4053,7 +4046,6 @@ "integrity": "sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=14" } @@ -4929,7 +4921,6 @@ "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", "dev": true, - "peer": true, "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -5186,7 +5177,6 @@ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.7.tgz", "integrity": "sha512-f5ORu2hcBbKei97U73mf+l9t4zTGl74IqZ0GQk4oVea/VS8tQZYkUveSYojk+frraAVYId0V2WC9O4PTNru2FQ==", "dev": true, - "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -5349,7 +5339,6 @@ "integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -5423,7 +5412,6 @@ "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -5516,7 +5504,8 @@ "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.6.tgz", "integrity": "sha512-5JcVt1u5HDmlXkwOD2nslZVllBBc7HDuOICfiZah2Z0is8M8g+ddAEawbmd3VjedfDHBzxCaXLs07QEmb7y54g==", "dev": true, - "optional": true + "optional": true, + "peer": true }, "node_modules/@types/stack-utils": { "version": "2.0.3", @@ -5529,7 +5518,8 @@ "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.12.tgz", "integrity": "sha512-bTHG8fcxEqv1M9+TD14P8ok8hjxoOCkfKc8XXLaaD05kI7ohpeI956jtDOD3XHKBQrlyPughUtzm1jtVhHpA5Q==", "dev": true, - "optional": true + "optional": true, + "peer": true }, "node_modules/@types/tedious": { "version": "4.0.14", @@ -5553,6 +5543,7 @@ "integrity": "sha512-Hm/T0kV3ywpJyMGNbsItdivRhYNCQQf1IIsYsXnoVPES4t+FMLyDe0/K+Ea7ahWtMtSNb22ZdY7MIyoD9rqARg==", "dev": true, "optional": true, + "peer": true, "dependencies": { "source-map": "^0.6.1" } @@ -5563,6 +5554,7 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -5573,6 +5565,7 @@ "integrity": "sha512-oOW7E931XJU1mVfCnxCVgv8GLFL768pDO5u2Gzk82i8yTIgX6i7cntyZOkZYb/JtYM8252SN9bQp9tgkVDSsRw==", "dev": true, "optional": true, + "peer": true, "dependencies": { "@types/node": "*", "@types/tapable": "^1", @@ -5588,6 +5581,7 @@ "integrity": "sha512-4nZOdMwSPHZ4pTEZzSp0AsTM4K7Qmu40UKW4tJDiOVs20UzYF9l+qUe4s0ftfN0pin06n+5cWWDJXH+sbhAiDw==", "dev": true, "optional": true, + "peer": true, "dependencies": { "@types/node": "*", "@types/source-list-map": "*", @@ -5600,6 +5594,7 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -5712,7 +5707,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.1.tgz", "integrity": "sha512-fs2XOhWCzRhqMmQf0eicLa/CWSaYss2feXsy7xBD/pLyWke/jCIVc2s1ikEAtSW7ina1HNhv7kONoEfVNEcdDQ==", "dev": true, - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.13.1", "@typescript-eslint/types": "6.13.1", @@ -6507,15 +6501,15 @@ } }, "node_modules/@wordpress/element": { - "version": "6.38.0", - "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-6.38.0.tgz", - "integrity": "sha512-4N9xtuE2TIvaZ037fQREKrBw4TTt90PWswAfNFvxUVu5vdp91PLuR6rmeO0ohKg2CJLzozQLEKZF+6VuSaYowQ==", + "version": "6.39.0", + "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-6.39.0.tgz", + "integrity": "sha512-yVEjTddIVEsYjwRCY1pEzav/dGVH9S1xfwYwRyGsUGOQw5hXtIVnTbCIfQ2t3OCOGgSIYEh2ZmsSt0eTxZvtBw==", "dev": true, "license": "GPL-2.0-or-later", "dependencies": { "@types/react": "^18.3.27", "@types/react-dom": "^18.3.1", - "@wordpress/escape-html": "^3.38.0", + "@wordpress/escape-html": "^3.39.0", "change-case": "^4.1.2", "is-plain-object": "^5.0.0", "react": "^18.3.0", @@ -6537,9 +6531,9 @@ } }, "node_modules/@wordpress/escape-html": { - "version": "3.38.0", - "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-3.38.0.tgz", - "integrity": "sha512-LItqIdFnn/JAVEzTtvDZGJfsdmzeIE4ryMjovHkJhRElzcQWKHuq806DMprL40NcvJbdn0YfJZSv+aeP+9Ah3g==", + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-3.39.0.tgz", + "integrity": "sha512-YhAmZYQsOnnfafMwInzy/M1AR77O59u4aaIVSqiQjvCLkY+BQABsU6AizDckCg57mF2SoDnARl6xQY/7FCwEmw==", "dev": true, "license": "GPL-2.0-or-later", "engines": { @@ -6647,6 +6641,24 @@ "npm": ">=8.19.2" } }, + "node_modules/@wordpress/icons": { + "version": "11.6.0", + "resolved": "https://registry.npmjs.org/@wordpress/icons/-/icons-11.6.0.tgz", + "integrity": "sha512-X3Tp3ARJWRokFOTJ1SJQef3J+bykyZHuHL6NSaMnEtbQzs7Tre+3HEghP5S5K/AGnLr+RvpGnwEN0uNy53uIiA==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/element": "^6.39.0", + "@wordpress/primitives": "^4.39.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, "node_modules/@wordpress/jest-console": { "version": "8.38.0", "resolved": "https://registry.npmjs.org/@wordpress/jest-console/-/jest-console-8.38.0.tgz", @@ -6729,6 +6741,24 @@ "prettier": ">=3" } }, + "node_modules/@wordpress/primitives": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@wordpress/primitives/-/primitives-4.39.0.tgz", + "integrity": "sha512-RV9s+KzyuS8p0YKczLecmhFAtOHIWkCToHyDSmRf8N3XOy4id/T0+B5SJ2nMZJleG1oYINf5lrVcccqP2VmFJg==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/element": "^6.39.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, "node_modules/@wordpress/private-apis": { "version": "1.38.0", "resolved": "https://registry.npmjs.org/@wordpress/private-apis/-/private-apis-1.38.0.tgz", @@ -7097,7 +7127,6 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -7609,7 +7638,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -7708,7 +7736,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -7916,13 +7943,17 @@ } }, "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7935,16 +7966,20 @@ "dev": true }, "node_modules/array-includes": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", - "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-string": "^1.0.7" + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -7971,6 +8006,27 @@ "node": ">=0.10.0" } }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array.prototype.findlastindex": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", @@ -8009,15 +8065,16 @@ } }, "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -8027,31 +8084,36 @@ } }, "node_modules/array.prototype.tosorted": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.2.tgz", - "integrity": "sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.2.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", - "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, + "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", - "is-shared-array-buffer": "^1.0.2" + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" }, "engines": { "node": ">= 0.4" @@ -8123,13 +8185,14 @@ "dev": true, "license": "MIT" }, - "node_modules/asynciterator.prototype": { + "node_modules/async-function": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", - "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", "dev": true, - "dependencies": { - "has-symbols": "^1.0.3" + "license": "MIT", + "engines": { + "node": ">= 0.4" } }, "node_modules/asynckit": { @@ -8196,10 +8259,14 @@ } }, "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -8297,7 +8364,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -8804,7 +8870,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -8938,16 +9003,16 @@ } }, "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "dev": true, + "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "set-function-length": "^1.2.2" }, "engines": { "node": ">= 0.4" @@ -9451,6 +9516,16 @@ "node": ">=0.10.0" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -9736,7 +9811,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -10088,7 +10162,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -10679,6 +10752,60 @@ "node": ">=12" } }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/dayjs": { "version": "1.11.13", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", @@ -11155,8 +11282,7 @@ "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1507524.tgz", "integrity": "sha512-OjaNE7qpk6GRTXtqQjAE5bGx6+c4F1zZH0YXtpZQLM92HNXx4zMAaqlKhP4T52DosG6hDW8gPMNhGOF8xbwk/w==", "dev": true, - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/diff-sequences": { "version": "29.6.3", @@ -11505,50 +11631,66 @@ } }, "node_modules/es-abstract": { - "version": "1.22.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", - "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", + "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", "dev": true, + "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.2", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.5", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.2", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.12", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "safe-array-concat": "^1.0.1", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.13" + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" }, "engines": { "node": ">= 0.4" @@ -11577,25 +11719,31 @@ } }, "node_modules/es-iterator-helpers": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz", - "integrity": "sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.2.tgz", + "integrity": "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==", "dev": true, + "license": "MIT", "dependencies": { - "asynciterator.prototype": "^1.0.0", - "call-bind": "^1.0.2", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.22.1", - "es-set-tostringtag": "^2.0.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.2.1", - "globalthis": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "iterator.prototype": "^1.1.2", - "safe-array-concat": "^1.0.1" + "es-abstract": "^1.24.1", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.1.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.3.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.5", + "safe-array-concat": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/es-module-lexer": { @@ -11644,14 +11792,15 @@ } }, "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", "dev": true, + "license": "MIT", "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" }, "engines": { "node": ">= 0.4" @@ -11722,7 +11871,6 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -12230,33 +12378,36 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.33.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz", - "integrity": "sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==", + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "dev": true, + "license": "MIT", "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flatmap": "^1.3.1", - "array.prototype.tosorted": "^1.1.1", + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.0.12", + "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", + "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", - "object.entries": "^1.1.6", - "object.fromentries": "^2.0.6", - "object.hasown": "^1.1.2", - "object.values": "^1.1.6", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.4", + "resolve": "^2.0.0-next.5", "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.8" + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" }, "engines": { "node": ">=4" }, "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "node_modules/eslint-plugin-react-hooks": { @@ -13104,12 +13255,19 @@ } }, "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dev": true, + "license": "MIT", "dependencies": { - "is-callable": "^1.1.3" + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/for-in": { @@ -13260,15 +13418,18 @@ } }, "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" }, "engines": { "node": ">= 0.4" @@ -13282,10 +13443,21 @@ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -13405,13 +13577,15 @@ } }, "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -13615,12 +13789,14 @@ } }, "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, + "license": "MIT", "dependencies": { - "define-properties": "^1.1.3" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -13713,11 +13889,15 @@ } }, "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, - "funding": { + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { "url": "https://github.com/sponsors/ljharb" } }, @@ -13744,10 +13924,14 @@ } }, "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -14328,14 +14512,15 @@ "license": "ISC" }, "node_modules/internal-slot": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", - "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.2", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -14402,14 +14587,18 @@ } }, "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -14422,12 +14611,17 @@ "dev": true }, "node_modules/is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -14437,12 +14631,16 @@ } }, "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, + "license": "MIT", "dependencies": { - "has-bigints": "^1.0.1" + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -14461,13 +14659,14 @@ } }, "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -14525,6 +14724,7 @@ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -14548,13 +14748,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -14597,12 +14817,16 @@ } }, "node_modules/is-finalizationregistry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", - "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -14627,12 +14851,17 @@ } }, "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -14704,19 +14933,24 @@ } }, "node_modules/is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -14734,12 +14968,14 @@ } }, "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -14822,13 +15058,16 @@ "dev": true }, "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -14838,21 +15077,29 @@ } }, "node_modules/is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -14871,12 +15118,14 @@ } }, "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -14886,12 +15135,15 @@ } }, "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, + "license": "MIT", "dependencies": { - "has-symbols": "^1.0.2" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -14901,12 +15153,13 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, + "license": "MIT", "dependencies": { - "which-typed-array": "^1.1.11" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -14934,34 +15187,46 @@ } }, "node_modules/is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -15092,16 +15357,21 @@ } }, "node_modules/iterator.prototype": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", - "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", "dev": true, + "license": "MIT", "dependencies": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/jest": { @@ -15109,7 +15379,6 @@ "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, - "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -15802,7 +16071,6 @@ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", "dev": true, - "peer": true, "dependencies": { "abab": "^2.0.6", "acorn": "^8.8.1", @@ -16224,8 +16492,7 @@ "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1551306.tgz", "integrity": "sha512-CFx8QdSim8iIv+2ZcEOclBKTQY6BI1IEDa7Tm9YkwAXzEWFndTEzpTo5jAUhSnq24IC7xaDw0wvGcm96+Y3PEg==", "dev": true, - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/lighthouse/node_modules/puppeteer-core/node_modules/ws": { "version": "8.19.0", @@ -17458,7 +17725,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -17951,14 +18217,17 @@ } }, "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", "object-keys": "^1.1.1" }, "engines": { @@ -17969,28 +18238,32 @@ } }, "node_modules/object.entries": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", - "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" }, "engines": { "node": ">= 0.4" } }, "node_modules/object.fromentries": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", - "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -18011,28 +18284,17 @@ "get-intrinsic": "^1.2.1" } }, - "node_modules/object.hasown": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.3.tgz", - "integrity": "sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==", - "dev": true, - "dependencies": { - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/object.values": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", - "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -18161,6 +18423,24 @@ "dev": true, "license": "MIT" }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -18804,6 +19084,7 @@ "integrity": "sha512-2SVA0sbPktiIY/MCOPX8e86ehA/e+tDNq+e5Y8qjKYti2Z/JG7xnronT/TXTIkKbYGWlCbuucZ6dziEgkoEjQQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "playwright-core": "1.58.0" }, @@ -18823,6 +19104,7 @@ "integrity": "sha512-aaoB1RWrdNi3//rOeKuMiS65UCcgOVljU46At6eFcOFPFHWtd2weHRRow6z/n+Lec0Lvu0k9ZPKJSjPugikirw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "playwright-core": "cli.js" }, @@ -18841,6 +19123,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } @@ -18861,6 +19144,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", @@ -18881,7 +19174,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -19600,7 +19892,6 @@ "resolved": "https://registry.npmjs.org/wp-prettier/-/wp-prettier-3.0.3.tgz", "integrity": "sha512-X4UlrxDTH8oom9qXlcjnydsjAOD2BmB6yFmvS4Z2zdTzqqpRWb+fbqrH412+l+OUXmbzJlSXjlMFYPgYG12IAA==", "dev": true, - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -20005,7 +20296,6 @@ "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -20019,7 +20309,6 @@ "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -20040,7 +20329,6 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", "dev": true, - "peer": true, "engines": { "node": ">=0.10.0" } @@ -20271,17 +20559,20 @@ } }, "node_modules/reflect.getprototypeof": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", - "integrity": "sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -20317,14 +20608,18 @@ "dev": true }, "node_modules/regexp.prototype.flags": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -20666,14 +20961,16 @@ } }, "node_modules/safe-array-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", - "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", "isarray": "^2.0.5" }, "engines": { @@ -20703,15 +21000,36 @@ } ] }, - "node_modules/safe-regex-test": { + "node_modules/safe-push-apply": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -21072,14 +21390,31 @@ } }, "node_modules/set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, + "license": "MIT", "dependencies": { - "define-data-property": "^1.0.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -21626,6 +21961,20 @@ "node": ">= 0.8" } }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/streamx": { "version": "2.23.0", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", @@ -21696,34 +22045,58 @@ "dev": true }, "node_modules/string.prototype.matchall": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", - "integrity": "sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==", + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "regexp.prototype.flags": "^1.5.0", - "set-function-name": "^2.0.0", - "side-channel": "^1.0.4" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, "node_modules/string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -21733,28 +22106,37 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -21883,7 +22265,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-syntax-patches-for-csstree": "^1.0.19", @@ -22275,7 +22656,6 @@ "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -22623,7 +23003,6 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -22844,7 +23223,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -23133,7 +23511,6 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, - "peer": true, "engines": { "node": ">=10" }, @@ -23155,29 +23532,32 @@ } }, "node_modules/typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" } }, "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -23187,16 +23567,19 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, + "license": "MIT", "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" }, "engines": { "node": ">= 0.4" @@ -23206,14 +23589,21 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -23247,15 +23637,19 @@ "dev": true }, "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", + "call-bound": "^1.0.3", "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -23354,7 +23748,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "dependencies": { "napi-postinstall": "^0.3.0" }, @@ -23661,7 +24054,6 @@ "integrity": "sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -23788,7 +24180,6 @@ "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "dev": true, - "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^2.1.1", @@ -23866,7 +24257,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -23979,7 +24369,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -24093,7 +24482,6 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -24226,39 +24614,45 @@ } }, "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dev": true, + "license": "MIT", "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-builtin-type": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", - "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", "dev": true, + "license": "MIT", "dependencies": { - "function.prototype.name": "^1.1.5", - "has-tostringtag": "^1.0.0", + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", + "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -24268,31 +24662,38 @@ } }, "node_modules/which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, + "license": "MIT", "dependencies": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", - "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", "dev": true, + "license": "MIT", "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.4", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" From faecdc93e91e879c49d95b28cbde0a74df8fb3fd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 17:17:53 +0000 Subject: [PATCH 14/26] feat: cache is_domain_loopback DNS lookup in a short-lived transient Co-authored-by: pattonwebz <3902039+pattonwebz@users.noreply.github.com> --- admin/class-helpers.php | 17 ++++++ tests/phpunit/Admin/HelpersLoopbackTest.php | 61 +++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/admin/class-helpers.php b/admin/class-helpers.php index 90b42c0af..f2dda1f35 100644 --- a/admin/class-helpers.php +++ b/admin/class-helpers.php @@ -143,11 +143,22 @@ public static function get_option_as_array( $option_name ) { /** * Determine if a domain is hosted on a local loopback * + * Results are cached in a short-lived transient to avoid repeated DNS + * lookups when the same domain is checked multiple times during a scan. + * * @param string $domain The domain to check. * @return boolean */ public static function is_domain_loopback( $domain ) { + // Check the transient cache first to avoid repeated DNS lookups. + $cache_key = 'edac_loopback_' . md5( sanitize_text_field( $domain ) ); + $cached = get_transient( $cache_key ); + + if ( false !== $cached ) { + return (bool) $cached; + } + // Check if this is an ipv4 address in the loopback range. $record = gethostbyname( $domain ); @@ -156,6 +167,9 @@ public static function is_domain_loopback( $domain ) { $ip_long = ip2long( $record ); if ( $ip_long >= $loopback_start && $ip_long <= $loopback_end ) { + // Store as int (1/0) so that a false result is distinguishable from a + // cache miss, since get_transient() also returns false on a miss. + set_transient( $cache_key, 1, HOUR_IN_SECONDS ); return true; } @@ -164,6 +178,7 @@ public static function is_domain_loopback( $domain ) { try { $records = dns_get_record( $domain, DNS_AAAA ); } catch ( \Throwable $th ) { + set_transient( $cache_key, 0, HOUR_IN_SECONDS ); return false; } @@ -179,11 +194,13 @@ public static function is_domain_loopback( $domain ) { $loopback_ipv6 = inet_pton( '::1' ); if ( $normalized_ipv6 === $loopback_ipv6 ) { + set_transient( $cache_key, 1, HOUR_IN_SECONDS ); return true; } } } + set_transient( $cache_key, 0, HOUR_IN_SECONDS ); return false; } diff --git a/tests/phpunit/Admin/HelpersLoopbackTest.php b/tests/phpunit/Admin/HelpersLoopbackTest.php index 503b1159d..312b3b15c 100644 --- a/tests/phpunit/Admin/HelpersLoopbackTest.php +++ b/tests/phpunit/Admin/HelpersLoopbackTest.php @@ -242,4 +242,65 @@ public function test_performance_multiple_calls() { // Should complete within a reasonable time (5 seconds). $this->assertLessThan( 5.0, $execution_time, 'Method took too long to execute multiple calls' ); } + + /** + * Test that the result is cached in a transient after the first call. + */ + public function test_result_is_cached_in_transient() { + $domain = 'localhost'; + $cache_key = 'edac_loopback_' . md5( sanitize_text_field( $domain ) ); + + // Clear any existing transient. + delete_transient( $cache_key ); + + // First call performs the DNS lookup and stores the result. + $result = Helpers::is_domain_loopback( $domain ); + $this->assertTrue( $result ); + + // The transient should now be set with an integer value. + $cached = get_transient( $cache_key ); + $this->assertNotFalse( $cached, 'Transient should be set after first call.' ); + $this->assertSame( 1, (int) $cached ); + } + + /** + * Test that a non-loopback result is also cached correctly. + */ + public function test_non_loopback_result_is_cached() { + $domain = '8.8.8.8'; + $cache_key = 'edac_loopback_' . md5( sanitize_text_field( $domain ) ); + + // Clear any existing transient. + delete_transient( $cache_key ); + + // First call performs the DNS lookup. + $result = Helpers::is_domain_loopback( $domain ); + $this->assertFalse( $result ); + + // The transient should be set with 0 (not false) so a cache miss can + // be distinguished from a cached false result. + $cached = get_transient( $cache_key ); + $this->assertNotFalse( $cached, 'Transient should be set even for non-loopback domains.' ); + $this->assertSame( 0, (int) $cached ); + } + + /** + * Test that a cached result is returned without re-doing the DNS lookup. + */ + public function test_cached_result_is_returned() { + $domain = '127.0.0.1'; + $cache_key = 'edac_loopback_' . md5( sanitize_text_field( $domain ) ); + + // Pre-seed the transient with a value opposite to the real DNS answer. + // If the cache is respected, this value will be returned as-is. + set_transient( $cache_key, 0, HOUR_IN_SECONDS ); + + $result = Helpers::is_domain_loopback( $domain ); + + // The seeded value (0 / false) should be returned from cache. + $this->assertFalse( $result ); + + // Clean up. + delete_transient( $cache_key ); + } } From b7577705d416f201b4ef96a829a27d5f49bedbd0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Feb 2026 17:42:12 +0000 Subject: [PATCH 15/26] =?UTF-8?q?feat:=20remove=20is=5Fdomain=5Floopback?= =?UTF-8?q?=20=E2=80=94=20unused=20function=20with=20no=20production=20cal?= =?UTF-8?q?lers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pattonwebz <3902039+pattonwebz@users.noreply.github.com> --- admin/class-helpers.php | 65 ----- tests/phpunit/Admin/HelpersLoopbackTest.php | 306 -------------------- 2 files changed, 371 deletions(-) delete mode 100644 tests/phpunit/Admin/HelpersLoopbackTest.php diff --git a/admin/class-helpers.php b/admin/class-helpers.php index f2dda1f35..c2ce6ad8b 100644 --- a/admin/class-helpers.php +++ b/admin/class-helpers.php @@ -139,71 +139,6 @@ public static function get_option_as_array( $option_name ) { return []; } - - /** - * Determine if a domain is hosted on a local loopback - * - * Results are cached in a short-lived transient to avoid repeated DNS - * lookups when the same domain is checked multiple times during a scan. - * - * @param string $domain The domain to check. - * @return boolean - */ - public static function is_domain_loopback( $domain ) { - - // Check the transient cache first to avoid repeated DNS lookups. - $cache_key = 'edac_loopback_' . md5( sanitize_text_field( $domain ) ); - $cached = get_transient( $cache_key ); - - if ( false !== $cached ) { - return (bool) $cached; - } - - // Check if this is an ipv4 address in the loopback range. - - $record = gethostbyname( $domain ); - $loopback_start = ip2long( '127.0.0.0' ); - $loopback_end = ip2long( '127.255.255.255' ); - $ip_long = ip2long( $record ); - - if ( $ip_long >= $loopback_start && $ip_long <= $loopback_end ) { - // Store as int (1/0) so that a false result is distinguishable from a - // cache miss, since get_transient() also returns false on a miss. - set_transient( $cache_key, 1, HOUR_IN_SECONDS ); - return true; - } - - // Check if this is an ipv6 loopback. - - try { - $records = dns_get_record( $domain, DNS_AAAA ); - } catch ( \Throwable $th ) { - set_transient( $cache_key, 0, HOUR_IN_SECONDS ); - return false; - } - - foreach ( $records as $record ) { - - // Do ipv6 check. - if ( isset( $record['type'] ) && 'AAAA' === $record['type'] ) { - - // Normalize the IPv6 address for comparison. - $normalized_ipv6 = inet_pton( $record['ipv6'] ); - - // Normalize the loopback address. - $loopback_ipv6 = inet_pton( '::1' ); - - if ( $normalized_ipv6 === $loopback_ipv6 ) { - set_transient( $cache_key, 1, HOUR_IN_SECONDS ); - return true; - } - } - } - - set_transient( $cache_key, 0, HOUR_IN_SECONDS ); - return false; - } - /** * Filter out inactive rules from the results returned. * diff --git a/tests/phpunit/Admin/HelpersLoopbackTest.php b/tests/phpunit/Admin/HelpersLoopbackTest.php deleted file mode 100644 index 312b3b15c..000000000 --- a/tests/phpunit/Admin/HelpersLoopbackTest.php +++ /dev/null @@ -1,306 +0,0 @@ -assertIsBool( $result ); - $this->assertSame( $expected, $result ); - } - - /** - * Data provider for test_is_domain_loopback. - */ - public function domain_loopback_data() { - return [ - 'localhost' => [ - 'domain' => 'localhost', - 'expected' => true, - ], - '127.0.0.1 direct IP' => [ - 'domain' => '127.0.0.1', - 'expected' => true, - ], - '127.0.0.2 loopback range' => [ - 'domain' => '127.0.0.2', - 'expected' => true, - ], - '127.255.255.255 loopback range end' => [ - 'domain' => '127.255.255.255', - 'expected' => true, - ], - '127.100.50.25 mid loopback range' => [ - 'domain' => '127.100.50.25', - 'expected' => true, - ], - 'google.com external domain' => [ - 'domain' => 'google.com', - 'expected' => false, - ], - 'example.com external domain' => [ - 'domain' => 'example.com', - 'expected' => false, - ], - '192.168.1.1 private IP (not loopback)' => [ - 'domain' => '192.168.1.1', - 'expected' => false, - ], - '10.0.0.1 private IP (not loopback)' => [ - 'domain' => '10.0.0.1', - 'expected' => false, - ], - '8.8.8.8 public IP' => [ - 'domain' => '8.8.8.8', - 'expected' => false, - ], - ]; - } - - /** - * Test IPv4 loopback range boundaries. - */ - public function test_ipv4_loopback_boundaries() { - // Test the exact start of loopback range. - $this->assertTrue( Helpers::is_domain_loopback( '127.0.0.0' ) ); - - // Test just before loopback range. - $this->assertFalse( Helpers::is_domain_loopback( '126.255.255.255' ) ); - - // Test just after loopback range. - $this->assertFalse( Helpers::is_domain_loopback( '128.0.0.0' ) ); - } - - /** - * Test edge cases and invalid inputs. - */ - public function test_edge_cases() { - // Empty string. - $result = Helpers::is_domain_loopback( '' ); - $this->assertIsBool( $result ); - $this->assertFalse( $result ); - - // Invalid domain format. - $result = Helpers::is_domain_loopback( 'not-a-domain' ); - $this->assertIsBool( $result ); - - // Malformed IP. - $result = Helpers::is_domain_loopback( '999.999.999.999' ); - $this->assertIsBool( $result ); - $this->assertFalse( $result ); - - // Domain with protocol. - $result = Helpers::is_domain_loopback( 'http://localhost' ); - $this->assertIsBool( $result ); - } - - /** - * Test special localhost variations. - */ - public function test_localhost_variations() { - // Standard localhost should resolve to loopback. - $this->assertTrue( Helpers::is_domain_loopback( 'localhost' ) ); - - // Test case sensitivity (if applicable). - $result = Helpers::is_domain_loopback( 'LOCALHOST' ); - $this->assertIsBool( $result ); - - // Test with port (should still work since we're testing the domain part). - $result = Helpers::is_domain_loopback( 'localhost:8080' ); - $this->assertIsBool( $result ); - } - - /** - * Test that the method handles DNS resolution errors gracefully. - */ - public function test_dns_resolution_error_handling() { - // Test with a clearly non-existent domain. - $fake_domain = 'definitely-not-a-real-domain-' . uniqid() . '.invalid'; - $result = Helpers::is_domain_loopback( $fake_domain ); - - // Should return a boolean and not throw an exception. - $this->assertIsBool( $result ); - $this->assertFalse( $result ); - } - - /** - * Test IPv6 loopback detection (if supported). - */ - public function test_ipv6_loopback_detection() { - // The method checks for IPv6 AAAA records. - // This test may be limited by the test environment's DNS capabilities. - - // Test direct IPv6 loopback address (may not work in all environments). - $result = Helpers::is_domain_loopback( '::1' ); - $this->assertIsBool( $result ); - - // If IPv6 is supported, ::1 should be detected as loopback. - // However, we can't guarantee this in all test environments. - } - - /** - * Test that the method properly validates IP address formats. - */ - public function test_ip_address_validation() { - // Valid IPv4 addresses in loopback range. - $valid_loopback_ips = [ - '127.0.0.1', - '127.1.2.3', - '127.254.255.254', - ]; - - foreach ( $valid_loopback_ips as $ip ) { - $result = Helpers::is_domain_loopback( $ip ); - $this->assertTrue( $result, "Failed to detect loopback for IP: $ip" ); - } - - // Valid IPv4 addresses outside loopback range. - $non_loopback_ips = [ - '1.1.1.1', - '8.8.8.8', - '192.168.1.1', - '172.16.0.1', - '203.0.113.1', - ]; - - foreach ( $non_loopback_ips as $ip ) { - $result = Helpers::is_domain_loopback( $ip ); - $this->assertFalse( $result, "Incorrectly detected loopback for IP: $ip" ); - } - } - - /** - * Test behavior with domains that might have multiple A records. - */ - public function test_multiple_a_records() { - // Some domains may have multiple A records. - // The method should handle this correctly by checking the resolved IP. - - // Test a well-known domain that should resolve to non-loopback. - $result = Helpers::is_domain_loopback( 'github.com' ); - $this->assertIsBool( $result ); - $this->assertFalse( $result ); - } - - /** - * Test the method's handling of the gethostbyname function. - */ - public function test_gethostbyname_behavior() { - // gethostbyname returns the hostname unchanged if resolution fails. - // The method should handle this case. - - $non_resolvable = 'non-resolvable-domain-' . uniqid() . '.invalid'; - $result = Helpers::is_domain_loopback( $non_resolvable ); - - // Should return false for non-resolvable domains. - $this->assertIsBool( $result ); - $this->assertFalse( $result ); - } - - /** - * Test performance with multiple calls. - */ - public function test_performance_multiple_calls() { - // Test that the method performs reasonably with multiple calls. - $domains = [ - 'localhost', - '127.0.0.1', - 'google.com', - 'example.com', - '127.0.0.2', - ]; - - $start_time = microtime( true ); - - foreach ( $domains as $domain ) { - $result = Helpers::is_domain_loopback( $domain ); - $this->assertIsBool( $result ); - } - - $end_time = microtime( true ); - $execution_time = $end_time - $start_time; - - // Should complete within a reasonable time (5 seconds). - $this->assertLessThan( 5.0, $execution_time, 'Method took too long to execute multiple calls' ); - } - - /** - * Test that the result is cached in a transient after the first call. - */ - public function test_result_is_cached_in_transient() { - $domain = 'localhost'; - $cache_key = 'edac_loopback_' . md5( sanitize_text_field( $domain ) ); - - // Clear any existing transient. - delete_transient( $cache_key ); - - // First call performs the DNS lookup and stores the result. - $result = Helpers::is_domain_loopback( $domain ); - $this->assertTrue( $result ); - - // The transient should now be set with an integer value. - $cached = get_transient( $cache_key ); - $this->assertNotFalse( $cached, 'Transient should be set after first call.' ); - $this->assertSame( 1, (int) $cached ); - } - - /** - * Test that a non-loopback result is also cached correctly. - */ - public function test_non_loopback_result_is_cached() { - $domain = '8.8.8.8'; - $cache_key = 'edac_loopback_' . md5( sanitize_text_field( $domain ) ); - - // Clear any existing transient. - delete_transient( $cache_key ); - - // First call performs the DNS lookup. - $result = Helpers::is_domain_loopback( $domain ); - $this->assertFalse( $result ); - - // The transient should be set with 0 (not false) so a cache miss can - // be distinguished from a cached false result. - $cached = get_transient( $cache_key ); - $this->assertNotFalse( $cached, 'Transient should be set even for non-loopback domains.' ); - $this->assertSame( 0, (int) $cached ); - } - - /** - * Test that a cached result is returned without re-doing the DNS lookup. - */ - public function test_cached_result_is_returned() { - $domain = '127.0.0.1'; - $cache_key = 'edac_loopback_' . md5( sanitize_text_field( $domain ) ); - - // Pre-seed the transient with a value opposite to the real DNS answer. - // If the cache is respected, this value will be returned as-is. - set_transient( $cache_key, 0, HOUR_IN_SECONDS ); - - $result = Helpers::is_domain_loopback( $domain ); - - // The seeded value (0 / false) should be returned from cache. - $this->assertFalse( $result ); - - // Clean up. - delete_transient( $cache_key ); - } -} From 1f771dc62cd0239d263ccf227fc6cc48f960cec1 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 5 Jul 2026 15:43:52 +0000 Subject: [PATCH 16/26] Add programmatic label to Simplified Summary textarea The Simplified Summary textareas in the editor sidebar relied solely on placeholder text for their accessible name, which disappears once the user types. Add a visually hidden label so assistive technology always announces the field's purpose (WCAG 3.3.2). Fixes #1766 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_016H6Hjd5LrCUVG8LYiQ6y7X --- src/sidebar/components/Panels/ReadabilityAnalysis.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sidebar/components/Panels/ReadabilityAnalysis.js b/src/sidebar/components/Panels/ReadabilityAnalysis.js index 120d08d1e..1c1e2cac0 100644 --- a/src/sidebar/components/Panels/ReadabilityAnalysis.js +++ b/src/sidebar/components/Panels/ReadabilityAnalysis.js @@ -362,6 +362,8 @@ const ReadabilityAnalysis = () => { { __( 'Simplified Summary', 'accessibility-checker' ) } { { __( 'Simplified Summary', 'accessibility-checker' ) } Date: Sun, 5 Jul 2026 21:25:50 +0000 Subject: [PATCH 17/26] Add white contrast ring around highlighted element outline The magenta outline used to highlight elements with issues can lack sufficient contrast against page backgrounds close to that color. A white box-shadow ring between the element and the outline ensures the indicator stays visible regardless of the underlying page colors. Fixes #1768 --- src/frontendHighlighterApp/sass/app.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/frontendHighlighterApp/sass/app.scss b/src/frontendHighlighterApp/sass/app.scss index db48f0d6d..45ebea37e 100644 --- a/src/frontendHighlighterApp/sass/app.scss +++ b/src/frontendHighlighterApp/sass/app.scss @@ -51,6 +51,9 @@ body { outline: dashed 4px transparent !important; outline-offset: 5px !important; outline-color: magenta !important; + // White ring between the element and the magenta outline so the indicator + // stays visible even when the page background is close to the outline color. + box-shadow: 0 0 0 2px variables.$color-white !important; &-min-width { min-width: 25px !important; From 42e5f49c718435417cf88471826cd30b99fedeb4 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Thu, 9 Jul 2026 15:35:10 +0100 Subject: [PATCH 18/26] fix: render SVG code snippets in the Accessibility Analysis panel as images The metabox's details panel previously injected an issue's raw SVG markup straight into the panel's HTML. Convert it to a data: URI and render it inside an instead, the same technique already used for inline SVGs in the issue modal's image finder (IssueImage.js) - this keeps the metabox and the sidebar/modal consistent in how they display this content, and matches how untrusted markup should be handled here. Co-Authored-By: Claude Sonnet 5 --- admin/class-ajax.php | 10 ++- includes/helper-functions.php | 20 ++++++ .../SvgMarkupToDataUriTest.php | 61 +++++++++++++++++++ 3 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 tests/phpunit/helper-functions/SvgMarkupToDataUriTest.php diff --git a/admin/class-ajax.php b/admin/class-ajax.php index 3f34ddbe0..2bb38a891 100644 --- a/admin/class-ajax.php +++ b/admin/class-ajax.php @@ -526,7 +526,15 @@ function ( $a, $b ) { ) ) . '" />'; } elseif ( $object_svg ) { - $html .= $object_svg; + // Rendered as an via a data URI, not injected as inline markup - + // see edac_svg_markup_to_data_uri()'s docblock for why. + $html .= '' . esc_attr(
+								sprintf(
+									/* translators: %d: issue ID number */
+									__( 'image for issue %d', 'accessibility-checker' ),
+									$id
+								)
+							) . ''; } $html .= ''; diff --git a/includes/helper-functions.php b/includes/helper-functions.php index 7401ed93b..1febbf0aa 100644 --- a/includes/helper-functions.php +++ b/includes/helper-functions.php @@ -788,6 +788,26 @@ function edac_parse_html_for_media( $html ) { ]; } +/** + * Convert raw SVG markup into a data: URI safe for use as an src. + * + * The source markup comes from scanned page code stored in the issues + * table, which can contain arbitrary attributes/children (e.g. ' ], + 'foreignObject' => [ '' ], + 'javascript: xlink' => [ 'click' ], + 'animate onbegin' => [ '' ], + ]; + } +} From be597921ecec476829554afc958d0649a044fbb5 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Thu, 9 Jul 2026 17:50:54 +0100 Subject: [PATCH 19/26] fix: harden edac_svg_markup_to_data_uri() against non-string input, trim docblock Address review feedback on PR #1832 (CodeRabbit + Gemini Code Assist): add a return type declaration, tighten the docblock down to the non-obvious "why", and cover a combined-vector payload plus non-string inputs in tests. The parameter itself stays untyped: a strict `string` type hint would turn a wrong-type caller into a fatal TypeError. Instead the function checks is_string() itself and always returns a valid (if payload-less) data: URI, even on bad input. Co-Authored-By: Claude Sonnet 5 --- includes/helper-functions.php | 22 ++++---- .../SvgMarkupToDataUriTest.php | 54 +++++++++++++++++-- 2 files changed, 59 insertions(+), 17 deletions(-) diff --git a/includes/helper-functions.php b/includes/helper-functions.php index 1febbf0aa..66d279689 100644 --- a/includes/helper-functions.php +++ b/includes/helper-functions.php @@ -789,22 +789,20 @@ function edac_parse_html_for_media( $html ) { } /** - * Convert raw SVG markup into a data: URI safe for use as an src. - * - * The source markup comes from scanned page code stored in the issues - * table, which can contain arbitrary attributes/children (e.g. ' ], - 'foreignObject' => [ '' ], - 'javascript: xlink' => [ 'click' ], - 'animate onbegin' => [ '' ], + 'onload handler' => [ '' ], + 'script child' => [ '' ], + 'foreignObject' => [ '' ], + 'javascript: xlink' => [ 'click' ], + 'animate onbegin' => [ '' ], + 'combined all-in-one' => [ + 'hi', + ], + ]; + } + + /** + * Tests that the combined-vector SVG's dangerous constructs survive + * encoding intact (this function encodes, it does not sanitize - the + * caller's context is what neutralizes them, not this string). + */ + public function test_combined_vector_payload_is_preserved_not_stripped() { + $svg = 'hi'; + $decoded = rawurldecode( substr( edac_svg_markup_to_data_uri( $svg ), strlen( 'data:image/svg+xml,' ) ) ); + + $this->assertSame( $svg, $decoded ); + $this->assertStringContainsString( 'onload="alert(1)"', $decoded ); + $this->assertStringContainsString( '', $decoded ); + $this->assertStringContainsString( 'onclick="alert(3)"', $decoded ); + $this->assertStringContainsString( '', $decoded ); + } + + /** + * Tests that non-string input never fatals and always yields a bare, + * payload-less data URI rather than attempting to encode it. + * + * @dataProvider non_string_data + * + * @param mixed $value A non-string value. + */ + public function test_non_string_input_returns_bare_data_uri( $value ) { + $this->assertSame( 'data:image/svg+xml,', edac_svg_markup_to_data_uri( $value ) ); + } + + /** + * Data provider of non-string values. + */ + public function non_string_data() { + return [ + 'null' => [ null ], + 'array' => [ [ '' ] ], + 'int' => [ 42 ], + 'bool' => [ true ], + 'object' => [ (object) [ 'markup' => '' ] ], ]; } } From 09ec08295d5196cc11533b2fe907acfda4e293ca Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Fri, 10 Jul 2026 15:39:56 +0100 Subject: [PATCH 20/26] docs: document the esc_url() protocols requirement for the SVG data URI Review feedback on PR #1832: plain esc_url() rejects data: URIs and returns '' - callers must pass a protocols list that includes 'data', as the Ajax::details() call site does. Co-Authored-By: Claude Fable 5 --- includes/helper-functions.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/includes/helper-functions.php b/includes/helper-functions.php index 66d279689..cb88ca000 100644 --- a/includes/helper-functions.php +++ b/includes/helper-functions.php @@ -796,7 +796,10 @@ function edac_parse_html_for_media( $html ) { * @since x.x.x * * @param mixed $svg_markup Raw SVG markup - expected to be a string. - * @return string Unescaped data URI - callers must esc_url() it before output. + * @return string Unescaped data URI - callers must esc_url() it before output, + * passing a protocols list that includes 'data' (e.g. + * esc_url( $uri, [ 'data', 'http', 'https' ] )); with the + * default protocols esc_url() rejects data: URIs and returns ''. */ function edac_svg_markup_to_data_uri( $svg_markup ): string { if ( ! is_string( $svg_markup ) ) { From b334071dc6801404ef8b8cbf0d9b65d91b1d2a80 Mon Sep 17 00:00:00 2001 From: Steve Jones Date: Tue, 14 Jul 2026 10:34:23 -0400 Subject: [PATCH 21/26] fix: adjust outline offset and box-shadow for selected elements --- src/frontendHighlighterApp/sass/app.scss | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/frontendHighlighterApp/sass/app.scss b/src/frontendHighlighterApp/sass/app.scss index 45ebea37e..97fbb1803 100644 --- a/src/frontendHighlighterApp/sass/app.scss +++ b/src/frontendHighlighterApp/sass/app.scss @@ -49,11 +49,9 @@ body { &-element-selected { outline: dashed 4px transparent !important; - outline-offset: 5px !important; + outline-offset: 2px !important; outline-color: magenta !important; - // White ring between the element and the magenta outline so the indicator - // stays visible even when the page background is close to the outline color. - box-shadow: 0 0 0 2px variables.$color-white !important; + box-shadow: 0 0 0 8px variables.$color-white !important; &-min-width { min-width: 25px !important; From 1fe86b6761b46f332e4256b134deace7d51529f5 Mon Sep 17 00:00:00 2001 From: Steve Jones Date: Tue, 14 Jul 2026 10:51:27 -0400 Subject: [PATCH 22/26] fix: update test case for image with empty alt attribute to include shouldPass flag --- tests/jest/rules/imgAltEmpty.test.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/jest/rules/imgAltEmpty.test.js b/tests/jest/rules/imgAltEmpty.test.js index 1cf39b985..13f65db31 100644 --- a/tests/jest/rules/imgAltEmpty.test.js +++ b/tests/jest/rules/imgAltEmpty.test.js @@ -120,8 +120,9 @@ describe( 'Image Alt Empty Validation', () => { { name: 'should fail for img with empty alt and no dimension attributes (cannot confirm tracking pixel without dimensions)', html: '', - }, - + shouldPass: false, + }, + // Button context - image with empty alt inside button with accessible name { name: 'should pass for img with empty alt inside button with aria-label', From 2c4d2807a73d969cca5c7922d00b2b767be91019 Mon Sep 17 00:00:00 2001 From: pattonwebz Date: Tue, 14 Jul 2026 15:58:20 +0100 Subject: [PATCH 23/26] docs: tighten verbose comments on the homepage scan fix Reduce the added inline comments and test docblocks to 1-2 sentences, drop the ephemeral "Before Fix 3" reference, and restore the pull/9557 URL that had a stray period inserted. Co-Authored-By: Claude Opus 4.8 --- admin/class-enqueue-admin.php | 18 ++++---------- includes/classes/class-enqueue-frontend.php | 8 ++----- tests/phpunit/Admin/EnqueueAdminTest.php | 26 +++++++-------------- 3 files changed, 15 insertions(+), 37 deletions(-) diff --git a/admin/class-enqueue-admin.php b/admin/class-enqueue-admin.php index 878041f80..b9cf73271 100644 --- a/admin/class-enqueue-admin.php +++ b/admin/class-enqueue-admin.php @@ -79,11 +79,8 @@ public static function maybe_enqueue_admin_and_editor_app_scripts() { global $post; $post_id = is_object( $post ) ? $post->ID : null; - // When the "latest posts" homepage is active (show_on_front=posts), the global - // $post can be set to the first blog post from the main query — for example in - // the FSE site editor while previewing the home template. Apply a filter so that - // extensions can supply the correct post ID (e.g. a Pro virtual-page ID) rather - // than letting the wrong post's ID propagate into scan results. + // On a latest-posts homepage the global $post is the first blog post, not the page; + // let extensions supply the correct ID (e.g. a Pro virtual-page ID). $post_id = apply_filters( 'edac_filter_admin_post_id', $post_id ); wp_enqueue_script( 'edac', plugin_dir_url( EDAC_PLUGIN_FILE ) . 'build/admin.bundle.js', [ 'jquery' ], EDAC_VERSION, false ); @@ -105,9 +102,7 @@ public static function maybe_enqueue_admin_and_editor_app_scripts() { if ( 'post.php' === $pagenow || 'post-new.php' === $pagenow ) { - // Is this posttype setup to be checked? - // Re-evaluate based on the filtered $post_id, which may have been overridden - // by edac_filter_admin_post_id (e.g. a Pro virtual-page ID for the homepage). + // Base the scannable check on the filtered $post_id, not the original global $post. $filtered_post_type = $post_id ? get_post_type( $post_id ) : false; $active = $filtered_post_type && is_array( $post_types ) && in_array( $filtered_post_type, $post_types, true ); @@ -122,11 +117,8 @@ public static function maybe_enqueue_admin_and_editor_app_scripts() { wp_enqueue_script( 'edac-editor-app', plugin_dir_url( EDAC_PLUGIN_FILE ) . 'build/editorApp.bundle.js', false, EDAC_VERSION, false ); wp_set_script_translations( 'edac-editor-app', 'accessibility-checker', plugin_dir_path( EDAC_PLUGIN_FILE ) . 'languages' ); - // If this is the frontpage or homepage, preview URLs won't work. Use the live URL. - // Cover both the explicit "latest posts" setting (show_on_front=posts) and the - // fallback where show_on_front=page but no static front page is configured. - // Allow extensions to flag the current post as the latest-posts homepage so we - // use get_home_url() for the scan rather than a preview link. + // Preview URLs don't work for the homepage. On a latest-posts homepage (including the + // show_on_front=page fallback with no static front page) use the live home URL instead. $show_on_front = get_option( 'show_on_front', 'posts' ); $is_latest_posts_home = ( 'posts' === $show_on_front || ( 'page' === $show_on_front && ! get_option( 'page_on_front' ) ) ) && apply_filters( 'edac_filter_post_is_latest_posts_home', false, $post_id ); diff --git a/includes/classes/class-enqueue-frontend.php b/includes/classes/class-enqueue-frontend.php index 8f919a44a..583a4ac27 100644 --- a/includes/classes/class-enqueue-frontend.php +++ b/includes/classes/class-enqueue-frontend.php @@ -103,12 +103,8 @@ public static function maybe_enqueue_frontend_highlighter() { // Don't load on the frontend if we don't have a post to work with. global $post; - // When the homepage displays latest posts, WordPress populates the global $post with the - // first post from the blog query rather than a page object. This happens when - // show_on_front=posts, but also in the fallback case where show_on_front=page but no - // valid static front page is configured. is_home() && is_front_page() covers both. - // Passing the first post's ID would attribute homepage scan results to the wrong entry, - // so pass null and let the filter supply a virtual-page ID (Pro) or bail cleanly. + // On a latest-posts homepage the global $post is the first blog post, so using its ID + // would misattribute results; pass null and let the filter supply an ID (Pro) or bail. $default_post_id = ( is_home() && is_front_page() ) ? null : ( is_object( $post ) ? $post->ID : null ); diff --git a/tests/phpunit/Admin/EnqueueAdminTest.php b/tests/phpunit/Admin/EnqueueAdminTest.php index a01883226..29c676da4 100644 --- a/tests/phpunit/Admin/EnqueueAdminTest.php +++ b/tests/phpunit/Admin/EnqueueAdminTest.php @@ -166,7 +166,7 @@ public function testScanUrlUsesPermalinkForFrontpage() { $this->assertStringContainsString( 'edac_pageScanner', $localized_data ); // In WP 6.9 there were changes to the flags passed to wp_json_encode() that made slashes no longer get escaped by default. // We should check for both possibilities here to ensure compatibility across versions. - // See: https://github.com/WordPress/wordpress-develop/pull/9557. for more details. + // See: https://github.com/WordPress/wordpress-develop/pull/9557 for more details. if ( version_compare( get_bloginfo( 'version' ), '6.9', '>=' ) ) { $this->assertStringContainsString( esc_url_raw( get_permalink( $post->ID ) ), $localized_data ); } else { @@ -198,7 +198,7 @@ public function testScanUrlUsesPermalinkForPostsPage() { $this->assertStringContainsString( 'edac_pageScanner', $localized_data ); // In WP 6.9 there were changes to the flags passed to wp_json_encode() that made slashes no longer get escaped by default. // We should check for both possibilities here to ensure compatibility across versions. - // See: https://github.com/WordPress/wordpress-develop/pull/9557. for more details. + // See: https://github.com/WordPress/wordpress-develop/pull/9557 for more details. if ( version_compare( get_bloginfo( 'version' ), '6.9', '>=' ) ) { $this->assertStringContainsString( esc_url_raw( get_permalink( $post->ID ) ), $localized_data ); } else { @@ -396,11 +396,8 @@ public function testAdminPostIdFilterOverridesLocalizedPostId() { } /** - * Test that edac_filter_post_is_latest_posts_home causes the scan URL to use the home URL. - * - * When show_on_front=posts and page_for_posts=0, the standard WP options cannot identify a - * virtual homepage post. The filter lets extensions signal this so we use get_home_url() - * for the scanner iframe rather than an invalid preview URL. + * When the filter flags a latest-posts homepage (show_on_front=posts), the scan URL + * uses get_home_url() rather than an invalid preview link. * * @return void */ @@ -438,12 +435,8 @@ public function testScanUrlUsesHomeUrlWhenLatestPostsHomeFilterReturnsTrue() { } /** - * Test that edac_filter_post_is_latest_posts_home causes the scan URL to use the home URL - * when show_on_front=page but no static front page is configured (fallback case). - * - * WordPress falls back to showing latest posts when show_on_front=page but page_on_front - * is empty. The $is_latest_posts_home condition must cover this case so extensions can - * still signal that get_home_url() should be used as the scan URL. + * Fallback case: show_on_front=page with no static front page configured also + * counts as a latest-posts homepage, so the scan URL still uses get_home_url(). * * @return void */ @@ -480,11 +473,8 @@ public function testScanUrlUsesHomeUrlWhenShowOnFrontIsPageWithNoFrontPageConfig } /** - * Test that the 'active' flag in edac_editor_app reflects the filtered post ID's post type. - * - * Before Fix 3, $active was set to $is_scannable_post which was computed from the global - * $post before the edac_filter_admin_post_id filter ran. If the filter returns a different - * post ID whose type is not scannable, $active must be false so the scanner doesn't run. + * The 'active' flag must reflect the filtered post ID's type: when the filter returns a + * non-scannable post type, $active is false so the scanner doesn't run. * * @return void */ From b7f0e59c5cff4fb3dedc8d5f1da095967bfacdcb Mon Sep 17 00:00:00 2001 From: William Patton Date: Tue, 14 Jul 2026 18:32:46 +0100 Subject: [PATCH 24/26] Bump version * Version: 1.46.0 -> 1.47.0 --- accessibility-checker.php | 4 ++-- package.json | 2 +- readme.txt | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/accessibility-checker.php b/accessibility-checker.php index d7b477c3a..d6b406194 100755 --- a/accessibility-checker.php +++ b/accessibility-checker.php @@ -10,7 +10,7 @@ * Plugin Name: Accessibility Checker * Plugin URI: https://equalizedigital.com/accessibility-checker * Description: Audit and check your website for accessibility before you hit publish. In-post accessibility scanner and guidance. - * Version: 1.46.0 + * Version: 1.47.0 * Requires PHP: 7.4 * Author: Equalize Digital * Author URI: https://equalizedigital.com @@ -36,7 +36,7 @@ // Current plugin version. if ( ! defined( 'EDAC_VERSION' ) ) { - define( 'EDAC_VERSION', '1.46.0' ); + define( 'EDAC_VERSION', '1.47.0' ); } // Current database version. diff --git a/package.json b/package.json index 093cd052a..0cb1e6217 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "accessibility-checker", - "version": "1.46.0", + "version": "1.47.0", "description": "Audit and check your website for accessibility before you hit publish. In-post accessibility scanner and guidance.", "author": "Equalize Digital", "license": "GPL-2.0+", diff --git a/readme.txt b/readme.txt index c16fea1d7..a05a5ea51 100644 --- a/readme.txt +++ b/readme.txt @@ -1,9 +1,9 @@ === Equalize Digital Accessibility Checker - WCAG, ADA, EAA and Section 508 compliance === Contributors: equalizedigital, alh0319, stevejonesdev Tags: accessibility, EAA, WCAG, ADA, WP accessibility -Requires at least: 6.7 +Requires at least: 6.8 Tested up to: 7.0 -Stable tag: 1.46.0 +Stable tag: 1.47.0 Requires PHP: 7.4 License: GPL-2.0-or-later License URI: https://www.gnu.org/licenses/gpl-2.0.html From 6d566fff34b765d74a806dbc8d2c8a89dfcbadfb Mon Sep 17 00:00:00 2001 From: William Patton Date: Tue, 14 Jul 2026 18:32:46 +0100 Subject: [PATCH 25/26] Update @since placeholders to 1.47.0 --- includes/helper-functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/helper-functions.php b/includes/helper-functions.php index 5e09e0920..cb51b374b 100644 --- a/includes/helper-functions.php +++ b/includes/helper-functions.php @@ -793,7 +793,7 @@ function edac_parse_html_for_media( $html ) { * don't execute scripts or event handlers in SVGs loaded as images. Returns * a bare (payload-less) data URI if given anything other than a string. * - * @since x.x.x + * @since 1.47.0 * * @param mixed $svg_markup Raw SVG markup - expected to be a string. * @return string Unescaped data URI - callers must esc_url() it before output, From bcffe7224d82ca7a9aee16634a46cccd87c0abdc Mon Sep 17 00:00:00 2001 From: William Patton Date: Tue, 14 Jul 2026 18:36:46 +0100 Subject: [PATCH 26/26] Add changelog for v1.47.0 Co-Authored-By: Claude Fable 5 --- changelog.txt | 10 ++++++++++ readme.txt | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/changelog.txt b/changelog.txt index 93716661c..09348ae3b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,15 @@ *** Accessibility Checker *** +2026-07-14 - version 1.47.0 +* Updated - the frontend highlighter now draws a white ring around the outline so highlighted elements stay visible on any background color. +* Updated - the empty alt text check no longer flags 1x1 tracking pixels. +* Updated - the link protocol checks now handle href values that contain leading whitespace. +* Updated - SVG code snippets in the Accessibility Analysis panel are now rendered as images instead of raw markup. +* Fix - scan results for the homepage are now stored against the homepage instead of the first blog post when the site is set to show latest posts. +* Fix - the Simplified Summary text areas in the editor sidebar now have a label that assistive technology can announce. +* Fix - corrected a caching issue that could cause database table name lookups to return the wrong table. +* Remove - deleted an unused internal helper function. + 2026-07-08 - version 1.46.0 * Updated - the aria-hidden rule no longer flags core Cover blocks. * Updated - the Incorrect Heading Order summary no longer claims specific heading levels that may not match the actual issue. diff --git a/readme.txt b/readme.txt index a05a5ea51..8854adaec 100644 --- a/readme.txt +++ b/readme.txt @@ -279,6 +279,16 @@ You can report security bugs through the Patchstack Vulnerability Disclosure Pro == Changelog == +2026-07-14 - version 1.47.0 +* Updated - the frontend highlighter now draws a white ring around the outline so highlighted elements stay visible on any background color. +* Updated - the empty alt text check no longer flags 1x1 tracking pixels. +* Updated - the link protocol checks now handle href values that contain leading whitespace. +* Updated - SVG code snippets in the Accessibility Analysis panel are now rendered as images instead of raw markup. +* Fix - scan results for the homepage are now stored against the homepage instead of the first blog post when the site is set to show latest posts. +* Fix - the Simplified Summary text areas in the editor sidebar now have a label that assistive technology can announce. +* Fix - corrected a caching issue that could cause database table name lookups to return the wrong table. +* Remove - deleted an unused internal helper function. + 2026-07-08 - version 1.46.0 * Updated - the aria-hidden rule no longer flags core Cover blocks. * Updated - the Incorrect Heading Order summary no longer claims specific heading levels that may not match the actual issue.