diff --git a/.github/workflows/test-assistant-api-rest-change-tracker.yml b/.github/workflows/test-assistant-api-rest-change-tracker.yml index 6ead4960156..fcc2451d42c 100644 --- a/.github/workflows/test-assistant-api-rest-change-tracker.yml +++ b/.github/workflows/test-assistant-api-rest-change-tracker.yml @@ -59,8 +59,8 @@ jobs: <${{ github.event.pull_request.html_url }}|${{ github.event.pull_request.title }}> *Labels:* ${{ join(github.event.pull_request.labels.*.name, ', ') }} *Monthly Release Milestone:* <${{ github.event.pull_request.milestone.html_url }}|${{ github.event.pull_request.milestone.title }}> (Release Date: ${{ env.MILESTONE_DATE }}) - *WooAF (weekly) Timeline: this PR can be tested from:* ${{ env.TEST_DATE_MESSAGE }} - Please visit the <#${{ secrets.WOO_CORE_RELEASES_SLACK_CHANNEL }}> to obtain the latest WooAF build for testing. + Please check the Milestone above and test using the latest . + If a pre-release build for the stated Milestone does not exist, please use the Nightly build. slack-optional-unfurl_links: false slack-optional-unfurl_media: false diff --git a/.syncpackrc b/.syncpackrc index 9b34a1b2fe4..ce95ab63970 100644 --- a/.syncpackrc +++ b/.syncpackrc @@ -12,7 +12,7 @@ "dependencies": [ "pnpm" ], - "pinVersion": "^9.1.0", + "pinVersion": "9.1.3", "packages": [ "**" ] @@ -88,7 +88,8 @@ "webpack*" ], "packages": [ - "@woocommerce/block-library" + "@woocommerce/block-library", + "@woocommerce/storybook" ], "isIgnored": true }, @@ -172,7 +173,7 @@ "packages": [ "**" ], - "pinVersion": "^1.45.1" + "pinVersion": "^1.46.1" }, { "dependencies": [ @@ -199,7 +200,10 @@ "@wordpress/viewport", "@wordpress/interface", "@wordpress/router", - "@wordpress/edit-site" + "@wordpress/edit-site", + "@wordpress/private-apis", + "@wordpress/dataviews", + "@wordpress/icons" ], "packages": [ "@woocommerce/block-templates", diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index f6b77a94530..06f3593c515 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -4,7 +4,7 @@ This document aims to provide as much context as possible to aid in the developm ## Getting Started -Please refer to [the Getting Started section of the `README.md`](README.md#getting-started) for a general-purpose guide on getting started. The rest of this document will assume that you've installed all of the prequisites and setup described there. +Please refer to [the Getting Started section of the `README.md`](README.md#getting-started) for a general-purpose guide on getting started. The rest of this document will assume that you've installed all of the prerequisites and setup described there. ### Plugin, Package, and Tool Filtering diff --git a/changelog.txt b/changelog.txt index 01b8c0ed896..e34b03260d4 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,351 @@ == Changelog == += 9.2.3 2024-08-26 = + +**WooCommerce** + +* Fix - Ensure translation is fully loaded for certain parts of Checkout block. [#50892](https://github.com/woocommerce/woocommerce/pull/50892) + + += 9.2.2 2024-08-22 = + +**WooCommerce** + +* Fix - Revert PR#48731 to address possible issues with plugins using WC's bundled select2 package. [#50854](https://github.com/woocommerce/woocommerce/pull/50854) +* Fix - Partially revert PR#48709 as it could cause issues for some users of the REST API system_status endpoint. [#50881](https://github.com/woocommerce/woocommerce/pull/50881) + + += 9.2.1 2024-08-21 = + +**WooCommerce** + +* Fix - Revert turning of AccessiblePrivateMethods::_accessible_private_methods into a static property because it caused fatal errors in some edge cases. [#50809](https://github.com/woocommerce/woocommerce/pull/50809) + += 9.2.0 2024-08-20 = + +**WooCommerce** + +* Update - Enhanced sanitization in `@woocommerce/currency`. [#50802](https://github.com/woocommerce/woocommerce/pull/50802) +* Fix - Hardening against XSS by leveraging HTML API for adding block attribute data. [#50801](https://github.com/woocommerce/woocommerce/pull/50801) +* Fix - Prevent downloads of digitial products after partial refund. [#50804](https://github.com/woocommerce/woocommerce/pull/50804) +* Add - Adds support for tracking downloads when a partial (ranged) request is made. [#50805](https://github.com/woocommerce/woocommerce/pull/50805) +* Update - Turn AccessiblePrivateMethods::_accessible_private_methods into a static property. [#50806](https://github.com/woocommerce/woocommerce/pull/50806) +* Fix - Correct label of shipping dimensions length field. [#50180](https://github.com/woocommerce/woocommerce/pull/50180) +* Fix - Allow new accounts to set new password even if logged in. [#50700](https://github.com/woocommerce/woocommerce/pull/50700) +* Fix - Remove global_unique_id from interface and add warning in case it is not implemented [#50685](https://github.com/woocommerce/woocommerce/pull/50685) +* Fix - CYS: avoid to enqueue Jetpack scripts [#50679](https://github.com/woocommerce/woocommerce/pull/50679) +* Fix - Clear product unique ID (`global_unique_id`) when duplicating products. [#50629](https://github.com/woocommerce/woocommerce/pull/50629) +* Fix - Resolved an issue that caused the page title and content text to display in the incorrect order on the Order Confirmation page. [#50592](https://github.com/woocommerce/woocommerce/pull/50592) +* Fix - Customer Account - Maintain the size of the icon in smaller screens. [#50410](https://github.com/woocommerce/woocommerce/pull/50410) +* Fix - Accessibility: Prevent shipping losing focus when making selections during checkout. [#48370](https://github.com/woocommerce/woocommerce/pull/48370) +* Fix - Add aria-label to mini-cart button on first render to improve accessibility [#48329](https://github.com/woocommerce/woocommerce/pull/48329) +* Fix - Add missing script dependencies to product variation generation script. [#49595](https://github.com/woocommerce/woocommerce/pull/49595) +* Fix - Address timing issue with React 18 when unregistering blocks and add missing dependency to editor bootstrapping code. [#49642](https://github.com/woocommerce/woocommerce/pull/49642) +* Fix - Add the "Customer account" block into the "Minimal Header" to fix the spacing. [#49893](https://github.com/woocommerce/woocommerce/pull/49893) +* Fix - Avoid Product Search Results block template to fall back to the Product Catalog template from the theme [#48887](https://github.com/woocommerce/woocommerce/pull/48887) +* Fix - Changed from using React.render to React.createRoot for product editor areas as it has been deprecated since React 18 [#48834](https://github.com/woocommerce/woocommerce/pull/48834) +* Fix - Check if parent product exists when creating attribute lookup data for variations [#49474](https://github.com/woocommerce/woocommerce/pull/49474) +* Fix - Check if there's an actual session available inside wc_clear_cart_after_payment [#45821](https://github.com/woocommerce/woocommerce/pull/45821) +* Fix - Comment: Update db_version variable and use it to prevent adding global_unique_id when the lookup table was not yet updated [#49472](https://github.com/woocommerce/woocommerce/pull/49472) +* Fix - Correctly escape the HTML when linking customer orders. [#49195](https://github.com/woocommerce/woocommerce/pull/49195) +* Fix - CSY - Fix intro banner copy for existing themes [#49787](https://github.com/woocommerce/woocommerce/pull/49787) +* Fix - CYS - Fix marking the CYS task as completed only by accessing the Intro page. [#49374](https://github.com/woocommerce/woocommerce/pull/49374) +* Fix - CYS - Remove the site title block length from the "Large Header" and the "Centered Menu Header". [#48671](https://github.com/woocommerce/woocommerce/pull/48671) +* Fix - CYS: Disable readonly mode only when full composability feature flag is enabled. [#48752](https://github.com/woocommerce/woocommerce/pull/48752) +* Fix - CYS: Ensure that the button in the Intro page redirects to the Customizer when a classic theme is enabled. [#49804](https://github.com/woocommerce/woocommerce/pull/49804) +* Fix - CYS: fix: Assembler follows admin color schema. [#49159](https://github.com/woocommerce/woocommerce/pull/49159) +* Fix - CYS: fix arrow welcome tour [#49607](https://github.com/woocommerce/woocommerce/pull/49607) +* Fix - CYS: Fix border width pattern preview. [#49753](https://github.com/woocommerce/woocommerce/pull/49753) +* Fix - CYS: Fix button background on Featured Category Cover image [#49659](https://github.com/woocommerce/woocommerce/pull/49659) +* Fix - CYS: fix drag to resize bar [#49657](https://github.com/woocommerce/woocommerce/pull/49657) +* Fix - CYS: fix flickering effect. [#48767](https://github.com/woocommerce/woocommerce/pull/48767) +* Fix - CYS: fix logic to disable click on the no block placeholder [#48722](https://github.com/woocommerce/woocommerce/pull/48722) +* Fix - CYS: Fix no block placeholder style. [#49673](https://github.com/woocommerce/woocommerce/pull/49673) +* Fix - CYS: Fix pattern rendering issues [#49041](https://github.com/woocommerce/woocommerce/pull/49041) +* Fix - CYS: fix pattern wrapped twice by group blocks [#48712](https://github.com/woocommerce/woocommerce/pull/48712) +* Fix - CYS: fix selected header/footer pattern [#49788](https://github.com/woocommerce/woocommerce/pull/49788) +* Fix - CYS: fix shuffle feature logic. [#49153](https://github.com/woocommerce/woocommerce/pull/49153) +* Fix - CYS: fix the default intro pattern. [#49082](https://github.com/woocommerce/woocommerce/pull/49082) +* Fix - CYS: Fix the tooltip overlap with the pattern. [#49700](https://github.com/woocommerce/woocommerce/pull/49700) +* Fix - CYS: fix toolbar position after the site preview resizes [#49028](https://github.com/woocommerce/woocommerce/pull/49028) +* Fix - CYS: hide button to resize the image [#48714](https://github.com/woocommerce/woocommerce/pull/48714) +* Fix - CYS: Hide shuffle button when only one pattern is available [#49790](https://github.com/woocommerce/woocommerce/pull/49790) +* Fix - CYS: not enable PTK features on WordPress 6.5. [#49591](https://github.com/woocommerce/woocommerce/pull/49591) +* Fix - CYS: open Intro panel when user clicks on Design your homepage [#49005](https://github.com/woocommerce/woocommerce/pull/49005) +* Fix - CYS: Remove not necessary placeholder on homepage sidebar. [#49705](https://github.com/woocommerce/woocommerce/pull/49705) +* Fix - CYS: show default TT4 fonts pair. [#49675](https://github.com/woocommerce/woocommerce/pull/49675) +* Fix - CYS: show logo when the header is updated. [#49681](https://github.com/woocommerce/woocommerce/pull/49681) +* Fix - Display "View store" button text by default in the toolbar. [#48690](https://github.com/woocommerce/woocommerce/pull/48690) +* Fix - Display admin notice of expired and expiring Woo subscription when the product + is connected / activated on the site. [#49717](https://github.com/woocommerce/woocommerce/pull/49717) +* Fix - Do not set the `tk_ai` tracking cookie if tracking is disabled. [#47863](https://github.com/woocommerce/woocommerce/pull/47863) +* Fix - Ensure the second address line input appears when using autofill [#49730](https://github.com/woocommerce/woocommerce/pull/49730) +* Fix - Ensure the Store API recalculates cart totals prior to running validation hooks. [#49455](https://github.com/woocommerce/woocommerce/pull/49455) +* Fix - Exclude simple products from variations reports by default. [#49244](https://github.com/woocommerce/woocommerce/pull/49244) +* Fix - Failure to fetch the country list no longer blocks users in the profiler. [#49519](https://github.com/woocommerce/woocommerce/pull/49519) +* Fix - Fetch site cache status correctly if directly navigating to LYS Success page, and some refactoring [#48710](https://github.com/woocommerce/woocommerce/pull/48710) +* Fix - Fix: Make woocommerce/product-price available in the Single Product template [#49906](https://github.com/woocommerce/woocommerce/pull/49906) +* Fix - Fix a bug where woocommerce removes the current-menu-item class. [#45095](https://github.com/woocommerce/woocommerce/pull/45095) +* Fix - Fix add product task to create template first and simplify logic [#49631](https://github.com/woocommerce/woocommerce/pull/49631) +* Fix - Fix add zone button flinching and vertical centering [#48869](https://github.com/woocommerce/woocommerce/pull/48869) +* Fix - Fix Analytics - Tax Report Pagination [#49562](https://github.com/woocommerce/woocommerce/pull/49562) +* Fix - Fix confusing messages prompting switch to classic templates [#48717](https://github.com/woocommerce/woocommerce/pull/48717) +* Fix - Fix default shipping selection when changing between pickup and shipping on block checkout. [#49718](https://github.com/woocommerce/woocommerce/pull/49718) +* Fix - Fixed "woocommerce_new_order" triggering on checkout blocks page visit. [#47422](https://github.com/woocommerce/woocommerce/pull/47422) +* Fix - Fixed a bug causing account email not to be taken in consideration for coupon validation when a customer has a different billing email set. [#48488](https://github.com/woocommerce/woocommerce/pull/48488) +* Fix - Fixed a bug that would cause incorrect pricing at checkout for very large amounts. [#49361](https://github.com/woocommerce/woocommerce/pull/49361) +* Fix - Fixed a bug where the close button is not visible on the mini cart when viewed on a mobile device [#48769](https://github.com/woocommerce/woocommerce/pull/48769) +* Fix - Fixed horizontal and vertical layout overflows on LYS success page [#49127](https://github.com/woocommerce/woocommerce/pull/49127) +* Fix - Fix fatal error in order reports when parent order doesn't exist [#49006](https://github.com/woocommerce/woocommerce/pull/49006) +* Fix - Fix get_options deprecated notice when viewing Analytics > Orders [#49092](https://github.com/woocommerce/woocommerce/pull/49092) +* Fix - Fix Order Count inconsistency between stats and reports [#49283](https://github.com/woocommerce/woocommerce/pull/49283) +* Fix - Fix sidebar attribute control in Products by Attribute block [#49351](https://github.com/woocommerce/woocommerce/pull/49351) +* Fix - Fix site coming soon page heading color [#49129](https://github.com/woocommerce/woocommerce/pull/49129) +* Fix - Fix Task List - Reminder bar close button styling [#49532](https://github.com/woocommerce/woocommerce/pull/49532) +* Fix - Fix the mini cart items not being visible when zoomed in [#48384](https://github.com/woocommerce/woocommerce/pull/48384) +* Fix - Fix the namespace of the RestApiControllerBase class [#49333](https://github.com/woocommerce/woocommerce/pull/49333) +* Fix - Fix the undefined array key "name" warning in ComingSoonRequestHandler.php when the font name is not set [#49795](https://github.com/woocommerce/woocommerce/pull/49795) +* Fix - Fix variation name in analytics variations report [#49440](https://github.com/woocommerce/woocommerce/pull/49440) +* Fix - Issue fixed where tags are overlapping divider line in "Filter by product category". [#48756](https://github.com/woocommerce/woocommerce/pull/48756) +* Fix - Made coupon fields during block checkout stack on smaller screen sizes [#48623](https://github.com/woocommerce/woocommerce/pull/48623) +* Fix - Make sure the correct block template file is used in the Site Editor for templates with fallback [#48621](https://github.com/woocommerce/woocommerce/pull/48621) +* Fix - Make the edit/delete actions of shipping/products in manual orders accessible [#48238](https://github.com/woocommerce/woocommerce/pull/48238) +* Fix - Make the Leaderboards on the Analytics > Dashboard page use consistent currency and number formatting across the page, and perceive the currency setting comes from the relevant filter. [#49097](https://github.com/woocommerce/woocommerce/pull/49097) +* Fix - Narrowed scope of block theme notice templates so other template overrides are unaffected [#48686](https://github.com/woocommerce/woocommerce/pull/48686) +* Fix - Prevent a warning showing when using the Shipping Address Calculator in combination with the additional checkout fields API [#49685](https://github.com/woocommerce/woocommerce/pull/49685) +* Fix - Prevent download permissions metabox from being toggled when toggling individual permission details. [#49022](https://github.com/woocommerce/woocommerce/pull/49022) +* Fix - Prevent possible type error in BatchProcessingController. [#49728](https://github.com/woocommerce/woocommerce/pull/49728) +* Fix - Prevent product editor styles loading on non wc-admin pages [#49170](https://github.com/woocommerce/woocommerce/pull/49170) +* Fix - Product Collection: allow custom collections to hide all of the filter controls, not only some of them [#49713](https://github.com/woocommerce/woocommerce/pull/49713) +* Fix - Product Collection: Fix alignment of the first item in Grid layout for WP 6.6 [#49096](https://github.com/woocommerce/woocommerce/pull/49096) +* Fix - Product Collection: Fix the Preview badge's corner radius [#48856](https://github.com/woocommerce/woocommerce/pull/48856) +* Fix - Product Collection: Show "Sync with current query" option only in archive templates where it makes sense [#48917](https://github.com/woocommerce/woocommerce/pull/48917) +* Fix - Product Fitlers: Bring back pattern [#49601](https://github.com/woocommerce/woocommerce/pull/49601) +* Fix - Product rating - Inherit the color of the star when no ratings [#49678](https://github.com/woocommerce/woocommerce/pull/49678) +* Fix - Provide more informative errors if a refund cannot be requested via the REST API, due to plugin conflicts. [#47918](https://github.com/woocommerce/woocommerce/pull/47918) +* Fix - Redirect the lost password page to edit account while logged in. [#49670](https://github.com/woocommerce/woocommerce/pull/49670) +* Fix - Removes several side effects in the code bases that caused translations to be loaded too early. [#47113](https://github.com/woocommerce/woocommerce/pull/47113) +* Fix - Remove strong tags from patterns [#49901](https://github.com/woocommerce/woocommerce/pull/49901) +* Fix - Replace the red CSS color with the $red SASS variable in WooCommerce legacy elements [#48742](https://github.com/woocommerce/woocommerce/pull/48742) +* Fix - Resolved issues with new order hook triggers during transitions to and from draft statuses. [#49098](https://github.com/woocommerce/woocommerce/pull/49098) +* Fix - Set coming soon preview body aspect ratio to 1/1 to fix broken template [#49749](https://github.com/woocommerce/woocommerce/pull/49749) +* Fix - Show variations only for the selected product's variations [#49470](https://github.com/woocommerce/woocommerce/pull/49470) +* Fix - Tooltips message is now selected based on the order status instead of the label of the order status, which would break if the current WordPress language was not English. Also passes the order object to the woocommerce_get_order_status_labels filter to allow for more personalized tooltips. [#49812](https://github.com/woocommerce/woocommerce/pull/49812) +* Fix - Update allowed statuses in legacy payment handler for checkout block. [#48788](https://github.com/woocommerce/woocommerce/pull/48788) +* Add - Use UTM parameters to link Tracks events from connect notice CTA and successful site connection [#50126](https://github.com/woocommerce/woocommerce/pull/50126) +* Add - Add a generic function to determine remote logging eligibility [#49371](https://github.com/woocommerce/woocommerce/pull/49371) +* Add - Add a rest api to manage the product custom fields [#48504](https://github.com/woocommerce/woocommerce/pull/48504) +* Add - Add column `global_unique_id` to `wc_product_meta_lookup` table + Add global_unique_id field to product and product variations [#49472](https://github.com/woocommerce/woocommerce/pull/49472) +* Add - Added notice to the order confirmation page for new accounts instructing them to change the default password. [#48673](https://github.com/woocommerce/woocommerce/pull/48673) +* Add - Add global unique ID field to the classic product editor [#49312](https://github.com/woocommerce/woocommerce/pull/49312) +* Add - Add placeholder options and validation to checkout country and state select inputs. [#49616](https://github.com/woocommerce/woocommerce/pull/49616) +* Add - Add Refresh Remote Inbox Notifications and Delete Remote Notification Tools [#48961](https://github.com/woocommerce/woocommerce/pull/48961) +* Add - Add some robots.txt rules about directories created by WooCommerce [#49432](https://github.com/woocommerce/woocommerce/pull/49432) +* Add - Adds product usage notice [#47697](https://github.com/woocommerce/woocommerce/pull/47697) +* Add - Add support to run e2e tests against WPCOM website. [#49403](https://github.com/woocommerce/woocommerce/pull/49403) +* Add - Add UTM parameters to missing payment method notice links. [#49621](https://github.com/woocommerce/woocommerce/pull/49621) +* Add - Add validation for `__experimentalRegisterProductCollection` arguments [#48141](https://github.com/woocommerce/woocommerce/pull/48141) +* Add - CYS: add badge that informs how many patterns have been inserted from each category. [#48668](https://github.com/woocommerce/woocommerce/pull/48668) +* Add - CYS: Add default patterns. [#48839](https://github.com/woocommerce/woocommerce/pull/48839) +* Add - CYS: Add Tracking for Fiverr Logo Maker CTA [#49783](https://github.com/woocommerce/woocommerce/pull/49783) +* Add - CYS: Enable the PTK feature. [#49565](https://github.com/woocommerce/woocommerce/pull/49565) +* Add - Expose __experimentalRegisterProductCollection in @woocommerce/blocks-registry Package [#48141](https://github.com/woocommerce/woocommerce/pull/48141) +* Add - Improvements in the handling of feature compatibility for plugins [#48169](https://github.com/woocommerce/woocommerce/pull/48169) +* Add - Product Collection: Rename "Sync with current query" option to "Use page context" and make it working in non-archive context as well [#49627](https://github.com/woocommerce/woocommerce/pull/49627) +* Update - Add pattern validation for global_unique_id [#50501](https://github.com/woocommerce/woocommerce/pull/50501) +* Update - Clear global_unique_id when restoring a product that doesn't have an unique id [#50496](https://github.com/woocommerce/woocommerce/pull/50496) +* Update - Use admin password reset on admin login screen [#50200](https://github.com/woocommerce/woocommerce/pull/50200) +* Update - Prevent creation of password-protected coupons. [#50236](https://github.com/woocommerce/woocommerce/pull/50236) +* Update - Add a link to the Theming docs from the blockified templates README.md file [#48538](https://github.com/woocommerce/woocommerce/pull/48538) +* Update - Add a new icon style to the "Customer Account" block. [#48979](https://github.com/woocommerce/woocommerce/pull/48979) +* Update - Adds setting to control the visibility of product count in Mini cart block [#48545](https://github.com/woocommerce/woocommerce/pull/48545) +* Update - Add tracking for enable auto renew links on notices [#49710](https://github.com/woocommerce/woocommerce/pull/49710) +* Update - Add UTM parameters to subscription renewal notice links. [#49645](https://github.com/woocommerce/woocommerce/pull/49645) +* Update - Adjust input field height and input label text size. [#49636](https://github.com/woocommerce/woocommerce/pull/49636) +* Update - Adjust top margin of the coupon code in the Cart and Checkout blocks. [#49603](https://github.com/woocommerce/woocommerce/pull/49603) +* Update - CYS - Add borders to footer and header patterns on the assembler. [#49299](https://github.com/woocommerce/woocommerce/pull/49299) +* Update - CYS - Add missing patterns to their categories for the assembler [#49154](https://github.com/woocommerce/woocommerce/pull/49154) +* Update - CYS - Add tracking events to pattern features. [#49556](https://github.com/woocommerce/woocommerce/pull/49556) +* Update - CYS - Disable the Full Composability for CYS AI flows [#49290](https://github.com/woocommerce/woocommerce/pull/49290) +* Update - CYS - Fetch patterns from the private dotcom patterns category instead of from the default source site. [#49007](https://github.com/woocommerce/woocommerce/pull/49007) +* Update - CYS - Fetch patterns from the WooCommerce PTK source site. [#48492](https://github.com/woocommerce/woocommerce/pull/48492) +* Update - CYS - Filter out patterns with external dependencies. [#48618](https://github.com/woocommerce/woocommerce/pull/48618) +* Update - CYS - Fix CSS spacing issues in the assembler. [#49232](https://github.com/woocommerce/woocommerce/pull/49232) +* Update - CYS - Fix dark patterns buttons color. [#49181](https://github.com/woocommerce/woocommerce/pull/49181) +* Update - CYS - Fix the column spacing for the "Four Image Grid Content Left" pattern [#49669](https://github.com/woocommerce/woocommerce/pull/49669) +* Update - CYS - Fix the font size of the "DON'T HAVE A LOGO YET?" title. [#49231](https://github.com/woocommerce/woocommerce/pull/49231) +* Update - CYS - Fix the intro cards size to match the designs. [#49297](https://github.com/woocommerce/woocommerce/pull/49297) +* Update - CYS - Fix the pattern preview border color on hover and for inserted patterns. [#49206](https://github.com/woocommerce/woocommerce/pull/49206) +* Update - CYS - Improve margins for CYS core patterns. [#49196](https://github.com/woocommerce/woocommerce/pull/49196) +* Update - CYS - Improve the designs of the Intro page bottom cards. [#48983](https://github.com/woocommerce/woocommerce/pull/48983) +* Update - CYS - Include the dotcom patterns from the "Reviews" category. [#49140](https://github.com/woocommerce/woocommerce/pull/49140) +* Update - CYS - Make some titles bold on CYS patterns. [#49151](https://github.com/woocommerce/woocommerce/pull/49151) +* Update - CYS - Make the patterns content translatable for patterns in the dictionary. [#49633](https://github.com/woocommerce/woocommerce/pull/49633) +* Update - CYS - Register PTK "Testimonials" patterns as "Reviews" [#48674](https://github.com/woocommerce/woocommerce/pull/48674) +* Update - CYS - Remove non-default patterns and register them from the PTK. Update margins. [#49101](https://github.com/woocommerce/woocommerce/pull/49101) +* Update - CYS - Schedule the `fetch_patterns` actions only once every hour. [#49754](https://github.com/woocommerce/woocommerce/pull/49754) +* Update - CYS - Update pattern categories and its descriptions. [#48665](https://github.com/woocommerce/woocommerce/pull/48665) +* Update - CYS - Update pattern toolbar delete button copy to `Delete`. [#49295](https://github.com/woocommerce/woocommerce/pull/49295) +* Update - CYS - Update the full composability layout styles [#49303](https://github.com/woocommerce/woocommerce/pull/49303) +* Update - CYS - Update the intro pages for different type of themes. [#49910](https://github.com/woocommerce/woocommerce/pull/49910) +* Update - CYS: Add the proper tracking string to the external Fiverr link in sidebar of the **Add your logo** screen. [#49745](https://github.com/woocommerce/woocommerce/pull/49745) +* Update - CYS: Add `rel="noreferrer"` to External Fiverr Link in sidebar of **Add your logo** screen. [#49314](https://github.com/woocommerce/woocommerce/pull/49314) +* Update - CYS: Improve Block Toolbar logic. [#48799](https://github.com/woocommerce/woocommerce/pull/48799) +* Update - CYS: improve copy no blocks placeholder. [#49030](https://github.com/woocommerce/woocommerce/pull/49030) +* Update - CYS: Improve patterns order [#49204](https://github.com/woocommerce/woocommerce/pull/49204) +* Update - CYS: no highlight the pattern when it is added. [#48802](https://github.com/woocommerce/woocommerce/pull/48802) +* Update - CYS: Remove margin last pattern preview [#49767](https://github.com/woocommerce/woocommerce/pull/49767) +* Update - CYS: Remove not necessary patterns. [#48750](https://github.com/woocommerce/woocommerce/pull/48750) +* Update - CYS: Revisit sidebar layout. [#48803](https://github.com/woocommerce/woocommerce/pull/48803) +* Update - CYS: Update Block Toolbar Position [#48662](https://github.com/woocommerce/woocommerce/pull/48662) +* Update - CYS: Update icon used by the "Customer account" block into header patterns [#49133](https://github.com/woocommerce/woocommerce/pull/49133) +* Update - CYS: Update sidebar homepage copy [#48882](https://github.com/woocommerce/woocommerce/pull/48882) +* Update - CYS: Update verbiage in the CTA to our Fiverr Logo Maker landing page. [#48987](https://github.com/woocommerce/woocommerce/pull/48987) +* Update - CYS: when the footer/header is clicked, the border color is blue. [#48765](https://github.com/woocommerce/woocommerce/pull/48765) +* Update - Deprecate and create new coming soon block version [#49786](https://github.com/woocommerce/woocommerce/pull/49786) +* Update - E2E: check the `Add` button when creating product variations in the new Product Editor [#48928](https://github.com/woocommerce/woocommerce/pull/48928) +* Update - E2E: in the new Product Editor app, update how to detect when global attributes are loaded. [#48915](https://github.com/woocommerce/woocommerce/pull/48915) +* Update - E2E: remove UI check when creating attribute global terms [#48934](https://github.com/woocommerce/woocommerce/pull/48934) +* Update - Ensure expiration-related modal is shown to the installed Woo subscriptions [#49747](https://github.com/woocommerce/woocommerce/pull/49747) +* Update - Ensures the product ID is valid when interacting with product variations via the REST API. [#48804](https://github.com/woocommerce/woocommerce/pull/48804) +* Update - Ensure that active plugins shown in the System Status api endpoint actually exist [#48709](https://github.com/woocommerce/woocommerce/pull/48709) +* Update - Ensuring product creation with unique sku for concurrent requests [#47476](https://github.com/woocommerce/woocommerce/pull/47476) +* Update - Fix: Show preview label only when Product Collection block is selected [#48795](https://github.com/woocommerce/woocommerce/pull/48795) +* Update - Fix Classic Template block registration on WP 6.6 [#48730](https://github.com/woocommerce/woocommerce/pull/48730) +* Update - Fix typo on Congratulations screen [#49233](https://github.com/woocommerce/woocommerce/pull/49233) +* Update - Hide account creation options not relevent to block checkout when using block checkout. [#49389](https://github.com/woocommerce/woocommerce/pull/49389) +* Update - Improve rendering of order list table on mobile. [#49592](https://github.com/woocommerce/woocommerce/pull/49592) +* Update - Improve the handling of the deprecated WC()->api property [#48884](https://github.com/woocommerce/woocommerce/pull/48884) +* Update - Make proceed to order button non sticky when zoom level is bigger than 100% [#48391](https://github.com/woocommerce/woocommerce/pull/48391) +* Update - Make Single Product gallery thumbnail images sharper by defining a srcset [#49112](https://github.com/woocommerce/woocommerce/pull/49112) +* Update - Migrate the cart and checkout block's state, country and custom field input to a native select [#48180](https://github.com/woocommerce/woocommerce/pull/48180) +* Update - Move remote logger to `./src` and improve `fetch_latest_woocommerce_version()` logic [#49639](https://github.com/woocommerce/woocommerce/pull/49639) +* Update - Optimize the method that gets the downloads count for a given download [#49008](https://github.com/woocommerce/woocommerce/pull/49008) +* Update - preparing checkout blocks docs for dev docs site [#49010](https://github.com/woocommerce/woocommerce/pull/49010) +* Update - Product Collection: revert renaming "Sync with current query" option [#49907](https://github.com/woocommerce/woocommerce/pull/49907) +* Update - Product Editor: improve E2E tests. Test the `+3 More` item label in the Organization tab [#48891](https://github.com/woocommerce/woocommerce/pull/48891) +* Update - Product Editor: restore and fix E2E test that creates product variations [#48725](https://github.com/woocommerce/woocommerce/pull/48725) +* Update - Product Editor: restore Product (local) Attributes E2E test [#48871](https://github.com/woocommerce/woocommerce/pull/48871) +* Update - Product Editor: update create product variations E2E test [#48627](https://github.com/woocommerce/woocommerce/pull/48627) +* Update - Redesigned the Product Collection block's insertion journey. [#48911](https://github.com/woocommerce/woocommerce/pull/48911) +* Update - Remove Jetpack copy experiment from core profiler [#49452](https://github.com/woocommerce/woocommerce/pull/49452) +* Update - Remove the hooked blocks feature gate and replace with wc_hooked_blocks_version option [#49302](https://github.com/woocommerce/woocommerce/pull/49302) +* Update - Replace the "Customer account" line logo. [#49666](https://github.com/woocommerce/woocommerce/pull/49666) +* Update - Return HTTP 404 for REST API requests involving non-existing tax class. [#48579](https://github.com/woocommerce/woocommerce/pull/48579) +* Update - Return HTTP 404 when accessing non-existent webhooks via REST API. [#48729](https://github.com/woocommerce/woocommerce/pull/48729) +* Update - Return HTTP 404 when trying to read or delete a non-existent product review. [#48726](https://github.com/woocommerce/woocommerce/pull/48726) +* Update - Run full e2e tests suite against Pressable and WPCOM websites. [#49597](https://github.com/woocommerce/woocommerce/pull/49597) +* Update - Update content for usage tracking modal for CYS experience [#49911](https://github.com/woocommerce/woocommerce/pull/49911) +* Update - Updated account settings descriptions for added clarity [#48556](https://github.com/woocommerce/woocommerce/pull/48556) +* Update - Update Jetpack's new SSO classes and methods to prevent deprecation notice. [#49752](https://github.com/woocommerce/woocommerce/pull/49752) +* Update - Update required and tested up to WP versions for the WordPress 6.6 release. [#49619](https://github.com/woocommerce/woocommerce/pull/49619) +* Update - Update Settings to disable Save button unless modifications are made. [#47444](https://github.com/woocommerce/woocommerce/pull/47444) +* Update - Update shipping method setup modal copy if the block-based local pickup is enabled [#48529](https://github.com/woocommerce/woocommerce/pull/48529) +* Update - Update Store Alert styles [#49174](https://github.com/woocommerce/woocommerce/pull/49174) +* Update - Update Store Alert widths to match main body [#48487](https://github.com/woocommerce/woocommerce/pull/48487) +* Update - Update the "Customer Account" block icon for the line style. [#49401](https://github.com/woocommerce/woocommerce/pull/49401) +* Update - Update the CYS opt-in messaging [#49894](https://github.com/woocommerce/woocommerce/pull/49894) +* Update - Update the footer section in "Add products" task [#49782](https://github.com/woocommerce/woocommerce/pull/49782) +* Update - Update WC Tasks in the WC Home. Rename to WooCommerce marketplace, add new browse marketplace, remove connect to woocomerce.com from inbox [#48128](https://github.com/woocommerce/woocommerce/pull/48128) +* Update - Utilize the new shared component to showcase WooPayments payment method logos. [#49300](https://github.com/woocommerce/woocommerce/pull/49300) +* Dev - Add Allure to Blocks e2e tests [#49228](https://github.com/woocommerce/woocommerce/pull/49228) +* Dev - Add daily checks for core e2e with PHP 8.1 and WP latest-1 [#48929](https://github.com/woocommerce/woocommerce/pull/48929) +* Dev - Add hook to customize the rendered receipt template [#48872](https://github.com/woocommerce/woocommerce/pull/48872) +* Dev - Add new CI workflow to trigger tests on demand. [#49674](https://github.com/woocommerce/woocommerce/pull/49674) +* Dev - Add support for e2e testing against external sites in CI [#49017](https://github.com/woocommerce/woocommerce/pull/49017) +* Dev - Another attempt to stabilize flaky Product Collection E2E tests. [#49638](https://github.com/woocommerce/woocommerce/pull/49638) +* Dev - Blocks E2E: Fix a flaky Product Collection test where accidental multiple edits occur and break the template saving step. [#49590](https://github.com/woocommerce/woocommerce/pull/49590) +* Dev - Blocks E2E: Fix DB snapshot removal step in setup script. [#49677](https://github.com/woocommerce/woocommerce/pull/49677) +* Dev - Build: speedup dependencies installation by disabling composer optimize autoloading by default. [#48980](https://github.com/woocommerce/woocommerce/pull/48980) +* Dev - CI: buffix for linting missing strict types directive. [#49015](https://github.com/woocommerce/woocommerce/pull/49015) +* Dev - CI: cleanup CI related command after fixing jobs matrix generation. [#49330](https://github.com/woocommerce/woocommerce/pull/49330) +* Dev - CI: code style fixes to pass linting in updated CI environment. [#49020](https://github.com/woocommerce/woocommerce/pull/49020) +* Dev - CI: improve flacky tests reporting job execution time. [#49665](https://github.com/woocommerce/woocommerce/pull/49665) +* Dev - CI: Re-group PHPUnit jobs. [#49443](https://github.com/woocommerce/woocommerce/pull/49443) +* Dev - CI: reduce running time for PHPUnit related jobs. [#49193](https://github.com/woocommerce/woocommerce/pull/49193) +* Dev - CI: tuning deps caching for playwright. [#49081](https://github.com/woocommerce/woocommerce/pull/49081) +* Dev - CI config: update changes lists to include wp-env config [#49626](https://github.com/woocommerce/woocommerce/pull/49626) +* Dev - Clean up unused files in plugins/woocommerce-blocks [#49319](https://github.com/woocommerce/woocommerce/pull/49319) +* Dev - Create a separate JS cart and checkout JavaScript bundle to improve performance. [#48010](https://github.com/woocommerce/woocommerce/pull/48010) +* Dev - CYS - Fix the "test_fetch_patterns_should_register_testimonials_category_as_reviews" tests. [#48719](https://github.com/woocommerce/woocommerce/pull/48719) +* Dev - Dropping select2 and point it to SelectWoo [#48731](https://github.com/woocommerce/woocommerce/pull/48731) +* Dev - E2E: enable slow tests reporting for blocks E2E tests. [#49367](https://github.com/woocommerce/woocommerce/pull/49367) +* Dev - E2E tests: fix basic spec for multiple environments [#49609](https://github.com/woocommerce/woocommerce/pull/49609) +* Dev - E2E tests: fix failing settings-tax e2e test [#48792](https://github.com/woocommerce/woocommerce/pull/48792) +* Dev - E2E tests: Fix flaky account email receiving test [#48957](https://github.com/woocommerce/woocommerce/pull/48957) +* Dev - E2E tests: Fix flaky connect to Woo.com test [#48926](https://github.com/woocommerce/woocommerce/pull/48926) +* Dev - E2E tests: Fix flaky filling regular price in the inventory tab [#49226](https://github.com/woocommerce/woocommerce/pull/49226) +* Dev - E2E tests: Fix flaky filling SKU field and CYS footer [#49191](https://github.com/woocommerce/woocommerce/pull/49191) +* Dev - E2E tests: Fix flaky Gutenberg, WC Services tests [#48916](https://github.com/woocommerce/woocommerce/pull/48916) +* Dev - E2E tests: Fix flaky Gutenberg tests [#48896](https://github.com/woocommerce/woocommerce/pull/48896) +* Dev - E2E tests: Fix flaky Gutenberg tests [#49548](https://github.com/woocommerce/woocommerce/pull/49548) +* Dev - E2E tests: Fix flaky logo picker waiting for response tests [#49451](https://github.com/woocommerce/woocommerce/pull/49451) +* Dev - E2E tests: Fix flaky merchant filling sku field in the new editor [#49134](https://github.com/woocommerce/woocommerce/pull/49134) +* Dev - E2E tests: Fix flaky merchant tests [#49381](https://github.com/woocommerce/woocommerce/pull/49381) +* Dev - E2E tests: Fix merchant settings general test [#48907](https://github.com/woocommerce/woocommerce/pull/48907) +* Dev - E2E tests: fix shopper checkout block test [#49596](https://github.com/woocommerce/woocommerce/pull/49596) +* Dev - E2E tests: fix some tests for WP 6.6 [#49634](https://github.com/woocommerce/woocommerce/pull/49634) +* Dev - E2E tests: tag different envs in e2e test suite to run in workflows [#48715](https://github.com/woocommerce/woocommerce/pull/48715) +* Dev - Final sanity check to make sure attributes are done saving [#48737](https://github.com/woocommerce/woocommerce/pull/48737) +* Dev - Fix broken syntax in e2e-guidelines.md. [#49018](https://github.com/woocommerce/woocommerce/pull/49018) +* Dev - Fix flaky orphaned refund test [#49741](https://github.com/woocommerce/woocommerce/pull/49741) +* Dev - Hide product filters overlay template part experimental feature from public release [#49564](https://github.com/woocommerce/woocommerce/pull/49564) +* Dev - In blocks codebase, export SITE_CURRENCY property with properties matching typescript definitions. [#48727](https://github.com/woocommerce/woocommerce/pull/48727) +* Dev - Lint new PHP files for strict types directive [#48943](https://github.com/woocommerce/woocommerce/pull/48943) +* Dev - Minor tooling tweaks (zip compression level, composer invocation) [#48857](https://github.com/woocommerce/woocommerce/pull/48857) +* Dev - Monorepo: enable Jest and babel-loader caching. [#49656](https://github.com/woocommerce/woocommerce/pull/49656) +* Dev - Monorepo: fix side-effects of setting strict mode for linting script. [#49436](https://github.com/woocommerce/woocommerce/pull/49436) +* Dev - Monorepo: minor tweaks in zip building script (use frozen lock file when installing dependecies). [#49640](https://github.com/woocommerce/woocommerce/pull/49640) +* Dev - Monorepo: refine approach to patching dependecies in favour of built-in pnpm functionality. [#49892](https://github.com/woocommerce/woocommerce/pull/49892) +* Dev - Monorepo: set strict mode for linting script. [#49366](https://github.com/woocommerce/woocommerce/pull/49366) +* Dev - Monorepo: tweak patching dependencies for better Webpack perfromance. [#49703](https://github.com/woocommerce/woocommerce/pull/49703) +* Dev - Move buildkite-test-collector to devDependencies [#49051](https://github.com/woocommerce/woocommerce/pull/49051) +* Dev - move docs out of main folder until subcategories are ready [#49354](https://github.com/woocommerce/woocommerce/pull/49354) +* Dev - Product Collection: add tracking for block usage [#46466](https://github.com/woocommerce/woocommerce/pull/46466) +* Dev - Remove performance tests from PR checks (leave on push to trunk) [#48927](https://github.com/woocommerce/woocommerce/pull/48927) +* Dev - Switch `render()` to `createRoot().render()` to use React 18 features. [#48858](https://github.com/woocommerce/woocommerce/pull/48858) +* Dev - Tests: pin wp-env core version to 6.5 (temporary until tests are fixed to pass with 6.6) [#49620](https://github.com/woocommerce/woocommerce/pull/49620) +* Dev - Tweaks related to caching Composer dependecies and Playwright downloads in CI. [#48865](https://github.com/woocommerce/woocommerce/pull/48865) +* Dev - Update Action Scheduler to 3.8.1 [#49483](https://github.com/woocommerce/woocommerce/pull/49483) +* Dev - Update a few e2e tests failing on the daily run [#49313](https://github.com/woocommerce/woocommerce/pull/49313) +* Dev - Update all values to be random so that retries don't fail on assertions [#48734](https://github.com/woocommerce/woocommerce/pull/48734) +* Dev - Updated CodeSniffer configuration to address conflicting rules. [#49183](https://github.com/woocommerce/woocommerce/pull/49183) +* Dev - Update Playwright from 1.44 to 1.45 [#49202](https://github.com/woocommerce/woocommerce/pull/49202) +* Tweak - Add admin_install_timestamp in WC_Tracker [#50076](https://github.com/woocommerce/woocommerce/pull/50076) +* Tweak - Add $wpdb->esc_like to the search criteria when searching for a product custom field name [#48949](https://github.com/woocommerce/woocommerce/pull/48949) +* Tweak - Add the initially installed WooCommerce version to the wp_options table [#49139](https://github.com/woocommerce/woocommerce/pull/49139) +* Tweak - Change CLIRunner namespace for better PSR compatibility. [#49390](https://github.com/woocommerce/woocommerce/pull/49390) +* Tweak - Equalize data in Order export with data in Client side CSV export [#49356](https://github.com/woocommerce/woocommerce/pull/49356) +* Tweak - Exclude coming soon patterns from block inserter [#48821](https://github.com/woocommerce/woocommerce/pull/48821) +* Tweak - Fix tip block syntax in register-product-collection.md [#49785](https://github.com/woocommerce/woocommerce/pull/49785) +* Tweak - Get tax line label instead of name in StoreAPI Order endpoint. [#48445](https://github.com/woocommerce/woocommerce/pull/48445) +* Tweak - Improve checks when offering to remove test orders [#49032](https://github.com/woocommerce/woocommerce/pull/49032) +* Tweak - Initialize BlockTemplatesController for block themes only. [#48905](https://github.com/woocommerce/woocommerce/pull/48905) +* Tweak - Product Collection: flatten telemetry query filters data to json string [#49680](https://github.com/woocommerce/woocommerce/pull/49680) +* Tweak - Remove enctype from verify email form [#48859](https://github.com/woocommerce/woocommerce/pull/48859) +* Tweak - Remove unneeded IE styling as IE is no longer supported [#49027](https://github.com/woocommerce/woocommerce/pull/49027) +* Tweak - Rename Google Listings and Ads with Google for WooCommerce [#47614](https://github.com/woocommerce/woocommerce/pull/47614) +* Tweak - Show Guest when there is no Customer name [#49594](https://github.com/woocommerce/woocommerce/pull/49594) +* Tweak - Trigger doing_it_wrong() when using HPOS query args for CPT order queries. [#47457](https://github.com/woocommerce/woocommerce/pull/47457) +* Tweak - Update text to Content right with image left pattern [#49792](https://github.com/woocommerce/woocommerce/pull/49792) +* Tweak - Update the "API enabled" entry in the System Status Report to clarify that it pertains to the Legacy REST API. [#48878](https://github.com/woocommerce/woocommerce/pull/48878) +* Tweak - Add placeholder options and validation to all checkout select inputs. [#49929](https://github.com/woocommerce/woocommerce/pull/49929) +* Performance - Load REST API namespaces only when needed. [#47704](https://github.com/woocommerce/woocommerce/pull/47704) +* Performance - Reduced number of recalculations on Store API cart routes [#48944](https://github.com/woocommerce/woocommerce/pull/48944) +* Enhancement - Add address title to edit/add buttons on My Account page [#49171](https://github.com/woocommerce/woocommerce/pull/49171) +* Enhancement - Added password field to block checkout (when enabled in settings) for new accounts to set a custom password. [#48985](https://github.com/woocommerce/woocommerce/pull/48985) +* Enhancement - Add required indication to login forms [#48743](https://github.com/woocommerce/woocommerce/pull/48743) +* Enhancement - Add scope attributes to the order table on My Account [#49201](https://github.com/woocommerce/woocommerce/pull/49201) +* Enhancement - Add strength meter to block checkout password field. [#49164](https://github.com/woocommerce/woocommerce/pull/49164) +* Enhancement - Allow blocks with parents in the "woocommerce" namespace to be added to the Checkout block without requiring them to be added to the "__experimental_woocommerce_blocks_add_data_attributes_to_block" hook. [#48543](https://github.com/woocommerce/woocommerce/pull/48543) +* Enhancement - Convert edit address link to a button on checkout [#49471](https://github.com/woocommerce/woocommerce/pull/49471) +* Enhancement - CYS: make the entire shuffle section clickable. [#48889](https://github.com/woocommerce/woocommerce/pull/48889) +* Enhancement - CYS: Remove iframe animation [#48941](https://github.com/woocommerce/woocommerce/pull/48941) +* Enhancement - Made the "return to cart" link (in the checkout block) hidden by default. [#48762](https://github.com/woocommerce/woocommerce/pull/48762) +* Enhancement - Provide the location context within the Product Collection block context [#44145](https://github.com/woocommerce/woocommerce/pull/44145) +* Enhancement - Updated block checkout and Store API stock handling so stock is only reserved when attempting payment for an order. [#49446](https://github.com/woocommerce/woocommerce/pull/49446) + = 9.1.4 2024-07-26 = **WooCommerce** @@ -8,7 +354,6 @@ * Fix - Hardening against XSS via the Product Button unescaped attribute. [#50010](https://github.com/woocommerce/woocommerce/pull/50010) * Fix - Enhance escaping for block attributes. [#50015](https://github.com/woocommerce/woocommerce/pull/50015) - = 9.1.2 2024-07-12 = **WooCommerce** @@ -5901,12 +6246,12 @@ * Dev - Revert work done in #4857 for automated shipping after OBW is completed #5971 * Add - Welcome modal when coming from Calypso #6004 * Enhancement - Add an a/b experiment for installing free business features #5786 -* Dev - Add `onChangeCallback` feature to the wc-admin `
` component #5786 +* Dev - Add `onChangeCallback` feature to the wc-admin `` component #5786 * Fix - Generate JSON translation chunks on plugin activation #6028 -* Dev - Add merchant email notifications #5922 +* Dev - Add merchant email notifications #5922 * Add - Email note to add first product. #6024 * Add - Note for users coming from Calypso. #6030 -* Enhancement - Add an "unread" indicator to inbox messages. #6047 +* Enhancement - Add an "unread" indicator to inbox messages. #6047 * Add - Manage activity from home screen inbox message. #6072 = 4.9.5 2022-03-10 = @@ -6709,7 +7054,7 @@ * Fix - The WCPay method not appearing as recommended sometimes #4345 * Fix - Removed URLSearchParams method #4501 * Fix - REST API collections schema. #4484 -* Fix - null issue in wpNavMenuClassChange #4513 🎉 gradosevic +* Fix - null issue in wpNavMenuClassChange #4513 🎉 gradosevic * Fix - RTL stylesheet loading for split code chunks. #4542 * Fix - Don't show store location step in tax and shipping tasks if the address has already been provided #4507 * Fix - Check for enabled methods before payment task completion #4530 @@ -6851,7 +7196,7 @@ * Dev - Fixed failing unit tests. #105 **WooCommerce Admin 1.2.3** -* Enhancement - Add onboarding payments note #4157 +* Enhancement - Add onboarding payments note #4157 * Enhancement - Marketing Inbox Note #4030 * Performance - Use Route based code splitting to reduce bundle size #4094 * Performance - trim down inbox note API request. #3977 @@ -7262,7 +7607,7 @@ * Dev - Add a filter `woocommerce_ajax_add_order_item_validation` to allow validations in `add_order_item` function. #24518 * Dev - Use `wc_get_cart_url` instead of `wc_get_page_permalink( 'cart' )` because former has a filter `woocommerce_get_cart_url` to allow customization. #24530 * Dev - New `woocommerce_product_after_tabs` action hook added. #24694 -* Dev - Enable append hashes on custom events (like ajax requests) #24665 +* Dev - Enable append hashes on custom events (like ajax requests) #24665 * Dev - Introduced `woocommerce_order_get_formatted_billing_address` and `woocommerce_order_get_formatted_shipping_address` filters. #24677 * Dev - WC_Abstract_Order::recalculate_coupons() is public now. #24740 * Dev - Added 'applied_coupon' trigger to checkout.js. #24406 @@ -7375,7 +7720,7 @@ * Tweak - Update the generate username setting description label to reflect how the username is actually generated. #23911 * Tweak - OBW: Adjust plugin highlight container sizes to avoid overlap. #23997 * Tweak - Round tax amounts late when the round at subtotal level setting is enabled to reduce rounding errors. #24024 -* Tweak - OBW: Now includes WooCommerce Admin as a recommended plugin. #24058 +* Tweak - OBW: Now includes WooCommerce Admin as a recommended plugin. #24058 * Template - Review and update all template files escaping. #23460 * Template - Remove mention of shipping section from the checkout/form-login.php template as shipping is not always a requirement for an order. #23941 * Template - Add new filter `woocommerce_before_thankyou` to the checkout/thankyou.php template. #23538 @@ -8047,7 +8392,7 @@ * Fix - Fix warning when using logger instance in woocommerce_logging_class filter. #21448 * Fix - Use uppercase "ID" when sorting product queries by ID. #21461 * Fix - Consistently escape the gateway ID in the checkout payment method template. #21439 -* Fix - Avoid treating HTTP 301 and 302 codes as failures for webhooks. #21491 +* Fix - Avoid treating HTTP 301 and 302 codes as failures for webhooks. #21491 * Fix - Add address_1 to shipping packages info in WC_Cart:: get_shipping_packages to make it work correctly in address formatting functions. #21493 * Fix - Don't fire two of the same action when saving shipping settings. #21494 * Fix - Remove double condition for address line 2 in `WC_Countries::get_default_address_fields`. #20629 @@ -8359,7 +8704,7 @@ * Fix - Delete orphaned variations after product import. #19378 * Fix - Ensure API credentials exist before defining PayPal refund support. #19380 * Fix - Force word-wrapping in the log viewer to prevent layout-breaking long lines. #19503 -* Fix - Removes permission checks that were preventing webhooks from displaying properly when no post object existed. #19508 +* Fix - Removes permission checks that were preventing webhooks from displaying properly when no post object existed. #19508 * Fix - Empty cart after completing PayPal payment. #19509 * Fix - Strip tags on aria-labels in Add to Cart template to prevent broken HTML. #19522 * Fix - Update post_modified date when saving products and variations but no other product data. #19595 @@ -8381,7 +8726,7 @@ * Fix - WC API should not try to create a product image when creating a product variation if an empty image is passed. #19971 * Fix - Force settings API settings to autoload by default. #19998 * Fix - Cart html5 validation events when using keyboard. #20001 -* Fix - Don't show stock status fields in external product quick-edit. #20005 +* Fix - Don't show stock status fields in external product quick-edit. #20005 * Fix - Prevent an infinite loop if 2 grouped products are linked. #20020 * Fix - Switch stock_status when manage stock gets changed to prevent being out of stock if stock quantity is > 0. #20021 * Fix - When duplicating variation, set the date to null. #20083 @@ -8643,7 +8988,7 @@ * Fix - is_visible should ensure product is is not trashed before returning true. * Fix - Return packages with no rates back to the cart so the shipping calculator is displayed even when the current country is not shippable. * Fix - Merge session and persistent carts when both exists after login. -* Fix - Remove "wc_error" query string after login. +* Fix - Remove "wc_error" query string after login. * Fix - Allow woocommerce_form_field() have 'custom_attributes' equal 0. * Fix - Bulk actions in status logs table. * Fix - Exclude add-to-cart from pagination links. @@ -8684,19 +9029,19 @@ * Fix - Removed class within class in admin meta boxes HTML. * Fix - Fixed wrong `flex-control-nav` selector scope in `add-to-cart-variation.js` * Fix - Allow variations to be added to cart from query string. -* Fix - Use `add_filter` for `comment_feed_where` hook. +* Fix - Use `add_filter` for `comment_feed_where` hook. * Fix - Change nocache_headers hook firing in the cache helper. * Fix - Coupon min/max spend based on displayed subtotal. * Fix - Fix event propagation on click in setup wizard and improve validation. * Fix - API - Change how line items are saved in API so calculations are correct. -* Tweak - Hide downloads from admin emails. +* Tweak - Hide downloads from admin emails. * Tweak - Set placeholder for variation lxwxh field to that of the parent. * Tweak - Improve the Add Payment Methods display so buttons are not shown when no payment methods support the feature. * Localization - Update NJ tax rate. * Localization - Add Belarusian ruble BYN. = 3.2.3 - 2017-11-02 = -* Fix - Fixed a conflict with some slider plugins due to sanitization of archive/term descriptions. +* Fix - Fixed a conflict with some slider plugins due to sanitization of archive/term descriptions. * Fix - Fixed a flexslider bug when there is only 1 image on the product page (no gallery). * Fix - Prevent potential notices when someone extends product tabs wrongly. * Fix - Fixed display of shipping calculator under some conditions. diff --git a/docs/.markdownlint.json b/docs/.markdownlint.json index 6728d0fd76a..3297523f014 100644 --- a/docs/.markdownlint.json +++ b/docs/.markdownlint.json @@ -3,7 +3,7 @@ "MD003": { "style": "atx" }, "MD007": { "indent": 4 }, "MD013": { "line_length": 9999 }, - "MD024": { "allow_different_nesting": true }, + "MD024": { "siblings_only": true }, "MD033": { "allowed_elements": ["video"] }, "MD035": false, "MD041": false, diff --git a/docs/block-theme-development/README.md b/docs/block-theme-development/README.md new file mode 100644 index 00000000000..7b2564c7433 --- /dev/null +++ b/docs/block-theme-development/README.md @@ -0,0 +1,5 @@ +--- +category_title: Block Theme Development +category_slug: block-theme-development +post_title: Block theme development +--- \ No newline at end of file diff --git a/plugins/woocommerce-blocks/docs/designers/theming/cart-and-checkout.md b/docs/block-theme-development/cart-and-checkout.md similarity index 81% rename from plugins/woocommerce-blocks/docs/designers/theming/cart-and-checkout.md rename to docs/block-theme-development/cart-and-checkout.md index 5af905846d8..f3151f8fa5c 100644 --- a/plugins/woocommerce-blocks/docs/designers/theming/cart-and-checkout.md +++ b/docs/block-theme-development/cart-and-checkout.md @@ -1,13 +1,12 @@ -# Cart and Checkout Blocks Theming +--- +post_title: Cart and checkout blocks theming +menu_title: Cart and Checkout Blocks Theming +tags: reference +--- > [!IMPORTANT] -> We strongly discourage writing CSS code based on existing block class names and prioritize using global styles when possible. We especially discourage writing CSS selectors that rely on a specific block being a descendant of another one, as users can move blocks around freely, so they are prone to breaking. Similar to WordPress itself, we consider the HTML structure within components, blocks, and block templates to be “private”, and subject to further change in the future, so using CSS to target the internals of a block or a block template is _not recommended or supported_. +> We strongly discourage writing CSS code based on existing block class names and prioritize using global styles when possible. We especially discourage writing CSS selectors that rely on a specific block being a descendant of another one, as users can move blocks around freely, so they are prone to breaking. Similar to WordPress itself, we consider the HTML structure within components, blocks, and block templates to be "private", and subject to further change in the future, so using CSS to target the internals of a block or a block template is _not recommended or supported_. -## Table of Contents - -- [Buttons](#buttons) -- [Mobile submit container](#mobile-submit-container) -- [Item quantity badge](#item-quantity-badge) ## Buttons @@ -81,13 +80,5 @@ By default, it uses a combination of black and white borders and shadows so it h ![Order summary screenshot with custom styles for the item quantity badge](https://user-images.githubusercontent.com/3616980/83863109-2e421c80-a723-11ea-9bf7-2033a96cf5b2.png) - ---- - -[We're hiring!](https://woocommerce.com/careers/) Come work with us! - -🐞 Found a mistake, or have a suggestion? [Leave feedback about this document here.](https://github.com/woocommerce/woocommerce-blocks/issues/new?assignees=&labels=type%3A+documentation&template=--doc-feedback.md&title=Feedback%20on%20./docs/designers/theming/cart-and-checkout.md) - - diff --git a/plugins/woocommerce-blocks/docs/designers/theming/css-styling.md b/docs/block-theme-development/css-styling.md similarity index 69% rename from plugins/woocommerce-blocks/docs/designers/theming/css-styling.md rename to docs/block-theme-development/css-styling.md index 978dfb7444d..2470d621ae0 100644 --- a/plugins/woocommerce-blocks/docs/designers/theming/css-styling.md +++ b/docs/block-theme-development/css-styling.md @@ -1,11 +1,15 @@ -# CSS Styling +--- +post_title: CSS styling for themes +menu_title: CSS Styling for Themes +tags: reference +--- ## Block and component class names > [!IMPORTANT] -> We strongly discourage writing CSS code based on existing block class names and prioritize using global styles when possible. We especially discourage writing CSS selectors that rely on a specific block being a descendant of another one, as users can move blocks around freely, so they are prone to breaking. Similar to WordPress itself, we consider the HTML structure within components, blocks, and block templates to be “private”, and subject to further change in the future, so using CSS to target the internals of a block or a block template is _not recommended or supported_. +> We strongly discourage writing CSS code based on existing block class names and prioritize using global styles when possible. We especially discourage writing CSS selectors that rely on a specific block being a descendant of another one, as users can move blocks around freely, so they are prone to breaking. Similar to WordPress itself, we consider the HTML structure within components, blocks, and block templates to be "private", and subject to further change in the future, so using CSS to target the internals of a block or a block template is _not recommended or supported_. -WooCommerce Blocks follows BEM for class names, as [stated in our coding guidelines](../../contributors/coding-guidelines.md). All classes start with one of these two prefixes: +WooCommerce Blocks follows BEM for class names, as [stated in our coding guidelines](https://github.com/woocommerce/woocommerce/blob/trunk/plugins/woocommerce-blocks/docs/contributors/coding-guidelines.md). All classes start with one of these two prefixes: * `.wc-block-`: class names specific to a single block. * `.wc-block-components-`: class names specific to a component. The component might be reused by different blocks. @@ -38,10 +42,10 @@ Those classes are: Container width | Class name ----------------|------------ -\>700px | `is-large` +\>700px | `is-large` 521px-700px | `is-medium` 401px-520px | `is-small` -<=400px | `is-mobile` +<=400px | `is-mobile` As an example, if we wanted to do the Checkout font size 10% larger when the container has a width of 521px or wider, we could do so with this code: @@ -54,9 +58,9 @@ As an example, if we wanted to do the Checkout font size 10% larger when the con ## WC Blocks _vs._ theme style conflicts for semantic elements -WooCommerce Blocks uses HTML elements according to their semantic meaning, not their default layout. That means that some times blocks might use an anchor link (``) but display it as a button. Or the other way around, a ` + + + +`; diff --git a/packages/js/components/src/analytics/error/tests/index.test.js b/packages/js/components/src/analytics/error/tests/index.test.js new file mode 100644 index 00000000000..d01f55c8724 --- /dev/null +++ b/packages/js/components/src/analytics/error/tests/index.test.js @@ -0,0 +1,62 @@ +/** + * External dependencies + */ +import { createElement } from '@wordpress/element'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +/** + * Internal dependencies + */ +import AnalyticsError from '..'; + +describe( 'AnalyticsError', () => { + // Mock window.location.reload by using a global variable + const originalLocation = window.location; + + beforeAll( () => { + delete window.location; + window.location = { reload: jest.fn() }; + } ); + + afterAll( () => { + window.location = originalLocation; + } ); + + it( 'displays an error message', () => { + render( ); + + expect( + screen.getByText( + 'There was an error getting your stats. Please try again.' + ) + ).toBeInTheDocument(); + } ); + + it( 'shows reload button', () => { + render( ); + + expect( + screen.getByRole( 'button', { name: 'Reload' } ) + ).toBeInTheDocument(); + } ); + + it( 'refreshes the page when Reload Page button is clicked', () => { + const reloadMock = jest.fn(); + Object.defineProperty( window.location, 'reload', { + configurable: true, + value: reloadMock, + } ); + + render( ); + + userEvent.click( screen.getByText( 'Reload' ) ); + + expect( reloadMock ).toHaveBeenCalled(); + } ); + + it( 'should match snapshot', () => { + const { container } = render( ); + expect( container ).toMatchSnapshot(); + } ); +} ); diff --git a/packages/js/components/src/analytics/index.js b/packages/js/components/src/analytics/index.js new file mode 100644 index 00000000000..ebf10a5b7e5 --- /dev/null +++ b/packages/js/components/src/analytics/index.js @@ -0,0 +1 @@ +export { default as AnalyticsError } from './error'; diff --git a/packages/js/components/src/index.ts b/packages/js/components/src/index.ts index ff64f547fda..06914504e6a 100644 --- a/packages/js/components/src/index.ts +++ b/packages/js/components/src/index.ts @@ -1,5 +1,6 @@ export { default as AbbreviatedCard } from './abbreviated-card'; export { default as AdvancedFilters } from './advanced-filters'; +export * from './analytics'; export { default as AnimationSlider } from './animation-slider'; export { default as Chart } from './chart'; export { default as ChartPlaceholder } from './chart/placeholder'; diff --git a/packages/js/components/src/plugins/index.tsx b/packages/js/components/src/plugins/index.tsx index fc3971f0346..df2561ea28f 100644 --- a/packages/js/components/src/plugins/index.tsx +++ b/packages/js/components/src/plugins/index.tsx @@ -19,6 +19,7 @@ type PluginsProps = { response: InstallPluginsResponse ) => void; onError: ( errors: unknown, response: InstallPluginsResponse ) => void; + onClick?: () => void; onSkip?: () => void; skipText?: string; autoInstall?: boolean; @@ -37,6 +38,7 @@ export const Plugins = ( { onAbort, onComplete, onError = () => null, + onClick = () => null, pluginSlugs = [ 'woocommerce-services' ], onSkip, installText = __( 'Install & enable', 'woocommerce' ), @@ -159,6 +161,7 @@ export const Plugins = ( { } disabled={ isRequesting && hasBeenClicked } onClick={ () => { + onClick(); setHasBeenClicked( true ); installAndActivate(); } } diff --git a/packages/js/components/src/summary/number.js b/packages/js/components/src/summary/number.js index 8c1243311f8..aa10d0f9b01 100644 --- a/packages/js/components/src/summary/number.js +++ b/packages/js/components/src/summary/number.js @@ -192,8 +192,7 @@ SummaryNumber.propTypes = { /** * The type of the link */ - hrefType: PropTypes.oneOf( [ 'wp-admin', 'wc-admin', 'external' ] ) - .isRequired, + hrefType: PropTypes.oneOf( [ 'wp-admin', 'wc-admin', 'external' ] ), /** * Boolean describing whether the menu list is open. Only applies in mobile view, * and only applies to the toggle-able item (first in the list). diff --git a/packages/js/components/src/timeline/index.js b/packages/js/components/src/timeline/index.js index 5d0d67ce8c2..b5d7ddfb72b 100644 --- a/packages/js/components/src/timeline/index.js +++ b/packages/js/components/src/timeline/index.js @@ -99,7 +99,7 @@ Timeline.propTypes = { */ hideTimestamp: PropTypes.bool, } ) - ).isRequired, + ), /** * Defines how items should be grouped together. */ diff --git a/packages/js/components/src/timeline/timeline-group.js b/packages/js/components/src/timeline/timeline-group.js index 6ddcde8e02b..73ecbb8f922 100644 --- a/packages/js/components/src/timeline/timeline-group.js +++ b/packages/js/components/src/timeline/timeline-group.js @@ -94,8 +94,8 @@ TimelineGroup.propTypes = { */ hideTimestamp: PropTypes.bool, } ) - ).isRequired, - } ).isRequired, + ), + } ), /** * Defines how items should be ordered. */ diff --git a/packages/js/components/src/timeline/timeline-item.js b/packages/js/components/src/timeline/timeline-item.js index 59914878900..19961993e67 100644 --- a/packages/js/components/src/timeline/timeline-item.js +++ b/packages/js/components/src/timeline/timeline-item.js @@ -69,7 +69,7 @@ TimelineItem.propTypes = { * The PHP clock format string used to format times, see php.net/date. */ clockFormat: PropTypes.string, - } ).isRequired, + } ), }; export default TimelineItem; diff --git a/packages/js/components/src/tree-select-control/index.scss b/packages/js/components/src/tree-select-control/index.scss index bec8900acfa..a2574fd39d9 100644 --- a/packages/js/components/src/tree-select-control/index.scss +++ b/packages/js/components/src/tree-select-control/index.scss @@ -215,7 +215,7 @@ $muriel-box-shadow-8dp: 0 5px 5px -3px rgb(0 0 0 / 20%), } // At the time of this comment, it was discovered that this component has - // the same classnames as the WP Components Checkbox, without it being a code depedency. + // the same class names as the WP Components Checkbox, without it being a code dependency. // This caused some visual breakages when changes happened in WP 6.6, and // the rules have been copied over from WP Components styles of 6.5.1. // https://github.com/WordPress/gutenberg/blob/403b4b8d014ef7f6edc15c822e455e109bf49c6d/packages/components/src/checkbox-control/style.scss#L4 diff --git a/packages/js/create-product-editor-block/changelog/50828-dev-constrain-pnpm-version b/packages/js/create-product-editor-block/changelog/50828-dev-constrain-pnpm-version new file mode 100644 index 00000000000..fdead95ceb5 --- /dev/null +++ b/packages/js/create-product-editor-block/changelog/50828-dev-constrain-pnpm-version @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix pnpm version to 9.1.3 to avoid dependency installation issues. \ No newline at end of file diff --git a/packages/js/create-product-editor-block/package.json b/packages/js/create-product-editor-block/package.json index c21112cd09d..0e69b805f17 100644 --- a/packages/js/create-product-editor-block/package.json +++ b/packages/js/create-product-editor-block/package.json @@ -7,7 +7,7 @@ "main": "index.js", "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "scripts": { "changelog": "composer install && composer exec -- changelogger" diff --git a/packages/js/create-woo-extension/changelog/50828-dev-constrain-pnpm-version b/packages/js/create-woo-extension/changelog/50828-dev-constrain-pnpm-version new file mode 100644 index 00000000000..fdead95ceb5 --- /dev/null +++ b/packages/js/create-woo-extension/changelog/50828-dev-constrain-pnpm-version @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix pnpm version to 9.1.3 to avoid dependency installation issues. \ No newline at end of file diff --git a/packages/js/create-woo-extension/package.json b/packages/js/create-woo-extension/package.json index d7f3be1588f..6f5a61495b7 100644 --- a/packages/js/create-woo-extension/package.json +++ b/packages/js/create-woo-extension/package.json @@ -5,7 +5,7 @@ "main": "index.js", "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "repository": { "type": "git", diff --git a/packages/js/csv-export/changelog/50828-dev-constrain-pnpm-version b/packages/js/csv-export/changelog/50828-dev-constrain-pnpm-version new file mode 100644 index 00000000000..fdead95ceb5 --- /dev/null +++ b/packages/js/csv-export/changelog/50828-dev-constrain-pnpm-version @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix pnpm version to 9.1.3 to avoid dependency installation issues. \ No newline at end of file diff --git a/packages/js/csv-export/package.json b/packages/js/csv-export/package.json index 772d22e3c16..11a576f414f 100644 --- a/packages/js/csv-export/package.json +++ b/packages/js/csv-export/package.json @@ -6,7 +6,7 @@ "license": "GPL-3.0-or-later", "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "keywords": [ "wordpress", diff --git a/packages/js/currency/changelog/50828-dev-constrain-pnpm-version b/packages/js/currency/changelog/50828-dev-constrain-pnpm-version new file mode 100644 index 00000000000..fdead95ceb5 --- /dev/null +++ b/packages/js/currency/changelog/50828-dev-constrain-pnpm-version @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix pnpm version to 9.1.3 to avoid dependency installation issues. \ No newline at end of file diff --git a/packages/js/currency/package.json b/packages/js/currency/package.json index 8e36f833963..a8b5ee2d4ce 100644 --- a/packages/js/currency/package.json +++ b/packages/js/currency/package.json @@ -11,7 +11,7 @@ ], "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "homepage": "https://github.com/woocommerce/woocommerce/tree/trunk/packages/js/currency/README.md", "repository": { diff --git a/packages/js/currency/src/test/index.ts b/packages/js/currency/src/test/index.ts index 3f6ef2529de..f3bdf8c1b68 100644 --- a/packages/js/currency/src/test/index.ts +++ b/packages/js/currency/src/test/index.ts @@ -102,4 +102,20 @@ describe( 'currency.formatDecimalString', () => { // @ts-expect-error formatAccount expects a number or string; expect( currency.formatDecimalString( null ) ).toBe( '' ); } ); + + it( 'should strip tags in getPriceFormat', () => { + const currency = Currency(); + + expect( + currency.getPriceFormat( { + priceFormat: 'tagformat', + } ) + ).toBe( 'tagformat' ); + + expect( + currency.getPriceFormat( { + priceFormat: 'format', + } ) + ).toBe( 'format' ); + } ); } ); diff --git a/packages/js/currency/src/utils.tsx b/packages/js/currency/src/utils.tsx index 2a8901991fa..b0807de6866 100644 --- a/packages/js/currency/src/utils.tsx +++ b/packages/js/currency/src/utils.tsx @@ -66,9 +66,17 @@ const CurrencyFactoryBase = function ( currencySetting?: CurrencyConfig ) { let currency: Currency; function stripTags( str: string ) { - const tmp = document.createElement( 'DIV' ); - tmp.innerHTML = str; - return tmp.textContent || tmp.innerText || ''; + // sanitize Polyfill - see https://github.com/WordPress/WordPress/blob/master/wp-includes/js/wp-sanitize.js + const strippedStr = str + .replace( /|$)/g, '' ) + .replace( /<(script|style)[^>]*>[\s\S]*?(<\/\1>|$)/gi, '' ) + .replace( /<\/?[a-z][\s\S]*?(>|$)/gi, '' ); + + if ( strippedStr !== str ) { + return stripTags( strippedStr ); + } + + return strippedStr; } /** diff --git a/packages/js/customer-effort-score/changelog/50828-dev-constrain-pnpm-version b/packages/js/customer-effort-score/changelog/50828-dev-constrain-pnpm-version new file mode 100644 index 00000000000..fdead95ceb5 --- /dev/null +++ b/packages/js/customer-effort-score/changelog/50828-dev-constrain-pnpm-version @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix pnpm version to 9.1.3 to avoid dependency installation issues. \ No newline at end of file diff --git a/packages/js/customer-effort-score/package.json b/packages/js/customer-effort-score/package.json index 110b88b358e..8c78924af57 100644 --- a/packages/js/customer-effort-score/package.json +++ b/packages/js/customer-effort-score/package.json @@ -10,7 +10,7 @@ ], "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "homepage": "https://github.com/woocommerce/woocommerce/tree/trunk/packages/js/customer-effort-score/README.md", "repository": { diff --git a/packages/js/data/changelog/50828-dev-constrain-pnpm-version b/packages/js/data/changelog/50828-dev-constrain-pnpm-version new file mode 100644 index 00000000000..fdead95ceb5 --- /dev/null +++ b/packages/js/data/changelog/50828-dev-constrain-pnpm-version @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix pnpm version to 9.1.3 to avoid dependency installation issues. \ No newline at end of file diff --git a/packages/js/data/changelog/add-react-main-payments-settings-screen b/packages/js/data/changelog/add-react-main-payments-settings-screen new file mode 100644 index 00000000000..0aa0b00a417 --- /dev/null +++ b/packages/js/data/changelog/add-react-main-payments-settings-screen @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix payment store selector type diff --git a/packages/js/data/changelog/add-stripe-tax-to task b/packages/js/data/changelog/add-stripe-tax-to task new file mode 100644 index 00000000000..44d5916dff9 --- /dev/null +++ b/packages/js/data/changelog/add-stripe-tax-to task @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Add stripe tax status to task type diff --git a/packages/js/data/changelog/update-lys-tour-meta b/packages/js/data/changelog/update-lys-tour-meta new file mode 100644 index 00000000000..eec09d7e111 --- /dev/null +++ b/packages/js/data/changelog/update-lys-tour-meta @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Update user preferences types and logic diff --git a/packages/js/data/package.json b/packages/js/data/package.json index c52f4e22698..178022e8a2b 100644 --- a/packages/js/data/package.json +++ b/packages/js/data/package.json @@ -11,7 +11,7 @@ ], "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "homepage": "https://github.com/woocommerce/woocommerce/tree/trunk/packages/js/data/README.md", "repository": { diff --git a/packages/js/data/src/onboarding/types.ts b/packages/js/data/src/onboarding/types.ts index 1e62f7036ca..a39fa627e63 100644 --- a/packages/js/data/src/onboarding/types.ts +++ b/packages/js/data/src/onboarding/types.ts @@ -29,8 +29,10 @@ export type TaskType = { badge?: string; additionalData?: { woocommerceTaxCountries?: string[]; + stripeTaxCountries?: string[]; taxJarActivated?: boolean; avalaraActivated?: boolean; + stripeTaxActivated?: boolean; woocommerceTaxActivated?: boolean; woocommerceShippingActivated?: boolean; }; diff --git a/packages/js/data/src/payment-gateways/index.ts b/packages/js/data/src/payment-gateways/index.ts index b9c74e18f19..afd3e78c926 100644 --- a/packages/js/data/src/payment-gateways/index.ts +++ b/packages/js/data/src/payment-gateways/index.ts @@ -13,7 +13,7 @@ import * as resolvers from './resolvers'; import * as selectors from './selectors'; import reducer from './reducer'; import { STORE_KEY } from './constants'; -import { WPDataActions } from '../types'; +import { WPDataSelectors } from '../types'; import { PromiseifySelectors } from '../types/promiseify-selectors'; export const PAYMENT_GATEWAYS_STORE_NAME = STORE_KEY; @@ -33,7 +33,7 @@ declare module '@wordpress/data' { ): DispatchFromMap< typeof actions >; function select( key: typeof STORE_KEY - ): SelectFromMap< typeof selectors > & WPDataActions; + ): SelectFromMap< typeof selectors > & WPDataSelectors; function resolveSelect( key: typeof STORE_KEY ): PromiseifySelectors< SelectFromMap< typeof selectors > >; diff --git a/packages/js/data/src/user/types.ts b/packages/js/data/src/user/types.ts index 91b057b7eff..34328a41f18 100644 --- a/packages/js/data/src/user/types.ts +++ b/packages/js/data/src/user/types.ts @@ -33,13 +33,12 @@ export type UserPreferences = { product_advice_card_dismissed?: { [ key: string ]: 'yes' | 'no'; }; + launch_your_store_tour_hidden?: 'yes' | 'no' | ''; + coming_soon_banner_dismissed?: 'yes' | 'no' | ''; }; -export type WoocommerceMeta = UserPreferences & { - task_list_tracked_started_tasks?: string; - variable_items_without_price_notice_dismissed?: string; - local_attributes_notice_dismissed_ids?: string; - product_advice_card_dismissed?: string; +export type WoocommerceMeta = { + [ key in keyof UserPreferences ]: string; }; export type WCUser< diff --git a/packages/js/data/src/user/use-user-preferences.ts b/packages/js/data/src/user/use-user-preferences.ts index e1367d482f0..779cb8adf03 100644 --- a/packages/js/data/src/user/use-user-preferences.ts +++ b/packages/js/data/src/user/use-user-preferences.ts @@ -19,28 +19,15 @@ import { WCUser, UserPreferences } from './types'; const getWooCommerceMeta = ( user: WCUser ) => { const wooMeta = user.woocommerce_meta || {}; - const userData = mapValues( wooMeta, ( data, key ) => { + const userData = mapValues( wooMeta, ( data ) => { if ( ! data || data.length === 0 ) { return ''; } try { return JSON.parse( data ); } catch ( e ) { - if ( e instanceof Error ) { - /* eslint-disable no-console */ - console.error( - `Error parsing value '${ data }' for ${ key }`, - e.message - ); - /* eslint-enable no-console */ - } else { - /* eslint-disable no-console */ - console.error( - `Unexpected Error parsing value '${ data }' for ${ key } ${ e }` - ); - /* eslint-enable no-console */ - } - return ''; + // If we can't parse the value, return the raw data. The meta value could be a string like 'yes' or 'no'. + return data; } } ); @@ -53,7 +40,7 @@ async function updateUserPrefs( user: WCUser, saveUser: ( userToSave: { id: number; - woocommerce_meta: { [ key: string ]: boolean }; + woocommerce_meta: WCUser[ 'woocommerce_meta' ]; } ) => WCUser, getLastEntitySaveError: ( kind: string, @@ -64,7 +51,14 @@ async function updateUserPrefs( ) { // @todo Handle unresolved getCurrentUser() here. // Prep fields for update. - const metaData = mapValues( userPrefs, JSON.stringify ); + const metaData = mapValues( userPrefs, ( value ) => { + if ( typeof value === 'string' ) { + // If the value is a string, we don't need to serialize it. + return value; + } + + return JSON.stringify( value ); + } ); if ( Object.keys( metaData ).length === 0 ) { return { @@ -81,7 +75,6 @@ async function updateUserPrefs( ...metaData, }, } ); - // Use saveUser() to update WooCommerce meta values. const updatedUser = await saveUser( { id: user.id, diff --git a/packages/js/date/README.md b/packages/js/date/README.md index 722d8fc7985..4402f9e5f09 100644 --- a/packages/js/date/README.md +++ b/packages/js/date/README.md @@ -212,7 +212,7 @@ Calculates the date difference between two dates. Used in calculating a matching | Param | Type | Description | | --- | --- | --- | | date | string | Date to compare | -| date2 | string | Seconary date to compare | +| date2 | string | Secondary date to compare | @@ -279,7 +279,7 @@ Get a DateValue object for a period prior to the current period. ### getCurrentPeriod(period, compare) ⇒ [DateValue](#DateValue) -Get a DateValue object for a curent period. The period begins on the first day of the period, +Get a DateValue object for a current period. The period begins on the first day of the period, and ends on the current day. **Kind**: global function diff --git a/packages/js/date/changelog/50047-fix-typos b/packages/js/date/changelog/50047-fix-typos new file mode 100644 index 00000000000..e5d2f2b3db2 --- /dev/null +++ b/packages/js/date/changelog/50047-fix-typos @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Comment: Fix comment typos across various files. diff --git a/packages/js/date/changelog/502820-fix-markdown-typos b/packages/js/date/changelog/502820-fix-markdown-typos new file mode 100644 index 00000000000..f3ee7e05d02 --- /dev/null +++ b/packages/js/date/changelog/502820-fix-markdown-typos @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Comment: Fix typos in documentation. \ No newline at end of file diff --git a/packages/js/date/changelog/50828-dev-constrain-pnpm-version b/packages/js/date/changelog/50828-dev-constrain-pnpm-version new file mode 100644 index 00000000000..fdead95ceb5 --- /dev/null +++ b/packages/js/date/changelog/50828-dev-constrain-pnpm-version @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix pnpm version to 9.1.3 to avoid dependency installation issues. \ No newline at end of file diff --git a/packages/js/date/package.json b/packages/js/date/package.json index 17e3f6152c8..d7f2fef1241 100644 --- a/packages/js/date/package.json +++ b/packages/js/date/package.json @@ -11,7 +11,7 @@ ], "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "homepage": "https://github.com/woocommerce/woocommerce/tree/trunk/packages/js/date/README.md", "repository": { diff --git a/packages/js/date/src/test/index.ts b/packages/js/date/src/test/index.ts index 7c47f6a2182..87f2a653107 100644 --- a/packages/js/date/src/test/index.ts +++ b/packages/js/date/src/test/index.ts @@ -1052,7 +1052,7 @@ describe( 'getStoreTimeZoneMoment', () => { expect( utcOffset ).not.toHaveBeenCalled(); } ); - it( 'should use the utc offest when it is set', () => { + it( 'should use the utc offset when it is set', () => { global.window.wcSettings = { timeZone: '+06:00', }; diff --git a/packages/js/dependency-extraction-webpack-plugin/changelog/50828-dev-constrain-pnpm-version b/packages/js/dependency-extraction-webpack-plugin/changelog/50828-dev-constrain-pnpm-version new file mode 100644 index 00000000000..fdead95ceb5 --- /dev/null +++ b/packages/js/dependency-extraction-webpack-plugin/changelog/50828-dev-constrain-pnpm-version @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix pnpm version to 9.1.3 to avoid dependency installation issues. \ No newline at end of file diff --git a/packages/js/dependency-extraction-webpack-plugin/package.json b/packages/js/dependency-extraction-webpack-plugin/package.json index 18eb2cca63e..869625dd58b 100644 --- a/packages/js/dependency-extraction-webpack-plugin/package.json +++ b/packages/js/dependency-extraction-webpack-plugin/package.json @@ -10,7 +10,7 @@ ], "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "homepage": "https://github.com/woocommerce/woocommerce/tree/trunk/packages/js/dependency-extraction-webpack-plugin/README.md", "repository": { diff --git a/packages/js/e2e-core-tests/package.json b/packages/js/e2e-core-tests/package.json index 7aa1cb71ad0..3f512664f5b 100644 --- a/packages/js/e2e-core-tests/package.json +++ b/packages/js/e2e-core-tests/package.json @@ -10,7 +10,7 @@ "license": "GPL-3.0+", "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "main": "build/index.js", "module": "build-module/index.js", diff --git a/packages/js/e2e-environment/package.json b/packages/js/e2e-environment/package.json index f73aff3163c..f5f0afa6411 100644 --- a/packages/js/e2e-environment/package.json +++ b/packages/js/e2e-environment/package.json @@ -12,7 +12,7 @@ ], "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "homepage": "https://github.com/woocommerce/woocommerce/tree/trunk/packages/js/e2e-environment/README.md", "bugs": { diff --git a/packages/js/e2e-utils/package.json b/packages/js/e2e-utils/package.json index d133e6c9a8f..1e47b320022 100644 --- a/packages/js/e2e-utils/package.json +++ b/packages/js/e2e-utils/package.json @@ -10,7 +10,7 @@ "license": "GPL-3.0+", "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "main": "build/index.js", "module": "build-module/index.js", diff --git a/packages/js/eslint-plugin/changelog/50828-dev-constrain-pnpm-version b/packages/js/eslint-plugin/changelog/50828-dev-constrain-pnpm-version new file mode 100644 index 00000000000..fdead95ceb5 --- /dev/null +++ b/packages/js/eslint-plugin/changelog/50828-dev-constrain-pnpm-version @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix pnpm version to 9.1.3 to avoid dependency installation issues. \ No newline at end of file diff --git a/packages/js/eslint-plugin/package.json b/packages/js/eslint-plugin/package.json index 7913b5a2b3e..4e5407484dc 100644 --- a/packages/js/eslint-plugin/package.json +++ b/packages/js/eslint-plugin/package.json @@ -6,7 +6,7 @@ "license": "GPL-2.0-or-later", "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "keywords": [ "wordpress", diff --git a/packages/js/experimental/changelog/50828-dev-constrain-pnpm-version b/packages/js/experimental/changelog/50828-dev-constrain-pnpm-version new file mode 100644 index 00000000000..fdead95ceb5 --- /dev/null +++ b/packages/js/experimental/changelog/50828-dev-constrain-pnpm-version @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix pnpm version to 9.1.3 to avoid dependency installation issues. \ No newline at end of file diff --git a/packages/js/experimental/package.json b/packages/js/experimental/package.json index de525dbc624..3a2016f62ce 100644 --- a/packages/js/experimental/package.json +++ b/packages/js/experimental/package.json @@ -6,7 +6,7 @@ "license": "GPL-3.0-or-later", "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "keywords": [ "wordpress", diff --git a/packages/js/explat/changelog/50828-dev-constrain-pnpm-version b/packages/js/explat/changelog/50828-dev-constrain-pnpm-version new file mode 100644 index 00000000000..fdead95ceb5 --- /dev/null +++ b/packages/js/explat/changelog/50828-dev-constrain-pnpm-version @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix pnpm version to 9.1.3 to avoid dependency installation issues. \ No newline at end of file diff --git a/packages/js/explat/package.json b/packages/js/explat/package.json index 376e4893ae4..377b1f25e98 100644 --- a/packages/js/explat/package.json +++ b/packages/js/explat/package.json @@ -6,7 +6,7 @@ "license": "GPL-2.0-or-later", "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "keywords": [ "wordpress", diff --git a/packages/js/expression-evaluation/changelog/50047-fix-typos b/packages/js/expression-evaluation/changelog/50047-fix-typos new file mode 100644 index 00000000000..e5d2f2b3db2 --- /dev/null +++ b/packages/js/expression-evaluation/changelog/50047-fix-typos @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Comment: Fix comment typos across various files. diff --git a/packages/js/expression-evaluation/changelog/50828-dev-constrain-pnpm-version b/packages/js/expression-evaluation/changelog/50828-dev-constrain-pnpm-version new file mode 100644 index 00000000000..fdead95ceb5 --- /dev/null +++ b/packages/js/expression-evaluation/changelog/50828-dev-constrain-pnpm-version @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix pnpm version to 9.1.3 to avoid dependency installation issues. \ No newline at end of file diff --git a/packages/js/expression-evaluation/package.json b/packages/js/expression-evaluation/package.json index dc8316bd48c..7ebe4e2fb67 100644 --- a/packages/js/expression-evaluation/package.json +++ b/packages/js/expression-evaluation/package.json @@ -8,11 +8,11 @@ "wordpress", "woocommerce", "expression", - "evalution" + "evaluation" ], "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "homepage": "https://github.com/woocommerce/woocommerce/tree/trunk/packages/js/expression-evaluation/README.md", "repository": { diff --git a/packages/js/extend-cart-checkout-block/changelog/50828-dev-constrain-pnpm-version b/packages/js/extend-cart-checkout-block/changelog/50828-dev-constrain-pnpm-version new file mode 100644 index 00000000000..fdead95ceb5 --- /dev/null +++ b/packages/js/extend-cart-checkout-block/changelog/50828-dev-constrain-pnpm-version @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix pnpm version to 9.1.3 to avoid dependency installation issues. \ No newline at end of file diff --git a/packages/js/extend-cart-checkout-block/changelog/add-upgrade-blocks-to-v3 b/packages/js/extend-cart-checkout-block/changelog/add-upgrade-blocks-to-v3 new file mode 100644 index 00000000000..def28ee9bcc --- /dev/null +++ b/packages/js/extend-cart-checkout-block/changelog/add-upgrade-blocks-to-v3 @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Update all blocks to use API Version 3. diff --git a/packages/js/extend-cart-checkout-block/package.json b/packages/js/extend-cart-checkout-block/package.json index 5695910fd8c..0e032c37149 100644 --- a/packages/js/extend-cart-checkout-block/package.json +++ b/packages/js/extend-cart-checkout-block/package.json @@ -5,7 +5,7 @@ "main": "index.js", "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "repository": { "type": "git", diff --git a/packages/js/extend-cart-checkout-block/src/js/checkout-newsletter-subscription-block/block.json.mustache b/packages/js/extend-cart-checkout-block/src/js/checkout-newsletter-subscription-block/block.json.mustache index 98553e8cae7..9a157b4c339 100644 --- a/packages/js/extend-cart-checkout-block/src/js/checkout-newsletter-subscription-block/block.json.mustache +++ b/packages/js/extend-cart-checkout-block/src/js/checkout-newsletter-subscription-block/block.json.mustache @@ -1,5 +1,5 @@ { - "apiVersion": 2, + "apiVersion": 3, "name": "{{slug}}/checkout-newsletter-subscription", "version": "2.0.0", "title": "Newsletter Subscription!", diff --git a/packages/js/internal-e2e-builds/package.json b/packages/js/internal-e2e-builds/package.json index 49c4030cf54..ba7b5c89049 100644 --- a/packages/js/internal-e2e-builds/package.json +++ b/packages/js/internal-e2e-builds/package.json @@ -10,7 +10,7 @@ ], "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "bin": { "e2e-builds": "./build.js" diff --git a/packages/js/internal-js-tests/changelog/50828-dev-constrain-pnpm-version b/packages/js/internal-js-tests/changelog/50828-dev-constrain-pnpm-version new file mode 100644 index 00000000000..fdead95ceb5 --- /dev/null +++ b/packages/js/internal-js-tests/changelog/50828-dev-constrain-pnpm-version @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix pnpm version to 9.1.3 to avoid dependency installation issues. \ No newline at end of file diff --git a/packages/js/internal-js-tests/package.json b/packages/js/internal-js-tests/package.json index 2a112db89d5..4f1049b8572 100644 --- a/packages/js/internal-js-tests/package.json +++ b/packages/js/internal-js-tests/package.json @@ -6,7 +6,7 @@ "license": "GPL-2.0-or-later", "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "homepage": "https://github.com/woocommerce/woocommerce/tree/trunk/packages/js/internal-js-tests/README.md", "repository": { diff --git a/packages/js/internal-style-build/package.json b/packages/js/internal-style-build/package.json index a777f36e231..9955a65abe9 100644 --- a/packages/js/internal-style-build/package.json +++ b/packages/js/internal-style-build/package.json @@ -6,7 +6,7 @@ "license": "GPL-2.0-or-later", "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "keywords": [ "wordpress", diff --git a/packages/js/navigation/changelog/50828-dev-constrain-pnpm-version b/packages/js/navigation/changelog/50828-dev-constrain-pnpm-version new file mode 100644 index 00000000000..fdead95ceb5 --- /dev/null +++ b/packages/js/navigation/changelog/50828-dev-constrain-pnpm-version @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix pnpm version to 9.1.3 to avoid dependency installation issues. \ No newline at end of file diff --git a/packages/js/navigation/package.json b/packages/js/navigation/package.json index 903b2f3ef26..56c29eacab8 100644 --- a/packages/js/navigation/package.json +++ b/packages/js/navigation/package.json @@ -6,7 +6,7 @@ "license": "GPL-3.0-or-later", "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "keywords": [ "wordpress", diff --git a/packages/js/notices/changelog/50828-dev-constrain-pnpm-version b/packages/js/notices/changelog/50828-dev-constrain-pnpm-version new file mode 100644 index 00000000000..fdead95ceb5 --- /dev/null +++ b/packages/js/notices/changelog/50828-dev-constrain-pnpm-version @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix pnpm version to 9.1.3 to avoid dependency installation issues. \ No newline at end of file diff --git a/packages/js/notices/package.json b/packages/js/notices/package.json index 0a649b7c7b3..b2c5ac704f8 100644 --- a/packages/js/notices/package.json +++ b/packages/js/notices/package.json @@ -6,7 +6,7 @@ "license": "GPL-2.0-or-later", "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "keywords": [ "wordpress", diff --git a/packages/js/number/changelog/50047-fix-typos b/packages/js/number/changelog/50047-fix-typos new file mode 100644 index 00000000000..e5d2f2b3db2 --- /dev/null +++ b/packages/js/number/changelog/50047-fix-typos @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Comment: Fix comment typos across various files. diff --git a/packages/js/number/changelog/50828-dev-constrain-pnpm-version b/packages/js/number/changelog/50828-dev-constrain-pnpm-version new file mode 100644 index 00000000000..fdead95ceb5 --- /dev/null +++ b/packages/js/number/changelog/50828-dev-constrain-pnpm-version @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix pnpm version to 9.1.3 to avoid dependency installation issues. \ No newline at end of file diff --git a/packages/js/number/changelog/fix-misspelling-in-parse-number-tests b/packages/js/number/changelog/fix-misspelling-in-parse-number-tests new file mode 100644 index 00000000000..25ded53e90c --- /dev/null +++ b/packages/js/number/changelog/fix-misspelling-in-parse-number-tests @@ -0,0 +1,4 @@ +Significance: minor +Type: tweak + +Fix typos in parse number tests diff --git a/packages/js/number/package.json b/packages/js/number/package.json index eebf9a2dede..44a2332d8df 100644 --- a/packages/js/number/package.json +++ b/packages/js/number/package.json @@ -6,7 +6,7 @@ "license": "GPL-3.0-or-later", "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "keywords": [ "wordpress", diff --git a/packages/js/number/src/test/index.ts b/packages/js/number/src/test/index.ts index b5d2ac089e3..f2db08195dc 100644 --- a/packages/js/number/src/test/index.ts +++ b/packages/js/number/src/test/index.ts @@ -50,7 +50,7 @@ describe( 'numberFormat', () => { } ); describe( 'parseNumber', () => { - it( 'should remove thousand seperator before parsing number', () => { + it( 'should remove thousand separator before parsing number', () => { const config = { decimalSeparator: ',', thousandSeparator: '.', @@ -59,7 +59,7 @@ describe( 'parseNumber', () => { expect( parseNumber( config, '12.345,679' ) ).toBe( '12345.679' ); } ); - it( 'supports empty string as the thousandSeperator', () => { + it( 'supports empty string as the thousandSeparator', () => { const config = { decimalSeparator: ',', thousandSeparator: '', @@ -68,7 +68,7 @@ describe( 'parseNumber', () => { expect( parseNumber( config, '12345,679' ) ).toBe( '12345.679' ); } ); - it( 'supports empty string as the decimalSeperator', () => { + it( 'supports empty string as the decimalSeparator', () => { const config = { decimalSeparator: '', thousandSeparator: ',', diff --git a/packages/js/onboarding/changelog/50828-dev-constrain-pnpm-version b/packages/js/onboarding/changelog/50828-dev-constrain-pnpm-version new file mode 100644 index 00000000000..fdead95ceb5 --- /dev/null +++ b/packages/js/onboarding/changelog/50828-dev-constrain-pnpm-version @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix pnpm version to 9.1.3 to avoid dependency installation issues. \ No newline at end of file diff --git a/packages/js/onboarding/package.json b/packages/js/onboarding/package.json index 55825d51629..0521b2f2163 100644 --- a/packages/js/onboarding/package.json +++ b/packages/js/onboarding/package.json @@ -6,7 +6,7 @@ "license": "GPL-3.0-or-later", "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "keywords": [ "wordpress", diff --git a/plugins/woocommerce/changelog/50196-add-improve_tracking_survey b/packages/js/product-editor/changelog/50993-fix-some-comment-typos similarity index 50% rename from plugins/woocommerce/changelog/50196-add-improve_tracking_survey rename to packages/js/product-editor/changelog/50993-fix-some-comment-typos index 36fc17908b2..3dce9f8d32b 100644 --- a/plugins/woocommerce/changelog/50196-add-improve_tracking_survey +++ b/packages/js/product-editor/changelog/50993-fix-some-comment-typos @@ -1,4 +1,4 @@ Significance: patch Type: update -CYS: Improve tracking survey \ No newline at end of file +Comment: Fix some comment typos. diff --git a/packages/js/product-editor/changelog/add-initial_product_data_views_screen b/packages/js/product-editor/changelog/add-initial_product_data_views_screen new file mode 100644 index 00000000000..9ad9bd3724f --- /dev/null +++ b/packages/js/product-editor/changelog/add-initial_product_data_views_screen @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add new product data views component for use on new product data views page. diff --git a/packages/js/product-editor/changelog/add-product_data_views_list b/packages/js/product-editor/changelog/add-product_data_views_list new file mode 100644 index 00000000000..548ca3eb055 --- /dev/null +++ b/packages/js/product-editor/changelog/add-product_data_views_list @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add sidebar and dataviews list to the experimental dataviews products page. diff --git a/packages/js/product-editor/changelog/add-upgrade-blocks-to-v3 b/packages/js/product-editor/changelog/add-upgrade-blocks-to-v3 new file mode 100644 index 00000000000..def28ee9bcc --- /dev/null +++ b/packages/js/product-editor/changelog/add-upgrade-blocks-to-v3 @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Update all blocks to use API Version 3. diff --git a/packages/js/product-editor/changelog/fix-misspelling-in-AligmentToolbarButton-func-name b/packages/js/product-editor/changelog/fix-misspelling-in-AligmentToolbarButton-func-name new file mode 100644 index 00000000000..3cc637e471c --- /dev/null +++ b/packages/js/product-editor/changelog/fix-misspelling-in-AligmentToolbarButton-func-name @@ -0,0 +1,4 @@ +Significance: minor +Type: tweak + +Fix typo in AligmentToolbarButton function name diff --git a/packages/js/product-editor/changelog/fix-misspelling-in-handleAdviceCardDismiss-func-name b/packages/js/product-editor/changelog/fix-misspelling-in-handleAdviceCardDismiss-func-name new file mode 100644 index 00000000000..2f54b48b2f0 --- /dev/null +++ b/packages/js/product-editor/changelog/fix-misspelling-in-handleAdviceCardDismiss-func-name @@ -0,0 +1,4 @@ +Significance: minor +Type: tweak + +Fix typo in handleAdviceCardDissmiss function name diff --git a/packages/js/product-editor/changelog/fix-misspelling-in-initCustomFieldsToogle-variable b/packages/js/product-editor/changelog/fix-misspelling-in-initCustomFieldsToogle-variable new file mode 100644 index 00000000000..81ef2df71bd --- /dev/null +++ b/packages/js/product-editor/changelog/fix-misspelling-in-initCustomFieldsToogle-variable @@ -0,0 +1,4 @@ +Significance: minor +Type: tweak + +Fix typo in initCustomFieldsToogle variable name diff --git a/packages/js/product-editor/changelog/fix-misspelling-in-noticeDimissed-ref b/packages/js/product-editor/changelog/fix-misspelling-in-noticeDimissed-ref new file mode 100644 index 00000000000..f9fda3b1ea1 --- /dev/null +++ b/packages/js/product-editor/changelog/fix-misspelling-in-noticeDimissed-ref @@ -0,0 +1,4 @@ +Significance: minor +Type: tweak + +Fix typo in noticeDismissed ref diff --git a/packages/js/product-editor/changelog/fix-toggle-misspelling-in-class-names b/packages/js/product-editor/changelog/fix-toggle-misspelling-in-class-names new file mode 100644 index 00000000000..cdef9bd7270 --- /dev/null +++ b/packages/js/product-editor/changelog/fix-toggle-misspelling-in-class-names @@ -0,0 +1,4 @@ +Significance: minor +Type: tweak + +Fix toogle typo in class names diff --git a/packages/js/product-editor/package.json b/packages/js/product-editor/package.json index 1496fa6de08..52ec6eacb88 100644 --- a/packages/js/product-editor/package.json +++ b/packages/js/product-editor/package.json @@ -56,6 +56,7 @@ "@wordpress/compose": "wp-6.0", "@wordpress/core-data": "wp-6.0", "@wordpress/data": "wp-6.0", + "@wordpress/dataviews": "^4.2.0", "@wordpress/date": "wp-6.0", "@wordpress/deprecated": "wp-6.0", "@wordpress/edit-post": "wp-6.0", @@ -64,13 +65,14 @@ "@wordpress/hooks": "wp-6.0", "@wordpress/html-entities": "wp-6.0", "@wordpress/i18n": "wp-6.0", - "@wordpress/icons": "wp-6.0", + "@wordpress/icons": "10.6.0", "@wordpress/interface": "wp-6.0", "@wordpress/keyboard-shortcuts": "wp-6.0", "@wordpress/keycodes": "wp-6.0", "@wordpress/media-utils": "wp-6.0", "@wordpress/plugins": "wp-6.0", "@wordpress/preferences": "wp-6.0", + "@wordpress/private-apis": "^1.6.0", "@wordpress/url": "wp-6.0", "classnames": "^2.3.2", "dompurify": "^2.4.7", diff --git a/packages/js/product-editor/src/blocks/generic/checkbox/block.json b/packages/js/product-editor/src/blocks/generic/checkbox/block.json index 49b4d26080f..8cd8ae0109a 100644 --- a/packages/js/product-editor/src/blocks/generic/checkbox/block.json +++ b/packages/js/product-editor/src/blocks/generic/checkbox/block.json @@ -1,11 +1,15 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-checkbox-field", "title": "Product checkbox control", "category": "woocommerce", "description": "A reusable checkbox for the product editor.", - "keywords": [ "products", "checkbox", "input" ], + "keywords": [ + "products", + "checkbox", + "input" + ], "textdomain": "default", "attributes": { "title": { @@ -38,5 +42,7 @@ "__experimentalToolbar": false }, "editorStyle": "file:./editor.css", - "usesContext": [ "postType" ] -} + "usesContext": [ + "postType" + ] +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/generic/collapsible/block.json b/packages/js/product-editor/src/blocks/generic/collapsible/block.json index 8cf084538cf..08ef5803965 100644 --- a/packages/js/product-editor/src/blocks/generic/collapsible/block.json +++ b/packages/js/product-editor/src/blocks/generic/collapsible/block.json @@ -1,6 +1,6 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-collapsible", "title": "Collapsible", "category": "widgets", @@ -27,4 +27,4 @@ "lock": false, "__experimentalToolbar": false } -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/generic/conditional/block.json b/packages/js/product-editor/src/blocks/generic/conditional/block.json index 5afff2f5865..20b825c2f25 100644 --- a/packages/js/product-editor/src/blocks/generic/conditional/block.json +++ b/packages/js/product-editor/src/blocks/generic/conditional/block.json @@ -1,6 +1,6 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/conditional", "title": "Conditional", "category": "widgets", @@ -25,4 +25,4 @@ "lock": false, "__experimentalToolbar": false } -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/generic/linked-product-list/block.json b/packages/js/product-editor/src/blocks/generic/linked-product-list/block.json index 245876fc294..65aa7146e71 100644 --- a/packages/js/product-editor/src/blocks/generic/linked-product-list/block.json +++ b/packages/js/product-editor/src/blocks/generic/linked-product-list/block.json @@ -1,11 +1,15 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-linked-list-field", "title": "Linked product list", "category": "widgets", "description": "The linked product list.", - "keywords": [ "products", "linked", "list" ], + "keywords": [ + "products", + "linked", + "list" + ], "textdomain": "default", "attributes": { "property": { @@ -27,5 +31,8 @@ "__experimentalToolbar": false }, "editorStyle": "file:./editor.css", - "usesContext": [ "postType", "isInSelectedTab" ] -} + "usesContext": [ + "postType", + "isInSelectedTab" + ] +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/generic/linked-product-list/edit.tsx b/packages/js/product-editor/src/blocks/generic/linked-product-list/edit.tsx index 96a3767afa1..12636655cd8 100644 --- a/packages/js/product-editor/src/blocks/generic/linked-product-list/edit.tsx +++ b/packages/js/product-editor/src/blocks/generic/linked-product-list/edit.tsx @@ -259,7 +259,7 @@ export function LinkedProductListBlockEdit( { setLinkedProductIds( newLinkedProducts ); } - function handleAdviceCardDissmiss() { + function handleAdviceCardDismiss() { recordEvent( 'linked_products_placeholder_dismiss', { source: TRACKS_SOURCE, field: property, @@ -297,7 +297,7 @@ export function LinkedProductListBlockEdit( { tip={ emptyState.tip } dismissPreferenceId={ `woocommerce-product-${ property }-advice-card-dismissed` } isDismissible={ emptyState.isDismissible } - onDismiss={ handleAdviceCardDissmiss } + onDismiss={ handleAdviceCardDismiss } > diff --git a/packages/js/product-editor/src/blocks/generic/number/block.json b/packages/js/product-editor/src/blocks/generic/number/block.json index c8fdbc69878..dada23bf4fb 100644 --- a/packages/js/product-editor/src/blocks/generic/number/block.json +++ b/packages/js/product-editor/src/blocks/generic/number/block.json @@ -1,11 +1,15 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-number-field", "title": "Product number control", "category": "woocommerce", "description": "A reusable number field for the product editor.", - "keywords": [ "products", "number", "input" ], + "keywords": [ + "products", + "number", + "input" + ], "textdomain": "default", "attributes": { "label": { @@ -52,4 +56,4 @@ "__experimentalToolbar": false }, "editorStyle": "file:./editor.css" -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/generic/pricing/block.json b/packages/js/product-editor/src/blocks/generic/pricing/block.json index 1402b64c62f..b2f1b6eaff8 100644 --- a/packages/js/product-editor/src/blocks/generic/pricing/block.json +++ b/packages/js/product-editor/src/blocks/generic/pricing/block.json @@ -1,11 +1,14 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-pricing-field", "description": "A product price block with currency display.", "title": "Product pricing", "category": "widgets", - "keywords": [ "products", "price" ], + "keywords": [ + "products", + "price" + ], "textdomain": "default", "attributes": { "property": { @@ -32,5 +35,7 @@ "__experimentalToolbar": false }, "editorStyle": "file:./editor.css", - "usesContext": [ "postType" ] -} + "usesContext": [ + "postType" + ] +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/generic/radio/block.json b/packages/js/product-editor/src/blocks/generic/radio/block.json index d480795cf9e..45dc7f7d084 100644 --- a/packages/js/product-editor/src/blocks/generic/radio/block.json +++ b/packages/js/product-editor/src/blocks/generic/radio/block.json @@ -1,11 +1,15 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-radio-field", "title": "Product radio control", "category": "woocommerce", "description": "The product radio.", - "keywords": [ "products", "radio", "input" ], + "keywords": [ + "products", + "radio", + "input" + ], "textdomain": "default", "attributes": { "title": { @@ -39,5 +43,7 @@ "lock": false, "__experimentalToolbar": false }, - "usesContext": [ "postType" ] -} + "usesContext": [ + "postType" + ] +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/generic/section-description/block.json b/packages/js/product-editor/src/blocks/generic/section-description/block.json index a31b7a0e3e6..dbccdf98913 100644 --- a/packages/js/product-editor/src/blocks/generic/section-description/block.json +++ b/packages/js/product-editor/src/blocks/generic/section-description/block.json @@ -1,11 +1,15 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-section-description", "title": "Product section description", "category": "woocommerce", "description": "The product section description.", - "keywords": [ "products", "section", "description" ], + "keywords": [ + "products", + "section", + "description" + ], "textdomain": "default", "attributes": { "content": { @@ -22,4 +26,4 @@ "lock": false, "__experimentalToolbar": false } -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/generic/section/block.json b/packages/js/product-editor/src/blocks/generic/section/block.json index 17a503bbdfd..b2d77ecfb43 100644 --- a/packages/js/product-editor/src/blocks/generic/section/block.json +++ b/packages/js/product-editor/src/blocks/generic/section/block.json @@ -1,11 +1,15 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-section", "title": "Product section", "category": "woocommerce", "description": "The product section.", - "keywords": [ "products", "section", "group" ], + "keywords": [ + "products", + "section", + "group" + ], "textdomain": "default", "attributes": { "title": { @@ -17,7 +21,11 @@ }, "blockGap": { "type": "string", - "enum": [ "unit-20", "unit-30", "unit-40" ], + "enum": [ + "unit-20", + "unit-30", + "unit-40" + ], "default": "unit-20" } }, @@ -31,4 +39,4 @@ "__experimentalToolbar": false }, "editorStyle": "file:./editor.css" -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/generic/select/block.json b/packages/js/product-editor/src/blocks/generic/select/block.json index 620f3300fa2..738ebdcbfbd 100644 --- a/packages/js/product-editor/src/blocks/generic/select/block.json +++ b/packages/js/product-editor/src/blocks/generic/select/block.json @@ -1,11 +1,14 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-select-field", "title": "Product select field", "category": "woocommerce", "description": "A select field for use in the product editor.", - "keywords": [ "products", "select" ], + "keywords": [ + "products", + "select" + ], "textdomain": "default", "attributes": { "label": { @@ -60,5 +63,7 @@ "lock": false, "__experimentalToolbar": false }, - "usesContext": [ "postType" ] -} + "usesContext": [ + "postType" + ] +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/generic/subsection-description/block.json b/packages/js/product-editor/src/blocks/generic/subsection-description/block.json index 58bb4dbc4ab..12c0712c53c 100644 --- a/packages/js/product-editor/src/blocks/generic/subsection-description/block.json +++ b/packages/js/product-editor/src/blocks/generic/subsection-description/block.json @@ -1,11 +1,15 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-subsection-description", "title": "Product subsection description", "category": "woocommerce", "description": "The product subsection description.", - "keywords": [ "products", "subsection", "description" ], + "keywords": [ + "products", + "subsection", + "description" + ], "textdomain": "default", "attributes": { "content": { @@ -22,4 +26,4 @@ "lock": false, "__experimentalToolbar": false } -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/generic/subsection/block.json b/packages/js/product-editor/src/blocks/generic/subsection/block.json index 6cbac142291..4dc20a21fd2 100644 --- a/packages/js/product-editor/src/blocks/generic/subsection/block.json +++ b/packages/js/product-editor/src/blocks/generic/subsection/block.json @@ -1,11 +1,15 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-subsection", "title": "Product subsection", "category": "woocommerce", "description": "The product subsection.", - "keywords": [ "products", "subsection", "group" ], + "keywords": [ + "products", + "subsection", + "group" + ], "textdomain": "default", "attributes": { "title": { @@ -17,7 +21,11 @@ }, "blockGap": { "type": "string", - "enum": [ "unit-20", "unit-30", "unit-40" ], + "enum": [ + "unit-20", + "unit-30", + "unit-40" + ], "default": "unit-20" } }, @@ -31,4 +39,4 @@ "__experimentalToolbar": false }, "editorStyle": "file:./editor.css" -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/generic/tab/block.json b/packages/js/product-editor/src/blocks/generic/tab/block.json index 537e245ae57..20c1e649166 100644 --- a/packages/js/product-editor/src/blocks/generic/tab/block.json +++ b/packages/js/product-editor/src/blocks/generic/tab/block.json @@ -1,11 +1,15 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-tab", "title": "Product tab", "category": "woocommerce", "description": "The product tab.", - "keywords": [ "products", "tab", "group" ], + "keywords": [ + "products", + "tab", + "group" + ], "textdomain": "default", "attributes": { "id": { @@ -27,6 +31,8 @@ "providesContext": { "isInSelectedTab": "isSelected" }, - "usesContext": [ "selectedTab" ], + "usesContext": [ + "selectedTab" + ], "editorStyle": "file:./editor.css" -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/generic/taxonomy/block.json b/packages/js/product-editor/src/blocks/generic/taxonomy/block.json index 69b5423852b..eeab0afc2c5 100644 --- a/packages/js/product-editor/src/blocks/generic/taxonomy/block.json +++ b/packages/js/product-editor/src/blocks/generic/taxonomy/block.json @@ -1,11 +1,13 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-taxonomy-field", "title": "Taxonomy", "category": "widgets", "description": "A block that displays a taxonomy field, allowing searching, selection, and creation of new items", - "keywords": [ "taxonomy" ], + "keywords": [ + "taxonomy" + ], "textdomain": "default", "attributes": { "slug": { @@ -51,5 +53,8 @@ "__experimentalToolbar": false }, "editorStyle": "file:./editor.css", - "usesContext": [ "postType", "isInSelectedTab" ] -} + "usesContext": [ + "postType", + "isInSelectedTab" + ] +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/generic/text-area/block.json b/packages/js/product-editor/src/blocks/generic/text-area/block.json index dd750f115d3..a9b16ba55f7 100644 --- a/packages/js/product-editor/src/blocks/generic/text-area/block.json +++ b/packages/js/product-editor/src/blocks/generic/text-area/block.json @@ -1,11 +1,14 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-text-area-field", "title": "Product textarea block", "category": "woocommerce", "description": "A text-area field for use in the product editor.", - "keywords": [ "textarea", "rich-text" ], + "keywords": [ + "textarea", + "rich-text" + ], "textdomain": "default", "attributes": { "property": { @@ -32,11 +35,19 @@ }, "align": { "type": "string", - "enum": [ "left", "center", "right", "justify" ] + "enum": [ + "left", + "center", + "right", + "justify" + ] }, "mode": { "type": "string", - "enum": [ "plain-text", "rich-text" ], + "enum": [ + "plain-text", + "rich-text" + ], "default": "rich-text" }, "allowedFormats": { @@ -56,7 +67,10 @@ }, "direction": { "type": "string", - "enum": [ "ltr", "rtl" ] + "enum": [ + "ltr", + "rtl" + ] } }, "supports": { @@ -68,4 +82,4 @@ "lock": false, "__experimentalToolbar": true } -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/generic/text-area/edit.tsx b/packages/js/product-editor/src/blocks/generic/text-area/edit.tsx index 163c9fcb927..7c0430ef478 100644 --- a/packages/js/product-editor/src/blocks/generic/text-area/edit.tsx +++ b/packages/js/product-editor/src/blocks/generic/text-area/edit.tsx @@ -18,7 +18,7 @@ import type { TextAreaBlockEditAttributes, TextAreaBlockEditProps, } from './types'; -import AligmentToolbarButton from './toolbar/toolbar-button-alignment'; +import AlignmentToolbarButton from './toolbar/toolbar-button-alignment'; import { useClearSelectedBlockOnBlur } from '../../../hooks/use-clear-selected-block-on-blur'; import useProductEntityProp from '../../../hooks/use-product-entity-prop'; import { Label } from '../../../components/label/label'; @@ -100,7 +100,7 @@ export function TextAreaBlockEdit( {
{ isRichTextMode && ( - diff --git a/packages/js/product-editor/src/blocks/generic/text-area/toolbar/toolbar-button-alignment/index.tsx b/packages/js/product-editor/src/blocks/generic/text-area/toolbar/toolbar-button-alignment/index.tsx index 9b0e48d6c94..5ca9d283d31 100644 --- a/packages/js/product-editor/src/blocks/generic/text-area/toolbar/toolbar-button-alignment/index.tsx +++ b/packages/js/product-editor/src/blocks/generic/text-area/toolbar/toolbar-button-alignment/index.tsx @@ -38,7 +38,7 @@ export const ALIGNMENT_CONTROLS = [ }, ]; -export default function AligmentToolbarButton( { +export default function AlignmentToolbarButton( { align, setAlignment, }: AlignmentControl ) { diff --git a/packages/js/product-editor/src/blocks/generic/text/block.json b/packages/js/product-editor/src/blocks/generic/text/block.json index 4a2f037fe73..88314b4be5e 100644 --- a/packages/js/product-editor/src/blocks/generic/text/block.json +++ b/packages/js/product-editor/src/blocks/generic/text/block.json @@ -1,11 +1,14 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-text-field", "title": "Product text field", "category": "woocommerce", "description": "A text field for use in the product editor.", - "keywords": [ "products", "text" ], + "keywords": [ + "products", + "text" + ], "textdomain": "default", "attributes": { "label": { @@ -59,5 +62,7 @@ "__experimentalToolbar": false }, "editorStyle": "file:./editor.css", - "usesContext": [ "postType" ] -} + "usesContext": [ + "postType" + ] +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/generic/toggle/block.json b/packages/js/product-editor/src/blocks/generic/toggle/block.json index 5b41cb951c8..c26a3261401 100644 --- a/packages/js/product-editor/src/blocks/generic/toggle/block.json +++ b/packages/js/product-editor/src/blocks/generic/toggle/block.json @@ -1,11 +1,15 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-toggle-field", "title": "Product toggle control", "category": "woocommerce", "description": "The product toggle.", - "keywords": [ "products", "radio", "input" ], + "keywords": [ + "products", + "radio", + "input" + ], "textdomain": "default", "attributes": { "label": { @@ -48,5 +52,7 @@ "lock": false, "__experimentalToolbar": false }, - "usesContext": [ "postType" ] -} + "usesContext": [ + "postType" + ] +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/index.ts b/packages/js/product-editor/src/blocks/index.ts index e6cdbf926a8..80c8f4b2a50 100644 --- a/packages/js/product-editor/src/blocks/index.ts +++ b/packages/js/product-editor/src/blocks/index.ts @@ -1,6 +1,6 @@ export { init as initCatalogVisibility } from './product-fields/catalog-visibility'; export { init as initCustomFields } from './product-fields/custom-fields'; -export { init as initCustomFieldsToogle } from './product-fields/custom-fields-toggle'; +export { init as initCustomFieldsToggle } from './product-fields/custom-fields-toggle'; export { init as initCheckbox } from './generic/checkbox'; export { init as initCollapsible } from './generic/collapsible'; export { init as initConditional } from './generic/conditional'; diff --git a/packages/js/product-editor/src/blocks/product-fields/attributes/block.json b/packages/js/product-editor/src/blocks/product-fields/attributes/block.json index aed5dd2c121..7d69925cfdc 100644 --- a/packages/js/product-editor/src/blocks/product-fields/attributes/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/attributes/block.json @@ -1,11 +1,14 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-attributes-field", "title": "Product attributes", "category": "widgets", "description": "The product attributes.", - "keywords": [ "products", "attributes" ], + "keywords": [ + "products", + "attributes" + ], "textdomain": "default", "attributes": { "name": { @@ -22,6 +25,8 @@ "lock": false, "__experimentalToolbar": false }, - "usesContext": [ "isInSelectedTab" ], + "usesContext": [ + "isInSelectedTab" + ], "editorStyle": "file:./editor.css" -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/product-fields/catalog-visibility/block.json b/packages/js/product-editor/src/blocks/product-fields/catalog-visibility/block.json index b70967f8fa0..18b37da13e3 100644 --- a/packages/js/product-editor/src/blocks/product-fields/catalog-visibility/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/catalog-visibility/block.json @@ -1,11 +1,14 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-catalog-visibility-field", "description": "A checkbox to manage the catalog visibility of the product.", "title": "Product catalog visibility", "category": "widgets", - "keywords": [ "products", "catalog" ], + "keywords": [ + "products", + "catalog" + ], "textdomain": "default", "attributes": { "label": { @@ -14,7 +17,12 @@ }, "visibility": { "type": "string", - "enum": [ "visible", "catalog", "search", "hidden" ], + "enum": [ + "visible", + "catalog", + "search", + "hidden" + ], "default": "visible" } }, @@ -27,4 +35,4 @@ "lock": false, "__experimentalToolbar": false } -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/product-fields/custom-fields-toggle/block.json b/packages/js/product-editor/src/blocks/product-fields/custom-fields-toggle/block.json index ae2da4df2df..08aca1dcf79 100644 --- a/packages/js/product-editor/src/blocks/product-fields/custom-fields-toggle/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/custom-fields-toggle/block.json @@ -1,11 +1,15 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-custom-fields-toggle-field", "title": "Product custom fields toggle control", "category": "woocommerce", "description": "The product custom fields toggle.", - "keywords": [ "products", "custom", "fields" ], + "keywords": [ + "products", + "custom", + "fields" + ], "textdomain": "default", "attributes": { "label": { @@ -23,4 +27,4 @@ "__experimentalToolbar": false }, "editorStyle": "file:./editor.css" -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/product-fields/custom-fields/block.json b/packages/js/product-editor/src/blocks/product-fields/custom-fields/block.json index 73824721fe2..250d1b3a2db 100644 --- a/packages/js/product-editor/src/blocks/product-fields/custom-fields/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/custom-fields/block.json @@ -1,11 +1,15 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-custom-fields", "title": "Product custom fields control", "category": "woocommerce", "description": "The product custom fields.", - "keywords": [ "products", "custom", "fields" ], + "keywords": [ + "products", + "custom", + "fields" + ], "textdomain": "default", "attributes": { "name": { @@ -22,4 +26,4 @@ "lock": false, "__experimentalToolbar": false } -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/product-fields/description/block.json b/packages/js/product-editor/src/blocks/product-fields/description/block.json index 3306eedfe1a..d8cbbbc98a7 100644 --- a/packages/js/product-editor/src/blocks/product-fields/description/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/description/block.json @@ -1,11 +1,14 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-description-field", "title": "Product description", "category": "woocommerce", "description": "The product description.", - "keywords": [ "products", "description" ], + "keywords": [ + "products", + "description" + ], "textdomain": "default", "attributes": { "__contentEditable": { @@ -22,4 +25,4 @@ "lock": false, "__experimentalToolbar": true } -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/product-fields/downloads/block.json b/packages/js/product-editor/src/blocks/product-fields/downloads/block.json index 4f081aa6a28..a80b3ad6439 100644 --- a/packages/js/product-editor/src/blocks/product-fields/downloads/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/downloads/block.json @@ -1,11 +1,14 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-downloads-field", "title": "Product downloads", "category": "widgets", "description": "The product downloads.", - "keywords": [ "products", "downloads" ], + "keywords": [ + "products", + "downloads" + ], "textdomain": "default", "attributes": { "name": { @@ -23,5 +26,7 @@ "__experimentalToolbar": false }, "editorStyle": "file:./editor.css", - "usesContext": [ "postType" ] -} + "usesContext": [ + "postType" + ] +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/product-fields/downloads/downloads-menu/downloads-menu.tsx b/packages/js/product-editor/src/blocks/product-fields/downloads/downloads-menu/downloads-menu.tsx index 3bf0ff3f2d5..e90a3921cf7 100644 --- a/packages/js/product-editor/src/blocks/product-fields/downloads/downloads-menu/downloads-menu.tsx +++ b/packages/js/product-editor/src/blocks/product-fields/downloads/downloads-menu/downloads-menu.tsx @@ -33,7 +33,7 @@ export function DownloadsMenu( { icon={ isOpen ? chevronUp : chevronDown } variant="secondary" onClick={ onToggle } - className="woocommerce-downloads-menu__toogle" + className="woocommerce-downloads-menu__toggle" > { __( 'Add new', 'woocommerce' ) } diff --git a/packages/js/product-editor/src/blocks/product-fields/downloads/downloads-menu/style.scss b/packages/js/product-editor/src/blocks/product-fields/downloads/downloads-menu/style.scss index fe56bd2c248..0cec19b4642 100644 --- a/packages/js/product-editor/src/blocks/product-fields/downloads/downloads-menu/style.scss +++ b/packages/js/product-editor/src/blocks/product-fields/downloads/downloads-menu/style.scss @@ -1,5 +1,5 @@ .woocommerce-downloads-menu { - &__toogle { + &__toggle { flex-direction: row-reverse; > span { diff --git a/packages/js/product-editor/src/blocks/product-fields/images/block.json b/packages/js/product-editor/src/blocks/product-fields/images/block.json index 12fe9006930..40885aeb5fc 100644 --- a/packages/js/product-editor/src/blocks/product-fields/images/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/images/block.json @@ -1,11 +1,16 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-images-field", "title": "Product images", "category": "widgets", "description": "The product images.", - "keywords": [ "products", "image", "images", "gallery" ], + "keywords": [ + "products", + "image", + "images", + "gallery" + ], "textdomain": "default", "attributes": { "mediaId": { @@ -38,5 +43,7 @@ "__experimentalToolbar": false }, "editorStyle": "file:./editor.css", - "usesContext": [ "postType" ] -} + "usesContext": [ + "postType" + ] +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/product-fields/inventory-email/block.json b/packages/js/product-editor/src/blocks/product-fields/inventory-email/block.json index 5ac6cf80d4c..2b54ab1cb5f 100644 --- a/packages/js/product-editor/src/blocks/product-fields/inventory-email/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/inventory-email/block.json @@ -1,11 +1,16 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-inventory-email-field", "title": "Stock level threshold", "category": "widgets", "description": "Stock management minimum quantity.", - "keywords": [ "products", "inventory", "email", "minimum" ], + "keywords": [ + "products", + "inventory", + "email", + "minimum" + ], "textdomain": "default", "attributes": { "name": { @@ -23,4 +28,4 @@ "__experimentalToolbar": false }, "editorStyle": "file:./editor.css" -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/product-fields/inventory-quantity/block.json b/packages/js/product-editor/src/blocks/product-fields/inventory-quantity/block.json index fe649ca6a38..67b23f5d447 100644 --- a/packages/js/product-editor/src/blocks/product-fields/inventory-quantity/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/inventory-quantity/block.json @@ -1,11 +1,15 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-inventory-quantity-field", "title": "Product inventory quantity available", "category": "woocommerce", "description": "The product available quantity.", - "keywords": [ "products", "quantity", "inventory" ], + "keywords": [ + "products", + "quantity", + "inventory" + ], "textdomain": "default", "attributes": { "name": { @@ -22,5 +26,7 @@ "lock": false, "__experimentalToolbar": false }, - "usesContext": [ "postType" ] -} + "usesContext": [ + "postType" + ] +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/product-fields/inventory-sku/block.json b/packages/js/product-editor/src/blocks/product-fields/inventory-sku/block.json index 5608abb15a1..39940b25b6c 100644 --- a/packages/js/product-editor/src/blocks/product-fields/inventory-sku/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/inventory-sku/block.json @@ -1,11 +1,14 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-sku-field", "title": "Product text control", "category": "woocommerce", "description": "The product sku.", - "keywords": [ "products", "sku" ], + "keywords": [ + "products", + "sku" + ], "textdomain": "default", "attributes": { "name": { @@ -27,5 +30,7 @@ "__experimentalToolbar": false }, "editorStyle": "file:./editor.css", - "usesContext": [ "postType" ] -} + "usesContext": [ + "postType" + ] +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/product-fields/name/block.json b/packages/js/product-editor/src/blocks/product-fields/name/block.json index 4196c26f22c..06f41489f8c 100644 --- a/packages/js/product-editor/src/blocks/product-fields/name/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/name/block.json @@ -1,11 +1,15 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-name-field", "title": "Product name", "category": "widgets", "description": "The product name.", - "keywords": [ "products", "name", "title" ], + "keywords": [ + "products", + "name", + "title" + ], "textdomain": "default", "attributes": { "name": { @@ -27,4 +31,4 @@ "__experimentalToolbar": false }, "editorStyle": "file:./editor.css" -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/product-fields/notice-edit-single-variation/block.json b/packages/js/product-editor/src/blocks/product-fields/notice-edit-single-variation/block.json index bc9bc1b23cf..1704cb9762b 100644 --- a/packages/js/product-editor/src/blocks/product-fields/notice-edit-single-variation/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/notice-edit-single-variation/block.json @@ -1,11 +1,14 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-single-variation-notice", "title": "Notice", "category": "woocommerce", "description": "Notice description", - "keywords": [ "products", "notice" ], + "keywords": [ + "products", + "notice" + ], "textdomain": "default", "attributes": { "title": { @@ -31,4 +34,4 @@ "__experimentalToolbar": false }, "editorStyle": "file:./editor.css" -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/product-fields/notice-has-variations/block.json b/packages/js/product-editor/src/blocks/product-fields/notice-has-variations/block.json index 5537dd68381..655557e52ed 100644 --- a/packages/js/product-editor/src/blocks/product-fields/notice-has-variations/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/notice-has-variations/block.json @@ -1,11 +1,14 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-has-variations-notice", "title": "Notice", "category": "woocommerce", "description": "Notice description", - "keywords": [ "products", "notice" ], + "keywords": [ + "products", + "notice" + ], "textdomain": "default", "attributes": { "title": { @@ -31,4 +34,4 @@ "__experimentalToolbar": false }, "editorStyle": "file:./editor.css" -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/product-fields/password/block.json b/packages/js/product-editor/src/blocks/product-fields/password/block.json index 15f4517dc22..35044a5d4e8 100644 --- a/packages/js/product-editor/src/blocks/product-fields/password/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/password/block.json @@ -1,11 +1,14 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-password-field", "description": "A checkbox and an input to type a password to view a product.", "title": "Product password", "category": "widgets", - "keywords": [ "products", "password" ], + "keywords": [ + "products", + "password" + ], "textdomain": "default", "attributes": { "label": { @@ -23,4 +26,4 @@ "__experimentalToolbar": false }, "editorStyle": "file:./editor.css" -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/product-fields/product-details-section-description/block.json b/packages/js/product-editor/src/blocks/product-fields/product-details-section-description/block.json index c3c390e70d4..8f43d17d52b 100644 --- a/packages/js/product-editor/src/blocks/product-fields/product-details-section-description/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/product-details-section-description/block.json @@ -1,11 +1,15 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-details-section-description", "title": "Product details section description", "category": "woocommerce", "description": "The product details section description.", - "keywords": [ "products", "section", "description" ], + "keywords": [ + "products", + "section", + "description" + ], "textdomain": "default", "attributes": { "content": { @@ -24,4 +28,4 @@ }, "usesContext": [ "selectedTab" ], "editorStyle": "file:./editor.css" -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/product-fields/product-list/block.json b/packages/js/product-editor/src/blocks/product-fields/product-list/block.json index 644a6a4a18b..e32abe029ae 100644 --- a/packages/js/product-editor/src/blocks/product-fields/product-list/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/product-list/block.json @@ -1,11 +1,13 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-list-field", "title": "Product list", "category": "widgets", "description": "The product list.", - "keywords": [ "products" ], + "keywords": [ + "products" + ], "textdomain": "default", "attributes": { "property": { @@ -23,5 +25,7 @@ "__experimentalToolbar": false }, "editorStyle": "file:./editor.css", - "usesContext": [ "postType" ] -} + "usesContext": [ + "postType" + ] +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/product-fields/regular-price/block.json b/packages/js/product-editor/src/blocks/product-fields/regular-price/block.json index ae8a00100d8..50ac2bd40a7 100644 --- a/packages/js/product-editor/src/blocks/product-fields/regular-price/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/regular-price/block.json @@ -1,11 +1,14 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-regular-price-field", "description": "A product price block with currency display.", "title": "Product regular price", "category": "widgets", - "keywords": [ "products", "price" ], + "keywords": [ + "products", + "price" + ], "textdomain": "default", "attributes": { "label": { @@ -36,6 +39,8 @@ "lock": false, "__experimentalToolbar": false }, - "usesContext": [ "postType" ], + "usesContext": [ + "postType" + ], "editorStyle": "file:./editor.css" -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/product-fields/sale-price/block.json b/packages/js/product-editor/src/blocks/product-fields/sale-price/block.json index b68ecb4522a..0854aca955d 100644 --- a/packages/js/product-editor/src/blocks/product-fields/sale-price/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/sale-price/block.json @@ -1,11 +1,14 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-sale-price-field", "description": "A product price block with currency display.", "title": "Product sale price", "category": "widgets", - "keywords": [ "products", "price" ], + "keywords": [ + "products", + "price" + ], "textdomain": "default", "attributes": { "label": { @@ -32,6 +35,8 @@ "lock": false, "__experimentalToolbar": false }, - "usesContext": [ "postType" ], + "usesContext": [ + "postType" + ], "editorStyle": "file:./editor.css" -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/product-fields/schedule-sale/block.json b/packages/js/product-editor/src/blocks/product-fields/schedule-sale/block.json index fe4a0d8dc6f..147ef02fb3d 100644 --- a/packages/js/product-editor/src/blocks/product-fields/schedule-sale/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/schedule-sale/block.json @@ -1,11 +1,15 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-schedule-sale-fields", "title": "Product schedule sale fields", "category": "woocommerce", "description": "The product schedule sale fields.", - "keywords": [ "products", "schedule", "sale" ], + "keywords": [ + "products", + "schedule", + "sale" + ], "textdomain": "default", "attributes": { "name": { @@ -23,5 +27,7 @@ "__experimentalToolbar": false }, "editorStyle": "file:./editor.css", - "usesContext": [ "postType" ] -} + "usesContext": [ + "postType" + ] +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/product-fields/shipping-class/block.json b/packages/js/product-editor/src/blocks/product-fields/shipping-class/block.json index 3ff8fcaccd5..bc52ab051bb 100644 --- a/packages/js/product-editor/src/blocks/product-fields/shipping-class/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/shipping-class/block.json @@ -1,11 +1,15 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-shipping-class-field", "title": "Product shipping class field", "category": "woocommerce", "description": "The product shipping class field.", - "keywords": [ "products", "shipping", "class" ], + "keywords": [ + "products", + "shipping", + "class" + ], "textdomain": "default", "attributes": { "title": { @@ -26,5 +30,8 @@ "lock": false, "__experimentalToolbar": false }, - "usesContext": [ "postType", "isInSelectedTab" ] -} + "usesContext": [ + "postType", + "isInSelectedTab" + ] +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/product-fields/shipping-dimensions/block.json b/packages/js/product-editor/src/blocks/product-fields/shipping-dimensions/block.json index d012cf18546..abe8db37d3d 100644 --- a/packages/js/product-editor/src/blocks/product-fields/shipping-dimensions/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/shipping-dimensions/block.json @@ -1,11 +1,15 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-shipping-dimensions-fields", "title": "Product shipping dimensions fields", "category": "woocommerce", "description": "The product shipping dimensions fields.", - "keywords": [ "products", "shipping", "dimensions" ], + "keywords": [ + "products", + "shipping", + "dimensions" + ], "textdomain": "default", "attributes": { "__contentEditable": { @@ -27,5 +31,7 @@ "__experimentalToolbar": false }, "editorStyle": "file:./editor.css", - "usesContext": [ "postType" ] -} + "usesContext": [ + "postType" + ] +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/product-fields/summary/block.json b/packages/js/product-editor/src/blocks/product-fields/summary/block.json index 50a10675278..fec3164c06a 100644 --- a/packages/js/product-editor/src/blocks/product-fields/summary/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/summary/block.json @@ -1,11 +1,15 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-summary-field", "title": "Product summary", "category": "widgets", "description": "The product summary.", - "keywords": [ "products", "summary", "excerpt" ], + "keywords": [ + "products", + "summary", + "excerpt" + ], "textdomain": "default", "attributes": { "property": { @@ -31,7 +35,10 @@ }, "direction": { "type": "string", - "enum": [ "ltr", "rtl" ] + "enum": [ + "ltr", + "rtl" + ] }, "label": { "type": "string" @@ -53,5 +60,7 @@ "lock": false }, "editorStyle": "file:./editor.css", - "usesContext": [ "postType" ] -} + "usesContext": [ + "postType" + ] +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/product-fields/tag/block.json b/packages/js/product-editor/src/blocks/product-fields/tag/block.json index 57c997a6bd1..8e5724eb69b 100644 --- a/packages/js/product-editor/src/blocks/product-fields/tag/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/tag/block.json @@ -1,11 +1,14 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-tag-field", "title": "Product Tag", "category": "widgets", "description": "A field to select product tags.", - "keywords": [ "products", "tag" ], + "keywords": [ + "products", + "tag" + ], "textdomain": "default", "attributes": { "name": { @@ -19,7 +22,10 @@ "type": "string" } }, - "usesContext": [ "postType", "isInSelectedTab" ], + "usesContext": [ + "postType", + "isInSelectedTab" + ], "supports": { "align": false, "html": false, @@ -30,4 +36,4 @@ "__experimentalToolbar": false }, "editorStyle": "file:./editor.css" -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/product-fields/variation-items/block.json b/packages/js/product-editor/src/blocks/product-fields/variation-items/block.json index 7c0ad11950c..1dc1d746e62 100644 --- a/packages/js/product-editor/src/blocks/product-fields/variation-items/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/variation-items/block.json @@ -1,11 +1,14 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-variation-items-field", "title": "Product variations items", "category": "woocommerce", "description": "The product variations items.", - "keywords": [ "products", "variations" ], + "keywords": [ + "products", + "variations" + ], "textdomain": "default", "attributes": { "description": { @@ -22,6 +25,8 @@ "lock": false, "__experimentalToolbar": false }, - "usesContext": [ "isInSelectedTab" ], + "usesContext": [ + "isInSelectedTab" + ], "editorStyle": "file:./editor.css" -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/product-fields/variation-items/edit.tsx b/packages/js/product-editor/src/blocks/product-fields/variation-items/edit.tsx index df6fb636d48..7e8b0d4d138 100644 --- a/packages/js/product-editor/src/blocks/product-fields/variation-items/edit.tsx +++ b/packages/js/product-editor/src/blocks/product-fields/variation-items/edit.tsx @@ -34,7 +34,7 @@ export function Edit( { attributes, context: { isInSelectedTab }, }: ProductEditorBlockEditProps< VariationOptionsBlockAttributes > ) { - const noticeDimissed = useRef( false ); + const noticeDismissed = useRef( false ); const { invalidateResolution } = useDispatch( EXPERIMENTAL_PRODUCT_VARIATIONS_STORE_NAME ); @@ -107,7 +107,7 @@ export function Edit( { */ if ( totalCountWithoutPrice > 0 && - ! noticeDimissed.current && + ! noticeDismissed.current && productStatus !== 'publish' && // New status. newData?.status === 'publish' @@ -198,7 +198,7 @@ export function Edit( { ref={ variationTableRef as React.Ref< HTMLDivElement > } noticeText={ noticeText } onNoticeDismiss={ () => { - noticeDimissed.current = true; + noticeDismissed.current = true; updateUserPreferences( { variable_items_without_price_notice_dismissed: { ...( itemsWithoutPriceNoticeDismissed || {} ), diff --git a/packages/js/product-editor/src/blocks/product-fields/variation-options/block.json b/packages/js/product-editor/src/blocks/product-fields/variation-options/block.json index 4b2f7c70d33..e16d5ebb37f 100644 --- a/packages/js/product-editor/src/blocks/product-fields/variation-options/block.json +++ b/packages/js/product-editor/src/blocks/product-fields/variation-options/block.json @@ -1,11 +1,14 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-variations-options-field", "title": "Product variations options", "category": "woocommerce", "description": "The product variations options.", - "keywords": [ "products", "variations" ], + "keywords": [ + "products", + "variations" + ], "textdomain": "default", "attributes": { "description": { @@ -22,6 +25,9 @@ "lock": false, "__experimentalToolbar": false }, - "usesContext": [ "postType", "isInSelectedTab" ], + "usesContext": [ + "postType", + "isInSelectedTab" + ], "editorStyle": "file:./editor.css" -} +} \ No newline at end of file diff --git a/packages/js/product-editor/src/components/custom-fields/custom-field-name-control/custom-field-name-control.tsx b/packages/js/product-editor/src/components/custom-fields/custom-field-name-control/custom-field-name-control.tsx index 44eb7fd03c2..94818f40ae3 100644 --- a/packages/js/product-editor/src/components/custom-fields/custom-field-name-control/custom-field-name-control.tsx +++ b/packages/js/product-editor/src/components/custom-fields/custom-field-name-control/custom-field-name-control.tsx @@ -26,7 +26,7 @@ import type { CustomFieldNameControlProps } from './types'; * the arbitrary value into an option so it can be selected as * a valid value * - * @param search The seraching criteria. + * @param search The search criteria. * @return The list of filtered custom field names as a Promise. */ async function searchCustomFieldNames( search?: string ) { diff --git a/packages/js/product-editor/src/components/variations-table/styles.scss b/packages/js/product-editor/src/components/variations-table/styles.scss index 9e78670f16a..a58f35e4c3f 100644 --- a/packages/js/product-editor/src/components/variations-table/styles.scss +++ b/packages/js/product-editor/src/components/variations-table/styles.scss @@ -162,7 +162,7 @@ gap: $gap-smaller; margin-right: $gap-smallest; - .variations-actions-menu__toogle:disabled { + .variations-actions-menu__toggle:disabled { cursor: not-allowed; } diff --git a/packages/js/product-editor/src/components/variations-table/variation-actions-menus/multiple-update-menu.tsx b/packages/js/product-editor/src/components/variations-table/variation-actions-menus/multiple-update-menu.tsx index 8c5f6e74fa7..12c3eb0b0a4 100644 --- a/packages/js/product-editor/src/components/variations-table/variation-actions-menus/multiple-update-menu.tsx +++ b/packages/js/product-editor/src/components/variations-table/variation-actions-menus/multiple-update-menu.tsx @@ -35,7 +35,7 @@ export function MultipleUpdateMenu( { icon={ isOpen ? chevronUp : chevronDown } variant="secondary" onClick={ onToggle } - className="variations-actions-menu__toogle" + className="variations-actions-menu__toggle" > { __( 'Quick update', 'woocommerce' ) } diff --git a/packages/js/product-editor/src/components/variations-table/variation-actions-menus/styles.scss b/packages/js/product-editor/src/components/variations-table/variation-actions-menus/styles.scss index 99143614edc..7956e681575 100644 --- a/packages/js/product-editor/src/components/variations-table/variation-actions-menus/styles.scss +++ b/packages/js/product-editor/src/components/variations-table/variation-actions-menus/styles.scss @@ -1,5 +1,5 @@ .variations-actions-menu { - &__toogle { + &__toggle { flex-direction: row-reverse; > span { diff --git a/packages/js/product-editor/src/index.ts b/packages/js/product-editor/src/index.ts index be6145a3587..6b4862e1408 100644 --- a/packages/js/product-editor/src/index.ts +++ b/packages/js/product-editor/src/index.ts @@ -35,6 +35,11 @@ export * from './contexts/validation-context/types'; export { EditorLoadingContext as __experimentalEditorLoadingContext } from './contexts/editor-loading-context'; export { PostTypeContext } from './contexts/post-type-context'; +/** + * Product data views page. + */ +export * from './products'; + // Init the store registerProductEditorUiStore(); diff --git a/packages/js/product-editor/src/lock-unlock.ts b/packages/js/product-editor/src/lock-unlock.ts new file mode 100644 index 00000000000..8c585c29833 --- /dev/null +++ b/packages/js/product-editor/src/lock-unlock.ts @@ -0,0 +1,10 @@ +/** + * External dependencies + */ +import { __dangerousOptInToUnstableAPIsOnlyForCoreModules } from '@wordpress/private-apis'; + +export const { lock, unlock } = + __dangerousOptInToUnstableAPIsOnlyForCoreModules( + 'I acknowledge private features are not for use in themes or plugins and doing so will break in the next version of WordPress.', + '@wordpress/edit-site' + ); diff --git a/packages/js/product-editor/src/products-app/constants.ts b/packages/js/product-editor/src/products-app/constants.ts new file mode 100644 index 00000000000..8c2b1b338f9 --- /dev/null +++ b/packages/js/product-editor/src/products-app/constants.ts @@ -0,0 +1,8 @@ +export const LAYOUT_GRID = 'grid'; +export const LAYOUT_TABLE = 'table'; +export const LAYOUT_LIST = 'list'; + +export const OPERATOR_IS = 'is'; +export const OPERATOR_IS_NOT = 'isNot'; +export const OPERATOR_IS_ANY = 'isAny'; +export const OPERATOR_IS_NONE = 'isNone'; diff --git a/packages/js/product-editor/src/products-app/index.tsx b/packages/js/product-editor/src/products-app/index.tsx new file mode 100644 index 00000000000..44c15e41dfa --- /dev/null +++ b/packages/js/product-editor/src/products-app/index.tsx @@ -0,0 +1,37 @@ +/** + * External dependencies + */ +import { createElement } from '@wordpress/element'; +import { privateApis as routerPrivateApis } from '@wordpress/router'; +import { + UnsavedChangesWarning, + // @ts-expect-error No types for this exist yet. + privateApis as editorPrivateApis, +} from '@wordpress/editor'; + +/** + * Internal dependencies + */ +import { unlock } from '../lock-unlock'; +import useLayoutAreas from './router'; +import { Layout } from './layout'; + +const { RouterProvider } = unlock( routerPrivateApis ); +const { GlobalStylesProvider } = unlock( editorPrivateApis ); + +function ProductsLayout() { + // This ensures the edited entity id and type are initialized properly. + const route = useLayoutAreas(); + return ; +} + +export function ProductsApp() { + return ( + + + + + + + ); +} diff --git a/packages/js/product-editor/src/products-app/layout.tsx b/packages/js/product-editor/src/products-app/layout.tsx new file mode 100644 index 00000000000..fe084ebf646 --- /dev/null +++ b/packages/js/product-editor/src/products-app/layout.tsx @@ -0,0 +1,118 @@ +/** + * External dependencies + */ +import { createElement, Fragment, useRef } from '@wordpress/element'; +import { + useViewportMatch, + useResizeObserver, + useReducedMotion, +} from '@wordpress/compose'; +import { __ } from '@wordpress/i18n'; +import { + // @ts-expect-error missing type. + EditorSnackbars, + // @ts-expect-error missing type. + privateApis as editorPrivateApis, +} from '@wordpress/editor'; +// eslint-disable-next-line @woocommerce/dependency-group +import { + // @ts-expect-error missing type. + __unstableMotion as motion, + // @ts-expect-error missing type. + __unstableAnimatePresence as AnimatePresence, +} from '@wordpress/components'; + +/** + * Internal dependencies + */ +import SidebarContent from './sidebar'; +import SiteHub from './site-hub'; +import { Route } from './router'; +import { unlock } from '../lock-unlock'; + +const { NavigableRegion } = unlock( editorPrivateApis ); + +const ANIMATION_DURATION = 0.3; + +type LayoutProps = { + route: Route; +}; + +export function Layout( { route }: LayoutProps ) { + const [ fullResizer ] = useResizeObserver(); + const toggleRef = useRef< HTMLAnchorElement >( null ); + const isMobileViewport = useViewportMatch( 'medium', '<' ); + const disableMotion = useReducedMotion(); + + const { key: routeKey, areas, widths } = route; + + return ( + <> + { fullResizer } +
+
+ { /* + The NavigableRegion must always be rendered and not use + `inert` otherwise `useNavigateRegions` will fail. + */ } + { ( ! isMobileViewport || ! areas.mobile ) && ( + + + + + + { areas.sidebar } + + + + + ) } + + + + { ! isMobileViewport && areas.content && ( +
+ { areas.content } +
+ ) } + + { ! isMobileViewport && areas.edit && ( +
+ { areas.edit } +
+ ) } +
+
+ + ); +} diff --git a/packages/js/product-editor/src/products-app/product-list/index.tsx b/packages/js/product-editor/src/products-app/product-list/index.tsx new file mode 100644 index 00000000000..6a0988b2448 --- /dev/null +++ b/packages/js/product-editor/src/products-app/product-list/index.tsx @@ -0,0 +1,343 @@ +/** + * External dependencies + */ +import { Action, DataViews, View } from '@wordpress/dataviews'; +import { + createElement, + useState, + useMemo, + useCallback, + useEffect, +} from '@wordpress/element'; +import { Product, ProductQuery } from '@woocommerce/data'; +import { drawerRight } from '@wordpress/icons'; +import { privateApis as routerPrivateApis } from '@wordpress/router'; +import { __ } from '@wordpress/i18n'; +import { useSelect } from '@wordpress/data'; +import classNames from 'classnames'; +import { + // @ts-expect-error missing types. + __experimentalHeading as Heading, + // @ts-expect-error missing types. + __experimentalText as Text, + // @ts-expect-error missing types. + __experimentalHStack as HStack, + // @ts-expect-error missing types. + __experimentalVStack as VStack, + FlexItem, + Button, +} from '@wordpress/components'; +// @ts-expect-error missing type. +// eslint-disable-next-line @woocommerce/dependency-group +import { privateApis as editorPrivateApis } from '@wordpress/editor'; + +/** + * Internal dependencies + */ +import { unlock } from '../../lock-unlock'; +import { + useDefaultViews, + defaultLayouts, +} from '../sidebar-dataviews/default-views'; +import { LAYOUT_LIST, OPERATOR_IS } from '../constants'; + +const { NavigableRegion } = unlock( editorPrivateApis ); +const { useHistory, useLocation } = unlock( routerPrivateApis ); + +const STATUSES = [ + { value: 'draft', label: __( 'Draft', 'woocommerce' ) }, + { value: 'future', label: __( 'Scheduled', 'woocommerce' ) }, + { value: 'private', label: __( 'Private', 'woocommerce' ) }, + { value: 'publish', label: __( 'Published', 'woocommerce' ) }, + { value: 'trash', label: __( 'Trash', 'woocommerce' ) }, +]; + +/** + * TODO: auto convert some of the product editor blocks ( from the blocks directory ) to this format. + * The edit function should work relatively well with the edit from the blocks, the only difference is that the blocks rely on getEntityProp to get the value + */ +const fields = [ + { + id: 'name', + label: __( 'Name', 'woocommerce' ), + enableHiding: false, + type: 'text', + render: function nameRender( { item }: { item: Product } ) { + return item.name; + }, + }, + { + id: 'sku', + label: __( 'SKU', 'woocommerce' ), + enableHiding: false, + enableSorting: false, + render: ( { item }: { item: Product } ) => { + return item.sku; + }, + }, + { + id: 'date', + label: __( 'Date', 'woocommerce' ), + render: ( { item }: { item: Product } ) => { + return ; + }, + }, + { + label: __( 'Status', 'woocommerce' ), + id: 'status', + getValue: ( { item }: { item: Product } ) => + STATUSES.find( ( { value } ) => value === item.status )?.label ?? + item.status, + elements: STATUSES, + filterBy: { + operators: [ OPERATOR_IS ], + }, + enableSorting: false, + }, +]; + +export type ProductListProps = { + subTitle?: string; + className?: string; + hideTitleFromUI?: boolean; + postType?: string; +}; + +const PAGE_SIZE = 25; +const EMPTY_ARRAY: Product[] = []; +const EMPTY_ACTIONS_ARRAY: Action< Product >[] = []; + +const getDefaultView = ( + defaultViews: Array< { slug: string; view: View } >, + activeView: string +) => { + return defaultViews.find( ( { slug } ) => slug === activeView )?.view; +}; + +/** + * This function abstracts working with default & custom views by + * providing a [ state, setState ] tuple based on the URL parameters. + * + * Consumers use the provided tuple to work with state + * and don't have to deal with the specifics of default & custom views. + * + * @param {string} postType Post type to retrieve default views for. + * @return {Array} The [ state, setState ] tuple. + */ +function useView( + postType: string +): [ View, ( view: View ) => void, ( view: View ) => void ] { + const { + params: { activeView = 'all', isCustom = 'false', layout }, + } = useLocation(); + const history = useHistory(); + + const defaultViews = useDefaultViews( { postType } ); + const [ view, setView ] = useState< View >( () => { + const initialView = getDefaultView( defaultViews, activeView ) ?? { + type: layout ?? LAYOUT_LIST, + }; + + const type = layout ?? initialView.type; + return { + ...initialView, + type, + }; + } ); + + const setViewWithUrlUpdate = useCallback( + ( newView: View ) => { + const { params } = history.getLocationWithParams(); + + if ( newView.type === LAYOUT_LIST && ! params?.layout ) { + // Skip updating the layout URL param if + // it is not present and the newView.type is LAYOUT_LIST. + } else if ( newView.type !== params?.layout ) { + history.push( { + ...params, + layout: newView.type, + } ); + } + + setView( newView ); + }, + [ history, isCustom ] + ); + + // When layout URL param changes, update the view type + // without affecting any other config. + useEffect( () => { + setView( ( prevView ) => ( { + ...prevView, + type: layout ?? LAYOUT_LIST, + } ) ); + }, [ layout ] ); + + // When activeView or isCustom URL parameters change, reset the view. + useEffect( () => { + const newView = getDefaultView( defaultViews, activeView ); + + if ( newView ) { + const type = layout ?? newView.type; + setView( { + ...newView, + type, + } ); + } + }, [ activeView, isCustom, layout, defaultViews ] ); + + return [ view, setViewWithUrlUpdate, setViewWithUrlUpdate ]; +} + +function getItemId( item: Product ) { + return item.id.toString(); +} + +export default function ProductList( { + subTitle, + className, + hideTitleFromUI = false, +}: ProductListProps ) { + const history = useHistory(); + const location = useLocation(); + const { + postId, + quickEdit = false, + postType = 'product', + isCustom, + activeView = 'all', + } = location.params; + const [ selection, setSelection ] = useState( [ postId ] ); + const [ view, setView ] = useView( postType ); + + const queryParams = useMemo( () => { + const filters: Partial< ProductQuery > = {}; + view.filters?.forEach( ( filter ) => { + if ( filter.field === 'status' ) { + filters.status = Array.isArray( filter.value ) + ? filter.value.join( ',' ) + : filter.value; + } + } ); + const orderby = + view.sort?.field === 'name' ? 'title' : view.sort?.field; + + return { + per_page: view.perPage, + page: view.page, + order: view.sort?.direction, + orderby, + search: view.search, + ...filters, + }; + }, [ location.params, view ] ); + + const onChangeSelection = useCallback( + ( items ) => { + setSelection( items ); + history.push( { + ...location.params, + postId: items.join( ',' ), + } ); + }, + [ history, location.params, view?.type ] + ); + + // TODO: Use the Woo data store to get all the products, as this doesn't contain all the product data. + const { records, totalCount, isLoading } = useSelect( + ( select ) => { + const { getProducts, getProductsTotalCount, isResolving } = + select( 'wc/admin/products' ); + return { + records: getProducts( queryParams ) as Product[], + totalCount: getProductsTotalCount( queryParams ) as number, + isLoading: isResolving( 'getProducts', [ queryParams ] ), + }; + }, + [ queryParams ] + ); + + const paginationInfo = useMemo( + () => ( { + totalItems: totalCount, + totalPages: Math.ceil( totalCount / ( view.perPage || PAGE_SIZE ) ), + } ), + [ totalCount, view.perPage ] + ); + + const classes = classNames( 'edit-site-page', className ); + + return ( + +
+ { ! hideTitleFromUI && ( + + + + { __( 'Products', 'woocommerce' ) } + + + { /* { actions } */ } + + + { subTitle && ( + + { subTitle } + + ) } + + ) } + { + history.push( { + ...location.params, + quickEdit: quickEdit ? undefined : true, + } ); + } } + /> + } + /> +
+
+ ); +} diff --git a/packages/js/product-editor/src/products-app/router.tsx b/packages/js/product-editor/src/products-app/router.tsx new file mode 100644 index 00000000000..3acbc510aba --- /dev/null +++ b/packages/js/product-editor/src/products-app/router.tsx @@ -0,0 +1,68 @@ +/** + * External dependencies + */ +import { createElement } from '@wordpress/element'; +import { privateApis as routerPrivateApis } from '@wordpress/router'; + +/** + * Internal dependencies + */ +import { unlock } from '../lock-unlock'; +import ProductList from './product-list'; +import DataViewsSidebarContent from './sidebar-dataviews'; +import SidebarNavigationScreen from './sidebar-navigation-screen'; + +const { useLocation } = unlock( routerPrivateApis ); + +export type Route = { + key: string; + areas: { + sidebar: React.JSX.Element | React.FunctionComponent; + content?: React.JSX.Element | React.FunctionComponent; + edit?: React.JSX.Element | React.FunctionComponent; + mobile?: React.JSX.Element | React.FunctionComponent | boolean; + preview?: boolean; + }; + widths?: { + content?: number; + edit?: number; + sidebar?: number; + }; +}; + +export default function useLayoutAreas() { + const { params = {} } = useLocation(); + const { postType = 'product', layout = 'table', canvas } = params; + // Products list. + if ( [ 'product' ].includes( postType ) ) { + const isListLayout = layout === 'list' || ! layout; + return { + key: 'products-list', + areas: { + sidebar: ( + } + /> + ), + content: , + preview: false, + mobile: , + }, + widths: { + content: isListLayout ? 380 : undefined, + }, + }; + } + + // Fallback shows the home page preview + return { + key: 'default', + areas: { + sidebar: () => null, + preview: false, + mobile: canvas === 'edit', + }, + }; +} diff --git a/packages/js/product-editor/src/products-app/sidebar-dataviews/dataview-item.tsx b/packages/js/product-editor/src/products-app/sidebar-dataviews/dataview-item.tsx new file mode 100644 index 00000000000..03a4f8cee85 --- /dev/null +++ b/packages/js/product-editor/src/products-app/sidebar-dataviews/dataview-item.tsx @@ -0,0 +1,110 @@ +/** + * External dependencies + */ +import { createElement } from '@wordpress/element'; +import classNames from 'classnames'; +import { privateApis as routerPrivateApis } from '@wordpress/router'; +import { addQueryArgs, getQueryArgs, removeQueryArgs } from '@wordpress/url'; +import { VIEW_LAYOUTS } from '@wordpress/dataviews'; +// @ts-expect-error missing type. +// eslint-disable-next-line @woocommerce/dependency-group +import { __experimentalHStack as HStack } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import SidebarNavigationItem from '../sidebar-navigation-item'; +import { unlock } from '../../lock-unlock'; + +const { useHistory, useLocation } = unlock( routerPrivateApis ); + +type DataViewItemProps = { + title: string; + slug: string; + customViewId?: string; + type: string; + icon: React.JSX.Element; + isActive: boolean; + isCustom: boolean; + suffix?: string; +}; + +function useLink( + params: Record< string, string | undefined >, + state?: Record< string, string | undefined >, + shouldReplace = false +) { + const history = useHistory(); + function onClick( event: Event ) { + event?.preventDefault(); + + if ( shouldReplace ) { + history.replace( params, state ); + } else { + history.push( params, state ); + } + } + + const currentArgs = getQueryArgs( window.location.href ); + const currentUrlWithoutArgs = removeQueryArgs( + window.location.href, + ...Object.keys( currentArgs ) + ); + + const newUrl = addQueryArgs( currentUrlWithoutArgs, params ); + + return { + href: newUrl, + onClick, + }; +} + +export default function DataViewItem( { + title, + slug, + customViewId, + type, + icon, + isActive, + isCustom, + suffix, +}: DataViewItemProps ) { + const { + params: { postType, page }, + } = useLocation(); + + const iconToUse = + icon || VIEW_LAYOUTS.find( ( v ) => v.type === type )?.icon; + + let activeView: undefined | string = isCustom ? customViewId : slug; + if ( activeView === 'all' ) { + activeView = undefined; + } + const linkInfo = useLink( { + page, + postType, + layout: type, + activeView, + isCustom: isCustom ? 'true' : undefined, + } ); + return ( + + + { title } + + { suffix } + + ); +} diff --git a/packages/js/product-editor/src/products-app/sidebar-dataviews/default-views.ts b/packages/js/product-editor/src/products-app/sidebar-dataviews/default-views.ts new file mode 100644 index 00000000000..816477363f3 --- /dev/null +++ b/packages/js/product-editor/src/products-app/sidebar-dataviews/default-views.ts @@ -0,0 +1,179 @@ +/** + * External dependencies + */ +import { __ } from '@wordpress/i18n'; +import { useSelect } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; +import { useMemo } from '@wordpress/element'; +import { + trash, + pages, + drafts, + published, + scheduled, + notAllowed, +} from '@wordpress/icons'; +import type { ColumnStyle, ViewTable } from '@wordpress/dataviews'; + +/** + * Internal dependencies + */ +import { + LAYOUT_LIST, + LAYOUT_TABLE, + LAYOUT_GRID, + OPERATOR_IS, +} from '../constants'; + +export const defaultLayouts: Record< + string, + { + layout: { + primaryField: string; + mediaField?: string; + styles?: Record< string, ColumnStyle >; + }; + } +> = { + [ LAYOUT_TABLE ]: { + layout: { + primaryField: 'name', + styles: { + name: { + maxWidth: 300, + }, + }, + }, + }, + [ LAYOUT_GRID ]: { + layout: { + mediaField: 'featured-image', + primaryField: 'name', + }, + }, + [ LAYOUT_LIST ]: { + layout: { + primaryField: 'name', + mediaField: 'featured-image', + }, + }, +}; + +const DEFAULT_POST_BASE: Omit< ViewTable, 'view' | 'title' | 'slug' | 'icon' > = + { + type: LAYOUT_TABLE, + search: '', + filters: [], + page: 1, + perPage: 20, + sort: { + field: 'date', + direction: 'desc', + }, + fields: [ 'name', 'sku', 'status', 'date' ], + layout: defaultLayouts[ LAYOUT_LIST ].layout, + }; + +export function useDefaultViews( { postType }: { postType: string } ): Array< { + title: string; + slug: string; + icon: React.JSX.Element; + view: ViewTable; +} > { + const labels = useSelect( + ( select ) => { + const { getPostType } = select( coreStore ); + const postTypeData: { labels?: Record< string, string > } = + getPostType( postType ); + return postTypeData?.labels; + }, + [ postType ] + ); + return useMemo( () => { + return [ + { + title: labels?.all_items || __( 'All items', 'woocommerce' ), + slug: 'all', + icon: pages, + view: { ...DEFAULT_POST_BASE }, + }, + { + title: __( 'Published', 'woocommerce' ), + slug: 'published', + icon: published, + view: { + ...DEFAULT_POST_BASE, + filters: [ + { + field: 'status', + operator: OPERATOR_IS, + value: 'publish', + }, + ], + }, + }, + { + title: __( 'Scheduled', 'woocommerce' ), + slug: 'future', + icon: scheduled, + view: { + ...DEFAULT_POST_BASE, + filters: [ + { + field: 'status', + operator: OPERATOR_IS, + value: 'future', + }, + ], + }, + }, + { + title: __( 'Drafts', 'woocommerce' ), + slug: 'drafts', + icon: drafts, + view: { + ...DEFAULT_POST_BASE, + filters: [ + { + field: 'status', + operator: OPERATOR_IS, + value: 'draft', + }, + ], + }, + }, + { + title: __( 'Private', 'woocommerce' ), + slug: 'private', + icon: notAllowed, + view: { + ...DEFAULT_POST_BASE, + filters: [ + { + field: 'status', + operator: OPERATOR_IS, + value: 'private', + }, + ], + }, + }, + { + title: __( 'Trash', 'woocommerce' ), + slug: 'trash', + icon: trash, + view: { + ...DEFAULT_POST_BASE, + type: LAYOUT_TABLE, + layout: defaultLayouts[ LAYOUT_TABLE ].layout, + filters: [ + { + field: 'status', + operator: OPERATOR_IS, + value: 'trash', + }, + ], + }, + }, + ]; + }, [ labels ] ); +} diff --git a/packages/js/product-editor/src/products-app/sidebar-dataviews/index.tsx b/packages/js/product-editor/src/products-app/sidebar-dataviews/index.tsx new file mode 100644 index 00000000000..245675bfcf3 --- /dev/null +++ b/packages/js/product-editor/src/products-app/sidebar-dataviews/index.tsx @@ -0,0 +1,55 @@ +/** + * External dependencies + */ +import { createElement, Fragment } from '@wordpress/element'; +import { privateApis as routerPrivateApis } from '@wordpress/router'; +// @ts-expect-error missing type. +// eslint-disable-next-line @wordpress/no-unsafe-wp-apis, @woocommerce/dependency-group +import { __experimentalItemGroup as ItemGroup } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { unlock } from '../../lock-unlock'; +import DataViewItem from './dataview-item'; +import { useDefaultViews } from './default-views'; + +const { useLocation } = unlock( routerPrivateApis ); + +export default function DataViewsSidebarContent() { + const { + params: { + postType = 'product', + activeView = 'all', + isCustom = 'false', + }, + } = useLocation(); + const defaultViews = useDefaultViews( { postType } ); + if ( ! postType ) { + return null; + } + const isCustomBoolean = isCustom === 'true'; + + return ( + <> + + { defaultViews.map( ( dataview ) => { + return ( + + ); + } ) } + + + ); +} diff --git a/packages/js/product-editor/src/products-app/sidebar-dataviews/style.scss b/packages/js/product-editor/src/products-app/sidebar-dataviews/style.scss new file mode 100644 index 00000000000..d3744634f6a --- /dev/null +++ b/packages/js/product-editor/src/products-app/sidebar-dataviews/style.scss @@ -0,0 +1,19 @@ +.edit-site-sidebar-navigation-screen__title-icon { + position: sticky; + top: 0; + background: $gray-900; + padding-top: $grid-unit-60; + margin-bottom: $grid-unit-10; + padding-bottom: $grid-unit-10; +} + +.edit-site-sidebar-button { + color: #e0e0e0; + flex-shrink: 0; +} + +.edit-site-sidebar-navigation-screen__title { + flex-grow: 1; + overflow-wrap: break-word; + padding: 2px 0 0 +} diff --git a/packages/js/product-editor/src/products-app/sidebar-navigation-item/index.tsx b/packages/js/product-editor/src/products-app/sidebar-navigation-item/index.tsx new file mode 100644 index 00000000000..57f8c23657c --- /dev/null +++ b/packages/js/product-editor/src/products-app/sidebar-navigation-item/index.tsx @@ -0,0 +1,88 @@ +/** + * External dependencies + */ +import { isRTL } from '@wordpress/i18n'; +import { chevronRightSmall, chevronLeftSmall, Icon } from '@wordpress/icons'; +import { privateApis as routerPrivateApis } from '@wordpress/router'; +import classNames from 'classnames'; +import { createElement } from '@wordpress/element'; +import { + // @ts-expect-error missing type. + __experimentalItem as Item, + // @ts-expect-error missing type. + __experimentalHStack as HStack, + FlexBlock, +} from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { unlock } from '../../lock-unlock'; + +const { useHistory } = unlock( routerPrivateApis ); + +type SidebarNavigationItemProps = { + className?: string; + icon?: React.JSX.Element; + suffix?: string; + withChevron?: boolean; + uid?: string; + params?: Record< string, string >; + onClick?: ( e: Event ) => void; + children: React.ReactNode; +}; + +export default function SidebarNavigationItem( { + className, + icon, + withChevron = false, + suffix, + uid, + params, + onClick, + children, + ...props +}: SidebarNavigationItemProps ) { + const history = useHistory(); + // If there is no custom click handler, create one that navigates to `params`. + function handleClick( e: Event ) { + if ( onClick ) { + onClick( e ); + } else if ( params ) { + e.preventDefault(); + history.push( params ); + } + } + + return ( + + + { icon && ( + + ) } + { children } + { withChevron && ( + + ) } + { ! withChevron && suffix } + + + ); +} diff --git a/packages/js/product-editor/src/products-app/sidebar-navigation-screen/index.tsx b/packages/js/product-editor/src/products-app/sidebar-navigation-screen/index.tsx new file mode 100644 index 00000000000..03a669c3ea2 --- /dev/null +++ b/packages/js/product-editor/src/products-app/sidebar-navigation-screen/index.tsx @@ -0,0 +1,136 @@ +/** + * External dependencies + */ + +import { isRTL, __ } from '@wordpress/i18n'; +import classNames from 'classnames'; +import { chevronRight, chevronLeft } from '@wordpress/icons'; +import { useSelect } from '@wordpress/data'; +import { privateApis as routerPrivateApis } from '@wordpress/router'; +import { createElement, Fragment } from '@wordpress/element'; +import { + // @ts-expect-error missing type. + __experimentalHStack as HStack, + // @ts-expect-error missing type. + __experimentalHeading as Heading, + // @ts-expect-error missing type. + __experimentalVStack as VStack, +} from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { unlock } from '../../lock-unlock'; +import SidebarButton from './sidebar-button'; + +const { useHistory, useLocation } = unlock( routerPrivateApis ); + +type SidebarNavigationScreenProps = { + isRoot?: boolean; + title: string; + actions?: React.JSX.Element; + meta?: string; + content: React.JSX.Element; + footer?: string; + description?: string; + backPath?: string; +}; + +export default function SidebarNavigationScreen( { + isRoot, + title, + actions, + meta, + content, + footer, + description, + backPath: backPathProp, +}: SidebarNavigationScreenProps ) { + const { dashboardLink, dashboardLinkText } = useSelect( ( select ) => { + const { getSettings } = unlock( select( 'core/edit-site' ) ); + return { + dashboardLink: getSettings().__experimentalDashboardLink, + dashboardLinkText: getSettings().__experimentalDashboardLinkText, + }; + }, [] ); + const location = useLocation(); + const history = useHistory(); + const backPath = backPathProp ?? location.state?.backPath; + const icon = isRTL() ? chevronRight : chevronLeft; + + return ( + <> + + + { ! isRoot && ( + { + history.push( backPath ); + } } + icon={ icon } + label={ __( 'Back', 'woocommerce' ) } + showTooltip={ false } + /> + ) } + { isRoot && ( + + ) } + + { title } + + { actions && ( +
+ { actions } +
+ ) } +
+ { meta && ( + <> +
+ { meta } +
+ + ) } + +
+ { description && ( +

+ { description } +

+ ) } + { content } +
+
+ { footer && ( +
+ { footer } +
+ ) } + + ); +} diff --git a/packages/js/product-editor/src/products-app/sidebar-navigation-screen/sidebar-button.tsx b/packages/js/product-editor/src/products-app/sidebar-navigation-screen/sidebar-button.tsx new file mode 100644 index 00000000000..6033e202bf6 --- /dev/null +++ b/packages/js/product-editor/src/products-app/sidebar-navigation-screen/sidebar-button.tsx @@ -0,0 +1,18 @@ +/** + * External dependencies + */ +import { createElement } from '@wordpress/element'; +import { Button } from '@wordpress/components'; +import classNames from 'classnames'; + +export default function SidebarButton( props: Button.Props ) { + return ( + +
+ + +
+ +
+
+ + + ); + } + ) +); + +export default SiteHub; diff --git a/packages/js/product-editor/src/products-app/site-hub/site-icon.tsx b/packages/js/product-editor/src/products-app/site-hub/site-icon.tsx new file mode 100644 index 00000000000..754dd6e39fc --- /dev/null +++ b/packages/js/product-editor/src/products-app/site-hub/site-icon.tsx @@ -0,0 +1,56 @@ +/** + * External dependencies + */ +import { createElement } from '@wordpress/element'; +import { useSelect } from '@wordpress/data'; +import { Icon } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { wordpress } from '@wordpress/icons'; +import { store as coreDataStore } from '@wordpress/core-data'; +import classNames from 'classnames'; + +type SiteIconProps = { + className: string; +}; + +function SiteIcon( { className }: SiteIconProps ) { + const { isRequestingSite, siteIconUrl } = useSelect( ( select ) => { + const { getEntityRecord } = select( coreDataStore ); + const siteData: { site_icon_url?: string } = getEntityRecord( + 'root', + '__unstableBase', + undefined + ); + + return { + isRequestingSite: ! siteData, + siteIconUrl: siteData?.site_icon_url, + }; + }, [] ); + + if ( isRequestingSite && ! siteIconUrl ) { + return
; + } + + const icon = siteIconUrl ? ( + { + ) : ( + + ); + + return ( +
+ { icon } +
+ ); +} + +export default SiteIcon; diff --git a/packages/js/product-editor/src/products.scss b/packages/js/product-editor/src/products.scss new file mode 100644 index 00000000000..7aaa6b290a4 --- /dev/null +++ b/packages/js/product-editor/src/products.scss @@ -0,0 +1,43 @@ +@include wordpress-admin-schemes(); + +.woocommerce_page_woocommerce-products-dashboard #wpadminbar, +.woocommerce_page_woocommerce-products-dashboard #adminmenumain { + display: none; +} + +.woocommerce_page_woocommerce-products-dashboard #wpcontent { + margin-left: 0; +} + +body.woocommerce_page_woocommerce-products-dashboard #woocommerce-products-dashboard { + @include wp-admin-reset("#woocommerce-products-dashboard"); + @include reset; + display: block !important; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + min-height: 100vh; +} + +body.js.is-fullscreen-mode { + @include break-medium { + // Reset the html.wp-topbar padding. + // Because this uses negative margins, we have to compensate for the height. + margin-top: -$admin-bar-height; + height: calc(100% + #{$admin-bar-height}); + + #adminmenumain, + #wpadminbar { + display: none; + } + + #wpcontent, + #wpfooter { + margin-left: 0; + } + } +} + +@import "products-app/sidebar-dataviews/style.scss"; diff --git a/packages/js/product-editor/src/products.tsx b/packages/js/product-editor/src/products.tsx new file mode 100644 index 00000000000..016ba882661 --- /dev/null +++ b/packages/js/product-editor/src/products.tsx @@ -0,0 +1,53 @@ +/** + * External dependencies + */ +import { __ } from '@wordpress/i18n'; +import { + StrictMode, + Suspense, + createElement, + // @ts-expect-error createRoot is available. + createRoot, + lazy, +} from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { getGutenbergVersion } from './utils/get-gutenberg-version'; + +const ProductsApp = lazy( () => + import( './products-app' ).then( ( module ) => ( { + default: module.ProductsApp, + } ) ) +); + +/** + * Initializes the "Products Dashboard". + * + * @param {string} id DOM element id. + */ +export function initializeProductsDashboard( id: string ) { + const target = document.getElementById( id ); + const root = createRoot( target ); + const isGutenbergEnabled = getGutenbergVersion() > 0; + + root.render( + + { isGutenbergEnabled ? ( + + + + ) : ( +
+ { __( + 'Please enabled Gutenberg for this feature', + 'woocommerce' + ) } +
+ ) } +
+ ); + + return root; +} diff --git a/packages/js/product-editor/src/style.scss b/packages/js/product-editor/src/style.scss index ce54a3a1c3a..e8dde242c74 100644 --- a/packages/js/product-editor/src/style.scss +++ b/packages/js/product-editor/src/style.scss @@ -60,3 +60,5 @@ /* Hooks */ @import "hooks/use-draggable/styles.scss"; + +@import "products.scss"; diff --git a/packages/js/product-editor/typings/index.d.ts b/packages/js/product-editor/typings/index.d.ts index daf2d188e73..1fb7d394b5b 100644 --- a/packages/js/product-editor/typings/index.d.ts +++ b/packages/js/product-editor/typings/index.d.ts @@ -25,9 +25,43 @@ declare module '@wordpress/core-data' { name: string, id: number | string, options?: { enabled: boolean } - ): { record: T, editedRecord: T, isResolving: boolean, hasResolved: boolean }; + ): { + record: T; + editedRecord: T; + isResolving: boolean; + hasResolved: boolean; + }; + const store: string; } declare module '@wordpress/keyboard-shortcuts' { - function useShortcut(name: string, callback: (event: KeyboardEvent) => void): void; + function useShortcut( + name: string, + callback: ( event: KeyboardEvent ) => void + ): void; const store; } + +declare module '@wordpress/router' { + const privateApis; +} + +declare module '@wordpress/edit-site/build-module/components/sync-state-with-url/use-init-edited-entity-from-url' { + export default function useInitEditedEntityFromURL(): void; +} + +declare module '@wordpress/edit-site/build-module/components/sidebar-navigation-screen' { + const SidebarNavigationScreen: React.FunctionComponent< { + title: string; + isRoot: boolean; + content: JSX.Element; + } >; + export default SidebarNavigationScreen; +} + +declare module '@wordpress/edit-site/build-module/components/site-hub' { + const SiteHub: React.FunctionComponent< { + ref: React.Ref; + isTransparent: boolean; + } >; + export default SiteHub; +} diff --git a/packages/js/remote-logging/README.md b/packages/js/remote-logging/README.md index 46a3b9aa45f..c98a43a9ed8 100644 --- a/packages/js/remote-logging/README.md +++ b/packages/js/remote-logging/README.md @@ -2,6 +2,12 @@ A remote logging package for Automattic based projects. This package provides error tracking and logging capabilities, with support for rate limiting, stack trace formatting, and customizable error filtering. +## Installation + +```bash +npm install @woocommerce/remote-logging --save +``` + ## Description The WooCommerce Remote Logging package offers the following features: @@ -44,16 +50,42 @@ The WooCommerce Remote Logging package offers the following features: } ``` +## Remote Logging Conditions + +Remote logging is subject to the following conditions: + +1. **Remote Logging Enabled**: The package checks `window.wcSettings.isRemoteLoggingEnabled` to determine if the feature should be enabled. The value is set via PHP and passed to JS as a boolean. It requires tracks to be enabled and a few other conditions internally. Please see the [RemoteLogger.php](https://github.com/woocommerce/woocommerce/blob/trunk/plugins/woocommerce/src/Internal/Logging/RemoteLogger.php) for more details. + +2. **Non-Development Environment**: It also checks `process.env.NODE_ENV` to ensure logging only occurs in non-development environments. + +If either of these conditions are not met (Tracks is not enabled or the environment is development), no logs will be transmitted to the remote server. + +## API Reference + +- `init(config: RemoteLoggerConfig): void`: Initializes the remote logger with the given configuration. +- `log(severity: LogSeverity, message: string, extraData?: object): Promise`: Logs a message with the specified severity and optional extra data. +- `captureException(error: Error, extraData?: object): void`: Captures an error and sends it to the remote API. + +For more detailed information about types and interfaces, refer to the source code and inline documentation. + ## Customization -You can customize the behavior of the remote logger using WordPress filters: -- `woocommerce_remote_logging_should_send_error`: Control whether an error should be sent to the remote API. -- `woocommerce_remote_logging_error_data`: Modify the error data before sending it to the remote API. -- `woocommerce_remote_logging_log_endpoint`: Customize the endpoint URL for sending log messages. -- `woocommerce_remote_logging_js_error_endpoint`: Customize the endpoint URL for sending JavaScript errors. +You can customize the behavior of the remote logger using WordPress filters. Here are the available filters: -### Example +### `woocommerce_remote_logging_should_send_error` + +Control whether an error should be sent to the remote API. + +**Parameters:** + +- `shouldSend` (boolean): The default decision on whether to send the error. +- `error` (Error): The error object. +- `stackFrames` (Array): An array of stack frames from the error. + +**Return value:** (boolean) Whether the error should be sent. + +**Usage example:** ```js import { addFilter } from '@wordpress/hooks'; @@ -62,20 +94,105 @@ addFilter( 'woocommerce_remote_logging_should_send_error', 'my-plugin', (shouldSend, error, stackFrames) => { - const containsPluginFrame = stackFrames.some( - ( frame ) => - frame.url && frame.url.includes( '/my-plugin/' ); - ); - // Custom logic to determine if the error should be sent + const containsPluginFrame = stackFrames.some( + (frame) => frame.url && frame.url.includes( /YOUR_PLUGIN_ASSET_PATH/ ) + ); + // Only send errors that originate from our plugin return shouldSend && containsPluginFrame; } ); ``` -### API Reference +### `woocommerce_remote_logging_error_data` -- `init(config: RemoteLoggerConfig): void`: Initializes the remote logger with the given configuration. -- `log(severity: LogSeverity, message: string, extraData?: object): Promise`: Logs a message with the specified severity and optional extra data. -- `captureException(error: Error, extraData?: object): void`: Captures an error and sends it to the remote API. +Modify the error data before sending it to the remote API. -For more detailed information about types and interfaces, refer to the source code and inline documentation. +**Parameters:** + +- `errorData` (ErrorData): The error data object to be sent. + +**Return value:** (ErrorData) The modified error data object. + +**Usage example:** + +```js +import { addFilter } from '@wordpress/hooks'; + +addFilter( + 'woocommerce_remote_logging_error_data', + 'my-plugin', + (errorData) => { + // Custom logic to modify error data + errorData.tags = [ ...errorData.tags, 'my-plugin' ]; + return errorData; + } +); +``` + +### `woocommerce_remote_logging_log_endpoint` + +Modify the URL of the remote logging API endpoint. + +**Parameters:** + +- `endpoint` (string): The default endpoint URL. + +**Return value:** (string) The modified endpoint URL. + +**Usage example:** + +```js +import { addFilter } from '@wordpress/hooks'; + +addFilter( + 'woocommerce_remote_logging_log_endpoint', + 'my-plugin', + (endpoint) => 'https://my-custom-endpoint.com/log' +); +``` + +### `woocommerce_remote_logging_js_error_endpoint` + +Modify the URL of the remote logging API endpoint for JavaScript errors. + +**Parameters:** + +- `endpoint` (string): The default endpoint URL for JavaScript errors. + +**Return value:** (string) The modified endpoint URL for JavaScript errors. + +**Usage example:** + +```js +import { addFilter } from '@wordpress/hooks'; + +addFilter( + 'woocommerce_remote_logging_js_error_endpoint', + 'my-plugin', + (endpoint) => 'https://my-custom-endpoint.com/js-error-log' +); +``` + +### `woocommerce_remote_logging_request_uri_whitelist` + +Modifies the list of whitelisted query parameters that won't be masked in the logged request URI + +**Parameters:** + +- `whitelist` (string[]): The default whitelist. + +**Return value:** (string[]) The modified whitelist. + +**Usage example:** + +```js +import { addFilter } from '@wordpress/hooks'; + +addFilter( + 'woocommerce_remote_logging_request_uri_whitelist', + 'my-plugin', + ( whitelist ) => { + return [ ...whitelist, 'exampleParam' ] + } +); +``` diff --git a/packages/js/remote-logging/changelog/50828-dev-constrain-pnpm-version b/packages/js/remote-logging/changelog/50828-dev-constrain-pnpm-version new file mode 100644 index 00000000000..fdead95ceb5 --- /dev/null +++ b/packages/js/remote-logging/changelog/50828-dev-constrain-pnpm-version @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix pnpm version to 9.1.3 to avoid dependency installation issues. \ No newline at end of file diff --git a/packages/js/remote-logging/changelog/50993-fix-some-comment-typos b/packages/js/remote-logging/changelog/50993-fix-some-comment-typos new file mode 100644 index 00000000000..3dce9f8d32b --- /dev/null +++ b/packages/js/remote-logging/changelog/50993-fix-some-comment-typos @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Comment: Fix some comment typos. diff --git a/plugins/woocommerce/changelog/50086-fix-opt-in-fonts b/packages/js/remote-logging/changelog/add-query-params-sanitisation similarity index 52% rename from plugins/woocommerce/changelog/50086-fix-opt-in-fonts rename to packages/js/remote-logging/changelog/add-query-params-sanitisation index 3fb992150ab..59d27ec04e8 100644 --- a/plugins/woocommerce/changelog/50086-fix-opt-in-fonts +++ b/packages/js/remote-logging/changelog/add-query-params-sanitisation @@ -1,4 +1,4 @@ Significance: minor Type: update -CYS: Improve opt-in flow fonts. \ No newline at end of file +Add query params sanitisation diff --git a/packages/js/remote-logging/changelog/fix-remote-logging-asset-path b/packages/js/remote-logging/changelog/fix-remote-logging-asset-path new file mode 100644 index 00000000000..fc166a1888b --- /dev/null +++ b/packages/js/remote-logging/changelog/fix-remote-logging-asset-path @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix wc asset url check diff --git a/packages/js/remote-logging/changelog/update-remote-logging-readme b/packages/js/remote-logging/changelog/update-remote-logging-readme new file mode 100644 index 00000000000..db2068ed42a --- /dev/null +++ b/packages/js/remote-logging/changelog/update-remote-logging-readme @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Update README.md to document the filters specs & usage diff --git a/packages/js/remote-logging/package.json b/packages/js/remote-logging/package.json index 8bdc1cb7ace..c96e6ca3f18 100644 --- a/packages/js/remote-logging/package.json +++ b/packages/js/remote-logging/package.json @@ -6,7 +6,7 @@ "license": "GPL-2.0-or-later", "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "keywords": [ "wordpress", diff --git a/packages/js/remote-logging/src/remote-logger.ts b/packages/js/remote-logging/src/remote-logger.ts index 8808e9f9c59..c35024a7110 100644 --- a/packages/js/remote-logging/src/remote-logger.ts +++ b/packages/js/remote-logging/src/remote-logger.ts @@ -33,6 +33,44 @@ export const REMOTE_LOGGING_LOG_ENDPOINT_FILTER = export const REMOTE_LOGGING_JS_ERROR_ENDPOINT_FILTER = 'woocommerce_remote_logging_js_error_endpoint'; +export const REMOTE_LOGGING_REQUEST_URI_PARAMS_WHITELIST_FILTER = + 'woocommerce_remote_logging_request_uri_whitelist'; + +export const REMPOTE_LOGGING_REQUEST_URI_PARAMS_DEFAULT_WHITELIST = [ + 'path', + 'page', + 'step', + 'task', + 'tab', + 'section', + 'status', + 'post_type', + 'taxonomy', + 'action', +]; + +export const sanitiseRequestUriParams = ( search: string ) => { + const params = new URLSearchParams( search ); + + /** + * This filter modifies the list of whitelisted query parameters that won't be masked + * in the logged request URI + * + * @filter woocommerce_remote_logging_request_uri_whitelist + * @param {string[]} whitelist The default whitelist + */ + const whitelist = applyFilters( + REMOTE_LOGGING_REQUEST_URI_PARAMS_WHITELIST_FILTER, + REMPOTE_LOGGING_REQUEST_URI_PARAMS_DEFAULT_WHITELIST + ) as typeof REMPOTE_LOGGING_REQUEST_URI_PARAMS_DEFAULT_WHITELIST; + for ( const [ key ] of params ) { + if ( ! whitelist.includes( key ) ) { + params.set( key, 'xxxxxx' ); + } + } + return params.toString(); +}; + const REMOTE_LOGGING_LAST_ERROR_SENT_KEY = 'wc_remote_logging_last_error_sent_time'; @@ -106,7 +144,8 @@ export class RemoteLogger { properties: { ...extraData?.properties, request_uri: - window.location.pathname + window.location.search, + window.location.pathname + + sanitiseRequestUriParams( window.location.search ), }, } ), trace: this.getFormattedStackFrame( @@ -213,7 +252,8 @@ export class RemoteLogger { tags: [ 'js-unhandled-error' ], properties: { request_uri: - window.location.pathname + window.location.search, + window.location.pathname + + sanitiseRequestUriParams( window.location.search ), }, } ), trace: this.getFormattedStackFrame( trace ), @@ -292,7 +332,7 @@ export class RemoteLogger { .map( this.getFormattedFrame ) .join( '\n\n' ); - // Set hard limit of 8192 characters for the stack trace so it does not use too much user bandwith and also our computation. + // Set hard limit of 8192 characters for the stack trace so it does not use too much user bandwidth and also our computation. return trace.length > 8192 ? trace.substring( 0, 8192 ) : trace; } @@ -343,7 +383,7 @@ export class RemoteLogger { ) { const containsWooCommerceFrame = stackFrames.some( ( frame ) => - frame.url && frame.url.includes( '/woocommerce/assets/' ) + frame.url && frame.url.startsWith( getSetting( 'wcAssetUrl' ) ) ); /** diff --git a/packages/js/remote-logging/src/test/index.ts b/packages/js/remote-logging/src/test/index.ts index 155b32d2e70..617467f3561 100644 --- a/packages/js/remote-logging/src/test/index.ts +++ b/packages/js/remote-logging/src/test/index.ts @@ -13,6 +13,8 @@ import { REMOTE_LOGGING_ERROR_DATA_FILTER, REMOTE_LOGGING_LOG_ENDPOINT_FILTER, REMOTE_LOGGING_JS_ERROR_ENDPOINT_FILTER, + sanitiseRequestUriParams, + REMOTE_LOGGING_REQUEST_URI_PARAMS_WHITELIST_FILTER, } from '../remote-logger'; import { fetchMock } from './__mocks__/fetch'; @@ -32,6 +34,17 @@ jest.mock( 'tracekit', () => ( { } ), } ) ); +jest.mock( '@woocommerce/settings', () => { + return { + getSetting: jest.fn().mockImplementation( ( key ) => { + if ( key === 'wcAssetUrl' ) { + return 'http://example.com/woocommerce/assets'; + } + return null; + } ), + }; +} ); + describe( 'RemoteLogger', () => { const originalConsoleWarn = console.warn; let logger: RemoteLogger; @@ -222,7 +235,7 @@ describe( 'RemoteLogger', () => { const error = new Error( 'Test error' ); const stackFrames = [ { - url: 'http://example.com/wp-content/plugins/woocommerce/assets/js/admin/app.min.js', + url: 'http://example.com/woocommerce/assets/js/admin/app.min.js', func: 'testFunction', args: [], line: 1, @@ -246,6 +259,13 @@ describe( 'RemoteLogger', () => { line: 1, column: 1, }, + { + url: 'http://example.com/other/plugin/woocommerce/assets/js/app.min.js', + func: 'testFunction', + args: [], + line: 1, + column: 1, + }, ]; const result = ( logger as any ).shouldHandleError( error, @@ -362,3 +382,24 @@ describe( 'captureException', () => { expect( fetchMock ).not.toHaveBeenCalled(); } ); } ); + +describe( 'sanitiseRequestUriParams', () => { + afterEach(() => { + removeFilter(REMOTE_LOGGING_REQUEST_URI_PARAMS_WHITELIST_FILTER, 'test' ); + }) + it( 'should replace non-whitelisted params with xxxxxx', () => { + expect(sanitiseRequestUriParams('?path=home&user=admin&token=abc123')).toEqual('path=home&user=xxxxxx&token=xxxxxx') + }) + it( 'should not replace whitelisted params with xxxxxx', () => { + expect(sanitiseRequestUriParams('?path=home')).toEqual('path=home') + }) + it( 'should not do anything if empty string is passed in', () => { + expect(sanitiseRequestUriParams('')).toEqual('') + }) + it( 'should apply filters correctly', () => { + addFilter( REMOTE_LOGGING_REQUEST_URI_PARAMS_WHITELIST_FILTER, 'test', (defaultWhitelist) => { + return [ ... defaultWhitelist, 'foo' ]; + }) + expect(sanitiseRequestUriParams('?path=home&foo=bar&user=admin&token=abc123')).toEqual('path=home&foo=bar&user=xxxxxx&token=xxxxxx') + }) +}) \ No newline at end of file diff --git a/packages/js/tracks/changelog/50828-dev-constrain-pnpm-version b/packages/js/tracks/changelog/50828-dev-constrain-pnpm-version new file mode 100644 index 00000000000..fdead95ceb5 --- /dev/null +++ b/packages/js/tracks/changelog/50828-dev-constrain-pnpm-version @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix pnpm version to 9.1.3 to avoid dependency installation issues. \ No newline at end of file diff --git a/packages/js/tracks/changelog/50993-fix-some-comment-typos b/packages/js/tracks/changelog/50993-fix-some-comment-typos new file mode 100644 index 00000000000..3dce9f8d32b --- /dev/null +++ b/packages/js/tracks/changelog/50993-fix-some-comment-typos @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Comment: Fix some comment typos. diff --git a/packages/js/tracks/package.json b/packages/js/tracks/package.json index 82aaf9e44ee..50fee1aac21 100644 --- a/packages/js/tracks/package.json +++ b/packages/js/tracks/package.json @@ -6,7 +6,7 @@ "license": "GPL-3.0-or-later", "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "keywords": [ "wordpress", diff --git a/packages/js/tracks/src/stats.ts b/packages/js/tracks/src/stats.ts index 79a28497776..2040f1e5676 100644 --- a/packages/js/tracks/src/stats.ts +++ b/packages/js/tracks/src/stats.ts @@ -22,7 +22,7 @@ const GROUP_PREFIX = 'x_woocommerce-'; * @param {Record | string} group - The group of stats or a single stat name. * @param {string} [name] - The name of the stat if group is a string. * - * @return {URLSearchParams} The constructed querys. + * @return {URLSearchParams} The constructed query. */ function buildQueryParams( group: Record< string, string > | string, diff --git a/plugins/woo-ai/changelog/50047-fix-typos b/plugins/woo-ai/changelog/50047-fix-typos new file mode 100644 index 00000000000..e5d2f2b3db2 --- /dev/null +++ b/plugins/woo-ai/changelog/50047-fix-typos @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Comment: Fix comment typos across various files. diff --git a/plugins/woo-ai/changelog/50828-dev-constrain-pnpm-version b/plugins/woo-ai/changelog/50828-dev-constrain-pnpm-version new file mode 100644 index 00000000000..fdead95ceb5 --- /dev/null +++ b/plugins/woo-ai/changelog/50828-dev-constrain-pnpm-version @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix pnpm version to 9.1.3 to avoid dependency installation issues. \ No newline at end of file diff --git a/plugins/woo-ai/package.json b/plugins/woo-ai/package.json index c511404c47b..3083d8d3cfe 100644 --- a/plugins/woo-ai/package.json +++ b/plugins/woo-ai/package.json @@ -94,7 +94,7 @@ }, "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "lint-staged": { "*.php": [ diff --git a/plugins/woo-ai/src/utils/categorySelector.ts b/plugins/woo-ai/src/utils/categorySelector.ts index 7322497bb1c..8f4f1ecea85 100644 --- a/plugins/woo-ai/src/utils/categorySelector.ts +++ b/plugins/woo-ai/src/utils/categorySelector.ts @@ -1,5 +1,5 @@ /** - * Helper function to select a checkbox if it exists within a element + * Helper function to select a checkbox if it exists within an element * * @param element - The DOM element to check for a checkbox */ diff --git a/plugins/woocommerce-admin/changelog/50047-fix-typos b/plugins/woocommerce-admin/changelog/50047-fix-typos new file mode 100644 index 00000000000..e5d2f2b3db2 --- /dev/null +++ b/plugins/woocommerce-admin/changelog/50047-fix-typos @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Comment: Fix comment typos across various files. diff --git a/plugins/woocommerce-admin/client/activity-panel/activity-panel.js b/plugins/woocommerce-admin/client/activity-panel/activity-panel.js index 33b68c7f05b..65fb6d08588 100644 --- a/plugins/woocommerce-admin/client/activity-panel/activity-panel.js +++ b/plugins/woocommerce-admin/client/activity-panel/activity-panel.js @@ -12,7 +12,6 @@ import { ONBOARDING_STORE_NAME, OPTIONS_STORE_NAME, useUser, - useUserPreferences, getVisibleTasks, } from '@woocommerce/data'; import { addHistoryListener } from '@woocommerce/navigation'; @@ -32,7 +31,6 @@ import { hasUnreadNotes as checkIfHasUnreadNotes } from './unread-indicators'; import { Tabs } from './tabs'; import { SetupProgress } from './setup-progress'; import { DisplayOptions } from './display-options'; -import { HighlightTooltip } from './highlight-tooltip'; import { Panel } from './panel'; import { getLowStockCount as getLowStockProducts, @@ -73,7 +71,6 @@ export const ActivityPanel = ( { isEmbedded, query } ) => { const [ isPanelSwitching, setIsPanelSwitching ] = useState( false ); const { fills } = useSlot( ABBREVIATED_NOTIFICATION_SLOT_NAME ); const hasExtendedNotifications = Boolean( fills?.length ); - const { updateUserPreferences, ...userData } = useUserPreferences(); const activeSetupList = useActiveSetupTasklist(); const { comingSoon } = useLaunchYourStore( { enabled: isHomescreen, @@ -168,7 +165,6 @@ export const ActivityPanel = ( { isEmbedded, query } ) => { const { hasUnreadNotes, hasAbbreviatedNotifications, - isCompletedTask, thingsToDoNextCount, requestingTaskListOptions, setupTaskListComplete, @@ -419,34 +415,8 @@ export const ActivityPanel = ( { isEmbedded, query } ) => { } }; - const closedHelpPanelHighlight = () => { - recordEvent( 'help_tooltip_click' ); - if ( userData && updateUserPreferences ) { - updateUserPreferences( { - help_panel_highlight_shown: 'yes', - } ); - } - }; - - const shouldShowHelpTooltip = () => { - const { task } = query; - const startedTasks = - userData && userData.task_list_tracked_started_tasks; - const highlightShown = userData && userData.help_panel_highlight_shown; - if ( - task && - highlightShown !== 'yes' && - ( startedTasks || {} )[ task ] > 1 && - ! isCompletedTask - ) { - return true; - } - return false; - }; - const tabs = getTabs(); const headerId = uniqueId( 'activity-panel-header_' ); - const showHelpHighlightTooltip = shouldShowHelpTooltip(); return ( @@ -483,21 +453,6 @@ export const ActivityPanel = ( { isEmbedded, query } ) => { clearPanel={ () => clearPanel() } /> - { showHelpHighlightTooltip ? ( - closedHelpPanelHighlight() } - onShow={ () => recordEvent( 'help_tooltip_view' ) } - /> - ) : null }
); diff --git a/plugins/woocommerce-admin/client/activity-panel/test/index.js b/plugins/woocommerce-admin/client/activity-panel/test/index.js index d364494557f..06db14fb6ab 100644 --- a/plugins/woocommerce-admin/client/activity-panel/test/index.js +++ b/plugins/woocommerce-admin/client/activity-panel/test/index.js @@ -9,7 +9,7 @@ import { createEvent, } from '@testing-library/react'; import { useSelect } from '@wordpress/data'; -import { useUser, useUserPreferences } from '@woocommerce/data'; +import { useUser } from '@woocommerce/data'; import { useState } from '@wordpress/element'; /** @@ -247,82 +247,6 @@ describe( 'Activity Panel', () => { expect( queryByText( 'Finish setup' ) ).toBeDefined(); } ); - describe( 'help panel tooltip', () => { - it( 'should render highlight tooltip when task count is at-least 2, task is not completed, and tooltip not shown yet', () => { - useUserPreferences.mockReturnValue( { - updateUserPreferences: () => {}, - task_list_tracked_started_tasks: { payment: 2 }, - } ); - const { getByText } = render( - - ); - - expect( getByText( '[HighlightTooltip]' ) ).toBeInTheDocument(); - } ); - - it( 'should not render highlight tooltip when task is not visited more then once', () => { - useSelect.mockImplementation( () => ( { - requestingTaskListOptions: false, - setupTaskListComplete: false, - setupTaskListHidden: false, - trackedCompletedTasks: [], - } ) ); - useUserPreferences.mockReturnValue( { - updateUserPreferences: () => {}, - task_list_tracked_started_tasks: { payment: 1 }, - } ); - render( - - ); - - expect( screen.queryByText( '[HighlightTooltip]' ) ).toBeNull(); - - useUserPreferences.mockReturnValue( { - updateUserPreferences: () => {}, - task_list_tracked_started_tasks: {}, - } ); - - render( - - ); - - expect( screen.queryByText( '[HighlightTooltip]' ) ).toBeNull(); - } ); - - it( 'should not render highlight tooltip when task is visited twice, but completed already', () => { - useSelect.mockImplementation( () => ( { - requestingTaskListOptions: false, - setupTaskListComplete: false, - setupTaskListHidden: false, - isCompletedTask: true, - } ) ); - - useUserPreferences.mockReturnValue( { - updateUserPreferences: () => {}, - task_list_tracked_started_tasks: { payment: 2 }, - } ); - - const { queryByText } = render( - - ); - - expect( queryByText( '[HighlightTooltip]' ) ).toBeNull(); - } ); - - it( 'should not render highlight tooltip when task is visited twice, not completed, but already shown', () => { - useUserPreferences.mockReturnValue( { - task_list_tracked_started_tasks: { payment: 2 }, - help_panel_highlight_shown: 'yes', - } ); - - const { queryByText } = render( - - ); - - expect( queryByText( '[HighlightTooltip]' ) ).toBeNull(); - } ); - } ); - describe( 'panel', () => { it( 'should set focus when panel opened/closed without removing element when onTransitionEnd is triggered', () => { const content = 'test'; diff --git a/plugins/woocommerce-admin/client/analytics/components/index.js b/plugins/woocommerce-admin/client/analytics/components/index.js index cc02f168d36..4ee3476b0b2 100644 --- a/plugins/woocommerce-admin/client/analytics/components/index.js +++ b/plugins/woocommerce-admin/client/analytics/components/index.js @@ -1,4 +1,3 @@ export { default as ReportChart } from './report-chart'; -export { default as ReportError } from './report-error'; export { default as ReportSummary } from './report-summary'; export { default as ReportTable } from './report-table'; diff --git a/plugins/woocommerce-admin/client/analytics/components/leaderboard/index.js b/plugins/woocommerce-admin/client/analytics/components/leaderboard/index.js index 98e9bdbd9b3..2e8d406247f 100644 --- a/plugins/woocommerce-admin/client/analytics/components/leaderboard/index.js +++ b/plugins/woocommerce-admin/client/analytics/components/leaderboard/index.js @@ -5,7 +5,7 @@ import { __ } from '@wordpress/i18n'; import { Card, CardBody, CardHeader } from '@wordpress/components'; import { Component } from '@wordpress/element'; import { compose } from '@wordpress/compose'; -import { EmptyTable, TableCard } from '@woocommerce/components'; +import { EmptyTable, AnalyticsError, TableCard } from '@woocommerce/components'; import { withSelect } from '@wordpress/data'; import PropTypes from 'prop-types'; import { getPersistedQuery } from '@woocommerce/navigation'; @@ -21,7 +21,6 @@ import { Text } from '@woocommerce/experimental'; /** * Internal dependencies */ -import ReportError from '../report-error'; import sanitizeHTML from '../../../lib/sanitize-html'; import './style.scss'; @@ -88,7 +87,7 @@ export class Leaderboard extends Component { const classes = 'woocommerce-leaderboard'; if ( isError ) { - return ; + return ; } const rows = this.getFormattedRows(); diff --git a/plugins/woocommerce-admin/client/analytics/components/report-chart/index.js b/plugins/woocommerce-admin/client/analytics/components/report-chart/index.js index 31f714c3d91..a19830c0b44 100644 --- a/plugins/woocommerce-admin/client/analytics/components/report-chart/index.js +++ b/plugins/woocommerce-admin/client/analytics/components/report-chart/index.js @@ -8,7 +8,7 @@ import { format as formatDate } from '@wordpress/date'; import { withSelect } from '@wordpress/data'; import { get, isEqual } from 'lodash'; import PropTypes from 'prop-types'; -import { Chart } from '@woocommerce/components'; +import { Chart, AnalyticsError } from '@woocommerce/components'; import { getReportChartData, getTooltipValueFormat, @@ -27,7 +27,6 @@ import { CurrencyContext } from '@woocommerce/currency'; /** * Internal dependencies */ -import ReportError from '../report-error'; import { getChartMode, getSelectedFilter, @@ -197,7 +196,7 @@ export class ReportChart extends Component { const { isRequesting, primaryData } = this.props; if ( primaryData.isError ) { - return ; + return ; } const isChartRequesting = isRequesting || primaryData.isRequesting; @@ -214,7 +213,7 @@ export class ReportChart extends Component { const { isRequesting, primaryData, secondaryData } = this.props; if ( ! primaryData || primaryData.isError || secondaryData.isError ) { - return ; + return ; } const isChartRequesting = diff --git a/plugins/woocommerce-admin/client/analytics/components/report-summary/index.js b/plugins/woocommerce-admin/client/analytics/components/report-summary/index.js index 57eefcd17f2..ac1b509619b 100644 --- a/plugins/woocommerce-admin/client/analytics/components/report-summary/index.js +++ b/plugins/woocommerce-admin/client/analytics/components/report-summary/index.js @@ -8,6 +8,7 @@ import { withSelect } from '@wordpress/data'; import PropTypes from 'prop-types'; import { getNewPath } from '@woocommerce/navigation'; import { + AnalyticsError, SummaryList, SummaryListPlaceholder, SummaryNumber, @@ -21,7 +22,6 @@ import { CurrencyContext } from '@woocommerce/currency'; /** * Internal dependencies */ -import ReportError from '../report-error'; /** * Component to render summary numbers in reports. @@ -64,7 +64,7 @@ export class ReportSummary extends Component { const { isError, isRequesting } = summaryData; if ( isError ) { - return ; + return ; } if ( isRequesting ) { @@ -138,7 +138,7 @@ ReportSummary.propTypes = { * The endpoint to use in API calls to populate the Summary Numbers. * For example, if `taxes` is provided, data will be fetched from the report * `taxes` endpoint (ie: `/wc-analytics/reports/taxes/stats`). If the provided endpoint - * doesn't exist, an error will be shown to the user with `ReportError`. + * doesn't exist, an error will be shown to the user with `AnalyticsError`. */ endpoint: PropTypes.string.isRequired, /** diff --git a/plugins/woocommerce-admin/client/analytics/components/report-summary/test/index.js b/plugins/woocommerce-admin/client/analytics/components/report-summary/test/index.js index fa51e9abde8..697071d8b69 100644 --- a/plugins/woocommerce-admin/client/analytics/components/report-summary/test/index.js +++ b/plugins/woocommerce-admin/client/analytics/components/report-summary/test/index.js @@ -124,7 +124,7 @@ describe( 'ReportSummary', () => { expect( delta ).toBeInTheDocument(); } ); - test( 'should display ReportError when isError is true', () => { + test( 'should display AnalyticsError when isError is true', () => { renderChart( 'number', null, null, true ); expect( @@ -134,7 +134,7 @@ describe( 'ReportSummary', () => { ).toBeInTheDocument(); } ); - test( 'should display SummaryListPlaceholder when isRequesting is true', () => { + test( 'should display SummaryListPlaceholder when summaryData.isRequesting is true', () => { const { container } = renderChart( 'number', null, null, false, true ); expect( diff --git a/plugins/woocommerce-admin/client/analytics/components/report-table/index.js b/plugins/woocommerce-admin/client/analytics/components/report-table/index.js index f43cf9f7996..b486e059fdc 100644 --- a/plugins/woocommerce-admin/client/analytics/components/report-table/index.js +++ b/plugins/woocommerce-admin/client/analytics/components/report-table/index.js @@ -11,7 +11,12 @@ import { get, partial, uniq } from 'lodash'; import { __, sprintf } from '@wordpress/i18n'; import PropTypes from 'prop-types'; import { STORE_KEY as CES_STORE_KEY } from '@woocommerce/customer-effort-score'; -import { CompareButton, Search, TableCard } from '@woocommerce/components'; +import { + CompareButton, + AnalyticsError, + Search, + TableCard, +} from '@woocommerce/components'; import { getIdsFromQuery, getSearchWords, @@ -38,7 +43,6 @@ import { recordEvent } from '@woocommerce/tracks'; * Internal dependencies */ import DownloadIcon from './download-icon'; -import ReportError from '../report-error'; import { extendTableData } from './utils'; import './style.scss'; @@ -89,7 +93,7 @@ const ReportTable = ( props ) => { const isError = tableData.isError || primaryData.isError; if ( isError ) { - return ; + return ; } let userPrefColumns = []; @@ -489,7 +493,7 @@ ReportTable.propTypes = { * For example, if `taxes` is provided, data will be fetched from the report * `taxes` endpoint (ie: `/wc-analytics/reports/taxes` and `/wc/v4/reports/taxes/stats`). * If the provided endpoint doesn't exist, an error will be shown to the user - * with `ReportError`. + * with `AnalyticsError`. */ endpoint: PropTypes.string, /** @@ -548,7 +552,7 @@ ReportTable.propTypes = { * Table data of that report. If it's not provided, it will be automatically * loaded via the provided `endpoint`. */ - tableData: PropTypes.object.isRequired, + tableData: PropTypes.object, /** * Properties to be added to the query sent to the report table endpoint. */ diff --git a/plugins/woocommerce-admin/client/analytics/report/categories/index.js b/plugins/woocommerce-admin/client/analytics/report/categories/index.js index 904f1e1bac1..02110c765da 100644 --- a/plugins/woocommerce-admin/client/analytics/report/categories/index.js +++ b/plugins/woocommerce-admin/client/analytics/report/categories/index.js @@ -69,7 +69,6 @@ class CategoriesReport extends Component { Order # ', 'woocommerce' @@ -210,7 +210,7 @@ export const advancedFilters = applyFilters( 'Select an IP address filter match', 'woocommerce' ), - /* translators: A sentence describing a order number filter. See screen shot for context: https://cloudup.com/ccxhyH2mEDg */ + /* translators: A sentence describing an order number filter. See screen shot for context: https://cloudup.com/ccxhyH2mEDg */ title: __( 'IP Address ', 'woocommerce' diff --git a/plugins/woocommerce-admin/client/analytics/report/index.js b/plugins/woocommerce-admin/client/analytics/report/index.js index 85118becdbf..3a7da1d481d 100644 --- a/plugins/woocommerce-admin/client/analytics/report/index.js +++ b/plugins/woocommerce-admin/client/analytics/report/index.js @@ -8,6 +8,7 @@ import PropTypes from 'prop-types'; import { find } from 'lodash'; import { getQuery, getSearchWords } from '@woocommerce/navigation'; import { searchItemsByString, ITEMS_STORE_NAME } from '@woocommerce/data'; +import { AnalyticsError } from '@woocommerce/components'; import { CurrencyContext, getFilteredCurrencyInstance, @@ -18,7 +19,6 @@ import { */ import './style.scss'; import { NoMatch } from '~/layout/NoMatch'; -import ReportError from '../components/report-error'; import getReports from './get-reports'; /** @@ -83,7 +83,7 @@ class Report extends Component { const { isError } = this.props; if ( isError ) { - return ; + return ; } const reportParam = getReportParam( this.props ); diff --git a/plugins/woocommerce-admin/client/analytics/report/products/index.js b/plugins/woocommerce-admin/client/analytics/report/products/index.js index b3d7d893998..bab9201f385 100644 --- a/plugins/woocommerce-admin/client/analytics/report/products/index.js +++ b/plugins/woocommerce-admin/client/analytics/report/products/index.js @@ -6,6 +6,7 @@ import { Component, Fragment } from '@wordpress/element'; import { compose } from '@wordpress/compose'; import PropTypes from 'prop-types'; import { ITEMS_STORE_NAME } from '@woocommerce/data'; +import { AnalyticsError } from '@woocommerce/components'; import { withSelect } from '@wordpress/data'; /** @@ -15,7 +16,6 @@ import { advancedFilters, charts, filters } from './config'; import getSelectedChart from '../../../lib/get-selected-chart'; import ProductsReportTable from './table'; import ReportChart from '../../components/report-chart'; -import ReportError from '../../components/report-error'; import ReportSummary from '../../components/report-summary'; import VariationsReportTable from '../variations/table'; import ReportFilters from '../../components/report-filters'; @@ -57,7 +57,7 @@ class ProductsReport extends Component { this.props; if ( isError ) { - return ; + return ; } const chartQuery = { @@ -82,7 +82,6 @@ class ProductsReport extends Component { mode={ mode } charts={ charts } endpoint="products" - isRequesting={ isRequesting } query={ chartQuery } selectedChart={ getSelectedChart( query.chart, charts ) } filters={ filters } diff --git a/plugins/woocommerce-admin/client/analytics/report/taxes/index.js b/plugins/woocommerce-admin/client/analytics/report/taxes/index.js index 1b089e790c0..493ca5e30b8 100644 --- a/plugins/woocommerce-admin/client/analytics/report/taxes/index.js +++ b/plugins/woocommerce-admin/client/analytics/report/taxes/index.js @@ -52,7 +52,6 @@ class TaxesReport extends Component { { const { path, query, isError, isRequesting } = props; if ( isError ) { - return ; + return ; } const chartQuery = { @@ -59,7 +59,6 @@ const VariationsReport = ( props ) => { mode={ mode } charts={ charts } endpoint="variations" - isRequesting={ isRequesting } query={ chartQuery } selectedChart={ getSelectedChart( query.chart, charts ) } filters={ filters } diff --git a/plugins/woocommerce-admin/client/core-profiler/pages/BusinessInfo.tsx b/plugins/woocommerce-admin/client/core-profiler/pages/BusinessInfo.tsx index 8a682cee24a..7d28a8e1395 100644 --- a/plugins/woocommerce-admin/client/core-profiler/pages/BusinessInfo.tsx +++ b/plugins/woocommerce-admin/client/core-profiler/pages/BusinessInfo.tsx @@ -90,7 +90,7 @@ export const selectIndustryMapping = { 'woocommerce' ), im_setting_up_a_store_for_a_client: __( - "Which industry is your client's business in?", + 'Which industry is your client’s business in?', 'woocommerce' ), }; @@ -238,7 +238,7 @@ export const BusinessInfo = ( { 'woocommerce' ) } subTitle={ __( - "We'll use this information to help you set up payments, shipping, and taxes, as well as recommending the best theme for your store.", + 'We’ll use this information to help you set up payments, shipping, and taxes, as well as recommending the best theme for your store.', 'woocommerce' ) } /> @@ -268,7 +268,7 @@ export const BusinessInfo = ( { />

{ __( - "Don't worry — you can always change it later!", + 'Don’t worry — you can always change it later!', 'woocommerce' ) }

@@ -390,7 +390,7 @@ export const BusinessInfo = ( { { createInterpolateElement( __( // translators: first tag is filled with the country name detected by geolocation, second tag is the country name selected by the user - "It looks like you're located in . Are you sure you want to create a store in ?", + 'It looks like you’re located in . Are you sure you want to create a store in ?', 'woocommerce' ), { diff --git a/plugins/woocommerce-admin/client/core-profiler/pages/BusinessLocation.tsx b/plugins/woocommerce-admin/client/core-profiler/pages/BusinessLocation.tsx index 71b7b3676e4..ab0160ddf09 100644 --- a/plugins/woocommerce-admin/client/core-profiler/pages/BusinessLocation.tsx +++ b/plugins/woocommerce-admin/client/core-profiler/pages/BusinessLocation.tsx @@ -45,7 +45,7 @@ export const BusinessLocation = ( { 'woocommerce' ) } subTitle={ __( - "We'll use this information to help you set up payments, shipping, and taxes.", + 'We’ll use this information to help you set up payments, shipping, and taxes.', 'woocommerce' ) } /> diff --git a/plugins/woocommerce-admin/client/core-profiler/pages/IntroOptIn.tsx b/plugins/woocommerce-admin/client/core-profiler/pages/IntroOptIn.tsx index aa795e175c4..e8861744aaa 100644 --- a/plugins/woocommerce-admin/client/core-profiler/pages/IntroOptIn.tsx +++ b/plugins/woocommerce-admin/client/core-profiler/pages/IntroOptIn.tsx @@ -45,7 +45,7 @@ export const IntroOptIn = ( { title={ __( 'Welcome to Woo!', 'woocommerce' ) } subTitle={ interpolateComponents( { mixedString: __( - "It's great to have you here with us! We'll be guiding you through the setup process – first, answer a few questions to tailor your experience.", + 'It’s great to have you here with us! We’ll be guiding you through the setup process – first, answer a few questions to tailor your experience.', 'woocommerce' ), components: { diff --git a/plugins/woocommerce-admin/client/core-profiler/pages/Plugins.tsx b/plugins/woocommerce-admin/client/core-profiler/pages/Plugins.tsx index f3c50c28bc3..8964ed1ab2c 100644 --- a/plugins/woocommerce-admin/client/core-profiler/pages/Plugins.tsx +++ b/plugins/woocommerce-admin/client/core-profiler/pages/Plugins.tsx @@ -7,6 +7,7 @@ import interpolateComponents from '@automattic/interpolate-components'; import { Link } from '@woocommerce/components'; import { Extension, ExtensionList } from '@woocommerce/data'; import { useState } from 'react'; +import clsx from 'clsx'; /** * Internal dependencies @@ -146,6 +147,10 @@ export const Plugins = ( { ].includes( plugin.key ) ); + const pluginsCardRowCount = Math.ceil( + context.pluginsAvailable.length / 2 + ); + return (
{ errorMessage }

) } -
+
{ context.pluginsAvailable.map( ( plugin ) => { const learnMoreLink = plugin.learn_more_link ? ( -
+
diff --git a/plugins/woocommerce-admin/client/homescreen/activity-panel/orders/index.js b/plugins/woocommerce-admin/client/homescreen/activity-panel/orders/index.js index a443b883e3e..143b8a65d0c 100644 --- a/plugins/woocommerce-admin/client/homescreen/activity-panel/orders/index.js +++ b/plugins/woocommerce-admin/client/homescreen/activity-panel/orders/index.js @@ -321,7 +321,7 @@ function OrdersPanel( { unreadOrdersCount, orderStatuses } ) { return ( diff --git a/plugins/woocommerce-admin/client/homescreen/mobile-app-modal/components/useSendMagicLink.tsx b/plugins/woocommerce-admin/client/homescreen/mobile-app-modal/components/useSendMagicLink.tsx index e162edfd22c..fa4fc7e276e 100644 --- a/plugins/woocommerce-admin/client/homescreen/mobile-app-modal/components/useSendMagicLink.tsx +++ b/plugins/woocommerce-admin/client/homescreen/mobile-app-modal/components/useSendMagicLink.tsx @@ -61,7 +61,7 @@ export const useSendMagicLink = () => { createNotice( 'error', __( - "We couldn't send the link. Try again in a few seconds.", + 'We couldn’t send the link. Try again in a few seconds.', 'woocommerce' ) ); @@ -71,7 +71,7 @@ export const useSendMagicLink = () => { createNotice( 'error', __( - "Sorry, your account doesn't have sufficient permission.", + 'Sorry, your account doesn’t have sufficient permission.', 'woocommerce' ) ); @@ -80,7 +80,7 @@ export const useSendMagicLink = () => { } else { createNotice( 'error', - "We couldn't send the link. Try again in a few seconds." + 'We couldn’t send the link. Try again in a few seconds.' ); } } ); diff --git a/plugins/woocommerce-admin/client/inbox-panel/index.js b/plugins/woocommerce-admin/client/inbox-panel/index.js index 32e56c6c12d..06ab9f474a6 100644 --- a/plugins/woocommerce-admin/client/inbox-panel/index.js +++ b/plugins/woocommerce-admin/client/inbox-panel/index.js @@ -71,7 +71,7 @@ const renderEmptyCard = () => ( > { __( 'As things begin to happen in your store your inbox will start to fill up. ' + - "You'll see things like achievements, new feature announcements, extension recommendations and more!", + 'You’ll see things like achievements, new feature announcements, extension recommendations and more!', 'woocommerce' ) } diff --git a/plugins/woocommerce-admin/client/inbox-panel/test/index.js b/plugins/woocommerce-admin/client/inbox-panel/test/index.js index c26bbecf821..f6cd8f84419 100644 --- a/plugins/woocommerce-admin/client/inbox-panel/test/index.js +++ b/plugins/woocommerce-admin/client/inbox-panel/test/index.js @@ -203,7 +203,7 @@ describe( 'inbox_note_view event', () => { notesHaveResolved: true, isBatchUpdating: false, } ) ); - // The original InboxNotecard has a VisibilityDetector so I prefered to mock it and always call onNoteVisible + // The original InboxNotecard has a VisibilityDetector so I preferred to mock it and always call onNoteVisible InboxNoteCard.mockImplementation( ( { onNoteVisible, note } ) => { useEffect( () => onNoteVisible( note ), [] ); return
{ note.id }
; diff --git a/plugins/woocommerce-admin/client/launch-your-store/constants.ts b/plugins/woocommerce-admin/client/launch-your-store/constants.ts index a6a2bbdf15c..9b95ffc736c 100644 --- a/plugins/woocommerce-admin/client/launch-your-store/constants.ts +++ b/plugins/woocommerce-admin/client/launch-your-store/constants.ts @@ -8,7 +8,7 @@ export const COMING_SOON_PAGE_EDITOR_LINK = getAdminLink( ); export const SITE_VISIBILITY_DOC_LINK = - 'https://woocommerce.com/document/woocommerce-launch-your-store/'; + 'https://woocommerce.com/document/configuring-woocommerce-settings/coming-soon-mode/'; export const LAUNCH_YOUR_STORE_DOC_LINK = 'https://woocommerce.com/document/configuring-woocommerce-settings/#site-visibility'; diff --git a/plugins/woocommerce-admin/client/launch-your-store/hub/main-content/pages/launch-store-success/WhatsNext.tsx b/plugins/woocommerce-admin/client/launch-your-store/hub/main-content/pages/launch-store-success/WhatsNext.tsx index 493d68f9b16..a3b646ed31d 100644 --- a/plugins/woocommerce-admin/client/launch-your-store/hub/main-content/pages/launch-store-success/WhatsNext.tsx +++ b/plugins/woocommerce-admin/client/launch-your-store/hub/main-content/pages/launch-store-success/WhatsNext.tsx @@ -86,7 +86,7 @@ const getActionsList = ( { activePlugins, allTasklists }: WhatsNextProps ) => { const mailchimp = { title: __( 'Build customer relationships', 'woocommerce' ), description: __( - "Keep your shoppers up to date with what's new in your store and set up clever post-purchase automations.", + 'Keep your shoppers up to date with what’s new in your store and set up clever post-purchase automations.', 'woocommerce' ), link: isMailChimpActivated @@ -123,7 +123,7 @@ const getActionsList = ( { activePlugins, allTasklists }: WhatsNextProps ) => { const externalDocumentation = { title: __( 'Help is on hand', 'woocommerce' ), description: __( - "Detailed guides and our support team are always available if you're feeling stuck or need some guidance.", + 'Detailed guides and our support team are always available if you’re feeling stuck or need some guidance.', 'woocommerce' ), link: `https://woo.com/documentation/woocommerce/?utm_source=launch_your_store&utm_medium=product`, diff --git a/plugins/woocommerce-admin/client/launch-your-store/hub/main-content/pages/launch-store-success/index.tsx b/plugins/woocommerce-admin/client/launch-your-store/hub/main-content/pages/launch-store-success/index.tsx index c0c08602013..646df4a79de 100644 --- a/plugins/woocommerce-admin/client/launch-your-store/hub/main-content/pages/launch-store-success/index.tsx +++ b/plugins/woocommerce-admin/client/launch-your-store/hub/main-content/pages/launch-store-success/index.tsx @@ -148,7 +148,7 @@ export const LaunchYourStoreSuccess = ( { } ) : __( - "You've successfully launched your store and are ready to start selling! We can't wait to see your business grow.", + 'You’ve successfully launched your store and are ready to start selling! We can’t wait to see your business grow.', 'woocommerce' ) } @@ -193,7 +193,7 @@ export const LaunchYourStoreSuccess = ( { />

- { __( "What's next?", 'woocommerce' ) } + { __( 'What’s next?', 'woocommerce' ) }

{ + const [ pendingSubmitEvent, setPendingSubmitEvent ] = useState( null ); + const [ isConfirmModalOpen, setIsConfirmModalOpen ] = useState( false ); + const currentComingSoon = currentSetting?.woocommerce_coming_soon ?? 'no'; + + // Hooks into settings' "mainform" to show a confirmation modal when the form is submitted. + useEffect( () => { + const form = formRef.current; + const handleFormSubmit = ( event ) => { + const formData = new FormData( form ); + + // Only block submission when switching to coming soon mode from live. + if ( + currentComingSoon === 'no' && + formData.get( 'woocommerce_coming_soon' ) === 'yes' + ) { + event.preventDefault(); + setIsConfirmModalOpen( true ); + setPendingSubmitEvent( event ); + } + }; + if ( form ) { + form.addEventListener( 'submit', handleFormSubmit ); + } + + return () => { + if ( form ) { + form.removeEventListener( 'submit', handleFormSubmit ); + } + }; + }, [ currentSetting, formRef ] ); + + const cancelSubmit = () => { + setPendingSubmitEvent( null ); // Clear the pending submit + setIsConfirmModalOpen( false ); // Close the modal + + if ( saveButtonRef.current ) { + saveButtonRef.current.classList.remove( 'is-busy' ); + } + }; + + const confirmSubmit = () => { + if ( pendingSubmitEvent ) { + // WooCommerce checks for the "save" input. + if ( saveButtonRef.current && formRef.current ) { + const hiddenInput = document.createElement( 'input' ); + hiddenInput.type = 'hidden'; + hiddenInput.name = saveButtonRef.current.name || 'save'; + hiddenInput.value = + saveButtonRef.current.value || + __( 'Save changes', 'woocommerce' ); + formRef.current.appendChild( hiddenInput ); + } + + pendingSubmitEvent.target.submit(); + setPendingSubmitEvent( null ); + } + setIsConfirmModalOpen( false ); // Close the modal + }; + + return isConfirmModalOpen ? ( + +
+ { __( + 'Are you sure you want to switch from live to coming soon mode? Your site will not be visible, and customers won’t be able to make purchases during this time.', + 'woocommerce' + ) } +
+
+
+
+
+ + +
+
+ ) : null; +}; diff --git a/plugins/woocommerce-admin/client/launch-your-store/settings/components/confirmation-modal.scss b/plugins/woocommerce-admin/client/launch-your-store/settings/components/confirmation-modal.scss new file mode 100644 index 00000000000..be45ad905a6 --- /dev/null +++ b/plugins/woocommerce-admin/client/launch-your-store/settings/components/confirmation-modal.scss @@ -0,0 +1,24 @@ +.site-visibility-settings-confirmation-modal { + .site-visibility-settings-confirmation-modal__content { + margin-top: 15px; + } + + .site-visibility-settings-confirmation-modal__buttons { + display: flex; + flex-flow: row-reverse; + } + + // Hacky solution for a divider line that spans over its container's paddings. + .divider-container { + height: 1px; + margin-bottom: 30px; + margin-top: 25px; + + & > hr { + position: absolute; + width: 100%; + left: 0; + right: 0; + } + } +} diff --git a/plugins/woocommerce-admin/client/launch-your-store/settings/components/test/confirmation-modal.test.js b/plugins/woocommerce-admin/client/launch-your-store/settings/components/test/confirmation-modal.test.js new file mode 100644 index 00000000000..d7267e32cf1 --- /dev/null +++ b/plugins/woocommerce-admin/client/launch-your-store/settings/components/test/confirmation-modal.test.js @@ -0,0 +1,183 @@ +/** + * External dependencies + */ +import React from 'react'; +import { render, fireEvent, screen } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; + +/** + * Internal dependencies + */ +import { ConfirmationModal } from '../confirmation-modal'; + +// Mock the necessary external dependencies +jest.mock( '@wordpress/components', () => ( { + Modal: jest.fn( ( { title, children, onRequestClose } ) => ( +
+
{ title }
+
{ children }
+ +
+ ) ), + Button: jest.fn( ( { children, onClick } ) => ( + + ) ), +} ) ); + +describe( 'ConfirmationModal', () => { + let formRef, saveButtonRef; + + const mockSelectComingSoon = ( value ) => { + // Set up form data + const input = document.createElement( 'input' ); + input.name = 'woocommerce_coming_soon'; + input.value = value; + formRef.current.appendChild( input ); + }; + + const fireSubmitEvent = () => { + // Simulate form submission + const submitEvent = new Event( 'submit', { + bubbles: true, + cancelable: true, + } ); + fireEvent( formRef.current, submitEvent ); + }; + + beforeEach( () => { + formRef = { current: document.createElement( 'form' ) }; + saveButtonRef = { current: document.createElement( 'button' ) }; + formRef.current.appendChild( saveButtonRef.current ); + document.body.appendChild( formRef.current ); + } ); + + afterEach( () => { + document.body.removeChild( formRef.current ); + } ); + + it( 'should prompt the modal if current setting is live and submit the form', () => { + const currentSetting = { woocommerce_coming_soon: 'no' }; + + render( + + ); + + const submitListener = jest.fn(); + formRef.current.onsubmit = submitListener; + + mockSelectComingSoon( 'yes' ); + fireSubmitEvent(); + + // Confirm modal is prompted + expect( + screen.getByText( 'Confirm switch to ‘Coming soon’ mode' ) + ).toBeInTheDocument(); + + // Simulate confirming submission + fireEvent.click( screen.getByText( 'Switch' ) ); + + // Ensure the form is submitted + expect( submitListener ).toHaveBeenCalled(); + } ); + + it( 'should prompt the modal if current setting is not set', () => { + render( + + ); + + mockSelectComingSoon( 'yes' ); + fireSubmitEvent(); + + // Confirm that the modal is not prompted + expect( + screen.queryByText( 'Confirm switch to ‘Coming soon’ mode' ) + ).toBeInTheDocument(); + } ); + + it( 'should not prompt the modal if current setting is already "coming soon"', () => { + const currentSetting = { woocommerce_coming_soon: 'yes' }; + + render( + + ); + + mockSelectComingSoon( 'yes' ); + fireSubmitEvent(); + + // Confirm that the modal is not prompted + expect( + screen.queryByText( 'Confirm switch to ‘Coming soon’ mode' ) + ).not.toBeInTheDocument(); + } ); + + it( 'should close the modal on cancel', () => { + const currentSetting = { woocommerce_coming_soon: 'no' }; + + render( + + ); + + mockSelectComingSoon( 'yes' ); + fireSubmitEvent(); + + // Confirm modal is prompted + expect( + screen.getByText( 'Confirm switch to ‘Coming soon’ mode' ) + ).toBeInTheDocument(); + + // Simulate canceling the modal + fireEvent.click( screen.getByText( 'Cancel' ) ); + + // Confirm that the modal is closed + expect( + screen.queryByText( 'Confirm switch to ‘Coming soon’ mode' ) + ).not.toBeInTheDocument(); + } ); + + it( 'should handle the save button correctly', () => { + const currentSetting = { woocommerce_coming_soon: 'no' }; + saveButtonRef.current.name = 'save'; + saveButtonRef.current.value = 'Save changes'; + + render( + + ); + + mockSelectComingSoon( 'yes' ); + fireSubmitEvent(); + + // Confirm modal is prompted + expect( + screen.getByText( 'Confirm switch to ‘Coming soon’ mode' ) + ).toBeInTheDocument(); + + // Simulate confirming submission + fireEvent.click( screen.getByText( 'Switch' ) ); + + // Check that the hidden input with "save" has been added to the form + const hiddenInput = + formRef.current.querySelector( 'input[name="save"]' ); + expect( hiddenInput ).toBeInTheDocument(); + expect( hiddenInput.value ).toBe( 'Save changes' ); + } ); +} ); diff --git a/plugins/woocommerce-admin/client/launch-your-store/settings/slotfill.js b/plugins/woocommerce-admin/client/launch-your-store/settings/slotfill.js index aba9bd41ee6..370675bc000 100644 --- a/plugins/woocommerce-admin/client/launch-your-store/settings/slotfill.js +++ b/plugins/woocommerce-admin/client/launch-your-store/settings/slotfill.js @@ -12,6 +12,7 @@ import { createInterpolateElement, createElement, useEffect, + useRef, } from '@wordpress/element'; import { registerPlugin } from '@wordpress/plugins'; import { __ } from '@wordpress/i18n'; @@ -29,6 +30,7 @@ import { COMING_SOON_PAGE_EDITOR_LINK, SITE_VISIBILITY_DOC_LINK, } from '../constants'; +import { ConfirmationModal } from './components/confirmation-modal'; const { Fill } = createSlotFill( SETTINGS_SLOT_FILL_CONSTANT ); @@ -45,6 +47,21 @@ const SiteVisibility = () => { const [ privateLink, setPrivateLink ] = useState( setting?.woocommerce_private_link || 'no' ); + const formRef = useRef( null ); + const saveButtonRef = useRef( null ); + + useEffect( () => { + const saveButton = document.getElementsByClassName( + 'woocommerce-save-button' + )[ 0 ]; + if ( saveButton ) { + saveButtonRef.current = saveButton; + } + const form = document.querySelector( '#mainform' ); + if ( form ) { + formRef.current = form; + } + }, [] ); useEffect( () => { const initValues = { @@ -115,9 +132,18 @@ const SiteVisibility = () => { />

{ __( 'Site visibility', 'woocommerce' ) }

- { __( - 'Manage how your site appears to visitors.', - 'woocommerce' + { createInterpolateElement( + __( + 'Manage how your site appears to visitors. Learn more', + 'woocommerce' + ), + { + a: createElement( 'a', { + target: '_blank', + rel: 'noreferrer', + href: SITE_VISIBILITY_DOC_LINK, + } ), + } ) }

@@ -145,6 +171,7 @@ const SiteVisibility = () => { ), { a: createElement( 'a', { + target: '_blank', href: COMING_SOON_PAGE_EDITOR_LINK, } ), } @@ -166,20 +193,13 @@ const SiteVisibility = () => { label={ <> { __( - 'Restrict to store pages only', + 'Apply to store pages only', 'woocommerce' ) }

- { createInterpolateElement( - __( - 'Display a "coming soon" message on your store pages — the rest of your site will remain visible.', - 'woocommerce' - ), - { - a: createElement( 'a', { - href: SITE_VISIBILITY_DOC_LINK, - } ), - } + { __( + 'Display a “coming soon” message on your store pages — the rest of your site will remain visible.', + 'woocommerce' ) }

@@ -266,6 +286,13 @@ const SiteVisibility = () => { ) }

+ { formRef.current && saveButtonRef.current ? ( + + ) : null }
); }; diff --git a/plugins/woocommerce-admin/client/launch-your-store/status/index.tsx b/plugins/woocommerce-admin/client/launch-your-store/status/index.tsx index 96f5bb28a2a..b5085fea068 100644 --- a/plugins/woocommerce-admin/client/launch-your-store/status/index.tsx +++ b/plugins/woocommerce-admin/client/launch-your-store/status/index.tsx @@ -1,12 +1,6 @@ /** * External dependencies */ -import { __ } from '@wordpress/i18n'; -import { Icon, moreVertical, edit, cog } from '@wordpress/icons'; -import { Dropdown, Button, MenuGroup, MenuItem } from '@wordpress/components'; -import { getAdminLink, getSetting } from '@woocommerce/settings'; -import clsx from 'clsx'; -import { recordEvent } from '@woocommerce/tracks'; /** * Internal dependencies @@ -14,23 +8,8 @@ import { recordEvent } from '@woocommerce/tracks'; import './style.scss'; import { SiteVisibilityTour } from '../tour'; import { useSiteVisibilityTour } from '../tour/use-site-visibility-tour'; -import { COMING_SOON_PAGE_EDITOR_LINK } from '../constants'; -export const LaunchYourStoreStatus = ( { - comingSoon, - storePagesOnly, -}: { - comingSoon: string; - storePagesOnly: string; -} ) => { - const isComingSoon = comingSoon && comingSoon === 'yes'; - const isStorePagesOnly = - isComingSoon && storePagesOnly && storePagesOnly === 'yes'; - const comingSoonText = isStorePagesOnly - ? __( 'Store coming soon', 'woocommerce' ) - : __( 'Site coming soon', 'woocommerce' ); - const liveText = __( 'Live', 'woocommerce' ); - const dropdownText = isComingSoon ? comingSoonText : liveText; +export const LaunchYourStoreStatus = () => { const { showTour, setShowTour, onClose, shouldTourBeShown } = useSiteVisibilityTour(); @@ -44,75 +23,6 @@ export const LaunchYourStoreStatus = ( { } } /> ) } -
- ( - - ) } - renderContent={ () => ( - <> - - { - recordEvent( - 'launch_your_store_badge_menu_manage_site_visibility_click', - { - site_visibility: isComingSoon - ? 'coming_soon' - : 'live', - } - ); - } } - // @ts-expect-error Prop gets passed down to underlying button https://developer.wordpress.org/block-editor/reference-guides/components/menu-item/#props - href={ getAdminLink( - 'admin.php?page=wc-settings&tab=site-visibility' - ) } - > - - { __( - 'Manage site visibility', - 'woocommerce' - ) } - - { isComingSoon && - getSetting( 'currentThemeIsFSETheme' ) && ( - { - recordEvent( - 'launch_your_store_badge_menu_customize_coming_soon_click' - ); - } } - // @ts-expect-error Prop gets passed down to underlying button https://developer.wordpress.org/block-editor/reference-guides/components/menu-item/#props - href={ - COMING_SOON_PAGE_EDITOR_LINK - } - > - - { __( - 'Customize "Coming soon" page', - 'woocommerce' - ) } - - ) } - - - ) } - /> -
); }; diff --git a/plugins/woocommerce-admin/client/launch-your-store/tour/index.tsx b/plugins/woocommerce-admin/client/launch-your-store/tour/index.tsx index 0ffa96e8189..0edad4961a4 100644 --- a/plugins/woocommerce-admin/client/launch-your-store/tour/index.tsx +++ b/plugins/woocommerce-admin/client/launch-your-store/tour/index.tsx @@ -49,18 +49,19 @@ export const SiteVisibilityTour = ( { onClose }: { onClose: () => void } ) => { steps: [ { referenceElements: { - desktop: '.woocommerce-lys-status-pill', + desktop: + '#wp-admin-bar-woocommerce-site-visibility-badge', }, meta: { name: 'set-your-store-visibility', heading: __( - "Set your store's visibility", + 'Set your store’s visibility', 'woocommerce' ), descriptions: { desktop: createInterpolateElement( __( - 'Launch your store only when you\'re ready to by switching between "Coming soon" and "Live" modes. Build excitement by creating a custom page visitors will see before you\'re ready to go live. Discover more', + 'Launch your store only when you’re ready to by switching between "Coming soon" and "Live" modes. Build excitement by creating a custom page visitors will see before you’re ready to go live. Discover more', 'woocommerce' ), { diff --git a/plugins/woocommerce-admin/client/launch-your-store/tour/use-site-visibility-tour.tsx b/plugins/woocommerce-admin/client/launch-your-store/tour/use-site-visibility-tour.tsx index 3b6562d254f..52460350aec 100644 --- a/plugins/woocommerce-admin/client/launch-your-store/tour/use-site-visibility-tour.tsx +++ b/plugins/woocommerce-admin/client/launch-your-store/tour/use-site-visibility-tour.tsx @@ -1,50 +1,57 @@ /** * External dependencies */ -import { OPTIONS_STORE_NAME } from '@woocommerce/data'; -import { useSelect, dispatch } from '@wordpress/data'; +import { OPTIONS_STORE_NAME, useUserPreferences } from '@woocommerce/data'; +import { useSelect } from '@wordpress/data'; import { useState } from 'react'; -const LYS_TOUR_HIDDEN = 'woocommerce_launch_your_store_tour_hidden'; - export const useSiteVisibilityTour = () => { const [ showTour, setShowTour ] = useState( true ); - const { shouldTourBeShown } = useSelect( ( select ) => { - // Tour should only be shown if the user has not seen it before and the `woocommerce_show_lys_tour` option is "yes" (for sites upgrading from a previous WooCommerce version) - const { getCurrentUser } = select( 'core' ); - const wasTourShown = + // Tour should only be shown if the user has not seen it before and the `woocommerce_show_lys_tour` option is "yes" (for sites upgrading from a previous WooCommerce version) + const shouldStoreShowLYSTour = useSelect( + ( select ) => + select( OPTIONS_STORE_NAME ).getOption( + 'woocommerce_show_lys_tour' + ) === 'yes' + ); + + /** + * This is temporary to support sites upgrading from a previous version of WooCommerce. + * We used user meta to store the tour dismissal state but now we use WooCommerce meta instead. + * It will be removed in WC 9.4. + */ + const hasUserDismissedTourMeta = useSelect( ( select ) => { + const currentUser = select( 'core' ).getCurrentUser(); + if ( ! currentUser ) { + // If the user is not logged in, we don't want to show the tour. + return true; + } + + return ( // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - ( getCurrentUser() as { meta?: { [ key: string ]: string } } ) - ?.meta?.[ LYS_TOUR_HIDDEN ] === 'yes'; - - const { getOption } = select( OPTIONS_STORE_NAME ); - - const showLYSTourOption = getOption( 'woocommerce_show_lys_tour' ); - - const _shouldTourBeShown = - showLYSTourOption === 'yes' && ! wasTourShown; - - return { - shouldTourBeShown: _shouldTourBeShown, - }; + ( currentUser as { meta: { [ key: string ]: string } } ).meta + .woocommerce_launch_your_store_tour_hidden === 'yes' + ); } ); + const { + launch_your_store_tour_hidden: lysTourHidden, + updateUserPreferences, + } = useUserPreferences(); + const onClose = () => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - dispatch( 'core' ).saveUser( { - id: window?.wcSettings?.currentUserId, - meta: { - woocommerce_launch_your_store_tour_hidden: 'yes', - }, + updateUserPreferences( { + launch_your_store_tour_hidden: 'yes', } ); }; return { onClose, - shouldTourBeShown, + shouldTourBeShown: + shouldStoreShowLYSTour && + ! ( hasUserDismissedTourMeta || lysTourHidden === 'yes' ), showTour, setShowTour, }; diff --git a/plugins/woocommerce-admin/client/layout/controller.js b/plugins/woocommerce-admin/client/layout/controller.js index f16d7b3f78d..dd686f064c3 100644 --- a/plugins/woocommerce-admin/client/layout/controller.js +++ b/plugins/woocommerce-admin/client/layout/controller.js @@ -275,6 +275,13 @@ export const getPages = () => { __( 'Profiler', 'woocommerce' ), ], capability: 'manage_woocommerce', + layout: { + header: false, + footer: false, + showNotices: true, + showStoreAlerts: false, + showPluginArea: false, + }, } ); } } diff --git a/plugins/woocommerce-admin/client/marketing/hooks/useRegisteredChannels.ts b/plugins/woocommerce-admin/client/marketing/hooks/useRegisteredChannels.ts index 199bdd584b1..25a732d43ce 100644 --- a/plugins/woocommerce-admin/client/marketing/hooks/useRegisteredChannels.ts +++ b/plugins/woocommerce-admin/client/marketing/hooks/useRegisteredChannels.ts @@ -24,7 +24,7 @@ type UseRegisteredChannels = { }; /** - * A object that maps the product listings status in + * An object that maps the product listings status in * plugins/woocommerce/src/Admin/Marketing/MarketingChannelInterface.php backend * to SyncStatusType frontend. */ diff --git a/plugins/woocommerce-admin/client/payments/payment-recommendations-wrapper.tsx b/plugins/woocommerce-admin/client/payments/payment-recommendations-wrapper.tsx index 1a57546b462..d00e034175c 100644 --- a/plugins/woocommerce-admin/client/payments/payment-recommendations-wrapper.tsx +++ b/plugins/woocommerce-admin/client/payments/payment-recommendations-wrapper.tsx @@ -21,15 +21,11 @@ export const PaymentRecommendations: React.FC< EmbeddedBodyProps > = ( { tab, section, } ) => { - if ( page === 'wc-settings' && tab === 'checkout' && ! section ) { - if ( - window?.wcAdminFeatures?.[ - 'reactify-classic-payments-settings' - ] === true - ) { - return null; - } - + if ( + page === 'wc-settings' && + tab === 'checkout' && + ( ! section || section === 'main' ) + ) { return ( diff --git a/plugins/woocommerce-admin/client/payments/payment-recommendations.tsx b/plugins/woocommerce-admin/client/payments/payment-recommendations.tsx index 1425d26fab0..798eb01fde5 100644 --- a/plugins/woocommerce-admin/client/payments/payment-recommendations.tsx +++ b/plugins/woocommerce-admin/client/payments/payment-recommendations.tsx @@ -205,6 +205,10 @@ const PaymentRecommendations: React.FC = () => { }; } ); + if ( pluginsList.length === 0 ) { + return null; + } + return ( diff --git a/plugins/woocommerce-admin/client/settings-payments/components/other-payment-methods.tsx b/plugins/woocommerce-admin/client/settings-payments/components/other-payment-methods.tsx new file mode 100644 index 00000000000..49228cb4276 --- /dev/null +++ b/plugins/woocommerce-admin/client/settings-payments/components/other-payment-methods.tsx @@ -0,0 +1,155 @@ +/** + * External dependencies + */ +import React, { useMemo } from '@wordpress/element'; +import { Button } from '@wordpress/components'; +import ExternalIcon from 'gridicons/dist/external'; +import { __, _x } from '@wordpress/i18n'; +import { + ONBOARDING_STORE_NAME, + PAYMENT_GATEWAYS_STORE_NAME, + SETTINGS_STORE_NAME, +} from '@woocommerce/data'; +import { useSelect } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { getCountryCode } from '~/dashboard/utils'; +import { + getEnrichedPaymentGateways, + getIsGatewayWCPay, + getIsWCPayOrOtherCategoryDoneSetup, + getSplitGateways, +} from '~/task-lists/fills/PaymentGatewaySuggestions/utils'; + +type PaymentGateway = { + id: string; + image_72x72: string; + title: string; + enabled: boolean; + needsSetup: boolean; + // Add other properties as needed... +}; + +const usePaymentGatewayData = () => { + return useSelect( ( select ) => { + const { getSettings } = select( SETTINGS_STORE_NAME ); + const { general: settings = {} } = getSettings( 'general' ); + return { + getPaymentGateway: select( PAYMENT_GATEWAYS_STORE_NAME ) + .getPaymentGateway, + installedPaymentGateways: select( + PAYMENT_GATEWAYS_STORE_NAME + ).getPaymentGateways(), + isResolving: select( ONBOARDING_STORE_NAME ).isResolving( + 'getPaymentGatewaySuggestions' + ), + paymentGatewaySuggestions: select( + ONBOARDING_STORE_NAME + ).getPaymentGatewaySuggestions(), + countryCode: getCountryCode( settings.woocommerce_default_country ), + }; + }, [] ); +}; + +const AdditionalGatewayImages = ( { + additionalGateways, +}: { + additionalGateways: PaymentGateway[]; +} ) => ( + <> + { additionalGateways.map( ( gateway ) => ( + { + ) ) } + { _x( '& more.', 'More payment providers to discover', 'woocommerce' ) } + +); + +export const OtherPaymentMethods = () => { + const { + paymentGatewaySuggestions, + installedPaymentGateways, + isResolving, + countryCode, + } = usePaymentGatewayData(); + + const paymentGateways = useMemo( + () => + getEnrichedPaymentGateways( + installedPaymentGateways, + paymentGatewaySuggestions + ), + [ installedPaymentGateways, paymentGatewaySuggestions ] + ); + + const isWCPayOrOtherCategoryDoneSetup = useMemo( + () => + getIsWCPayOrOtherCategoryDoneSetup( paymentGateways, countryCode ), + [ countryCode, paymentGateways ] + ); + + const isWCPaySupported = Array.from( paymentGateways.values() ).some( + getIsGatewayWCPay + ); + + // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars + const [ wcPayGateway, _offlineGateways, additionalGateways ] = useMemo( + () => + getSplitGateways( + paymentGateways, + countryCode ?? '', + isWCPaySupported, + isWCPayOrOtherCategoryDoneSetup + ), + [ + paymentGateways, + countryCode, + isWCPaySupported, + isWCPayOrOtherCategoryDoneSetup, + ] + ); + + if ( isResolving || ! wcPayGateway ) { + return null; + } + + const hasWcPaySetup = wcPayGateway.enabled && ! wcPayGateway.needsSetup; + + return ( + <> + + { additionalGateways.length > 0 && ( + + ) } + + ); +}; diff --git a/plugins/woocommerce-admin/client/settings-payments/components/payment-method.tsx b/plugins/woocommerce-admin/client/settings-payments/components/payment-method.tsx new file mode 100644 index 00000000000..fa7adf07c28 --- /dev/null +++ b/plugins/woocommerce-admin/client/settings-payments/components/payment-method.tsx @@ -0,0 +1,183 @@ +/** + * External dependencies + */ +import { useState } from '@wordpress/element'; +import { __, sprintf } from '@wordpress/i18n'; +import { PaymentGateway } from '@woocommerce/data'; +import { WooPaymentMethodsLogos } from '@woocommerce/onboarding'; + +/** + * Internal dependencies + */ +import { getAdminSetting } from '~/utils/admin-settings'; +import sanitizeHTML from '~/lib/sanitize-html'; +import { WCPayInstallButton } from './wcpay-install-button'; + +export const PaymentMethod = ( { + id, + enabled, + title, + method_title, + method_description, + settings_url, +}: PaymentGateway ) => { + const isWooPayEligible = getAdminSetting( 'isWooPayEligible', false ); + const [ isEnabled, setIsEnabled ] = useState( enabled ); + const [ isLoading, setIsLoading ] = useState( false ); + + const toggleEnabled = async ( e: React.MouseEvent ) => { + e.preventDefault(); + setIsLoading( true ); + + if ( ! window.woocommerce_admin.nonces?.gateway_toggle ) { + // eslint-disable-next-line no-console + console.warn( 'Unexpected error: Nonce not found' ); + // Redirect to payment setting page if nonce is not found. Users should still be able to toggle the payment method from that page. + window.location.href = settings_url; + return; + } + + try { + const response = await fetch( window.woocommerce_admin.ajax_url, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams( { + action: 'woocommerce_toggle_gateway_enabled', + security: window.woocommerce_admin.nonces?.gateway_toggle, + gateway_id: id, + } ), + } ); + + const result = await response.json(); + + if ( result.success ) { + if ( result.data === true ) { + setIsEnabled( true ); + } else if ( result.data === false ) { + setIsEnabled( false ); + } else if ( result.data === 'needs_setup' ) { + window.location.href = settings_url; + } + } else { + window.location.href = settings_url; + } + } catch ( error ) { + // eslint-disable-next-line no-console + console.error( 'Error toggling gateway:', error ); + } finally { + setIsLoading( false ); + } + }; + + return ( + + + +
+ + { method_title } + + { id !== 'pre_install_woocommerce_payments_promotion' && + method_title !== title && ( + +  –  + { title } + + ) } + { id === 'pre_install_woocommerce_payments_promotion' && ( +
+ +
+ ) } +
+ + + + + { isEnabled + ? __( 'Yes', 'woocommerce' ) + : __( 'No', 'woocommerce' ) } + + + + + + { id === 'pre_install_woocommerce_payments_promotion' ? ( + + ) : ( + + { enabled + ? __( 'Manage', 'woocommerce' ) + : __( 'Finish setup', 'woocommerce' ) } + + ) } + + + ); +}; diff --git a/plugins/woocommerce-admin/client/settings-payments/components/wcpay-install-button.tsx b/plugins/woocommerce-admin/client/settings-payments/components/wcpay-install-button.tsx new file mode 100644 index 00000000000..a5583bd73f6 --- /dev/null +++ b/plugins/woocommerce-admin/client/settings-payments/components/wcpay-install-button.tsx @@ -0,0 +1,60 @@ +/** + * External dependencies + */ +import React, { useState } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { + PAYMENT_GATEWAYS_STORE_NAME, + PLUGINS_STORE_NAME, +} from '@woocommerce/data'; +import { Button } from '@wordpress/components'; +import { resolveSelect, useDispatch } from '@wordpress/data'; +import { recordEvent } from '@woocommerce/tracks'; + +const slug = 'woocommerce-payments'; +export const WCPayInstallButton = () => { + const [ installing, setInstalling ] = useState( false ); + const { installAndActivatePlugins } = useDispatch( PLUGINS_STORE_NAME ); + const { createNotice } = useDispatch( 'core/notices' ); + + const redirectToSettings = async () => { + const paymentGateway = await resolveSelect( + PAYMENT_GATEWAYS_STORE_NAME + ).getPaymentGateway( slug.replace( /-/g, '_' ) ); + + if ( paymentGateway?.settings_url ) { + window.location.href = paymentGateway.settings_url; + } + }; + + const installWooCommercePayments = async () => { + if ( installing ) return; + + setInstalling( true ); + recordEvent( 'settings_payments_recommendations_setup', { + extension_selected: slug, + } ); + + try { + await installAndActivatePlugins( [ slug ] ); + redirectToSettings(); + } catch ( error ) { + if ( error instanceof Error ) { + createNotice( 'error', error.message ); + } + setInstalling( false ); + } + }; + + return ( + + ); +}; diff --git a/plugins/woocommerce-admin/client/settings-payments/settings-payments-main.scss b/plugins/woocommerce-admin/client/settings-payments/settings-payments-main.scss index a63d140104c..64beb4baac8 100644 --- a/plugins/woocommerce-admin/client/settings-payments/settings-payments-main.scss +++ b/plugins/woocommerce-admin/client/settings-payments/settings-payments-main.scss @@ -1,9 +1,29 @@ +@import "~/wp-admin-scripts/payment-method-promotions/payment-promotion-row.scss"; + .settings-payments-main__container { - h1 { - color: #fff; + .settings-payments-main__spinner { + display: flex; + justify-content: center; + align-items: center; + position: absolute; + width: 100%; + } + + + table.wc_gateways { + .other-payment-methods__button-text { + margin-right: 4px; + } + + td.other-payment-methods-row { + border-top: 1px solid #c3c4c7; + background-color: #fff; + + } + + .other-payment-methods__image { + vertical-align: middle; + margin-right: 8px; + } } - background: #000; - text-align: center; - padding: 50px 0; - width: 100%; } diff --git a/plugins/woocommerce-admin/client/settings-payments/settings-payments-main.tsx b/plugins/woocommerce-admin/client/settings-payments/settings-payments-main.tsx index c2671e205bf..94f85355931 100644 --- a/plugins/woocommerce-admin/client/settings-payments/settings-payments-main.tsx +++ b/plugins/woocommerce-admin/client/settings-payments/settings-payments-main.tsx @@ -1,17 +1,108 @@ /** * External dependencies */ -import '@wordpress/element'; +import { useMemo } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { PaymentGateway } from '@woocommerce/data'; /** * Internal dependencies */ import './settings-payments-main.scss'; +import { PaymentMethod } from './components/payment-method'; +import { OtherPaymentMethods } from './components/other-payment-methods'; +import { PaymentsBannerWrapper } from '~/payments/payment-settings-banner'; export const SettingsPaymentsMain: React.FC = () => { + const [ paymentGateways, error ] = useMemo( () => { + const script = document.getElementById( + 'experimental_wc_settings_payments_gateways' + ); + + try { + if ( script && script.textContent ) { + return [ + JSON.parse( script.textContent ) as PaymentGateway[], + null, + ]; + } + throw new Error( 'Could not find payment gateways data' ); + } catch ( e ) { + return [ [], e as Error ]; + } + }, [] ); + + if ( error ) { + // This is a temporary error message to be replaced by error boundary. + return ( +
+

+ { __( 'Error loading payment gateways', 'woocommerce' ) } +

+

{ error.message }

+
+ ); + } + return (
-

Main payments screen

+
+ +
+ + + + + + +
+ + + + + + + + + + + + { paymentGateways.map( + ( gateway: PaymentGateway ) => ( + + ) + ) } + + + + + +
+ { __( 'Method', 'woocommerce' ) } + + { __( 'Enabled', 'woocommerce' ) } + + { __( + 'Description', + 'woocommerce' + ) } +
+ +
+
); }; diff --git a/plugins/woocommerce-admin/client/task-lists/fills/PaymentGatewaySuggestions/components/Setup/Configure.js b/plugins/woocommerce-admin/client/task-lists/fills/PaymentGatewaySuggestions/components/Setup/Configure.js index 23886151378..f64becb576e 100644 --- a/plugins/woocommerce-admin/client/task-lists/fills/PaymentGatewaySuggestions/components/Setup/Configure.js +++ b/plugins/woocommerce-admin/client/task-lists/fills/PaymentGatewaySuggestions/components/Setup/Configure.js @@ -150,7 +150,7 @@ export const Configure = ( { markConfigured, paymentGateway } ) => { { helpText || (

{ __( - "You can manage this payment gateway's settings by clicking the button below", + 'You can manage this payment gateway’s settings by clicking the button below', 'woocommerce' ) }

diff --git a/plugins/woocommerce-admin/client/task-lists/fills/components/load-sample-product-confirm-modal.tsx b/plugins/woocommerce-admin/client/task-lists/fills/components/load-sample-product-confirm-modal.tsx index 24423fc065f..4e0669b811b 100644 --- a/plugins/woocommerce-admin/client/task-lists/fills/components/load-sample-product-confirm-modal.tsx +++ b/plugins/woocommerce-admin/client/task-lists/fills/components/load-sample-product-confirm-modal.tsx @@ -28,7 +28,7 @@ export const LoadSampleProductConfirmModal: React.VFC< Props > = ( { > { __( - "We'll import images from WooCommerce.com to set up your sample products.", + 'We’ll import images from WooCommerce.com to set up your sample products.', 'woocommerce' ) } diff --git a/plugins/woocommerce-admin/client/task-lists/fills/import-products/test/index.tsx b/plugins/woocommerce-admin/client/task-lists/fills/import-products/test/index.tsx index 48d6275688c..d15b969ae51 100644 --- a/plugins/woocommerce-admin/client/task-lists/fills/import-products/test/index.tsx +++ b/plugins/woocommerce-admin/client/task-lists/fills/import-products/test/index.tsx @@ -19,7 +19,7 @@ global.fetch = jest.fn().mockImplementation( () => ); const confirmModalText = - "We'll import images from WooCommerce.com to set up your sample products."; + 'We’ll import images from WooCommerce.com to set up your sample products.'; describe( 'Products', () => { beforeEach( () => { diff --git a/plugins/woocommerce-admin/client/task-lists/fills/products/test/index.tsx b/plugins/woocommerce-admin/client/task-lists/fills/products/test/index.tsx index eaf725ef5e1..36bcd02d9b8 100644 --- a/plugins/woocommerce-admin/client/task-lists/fills/products/test/index.tsx +++ b/plugins/woocommerce-admin/client/task-lists/fills/products/test/index.tsx @@ -42,7 +42,7 @@ global.fetch = jest.fn().mockImplementation( () => jest.mock( '@woocommerce/tracks', () => ( { recordEvent: jest.fn() } ) ); const confirmModalText = - "We'll import images from WooCommerce.com to set up your sample products."; + 'We’ll import images from WooCommerce.com to set up your sample products.'; describe( 'Products', () => { beforeEach( () => { diff --git a/plugins/woocommerce-admin/client/task-lists/fills/shipping/index.js b/plugins/woocommerce-admin/client/task-lists/fills/shipping/index.js index da45e5fb7f3..9bdf2a3f498 100644 --- a/plugins/woocommerce-admin/client/task-lists/fills/shipping/index.js +++ b/plugins/woocommerce-admin/client/task-lists/fills/shipping/index.js @@ -182,7 +182,7 @@ export class Shipping extends Component { createNotice( 'success', __( - "📦 Shipping is done! Don't worry, you can always change it later", + '📦 Shipping is done! Don’t worry, you can always change it later', 'woocommerce' ) ); diff --git a/plugins/woocommerce-admin/client/task-lists/fills/tax/avalara/card.tsx b/plugins/woocommerce-admin/client/task-lists/fills/tax/avalara/card.tsx deleted file mode 100644 index d88b1c114aa..00000000000 --- a/plugins/woocommerce-admin/client/task-lists/fills/tax/avalara/card.tsx +++ /dev/null @@ -1,73 +0,0 @@ -/** - * External dependencies - */ -import { __ } from '@wordpress/i18n'; -import { getAdminLink } from '@woocommerce/settings'; -import interpolateComponents from '@automattic/interpolate-components'; -import { recordEvent } from '@woocommerce/tracks'; - -/** - * Internal dependencies - */ -import { PartnerCard } from '../components/partner-card'; -import { TaxChildProps } from '../utils'; -import logo from './logo.png'; - -export const Card: React.FC< TaxChildProps > = ( { task } ) => { - const { additionalData: { avalaraActivated } = {} } = task; - - return ( - , - }, - } ), - __( - 'Cross-border and multi-channel compliance', - 'woocommerce' - ), - __( 'Automate filing & remittance', 'woocommerce' ), - __( - 'Return-ready, jurisdiction-level reporting.', - 'woocommerce' - ), - ] } - terms={ '' } - actionText={ - avalaraActivated - ? __( 'Continue setup', 'woocommerce' ) - : __( 'Download', 'woocommerce' ) - } - onClick={ () => { - recordEvent( 'tasklist_tax_select_option', { - selected_option: 'avalara', - } ); - - if ( avalaraActivated ) { - window.location.href = getAdminLink( - '/admin.php?page=wc-settings&tab=tax§ion=avatax' - ); - - return; - } - - window.open( - new URL( - 'https://woocommerce.com/products/woocommerce-avatax/' - ).toString(), - '_blank' - ); - } } - /> - ); -}; diff --git a/plugins/woocommerce-admin/client/task-lists/fills/tax/avalara/logo.png b/plugins/woocommerce-admin/client/task-lists/fills/tax/avalara/logo.png deleted file mode 100644 index 8430603eb9f..00000000000 Binary files a/plugins/woocommerce-admin/client/task-lists/fills/tax/avalara/logo.png and /dev/null differ diff --git a/plugins/woocommerce-admin/client/task-lists/fills/tax/components/partner-card.scss b/plugins/woocommerce-admin/client/task-lists/fills/tax/components/partner-card.scss index 7ad3feef6b7..9bcdb291555 100644 --- a/plugins/woocommerce-admin/client/task-lists/fills/tax/components/partner-card.scss +++ b/plugins/woocommerce-admin/client/task-lists/fills/tax/components/partner-card.scss @@ -41,7 +41,7 @@ .woocommerce-tax-partner-card__terms { color: $gray-600; - font-size: 9px; + font-size: 12px; margin-bottom: $gap-smaller; } diff --git a/plugins/woocommerce-admin/client/task-lists/fills/tax/components/partner-card.tsx b/plugins/woocommerce-admin/client/task-lists/fills/tax/components/partner-card.tsx index 30e2e1e5eb7..cef2b27b108 100644 --- a/plugins/woocommerce-admin/client/task-lists/fills/tax/components/partner-card.tsx +++ b/plugins/woocommerce-admin/client/task-lists/fills/tax/components/partner-card.tsx @@ -15,7 +15,8 @@ export const PartnerCard: React.FC< { description: string; benefits: ( string | JSX.Element )[]; terms: string | JSX.Element; - actionText: string; + children?: React.ReactNode; + actionText?: string; onClick: () => void; isBusy?: boolean; } > = ( { @@ -27,6 +28,7 @@ export const PartnerCard: React.FC< { actionText, onClick, isBusy, + children, } ) => { return (
@@ -59,14 +61,18 @@ export const PartnerCard: React.FC< {
{ terms }
- + { children ? ( + children + ) : ( + + ) }
); diff --git a/plugins/woocommerce-admin/client/task-lists/fills/tax/components/partners.tsx b/plugins/woocommerce-admin/client/task-lists/fills/tax/components/partners.tsx index 91c55548b3d..8b243e86285 100644 --- a/plugins/woocommerce-admin/client/task-lists/fills/tax/components/partners.tsx +++ b/plugins/woocommerce-admin/client/task-lists/fills/tax/components/partners.tsx @@ -56,7 +56,7 @@ export const Partners: React.FC< TaxChildProps > = ( { onDisable(); } } > - { __( "I don't charge sales tax", 'woocommerce' ) } + { __( 'I don’t charge sales tax', 'woocommerce' ) } diff --git a/plugins/woocommerce-admin/client/task-lists/fills/tax/index.tsx b/plugins/woocommerce-admin/client/task-lists/fills/tax/index.tsx index 23e5952d0ce..f98fd06a168 100644 --- a/plugins/woocommerce-admin/client/task-lists/fills/tax/index.tsx +++ b/plugins/woocommerce-admin/client/task-lists/fills/tax/index.tsx @@ -17,6 +17,7 @@ import { useEffect, useState, createElement, + useMemo, } from '@wordpress/element'; import { WooOnboardingTask } from '@woocommerce/onboarding'; @@ -25,6 +26,7 @@ import { WooOnboardingTask } from '@woocommerce/onboarding'; */ import { redirectToTaxSettings } from './utils'; import { Card as WooCommerceTaxCard } from './woocommerce-tax/card'; +import { Card as StripeTaxCard } from './stripe-tax/card'; import { createNoticesFromResponse } from '../../../lib/notices'; import { getCountryCode } from '~/dashboard/utils'; import { ManualConfiguration } from './manual-configuration'; @@ -122,7 +124,7 @@ export const Tax: React.FC< TaxProps > = ( { onComplete, query, task } ) => { createNotice( 'success', __( - "You're awesome! One less item on your to-do list ✅", + 'You’re awesome! One less item on your to-do list ✅', 'woocommerce' ) ); @@ -150,20 +152,21 @@ export const Tax: React.FC< TaxProps > = ( { onComplete, query, task } ) => { } ); }, [ updateOptions ] ); - const getVisiblePartners = () => { + const partners = useMemo( () => { const countryCode = getCountryCode( generalSettings?.woocommerce_default_country ) || ''; const { additionalData: { woocommerceTaxCountries = [], + stripeTaxCountries = [], taxJarActivated, woocommerceTaxActivated, woocommerceShippingActivated, } = {}, } = task; - const partners = [ + const allPartners = [ { id: 'woocommerce-tax', card: WooCommerceTaxCard, @@ -174,31 +177,35 @@ export const Tax: React.FC< TaxProps > = ( { onComplete, query, task } ) => { ! woocommerceShippingActivated && woocommerceTaxCountries.includes( countryCode ), }, + { + id: 'stripe-tax', + card: StripeTaxCard, + + isVisible: stripeTaxCountries.includes( countryCode ), + }, ]; - return partners.filter( ( partner ) => partner.isVisible ); - }; - - const partners = getVisiblePartners(); + return allPartners.filter( ( partner ) => partner.isVisible ); + // eslint-disable-next-line react-hooks/exhaustive-deps -- the partner list shouldn't be changing in the middle of interaction. for some reason the country is becoming null in a re-render and causing unexpected behaviour + }, [] ); + const { auto } = query; useEffect( () => { - const { auto } = query; - if ( auto === 'true' ) { onAutomate(); - return; } + }, [ auto, onAutomate ] ); + useEffect( () => { if ( query.partner ) { return; } - recordEvent( 'tasklist_tax_view_options', { options: partners.map( ( partner ) => partner.id ), } ); - }, [ onAutomate, partners, query ] ); + }, [ partners, query.partner ] ); - const getCurrentPartner = () => { + const currentPartner = useMemo( () => { if ( ! query.partner ) { return null; } @@ -206,7 +213,7 @@ export const Tax: React.FC< TaxProps > = ( { onComplete, query, task } ) => { return ( partners.find( ( partner ) => partner.id === query.partner ) || null ); - }; + }, [ partners, query.partner ] ); const childProps = { isPending, @@ -220,8 +227,6 @@ export const Tax: React.FC< TaxProps > = ( { onComplete, query, task } ) => { return ; } - const currentPartner = getCurrentPartner(); - if ( ! partners.length ) { return ( diff --git a/plugins/woocommerce-admin/client/task-lists/fills/tax/stripe-tax/card.tsx b/plugins/woocommerce-admin/client/task-lists/fills/tax/stripe-tax/card.tsx new file mode 100644 index 00000000000..3795a7dee60 --- /dev/null +++ b/plugins/woocommerce-admin/client/task-lists/fills/tax/stripe-tax/card.tsx @@ -0,0 +1,107 @@ +/** + * External dependencies + */ +import { __ } from '@wordpress/i18n'; +import { getAdminLink } from '@woocommerce/settings'; +import { recordEvent } from '@woocommerce/tracks'; +import { Plugins } from '@woocommerce/components'; +import { dispatch, useDispatch } from '@wordpress/data'; +import { SETTINGS_STORE_NAME } from '@woocommerce/data'; +import { Button } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { PartnerCard } from '../components/partner-card'; +import { TaxChildProps } from '../utils'; +import StripeTaxLogo from './stripe-tax-logo.svg'; +import { createNoticesFromResponse } from '~/lib/notices'; + +const STRIPE_TAX_PLUGIN_SLUG = 'stripe-tax-for-woocommerce'; + +const redirectToStripeTaxSettings = () => { + window.location.href = getAdminLink( + '/admin.php?page=wc-settings&tab=stripe_tax_for_woocommerce' + ); +}; + +export const Card: React.FC< TaxChildProps > = ( { + task: { + additionalData: { stripeTaxActivated } = { + stripeTaxActivated: false, + }, + }, +} ) => { + const { createSuccessNotice } = useDispatch( 'core/notices' ); + + return ( + {} } + > + { stripeTaxActivated ? ( + + ) : ( + { + recordEvent( 'tasklist_tax_select_option', { + selected_option: STRIPE_TAX_PLUGIN_SLUG, + } ); + } } + onComplete={ () => { + recordEvent( 'tasklist_tax_install_plugin_success', { + selected_option: STRIPE_TAX_PLUGIN_SLUG, + } ); + const { updateAndPersistSettingsForGroup } = + dispatch( SETTINGS_STORE_NAME ); + updateAndPersistSettingsForGroup( 'general', { + general: { + woocommerce_calc_taxes: 'yes', // Stripe tax requires tax calculation to be enabled so let's do it here to save the user from doing it manually + }, + } ).then( () => { + createSuccessNotice( + __( + "Stripe Tax for Woocommerce has been successfully installed. Let's configure it now.", + 'woocommerce' + ) + ); + redirectToStripeTaxSettings(); + } ); + } } + onError={ ( errors, response ) => { + recordEvent( 'tasklist_tax_install_plugin_error', { + selected_option: STRIPE_TAX_PLUGIN_SLUG, + errors, + } ); + createNoticesFromResponse( response ); + } } + installButtonVariant="secondary" + pluginSlugs={ [ STRIPE_TAX_PLUGIN_SLUG ] } + /> + ) } + + ); +}; diff --git a/plugins/woocommerce-admin/client/task-lists/fills/tax/stripe-tax/stripe-tax-logo.svg b/plugins/woocommerce-admin/client/task-lists/fills/tax/stripe-tax/stripe-tax-logo.svg new file mode 100644 index 00000000000..d290cefc63f --- /dev/null +++ b/plugins/woocommerce-admin/client/task-lists/fills/tax/stripe-tax/stripe-tax-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/plugins/woocommerce-admin/client/task-lists/progress-title/default-progress-title.tsx b/plugins/woocommerce-admin/client/task-lists/progress-title/default-progress-title.tsx index 45c06e34ac1..ca2be82ba3e 100644 --- a/plugins/woocommerce-admin/client/task-lists/progress-title/default-progress-title.tsx +++ b/plugins/woocommerce-admin/client/task-lists/progress-title/default-progress-title.tsx @@ -56,12 +56,12 @@ export const DefaultProgressTitle: React.FC< DefaultProgressTitleProps > = ( { : __( 'Welcome to your store', 'woocommerce' ); } if ( completedCount <= 3 ) { - return __( "Let's get you started", 'woocommerce' ) + ' 🚀'; + return __( 'Let’s get you started', 'woocommerce' ) + ' 🚀'; } if ( completedCount > 3 && completedCount < 6 ) { - return __( "You're on the right track", 'woocommerce' ); + return __( 'You’re on the right track', 'woocommerce' ); } - return __( "You're almost there", 'woocommerce' ); + return __( 'You’re almost there', 'woocommerce' ); }, [ completedCount, hasVisitedTasks, tasksCount ] ); if ( loading ) { diff --git a/plugins/woocommerce-admin/client/task-lists/progress-title/test/default-progress-title.test.tsx b/plugins/woocommerce-admin/client/task-lists/progress-title/test/default-progress-title.test.tsx index 4dd25686444..4ab493c3d35 100644 --- a/plugins/woocommerce-admin/client/task-lists/progress-title/test/default-progress-title.test.tsx +++ b/plugins/woocommerce-admin/client/task-lists/progress-title/test/default-progress-title.test.tsx @@ -49,7 +49,7 @@ describe( 'default-progress-title', () => { ).toBeInTheDocument(); } ); - it( 'should render "Let\'s get you started" when has task visited and task completed count <= 3', () => { + it( 'should render "Let’s get you started" when has task visited and task completed count <= 3', () => { ( useSelect as jest.Mock ).mockImplementation( ( fn ) => fn( () => ( { getTaskList: () => ( { @@ -60,11 +60,11 @@ describe( 'default-progress-title', () => { ); render( ); expect( - screen.getByText( "Let's get you started", { exact: false } ) + screen.getByText( 'Let’s get you started', { exact: false } ) ).toBeInTheDocument(); } ); - it( 'should render "You\'re on the right track" when has task visited and task completed count > 3', () => { + it( 'should render "You’re on the right track" when has task visited and task completed count > 3', () => { ( useSelect as jest.Mock ).mockImplementation( ( fn ) => fn( () => ( { getTaskList: () => ( { @@ -81,11 +81,11 @@ describe( 'default-progress-title', () => { ); render( ); expect( - screen.getByText( "You're on the right track", { exact: false } ) + screen.getByText( 'You’re on the right track', { exact: false } ) ).toBeInTheDocument(); } ); - it( 'should render "You\'re almost there" when has task visited and task completed count > 5', () => { + it( 'should render "You’re almost there" when has task visited and task completed count > 5', () => { ( useSelect as jest.Mock ).mockImplementation( ( fn ) => fn( () => ( { getTaskList: () => ( { @@ -104,7 +104,7 @@ describe( 'default-progress-title', () => { ); render( ); expect( - screen.getByText( "You're almost there", { exact: false } ) + screen.getByText( 'You’re almost there', { exact: false } ) ).toBeInTheDocument(); } ); } ); diff --git a/plugins/woocommerce-admin/client/task-lists/reminder-bar/reminder-bar.tsx b/plugins/woocommerce-admin/client/task-lists/reminder-bar/reminder-bar.tsx index 1a96b202e3c..64bb0e04d76 100644 --- a/plugins/woocommerce-admin/client/task-lists/reminder-bar/reminder-bar.tsx +++ b/plugins/woocommerce-admin/client/task-lists/reminder-bar/reminder-bar.tsx @@ -52,7 +52,7 @@ const ReminderText: React.FC< ReminderTextProps > = ( { ) : /* translators: 1: remaining tasks count */ __( - "🚀 You're doing great! {{strongText}}%1$d steps left{{/strongText}} to get your store up and running. {{setupLink}}Continue setup{{/setupLink}}", + '🚀 You’re doing great! {{strongText}}%1$d steps left{{/strongText}} to get your store up and running. {{setupLink}}Continue setup{{/setupLink}}', 'woocommerce' ); diff --git a/plugins/woocommerce-admin/client/task-lists/setup-task-list/components/task-headers/launch-your-store.tsx b/plugins/woocommerce-admin/client/task-lists/setup-task-list/components/task-headers/launch-your-store.tsx index bbe78e5a4fb..355e0682eb0 100644 --- a/plugins/woocommerce-admin/client/task-lists/setup-task-list/components/task-headers/launch-your-store.tsx +++ b/plugins/woocommerce-admin/client/task-lists/setup-task-list/components/task-headers/launch-your-store.tsx @@ -34,7 +34,7 @@ const LaunchYourStoreHeader = ( {

{ __( - "It's time to celebrate – you're ready to launch your store! Woo! Hit the button to preview your store and make it public.", + 'It’s time to celebrate – you’re ready to launch your store! Woo! Hit the button to preview your store and make it public.', 'woocommerce' ) }

diff --git a/plugins/woocommerce-admin/client/task-lists/setup-task-list/components/task-headers/store-details.js b/plugins/woocommerce-admin/client/task-lists/setup-task-list/components/task-headers/store-details.js index 67fc76a2442..c66eb139a6b 100644 --- a/plugins/woocommerce-admin/client/task-lists/setup-task-list/components/task-headers/store-details.js +++ b/plugins/woocommerce-admin/client/task-lists/setup-task-list/components/task-headers/store-details.js @@ -26,7 +26,7 @@ const StoreDetailsHeader = ( { task, goToTask } ) => {

{ __( - "Get your store up and running in no time. Add your store's address to set up shipping, tax and payments faster.", + 'Get your store up and running in no time. Add your store’s address to set up shipping, tax and payments faster.', 'woocommerce' ) }

diff --git a/plugins/woocommerce-admin/client/task-lists/setup-task-list/components/task-headers/woocommerce-payments.js b/plugins/woocommerce-admin/client/task-lists/setup-task-list/components/task-headers/woocommerce-payments.js index a6f21627e19..47fe2ffb4d6 100644 --- a/plugins/woocommerce-admin/client/task-lists/setup-task-list/components/task-headers/woocommerce-payments.js +++ b/plugins/woocommerce-admin/client/task-lists/setup-task-list/components/task-headers/woocommerce-payments.js @@ -54,7 +54,7 @@ const WoocommercePaymentsHeader = ( { task, trackClick } ) => { className="svg-background" />
-

{ __( "It's time to get paid", 'woocommerce' ) }

+

{ __( 'It’s time to get paid', 'woocommerce' ) }

{ incentive?.task_header_content ? (

{ __( - "You've completed store setup", + 'You’ve completed store setup', 'woocommerce' ) } diff --git a/plugins/woocommerce-admin/client/task-lists/setup-task-list/components/task-list-completed.tsx b/plugins/woocommerce-admin/client/task-lists/setup-task-list/components/task-list-completed.tsx index d19bd0806c3..a91359b9b4c 100644 --- a/plugins/woocommerce-admin/client/task-lists/setup-task-list/components/task-list-completed.tsx +++ b/plugins/woocommerce-admin/client/task-lists/setup-task-list/components/task-list-completed.tsx @@ -33,7 +33,7 @@ export const TaskListCompleted = ( { Completed

{ __( - "You've completed store setup", + 'You’ve completed store setup', 'woocommerce' ) }

diff --git a/plugins/woocommerce-admin/client/typings/global.d.ts b/plugins/woocommerce-admin/client/typings/global.d.ts index 7e0aeebf77c..9c3a8c79e4b 100644 --- a/plugins/woocommerce-admin/client/typings/global.d.ts +++ b/plugins/woocommerce-admin/client/typings/global.d.ts @@ -88,6 +88,12 @@ declare global { getUserSetting?: ( name: string ) => string | undefined; setUserSetting?: ( name: string, value: string ) => void; deleteUserSetting?: ( name: string ) => void; + woocommerce_admin: { + ajax_url: string; + nonces: { + gateway_toggle?: string; + } + } } } diff --git a/plugins/woocommerce-admin/client/wp-admin-scripts/onboarding-homepage-notice/index.js b/plugins/woocommerce-admin/client/wp-admin-scripts/onboarding-homepage-notice/index.js index c12455cbf8b..d4fee7e468c 100644 --- a/plugins/woocommerce-admin/client/wp-admin-scripts/onboarding-homepage-notice/index.js +++ b/plugins/woocommerce-admin/client/wp-admin-scripts/onboarding-homepage-notice/index.js @@ -70,7 +70,7 @@ const onboardingHomepageNotice = () => { dispatch( 'core/notices' ).removeNotice( 'SAVE_POST_NOTICE_ID' ); dispatch( 'core/notices' ).createSuccessNotice( - __( "🏠 Nice work creating your store's homepage!", 'woocommerce' ), + __( '🏠 Nice work creating your store’s homepage!', 'woocommerce' ), { id: 'WOOCOMMERCE_ONBOARDING_HOME_PAGE_NOTICE', type: notificationType, diff --git a/plugins/woocommerce-admin/client/wp-admin-scripts/onboarding-tax-notice/index.js b/plugins/woocommerce-admin/client/wp-admin-scripts/onboarding-tax-notice/index.js index 5b94d57b28b..8cf91d192a0 100644 --- a/plugins/woocommerce-admin/client/wp-admin-scripts/onboarding-tax-notice/index.js +++ b/plugins/woocommerce-admin/client/wp-admin-scripts/onboarding-tax-notice/index.js @@ -41,7 +41,7 @@ const showTaxCompletionNotice = () => { saveButton.classList.add( 'has-tax' ); dispatch( 'core/notices' ).createSuccessNotice( - __( "You've added your first tax rate!", 'woocommerce' ), + __( 'You’ve added your first tax rate!', 'woocommerce' ), { id: 'WOOCOMMERCE_ONBOARDING_TAX_NOTICE', actions: [ diff --git a/plugins/woocommerce-admin/client/wp-admin-scripts/print-shipping-label-banner/shipping-banner/index.js b/plugins/woocommerce-admin/client/wp-admin-scripts/print-shipping-label-banner/shipping-banner/index.js index 4a41285e9cd..1a29de7cc3e 100644 --- a/plugins/woocommerce-admin/client/wp-admin-scripts/print-shipping-label-banner/shipping-banner/index.js +++ b/plugins/woocommerce-admin/client/wp-admin-scripts/print-shipping-label-banner/shipping-banner/index.js @@ -1,17 +1,17 @@ /** * External dependencies */ -import { __ } from '@wordpress/i18n'; +import { __, sprintf } from '@wordpress/i18n'; import { Component } from '@wordpress/element'; import { Button, ExternalLink } from '@wordpress/components'; import { compose } from '@wordpress/compose'; import interpolateComponents from '@automattic/interpolate-components'; import PropTypes from 'prop-types'; -import { get, isArray } from 'lodash'; import { PLUGINS_STORE_NAME } from '@woocommerce/data'; import { withDispatch, withSelect } from '@wordpress/data'; import { recordEvent } from '@woocommerce/tracks'; -import { getSetting } from '@woocommerce/settings'; +import { getSetting, getAdminLink } from '@woocommerce/settings'; +import { Link } from '@woocommerce/components'; /** * Internal dependencies @@ -19,29 +19,28 @@ import { getSetting } from '@woocommerce/settings'; import '../style.scss'; import DismissModal from '../dismiss-modal'; import SetupNotice, { setupErrorTypes } from '../setup-notice'; -import { getWcsAssets, acceptWcsTos } from '../wcs-api'; +import { + getWcsAssets, + acceptWcsTos, + getWcsLabelPurchaseConfigs, +} from '../wcs-api'; const wcAssetUrl = getSetting( 'wcAssetUrl', '' ); -const wcsPluginSlug = 'woocommerce-services'; +const wcShippingPluginSlug = 'woocommerce-shipping'; +const wcstPluginSlug = 'woocommerce-services'; export class ShippingBanner extends Component { constructor( props ) { super( props ); - const orderId = new URL( window.location.href ).searchParams.get( - 'post' - ); - this.state = { showShippingBanner: true, isDismissModalOpen: false, setupErrorReason: setupErrorTypes.SETUP, - orderId: parseInt( orderId, 10 ), wcsAssetsLoaded: false, wcsAssetsLoading: false, wcsSetupError: false, isShippingLabelButtonBusy: false, - installText: this.getInstallText(), isWcsModalOpen: false, }; } @@ -76,8 +75,8 @@ export class ShippingBanner extends Component { const { activePlugins } = this.props; this.setState( { isShippingLabelButtonBusy: true } ); this.trackElementClicked( 'shipping_banner_create_label' ); - if ( ! activePlugins.includes( wcsPluginSlug ) ) { - this.installAndActivatePlugins( wcsPluginSlug ); + if ( ! activePlugins.includes( wcShippingPluginSlug ) ) { + this.installAndActivatePlugins( wcShippingPluginSlug ); } else { this.acceptTosAndGetWCSAssets(); } @@ -85,7 +84,14 @@ export class ShippingBanner extends Component { async installAndActivatePlugins( pluginSlug ) { // Avoid double activating. - const { installPlugins, activatePlugins, isRequesting } = this.props; + const { + installPlugins, + activatePlugins, + isRequesting, + activePlugins, + isWcstCompatible, + isIncompatibleWCShippingInstalled, + } = this.props; if ( isRequesting ) { return false; } @@ -107,7 +113,25 @@ export class ShippingBanner extends Component { return; } - this.acceptTosAndGetWCSAssets(); + /** + * If a incompatible version of the WooCommerce Shipping plugin is installed, the necessary endpoints + * are not available, so we need to reload the page to ensure to make the plugin usable. + */ + if ( isIncompatibleWCShippingInstalled ) { + window.location.reload( true ); + return; + } + + if ( + ! activePlugins.includes( wcShippingPluginSlug ) && + isWcstCompatible + ) { + this.acceptTosAndGetWCSAssets(); + } else { + this.setState( { + showShippingBanner: false, + } ); + } } woocommerceServiceLinkClicked = () => { @@ -120,7 +144,7 @@ export class ShippingBanner extends Component { banner_name: 'wcadmin_install_wcs_prompt', jetpack_installed: activePlugins.includes( 'jetpack' ), jetpack_connected: isJetpackConnected, - wcs_installed: activePlugins.includes( wcsPluginSlug ), + wcs_installed: activePlugins.includes( wcShippingPluginSlug ), ...customProps, } ); }; @@ -135,16 +159,21 @@ export class ShippingBanner extends Component { } ); }; - acceptTosAndGetWCSAssets() { + acceptTosAndGetWCSAssets = () => { return acceptWcsTos() + .then( () => getWcsLabelPurchaseConfigs( this.props.orderId ) ) + .then( ( configs ) => { + window.WCShipping_Config = configs.config; + return configs; + } ) .then( () => getWcsAssets() ) .then( ( wcsAssets ) => this.loadWcsAssets( wcsAssets ) ) - .catch( () => this.setState( { wcsSetupError: true } ) ); - } + .catch( () => { + this.setState( { wcsSetupError: true } ); + } ); + }; generateMetaBoxHtml( nodeId, title, args ) { - const argsJsonString = JSON.stringify( args ).replace( /"/g, '"' ); // JS has no native html_entities so we just replace. - const togglePanelText = __( 'Toggle panel:', 'woocommerce' ); return ` @@ -159,8 +188,7 @@ export class ShippingBanner extends Component {
-
-
+
`; @@ -174,27 +202,24 @@ export class ShippingBanner extends Component { this.setState( { wcsAssetsLoading: true } ); - const jsPath = assets.wc_connect_admin_script; - const stylePath = assets.wc_connect_admin_style; + const labelPurchaseMetaboxId = 'woocommerce-order-label'; + const shipmentTrackingMetaboxId = 'woocommerce-order-shipment-tracking'; + const jsPath = assets.wcshipping_create_label_script; + const stylePath = assets.wcshipping_create_label_style; - if ( undefined === window.wcsPluginData ) { - const assetPath = jsPath.substring( - 0, - jsPath.lastIndexOf( '/' ) + 1 - ); - window.wcsPluginData = { assetPath }; - } + const shipmentTrackingJsPath = + assets.wcshipping_shipment_tracking_script; + const shipmentTrackingStylePath = + assets.wcshipping_shipment_tracking_style; - const { orderId } = this.state; - const { itemsCount } = this.props; + const { activePlugins } = this.props; + document.getElementById( labelPurchaseMetaboxId )?.remove(); const shippingLabelContainerHtml = this.generateMetaBoxHtml( - 'woocommerce-order-label', + labelPurchaseMetaboxId, __( 'Shipping Label', 'woocommerce' ), { - order: { id: orderId }, context: 'shipping_label', - items: itemsCount, } ); // Insert shipping label metabox just above main order details box. @@ -202,13 +227,12 @@ export class ShippingBanner extends Component { .getElementById( 'woocommerce-order-data' ) .insertAdjacentHTML( 'beforebegin', shippingLabelContainerHtml ); + document.getElementById( shipmentTrackingMetaboxId )?.remove(); const shipmentTrackingHtml = this.generateMetaBoxHtml( - 'woocommerce-order-shipment-tracking', + shipmentTrackingMetaboxId, __( 'Shipment Tracking', 'woocommerce' ), { - order: { id: orderId }, context: 'shipment_tracking', - items: itemsCount, } ); // Insert tracking metabox in the side after the order actions. @@ -224,6 +248,13 @@ export class ShippingBanner extends Component { window.jQuery( '#woocommerce-order-label' ).hide(); } + document + .querySelectorAll( 'script[src*="/woocommerce-services/"]' ) + .forEach( ( node ) => node.remove?.() ); + document + .querySelectorAll( 'link[href*="/woocommerce-services/"]' ) + .forEach( ( node ) => node.remove?.() ); + Promise.all( [ new Promise( ( resolve, reject ) => { const script = document.createElement( 'script' ); @@ -233,9 +264,16 @@ export class ShippingBanner extends Component { script.onerror = reject; document.body.appendChild( script ); } ), + new Promise( ( resolve, reject ) => { + const script = document.createElement( 'script' ); + script.src = shipmentTrackingJsPath; + script.async = true; + script.onload = resolve; + script.onerror = reject; + document.body.appendChild( script ); + } ), new Promise( ( resolve, reject ) => { if ( stylePath !== '' ) { - const head = document.getElementsByTagName( 'head' )[ 0 ]; const link = document.createElement( 'link' ); link.rel = 'stylesheet'; link.type = 'text/css'; @@ -243,7 +281,23 @@ export class ShippingBanner extends Component { link.media = 'all'; link.onload = resolve; link.onerror = reject; - head.appendChild( link ); + link.id = 'wcshipping-injected-styles'; + document.head.appendChild( link ); + } else { + resolve(); + } + } ), + new Promise( ( resolve, reject ) => { + if ( shipmentTrackingStylePath !== '' ) { + const link = document.createElement( 'link' ); + link.rel = 'stylesheet'; + link.type = 'text/css'; + link.href = shipmentTrackingStylePath; + link.media = 'all'; + link.onload = resolve; + link.onerror = reject; + link.id = 'wcshipping-injected-styles'; + document.head.appendChild( link ); } else { resolve(); } @@ -254,133 +308,61 @@ export class ShippingBanner extends Component { wcsAssetsLoading: false, isShippingLabelButtonBusy: false, } ); - this.openWcsModal(); + + // Reshow the shipping label metabox. + if ( window.jQuery ) { + window.jQuery( '#woocommerce-order-label' ).show(); + } + + document.getElementById( + 'woocommerce-admin-print-label' + ).style.display = 'none'; + + /** + * We'll only get to this point if either WCS&T is not active or is active but compatible with WooCommerce Shipping + * so once we check if the WCS&T is not active, we can open the label purchase modal immediately. + */ + if ( ! activePlugins.includes( wcstPluginSlug ) ) { + this.openWcsModal(); + } } ); } - getInstallText = () => { - const { activePlugins } = this.props; - if ( activePlugins.includes( wcsPluginSlug ) ) { - // If WCS is active, then the only remaining step is to agree to the ToS. - return __( - 'You\'ve already installed WooCommerce Shipping. By clicking "Create shipping label", you agree to its {{tosLink}}Terms of Service{{/tosLink}}.', - 'woocommerce' - ); - } - return __( - 'By clicking "Create shipping label", {{wcsLink}}WooCommerce Shipping{{/wcsLink}} will be installed and you agree to its {{tosLink}}Terms of Service{{/tosLink}}.', - 'woocommerce' - ); - }; - openWcsModal() { - if ( window.wcsGetAppStoreAsync ) { - window - .wcsGetAppStoreAsync( 'wc-connect-create-shipping-label' ) - .then( ( wcsStore ) => { - const state = wcsStore.getState(); - const { orderId } = this.state; - const siteId = state.ui.selectedSiteId; + // Since the button is dynamically added, we need to wait for it to become selectable and then click it. - const wcsStoreUnsubscribe = wcsStore.subscribe( () => { - const latestState = wcsStore.getState(); + const buttonSelector = + '#woocommerce-shipping-shipping-label-shipping_label button'; + if ( window.MutationObserver ) { + const observer = new window.MutationObserver( + ( mutationsList, observing ) => { + const button = document.querySelector( buttonSelector ); + if ( button ) { + button.click(); + observing.disconnect(); + } + } + ); - const shippingLabelState = get( - latestState, - [ - 'extensions', - 'woocommerce', - 'woocommerceServices', - siteId, - 'shippingLabel', - orderId, - ], - null - ); - - const labelSettingsState = get( - latestState, - [ - 'extensions', - 'woocommerce', - 'woocommerceServices', - siteId, - 'labelSettings', - ], - null - ); - - const packageState = get( - latestState, - [ - 'extensions', - 'woocommerce', - 'woocommerceServices', - siteId, - 'packages', - ], - null - ); - - const locationsState = get( latestState, [ - 'extensions', - 'woocommerce', - 'sites', - siteId, - 'data', - 'locations', - ] ); - - if ( - shippingLabelState && - labelSettingsState && - labelSettingsState.meta && - packageState && - locationsState - ) { - if ( - shippingLabelState.loaded && - labelSettingsState.meta.isLoaded && - packageState.isLoaded && - isArray( locationsState ) && - ! this.state.isWcsModalOpen - ) { - if ( window.jQuery ) { - this.setState( { isWcsModalOpen: true } ); - window - .jQuery( - '.shipping-label__new-label-button' - ) - .click(); - } - wcsStore.dispatch( { - type: 'NOTICE_CREATE', - notice: { - duration: 10000, - status: 'is-success', - text: __( - 'Plugin installed and activated', - 'woocommerce' - ), - }, - } ); - } else if ( - shippingLabelState.showPurchaseDialog - ) { - wcsStoreUnsubscribe(); - if ( window.jQuery ) { - window - .jQuery( '#woocommerce-order-label' ) - .show(); - } - } - } - } ); - - document.getElementById( - 'woocommerce-admin-print-label' - ).style.display = 'none'; - } ); + observer.observe( + document.getElementById( + 'woocommerce-shipping-shipping-label-shipping_label' + ) ?? + document.getElementById( 'wpbody-content' ) ?? + document.body, + { + childList: true, + subtree: true, + } + ); + } else { + const interval = setInterval( () => { + const targetElement = document.querySelector( buttonSelector ); + if ( targetElement ) { + targetElement.click(); + clearInterval( interval ); + } + }, 300 ); } } @@ -390,10 +372,40 @@ export class ShippingBanner extends Component { showShippingBanner, isShippingLabelButtonBusy, } = this.state; + const { isWcstCompatible } = this.props; + if ( ! showShippingBanner && ! isWcstCompatible ) { + document + .getElementById( 'woocommerce-admin-print-label' ) + .classList.add( 'error' ); + + return ( +

+ + { interpolateComponents( { + mixedString: __( + 'Please {{pluginPageLink}}update{{/pluginPageLink}} the WooCommerce Shipping & Tax plugin to the latest version to ensure compatibility with WooCommerce Shipping.', + 'woocommerce' + ), + components: { + pluginPageLink: ( + + ), + }, + } ) } + +

+ ); + } + if ( ! showShippingBanner ) { return null; } + const { actionButtonLabel, headline } = this.props; return (
@@ -403,15 +415,17 @@ export class ShippingBanner extends Component { alt={ __( 'Shipping ', 'woocommerce' ) } />
-

- { __( - 'Print discounted shipping labels with a click.', - 'woocommerce' - ) } -

+

{ headline }

{ interpolateComponents( { - mixedString: this.state.installText, + mixedString: sprintf( + // translators: %s is the action button label. + __( + 'By clicking "%s", {{wcsLink}}WooCommerce Shipping{{/wcsLink}} will be installed and you agree to its {{tosLink}}Terms of Service{{/tosLink}}.', + 'woocommerce' + ), + actionButtonLabel + ), components: { tosLink: ( - { __( 'Create shipping label', 'woocommerce' ) } + { actionButtonLabel } + ); +}; + +const LinkedProductPopoverContent: React.FC< { + query: ProductCollectionQuery; + setAttributes: ProductCollectionSetAttributes; + setIsDropdownOpen: React.Dispatch< React.SetStateAction< boolean > >; +} > = ( { query, setAttributes, setIsDropdownOpen } ) => ( + { + const productId = value[ 0 ]?.id ?? null; + if ( productId !== null ) { + setAttributes( { + query: { + ...query, + productReference: productId, + }, + } ); + setIsDropdownOpen( false ); + } + } } + messages={ { + search: __( 'Select a product', 'woocommerce' ), + } } + /> +); + +const LinkedProductControl = ( { + query, + setAttributes, + location, + usesReference, +}: { + query: ProductCollectionQuery; + setAttributes: ProductCollectionSetAttributes; + location: WooCommerceBlockLocation; + usesReference: string[] | undefined; +} ) => { + const [ isDropdownOpen, setIsDropdownOpen ] = useState< boolean >( false ); + const { product, isLoading } = useGetProduct( query.productReference ); + + const showLinkedProductControl = useMemo( () => { + const isInRequiredLocation = usesReference?.includes( location.type ); + const isProductContextRequired = usesReference?.includes( 'product' ); + const isProductContextSelected = + ( query?.productReference ?? null ) !== null; + + return ( + isProductContextRequired && + ! isInRequiredLocation && + isProductContextSelected + ); + }, [ location.type, query?.productReference, usesReference ] ); + + if ( ! showLinkedProductControl ) return null; + + return ( + + + ( + + ) } + renderContent={ () => ( + + ) } + open={ isDropdownOpen } + onToggle={ () => setIsDropdownOpen( ! isDropdownOpen ) } + /> + + + ); +}; + +export default LinkedProductControl; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/product-collection-content.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/product-collection-content.tsx index dadbddb7751..35714946c42 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/product-collection-content.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/edit/product-collection-content.tsx @@ -10,7 +10,6 @@ import { useInstanceId } from '@wordpress/compose'; import { useEffect, useRef, useMemo } from '@wordpress/element'; import { Button } from '@wordpress/components'; import { useSelect } from '@wordpress/data'; -import { useGetLocation } from '@woocommerce/blocks/product-template/utils'; import fastDeepEqual from 'fast-deep-equal/es6'; /** @@ -68,19 +67,23 @@ const useQueryId = ( const ProductCollectionContent = ( { preview: { setPreviewState, initialPreviewState } = {}, - usesReference, ...props }: ProductCollectionEditComponentProps ) => { const isInitialAttributesSet = useRef( false ); - const { clientId, attributes, setAttributes } = props; - const location = useGetLocation( props.context, props.clientId ); + const { + clientId, + attributes, + setAttributes, + location, + isUsingReferencePreviewMode, + } = props; useSetPreviewState( { setPreviewState, setAttributes, location, attributes, - usesReference, + isUsingReferencePreviewMode, } ); const blockProps = useBlockProps(); diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/types.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/types.ts index 4407c682abe..55a8ee9b460 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/types.ts +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/types.ts @@ -9,6 +9,16 @@ import { type AttributeMetadata } from '@woocommerce/types'; */ import { WooCommerceBlockLocation } from '../product-template/utils'; +export enum ProductCollectionUIStatesInEditor { + COLLECTION_PICKER = 'collection_chooser', + PRODUCT_REFERENCE_PICKER = 'product_context_picker', + VALID_WITH_PREVIEW = 'uses_reference_preview_mode', + VALID = 'valid', + // Future states + // INVALID = 'invalid', + // DELETED_PRODUCT_REFERENCE = 'deleted_product_reference', +} + export interface ProductCollectionAttributes { query: ProductCollectionQuery; queryId: number; @@ -95,6 +105,7 @@ export interface ProductCollectionQuery { woocommerceHandPickedProducts: string[]; priceRange: undefined | PriceRange; filterable: boolean; + productReference?: number; } export type ProductCollectionEditComponentProps = @@ -108,6 +119,8 @@ export type ProductCollectionEditComponentProps = context: { templateSlug: string; }; + isUsingReferencePreviewMode: boolean; + location: WooCommerceBlockLocation; }; export type TProductCollectionOrder = 'asc' | 'desc'; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/utils.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/utils.tsx index bdbd882e88a..0565027bfe1 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/utils.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/utils.tsx @@ -6,8 +6,10 @@ import { addFilter } from '@wordpress/hooks'; import { select } from '@wordpress/data'; import { isWpVersion } from '@woocommerce/settings'; import type { BlockEditProps, Block } from '@wordpress/blocks'; -import { useLayoutEffect } from '@wordpress/element'; +import { useEffect, useLayoutEffect, useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; +import type { ProductResponseItem } from '@woocommerce/types'; +import { getProduct } from '@woocommerce/editor-components/utils'; import { createBlock, // @ts-expect-error Type definitions for this function are missing in Guteberg @@ -18,13 +20,14 @@ import { * Internal dependencies */ import { - type ProductCollectionAttributes, - type TProductCollectionOrder, - type TProductCollectionOrderBy, - type ProductCollectionQuery, - type ProductCollectionDisplayLayout, - type PreviewState, - type SetPreviewState, + ProductCollectionAttributes, + TProductCollectionOrder, + TProductCollectionOrderBy, + ProductCollectionQuery, + ProductCollectionDisplayLayout, + PreviewState, + SetPreviewState, + ProductCollectionUIStatesInEditor, } from './types'; import { coreQueryPaginationBlockName, @@ -166,41 +169,14 @@ export const addProductCollectionToQueryPaginationParentOrAncestor = () => { }; /** - * Get the preview message for the Product Collection block based on the usesReference. - * There are two scenarios: - * 1. When usesReference is product, the preview message will be: - * "Actual products will vary depending on the product being viewed." - * 2. For all other usesReference, the preview message will be: - * "Actual products will vary depending on the page being viewed." - * - * This message will be shown when the usesReference isn't available on the Editor side, but is available on the Frontend. + * Get the message to show in the preview label when the block is in preview mode based + * on the `usesReference` value. */ export const getUsesReferencePreviewMessage = ( location: WooCommerceBlockLocation, - usesReference?: string[] + isUsingReferencePreviewMode: boolean ) => { - if ( ! ( Array.isArray( usesReference ) && usesReference.length > 0 ) ) { - return ''; - } - - if ( usesReference.includes( location.type ) ) { - /** - * Block shouldn't be in preview mode when: - * 1. Current location is archive and termId is available. - * 2. Current location is product and productId is available. - * - * Because in these cases, we have required context on the editor side. - */ - const isArchiveLocationWithTermId = - location.type === LocationType.Archive && - ( location.sourceData?.termId ?? null ) !== null; - const isProductLocationWithProductId = - location.type === LocationType.Product && - ( location.sourceData?.productId ?? null ) !== null; - if ( isArchiveLocationWithTermId || isProductLocationWithProductId ) { - return ''; - } - + if ( isUsingReferencePreviewMode ) { if ( location.type === LocationType.Product ) { return __( 'Actual products will vary depending on the product being viewed.', @@ -217,12 +193,77 @@ export const getUsesReferencePreviewMessage = ( return ''; }; +export const getProductCollectionUIStateInEditor = ( { + location, + usesReference, + attributes, + hasInnerBlocks, +}: { + location: WooCommerceBlockLocation; + usesReference?: string[] | undefined; + attributes: ProductCollectionAttributes; + hasInnerBlocks: boolean; +} ): ProductCollectionUIStatesInEditor => { + const isInRequiredLocation = usesReference?.includes( location.type ); + const isCollectionSelected = !! attributes.collection; + + /** + * Case 1: Product context picker + */ + const isProductContextRequired = usesReference?.includes( 'product' ); + const isProductContextSelected = + ( attributes.query?.productReference ?? null ) !== null; + if ( + isCollectionSelected && + isProductContextRequired && + ! isInRequiredLocation && + ! isProductContextSelected + ) { + return ProductCollectionUIStatesInEditor.PRODUCT_REFERENCE_PICKER; + } + + /** + * Case 2: Preview mode - based on `usesReference` value + */ + if ( isInRequiredLocation ) { + /** + * Block shouldn't be in preview mode when: + * 1. Current location is archive and termId is available. + * 2. Current location is product and productId is available. + * + * Because in these cases, we have required context on the editor side. + */ + const isArchiveLocationWithTermId = + location.type === LocationType.Archive && + ( location.sourceData?.termId ?? null ) !== null; + const isProductLocationWithProductId = + location.type === LocationType.Product && + ( location.sourceData?.productId ?? null ) !== null; + + if ( + ! isArchiveLocationWithTermId && + ! isProductLocationWithProductId + ) { + return ProductCollectionUIStatesInEditor.VALID_WITH_PREVIEW; + } + } + + /** + * Case 3: Collection chooser + */ + if ( ! hasInnerBlocks && ! isCollectionSelected ) { + return ProductCollectionUIStatesInEditor.COLLECTION_PICKER; + } + + return ProductCollectionUIStatesInEditor.VALID; +}; + export const useSetPreviewState = ( { setPreviewState, location, attributes, setAttributes, - usesReference, + isUsingReferencePreviewMode, }: { setPreviewState?: SetPreviewState | undefined; location: WooCommerceBlockLocation; @@ -231,6 +272,7 @@ export const useSetPreviewState = ( { attributes: Partial< ProductCollectionAttributes > ) => void; usesReference?: string[] | undefined; + isUsingReferencePreviewMode: boolean; } ) => { const setState = ( newPreviewState: PreviewState ) => { setAttributes( { @@ -240,8 +282,6 @@ export const useSetPreviewState = ( { }, } ); }; - const isCollectionUsesReference = - usesReference && usesReference?.length > 0; /** * When usesReference is available on Frontend but not on Editor side, @@ -249,10 +289,10 @@ export const useSetPreviewState = ( { */ const usesReferencePreviewMessage = getUsesReferencePreviewMessage( location, - usesReference + isUsingReferencePreviewMode ); useLayoutEffect( () => { - if ( isCollectionUsesReference ) { + if ( isUsingReferencePreviewMode ) { setAttributes( { __privatePreviewState: { isPreview: usesReferencePreviewMessage.length > 0, @@ -263,12 +303,12 @@ export const useSetPreviewState = ( { }, [ setAttributes, usesReferencePreviewMessage, - isCollectionUsesReference, + isUsingReferencePreviewMode, ] ); // Running setPreviewState function provided by Collection, if it exists. useLayoutEffect( () => { - if ( ! setPreviewState && ! isCollectionUsesReference ) { + if ( ! setPreviewState && ! isUsingReferencePreviewMode ) { return; } @@ -294,11 +334,14 @@ export const useSetPreviewState = ( { * - Products by tag * - Products by attribute */ + const termId = + location.type === LocationType.Archive + ? location.sourceData?.termId + : null; useLayoutEffect( () => { - if ( ! setPreviewState && ! isCollectionUsesReference ) { + if ( ! setPreviewState && ! isUsingReferencePreviewMode ) { const isGenericArchiveTemplate = - location.type === LocationType.Archive && - location.sourceData?.termId === null; + location.type === LocationType.Archive && termId === null; setAttributes( { __privatePreviewState: { @@ -315,11 +358,11 @@ export const useSetPreviewState = ( { }, [ attributes?.query?.inherit, usesReferencePreviewMessage, - location.sourceData?.termId, + termId, location.type, setAttributes, setPreviewState, - isCollectionUsesReference, + isUsingReferencePreviewMode, ] ); }; @@ -356,3 +399,35 @@ export const getDefaultProductCollection = () => }, createBlocksFromInnerBlocksTemplate( INNER_BLOCKS_TEMPLATE ) ); + +export const useGetProduct = ( productId: number | undefined ) => { + const [ product, setProduct ] = useState< ProductResponseItem | null >( + null + ); + const [ isLoading, setIsLoading ] = useState< boolean >( false ); + + useEffect( () => { + const fetchProduct = async () => { + if ( productId ) { + setIsLoading( true ); + try { + const fetchedProduct = ( await getProduct( + productId + ) ) as ProductResponseItem; + setProduct( fetchedProduct ); + } catch ( error ) { + setProduct( null ); + } finally { + setIsLoading( false ); + } + } else { + setProduct( null ); + setIsLoading( false ); + } + }; + + fetchProduct(); + }, [ productId ] ); + + return { product, isLoading }; +}; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/price-filter/frontend.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/price-filter/frontend.ts deleted file mode 100644 index 90a97d5b58a..00000000000 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/price-filter/frontend.ts +++ /dev/null @@ -1,130 +0,0 @@ -/** - * External dependencies - */ -import { store, getContext, getElement } from '@woocommerce/interactivity'; -import { formatPrice, getCurrency } from '@woocommerce/price-format'; -import { HTMLElementEvent } from '@woocommerce/types'; - -/** - * Internal dependencies - */ -import { navigate } from '../../frontend'; -import type { PriceFilterContext, PriceFilterStore } from './types'; - -const getUrl = ( context: PriceFilterContext ) => { - const { minPrice, maxPrice, minRange, maxRange } = context; - const url = new URL( window.location.href ); - const { searchParams } = url; - - if ( minPrice > minRange ) { - searchParams.set( 'min_price', minPrice.toString() ); - } else { - searchParams.delete( 'min_price' ); - } - - if ( maxPrice < maxRange && maxPrice > minRange ) { - searchParams.set( 'max_price', maxPrice.toString() ); - } else { - searchParams.delete( 'max_price' ); - } - - searchParams.forEach( ( _, key ) => { - if ( /query-[0-9]+-page/.test( key ) ) searchParams.delete( key ); - } ); - - return url.href; -}; - -store< PriceFilterStore >( 'woocommerce/product-filter-price', { - state: { - rangeStyle: () => { - const { minPrice, maxPrice, minRange, maxRange } = - getContext< PriceFilterContext >(); - - return [ - `--low: ${ - ( 100 * ( minPrice - minRange ) ) / ( maxRange - minRange ) - }%`, - `--high: ${ - ( 100 * ( maxPrice - minRange ) ) / ( maxRange - minRange ) - }%`, - ].join( ';' ); - }, - formattedMinPrice: () => { - const { minPrice } = getContext< PriceFilterContext >(); - return formatPrice( minPrice, getCurrency( { minorUnit: 0 } ) ); - }, - formattedMaxPrice: () => { - const { maxPrice } = getContext< PriceFilterContext >(); - return formatPrice( maxPrice, getCurrency( { minorUnit: 0 } ) ); - }, - }, - actions: { - updateProducts: ( event: HTMLElementEvent< HTMLInputElement > ) => { - const { decimalSeparator } = getCurrency(); - const context = getContext< PriceFilterContext >(); - const { minRange, minPrice, maxPrice, maxRange } = context; - const type = event.target.name; - const value = parseInt( - event.target.value - .replace( - new RegExp( `[^0-9\\${ decimalSeparator }]+`, 'g' ), - '' - ) - .replace( - new RegExp( `\\${ decimalSeparator }`, 'g' ), - '.' - ), - 10 - ); - - const currentMinPrice = - type === 'min' - ? Math.min( - Number.isNaN( value ) ? minRange : value, - maxPrice - ) - : minPrice; - const currentMaxPrice = - type === 'max' - ? Math.max( - Number.isNaN( value ) ? maxRange : value, - minPrice - ) - : maxPrice; - - // In some occasions the input element is updated with the incorrect value. - // By using the element that triggered the event, we can ensure the correct value is used for the input. - const element = getElement(); - if ( type === 'min' ) { - element.ref.value = currentMinPrice; - } - - if ( type === 'max' ) { - element.ref.value = currentMaxPrice; - } - - context.minPrice = currentMinPrice; - context.maxPrice = currentMaxPrice; - - navigate( - getUrl( { - minRange, - maxRange, - minPrice: currentMinPrice, - maxPrice: currentMaxPrice, - } ) - ); - }, - reset: () => { - navigate( - getUrl( { - minRange: 0, - maxRange: 0, - minPrice: 0, - maxPrice: 0, - } ) - ); - }, - }, -} ); diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filters/block.json b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/block.json index ead6774c95c..ec5bd9b5d3c 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filters/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/block.json @@ -1,12 +1,14 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-filters", "version": "1.0.0", "title": "Product Filters (Experimental)", "description": "Let shoppers filter products displayed on the page.", "category": "woocommerce", - "keywords": [ "WooCommerce" ], + "keywords": [ + "WooCommerce" + ], "supports": { "align": true, "__experimentalBorder": { diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/active-filters/block.json b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/block.json similarity index 77% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/active-filters/block.json rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/block.json index 045349fe65e..6d002248202 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/active-filters/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/block.json @@ -5,10 +5,14 @@ "title": "Filter Options", "description": "Display the currently active filters.", "category": "woocommerce", - "keywords": [ "WooCommerce" ], + "keywords": [ + "WooCommerce" + ], "textdomain": "woocommerce", - "apiVersion": 2, - "ancestor": [ "woocommerce/product-filter" ], + "apiVersion": 3, + "ancestor": [ + "woocommerce/product-filter" + ], "supports": { "interactivity": true, "inserter": false, @@ -17,11 +21,13 @@ "background": false } }, - "usesContext": [ "queryId" ], + "usesContext": [ + "queryId" + ], "attributes": { "displayStyle": { "type": "string", "default": "list" } } -} +} \ No newline at end of file diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/active-filters/components/inspector.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/components/inspector.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/active-filters/components/inspector.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/components/inspector.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/active-filters/components/removable-list-item.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/components/removable-list-item.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/active-filters/components/removable-list-item.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/components/removable-list-item.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/active-filters/edit.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/edit.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/active-filters/edit.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/edit.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/active-filters/frontend.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/frontend.ts similarity index 90% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/active-filters/frontend.ts rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/frontend.ts index 69cab9093e1..0b462692f15 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/active-filters/frontend.ts +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/frontend.ts @@ -6,7 +6,7 @@ import { store, getContext } from '@woocommerce/interactivity'; /** * Internal dependencies */ -import { navigate } from '../../frontend'; +import { navigate } from '../product-filter/frontend'; type ActiveFiltersContext = { queryId: number; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/active-filters/index.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/index.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/active-filters/index.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/index.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/active-filters/style.scss b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/style.scss similarity index 98% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/active-filters/style.scss rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/style.scss index 008c83df852..46021c156c4 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/active-filters/style.scss +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/style.scss @@ -1,7 +1,6 @@ @import "../../../../../../packages/components/chip/style"; .wp-block-woocommerce-product-filter-active { - margin-bottom: $gap-large; overflow: hidden; .clear-all { diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/active-filters/types.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/types.ts similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/active-filters/types.ts rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/active-filters/types.ts diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/block.json b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/block.json similarity index 92% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/block.json rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/block.json index 6a497c20a33..2cc947376f2 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/block.json @@ -5,10 +5,14 @@ "title": "Filter Options", "description": "Enable customers to filter the product grid by selecting one or more attributes, such as color.", "category": "woocommerce", - "keywords": [ "WooCommerce" ], + "keywords": [ + "WooCommerce" + ], "textdomain": "woocommerce", - "apiVersion": 2, - "ancestor": [ "woocommerce/product-filter" ], + "apiVersion": 3, + "ancestor": [ + "woocommerce/product-filter" + ], "supports": { "interactivity": true, "inserter": false, @@ -55,7 +59,10 @@ } } }, - "usesContext": [ "query", "queryId" ], + "usesContext": [ + "query", + "queryId" + ], "attributes": { "attributeId": { "type": "number", @@ -94,4 +101,4 @@ "default":true } } -} +} \ No newline at end of file diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/components/attribute-dropdown.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/components/attribute-dropdown.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/components/attribute-dropdown.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/components/attribute-dropdown.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/components/attribute-select-controls.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/components/attribute-select-controls.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/components/attribute-select-controls.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/components/attribute-select-controls.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/components/checkbox-list-editor.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/components/checkbox-list-editor.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/components/checkbox-list-editor.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/components/checkbox-list-editor.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/components/inspector.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/components/inspector.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/components/inspector.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/components/inspector.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/components/placeholder.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/components/placeholder.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/components/placeholder.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/components/placeholder.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/constants.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/constants.ts similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/constants.ts rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/constants.ts diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/edit.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/edit.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/edit.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/edit.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/frontend.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/frontend.ts similarity index 98% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/frontend.ts rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/frontend.ts index 9c0300a8dd6..0f9eb935586 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/frontend.ts +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/frontend.ts @@ -8,7 +8,7 @@ import { HTMLElementEvent } from '@woocommerce/types'; /** * Internal dependencies */ -import { navigate } from '../../frontend'; +import { navigate } from '../product-filter/frontend'; type AttributeFilterContext = { attributeSlug: string; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/index.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/index.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/index.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/index.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/style.scss b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/style.scss similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/style.scss rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/style.scss diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/types.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/types.ts similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/types.ts rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/types.ts diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/utils.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/utils.ts similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/attribute-filter/utils.ts rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/utils.ts diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/clear-button/block.json b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/clear-button/block.json similarity index 73% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/clear-button/block.json rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/clear-button/block.json index afbd38c530e..091659a2495 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/clear-button/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/clear-button/block.json @@ -5,12 +5,17 @@ "title": "Clear (Experimental)", "description": "Allows shoppers to reset this filter.", "category": "woocommerce", - "keywords": [ "WooCommerce", "reset filter" ], + "keywords": [ + "WooCommerce", + "reset filter" + ], "textdomain": "woocommerce", - "apiVersion": 2, - "ancestor": [ "woocommerce/product-filter" ], + "apiVersion": 3, + "ancestor": [ + "woocommerce/product-filter" + ], "supports": { "interactivity": true, "inserter": false } -} +} \ No newline at end of file diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/clear-button/edit.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/clear-button/edit.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/clear-button/edit.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/clear-button/edit.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/clear-button/frontend.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/clear-button/frontend.ts similarity index 96% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/clear-button/frontend.ts rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/clear-button/frontend.ts index 7f1164ddf2d..0bfc299b660 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/clear-button/frontend.ts +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/clear-button/frontend.ts @@ -6,7 +6,7 @@ import { store, getContext } from '@woocommerce/interactivity'; /** * Internal dependencies */ -import { navigate } from '../../frontend'; +import { navigate } from '../product-filter/frontend'; const getQueryParams = ( e: Event ) => { const filterNavContainer = ( e.target as HTMLElement )?.closest( diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/clear-button/index.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/clear-button/index.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/clear-button/index.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/clear-button/index.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/clear-button/save.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/clear-button/save.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/clear-button/save.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/clear-button/save.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/components/preview-dropdown/index.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/components/preview-dropdown/index.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/components/preview-dropdown/index.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/components/preview-dropdown/index.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay-navigation/block-variations.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay-navigation/block-variations.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay-navigation/block-variations.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay-navigation/block-variations.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay-navigation/block.json b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay-navigation/block.json similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay-navigation/block.json rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay-navigation/block.json diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay-navigation/edit.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay-navigation/edit.tsx similarity index 97% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay-navigation/edit.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay-navigation/edit.tsx index 58bec7471d0..43e026f95cf 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay-navigation/edit.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay-navigation/edit.tsx @@ -16,8 +16,8 @@ import type { BlockContext, BlockVariationTriggerType, } from './types'; -import { default as productFiltersIcon } from '../product-filters/icon'; -import { BlockOverlayAttribute as ProductFiltersBlockOverlayAttribute } from '../product-filters/constants'; +import { default as productFiltersIcon } from '../../icon'; +import { BlockOverlayAttribute as ProductFiltersBlockOverlayAttribute } from '../../constants'; import './editor.scss'; import { Inspector } from './inspector-controls'; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay-navigation/editor.scss b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay-navigation/editor.scss similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay-navigation/editor.scss rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay-navigation/editor.scss diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay-navigation/index.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay-navigation/index.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay-navigation/index.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay-navigation/index.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay-navigation/inspector-controls.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay-navigation/inspector-controls.tsx similarity index 98% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay-navigation/inspector-controls.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay-navigation/inspector-controls.tsx index 16c7352cfdf..7c439cd1ac3 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay-navigation/inspector-controls.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay-navigation/inspector-controls.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @wordpress/no-unsafe-wp-apis */ /** * External dependencies */ diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay-navigation/save.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay-navigation/save.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay-navigation/save.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay-navigation/save.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay-navigation/style.scss b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay-navigation/style.scss similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay-navigation/style.scss rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay-navigation/style.scss diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay-navigation/types.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay-navigation/types.ts similarity index 96% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay-navigation/types.ts rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay-navigation/types.ts index 70d465198e2..e5694e1a323 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay-navigation/types.ts +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay-navigation/types.ts @@ -1,7 +1,7 @@ /** * Internal dependencies */ -import { BlockOverlayAttributeOptions as ProductFiltersBlockOverlayAttributeOptions } from '../product-filters/types'; +import { BlockOverlayAttributeOptions as ProductFiltersBlockOverlayAttributeOptions } from '../../types'; type BorderRadius = { bottomLeft: string; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay/block.json b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay/block.json similarity index 94% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay/block.json rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay/block.json index a2241e3d294..6278e472b09 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay/block.json @@ -1,12 +1,14 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-filters-overlay", "version": "1.0.0", "title": "Product Filters Overlay (Experimental)", "description": "Display product filters in an overlay on top of a page.", "category": "woocommerce", - "keywords": [ "WooCommerce" ], + "keywords": [ + "WooCommerce" + ], "supports": { "align": true, "multiple": false, @@ -48,4 +50,4 @@ } }, "example": {} -} +} \ No newline at end of file diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay/edit.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay/edit.tsx similarity index 98% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay/edit.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay/edit.tsx index aa1f24cbbec..ed16b732af2 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay/edit.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay/edit.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @wordpress/no-unsafe-wp-apis */ /** * External dependencies */ diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay/icon.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay/icon.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay/icon.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay/icon.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay/index.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay/index.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay/index.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay/index.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay/save.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay/save.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay/save.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay/save.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay/settings.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay/settings.ts similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay/settings.ts rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay/settings.ts diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay/types.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay/types.ts similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filters-overlay/types.ts rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/overlay/types.ts diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/price-filter/block.json b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/block.json similarity index 77% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/price-filter/block.json rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/block.json index da2b28598c8..326a9c45ada 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/price-filter/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/block.json @@ -5,15 +5,22 @@ "title": "Filter Options", "description": "Enable customers to filter the product collection by choosing a price range.", "category": "woocommerce", - "keywords": [ "WooCommerce" ], + "keywords": [ + "WooCommerce" + ], "textdomain": "woocommerce", - "apiVersion": 2, - "ancestor": [ "woocommerce/product-filter" ], + "apiVersion": 3, + "ancestor": [ + "woocommerce/product-filter" + ], "supports": { "interactivity": true, "inserter": false }, - "usesContext": [ "query", "queryId" ], + "usesContext": [ + "query", + "queryId" + ], "attributes": { "showInputFields": { "type": "boolean", @@ -24,4 +31,4 @@ "default": false } } -} +} \ No newline at end of file diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/price-filter/components/inspector.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/components/inspector.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/price-filter/components/inspector.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/components/inspector.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/price-filter/components/price-slider.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/components/price-slider.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/price-filter/components/price-slider.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/components/price-slider.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/price-filter/edit.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/edit.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/price-filter/edit.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/edit.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/frontend.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/frontend.ts new file mode 100644 index 00000000000..844e3c364f6 --- /dev/null +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/frontend.ts @@ -0,0 +1,233 @@ +/** + * External dependencies + */ +import { store, getContext, getElement } from '@woocommerce/interactivity'; +import { formatPrice, getCurrency } from '@woocommerce/price-format'; +import { HTMLElementEvent } from '@woocommerce/types'; +import { debounce } from '@woocommerce/base-utils'; + +/** + * Internal dependencies + */ +import { navigate } from '../product-filter/frontend'; +import type { PriceFilterContext, PriceFilterStore } from './types'; + +const getUrl = ( context: PriceFilterContext ) => { + const { minPrice, maxPrice, minRange, maxRange } = context; + const url = new URL( window.location.href ); + const { searchParams } = url; + + if ( minPrice > minRange ) { + searchParams.set( 'min_price', minPrice.toString() ); + } else { + searchParams.delete( 'min_price' ); + } + + if ( maxPrice < maxRange && maxPrice > minRange ) { + searchParams.set( 'max_price', maxPrice.toString() ); + } else { + searchParams.delete( 'max_price' ); + } + + searchParams.forEach( ( _, key ) => { + if ( /query-[0-9]+-page/.test( key ) ) searchParams.delete( key ); + } ); + + return url.href; +}; + +const debounceUpdate = debounce( ( context, element, event ) => { + const { decimalSeparator } = getCurrency(); + const { minRange, minPrice, maxPrice, maxRange } = context; + const type = event.target.name; + const value = parseInt( + event.target.value + .replace( new RegExp( `[^0-9\\${ decimalSeparator }]+`, 'g' ), '' ) + .replace( new RegExp( `\\${ decimalSeparator }`, 'g' ), '.' ), + 10 + ); + + const currentMinPrice = + type === 'min' + ? Math.min( Number.isNaN( value ) ? minRange : value, maxPrice ) + : minPrice; + + const currentMaxPrice = + type === 'max' + ? Math.max( Number.isNaN( value ) ? maxRange : value, minPrice ) + : maxPrice; + + if ( type === 'min' ) { + element.ref.value = currentMinPrice; + } else if ( type === 'max' ) { + element.ref.value = currentMaxPrice; + } + + context.minPrice = currentMinPrice; + context.maxPrice = currentMaxPrice; + + navigate( + getUrl( { + minRange, + maxRange, + minPrice: currentMinPrice, + maxPrice: currentMaxPrice, + } ) + ); +}, 1000 ); + +const constrainRangeSliderValues = ( + /** + * Tuple containing min and max values. + */ + values: [ number, number ], + /** + * Min allowed value for the sliders. + */ + min?: number | null, + /** + * Max allowed value for the sliders. + */ + max?: number | null, + /** + * Step value for the sliders. + */ + step = 1, + /** + * Whether we're currently interacting with the min range slider or not, so we update the correct values. + */ + isMin = false +): { minPrice: number; maxPrice: number } => { + let [ minValue, maxValue ] = values; + + const isFinite = ( n: number | null | undefined ): n is number => + Number.isFinite( n ); + + if ( ! isFinite( minValue ) ) { + minValue = min || 0; + } + + if ( ! isFinite( maxValue ) ) { + maxValue = max || step; + } + + if ( isFinite( min ) && min > minValue ) { + minValue = min; + } + + if ( isFinite( max ) && max <= minValue ) { + minValue = max - step; + } + + if ( isFinite( min ) && min >= maxValue ) { + maxValue = min + step; + } + + if ( isFinite( max ) && max < maxValue ) { + maxValue = max; + } + + if ( ! isMin && minValue >= maxValue ) { + minValue = maxValue - step; + } + + if ( isMin && maxValue <= minValue ) { + maxValue = minValue + step; + } + + return { minPrice: minValue, maxPrice: maxValue }; +}; + +const getRangeStyle = ( + minPrice: number, + maxPrice: number, + minRange: number, + maxRange: number +) => { + return `--low: ${ + ( 100 * ( minPrice - minRange ) ) / ( maxRange - minRange ) + }%; --high: ${ + ( 100 * ( maxPrice - minRange ) ) / ( maxRange - minRange ) + }%;`; +}; + +store< PriceFilterStore >( 'woocommerce/product-filter-price', { + state: { + rangeStyle: () => { + const { minPrice, maxPrice, minRange, maxRange } = + getContext< PriceFilterContext >(); + + return getRangeStyle( minPrice, maxPrice, minRange, maxRange ); + }, + formattedMinPrice: () => { + const { minPrice } = getContext< PriceFilterContext >(); + return formatPrice( minPrice, getCurrency( { minorUnit: 0 } ) ); + }, + formattedMaxPrice: () => { + const { maxPrice } = getContext< PriceFilterContext >(); + return formatPrice( maxPrice, getCurrency( { minorUnit: 0 } ) ); + }, + }, + actions: { + updateProducts: ( event: HTMLElementEvent< HTMLInputElement > ) => { + const context = getContext< PriceFilterContext >(); + + // In some occasions the input element is updated with the incorrect value. + // By using the element that triggered the event, we can ensure the correct value is used for the input. + const element = getElement(); + + debounceUpdate( context, element, event ); + }, + selectInputContent: () => { + const element = getElement(); + if ( element && element.ref ) { + element.ref.select(); + } + }, + reset: () => { + navigate( + getUrl( { + minRange: 0, + maxRange: 0, + minPrice: 0, + maxPrice: 0, + } ) + ); + }, + updateRange: ( event: HTMLElementEvent< HTMLInputElement > ) => { + const context = getContext< PriceFilterContext >(); + const { minPrice, maxPrice, minRange, maxRange } = context; + const isMin = event.target.classList.contains( 'min' ); + const targetValue = Number( event.target.value ); + const stepValue = 1; + const currentValues: [ number, number ] = isMin + ? [ + Math.round( targetValue / stepValue ) * stepValue, + maxPrice, + ] + : [ + minPrice, + Math.round( targetValue / stepValue ) * stepValue, + ]; + const values = constrainRangeSliderValues( + currentValues, + minRange, + maxRange, + stepValue, + isMin + ); + + if ( targetValue >= maxPrice ) { + context.maxPrice = minPrice + 1; + } else if ( targetValue <= minPrice ) { + context.minPrice = maxPrice - 1; + } + + if ( isMin ) { + context.minPrice = values.minPrice; + } else { + context.maxPrice = values.maxPrice; + } + }, + }, +} ); diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/price-filter/index.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/index.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/price-filter/index.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/index.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/price-filter/style.scss b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/style.scss similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/price-filter/style.scss rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/style.scss diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/price-filter/types.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/types.ts similarity index 88% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/price-filter/types.ts rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/types.ts index 325aed671c4..c08e024e8c5 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/price-filter/types.ts +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/types.ts @@ -32,6 +32,8 @@ export type PriceFilterStore = { state: PriceFilterState; actions: { updateProducts: ( event: HTMLElementEvent< HTMLInputElement > ) => void; + selectInputContent: () => void; reset: () => void; + updateRange: ( event: HTMLElementEvent< HTMLInputElement > ) => void; }; }; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/price-filter/utils.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/utils.ts similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/price-filter/utils.ts rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/price-filter/utils.ts diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/block-variations.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/product-filter/block-variations.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/block-variations.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/product-filter/block-variations.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/block.json b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/product-filter/block.json similarity index 82% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/block.json rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/product-filter/block.json index 3cf41cf54aa..bd07a4b1563 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/product-filter/block.json @@ -4,15 +4,23 @@ "title": "Product Filter (Experimental)", "description": "A block that adds product filters to the product collection.", "category": "woocommerce", - "keywords": [ "WooCommerce", "Filters" ], + "keywords": [ + "WooCommerce", + "Filters" + ], "textdomain": "woocommerce", "supports": { "html": false, "reusable": false, "inserter": false }, - "ancestor": [ "woocommerce/product-filters" ], - "usesContext": [ "query", "queryId" ], + "ancestor": [ + "woocommerce/product-filters" + ], + "usesContext": [ + "query", + "queryId" + ], "attributes": { "filterType": { "type": "string" @@ -36,4 +44,4 @@ }, "apiVersion": 3, "$schema": "https://schemas.wp.org/trunk/block.json" -} +} \ No newline at end of file diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/components/warning.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/product-filter/components/warning.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/components/warning.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/product-filter/components/warning.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/constants.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/product-filter/constants.ts similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/constants.ts rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/product-filter/constants.ts diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/edit.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/product-filter/edit.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/edit.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/product-filter/edit.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/editor.scss b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/product-filter/editor.scss similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/editor.scss rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/product-filter/editor.scss diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/frontend.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/product-filter/frontend.ts similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/frontend.ts rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/product-filter/frontend.ts diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/index.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/product-filter/index.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/index.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/product-filter/index.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/save.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/product-filter/save.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/save.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/product-filter/save.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/types.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/product-filter/types.ts similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/types.ts rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/product-filter/types.ts diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/utils.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/product-filter/utils.ts similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/utils.ts rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/product-filter/utils.ts diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/rating-filter/block.json b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/block.json similarity index 83% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/rating-filter/block.json rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/block.json index a1f35fb4845..813f26f495a 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/rating-filter/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/block.json @@ -4,7 +4,9 @@ "title": "Filter Options", "description": "Enable customers to filter the product collection by rating.", "category": "woocommerce", - "keywords": [ "WooCommerce" ], + "keywords": [ + "WooCommerce" + ], "supports": { "interactivity": true, "inserter": false, @@ -13,8 +15,13 @@ "text": true } }, - "ancestor": [ "woocommerce/product-filter" ], - "usesContext": [ "query", "queryId" ], + "ancestor": [ + "woocommerce/product-filter" + ], + "usesContext": [ + "query", + "queryId" + ], "attributes": { "className": { "type": "string", @@ -38,6 +45,6 @@ } }, "textdomain": "woocommerce", - "apiVersion": 2, + "apiVersion": 3, "$schema": "https://schemas.wp.org/trunk/block.json" -} +} \ No newline at end of file diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/rating-filter/components/inspector.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/components/inspector.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/rating-filter/components/inspector.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/components/inspector.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/rating-filter/edit.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/edit.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/rating-filter/edit.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/edit.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/rating-filter/frontend.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/frontend.ts similarity index 97% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/rating-filter/frontend.ts rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/frontend.ts index 8d865131b33..3bd9f961704 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/rating-filter/frontend.ts +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/frontend.ts @@ -8,7 +8,7 @@ import { DropdownContext } from '@woocommerce/interactivity-components/dropdown' /** * Internal dependencies */ -import { navigate } from '../../frontend'; +import { navigate } from '../product-filter/frontend'; function getUrl( filters: Array< string | null > ) { filters = filters.filter( Boolean ); diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/rating-filter/index.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/index.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/rating-filter/index.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/index.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/rating-filter/preview.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/preview.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/rating-filter/preview.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/preview.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/rating-filter/style.scss b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/style.scss similarity index 92% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/rating-filter/style.scss rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/style.scss index ff6a707758e..05109040dcd 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/rating-filter/style.scss +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/style.scss @@ -1,4 +1,6 @@ .wp-block-woocommerce-product-filter-rating { + display: grid; + .wc-block-components-product-rating { margin-bottom: 0; display: flex; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/rating-filter/types.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/types.ts similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/rating-filter/types.ts rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/types.ts diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/rating-filter/utils.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/utils.ts similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/rating-filter/utils.ts rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/rating-filter/utils.ts diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/stock-filter/block.json b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/stock-filter/block.json similarity index 82% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/stock-filter/block.json rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/stock-filter/block.json index 384b0186e14..1b023dccbbe 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/stock-filter/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/stock-filter/block.json @@ -4,7 +4,11 @@ "title": "Filter Options", "description": "Enable customers to filter the product collection by stock status.", "category": "woocommerce", - "keywords": [ "WooCommerce", "filter", "stock" ], + "keywords": [ + "WooCommerce", + "filter", + "stock" + ], "supports": { "interactivity": true, "html": false, @@ -37,9 +41,14 @@ "default": false } }, - "usesContext": [ "query", "queryId" ], - "ancestor": [ "woocommerce/product-filter" ], + "usesContext": [ + "query", + "queryId" + ], + "ancestor": [ + "woocommerce/product-filter" + ], "textdomain": "woocommerce", - "apiVersion": 2, + "apiVersion": 3, "$schema": "https://schemas.wp.org/trunk/block.json" -} +} \ No newline at end of file diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/stock-filter/components/inspector.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/stock-filter/components/inspector.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/stock-filter/components/inspector.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/stock-filter/components/inspector.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/stock-filter/edit.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/stock-filter/edit.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/stock-filter/edit.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/stock-filter/edit.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/stock-filter/frontend.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/stock-filter/frontend.ts similarity index 98% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/stock-filter/frontend.ts rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/stock-filter/frontend.ts index f0037014671..9833ff753c1 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/stock-filter/frontend.ts +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/stock-filter/frontend.ts @@ -9,7 +9,7 @@ import { CheckboxListContext } from '@woocommerce/interactivity-components/check /** * Internal dependencies */ -import { navigate } from '../../frontend'; +import { navigate } from '../product-filter/frontend'; const getUrl = ( activeFilters: string ) => { const url = new URL( window.location.href ); diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/stock-filter/index.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/stock-filter/index.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/stock-filter/index.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/stock-filter/index.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/stock-filter/preview.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/stock-filter/preview.tsx similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/stock-filter/preview.tsx rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/stock-filter/preview.tsx diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/stock-filter/style.scss b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/stock-filter/style.scss similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/stock-filter/style.scss rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/stock-filter/style.scss diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/stock-filter/types.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/stock-filter/types.ts similarity index 100% rename from plugins/woocommerce-blocks/assets/js/blocks/product-filter/inner-blocks/stock-filter/types.ts rename to plugins/woocommerce-blocks/assets/js/blocks/product-filters/inner-blocks/stock-filter/types.ts diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-gallery/block.json b/plugins/woocommerce-blocks/assets/js/blocks/product-gallery/block.json index 7480b7784a2..ed40700ddd7 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-gallery/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-gallery/block.json @@ -1,19 +1,23 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "woocommerce/product-gallery", "version": "1.0.0", "title": "Product Gallery (Beta)", "description": "Showcase your products relevant images and media.", "category": "woocommerce", - "keywords": [ "WooCommerce" ], + "keywords": [ + "WooCommerce" + ], "supports": { "align": true, "multiple": false, "interactivity": true }, "textdomain": "woocommerce", - "usesContext": [ "postId" ], + "usesContext": [ + "postId" + ], "providesContext": { "thumbnailsPosition": "thumbnailsPosition", "thumbnailsNumberOfThumbnails": "thumbnailsNumberOfThumbnails", @@ -54,7 +58,7 @@ "type": "boolean", "default": true }, - "nextPreviousButtonsPosition":{ + "nextPreviousButtonsPosition": { "type": "string", "default": "insideTheImage" }, @@ -65,4 +69,4 @@ }, "viewScript": "wc-product-gallery-frontend", "example": {} -} +} \ No newline at end of file diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-large-image-next-previous/block.json b/plugins/woocommerce-blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-large-image-next-previous/block.json index 911c3f31f6d..fc9841a74d8 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-large-image-next-previous/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-large-image-next-previous/block.json @@ -1,14 +1,20 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, - "name": "woocommerce/product-gallery-large-image-next-previous", - "version": "1.0.0", - "title": "Next/Previous Buttons", - "description": "Display next and previous buttons.", - "category": "woocommerce", - "keywords": [ "WooCommerce" ], - "usesContext": [ "nextPreviousButtonsPosition", "productGalleryClientId", "postId"], - "textdomain": "woocommerce", + "apiVersion": 3, + "name": "woocommerce/product-gallery-large-image-next-previous", + "version": "1.0.0", + "title": "Next/Previous Buttons", + "description": "Display next and previous buttons.", + "category": "woocommerce", + "keywords": [ + "WooCommerce" + ], + "usesContext": [ + "nextPreviousButtonsPosition", + "productGalleryClientId", + "postId" + ], + "textdomain": "woocommerce", "supports": { "layout": { "default": { @@ -21,5 +27,7 @@ "allowInheriting": false } }, - "ancestor": [ "woocommerce/product-gallery-large-image" ] -} + "ancestor": [ + "woocommerce/product-gallery-large-image" + ] +} \ No newline at end of file diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-large-image/block.json b/plugins/woocommerce-blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-large-image/block.json index 56e5870d316..aba37b694a2 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-large-image/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-large-image/block.json @@ -1,16 +1,26 @@ { - "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 3, "name": "woocommerce/product-gallery-large-image", "version": "1.0.0", "title": "Large Image", "description": "Display the Large Image of a product.", "category": "woocommerce", - "keywords": [ "WooCommerce" ], - "usesContext": [ "nextPreviousButtonsPosition", "postId", "hoverZoom", "fullScreenOnClick", "cropImages"], - "supports": { - "interactivity": true - }, + "keywords": [ + "WooCommerce" + ], + "usesContext": [ + "nextPreviousButtonsPosition", + "postId", + "hoverZoom", + "fullScreenOnClick", + "cropImages" + ], + "supports": { + "interactivity": true + }, "textdomain": "woocommerce", - "ancestor": [ "woocommerce/product-gallery" ] -} + "ancestor": [ + "woocommerce/product-gallery" + ] +} \ No newline at end of file diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-pager/block.json b/plugins/woocommerce-blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-pager/block.json index b08b3c9294a..80d2ff36620 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-pager/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-pager/block.json @@ -1,13 +1,22 @@ { - "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 3, "name": "woocommerce/product-gallery-pager", "version": "1.0.0", "title": "Pager", "description": "Display the gallery pager.", "category": "woocommerce", - "keywords": [ "WooCommerce" ], + "keywords": [ + "WooCommerce" + ], "textdomain": "woocommerce", - "ancestor": [ "woocommerce/product-gallery" ], - "usesContext": [ "pagerDisplayMode", "productGalleryClientId", "thumbnailsNumberOfThumbnails", "postId" ] -} + "ancestor": [ + "woocommerce/product-gallery" + ], + "usesContext": [ + "pagerDisplayMode", + "productGalleryClientId", + "thumbnailsNumberOfThumbnails", + "postId" + ] +} \ No newline at end of file diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-thumbnails/block.json b/plugins/woocommerce-blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-thumbnails/block.json index 60f470aaef2..a23592c9b03 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-thumbnails/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-gallery/inner-blocks/product-gallery-thumbnails/block.json @@ -1,15 +1,26 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, - "name": "woocommerce/product-gallery-thumbnails", - "version": "1.0.0", - "title": "Thumbnails", - "description": "Display the Thumbnails of a product.", - "category": "woocommerce", - "keywords": [ "WooCommerce" ], - "usesContext": [ "postId", "thumbnailsPosition", "thumbnailsNumberOfThumbnails", "productGalleryClientId", "mode", "cropImages" ], - "textdomain": "woocommerce", - "ancestor": [ "woocommerce/product-gallery" ], + "apiVersion": 3, + "name": "woocommerce/product-gallery-thumbnails", + "version": "1.0.0", + "title": "Thumbnails", + "description": "Display the Thumbnails of a product.", + "category": "woocommerce", + "keywords": [ + "WooCommerce" + ], + "usesContext": [ + "postId", + "thumbnailsPosition", + "thumbnailsNumberOfThumbnails", + "productGalleryClientId", + "mode", + "cropImages" + ], + "textdomain": "woocommerce", + "ancestor": [ + "woocommerce/product-gallery" + ], "supports": { "spacing": { "margin": true, @@ -18,4 +29,4 @@ } } } -} +} \ No newline at end of file diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-new/block.json b/plugins/woocommerce-blocks/assets/js/blocks/product-new/block.json index fcb471f9d67..d0c71441363 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-new/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-new/block.json @@ -2,10 +2,15 @@ "name": "woocommerce/product-new", "title": "Newest Products", "category": "woocommerce", - "keywords": [ "WooCommerce" ], + "keywords": [ + "WooCommerce" + ], "description": "Display a grid of your newest products.", "supports": { - "align": [ "wide", "full" ], + "align": [ + "wide", + "full" + ], "html": false }, "attributes": { @@ -87,6 +92,6 @@ } }, "textdomain": "woocommerce", - "apiVersion": 2, + "apiVersion": 3, "$schema": "https://schemas.wp.org/trunk/block.json" -} +} \ No newline at end of file diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-on-sale/block.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-on-sale/block.tsx index a4735d50890..8f1ee05cc51 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-on-sale/block.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-on-sale/block.tsx @@ -6,6 +6,7 @@ import { Disabled, Placeholder } from '@wordpress/components'; import ServerSideRender from '@wordpress/server-side-render'; import { gridBlockPreview } from '@woocommerce/resource-previews'; import { Icon, percent } from '@wordpress/icons'; +import { useBlockProps } from '@wordpress/block-editor'; /** * Internal dependencies @@ -39,13 +40,14 @@ const ProductOnSaleBlock: React.FunctionComponent< Props > = ( props: Props ) => { const { attributes, setAttributes, name } = props; + const blockProps = useBlockProps(); if ( attributes.isPreview ) { - return gridBlockPreview; + return

{ gridBlockPreview }
; } return ( - <> +
= ( EmptyResponsePlaceholder={ EmptyPlaceholder } /> - +
); }; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-on-sale/index.js b/plugins/woocommerce-blocks/assets/js/blocks/product-on-sale/index.js index f93f70c3f13..d567b00c06a 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-on-sale/index.js +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-on-sale/index.js @@ -15,6 +15,7 @@ import sharedAttributes, { registerBlockType( 'woocommerce/product-on-sale', { title: __( 'On Sale Products', 'woocommerce' ), + apiVersion: 3, icon: { src: ( { registerBlockType( 'woocommerce/product-search', { title: __( 'Product Search', 'woocommerce' ), + apiVersion: 3, icon: { src: ( templateSlug === entitySlug; -export type WooCommerceBlockLocation = ReturnType< - typeof createLocationObject ->; +interface WooCommerceBaseLocation { + type: LocationType; + sourceData?: object | undefined; +} -const createLocationObject = ( - type: LocationType, - sourceData: Record< string, unknown > = {} -) => ( { - type, - sourceData, -} ); +interface ProductLocation extends WooCommerceBaseLocation { + type: LocationType.Product; + sourceData?: + | { + productId: number; + } + | undefined; +} + +interface ArchiveLocation extends WooCommerceBaseLocation { + type: LocationType.Archive; + sourceData?: + | { + taxonomy: string; + termId: number; + } + | undefined; +} + +interface CartLocation extends WooCommerceBaseLocation { + type: LocationType.Cart; + sourceData?: + | { + productIds: number[]; + } + | undefined; +} + +interface OrderLocation extends WooCommerceBaseLocation { + type: LocationType.Order; + sourceData?: + | { + orderId: number; + } + | undefined; +} + +interface SiteLocation extends WooCommerceBaseLocation { + type: LocationType.Site; + sourceData?: object | undefined; +} + +export type WooCommerceBlockLocation = + | ProductLocation + | ArchiveLocation + | CartLocation + | OrderLocation + | SiteLocation; + +const createLocationObject = ( type: LocationType, sourceData: object = {} ) => + ( { + type, + sourceData, + } as WooCommerceBlockLocation ); type ContextProperties = { templateSlug: string; @@ -83,7 +131,7 @@ type ContextProperties = { export const useGetLocation = < T, >( context: Context< T & ContextProperties >, clientId: string -) => { +): WooCommerceBlockLocation => { const templateSlug = context.templateSlug || ''; const postId = context.postId || null; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-top-rated/block.json b/plugins/woocommerce-blocks/assets/js/blocks/product-top-rated/block.json index fdf530fd32e..828aee084d6 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-top-rated/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-top-rated/block.json @@ -2,10 +2,15 @@ "name": "woocommerce/product-top-rated", "title": "Top Rated Products", "category": "woocommerce", - "keywords": [ "WooCommerce" ], + "keywords": [ + "WooCommerce" + ], "description": "Display a grid of your top rated products.", "supports": { - "align": [ "wide", "full" ], + "align": [ + "wide", + "full" + ], "html": false }, "attributes": { @@ -87,6 +92,6 @@ } }, "textdomain": "woocommerce", - "apiVersion": 2, + "apiVersion": 3, "$schema": "https://schemas.wp.org/trunk/block.json" -} +} \ No newline at end of file diff --git a/plugins/woocommerce-blocks/assets/js/blocks/products-by-attribute/block.json b/plugins/woocommerce-blocks/assets/js/blocks/products-by-attribute/block.json index b9f5f20e3ba..f3b91edf0a0 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/products-by-attribute/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/products-by-attribute/block.json @@ -2,10 +2,15 @@ "name": "woocommerce/products-by-attribute", "title": "Products by Attribute", "category": "woocommerce", - "keywords": [ "WooCommerce" ], + "keywords": [ + "WooCommerce" + ], "description": "Display a grid of products with selected attributes.", "supports": { - "align": [ "wide", "full" ], + "align": [ + "wide", + "full" + ], "html": false }, "attributes": { @@ -15,7 +20,10 @@ }, "attrOperator": { "type": "string", - "enum": [ "all", "any" ], + "enum": [ + "all", + "any" + ], "default": "any" }, "columns": { @@ -84,6 +92,6 @@ } }, "textdomain": "woocommerce", - "apiVersion": 2, + "apiVersion": 3, "$schema": "https://schemas.wp.org/trunk/block.json" -} +} \ No newline at end of file diff --git a/plugins/woocommerce-blocks/assets/js/blocks/products/all-products/block.json b/plugins/woocommerce-blocks/assets/js/blocks/products/all-products/block.json index 3dc6cbb63b0..b96667fea06 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/products/all-products/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/products/all-products/block.json @@ -5,10 +5,15 @@ "name": "woocommerce/all-products", "title": "All Products", "category": "woocommerce", - "keywords": [ "WooCommerce" ], + "keywords": [ + "WooCommerce" + ], "description": "Display products from your store in a grid layout.", "supports": { - "align": [ "wide", "full" ], + "align": [ + "wide", + "full" + ], "html": false, "multiple": false }, @@ -36,4 +41,4 @@ "default": false } } -} +} \ No newline at end of file diff --git a/plugins/woocommerce-blocks/assets/js/blocks/rating-filter/block.json b/plugins/woocommerce-blocks/assets/js/blocks/rating-filter/block.json index 20ce2e47fcf..7a457c23b0b 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/rating-filter/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/rating-filter/block.json @@ -4,7 +4,9 @@ "title": "Filter by Rating Controls", "description": "Enable customers to filter the product grid by rating.", "category": "woocommerce", - "keywords": [ "WooCommerce" ], + "keywords": [ + "WooCommerce" + ], "supports": { "html": false, "multiple": false, @@ -39,6 +41,6 @@ } }, "textdomain": "woocommerce", - "apiVersion": 2, + "apiVersion": 3, "$schema": "https://schemas.wp.org/trunk/block.json" -} +} \ No newline at end of file diff --git a/plugins/woocommerce-blocks/assets/js/blocks/reviews/all-reviews/index.tsx b/plugins/woocommerce-blocks/assets/js/blocks/reviews/all-reviews/index.tsx index 2c39c9c3630..d94233fc082 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/reviews/all-reviews/index.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/reviews/all-reviews/index.tsx @@ -20,7 +20,7 @@ import type { AllReviewsEditorProps } from './types'; * This block lists all product reviews. */ registerBlockType( 'woocommerce/all-reviews', { - apiVersion: 2, + apiVersion: 3, title: __( 'All Reviews', 'woocommerce' ), icon: { src: ( diff --git a/plugins/woocommerce-blocks/assets/js/blocks/reviews/reviews-by-category/index.tsx b/plugins/woocommerce-blocks/assets/js/blocks/reviews/reviews-by-category/index.tsx index 83d574ae4af..097c1be6eff 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/reviews/reviews-by-category/index.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/reviews/reviews-by-category/index.tsx @@ -17,7 +17,7 @@ import { example } from '../example.js'; * Register and run the "Reviews by category" block. */ registerBlockType( 'woocommerce/reviews-by-category', { - apiVersion: 2, + apiVersion: 3, title: __( 'Reviews by Category', 'woocommerce' ), icon: { src: ( diff --git a/plugins/woocommerce-blocks/assets/js/blocks/reviews/reviews-by-product/index.js b/plugins/woocommerce-blocks/assets/js/blocks/reviews/reviews-by-product/index.js index 0573bee8947..237f4f97d07 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/reviews/reviews-by-product/index.js +++ b/plugins/woocommerce-blocks/assets/js/blocks/reviews/reviews-by-product/index.js @@ -17,7 +17,7 @@ import { example } from '../example'; * Register and run the "Reviews by Product" block. */ registerBlockType( 'woocommerce/reviews-by-product', { - apiVersion: 2, + apiVersion: 3, title: __( 'Reviews by Product', 'woocommerce' ), icon: { src: ( diff --git a/plugins/woocommerce-blocks/assets/js/blocks/single-product/block.json b/plugins/woocommerce-blocks/assets/js/blocks/single-product/block.json index 0e9dafa7dd7..2306fae7b51 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/single-product/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/single-product/block.json @@ -5,17 +5,22 @@ "title": "Single Product", "description": "Display a single product.", "category": "woocommerce", - "keywords": [ "WooCommerce" ], + "keywords": [ + "WooCommerce" + ], "supports": { - "align": [ "wide", "full" ] + "align": [ + "wide", + "full" + ] }, "attributes": { "isPreview": { - "type": "boolean", - "default": false + "type": "boolean", + "default": false }, "productId": { - "type": "number" + "type": "number" } }, "example": { @@ -25,6 +30,6 @@ }, "usesContext": [ "postId", "postType", "queryId" ], "textdomain": "woocommerce", - "apiVersion": 2, + "apiVersion": 3, "$schema": "https://schemas.wp.org/trunk/block.json" } diff --git a/plugins/woocommerce-blocks/assets/js/blocks/single-product/save.tsx b/plugins/woocommerce-blocks/assets/js/blocks/single-product/save.tsx index 0feb6d8f950..4c8f4ef2973 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/single-product/save.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/single-product/save.tsx @@ -4,7 +4,9 @@ import { InnerBlocks, useBlockProps } from '@wordpress/block-editor'; const Save = () => { - const blockProps = useBlockProps.save(); + const blockProps = useBlockProps.save( { + className: 'woocommerce', + } ); return (
diff --git a/plugins/woocommerce-blocks/assets/js/blocks/stock-filter/block.json b/plugins/woocommerce-blocks/assets/js/blocks/stock-filter/block.json index 046c0aaa5ee..0ee1da12a5a 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/stock-filter/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/stock-filter/block.json @@ -4,7 +4,9 @@ "title": "Filter by Stock Controls", "description": "Enable customers to filter the product grid by stock status.", "category": "woocommerce", - "keywords": [ "WooCommerce" ], + "keywords": [ + "WooCommerce" + ], "supports": { "html": false, "multiple": false, @@ -43,6 +45,6 @@ } }, "textdomain": "woocommerce", - "apiVersion": 2, + "apiVersion": 3, "$schema": "https://schemas.wp.org/trunk/block.json" -} +} \ No newline at end of file diff --git a/plugins/woocommerce-blocks/assets/js/blocks/stock-filter/style.scss b/plugins/woocommerce-blocks/assets/js/blocks/stock-filter/style.scss index 8e5f5c92127..8598cf1fe27 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/stock-filter/style.scss +++ b/plugins/woocommerce-blocks/assets/js/blocks/stock-filter/style.scss @@ -12,6 +12,8 @@ } .wc-block-stock-filter { + display: grid; + &.is-loading { @include placeholder(); margin-top: $gap; @@ -19,8 +21,6 @@ border-radius: 0; } - margin-bottom: $gap-large; - .wc-block-stock-filter-list { margin: 0; diff --git a/plugins/woocommerce-blocks/assets/js/blocks/store-notices/block.json b/plugins/woocommerce-blocks/assets/js/blocks/store-notices/block.json index 91d389be5b1..d4fb6cb3ed8 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/store-notices/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/store-notices/block.json @@ -4,10 +4,15 @@ "title": "Store Notices", "description": "Display shopper-facing notifications generated by WooCommerce or extensions.", "category": "woocommerce", - "keywords": [ "WooCommerce" ], + "keywords": [ + "WooCommerce" + ], "supports": { "multiple": false, - "align": [ "wide", "full" ] + "align": [ + "wide", + "full" + ] }, "attributes": { "align": { @@ -16,6 +21,6 @@ } }, "textdomain": "woocommerce", - "apiVersion": 2, + "apiVersion": 3, "$schema": "https://schemas.wp.org/trunk/block.json" -} +} \ No newline at end of file diff --git a/plugins/woocommerce-blocks/assets/js/data/cart/notify-quantity-changes.ts b/plugins/woocommerce-blocks/assets/js/data/cart/notify-quantity-changes.ts index ad7faeea2fa..44910bf4bb8 100644 --- a/plugins/woocommerce-blocks/assets/js/data/cart/notify-quantity-changes.ts +++ b/plugins/woocommerce-blocks/assets/js/data/cart/notify-quantity-changes.ts @@ -3,7 +3,10 @@ */ import { Cart, CartItem } from '@woocommerce/types'; import { dispatch, select } from '@wordpress/data'; +import { decodeEntities } from '@wordpress/html-entities'; import { __, sprintf } from '@wordpress/i18n'; +// eslint-disable-next-line @wordpress/no-unsafe-wp-apis, @woocommerce/dependency-group +import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; /** * Internal dependencies @@ -25,6 +28,9 @@ const isWithinQuantityLimits = ( cartItem: CartItem ) => { ); }; +const stripAndDecode = ( text: string ) => { + return stripHTML( decodeEntities( text ) ); +}; const notifyIfQuantityLimitsChanged = ( oldCart: Cart, newCart: Cart ) => { newCart.items.forEach( ( cartItem ) => { const oldCartItem = oldCart.items.find( ( item ) => { @@ -64,7 +70,7 @@ const notifyIfQuantityLimitsChanged = ( oldCart: Cart, newCart: Cart ) => { 'The quantity of "%1$s" was changed to %2$d. You must purchase this product in groups of %3$d.', 'woocommerce' ), - cartItem.name, + stripAndDecode( cartItem.name ), // We round down to the nearest step value here. We need to do it this way because at this point we // don't know the next quantity. That only gets set once the HTML Input field applies its min/max // constraints. @@ -91,7 +97,7 @@ const notifyIfQuantityLimitsChanged = ( oldCart: Cart, newCart: Cart ) => { 'The quantity of "%1$s" was increased to %2$d. This is the minimum required quantity.', 'woocommerce' ), - cartItem.name, + stripAndDecode( cartItem.name ), cartItem.quantity_limits.minimum ), { @@ -112,7 +118,7 @@ const notifyIfQuantityLimitsChanged = ( oldCart: Cart, newCart: Cart ) => { 'The quantity of "%1$s" was decreased to %2$d. This is the maximum allowed quantity.', 'woocommerce' ), - cartItem.name, + stripAndDecode( cartItem.name ), cartItem.quantity_limits.maximum ), { @@ -153,7 +159,7 @@ const notifyIfQuantityChanged = ( 'The quantity of "%1$s" was changed to %2$d.', 'woocommerce' ), - cartItem.name, + stripAndDecode( cartItem.name ), cartItem.quantity ), { @@ -195,7 +201,7 @@ const notifyIfRemoved = ( sprintf( /* translators: %s is the name of the item. */ __( '"%s" was removed from your cart.', 'woocommerce' ), - oldCartItem.name + stripAndDecode( oldCartItem.name ) ), { context: 'wc/cart', diff --git a/plugins/woocommerce-blocks/assets/js/data/checkout/actions.ts b/plugins/woocommerce-blocks/assets/js/data/checkout/actions.ts index 0f039081dfa..8cc1f724274 100644 --- a/plugins/woocommerce-blocks/assets/js/data/checkout/actions.ts +++ b/plugins/woocommerce-blocks/assets/js/data/checkout/actions.ts @@ -61,9 +61,9 @@ export const __internalSetRedirectUrl = ( redirectUrl: string ) => ( { } ); /** - * Set whether the checkout has an error or not + * Set whether the checkout has an error * - * @param hasError Wether the checkout has an error or not + * @param hasError Whether the checkout has an error */ export const __internalSetHasError = ( hasError = true ) => ( { type: types.SET_HAS_ERROR, diff --git a/plugins/woocommerce-blocks/assets/js/data/payment/default-state.ts b/plugins/woocommerce-blocks/assets/js/data/payment/default-state.ts index 884799f2a60..e2fc44a0be5 100644 --- a/plugins/woocommerce-blocks/assets/js/data/payment/default-state.ts +++ b/plugins/woocommerce-blocks/assets/js/data/payment/default-state.ts @@ -17,7 +17,6 @@ import { STATUS as PAYMENT_STATUS } from './constants'; export interface PaymentState { status: string; activePaymentMethod: string; - activeSavedToken: string; // Available payment methods are payment methods which have been validated and can make payment. availablePaymentMethods: PlainPaymentMethods; availableExpressPaymentMethods: PlainExpressPaymentMethods; @@ -34,7 +33,6 @@ export interface PaymentState { export const defaultPaymentState: PaymentState = { status: PAYMENT_STATUS.IDLE, activePaymentMethod: '', - activeSavedToken: '', availablePaymentMethods: {}, availableExpressPaymentMethods: {}, savedPaymentMethods: getSetting< diff --git a/plugins/woocommerce-blocks/assets/js/data/payment/reducers.ts b/plugins/woocommerce-blocks/assets/js/data/payment/reducers.ts index 4e7502c72d6..d1e2a786c80 100644 --- a/plugins/woocommerce-blocks/assets/js/data/payment/reducers.ts +++ b/plugins/woocommerce-blocks/assets/js/data/payment/reducers.ts @@ -2,7 +2,7 @@ * External dependencies */ import type { Reducer } from 'redux'; -import { objectHasProp, PaymentResult } from '@woocommerce/types'; +import { PaymentResult } from '@woocommerce/types'; /** * Internal dependencies @@ -129,14 +129,8 @@ const reducer: Reducer< PaymentState > = ( break; case ACTION_TYPES.SET_ACTIVE_PAYMENT_METHOD: - const activeSavedToken = - typeof state.paymentMethodData === 'object' && - objectHasProp( action.paymentMethodData, 'token' ) - ? action.paymentMethodData.token + '' - : ''; newState = { ...state, - activeSavedToken, activePaymentMethod: action.activePaymentMethod, paymentMethodData: action.paymentMethodData || state.paymentMethodData, diff --git a/plugins/woocommerce-blocks/assets/js/data/payment/test/reducers.js b/plugins/woocommerce-blocks/assets/js/data/payment/test/reducers.js index 5aed26c0e01..63fb4466949 100644 --- a/plugins/woocommerce-blocks/assets/js/data/payment/test/reducers.js +++ b/plugins/woocommerce-blocks/assets/js/data/payment/test/reducers.js @@ -28,7 +28,6 @@ describe( 'paymentMethodDataReducer', () => { shouldSavePaymentMethod: false, errorMessage: '', activePaymentMethod: '', - activeSavedToken: '', incompatiblePaymentMethods: {}, } ); @@ -55,7 +54,6 @@ describe( 'paymentMethodDataReducer', () => { shouldSavePaymentMethod: false, errorMessage: '', activePaymentMethod: '', - activeSavedToken: '', incompatiblePaymentMethods: {}, } ); } ); @@ -79,7 +77,6 @@ describe( 'paymentMethodDataReducer', () => { shouldSavePaymentMethod: false, errorMessage: '', activePaymentMethod: '', - activeSavedToken: '', incompatiblePaymentMethods: {}, } ); const nextState = reducer( stateWithRegisteredMethod, { @@ -104,7 +101,6 @@ describe( 'paymentMethodDataReducer', () => { shouldSavePaymentMethod: false, errorMessage: '', activePaymentMethod: '', - activeSavedToken: '', incompatiblePaymentMethods: {}, } ); } ); @@ -134,7 +130,6 @@ describe( 'paymentMethodDataReducer', () => { shouldSavePaymentMethod: false, errorMessage: '', activePaymentMethod: '', - activeSavedToken: '', incompatiblePaymentMethods: {}, } ); } ); @@ -162,7 +157,6 @@ describe( 'paymentMethodDataReducer', () => { shouldSavePaymentMethod: false, errorMessage: '', activePaymentMethod: '', - activeSavedToken: '', incompatiblePaymentMethods: {}, } ); const nextState = reducer( stateWithRegisteredMethod, { @@ -187,7 +181,6 @@ describe( 'paymentMethodDataReducer', () => { shouldSavePaymentMethod: false, errorMessage: '', activePaymentMethod: '', - activeSavedToken: '', incompatiblePaymentMethods: {}, } ); } ); @@ -217,7 +210,6 @@ describe( 'paymentMethodDataReducer', () => { shouldSavePaymentMethod: false, errorMessage: '', activePaymentMethod: '', - activeSavedToken: '', incompatiblePaymentMethods: {}, } ); const nextState = reducer( stateWithRegisteredMethod, { @@ -250,7 +242,6 @@ describe( 'paymentMethodDataReducer', () => { shouldSavePaymentMethod: false, errorMessage: '', activePaymentMethod: '', - activeSavedToken: '', incompatiblePaymentMethods: {}, } ); } ); diff --git a/plugins/woocommerce-blocks/assets/js/data/payment/utils/set-default-payment-method.ts b/plugins/woocommerce-blocks/assets/js/data/payment/utils/set-default-payment-method.ts index 71fe94e7e76..907d3d65388 100644 --- a/plugins/woocommerce-blocks/assets/js/data/payment/utils/set-default-payment-method.ts +++ b/plugins/woocommerce-blocks/assets/js/data/payment/utils/set-default-payment-method.ts @@ -25,11 +25,13 @@ export const setDefaultPaymentMethod = async ( const savedPaymentMethods = select( PAYMENT_STORE_KEY ).getSavedPaymentMethods(); - + const flatSavedPaymentMethods = Object.keys( savedPaymentMethods ).flatMap( + ( type ) => savedPaymentMethods[ type ] + ); const savedPaymentMethod = - Object.keys( savedPaymentMethods ).flatMap( - ( type ) => savedPaymentMethods[ type ] - )[ 0 ] || undefined; + flatSavedPaymentMethods.find( ( method ) => method.is_default ) || + flatSavedPaymentMethods[ 0 ] || + undefined; if ( savedPaymentMethod ) { const token = savedPaymentMethod.tokenId.toString(); @@ -61,7 +63,6 @@ export const setDefaultPaymentMethod = async ( } dispatch( PAYMENT_STORE_KEY ).__internalSetPaymentIdle(); - dispatch( PAYMENT_STORE_KEY ).__internalSetActivePaymentMethod( paymentMethodKeys[ 0 ] ); diff --git a/plugins/woocommerce-blocks/assets/js/data/validation/selectors.ts b/plugins/woocommerce-blocks/assets/js/data/validation/selectors.ts index 042b76640fa..efadb3b2f9e 100644 --- a/plugins/woocommerce-blocks/assets/js/data/validation/selectors.ts +++ b/plugins/woocommerce-blocks/assets/js/data/validation/selectors.ts @@ -18,7 +18,7 @@ export const getValidationError = ( state: State, errorId: string ) => * * @param { State } state The current state. * @param { string } errorId The error ID. - * @return { string } The validation error ID. + * @return { string | undefined } The validation error ID. */ export const getValidationErrorId = ( state: State, errorId: string ) => { if ( ! state.hasOwnProperty( errorId ) || state[ errorId ].hidden ) { diff --git a/plugins/woocommerce-blocks/assets/js/editor-components/incompatible-extension-notice/index.tsx b/plugins/woocommerce-blocks/assets/js/editor-components/incompatible-extension-notice/index.tsx index 0897006d461..d8b1451cc7f 100644 --- a/plugins/woocommerce-blocks/assets/js/editor-components/incompatible-extension-notice/index.tsx +++ b/plugins/woocommerce-blocks/assets/js/editor-components/incompatible-extension-notice/index.tsx @@ -114,7 +114,7 @@ export function IncompatibleExtensionsNotice( { // translators: %s is the number of incompatible extensions. _n( '%s more incompatibility', - '%s more incompatibilites', + '%s more incompatibilities', remainingEntries, 'woocommerce' ), diff --git a/plugins/woocommerce-blocks/assets/js/editor-components/product-control/index.tsx b/plugins/woocommerce-blocks/assets/js/editor-components/product-control/index.tsx index bfa1e5f2a85..f86f2878dc7 100644 --- a/plugins/woocommerce-blocks/assets/js/editor-components/product-control/index.tsx +++ b/plugins/woocommerce-blocks/assets/js/editor-components/product-control/index.tsx @@ -62,6 +62,16 @@ interface ProductControlProps { * Whether to show variations in the list of items available. */ showVariations?: boolean; + /** + * Different messages to display in the component. + * If any of the messages are not provided, the default message will be used. + */ + messages?: { + list?: string; + noItems?: string; + search?: string; + updated?: string; + }; } const messages = { @@ -188,7 +198,7 @@ const ProductControl = ( } else if ( showVariations ) { return renderItemWithVariations; } - return () => null; + return undefined; }; if ( error ) { @@ -216,7 +226,10 @@ const ProductControl = ( onChange={ onChange } renderItem={ getRenderItemFunc() } onSearch={ onSearch } - messages={ messages } + messages={ { + ...messages, + ...props.messages, + } } isHierarchical /> ); diff --git a/plugins/woocommerce-blocks/assets/js/editor-components/search-list-control/item.tsx b/plugins/woocommerce-blocks/assets/js/editor-components/search-list-control/item.tsx index 73913fa80a9..4f28595c081 100644 --- a/plugins/woocommerce-blocks/assets/js/editor-components/search-list-control/item.tsx +++ b/plugins/woocommerce-blocks/assets/js/editor-components/search-list-control/item.tsx @@ -3,7 +3,7 @@ */ import clsx from 'clsx'; import { CheckboxControl } from '@wordpress/components'; -import { useCallback } from '@wordpress/element'; +import { useCallback, useEffect } from '@wordpress/element'; import { arrayDifferenceBy, arrayUnionBy } from '@woocommerce/utils'; import { decodeEntities } from '@wordpress/html-entities'; @@ -74,6 +74,12 @@ export const SearchListItem = < T extends object = object >( { } ); + useEffect( () => { + if ( hasChildren && isSelected ) { + setExpandedPanelId( item.id as number ); + } + }, [ item, hasChildren, isSelected, setExpandedPanelId ] ); + const name = props.name || `search-list-item-${ controlId }`; const id = `${ name }-${ item.id }`; diff --git a/plugins/woocommerce-blocks/assets/js/editor-components/search-list-control/style.scss b/plugins/woocommerce-blocks/assets/js/editor-components/search-list-control/style.scss index 441b52d6a36..a6d27b3fa4b 100644 --- a/plugins/woocommerce-blocks/assets/js/editor-components/search-list-control/style.scss +++ b/plugins/woocommerce-blocks/assets/js/editor-components/search-list-control/style.scss @@ -177,7 +177,6 @@ cursor: pointer; &::after { - background-image: url('data:image/svg+xml;utf8,'); background-position: center right; background-repeat: no-repeat; background-size: contain; @@ -186,6 +185,9 @@ margin-left: $gap-smaller; width: $gap-large; } + &:not(.is-expanded)::after { + background-image: url('data:image/svg+xml;utf8,'); + } &[disabled]::after { background: none; @@ -194,7 +196,7 @@ } &.is-expanded::after { - background-image: url('data:image/svg+xml;utf8,'); + background-image: url('data:image/svg+xml;utf8,'); } } diff --git a/plugins/woocommerce-blocks/assets/js/editor-components/search-list-control/test/hierarchy.js b/plugins/woocommerce-blocks/assets/js/editor-components/search-list-control/test/hierarchy.js index e4fb1def8a2..018723a06c7 100644 --- a/plugins/woocommerce-blocks/assets/js/editor-components/search-list-control/test/hierarchy.js +++ b/plugins/woocommerce-blocks/assets/js/editor-components/search-list-control/test/hierarchy.js @@ -153,7 +153,7 @@ describe( 'buildTermsTree', () => { ] ); } ); - test( 'should return a tree of items, with orphan categories appended to the end, with children of thier own', () => { + test( 'should return a tree of items, with orphan categories appended to the end, with children of their own', () => { const filteredList = [ { id: 1, name: 'Apricots', parent: 0 }, { id: 3, name: 'Elderberry', parent: 2 }, diff --git a/plugins/woocommerce-blocks/assets/js/interactivity/vdom.js b/plugins/woocommerce-blocks/assets/js/interactivity/vdom.js index f399d658a47..7934a73f860 100644 --- a/plugins/woocommerce-blocks/assets/js/interactivity/vdom.js +++ b/plugins/woocommerce-blocks/assets/js/interactivity/vdom.js @@ -21,7 +21,7 @@ const directiveParser = new RegExp( // segments. It excludes underscore intentionally to prevent confusion. // E.g., "custom-directive". '([a-z0-9]+(?:-[a-z0-9]+)*)' + - // (Optional) Match '--' followed by any alphanumeric charachters. It + // (Optional) Match '--' followed by any alphanumeric characters. It // excludes underscore intentionally to prevent confusion, but it can // contain multiple hyphens. E.g., "--custom-prefix--with-more-info". '(?:--([a-z0-9_-]+))?$', diff --git a/plugins/woocommerce-blocks/assets/js/settings/shared/default-fields.ts b/plugins/woocommerce-blocks/assets/js/settings/shared/default-fields.ts index 829d12e849e..6ecc9456671 100644 --- a/plugins/woocommerce-blocks/assets/js/settings/shared/default-fields.ts +++ b/plugins/woocommerce-blocks/assets/js/settings/shared/default-fields.ts @@ -1,13 +1,13 @@ /** * External dependencies */ -import { ComboboxControlOption } from '@woocommerce/base-components/combobox'; import type { AllHTMLAttributes, AriaAttributes } from 'react'; /** * Internal dependencies */ import { getSetting } from './utils'; +import { SelectOption } from '../../base/components'; // A list of attributes that can be added to a custom field when registering it. type CustomFieldAttributes = Pick< @@ -39,7 +39,7 @@ export interface FormField { // The type of input to render. Defaults to text. type?: string; // The options if this is a select field - options?: ComboboxControlOption[]; + options?: SelectOption[]; // The placeholder for the field, only applicable for select fields. placeholder?: string; // Additional attributes added when registering a field. String in key is required for data attributes. diff --git a/plugins/woocommerce-blocks/bin/gen-block-list-doc.js b/plugins/woocommerce-blocks/bin/gen-block-list-doc.js index a4283e99328..bc1a178e2fd 100644 --- a/plugins/woocommerce-blocks/bin/gen-block-list-doc.js +++ b/plugins/woocommerce-blocks/bin/gen-block-list-doc.js @@ -17,14 +17,21 @@ const fs = require( 'fs' ); * * @type {string} */ -const ROOT_DIR = path.resolve( __dirname, '../' ); +const ROOT_DIR = path.resolve( __dirname, '../../../' ); + +/** + * Path to root Blocks project directory. + * + * @type {string} + */ +const BLOCK_LIBRARY_ROOT_DIR = path.resolve( __dirname, '../' ); /** * Path to blocks directory. * * @type {string} */ -const BLOCK_LIBRARY_DIR = path.resolve( ROOT_DIR, 'assets/js' ); +const BLOCK_LIBRARY_DIR = path.resolve( BLOCK_LIBRARY_ROOT_DIR, 'assets/js' ); /** * Path to docs file. @@ -33,7 +40,7 @@ const BLOCK_LIBRARY_DIR = path.resolve( ROOT_DIR, 'assets/js' ); */ const BLOCK_LIBRARY_DOCS_FILE = path.resolve( ROOT_DIR, - 'docs/block-references/block-references.md' + 'docs/building-a-woo-store/block-references.md' ); /** @@ -103,13 +110,13 @@ function processObjWithInnerKeys( obj ) { * not disabled. So adding { color: 'link' } support also brings along * background and text. * - * @param {Object} supports - keys supported by blokc + * @param {Object} supports - keys supported by block * @return {Object} supports augmented with defaults */ function augmentSupports( supports ) { if ( supports && 'color' in supports ) { - // If backgroud or text is not specified (true or false) - // then add it as true.a + // If background or text is not specified (true or false) + // then add it as true. if ( typeof supports.color === 'object' && ! ( 'background' in supports.color ) diff --git a/plugins/woocommerce-blocks/bin/webpack-entries.js b/plugins/woocommerce-blocks/bin/webpack-entries.js index 349468e1659..a3adb0662fc 100644 --- a/plugins/woocommerce-blocks/bin/webpack-entries.js +++ b/plugins/woocommerce-blocks/bin/webpack-entries.js @@ -85,40 +85,43 @@ const blocks = { }, 'single-product': {}, 'stock-filter': {}, - 'product-filter': { - isExperimental: true, - }, 'product-filters': { isExperimental: true, }, + 'product-filter': { + isExperimental: true, + customDir: 'product-filters/inner-blocks/product-filter', + }, 'product-filters-overlay': { isExperimental: true, + customDir: 'product-filters/inner-blocks/overlay', }, 'product-filters-overlay-navigation': { isExperimental: true, + customDir: 'product-filters/inner-blocks/overlay-navigation', }, 'product-filter-stock-status': { isExperimental: true, - customDir: 'product-filter/inner-blocks/stock-filter', + customDir: 'product-filters/inner-blocks/stock-filter', }, 'product-filter-price': { - customDir: 'product-filter/inner-blocks/price-filter', + customDir: 'product-filters/inner-blocks/price-filter', isExperimental: true, }, 'product-filter-attribute': { - customDir: 'product-filter/inner-blocks/attribute-filter', + customDir: 'product-filters/inner-blocks/attribute-filter', isExperimental: true, }, 'product-filter-rating': { - customDir: 'product-filter/inner-blocks/rating-filter', + customDir: 'product-filters/inner-blocks/rating-filter', isExperimental: true, }, 'product-filter-active': { - customDir: 'product-filter/inner-blocks/active-filters', + customDir: 'product-filters/inner-blocks/active-filters', isExperimental: true, }, 'product-filter-clear-button': { - customDir: 'product-filter/inner-blocks/clear-button', + customDir: 'product-filters/inner-blocks/clear-button', isExperimental: true, }, 'order-confirmation-summary': { diff --git a/plugins/woocommerce-blocks/docs/contributors/coding-guidelines.md b/plugins/woocommerce-blocks/docs/contributors/coding-guidelines.md index 9f46adcb41e..2afbe5eb722 100644 --- a/plugins/woocommerce-blocks/docs/contributors/coding-guidelines.md +++ b/plugins/woocommerce-blocks/docs/contributors/coding-guidelines.md @@ -141,7 +141,7 @@ And these are the styles of the block: } ``` -As you can see, the styles coming from the themes have higher specificity, so our styles would be overriden. In order to solve this: +As you can see, the styles coming from the themes have higher specificity, so our styles would be overridden. In order to solve this: 1. Never use `!important` rules in CSS to engage in a specificity war with a theme. 2. Never use ID selectors. diff --git a/plugins/woocommerce-blocks/docs/contributors/e2e-guidelines.md b/plugins/woocommerce-blocks/docs/contributors/e2e-guidelines.md index 08e39812ed9..b82a815447a 100644 --- a/plugins/woocommerce-blocks/docs/contributors/e2e-guidelines.md +++ b/plugins/woocommerce-blocks/docs/contributors/e2e-guidelines.md @@ -4,7 +4,7 @@ This living document serves as a guide for writing end-to-end (E2E) tests with P ## Preparing the environment -Please refer to [the Getting Started section of the main `README.md`](https://github.com/woocommerce/woocommerce/blob/trunk/README.md) for a general-purpose guide on getting started. The rest of this document will assume that you've installed all of the prequisites and setup described there. +Please refer to [the Getting Started section of the main `README.md`](https://github.com/woocommerce/woocommerce/blob/trunk/README.md) for a general-purpose guide on getting started. The rest of this document will assume that you've installed all of the prerequisites and setup described there. Run the following command from the repository root to build the WooCommerce plugin: diff --git a/plugins/woocommerce-blocks/docs/internal-developers/blocks/feature-flags-and-experimental-interfaces.md b/plugins/woocommerce-blocks/docs/internal-developers/blocks/feature-flags-and-experimental-interfaces.md index ba7177b08a2..2254ddb6d82 100644 --- a/plugins/woocommerce-blocks/docs/internal-developers/blocks/feature-flags-and-experimental-interfaces.md +++ b/plugins/woocommerce-blocks/docs/internal-developers/blocks/feature-flags-and-experimental-interfaces.md @@ -85,7 +85,7 @@ We also have individual features or code blocks behind a feature flag, this is a - **Deprecated** - `__experimental_woocommerce_blocks_checkout_update_order_from_request` hook gives extensions the chance to update orders based on the data in the request ([deprecated experimental hook](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/d469a45d572f2c52d7917707c492dfb905ddfac0/src/StoreApi/Routes/Checkout.php#L466-L477)). [Deprecated in PR 5015](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5015). - **Deprecated** - `__experimental_woocommerce_blocks_checkout_order_processed` hook when order has completed processing and is ready for payment ([deprecated experimental hook](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/accd1bbf402e043b9fc322f118ab614ba7437c92/src/StoreApi/Routes/Checkout.php#L237)). [Deprecated in PR 5014](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5014). - `__experimental_woocommerce_blocks_add_data_attributes_to_namespace` hook that allows 3PD to add a namespace of blocks to receive block attributes as `data-` attributes ([experimental property](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/4a1ee97eb97011458174e93e44a9b7ad2f10ca36/src/BlockTypesController.php#L88)). -- `__experimental_woocommerce_blocks_add_data_attributes_to_block` hook that allows 3PD to add a block to recieve block attributes as `data-` attributes ([experimental property](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/4a1ee97eb97011458174e93e44a9b7ad2f10ca36/src/BlockTypesController.php#L97)). +- `__experimental_woocommerce_blocks_add_data_attributes_to_block` hook that allows 3PD to add a block to receive block attributes as `data-` attributes ([experimental property](https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/4a1ee97eb97011458174e93e44a9b7ad2f10ca36/src/BlockTypesController.php#L97)). ### JS methods diff --git a/plugins/woocommerce-blocks/docs/internal-developers/testing/cart-checkout/local-pickup.md b/plugins/woocommerce-blocks/docs/internal-developers/testing/cart-checkout/local-pickup.md index acd7bf7702c..c7b34613182 100644 --- a/plugins/woocommerce-blocks/docs/internal-developers/testing/cart-checkout/local-pickup.md +++ b/plugins/woocommerce-blocks/docs/internal-developers/testing/cart-checkout/local-pickup.md @@ -122,7 +122,7 @@ ### Taxes -1. Setup two differnt tax rates, one for your store base address for 10%, one for a differnt address for 20%. +1. Setup two different tax rates, one for your store base address for 10%, one for a different address for 20%. 2. Create a pickup location with an address that matches the 20% tax address. 3. Create a pickup location without an address. 4. On Checkout, select regular shipping, use an address that doesn't match the ones above, you should **not** see a tax line item. diff --git a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/1000.md b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/1000.md index e23429382c4..436a8d28344 100644 --- a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/1000.md +++ b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/1000.md @@ -122,7 +122,7 @@ Make sure `Single Product` template customizations are reset to the default stat 8. Check the sidebar and ensure the sidebar shows local pickup, and shows the address for the pickup location you have chosen. 9. Change the location and ensure the sidebar updates. -### Product Rating: Normalize the height of icons and the add revew link ([8399](https://github.com/woocommerce/woocommerce-blocks/pull/8399)) +### Product Rating: Normalize the height of icons and the add review link ([8399](https://github.com/woocommerce/woocommerce-blocks/pull/8399)) 1. Add a new page and the **Products** block. 2. Add the **Product Rating** block. diff --git a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/1010.md b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/1010.md index 6cc214e1480..8e45091f871 100644 --- a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/1010.md +++ b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/1010.md @@ -342,6 +342,6 @@ Mini Cart | Cart 4. In the sidebar, set a custom width lower than 300px. 5. Verify when you move the focus somewhere else, it's set to 300px. 6. Remove the value from the custom width input. -7. Verify it gets resetted to the default (480px). +7. Verify it gets reset to the default (480px). 8. Try setting an allowed value (ie: 350px, 500px, etc.). 9. Verify the value persists. diff --git a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/1030.md b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/1030.md index 4230538e7b2..69533efa19a 100644 --- a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/1030.md +++ b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/1030.md @@ -220,7 +220,7 @@ Now, let's test that things keep working if there are two Mini Cart blocks in th 2. Ensure the preview shows nicely per the screenshot above. 3. After adding the pattern to the page/post, publish/update and view it in the frontend and ensure it displays as what you saw in the preview. -### Add to Cart with Options block > Restore the global variable to its original value after being overriden [#9581](https://github.com/woocommerce/woocommerce-blocks/pull/9581) +### Add to Cart with Options block > Restore the global variable to its original value after being overridden [#9581](https://github.com/woocommerce/woocommerce-blocks/pull/9581) 1. While having a block theme enabled such as Twenty-twenty Three, head over to your Dashboard and on the sidebar, click on "Appearance > Editor". 2. Select the Single Product template to customize it and click on edit. diff --git a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/1040.md b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/1040.md index ac21514c59b..77cd02306ab 100644 --- a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/1040.md +++ b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/1040.md @@ -53,7 +53,7 @@ Zip file for testing: [woocommerce-gutenberg-products-block.zip](https://github. 2. Visit a product page. 3. Check the body and ensure that the product classes are added -### Add to Cart with Options block: Restore the global variable to its original value after being overriden [#9581](https://github.com/woocommerce/woocommerce-blocks/pull/9581) +### Add to Cart with Options block: Restore the global variable to its original value after being overridden [#9581](https://github.com/woocommerce/woocommerce-blocks/pull/9581) 1. While having a block theme enabled such as Twenty-twenty Three, head over to your Dashboard and on the sidebar, click on "Appearance > Editor". 2. Select the Single Product template to customize it and click on edit. diff --git a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/1062.md b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/1062.md index 4fad5c319a5..14d264aede4 100644 --- a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/1062.md +++ b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/1062.md @@ -11,7 +11,7 @@ Zip file for testing: [woocommerce-gutenberg-products-block.zip](https://github. 3. Create a new post 4. Insert the navigation block 5. Make sure you can correctly preview it without any problems, as demonstrated in the screenshot in the description of this PR. -6. Now head over to Appearence > Editor > Templates > Product Catalog and edit it. +6. Now head over to Appearance > Editor > Templates > Product Catalog and edit it. 7. If you are seeing the "WooCommerce Product Grid Block", click on "Transform into blocks": Screenshot 2023-07-27 at 18 56 37 diff --git a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/1140.md b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/1140.md index d758d2c5c39..b94d1b78cfd 100644 --- a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/1140.md +++ b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/1140.md @@ -1,3 +1,5 @@ + + # Testing notes and ZIP for release 11.4.0 Zip file for testing: [woocommerce-gutenberg-products-block.zip](https://github.com/woocommerce/woocommerce-blocks/files/13127290/woocommerce-gutenberg-products-block.zip) @@ -151,7 +153,7 @@ Zip file for testing: [woocommerce-gutenberg-products-block.zip](https://github. Prerequisite: Ensure you're already using cart and checkout blocks and you have an incompatible extension installed. You can use `WooCommerce Checkout Field Editor` as an example. -1. Go to WooCommece > Home. There should be a task list item "Review your shopper's checkout experience". Click it. You should be taken to the page editor. +1. Go to WooCommerce > Home. There should be a task list item "Review your shopper's checkout experience". Click it. You should be taken to the page editor. 2. Edit the cart page 1. Edit the cart page 2. Focus on the parent cart block and click its icon. You should be able to transform to "classic cart" @@ -241,10 +243,10 @@ Compare the 3 templates show below to the designs: Mrk6SERPZ4KrFHSjM0a8TK-fi-571 #### Fix: Mini-Cart block shows wrong total if theres multiple installs on the same domain [11257](https://github.com/woocommerce/woocommerce-blocks/pull/11257) 1. Setup _Site B_ on domain.com/test and use USD as the currency -5. See the Site B Mini Cart works as expected. -1. Setup _Site A_ on domain.com, lets use EUR as the currency -3. Add something to the cart on _Site A_ -4. Go to _Site B_ and **don't see** its mini-cart affected by site A. +2. See the Site B Mini Cart works as expected. +3. Setup _Site A_ on domain.com, lets use EUR as the currency +4. Add something to the cart on _Site A_ +5. Go to _Site B_ and **don't see** its mini-cart affected by site A. #### Fix inconsistent border focus styles [11203](https://github.com/woocommerce/woocommerce-blocks/pull/11203) diff --git a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/272.md b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/272.md index 6495978f2e9..1bfdbb86f22 100644 --- a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/272.md +++ b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/272.md @@ -21,7 +21,7 @@ These actions only appear when the feature flag is on for the install. If you ar woocommerce_blocks_phase = 1 ``` -(remember to remove tha file after testing!) +(remember to remove the file after testing!) ## When distributed as the feature plugin @@ -30,7 +30,7 @@ Expected behaviour is: - [ ] `woocommerce_cleanup_draft_orders` action **should** appear in the scheduled tasks for Action Scheduler (Tools->Scheduled Actions). - [ ] Create a draft order (on the frontend load the checkout block with products in the cart and do not complete the order). - [ ] Trigger the scheduled action for `woocommerce_cleanup_draft_orders` and verify the just created draft order and none of the other orders on your test site have been deleted. -- [ ] Modify the draft order date so that it's greater than 24 hours in the past from now (you need to modify the `post_modifed_gmt` field) +- [ ] Modify the draft order date so that it's greater than 24 hours in the past from now (you need to modify the `post_modified_gmt` field) - [ ] Trigger the scheduled action again, this time the draft order you modified (and only that draft order) should be deleted. There are PHPUnit tests covering behaviour as well (including catching errors). diff --git a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/440.md b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/440.md index 304a772709a..8ee38e8ff4f 100644 --- a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/440.md +++ b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/440.md @@ -61,7 +61,7 @@ This test requires the browser dev tools to inspect rendered HTML. 4. Make sure it has role="img" & aria-label="Rated X out of 5" 5. Check that the child span has the same text content as the aria-label attribute -### Set correct text color in BlockErrorBoundry notices. #3738 +### Set correct text color in BlockErrorBoundary notices. #3738 This test requires a code edit to force an error. diff --git a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/620.md b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/620.md index c3ad5016ea0..6fc67f4c3b6 100644 --- a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/620.md +++ b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/620.md @@ -10,7 +10,7 @@ Zip file for testing: [woocommerce-gutenberg-products-block.zip](https://github. - Confirm you can select inner blocks in the Cart. - Change some settings of inner blocks. Preview your changes and make sure they persist on the frontend of your store. -- Using the Cart block's toolbar switch from the Filled Cart to the Empty Cart. Confirm you can switch back and forth between boths states. +- Using the Cart block's toolbar switch from the Filled Cart to the Empty Cart. Confirm you can switch back and forth between both states. - Change the content of the empty cart. Preview your changes and make sure they persist on the frontend of your store. - Add custom classnames. They should persist on all blocks. - Change the alignment options. Again, changes should persist. diff --git a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/740.md b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/740.md index 8a8d6af35b6..8de8e487f36 100644 --- a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/740.md +++ b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/740.md @@ -62,7 +62,7 @@ Zip file for testing: [woocommerce-gutenberg-products-block.zip](https://github. 4. Try to move some of the inner blocks and save. Confirm that in the Cart block only Order Summary Heading and the Coupon form inner blocks can be removed, and for the Checkout block only the Coupon form. 5. Check on the website that your changes are reflected. Note that Taxes, Fees and Coupons will appear on the website only if the shop supports them. -6. For the Cart > Shipping inner block make sure you can enable disable the shipping calculator and that the change is visibile in the website +6. For the Cart > Shipping inner block make sure you can enable disable the shipping calculator and that the change is visible in the website 7. Enable Taxes on your website (`/wp-admin/admin.php?page=wc-settings`) by clicking `Enable tax rates and calculations` 8. Go to the C & C blocks and select the Taxes inner block. 9. In the inner block's setting toggle on / of `Show rate after tax name` and make sure that is reflected in the website diff --git a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/890.md b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/890.md index 8d51133bc3e..7cca86de40f 100644 --- a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/890.md +++ b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/890.md @@ -109,7 +109,7 @@ Zip file for testing: [woocommerce-gutenberg-products-block.zip](https://github. 2. Try to apply a coupon or access `/wp-json/wc/store/cart` beyond current limits (currently 25 requests under 10 seconds) 3. Ensure you get presented with an error "_Too many requests. Please wait xx seconds before trying again_." -### Prevent padding from placeholder instructions to be overriden ([7552](https://github.com/woocommerce/woocommerce-blocks/pull/7552)) +### Prevent padding from placeholder instructions to be overridden ([7552](https://github.com/woocommerce/woocommerce-blocks/pull/7552)) 1. Add a “Filter Products by Attribute” block 2. Notice that the text "Display a list of filters based on a chosen attribute" is NOT too close to the separator diff --git a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/930.md b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/930.md index 70cb0227788..a15cceab48f 100644 --- a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/930.md +++ b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/930.md @@ -301,7 +301,7 @@ Create three posts, one with the Mini Cart block, another one with the Cart bloc 1. Twenty Twenty Three: 1.1. Install it from here: 1.2. Go to the pages created in step 1 and verify the Mini Cart, Cart and Checkout buttons **follow** the theme styles. - 1.3. Go to Apperance > Editor > Styles > Browse Styles and change between style variations. Verify the buttons follow the styles in all of them. + 1.3. Go to Appearance > Editor > Styles > Browse Styles and change between style variations. Verify the buttons follow the styles in all of them. 2. Twenty Twenty Two (or another block theme): 2.1. Install it from here: diff --git a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/944.md b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/944.md index c34d572ed80..0762cb88ec9 100644 --- a/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/944.md +++ b/plugins/woocommerce-blocks/docs/internal-developers/testing/releases/944.md @@ -4,7 +4,7 @@ Zip file for testing: [woocommerce-gutenberg-products-block.zip](https://github. ## Feature plugin and package inclusion in WooCommerce -### Check if session is set before returing updated customer address. ([8537](https://github.com/woocommerce/woocommerce-blocks/pull/8537)) +### Check if session is set before returning updated customer address. ([8537](https://github.com/woocommerce/woocommerce-blocks/pull/8537)) 1. Install [AvaTax](https://woocommerce.com/products/woocommerce-avatax/) (credentials in secret 7715) and set it up so taxes are applied to your orders. I used a store in the USA and used USA addresses. 2. Install WooCommerce Subscriptions diff --git a/plugins/woocommerce-blocks/docs/third-party-developers/extensibility/data-store/schema.md b/plugins/woocommerce-blocks/docs/third-party-developers/extensibility/data-store/schema.md index 7e4525cc527..71525bf832d 100644 --- a/plugins/woocommerce-blocks/docs/third-party-developers/extensibility/data-store/schema.md +++ b/plugins/woocommerce-blocks/docs/third-party-developers/extensibility/data-store/schema.md @@ -40,7 +40,7 @@ This returns an action object used to update the store with the provided list of #### _Returns_ -- `object`: An action object used to update the store with the provided list of resource routes with teh following keys: +- `object`: An action object used to update the store with the provided list of resource routes with the following keys: - _type_ `string`: The action type. - _routes_ `object`: An object of routes keyed by the route name. - _namespace_ `string`: The namespace the routes belong to, eg. `/wc/blocks`. diff --git a/plugins/woocommerce-blocks/docs/third-party-developers/extensibility/data-store/validation.md b/plugins/woocommerce-blocks/docs/third-party-developers/extensibility/data-store/validation.md index b9ab4f0bd81..909b3217712 100644 --- a/plugins/woocommerce-blocks/docs/third-party-developers/extensibility/data-store/validation.md +++ b/plugins/woocommerce-blocks/docs/third-party-developers/extensibility/data-store/validation.md @@ -108,7 +108,7 @@ const hasError = validationError?.message && ! validationError?.hidden; In the example above, the `message` is hidden and only the text color is changed to red, highlighting that this field has a validation error. -In some cases, it's desired to show the validation error message to the user. For example, if the buyer tries to submit the checkout form without filling in the required fields. An example can seen when leaving the first name, last name and address fileds empty: +In some cases, it's desired to show the validation error message to the user. For example, if the buyer tries to submit the checkout form without filling in the required fields. An example can seen when leaving the first name, last name and address fields empty: ![image](https://woocommerce.com/wp-content/uploads/2023/10/Screenshot-2023-10-25-at-18.28.30.png) diff --git a/plugins/woocommerce-blocks/docs/third-party-developers/extensibility/hooks/migrated-hooks.md b/plugins/woocommerce-blocks/docs/third-party-developers/extensibility/hooks/migrated-hooks.md index 0af415e0e24..377c2300d7c 100644 --- a/plugins/woocommerce-blocks/docs/third-party-developers/extensibility/hooks/migrated-hooks.md +++ b/plugins/woocommerce-blocks/docs/third-party-developers/extensibility/hooks/migrated-hooks.md @@ -6,7 +6,7 @@ tags: reference, checkout-hooks # Migrated Legacy Hooks -Below are the hooks that exist in WooCommerce core and that were brough over to WooCommerce Blocks. +Below are the hooks that exist in WooCommerce core and that were brought over to WooCommerce Blocks. Please note that the actions and filters here run on the server side. The client-side blocks won't necessarily change based on a callback added to a server side hook. [Please see our documentation relating to APIs for manipulating the blocks on the client-side](../README.md). diff --git a/plugins/woocommerce-blocks/package.json b/plugins/woocommerce-blocks/package.json index f5a34e80f9f..cd63f8a5ca4 100644 --- a/plugins/woocommerce-blocks/package.json +++ b/plugins/woocommerce-blocks/package.json @@ -113,7 +113,7 @@ "@babel/preset-react": "7.23.3", "@babel/preset-typescript": "7.23.2", "@bartekbp/typescript-checkstyle": "5.0.0", - "@playwright/test": "^1.45.1", + "@playwright/test": "^1.46.1", "@storybook/addon-a11y": "7.5.2", "@storybook/addon-actions": "^7.6.4", "@storybook/addon-docs": "^7.6.4", @@ -253,7 +253,7 @@ }, "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "dependencies": { "@ariakit/react": "^0.4.4", diff --git a/plugins/woocommerce-blocks/packages/components/text-input/validated-text-input.tsx b/plugins/woocommerce-blocks/packages/components/text-input/validated-text-input.tsx index 583690fa340..557b5a8e4d4 100644 --- a/plugins/woocommerce-blocks/packages/components/text-input/validated-text-input.tsx +++ b/plugins/woocommerce-blocks/packages/components/text-input/validated-text-input.tsx @@ -26,6 +26,7 @@ import { getValidityMessageForInput } from '../../checkout/utils'; import { ValidatedTextInputProps } from './types'; export type ValidatedTextInputHandle = { + focus?: () => void; revalidate: () => void; }; @@ -147,6 +148,9 @@ const ValidatedTextInput = forwardRef< forwardedRef, function () { return { + focus() { + inputRef.current?.focus(); + }, revalidate() { validateInput( ! value ); }, diff --git a/plugins/woocommerce-blocks/phpcs.xml b/plugins/woocommerce-blocks/phpcs.xml index 51ada14dae7..9369e670336 100644 --- a/plugins/woocommerce-blocks/phpcs.xml +++ b/plugins/woocommerce-blocks/phpcs.xml @@ -9,7 +9,7 @@ diff --git a/plugins/woocommerce-blocks/readme.txt b/plugins/woocommerce-blocks/readme.txt index bf4f237bcf8..951a43ab579 100644 --- a/plugins/woocommerce-blocks/readme.txt +++ b/plugins/woocommerce-blocks/readme.txt @@ -2723,7 +2723,7 @@ This release fixes an error that some users experienced when their site automati - Fix - Ensure empty categories are correctly hidden in the product categories block. ([3765](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3765)) - Fix - Added missing wrapper div within FeaturedCategory and FeatureProduct blocks. ([3746](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3746)) -- Fix - Set correct text color in BlockErrorBoundry notices. ([3738](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3738)) +- Fix - Set correct text color in BlockErrorBoundary notices. ([3738](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3738)) - Hidden cart item meta data will not be rendered in the Cart and Checkout blocks. ([3732](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3732)) - Fix - Improved accessibility of product image links in the products block by using correct aria tags and hiding empty image placeholders. ([3722](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3722)) - Add missing aria-label for stars image in the review-list-item component. ([3706](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3706)) diff --git a/plugins/woocommerce-blocks/storybook/main.js b/plugins/woocommerce-blocks/storybook/main.js index a1f07ef3e8a..4a16e281158 100644 --- a/plugins/woocommerce-blocks/storybook/main.js +++ b/plugins/woocommerce-blocks/storybook/main.js @@ -35,7 +35,7 @@ module.exports = { }, // webpackFinal field was added in following PR: https://github.com/woocommerce/woocommerce-blocks/pull/7514 // This fixes "storybook build issue" related to framer-motion library. - // Solution is from this commment: https://github.com/storybookjs/storybook/issues/16690#issuecomment-971579785 + // Solution is from this comment: https://github.com/storybookjs/storybook/issues/16690#issuecomment-971579785 webpackFinal: async ( config ) => { config.module.rules.push( { test: /\.mjs$/, diff --git a/plugins/woocommerce-blocks/tests/e2e/flaky-tests-reporter.ts b/plugins/woocommerce-blocks/tests/e2e/flaky-tests-reporter.ts index c8603d06421..effacb82737 100644 --- a/plugins/woocommerce-blocks/tests/e2e/flaky-tests-reporter.ts +++ b/plugins/woocommerce-blocks/tests/e2e/flaky-tests-reporter.ts @@ -57,8 +57,8 @@ class FlakyTestsReporter implements Reporter { this.failingTestCaseResults.set( testTitle, [] ); } this.failingTestCaseResults - .get( testTitle )! - .push( formatTestResult( testCaseResult ) ); + .get( testTitle ) + ?.push( formatTestResult( testCaseResult ) ); break; } case 'flaky': { diff --git a/plugins/woocommerce-blocks/tests/e2e/plugins/enable-experimental-features.php b/plugins/woocommerce-blocks/tests/e2e/plugins/enable-experimental-features.php index 741fe04b4c2..0b03452dd33 100644 --- a/plugins/woocommerce-blocks/tests/e2e/plugins/enable-experimental-features.php +++ b/plugins/woocommerce-blocks/tests/e2e/plugins/enable-experimental-features.php @@ -1,7 +1,7 @@ { test( 'block can be inserted and it sorts reviews by most recent by default', async ( { frontendUtils, - page, editor, } ) => { - await expect( page.getByText( allReviews[ 0 ].review ) ).toBeVisible(); + await expect( + editor.canvas.getByText( allReviews[ 0 ].review ) + ).toBeVisible(); await editor.publishAndVisitPost(); diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/attributes-filter/attribute-filter.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/attributes-filter/attribute-filter.block_theme.spec.ts index 203aa6f0890..7466fd0bbf6 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/attributes-filter/attribute-filter.block_theme.spec.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/attributes-filter/attribute-filter.block_theme.spec.ts @@ -40,15 +40,17 @@ test.describe( `${ blockData.name } Block`, () => { await editor.openDocumentSettingsSidebar(); } ); - test( "should allow changing the block's title", async ( { page } ) => { + test( "should allow changing the block's title", async ( { editor } ) => { const textSelector = '.wp-block-woocommerce-filter-wrapper .wp-block-heading'; const title = 'New Title'; - await page.locator( textSelector ).fill( title ); + await editor.canvas.locator( textSelector ).fill( title ); - await expect( page.locator( textSelector ) ).toHaveText( title ); + await expect( editor.canvas.locator( textSelector ) ).toHaveText( + title + ); } ); test( 'should allow changing the display style', async ( { @@ -59,7 +61,7 @@ test.describe( `${ blockData.name } Block`, () => { await editor.selectBlocks( attributeFilter ); await expect( - page.getByRole( 'checkbox', { name: 'Small' } ) + editor.canvas.getByRole( 'checkbox', { name: 'Small' } ) ).toBeVisible(); await page.getByLabel( 'DropDown' ).click(); @@ -71,10 +73,10 @@ test.describe( `${ blockData.name } Block`, () => { ).toBeHidden(); await expect( - page.getByRole( 'checkbox', { name: 'Small' } ) + editor.canvas.getByRole( 'checkbox', { name: 'Small' } ) ).toBeHidden(); - await expect( page.getByRole( 'combobox' ) ).toBeVisible(); + await expect( editor.canvas.getByRole( 'combobox' ) ).toBeVisible(); } ); test( 'should allow toggling the visibility of the filter button', async ( { diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/basic.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/basic.block_theme.spec.ts index c99e5903da2..3a96fd42bf9 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/basic.block_theme.spec.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/basic.block_theme.spec.ts @@ -13,6 +13,11 @@ test.describe( 'Basic role-based functionality tests', () => { page.getByRole( 'heading', { name: 'Dashboard' } ) ).toHaveText( 'Dashboard' ); } ); + + test( 'Load iframed post editor', async ( { admin, editor } ) => { + await admin.createNewPost(); + await expect( editor.canvas.owner() ).toBeVisible(); + } ); } ); test.describe( 'As customer', () => { diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/cart/cart-checkout-block-notices.shopper.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/cart/cart-checkout-block-notices.shopper.block_theme.spec.ts index 49e9a62a5be..9ff873fac4d 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/cart/cart-checkout-block-notices.shopper.block_theme.spec.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/cart/cart-checkout-block-notices.shopper.block_theme.spec.ts @@ -15,7 +15,10 @@ import { * Internal dependencies */ import { CheckoutPage } from '../checkout/checkout.page'; -import { REGULAR_PRICED_PRODUCT_NAME } from '../checkout/constants'; +import { + REGULAR_PRICED_PRODUCT_NAME, + INVALID_COUPON, +} from '../checkout/constants'; const test = base.extend< { checkoutPageObject: CheckoutPage } >( { checkoutPageObject: async ( { page }, use ) => { @@ -41,7 +44,7 @@ test.describe( 'Shopper → Notice Templates', () => { await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME ); } ); - test( 'default block notice templates are visible', async ( { + test( 'default block notice templates, except for coupon errors, are visible', async ( { frontendUtils, page, } ) => { @@ -70,10 +73,10 @@ test.describe( 'Shopper → Notice Templates', () => { } ) ).toBeVisible(); - // We're explicitly checking the CSS classes of the block notices, and that the SVG is visible. + // We're explicitly checking the CSS classes of the block notices, and that the SVG is hidden. await expect( page.locator( '.wc-block-components-notice-banner.is-error svg' ) - ).toBeVisible(); + ).toBeHidden(); await page.getByLabel( 'Remove Polo from cart' ).click(); @@ -89,7 +92,7 @@ test.describe( 'Shopper → Notice Templates', () => { ).toBeVisible(); } ); - test( 'custom block notice templates are visible by template overwrite', async ( { + test( 'custom block notice templates, except for coupon errors, are visible by template overwrite', async ( { requestUtils, frontendUtils, page, @@ -121,10 +124,10 @@ test.describe( 'Shopper → Notice Templates', () => { page.getByText( 'BLOCK ERROR NOTICE: Coupon code already applied!' ) ).toBeVisible(); - // We're explicitly checking the CSS classes of the block notices, and that the SVG is visible. + // We're explicitly checking the CSS classes of the block notices, and that the SVG is hidden. await expect( page.locator( '.wc-block-components-notice-banner.is-error svg' ) - ).toBeVisible(); + ).toBeHidden(); await page.getByLabel( 'Remove Polo from cart' ).click(); @@ -140,7 +143,7 @@ test.describe( 'Shopper → Notice Templates', () => { await requestUtils.activateTheme( BLOCK_THEME_SLUG ); } ); - test( 'classic notice templates are visible by template overwrite', async ( { + test( 'classic notice templates, except for coupon errors, are visible by template overwrite', async ( { requestUtils, frontendUtils, page, @@ -177,7 +180,7 @@ test.describe( 'Shopper → Notice Templates', () => { // We're explicitly checking the CSS classes of the classic notices. await expect( page.locator( '.woocommerce-notices-wrapper .woocommerce-error' ) - ).toBeVisible(); + ).toBeHidden(); await page.getByLabel( 'Remove Polo from cart' ).click(); @@ -229,10 +232,10 @@ test.describe( 'Shopper → Notice Templates', () => { } ) ).toBeVisible(); - // We're explicitly checking the CSS classes and that the SVG is visible. + // We're explicitly checking the CSS classes and that the SVG is hidden. await expect( page.locator( '.wc-block-components-notice-banner.is-error svg' ) - ).toBeVisible(); + ).toBeHidden(); await page.getByLabel( 'Remove Polo from cart' ).click(); @@ -249,4 +252,26 @@ test.describe( 'Shopper → Notice Templates', () => { await requestUtils.activateTheme( BLOCK_THEME_SLUG ); } ); + + test( 'coupon inline notice is visible', async ( { + frontendUtils, + page, + } ) => { + await frontendUtils.goToCartShortcode(); + await page.getByPlaceholder( 'Coupon code' ).fill( INVALID_COUPON ); + await page.getByRole( 'button', { name: 'Apply coupon' } ).click(); + + await expect( + page.getByText( `Coupon "${ INVALID_COUPON }" does not exist!`, { + exact: true, + } ) + ).toBeVisible(); + + // We're explicitly checking the CSS classes of the block notices, and that the SVG is hidden. + await expect( + page.locator( '.wc-block-components-notice-banner.is-error svg' ) + ).toBeHidden(); + + await expect( page.locator( '.coupon-error-notice' ) ).toBeVisible(); + } ); } ); diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/cart/cart-checkout-block-notices.shopper.classic_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/cart/cart-checkout-block-notices.shopper.classic_theme.spec.ts index ad4986e30ac..6b0dbfbb346 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/cart/cart-checkout-block-notices.shopper.classic_theme.spec.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/cart/cart-checkout-block-notices.shopper.classic_theme.spec.ts @@ -15,7 +15,10 @@ import { * Internal dependencies */ import { CheckoutPage } from '../checkout/checkout.page'; -import { REGULAR_PRICED_PRODUCT_NAME } from '../checkout/constants'; +import { + REGULAR_PRICED_PRODUCT_NAME, + INVALID_COUPON, +} from '../checkout/constants'; const test = base.extend< { checkoutPageObject: CheckoutPage } >( { checkoutPageObject: async ( { page }, use ) => { @@ -43,7 +46,7 @@ test.describe( 'Shopper → Notice Templates', () => { await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME ); } ); - test( 'default classic notice templates are visible', async ( { + test( 'default classic notice templates, except for coupon errors, are visible', async ( { frontendUtils, page, } ) => { @@ -73,7 +76,7 @@ test.describe( 'Shopper → Notice Templates', () => { // We're explicitly checking the CSS classes of the classic notices. await expect( page.locator( '.woocommerce-notices-wrapper .woocommerce-error' ) - ).toBeVisible(); + ).toBeHidden(); await page.getByLabel( 'Remove Polo from cart' ).click(); @@ -87,7 +90,7 @@ test.describe( 'Shopper → Notice Templates', () => { ).toBeVisible(); } ); - test( 'custom classic notice templates are visible by template overwrite', async ( { + test( 'custom classic notice templates, except for coupon errors, are visible by template overwrite', async ( { requestUtils, frontendUtils, page, @@ -124,7 +127,7 @@ test.describe( 'Shopper → Notice Templates', () => { // We're explicitly checking the CSS classes of the classic notices. await expect( page.locator( '.woocommerce-notices-wrapper .woocommerce-error' ) - ).toBeVisible(); + ).toBeHidden(); await page.getByLabel( 'Remove Polo from cart' ).click(); @@ -142,7 +145,7 @@ test.describe( 'Shopper → Notice Templates', () => { await requestUtils.activateTheme( CLASSIC_THEME_SLUG ); } ); - test( 'custom block notice templates are visible by template overwrite', async ( { + test( 'custom block notice templates, except for coupon errors, are visible by template overwrite', async ( { requestUtils, frontendUtils, page, @@ -174,10 +177,10 @@ test.describe( 'Shopper → Notice Templates', () => { page.getByText( 'BLOCK ERROR NOTICE: Coupon code already applied!' ) ).toBeVisible(); - // We're explicitly checking the CSS classes of the block notices, and that the SVG is visible. + // We're explicitly checking the CSS classes of the block notices, and that the SVG is hidden. await expect( page.locator( '.wc-block-components-notice-banner.is-error svg' ) - ).toBeVisible(); + ).toBeHidden(); await page.getByLabel( 'Remove Polo from cart' ).click(); @@ -193,7 +196,7 @@ test.describe( 'Shopper → Notice Templates', () => { await requestUtils.activateTheme( CLASSIC_THEME_SLUG ); } ); - test( 'default block notice templates are visible by filter', async ( { + test( 'default block notice templates, except for coupon errors, are visible by filter', async ( { requestUtils, frontendUtils, page, @@ -223,10 +226,10 @@ test.describe( 'Shopper → Notice Templates', () => { page.getByText( 'Coupon code already applied!' ) ).toBeVisible(); - // We're explicitly checking the CSS classes and that the SVG is visible. + // We're explicitly checking the CSS classes and that the SVG is hidden. await expect( page.locator( '.wc-block-components-notice-banner.is-error svg' ) - ).toBeVisible(); + ).toBeHidden(); await page.getByLabel( 'Remove Polo from cart' ).click(); @@ -241,4 +244,26 @@ test.describe( 'Shopper → Notice Templates', () => { await requestUtils.activateTheme( CLASSIC_THEME_SLUG ); } ); + + test( 'coupon inline notice is visible', async ( { + frontendUtils, + page, + } ) => { + await frontendUtils.goToCartShortcode(); + await page.getByPlaceholder( 'Coupon code' ).fill( INVALID_COUPON ); + await page.getByRole( 'button', { name: 'Apply coupon' } ).click(); + + await expect( + page.getByText( `Coupon "${ INVALID_COUPON }" does not exist!`, { + exact: true, + } ) + ).toBeVisible(); + + // We're explicitly checking the CSS classes of the block notices, and that the SVG is hidden. + await expect( + page.locator( '.wc-block-components-notice-banner.is-error svg' ) + ).toBeHidden(); + + await expect( page.locator( '.coupon-error-notice' ) ).toBeVisible(); + } ); } ); diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/cart/cart-checkout-block-shipping.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/cart/cart-checkout-block-shipping.block_theme.spec.ts index 98380a90726..2d404e92bae 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/cart/cart-checkout-block-shipping.block_theme.spec.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/cart/cart-checkout-block-shipping.block_theme.spec.ts @@ -78,7 +78,7 @@ test.describe( 'Shopper → Shipping', () => { await expect( userPage.getByText( - 'Shipping options will be displayed here after entering your full shipping addres' + 'Shipping options will be displayed here after entering your full shipping address' ) ).toBeVisible(); @@ -86,7 +86,7 @@ test.describe( 'Shopper → Shipping', () => { await expect( userPage.getByText( - 'Shipping options will be displayed here after entering your full shipping addres' + 'Shipping options will be displayed here after entering your full shipping address' ) ).toBeHidden(); } ); diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/checkout/constants.ts b/plugins/woocommerce-blocks/tests/e2e/tests/checkout/constants.ts index e6f8276ade6..bbd35e81533 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/checkout/constants.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/checkout/constants.ts @@ -6,3 +6,4 @@ export const FLAT_RATE_SHIPPING_NAME = 'Flat rate shipping'; export const FLAT_RATE_SHIPPING_PRICE = '$10.00'; export const DISCOUNTED_PRODUCT_NAME = 'Cap'; export const REGULAR_PRICED_PRODUCT_NAME = 'Polo'; +export const INVALID_COUPON = 'invalidcoupon'; diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/featured-category/featured-category.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/featured-category/featured-category.block_theme.spec.ts index 48c2273b742..c73c6b4c56c 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/featured-category/featured-category.block_theme.spec.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/featured-category/featured-category.block_theme.spec.ts @@ -56,11 +56,11 @@ test.describe( `${ blockData.slug } Block`, () => { const blockLocator = await editor.getBlockByName( blockData.slug ); await blockLocator.getByText( 'Test Category' ).click(); await blockLocator.getByText( 'Done' ).click(); - await editor.page.getByLabel( 'Edit category image' ).click(); - await editor.page.getByLabel( 'Rotate' ).click(); + await editor.clickBlockToolbarButton( 'Edit category image' ); + await editor.clickBlockToolbarButton( 'Rotate' ); await editor.page.getByRole( 'button', { name: 'Apply' } ).click(); await expect( - editor.page.locator( 'img[alt="Test Category"][src*="-edited"]' ) + editor.canvas.locator( 'img[alt="Test Category"][src*="-edited"]' ) ).toBeVisible(); } ); } ); diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/featured-product/featured-product.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/featured-product/featured-product.block_theme.spec.ts index 54056ab2485..91f7ff6a0c3 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/featured-product/featured-product.block_theme.spec.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/featured-product/featured-product.block_theme.spec.ts @@ -35,11 +35,11 @@ test.describe( `${ blockData.slug } Block`, () => { const blockLocator = await editor.getBlockByName( blockData.slug ); await blockLocator.getByText( 'Album' ).click(); await blockLocator.getByText( 'Done' ).click(); - await editor.page.getByLabel( 'Edit product image' ).click(); - await editor.page.getByLabel( 'Rotate' ).click(); + await editor.clickBlockToolbarButton( 'Edit product image' ); + await editor.clickBlockToolbarButton( 'Rotate' ); await editor.page.getByRole( 'button', { name: 'Apply' } ).click(); await expect( - editor.page.locator( 'img[alt="Album"][src*="-edited"]' ) + editor.canvas.locator( 'img[alt="Album"][src*="-edited"]' ) ).toBeVisible(); } ); } ); diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/filter-blocks/price-filter.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/filter-blocks/price-filter.block_theme.spec.ts index 5172c32e94e..796fd4af488 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/filter-blocks/price-filter.block_theme.spec.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/filter-blocks/price-filter.block_theme.spec.ts @@ -93,7 +93,10 @@ test.describe( 'Product Filter: Price Filter Block', () => { ); const minPriceInput = leftInputContainer.locator( '.min' ); await minPriceInput.fill( String( defaultMinRange ) ); - await minPriceInput.blur(); + + await page.waitForURL( + ( url ) => ! url.href.includes( 'min_price' ) + ); // Max price input field const rightInputContainer = page.locator( @@ -101,7 +104,10 @@ test.describe( 'Product Filter: Price Filter Block', () => { ); const maxPriceInput = rightInputContainer.locator( '.max' ); await maxPriceInput.fill( String( defaultMaxRange ) ); - await maxPriceInput.blur(); + + await page.waitForURL( + ( url ) => ! url.href.includes( 'max_price' ) + ); const button = page.getByRole( 'button', { name: 'Clear' } ); diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/page-content-wrapper/page-content-wrapper.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/page-content-wrapper/page-content-wrapper.block_theme.spec.ts index 2e298354992..fa39502fe26 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/page-content-wrapper/page-content-wrapper.block_theme.spec.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/page-content-wrapper/page-content-wrapper.block_theme.spec.ts @@ -54,7 +54,7 @@ for ( const template of templates ) { // Prevent trying to insert the paragraph block before the editor is // ready. await expect( - page.locator( template.blockClassName ) + editor.canvas.locator( template.blockClassName ) ).toBeVisible(); await editor.insertBlock( { diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/price-filter/price-filter.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/price-filter/price-filter.block_theme.spec.ts index 644aa5a08e1..36d10574658 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/price-filter/price-filter.block_theme.spec.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/price-filter/price-filter.block_theme.spec.ts @@ -44,15 +44,17 @@ test.describe( `${ blockData.name } Block - editor side`, () => { await editor.openDocumentSettingsSidebar(); } ); - test( "should allow changing the block's title", async ( { page } ) => { + test( "should allow changing the block's title", async ( { editor } ) => { const textSelector = '.wp-block-woocommerce-filter-wrapper .wp-block-heading'; const title = 'New Title'; - await page.locator( textSelector ).fill( title ); + await editor.canvas.locator( textSelector ).fill( title ); - await expect( page.locator( textSelector ) ).toHaveText( title ); + await expect( editor.canvas.locator( textSelector ) ).toHaveText( + title + ); } ); test( 'should allow changing the display style', async ( { @@ -359,6 +361,12 @@ test.describe( `${ blockData.name } Block - with Product Collection`, () => { await maxPriceInput.dblclick(); await maxPriceInput.fill( '$5' ); + + const resetPriceFilterButton = page.getByRole( 'button', { + name: 'Reset price filter', + } ); + await expect( resetPriceFilterButton ).toBeVisible(); + await page .getByRole( 'button', { name: 'Apply price filter' } ) .click(); diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/collections.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/collections.block_theme.spec.ts new file mode 100644 index 00000000000..eac6f622591 --- /dev/null +++ b/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/collections.block_theme.spec.ts @@ -0,0 +1,221 @@ +/** + * External dependencies + */ +import { test as base, expect } from '@woocommerce/e2e-utils'; + +/** + * Internal dependencies + */ +import ProductCollectionPage, { SELECTORS } from './product-collection.page'; + +const test = base.extend< { pageObject: ProductCollectionPage } >( { + pageObject: async ( { page, admin, editor }, use ) => { + const pageObject = new ProductCollectionPage( { + page, + admin, + editor, + } ); + await use( pageObject ); + }, +} ); + +test.describe( 'Product Collection', () => { + test.describe( 'Collections', () => { + test( 'New Arrivals Collection can be added and displays proper products', async ( { + pageObject, + } ) => { + await pageObject.createNewPostAndInsertBlock( 'newArrivals' ); + + // New Arrivals are by default filtered to display products from last 7 days. + // Products in our test env have creation date set to much older, hence + // no products are expected to be displayed by default. + await expect( pageObject.products ).toHaveCount( 0 ); + + await pageObject.publishAndGoToFrontend(); + + await expect( pageObject.products ).toHaveCount( 0 ); + } ); + + // When creating reviews programmatically the ratings are not propagated + // properly so products order by rating is undeterministic in test env. + // eslint-disable-next-line playwright/no-skipped-test + test.skip( 'Top Rated Collection can be added and displays proper products', async ( { + pageObject, + } ) => { + await pageObject.createNewPostAndInsertBlock( 'topRated' ); + + const topRatedProducts = [ + 'V Neck T Shirt', + 'Hoodie', + 'Hoodie with Logo', + 'T-Shirt', + 'Beanie', + ]; + + await expect( pageObject.products ).toHaveCount( 5 ); + await expect( pageObject.productTitles ).toHaveText( + topRatedProducts + ); + + await pageObject.publishAndGoToFrontend(); + + await expect( pageObject.products ).toHaveCount( 5 ); + } ); + + // There's no orders in test env so the order of Best Sellers + // is undeterministic in test env. Requires further work. + // eslint-disable-next-line playwright/no-skipped-test + test.skip( 'Best Sellers Collection can be added and displays proper products', async ( { + pageObject, + } ) => { + await pageObject.createNewPostAndInsertBlock( 'bestSellers' ); + + const bestSellersProducts = [ + 'Album', + 'Hoodie', + 'Single', + 'Hoodie with Logo', + 'T-Shirt with Logo', + ]; + + await expect( pageObject.products ).toHaveCount( 5 ); + await expect( pageObject.productTitles ).toHaveText( + bestSellersProducts + ); + + await pageObject.publishAndGoToFrontend(); + + await expect( pageObject.products ).toHaveCount( 5 ); + } ); + + test( 'On Sale Collection can be added and displays proper products', async ( { + pageObject, + } ) => { + await pageObject.createNewPostAndInsertBlock( 'onSale' ); + + const onSaleProducts = [ + 'Beanie', + 'Beanie with Logo', + 'Belt', + 'Cap', + 'Hoodie', + ]; + + await expect( pageObject.products ).toHaveCount( 5 ); + await expect( pageObject.productTitles ).toHaveText( + onSaleProducts + ); + + await pageObject.publishAndGoToFrontend(); + + await expect( pageObject.products ).toHaveCount( 5 ); + } ); + + test( 'Featured Collection can be added and displays proper products', async ( { + pageObject, + } ) => { + await pageObject.createNewPostAndInsertBlock( 'featured' ); + + const featuredProducts = [ + 'Cap', + 'Hoodie with Zipper', + 'Sunglasses', + 'V-Neck T-Shirt', + ]; + + await expect( pageObject.products ).toHaveCount( 4 ); + await expect( pageObject.productTitles ).toHaveText( + featuredProducts + ); + + await pageObject.publishAndGoToFrontend(); + + await expect( pageObject.products ).toHaveCount( 4 ); + } ); + + test( 'Product Catalog Collection can be added in post and syncs query with template', async ( { + pageObject, + } ) => { + await pageObject.createNewPostAndInsertBlock( 'productCatalog' ); + + const usePageContextToggle = pageObject + .locateSidebarSettings() + .locator( `${ SELECTORS.usePageContextControl } input` ); + + await expect( usePageContextToggle ).toBeVisible(); + await expect( pageObject.products ).toHaveCount( 9 ); + + await pageObject.publishAndGoToFrontend(); + + await expect( pageObject.products ).toHaveCount( 9 ); + } ); + + test( 'Product Catalog Collection can be added in product archive and syncs query with template', async ( { + pageObject, + editor, + admin, + } ) => { + await admin.visitSiteEditor( { + postId: 'woocommerce/woocommerce//archive-product', + postType: 'wp_template', + canvas: 'edit', + } ); + + await editor.setContent( '' ); + + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInTemplate(); + await editor.openDocumentSettingsSidebar(); + + const sidebarSettings = pageObject.locateSidebarSettings(); + const input = sidebarSettings.locator( + `${ SELECTORS.usePageContextControl } input` + ); + + await expect( input ).toBeChecked(); + } ); + + test.describe( 'Have hidden implementation in UI', () => { + test( 'New Arrivals', async ( { pageObject } ) => { + await pageObject.createNewPostAndInsertBlock( 'newArrivals' ); + const input = await pageObject.getOrderByElement(); + + await expect( input ).toBeHidden(); + } ); + + test( 'Top Rated', async ( { pageObject } ) => { + await pageObject.createNewPostAndInsertBlock( 'topRated' ); + const input = await pageObject.getOrderByElement(); + + await expect( input ).toBeHidden(); + } ); + + test( 'Best Sellers', async ( { pageObject } ) => { + await pageObject.createNewPostAndInsertBlock( 'bestSellers' ); + const input = await pageObject.getOrderByElement(); + + await expect( input ).toBeHidden(); + } ); + + test( 'On Sale', async ( { pageObject } ) => { + await pageObject.createNewPostAndInsertBlock( 'onSale' ); + const sidebarSettings = pageObject.locateSidebarSettings(); + const input = sidebarSettings.getByLabel( + SELECTORS.onSaleControlLabel + ); + + await expect( input ).toBeHidden(); + } ); + + test( 'Featured', async ( { pageObject } ) => { + await pageObject.createNewPostAndInsertBlock( 'featured' ); + const sidebarSettings = pageObject.locateSidebarSettings(); + const input = sidebarSettings.getByLabel( + SELECTORS.featuredControlLabel + ); + + await expect( input ).toBeHidden(); + } ); + } ); + } ); +} ); diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/inspector-controls.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/inspector-controls.block_theme.spec.ts new file mode 100644 index 00000000000..92b1723c6a3 --- /dev/null +++ b/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/inspector-controls.block_theme.spec.ts @@ -0,0 +1,639 @@ +/** + * External dependencies + */ +import { test as base, expect } from '@woocommerce/e2e-utils'; + +/** + * Internal dependencies + */ +import ProductCollectionPage, { SELECTORS } from './product-collection.page'; + +const test = base.extend< { pageObject: ProductCollectionPage } >( { + pageObject: async ( { page, admin, editor }, use ) => { + const pageObject = new ProductCollectionPage( { + page, + admin, + editor, + } ); + await use( pageObject ); + }, +} ); + +test.describe( 'Product Collection', () => { + test.describe( 'Inspector Controls', () => { + test( 'Reflects the correct number of columns according to sidebar settings', async ( { + pageObject, + } ) => { + await pageObject.createNewPostAndInsertBlock(); + + await pageObject.setNumberOfColumns( 2 ); + await expect( pageObject.productTemplate ).toHaveClass( + /columns-2/ + ); + + await pageObject.setNumberOfColumns( 4 ); + await expect( pageObject.productTemplate ).toHaveClass( + /columns-4/ + ); + + await pageObject.publishAndGoToFrontend(); + + await expect( pageObject.productTemplate ).toHaveClass( + /columns-4/ + ); + } ); + + test( 'Order By - sort products by title in descending order correctly', async ( { + pageObject, + } ) => { + await pageObject.createNewPostAndInsertBlock(); + + const sortedTitles = [ + 'WordPress Pennant', + 'V-Neck T-Shirt', + 'T-Shirt with Logo', + 'T-Shirt', + /Sunglasses/, // In the frontend it's "Protected: Sunglasses" + 'Single', + 'Polo', + 'Long Sleeve Tee', + 'Logo Collection', + ]; + + await pageObject.setOrderBy( 'title/desc' ); + await expect( pageObject.productTitles ).toHaveText( sortedTitles ); + + await pageObject.publishAndGoToFrontend(); + await expect( pageObject.productTitles ).toHaveText( sortedTitles ); + } ); + + // Products can be filtered based on 'on sale' status. + test( 'Products can be filtered based on "on sale" status', async ( { + pageObject, + } ) => { + await pageObject.createNewPostAndInsertBlock(); + + let allProducts = pageObject.products; + let saleProducts = pageObject.products.filter( { + hasText: 'Product on sale', + } ); + + await expect( allProducts ).toHaveCount( 9 ); + await expect( saleProducts ).toHaveCount( 6 ); + + await pageObject.setShowOnlyProductsOnSale( { + onSale: true, + } ); + + await expect( allProducts ).toHaveCount( 6 ); + await expect( saleProducts ).toHaveCount( 6 ); + + await pageObject.publishAndGoToFrontend(); + await pageObject.refreshLocators( 'frontend' ); + allProducts = pageObject.products; + saleProducts = pageObject.products.filter( { + hasText: 'Product on sale', + } ); + + await expect( allProducts ).toHaveCount( 6 ); + await expect( saleProducts ).toHaveCount( 6 ); + } ); + + test( 'Products can be filtered based on selection in handpicked products option', async ( { + pageObject, + } ) => { + await pageObject.createNewPostAndInsertBlock(); + + await pageObject.addFilter( 'Show Hand-picked Products' ); + + const filterName = 'Hand-picked Products'; + await pageObject.setFilterComboboxValue( filterName, [ 'Album' ] ); + await expect( pageObject.products ).toHaveCount( 1 ); + + const productNames = [ 'Album', 'Cap' ]; + await pageObject.setFilterComboboxValue( filterName, productNames ); + await expect( pageObject.products ).toHaveCount( 2 ); + await expect( pageObject.productTitles ).toHaveText( productNames ); + + await pageObject.publishAndGoToFrontend(); + await expect( pageObject.products ).toHaveCount( 2 ); + await expect( pageObject.productTitles ).toHaveText( productNames ); + } ); + + test( 'Products can be filtered based on keyword.', async ( { + pageObject, + } ) => { + await pageObject.createNewPostAndInsertBlock(); + + await pageObject.addFilter( 'Keyword' ); + + await pageObject.setKeyword( 'Album' ); + await expect( pageObject.productTitles ).toHaveText( [ 'Album' ] ); + + await pageObject.setKeyword( 'Cap' ); + await expect( pageObject.productTitles ).toHaveText( [ 'Cap' ] ); + + await pageObject.publishAndGoToFrontend(); + await expect( pageObject.productTitles ).toHaveText( [ 'Cap' ] ); + } ); + + test( 'Products can be filtered based on category.', async ( { + pageObject, + } ) => { + await pageObject.createNewPostAndInsertBlock(); + + const filterName = 'Product categories'; + await pageObject.addFilter( 'Show product categories' ); + await pageObject.setFilterComboboxValue( filterName, [ + 'Clothing', + ] ); + await expect( pageObject.productTitles ).toHaveText( [ + 'Logo Collection', + ] ); + + await pageObject.setFilterComboboxValue( filterName, [ + 'Accessories', + ] ); + const accessoriesProductNames = [ + 'Beanie', + 'Beanie with Logo', + 'Belt', + 'Cap', + 'Sunglasses', + ]; + await expect( pageObject.productTitles ).toHaveText( + accessoriesProductNames + ); + + await pageObject.publishAndGoToFrontend(); + + const frontendAccessoriesProductNames = [ + 'Beanie', + 'Beanie with Logo', + 'Belt', + 'Cap', + 'Protected: Sunglasses', + ]; + await expect( pageObject.productTitles ).toHaveText( + frontendAccessoriesProductNames + ); + } ); + + test( 'Products can be filtered based on tags.', async ( { + pageObject, + } ) => { + await pageObject.createNewPostAndInsertBlock(); + + const filterName = 'Product tags'; + await pageObject.addFilter( 'Show product tags' ); + await pageObject.setFilterComboboxValue( filterName, [ + 'Recommended', + ] ); + await expect( pageObject.productTitles ).toHaveText( [ + 'Beanie', + 'Hoodie', + ] ); + + await pageObject.publishAndGoToFrontend(); + await expect( pageObject.productTitles ).toHaveText( [ + 'Beanie', + 'Hoodie', + ] ); + } ); + + test( 'Products can be filtered based on product attributes like color, size etc.', async ( { + pageObject, + } ) => { + await pageObject.createNewPostAndInsertBlock(); + + await pageObject.addFilter( 'Show Product Attributes' ); + await pageObject.setProductAttribute( 'Color', 'Green' ); + + await expect( pageObject.products ).toHaveCount( 3 ); + + await pageObject.setProductAttribute( 'Size', 'Large' ); + + await expect( pageObject.products ).toHaveCount( 1 ); + + await pageObject.publishAndGoToFrontend(); + + await expect( pageObject.products ).toHaveCount( 1 ); + } ); + + test( 'Products can be filtered based on stock status (in stock, out of stock, or backorder).', async ( { + pageObject, + } ) => { + await pageObject.createNewPostAndInsertBlock(); + + await pageObject.setFilterComboboxValue( 'Stock status', [ + 'Out of stock', + ] ); + + await expect( pageObject.productTitles ).toHaveText( [ + 'T-Shirt with Logo', + ] ); + + await pageObject.publishAndGoToFrontend(); + + await expect( pageObject.productTitles ).toHaveText( [ + 'T-Shirt with Logo', + ] ); + } ); + + test( 'Products can be filtered based on featured status.', async ( { + pageObject, + } ) => { + await pageObject.createNewPostAndInsertBlock(); + + await expect( pageObject.products ).toHaveCount( 9 ); + + await pageObject.addFilter( 'Featured' ); + await pageObject.setShowOnlyFeaturedProducts( { + featured: true, + } ); + + // In test data we have only 4 featured products. + await expect( pageObject.products ).toHaveCount( 4 ); + + await pageObject.publishAndGoToFrontend(); + + await expect( pageObject.products ).toHaveCount( 4 ); + } ); + + test( 'Products can be filtered based on created date.', async ( { + pageObject, + } ) => { + await pageObject.createNewPostAndInsertBlock(); + + await expect( pageObject.products ).toHaveCount( 9 ); + + await pageObject.addFilter( 'Created' ); + await pageObject.setCreatedFilter( { + operator: 'within', + range: 'last3months', + } ); + + // Products are created with the fixed publish date back in 2019 + // so there's no products published in last 3 months. + await expect( pageObject.products ).toHaveCount( 0 ); + + await pageObject.setCreatedFilter( { + operator: 'before', + range: 'last3months', + } ); + + await expect( pageObject.products ).toHaveCount( 9 ); + + await pageObject.publishAndGoToFrontend(); + + await expect( pageObject.products ).toHaveCount( 9 ); + } ); + + test( 'Products can be filtered based on price range.', async ( { + pageObject, + } ) => { + await pageObject.createNewPostAndInsertBlock(); + + await expect( pageObject.products ).toHaveCount( 9 ); + + await pageObject.addFilter( 'Price Range' ); + await pageObject.setPriceRange( { + min: '18.33', + } ); + + await expect( pageObject.products ).toHaveCount( 7 ); + + await pageObject.setPriceRange( { + min: '15.28', + max: '17.21', + } ); + + await expect( pageObject.products ).toHaveCount( 1 ); + + await pageObject.setPriceRange( { + max: '17.29', + } ); + + await expect( pageObject.products ).toHaveCount( 4 ); + + await pageObject.publishAndGoToFrontend(); + + await expect( pageObject.products ).toHaveCount( 4 ); + } ); + + // See https://github.com/woocommerce/woocommerce/pull/49917 + test( 'Price range is inclusive in both editor and frontend.', async ( { + page, + pageObject, + editor, + } ) => { + await pageObject.createNewPostAndInsertBlock(); + + await expect( pageObject.products ).toHaveCount( 9 ); + + await pageObject.addFilter( 'Price Range' ); + await pageObject.setPriceRange( { + min: '45', + max: '55', + } ); + + // Wait for the products to be filtered. + await expect( pageObject.products ).not.toHaveCount( 9 ); + + await expect( + pageObject.products.filter( { hasText: '$45.00' } ) + ).not.toHaveCount( 0 ); + await expect( + pageObject.products.filter( { hasText: '$55.00' } ) + ).not.toHaveCount( 0 ); + + // Reset the price range. + await pageObject.setPriceRange( { + min: '0', + max: '0', + } ); + + await expect( pageObject.products ).toHaveCount( 9 ); + + await editor.insertBlock( { + name: 'woocommerce/filter-wrapper', + attributes: { filterType: 'price-filter' }, + } ); + + await pageObject.publishAndGoToFrontend(); + + await expect( pageObject.products ).toHaveCount( 9 ); + + await page + .getByRole( 'textbox', { + name: 'Filter products by minimum', + } ) + .dblclick(); + await page.keyboard.type( '45' ); + + await page + .getByRole( 'textbox', { + name: 'Filter products by maximum', + } ) + .dblclick(); + await page.keyboard.type( '55' ); + + await page.keyboard.press( 'Tab' ); + + // Wait for the products to be filtered. + await expect( pageObject.products ).not.toHaveCount( 9 ); + + await expect( + pageObject.products.filter( { hasText: '$45.00' } ) + ).not.toHaveCount( 0 ); + await expect( + pageObject.products.filter( { hasText: '$55.00' } ) + ).not.toHaveCount( 0 ); + } ); + + test.describe( '"Use page context" control', () => { + test( 'should be visible on posts', async ( { pageObject } ) => { + await pageObject.createNewPostAndInsertBlock(); + + await expect( + pageObject + .locateSidebarSettings() + .locator( SELECTORS.usePageContextControl ) + ).toBeVisible(); + } ); + + [ + 'woocommerce/woocommerce//archive-product', + 'woocommerce/woocommerce//taxonomy-product_cat', + 'woocommerce/woocommerce//taxonomy-product_tag', + 'woocommerce/woocommerce//taxonomy-product_attribute', + 'woocommerce/woocommerce//product-search-results', + ].forEach( ( slug ) => { + test( `should be visible in archive template: ${ slug }`, async ( { + pageObject, + editor, + } ) => { + await pageObject.goToEditorTemplate( slug ); + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInTemplate(); + await pageObject.focusProductCollection(); + await editor.openDocumentSettingsSidebar(); + + await expect( + pageObject + .locateSidebarSettings() + .locator( SELECTORS.usePageContextControl ) + ).toBeVisible(); + } ); + } ); + + [ + 'woocommerce/woocommerce//single-product', + 'twentytwentyfour//home', + 'twentytwentyfour//index', + ].forEach( ( slug ) => { + test( `should be visible in non-archive template: ${ slug }`, async ( { + pageObject, + editor, + } ) => { + await pageObject.goToEditorTemplate( slug ); + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInTemplate(); + await pageObject.focusProductCollection(); + await editor.openDocumentSettingsSidebar(); + + await expect( + pageObject + .locateSidebarSettings() + .locator( SELECTORS.usePageContextControl ) + ).toBeVisible(); + } ); + } ); + + test( 'should work as expected in Product Catalog template', async ( { + pageObject, + editor, + } ) => { + await pageObject.goToEditorTemplate(); + await pageObject.focusProductCollection(); + await editor.openDocumentSettingsSidebar(); + + const sidebarSettings = pageObject.locateSidebarSettings(); + + // Inherit query from template should be visible & enabled by default + await expect( + sidebarSettings.locator( SELECTORS.usePageContextControl ) + ).toBeVisible(); + await expect( + sidebarSettings.locator( + `${ SELECTORS.usePageContextControl } input` + ) + ).toBeChecked(); + + // "On sale control" should be hidden when inherit query from template is enabled + await expect( + sidebarSettings.getByLabel( SELECTORS.onSaleControlLabel ) + ).toBeHidden(); + + // "On sale control" should be visible when inherit query from template is disabled + await pageObject.setInheritQueryFromTemplate( false ); + await expect( + sidebarSettings.getByLabel( SELECTORS.onSaleControlLabel ) + ).toBeVisible(); + + // "On sale control" should retain its state when inherit query from template is enabled again + await pageObject.setShowOnlyProductsOnSale( { + onSale: true, + isLocatorsRefreshNeeded: false, + } ); + await expect( + sidebarSettings.getByLabel( SELECTORS.onSaleControlLabel ) + ).toBeChecked(); + await pageObject.setInheritQueryFromTemplate( true ); + await expect( + sidebarSettings.getByLabel( SELECTORS.onSaleControlLabel ) + ).toBeHidden(); + await pageObject.setInheritQueryFromTemplate( false ); + await expect( + sidebarSettings.getByLabel( SELECTORS.onSaleControlLabel ) + ).toBeVisible(); + await expect( + sidebarSettings.getByLabel( SELECTORS.onSaleControlLabel ) + ).toBeChecked(); + } ); + + test( 'is enabled by default unless already enabled elsewhere', async ( { + pageObject, + editor, + } ) => { + const productCollection = editor.canvas.getByLabel( + 'Block: Product Collection', + { exact: true } + ); + const usePageContextToggle = pageObject + .locateSidebarSettings() + .locator( SELECTORS.usePageContextControl ) + .locator( 'input' ); + + // First Product Catalog + // Option should be visible & ENABLED by default + await pageObject.goToEditorTemplate(); + await editor.selectBlocks( productCollection.first() ); + await editor.openDocumentSettingsSidebar(); + + await expect( usePageContextToggle ).toBeChecked(); + + // Second Product Catalog + // Option should be visible & DISABLED by default + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInTemplate( 'productCatalog' ); + await editor.selectBlocks( productCollection.last() ); + + await expect( usePageContextToggle ).not.toBeChecked(); + + // Disable the option in the first Product Catalog + await editor.selectBlocks( productCollection.first() ); + await usePageContextToggle.click(); + + // Third Product Catalog + // Option should be visible & ENABLED by default + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInTemplate( 'productCatalog' ); + await editor.selectBlocks( productCollection.last() ); + + await expect( usePageContextToggle ).toBeChecked(); + } ); + + test( 'allows filtering in non-archive context', async ( { + pageObject, + editor, + page, + } ) => { + await pageObject.createNewPostAndInsertBlock(); + + await expect( pageObject.products ).toHaveCount( 9 ); + + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInPost( 'productCatalog' ); + + await expect( pageObject.products ).toHaveCount( 18 ); + + await page.getByLabel( 'Toggle block inserter' ).click(); + await page.getByRole( 'tab', { name: 'Patterns' } ).click(); + await page + .getByPlaceholder( 'Search' ) + .fill( 'product filters' ); + await page.getByLabel( 'Product Filters' ).click(); + + const postId = await editor.publishPost(); + await page.goto( `/?p=${ postId }` ); + + const productCollection = page.locator( + '.wp-block-woocommerce-product-collection' + ); + + await expect( + productCollection.first().locator( SELECTORS.product ) + ).toHaveCount( 9 ); + await expect( + productCollection.last().locator( SELECTORS.product ) + ).toHaveCount( 9 ); + + await page + .getByRole( 'textbox', { + name: 'Filter products by maximum', + } ) + .dblclick(); + await page.keyboard.type( '10' ); + await page.keyboard.press( 'Tab' ); + + await expect( + productCollection.first().locator( SELECTORS.product ) + ).toHaveCount( 1 ); + await expect( + productCollection.last().locator( SELECTORS.product ) + ).toHaveCount( 9 ); + } ); + + test( 'correctly combines editor and front-end filters', async ( { + pageObject, + editor, + page, + } ) => { + await pageObject.createNewPostAndInsertBlock(); + + await expect( pageObject.products ).toHaveCount( 9 ); + + await pageObject.addFilter( 'Show product categories' ); + await pageObject.setFilterComboboxValue( 'Product categories', [ + 'Music', + ] ); + + await page.getByLabel( 'Toggle block inserter' ).click(); + await page.getByRole( 'tab', { name: 'Patterns' } ).click(); + await page + .getByPlaceholder( 'Search' ) + .fill( 'product filters' ); + await page.getByLabel( 'Product Filters' ).click(); + + await expect( pageObject.products ).toHaveCount( 2 ); + + const postId = await editor.publishPost(); + await page.goto( `/?p=${ postId }` ); + await pageObject.refreshLocators( 'frontend' ); + + await expect( pageObject.products ).toHaveCount( 2 ); + + await page + .getByRole( 'textbox', { + name: 'Filter products by maximum', + } ) + .dblclick(); + await page.keyboard.type( '5' ); + await page.keyboard.press( 'Tab' ); + + await expect( pageObject.products ).toHaveCount( 1 ); + } ); + } ); + } ); +} ); diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.block_theme.spec.ts index f1f0f2575dc..e38db1b526a 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.block_theme.spec.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.block_theme.spec.ts @@ -59,27 +59,110 @@ test.describe( 'Product Collection', () => { } ); await expect( - page.getByLabel( 'Block: Products (Beta)' ) + editor.canvas.getByLabel( 'Block: Products (Beta)' ) ).toBeVisible(); - await page.getByRole( 'button', { name: 'Start blank' } ).click(); - await page.getByLabel( 'Title & Date' ).click(); + await editor.canvas + .getByRole( 'button', { name: 'Start blank' } ) + .click(); + await editor.canvas.getByLabel( 'Title & Date' ).click(); await page .getByRole( 'button', { name: 'Upgrade to Product Collection' } ) .click(); await expect( - page.getByLabel( 'Block: Products (Beta)' ) + editor.canvas.getByLabel( 'Block: Products (Beta)' ) ).toBeHidden(); await expect( - page.getByLabel( 'Block: Product Collection' ).first() + editor.canvas.getByLabel( 'Block: Product Collection' ).first() ).toBeVisible(); await expect( page.getByRole( 'button', { name: 'Choose collection' } ) ).toBeVisible(); } ); + test.describe( 'when no results are found', () => { + test.beforeEach( async ( { admin } ) => { + await admin.createNewPost(); + } ); + + test( 'does not render', async ( { page, editor, pageObject } ) => { + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInPost( 'featured' ); + await pageObject.addFilter( 'Price Range' ); + await pageObject.setPriceRange( { + max: '1', + } ); + + const featuredBlock = editor.canvas.getByLabel( 'Block: Featured' ); + + await expect( + featuredBlock.getByText( 'Featured products' ) + ).toBeVisible(); + // The "No results found" info is rendered in editor for all collections. + await expect( + featuredBlock.getByText( 'No results found' ) + ).toBeVisible(); + + await pageObject.publishAndGoToFrontend(); + + const content = page.locator( 'main' ); + + await expect( content ).not.toContainText( 'Featured products' ); + await expect( content ).not.toContainText( 'No results found' ); + } ); + + // This test ensures the runtime render state is correctly reset for + // each block. + test( 'does not prevent subsequent blocks from render', async ( { + page, + pageObject, + } ) => { + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInPost( 'featured' ); + await pageObject.addFilter( 'Price Range' ); + await pageObject.setPriceRange( { + max: '1', + } ); + + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInPost( 'topRated' ); + + await pageObject.refreshLocators( 'editor' ); + await expect( pageObject.products ).toHaveCount( 5 ); + + await pageObject.publishAndGoToFrontend(); + + await pageObject.refreshLocators( 'frontend' ); + await expect( pageObject.products ).toHaveCount( 5 ); + await expect( page.locator( 'main' ) ).not.toContainText( + 'Featured products' + ); + } ); + + test( 'renders if No Results block is present', async ( { + page, + editor, + pageObject, + } ) => { + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInPost( 'productCatalog' ); + await pageObject.addFilter( 'Price Range' ); + await pageObject.setPriceRange( { + max: '1', + } ); + + await expect( + editor.canvas.getByText( 'No results found' ) + ).toBeVisible(); + + await pageObject.publishAndGoToFrontend(); + + await expect( page.getByText( 'No results found' ) ).toBeVisible(); + } ); + } ); + test.describe( 'Renders correctly with all Product Elements', () => { const expectedProductContent = [ 'Beanie', // core/post-title @@ -93,11 +176,11 @@ test.describe( 'Product Collection', () => { 'Add to cart', // woocommerce/product-button ]; - test( 'In a post', async ( { page, pageObject } ) => { + test( 'In a post', async ( { page, editor, pageObject } ) => { await pageObject.createNewPostAndInsertBlock(); await expect( - page.locator( '[data-testid="product-image"]:visible' ) + editor.canvas.locator( '[data-testid="product-image"]:visible' ) ).toHaveCount( 9 ); await pageObject.insertProductElements(); @@ -163,617 +246,6 @@ test.describe( 'Product Collection', () => { } ); } ); - test.describe( 'Inspector Controls', () => { - test( 'Reflects the correct number of columns according to sidebar settings', async ( { - pageObject, - } ) => { - await pageObject.createNewPostAndInsertBlock(); - - await pageObject.setNumberOfColumns( 2 ); - await expect( pageObject.productTemplate ).toHaveClass( - /columns-2/ - ); - - await pageObject.setNumberOfColumns( 4 ); - await expect( pageObject.productTemplate ).toHaveClass( - /columns-4/ - ); - - await pageObject.publishAndGoToFrontend(); - - await expect( pageObject.productTemplate ).toHaveClass( - /columns-4/ - ); - } ); - - test( 'Order By - sort products by title in descending order correctly', async ( { - pageObject, - } ) => { - await pageObject.createNewPostAndInsertBlock(); - - const sortedTitles = [ - 'WordPress Pennant', - 'V-Neck T-Shirt', - 'T-Shirt with Logo', - 'T-Shirt', - /Sunglasses/, // In the frontend it's "Protected: Sunglasses" - 'Single', - 'Polo', - 'Long Sleeve Tee', - 'Logo Collection', - ]; - - await pageObject.setOrderBy( 'title/desc' ); - await expect( pageObject.productTitles ).toHaveText( sortedTitles ); - - await pageObject.publishAndGoToFrontend(); - await expect( pageObject.productTitles ).toHaveText( sortedTitles ); - } ); - - // Products can be filtered based on 'on sale' status. - test( 'Products can be filtered based on "on sale" status', async ( { - pageObject, - } ) => { - await pageObject.createNewPostAndInsertBlock(); - - const allProducts = pageObject.products; - const saleProducts = pageObject.products.filter( { - hasText: 'Product on sale', - } ); - - await expect( allProducts ).toHaveCount( 9 ); - await expect( saleProducts ).toHaveCount( 6 ); - - await pageObject.setShowOnlyProductsOnSale( { - onSale: true, - } ); - - await expect( allProducts ).toHaveCount( 6 ); - await expect( saleProducts ).toHaveCount( 6 ); - - await pageObject.publishAndGoToFrontend(); - - await expect( allProducts ).toHaveCount( 6 ); - await expect( saleProducts ).toHaveCount( 6 ); - } ); - - test( 'Products can be filtered based on selection in handpicked products option', async ( { - pageObject, - } ) => { - await pageObject.createNewPostAndInsertBlock(); - - await pageObject.addFilter( 'Show Hand-picked Products' ); - - const filterName = 'Hand-picked Products'; - await pageObject.setFilterComboboxValue( filterName, [ 'Album' ] ); - await expect( pageObject.products ).toHaveCount( 1 ); - - const productNames = [ 'Album', 'Cap' ]; - await pageObject.setFilterComboboxValue( filterName, productNames ); - await expect( pageObject.products ).toHaveCount( 2 ); - await expect( pageObject.productTitles ).toHaveText( productNames ); - - await pageObject.publishAndGoToFrontend(); - await expect( pageObject.products ).toHaveCount( 2 ); - await expect( pageObject.productTitles ).toHaveText( productNames ); - } ); - - test( 'Products can be filtered based on keyword.', async ( { - pageObject, - } ) => { - await pageObject.createNewPostAndInsertBlock(); - - await pageObject.addFilter( 'Keyword' ); - - await pageObject.setKeyword( 'Album' ); - await expect( pageObject.productTitles ).toHaveText( [ 'Album' ] ); - - await pageObject.setKeyword( 'Cap' ); - await expect( pageObject.productTitles ).toHaveText( [ 'Cap' ] ); - - await pageObject.publishAndGoToFrontend(); - await expect( pageObject.productTitles ).toHaveText( [ 'Cap' ] ); - } ); - - test( 'Products can be filtered based on category.', async ( { - pageObject, - } ) => { - await pageObject.createNewPostAndInsertBlock(); - - const filterName = 'Product categories'; - await pageObject.addFilter( 'Show product categories' ); - await pageObject.setFilterComboboxValue( filterName, [ - 'Clothing', - ] ); - await expect( pageObject.productTitles ).toHaveText( [ - 'Logo Collection', - ] ); - - await pageObject.setFilterComboboxValue( filterName, [ - 'Accessories', - ] ); - const accessoriesProductNames = [ - 'Beanie', - 'Beanie with Logo', - 'Belt', - 'Cap', - 'Sunglasses', - ]; - await expect( pageObject.productTitles ).toHaveText( - accessoriesProductNames - ); - - await pageObject.publishAndGoToFrontend(); - - const frontendAccessoriesProductNames = [ - 'Beanie', - 'Beanie with Logo', - 'Belt', - 'Cap', - 'Protected: Sunglasses', - ]; - await expect( pageObject.productTitles ).toHaveText( - frontendAccessoriesProductNames - ); - } ); - - test( 'Products can be filtered based on tags.', async ( { - pageObject, - } ) => { - await pageObject.createNewPostAndInsertBlock(); - - const filterName = 'Product tags'; - await pageObject.addFilter( 'Show product tags' ); - await pageObject.setFilterComboboxValue( filterName, [ - 'Recommended', - ] ); - await expect( pageObject.productTitles ).toHaveText( [ - 'Beanie', - 'Hoodie', - ] ); - - await pageObject.publishAndGoToFrontend(); - await expect( pageObject.productTitles ).toHaveText( [ - 'Beanie', - 'Hoodie', - ] ); - } ); - - test( 'Products can be filtered based on product attributes like color, size etc.', async ( { - pageObject, - } ) => { - await pageObject.createNewPostAndInsertBlock(); - - await pageObject.addFilter( 'Show Product Attributes' ); - await pageObject.setProductAttribute( 'Color', 'Green' ); - - await expect( pageObject.products ).toHaveCount( 3 ); - - await pageObject.setProductAttribute( 'Size', 'Large' ); - - await expect( pageObject.products ).toHaveCount( 1 ); - - await pageObject.publishAndGoToFrontend(); - - await expect( pageObject.products ).toHaveCount( 1 ); - } ); - - test( 'Products can be filtered based on stock status (in stock, out of stock, or backorder).', async ( { - pageObject, - } ) => { - await pageObject.createNewPostAndInsertBlock(); - - await pageObject.setFilterComboboxValue( 'Stock status', [ - 'Out of stock', - ] ); - - await expect( pageObject.productTitles ).toHaveText( [ - 'T-Shirt with Logo', - ] ); - - await pageObject.publishAndGoToFrontend(); - - await expect( pageObject.productTitles ).toHaveText( [ - 'T-Shirt with Logo', - ] ); - } ); - - test( 'Products can be filtered based on featured status.', async ( { - pageObject, - } ) => { - await pageObject.createNewPostAndInsertBlock(); - - await expect( pageObject.products ).toHaveCount( 9 ); - - await pageObject.addFilter( 'Featured' ); - await pageObject.setShowOnlyFeaturedProducts( { - featured: true, - } ); - - // In test data we have only 4 featured products. - await expect( pageObject.products ).toHaveCount( 4 ); - - await pageObject.publishAndGoToFrontend(); - - await expect( pageObject.products ).toHaveCount( 4 ); - } ); - - test( 'Products can be filtered based on created date.', async ( { - pageObject, - } ) => { - await pageObject.createNewPostAndInsertBlock(); - - await expect( pageObject.products ).toHaveCount( 9 ); - - await pageObject.addFilter( 'Created' ); - await pageObject.setCreatedFilter( { - operator: 'within', - range: 'last3months', - } ); - - // Products are created with the fixed publish date back in 2019 - // so there's no products published in last 3 months. - await expect( pageObject.products ).toHaveCount( 0 ); - - await pageObject.setCreatedFilter( { - operator: 'before', - range: 'last3months', - } ); - - await expect( pageObject.products ).toHaveCount( 9 ); - - await pageObject.publishAndGoToFrontend(); - - await expect( pageObject.products ).toHaveCount( 9 ); - } ); - - test( 'Products can be filtered based on price range.', async ( { - pageObject, - } ) => { - await pageObject.createNewPostAndInsertBlock(); - - await expect( pageObject.products ).toHaveCount( 9 ); - - await pageObject.addFilter( 'Price Range' ); - await pageObject.setPriceRange( { - min: '18.33', - } ); - - await expect( pageObject.products ).toHaveCount( 7 ); - - await pageObject.setPriceRange( { - min: '15.28', - max: '17.21', - } ); - - await expect( pageObject.products ).toHaveCount( 1 ); - - await pageObject.setPriceRange( { - max: '17.29', - } ); - - await expect( pageObject.products ).toHaveCount( 4 ); - - await pageObject.publishAndGoToFrontend(); - - await expect( pageObject.products ).toHaveCount( 4 ); - } ); - - // See https://github.com/woocommerce/woocommerce/pull/49917 - test( 'Price range is inclusive in both editor and frontend.', async ( { - page, - pageObject, - editor, - } ) => { - await pageObject.createNewPostAndInsertBlock(); - - await expect( pageObject.products ).toHaveCount( 9 ); - - await pageObject.addFilter( 'Price Range' ); - await pageObject.setPriceRange( { - min: '45', - max: '55', - } ); - - // Wait for the products to be filtered. - await expect( pageObject.products ).not.toHaveCount( 9 ); - - await expect( - pageObject.products.filter( { hasText: '$45.00' } ) - ).not.toHaveCount( 0 ); - await expect( - pageObject.products.filter( { hasText: '$55.00' } ) - ).not.toHaveCount( 0 ); - - // Reset the price range. - await pageObject.setPriceRange( { - min: '0', - max: '0', - } ); - - await expect( pageObject.products ).toHaveCount( 9 ); - - await editor.insertBlock( { - name: 'woocommerce/filter-wrapper', - attributes: { filterType: 'price-filter' }, - } ); - - await pageObject.publishAndGoToFrontend(); - - await expect( pageObject.products ).toHaveCount( 9 ); - - await page - .getByRole( 'textbox', { - name: 'Filter products by minimum', - } ) - .dblclick(); - await page.keyboard.type( '45' ); - - await page - .getByRole( 'textbox', { - name: 'Filter products by maximum', - } ) - .dblclick(); - await page.keyboard.type( '55' ); - - await page.keyboard.press( 'Tab' ); - - // Wait for the products to be filtered. - await expect( pageObject.products ).not.toHaveCount( 9 ); - - await expect( - pageObject.products.filter( { hasText: '$45.00' } ) - ).not.toHaveCount( 0 ); - await expect( - pageObject.products.filter( { hasText: '$55.00' } ) - ).not.toHaveCount( 0 ); - } ); - - test.describe( '"Use page context" control', () => { - test( 'should be visible on posts', async ( { pageObject } ) => { - await pageObject.createNewPostAndInsertBlock(); - - await expect( - pageObject - .locateSidebarSettings() - .locator( SELECTORS.usePageContextControl ) - ).toBeVisible(); - } ); - - [ - 'woocommerce/woocommerce//archive-product', - 'woocommerce/woocommerce//taxonomy-product_cat', - 'woocommerce/woocommerce//taxonomy-product_tag', - 'woocommerce/woocommerce//taxonomy-product_attribute', - 'woocommerce/woocommerce//product-search-results', - ].forEach( ( slug ) => { - test( `should be visible in archive template: ${ slug }`, async ( { - pageObject, - editor, - } ) => { - await pageObject.goToEditorTemplate( slug ); - await pageObject.insertProductCollection(); - await pageObject.chooseCollectionInTemplate(); - await pageObject.focusProductCollection(); - await editor.openDocumentSettingsSidebar(); - - await expect( - pageObject - .locateSidebarSettings() - .locator( SELECTORS.usePageContextControl ) - ).toBeVisible(); - } ); - } ); - - [ - 'woocommerce/woocommerce//single-product', - 'twentytwentyfour//home', - 'twentytwentyfour//index', - ].forEach( ( slug ) => { - test( `should be visible in non-archive template: ${ slug }`, async ( { - pageObject, - editor, - } ) => { - await pageObject.goToEditorTemplate( slug ); - await pageObject.insertProductCollection(); - await pageObject.chooseCollectionInTemplate(); - await pageObject.focusProductCollection(); - await editor.openDocumentSettingsSidebar(); - - await expect( - pageObject - .locateSidebarSettings() - .locator( SELECTORS.usePageContextControl ) - ).toBeVisible(); - } ); - } ); - - test( 'should work as expected in Product Catalog template', async ( { - pageObject, - editor, - } ) => { - await pageObject.goToEditorTemplate(); - await pageObject.focusProductCollection(); - await editor.openDocumentSettingsSidebar(); - - const sidebarSettings = pageObject.locateSidebarSettings(); - - // Inherit query from template should be visible & enabled by default - await expect( - sidebarSettings.locator( SELECTORS.usePageContextControl ) - ).toBeVisible(); - await expect( - sidebarSettings.locator( - `${ SELECTORS.usePageContextControl } input` - ) - ).toBeChecked(); - - // "On sale control" should be hidden when inherit query from template is enabled - await expect( - sidebarSettings.getByLabel( SELECTORS.onSaleControlLabel ) - ).toBeHidden(); - - // "On sale control" should be visible when inherit query from template is disabled - await pageObject.setInheritQueryFromTemplate( false ); - await expect( - sidebarSettings.getByLabel( SELECTORS.onSaleControlLabel ) - ).toBeVisible(); - - // "On sale control" should retain its state when inherit query from template is enabled again - await pageObject.setShowOnlyProductsOnSale( { - onSale: true, - isLocatorsRefreshNeeded: false, - } ); - await expect( - sidebarSettings.getByLabel( SELECTORS.onSaleControlLabel ) - ).toBeChecked(); - await pageObject.setInheritQueryFromTemplate( true ); - await expect( - sidebarSettings.getByLabel( SELECTORS.onSaleControlLabel ) - ).toBeHidden(); - await pageObject.setInheritQueryFromTemplate( false ); - await expect( - sidebarSettings.getByLabel( SELECTORS.onSaleControlLabel ) - ).toBeVisible(); - await expect( - sidebarSettings.getByLabel( SELECTORS.onSaleControlLabel ) - ).toBeChecked(); - } ); - - test( 'is enabled by default unless already enabled elsewhere', async ( { - pageObject, - editor, - } ) => { - const productCollection = editor.canvas.getByLabel( - 'Block: Product Collection', - { exact: true } - ); - const usePageContextToggle = pageObject - .locateSidebarSettings() - .locator( SELECTORS.usePageContextControl ) - .locator( 'input' ); - - // First Product Catalog - // Option should be visible & ENABLED by default - await pageObject.goToEditorTemplate(); - await editor.selectBlocks( productCollection.first() ); - await editor.openDocumentSettingsSidebar(); - - await expect( usePageContextToggle ).toBeChecked(); - - // Second Product Catalog - // Option should be visible & DISABLED by default - await pageObject.insertProductCollection(); - await pageObject.chooseCollectionInTemplate( 'productCatalog' ); - await editor.selectBlocks( productCollection.last() ); - - await expect( usePageContextToggle ).not.toBeChecked(); - - // Disable the option in the first Product Catalog - await editor.selectBlocks( productCollection.first() ); - await usePageContextToggle.click(); - - // Third Product Catalog - // Option should be visible & ENABLED by default - await pageObject.insertProductCollection(); - await pageObject.chooseCollectionInTemplate( 'productCatalog' ); - await editor.selectBlocks( productCollection.last() ); - - await expect( usePageContextToggle ).toBeChecked(); - } ); - - test( 'allows filtering in non-archive context', async ( { - pageObject, - editor, - page, - } ) => { - await pageObject.createNewPostAndInsertBlock(); - - await expect( pageObject.products ).toHaveCount( 9 ); - - await pageObject.insertProductCollection(); - await pageObject.chooseCollectionInPost( 'productCatalog' ); - - await expect( pageObject.products ).toHaveCount( 18 ); - - await page.getByLabel( 'Toggle block inserter' ).click(); - await page.getByRole( 'tab', { name: 'Patterns' } ).click(); - await page - .getByPlaceholder( 'Search' ) - .fill( 'product filters' ); - await page.getByLabel( 'Product Filters' ).click(); - - const postId = await editor.publishPost(); - await page.goto( `/?p=${ postId }` ); - - const productCollection = page.locator( - '.wp-block-woocommerce-product-collection' - ); - - await expect( - productCollection.first().locator( SELECTORS.product ) - ).toHaveCount( 9 ); - await expect( - productCollection.last().locator( SELECTORS.product ) - ).toHaveCount( 9 ); - - await page - .getByRole( 'textbox', { - name: 'Filter products by maximum', - } ) - .dblclick(); - await page.keyboard.type( '10' ); - await page.keyboard.press( 'Tab' ); - - await expect( - productCollection.first().locator( SELECTORS.product ) - ).toHaveCount( 1 ); - await expect( - productCollection.last().locator( SELECTORS.product ) - ).toHaveCount( 9 ); - } ); - - test( 'correctly combines editor and front-end filters', async ( { - pageObject, - editor, - page, - } ) => { - await pageObject.createNewPostAndInsertBlock(); - - await expect( pageObject.products ).toHaveCount( 9 ); - - await pageObject.addFilter( 'Show product categories' ); - await pageObject.setFilterComboboxValue( 'Product categories', [ - 'Music', - ] ); - - await page.getByLabel( 'Toggle block inserter' ).click(); - await page.getByRole( 'tab', { name: 'Patterns' } ).click(); - await page - .getByPlaceholder( 'Search' ) - .fill( 'product filters' ); - await page.getByLabel( 'Product Filters' ).click(); - - await expect( pageObject.products ).toHaveCount( 2 ); - - const postId = await editor.publishPost(); - await page.goto( `/?p=${ postId }` ); - - await expect( pageObject.products ).toHaveCount( 2 ); - - await page - .getByRole( 'textbox', { - name: 'Filter products by maximum', - } ) - .dblclick(); - await page.keyboard.type( '5' ); - await page.keyboard.press( 'Tab' ); - - await expect( pageObject.products ).toHaveCount( 1 ); - } ); - } ); - } ); - test.describe( 'Toolbar settings', () => { test.beforeEach( async ( { pageObject } ) => { await pageObject.createNewPostAndInsertBlock(); @@ -876,205 +348,6 @@ test.describe( 'Product Collection', () => { } ); } ); - test.describe( 'Collections', () => { - test( 'New Arrivals Collection can be added and displays proper products', async ( { - pageObject, - } ) => { - await pageObject.createNewPostAndInsertBlock( 'newArrivals' ); - - // New Arrivals are by default filtered to display products from last 7 days. - // Products in our test env have creation date set to much older, hence - // no products are expected to be displayed by default. - await expect( pageObject.products ).toHaveCount( 0 ); - - await pageObject.publishAndGoToFrontend(); - - await expect( pageObject.products ).toHaveCount( 0 ); - } ); - - // When creating reviews programmatically the ratings are not propagated - // properly so products order by rating is undeterministic in test env. - // eslint-disable-next-line playwright/no-skipped-test - test.skip( 'Top Rated Collection can be added and displays proper products', async ( { - pageObject, - } ) => { - await pageObject.createNewPostAndInsertBlock( 'topRated' ); - - const topRatedProducts = [ - 'V Neck T Shirt', - 'Hoodie', - 'Hoodie with Logo', - 'T-Shirt', - 'Beanie', - ]; - - await expect( pageObject.products ).toHaveCount( 5 ); - await expect( pageObject.productTitles ).toHaveText( - topRatedProducts - ); - - await pageObject.publishAndGoToFrontend(); - - await expect( pageObject.products ).toHaveCount( 5 ); - } ); - - // There's no orders in test env so the order of Best Sellers - // is undeterministic in test env. Requires further work. - // eslint-disable-next-line playwright/no-skipped-test - test.skip( 'Best Sellers Collection can be added and displays proper products', async ( { - pageObject, - } ) => { - await pageObject.createNewPostAndInsertBlock( 'bestSellers' ); - - const bestSellersProducts = [ - 'Album', - 'Hoodie', - 'Single', - 'Hoodie with Logo', - 'T-Shirt with Logo', - ]; - - await expect( pageObject.products ).toHaveCount( 5 ); - await expect( pageObject.productTitles ).toHaveText( - bestSellersProducts - ); - - await pageObject.publishAndGoToFrontend(); - - await expect( pageObject.products ).toHaveCount( 5 ); - } ); - - test( 'On Sale Collection can be added and displays proper products', async ( { - pageObject, - } ) => { - await pageObject.createNewPostAndInsertBlock( 'onSale' ); - - const onSaleProducts = [ - 'Beanie', - 'Beanie with Logo', - 'Belt', - 'Cap', - 'Hoodie', - ]; - - await expect( pageObject.products ).toHaveCount( 5 ); - await expect( pageObject.productTitles ).toHaveText( - onSaleProducts - ); - - await pageObject.publishAndGoToFrontend(); - - await expect( pageObject.products ).toHaveCount( 5 ); - } ); - - test( 'Featured Collection can be added and displays proper products', async ( { - pageObject, - } ) => { - await pageObject.createNewPostAndInsertBlock( 'featured' ); - - const featuredProducts = [ - 'Cap', - 'Hoodie with Zipper', - 'Sunglasses', - 'V-Neck T-Shirt', - ]; - - await expect( pageObject.products ).toHaveCount( 4 ); - await expect( pageObject.productTitles ).toHaveText( - featuredProducts - ); - - await pageObject.publishAndGoToFrontend(); - - await expect( pageObject.products ).toHaveCount( 4 ); - } ); - - test( 'Product Catalog Collection can be added in post and syncs query with template', async ( { - pageObject, - } ) => { - await pageObject.createNewPostAndInsertBlock( 'productCatalog' ); - - const usePageContextToggle = pageObject - .locateSidebarSettings() - .locator( `${ SELECTORS.usePageContextControl } input` ); - - await expect( usePageContextToggle ).toBeVisible(); - await expect( pageObject.products ).toHaveCount( 9 ); - - await pageObject.publishAndGoToFrontend(); - - await expect( pageObject.products ).toHaveCount( 9 ); - } ); - - test( 'Product Catalog Collection can be added in product archive and syncs query with template', async ( { - pageObject, - editor, - admin, - } ) => { - await admin.visitSiteEditor( { - postId: 'woocommerce/woocommerce//archive-product', - postType: 'wp_template', - canvas: 'edit', - } ); - - await editor.setContent( '' ); - - await pageObject.insertProductCollection(); - await pageObject.chooseCollectionInTemplate(); - await editor.openDocumentSettingsSidebar(); - - const sidebarSettings = pageObject.locateSidebarSettings(); - const input = sidebarSettings.locator( - `${ SELECTORS.usePageContextControl } input` - ); - - await expect( input ).toBeChecked(); - } ); - - test.describe( 'Have hidden implementation in UI', () => { - test( 'New Arrivals', async ( { pageObject } ) => { - await pageObject.createNewPostAndInsertBlock( 'newArrivals' ); - const input = await pageObject.getOrderByElement(); - - await expect( input ).toBeHidden(); - } ); - - test( 'Top Rated', async ( { pageObject } ) => { - await pageObject.createNewPostAndInsertBlock( 'topRated' ); - const input = await pageObject.getOrderByElement(); - - await expect( input ).toBeHidden(); - } ); - - test( 'Best Sellers', async ( { pageObject } ) => { - await pageObject.createNewPostAndInsertBlock( 'bestSellers' ); - const input = await pageObject.getOrderByElement(); - - await expect( input ).toBeHidden(); - } ); - - test( 'On Sale', async ( { pageObject } ) => { - await pageObject.createNewPostAndInsertBlock( 'onSale' ); - const sidebarSettings = pageObject.locateSidebarSettings(); - const input = sidebarSettings.getByLabel( - SELECTORS.onSaleControlLabel - ); - - await expect( input ).toBeHidden(); - } ); - - test( 'Featured', async ( { pageObject } ) => { - await pageObject.createNewPostAndInsertBlock( 'featured' ); - const sidebarSettings = pageObject.locateSidebarSettings(); - const input = sidebarSettings.getByLabel( - SELECTORS.featuredControlLabel - ); - - await expect( input ).toBeHidden(); - } ); - } ); - } ); - test.describe( 'With other blocks', () => { test( 'In Single Product block', async ( { admin, pageObject } ) => { await admin.createNewPost(); @@ -1107,7 +380,6 @@ test.describe( 'Product Collection', () => { } ); test( 'With multiple Pagination blocks', async ( { - page, admin, editor, pageObject, @@ -1115,7 +387,9 @@ test.describe( 'Product Collection', () => { await admin.createNewPost(); await pageObject.insertProductCollection(); await pageObject.chooseCollectionInPost( 'productCatalog' ); - const paginations = page.getByLabel( BLOCK_LABELS.pagination ); + const paginations = editor.canvas.getByLabel( + BLOCK_LABELS.pagination + ); await expect( paginations ).toHaveCount( 1 ); @@ -1129,7 +403,7 @@ test.describe( 'Product Collection', () => { } ); } ); - test.describe( 'Location is recognised', () => { + test.describe( 'Location is recognized', () => { const filterRequest = ( request: Request ) => { const url = request.url(); return ( @@ -1145,7 +419,9 @@ test.describe( 'Product Collection', () => { return ( url.includes( 'wp/v2/product' ) && searchParams.get( 'isProductCollectionBlock' ) === 'true' && - !! searchParams.get( `location[sourceData][productId]` ) + !! searchParams.get( + `productCollectionLocation[sourceData][productId]` + ) ); }; @@ -1157,26 +433,30 @@ test.describe( 'Product Collection', () => { if ( locationType === 'product' ) { return { - type: searchParams.get( 'location[type]' ), + type: searchParams.get( 'productCollectionLocation[type]' ), productId: searchParams.get( - `location[sourceData][productId]` + `productCollectionLocation[sourceData][productId]` ), }; } if ( locationType === 'archive' ) { return { - type: searchParams.get( 'location[type]' ), + type: searchParams.get( 'productCollectionLocation[type]' ), taxonomy: searchParams.get( - `location[sourceData][taxonomy]` + `productCollectionLocation[sourceData][taxonomy]` + ), + termId: searchParams.get( + `productCollectionLocation[sourceData][termId]` ), - termId: searchParams.get( `location[sourceData][termId]` ), }; } return { - type: searchParams.get( 'location[type]' ), - sourceData: searchParams.get( `location[sourceData]` ), + type: searchParams.get( 'productCollectionLocation[type]' ), + sourceData: searchParams.get( + `productCollectionLocation[sourceData]` + ), }; }; @@ -1209,10 +489,10 @@ test.describe( 'Product Collection', () => { pageObject.BLOCK_NAME ); - const locationReuqestPromise = + const locationRequestPromise = page.waitForRequest( filterProductRequest ); await pageObject.chooseCollectionInTemplate( 'featured' ); - const locationRequest = await locationReuqestPromise; + const locationRequest = await locationRequestPromise; const { type, productId } = getLocationDetailsFromRequest( locationRequest, @@ -1409,15 +689,12 @@ test.describe( 'Product Collection', () => { editor, } ) => { await pageObject.createNewPostAndInsertBlock(); - const productTemplate = page.getByLabel( - BLOCK_LABELS.productTemplate - ); - await expect( productTemplate ).toBeVisible(); + await expect( pageObject.productTemplate ).toBeVisible(); // Refresh the post and verify the block is still visible await editor.publishPost(); await page.reload(); - await expect( productTemplate ).toBeVisible(); + await expect( pageObject.productTemplate ).toBeVisible(); } ); test( 'On Sale collection should be visible after Refresh', async ( { @@ -1449,15 +726,12 @@ test.describe( 'Product Collection', () => { editor, } ) => { await pageObject.createNewPostAndInsertBlock( 'onSale' ); - const productTemplate = page.getByLabel( - BLOCK_LABELS.productTemplate - ); - await expect( productTemplate ).toBeVisible(); + await expect( pageObject.productTemplate ).toBeVisible(); // Refresh the post and verify "On Sale" collection is still visible await editor.saveDraft(); await page.reload(); - await expect( productTemplate ).toBeVisible(); + await expect( pageObject.productTemplate ).toBeVisible(); } ); } ); @@ -1695,279 +969,19 @@ test.describe( 'Product Collection', () => { } ); } ); -/** - * These E2E tests are for `registerProductCollection` which we are exposing - * for 3PDs to register new product collections. - */ -test.describe( 'Testing registerProductCollection', () => { - const MY_REGISTERED_COLLECTIONS = { - myCustomCollection: { - name: 'My Custom Collection', - label: 'Block: My Custom Collection', - }, - myCustomCollectionWithPreview: { - name: 'My Custom Collection with Preview', - label: 'Block: My Custom Collection with Preview', - }, - myCustomCollectionWithAdvancedPreview: { - name: 'My Custom Collection with Advanced Preview', - label: 'Block: My Custom Collection with Advanced Preview', - }, - }; - - // Activate plugin which registers custom product collections - test.beforeEach( async ( { requestUtils } ) => { - await requestUtils.activatePlugin( - 'register-product-collection-tester' - ); - } ); - - test( `Registered collections should be available in Collection chooser`, async ( { - pageObject, - editor, - admin, - page, - } ) => { - await admin.createNewPost(); - await editor.insertBlockUsingGlobalInserter( pageObject.BLOCK_NAME ); - await page - .getByRole( 'button', { - name: 'Choose collection', - } ) - .click(); - - // Get text of all buttons in the collection chooser - const collectionChooserButtonsTexts = await editor.page - .locator( '.wc-blocks-product-collection__collection-button-title' ) - .allTextContents(); - - // Check if all registered collections are available in the collection chooser - expect( - collectionChooserButtonsTexts.includes( - MY_REGISTERED_COLLECTIONS.myCustomCollection.name - ) - ).toBeTruthy(); - expect( - collectionChooserButtonsTexts.includes( - MY_REGISTERED_COLLECTIONS.myCustomCollectionWithPreview.name - ) - ).toBeTruthy(); - expect( - collectionChooserButtonsTexts.includes( - MY_REGISTERED_COLLECTIONS.myCustomCollectionWithAdvancedPreview - .name - ) - ).toBeTruthy(); - } ); - - test.describe( 'My Custom Collection', () => { - test( 'Clicking "My Custom Collection" should insert block and show 5 products', async ( { - pageObject, - } ) => { - await pageObject.createNewPostAndInsertBlock( - 'myCustomCollection' - ); - - await expect( pageObject.products ).toHaveCount( 5 ); - await expect( pageObject.productImages ).toHaveCount( 5 ); - await expect( pageObject.productTitles ).toHaveCount( 5 ); - await expect( pageObject.productPrices ).toHaveCount( 5 ); - await expect( pageObject.addToCartButtons ).toHaveCount( 5 ); - - await pageObject.publishAndGoToFrontend(); - await expect( pageObject.products ).toHaveCount( 5 ); - } ); - - test( 'Should display properly in Product Catalog template', async ( { - pageObject, - editor, - } ) => { - await pageObject.goToProductCatalogAndInsertCollection( - 'myCustomCollection' - ); - - const block = editor.canvas.getByLabel( - MY_REGISTERED_COLLECTIONS.myCustomCollection.label - ); - - const products = block - .getByLabel( BLOCK_LABELS.productImage ) - .locator( 'visible=true' ); - await expect( products ).toHaveCount( 5 ); - } ); - - test( 'hideControls allows to hide filters', async ( { - pageObject, - page, - } ) => { - await pageObject.goToProductCatalogAndInsertCollection( - 'myCustomCollection' - ); - - const sidebarSettings = pageObject.locateSidebarSettings(); - const onsaleControl = sidebarSettings.getByLabel( - SELECTORS.onSaleControlLabel - ); - await expect( onsaleControl ).toBeHidden(); - - await page - .getByRole( 'button', { name: 'Filters options' } ) - .click(); - const keywordControl = page.getByRole( 'menuitemcheckbox', { - name: 'Keyword', - } ); - - await expect( keywordControl ).toBeHidden(); - } ); - } ); - - test.describe( 'My Custom Collection with Preview', () => { - test( 'Clicking "My Custom Collection with Preview" should insert block and show 9 products', async ( { - pageObject, - } ) => { - await pageObject.createNewPostAndInsertBlock( - 'myCustomCollectionWithPreview' - ); - - await expect( pageObject.products ).toHaveCount( 9 ); - await expect( pageObject.productImages ).toHaveCount( 9 ); - await expect( pageObject.productTitles ).toHaveCount( 9 ); - await expect( pageObject.productPrices ).toHaveCount( 9 ); - await expect( pageObject.addToCartButtons ).toHaveCount( 9 ); - - await pageObject.publishAndGoToFrontend(); - await expect( pageObject.products ).toHaveCount( 9 ); - } ); - - test( 'Clicking "My Custom Collection with Preview" should show preview', async ( { - pageObject, - editor, - } ) => { - await pageObject.createNewPostAndInsertBlock( - 'myCustomCollectionWithPreview' - ); - const previewButtonLocator = editor.page.getByTestId( - SELECTORS.previewButtonTestID - ); - - // The preview button should be visible - await expect( previewButtonLocator ).toBeVisible(); - } ); - - test( 'Should display properly in Product Catalog template', async ( { - pageObject, - editor, - } ) => { - await pageObject.goToProductCatalogAndInsertCollection( - 'myCustomCollectionWithPreview' - ); - - const block = editor.canvas.getByLabel( - MY_REGISTERED_COLLECTIONS.myCustomCollectionWithPreview.label - ); - - // Check if products are visible - const products = block - .getByLabel( BLOCK_LABELS.productImage ) - .locator( 'visible=true' ); - await expect( products ).toHaveCount( 9 ); - - // Check if the preview button is visible - const previewButtonLocator = block.getByTestId( - SELECTORS.previewButtonTestID - ); - await expect( previewButtonLocator ).toBeVisible(); - } ); - } ); - - test.describe( 'My Custom Collection with Advanced Preview', () => { - test( 'Clicking "My Custom Collection with Advanced Preview" should insert block and show 9 products', async ( { - pageObject, - } ) => { - await pageObject.createNewPostAndInsertBlock( - 'myCustomCollectionWithAdvancedPreview' - ); - - await expect( pageObject.products ).toHaveCount( 9 ); - await expect( pageObject.productImages ).toHaveCount( 9 ); - await expect( pageObject.productTitles ).toHaveCount( 9 ); - await expect( pageObject.productPrices ).toHaveCount( 9 ); - await expect( pageObject.addToCartButtons ).toHaveCount( 9 ); - - await pageObject.publishAndGoToFrontend(); - await expect( pageObject.products ).toHaveCount( 9 ); - } ); - - test( 'Clicking "My Custom Collection with Advanced Preview" should show preview for 1 second', async ( { - pageObject, - editor, - page, - } ) => { - await pageObject.createNewPostAndInsertBlock( - 'myCustomCollectionWithAdvancedPreview' - ); - const previewButtonLocator = editor.page.getByTestId( - SELECTORS.previewButtonTestID - ); - - // The preview button should be visible - await expect( previewButtonLocator ).toBeVisible(); - - // Disabling eslint rule because we need to wait for the preview to disappear - // eslint-disable-next-line playwright/no-wait-for-timeout, no-restricted-syntax - await page.waitForTimeout( 1000 ); - - // The preview button should be hidden - await expect( previewButtonLocator ).toBeHidden(); - } ); - - test( 'Should display properly in Product Catalog template', async ( { - pageObject, - editor, - page, - } ) => { - await pageObject.goToProductCatalogAndInsertCollection( - 'myCustomCollectionWithAdvancedPreview' - ); - - const block = editor.canvas.getByLabel( - MY_REGISTERED_COLLECTIONS.myCustomCollectionWithAdvancedPreview - .label - ); - - // Check if the preview button is visible - const previewButtonLocator = block.getByTestId( - SELECTORS.previewButtonTestID - ); - await expect( previewButtonLocator ).toBeVisible(); - - // Check if products are visible - const products = block - .getByLabel( BLOCK_LABELS.productImage ) - .locator( 'visible=true' ); - await expect( products ).toHaveCount( 9 ); - - // Disabling eslint rule because we need to wait for the preview to disappear - // eslint-disable-next-line playwright/no-wait-for-timeout, no-restricted-syntax - await page.waitForTimeout( 1000 ); - - // The preview button should be hidden after 1 second - await expect( previewButtonLocator ).toBeHidden(); - } ); - } ); -} ); - test.describe( 'Testing "usesReference" argument in "registerProductCollection"', () => { const MY_REGISTERED_COLLECTIONS = { myCustomCollectionWithProductContext: { name: 'My Custom Collection - Product Context', label: 'Block: My Custom Collection - Product Context', previewLabelTemplate: [ 'woocommerce/woocommerce//single-product' ], + shouldShowProductPicker: true, }, myCustomCollectionWithCartContext: { name: 'My Custom Collection - Cart Context', label: 'Block: My Custom Collection - Cart Context', previewLabelTemplate: [ 'woocommerce/woocommerce//page-cart' ], + shouldShowProductPicker: false, }, myCustomCollectionWithOrderContext: { name: 'My Custom Collection - Order Context', @@ -1975,6 +989,7 @@ test.describe( 'Testing "usesReference" argument in "registerProductCollection"' previewLabelTemplate: [ 'woocommerce/woocommerce//order-confirmation', ], + shouldShowProductPicker: false, }, myCustomCollectionWithArchiveContext: { name: 'My Custom Collection - Archive Context', @@ -1982,6 +997,7 @@ test.describe( 'Testing "usesReference" argument in "registerProductCollection"' previewLabelTemplate: [ 'woocommerce/woocommerce//taxonomy-product_cat', ], + shouldShowProductPicker: false, }, myCustomCollectionMultipleContexts: { name: 'My Custom Collection - Multiple Contexts', @@ -1990,6 +1006,7 @@ test.describe( 'Testing "usesReference" argument in "registerProductCollection"' 'woocommerce/woocommerce//single-product', 'woocommerce/woocommerce//order-confirmation', ], + shouldShowProductPicker: true, }, }; @@ -2025,11 +1042,31 @@ test.describe( 'Testing "usesReference" argument in "registerProductCollection"' test( `Collection "${ collection.name }" should not show preview label in a post`, async ( { pageObject, editor, + admin, } ) => { - await pageObject.createNewPostAndInsertBlock( - key as Collections - ); + await admin.createNewPost(); + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInPost( key as Collections ); + // Check visibility of product picker + const editorProductPicker = editor.canvas.locator( + SELECTORS.productPicker + ); + const expectedVisibility = collection.shouldShowProductPicker + ? 'toBeVisible' + : 'toBeHidden'; + await expect( editorProductPicker )[ expectedVisibility ](); + + if ( collection.shouldShowProductPicker ) { + await pageObject.chooseProductInEditorProductPickerIfAvailable( + editor.canvas + ); + } + + // At this point, the product picker should be hidden + await expect( editorProductPicker ).toBeHidden(); + + // Check visibility of preview label const block = editor.canvas.getByLabel( collection.label ); const previewButtonLocator = block.getByTestId( SELECTORS.previewButtonTestID @@ -2056,3 +1093,184 @@ test.describe( 'Testing "usesReference" argument in "registerProductCollection"' } ); } ); + +test.describe( 'Product picker', () => { + const MY_REGISTERED_COLLECTIONS_THAT_NEEDS_PRODUCT = { + myCustomCollectionWithProductContext: { + name: 'My Custom Collection - Product Context', + label: 'Block: My Custom Collection - Product Context', + collection: + 'woocommerce/product-collection/my-custom-collection-product-context', + }, + myCustomCollectionMultipleContexts: { + name: 'My Custom Collection - Multiple Contexts', + label: 'Block: My Custom Collection - Multiple Contexts', + collection: + 'woocommerce/product-collection/my-custom-collection-multiple-contexts', + }, + }; + + // Activate plugin which registers custom product collections + test.beforeEach( async ( { requestUtils } ) => { + await requestUtils.activatePlugin( + 'register-product-collection-tester' + ); + } ); + + Object.entries( MY_REGISTERED_COLLECTIONS_THAT_NEEDS_PRODUCT ).forEach( + ( [ key, collection ] ) => { + test( `For collection "${ collection.name }" - manually selected product reference should be available on Frontend in a post`, async ( { + pageObject, + admin, + page, + editor, + } ) => { + await admin.createNewPost(); + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInPost( key as Collections ); + + // Verify that product picker is shown in Editor + const editorProductPicker = editor.canvas.locator( + SELECTORS.productPicker + ); + await expect( editorProductPicker ).toBeVisible(); + + // Once a product is selected, the product picker should be hidden + await pageObject.chooseProductInEditorProductPickerIfAvailable( + editor.canvas + ); + await expect( editorProductPicker ).toBeHidden(); + + // On Frontend, verify that product reference is a number + await pageObject.publishAndGoToFrontend(); + const collectionWithProductContext = page.locator( + `[data-collection="${ collection.collection }"]` + ); + const queryAttribute = JSON.parse( + ( await collectionWithProductContext.getAttribute( + 'data-query' + ) ) || '{}' + ); + expect( typeof queryAttribute?.productReference ).toBe( + 'number' + ); + } ); + + test( `For collection "${ collection.name }" - changing product using inspector control`, async ( { + pageObject, + admin, + page, + editor, + } ) => { + await admin.createNewPost(); + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInPost( key as Collections ); + + // Verify that product picker is shown in Editor + const editorProductPicker = editor.canvas.locator( + SELECTORS.productPicker + ); + await expect( editorProductPicker ).toBeVisible(); + + // Once a product is selected, the product picker should be hidden + await pageObject.chooseProductInEditorProductPickerIfAvailable( + editor.canvas + ); + await expect( editorProductPicker ).toBeHidden(); + + // Verify that Album is selected + await expect( + admin.page.locator( SELECTORS.linkedProductControl.button ) + ).toContainText( 'Album' ); + + // Change product using inspector control to Beanie + await admin.page + .locator( SELECTORS.linkedProductControl.button ) + .click(); + await admin.page + .locator( SELECTORS.linkedProductControl.popoverContent ) + .getByLabel( 'Beanie', { exact: true } ) + .click(); + await expect( + admin.page.locator( SELECTORS.linkedProductControl.button ) + ).toContainText( 'Beanie' ); + + // On Frontend, verify that product reference is a number + await pageObject.publishAndGoToFrontend(); + const collectionWithProductContext = page.locator( + `[data-collection="${ collection.collection }"]` + ); + const queryAttribute = JSON.parse( + ( await collectionWithProductContext.getAttribute( + 'data-query' + ) ) || '{}' + ); + expect( typeof queryAttribute?.productReference ).toBe( + 'number' + ); + } ); + + test( `For collection "${ collection.name }" - product picker shouldn't be shown in Single Product template`, async ( { + pageObject, + admin, + editor, + } ) => { + await admin.visitSiteEditor( { + postId: `woocommerce/woocommerce//single-product`, + postType: 'wp_template', + canvas: 'edit', + } ); + await editor.canvas.locator( 'body' ).click(); + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInTemplate( + key as Collections + ); + + const editorProductPicker = editor.canvas.locator( + SELECTORS.productPicker + ); + await expect( editorProductPicker ).toBeHidden(); + } ); + } + ); + + test( 'Product picker should work as expected while changing collection using "Choose collection" button from Toolbar', async ( { + pageObject, + admin, + editor, + } ) => { + await admin.createNewPost(); + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInPost( + 'myCustomCollectionWithProductContext' + ); + + // Verify that product picker is shown in Editor + const editorProductPicker = editor.canvas.locator( + SELECTORS.productPicker + ); + await expect( editorProductPicker ).toBeVisible(); + + // Once a product is selected, the product picker should be hidden + await pageObject.chooseProductInEditorProductPickerIfAvailable( + editor.canvas + ); + await expect( editorProductPicker ).toBeHidden(); + + // Change collection using Toolbar + await pageObject.changeCollectionUsingToolbar( + 'myCustomCollectionMultipleContexts' + ); + await expect( editorProductPicker ).toBeVisible(); + + // Once a product is selected, the product picker should be hidden + await pageObject.chooseProductInEditorProductPickerIfAvailable( + editor.canvas + ); + await expect( editorProductPicker ).toBeHidden(); + + // Product picker should be hidden for collections that don't need product + await pageObject.changeCollectionUsingToolbar( 'featured' ); + await expect( editorProductPicker ).toBeHidden(); + } ); +} ); diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.page.ts b/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.page.ts index e3958814926..1a87ebeb605 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.page.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.page.ts @@ -1,7 +1,7 @@ /** * External dependencies */ -import { Locator, Page } from '@playwright/test'; +import { FrameLocator, Locator, Page } from '@playwright/test'; import { Editor, Admin } from '@woocommerce/e2e-utils'; import { BlockRepresentation } from '@wordpress/e2e-test-utils-playwright/build-types/editor/insert-block'; @@ -62,6 +62,12 @@ export const SELECTORS = { previewButtonTestID: 'product-collection-preview-button', collectionPlaceholder: '[data-type="woocommerce/product-collection"] .components-placeholder', + productPicker: '.wc-blocks-product-collection__editor-product-picker', + linkedProductControl: { + button: '.wc-block-product-collection-linked-product-control__button', + popoverContent: + '.wc-block-product-collection-linked-product__popover-content', + }, }; export type Collections = @@ -134,7 +140,7 @@ class ProductCollectionPage { ? collectionToButtonNameMap[ collection ] : collectionToButtonNameMap.productCatalog; - const placeholderSelector = this.admin.page.locator( + const placeholderSelector = this.editor.canvas.locator( SELECTORS.collectionPlaceholder ); @@ -200,10 +206,31 @@ class ProductCollectionPage { } } + async chooseProductInEditorProductPickerIfAvailable( + pageReference: Page | FrameLocator + ) { + const editorProductPicker = pageReference.locator( + SELECTORS.productPicker + ); + + if ( await editorProductPicker.isVisible() ) { + await editorProductPicker + .locator( 'label' ) + .filter( { + hasText: 'Album', + } ) + .click(); + } + } + async createNewPostAndInsertBlock( collection?: Collections ) { await this.admin.createNewPost(); await this.insertProductCollection(); await this.chooseCollectionInPost( collection ); + // If product picker is available, choose a product. + await this.chooseProductInEditorProductPickerIfAvailable( + this.admin.page + ); await this.refreshLocators( 'editor' ); await this.editor.openDocumentSettingsSidebar(); } @@ -345,6 +372,10 @@ class ProductCollectionPage { await this.editor.canvas.locator( 'body' ).click(); await this.insertProductCollection(); await this.chooseCollectionInTemplate( collection ); + // If product picker is available, choose a product. + await this.chooseProductInEditorProductPickerIfAvailable( + this.editor.canvas + ); await this.refreshLocators( 'editor' ); } @@ -408,7 +439,7 @@ class ProductCollectionPage { name: 'Order by', } ); await orderByComboBox.selectOption( orderBy ); - await this.page.locator( SELECTORS.product ).first().waitFor(); + await this.editor.canvas.locator( SELECTORS.product ).first().waitFor(); await this.refreshLocators( 'editor' ); } @@ -571,6 +602,30 @@ class ProductCollectionPage { .click(); } + async changeCollectionUsingToolbar( collection: Collections ) { + // Click "Choose collection" button in the toolbar. + await this.admin.page + .getByRole( 'toolbar', { name: 'Block Tools' } ) + .getByRole( 'button', { name: 'Choose collection' } ) + .click(); + + // Select the collection from the modal. + const collectionChooserModal = this.admin.page.locator( + '.wc-blocks-product-collection__modal' + ); + await collectionChooserModal + .getByRole( 'button', { + name: collectionToButtonNameMap[ collection ], + } ) + .click(); + + await collectionChooserModal + .getByRole( 'button', { + name: 'Continue', + } ) + .click(); + } + async setDisplaySettings( { itemsPerPage, offset, @@ -743,11 +798,13 @@ class ProductCollectionPage { } private async initializeLocatorsForEditor() { - this.productTemplate = this.page.locator( SELECTORS.productTemplate ); - this.products = this.page + this.productTemplate = this.editor.canvas.locator( + SELECTORS.productTemplate + ); + this.products = this.editor.canvas .locator( SELECTORS.product ) .locator( 'visible=true' ); - this.productImages = this.page + this.productImages = this.editor.canvas .locator( SELECTORS.productImage.inEditor ) .locator( 'visible=true' ); this.productTitles = this.productTemplate @@ -756,10 +813,10 @@ class ProductCollectionPage { this.productPrices = this.productTemplate .locator( SELECTORS.productPrice.inEditor ) .locator( 'visible=true' ); - this.addToCartButtons = this.page + this.addToCartButtons = this.editor.canvas .locator( SELECTORS.addToCartButton.inEditor ) .locator( 'visible=true' ); - this.pagination = this.page.getByRole( 'document', { + this.pagination = this.editor.canvas.getByRole( 'document', { name: 'Block: Pagination', } ); } diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/register-product-collection-tester.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/register-product-collection-tester.block_theme.spec.ts new file mode 100644 index 00000000000..a7ea710f8a4 --- /dev/null +++ b/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/register-product-collection-tester.block_theme.spec.ts @@ -0,0 +1,359 @@ +/** + * External dependencies + */ +import { test as base, expect } from '@woocommerce/e2e-utils'; + +/** + * Internal dependencies + */ +import ProductCollectionPage, { + BLOCK_LABELS, + Collections, + SELECTORS, +} from './product-collection.page'; + +const test = base.extend< { pageObject: ProductCollectionPage } >( { + pageObject: async ( { page, admin, editor }, use ) => { + const pageObject = new ProductCollectionPage( { + page, + admin, + editor, + } ); + await use( pageObject ); + }, +} ); + +/** + * These E2E tests are for `registerProductCollection` which we are exposing + * for 3PDs to register new product collections. + */ +test.describe( 'Product Collection registration', () => { + const MY_REGISTERED_COLLECTIONS = { + myCustomCollection: { + name: 'My Custom Collection', + label: 'Block: My Custom Collection', + }, + myCustomCollectionWithPreview: { + name: 'My Custom Collection with Preview', + label: 'Block: My Custom Collection with Preview', + }, + myCustomCollectionWithAdvancedPreview: { + name: 'My Custom Collection with Advanced Preview', + label: 'Block: My Custom Collection with Advanced Preview', + }, + }; + + // Activate plugin which registers custom product collections + test.beforeEach( async ( { requestUtils } ) => { + await requestUtils.activatePlugin( + 'register-product-collection-tester' + ); + } ); + + test( `Registered collections should be available in Collection chooser`, async ( { + pageObject, + editor, + admin, + } ) => { + await admin.createNewPost(); + await editor.insertBlockUsingGlobalInserter( pageObject.BLOCK_NAME ); + await editor.canvas + .getByRole( 'button', { + name: 'Choose collection', + } ) + .click(); + + const productCollectionBlock = editor.canvas.getByLabel( + 'Block: Product Collection' + ); + + for ( const myCollection of Object.values( + MY_REGISTERED_COLLECTIONS + ) ) { + await expect( + productCollectionBlock.getByRole( 'button', { + name: myCollection.name, + exact: true, + } ) + ).toBeVisible(); + } + } ); + + test.describe( 'My Custom Collection', () => { + test( 'Clicking "My Custom Collection" should insert block and show 5 products', async ( { + pageObject, + } ) => { + await pageObject.createNewPostAndInsertBlock( + 'myCustomCollection' + ); + + await expect( pageObject.products ).toHaveCount( 5 ); + await expect( pageObject.productImages ).toHaveCount( 5 ); + await expect( pageObject.productTitles ).toHaveCount( 5 ); + await expect( pageObject.productPrices ).toHaveCount( 5 ); + await expect( pageObject.addToCartButtons ).toHaveCount( 5 ); + + await pageObject.publishAndGoToFrontend(); + await expect( pageObject.products ).toHaveCount( 5 ); + } ); + + test( 'Should display properly in Product Catalog template', async ( { + pageObject, + editor, + } ) => { + await pageObject.goToProductCatalogAndInsertCollection( + 'myCustomCollection' + ); + + const block = editor.canvas.getByLabel( + MY_REGISTERED_COLLECTIONS.myCustomCollection.label + ); + + const products = block + .getByLabel( BLOCK_LABELS.productImage ) + .locator( 'visible=true' ); + await expect( products ).toHaveCount( 5 ); + } ); + + test( 'hideControls allows to hide filters', async ( { + pageObject, + page, + } ) => { + await pageObject.goToProductCatalogAndInsertCollection( + 'myCustomCollection' + ); + + const sidebarSettings = pageObject.locateSidebarSettings(); + const onsaleControl = sidebarSettings.getByLabel( + SELECTORS.onSaleControlLabel + ); + await expect( onsaleControl ).toBeHidden(); + + await page + .getByRole( 'button', { name: 'Filters options' } ) + .click(); + const keywordControl = page.getByRole( 'menuitemcheckbox', { + name: 'Keyword', + } ); + + await expect( keywordControl ).toBeHidden(); + } ); + } ); + + test.describe( 'My Custom Collection with Preview', () => { + test( 'Clicking "My Custom Collection with Preview" should insert block and show 9 products', async ( { + pageObject, + } ) => { + await pageObject.createNewPostAndInsertBlock( + 'myCustomCollectionWithPreview' + ); + + await expect( pageObject.products ).toHaveCount( 9 ); + await expect( pageObject.productImages ).toHaveCount( 9 ); + await expect( pageObject.productTitles ).toHaveCount( 9 ); + await expect( pageObject.productPrices ).toHaveCount( 9 ); + await expect( pageObject.addToCartButtons ).toHaveCount( 9 ); + + await pageObject.publishAndGoToFrontend(); + await expect( pageObject.products ).toHaveCount( 9 ); + } ); + + test( 'Clicking "My Custom Collection with Preview" should show preview', async ( { + pageObject, + editor, + } ) => { + await pageObject.createNewPostAndInsertBlock( + 'myCustomCollectionWithPreview' + ); + const previewButtonLocator = editor.canvas.getByTestId( + SELECTORS.previewButtonTestID + ); + + // The preview button should be visible + await expect( previewButtonLocator ).toBeVisible(); + } ); + + test( 'Should display properly in Product Catalog template', async ( { + pageObject, + editor, + } ) => { + await pageObject.goToProductCatalogAndInsertCollection( + 'myCustomCollectionWithPreview' + ); + + const block = editor.canvas.getByLabel( + MY_REGISTERED_COLLECTIONS.myCustomCollectionWithPreview.label + ); + + // Check if products are visible + const products = block + .getByLabel( BLOCK_LABELS.productImage ) + .locator( 'visible=true' ); + await expect( products ).toHaveCount( 9 ); + + // Check if the preview button is visible + const previewButtonLocator = block.getByTestId( + SELECTORS.previewButtonTestID + ); + await expect( previewButtonLocator ).toBeVisible(); + } ); + } ); + + test.describe( 'My Custom Collection with Advanced Preview', () => { + test( 'Clicking "My Custom Collection with Advanced Preview" should insert block and show 9 products', async ( { + pageObject, + } ) => { + await pageObject.createNewPostAndInsertBlock( + 'myCustomCollectionWithAdvancedPreview' + ); + + await expect( pageObject.products ).toHaveCount( 9 ); + await expect( pageObject.productImages ).toHaveCount( 9 ); + await expect( pageObject.productTitles ).toHaveCount( 9 ); + await expect( pageObject.productPrices ).toHaveCount( 9 ); + await expect( pageObject.addToCartButtons ).toHaveCount( 9 ); + + await pageObject.publishAndGoToFrontend(); + await expect( pageObject.products ).toHaveCount( 9 ); + } ); + + test( 'Clicking "My Custom Collection with Advanced Preview" should show preview and then replace it by the actual content', async ( { + pageObject, + editor, + } ) => { + await pageObject.createNewPostAndInsertBlock( + 'myCustomCollectionWithAdvancedPreview' + ); + const previewButtonLocator = editor.canvas.getByTestId( + SELECTORS.previewButtonTestID + ); + + // The preview button should be visible + await expect( previewButtonLocator ).toBeVisible(); + + // The preview button should be hidden + await expect( previewButtonLocator ).toBeHidden(); + } ); + + test( 'Should display properly in Product Catalog template', async ( { + pageObject, + editor, + } ) => { + await pageObject.goToProductCatalogAndInsertCollection( + 'myCustomCollectionWithAdvancedPreview' + ); + + const block = editor.canvas.getByLabel( + MY_REGISTERED_COLLECTIONS.myCustomCollectionWithAdvancedPreview + .label + ); + + // Check if the preview button is visible + const previewButtonLocator = block.getByTestId( + SELECTORS.previewButtonTestID + ); + await expect( previewButtonLocator ).toBeVisible(); + + // Check if products are visible + const products = block + .getByLabel( BLOCK_LABELS.productImage ) + .locator( 'visible=true' ); + await expect( products ).toHaveCount( 9 ); + + // The preview button should be hidden after 1 second + await expect( previewButtonLocator ).toBeHidden(); + } ); + } ); + + [ + { + id: 'myCustomCollectionWithProductContext', + name: 'My Custom Collection - Product Context', + label: 'Block: My Custom Collection - Product Context', + previewLabelTemplate: [ 'woocommerce/woocommerce//single-product' ], + }, + { + id: 'myCustomCollectionWithCartContext', + name: 'My Custom Collection - Cart Context', + label: 'Block: My Custom Collection - Cart Context', + previewLabelTemplate: [ 'woocommerce/woocommerce//page-cart' ], + }, + { + id: 'myCustomCollectionWithOrderContext', + name: 'My Custom Collection - Order Context', + label: 'Block: My Custom Collection - Order Context', + previewLabelTemplate: [ + 'woocommerce/woocommerce//order-confirmation', + ], + }, + { + id: 'myCustomCollectionWithArchiveContext', + name: 'My Custom Collection - Archive Context', + label: 'Block: My Custom Collection - Archive Context', + previewLabelTemplate: [ + 'woocommerce/woocommerce//taxonomy-product_cat', + ], + }, + { + id: 'myCustomCollectionMultipleContexts', + name: 'My Custom Collection - Multiple Contexts', + label: 'Block: My Custom Collection - Multiple Contexts', + previewLabelTemplate: [ + 'woocommerce/woocommerce//single-product', + 'woocommerce/woocommerce//order-confirmation', + ], + }, + ].forEach( ( collection ) => { + for ( const template of collection.previewLabelTemplate ) { + test( `Collection "${ collection.name }" should show preview label in "${ template }"`, async ( { + pageObject, + editor, + } ) => { + await pageObject.goToEditorTemplate( template ); + await pageObject.insertProductCollection(); + await pageObject.chooseCollectionInTemplate( + collection.id as Collections + ); + + const block = editor.canvas.getByLabel( collection.label ); + const previewButtonLocator = block.getByTestId( + SELECTORS.previewButtonTestID + ); + + await expect( previewButtonLocator ).toBeVisible(); + } ); + } + + test( `Collection "${ collection.name }" should not show preview label in a post`, async ( { + pageObject, + editor, + } ) => { + await pageObject.createNewPostAndInsertBlock( + collection.id as Collections + ); + + const block = editor.canvas.getByLabel( collection.label ); + const previewButtonLocator = block.getByTestId( + SELECTORS.previewButtonTestID + ); + + await expect( previewButtonLocator ).toBeHidden(); + } ); + + test( `Collection "${ collection.name }" should not show preview label in Product Catalog template`, async ( { + pageObject, + editor, + } ) => { + await pageObject.goToProductCatalogAndInsertCollection( + collection.id as Collections + ); + + const block = editor.canvas.getByLabel( collection.label ); + const previewButtonLocator = block.getByTestId( + SELECTORS.previewButtonTestID + ); + + await expect( previewButtonLocator ).toBeHidden(); + } ); + } ); +} ); diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/product-filters/product-filters.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/product-filters/product-filters.block_theme.spec.ts index 1808a3ff867..8f4461180b6 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/product-filters/product-filters.block_theme.spec.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/product-filters/product-filters.block_theme.spec.ts @@ -412,7 +412,7 @@ test.describe( `${ blockData.name }`, () => { ).toBeVisible(); } ); - test( 'Dimentions > Block spacing: changing option should update the preview', async ( { + test( 'Dimensions > Block spacing: changing option should update the preview', async ( { editor, pageObject, } ) => { diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/rating-filter/rating-filter.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/rating-filter/rating-filter.block_theme.spec.ts index 0d7c4f9cd60..a92e204b61c 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/rating-filter/rating-filter.block_theme.spec.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/rating-filter/rating-filter.block_theme.spec.ts @@ -36,15 +36,17 @@ test.describe( `${ blockData.name } Block`, () => { await editor.openDocumentSettingsSidebar(); } ); - test( "should allow changing the block's title", async ( { page } ) => { + test( "should allow changing the block's title", async ( { editor } ) => { const textSelector = '.wp-block-woocommerce-filter-wrapper .wp-block-heading'; const title = 'New Title'; - await page.locator( textSelector ).fill( title ); + await editor.canvas.locator( textSelector ).fill( title ); - await expect( page.locator( textSelector ) ).toHaveText( title ); + await expect( editor.canvas.locator( textSelector ) ).toHaveText( + title + ); } ); test( 'should allow changing the display style', async ( { @@ -55,7 +57,7 @@ test.describe( `${ blockData.name } Block`, () => { await editor.selectBlocks( stockFilter ); await expect( - page.getByRole( 'checkbox', { name: 'Rated 1 out of 5' } ) + editor.canvas.getByRole( 'checkbox', { name: 'Rated 1 out of 5' } ) ).toBeVisible(); await page.getByLabel( 'DropDown' ).click(); @@ -67,10 +69,10 @@ test.describe( `${ blockData.name } Block`, () => { ).toBeHidden(); await expect( - page.getByRole( 'checkbox', { name: 'Rated 1 out of 5' } ) + editor.canvas.getByRole( 'checkbox', { name: 'Rated 1 out of 5' } ) ).toBeHidden(); - await expect( page.getByRole( 'combobox' ) ).toBeVisible(); + await expect( editor.canvas.getByRole( 'combobox' ) ).toBeVisible(); } ); test( 'should allow toggling the visibility of the filter button', async ( { diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/reviews-by-category/reviews-by-category.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/reviews-by-category/reviews-by-category.block_theme.spec.ts index 40e317d4694..9885a89970d 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/reviews-by-category/reviews-by-category.block_theme.spec.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/reviews-by-category/reviews-by-category.block_theme.spec.ts @@ -30,14 +30,16 @@ test.describe( `${ BLOCK_NAME } Block`, () => { page, editor, } ) => { - const categoryCheckbox = page.getByLabel( 'Clothing' ); + const categoryCheckbox = editor.canvas.getByLabel( 'Clothing' ).first(); await categoryCheckbox.check(); await expect( categoryCheckbox ).toBeChecked(); - const doneButton = page.getByRole( 'button', { name: 'Done' } ); + const doneButton = editor.canvas.getByRole( 'button', { + name: 'Done', + } ); await doneButton.click(); await expect( - page.getByText( hoodieReviews[ 0 ].review ) + editor.canvas.getByText( hoodieReviews[ 0 ].review ) ).toBeVisible(); await editor.publishAndVisitPost(); diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/reviews-by-product/reviews-by-product.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/reviews-by-product/reviews-by-product.block_theme.spec.ts index 3f4c7d4165a..67875a09fc9 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/reviews-by-product/reviews-by-product.block_theme.spec.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/reviews-by-product/reviews-by-product.block_theme.spec.ts @@ -30,15 +30,19 @@ test.describe( `${ BLOCK_NAME } Block`, () => { page, editor, } ) => { - const productCheckbox = page.getByLabel( 'Hoodie, has 2 reviews' ); + const productCheckbox = editor.canvas.getByLabel( + 'Hoodie, has 2 reviews' + ); await productCheckbox.check(); await expect( productCheckbox ).toBeChecked(); - const doneButton = page.getByRole( 'button', { name: 'Done' } ); + const doneButton = editor.canvas.getByRole( 'button', { + name: 'Done', + } ); await doneButton.click(); await expect( - page.getByText( hoodieReviews[ 0 ].review ) + editor.canvas.getByText( hoodieReviews[ 0 ].review ) ).toBeVisible(); await editor.publishAndVisitPost(); diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/single-product-details/single-product-details.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/single-product-details/single-product-details.block_theme.spec.ts index 967ddf62d00..9e45c19be36 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/single-product-details/single-product-details.block_theme.spec.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/single-product-details/single-product-details.block_theme.spec.ts @@ -55,4 +55,56 @@ test.describe( `${ blockData.slug } Block`, () => { /This block lists description, attributes and reviews for a single product./ ); } ); + + test( 'block hides tab title in content when toggle is off', async ( { + admin, + requestUtils, + editor, + page, + frontendUtils, + } ) => { + const template = await requestUtils.createTemplate( 'wp_template', { + slug: 'single-product-v-neck-t-shirt', + title: 'Product Details Test', + content: '', + } ); + + await admin.visitSiteEditor( { + postId: template.id, + postType: 'wp_template', + canvas: 'edit', + } ); + + await editor.insertBlock( { + name: blockData.slug, + } ); + + const block = await editor.getBlockByName( blockData.slug ); + await editor.selectBlocks( block ); + + // Verify the "Description" h2 heading is visible by default. + await expect( + editor.canvas.locator( 'h2:has-text("Description")' ) + ).toBeVisible(); + + await editor.openDocumentSettingsSidebar(); + await page.getByText( 'Show tab title in content' ).click(); + + // Verify the "Description" h2 heading has been hidden from the canvas. + await expect( + editor.canvas.locator( 'h2:has-text("Description")' ) + ).toBeHidden(); + + await editor.saveSiteEditorEntities( { + isOnlyCurrentEntityDirty: true, + } ); + + await frontendUtils.goToShop(); + await page.getByText( 'V-Neck T-Shirt' ).click(); + + // Verify the "Description" h2 heading is not in the page. + await expect( + page.locator( 'h2:has-text("Description")' ) + ).toBeHidden(); + } ); } ); diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/single-product-template/single-product-template.product-rating.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/single-product-template/single-product-template.product-rating.spec.ts new file mode 100644 index 00000000000..2fab767912d --- /dev/null +++ b/plugins/woocommerce-blocks/tests/e2e/tests/single-product-template/single-product-template.product-rating.spec.ts @@ -0,0 +1,101 @@ +/** + * External dependencies + */ +import { Editor, test as base, expect } from '@woocommerce/e2e-utils'; + +/** + * Internal dependencies + */ + +const blockData = { + name: 'Single Product', + slug: 'woocommerce/single-product', + product: 'Hoodie', + productSlug: 'hoodie', +}; + +class BlockUtils { + editor: Editor; + admin; + + constructor( { editor, admin }: { editor: Editor; admin } ) { + this.editor = editor; + this.admin = admin; + } + + async configureSingleProductBlockForProduct( product: string ) { + const singleProductBlock = await this.editor.getBlockByName( + 'woocommerce/single-product' + ); + + await singleProductBlock + .locator( `input[type="radio"][value="${ product }"]` ) + .nth( 0 ) + .click(); + + await singleProductBlock.getByText( 'Done' ).click(); + } + + async insertBlockAndVisit( block: string, product: string ) { + await this.admin.createNewPost(); + await this.editor.insertBlock( { name: block } ); + await this.configureSingleProductBlockForProduct( product ); + await this.editor.publishAndVisitPost(); + } +} + +const test = base.extend< { blockUtils: BlockUtils } >( { + blockUtils: async ( { editor, admin }, use ) => { + await use( new BlockUtils( { editor, admin } ) ); + }, +} ); + +test.describe( `${ blockData.slug } Block`, () => { + test( 'Product Rating block is not visible if ratings are disabled for product', async ( { + admin, + page, + } ) => { + await test.step( `Disable reviews for ${ blockData.productSlug }`, async () => { + await page.goto( `/products/${ blockData.productSlug }` ); + await page.click( 'text=Edit product' ); + await page.getByRole( 'link', { name: 'Inventory' } ).click(); + await page.getByRole( 'link', { name: 'Advanced' } ).click(); + await page.waitForSelector( 'text=Enable reviews' ); + await admin.page + .getByRole( 'checkbox', { + name: 'Enable reviews', + } ) + .uncheck(); + await page.getByRole( 'button', { name: 'Update' } ).click(); + } ); + + await page.goto( '/product/hoodie/' ); + + await expect( + page.locator( '.wc-block-components-product-rating' ) + ).not.toBeVisible(); + } ); + + test( 'Product Rating block is not visible if ratings are disabled globally in the store', async ( { + admin, + page, + } ) => { + await test.step( `Disable reviews in the store`, async () => { + await page.goto( + '/wp-admin/admin.php?page=wc-settings&tab=products' + ); + await admin.page + .getByRole( 'checkbox', { + name: 'Enable product reviews', + } ) + .uncheck(); + await page.getByRole( 'button', { name: 'Save changes' } ).click(); + } ); + + await page.goto( '/product/hoodie/' ); + + await expect( + page.locator( '.wc-block-components-product-rating' ) + ).not.toBeVisible(); + } ); +} ); diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/single-product/product-rating-single-product.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/single-product/product-rating-single-product.spec.ts new file mode 100644 index 00000000000..0a137f2fc92 --- /dev/null +++ b/plugins/woocommerce-blocks/tests/e2e/tests/single-product/product-rating-single-product.spec.ts @@ -0,0 +1,113 @@ +/** + * External dependencies + */ +import { Editor, test as base, expect } from '@woocommerce/e2e-utils'; + +/** + * Internal dependencies + */ + +const blockData = { + name: 'Single Product', + slug: 'woocommerce/single-product', + product: 'Hoodie', + productSlug: 'hoodie', +}; + +class BlockUtils { + editor: Editor; + admin; + + constructor( { editor, admin }: { editor: Editor; admin } ) { + this.editor = editor; + this.admin = admin; + } + + async configureSingleProductBlockForProduct( product: string ) { + const singleProductBlock = await this.editor.getBlockByName( + 'woocommerce/single-product' + ); + + await singleProductBlock + .locator( `input[type="radio"][value="${ product }"]` ) + .nth( 0 ) + .click(); + + await singleProductBlock.getByText( 'Done' ).click(); + } + + async insertBlockAndVisit( block: string, product: string ) { + await this.admin.createNewPost(); + await this.editor.insertBlock( { name: block } ); + await this.configureSingleProductBlockForProduct( product ); + await this.editor.publishAndVisitPost(); + } +} + +const test = base.extend< { blockUtils: BlockUtils } >( { + blockUtils: async ( { editor, admin }, use ) => { + await use( new BlockUtils( { editor, admin } ) ); + }, +} ); + +test.describe( `${ blockData.slug } Block`, () => { + test( 'Product Rating block is not visible if ratings are disabled for product', async ( { + admin, + page, + blockUtils, + } ) => { + await test.step( `Disable reviews for ${ blockData.productSlug }`, async () => { + await page.goto( `/products/${ blockData.productSlug }` ); + await page.click( 'text=Edit product' ); + await page.getByRole( 'link', { name: 'Inventory' } ).click(); + await page.getByRole( 'link', { name: 'Advanced' } ).click(); + await page.waitForSelector( 'text=Enable reviews' ); + await admin.page + .getByRole( 'checkbox', { + name: 'Enable reviews', + } ) + .uncheck(); + await page.getByRole( 'button', { name: 'Update' } ).click(); + } ); + + await test.step( `Insert the block in a post and visit it`, async () => { + await blockUtils.insertBlockAndVisit( + blockData.slug, + blockData.productSlug + ); + } ); + + await expect( + page.locator( '.wc-block-components-product-rating' ) + ).not.toBeVisible(); + } ); + + test( 'Product Rating block is not visible if ratings are disabled globally in the store', async ( { + admin, + page, + blockUtils, + } ) => { + await test.step( `Disable reviews in the store`, async () => { + await page.goto( + '/wp-admin/admin.php?page=wc-settings&tab=products' + ); + await admin.page + .getByRole( 'checkbox', { + name: 'Enable product reviews', + } ) + .uncheck(); + await page.getByRole( 'button', { name: 'Save changes' } ).click(); + } ); + + await test.step( 'Insert the block in a post and visit it', async () => { + await blockUtils.insertBlockAndVisit( + blockData.slug, + blockData.productSlug + ); + } ); + + await expect( + page.locator( '.wc-block-components-product-rating' ) + ).not.toBeVisible(); + } ); +} ); diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/stock-filter/stock-filter.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/stock-filter/stock-filter.block_theme.spec.ts index 765256309bf..bc00c4e846e 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/stock-filter/stock-filter.block_theme.spec.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/stock-filter/stock-filter.block_theme.spec.ts @@ -37,15 +37,17 @@ test.describe( `${ blockData.name } Block`, () => { await editor.openDocumentSettingsSidebar(); } ); - test( "should allow changing the block's title", async ( { page } ) => { + test( "should allow changing the block's title", async ( { editor } ) => { const textSelector = '.wp-block-woocommerce-filter-wrapper .wp-block-heading'; const title = 'New Title'; - await page.locator( textSelector ).fill( title ); + await editor.canvas.locator( textSelector ).fill( title ); - await expect( page.locator( textSelector ) ).toHaveText( title ); + await expect( editor.canvas.locator( textSelector ) ).toHaveText( + title + ); } ); test( 'should allow changing the display style', async ( { @@ -81,7 +83,7 @@ test.describe( `${ blockData.name } Block`, () => { } ) ).toBeHidden(); - await expect( page.getByRole( 'combobox' ) ).toBeVisible(); + await expect( editor.canvas.getByRole( 'combobox' ) ).toBeVisible(); } ); test( 'should allow toggling the visibility of the filter button', async ( { diff --git a/plugins/woocommerce/.wp-env.json b/plugins/woocommerce/.wp-env.json index 3cbe6bd12c5..333f1fdd26a 100644 --- a/plugins/woocommerce/.wp-env.json +++ b/plugins/woocommerce/.wp-env.json @@ -16,8 +16,8 @@ "test-data/images/": "./tests/e2e-pw/test-data/images/" }, "lifecycleScripts": { - "afterStart": "wp-env run tests-cli bash wp-content/plugins/woocommerce/tests/e2e-pw/bin/test-env-setup.sh", - "afterClean": "wp-env run tests-cli bash wp-content/plugins/woocommerce/tests/e2e-pw/bin/test-env-setup.sh" + "afterStart": "./tests/e2e-pw/bin/test-env-setup.sh", + "afterClean": "./tests/e2e-pw/bin/test-env-setup.sh" }, "env": { "development": { diff --git a/plugins/woocommerce/README.md b/plugins/woocommerce/README.md index 2bb739cbeab..2e02f101ff2 100644 --- a/plugins/woocommerce/README.md +++ b/plugins/woocommerce/README.md @@ -72,7 +72,7 @@ pnpm run --filter=@woocommerce/block-library test Here is a collection of scripts that can help when developing the React-based admin interface. ```bash -# Create a develoment build of the React-based admin client. +# Create a development build of the React-based admin client. pnpm --filter=@woocommerce/admin-library dev # Create and watch a development build of the React-based admin client. pnpm --filter=@woocommerce/admin-library start diff --git a/plugins/woocommerce/changelog/45439-feature-normalize-geolocation-hash b/plugins/woocommerce/changelog/45439-feature-normalize-geolocation-hash deleted file mode 100644 index 436f45eb4a0..00000000000 --- a/plugins/woocommerce/changelog/45439-feature-normalize-geolocation-hash +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak - -Make `geolocation_ajax_get_location_hash` case-insensitive, to reduce the number of cache misses. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/46201-product-collection-sync-with-current-query-doesnt-display-the-correct-products-in-the-specific-category-templates b/plugins/woocommerce/changelog/46201-product-collection-sync-with-current-query-doesnt-display-the-correct-products-in-the-specific-category-templates deleted file mode 100644 index d97345f0b51..00000000000 --- a/plugins/woocommerce/changelog/46201-product-collection-sync-with-current-query-doesnt-display-the-correct-products-in-the-specific-category-templates +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Product Collection: fix the preview if used in Products by specific Category or Tag diff --git a/plugins/woocommerce/changelog/47899-try-poc-express-checkout-button-styles b/plugins/woocommerce/changelog/47899-try-poc-express-checkout-button-styles deleted file mode 100644 index 857d00e2bb8..00000000000 --- a/plugins/woocommerce/changelog/47899-try-poc-express-checkout-button-styles +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: add - -Adds unified styles for the express checkout block \ No newline at end of file diff --git a/plugins/woocommerce/changelog/48371-frosso-patch-1 b/plugins/woocommerce/changelog/48371-frosso-patch-1 deleted file mode 100644 index 8b1a47a28e3..00000000000 --- a/plugins/woocommerce/changelog/48371-frosso-patch-1 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: update - -feat: add `aria-required` attributes to WC form fields \ No newline at end of file diff --git a/plugins/woocommerce/changelog/48843-update-48796-use-createRoot-instead-of-render b/plugins/woocommerce/changelog/48843-update-48796-use-createRoot-instead-of-render deleted file mode 100644 index c6f497480f0..00000000000 --- a/plugins/woocommerce/changelog/48843-update-48796-use-createRoot-instead-of-render +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -Switch `render()` to `createRoot().render()` to use React 18 features. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/48862-product-collection-trigger-wc-blocks_product_list_rendered-js-event b/plugins/woocommerce/changelog/48862-product-collection-trigger-wc-blocks_product_list_rendered-js-event deleted file mode 100644 index 6f4d379a7a3..00000000000 --- a/plugins/woocommerce/changelog/48862-product-collection-trigger-wc-blocks_product_list_rendered-js-event +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: add - -Product Collection: emit the JS event when PC block is rendered diff --git a/plugins/woocommerce/changelog/48937-update-perf-test-my-account-orders-id-boundary b/plugins/woocommerce/changelog/48937-update-perf-test-my-account-orders-id-boundary deleted file mode 100644 index 9008e91250a..00000000000 --- a/plugins/woocommerce/changelog/48937-update-perf-test-my-account-orders-id-boundary +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak -Comment: This is an update to a performance test id boundary and does not require a changelog - diff --git a/plugins/woocommerce/changelog/48969-dev-dont-warn-aria-missing b/plugins/woocommerce/changelog/48969-dev-dont-warn-aria-missing deleted file mode 100644 index d1387dbf02b..00000000000 --- a/plugins/woocommerce/changelog/48969-dev-dont-warn-aria-missing +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Avoid PHP warnings if `add-to-cart.php` template does not pass `aria-describedby_text` \ No newline at end of file diff --git a/plugins/woocommerce/changelog/49214-fix-remove-active-shipping-zone-check-cart-page b/plugins/woocommerce/changelog/49214-fix-remove-active-shipping-zone-check-cart-page deleted file mode 100644 index 985ae712a88..00000000000 --- a/plugins/woocommerce/changelog/49214-fix-remove-active-shipping-zone-check-cart-page +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Remove Active Shipping Zones check for displaying shipping calculator on the Cart Page. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/49304-update-express-checkout-layout b/plugins/woocommerce/changelog/49304-update-express-checkout-layout deleted file mode 100644 index bfeb46cfab9..00000000000 --- a/plugins/woocommerce/changelog/49304-update-express-checkout-layout +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Fixes a bug where some express payment buttons weren't being rendered correctly \ No newline at end of file diff --git a/plugins/woocommerce/changelog/49648-fix-43603-footer-text-color b/plugins/woocommerce/changelog/49648-fix-43603-footer-text-color deleted file mode 100644 index 4f9a70f7ee0..00000000000 --- a/plugins/woocommerce/changelog/49648-fix-43603-footer-text-color +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: add - -Add field for the email footer text color \ No newline at end of file diff --git a/plugins/woocommerce/changelog/49737-fix_add_login_in_reset_pass_emails b/plugins/woocommerce/changelog/49737-fix_add_login_in_reset_pass_emails deleted file mode 100644 index fd884039ac5..00000000000 --- a/plugins/woocommerce/changelog/49737-fix_add_login_in_reset_pass_emails +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: enhancement - -Add username in email reset-password link \ No newline at end of file diff --git a/plugins/woocommerce/changelog/49748-49589-new-cys-homepage-tests b/plugins/woocommerce/changelog/49748-49589-new-cys-homepage-tests deleted file mode 100644 index acceaa5c4c4..00000000000 --- a/plugins/woocommerce/changelog/49748-49589-new-cys-homepage-tests +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -CYS - Add tests for the Full Composability feature. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/49755-fix-49660-new-filter-sku-lock b/plugins/woocommerce/changelog/49755-fix-49660-new-filter-sku-lock deleted file mode 100644 index 307c6d2e39c..00000000000 --- a/plugins/woocommerce/changelog/49755-fix-49660-new-filter-sku-lock +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: enhancement - -Add a filter to override the SKU database lock. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/49757-dev-remove-unused-imports b/plugins/woocommerce/changelog/49757-dev-remove-unused-imports deleted file mode 100644 index 983bf338098..00000000000 --- a/plugins/woocommerce/changelog/49757-dev-remove-unused-imports +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev -Comment: Removal of an unused import is a non-functional change. - diff --git a/plugins/woocommerce/changelog/49758-update-49210-restrict-wccomhelper-to-admin b/plugins/woocommerce/changelog/49758-update-49210-restrict-wccomhelper-to-admin deleted file mode 100644 index f4767ee18cd..00000000000 --- a/plugins/woocommerce/changelog/49758-update-49210-restrict-wccomhelper-to-admin +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: enhancement - -Ensure `wccomHelper` data is only loaded on the Extensions page where it's needed. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/49763-add-blueprint-package-1 b/plugins/woocommerce/changelog/49763-add-blueprint-package-1 deleted file mode 100644 index 510d69598ef..00000000000 --- a/plugins/woocommerce/changelog/49763-add-blueprint-package-1 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: add - -Add blueprint behind a feature flag for testing purposes. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/49765-add-blueprint-rest-api-2 b/plugins/woocommerce/changelog/49765-add-blueprint-rest-api-2 deleted file mode 100644 index 623342b610b..00000000000 --- a/plugins/woocommerce/changelog/49765-add-blueprint-rest-api-2 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak -Comment: This is a sub-PR for add/blueprint-package-1 - diff --git a/plugins/woocommerce/changelog/49774-add-blueprint-frontend-3 b/plugins/woocommerce/changelog/49774-add-blueprint-frontend-3 deleted file mode 100644 index 4126fc75017..00000000000 --- a/plugins/woocommerce/changelog/49774-add-blueprint-frontend-3 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak -Comment: This is a sub-pr of add/blueprint-rest-api-2 - diff --git a/plugins/woocommerce/changelog/49775-add-blueprint-settings-page-3 b/plugins/woocommerce/changelog/49775-add-blueprint-settings-page-3 deleted file mode 100644 index 1a16bf00900..00000000000 --- a/plugins/woocommerce/changelog/49775-add-blueprint-settings-page-3 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak -Comment: This is a sub-PR of add/blueprint-rest-api-2 - diff --git a/plugins/woocommerce/changelog/49796-add-47964-product-collection-collection-should-be-able-to-specify-requiredcontext-or-requiredlocation-and-trigger-preview-mode-in-editor-when-theres-no-required-context b/plugins/woocommerce/changelog/49796-add-47964-product-collection-collection-should-be-able-to-specify-requiredcontext-or-requiredlocation-and-trigger-preview-mode-in-editor-when-theres-no-required-context deleted file mode 100644 index 16aff3bd0c2..00000000000 --- a/plugins/woocommerce/changelog/49796-add-47964-product-collection-collection-should-be-able-to-specify-requiredcontext-or-requiredlocation-and-trigger-preview-mode-in-editor-when-theres-no-required-context +++ /dev/null @@ -1,4 +0,0 @@ -Significance: major -Type: add - -Product Collection: Enable Context-Aware Previews by Adding `usesReference` to `registerProductCollection` \ No newline at end of file diff --git a/plugins/woocommerce/changelog/49803-fix-subscription-activation-with-multiple-subs-for-same-product b/plugins/woocommerce/changelog/49803-fix-subscription-activation-with-multiple-subs-for-same-product deleted file mode 100644 index 9f1841d18f5..00000000000 --- a/plugins/woocommerce/changelog/49803-fix-subscription-activation-with-multiple-subs-for-same-product +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Fix activating the installed subscription when the user has multiple active licenses for the same product. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/49961-update-server-caching-link b/plugins/woocommerce/changelog/49961-update-server-caching-link deleted file mode 100644 index 0f9b21daef7..00000000000 --- a/plugins/woocommerce/changelog/49961-update-server-caching-link +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak -Comment: This PR only updates an in-product link to the docs. The doc content is the same and has only been moved. No changelog entry needed IMO - diff --git a/plugins/woocommerce/changelog/49970-fix-cys-skip-test-wordpress-6-5 b/plugins/woocommerce/changelog/49970-fix-cys-skip-test-wordpress-6-5 deleted file mode 100644 index b43e5312c34..00000000000 --- a/plugins/woocommerce/changelog/49970-fix-cys-skip-test-wordpress-6-5 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak -Comment: Skip PTK is down test on WordPress 6.5 - diff --git a/plugins/woocommerce/changelog/49999-feat-49998-replace-product-filters-block-with-template-part-in-overlay-template-part b/plugins/woocommerce/changelog/49999-feat-49998-replace-product-filters-block-with-template-part-in-overlay-template-part deleted file mode 100644 index 3db8fb453d6..00000000000 --- a/plugins/woocommerce/changelog/49999-feat-49998-replace-product-filters-block-with-template-part-in-overlay-template-part +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak -Comment: Replace the Product Filters block within the Product Filters Overlay template part with the Product Filters template part. This functionality is experimental and not yet available. - diff --git a/plugins/woocommerce/changelog/50006-patch-1 b/plugins/woocommerce/changelog/50006-patch-1 deleted file mode 100644 index a3d41626782..00000000000 --- a/plugins/woocommerce/changelog/50006-patch-1 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: performance - -Improve performance of maybe_assign_default_product_cat by only dropping cache and term recounting if changes were made in the database \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50016-skip-cys-test-wordpress-version b/plugins/woocommerce/changelog/50016-skip-cys-test-wordpress-version deleted file mode 100644 index 4cccf692ea0..00000000000 --- a/plugins/woocommerce/changelog/50016-skip-cys-test-wordpress-version +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -CYS - Run appropriate tests depending on the WordPress version. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50047-fix-typos b/plugins/woocommerce/changelog/50047-fix-typos new file mode 100644 index 00000000000..e5d2f2b3db2 --- /dev/null +++ b/plugins/woocommerce/changelog/50047-fix-typos @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Comment: Fix comment typos across various files. diff --git a/plugins/woocommerce/changelog/50080-fix-revisit-opt-in-flow-patterns b/plugins/woocommerce/changelog/50080-fix-revisit-opt-in-flow-patterns deleted file mode 100644 index 33aefeeb400..00000000000 --- a/plugins/woocommerce/changelog/50080-fix-revisit-opt-in-flow-patterns +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -CYS: Improve opt-in flow patterns. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50083-49192-update-show-on-front-setting b/plugins/woocommerce/changelog/50083-49192-update-show-on-front-setting deleted file mode 100644 index 8da769f8bde..00000000000 --- a/plugins/woocommerce/changelog/50083-49192-update-show-on-front-setting +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -CYS - Update the "show_on_front" setting to "posts" to avoid overriding the "page" template. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50127-fix-47557-display-address-card-for-virtual-products b/plugins/woocommerce/changelog/50127-fix-47557-display-address-card-for-virtual-products deleted file mode 100644 index eabec756bb8..00000000000 --- a/plugins/woocommerce/changelog/50127-fix-47557-display-address-card-for-virtual-products +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Display address card for virtual products if shopper's address is known \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50136-update-46209-text-adjustments-on-shipping-zones-settings-page b/plugins/woocommerce/changelog/50136-update-46209-text-adjustments-on-shipping-zones-settings-page deleted file mode 100644 index 01b271bff23..00000000000 --- a/plugins/woocommerce/changelog/50136-update-46209-text-adjustments-on-shipping-zones-settings-page +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -Text adjustments on shipping zones settings page \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50145-patch-4 b/plugins/woocommerce/changelog/50145-patch-4 deleted file mode 100644 index ecd473d13c0..00000000000 --- a/plugins/woocommerce/changelog/50145-patch-4 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak -Comment: Fix typo (class-wc-rest-wccom-site-ssr-controller.php) - diff --git a/plugins/woocommerce/changelog/50154-patch-8 b/plugins/woocommerce/changelog/50154-patch-8 deleted file mode 100644 index a803d47eda9..00000000000 --- a/plugins/woocommerce/changelog/50154-patch-8 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak -Comment: Fix typo (product-collection.block_theme.spec.ts) - diff --git a/plugins/woocommerce/changelog/50157-patch-10 b/plugins/woocommerce/changelog/50157-patch-10 deleted file mode 100644 index dd21ce401f6..00000000000 --- a/plugins/woocommerce/changelog/50157-patch-10 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak -Comment: Fix typo (filter-setter.php) - diff --git a/plugins/woocommerce/changelog/50164-add-44877-context-linking-a-product-with-collection b/plugins/woocommerce/changelog/50164-add-44877-context-linking-a-product-with-collection new file mode 100644 index 00000000000..1308c1e61e7 --- /dev/null +++ b/plugins/woocommerce/changelog/50164-add-44877-context-linking-a-product-with-collection @@ -0,0 +1,4 @@ +Significance: major +Type: add + +Product Collection - Show product picker in Editor when collection requires a product but not available
A collection can define if it requires a product context. This can be done using `usesReference` argument i.e. ```tsx __experimentalRegisterProductCollection({ ..., usesReference: ['product'], ) ``` When product context doesn't exist in current template/page/post etc. then we show product picker in Editor. This way, merchant can manually provide a product context to the collection. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50171-cys-intro-docs b/plugins/woocommerce/changelog/50171-cys-intro-docs deleted file mode 100644 index d94813c978b..00000000000 --- a/plugins/woocommerce/changelog/50171-cys-intro-docs +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: dev - -CYS - Document possible Intro pages \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50174-fix-50173 b/plugins/woocommerce/changelog/50174-fix-50173 deleted file mode 100644 index f51740dbb97..00000000000 --- a/plugins/woocommerce/changelog/50174-fix-50173 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: enhancement - -Add filter `woocommerce_is_store_page` to modify whether Coming Soon mode considers a URL a store page or not. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50186-feat-49690-add-navigation-block-to-the-product-filters-block b/plugins/woocommerce/changelog/50186-feat-49690-add-navigation-block-to-the-product-filters-block deleted file mode 100644 index 2edc888a164..00000000000 --- a/plugins/woocommerce/changelog/50186-feat-49690-add-navigation-block-to-the-product-filters-block +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak -Comment: Add the Product Filters Overlay Navigation block to the Product Filters block. - diff --git a/plugins/woocommerce/changelog/50190-add-navigation-deprecation b/plugins/woocommerce/changelog/50190-add-navigation-deprecation deleted file mode 100644 index 1f80f12d42d..00000000000 --- a/plugins/woocommerce/changelog/50190-add-navigation-deprecation +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -Remove WooCommerce Navigation client side feature and deprecate PHP classes. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50198-patch-13 b/plugins/woocommerce/changelog/50198-patch-13 deleted file mode 100644 index 605ed0c3902..00000000000 --- a/plugins/woocommerce/changelog/50198-patch-13 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak -Comment: Made two strings translatable in slotfill.js - diff --git a/plugins/woocommerce/changelog/50210-44589-cys---core-e2e-test-add-coverage-for-the-fonts-added-via-font-library b/plugins/woocommerce/changelog/50210-44589-cys---core-e2e-test-add-coverage-for-the-fonts-added-via-font-library deleted file mode 100644 index 29256e8f357..00000000000 --- a/plugins/woocommerce/changelog/50210-44589-cys---core-e2e-test-add-coverage-for-the-fonts-added-via-font-library +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -CYS: add E2E tests for fonts installation. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50211-48657-add-better-error-message b/plugins/woocommerce/changelog/50211-48657-add-better-error-message deleted file mode 100644 index 9914447da0f..00000000000 --- a/plugins/woocommerce/changelog/50211-48657-add-better-error-message +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -CYS - Improve the error when a request fails due to permissions \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50239-update-in-product-blocks-docs-links b/plugins/woocommerce/changelog/50239-update-in-product-blocks-docs-links deleted file mode 100644 index 02d5d885cdc..00000000000 --- a/plugins/woocommerce/changelog/50239-update-in-product-blocks-docs-links +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak -Comment: Updating documentation links, I don't believe a comment is necessary for this small change. - diff --git a/plugins/woocommerce/changelog/50243-patch-14 b/plugins/woocommerce/changelog/50243-patch-14 deleted file mode 100644 index 56b54b123a4..00000000000 --- a/plugins/woocommerce/changelog/50243-patch-14 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak -Comment: Fixed typos in CSS classes - diff --git a/plugins/woocommerce/changelog/50247-patch-17 b/plugins/woocommerce/changelog/50247-patch-17 deleted file mode 100644 index d059e683b20..00000000000 --- a/plugins/woocommerce/changelog/50247-patch-17 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak -Comment: Fixed typo WooCommmerce - diff --git a/plugins/woocommerce/changelog/50266-fix-remove-functional-component-default-props b/plugins/woocommerce/changelog/50266-fix-remove-functional-component-default-props deleted file mode 100644 index 6e876bf13d2..00000000000 --- a/plugins/woocommerce/changelog/50266-fix-remove-functional-component-default-props +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: dev - -Removed defaultProps from React functional components since they will be deprecated for React 19 \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50278-fix-improve_label b/plugins/woocommerce/changelog/50278-fix-improve_label deleted file mode 100644 index 0bc697665eb..00000000000 --- a/plugins/woocommerce/changelog/50278-fix-improve_label +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: enhancement - -CYS: improve CTA \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50332-patch-19 b/plugins/woocommerce/changelog/50332-patch-19 deleted file mode 100644 index 043fa2f15ca..00000000000 --- a/plugins/woocommerce/changelog/50332-patch-19 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak -Comment: Fix typo (sidebar-navigation-screen-typography.tsx) - diff --git a/plugins/woocommerce/changelog/50334-patch-21 b/plugins/woocommerce/changelog/50334-patch-21 deleted file mode 100644 index 5ae79e96c9a..00000000000 --- a/plugins/woocommerce/changelog/50334-patch-21 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak -Comment: Fix typos (Produts) - diff --git a/plugins/woocommerce/changelog/50335-patch-22 b/plugins/woocommerce/changelog/50335-patch-22 deleted file mode 100644 index 71a83fde9ff..00000000000 --- a/plugins/woocommerce/changelog/50335-patch-22 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak -Comment: Fix typos (920.md) - diff --git a/plugins/woocommerce/changelog/50348-fix-improve-documentation b/plugins/woocommerce/changelog/50348-fix-improve-documentation deleted file mode 100644 index f109e8a86de..00000000000 --- a/plugins/woocommerce/changelog/50348-fix-improve-documentation +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak -Comment: Compatibility Layer: fix documentation. - diff --git a/plugins/woocommerce/changelog/50352-48150-move-endpoints b/plugins/woocommerce/changelog/50352-48150-move-endpoints deleted file mode 100644 index b4078ecd101..00000000000 --- a/plugins/woocommerce/changelog/50352-48150-move-endpoints +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: dev - -CYS - Move the ai/store-title endpoint to woocommerce admin API \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50378-update-remove-jetpack-boost b/plugins/woocommerce/changelog/50378-update-remove-jetpack-boost deleted file mode 100644 index 8c67ebcc144..00000000000 --- a/plugins/woocommerce/changelog/50378-update-remove-jetpack-boost +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: update -Comment: Remove `jetpack-boost` as we no longer run the experiment. - diff --git a/plugins/woocommerce/changelog/50392-fix-compatibility-layer b/plugins/woocommerce/changelog/50392-fix-compatibility-layer deleted file mode 100644 index 0a56565cda6..00000000000 --- a/plugins/woocommerce/changelog/50392-fix-compatibility-layer +++ /dev/null @@ -1,4 +0,0 @@ -Significance: major -Type: fix - -Compatibility Layer: fix 'woocommerce_before_single_product_summary' hook position. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50396-48150-move-products-endpoint b/plugins/woocommerce/changelog/50396-48150-move-products-endpoint new file mode 100644 index 00000000000..3a397780d9a --- /dev/null +++ b/plugins/woocommerce/changelog/50396-48150-move-products-endpoint @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +CYS - Move the "ai/products" endpoint to woocommerce admin API. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50400-48150-move-patterns-endpoint-to-admin b/plugins/woocommerce/changelog/50400-48150-move-patterns-endpoint-to-admin new file mode 100644 index 00000000000..1575e80c49a --- /dev/null +++ b/plugins/woocommerce/changelog/50400-48150-move-patterns-endpoint-to-admin @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +CYS - Move the "private/patterns" endpoint to woocommerce admin API. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50424-fix-store-alerts-action-keys b/plugins/woocommerce/changelog/50424-fix-store-alerts-action-keys deleted file mode 100644 index c8f5ba3d6c9..00000000000 --- a/plugins/woocommerce/changelog/50424-fix-store-alerts-action-keys +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: update - -Update Store Alert actions to have unique keys. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50430-update-log-remote-handler b/plugins/woocommerce/changelog/50430-update-log-remote-handler deleted file mode 100644 index 92b8f644e34..00000000000 --- a/plugins/woocommerce/changelog/50430-update-log-remote-handler +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: update - -Add remote logger as a log handler to wc logger \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50431-50371-cys-automatic-scrolling-to-added-pattern-not-functioning b/plugins/woocommerce/changelog/50431-50371-cys-automatic-scrolling-to-added-pattern-not-functioning deleted file mode 100644 index 4ef5a4f2829..00000000000 --- a/plugins/woocommerce/changelog/50431-50371-cys-automatic-scrolling-to-added-pattern-not-functioning +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -CYS: Fix auto scroll when a new block is added. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50478-50385-update-cys-colors b/plugins/woocommerce/changelog/50478-50385-update-cys-colors deleted file mode 100644 index f6d8b6ecf61..00000000000 --- a/plugins/woocommerce/changelog/50478-50385-update-cys-colors +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -CYS - Update icon and text colors in the assembler. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50495-50460-fatal-call-to-a-member-function-get_gallery_image_ids-on-bool b/plugins/woocommerce/changelog/50495-50460-fatal-call-to-a-member-function-get_gallery_image_ids-on-bool deleted file mode 100644 index 4e3dfe3d3fd..00000000000 --- a/plugins/woocommerce/changelog/50495-50460-fatal-call-to-a-member-function-get_gallery_image_ids-on-bool +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak -Comment: Product Gallery: add defensive check to avoid fatal errors. - diff --git a/plugins/woocommerce/changelog/50498-fix-fonts-zoom-out b/plugins/woocommerce/changelog/50498-fix-fonts-zoom-out deleted file mode 100644 index 1a00e0a30ec..00000000000 --- a/plugins/woocommerce/changelog/50498-fix-fonts-zoom-out +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -CYS: disable zoom out on fonts/color pairs iframe \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50519-fix-ci-49583 b/plugins/woocommerce/changelog/50519-fix-ci-49583 deleted file mode 100644 index 66a69ce95d0..00000000000 --- a/plugins/woocommerce/changelog/50519-fix-ci-49583 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak -Comment: This makes an adjustment to another change that is targeted for the same 9.3 release. - diff --git a/plugins/woocommerce/changelog/50521-fix-DataPoller-valid-result b/plugins/woocommerce/changelog/50521-fix-DataPoller-valid-result deleted file mode 100644 index 30c3f0ffc0c..00000000000 --- a/plugins/woocommerce/changelog/50521-fix-DataPoller-valid-result +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -In Remote Specs, treat empty arrays as valid cached values so individual engines can return default values. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50529-fix-improve-opt-in-flow b/plugins/woocommerce/changelog/50529-fix-improve-opt-in-flow deleted file mode 100644 index 356e0c26364..00000000000 --- a/plugins/woocommerce/changelog/50529-fix-improve-opt-in-flow +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -CYS: Improve opt in flow \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50568-update-wp-env b/plugins/woocommerce/changelog/50568-update-wp-env deleted file mode 100644 index 39d93fa45b0..00000000000 --- a/plugins/woocommerce/changelog/50568-update-wp-env +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak -Comment: This updates the configuration for the local wp-env development environment so that the mysql database ports remain consistent between sessions. - diff --git a/plugins/woocommerce/changelog/50590-add-44877-context-linking-a-product-with-collection-inspector-control b/plugins/woocommerce/changelog/50590-add-44877-context-linking-a-product-with-collection-inspector-control new file mode 100644 index 00000000000..2db092dad41 --- /dev/null +++ b/plugins/woocommerce/changelog/50590-add-44877-context-linking-a-product-with-collection-inspector-control @@ -0,0 +1,4 @@ +Significance: major +Type: add + +Product Collection - Implement Inspector control to change selected product \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50599-50049-fix-single-product-translations b/plugins/woocommerce/changelog/50599-50049-fix-single-product-translations deleted file mode 100644 index b18aa3a1715..00000000000 --- a/plugins/woocommerce/changelog/50599-50049-fix-single-product-translations +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Single product block - Fix translation for title and description in edit mode. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50606-fix-49651-rest-remove-line-item-stock b/plugins/woocommerce/changelog/50606-fix-49651-rest-remove-line-item-stock deleted file mode 100644 index c661cc15fb4..00000000000 --- a/plugins/woocommerce/changelog/50606-fix-49651-rest-remove-line-item-stock +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Ensure that the orders REST endpoint behaves the same as the UI when updating an order to remove a line item. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50611-add-lost-password-e2e-test b/plugins/woocommerce/changelog/50611-add-lost-password-e2e-test deleted file mode 100644 index e3885bc1872..00000000000 --- a/plugins/woocommerce/changelog/50611-add-lost-password-e2e-test +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: dev - -Add lost password e2e tests \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50612-fix-preview-button-cursor b/plugins/woocommerce/changelog/50612-fix-preview-button-cursor new file mode 100644 index 00000000000..fd0ed82e8c4 --- /dev/null +++ b/plugins/woocommerce/changelog/50612-fix-preview-button-cursor @@ -0,0 +1,4 @@ +Significance: patch +Type: enhancement + +Product Collection - Change cursor style of preview button to default \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50615-fix-product-rating-translations b/plugins/woocommerce/changelog/50615-fix-product-rating-translations deleted file mode 100644 index c2597b4ed26..00000000000 --- a/plugins/woocommerce/changelog/50615-fix-product-rating-translations +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Fix translation - Avoid registering blocks in the wrong context. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50625-fix-product-meta-translations b/plugins/woocommerce/changelog/50625-fix-product-meta-translations deleted file mode 100644 index 4800360beac..00000000000 --- a/plugins/woocommerce/changelog/50625-fix-product-meta-translations +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Fix "Product Meta" translations - Register the block server side. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50628-fix-add-to-cart-form-translation b/plugins/woocommerce/changelog/50628-fix-add-to-cart-form-translation deleted file mode 100644 index d51ceac9e78..00000000000 --- a/plugins/woocommerce/changelog/50628-fix-add-to-cart-form-translation +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Add to Cart with Options - Fix translation when used inside the Single Product block. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50629-fix-50614 b/plugins/woocommerce/changelog/50629-fix-50614 deleted file mode 100644 index 66dccf4dfe3..00000000000 --- a/plugins/woocommerce/changelog/50629-fix-50614 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Clear product unique ID (`global_unique_id`) when duplicating products. diff --git a/plugins/woocommerce/changelog/50644-update-express-checkout-design b/plugins/woocommerce/changelog/50644-update-express-checkout-design new file mode 100644 index 00000000000..e9e4e4c4f4a --- /dev/null +++ b/plugins/woocommerce/changelog/50644-update-express-checkout-design @@ -0,0 +1,4 @@ +Significance: patch +Type: tweak + +Small visual tweaks to the express checkout area \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50673-fix-store-title-error b/plugins/woocommerce/changelog/50673-fix-store-title-error deleted file mode 100644 index e9808147440..00000000000 --- a/plugins/woocommerce/changelog/50673-fix-store-title-error +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Fix `store-title` endpoint - Pass default value to `get_option`. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50680-fix-product-meta-editor-error b/plugins/woocommerce/changelog/50680-fix-product-meta-editor-error deleted file mode 100644 index 6dfd3c749b9..00000000000 --- a/plugins/woocommerce/changelog/50680-fix-product-meta-editor-error +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Fix `Product meta` console error. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50709-fix-mobile-sticky-header b/plugins/woocommerce/changelog/50709-fix-mobile-sticky-header deleted file mode 100644 index 22110e9733d..00000000000 --- a/plugins/woocommerce/changelog/50709-fix-mobile-sticky-header +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Fix an admin bar CSS positioning bug in WordPress.com on mobile diff --git a/plugins/woocommerce/changelog/50716-update-import-btn-loading-state b/plugins/woocommerce/changelog/50716-update-import-btn-loading-state deleted file mode 100644 index 0ccb7782395..00000000000 --- a/plugins/woocommerce/changelog/50716-update-import-btn-loading-state +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak -Comment: This feature is behind a feature flag. - diff --git a/plugins/woocommerce/changelog/50720-fix-missing-logout-confirmation b/plugins/woocommerce/changelog/50720-fix-missing-logout-confirmation deleted file mode 100644 index 6dc236fbc97..00000000000 --- a/plugins/woocommerce/changelog/50720-fix-missing-logout-confirmation +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak -Comment: Fixes missing global in unreleased code. - diff --git a/plugins/woocommerce/changelog/50751-fix-isRequesting-report-summary b/plugins/woocommerce/changelog/50751-fix-isRequesting-report-summary new file mode 100644 index 00000000000..052457d7f62 --- /dev/null +++ b/plugins/woocommerce/changelog/50751-fix-isRequesting-report-summary @@ -0,0 +1,4 @@ +Significance: patch +Type: tweak + +Remove the usage of `ReportSummary`s `isRequesting` property as it has no effect. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50770-image-add-additional-size-units b/plugins/woocommerce/changelog/50770-image-add-additional-size-units new file mode 100644 index 00000000000..f67f858fac8 --- /dev/null +++ b/plugins/woocommerce/changelog/50770-image-add-additional-size-units @@ -0,0 +1,4 @@ +Significance: patch +Type: enhancement + +Add additional sizing units for product image block \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50784-add-coming-soon-mode-e2e b/plugins/woocommerce/changelog/50784-add-coming-soon-mode-e2e new file mode 100644 index 00000000000..bfa1b361fd6 --- /dev/null +++ b/plugins/woocommerce/changelog/50784-add-coming-soon-mode-e2e @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Add e2e tests to confirm that the store is in coming soon mode after completing the core profiler \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50828-dev-constrain-pnpm-version b/plugins/woocommerce/changelog/50828-dev-constrain-pnpm-version new file mode 100644 index 00000000000..fdead95ceb5 --- /dev/null +++ b/plugins/woocommerce/changelog/50828-dev-constrain-pnpm-version @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix pnpm version to 9.1.3 to avoid dependency installation issues. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50836-29617-fix-bulk-edit-price b/plugins/woocommerce/changelog/50836-29617-fix-bulk-edit-price new file mode 100644 index 00000000000..08db0403878 --- /dev/null +++ b/plugins/woocommerce/changelog/50836-29617-fix-bulk-edit-price @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Product bulk edit: allow to restore original price and avoid fatal errors. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50842-43149-fix-bulk-product-price-in-de-crease b/plugins/woocommerce/changelog/50842-43149-fix-bulk-product-price-in-de-crease new file mode 100644 index 00000000000..affaea6bdf9 --- /dev/null +++ b/plugins/woocommerce/changelog/50842-43149-fix-bulk-product-price-in-de-crease @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Product bulk edit: fix increasing & decreasing sale price when there was no previous sale. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50876-fix-run-coupon-filter-on-empty b/plugins/woocommerce/changelog/50876-fix-run-coupon-filter-on-empty new file mode 100644 index 00000000000..07f2a7b08db --- /dev/null +++ b/plugins/woocommerce/changelog/50876-fix-run-coupon-filter-on-empty @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Make Checkout block `coupons` filter consistent by always running. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50927-e2e-external-sites-update-activate-and-setup b/plugins/woocommerce/changelog/50927-e2e-external-sites-update-activate-and-setup new file mode 100644 index 00000000000..eff90fd3b8d --- /dev/null +++ b/plugins/woocommerce/changelog/50927-e2e-external-sites-update-activate-and-setup @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Update `activate-and-setup` e2e tests, so they are passing against Pressable env. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50935-fix-price-filter-slider b/plugins/woocommerce/changelog/50935-fix-price-filter-slider new file mode 100644 index 00000000000..b8d0d2b926b --- /dev/null +++ b/plugins/woocommerce/changelog/50935-fix-price-filter-slider @@ -0,0 +1,4 @@ +Significance: patch +Type: tweak +Comment: Price slider: fix range bar styling and dynamic input + diff --git a/plugins/woocommerce/changelog/50942-e2e-external-sites-update-add-variable-product b/plugins/woocommerce/changelog/50942-e2e-external-sites-update-add-variable-product new file mode 100644 index 00000000000..a76d030d603 --- /dev/null +++ b/plugins/woocommerce/changelog/50942-e2e-external-sites-update-add-variable-product @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Update `add-variable-product` e2e tests, so they are passing against Pressable env. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50953-update-woo-pay-task b/plugins/woocommerce/changelog/50953-update-woo-pay-task new file mode 100644 index 00000000000..48e96069d4e --- /dev/null +++ b/plugins/woocommerce/changelog/50953-update-woo-pay-task @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Refactor WooCommercePayments task to use woo payment gateway from `WC()->payment_gateways->payment_gateways()` diff --git a/plugins/woocommerce/changelog/50960-e2e-external-sites-update-products-block-editor b/plugins/woocommerce/changelog/50960-e2e-external-sites-update-products-block-editor new file mode 100644 index 00000000000..81ca7834934 --- /dev/null +++ b/plugins/woocommerce/changelog/50960-e2e-external-sites-update-products-block-editor @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Update `products/block-editor` e2e tests, so they are passing against Pressable env. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50969-dev-deprecate-query b/plugins/woocommerce/changelog/50969-dev-deprecate-query new file mode 100644 index 00000000000..f6457b61fb6 --- /dev/null +++ b/plugins/woocommerce/changelog/50969-dev-deprecate-query @@ -0,0 +1,4 @@ +Significance: patch +Type: tweak + +Log deprecation warnings for `Query` class usage. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50975-dev-remove-combobox b/plugins/woocommerce/changelog/50975-dev-remove-combobox new file mode 100644 index 00000000000..2506b738075 --- /dev/null +++ b/plugins/woocommerce/changelog/50975-dev-remove-combobox @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Remove @wooocommerce `Combobox` component that is no longer used. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50976-fix-coming-soon-badge b/plugins/woocommerce/changelog/50976-fix-coming-soon-badge new file mode 100644 index 00000000000..dd7a9b78a5a --- /dev/null +++ b/plugins/woocommerce/changelog/50976-fix-coming-soon-badge @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix site visibility badge style \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50977-update-settings-feature-setup b/plugins/woocommerce/changelog/50977-update-settings-feature-setup new file mode 100644 index 00000000000..7d4c1547e4c --- /dev/null +++ b/plugins/woocommerce/changelog/50977-update-settings-feature-setup @@ -0,0 +1,4 @@ +Significance: patch +Type: tweak +Comment: Change to underlying Settings feature usage + diff --git a/plugins/woocommerce/changelog/50979-42947-disable-product-rating b/plugins/woocommerce/changelog/50979-42947-disable-product-rating new file mode 100644 index 00000000000..6eb724796d1 --- /dev/null +++ b/plugins/woocommerce/changelog/50979-42947-disable-product-rating @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Product Rating: hide reviews in the front end when disabled at store or product level. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50994-e2e-external-sites-update-admin-tests b/plugins/woocommerce/changelog/50994-e2e-external-sites-update-admin-tests new file mode 100644 index 00000000000..4c731ca0240 --- /dev/null +++ b/plugins/woocommerce/changelog/50994-e2e-external-sites-update-admin-tests @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Update admin-* folders, so e2e tests are passing against Pressable env. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/50996-fix-flaky-cart-coupon-test-50928 b/plugins/woocommerce/changelog/50996-fix-flaky-cart-coupon-test-50928 new file mode 100644 index 00000000000..a2098ace68a --- /dev/null +++ b/plugins/woocommerce/changelog/50996-fix-flaky-cart-coupon-test-50928 @@ -0,0 +1,4 @@ +Significance: patch +Type: tweak +Comment: Fixes a flaky coupon test. + diff --git a/plugins/woocommerce/changelog/51004-e2e-external-sites-update-customize-store-tests b/plugins/woocommerce/changelog/51004-e2e-external-sites-update-customize-store-tests new file mode 100644 index 00000000000..17ac089f383 --- /dev/null +++ b/plugins/woocommerce/changelog/51004-e2e-external-sites-update-customize-store-tests @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Update /customize-store tests, so they are passing against Pressable env. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/51012-47886-remove-padding-filters b/plugins/woocommerce/changelog/51012-47886-remove-padding-filters new file mode 100644 index 00000000000..b13cac4a88f --- /dev/null +++ b/plugins/woocommerce/changelog/51012-47886-remove-padding-filters @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Remove extra bottom padding in filters in the editor. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/51014-e2e-external-sites-update-merchant-tests-pt1 b/plugins/woocommerce/changelog/51014-e2e-external-sites-update-merchant-tests-pt1 new file mode 100644 index 00000000000..180673a625b --- /dev/null +++ b/plugins/woocommerce/changelog/51014-e2e-external-sites-update-merchant-tests-pt1 @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Update /merchant tests (first five files), so they are passing against Pressable env. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/51033-47922-product-sku-improvements b/plugins/woocommerce/changelog/51033-47922-product-sku-improvements new file mode 100644 index 00000000000..1849a43e716 --- /dev/null +++ b/plugins/woocommerce/changelog/51033-47922-product-sku-improvements @@ -0,0 +1,4 @@ +Significance: minor +Type: enhancement + +Product SKU block: add editable prefixes and suffixes. \ No newline at end of file diff --git a/plugins/woocommerce/changelog/51048-fix-flaky-cart-coupon-test-50928-2 b/plugins/woocommerce/changelog/51048-fix-flaky-cart-coupon-test-50928-2 new file mode 100644 index 00000000000..5f630ce9af0 --- /dev/null +++ b/plugins/woocommerce/changelog/51048-fix-flaky-cart-coupon-test-50928-2 @@ -0,0 +1,4 @@ +Significance: patch +Type: tweak +Comment: Fixes flaky coupon tests. + diff --git a/plugins/woocommerce/changelog/51067-revert-50531 b/plugins/woocommerce/changelog/51067-revert-50531 new file mode 100644 index 00000000000..e65258f8afe --- /dev/null +++ b/plugins/woocommerce/changelog/51067-revert-50531 @@ -0,0 +1,4 @@ +Significance: patch +Type: tweak +Comment: This reverts an existing PR. + diff --git a/plugins/woocommerce/changelog/51102-fix-settings-js-save-activation b/plugins/woocommerce/changelog/51102-fix-settings-js-save-activation new file mode 100644 index 00000000000..c19d8648fb1 --- /dev/null +++ b/plugins/woocommerce/changelog/51102-fix-settings-js-save-activation @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix Settings Save button when WP List Tables are present \ No newline at end of file diff --git a/plugins/woocommerce/changelog/GIT STATUS b/plugins/woocommerce/changelog/GIT STATUS deleted file mode 100644 index adaa301ed01..00000000000 --- a/plugins/woocommerce/changelog/GIT STATUS +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: dev - -move part of checkout docs to main docs folder diff --git a/plugins/woocommerce/changelog/add-42579 b/plugins/woocommerce/changelog/add-42579 deleted file mode 100644 index 49fbd7ec747..00000000000 --- a/plugins/woocommerce/changelog/add-42579 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: tweak - -Extract the checkbox list option logic into its own component diff --git a/plugins/woocommerce/changelog/add-47895-show-tab-title-attribute-product-details b/plugins/woocommerce/changelog/add-47895-show-tab-title-attribute-product-details new file mode 100644 index 00000000000..9b974a1f1cf --- /dev/null +++ b/plugins/woocommerce/changelog/add-47895-show-tab-title-attribute-product-details @@ -0,0 +1,4 @@ +Significance: minor +Type: enhancement + +Add 'Show tab title' attribute to the Product Details block diff --git a/plugins/woocommerce/changelog/add-49908_track_unit_tests b/plugins/woocommerce/changelog/add-49908_track_unit_tests deleted file mode 100644 index 1ce4a226e7d..00000000000 --- a/plugins/woocommerce/changelog/add-49908_track_unit_tests +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: dev - -Add unit tests for the product_add_publish track. diff --git a/plugins/woocommerce/changelog/add-49927-main-payments-screen b/plugins/woocommerce/changelog/add-49927-main-payments-screen deleted file mode 100644 index 7670ea53eda..00000000000 --- a/plugins/woocommerce/changelog/add-49927-main-payments-screen +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: add - -Add reactified main payments screen diff --git a/plugins/woocommerce/changelog/add-50231_e2e_description_using_block_editor b/plugins/woocommerce/changelog/add-50231_e2e_description_using_block_editor deleted file mode 100644 index b28bf5c3d64..00000000000 --- a/plugins/woocommerce/changelog/add-50231_e2e_description_using_block_editor +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: add - -[E2E tests]: Add product description using the block editor #50232 diff --git a/plugins/woocommerce/changelog/add-add-pattern-button-to-no-blocks-placeholder b/plugins/woocommerce/changelog/add-add-pattern-button-to-no-blocks-placeholder deleted file mode 100644 index 5ceb8715168..00000000000 --- a/plugins/woocommerce/changelog/add-add-pattern-button-to-no-blocks-placeholder +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: add - -Add Pattern button to no blocks view on the CYS assembler diff --git a/plugins/woocommerce/changelog/add-bump-stats-error b/plugins/woocommerce/changelog/add-bump-stats-error deleted file mode 100644 index 2a41a632378..00000000000 --- a/plugins/woocommerce/changelog/add-bump-stats-error +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: add - -Track frequency of unhandled JS errors with MC Stats diff --git a/plugins/woocommerce/changelog/add-coming-soon-options-for-all-sites b/plugins/woocommerce/changelog/add-coming-soon-options-for-all-sites deleted file mode 100644 index dd70504fe75..00000000000 --- a/plugins/woocommerce/changelog/add-coming-soon-options-for-all-sites +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: update - -Add woocommerce_coming_soon option for all sites diff --git a/plugins/woocommerce/changelog/add-docblocks-to-products-shortcode-hooks b/plugins/woocommerce/changelog/add-docblocks-to-products-shortcode-hooks new file mode 100644 index 00000000000..0c79db17c86 --- /dev/null +++ b/plugins/woocommerce/changelog/add-docblocks-to-products-shortcode-hooks @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Added doc blocks to conditionally fired hooks to better explain nuanced behavior diff --git a/plugins/woocommerce/changelog/add-filter-woocommerce-should-clear-cart-after-payment b/plugins/woocommerce/changelog/add-filter-woocommerce-should-clear-cart-after-payment deleted file mode 100644 index 765d96fd31d..00000000000 --- a/plugins/woocommerce/changelog/add-filter-woocommerce-should-clear-cart-after-payment +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: tweak - -Add the `woocommerce_should_clear_cart_after_payment` filter to influence whether the cart should be cleared after payment. - diff --git a/plugins/woocommerce/changelog/add-initial_product_data_views_screen b/plugins/woocommerce/changelog/add-initial_product_data_views_screen new file mode 100644 index 00000000000..5846ddf06cb --- /dev/null +++ b/plugins/woocommerce/changelog/add-initial_product_data_views_screen @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add new feature for a product data views page. diff --git a/plugins/woocommerce/changelog/add-more-params-to-remote-logger-whitelist b/plugins/woocommerce/changelog/add-more-params-to-remote-logger-whitelist new file mode 100644 index 00000000000..381b5ccebd0 --- /dev/null +++ b/plugins/woocommerce/changelog/add-more-params-to-remote-logger-whitelist @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Added more paths to remote logger query param whitelist diff --git a/plugins/woocommerce/changelog/add-php-mc-stats b/plugins/woocommerce/changelog/add-php-mc-stats deleted file mode 100644 index cf7db2f0bf3..00000000000 --- a/plugins/woocommerce/changelog/add-php-mc-stats +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: add - -Use MC Stats for PHP fatal error counting diff --git a/plugins/woocommerce/changelog/add-php-remote-error-logging b/plugins/woocommerce/changelog/add-php-remote-error-logging deleted file mode 100644 index 3bf11a301ee..00000000000 --- a/plugins/woocommerce/changelog/add-php-remote-error-logging +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: add - -Implement server-side remote error logging diff --git a/plugins/woocommerce/changelog/add-product_data_views_list b/plugins/woocommerce/changelog/add-product_data_views_list new file mode 100644 index 00000000000..7669d680be8 --- /dev/null +++ b/plugins/woocommerce/changelog/add-product_data_views_list @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Update webpack config to bundle in @wordpress/dataviews package. diff --git a/plugins/woocommerce/changelog/add-react-main-payments-settings-screen b/plugins/woocommerce/changelog/add-react-main-payments-settings-screen new file mode 100644 index 00000000000..d92bea60fbb --- /dev/null +++ b/plugins/woocommerce/changelog/add-react-main-payments-settings-screen @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add react-powered main payments settings screen diff --git a/plugins/woocommerce/changelog/add-reactify-classic-payments-settings-feature-flag b/plugins/woocommerce/changelog/add-reactify-classic-payments-settings-feature-flag deleted file mode 100644 index de54eabf17a..00000000000 --- a/plugins/woocommerce/changelog/add-reactify-classic-payments-settings-feature-flag +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: add - -Add reactify-classic-payments-settings feature flag diff --git a/plugins/woocommerce/changelog/add-reactify-offline-wcpay-settings-pages b/plugins/woocommerce/changelog/add-reactify-offline-wcpay-settings-pages deleted file mode 100644 index 01d4df29207..00000000000 --- a/plugins/woocommerce/changelog/add-reactify-offline-wcpay-settings-pages +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: dev - -Render a React placeholder for offline and WooCommerce Payments settings sections diff --git a/plugins/woocommerce/changelog/add-store-api-unit-tests b/plugins/woocommerce/changelog/add-store-api-unit-tests deleted file mode 100644 index 03b944ec7eb..00000000000 --- a/plugins/woocommerce/changelog/add-store-api-unit-tests +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: dev -Comment: This PR only adds CI tests. - - diff --git a/plugins/woocommerce/changelog/add-stripe-tax-onboarding-tax-task b/plugins/woocommerce/changelog/add-stripe-tax-onboarding-tax-task new file mode 100644 index 00000000000..7de97421d64 --- /dev/null +++ b/plugins/woocommerce/changelog/add-stripe-tax-onboarding-tax-task @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Added Stripe tax in onboarding tax task diff --git a/plugins/woocommerce/changelog/add-wcadmin-remote-logging b/plugins/woocommerce/changelog/add-wcadmin-remote-logging deleted file mode 100644 index 98741a8c74c..00000000000 --- a/plugins/woocommerce/changelog/add-wcadmin-remote-logging +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: add - -Integrate JS remote logging package in WooCommerce Admin diff --git a/plugins/woocommerce/changelog/bugfix-quantity-html-issue b/plugins/woocommerce/changelog/bugfix-quantity-html-issue new file mode 100644 index 00000000000..a243dfb8f2a --- /dev/null +++ b/plugins/woocommerce/changelog/bugfix-quantity-html-issue @@ -0,0 +1,5 @@ +Significance: patch +Type: fix +Comment: Fixes a warning that only shows up when validating page content + + diff --git a/plugins/woocommerce/changelog/cache-order-dates b/plugins/woocommerce/changelog/cache-order-dates deleted file mode 100644 index 4841e70ad16..00000000000 --- a/plugins/woocommerce/changelog/cache-order-dates +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: performance - -Cache order dates in options for performance. diff --git a/plugins/woocommerce/changelog/ci-dont-use-sharding-for-last-failed b/plugins/woocommerce/changelog/ci-dont-use-sharding-for-last-failed deleted file mode 100644 index 53db001a0c0..00000000000 --- a/plugins/woocommerce/changelog/ci-dont-use-sharding-for-last-failed +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -CI: Use a single shard when re-running failed tests in CI diff --git a/plugins/woocommerce/changelog/ci-include-woocommercephp-in-changes b/plugins/woocommerce/changelog/ci-include-woocommercephp-in-changes deleted file mode 100644 index d0330fa3202..00000000000 --- a/plugins/woocommerce/changelog/ci-include-woocommercephp-in-changes +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -CI config: update changes list to include more paths diff --git a/plugins/woocommerce/changelog/dev-42360_convert_withreviews_to_ts b/plugins/woocommerce/changelog/dev-42360_convert_withreviews_to_ts new file mode 100644 index 00000000000..785a3fc0721 --- /dev/null +++ b/plugins/woocommerce/changelog/dev-42360_convert_withreviews_to_ts @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Convert with-reviews to TS #50890 diff --git a/plugins/woocommerce/changelog/dev-46917-migrate-to-block-json-image b/plugins/woocommerce/changelog/dev-46917-migrate-to-block-json-image new file mode 100644 index 00000000000..0afb024afd3 --- /dev/null +++ b/plugins/woocommerce/changelog/dev-46917-migrate-to-block-json-image @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Product Image: migrate to block.json diff --git a/plugins/woocommerce/changelog/dev-46917-migrate-to-block-json-price b/plugins/woocommerce/changelog/dev-46917-migrate-to-block-json-price new file mode 100644 index 00000000000..037f08ea6ae --- /dev/null +++ b/plugins/woocommerce/changelog/dev-46917-migrate-to-block-json-price @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Product Price: migrate to block.json diff --git a/plugins/woocommerce/changelog/dev-46917-migrate-to-block-json-sale b/plugins/woocommerce/changelog/dev-46917-migrate-to-block-json-sale new file mode 100644 index 00000000000..1f578b35e80 --- /dev/null +++ b/plugins/woocommerce/changelog/dev-46917-migrate-to-block-json-sale @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +On Sale Badge: Migrate to block.json diff --git a/plugins/woocommerce/changelog/dev-49582_use_custom_link_for_sku_error b/plugins/woocommerce/changelog/dev-49582_use_custom_link_for_sku_error deleted file mode 100644 index 2dc0e3065f9..00000000000 --- a/plugins/woocommerce/changelog/dev-49582_use_custom_link_for_sku_error +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: dev - -Fix E2E tests SKU field id #49729 diff --git a/plugins/woocommerce/changelog/dev-ci-phpunit-sharding b/plugins/woocommerce/changelog/dev-ci-phpunit-sharding deleted file mode 100644 index 9be5fd6a8cd..00000000000 --- a/plugins/woocommerce/changelog/dev-ci-phpunit-sharding +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -CI: introduce PHPUnit tests sharding. diff --git a/plugins/woocommerce/changelog/dev-clean-up-images b/plugins/woocommerce/changelog/dev-clean-up-images deleted file mode 100644 index 182dcf3723f..00000000000 --- a/plugins/woocommerce/changelog/dev-clean-up-images +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -Clean up unused images diff --git a/plugins/woocommerce/changelog/dev-codesniffer-validate-psr4-autoloading b/plugins/woocommerce/changelog/dev-codesniffer-validate-psr4-autoloading deleted file mode 100644 index c7623c6ae00..00000000000 --- a/plugins/woocommerce/changelog/dev-codesniffer-validate-psr4-autoloading +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -Monorepo: enable new linting rules for PHP (PSR-4 naming, Strict types declaration). diff --git a/plugins/woocommerce/changelog/dev-fix-double-posting-review-comment b/plugins/woocommerce/changelog/dev-fix-double-posting-review-comment deleted file mode 100644 index 5f8b4602154..00000000000 --- a/plugins/woocommerce/changelog/dev-fix-double-posting-review-comment +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -Updated the workflow prompting for testing instructions to only run once (preventing double comments) diff --git a/plugins/woocommerce/changelog/dev-speedup-check-asset-sizes-take-3 b/plugins/woocommerce/changelog/dev-speedup-check-asset-sizes-take-3 deleted file mode 100644 index e96372f3c87..00000000000 --- a/plugins/woocommerce/changelog/dev-speedup-check-asset-sizes-take-3 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -CI: speedup assets size verification job execution time. diff --git a/plugins/woocommerce/changelog/dev-speedup-test-env-setup b/plugins/woocommerce/changelog/dev-speedup-test-env-setup deleted file mode 100644 index 8a63c75e1d1..00000000000 --- a/plugins/woocommerce/changelog/dev-speedup-test-env-setup +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -CI: minor speed boost of wp-env startup. diff --git a/plugins/woocommerce/changelog/dev-webpack-loaders-scannig-paths-tweaks b/plugins/woocommerce/changelog/dev-webpack-loaders-scannig-paths-tweaks deleted file mode 100644 index 30f765e3fca..00000000000 --- a/plugins/woocommerce/changelog/dev-webpack-loaders-scannig-paths-tweaks +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -Monorepo: tweak Webpack loaders paths filtering for better build perfromance. diff --git a/plugins/woocommerce/changelog/e2e-add-buildkite-reporter-for-blocks-e2e b/plugins/woocommerce/changelog/e2e-add-buildkite-reporter-for-blocks-e2e deleted file mode 100644 index 3a311aefc04..00000000000 --- a/plugins/woocommerce/changelog/e2e-add-buildkite-reporter-for-blocks-e2e +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -E2E tests: add buildkite-test-collector for blocks e2e tests diff --git a/plugins/woocommerce/changelog/e2e-add-environment-reporter b/plugins/woocommerce/changelog/e2e-add-environment-reporter deleted file mode 100644 index 4c4036f10ea..00000000000 --- a/plugins/woocommerce/changelog/e2e-add-environment-reporter +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -E2E tests: add environment reporter diff --git a/plugins/woocommerce/changelog/e2e-add-forbidOnly-config b/plugins/woocommerce/changelog/e2e-add-forbidOnly-config deleted file mode 100644 index 4f73f5cc6c2..00000000000 --- a/plugins/woocommerce/changelog/e2e-add-forbidOnly-config +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - - diff --git a/plugins/woocommerce/changelog/e2e-add-hpos-disabled-env b/plugins/woocommerce/changelog/e2e-add-hpos-disabled-env deleted file mode 100644 index 7f88973e400..00000000000 --- a/plugins/woocommerce/changelog/e2e-add-hpos-disabled-env +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -E2E tests: add hpos-disabled env and tagged tests with hpos tag diff --git a/plugins/woocommerce/changelog/e2e-core-e2e-add-new-flaky-reporter b/plugins/woocommerce/changelog/e2e-core-e2e-add-new-flaky-reporter deleted file mode 100644 index 1bc65f6780a..00000000000 --- a/plugins/woocommerce/changelog/e2e-core-e2e-add-new-flaky-reporter +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -E2E tests: add a flaky test reporter for Core e2e tests diff --git a/plugins/woocommerce/changelog/e2e-env-setup-add-option-to-skip-setup-script b/plugins/woocommerce/changelog/e2e-env-setup-add-option-to-skip-setup-script deleted file mode 100644 index c5ad559968d..00000000000 --- a/plugins/woocommerce/changelog/e2e-env-setup-add-option-to-skip-setup-script +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -E2E tests: add an option to skip the env setup script running before test execution diff --git a/plugins/woocommerce/changelog/e2e-fix-cart-checkout-tax-test b/plugins/woocommerce/changelog/e2e-fix-cart-checkout-tax-test deleted file mode 100644 index ec525a7f777..00000000000 --- a/plugins/woocommerce/changelog/e2e-fix-cart-checkout-tax-test +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -E2E tests: Removed unnecessary pause in the test \ No newline at end of file diff --git a/plugins/woocommerce/changelog/e2e-fix-create-checkout-block-gutenberg b/plugins/woocommerce/changelog/e2e-fix-create-checkout-block-gutenberg deleted file mode 100644 index 4f73f5cc6c2..00000000000 --- a/plugins/woocommerce/changelog/e2e-fix-create-checkout-block-gutenberg +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - - diff --git a/plugins/woocommerce/changelog/ci-disable-metrics-job b/plugins/woocommerce/changelog/e2e-fix-create-linked-product-tests similarity index 100% rename from plugins/woocommerce/changelog/ci-disable-metrics-job rename to plugins/woocommerce/changelog/e2e-fix-create-linked-product-tests diff --git a/plugins/woocommerce/changelog/e2e-fix-logo-picker-tests b/plugins/woocommerce/changelog/e2e-fix-logo-picker-tests deleted file mode 100644 index 5ca5e92030d..00000000000 --- a/plugins/woocommerce/changelog/e2e-fix-logo-picker-tests +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -E2E tests: fixed broken logo picker tests diff --git a/plugins/woocommerce/changelog/e2e-fix-product-attributes-block-editor-tests b/plugins/woocommerce/changelog/e2e-fix-product-attributes-block-editor-tests deleted file mode 100644 index 96726669b92..00000000000 --- a/plugins/woocommerce/changelog/e2e-fix-product-attributes-block-editor-tests +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -E2E tests: fix flakiness in product attributes test diff --git a/plugins/woocommerce/changelog/e2e-improve-page-loads-customers-test b/plugins/woocommerce/changelog/e2e-improve-page-loads-customers-test deleted file mode 100644 index 8e8ef68ef12..00000000000 --- a/plugins/woocommerce/changelog/e2e-improve-page-loads-customers-test +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -E2E tests: fix flakiness in page-loads customer page test diff --git a/plugins/woocommerce/changelog/e2e-remove-github-reporter b/plugins/woocommerce/changelog/e2e-remove-github-reporter deleted file mode 100644 index e863d412553..00000000000 --- a/plugins/woocommerce/changelog/e2e-remove-github-reporter +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -E2E tests: removed Github reporter diff --git a/plugins/woocommerce/changelog/e2e-remove-hardcoded-default-creds b/plugins/woocommerce/changelog/e2e-remove-hardcoded-default-creds deleted file mode 100644 index 4f73f5cc6c2..00000000000 --- a/plugins/woocommerce/changelog/e2e-remove-hardcoded-default-creds +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - - diff --git a/plugins/woocommerce/changelog/ci-only-run-last-failed-e2e-tests b/plugins/woocommerce/changelog/e2e-tag-e2e-tests similarity index 100% rename from plugins/woocommerce/changelog/ci-only-run-last-failed-e2e-tests rename to plugins/woocommerce/changelog/e2e-tag-e2e-tests diff --git a/plugins/woocommerce/changelog/e2e-tweak-reports-paths b/plugins/woocommerce/changelog/e2e-tweak-reports-paths deleted file mode 100644 index 4f73f5cc6c2..00000000000 --- a/plugins/woocommerce/changelog/e2e-tweak-reports-paths +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - - diff --git a/plugins/woocommerce/changelog/e2e-update-docs-clarify-env-use b/plugins/woocommerce/changelog/e2e-update-docs-clarify-env-use deleted file mode 100644 index ce573eb565b..00000000000 --- a/plugins/woocommerce/changelog/e2e-update-docs-clarify-env-use +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -Updated e2e tests docs to clarify the use of environments diff --git a/plugins/woocommerce/changelog/e2e-update-readme-pnpm-commands b/plugins/woocommerce/changelog/e2e-update-readme-pnpm-commands deleted file mode 100644 index 4f73f5cc6c2..00000000000 --- a/plugins/woocommerce/changelog/e2e-update-readme-pnpm-commands +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - - diff --git a/plugins/woocommerce/changelog/feature-37597-37598-inform-screen-reader-users-when-mini-cart-updates b/plugins/woocommerce/changelog/feature-37597-37598-inform-screen-reader-users-when-mini-cart-updates deleted file mode 100644 index fa6d46c15ab..00000000000 --- a/plugins/woocommerce/changelog/feature-37597-37598-inform-screen-reader-users-when-mini-cart-updates +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: add - -Inform screen reader users when mini cart updates diff --git a/plugins/woocommerce/changelog/feature-blueprint-visual b/plugins/woocommerce/changelog/feature-blueprint-visual deleted file mode 100644 index d1e8cb9ea63..00000000000 --- a/plugins/woocommerce/changelog/feature-blueprint-visual +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -Update Blueprint settings layout. diff --git a/plugins/woocommerce/changelog/fi-fatal-jetpack-stats b/plugins/woocommerce/changelog/fi-fatal-jetpack-stats deleted file mode 100644 index 5edc9674024..00000000000 --- a/plugins/woocommerce/changelog/fi-fatal-jetpack-stats +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Prevent fatal error if NULL is provided in array_search under Jetpack Stats diff --git a/plugins/woocommerce/changelog/fix-31664-stock-emails b/plugins/woocommerce/changelog/fix-31664-stock-emails deleted file mode 100644 index 3f4467d0b78..00000000000 --- a/plugins/woocommerce/changelog/fix-31664-stock-emails +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Ensure low and no stock email notification routine is triggered whenever product stock changes diff --git a/plugins/woocommerce/changelog/fix-37502-typos-in-inline-doc-in-woocommerce-plugin b/plugins/woocommerce/changelog/fix-37502-typos-in-inline-doc-in-woocommerce-plugin new file mode 100644 index 00000000000..185ab29d77b --- /dev/null +++ b/plugins/woocommerce/changelog/fix-37502-typos-in-inline-doc-in-woocommerce-plugin @@ -0,0 +1,4 @@ +Significance: minor +Type: tweak + +Fix typo in inline doc in woocommerce client, includes, and lib folders. diff --git a/plugins/woocommerce/changelog/fix-37602 b/plugins/woocommerce/changelog/fix-37602 new file mode 100644 index 00000000000..21976521935 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-37602 @@ -0,0 +1,4 @@ +Significance: patch +Type: enhancement + +Move focus into coupon input if there is an error diff --git a/plugins/woocommerce/changelog/fix-37606 b/plugins/woocommerce/changelog/fix-37606 deleted file mode 100644 index 641983f271f..00000000000 --- a/plugins/woocommerce/changelog/fix-37606 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Keep focus on shipping option input once selected diff --git a/plugins/woocommerce/changelog/fix-40589-order-status-color-contrast b/plugins/woocommerce/changelog/fix-40589-order-status-color-contrast deleted file mode 100644 index 15548e8a8fd..00000000000 --- a/plugins/woocommerce/changelog/fix-40589-order-status-color-contrast +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Update product order status colors to ensure accessible color contrasts diff --git a/plugins/woocommerce/changelog/fix-42064-product-reviews-endpoint-tests b/plugins/woocommerce/changelog/fix-42064-product-reviews-endpoint-tests new file mode 100644 index 00000000000..8193d364b24 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-42064-product-reviews-endpoint-tests @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: Store API: Add test coverage for Product Reviews endpoint + + diff --git a/plugins/woocommerce/changelog/fix-42328 b/plugins/woocommerce/changelog/fix-42328 deleted file mode 100644 index 71439a7f279..00000000000 --- a/plugins/woocommerce/changelog/fix-42328 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: enhancement - -Remove opacity from the hover style of the mini cart button diff --git a/plugins/woocommerce/changelog/fix-43576 b/plugins/woocommerce/changelog/fix-43576 new file mode 100644 index 00000000000..2ca80e18de7 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-43576 @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Remove title attribute from images in product slider. diff --git a/plugins/woocommerce/changelog/fix-43603-footer-text-color b/plugins/woocommerce/changelog/fix-43603-footer-text-color deleted file mode 100644 index 940d0071f2f..00000000000 --- a/plugins/woocommerce/changelog/fix-43603-footer-text-color +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: add - -Add an additional field for the email settings that sets the footer text color diff --git a/plugins/woocommerce/changelog/fix-43605 b/plugins/woocommerce/changelog/fix-43605 new file mode 100644 index 00000000000..ad1d926bd91 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-43605 @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Trap focus inside the product gallery modal. diff --git a/plugins/woocommerce/changelog/fix-43607-single-product-tab-aria-selected b/plugins/woocommerce/changelog/fix-43607-single-product-tab-aria-selected deleted file mode 100644 index 80e931170f3..00000000000 --- a/plugins/woocommerce/changelog/fix-43607-single-product-tab-aria-selected +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Add ability for a screen reader to announce the current tab on a single product page. diff --git a/plugins/woocommerce/changelog/fix-43608-products-table-headers-scope b/plugins/woocommerce/changelog/fix-43608-products-table-headers-scope deleted file mode 100644 index 3df4e2ae5ac..00000000000 --- a/plugins/woocommerce/changelog/fix-43608-products-table-headers-scope +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Add scope attribute and aria-label to the product attributes table diff --git a/plugins/woocommerce/changelog/fix-43617 b/plugins/woocommerce/changelog/fix-43617 deleted file mode 100644 index 6da35745338..00000000000 --- a/plugins/woocommerce/changelog/fix-43617 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: enhancement - -Improve hover style on product tabs when using the Minimal style in the Product Details block diff --git a/plugins/woocommerce/changelog/fix-43626-account-nav-aria-current b/plugins/woocommerce/changelog/fix-43626-account-nav-aria-current deleted file mode 100644 index e8ac0302abd..00000000000 --- a/plugins/woocommerce/changelog/fix-43626-account-nav-aria-current +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Add aria-current to the current link in My Account side nav diff --git a/plugins/woocommerce/changelog/fix-43628-view-button-orders-page b/plugins/woocommerce/changelog/fix-43628-view-button-orders-page deleted file mode 100644 index 5027ce08bf6..00000000000 --- a/plugins/woocommerce/changelog/fix-43628-view-button-orders-page +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Add aria-label on View order button to aid in accessibility for screen readers diff --git a/plugins/woocommerce/changelog/fix-43630 b/plugins/woocommerce/changelog/fix-43630 deleted file mode 100644 index a62f19d2e48..00000000000 --- a/plugins/woocommerce/changelog/fix-43630 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Fix address heading level on My Account page. diff --git a/plugins/woocommerce/changelog/fix-44232-cart-rendering-after-updates b/plugins/woocommerce/changelog/fix-44232-cart-rendering-after-updates deleted file mode 100644 index cff85926143..00000000000 --- a/plugins/woocommerce/changelog/fix-44232-cart-rendering-after-updates +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Fix cart shortcode updates when not used on the main cart page. diff --git a/plugins/woocommerce/changelog/fix-44478 b/plugins/woocommerce/changelog/fix-44478 deleted file mode 100644 index 36ad72e6f98..00000000000 --- a/plugins/woocommerce/changelog/fix-44478 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: enhancement - -Hide zoomed product images for screen readers. diff --git a/plugins/woocommerce/changelog/fix-44906-coupon-removal-notices b/plugins/woocommerce/changelog/fix-44906-coupon-removal-notices deleted file mode 100644 index 95343bbb156..00000000000 --- a/plugins/woocommerce/changelog/fix-44906-coupon-removal-notices +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Ensure coupon errors are visible on block checkout when invalid coupons are removed. diff --git a/plugins/woocommerce/changelog/fix-45609 b/plugins/woocommerce/changelog/fix-45609 deleted file mode 100644 index 6d36830808a..00000000000 --- a/plugins/woocommerce/changelog/fix-45609 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Allow verified parameter to be set by REST API request diff --git a/plugins/woocommerce/changelog/fix-46527-compatibility-layer-init b/plugins/woocommerce/changelog/fix-46527-compatibility-layer-init new file mode 100644 index 00000000000..91dbcabb154 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-46527-compatibility-layer-init @@ -0,0 +1,4 @@ +Significance: patch +Type: performance + +Conditionally initialize block template compatibility classes diff --git a/plugins/woocommerce/changelog/fix-47071-navigation-landmarks b/plugins/woocommerce/changelog/fix-47071-navigation-landmarks deleted file mode 100644 index c5346c0e5e1..00000000000 --- a/plugins/woocommerce/changelog/fix-47071-navigation-landmarks +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Add a label to the product pagination for the woocommerce pagination diff --git a/plugins/woocommerce/changelog/fix-47794_variation_selector_display b/plugins/woocommerce/changelog/fix-47794_variation_selector_display new file mode 100644 index 00000000000..84ebab4b1c5 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-47794_variation_selector_display @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Fix variation selector display issues on the front end #51023 diff --git a/plugins/woocommerce/changelog/fix-48088-select-default-saved-token b/plugins/woocommerce/changelog/fix-48088-select-default-saved-token new file mode 100644 index 00000000000..a126d93c503 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-48088-select-default-saved-token @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Default saved payment method is now respected by block checkout. diff --git a/plugins/woocommerce/changelog/fix-48132-ssr-active-plugins b/plugins/woocommerce/changelog/fix-48132-ssr-active-plugins new file mode 100644 index 00000000000..414c485738c --- /dev/null +++ b/plugins/woocommerce/changelog/fix-48132-ssr-active-plugins @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Ensure that active plugins shown in the System Status API endpoint actually exist diff --git a/plugins/woocommerce/changelog/fix-48380_blocks_config_product_count b/plugins/woocommerce/changelog/fix-48380_blocks_config_product_count deleted file mode 100644 index 31cb0584871..00000000000 --- a/plugins/woocommerce/changelog/fix-48380_blocks_config_product_count +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Only count published products in productCount diff --git a/plugins/woocommerce/changelog/fix-49423-allow-rendering-the-quantity-in-cart-when-the-quantity-is-not-editable b/plugins/woocommerce/changelog/fix-49423-allow-rendering-the-quantity-in-cart-when-the-quantity-is-not-editable deleted file mode 100644 index b3680215875..00000000000 --- a/plugins/woocommerce/changelog/fix-49423-allow-rendering-the-quantity-in-cart-when-the-quantity-is-not-editable +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak - -allows the quantity selector on block cart page to render as readonly when editable is false diff --git a/plugins/woocommerce/changelog/fix-49578-new-attribute-filter-inspector-settings b/plugins/woocommerce/changelog/fix-49578-new-attribute-filter-inspector-settings deleted file mode 100644 index d7ddbb723c4..00000000000 --- a/plugins/woocommerce/changelog/fix-49578-new-attribute-filter-inspector-settings +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: update -Comment: [Experimental] Attribute Filter: Update Inspector Control Settings - - diff --git a/plugins/woocommerce/changelog/fix-49974-list-view-update b/plugins/woocommerce/changelog/fix-49974-list-view-update deleted file mode 100644 index 275ebf5d620..00000000000 --- a/plugins/woocommerce/changelog/fix-49974-list-view-update +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: update -Comment: Experimental: Product Filters: Attribute Filter: Update List style - - diff --git a/plugins/woocommerce/changelog/fix-49978-default-product-attribute-pattern b/plugins/woocommerce/changelog/fix-49978-default-product-attribute-pattern deleted file mode 100644 index 127d4cc6878..00000000000 --- a/plugins/woocommerce/changelog/fix-49978-default-product-attribute-pattern +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: add -Comment: Add default product attribute pattern to dynamically show the default attribute in the Product Filters template part. - - diff --git a/plugins/woocommerce/changelog/fix-49979-attribute-filter-dynamic-block-title b/plugins/woocommerce/changelog/fix-49979-attribute-filter-dynamic-block-title deleted file mode 100644 index da693300081..00000000000 --- a/plugins/woocommerce/changelog/fix-49979-attribute-filter-dynamic-block-title +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: enhancement -Comment: Enhancement: Register variation for each product attribute. Remove the need of manipulating metadata/blockName. Improve editor performance by only updating wrapper and heading on product attribute change. - - diff --git a/plugins/woocommerce/changelog/fix-49980-overlay-navigation-title-icon b/plugins/woocommerce/changelog/fix-49980-overlay-navigation-title-icon deleted file mode 100644 index 22e9c27ed8d..00000000000 --- a/plugins/woocommerce/changelog/fix-49980-overlay-navigation-title-icon +++ /dev/null @@ -1,3 +0,0 @@ -Significance: patch -Type: update -Comment: New title and icon for Overlay Navigation block diff --git a/plugins/woocommerce/changelog/fix-50027_e2e_tests_product_attributes b/plugins/woocommerce/changelog/fix-50027_e2e_tests_product_attributes deleted file mode 100644 index c3c26e9927a..00000000000 --- a/plugins/woocommerce/changelog/fix-50027_e2e_tests_product_attributes +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Enable skipped E2E tests for attributes #50143 diff --git a/plugins/woocommerce/changelog/fix-50055-compatibility-layer-patterns b/plugins/woocommerce/changelog/fix-50055-compatibility-layer-patterns new file mode 100644 index 00000000000..e6811df2b13 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-50055-compatibility-layer-patterns @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Update the logic that conditionally activates the compatibility layer in WooCommerce block templates so it detects blocks inside patterns diff --git a/plugins/woocommerce/changelog/fix-50112-ux-improvements-core-profiler-checkbox b/plugins/woocommerce/changelog/fix-50112-ux-improvements-core-profiler-checkbox deleted file mode 100644 index c0c5732fe99..00000000000 --- a/plugins/woocommerce/changelog/fix-50112-ux-improvements-core-profiler-checkbox +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Fix core profiler checkbox vertical alignment and border color diff --git a/plugins/woocommerce/changelog/fix-50167-payment-gateway-suggestions-flaky-unit-test b/plugins/woocommerce/changelog/fix-50167-payment-gateway-suggestions-flaky-unit-test deleted file mode 100644 index c904ed2d217..00000000000 --- a/plugins/woocommerce/changelog/fix-50167-payment-gateway-suggestions-flaky-unit-test +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: dev -Comment: We fixed and improved PHPUnit tests. - - diff --git a/plugins/woocommerce/changelog/fix-50207 b/plugins/woocommerce/changelog/fix-50207 new file mode 100644 index 00000000000..b20874ec6a2 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-50207 @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Bust WC object cache on direct post meta update to prevent stale cache read. diff --git a/plugins/woocommerce/changelog/fix-50245-fatal-error-checkout-customer b/plugins/woocommerce/changelog/fix-50245-fatal-error-checkout-customer new file mode 100644 index 00000000000..4c7ba4fcea1 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-50245-fatal-error-checkout-customer @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Prevent an error in the WC_Checkout class when WooCommerce has not been properly initialized yet diff --git a/plugins/woocommerce/changelog/fix-50271-active-plugins-detection-in-multisites b/plugins/woocommerce/changelog/fix-50271-active-plugins-detection-in-multisites deleted file mode 100644 index 753188fa15a..00000000000 --- a/plugins/woocommerce/changelog/fix-50271-active-plugins-detection-in-multisites +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Properly detect active plugins in multisite WP installations. diff --git a/plugins/woocommerce/changelog/fix-50429-store-api-resuming-exclude-pending b/plugins/woocommerce/changelog/fix-50429-store-api-resuming-exclude-pending deleted file mode 100644 index 515687a8a55..00000000000 --- a/plugins/woocommerce/changelog/fix-50429-store-api-resuming-exclude-pending +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Store API: Do not resume pending orders--create a new order instead diff --git a/plugins/woocommerce/changelog/fix-50550-improve-product-details-minimal-style-focus-styles b/plugins/woocommerce/changelog/fix-50550-improve-product-details-minimal-style-focus-styles new file mode 100644 index 00000000000..a703bf3d9e8 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-50550-improve-product-details-minimal-style-focus-styles @@ -0,0 +1,4 @@ +Significance: patch +Type: enhancement + +Improve the tabs focus style in the Product Details block with Minimal style variation diff --git a/plugins/woocommerce/changelog/fix-50659-product-price-nowrap b/plugins/woocommerce/changelog/fix-50659-product-price-nowrap deleted file mode 100644 index c080d48c224..00000000000 --- a/plugins/woocommerce/changelog/fix-50659-product-price-nowrap +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Product Price block: prevent price amounts from breaking into multiple lines diff --git a/plugins/woocommerce/changelog/fix-50667-my-account-hooked-size b/plugins/woocommerce/changelog/fix-50667-my-account-hooked-size new file mode 100644 index 00000000000..4fcd396ddc4 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-50667-my-account-hooked-size @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix My Account block icon being too small when inserted via block hooks diff --git a/plugins/woocommerce/changelog/fix-50879-cart-my-account-icons-header-patterns b/plugins/woocommerce/changelog/fix-50879-cart-my-account-icons-header-patterns new file mode 100644 index 00000000000..93e50650c22 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-50879-cart-my-account-icons-header-patterns @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Improve icon aligment in Essential Header and Minimal Header patterns diff --git a/plugins/woocommerce/changelog/fix-50891 b/plugins/woocommerce/changelog/fix-50891 new file mode 100644 index 00000000000..33cc44e00b0 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-50891 @@ -0,0 +1,4 @@ +Significance: patch +Type: enhancement + +Add filter for response of `wc_rest_should_load_namespace` function to allow loading namespaces. diff --git a/plugins/woocommerce/changelog/fix-action-scheduler-blocking b/plugins/woocommerce/changelog/fix-action-scheduler-blocking deleted file mode 100644 index c4b446a5865..00000000000 --- a/plugins/woocommerce/changelog/fix-action-scheduler-blocking +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Fix rescheduling of actions that are blocked by other delayed actions diff --git a/plugins/woocommerce/changelog/fix-agnostic-fallback-methods b/plugins/woocommerce/changelog/fix-agnostic-fallback-methods deleted file mode 100644 index 0907886662d..00000000000 --- a/plugins/woocommerce/changelog/fix-agnostic-fallback-methods +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: tweak -Comment: Refactor block templates fallback methods so they are template-agnostic - - diff --git a/plugins/woocommerce/changelog/fix-badge-decrease-marketing-task b/plugins/woocommerce/changelog/fix-badge-decrease-marketing-task deleted file mode 100644 index d58e5d86e9f..00000000000 --- a/plugins/woocommerce/changelog/fix-badge-decrease-marketing-task +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Fix navigation badge decreases when installing extension in "Grow your business task" diff --git a/plugins/woocommerce/changelog/fix-cart-error-handling-49724 b/plugins/woocommerce/changelog/fix-cart-error-handling-49724 deleted file mode 100644 index 2d7050044be..00000000000 --- a/plugins/woocommerce/changelog/fix-cart-error-handling-49724 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Fix extensionCartUpdates to surface generic error messages, and include documentation for the error handling. diff --git a/plugins/woocommerce/changelog/fix-cart-token-disable-nonces-42341 b/plugins/woocommerce/changelog/fix-cart-token-disable-nonces-42341 deleted file mode 100644 index b60b590f644..00000000000 --- a/plugins/woocommerce/changelog/fix-cart-token-disable-nonces-42341 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -Store API: Remove the need for nonces when using cart tokens. Remove deprecated X-WC-Store-API-Nonce header. diff --git a/plugins/woocommerce/changelog/fix-ci-metrics-job b/plugins/woocommerce/changelog/fix-ci-metrics-job deleted file mode 100644 index d743f02743a..00000000000 --- a/plugins/woocommerce/changelog/fix-ci-metrics-job +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -Fix Metrics CI job diff --git a/plugins/woocommerce/changelog/fix-classic-template-tests-product-attribute-template b/plugins/woocommerce/changelog/fix-classic-template-tests-product-attribute-template deleted file mode 100644 index 999bb204a8f..00000000000 --- a/plugins/woocommerce/changelog/fix-classic-template-tests-product-attribute-template +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: tweak -Comment: Enable Classic Template tests in the frontend and the Product Attribute template - - diff --git a/plugins/woocommerce/changelog/fix-conditionally-set-new-order-metafield b/plugins/woocommerce/changelog/fix-conditionally-set-new-order-metafield new file mode 100644 index 00000000000..40da44a369b --- /dev/null +++ b/plugins/woocommerce/changelog/fix-conditionally-set-new-order-metafield @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Conditionally set new order email sent meta field diff --git a/plugins/woocommerce/changelog/fix-core-profiler-setup-tos-spacing b/plugins/woocommerce/changelog/fix-core-profiler-setup-tos-spacing deleted file mode 100644 index 13b00bc97e5..00000000000 --- a/plugins/woocommerce/changelog/fix-core-profiler-setup-tos-spacing +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Fix core profiler set up my store button and TOS are too close to each other diff --git a/plugins/woocommerce/changelog/fix-cys-internal-classes b/plugins/woocommerce/changelog/fix-cys-internal-classes new file mode 100644 index 00000000000..0bbe2f28b7d --- /dev/null +++ b/plugins/woocommerce/changelog/fix-cys-internal-classes @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Mark several Customize Your Store PHP classes as internal diff --git a/plugins/woocommerce/changelog/fix-cys-patterns-margin b/plugins/woocommerce/changelog/fix-cys-patterns-margin new file mode 100644 index 00000000000..cfee558dcc2 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-cys-patterns-margin @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix bottom margin of last patterns diff --git a/plugins/woocommerce/changelog/fix-flaky-remote-logging-test b/plugins/woocommerce/changelog/fix-flaky-remote-logging-test new file mode 100644 index 00000000000..d0a9491c758 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-flaky-remote-logging-test @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix flaky remote logging PHP unit test diff --git a/plugins/woocommerce/changelog/fix-global-product-object-compatibility-layer-product-collection b/plugins/woocommerce/changelog/fix-global-product-object-compatibility-layer-product-collection deleted file mode 100644 index 3e10c410ba2..00000000000 --- a/plugins/woocommerce/changelog/fix-global-product-object-compatibility-layer-product-collection +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Fix: ensure the global product object is always ready for compatibility layer by disabling default render routine of Product Templates inner blocks. diff --git a/plugins/woocommerce/changelog/fix-google-for-woocommerce-e2e b/plugins/woocommerce/changelog/fix-google-for-woocommerce-e2e deleted file mode 100644 index 62b59cd15b5..00000000000 --- a/plugins/woocommerce/changelog/fix-google-for-woocommerce-e2e +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Fix e2e Google for WooCommerce strict mode violation error diff --git a/plugins/woocommerce/changelog/fix-hide-payments-recommendation-when-no-extensions b/plugins/woocommerce/changelog/fix-hide-payments-recommendation-when-no-extensions new file mode 100644 index 00000000000..169df53a6f7 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-hide-payments-recommendation-when-no-extensions @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Hide payment recommendations when no extensions are available diff --git a/plugins/woocommerce/changelog/fix-hpos-diff-prevent-sync-on-read b/plugins/woocommerce/changelog/fix-hpos-diff-prevent-sync-on-read deleted file mode 100644 index be74651bc21..00000000000 --- a/plugins/woocommerce/changelog/fix-hpos-diff-prevent-sync-on-read +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Prevent sync-on-read from affecting results of HPOS diff CLI tool. diff --git a/plugins/woocommerce/changelog/fix-linked-product-timing-issue b/plugins/woocommerce/changelog/fix-linked-product-timing-issue deleted file mode 100644 index 39931fe1918..00000000000 --- a/plugins/woocommerce/changelog/fix-linked-product-timing-issue +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: dev - -Update E2E tests for linked list and variation creation with new component changes. diff --git a/plugins/woocommerce/changelog/e2e-cleanup-environment-console-log b/plugins/woocommerce/changelog/fix-lint-warnings similarity index 60% rename from plugins/woocommerce/changelog/e2e-cleanup-environment-console-log rename to plugins/woocommerce/changelog/fix-lint-warnings index 4f73f5cc6c2..7900b7702d5 100644 --- a/plugins/woocommerce/changelog/e2e-cleanup-environment-console-log +++ b/plugins/woocommerce/changelog/fix-lint-warnings @@ -1,4 +1,4 @@ Significance: patch Type: dev - +Fix eslint warnings diff --git a/plugins/woocommerce/changelog/fix-logout-endpoint-handling-25288 b/plugins/woocommerce/changelog/fix-logout-endpoint-handling-25288 deleted file mode 100644 index bad687d736c..00000000000 --- a/plugins/woocommerce/changelog/fix-logout-endpoint-handling-25288 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -Fixed log-out link behavior so that redirects work, and so that security nonces are automatically added to link in navigation menus. diff --git a/plugins/woocommerce/changelog/fix-lys-payment-methods-link b/plugins/woocommerce/changelog/fix-lys-payment-methods-link deleted file mode 100644 index 6e929fe3207..00000000000 --- a/plugins/woocommerce/changelog/fix-lys-payment-methods-link +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Fix the "Add payment methods" link in LYS congrat screen redirects to a blank page diff --git a/plugins/woocommerce/changelog/fix-make-themes-api-safer b/plugins/woocommerce/changelog/fix-make-themes-api-safer new file mode 100644 index 00000000000..c07fa424df5 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-make-themes-api-safer @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Add check to ensure themes API is safe diff --git a/plugins/woocommerce/changelog/fix-marketing-task b/plugins/woocommerce/changelog/fix-marketing-task deleted file mode 100644 index 71ecb38cb41..00000000000 --- a/plugins/woocommerce/changelog/fix-marketing-task +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -Remove remote API call from marketing task diff --git a/plugins/woocommerce/changelog/fix-menu-badge b/plugins/woocommerce/changelog/fix-menu-badge new file mode 100644 index 00000000000..c1e2007d859 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-menu-badge @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix badge number fails to match the number of unfinished tasks diff --git a/plugins/woocommerce/changelog/fix-metrics-job-part-deux b/plugins/woocommerce/changelog/fix-metrics-job-part-deux deleted file mode 100644 index d66f3c1137c..00000000000 --- a/plugins/woocommerce/changelog/fix-metrics-job-part-deux +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -Fix the Metrics job by adding a missing NVM install step diff --git a/plugins/woocommerce/changelog/fix-misspelling-in-plugins-woocommerce-blocks-docs b/plugins/woocommerce/changelog/fix-misspelling-in-plugins-woocommerce-blocks-docs new file mode 100644 index 00000000000..774235acaa7 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-misspelling-in-plugins-woocommerce-blocks-docs @@ -0,0 +1,4 @@ +Significance: minor +Type: tweak + +Fix typos in plugins/woocommerce-blocks/docs diff --git a/plugins/woocommerce/changelog/fix-page-titles-49798 b/plugins/woocommerce/changelog/fix-page-titles-49798 deleted file mode 100644 index 957f2bd8467..00000000000 --- a/plugins/woocommerce/changelog/fix-page-titles-49798 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Fix page titles of the cart and checkout page when using blocks and FSE themes. diff --git a/plugins/woocommerce/changelog/fix-payment-settings-remove-save-changes-button b/plugins/woocommerce/changelog/fix-payment-settings-remove-save-changes-button deleted file mode 100644 index 91dbfd1b8b1..00000000000 --- a/plugins/woocommerce/changelog/fix-payment-settings-remove-save-changes-button +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Hide save changes button in main payments screen diff --git a/plugins/woocommerce/changelog/fix-pc-max-price-filter-value-inclusion b/plugins/woocommerce/changelog/fix-pc-max-price-filter-value-inclusion deleted file mode 100644 index 2efb0084d96..00000000000 --- a/plugins/woocommerce/changelog/fix-pc-max-price-filter-value-inclusion +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Product Collection: Fix max price query to include prices less or equal to the given max value. diff --git a/plugins/woocommerce/changelog/fix-pc-prevent-rendering-on-empty-query b/plugins/woocommerce/changelog/fix-pc-prevent-rendering-on-empty-query new file mode 100644 index 00000000000..bbdaf94ece0 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-pc-prevent-rendering-on-empty-query @@ -0,0 +1,4 @@ +Significance: minor +Type: enhancement + +Product Collection: Don't render when empty unless the no results block is present. diff --git a/plugins/woocommerce/changelog/fix-php-warning b/plugins/woocommerce/changelog/fix-php-warning deleted file mode 100644 index 4a511ec5319..00000000000 --- a/plugins/woocommerce/changelog/fix-php-warning +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak - -Product Collection: fix the PHP deprecated warning diff --git a/plugins/woocommerce/changelog/fix-placeholder-display-25152 b/plugins/woocommerce/changelog/fix-placeholder-display-25152 deleted file mode 100644 index 40e5d7c046f..00000000000 --- a/plugins/woocommerce/changelog/fix-placeholder-display-25152 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Fixed placeholders in the classic cart shipping calculator to update with country selection. diff --git a/plugins/woocommerce/changelog/fix-remote-logger-sanitise-query-params b/plugins/woocommerce/changelog/fix-remote-logger-sanitise-query-params new file mode 100644 index 00000000000..bf2342b18e0 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-remote-logger-sanitise-query-params @@ -0,0 +1,4 @@ +Significance: minor +Type: enhancement + +Add query params masking to remote logger diff --git a/plugins/woocommerce/changelog/fix-set-new-password-regression-49670 b/plugins/woocommerce/changelog/fix-set-new-password-regression-49670 deleted file mode 100644 index 5d5e3d5f4dc..00000000000 --- a/plugins/woocommerce/changelog/fix-set-new-password-regression-49670 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Allow new accounts to set new password even if logged in. diff --git a/plugins/woocommerce/changelog/fix-shipping-zone-region-selector-decode-html-entities b/plugins/woocommerce/changelog/fix-shipping-zone-region-selector-decode-html-entities deleted file mode 100644 index ca081b99183..00000000000 --- a/plugins/woocommerce/changelog/fix-shipping-zone-region-selector-decode-html-entities +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: fix - -Transform labels in shipping zone region selector to decode html entities diff --git a/plugins/woocommerce/changelog/fix-show-recommendation-on-settings-payment-main-page b/plugins/woocommerce/changelog/fix-show-recommendation-on-settings-payment-main-page new file mode 100644 index 00000000000..b45252f8239 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-show-recommendation-on-settings-payment-main-page @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Show payments recommendation when main settings element is found diff --git a/plugins/woocommerce/changelog/fix-state-requirement b/plugins/woocommerce/changelog/fix-state-requirement deleted file mode 100644 index 2f55beac127..00000000000 --- a/plugins/woocommerce/changelog/fix-state-requirement +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Prevent Store API orders being placed with empty state diff --git a/plugins/woocommerce/changelog/fix-warning-related-to-customer-meta-field-without-class b/plugins/woocommerce/changelog/fix-warning-related-to-customer-meta-field-without-class deleted file mode 100644 index 992f39ad9d1..00000000000 --- a/plugins/woocommerce/changelog/fix-warning-related-to-customer-meta-field-without-class +++ /dev/null @@ -1,6 +0,0 @@ -Significance: patch -Type: fix - -Reduce error noise in the user profile screen, by removing the requirement for custom fields to have a class attribute. - - diff --git a/plugins/woocommerce/changelog/fix-wcadmin-react-18-create-root b/plugins/woocommerce/changelog/fix-wcadmin-react-18-create-root deleted file mode 100644 index 403d31e0f0b..00000000000 --- a/plugins/woocommerce/changelog/fix-wcadmin-react-18-create-root +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Changed from using React.render to React.createRoot for WCAdmin uses as it has been deprecated since React 18 diff --git a/plugins/woocommerce/changelog/fix-wcadmin-react-18-create-root-shippingzones-payment-methods b/plugins/woocommerce/changelog/fix-wcadmin-react-18-create-root-shippingzones-payment-methods deleted file mode 100644 index c42e8d67aed..00000000000 --- a/plugins/woocommerce/changelog/fix-wcadmin-react-18-create-root-shippingzones-payment-methods +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Changed from using React.render to React.createRoot for payment methods promotion, shipping settings region zone as it has been deprecated since React 18 \ No newline at end of file diff --git a/plugins/woocommerce/changelog/fix-wcadmin-react18-createroot-addon-tour b/plugins/woocommerce/changelog/fix-wcadmin-react18-createroot-addon-tour deleted file mode 100644 index 126551fa719..00000000000 --- a/plugins/woocommerce/changelog/fix-wcadmin-react18-createroot-addon-tour +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Changed from using React.render to React.createRoot for wc addon tour as it has been deprecated since React 18 \ No newline at end of file diff --git a/plugins/woocommerce/changelog/fix-wcadmin-react18-createroot-marketing-coupons b/plugins/woocommerce/changelog/fix-wcadmin-react18-createroot-marketing-coupons deleted file mode 100644 index 97441ed9d59..00000000000 --- a/plugins/woocommerce/changelog/fix-wcadmin-react18-createroot-marketing-coupons +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Changed from using React.render to React.createRoot for marketing coupons as it has been deprecated since React 18 \ No newline at end of file diff --git a/plugins/woocommerce/changelog/fix-wcadmin-react18-createroot-print-shipping-banner b/plugins/woocommerce/changelog/fix-wcadmin-react18-createroot-print-shipping-banner deleted file mode 100644 index a42519f5094..00000000000 --- a/plugins/woocommerce/changelog/fix-wcadmin-react18-createroot-print-shipping-banner +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Changed from using React.render to React.createRoot for print shipping banner as it has been deprecated since React 18 \ No newline at end of file diff --git a/plugins/woocommerce/changelog/move-product-collection b/plugins/woocommerce/changelog/move-product-collection deleted file mode 100644 index c7dcdc00e9c..00000000000 --- a/plugins/woocommerce/changelog/move-product-collection +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: dev - -moving product collection docs to main docs folder diff --git a/plugins/woocommerce/changelog/prep-post-release-tasks-9.1.4 b/plugins/woocommerce/changelog/prep-post-release-tasks-9.1.4 deleted file mode 100644 index c0f6da403d4..00000000000 --- a/plugins/woocommerce/changelog/prep-post-release-tasks-9.1.4 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: dev -Comment: Release task/automated change. No changelog needed. - - diff --git a/plugins/woocommerce/changelog/prep-trunk-for-next-dev-cycle-9.3 b/plugins/woocommerce/changelog/prep-trunk-for-next-dev-cycle-9.3 deleted file mode 100644 index f9f79951091..00000000000 --- a/plugins/woocommerce/changelog/prep-trunk-for-next-dev-cycle-9.3 +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: dev -Comment: Automated version bump, there is nothing of value worth communicating via the changelog. - - diff --git a/plugins/woocommerce/changelog/refactor-50063-migrate-all-products-block-to-functional-component b/plugins/woocommerce/changelog/refactor-50063-migrate-all-products-block-to-functional-component deleted file mode 100644 index 3940ec7ad8b..00000000000 --- a/plugins/woocommerce/changelog/refactor-50063-migrate-all-products-block-to-functional-component +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: enhancement - -Refactor: Migrate the All Products block to API version 3 diff --git a/plugins/woocommerce/changelog/refactor-pc-remove-automatic-migration b/plugins/woocommerce/changelog/refactor-pc-remove-automatic-migration deleted file mode 100644 index 54ccf670342..00000000000 --- a/plugins/woocommerce/changelog/refactor-pc-remove-automatic-migration +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: tweak - -Remove the code related to the automatic Products (Beta) -> Product Collection upgrade. diff --git a/plugins/woocommerce/changelog/remove-cys-sidebar-links b/plugins/woocommerce/changelog/remove-cys-sidebar-links deleted file mode 100644 index a133cf301d0..00000000000 --- a/plugins/woocommerce/changelog/remove-cys-sidebar-links +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: update - -Remove all links from the CYS sidebars diff --git a/plugins/woocommerce/changelog/revert-zoom-out-feature b/plugins/woocommerce/changelog/revert-zoom-out-feature deleted file mode 100644 index 5f2de3fdb4b..00000000000 --- a/plugins/woocommerce/changelog/revert-zoom-out-feature +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -Revert the Zoom Out feature for the CYS experience diff --git a/plugins/woocommerce/changelog/test-50444-collections b/plugins/woocommerce/changelog/test-50444-collections new file mode 100644 index 00000000000..2549e704bb5 --- /dev/null +++ b/plugins/woocommerce/changelog/test-50444-collections @@ -0,0 +1,4 @@ +Significance: minor +Type: tweak + +Move collections e2e tests from product collection file to its own file diff --git a/plugins/woocommerce/changelog/test-50444-inspector-control b/plugins/woocommerce/changelog/test-50444-inspector-control new file mode 100644 index 00000000000..fb469b0c0ad --- /dev/null +++ b/plugins/woocommerce/changelog/test-50444-inspector-control @@ -0,0 +1,4 @@ +Significance: minor +Type: tweak + +Move the inspector controls e2e tests from product collection file to its own file diff --git a/plugins/woocommerce/changelog/test-50444-register-product-collection-tester b/plugins/woocommerce/changelog/test-50444-register-product-collection-tester new file mode 100644 index 00000000000..061d77a57ad --- /dev/null +++ b/plugins/woocommerce/changelog/test-50444-register-product-collection-tester @@ -0,0 +1,4 @@ +Significance: minor +Type: tweak + +Move register product collection tester e2e tests from product collection file to its own file diff --git a/plugins/woocommerce/changelog/tests-move-api-tests-into-e2e b/plugins/woocommerce/changelog/tests-move-api-tests-into-e2e deleted file mode 100644 index c12f3a3d845..00000000000 --- a/plugins/woocommerce/changelog/tests-move-api-tests-into-e2e +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -Tests: moved api core tests as a suite in e2e-pw diff --git a/plugins/woocommerce/changelog/tweak-bump-jetpack-connection b/plugins/woocommerce/changelog/tweak-bump-jetpack-connection deleted file mode 100644 index b3b251e2566..00000000000 --- a/plugins/woocommerce/changelog/tweak-bump-jetpack-connection +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: tweak - -Bump Jetpack COnnection, Jetpack Constants and a8c MC Stats diff --git a/plugins/woocommerce/changelog/tweak-improve-reset-password-check b/plugins/woocommerce/changelog/tweak-improve-reset-password-check new file mode 100644 index 00000000000..4f657552c2d --- /dev/null +++ b/plugins/woocommerce/changelog/tweak-improve-reset-password-check @@ -0,0 +1,4 @@ +Significance: patch +Type: tweak + +Update reset password e2e to use or locator to check reset status diff --git a/plugins/woocommerce/changelog/tweak-lost-password-e2e b/plugins/woocommerce/changelog/tweak-lost-password-e2e deleted file mode 100644 index ef656311cf5..00000000000 --- a/plugins/woocommerce/changelog/tweak-lost-password-e2e +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -Tweak the lost password e2e logic diff --git a/plugins/woocommerce/changelog/update--remove-add-to-cart-from-webpack-entries b/plugins/woocommerce/changelog/update--remove-add-to-cart-from-webpack-entries deleted file mode 100644 index b5d22cfc666..00000000000 --- a/plugins/woocommerce/changelog/update--remove-add-to-cart-from-webpack-entries +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: dev -Comment: Remove duplicate Add to Cart Form block from Webpack entries - - diff --git a/plugins/woocommerce/changelog/update-50616-replace-colon-with-space-product-data-meta-box b/plugins/woocommerce/changelog/update-50616-replace-colon-with-space-product-data-meta-box deleted file mode 100644 index 4e9e207add2..00000000000 --- a/plugins/woocommerce/changelog/update-50616-replace-colon-with-space-product-data-meta-box +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak - -Remove colon from product data meta box checkboxes diff --git a/plugins/woocommerce/changelog/update-abrev-global-unique-id b/plugins/woocommerce/changelog/update-abrev-global-unique-id deleted file mode 100644 index d35e7d8f1e6..00000000000 --- a/plugins/woocommerce/changelog/update-abrev-global-unique-id +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -Add abbreviations for fields GTIN, UPC, EAN, OR ISBN diff --git a/plugins/woocommerce/changelog/update-account-handling-consolodation b/plugins/woocommerce/changelog/update-account-handling-consolodation new file mode 100644 index 00000000000..6ab464440e9 --- /dev/null +++ b/plugins/woocommerce/changelog/update-account-handling-consolodation @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Consolidate Store API and Core account creation functions. diff --git a/plugins/woocommerce/changelog/update-block-reference b/plugins/woocommerce/changelog/update-block-reference new file mode 100644 index 00000000000..64c0a52aa7f --- /dev/null +++ b/plugins/woocommerce/changelog/update-block-reference @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Make Block Reference a public doc diff --git a/plugins/woocommerce/changelog/update-blocks-wp-env-core-.6 b/plugins/woocommerce/changelog/update-blocks-wp-env-core-.6 deleted file mode 100644 index cfa04d44c0a..00000000000 --- a/plugins/woocommerce/changelog/update-blocks-wp-env-core-.6 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -Update WP version to 6.6 in Blocks wp-env config. diff --git a/plugins/woocommerce/changelog/update-contribution-docs b/plugins/woocommerce/changelog/update-contribution-docs new file mode 100644 index 00000000000..0712915d3bc --- /dev/null +++ b/plugins/woocommerce/changelog/update-contribution-docs @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +fix small lint errors and fix lint rule to make doc contribution easier diff --git a/plugins/woocommerce/changelog/update-core-profiler-sticky-continue-button b/plugins/woocommerce/changelog/update-core-profiler-sticky-continue-button deleted file mode 100644 index 25528ca7f07..00000000000 --- a/plugins/woocommerce/changelog/update-core-profiler-sticky-continue-button +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: update - -Update core profiler continue button container on extension screen diff --git a/plugins/woocommerce/changelog/update-e2e-product-editor-create-simple-product b/plugins/woocommerce/changelog/update-e2e-product-editor-create-simple-product deleted file mode 100644 index 24203f11547..00000000000 --- a/plugins/woocommerce/changelog/update-e2e-product-editor-create-simple-product +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -Add additional fields to new product editor e2e tests. diff --git a/plugins/woocommerce/changelog/update-e2e-test-utils-playwright b/plugins/woocommerce/changelog/update-e2e-test-utils-playwright deleted file mode 100644 index c333054d86e..00000000000 --- a/plugins/woocommerce/changelog/update-e2e-test-utils-playwright +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -Update @wordpress/e2e-test-utils-playwright core dependency to wp-6.6 diff --git a/plugins/woocommerce/changelog/update-enable-remote-logging b/plugins/woocommerce/changelog/update-enable-remote-logging deleted file mode 100644 index 292cb46acfa..00000000000 --- a/plugins/woocommerce/changelog/update-enable-remote-logging +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -Enable remote logging feature flag diff --git a/plugins/woocommerce/changelog/update-improve-image-size b/plugins/woocommerce/changelog/update-improve-image-size deleted file mode 100644 index ada7b2cf2c7..00000000000 --- a/plugins/woocommerce/changelog/update-improve-image-size +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak - -Optimize large image files diff --git a/plugins/woocommerce/changelog/update-log-directory-init b/plugins/woocommerce/changelog/update-log-directory-init deleted file mode 100644 index 8c520cd7e09..00000000000 --- a/plugins/woocommerce/changelog/update-log-directory-init +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: add - -Add parameter to avoid attempting to create the logs directory if it doesn't exist diff --git a/plugins/woocommerce/changelog/update-lys-e2e-test b/plugins/woocommerce/changelog/update-lys-e2e-test deleted file mode 100644 index cb5995be091..00000000000 --- a/plugins/woocommerce/changelog/update-lys-e2e-test +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -Update lys e2e tests to test with both classic and block themes diff --git a/plugins/woocommerce/changelog/update-lys-init-coming-soon b/plugins/woocommerce/changelog/update-lys-init-coming-soon deleted file mode 100644 index 07c3800ccea..00000000000 --- a/plugins/woocommerce/changelog/update-lys-init-coming-soon +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: tweak - -Prevent initializing coming soon feature if it's already initialized diff --git a/plugins/woocommerce/changelog/update-metrics-tests-utils-usage b/plugins/woocommerce/changelog/update-metrics-tests-utils-usage deleted file mode 100644 index 20bc32146db..00000000000 --- a/plugins/woocommerce/changelog/update-metrics-tests-utils-usage +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: dev - -Make the Metrics tests use utilities provided by the updated @wordpress/e2e-test-utils-playwright package. diff --git a/plugins/woocommerce/changelog/update-mobile-order-summary-styling-50635 b/plugins/woocommerce/changelog/update-mobile-order-summary-styling-50635 new file mode 100644 index 00000000000..e1a82f3272d --- /dev/null +++ b/plugins/woocommerce/changelog/update-mobile-order-summary-styling-50635 @@ -0,0 +1,4 @@ +Significance: minor +Type: tweak + +On mobile, style order confirmation page details side by side diff --git a/plugins/woocommerce/changelog/update-move-marketing-task b/plugins/woocommerce/changelog/update-move-marketing-task deleted file mode 100644 index 224fc602334..00000000000 --- a/plugins/woocommerce/changelog/update-move-marketing-task +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -Move marketing task to things to do next task list diff --git a/plugins/woocommerce/changelog/update-move-woo-version-check b/plugins/woocommerce/changelog/update-move-woo-version-check new file mode 100644 index 00000000000..af5e8c0070e --- /dev/null +++ b/plugins/woocommerce/changelog/update-move-woo-version-check @@ -0,0 +1,4 @@ +Significance: patch +Type: enhancement + +Enhance WooCommerce version checking for remote logging reliability diff --git a/plugins/woocommerce/changelog/update-my-subscriptions-column-names-and-actions b/plugins/woocommerce/changelog/update-my-subscriptions-column-names-and-actions deleted file mode 100644 index b6cff80e47b..00000000000 --- a/plugins/woocommerce/changelog/update-my-subscriptions-column-names-and-actions +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -Renamed columns inside In-App Marketplace > My subscriptions and added action to turn auto-renewal on for a subscription diff --git a/plugins/woocommerce/changelog/update-no-html-arialabel b/plugins/woocommerce/changelog/update-no-html-arialabel deleted file mode 100644 index 32d0ea01ff9..00000000000 --- a/plugins/woocommerce/changelog/update-no-html-arialabel +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -Strip HTML tags from aria-label in wc_help_tip function diff --git a/plugins/woocommerce/changelog/update-pattern-placeholder-image-optimization b/plugins/woocommerce/changelog/update-pattern-placeholder-image-optimization deleted file mode 100644 index 2ccb607797e..00000000000 --- a/plugins/woocommerce/changelog/update-pattern-placeholder-image-optimization +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: performance - -Compress pattern placeholder image assets diff --git a/plugins/woocommerce/changelog/update-product-filters-folder-structure b/plugins/woocommerce/changelog/update-product-filters-folder-structure new file mode 100644 index 00000000000..4904e404d8e --- /dev/null +++ b/plugins/woocommerce/changelog/update-product-filters-folder-structure @@ -0,0 +1,5 @@ +Significance: patch +Type: update +Comment: Update the folder structure of new filter blocks. + + diff --git a/plugins/woocommerce/changelog/update-product-task b/plugins/woocommerce/changelog/update-product-task deleted file mode 100644 index 5519757b9c4..00000000000 --- a/plugins/woocommerce/changelog/update-product-task +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: update - -Update add product task button section UI diff --git a/plugins/woocommerce/changelog/update-remote-logging-data b/plugins/woocommerce/changelog/update-remote-logging-data deleted file mode 100644 index 69c8edf57c4..00000000000 --- a/plugins/woocommerce/changelog/update-remote-logging-data +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: update - -Add request_uri prop to remote logging data diff --git a/plugins/woocommerce/changelog/update-show-expiring-expired-notices-for-active-or-connected-subs b/plugins/woocommerce/changelog/update-show-expiring-expired-notices-for-active-or-connected-subs deleted file mode 100644 index c0c93ec1aa3..00000000000 --- a/plugins/woocommerce/changelog/update-show-expiring-expired-notices-for-active-or-connected-subs +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: update - -Show expiring and expired notices to active and unconnected subscriptions diff --git a/plugins/woocommerce/changelog/update-stricter-global-unique-id b/plugins/woocommerce/changelog/update-stricter-global-unique-id deleted file mode 100644 index a542161138c..00000000000 --- a/plugins/woocommerce/changelog/update-stricter-global-unique-id +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -Add pattern validation for global_unique_id diff --git a/plugins/woocommerce/changelog/update-wcpay-promotion-add-default-promotions b/plugins/woocommerce/changelog/update-wcpay-promotion-add-default-promotions deleted file mode 100644 index 218c980b4d1..00000000000 --- a/plugins/woocommerce/changelog/update-wcpay-promotion-add-default-promotions +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: update -Comment: We add default WooPayments promotion specs when WooCommerce.com marketplace promotions are disabled. - - diff --git a/plugins/woocommerce/changelog/update-wcpay-task-to-use-default-specs b/plugins/woocommerce/changelog/update-wcpay-task-to-use-default-specs deleted file mode 100644 index 9d491a770c5..00000000000 --- a/plugins/woocommerce/changelog/update-wcpay-task-to-use-default-specs +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: update - -Update WooCommercePayments task is_supported to use default suggestions diff --git a/plugins/woocommerce/changelog/update-woocommerce-blocks-hooks-docs b/plugins/woocommerce/changelog/update-woocommerce-blocks-hooks-docs new file mode 100644 index 00000000000..92b592fa1c2 --- /dev/null +++ b/plugins/woocommerce/changelog/update-woocommerce-blocks-hooks-docs @@ -0,0 +1,5 @@ +Significance: patch +Type: update +Comment: This is just a docs update, no code changes will be released. + + diff --git a/plugins/woocommerce/changelog/update-woocommerce-shipping-promo-banner-188 b/plugins/woocommerce/changelog/update-woocommerce-shipping-promo-banner-188 new file mode 100644 index 00000000000..51e8df0eb8f --- /dev/null +++ b/plugins/woocommerce/changelog/update-woocommerce-shipping-promo-banner-188 @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Update WooCommerce Shipping Promo Banner to install the latest version of WooCommerce Shipping instead of WCS&T. diff --git a/plugins/woocommerce/client/admin/config/core.json b/plugins/woocommerce/client/admin/config/core.json index 20c72a64650..3c201f07922 100644 --- a/plugins/woocommerce/client/admin/config/core.json +++ b/plugins/woocommerce/client/admin/config/core.json @@ -3,6 +3,7 @@ "activity-panels": true, "analytics": true, "product-block-editor": true, + "product-data-views": false, "experimental-blocks": false, "coupons": true, "core-profiler": true, diff --git a/plugins/woocommerce/client/admin/config/development.json b/plugins/woocommerce/client/admin/config/development.json index 1c0f461e890..802c62fe6a3 100644 --- a/plugins/woocommerce/client/admin/config/development.json +++ b/plugins/woocommerce/client/admin/config/development.json @@ -3,6 +3,7 @@ "activity-panels": true, "analytics": true, "product-block-editor": true, + "product-data-views": false, "experimental-blocks": true, "coupons": true, "core-profiler": true, diff --git a/plugins/woocommerce/client/legacy/css/_mixins.scss b/plugins/woocommerce/client/legacy/css/_mixins.scss index 8c146ecb1cc..1ba81950b3b 100644 --- a/plugins/woocommerce/client/legacy/css/_mixins.scss +++ b/plugins/woocommerce/client/legacy/css/_mixins.scss @@ -301,6 +301,25 @@ } } +@mixin coupon-error-notice-cart() { + clear: left; + color: var(--wc-red); + flex-basis: 100%; + float: none; + font-size: 0.75em; + margin-bottom: 0; + margin-top: 8px; + text-align: left; + width: auto; +} + +@mixin coupon-error-notice-checkout() { + color: var(--wc-red); + display: block; + font-size: 0.75em; + margin-top: 8px; +} + @mixin woocommerce-product-gallery__trigger { background: #fff; border: none; diff --git a/plugins/woocommerce/client/legacy/css/admin.scss b/plugins/woocommerce/client/legacy/css/admin.scss index 3bd07728105..658ed0588d4 100644 --- a/plugins/woocommerce/client/legacy/css/admin.scss +++ b/plugins/woocommerce/client/legacy/css/admin.scss @@ -5579,7 +5579,7 @@ img.help_tip { input, select { - margin-top: -3px; + margin-top: -2px; vertical-align: middle; } @@ -8086,7 +8086,7 @@ table.bar_chart { #woocommerce-product-data { .hndle { display: block; - line-height: 24px; + line-height: 28px; .type_box { display: inline; diff --git a/plugins/woocommerce/client/legacy/css/helper.scss b/plugins/woocommerce/client/legacy/css/helper.scss index b4123b551e8..61f20279cc9 100644 --- a/plugins/woocommerce/client/legacy/css/helper.scss +++ b/plugins/woocommerce/client/legacy/css/helper.scss @@ -201,7 +201,7 @@ $admin-theme-color: var(--wp-admin-theme-color); } /*------------------------------------------------------------------------------ - Subscriptons Header + Subscriptions Header ------------------------------------------------------------------------------*/ .wc-helper { @@ -356,7 +356,7 @@ $admin-theme-color: var(--wp-admin-theme-color); } /*------------------------------------------------------------------------------ - Subscripton table + Subscription table ------------------------------------------------------------------------------*/ .wc-helper { diff --git a/plugins/woocommerce/client/legacy/css/twenty-nineteen.scss b/plugins/woocommerce/client/legacy/css/twenty-nineteen.scss index 71a7864ae8b..61bc0a23bee 100644 --- a/plugins/woocommerce/client/legacy/css/twenty-nineteen.scss +++ b/plugins/woocommerce/client/legacy/css/twenty-nineteen.scss @@ -1350,3 +1350,22 @@ table.variations { color: #fff; } } + +/** + * Coupon error notice + */ + .woocommerce-cart { + td.actions .coupon .coupon-error-notice { + @include coupon-error-notice-cart(); + } +} + +form.checkout_coupon { + .coupon-error-notice { + @include coupon-error-notice-checkout(); + } + + .input-text.has-error:focus { + border-color: var(--wc-red); + } +} diff --git a/plugins/woocommerce/client/legacy/css/twenty-seventeen.scss b/plugins/woocommerce/client/legacy/css/twenty-seventeen.scss index cffff75e3e0..a3d1d0d7b89 100644 --- a/plugins/woocommerce/client/legacy/css/twenty-seventeen.scss +++ b/plugins/woocommerce/client/legacy/css/twenty-seventeen.scss @@ -1256,3 +1256,22 @@ table.variations { color: #fff; } } + +/** + * Coupon error notice + */ + .woocommerce-cart { + td.actions .coupon .coupon-error-notice { + @include coupon-error-notice-cart(); + } +} + +form.checkout_coupon { + .coupon-error-notice { + @include coupon-error-notice-checkout(); + } + + .input-text.has-error:focus { + border-color: var(--wc-red); + } +} diff --git a/plugins/woocommerce/client/legacy/css/twenty-twenty-one.scss b/plugins/woocommerce/client/legacy/css/twenty-twenty-one.scss index 898b459d9a3..2d6ff7a2cfd 100644 --- a/plugins/woocommerce/client/legacy/css/twenty-twenty-one.scss +++ b/plugins/woocommerce/client/legacy/css/twenty-twenty-one.scss @@ -3077,3 +3077,22 @@ button.woocommerce-product-gallery__trigger { color: #000; } } + +/** + * Coupon error notice + */ + .woocommerce-cart { + td.actions .coupon .coupon-error-notice { + @include coupon-error-notice-cart(); + } +} + +form.checkout_coupon { + .coupon-error-notice { + @include coupon-error-notice-checkout(); + } + + .input-text.has-error:focus { + border-color: var(--wc-red); + } +} diff --git a/plugins/woocommerce/client/legacy/css/twenty-twenty-three.scss b/plugins/woocommerce/client/legacy/css/twenty-twenty-three.scss index 4209531e620..5f7e41af07a 100644 --- a/plugins/woocommerce/client/legacy/css/twenty-twenty-three.scss +++ b/plugins/woocommerce/client/legacy/css/twenty-twenty-three.scss @@ -609,7 +609,6 @@ ul.wc-tabs { #coupon_code { float: left; - margin-bottom: 1rem; } } } @@ -1131,3 +1130,23 @@ Notice messages (like 'Added to cart', 'Billing address needs to be filled in', content: '\2713'; } } + +/** + * Coupon error notice + */ + .woocommerce-cart { + td.actions .coupon .coupon-error-notice { + @include coupon-error-notice-cart(); + margin-top: 0; + } +} + +form.checkout_coupon { + .coupon-error-notice { + @include coupon-error-notice-checkout(); + } + + .input-text.has-error:focus { + border-color: var(--wc-red); + } +} diff --git a/plugins/woocommerce/client/legacy/css/twenty-twenty-two.scss b/plugins/woocommerce/client/legacy/css/twenty-twenty-two.scss index 0dc443382b4..b3f953c0517 100644 --- a/plugins/woocommerce/client/legacy/css/twenty-twenty-two.scss +++ b/plugins/woocommerce/client/legacy/css/twenty-twenty-two.scss @@ -1176,3 +1176,22 @@ ul.wc-tabs { content: "\2713"; } } + +/** + * Coupon error notice + */ + .woocommerce-cart { + td.actions .coupon .coupon-error-notice { + @include coupon-error-notice-cart(); + } +} + +form.checkout_coupon { + .coupon-error-notice { + @include coupon-error-notice-checkout(); + } + + .input-text.has-error:focus { + border-color: var(--wc-red); + } +} diff --git a/plugins/woocommerce/client/legacy/css/twenty-twenty.scss b/plugins/woocommerce/client/legacy/css/twenty-twenty.scss index fc0b318386f..80a270fe171 100644 --- a/plugins/woocommerce/client/legacy/css/twenty-twenty.scss +++ b/plugins/woocommerce/client/legacy/css/twenty-twenty.scss @@ -2409,3 +2409,22 @@ a.reset_variations { color: #000; } } + +/** + * Coupon error notice + */ +.woocommerce-cart { + td.actions .coupon .coupon-error-notice { + @include coupon-error-notice-cart(); + } +} + +form.checkout_coupon { + .coupon-error-notice { + @include coupon-error-notice-checkout(); + } + + .input-text.has-error:focus { + border-color: var(--wc-red); + } +} diff --git a/plugins/woocommerce/client/legacy/css/woocommerce-blocktheme.scss b/plugins/woocommerce/client/legacy/css/woocommerce-blocktheme.scss index d2e3e151cc6..6f1b6b5eaf3 100644 --- a/plugins/woocommerce/client/legacy/css/woocommerce-blocktheme.scss +++ b/plugins/woocommerce/client/legacy/css/woocommerce-blocktheme.scss @@ -310,6 +310,7 @@ a.added_to_cart { .coupon { display: flex; align-items: center; + flex-wrap: wrap; } #coupon_code { diff --git a/plugins/woocommerce/client/legacy/css/woocommerce-smallscreen.scss b/plugins/woocommerce/client/legacy/css/woocommerce-smallscreen.scss index 67aa6d79510..c7273a004fa 100644 --- a/plugins/woocommerce/client/legacy/css/woocommerce-smallscreen.scss +++ b/plugins/woocommerce/client/legacy/css/woocommerce-smallscreen.scss @@ -152,6 +152,16 @@ .button.alt { float: right; } + + .coupon-error-notice { + clear: left; + color: var(--wc-red); + float: left; + font-size: 0.75em; + margin-bottom: 0; + text-align: left; + width: 48%; + } } .button { diff --git a/plugins/woocommerce/client/legacy/css/woocommerce.scss b/plugins/woocommerce/client/legacy/css/woocommerce.scss index 5ed56ff8e5e..de97e4bc081 100644 --- a/plugins/woocommerce/client/legacy/css/woocommerce.scss +++ b/plugins/woocommerce/client/legacy/css/woocommerce.scss @@ -408,6 +408,18 @@ p.demo_store, min-width: 75%; display: inline-block; margin-right: 1em; + + /* We hide the default chevron because it cannot be directly modified. Instead, we add a custom chevron using a background image. */ + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + padding-right: 2em; + background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJmZWF0aGVyIGZlYXRoZXItY2hldnJvbi1kb3duIj48cG9seWxpbmUgcG9pbnRzPSI2IDkgMTIgMTUgMTggOSI+PC9wb2x5bGluZT48L3N2Zz4=) + no-repeat; + background-size: 16px; + -webkit-background-size: 16px; + background-position: calc(100% - 12px) 50%; + -webkit-background-position: calc(100% - 12px) 50%; } td.label { @@ -1269,6 +1281,16 @@ p.demo_store, border-radius: 5px; } + form.checkout_coupon { + .coupon-error-notice { + @include coupon-error-notice-checkout(); + } + + .input-text.has-error:focus { + border-color: var(--wc-red); + } + } + ul#shipping_method { list-style: none outside; margin: 0; @@ -1801,6 +1823,7 @@ p.demo_store, &::before { font-family: "WooCommerce"; content: "\e028"; + content: "\e028" / ""; // Add an empty alternative text so screen readers ignore it display: inline-block; position: absolute; top: 1em; @@ -1933,6 +1956,14 @@ p.demo_store, padding: 6px 6px 5px; margin: 0 4px 0 0; outline: 0; + + &.has-error:focus { + border-color: var(--wc-red); + } + } + + td.actions .coupon .coupon-error-notice { + @include coupon-error-notice-cart(); } input { diff --git a/plugins/woocommerce/client/legacy/js/accounting/accounting.js b/plugins/woocommerce/client/legacy/js/accounting/accounting.js index a6feca8a44b..a145cbf2322 100644 --- a/plugins/woocommerce/client/legacy/js/accounting/accounting.js +++ b/plugins/woocommerce/client/legacy/js/accounting/accounting.js @@ -230,7 +230,7 @@ * 2nd parameter `precision` can be an object matching `settings.number` */ var formatNumber = lib.formatNumber = lib.format = function(number, precision, thousand, decimal) { - // Resursively format arrays: + // Recursively format arrays: if (isArray(number)) { return map(number, function(val) { return formatNumber(val, precision, thousand, decimal); @@ -275,7 +275,7 @@ * To do: tidy up the parameters */ var formatMoney = lib.formatMoney = function(number, symbol, precision, thousand, decimal, format) { - // Resursively format arrays: + // Recursively format arrays: if (isArray(number)) { return map(number, function(val){ return formatMoney(val, symbol, precision, thousand, decimal, format); @@ -315,7 +315,7 @@ * List should be an array of numbers * Second parameter can be an object containing keys that match the params * - * Returns array of accouting-formatted number strings of same length + * Returns array of accounting-formatted number strings of the same length * * NB: `white-space:pre` CSS rule is required on the list container to prevent * browsers from collapsing the whitespace in the output strings. diff --git a/plugins/woocommerce/client/legacy/js/admin/meta-boxes-coupon.js b/plugins/woocommerce/client/legacy/js/admin/meta-boxes-coupon.js index c0eed310d81..115417eb48f 100644 --- a/plugins/woocommerce/client/legacy/js/admin/meta-boxes-coupon.js +++ b/plugins/woocommerce/client/legacy/js/admin/meta-boxes-coupon.js @@ -39,7 +39,7 @@ jQuery(function( $ ) { }, /** - * Insert generate coupon code buttom HTML. + * Insert generate coupon code button HTML. */ insert_generate_coupon_code_button: function() { $( '.post-type-shop_coupon' ).find( '#title' ).after( diff --git a/plugins/woocommerce/client/legacy/js/admin/settings.js b/plugins/woocommerce/client/legacy/js/admin/settings.js index afa04cc564a..bc7f4c8b725 100644 --- a/plugins/woocommerce/client/legacy/js/admin/settings.js +++ b/plugins/woocommerce/client/legacy/js/admin/settings.js @@ -98,9 +98,10 @@ event ) { // Toggling WP List Table checkboxes should not trigger navigation warnings. + // Theses checkboxes only select/unselect rows, they don't change the form. if ( $check_column.length && - $check_column.has( event.target ) + $check_column.has( event.target ).length ) { return; } diff --git a/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js b/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js index c29bdf90979..78e25432cb1 100755 --- a/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js +++ b/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js @@ -995,7 +995,7 @@ // update slider.slides slider.slides = $(slider.vars.selector + ':not(.clone)', slider); - // re-setup the slider to accomdate new slide + // re-setup the slider to accommodate new slide slider.setup(); //FlexSlider: added() Callback @@ -1021,7 +1021,7 @@ // update slider.slides slider.slides = $(slider.vars.selector + ':not(.clone)', slider); - // re-setup the slider to accomdate new slide + // re-setup the slider to accommodate new slide slider.setup(); // FlexSlider: removed() Callback @@ -1091,7 +1091,7 @@ itemWidth: 0, //{NEW} Integer: Box-model width of individual carousel items, including horizontal borders and padding. itemMargin: 0, //{NEW} Integer: Margin between carousel items. minItems: 1, //{NEW} Integer: Minimum number of carousel items that should be visible. Items will resize fluidly when below this. - maxItems: 0, //{NEW} Integer: Maxmimum number of carousel items that should be visible. Items will resize fluidly when above this limit. + maxItems: 0, //{NEW} Integer: Maximum number of carousel items that should be visible. Items will resize fluidly when above this limit. move: 0, //{NEW} Integer: Number of carousel items that should move on animation. If 0, slider will move all visible items. allowOneSlide: true, //{NEW} Boolean: Whether or not to allow a slider comprised of a single slide diff --git a/plugins/woocommerce/client/legacy/js/frontend/add-to-cart-variation.js b/plugins/woocommerce/client/legacy/js/frontend/add-to-cart-variation.js index 6e4e68f19b1..b84d6bb4196 100644 --- a/plugins/woocommerce/client/legacy/js/frontend/add-to-cart-variation.js +++ b/plugins/woocommerce/client/legacy/js/frontend/add-to-cart-variation.js @@ -22,10 +22,11 @@ self.$form.off( '.wc-variation-form' ); // Methods. - self.getChosenAttributes = self.getChosenAttributes.bind( self ); - self.findMatchingVariations = self.findMatchingVariations.bind( self ); - self.isMatch = self.isMatch.bind( self ); - self.toggleResetLink = self.toggleResetLink.bind( self ); + self.getChosenAttributes = self.getChosenAttributes.bind( self ); + self.findMatchingVariations = self.findMatchingVariations.bind( self ); + self.isMatch = self.isMatch.bind( self ); + self.toggleResetLink = self.toggleResetLink.bind( self ); + self.showNoMatchingVariationsMsg = self.showNoMatchingVariationsMsg.bind( self ); // Events. $form.on( 'click.wc-variation-form', '.reset_variations', { variationForm: self }, self.onReset ); @@ -185,14 +186,7 @@ attributes.chosenCount = 0; if ( ! form.loading ) { - form.$form - .find( '.single_variation' ) - .after( - '

' + - wc_add_to_cart_variation_params.i18n_no_matching_variations_text + - '

' - ); - form.$form.find( '.wc-no-matching-variations' ).slideDown( 200 ); + form.showNoMatchingVariationsMsg(); } } }, @@ -213,14 +207,7 @@ attributes.chosenCount = 0; if ( ! form.loading ) { - form.$form - .find( '.single_variation' ) - .after( - '

' + - wc_add_to_cart_variation_params.i18n_no_matching_variations_text + - '

' - ); - form.$form.find( '.wc-no-matching-variations' ).slideDown( 200 ); + form.showNoMatchingVariationsMsg(); } } } @@ -328,7 +315,7 @@ var form = event.data.variationForm; form.$form.find( 'input[name="variation_id"], input.variation_id' ).val( '' ).trigger( 'change' ); - form.$form.find( '.wc-no-matching-variations' ).remove(); + form.$form.find( '.wc-no-matching-variations' ).parent().remove(); if ( form.useAjax ) { form.$form.trigger( 'check_variations' ); @@ -569,6 +556,24 @@ } }; + /** + * Show no matching variation message. + */ + VariationForm.prototype.showNoMatchingVariationsMsg = function() { + this.$form + .find( '.single_variation' ) + .after( + '
' + + '

' + + wc_add_to_cart_variation_params.i18n_no_matching_variations_text + + '

' + + '
' + ) + .next( 'div' ) + .find( '.wc-no-matching-variations' ) + .slideDown( 200 ); + }; + /** * Function to call wc_variation_form on jquery selector. */ diff --git a/plugins/woocommerce/client/legacy/js/frontend/cart.js b/plugins/woocommerce/client/legacy/js/frontend/cart.js index fb4a8aeec04..c23b3e8fc55 100644 --- a/plugins/woocommerce/client/legacy/js/frontend/cart.js +++ b/plugins/woocommerce/client/legacy/js/frontend/cart.js @@ -104,7 +104,7 @@ jQuery( function ( $ ) { // Remove errors if ( ! preserve_notices ) { $( - '.woocommerce-error, .woocommerce-message, .woocommerce-info, .is-error, .is-info, .is-success' + '.woocommerce-error, .woocommerce-message, .woocommerce-info, .is-error, .is-info, .is-success, .coupon-error-notice' ).remove(); } @@ -135,12 +135,30 @@ jQuery( function ( $ ) { if ( $( '.woocommerce-checkout' ).length ) { $( document.body ).trigger( 'update_checkout' ); } + + // Store the old coupon error message and value before the + // .woocommerce-cart-form is replaced with the new form. + var $old_coupon_field_val = $( '#coupon_code' ).val(); + var $old_coupon_error_msg = $( '#coupon_code' ) + .closest( '.coupon' ) + .find( '.coupon-error-notice' ); $( '.woocommerce-cart-form' ).replaceWith( $new_form ); $( '.woocommerce-cart-form' ) .find( ':input[name="update_cart"]' ) .prop( 'disabled', true ); + if ( preserve_notices && $old_coupon_error_msg.length > 0 ) { + var $new_coupon_field = $( '.woocommerce-cart-form' ).find( '#coupon_code' ); + var $new_coupon_field_wrapper = $new_coupon_field.closest( '.coupon' ); + + $new_coupon_field.val( $old_coupon_field_val ); + // The coupon input with error needs to be focused before adding the live region + // with the error message, otherwise the screen reader won't read it. + $new_coupon_field.focus(); + show_coupon_error( $old_coupon_error_msg, $new_coupon_field_wrapper, true ); + } + if ( $notices.length > 0 ) { show_notice( $notices ); } @@ -176,6 +194,43 @@ jQuery( function ( $ ) { $target.prepend( html_element ); }; + /** + * Shows coupon form errors. + * + * @param {string|object} html_element The HTML string response after applying an invalid coupon or a jQuery element. + * @param {Object} $target Coupon field wrapper jQuery element. + * @param {boolean} is_live_region Whether role="alert" should be added or not. + */ + var show_coupon_error = function ( html_element, $target, is_live_region ) { + if ( $target.length === 0 ) { + return; + } + + var $coupon_error_el = ''; + + if ( typeof html_element === 'string' ) { + var msg = $( $.parseHTML( html_element ) ).text().trim(); + + if ( msg === '' ) { + return; + } + + $coupon_error_el = $( '

' + msg + '

' ); + } else { + $coupon_error_el = html_element; + } + + if ( is_live_region ) { + $coupon_error_el.attr( 'role', 'alert' ); + } + + $target.find( '#coupon_code' ) + .addClass( 'has-error' ) + .attr( 'aria-invalid', 'true' ) + .attr( 'aria-describedby', 'coupon-error-notice' ); + $target.append( $coupon_error_el ); + }; + /** * Object to handle AJAX calls for cart shipping changes. */ @@ -315,6 +370,7 @@ jQuery( function ( $ ) { this.apply_coupon = this.apply_coupon.bind( this ); this.remove_coupon_clicked = this.remove_coupon_clicked.bind( this ); + this.remove_coupon_error = this.remove_coupon_error.bind( this ); this.quantity_update = this.quantity_update.bind( this ); this.item_remove_clicked = this.item_remove_clicked.bind( this ); this.item_restore_clicked = this.item_restore_clicked.bind( this ); @@ -358,6 +414,11 @@ jQuery( function ( $ ) { '.woocommerce-cart-form .cart_item :input', this.input_changed ); + $( document ).on( + 'blur change input', + '#coupon_code', + this.remove_coupon_error + ); $( '.woocommerce-cart-form :input[name="update_cart"]' ).prop( 'disabled', @@ -525,16 +586,28 @@ jQuery( function ( $ ) { dataType: 'html', success: function ( response ) { $( - '.woocommerce-error, .woocommerce-message, .woocommerce-info, .is-error, .is-info, .is-success' + '.woocommerce-error, .woocommerce-message, .woocommerce-info, ' + + '.is-error, .is-info, .is-success, .coupon-error-notice' ).remove(); - show_notice( response ); + + // We only want to show coupon notices if they are not errors. + // Coupon errors are shown under the input. + if ( response.indexOf( 'woocommerce-error' ) === -1 && response.indexOf( 'is-error' ) === -1 ) { + show_notice( response ); + } else { + var $coupon_wrapper = $text_field.closest( '.coupon' ); + + if ( $coupon_wrapper.length > 0 ) { + show_coupon_error( response, $coupon_wrapper, false ); + } + } + $( document.body ).trigger( 'applied_coupon', [ coupon_code, ] ); }, complete: function () { unblock( $form ); - $text_field.val( '' ); cart.update_cart( true ); }, } ); @@ -578,6 +651,21 @@ jQuery( function ( $ ) { } ); }, + /** + * Handle when the coupon input loses focus. + * + * @param {Object} evt The JQuery event + */ + remove_coupon_error: function ( evt ) { + $( evt.currentTarget ) + .removeClass( 'has-error' ) + .removeAttr( 'aria-invalid' ) + .removeAttr( 'aria-describedby' ) + .closest( '.coupon' ) + .find( '.coupon-error-notice' ) + .remove(); + }, + /** * Handle a cart Quantity Update * diff --git a/plugins/woocommerce/client/legacy/js/frontend/checkout.js b/plugins/woocommerce/client/legacy/js/frontend/checkout.js index fc5320e8f34..7ac3b38496b 100644 --- a/plugins/woocommerce/client/legacy/js/frontend/checkout.js +++ b/plugins/woocommerce/client/legacy/js/frontend/checkout.js @@ -535,7 +535,8 @@ jQuery( function( $ ) { wc_checkout_form.detachUnloadEventsOnSubmit(); try { - if ( 'success' === result.result && $form.triggerHandler( 'checkout_place_order_success', [ result, wc_checkout_form ] ) !== false ) { + if ( 'success' === result.result && + $form.triggerHandler( 'checkout_place_order_success', [ result, wc_checkout_form ] ) !== false ) { if ( -1 === result.redirect.indexOf( 'https://' ) || -1 === result.redirect.indexOf( 'http://' ) ) { window.location = result.redirect; } else { @@ -614,7 +615,8 @@ jQuery( function( $ ) { init: function() { $( document.body ).on( 'click', 'a.showcoupon', this.show_coupon_form ); $( document.body ).on( 'click', '.woocommerce-remove-coupon', this.remove_coupon ); - $( 'form.checkout_coupon' ).hide().on( 'submit', this.submit ); + $( document.body ).on( 'blur change input', '#coupon_code', this.remove_coupon_error ); + $( 'form.checkout_coupon' ).hide().on( 'submit', this.submit.bind( this ) ); }, show_coupon_form: function() { $( '.checkout_coupon' ).slideToggle( 400, function() { @@ -622,8 +624,36 @@ jQuery( function( $ ) { }); return false; }, - submit: function() { - var $form = $( this ); + show_coupon_error: function( html_element, $target ) { + if ( $target.length === 0 ) { + return; + } + + var msg = $( $.parseHTML( html_element ) ).text().trim(); + + if ( msg === '' ) { + return; + } + + $target.find( '#coupon_code' ) + .focus() + .addClass( 'has-error' ) + .attr( 'aria-invalid', 'true' ) + .attr( 'aria-describedby', 'coupon-error-notice' ); + $target.append( '' + msg + '' ); + }, + remove_coupon_error: function( evt ) { + $( evt.currentTarget ) + .removeClass( 'has-error' ) + .removeAttr( 'aria-invalid' ) + .removeAttr( 'aria-describedby' ) + .next( '.coupon-error-notice' ) + .remove(); + }, + submit: function( evt ) { + var $form = $( evt.currentTarget ); + var $coupon_field = $form.find( '#coupon_code' ); + var self = this; if ( $form.is( '.processing' ) ) { return false; @@ -647,13 +677,20 @@ jQuery( function( $ ) { type: 'POST', url: wc_checkout_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'apply_coupon' ), data: data, - success: function( code ) { + success: function( response ) { $( '.woocommerce-error, .woocommerce-message, .is-error, .is-success' ).remove(); $form.removeClass( 'processing' ).unblock(); - if ( code ) { - $form.before( code ); - $form.slideUp(); + if ( response ) { + // We only want to show coupon notices if they are no errors. + // Coupon errors are shown under the input. + if ( response.indexOf( 'woocommerce-error' ) === -1 && response.indexOf( 'is-error' ) === -1 ) { + $form.slideUp( 400, function() { + $form.before( response ); + } ); + } else { + self.show_coupon_error( response, $coupon_field.parent() ); + } $( document.body ).trigger( 'applied_coupon_in_checkout', [ data.coupon_code ] ); $( document.body ).trigger( 'update_checkout', { update_shipping_method: false } ); @@ -699,6 +736,7 @@ jQuery( function( $ ) { // Remove coupon code from coupon field $( 'form.checkout_coupon' ).find( 'input[name="coupon_code"]' ).val( '' ); + $( 'form.checkout_coupon' ).slideUp(); } }, error: function ( jqXHR ) { diff --git a/plugins/woocommerce/client/legacy/js/frontend/order-attribution.js b/plugins/woocommerce/client/legacy/js/frontend/order-attribution.js index 3f4667548fc..170677f88b7 100644 --- a/plugins/woocommerce/client/legacy/js/frontend/order-attribution.js +++ b/plugins/woocommerce/client/legacy/js/frontend/order-attribution.js @@ -57,7 +57,7 @@ } /** - * Determin whether sourcebuster.js is available. + * Determine whether sourcebuster.js is available. * * @returns {boolean} Whether sourcebuster.js is available. */ diff --git a/plugins/woocommerce/client/legacy/js/frontend/single-product.js b/plugins/woocommerce/client/legacy/js/frontend/single-product.js index 8d47eada4fc..714ce096cfb 100644 --- a/plugins/woocommerce/client/legacy/js/frontend/single-product.js +++ b/plugins/woocommerce/client/legacy/js/frontend/single-product.js @@ -127,6 +127,8 @@ jQuery( function( $ ) { this.onResetSlidePosition = this.onResetSlidePosition.bind( this ); this.getGalleryItems = this.getGalleryItems.bind( this ); this.openPhotoswipe = this.openPhotoswipe.bind( this ); + this.trapFocusPhotoswipe = this.trapFocusPhotoswipe.bind( this ); + this.handlePswpTrapFocus = this.handlePswpTrapFocus.bind( this ); if ( this.flexslider_enabled ) { this.initFlexslider( args.flexslider ); @@ -312,8 +314,10 @@ jQuery( function( $ ) { e.preventDefault(); var pswpElement = $( '.pswp' )[0], - items = this.getGalleryItems(), - eventTarget = $( e.target ), + items = this.getGalleryItems(), + eventTarget = $( e.target ), + currentTarget = e.currentTarget, + self = this, clicked; if ( 0 < eventTarget.closest( '.woocommerce-product-gallery__trigger' ).length ) { @@ -331,14 +335,73 @@ jQuery( function( $ ) { } captionEl.children[0].textContent = item.title; return true; - } + }, + timeToIdle: 0, // Ensure the gallery controls are always visible to avoid keyboard navigation issues. }, wc_single_product_params.photoswipe_options ); // Initializes and opens PhotoSwipe. var photoswipe = new PhotoSwipe( pswpElement, PhotoSwipeUI_Default, items, options ); + + photoswipe.listen( 'afterInit', function() { + self.trapFocusPhotoswipe( true ); + }); + + photoswipe.listen( 'close', function() { + self.trapFocusPhotoswipe( false ); + currentTarget.focus(); + }); + photoswipe.init(); }; + /** + * Control focus in photoswipe modal. + * + * @param {boolean} trapFocus - Whether to trap focus or not. + */ + ProductGallery.prototype.trapFocusPhotoswipe = function( trapFocus ) { + var pswp = document.querySelector( '.pswp' ); + + if ( ! pswp ) { + return; + } + + if ( trapFocus ) { + pswp.addEventListener( 'keydown', this.handlePswpTrapFocus ); + } else { + pswp.removeEventListener( 'keydown', this.handlePswpTrapFocus ); + } + }; + + /** + * Handle keydown event in photoswipe modal. + */ + ProductGallery.prototype.handlePswpTrapFocus = function( e ) { + var allFocusablesEls = e.currentTarget.querySelectorAll( 'button:not([disabled])' ); + var filteredFocusablesEls = Array.from( allFocusablesEls ).filter( function( btn ) { + return btn.style.display !== 'none' && window.getComputedStyle( btn ).display !== 'none'; + } ); + + if ( 1 >= filteredFocusablesEls.length ) { + return; + } + + var firstTabStop = filteredFocusablesEls[0]; + var lastTabStop = filteredFocusablesEls[filteredFocusablesEls.length - 1]; + + if ( e.key === 'Tab' ) { + if ( e.shiftKey ) { + if ( document.activeElement === firstTabStop ) { + e.preventDefault(); + lastTabStop.focus(); + } + } else if ( document.activeElement === lastTabStop ) { + e.preventDefault(); + firstTabStop.focus(); + } + } + }; + /** * Function to call wc_product_gallery on jquery selector. */ diff --git a/plugins/woocommerce/client/legacy/js/frontend/woocommerce.js b/plugins/woocommerce/client/legacy/js/frontend/woocommerce.js index c3190ac687b..fd4abe3ac21 100644 --- a/plugins/woocommerce/client/legacy/js/frontend/woocommerce.js +++ b/plugins/woocommerce/client/legacy/js/frontend/woocommerce.js @@ -1,20 +1,20 @@ /* global Cookies */ -jQuery( function( $ ) { +jQuery( function ( $ ) { // Orderby - $( '.woocommerce-ordering' ).on( 'change', 'select.orderby', function() { + $( '.woocommerce-ordering' ).on( 'change', 'select.orderby', function () { $( this ).closest( 'form' ).trigger( 'submit' ); - }); + } ); // Target quantity inputs on product pages - $( 'input.qty:not(.product-quantity input.qty)' ).each( function() { + $( 'input.qty:not(.product-quantity input.qty)' ).each( function () { var min = parseFloat( $( this ).attr( 'min' ) ); if ( min >= 0 && parseFloat( $( this ).val() ) < min ) { $( this ).val( min ); } - }); + } ); - var noticeID = $( '.woocommerce-store-notice' ).data( 'noticeId' ) || '', + var noticeID = $( '.woocommerce-store-notice' ).data( 'noticeId' ) || '', cookieName = 'store_notice' + noticeID; // Check the value of that cookie and show/hide the notice accordingly @@ -25,43 +25,56 @@ jQuery( function( $ ) { } // Set a cookie and hide the store notice when the dismiss button is clicked - $( '.woocommerce-store-notice__dismiss-link' ).on( 'click', function( event ) { - Cookies.set( cookieName, 'hidden', { path: '/' } ); - $( '.woocommerce-store-notice' ).hide(); - event.preventDefault(); - }); + $( '.woocommerce-store-notice__dismiss-link' ).on( + 'click', + function ( event ) { + Cookies.set( cookieName, 'hidden', { path: '/' } ); + $( '.woocommerce-store-notice' ).hide(); + event.preventDefault(); + } + ); // Make form field descriptions toggle on focus. if ( $( '.woocommerce-input-wrapper span.description' ).length ) { - $( document.body ).on( 'click', function() { - $( '.woocommerce-input-wrapper span.description:visible' ).prop( 'aria-hidden', true ).slideUp( 250 ); + $( document.body ).on( 'click', function () { + $( '.woocommerce-input-wrapper span.description:visible' ) + .prop( 'aria-hidden', true ) + .slideUp( 250 ); } ); } - $( '.woocommerce-input-wrapper' ).on( 'click', function( event ) { + $( '.woocommerce-input-wrapper' ).on( 'click', function ( event ) { event.stopPropagation(); } ); $( '.woocommerce-input-wrapper :input' ) - .on( 'keydown', function( event ) { - var input = $( this ), - parent = input.parent(), + .on( 'keydown', function ( event ) { + var input = $( this ), + parent = input.parent(), description = parent.find( 'span.description' ); - if ( 27 === event.which && description.length && description.is( ':visible' ) ) { + if ( + 27 === event.which && + description.length && + description.is( ':visible' ) + ) { description.prop( 'aria-hidden', true ).slideUp( 250 ); event.preventDefault(); return false; } } ) - .on( 'click focus', function() { - var input = $( this ), - parent = input.parent(), + .on( 'click focus', function () { + var input = $( this ), + parent = input.parent(), description = parent.find( 'span.description' ); parent.addClass( 'currentTarget' ); - $( '.woocommerce-input-wrapper:not(.currentTarget) span.description:visible' ).prop( 'aria-hidden', true ).slideUp( 250 ); + $( + '.woocommerce-input-wrapper:not(.currentTarget) span.description:visible' + ) + .prop( 'aria-hidden', true ) + .slideUp( 250 ); if ( description.length && description.is( ':hidden' ) ) { description.prop( 'aria-hidden', false ).slideDown( 250 ); @@ -71,52 +84,88 @@ jQuery( function( $ ) { } ); // Common scroll to element code. - $.scroll_to_notices = function( scrollElement ) { + $.scroll_to_notices = function ( scrollElement ) { if ( scrollElement.length ) { - $( 'html, body' ).animate( { - scrollTop: ( scrollElement.offset().top - 100 ) - }, 1000 ); + $( 'html, body' ).animate( + { + scrollTop: scrollElement.offset().top - 100, + }, + 1000 + ); } }; // Show password visibility hover icon on woocommerce forms - $( '.woocommerce form .woocommerce-Input[type="password"]' ).wrap( '' ); + $( '.woocommerce form .woocommerce-Input[type="password"]' ).wrap( + '' + ); // Add 'password-input' class to the password wrapper in checkout page. - $( '.woocommerce form input' ).filter(':password').parent('span').addClass('password-input'); - $( '.password-input' ).append( '' ); - - $( '.show-password-input' ).on( 'click', - function() { - if ( $( this ).hasClass( 'display-password' ) ) { - $( this ).removeClass( 'display-password' ); - } else { - $( this ).addClass( 'display-password' ); - } - if ( $( this ).hasClass( 'display-password' ) ) { - $( this ).siblings( ['input[type="password"]'] ).prop( 'type', 'text' ); - } else { - $( this ).siblings( 'input[type="text"]' ).prop( 'type', 'password' ); - } - } + $( '.woocommerce form input' ) + .filter( ':password' ) + .parent( 'span' ) + .addClass( 'password-input' ); + $( '.password-input' ).append( + '' ); + $( '.show-password-input' ).on( 'click', function () { + if ( $( this ).hasClass( 'display-password' ) ) { + $( this ).removeClass( 'display-password' ); + } else { + $( this ).addClass( 'display-password' ); + } + if ( $( this ).hasClass( 'display-password' ) ) { + $( this ) + .siblings( [ 'input[type="password"]' ] ) + .prop( 'type', 'text' ); + } else { + $( this ) + .siblings( 'input[type="text"]' ) + .prop( 'type', 'password' ); + } + } ); - $( 'a.coming-soon-footer-banner-dismiss' ).on( 'click', function( e ) { + $( 'a.coming-soon-footer-banner-dismiss' ).on( 'click', function ( e ) { var target = $( e.target ); $.ajax( { type: 'post', url: target.data( 'rest-url' ), data: { - meta: { - 'woocommerce_coming_soon_banner_dismissed': 'yes' - } + woocommerce_meta: { + coming_soon_banner_dismissed: 'yes', + }, }, beforeSend: function ( xhr ) { - xhr.setRequestHeader( 'X-WP-Nonce', target.data( 'rest-nonce' ) ); + xhr.setRequestHeader( + 'X-WP-Nonce', + target.data( 'rest-nonce' ) + ); }, complete: function () { - $('#coming-soon-footer-banner').hide(); - } + $( '#coming-soon-footer-banner' ).hide(); + }, } ); } ); }); + +document.addEventListener( 'DOMContentLoaded' , function() { + var noticeClasses = [ 'woocommerce-message', 'woocommerce-error', 'wc-block-components-notice-banner' ]; + var noticeSelectors = noticeClasses.map( function( className ) { + return '.' + className + '[role="alert"]'; + } ).join( ', ' ); + var noticeElements = document.querySelectorAll( noticeSelectors ); + + if ( noticeElements.length === 0 ) { + return; + } + + var firstNotice = noticeElements[0]; + + firstNotice.setAttribute( 'tabindex', '-1' ); + + // Wait for the element to get the tabindex attribute so it can be focused. + var delayFocusNoticeId = setTimeout( function() { + firstNotice.focus(); + clearTimeout( delayFocusNoticeId ); + }, 500 ); +} ); diff --git a/plugins/woocommerce/client/legacy/js/jquery-blockui/jquery.blockUI.js b/plugins/woocommerce/client/legacy/js/jquery-blockui/jquery.blockUI.js index b2cba9e0678..229b6cf1ed9 100644 --- a/plugins/woocommerce/client/legacy/js/jquery-blockui/jquery.blockUI.js +++ b/plugins/woocommerce/client/legacy/js/jquery-blockui/jquery.blockUI.js @@ -191,7 +191,7 @@ // enable if you want key and mouse events to be disabled for content that is blocked bindEvents: true, - // be default blockUI will supress tab navigation from leaving blocking content + // by default blockUI will suppress tab navigation from leaving blocking content // (if bindEvents is true) constrainTabKey: true, @@ -286,7 +286,7 @@ var z = opts.baseZ; // blockUI uses 3 layers for blocking, for simplicity they are all used on every platform; - // layer1 is the iframe layer which is used to supress bleed through of underlying content + // layer1 is the iframe layer which is used to suppress bleed through of underlying content // layer2 is the overlay layer which has opacity and a wait cursor (by default) // layer3 is the message content that is displayed while blocking var lyr1, lyr2, lyr3, s; diff --git a/plugins/woocommerce/client/legacy/js/photoswipe/photoswipe-ui-default.js b/plugins/woocommerce/client/legacy/js/photoswipe/photoswipe-ui-default.js index f3733e1bc7a..5cb38042b4d 100755 --- a/plugins/woocommerce/client/legacy/js/photoswipe/photoswipe-ui-default.js +++ b/plugins/woocommerce/client/legacy/js/photoswipe/photoswipe-ui-default.js @@ -705,7 +705,7 @@ var PhotoSwipeUI_Default = }, 50); } - // toogle pswp--fs class on root element + // toggle pswp--fs class on root element framework[ (_fullscrenAPI.isFullscreen() ? 'add' : 'remove') + 'Class' ](pswp.template, 'pswp--fs'); }; diff --git a/plugins/woocommerce/client/legacy/js/select2/select2.full.js b/plugins/woocommerce/client/legacy/js/select2/select2.full.js new file mode 100644 index 00000000000..e750834ef5d --- /dev/null +++ b/plugins/woocommerce/client/legacy/js/select2/select2.full.js @@ -0,0 +1,6436 @@ +/*! + * Select2 4.0.3 + * https://select2.github.io + * + * Released under the MIT license + * https://github.com/select2/select2/blob/master/LICENSE.md + */ +(function (factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['jquery'], factory); + } else if (typeof exports === 'object') { + // Node/CommonJS + factory(require('jquery')); + } else { + // Browser globals + factory(jQuery); + } +}(function (jQuery) { + // This is needed so we can catch the AMD loader configuration and use it + // The inner file should be wrapped (by `banner.start.js`) in a function that + // returns the AMD loader references. + var S2 = +(function () { + // Restore the Select2 AMD loader so it can be used + // Needed mostly in the language files, where the loader is not inserted + if (jQuery && jQuery.fn && jQuery.fn.select2 && jQuery.fn.select2.amd) { + var S2 = jQuery.fn.select2.amd; + } +var S2;(function () { if (!S2 || !S2.requirejs) { +if (!S2) { S2 = {}; } else { require = S2; } +/** + * @license almond 0.3.1 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved. + * Available via the MIT or new BSD license. + * see: http://github.com/jrburke/almond for details + */ +//Going sloppy to avoid 'use strict' string cost, but strict practices should +//be followed. +/*jslint sloppy: true */ +/*global setTimeout: false */ + +var requirejs, require, define; +(function (undef) { + var main, req, makeMap, handlers, + defined = {}, + waiting = {}, + config = {}, + defining = {}, + hasOwn = Object.prototype.hasOwnProperty, + aps = [].slice, + jsSuffixRegExp = /\.js$/; + + function hasProp(obj, prop) { + return hasOwn.call(obj, prop); + } + + /** + * Given a relative module name, like ./something, normalize it to + * a real name that can be mapped to a path. + * @param {String} name the relative name + * @param {String} baseName a real name that the name arg is relative + * to. + * @returns {String} normalized name + */ + function normalize(name, baseName) { + var nameParts, nameSegment, mapValue, foundMap, lastIndex, + foundI, foundStarMap, starI, i, j, part, + baseParts = baseName && baseName.split("/"), + map = config.map, + starMap = (map && map['*']) || {}; + + //Adjust any relative paths. + if (name && name.charAt(0) === ".") { + //If have a base name, try to normalize against it, + //otherwise, assume it is a top-level require that will + //be relative to baseUrl in the end. + if (baseName) { + name = name.split('/'); + lastIndex = name.length - 1; + + // Node .js allowance: + if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) { + name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, ''); + } + + //Lop off the last part of baseParts, so that . matches the + //"directory" and not name of the baseName's module. For instance, + //baseName of "one/two/three", maps to "one/two/three.js", but we + //want the directory, "one/two" for this normalization. + name = baseParts.slice(0, baseParts.length - 1).concat(name); + + //start trimDots + for (i = 0; i < name.length; i += 1) { + part = name[i]; + if (part === ".") { + name.splice(i, 1); + i -= 1; + } else if (part === "..") { + if (i === 1 && (name[2] === '..' || name[0] === '..')) { + //End of the line. Keep at least one non-dot + //path segment at the front so it can be mapped + //correctly to disk. Otherwise, there is likely + //no path mapping for a path starting with '..'. + //This can still fail, but catches the most reasonable + //uses of .. + break; + } else if (i > 0) { + name.splice(i - 1, 2); + i -= 2; + } + } + } + //end trimDots + + name = name.join("/"); + } else if (name.indexOf('./') === 0) { + // No baseName, so this is ID is resolved relative + // to baseUrl, pull off the leading dot. + name = name.substring(2); + } + } + + //Apply map config if available. + if ((baseParts || starMap) && map) { + nameParts = name.split('/'); + + for (i = nameParts.length; i > 0; i -= 1) { + nameSegment = nameParts.slice(0, i).join("/"); + + if (baseParts) { + //Find the longest baseName segment match in the config. + //So, do joins on the biggest to smallest lengths of baseParts. + for (j = baseParts.length; j > 0; j -= 1) { + mapValue = map[baseParts.slice(0, j).join('/')]; + + //baseName segment has config, find if it has one for + //this name. + if (mapValue) { + mapValue = mapValue[nameSegment]; + if (mapValue) { + //Match, update name to the new value. + foundMap = mapValue; + foundI = i; + break; + } + } + } + } + + if (foundMap) { + break; + } + + //Check for a star map match, but just hold on to it, + //if there is a shorter segment match later in a matching + //config, then favor over this star map. + if (!foundStarMap && starMap && starMap[nameSegment]) { + foundStarMap = starMap[nameSegment]; + starI = i; + } + } + + if (!foundMap && foundStarMap) { + foundMap = foundStarMap; + foundI = starI; + } + + if (foundMap) { + nameParts.splice(0, foundI, foundMap); + name = nameParts.join('/'); + } + } + + return name; + } + + function makeRequire(relName, forceSync) { + return function () { + //A version of a require function that passes a moduleName + //value for items that may need to + //look up paths relative to the moduleName + var args = aps.call(arguments, 0); + + //If first arg is not require('string'), and there is only + //one arg, it is the array form without a callback. Insert + //a null so that the following concat is correct. + if (typeof args[0] !== 'string' && args.length === 1) { + args.push(null); + } + return req.apply(undef, args.concat([relName, forceSync])); + }; + } + + function makeNormalize(relName) { + return function (name) { + return normalize(name, relName); + }; + } + + function makeLoad(depName) { + return function (value) { + defined[depName] = value; + }; + } + + function callDep(name) { + if (hasProp(waiting, name)) { + var args = waiting[name]; + delete waiting[name]; + defining[name] = true; + main.apply(undef, args); + } + + if (!hasProp(defined, name) && !hasProp(defining, name)) { + throw new Error('No ' + name); + } + return defined[name]; + } + + //Turns a plugin!resource to [plugin, resource] + //with the plugin being undefined if the name + //did not have a plugin prefix. + function splitPrefix(name) { + var prefix, + index = name ? name.indexOf('!') : -1; + if (index > -1) { + prefix = name.substring(0, index); + name = name.substring(index + 1, name.length); + } + return [prefix, name]; + } + + /** + * Makes a name map, normalizing the name, and using a plugin + * for normalization if necessary. Grabs a ref to plugin + * too, as an optimization. + */ + makeMap = function (name, relName) { + var plugin, + parts = splitPrefix(name), + prefix = parts[0]; + + name = parts[1]; + + if (prefix) { + prefix = normalize(prefix, relName); + plugin = callDep(prefix); + } + + //Normalize according + if (prefix) { + if (plugin && plugin.normalize) { + name = plugin.normalize(name, makeNormalize(relName)); + } else { + name = normalize(name, relName); + } + } else { + name = normalize(name, relName); + parts = splitPrefix(name); + prefix = parts[0]; + name = parts[1]; + if (prefix) { + plugin = callDep(prefix); + } + } + + //Using ridiculous property names for space reasons + return { + f: prefix ? prefix + '!' + name : name, //fullName + n: name, + pr: prefix, + p: plugin + }; + }; + + function makeConfig(name) { + return function () { + return (config && config.config && config.config[name]) || {}; + }; + } + + handlers = { + require: function (name) { + return makeRequire(name); + }, + exports: function (name) { + var e = defined[name]; + if (typeof e !== 'undefined') { + return e; + } else { + return (defined[name] = {}); + } + }, + module: function (name) { + return { + id: name, + uri: '', + exports: defined[name], + config: makeConfig(name) + }; + } + }; + + main = function (name, deps, callback, relName) { + var cjsModule, depName, ret, map, i, + args = [], + callbackType = typeof callback, + usingExports; + + //Use name if no relName + relName = relName || name; + + //Call the callback to define the module, if necessary. + if (callbackType === 'undefined' || callbackType === 'function') { + //Pull out the defined dependencies and pass the ordered + //values to the callback. + //Default to [require, exports, module] if no deps + deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps; + for (i = 0; i < deps.length; i += 1) { + map = makeMap(deps[i], relName); + depName = map.f; + + //Fast path CommonJS standard dependencies. + if (depName === "require") { + args[i] = handlers.require(name); + } else if (depName === "exports") { + //CommonJS module spec 1.1 + args[i] = handlers.exports(name); + usingExports = true; + } else if (depName === "module") { + //CommonJS module spec 1.1 + cjsModule = args[i] = handlers.module(name); + } else if (hasProp(defined, depName) || + hasProp(waiting, depName) || + hasProp(defining, depName)) { + args[i] = callDep(depName); + } else if (map.p) { + map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {}); + args[i] = defined[depName]; + } else { + throw new Error(name + ' missing ' + depName); + } + } + + ret = callback ? callback.apply(defined[name], args) : undefined; + + if (name) { + //If setting exports via "module" is in play, + //favor that over return value and exports. After that, + //favor a non-undefined return value over exports use. + if (cjsModule && cjsModule.exports !== undef && + cjsModule.exports !== defined[name]) { + defined[name] = cjsModule.exports; + } else if (ret !== undef || !usingExports) { + //Use the return value from the function. + defined[name] = ret; + } + } + } else if (name) { + //May just be an object definition for the module. Only + //worry about defining if have a module name. + defined[name] = callback; + } + }; + + requirejs = require = req = function (deps, callback, relName, forceSync, alt) { + if (typeof deps === "string") { + if (handlers[deps]) { + //callback in this case is really relName + return handlers[deps](callback); + } + //Just return the module wanted. In this scenario, the + //deps arg is the module name, and second arg (if passed) + //is just the relName. + //Normalize module name, if it contains . or .. + return callDep(makeMap(deps, callback).f); + } else if (!deps.splice) { + //deps is a config object, not an array. + config = deps; + if (config.deps) { + req(config.deps, config.callback); + } + if (!callback) { + return; + } + + if (callback.splice) { + //callback is an array, which means it is a dependency list. + //Adjust args if there are dependencies + deps = callback; + callback = relName; + relName = null; + } else { + deps = undef; + } + } + + //Support require(['a']) + callback = callback || function () {}; + + //If relName is a function, it is an errback handler, + //so remove it. + if (typeof relName === 'function') { + relName = forceSync; + forceSync = alt; + } + + //Simulate async callback; + if (forceSync) { + main(undef, deps, callback, relName); + } else { + //Using a non-zero value because of concern for what old browsers + //do, and latest browsers "upgrade" to 4 if lower value is used: + //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout: + //If want a value immediately, use require('id') instead -- something + //that works in almond on the global level, but not guaranteed and + //unlikely to work in other AMD implementations. + setTimeout(function () { + main(undef, deps, callback, relName); + }, 4); + } + + return req; + }; + + /** + * Just drops the config on the floor, but returns req in case + * the config return value is used. + */ + req.config = function (cfg) { + return req(cfg); + }; + + /** + * Expose module registry for debugging and tooling + */ + requirejs._defined = defined; + + define = function (name, deps, callback) { + if (typeof name !== 'string') { + throw new Error('See almond README: incorrect module build, no module name'); + } + + //This module may not have dependencies + if (!deps.splice) { + //deps is not an array, so probably means + //an object literal or factory function for + //the value. Adjust args. + callback = deps; + deps = []; + } + + if (!hasProp(defined, name) && !hasProp(waiting, name)) { + waiting[name] = [name, deps, callback]; + } + }; + + define.amd = { + jQuery: true + }; +}()); + +S2.requirejs = requirejs;S2.require = require;S2.define = define; +} +}()); +S2.define("almond", function(){}); + +/* global jQuery:false, $:false */ +S2.define('jquery',[],function () { + var _$ = jQuery || $; + + if (_$ == null && console && console.error) { + console.error( + 'Select2: An instance of jQuery or a jQuery-compatible library was not ' + + 'found. Make sure that you are including jQuery before Select2 on your ' + + 'web page.' + ); + } + + return _$; +}); + +S2.define('select2/utils',[ + 'jquery' +], function ($) { + var Utils = {}; + + Utils.Extend = function (ChildClass, SuperClass) { + var __hasProp = {}.hasOwnProperty; + + function BaseConstructor () { + this.constructor = ChildClass; + } + + for (var key in SuperClass) { + if (__hasProp.call(SuperClass, key)) { + ChildClass[key] = SuperClass[key]; + } + } + + BaseConstructor.prototype = SuperClass.prototype; + ChildClass.prototype = new BaseConstructor(); + ChildClass.__super__ = SuperClass.prototype; + + return ChildClass; + }; + + function getMethods (theClass) { + var proto = theClass.prototype; + + var methods = []; + + for (var methodName in proto) { + var m = proto[methodName]; + + if (typeof m !== 'function') { + continue; + } + + if (methodName === 'constructor') { + continue; + } + + methods.push(methodName); + } + + return methods; + } + + Utils.Decorate = function (SuperClass, DecoratorClass) { + var decoratedMethods = getMethods(DecoratorClass); + var superMethods = getMethods(SuperClass); + + function DecoratedClass () { + var unshift = Array.prototype.unshift; + + var argCount = DecoratorClass.prototype.constructor.length; + + var calledConstructor = SuperClass.prototype.constructor; + + if (argCount > 0) { + unshift.call(arguments, SuperClass.prototype.constructor); + + calledConstructor = DecoratorClass.prototype.constructor; + } + + calledConstructor.apply(this, arguments); + } + + DecoratorClass.displayName = SuperClass.displayName; + + function ctr () { + this.constructor = DecoratedClass; + } + + DecoratedClass.prototype = new ctr(); + + for (var m = 0; m < superMethods.length; m++) { + var superMethod = superMethods[m]; + + DecoratedClass.prototype[superMethod] = + SuperClass.prototype[superMethod]; + } + + var calledMethod = function (methodName) { + // Stub out the original method if it's not decorating an actual method + var originalMethod = function () {}; + + if (methodName in DecoratedClass.prototype) { + originalMethod = DecoratedClass.prototype[methodName]; + } + + var decoratedMethod = DecoratorClass.prototype[methodName]; + + return function () { + var unshift = Array.prototype.unshift; + + unshift.call(arguments, originalMethod); + + return decoratedMethod.apply(this, arguments); + }; + }; + + for (var d = 0; d < decoratedMethods.length; d++) { + var decoratedMethod = decoratedMethods[d]; + + DecoratedClass.prototype[decoratedMethod] = calledMethod(decoratedMethod); + } + + return DecoratedClass; + }; + + var Observable = function () { + this.listeners = {}; + }; + + Observable.prototype.on = function (event, callback) { + this.listeners = this.listeners || {}; + + if (event in this.listeners) { + this.listeners[event].push(callback); + } else { + this.listeners[event] = [callback]; + } + }; + + Observable.prototype.trigger = function (event) { + var slice = Array.prototype.slice; + var params = slice.call(arguments, 1); + + this.listeners = this.listeners || {}; + + // Params should always come in as an array + if (params == null) { + params = []; + } + + // If there are no arguments to the event, use a temporary object + if (params.length === 0) { + params.push({}); + } + + // Set the `_type` of the first object to the event + params[0]._type = event; + + if (event in this.listeners) { + this.invoke(this.listeners[event], slice.call(arguments, 1)); + } + + if ('*' in this.listeners) { + this.invoke(this.listeners['*'], arguments); + } + }; + + Observable.prototype.invoke = function (listeners, params) { + for (var i = 0, len = listeners.length; i < len; i++) { + listeners[i].apply(this, params); + } + }; + + Utils.Observable = Observable; + + Utils.generateChars = function (length) { + var chars = ''; + + for (var i = 0; i < length; i++) { + var randomChar = Math.floor(Math.random() * 36); + chars += randomChar.toString(36); + } + + return chars; + }; + + Utils.bind = function (func, context) { + return function () { + func.apply(context, arguments); + }; + }; + + Utils._convertData = function (data) { + for (var originalKey in data) { + var keys = originalKey.split('-'); + + var dataLevel = data; + + if (keys.length === 1) { + continue; + } + + for (var k = 0; k < keys.length; k++) { + var key = keys[k]; + + // Lowercase the first letter + // By default, dash-separated becomes camelCase + key = key.substring(0, 1).toLowerCase() + key.substring(1); + + if (!(key in dataLevel)) { + dataLevel[key] = {}; + } + + if (k == keys.length - 1) { + dataLevel[key] = data[originalKey]; + } + + dataLevel = dataLevel[key]; + } + + delete data[originalKey]; + } + + return data; + }; + + Utils.hasScroll = function (index, el) { + // Adapted from the function created by @ShadowScripter + // and adapted by @BillBarry on the Stack Exchange Code Review website. + // The original code can be found at + // http://codereview.stackexchange.com/q/13338 + // and was designed to be used with the Sizzle selector engine. + + var $el = $(el); + var overflowX = el.style.overflowX; + var overflowY = el.style.overflowY; + + //Check both x and y declarations + if (overflowX === overflowY && + (overflowY === 'hidden' || overflowY === 'visible')) { + return false; + } + + if (overflowX === 'scroll' || overflowY === 'scroll') { + return true; + } + + return ($el.innerHeight() < el.scrollHeight || + $el.innerWidth() < el.scrollWidth); + }; + + Utils.escapeMarkup = function (markup) { + var replaceMap = { + '\\': '\', + '&': '&', + '<': '<', + '>': '>', + '"': '"', + '\'': ''', + '/': '/' + }; + + // Do not try to escape the markup if it's not a string + if (typeof markup !== 'string') { + return markup; + } + + return String(markup).replace(/[&<>"'\/\\]/g, function (match) { + return replaceMap[match]; + }); + }; + + // Append an array of jQuery nodes to a given element. + Utils.appendMany = function ($element, $nodes) { + // jQuery 1.7.x does not support $.fn.append() with an array + // Fall back to a jQuery object collection using $.fn.add() + if ($.fn.jquery.substr(0, 3) === '1.7') { + var $jqNodes = $(); + + $.map($nodes, function (node) { + $jqNodes = $jqNodes.add(node); + }); + + $nodes = $jqNodes; + } + + $element.append($nodes); + }; + + return Utils; +}); + +S2.define('select2/results',[ + 'jquery', + './utils' +], function ($, Utils) { + function Results ($element, options, dataAdapter) { + this.$element = $element; + this.data = dataAdapter; + this.options = options; + + Results.__super__.constructor.call(this); + } + + Utils.Extend(Results, Utils.Observable); + + Results.prototype.render = function () { + var $results = $( + '
    ' + ); + + if (this.options.get('multiple')) { + $results.attr('aria-multiselectable', 'true'); + } + + this.$results = $results; + + return $results; + }; + + Results.prototype.clear = function () { + this.$results.empty(); + }; + + Results.prototype.displayMessage = function (params) { + var escapeMarkup = this.options.get('escapeMarkup'); + + this.clear(); + this.hideLoading(); + + var $message = $( + '
  • ' + ); + + var message = this.options.get('translations').get(params.message); + + $message.append( + escapeMarkup( + message(params.args) + ) + ); + + $message[0].className += ' select2-results__message'; + + this.$results.append($message); + }; + + Results.prototype.hideMessages = function () { + this.$results.find('.select2-results__message').remove(); + }; + + Results.prototype.append = function (data) { + this.hideLoading(); + + var $options = []; + + if (data.results == null || data.results.length === 0) { + if (this.$results.children().length === 0) { + this.trigger('results:message', { + message: 'noResults' + }); + } + + return; + } + + data.results = this.sort(data.results); + + for (var d = 0; d < data.results.length; d++) { + var item = data.results[d]; + + var $option = this.option(item); + + $options.push($option); + } + + this.$results.append($options); + }; + + Results.prototype.position = function ($results, $dropdown) { + var $resultsContainer = $dropdown.find('.select2-results'); + $resultsContainer.append($results); + }; + + Results.prototype.sort = function (data) { + var sorter = this.options.get('sorter'); + + return sorter(data); + }; + + Results.prototype.highlightFirstItem = function () { + var $options = this.$results + .find('.select2-results__option[aria-selected]'); + + var $selected = $options.filter('[aria-selected=true]'); + + // Check if there are any selected options + if ($selected.length > 0) { + // If there are selected options, highlight the first + $selected.first().trigger('mouseenter'); + } else { + // If there are no selected options, highlight the first option + // in the dropdown + $options.first().trigger('mouseenter'); + } + + this.ensureHighlightVisible(); + }; + + Results.prototype.setClasses = function () { + var self = this; + + this.data.current(function (selected) { + var selectedIds = $.map(selected, function (s) { + return s.id.toString(); + }); + + var $options = self.$results + .find('.select2-results__option[aria-selected]'); + + $options.each(function () { + var $option = $(this); + + var item = $.data(this, 'data'); + + // id needs to be converted to a string when comparing + var id = '' + item.id; + + if ((item.element != null && item.element.selected) || + (item.element == null && $.inArray(id, selectedIds) > -1)) { + $option.attr('aria-selected', 'true'); + } else { + $option.attr('aria-selected', 'false'); + } + }); + + }); + }; + + Results.prototype.showLoading = function (params) { + this.hideLoading(); + + var loadingMore = this.options.get('translations').get('searching'); + + var loading = { + disabled: true, + loading: true, + text: loadingMore(params) + }; + var $loading = this.option(loading); + $loading.className += ' loading-results'; + + this.$results.prepend($loading); + }; + + Results.prototype.hideLoading = function () { + this.$results.find('.loading-results').remove(); + }; + + Results.prototype.option = function (data) { + var option = document.createElement('li'); + option.className = 'select2-results__option'; + + var attrs = { + 'role': 'treeitem', + 'aria-selected': 'false' + }; + + if (data.disabled) { + delete attrs['aria-selected']; + attrs['aria-disabled'] = 'true'; + } + + if (data.id == null) { + delete attrs['aria-selected']; + } + + if (data._resultId != null) { + option.id = data._resultId; + } + + if (data.title) { + option.title = data.title; + } + + if (data.children) { + attrs.role = 'group'; + attrs['aria-label'] = data.text; + delete attrs['aria-selected']; + } + + for (var attr in attrs) { + var val = attrs[attr]; + + option.setAttribute(attr, val); + } + + if (data.children) { + var $option = $(option); + + var label = document.createElement('strong'); + label.className = 'select2-results__group'; + + var $label = $(label); + this.template(data, label); + + var $children = []; + + for (var c = 0; c < data.children.length; c++) { + var child = data.children[c]; + + var $child = this.option(child); + + $children.push($child); + } + + var $childrenContainer = $('
      ', { + 'class': 'select2-results__options select2-results__options--nested' + }); + + $childrenContainer.append($children); + + $option.append(label); + $option.append($childrenContainer); + } else { + this.template(data, option); + } + + $.data(option, 'data', data); + + return option; + }; + + Results.prototype.bind = function (container, $container) { + var self = this; + + var id = container.id + '-results'; + + this.$results.attr('id', id); + + container.on('results:all', function (params) { + self.clear(); + self.append(params.data); + + if (container.isOpen()) { + self.setClasses(); + self.highlightFirstItem(); + } + }); + + container.on('results:append', function (params) { + self.append(params.data); + + if (container.isOpen()) { + self.setClasses(); + } + }); + + container.on('query', function (params) { + self.hideMessages(); + self.showLoading(params); + }); + + container.on('select', function () { + if (!container.isOpen()) { + return; + } + + self.setClasses(); + self.highlightFirstItem(); + }); + + container.on('unselect', function () { + if (!container.isOpen()) { + return; + } + + self.setClasses(); + self.highlightFirstItem(); + }); + + container.on('open', function () { + // When the dropdown is open, aria-expended="true" + self.$results.attr('aria-expanded', 'true'); + self.$results.attr('aria-hidden', 'false'); + + self.setClasses(); + self.ensureHighlightVisible(); + }); + + container.on('close', function () { + // When the dropdown is closed, aria-expended="false" + self.$results.attr('aria-expanded', 'false'); + self.$results.attr('aria-hidden', 'true'); + self.$results.removeAttr('aria-activedescendant'); + }); + + container.on('results:toggle', function () { + var $highlighted = self.getHighlightedResults(); + + if ($highlighted.length === 0) { + return; + } + + $highlighted.trigger('mouseup'); + }); + + container.on('results:select', function () { + var $highlighted = self.getHighlightedResults(); + + if ($highlighted.length === 0) { + return; + } + + var data = $highlighted.data('data'); + + if ($highlighted.attr('aria-selected') == 'true') { + self.trigger('close', {}); + } else { + self.trigger('select', { + data: data + }); + } + }); + + container.on('results:previous', function () { + var $highlighted = self.getHighlightedResults(); + + var $options = self.$results.find('[aria-selected]'); + + var currentIndex = $options.index($highlighted); + + // If we are already at te top, don't move further + if (currentIndex === 0) { + return; + } + + var nextIndex = currentIndex - 1; + + // If none are highlighted, highlight the first + if ($highlighted.length === 0) { + nextIndex = 0; + } + + var $next = $options.eq(nextIndex); + + $next.trigger('mouseenter'); + + var currentOffset = self.$results.offset().top; + var nextTop = $next.offset().top; + var nextOffset = self.$results.scrollTop() + (nextTop - currentOffset); + + if (nextIndex === 0) { + self.$results.scrollTop(0); + } else if (nextTop - currentOffset < 0) { + self.$results.scrollTop(nextOffset); + } + }); + + container.on('results:next', function () { + var $highlighted = self.getHighlightedResults(); + + var $options = self.$results.find('[aria-selected]'); + + var currentIndex = $options.index($highlighted); + + var nextIndex = currentIndex + 1; + + // If we are at the last option, stay there + if (nextIndex >= $options.length) { + return; + } + + var $next = $options.eq(nextIndex); + + $next.trigger('mouseenter'); + + var currentOffset = self.$results.offset().top + + self.$results.outerHeight(false); + var nextBottom = $next.offset().top + $next.outerHeight(false); + var nextOffset = self.$results.scrollTop() + nextBottom - currentOffset; + + if (nextIndex === 0) { + self.$results.scrollTop(0); + } else if (nextBottom > currentOffset) { + self.$results.scrollTop(nextOffset); + } + }); + + container.on('results:focus', function (params) { + params.element.addClass('select2-results__option--highlighted'); + }); + + container.on('results:message', function (params) { + self.displayMessage(params); + }); + + if ($.fn.mousewheel) { + this.$results.on('mousewheel', function (e) { + var top = self.$results.scrollTop(); + + var bottom = self.$results.get(0).scrollHeight - top + e.deltaY; + + var isAtTop = e.deltaY > 0 && top - e.deltaY <= 0; + var isAtBottom = e.deltaY < 0 && bottom <= self.$results.height(); + + if (isAtTop) { + self.$results.scrollTop(0); + + e.preventDefault(); + e.stopPropagation(); + } else if (isAtBottom) { + self.$results.scrollTop( + self.$results.get(0).scrollHeight - self.$results.height() + ); + + e.preventDefault(); + e.stopPropagation(); + } + }); + } + + this.$results.on('mouseup', '.select2-results__option[aria-selected]', + function (evt) { + var $this = $(this); + + var data = $this.data('data'); + + if ($this.attr('aria-selected') === 'true') { + if (self.options.get('multiple')) { + self.trigger('unselect', { + originalEvent: evt, + data: data + }); + } else { + self.trigger('close', {}); + } + + return; + } + + self.trigger('select', { + originalEvent: evt, + data: data + }); + }); + + this.$results.on('mouseenter', '.select2-results__option[aria-selected]', + function (evt) { + var data = $(this).data('data'); + + self.getHighlightedResults() + .removeClass('select2-results__option--highlighted'); + + self.trigger('results:focus', { + data: data, + element: $(this) + }); + }); + }; + + Results.prototype.getHighlightedResults = function () { + var $highlighted = this.$results + .find('.select2-results__option--highlighted'); + + return $highlighted; + }; + + Results.prototype.destroy = function () { + this.$results.remove(); + }; + + Results.prototype.ensureHighlightVisible = function () { + var $highlighted = this.getHighlightedResults(); + + if ($highlighted.length === 0) { + return; + } + + var $options = this.$results.find('[aria-selected]'); + + var currentIndex = $options.index($highlighted); + + var currentOffset = this.$results.offset().top; + var nextTop = $highlighted.offset().top; + var nextOffset = this.$results.scrollTop() + (nextTop - currentOffset); + + var offsetDelta = nextTop - currentOffset; + nextOffset -= $highlighted.outerHeight(false) * 2; + + if (currentIndex <= 2) { + this.$results.scrollTop(0); + } else if (offsetDelta > this.$results.outerHeight() || offsetDelta < 0) { + this.$results.scrollTop(nextOffset); + } + }; + + Results.prototype.template = function (result, container) { + var template = this.options.get('templateResult'); + var escapeMarkup = this.options.get('escapeMarkup'); + + var content = template(result, container); + + if (content == null) { + container.style.display = 'none'; + } else if (typeof content === 'string') { + container.innerHTML = escapeMarkup(content); + } else { + $(container).append(content); + } + }; + + return Results; +}); + +S2.define('select2/keys',[ + +], function () { + var KEYS = { + BACKSPACE: 8, + TAB: 9, + ENTER: 13, + SHIFT: 16, + CTRL: 17, + ALT: 18, + ESC: 27, + SPACE: 32, + PAGE_UP: 33, + PAGE_DOWN: 34, + END: 35, + HOME: 36, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40, + DELETE: 46 + }; + + return KEYS; +}); + +S2.define('select2/selection/base',[ + 'jquery', + '../utils', + '../keys' +], function ($, Utils, KEYS) { + function BaseSelection ($element, options) { + this.$element = $element; + this.options = options; + + BaseSelection.__super__.constructor.call(this); + } + + Utils.Extend(BaseSelection, Utils.Observable); + + BaseSelection.prototype.render = function () { + var $selection = $( + '' + ); + + this._tabindex = 0; + + if (this.$element.data('old-tabindex') != null) { + this._tabindex = this.$element.data('old-tabindex'); + } else if (this.$element.attr('tabindex') != null) { + this._tabindex = this.$element.attr('tabindex'); + } + + $selection.attr('title', this.$element.attr('title')); + $selection.attr('tabindex', this._tabindex); + + this.$selection = $selection; + + return $selection; + }; + + BaseSelection.prototype.bind = function (container, $container) { + var self = this; + + var id = container.id + '-container'; + var resultsId = container.id + '-results'; + + this.container = container; + + this.$selection.on('focus', function (evt) { + self.trigger('focus', evt); + }); + + this.$selection.on('blur', function (evt) { + self._handleBlur(evt); + }); + + this.$selection.on('keydown', function (evt) { + self.trigger('keypress', evt); + + if (evt.which === KEYS.SPACE) { + evt.preventDefault(); + } + }); + + container.on('results:focus', function (params) { + self.$selection.attr('aria-activedescendant', params.data._resultId); + }); + + container.on('selection:update', function (params) { + self.update(params.data); + }); + + container.on('open', function () { + // When the dropdown is open, aria-expanded="true" + self.$selection.attr('aria-expanded', 'true'); + self.$selection.attr('aria-owns', resultsId); + + self._attachCloseHandler(container); + }); + + container.on('close', function () { + // When the dropdown is closed, aria-expanded="false" + self.$selection.attr('aria-expanded', 'false'); + self.$selection.removeAttr('aria-activedescendant'); + self.$selection.removeAttr('aria-owns'); + + self.$selection.focus(); + + self._detachCloseHandler(container); + }); + + container.on('enable', function () { + self.$selection.attr('tabindex', self._tabindex); + }); + + container.on('disable', function () { + self.$selection.attr('tabindex', '-1'); + }); + }; + + BaseSelection.prototype._handleBlur = function (evt) { + var self = this; + + // This needs to be delayed as the active element is the body when the tab + // key is pressed, possibly along with others. + window.setTimeout(function () { + // Don't trigger `blur` if the focus is still in the selection + if ( + (document.activeElement == self.$selection[0]) || + ($.contains(self.$selection[0], document.activeElement)) + ) { + return; + } + + self.trigger('blur', evt); + }, 1); + }; + + BaseSelection.prototype._attachCloseHandler = function (container) { + var self = this; + + $(document.body).on('mousedown.select2.' + container.id, function (e) { + var $target = $(e.target); + + var $select = $target.closest('.select2'); + + var $all = $('.select2.select2-container--open'); + + $all.each(function () { + var $this = $(this); + + if (this == $select[0]) { + return; + } + + var $element = $this.data('element'); + + $element.select2('close'); + }); + }); + }; + + BaseSelection.prototype._detachCloseHandler = function (container) { + $(document.body).off('mousedown.select2.' + container.id); + }; + + BaseSelection.prototype.position = function ($selection, $container) { + var $selectionContainer = $container.find('.selection'); + $selectionContainer.append($selection); + }; + + BaseSelection.prototype.destroy = function () { + this._detachCloseHandler(this.container); + }; + + BaseSelection.prototype.update = function (data) { + throw new Error('The `update` method must be defined in child classes.'); + }; + + return BaseSelection; +}); + +S2.define('select2/selection/single',[ + 'jquery', + './base', + '../utils', + '../keys' +], function ($, BaseSelection, Utils, KEYS) { + function SingleSelection () { + SingleSelection.__super__.constructor.apply(this, arguments); + } + + Utils.Extend(SingleSelection, BaseSelection); + + SingleSelection.prototype.render = function () { + var $selection = SingleSelection.__super__.render.call(this); + + $selection.addClass('select2-selection--single'); + + $selection.html( + '' + + '' + + '' + + '' + ); + + return $selection; + }; + + SingleSelection.prototype.bind = function (container, $container) { + var self = this; + + SingleSelection.__super__.bind.apply(this, arguments); + + var id = container.id + '-container'; + + this.$selection.find('.select2-selection__rendered').attr('id', id); + this.$selection.attr('aria-labelledby', id); + + this.$selection.on('mousedown', function (evt) { + // Only respond to left clicks + if (evt.which !== 1) { + return; + } + + self.trigger('toggle', { + originalEvent: evt + }); + }); + + this.$selection.on('focus', function (evt) { + // User focuses on the container + }); + + this.$selection.on('blur', function (evt) { + // User exits the container + }); + + container.on('focus', function (evt) { + if (!container.isOpen()) { + self.$selection.focus(); + } + }); + + container.on('selection:update', function (params) { + self.update(params.data); + }); + }; + + SingleSelection.prototype.clear = function () { + this.$selection.find('.select2-selection__rendered').empty(); + }; + + SingleSelection.prototype.display = function (data, container) { + var template = this.options.get('templateSelection'); + var escapeMarkup = this.options.get('escapeMarkup'); + + return escapeMarkup(template(data, container)); + }; + + SingleSelection.prototype.selectionContainer = function () { + return $(''); + }; + + SingleSelection.prototype.update = function (data) { + if (data.length === 0) { + this.clear(); + return; + } + + var selection = data[0]; + + var $rendered = this.$selection.find('.select2-selection__rendered'); + var formatted = this.display(selection, $rendered); + + $rendered.empty().append(formatted); + $rendered.prop('title', selection.title || selection.text); + }; + + return SingleSelection; +}); + +S2.define('select2/selection/multiple',[ + 'jquery', + './base', + '../utils' +], function ($, BaseSelection, Utils) { + function MultipleSelection ($element, options) { + MultipleSelection.__super__.constructor.apply(this, arguments); + } + + Utils.Extend(MultipleSelection, BaseSelection); + + MultipleSelection.prototype.render = function () { + var $selection = MultipleSelection.__super__.render.call(this); + + $selection.addClass('select2-selection--multiple'); + + $selection.html( + '
        ' + ); + + return $selection; + }; + + MultipleSelection.prototype.bind = function (container, $container) { + var self = this; + + MultipleSelection.__super__.bind.apply(this, arguments); + + this.$selection.on('click', function (evt) { + self.trigger('toggle', { + originalEvent: evt + }); + }); + + this.$selection.on( + 'click', + '.select2-selection__choice__remove', + function (evt) { + // Ignore the event if it is disabled + if (self.options.get('disabled')) { + return; + } + + var $remove = $(this); + var $selection = $remove.parent(); + + var data = $selection.data('data'); + + self.trigger('unselect', { + originalEvent: evt, + data: data + }); + } + ); + }; + + MultipleSelection.prototype.clear = function () { + this.$selection.find('.select2-selection__rendered').empty(); + }; + + MultipleSelection.prototype.display = function (data, container) { + var template = this.options.get('templateSelection'); + var escapeMarkup = this.options.get('escapeMarkup'); + + return escapeMarkup(template(data, container)); + }; + + MultipleSelection.prototype.selectionContainer = function () { + var $container = $( + '
      • ' + + '' + + '×' + + '' + + '
      • ' + ); + + return $container; + }; + + MultipleSelection.prototype.update = function (data) { + this.clear(); + + if (data.length === 0) { + return; + } + + var $selections = []; + + for (var d = 0; d < data.length; d++) { + var selection = data[d]; + + var $selection = this.selectionContainer(); + var formatted = this.display(selection, $selection); + + $selection.append(formatted); + $selection.prop('title', selection.title || selection.text); + + $selection.data('data', selection); + + $selections.push($selection); + } + + var $rendered = this.$selection.find('.select2-selection__rendered'); + + Utils.appendMany($rendered, $selections); + }; + + return MultipleSelection; +}); + +S2.define('select2/selection/placeholder',[ + '../utils' +], function (Utils) { + function Placeholder (decorated, $element, options) { + this.placeholder = this.normalizePlaceholder(options.get('placeholder')); + + decorated.call(this, $element, options); + } + + Placeholder.prototype.normalizePlaceholder = function (_, placeholder) { + if (typeof placeholder === 'string') { + placeholder = { + id: '', + text: placeholder + }; + } + + return placeholder; + }; + + Placeholder.prototype.createPlaceholder = function (decorated, placeholder) { + var $placeholder = this.selectionContainer(); + + $placeholder.html(this.display(placeholder)); + $placeholder.addClass('select2-selection__placeholder') + .removeClass('select2-selection__choice'); + + return $placeholder; + }; + + Placeholder.prototype.update = function (decorated, data) { + var singlePlaceholder = ( + data.length == 1 && data[0].id != this.placeholder.id + ); + var multipleSelections = data.length > 1; + + if (multipleSelections || singlePlaceholder) { + return decorated.call(this, data); + } + + this.clear(); + + var $placeholder = this.createPlaceholder(this.placeholder); + + this.$selection.find('.select2-selection__rendered').append($placeholder); + }; + + return Placeholder; +}); + +S2.define('select2/selection/allowClear',[ + 'jquery', + '../keys' +], function ($, KEYS) { + function AllowClear () { } + + AllowClear.prototype.bind = function (decorated, container, $container) { + var self = this; + + decorated.call(this, container, $container); + + if (this.placeholder == null) { + if (this.options.get('debug') && window.console && console.error) { + console.error( + 'Select2: The `allowClear` option should be used in combination ' + + 'with the `placeholder` option.' + ); + } + } + + this.$selection.on('mousedown', '.select2-selection__clear', + function (evt) { + self._handleClear(evt); + }); + + container.on('keypress', function (evt) { + self._handleKeyboardClear(evt, container); + }); + }; + + AllowClear.prototype._handleClear = function (_, evt) { + // Ignore the event if it is disabled + if (this.options.get('disabled')) { + return; + } + + var $clear = this.$selection.find('.select2-selection__clear'); + + // Ignore the event if nothing has been selected + if ($clear.length === 0) { + return; + } + + evt.stopPropagation(); + + var data = $clear.data('data'); + + for (var d = 0; d < data.length; d++) { + var unselectData = { + data: data[d] + }; + + // Trigger the `unselect` event, so people can prevent it from being + // cleared. + this.trigger('unselect', unselectData); + + // If the event was prevented, don't clear it out. + if (unselectData.prevented) { + return; + } + } + + this.$element.val(this.placeholder.id).trigger('change'); + + this.trigger('toggle', {}); + }; + + AllowClear.prototype._handleKeyboardClear = function (_, evt, container) { + if (container.isOpen()) { + return; + } + + if (evt.which == KEYS.DELETE || evt.which == KEYS.BACKSPACE) { + this._handleClear(evt); + } + }; + + AllowClear.prototype.update = function (decorated, data) { + decorated.call(this, data); + + if (this.$selection.find('.select2-selection__placeholder').length > 0 || + data.length === 0) { + return; + } + + var $remove = $( + '' + + '×' + + '' + ); + $remove.data('data', data); + + this.$selection.find('.select2-selection__rendered').prepend($remove); + }; + + return AllowClear; +}); + +S2.define('select2/selection/search',[ + 'jquery', + '../utils', + '../keys' +], function ($, Utils, KEYS) { + function Search (decorated, $element, options) { + decorated.call(this, $element, options); + } + + Search.prototype.render = function (decorated) { + var $search = $( + '' + ); + + this.$searchContainer = $search; + this.$search = $search.find('input'); + + var $rendered = decorated.call(this); + + this._transferTabIndex(); + + return $rendered; + }; + + Search.prototype.bind = function (decorated, container, $container) { + var self = this; + + decorated.call(this, container, $container); + + container.on('open', function () { + self.$search.trigger('focus'); + }); + + container.on('close', function () { + self.$search.val(''); + self.$search.removeAttr('aria-activedescendant'); + self.$search.trigger('focus'); + }); + + container.on('enable', function () { + self.$search.prop('disabled', false); + + self._transferTabIndex(); + }); + + container.on('disable', function () { + self.$search.prop('disabled', true); + }); + + container.on('focus', function (evt) { + self.$search.trigger('focus'); + }); + + container.on('results:focus', function (params) { + self.$search.attr('aria-activedescendant', params.id); + }); + + this.$selection.on('focusin', '.select2-search--inline', function (evt) { + self.trigger('focus', evt); + }); + + this.$selection.on('focusout', '.select2-search--inline', function (evt) { + self._handleBlur(evt); + }); + + this.$selection.on('keydown', '.select2-search--inline', function (evt) { + evt.stopPropagation(); + + self.trigger('keypress', evt); + + self._keyUpPrevented = evt.isDefaultPrevented(); + + var key = evt.which; + + if (key === KEYS.BACKSPACE && self.$search.val() === '') { + var $previousChoice = self.$searchContainer + .prev('.select2-selection__choice'); + + if ($previousChoice.length > 0) { + var item = $previousChoice.data('data'); + + self.searchRemoveChoice(item); + + evt.preventDefault(); + } + } + }); + + // Try to detect the IE version should the `documentMode` property that + // is stored on the document. This is only implemented in IE and is + // slightly cleaner than doing a user agent check. + // This property is not available in Edge, but Edge also doesn't have + // this bug. + var msie = document.documentMode; + var disableInputEvents = msie && msie <= 11; + + // Workaround for browsers which do not support the `input` event + // This will prevent double-triggering of events for browsers which support + // both the `keyup` and `input` events. + this.$selection.on( + 'input.searchcheck', + '.select2-search--inline', + function (evt) { + // IE will trigger the `input` event when a placeholder is used on a + // search box. To get around this issue, we are forced to ignore all + // `input` events in IE and keep using `keyup`. + if (disableInputEvents) { + self.$selection.off('input.search input.searchcheck'); + return; + } + + // Unbind the duplicated `keyup` event + self.$selection.off('keyup.search'); + } + ); + + this.$selection.on( + 'keyup.search input.search', + '.select2-search--inline', + function (evt) { + // IE will trigger the `input` event when a placeholder is used on a + // search box. To get around this issue, we are forced to ignore all + // `input` events in IE and keep using `keyup`. + if (disableInputEvents && evt.type === 'input') { + self.$selection.off('input.search input.searchcheck'); + return; + } + + var key = evt.which; + + // We can freely ignore events from modifier keys + if (key == KEYS.SHIFT || key == KEYS.CTRL || key == KEYS.ALT) { + return; + } + + // Tabbing will be handled during the `keydown` phase + if (key == KEYS.TAB) { + return; + } + + self.handleSearch(evt); + } + ); + }; + + /** + * This method will transfer the tabindex attribute from the rendered + * selection to the search box. This allows for the search box to be used as + * the primary focus instead of the selection container. + * + * @private + */ + Search.prototype._transferTabIndex = function (decorated) { + this.$search.attr('tabindex', this.$selection.attr('tabindex')); + this.$selection.attr('tabindex', '-1'); + }; + + Search.prototype.createPlaceholder = function (decorated, placeholder) { + this.$search.attr('placeholder', placeholder.text); + }; + + Search.prototype.update = function (decorated, data) { + var searchHadFocus = this.$search[0] == document.activeElement; + + this.$search.attr('placeholder', ''); + + decorated.call(this, data); + + this.$selection.find('.select2-selection__rendered') + .append(this.$searchContainer); + + this.resizeSearch(); + if (searchHadFocus) { + this.$search.focus(); + } + }; + + Search.prototype.handleSearch = function () { + this.resizeSearch(); + + if (!this._keyUpPrevented) { + var input = this.$search.val(); + + this.trigger('query', { + term: input + }); + } + + this._keyUpPrevented = false; + }; + + Search.prototype.searchRemoveChoice = function (decorated, item) { + this.trigger('unselect', { + data: item + }); + + this.$search.val(item.text); + this.handleSearch(); + }; + + Search.prototype.resizeSearch = function () { + this.$search.css('width', '25px'); + + var width = ''; + + if (this.$search.attr('placeholder') !== '') { + width = this.$selection.find('.select2-selection__rendered').innerWidth(); + } else { + var minimumWidth = this.$search.val().length + 1; + + width = (minimumWidth * 0.75) + 'em'; + } + + this.$search.css('width', width); + }; + + return Search; +}); + +S2.define('select2/selection/eventRelay',[ + 'jquery' +], function ($) { + function EventRelay () { } + + EventRelay.prototype.bind = function (decorated, container, $container) { + var self = this; + var relayEvents = [ + 'open', 'opening', + 'close', 'closing', + 'select', 'selecting', + 'unselect', 'unselecting' + ]; + + var preventableEvents = ['opening', 'closing', 'selecting', 'unselecting']; + + decorated.call(this, container, $container); + + container.on('*', function (name, params) { + // Ignore events that should not be relayed + if ($.inArray(name, relayEvents) === -1) { + return; + } + + // The parameters should always be an object + params = params || {}; + + // Generate the jQuery event for the Select2 event + var evt = $.Event('select2:' + name, { + params: params + }); + + self.$element.trigger(evt); + + // Only handle preventable events if it was one + if ($.inArray(name, preventableEvents) === -1) { + return; + } + + params.prevented = evt.isDefaultPrevented(); + }); + }; + + return EventRelay; +}); + +S2.define('select2/translation',[ + 'jquery', + 'require' +], function ($, require) { + function Translation (dict) { + this.dict = dict || {}; + } + + Translation.prototype.all = function () { + return this.dict; + }; + + Translation.prototype.get = function (key) { + return this.dict[key]; + }; + + Translation.prototype.extend = function (translation) { + this.dict = $.extend({}, translation.all(), this.dict); + }; + + // Static functions + + Translation._cache = {}; + + Translation.loadPath = function (path) { + if (!(path in Translation._cache)) { + var translations = require(path); + + Translation._cache[path] = translations; + } + + return new Translation(Translation._cache[path]); + }; + + return Translation; +}); + +S2.define('select2/diacritics',[ + +], function () { + var diacritics = { + '\u24B6': 'A', + '\uFF21': 'A', + '\u00C0': 'A', + '\u00C1': 'A', + '\u00C2': 'A', + '\u1EA6': 'A', + '\u1EA4': 'A', + '\u1EAA': 'A', + '\u1EA8': 'A', + '\u00C3': 'A', + '\u0100': 'A', + '\u0102': 'A', + '\u1EB0': 'A', + '\u1EAE': 'A', + '\u1EB4': 'A', + '\u1EB2': 'A', + '\u0226': 'A', + '\u01E0': 'A', + '\u00C4': 'A', + '\u01DE': 'A', + '\u1EA2': 'A', + '\u00C5': 'A', + '\u01FA': 'A', + '\u01CD': 'A', + '\u0200': 'A', + '\u0202': 'A', + '\u1EA0': 'A', + '\u1EAC': 'A', + '\u1EB6': 'A', + '\u1E00': 'A', + '\u0104': 'A', + '\u023A': 'A', + '\u2C6F': 'A', + '\uA732': 'AA', + '\u00C6': 'AE', + '\u01FC': 'AE', + '\u01E2': 'AE', + '\uA734': 'AO', + '\uA736': 'AU', + '\uA738': 'AV', + '\uA73A': 'AV', + '\uA73C': 'AY', + '\u24B7': 'B', + '\uFF22': 'B', + '\u1E02': 'B', + '\u1E04': 'B', + '\u1E06': 'B', + '\u0243': 'B', + '\u0182': 'B', + '\u0181': 'B', + '\u24B8': 'C', + '\uFF23': 'C', + '\u0106': 'C', + '\u0108': 'C', + '\u010A': 'C', + '\u010C': 'C', + '\u00C7': 'C', + '\u1E08': 'C', + '\u0187': 'C', + '\u023B': 'C', + '\uA73E': 'C', + '\u24B9': 'D', + '\uFF24': 'D', + '\u1E0A': 'D', + '\u010E': 'D', + '\u1E0C': 'D', + '\u1E10': 'D', + '\u1E12': 'D', + '\u1E0E': 'D', + '\u0110': 'D', + '\u018B': 'D', + '\u018A': 'D', + '\u0189': 'D', + '\uA779': 'D', + '\u01F1': 'DZ', + '\u01C4': 'DZ', + '\u01F2': 'Dz', + '\u01C5': 'Dz', + '\u24BA': 'E', + '\uFF25': 'E', + '\u00C8': 'E', + '\u00C9': 'E', + '\u00CA': 'E', + '\u1EC0': 'E', + '\u1EBE': 'E', + '\u1EC4': 'E', + '\u1EC2': 'E', + '\u1EBC': 'E', + '\u0112': 'E', + '\u1E14': 'E', + '\u1E16': 'E', + '\u0114': 'E', + '\u0116': 'E', + '\u00CB': 'E', + '\u1EBA': 'E', + '\u011A': 'E', + '\u0204': 'E', + '\u0206': 'E', + '\u1EB8': 'E', + '\u1EC6': 'E', + '\u0228': 'E', + '\u1E1C': 'E', + '\u0118': 'E', + '\u1E18': 'E', + '\u1E1A': 'E', + '\u0190': 'E', + '\u018E': 'E', + '\u24BB': 'F', + '\uFF26': 'F', + '\u1E1E': 'F', + '\u0191': 'F', + '\uA77B': 'F', + '\u24BC': 'G', + '\uFF27': 'G', + '\u01F4': 'G', + '\u011C': 'G', + '\u1E20': 'G', + '\u011E': 'G', + '\u0120': 'G', + '\u01E6': 'G', + '\u0122': 'G', + '\u01E4': 'G', + '\u0193': 'G', + '\uA7A0': 'G', + '\uA77D': 'G', + '\uA77E': 'G', + '\u24BD': 'H', + '\uFF28': 'H', + '\u0124': 'H', + '\u1E22': 'H', + '\u1E26': 'H', + '\u021E': 'H', + '\u1E24': 'H', + '\u1E28': 'H', + '\u1E2A': 'H', + '\u0126': 'H', + '\u2C67': 'H', + '\u2C75': 'H', + '\uA78D': 'H', + '\u24BE': 'I', + '\uFF29': 'I', + '\u00CC': 'I', + '\u00CD': 'I', + '\u00CE': 'I', + '\u0128': 'I', + '\u012A': 'I', + '\u012C': 'I', + '\u0130': 'I', + '\u00CF': 'I', + '\u1E2E': 'I', + '\u1EC8': 'I', + '\u01CF': 'I', + '\u0208': 'I', + '\u020A': 'I', + '\u1ECA': 'I', + '\u012E': 'I', + '\u1E2C': 'I', + '\u0197': 'I', + '\u24BF': 'J', + '\uFF2A': 'J', + '\u0134': 'J', + '\u0248': 'J', + '\u24C0': 'K', + '\uFF2B': 'K', + '\u1E30': 'K', + '\u01E8': 'K', + '\u1E32': 'K', + '\u0136': 'K', + '\u1E34': 'K', + '\u0198': 'K', + '\u2C69': 'K', + '\uA740': 'K', + '\uA742': 'K', + '\uA744': 'K', + '\uA7A2': 'K', + '\u24C1': 'L', + '\uFF2C': 'L', + '\u013F': 'L', + '\u0139': 'L', + '\u013D': 'L', + '\u1E36': 'L', + '\u1E38': 'L', + '\u013B': 'L', + '\u1E3C': 'L', + '\u1E3A': 'L', + '\u0141': 'L', + '\u023D': 'L', + '\u2C62': 'L', + '\u2C60': 'L', + '\uA748': 'L', + '\uA746': 'L', + '\uA780': 'L', + '\u01C7': 'LJ', + '\u01C8': 'Lj', + '\u24C2': 'M', + '\uFF2D': 'M', + '\u1E3E': 'M', + '\u1E40': 'M', + '\u1E42': 'M', + '\u2C6E': 'M', + '\u019C': 'M', + '\u24C3': 'N', + '\uFF2E': 'N', + '\u01F8': 'N', + '\u0143': 'N', + '\u00D1': 'N', + '\u1E44': 'N', + '\u0147': 'N', + '\u1E46': 'N', + '\u0145': 'N', + '\u1E4A': 'N', + '\u1E48': 'N', + '\u0220': 'N', + '\u019D': 'N', + '\uA790': 'N', + '\uA7A4': 'N', + '\u01CA': 'NJ', + '\u01CB': 'Nj', + '\u24C4': 'O', + '\uFF2F': 'O', + '\u00D2': 'O', + '\u00D3': 'O', + '\u00D4': 'O', + '\u1ED2': 'O', + '\u1ED0': 'O', + '\u1ED6': 'O', + '\u1ED4': 'O', + '\u00D5': 'O', + '\u1E4C': 'O', + '\u022C': 'O', + '\u1E4E': 'O', + '\u014C': 'O', + '\u1E50': 'O', + '\u1E52': 'O', + '\u014E': 'O', + '\u022E': 'O', + '\u0230': 'O', + '\u00D6': 'O', + '\u022A': 'O', + '\u1ECE': 'O', + '\u0150': 'O', + '\u01D1': 'O', + '\u020C': 'O', + '\u020E': 'O', + '\u01A0': 'O', + '\u1EDC': 'O', + '\u1EDA': 'O', + '\u1EE0': 'O', + '\u1EDE': 'O', + '\u1EE2': 'O', + '\u1ECC': 'O', + '\u1ED8': 'O', + '\u01EA': 'O', + '\u01EC': 'O', + '\u00D8': 'O', + '\u01FE': 'O', + '\u0186': 'O', + '\u019F': 'O', + '\uA74A': 'O', + '\uA74C': 'O', + '\u01A2': 'OI', + '\uA74E': 'OO', + '\u0222': 'OU', + '\u24C5': 'P', + '\uFF30': 'P', + '\u1E54': 'P', + '\u1E56': 'P', + '\u01A4': 'P', + '\u2C63': 'P', + '\uA750': 'P', + '\uA752': 'P', + '\uA754': 'P', + '\u24C6': 'Q', + '\uFF31': 'Q', + '\uA756': 'Q', + '\uA758': 'Q', + '\u024A': 'Q', + '\u24C7': 'R', + '\uFF32': 'R', + '\u0154': 'R', + '\u1E58': 'R', + '\u0158': 'R', + '\u0210': 'R', + '\u0212': 'R', + '\u1E5A': 'R', + '\u1E5C': 'R', + '\u0156': 'R', + '\u1E5E': 'R', + '\u024C': 'R', + '\u2C64': 'R', + '\uA75A': 'R', + '\uA7A6': 'R', + '\uA782': 'R', + '\u24C8': 'S', + '\uFF33': 'S', + '\u1E9E': 'S', + '\u015A': 'S', + '\u1E64': 'S', + '\u015C': 'S', + '\u1E60': 'S', + '\u0160': 'S', + '\u1E66': 'S', + '\u1E62': 'S', + '\u1E68': 'S', + '\u0218': 'S', + '\u015E': 'S', + '\u2C7E': 'S', + '\uA7A8': 'S', + '\uA784': 'S', + '\u24C9': 'T', + '\uFF34': 'T', + '\u1E6A': 'T', + '\u0164': 'T', + '\u1E6C': 'T', + '\u021A': 'T', + '\u0162': 'T', + '\u1E70': 'T', + '\u1E6E': 'T', + '\u0166': 'T', + '\u01AC': 'T', + '\u01AE': 'T', + '\u023E': 'T', + '\uA786': 'T', + '\uA728': 'TZ', + '\u24CA': 'U', + '\uFF35': 'U', + '\u00D9': 'U', + '\u00DA': 'U', + '\u00DB': 'U', + '\u0168': 'U', + '\u1E78': 'U', + '\u016A': 'U', + '\u1E7A': 'U', + '\u016C': 'U', + '\u00DC': 'U', + '\u01DB': 'U', + '\u01D7': 'U', + '\u01D5': 'U', + '\u01D9': 'U', + '\u1EE6': 'U', + '\u016E': 'U', + '\u0170': 'U', + '\u01D3': 'U', + '\u0214': 'U', + '\u0216': 'U', + '\u01AF': 'U', + '\u1EEA': 'U', + '\u1EE8': 'U', + '\u1EEE': 'U', + '\u1EEC': 'U', + '\u1EF0': 'U', + '\u1EE4': 'U', + '\u1E72': 'U', + '\u0172': 'U', + '\u1E76': 'U', + '\u1E74': 'U', + '\u0244': 'U', + '\u24CB': 'V', + '\uFF36': 'V', + '\u1E7C': 'V', + '\u1E7E': 'V', + '\u01B2': 'V', + '\uA75E': 'V', + '\u0245': 'V', + '\uA760': 'VY', + '\u24CC': 'W', + '\uFF37': 'W', + '\u1E80': 'W', + '\u1E82': 'W', + '\u0174': 'W', + '\u1E86': 'W', + '\u1E84': 'W', + '\u1E88': 'W', + '\u2C72': 'W', + '\u24CD': 'X', + '\uFF38': 'X', + '\u1E8A': 'X', + '\u1E8C': 'X', + '\u24CE': 'Y', + '\uFF39': 'Y', + '\u1EF2': 'Y', + '\u00DD': 'Y', + '\u0176': 'Y', + '\u1EF8': 'Y', + '\u0232': 'Y', + '\u1E8E': 'Y', + '\u0178': 'Y', + '\u1EF6': 'Y', + '\u1EF4': 'Y', + '\u01B3': 'Y', + '\u024E': 'Y', + '\u1EFE': 'Y', + '\u24CF': 'Z', + '\uFF3A': 'Z', + '\u0179': 'Z', + '\u1E90': 'Z', + '\u017B': 'Z', + '\u017D': 'Z', + '\u1E92': 'Z', + '\u1E94': 'Z', + '\u01B5': 'Z', + '\u0224': 'Z', + '\u2C7F': 'Z', + '\u2C6B': 'Z', + '\uA762': 'Z', + '\u24D0': 'a', + '\uFF41': 'a', + '\u1E9A': 'a', + '\u00E0': 'a', + '\u00E1': 'a', + '\u00E2': 'a', + '\u1EA7': 'a', + '\u1EA5': 'a', + '\u1EAB': 'a', + '\u1EA9': 'a', + '\u00E3': 'a', + '\u0101': 'a', + '\u0103': 'a', + '\u1EB1': 'a', + '\u1EAF': 'a', + '\u1EB5': 'a', + '\u1EB3': 'a', + '\u0227': 'a', + '\u01E1': 'a', + '\u00E4': 'a', + '\u01DF': 'a', + '\u1EA3': 'a', + '\u00E5': 'a', + '\u01FB': 'a', + '\u01CE': 'a', + '\u0201': 'a', + '\u0203': 'a', + '\u1EA1': 'a', + '\u1EAD': 'a', + '\u1EB7': 'a', + '\u1E01': 'a', + '\u0105': 'a', + '\u2C65': 'a', + '\u0250': 'a', + '\uA733': 'aa', + '\u00E6': 'ae', + '\u01FD': 'ae', + '\u01E3': 'ae', + '\uA735': 'ao', + '\uA737': 'au', + '\uA739': 'av', + '\uA73B': 'av', + '\uA73D': 'ay', + '\u24D1': 'b', + '\uFF42': 'b', + '\u1E03': 'b', + '\u1E05': 'b', + '\u1E07': 'b', + '\u0180': 'b', + '\u0183': 'b', + '\u0253': 'b', + '\u24D2': 'c', + '\uFF43': 'c', + '\u0107': 'c', + '\u0109': 'c', + '\u010B': 'c', + '\u010D': 'c', + '\u00E7': 'c', + '\u1E09': 'c', + '\u0188': 'c', + '\u023C': 'c', + '\uA73F': 'c', + '\u2184': 'c', + '\u24D3': 'd', + '\uFF44': 'd', + '\u1E0B': 'd', + '\u010F': 'd', + '\u1E0D': 'd', + '\u1E11': 'd', + '\u1E13': 'd', + '\u1E0F': 'd', + '\u0111': 'd', + '\u018C': 'd', + '\u0256': 'd', + '\u0257': 'd', + '\uA77A': 'd', + '\u01F3': 'dz', + '\u01C6': 'dz', + '\u24D4': 'e', + '\uFF45': 'e', + '\u00E8': 'e', + '\u00E9': 'e', + '\u00EA': 'e', + '\u1EC1': 'e', + '\u1EBF': 'e', + '\u1EC5': 'e', + '\u1EC3': 'e', + '\u1EBD': 'e', + '\u0113': 'e', + '\u1E15': 'e', + '\u1E17': 'e', + '\u0115': 'e', + '\u0117': 'e', + '\u00EB': 'e', + '\u1EBB': 'e', + '\u011B': 'e', + '\u0205': 'e', + '\u0207': 'e', + '\u1EB9': 'e', + '\u1EC7': 'e', + '\u0229': 'e', + '\u1E1D': 'e', + '\u0119': 'e', + '\u1E19': 'e', + '\u1E1B': 'e', + '\u0247': 'e', + '\u025B': 'e', + '\u01DD': 'e', + '\u24D5': 'f', + '\uFF46': 'f', + '\u1E1F': 'f', + '\u0192': 'f', + '\uA77C': 'f', + '\u24D6': 'g', + '\uFF47': 'g', + '\u01F5': 'g', + '\u011D': 'g', + '\u1E21': 'g', + '\u011F': 'g', + '\u0121': 'g', + '\u01E7': 'g', + '\u0123': 'g', + '\u01E5': 'g', + '\u0260': 'g', + '\uA7A1': 'g', + '\u1D79': 'g', + '\uA77F': 'g', + '\u24D7': 'h', + '\uFF48': 'h', + '\u0125': 'h', + '\u1E23': 'h', + '\u1E27': 'h', + '\u021F': 'h', + '\u1E25': 'h', + '\u1E29': 'h', + '\u1E2B': 'h', + '\u1E96': 'h', + '\u0127': 'h', + '\u2C68': 'h', + '\u2C76': 'h', + '\u0265': 'h', + '\u0195': 'hv', + '\u24D8': 'i', + '\uFF49': 'i', + '\u00EC': 'i', + '\u00ED': 'i', + '\u00EE': 'i', + '\u0129': 'i', + '\u012B': 'i', + '\u012D': 'i', + '\u00EF': 'i', + '\u1E2F': 'i', + '\u1EC9': 'i', + '\u01D0': 'i', + '\u0209': 'i', + '\u020B': 'i', + '\u1ECB': 'i', + '\u012F': 'i', + '\u1E2D': 'i', + '\u0268': 'i', + '\u0131': 'i', + '\u24D9': 'j', + '\uFF4A': 'j', + '\u0135': 'j', + '\u01F0': 'j', + '\u0249': 'j', + '\u24DA': 'k', + '\uFF4B': 'k', + '\u1E31': 'k', + '\u01E9': 'k', + '\u1E33': 'k', + '\u0137': 'k', + '\u1E35': 'k', + '\u0199': 'k', + '\u2C6A': 'k', + '\uA741': 'k', + '\uA743': 'k', + '\uA745': 'k', + '\uA7A3': 'k', + '\u24DB': 'l', + '\uFF4C': 'l', + '\u0140': 'l', + '\u013A': 'l', + '\u013E': 'l', + '\u1E37': 'l', + '\u1E39': 'l', + '\u013C': 'l', + '\u1E3D': 'l', + '\u1E3B': 'l', + '\u017F': 'l', + '\u0142': 'l', + '\u019A': 'l', + '\u026B': 'l', + '\u2C61': 'l', + '\uA749': 'l', + '\uA781': 'l', + '\uA747': 'l', + '\u01C9': 'lj', + '\u24DC': 'm', + '\uFF4D': 'm', + '\u1E3F': 'm', + '\u1E41': 'm', + '\u1E43': 'm', + '\u0271': 'm', + '\u026F': 'm', + '\u24DD': 'n', + '\uFF4E': 'n', + '\u01F9': 'n', + '\u0144': 'n', + '\u00F1': 'n', + '\u1E45': 'n', + '\u0148': 'n', + '\u1E47': 'n', + '\u0146': 'n', + '\u1E4B': 'n', + '\u1E49': 'n', + '\u019E': 'n', + '\u0272': 'n', + '\u0149': 'n', + '\uA791': 'n', + '\uA7A5': 'n', + '\u01CC': 'nj', + '\u24DE': 'o', + '\uFF4F': 'o', + '\u00F2': 'o', + '\u00F3': 'o', + '\u00F4': 'o', + '\u1ED3': 'o', + '\u1ED1': 'o', + '\u1ED7': 'o', + '\u1ED5': 'o', + '\u00F5': 'o', + '\u1E4D': 'o', + '\u022D': 'o', + '\u1E4F': 'o', + '\u014D': 'o', + '\u1E51': 'o', + '\u1E53': 'o', + '\u014F': 'o', + '\u022F': 'o', + '\u0231': 'o', + '\u00F6': 'o', + '\u022B': 'o', + '\u1ECF': 'o', + '\u0151': 'o', + '\u01D2': 'o', + '\u020D': 'o', + '\u020F': 'o', + '\u01A1': 'o', + '\u1EDD': 'o', + '\u1EDB': 'o', + '\u1EE1': 'o', + '\u1EDF': 'o', + '\u1EE3': 'o', + '\u1ECD': 'o', + '\u1ED9': 'o', + '\u01EB': 'o', + '\u01ED': 'o', + '\u00F8': 'o', + '\u01FF': 'o', + '\u0254': 'o', + '\uA74B': 'o', + '\uA74D': 'o', + '\u0275': 'o', + '\u01A3': 'oi', + '\u0223': 'ou', + '\uA74F': 'oo', + '\u24DF': 'p', + '\uFF50': 'p', + '\u1E55': 'p', + '\u1E57': 'p', + '\u01A5': 'p', + '\u1D7D': 'p', + '\uA751': 'p', + '\uA753': 'p', + '\uA755': 'p', + '\u24E0': 'q', + '\uFF51': 'q', + '\u024B': 'q', + '\uA757': 'q', + '\uA759': 'q', + '\u24E1': 'r', + '\uFF52': 'r', + '\u0155': 'r', + '\u1E59': 'r', + '\u0159': 'r', + '\u0211': 'r', + '\u0213': 'r', + '\u1E5B': 'r', + '\u1E5D': 'r', + '\u0157': 'r', + '\u1E5F': 'r', + '\u024D': 'r', + '\u027D': 'r', + '\uA75B': 'r', + '\uA7A7': 'r', + '\uA783': 'r', + '\u24E2': 's', + '\uFF53': 's', + '\u00DF': 's', + '\u015B': 's', + '\u1E65': 's', + '\u015D': 's', + '\u1E61': 's', + '\u0161': 's', + '\u1E67': 's', + '\u1E63': 's', + '\u1E69': 's', + '\u0219': 's', + '\u015F': 's', + '\u023F': 's', + '\uA7A9': 's', + '\uA785': 's', + '\u1E9B': 's', + '\u24E3': 't', + '\uFF54': 't', + '\u1E6B': 't', + '\u1E97': 't', + '\u0165': 't', + '\u1E6D': 't', + '\u021B': 't', + '\u0163': 't', + '\u1E71': 't', + '\u1E6F': 't', + '\u0167': 't', + '\u01AD': 't', + '\u0288': 't', + '\u2C66': 't', + '\uA787': 't', + '\uA729': 'tz', + '\u24E4': 'u', + '\uFF55': 'u', + '\u00F9': 'u', + '\u00FA': 'u', + '\u00FB': 'u', + '\u0169': 'u', + '\u1E79': 'u', + '\u016B': 'u', + '\u1E7B': 'u', + '\u016D': 'u', + '\u00FC': 'u', + '\u01DC': 'u', + '\u01D8': 'u', + '\u01D6': 'u', + '\u01DA': 'u', + '\u1EE7': 'u', + '\u016F': 'u', + '\u0171': 'u', + '\u01D4': 'u', + '\u0215': 'u', + '\u0217': 'u', + '\u01B0': 'u', + '\u1EEB': 'u', + '\u1EE9': 'u', + '\u1EEF': 'u', + '\u1EED': 'u', + '\u1EF1': 'u', + '\u1EE5': 'u', + '\u1E73': 'u', + '\u0173': 'u', + '\u1E77': 'u', + '\u1E75': 'u', + '\u0289': 'u', + '\u24E5': 'v', + '\uFF56': 'v', + '\u1E7D': 'v', + '\u1E7F': 'v', + '\u028B': 'v', + '\uA75F': 'v', + '\u028C': 'v', + '\uA761': 'vy', + '\u24E6': 'w', + '\uFF57': 'w', + '\u1E81': 'w', + '\u1E83': 'w', + '\u0175': 'w', + '\u1E87': 'w', + '\u1E85': 'w', + '\u1E98': 'w', + '\u1E89': 'w', + '\u2C73': 'w', + '\u24E7': 'x', + '\uFF58': 'x', + '\u1E8B': 'x', + '\u1E8D': 'x', + '\u24E8': 'y', + '\uFF59': 'y', + '\u1EF3': 'y', + '\u00FD': 'y', + '\u0177': 'y', + '\u1EF9': 'y', + '\u0233': 'y', + '\u1E8F': 'y', + '\u00FF': 'y', + '\u1EF7': 'y', + '\u1E99': 'y', + '\u1EF5': 'y', + '\u01B4': 'y', + '\u024F': 'y', + '\u1EFF': 'y', + '\u24E9': 'z', + '\uFF5A': 'z', + '\u017A': 'z', + '\u1E91': 'z', + '\u017C': 'z', + '\u017E': 'z', + '\u1E93': 'z', + '\u1E95': 'z', + '\u01B6': 'z', + '\u0225': 'z', + '\u0240': 'z', + '\u2C6C': 'z', + '\uA763': 'z', + '\u0386': '\u0391', + '\u0388': '\u0395', + '\u0389': '\u0397', + '\u038A': '\u0399', + '\u03AA': '\u0399', + '\u038C': '\u039F', + '\u038E': '\u03A5', + '\u03AB': '\u03A5', + '\u038F': '\u03A9', + '\u03AC': '\u03B1', + '\u03AD': '\u03B5', + '\u03AE': '\u03B7', + '\u03AF': '\u03B9', + '\u03CA': '\u03B9', + '\u0390': '\u03B9', + '\u03CC': '\u03BF', + '\u03CD': '\u03C5', + '\u03CB': '\u03C5', + '\u03B0': '\u03C5', + '\u03C9': '\u03C9', + '\u03C2': '\u03C3' + }; + + return diacritics; +}); + +S2.define('select2/data/base',[ + '../utils' +], function (Utils) { + function BaseAdapter ($element, options) { + BaseAdapter.__super__.constructor.call(this); + } + + Utils.Extend(BaseAdapter, Utils.Observable); + + BaseAdapter.prototype.current = function (callback) { + throw new Error('The `current` method must be defined in child classes.'); + }; + + BaseAdapter.prototype.query = function (params, callback) { + throw new Error('The `query` method must be defined in child classes.'); + }; + + BaseAdapter.prototype.bind = function (container, $container) { + // Can be implemented in subclasses + }; + + BaseAdapter.prototype.destroy = function () { + // Can be implemented in subclasses + }; + + BaseAdapter.prototype.generateResultId = function (container, data) { + var id = container.id + '-result-'; + + id += Utils.generateChars(4); + + if (data.id != null) { + id += '-' + data.id.toString(); + } else { + id += '-' + Utils.generateChars(4); + } + return id; + }; + + return BaseAdapter; +}); + +S2.define('select2/data/select',[ + './base', + '../utils', + 'jquery' +], function (BaseAdapter, Utils, $) { + function SelectAdapter ($element, options) { + this.$element = $element; + this.options = options; + + SelectAdapter.__super__.constructor.call(this); + } + + Utils.Extend(SelectAdapter, BaseAdapter); + + SelectAdapter.prototype.current = function (callback) { + var data = []; + var self = this; + + this.$element.find(':selected').each(function () { + var $option = $(this); + + var option = self.item($option); + + data.push(option); + }); + + callback(data); + }; + + SelectAdapter.prototype.select = function (data) { + var self = this; + + data.selected = true; + + // If data.element is a DOM node, use it instead + if ($(data.element).is('option')) { + data.element.selected = true; + + this.$element.trigger('change'); + + return; + } + + if (this.$element.prop('multiple')) { + this.current(function (currentData) { + var val = []; + + data = [data]; + data.push.apply(data, currentData); + + for (var d = 0; d < data.length; d++) { + var id = data[d].id; + + if ($.inArray(id, val) === -1) { + val.push(id); + } + } + + self.$element.val(val); + self.$element.trigger('change'); + }); + } else { + var val = data.id; + + this.$element.val(val); + this.$element.trigger('change'); + } + }; + + SelectAdapter.prototype.unselect = function (data) { + var self = this; + + if (!this.$element.prop('multiple')) { + return; + } + + data.selected = false; + + if ($(data.element).is('option')) { + data.element.selected = false; + + this.$element.trigger('change'); + + return; + } + + this.current(function (currentData) { + var val = []; + + for (var d = 0; d < currentData.length; d++) { + var id = currentData[d].id; + + if (id !== data.id && $.inArray(id, val) === -1) { + val.push(id); + } + } + + self.$element.val(val); + + self.$element.trigger('change'); + }); + }; + + SelectAdapter.prototype.bind = function (container, $container) { + var self = this; + + this.container = container; + + container.on('select', function (params) { + self.select(params.data); + }); + + container.on('unselect', function (params) { + self.unselect(params.data); + }); + }; + + SelectAdapter.prototype.destroy = function () { + // Remove anything added to child elements + this.$element.find('*').each(function () { + // Remove any custom data set by Select2 + $.removeData(this, 'data'); + }); + }; + + SelectAdapter.prototype.query = function (params, callback) { + var data = []; + var self = this; + + var $options = this.$element.children(); + + $options.each(function () { + var $option = $(this); + + if (!$option.is('option') && !$option.is('optgroup')) { + return; + } + + var option = self.item($option); + + var matches = self.matches(params, option); + + if (matches !== null) { + data.push(matches); + } + }); + + callback({ + results: data + }); + }; + + SelectAdapter.prototype.addOptions = function ($options) { + Utils.appendMany(this.$element, $options); + }; + + SelectAdapter.prototype.option = function (data) { + var option; + + if (data.children) { + option = document.createElement('optgroup'); + option.label = data.text; + } else { + option = document.createElement('option'); + + if (option.textContent !== undefined) { + option.textContent = data.text; + } else { + option.innerText = data.text; + } + } + + if (data.id) { + option.value = data.id; + } + + if (data.disabled) { + option.disabled = true; + } + + if (data.selected) { + option.selected = true; + } + + if (data.title) { + option.title = data.title; + } + + var $option = $(option); + + var normalizedData = this._normalizeItem(data); + normalizedData.element = option; + + // Override the option's data with the combined data + $.data(option, 'data', normalizedData); + + return $option; + }; + + SelectAdapter.prototype.item = function ($option) { + var data = {}; + + data = $.data($option[0], 'data'); + + if (data != null) { + return data; + } + + if ($option.is('option')) { + data = { + id: $option.val(), + text: $option.text(), + disabled: $option.prop('disabled'), + selected: $option.prop('selected'), + title: $option.prop('title') + }; + } else if ($option.is('optgroup')) { + data = { + text: $option.prop('label'), + children: [], + title: $option.prop('title') + }; + + var $children = $option.children('option'); + var children = []; + + for (var c = 0; c < $children.length; c++) { + var $child = $($children[c]); + + var child = this.item($child); + + children.push(child); + } + + data.children = children; + } + + data = this._normalizeItem(data); + data.element = $option[0]; + + $.data($option[0], 'data', data); + + return data; + }; + + SelectAdapter.prototype._normalizeItem = function (item) { + if (!$.isPlainObject(item)) { + item = { + id: item, + text: item + }; + } + + item = $.extend({}, { + text: '' + }, item); + + var defaults = { + selected: false, + disabled: false + }; + + if (item.id != null) { + item.id = item.id.toString(); + } + + if (item.text != null) { + item.text = item.text.toString(); + } + + if (item._resultId == null && item.id && this.container != null) { + item._resultId = this.generateResultId(this.container, item); + } + + return $.extend({}, defaults, item); + }; + + SelectAdapter.prototype.matches = function (params, data) { + var matcher = this.options.get('matcher'); + + return matcher(params, data); + }; + + return SelectAdapter; +}); + +S2.define('select2/data/array',[ + './select', + '../utils', + 'jquery' +], function (SelectAdapter, Utils, $) { + function ArrayAdapter ($element, options) { + var data = options.get('data') || []; + + ArrayAdapter.__super__.constructor.call(this, $element, options); + + this.addOptions(this.convertToOptions(data)); + } + + Utils.Extend(ArrayAdapter, SelectAdapter); + + ArrayAdapter.prototype.select = function (data) { + var $option = this.$element.find('option').filter(function (i, elm) { + return elm.value == data.id.toString(); + }); + + if ($option.length === 0) { + $option = this.option(data); + + this.addOptions($option); + } + + ArrayAdapter.__super__.select.call(this, data); + }; + + ArrayAdapter.prototype.convertToOptions = function (data) { + var self = this; + + var $existing = this.$element.find('option'); + var existingIds = $existing.map(function () { + return self.item($(this)).id; + }).get(); + + var $options = []; + + // Filter out all items except for the one passed in the argument + function onlyItem (item) { + return function () { + return $(this).val() == item.id; + }; + } + + for (var d = 0; d < data.length; d++) { + var item = this._normalizeItem(data[d]); + + // Skip items which were pre-loaded, only merge the data + if ($.inArray(item.id, existingIds) >= 0) { + var $existingOption = $existing.filter(onlyItem(item)); + + var existingData = this.item($existingOption); + var newData = $.extend(true, {}, item, existingData); + + var $newOption = this.option(newData); + + $existingOption.replaceWith($newOption); + + continue; + } + + var $option = this.option(item); + + if (item.children) { + var $children = this.convertToOptions(item.children); + + Utils.appendMany($option, $children); + } + + $options.push($option); + } + + return $options; + }; + + return ArrayAdapter; +}); + +S2.define('select2/data/ajax',[ + './array', + '../utils', + 'jquery' +], function (ArrayAdapter, Utils, $) { + function AjaxAdapter ($element, options) { + this.ajaxOptions = this._applyDefaults(options.get('ajax')); + + if (this.ajaxOptions.processResults != null) { + this.processResults = this.ajaxOptions.processResults; + } + + AjaxAdapter.__super__.constructor.call(this, $element, options); + } + + Utils.Extend(AjaxAdapter, ArrayAdapter); + + AjaxAdapter.prototype._applyDefaults = function (options) { + var defaults = { + data: function (params) { + return $.extend({}, params, { + q: params.term + }); + }, + transport: function (params, success, failure) { + var $request = $.ajax(params); + + $request.then(success); + $request.fail(failure); + + return $request; + } + }; + + return $.extend({}, defaults, options, true); + }; + + AjaxAdapter.prototype.processResults = function (results) { + return results; + }; + + AjaxAdapter.prototype.query = function (params, callback) { + var matches = []; + var self = this; + + if (this._request != null) { + // JSONP requests cannot always be aborted + if ($.isFunction(this._request.abort)) { + this._request.abort(); + } + + this._request = null; + } + + var options = $.extend({ + type: 'GET' + }, this.ajaxOptions); + + if (typeof options.url === 'function') { + options.url = options.url.call(this.$element, params); + } + + if (typeof options.data === 'function') { + options.data = options.data.call(this.$element, params); + } + + function request () { + var $request = options.transport(options, function (data) { + var results = self.processResults(data, params); + + if (self.options.get('debug') && window.console && console.error) { + // Check to make sure that the response included a `results` key. + if (!results || !results.results || !$.isArray(results.results)) { + console.error( + 'Select2: The AJAX results did not return an array in the ' + + '`results` key of the response.' + ); + } + } + + callback(results); + }, function () { + // Attempt to detect if a request was aborted + // Only works if the transport exposes a status property + if ($request.status && $request.status === '0') { + return; + } + + self.trigger('results:message', { + message: 'errorLoading' + }); + }); + + self._request = $request; + } + + if (this.ajaxOptions.delay && params.term != null) { + if (this._queryTimeout) { + window.clearTimeout(this._queryTimeout); + } + + this._queryTimeout = window.setTimeout(request, this.ajaxOptions.delay); + } else { + request(); + } + }; + + return AjaxAdapter; +}); + +S2.define('select2/data/tags',[ + 'jquery' +], function ($) { + function Tags (decorated, $element, options) { + var tags = options.get('tags'); + + var createTag = options.get('createTag'); + + if (createTag !== undefined) { + this.createTag = createTag; + } + + var insertTag = options.get('insertTag'); + + if (insertTag !== undefined) { + this.insertTag = insertTag; + } + + decorated.call(this, $element, options); + + if ($.isArray(tags)) { + for (var t = 0; t < tags.length; t++) { + var tag = tags[t]; + var item = this._normalizeItem(tag); + + var $option = this.option(item); + + this.$element.append($option); + } + } + } + + Tags.prototype.query = function (decorated, params, callback) { + var self = this; + + this._removeOldTags(); + + if (params.term == null || params.page != null) { + decorated.call(this, params, callback); + return; + } + + function wrapper (obj, child) { + var data = obj.results; + + for (var i = 0; i < data.length; i++) { + var option = data[i]; + + var checkChildren = ( + option.children != null && + !wrapper({ + results: option.children + }, true) + ); + + var checkText = option.text === params.term; + + if (checkText || checkChildren) { + if (child) { + return false; + } + + obj.data = data; + callback(obj); + + return; + } + } + + if (child) { + return true; + } + + var tag = self.createTag(params); + + if (tag != null) { + var $option = self.option(tag); + $option.attr('data-select2-tag', true); + + self.addOptions([$option]); + + self.insertTag(data, tag); + } + + obj.results = data; + + callback(obj); + } + + decorated.call(this, params, wrapper); + }; + + Tags.prototype.createTag = function (decorated, params) { + var term = $.trim(params.term); + + if (term === '') { + return null; + } + + return { + id: term, + text: term + }; + }; + + Tags.prototype.insertTag = function (_, data, tag) { + data.unshift(tag); + }; + + Tags.prototype._removeOldTags = function (_) { + var tag = this._lastTag; + + var $options = this.$element.find('option[data-select2-tag]'); + + $options.each(function () { + if (this.selected) { + return; + } + + $(this).remove(); + }); + }; + + return Tags; +}); + +S2.define('select2/data/tokenizer',[ + 'jquery' +], function ($) { + function Tokenizer (decorated, $element, options) { + var tokenizer = options.get('tokenizer'); + + if (tokenizer !== undefined) { + this.tokenizer = tokenizer; + } + + decorated.call(this, $element, options); + } + + Tokenizer.prototype.bind = function (decorated, container, $container) { + decorated.call(this, container, $container); + + this.$search = container.dropdown.$search || container.selection.$search || + $container.find('.select2-search__field'); + }; + + Tokenizer.prototype.query = function (decorated, params, callback) { + var self = this; + + function createAndSelect (data) { + // Normalize the data object so we can use it for checks + var item = self._normalizeItem(data); + + // Check if the data object already exists as a tag + // Select it if it doesn't + var $existingOptions = self.$element.find('option').filter(function () { + return $(this).val() === item.id; + }); + + // If an existing option wasn't found for it, create the option + if (!$existingOptions.length) { + var $option = self.option(item); + $option.attr('data-select2-tag', true); + + self._removeOldTags(); + self.addOptions([$option]); + } + + // Select the item, now that we know there is an option for it + select(item); + } + + function select (data) { + self.trigger('select', { + data: data + }); + } + + params.term = params.term || ''; + + var tokenData = this.tokenizer(params, this.options, createAndSelect); + + if (tokenData.term !== params.term) { + // Replace the search term if we have the search box + if (this.$search.length) { + this.$search.val(tokenData.term); + this.$search.focus(); + } + + params.term = tokenData.term; + } + + decorated.call(this, params, callback); + }; + + Tokenizer.prototype.tokenizer = function (_, params, options, callback) { + var separators = options.get('tokenSeparators') || []; + var term = params.term; + var i = 0; + + var createTag = this.createTag || function (params) { + return { + id: params.term, + text: params.term + }; + }; + + while (i < term.length) { + var termChar = term[i]; + + if ($.inArray(termChar, separators) === -1) { + i++; + + continue; + } + + var part = term.substr(0, i); + var partParams = $.extend({}, params, { + term: part + }); + + var data = createTag(partParams); + + if (data == null) { + i++; + continue; + } + + callback(data); + + // Reset the term to not include the tokenized portion + term = term.substr(i + 1) || ''; + i = 0; + } + + return { + term: term + }; + }; + + return Tokenizer; +}); + +S2.define('select2/data/minimumInputLength',[ + +], function () { + function MinimumInputLength (decorated, $e, options) { + this.minimumInputLength = options.get('minimumInputLength'); + + decorated.call(this, $e, options); + } + + MinimumInputLength.prototype.query = function (decorated, params, callback) { + params.term = params.term || ''; + + if (params.term.length < this.minimumInputLength) { + this.trigger('results:message', { + message: 'inputTooShort', + args: { + minimum: this.minimumInputLength, + input: params.term, + params: params + } + }); + + return; + } + + decorated.call(this, params, callback); + }; + + return MinimumInputLength; +}); + +S2.define('select2/data/maximumInputLength',[ + +], function () { + function MaximumInputLength (decorated, $e, options) { + this.maximumInputLength = options.get('maximumInputLength'); + + decorated.call(this, $e, options); + } + + MaximumInputLength.prototype.query = function (decorated, params, callback) { + params.term = params.term || ''; + + if (this.maximumInputLength > 0 && + params.term.length > this.maximumInputLength) { + this.trigger('results:message', { + message: 'inputTooLong', + args: { + maximum: this.maximumInputLength, + input: params.term, + params: params + } + }); + + return; + } + + decorated.call(this, params, callback); + }; + + return MaximumInputLength; +}); + +S2.define('select2/data/maximumSelectionLength',[ + +], function (){ + function MaximumSelectionLength (decorated, $e, options) { + this.maximumSelectionLength = options.get('maximumSelectionLength'); + + decorated.call(this, $e, options); + } + + MaximumSelectionLength.prototype.query = + function (decorated, params, callback) { + var self = this; + + this.current(function (currentData) { + var count = currentData != null ? currentData.length : 0; + if (self.maximumSelectionLength > 0 && + count >= self.maximumSelectionLength) { + self.trigger('results:message', { + message: 'maximumSelected', + args: { + maximum: self.maximumSelectionLength + } + }); + return; + } + decorated.call(self, params, callback); + }); + }; + + return MaximumSelectionLength; +}); + +S2.define('select2/dropdown',[ + 'jquery', + './utils' +], function ($, Utils) { + function Dropdown ($element, options) { + this.$element = $element; + this.options = options; + + Dropdown.__super__.constructor.call(this); + } + + Utils.Extend(Dropdown, Utils.Observable); + + Dropdown.prototype.render = function () { + var $dropdown = $( + '' + + '' + + '' + ); + + $dropdown.attr('dir', this.options.get('dir')); + + this.$dropdown = $dropdown; + + return $dropdown; + }; + + Dropdown.prototype.bind = function () { + // Should be implemented in subclasses + }; + + Dropdown.prototype.position = function ($dropdown, $container) { + // Should be implmented in subclasses + }; + + Dropdown.prototype.destroy = function () { + // Remove the dropdown from the DOM + this.$dropdown.remove(); + }; + + return Dropdown; +}); + +S2.define('select2/dropdown/search',[ + 'jquery', + '../utils' +], function ($, Utils) { + function Search () { } + + Search.prototype.render = function (decorated) { + var $rendered = decorated.call(this); + + var $search = $( + '' + + '' + + '' + ); + + this.$searchContainer = $search; + this.$search = $search.find('input'); + + $rendered.prepend($search); + + return $rendered; + }; + + Search.prototype.bind = function (decorated, container, $container) { + var self = this; + + decorated.call(this, container, $container); + + this.$search.on('keydown', function (evt) { + self.trigger('keypress', evt); + + self._keyUpPrevented = evt.isDefaultPrevented(); + }); + + // Workaround for browsers which do not support the `input` event + // This will prevent double-triggering of events for browsers which support + // both the `keyup` and `input` events. + this.$search.on('input', function (evt) { + // Unbind the duplicated `keyup` event + $(this).off('keyup'); + }); + + this.$search.on('keyup input', function (evt) { + self.handleSearch(evt); + }); + + container.on('open', function () { + self.$search.attr('tabindex', 0); + + self.$search.focus(); + + window.setTimeout(function () { + self.$search.focus(); + }, 0); + }); + + container.on('close', function () { + self.$search.attr('tabindex', -1); + + self.$search.val(''); + }); + + container.on('focus', function () { + if (container.isOpen()) { + self.$search.focus(); + } + }); + + container.on('results:all', function (params) { + if (params.query.term == null || params.query.term === '') { + var showSearch = self.showSearch(params); + + if (showSearch) { + self.$searchContainer.removeClass('select2-search--hide'); + } else { + self.$searchContainer.addClass('select2-search--hide'); + } + } + }); + }; + + Search.prototype.handleSearch = function (evt) { + if (!this._keyUpPrevented) { + var input = this.$search.val(); + + this.trigger('query', { + term: input + }); + } + + this._keyUpPrevented = false; + }; + + Search.prototype.showSearch = function (_, params) { + return true; + }; + + return Search; +}); + +S2.define('select2/dropdown/hidePlaceholder',[ + +], function () { + function HidePlaceholder (decorated, $element, options, dataAdapter) { + this.placeholder = this.normalizePlaceholder(options.get('placeholder')); + + decorated.call(this, $element, options, dataAdapter); + } + + HidePlaceholder.prototype.append = function (decorated, data) { + data.results = this.removePlaceholder(data.results); + + decorated.call(this, data); + }; + + HidePlaceholder.prototype.normalizePlaceholder = function (_, placeholder) { + if (typeof placeholder === 'string') { + placeholder = { + id: '', + text: placeholder + }; + } + + return placeholder; + }; + + HidePlaceholder.prototype.removePlaceholder = function (_, data) { + var modifiedData = data.slice(0); + + for (var d = data.length - 1; d >= 0; d--) { + var item = data[d]; + + if (this.placeholder.id === item.id) { + modifiedData.splice(d, 1); + } + } + + return modifiedData; + }; + + return HidePlaceholder; +}); + +S2.define('select2/dropdown/infiniteScroll',[ + 'jquery' +], function ($) { + function InfiniteScroll (decorated, $element, options, dataAdapter) { + this.lastParams = {}; + + decorated.call(this, $element, options, dataAdapter); + + this.$loadingMore = this.createLoadingMore(); + this.loading = false; + } + + InfiniteScroll.prototype.append = function (decorated, data) { + this.$loadingMore.remove(); + this.loading = false; + + decorated.call(this, data); + + if (this.showLoadingMore(data)) { + this.$results.append(this.$loadingMore); + } + }; + + InfiniteScroll.prototype.bind = function (decorated, container, $container) { + var self = this; + + decorated.call(this, container, $container); + + container.on('query', function (params) { + self.lastParams = params; + self.loading = true; + }); + + container.on('query:append', function (params) { + self.lastParams = params; + self.loading = true; + }); + + this.$results.on('scroll', function () { + var isLoadMoreVisible = $.contains( + document.documentElement, + self.$loadingMore[0] + ); + + if (self.loading || !isLoadMoreVisible) { + return; + } + + var currentOffset = self.$results.offset().top + + self.$results.outerHeight(false); + var loadingMoreOffset = self.$loadingMore.offset().top + + self.$loadingMore.outerHeight(false); + + if (currentOffset + 50 >= loadingMoreOffset) { + self.loadMore(); + } + }); + }; + + InfiniteScroll.prototype.loadMore = function () { + this.loading = true; + + var params = $.extend({}, {page: 1}, this.lastParams); + + params.page++; + + this.trigger('query:append', params); + }; + + InfiniteScroll.prototype.showLoadingMore = function (_, data) { + return data.pagination && data.pagination.more; + }; + + InfiniteScroll.prototype.createLoadingMore = function () { + var $option = $( + '
      • ' + ); + + var message = this.options.get('translations').get('loadingMore'); + + $option.html(message(this.lastParams)); + + return $option; + }; + + return InfiniteScroll; +}); + +S2.define('select2/dropdown/attachBody',[ + 'jquery', + '../utils' +], function ($, Utils) { + function AttachBody (decorated, $element, options) { + this.$dropdownParent = options.get('dropdownParent') || $(document.body); + + decorated.call(this, $element, options); + } + + AttachBody.prototype.bind = function (decorated, container, $container) { + var self = this; + + var setupResultsEvents = false; + + decorated.call(this, container, $container); + + container.on('open', function () { + self._showDropdown(); + self._attachPositioningHandler(container); + + if (!setupResultsEvents) { + setupResultsEvents = true; + + container.on('results:all', function () { + self._positionDropdown(); + self._resizeDropdown(); + }); + + container.on('results:append', function () { + self._positionDropdown(); + self._resizeDropdown(); + }); + } + }); + + container.on('close', function () { + self._hideDropdown(); + self._detachPositioningHandler(container); + }); + + this.$dropdownContainer.on('mousedown', function (evt) { + evt.stopPropagation(); + }); + }; + + AttachBody.prototype.destroy = function (decorated) { + decorated.call(this); + + this.$dropdownContainer.remove(); + }; + + AttachBody.prototype.position = function (decorated, $dropdown, $container) { + // Clone all of the container classes + $dropdown.attr('class', $container.attr('class')); + + $dropdown.removeClass('select2'); + $dropdown.addClass('select2-container--open'); + + $dropdown.css({ + position: 'absolute', + top: -999999 + }); + + this.$container = $container; + }; + + AttachBody.prototype.render = function (decorated) { + var $container = $(''); + + var $dropdown = decorated.call(this); + $container.append($dropdown); + + this.$dropdownContainer = $container; + + return $container; + }; + + AttachBody.prototype._hideDropdown = function (decorated) { + this.$dropdownContainer.detach(); + }; + + AttachBody.prototype._attachPositioningHandler = + function (decorated, container) { + var self = this; + + var scrollEvent = 'scroll.select2.' + container.id; + var resizeEvent = 'resize.select2.' + container.id; + var orientationEvent = 'orientationchange.select2.' + container.id; + + var $watchers = this.$container.parents().filter(Utils.hasScroll); + $watchers.each(function () { + $(this).data('select2-scroll-position', { + x: $(this).scrollLeft(), + y: $(this).scrollTop() + }); + }); + + $watchers.on(scrollEvent, function (ev) { + var position = $(this).data('select2-scroll-position'); + $(this).scrollTop(position.y); + }); + + $(window).on(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent, + function (e) { + self._positionDropdown(); + self._resizeDropdown(); + }); + }; + + AttachBody.prototype._detachPositioningHandler = + function (decorated, container) { + var scrollEvent = 'scroll.select2.' + container.id; + var resizeEvent = 'resize.select2.' + container.id; + var orientationEvent = 'orientationchange.select2.' + container.id; + + var $watchers = this.$container.parents().filter(Utils.hasScroll); + $watchers.off(scrollEvent); + + $(window).off(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent); + }; + + AttachBody.prototype._positionDropdown = function () { + var $window = $(window); + + var isCurrentlyAbove = this.$dropdown.hasClass('select2-dropdown--above'); + var isCurrentlyBelow = this.$dropdown.hasClass('select2-dropdown--below'); + + var newDirection = null; + + var offset = this.$container.offset(); + + offset.bottom = offset.top + this.$container.outerHeight(false); + + var container = { + height: this.$container.outerHeight(false) + }; + + container.top = offset.top; + container.bottom = offset.top + container.height; + + var dropdown = { + height: this.$dropdown.outerHeight(false) + }; + + var viewport = { + top: $window.scrollTop(), + bottom: $window.scrollTop() + $window.height() + }; + + var enoughRoomAbove = viewport.top < (offset.top - dropdown.height); + var enoughRoomBelow = viewport.bottom > (offset.bottom + dropdown.height); + + var css = { + left: offset.left, + top: container.bottom + }; + + // Determine what the parent element is to use for calciulating the offset + var $offsetParent = this.$dropdownParent; + + // For statically positoned elements, we need to get the element + // that is determining the offset + if ($offsetParent.css('position') === 'static') { + $offsetParent = $offsetParent.offsetParent(); + } + + var parentOffset = $offsetParent.offset(); + + css.top -= parentOffset.top; + css.left -= parentOffset.left; + + if (!isCurrentlyAbove && !isCurrentlyBelow) { + newDirection = 'below'; + } + + if (!enoughRoomBelow && enoughRoomAbove && !isCurrentlyAbove) { + newDirection = 'above'; + } else if (!enoughRoomAbove && enoughRoomBelow && isCurrentlyAbove) { + newDirection = 'below'; + } + + if (newDirection == 'above' || + (isCurrentlyAbove && newDirection !== 'below')) { + css.top = container.top - parentOffset.top - dropdown.height; + } + + if (newDirection != null) { + this.$dropdown + .removeClass('select2-dropdown--below select2-dropdown--above') + .addClass('select2-dropdown--' + newDirection); + this.$container + .removeClass('select2-container--below select2-container--above') + .addClass('select2-container--' + newDirection); + } + + this.$dropdownContainer.css(css); + }; + + AttachBody.prototype._resizeDropdown = function () { + var css = { + width: this.$container.outerWidth(false) + 'px' + }; + + if (this.options.get('dropdownAutoWidth')) { + css.minWidth = css.width; + css.position = 'relative'; + css.width = 'auto'; + } + + this.$dropdown.css(css); + }; + + AttachBody.prototype._showDropdown = function (decorated) { + this.$dropdownContainer.appendTo(this.$dropdownParent); + + this._positionDropdown(); + this._resizeDropdown(); + }; + + return AttachBody; +}); + +S2.define('select2/dropdown/minimumResultsForSearch',[ + +], function () { + function countResults (data) { + var count = 0; + + for (var d = 0; d < data.length; d++) { + var item = data[d]; + + if (item.children) { + count += countResults(item.children); + } else { + count++; + } + } + + return count; + } + + function MinimumResultsForSearch (decorated, $element, options, dataAdapter) { + this.minimumResultsForSearch = options.get('minimumResultsForSearch'); + + if (this.minimumResultsForSearch < 0) { + this.minimumResultsForSearch = Infinity; + } + + decorated.call(this, $element, options, dataAdapter); + } + + MinimumResultsForSearch.prototype.showSearch = function (decorated, params) { + if (countResults(params.data.results) < this.minimumResultsForSearch) { + return false; + } + + return decorated.call(this, params); + }; + + return MinimumResultsForSearch; +}); + +S2.define('select2/dropdown/selectOnClose',[ + +], function () { + function SelectOnClose () { } + + SelectOnClose.prototype.bind = function (decorated, container, $container) { + var self = this; + + decorated.call(this, container, $container); + + container.on('close', function (params) { + self._handleSelectOnClose(params); + }); + }; + + SelectOnClose.prototype._handleSelectOnClose = function (_, params) { + if (params && params.originalSelect2Event != null) { + var event = params.originalSelect2Event; + + // Don't select an item if the close event was triggered from a select or + // unselect event + if (event._type === 'select' || event._type === 'unselect') { + return; + } + } + + var $highlightedResults = this.getHighlightedResults(); + + // Only select highlighted results + if ($highlightedResults.length < 1) { + return; + } + + var data = $highlightedResults.data('data'); + + // Don't re-select already selected resulte + if ( + (data.element != null && data.element.selected) || + (data.element == null && data.selected) + ) { + return; + } + + this.trigger('select', { + data: data + }); + }; + + return SelectOnClose; +}); + +S2.define('select2/dropdown/closeOnSelect',[ + +], function () { + function CloseOnSelect () { } + + CloseOnSelect.prototype.bind = function (decorated, container, $container) { + var self = this; + + decorated.call(this, container, $container); + + container.on('select', function (evt) { + self._selectTriggered(evt); + }); + + container.on('unselect', function (evt) { + self._selectTriggered(evt); + }); + }; + + CloseOnSelect.prototype._selectTriggered = function (_, evt) { + var originalEvent = evt.originalEvent; + + // Don't close if the control key is being held + if (originalEvent && originalEvent.ctrlKey) { + return; + } + + this.trigger('close', { + originalEvent: originalEvent, + originalSelect2Event: evt + }); + }; + + return CloseOnSelect; +}); + +S2.define('select2/i18n/en',[],function () { + // English + return { + errorLoading: function () { + return 'The results could not be loaded.'; + }, + inputTooLong: function (args) { + var overChars = args.input.length - args.maximum; + + var message = 'Please delete ' + overChars + ' character'; + + if (overChars != 1) { + message += 's'; + } + + return message; + }, + inputTooShort: function (args) { + var remainingChars = args.minimum - args.input.length; + + var message = 'Please enter ' + remainingChars + ' or more characters'; + + return message; + }, + loadingMore: function () { + return 'Loading more results…'; + }, + maximumSelected: function (args) { + var message = 'You can only select ' + args.maximum + ' item'; + + if (args.maximum != 1) { + message += 's'; + } + + return message; + }, + noResults: function () { + return 'No results found'; + }, + searching: function () { + return 'Searching…'; + } + }; +}); + +S2.define('select2/defaults',[ + 'jquery', + 'require', + + './results', + + './selection/single', + './selection/multiple', + './selection/placeholder', + './selection/allowClear', + './selection/search', + './selection/eventRelay', + + './utils', + './translation', + './diacritics', + + './data/select', + './data/array', + './data/ajax', + './data/tags', + './data/tokenizer', + './data/minimumInputLength', + './data/maximumInputLength', + './data/maximumSelectionLength', + + './dropdown', + './dropdown/search', + './dropdown/hidePlaceholder', + './dropdown/infiniteScroll', + './dropdown/attachBody', + './dropdown/minimumResultsForSearch', + './dropdown/selectOnClose', + './dropdown/closeOnSelect', + + './i18n/en' +], function ($, require, + + ResultsList, + + SingleSelection, MultipleSelection, Placeholder, AllowClear, + SelectionSearch, EventRelay, + + Utils, Translation, DIACRITICS, + + SelectData, ArrayData, AjaxData, Tags, Tokenizer, + MinimumInputLength, MaximumInputLength, MaximumSelectionLength, + + Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll, + AttachBody, MinimumResultsForSearch, SelectOnClose, CloseOnSelect, + + EnglishTranslation) { + function Defaults () { + this.reset(); + } + + Defaults.prototype.apply = function (options) { + options = $.extend(true, {}, this.defaults, options); + + if (options.dataAdapter == null) { + if (options.ajax != null) { + options.dataAdapter = AjaxData; + } else if (options.data != null) { + options.dataAdapter = ArrayData; + } else { + options.dataAdapter = SelectData; + } + + if (options.minimumInputLength > 0) { + options.dataAdapter = Utils.Decorate( + options.dataAdapter, + MinimumInputLength + ); + } + + if (options.maximumInputLength > 0) { + options.dataAdapter = Utils.Decorate( + options.dataAdapter, + MaximumInputLength + ); + } + + if (options.maximumSelectionLength > 0) { + options.dataAdapter = Utils.Decorate( + options.dataAdapter, + MaximumSelectionLength + ); + } + + if (options.tags) { + options.dataAdapter = Utils.Decorate(options.dataAdapter, Tags); + } + + if (options.tokenSeparators != null || options.tokenizer != null) { + options.dataAdapter = Utils.Decorate( + options.dataAdapter, + Tokenizer + ); + } + + if (options.query != null) { + var Query = require(options.amdBase + 'compat/query'); + + options.dataAdapter = Utils.Decorate( + options.dataAdapter, + Query + ); + } + + if (options.initSelection != null) { + var InitSelection = require(options.amdBase + 'compat/initSelection'); + + options.dataAdapter = Utils.Decorate( + options.dataAdapter, + InitSelection + ); + } + } + + if (options.resultsAdapter == null) { + options.resultsAdapter = ResultsList; + + if (options.ajax != null) { + options.resultsAdapter = Utils.Decorate( + options.resultsAdapter, + InfiniteScroll + ); + } + + if (options.placeholder != null) { + options.resultsAdapter = Utils.Decorate( + options.resultsAdapter, + HidePlaceholder + ); + } + + if (options.selectOnClose) { + options.resultsAdapter = Utils.Decorate( + options.resultsAdapter, + SelectOnClose + ); + } + } + + if (options.dropdownAdapter == null) { + if (options.multiple) { + options.dropdownAdapter = Dropdown; + } else { + var SearchableDropdown = Utils.Decorate(Dropdown, DropdownSearch); + + options.dropdownAdapter = SearchableDropdown; + } + + if (options.minimumResultsForSearch !== 0) { + options.dropdownAdapter = Utils.Decorate( + options.dropdownAdapter, + MinimumResultsForSearch + ); + } + + if (options.closeOnSelect) { + options.dropdownAdapter = Utils.Decorate( + options.dropdownAdapter, + CloseOnSelect + ); + } + + if ( + options.dropdownCssClass != null || + options.dropdownCss != null || + options.adaptDropdownCssClass != null + ) { + var DropdownCSS = require(options.amdBase + 'compat/dropdownCss'); + + options.dropdownAdapter = Utils.Decorate( + options.dropdownAdapter, + DropdownCSS + ); + } + + options.dropdownAdapter = Utils.Decorate( + options.dropdownAdapter, + AttachBody + ); + } + + if (options.selectionAdapter == null) { + if (options.multiple) { + options.selectionAdapter = MultipleSelection; + } else { + options.selectionAdapter = SingleSelection; + } + + // Add the placeholder mixin if a placeholder was specified + if (options.placeholder != null) { + options.selectionAdapter = Utils.Decorate( + options.selectionAdapter, + Placeholder + ); + } + + if (options.allowClear) { + options.selectionAdapter = Utils.Decorate( + options.selectionAdapter, + AllowClear + ); + } + + if (options.multiple) { + options.selectionAdapter = Utils.Decorate( + options.selectionAdapter, + SelectionSearch + ); + } + + if ( + options.containerCssClass != null || + options.containerCss != null || + options.adaptContainerCssClass != null + ) { + var ContainerCSS = require(options.amdBase + 'compat/containerCss'); + + options.selectionAdapter = Utils.Decorate( + options.selectionAdapter, + ContainerCSS + ); + } + + options.selectionAdapter = Utils.Decorate( + options.selectionAdapter, + EventRelay + ); + } + + if (typeof options.language === 'string') { + // Check if the language is specified with a region + if (options.language.indexOf('-') > 0) { + // Extract the region information if it is included + var languageParts = options.language.split('-'); + var baseLanguage = languageParts[0]; + + options.language = [options.language, baseLanguage]; + } else { + options.language = [options.language]; + } + } + + if ($.isArray(options.language)) { + var languages = new Translation(); + options.language.push('en'); + + var languageNames = options.language; + + for (var l = 0; l < languageNames.length; l++) { + var name = languageNames[l]; + var language = {}; + + try { + // Try to load it with the original name + language = Translation.loadPath(name); + } catch (e) { + try { + // If we couldn't load it, check if it wasn't the full path + name = this.defaults.amdLanguageBase + name; + language = Translation.loadPath(name); + } catch (ex) { + // The translation could not be loaded at all. Sometimes this is + // because of a configuration problem, other times this can be + // because of how Select2 helps load all possible translation files. + if (options.debug && window.console && console.warn) { + console.warn( + 'Select2: The language file for "' + name + '" could not be ' + + 'automatically loaded. A fallback will be used instead.' + ); + } + + continue; + } + } + + languages.extend(language); + } + + options.translations = languages; + } else { + var baseTranslation = Translation.loadPath( + this.defaults.amdLanguageBase + 'en' + ); + var customTranslation = new Translation(options.language); + + customTranslation.extend(baseTranslation); + + options.translations = customTranslation; + } + + return options; + }; + + Defaults.prototype.reset = function () { + function stripDiacritics (text) { + // Used 'uni range + named function' from http://jsperf.com/diacritics/18 + function match(a) { + return DIACRITICS[a] || a; + } + + return text.replace(/[^\u0000-\u007E]/g, match); + } + + function matcher (params, data) { + // Always return the object if there is nothing to compare + if ($.trim(params.term) === '') { + return data; + } + + // Do a recursive check for options with children + if (data.children && data.children.length > 0) { + // Clone the data object if there are children + // This is required as we modify the object to remove any non-matches + var match = $.extend(true, {}, data); + + // Check each child of the option + for (var c = data.children.length - 1; c >= 0; c--) { + var child = data.children[c]; + + var matches = matcher(params, child); + + // If there wasn't a match, remove the object in the array + if (matches == null) { + match.children.splice(c, 1); + } + } + + // If any children matched, return the new object + if (match.children.length > 0) { + return match; + } + + // If there were no matching children, check just the plain object + return matcher(params, match); + } + + var original = stripDiacritics(data.text).toUpperCase(); + var term = stripDiacritics(params.term).toUpperCase(); + + // Check if the text contains the term + if (original.indexOf(term) > -1) { + return data; + } + + // If it doesn't contain the term, don't return anything + return null; + } + + this.defaults = { + amdBase: './', + amdLanguageBase: './i18n/', + closeOnSelect: true, + debug: false, + dropdownAutoWidth: false, + escapeMarkup: Utils.escapeMarkup, + language: EnglishTranslation, + matcher: matcher, + minimumInputLength: 0, + maximumInputLength: 0, + maximumSelectionLength: 0, + minimumResultsForSearch: 0, + selectOnClose: false, + sorter: function (data) { + return data; + }, + templateResult: function (result) { + return result.text; + }, + templateSelection: function (selection) { + return selection.text; + }, + theme: 'default', + width: 'resolve' + }; + }; + + Defaults.prototype.set = function (key, value) { + var camelKey = $.camelCase(key); + + var data = {}; + data[camelKey] = value; + + var convertedData = Utils._convertData(data); + + $.extend(this.defaults, convertedData); + }; + + var defaults = new Defaults(); + + return defaults; +}); + +S2.define('select2/options',[ + 'require', + 'jquery', + './defaults', + './utils' +], function (require, $, Defaults, Utils) { + function Options (options, $element) { + this.options = options; + + if ($element != null) { + this.fromElement($element); + } + + this.options = Defaults.apply(this.options); + + if ($element && $element.is('input')) { + var InputCompat = require(this.get('amdBase') + 'compat/inputData'); + + this.options.dataAdapter = Utils.Decorate( + this.options.dataAdapter, + InputCompat + ); + } + } + + Options.prototype.fromElement = function ($e) { + var excludedData = ['select2']; + + if (this.options.multiple == null) { + this.options.multiple = $e.prop('multiple'); + } + + if (this.options.disabled == null) { + this.options.disabled = $e.prop('disabled'); + } + + if (this.options.language == null) { + if ($e.prop('lang')) { + this.options.language = $e.prop('lang').toLowerCase(); + } else if ($e.closest('[lang]').prop('lang')) { + this.options.language = $e.closest('[lang]').prop('lang'); + } + } + + if (this.options.dir == null) { + if ($e.prop('dir')) { + this.options.dir = $e.prop('dir'); + } else if ($e.closest('[dir]').prop('dir')) { + this.options.dir = $e.closest('[dir]').prop('dir'); + } else { + this.options.dir = 'ltr'; + } + } + + $e.prop('disabled', this.options.disabled); + $e.prop('multiple', this.options.multiple); + + if ($e.data('select2Tags')) { + if (this.options.debug && window.console && console.warn) { + console.warn( + 'Select2: The `data-select2-tags` attribute has been changed to ' + + 'use the `data-data` and `data-tags="true"` attributes and will be ' + + 'removed in future versions of Select2.' + ); + } + + $e.data('data', $e.data('select2Tags')); + $e.data('tags', true); + } + + if ($e.data('ajaxUrl')) { + if (this.options.debug && window.console && console.warn) { + console.warn( + 'Select2: The `data-ajax-url` attribute has been changed to ' + + '`data-ajax--url` and support for the old attribute will be removed' + + ' in future versions of Select2.' + ); + } + + $e.attr('ajax--url', $e.data('ajaxUrl')); + $e.data('ajax--url', $e.data('ajaxUrl')); + } + + var dataset = {}; + + // Prefer the element's `dataset` attribute if it exists + // jQuery 1.x does not correctly handle data attributes with multiple dashes + if ($.fn.jquery && $.fn.jquery.substr(0, 2) == '1.' && $e[0].dataset) { + dataset = $.extend(true, {}, $e[0].dataset, $e.data()); + } else { + dataset = $e.data(); + } + + var data = $.extend(true, {}, dataset); + + data = Utils._convertData(data); + + for (var key in data) { + if ($.inArray(key, excludedData) > -1) { + continue; + } + + if ($.isPlainObject(this.options[key])) { + $.extend(this.options[key], data[key]); + } else { + this.options[key] = data[key]; + } + } + + return this; + }; + + Options.prototype.get = function (key) { + return this.options[key]; + }; + + Options.prototype.set = function (key, val) { + this.options[key] = val; + }; + + return Options; +}); + +S2.define('select2/core',[ + 'jquery', + './options', + './utils', + './keys' +], function ($, Options, Utils, KEYS) { + var Select2 = function ($element, options) { + if ($element.data('select2') != null) { + $element.data('select2').destroy(); + } + + this.$element = $element; + + this.id = this._generateId($element); + + options = options || {}; + + this.options = new Options(options, $element); + + Select2.__super__.constructor.call(this); + + // Set up the tabindex + + var tabindex = $element.attr('tabindex') || 0; + $element.data('old-tabindex', tabindex); + $element.attr('tabindex', '-1'); + + // Set up containers and adapters + + var DataAdapter = this.options.get('dataAdapter'); + this.dataAdapter = new DataAdapter($element, this.options); + + var $container = this.render(); + + this._placeContainer($container); + + var SelectionAdapter = this.options.get('selectionAdapter'); + this.selection = new SelectionAdapter($element, this.options); + this.$selection = this.selection.render(); + + this.selection.position(this.$selection, $container); + + var DropdownAdapter = this.options.get('dropdownAdapter'); + this.dropdown = new DropdownAdapter($element, this.options); + this.$dropdown = this.dropdown.render(); + + this.dropdown.position(this.$dropdown, $container); + + var ResultsAdapter = this.options.get('resultsAdapter'); + this.results = new ResultsAdapter($element, this.options, this.dataAdapter); + this.$results = this.results.render(); + + this.results.position(this.$results, this.$dropdown); + + // Bind events + + var self = this; + + // Bind the container to all of the adapters + this._bindAdapters(); + + // Register any DOM event handlers + this._registerDomEvents(); + + // Register any internal event handlers + this._registerDataEvents(); + this._registerSelectionEvents(); + this._registerDropdownEvents(); + this._registerResultsEvents(); + this._registerEvents(); + + // Set the initial state + this.dataAdapter.current(function (initialData) { + self.trigger('selection:update', { + data: initialData + }); + }); + + // Hide the original select + $element.addClass('select2-hidden-accessible'); + $element.attr('aria-hidden', 'true'); + + // Synchronize any monitored attributes + this._syncAttributes(); + + $element.data('select2', this); + }; + + Utils.Extend(Select2, Utils.Observable); + + Select2.prototype._generateId = function ($element) { + var id = ''; + + if ($element.attr('id') != null) { + id = $element.attr('id'); + } else if ($element.attr('name') != null) { + id = $element.attr('name') + '-' + Utils.generateChars(2); + } else { + id = Utils.generateChars(4); + } + + id = id.replace(/(:|\.|\[|\]|,)/g, ''); + id = 'select2-' + id; + + return id; + }; + + Select2.prototype._placeContainer = function ($container) { + $container.insertAfter(this.$element); + + var width = this._resolveWidth(this.$element, this.options.get('width')); + + if (width != null) { + $container.css('width', width); + } + }; + + Select2.prototype._resolveWidth = function ($element, method) { + var WIDTH = /^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i; + + if (method == 'resolve') { + var styleWidth = this._resolveWidth($element, 'style'); + + if (styleWidth != null) { + return styleWidth; + } + + return this._resolveWidth($element, 'element'); + } + + if (method == 'element') { + var elementWidth = $element.outerWidth(false); + + if (elementWidth <= 0) { + return 'auto'; + } + + return elementWidth + 'px'; + } + + if (method == 'style') { + var style = $element.attr('style'); + + if (typeof(style) !== 'string') { + return null; + } + + var attrs = style.split(';'); + + for (var i = 0, l = attrs.length; i < l; i = i + 1) { + var attr = attrs[i].replace(/\s/g, ''); + var matches = attr.match(WIDTH); + + if (matches !== null && matches.length >= 1) { + return matches[1]; + } + } + + return null; + } + + return method; + }; + + Select2.prototype._bindAdapters = function () { + this.dataAdapter.bind(this, this.$container); + this.selection.bind(this, this.$container); + + this.dropdown.bind(this, this.$container); + this.results.bind(this, this.$container); + }; + + Select2.prototype._registerDomEvents = function () { + var self = this; + + this.$element.on('change.select2', function () { + self.dataAdapter.current(function (data) { + self.trigger('selection:update', { + data: data + }); + }); + }); + + this.$element.on('focus.select2', function (evt) { + self.trigger('focus', evt); + }); + + this._syncA = Utils.bind(this._syncAttributes, this); + this._syncS = Utils.bind(this._syncSubtree, this); + + if (this.$element[0].attachEvent) { + this.$element[0].attachEvent('onpropertychange', this._syncA); + } + + var observer = window.MutationObserver || + window.WebKitMutationObserver || + window.MozMutationObserver + ; + + if (observer != null) { + this._observer = new observer(function (mutations) { + $.each(mutations, self._syncA); + $.each(mutations, self._syncS); + }); + this._observer.observe(this.$element[0], { + attributes: true, + childList: true, + subtree: false + }); + } else if (this.$element[0].addEventListener) { + this.$element[0].addEventListener( + 'DOMAttrModified', + self._syncA, + false + ); + this.$element[0].addEventListener( + 'DOMNodeInserted', + self._syncS, + false + ); + this.$element[0].addEventListener( + 'DOMNodeRemoved', + self._syncS, + false + ); + } + }; + + Select2.prototype._registerDataEvents = function () { + var self = this; + + this.dataAdapter.on('*', function (name, params) { + self.trigger(name, params); + }); + }; + + Select2.prototype._registerSelectionEvents = function () { + var self = this; + var nonRelayEvents = ['toggle', 'focus']; + + this.selection.on('toggle', function () { + self.toggleDropdown(); + }); + + this.selection.on('focus', function (params) { + self.focus(params); + }); + + this.selection.on('*', function (name, params) { + if ($.inArray(name, nonRelayEvents) !== -1) { + return; + } + + self.trigger(name, params); + }); + }; + + Select2.prototype._registerDropdownEvents = function () { + var self = this; + + this.dropdown.on('*', function (name, params) { + self.trigger(name, params); + }); + }; + + Select2.prototype._registerResultsEvents = function () { + var self = this; + + this.results.on('*', function (name, params) { + self.trigger(name, params); + }); + }; + + Select2.prototype._registerEvents = function () { + var self = this; + + this.on('open', function () { + self.$container.addClass('select2-container--open'); + }); + + this.on('close', function () { + self.$container.removeClass('select2-container--open'); + }); + + this.on('enable', function () { + self.$container.removeClass('select2-container--disabled'); + }); + + this.on('disable', function () { + self.$container.addClass('select2-container--disabled'); + }); + + this.on('blur', function () { + self.$container.removeClass('select2-container--focus'); + }); + + this.on('query', function (params) { + if (!self.isOpen()) { + self.trigger('open', {}); + } + + this.dataAdapter.query(params, function (data) { + self.trigger('results:all', { + data: data, + query: params + }); + }); + }); + + this.on('query:append', function (params) { + this.dataAdapter.query(params, function (data) { + self.trigger('results:append', { + data: data, + query: params + }); + }); + }); + + this.on('keypress', function (evt) { + var key = evt.which; + + if (self.isOpen()) { + if (key === KEYS.ESC || key === KEYS.TAB || + (key === KEYS.UP && evt.altKey)) { + self.close(); + + evt.preventDefault(); + } else if (key === KEYS.ENTER) { + self.trigger('results:select', {}); + + evt.preventDefault(); + } else if ((key === KEYS.SPACE && evt.ctrlKey)) { + self.trigger('results:toggle', {}); + + evt.preventDefault(); + } else if (key === KEYS.UP) { + self.trigger('results:previous', {}); + + evt.preventDefault(); + } else if (key === KEYS.DOWN) { + self.trigger('results:next', {}); + + evt.preventDefault(); + } + } else { + if (key === KEYS.ENTER || key === KEYS.SPACE || + (key === KEYS.DOWN && evt.altKey)) { + self.open(); + + evt.preventDefault(); + } + } + }); + }; + + Select2.prototype._syncAttributes = function () { + this.options.set('disabled', this.$element.prop('disabled')); + + if (this.options.get('disabled')) { + if (this.isOpen()) { + this.close(); + } + + this.trigger('disable', {}); + } else { + this.trigger('enable', {}); + } + }; + + Select2.prototype._syncSubtree = function (evt, mutations) { + var changed = false; + var self = this; + + // Ignore any mutation events raised for elements that aren't options or + // optgroups. This handles the case when the select element is destroyed + if ( + evt && evt.target && ( + evt.target.nodeName !== 'OPTION' && evt.target.nodeName !== 'OPTGROUP' + ) + ) { + return; + } + + if (!mutations) { + // If mutation events aren't supported, then we can only assume that the + // change affected the selections + changed = true; + } else if (mutations.addedNodes && mutations.addedNodes.length > 0) { + for (var n = 0; n < mutations.addedNodes.length; n++) { + var node = mutations.addedNodes[n]; + + if (node.selected) { + changed = true; + } + } + } else if (mutations.removedNodes && mutations.removedNodes.length > 0) { + changed = true; + } + + // Only re-pull the data if we think there is a change + if (changed) { + this.dataAdapter.current(function (currentData) { + self.trigger('selection:update', { + data: currentData + }); + }); + } + }; + + /** + * Override the trigger method to automatically trigger pre-events when + * there are events that can be prevented. + */ + Select2.prototype.trigger = function (name, args) { + var actualTrigger = Select2.__super__.trigger; + var preTriggerMap = { + 'open': 'opening', + 'close': 'closing', + 'select': 'selecting', + 'unselect': 'unselecting' + }; + + if (args === undefined) { + args = {}; + } + + if (name in preTriggerMap) { + var preTriggerName = preTriggerMap[name]; + var preTriggerArgs = { + prevented: false, + name: name, + args: args + }; + + actualTrigger.call(this, preTriggerName, preTriggerArgs); + + if (preTriggerArgs.prevented) { + args.prevented = true; + + return; + } + } + + actualTrigger.call(this, name, args); + }; + + Select2.prototype.toggleDropdown = function () { + if (this.options.get('disabled')) { + return; + } + + if (this.isOpen()) { + this.close(); + } else { + this.open(); + } + }; + + Select2.prototype.open = function () { + if (this.isOpen()) { + return; + } + + this.trigger('query', {}); + }; + + Select2.prototype.close = function () { + if (!this.isOpen()) { + return; + } + + this.trigger('close', {}); + }; + + Select2.prototype.isOpen = function () { + return this.$container.hasClass('select2-container--open'); + }; + + Select2.prototype.hasFocus = function () { + return this.$container.hasClass('select2-container--focus'); + }; + + Select2.prototype.focus = function (data) { + // No need to re-trigger focus events if we are already focused + if (this.hasFocus()) { + return; + } + + this.$container.addClass('select2-container--focus'); + this.trigger('focus', {}); + }; + + Select2.prototype.enable = function (args) { + if (this.options.get('debug') && window.console && console.warn) { + console.warn( + 'Select2: The `select2("enable")` method has been deprecated and will' + + ' be removed in later Select2 versions. Use $element.prop("disabled")' + + ' instead.' + ); + } + + if (args == null || args.length === 0) { + args = [true]; + } + + var disabled = !args[0]; + + this.$element.prop('disabled', disabled); + }; + + Select2.prototype.data = function () { + if (this.options.get('debug') && + arguments.length > 0 && window.console && console.warn) { + console.warn( + 'Select2: Data can no longer be set using `select2("data")`. You ' + + 'should consider setting the value instead using `$element.val()`.' + ); + } + + var data = []; + + this.dataAdapter.current(function (currentData) { + data = currentData; + }); + + return data; + }; + + Select2.prototype.val = function (args) { + if (this.options.get('debug') && window.console && console.warn) { + console.warn( + 'Select2: The `select2("val")` method has been deprecated and will be' + + ' removed in later Select2 versions. Use $element.val() instead.' + ); + } + + if (args == null || args.length === 0) { + return this.$element.val(); + } + + var newVal = args[0]; + + if ($.isArray(newVal)) { + newVal = $.map(newVal, function (obj) { + return obj.toString(); + }); + } + + this.$element.val(newVal).trigger('change'); + }; + + Select2.prototype.destroy = function () { + this.$container.remove(); + + if (this.$element[0].detachEvent) { + this.$element[0].detachEvent('onpropertychange', this._syncA); + } + + if (this._observer != null) { + this._observer.disconnect(); + this._observer = null; + } else if (this.$element[0].removeEventListener) { + this.$element[0] + .removeEventListener('DOMAttrModified', this._syncA, false); + this.$element[0] + .removeEventListener('DOMNodeInserted', this._syncS, false); + this.$element[0] + .removeEventListener('DOMNodeRemoved', this._syncS, false); + } + + this._syncA = null; + this._syncS = null; + + this.$element.off('.select2'); + this.$element.attr('tabindex', this.$element.data('old-tabindex')); + + this.$element.removeClass('select2-hidden-accessible'); + this.$element.attr('aria-hidden', 'false'); + this.$element.removeData('select2'); + + this.dataAdapter.destroy(); + this.selection.destroy(); + this.dropdown.destroy(); + this.results.destroy(); + + this.dataAdapter = null; + this.selection = null; + this.dropdown = null; + this.results = null; + }; + + Select2.prototype.render = function () { + var $container = $( + '' + + '' + + '' + + '' + ); + + $container.attr('dir', this.options.get('dir')); + + this.$container = $container; + + this.$container.addClass('select2-container--' + this.options.get('theme')); + + $container.data('element', this.$element); + + return $container; + }; + + return Select2; +}); + +S2.define('select2/compat/utils',[ + 'jquery' +], function ($) { + function syncCssClasses ($dest, $src, adapter) { + var classes, replacements = [], adapted; + + classes = $.trim($dest.attr('class')); + + if (classes) { + classes = '' + classes; // for IE which returns object + + $(classes.split(/\s+/)).each(function () { + // Save all Select2 classes + if (this.indexOf('select2-') === 0) { + replacements.push(this); + } + }); + } + + classes = $.trim($src.attr('class')); + + if (classes) { + classes = '' + classes; // for IE which returns object + + $(classes.split(/\s+/)).each(function () { + // Only adapt non-Select2 classes + if (this.indexOf('select2-') !== 0) { + adapted = adapter(this); + + if (adapted != null) { + replacements.push(adapted); + } + } + }); + } + + $dest.attr('class', replacements.join(' ')); + } + + return { + syncCssClasses: syncCssClasses + }; +}); + +S2.define('select2/compat/containerCss',[ + 'jquery', + './utils' +], function ($, CompatUtils) { + // No-op CSS adapter that discards all classes by default + function _containerAdapter (clazz) { + return null; + } + + function ContainerCSS () { } + + ContainerCSS.prototype.render = function (decorated) { + var $container = decorated.call(this); + + var containerCssClass = this.options.get('containerCssClass') || ''; + + if ($.isFunction(containerCssClass)) { + containerCssClass = containerCssClass(this.$element); + } + + var containerCssAdapter = this.options.get('adaptContainerCssClass'); + containerCssAdapter = containerCssAdapter || _containerAdapter; + + if (containerCssClass.indexOf(':all:') !== -1) { + containerCssClass = containerCssClass.replace(':all:', ''); + + var _cssAdapter = containerCssAdapter; + + containerCssAdapter = function (clazz) { + var adapted = _cssAdapter(clazz); + + if (adapted != null) { + // Append the old one along with the adapted one + return adapted + ' ' + clazz; + } + + return clazz; + }; + } + + var containerCss = this.options.get('containerCss') || {}; + + if ($.isFunction(containerCss)) { + containerCss = containerCss(this.$element); + } + + CompatUtils.syncCssClasses($container, this.$element, containerCssAdapter); + + $container.css(containerCss); + $container.addClass(containerCssClass); + + return $container; + }; + + return ContainerCSS; +}); + +S2.define('select2/compat/dropdownCss',[ + 'jquery', + './utils' +], function ($, CompatUtils) { + // No-op CSS adapter that discards all classes by default + function _dropdownAdapter (clazz) { + return null; + } + + function DropdownCSS () { } + + DropdownCSS.prototype.render = function (decorated) { + var $dropdown = decorated.call(this); + + var dropdownCssClass = this.options.get('dropdownCssClass') || ''; + + if ($.isFunction(dropdownCssClass)) { + dropdownCssClass = dropdownCssClass(this.$element); + } + + var dropdownCssAdapter = this.options.get('adaptDropdownCssClass'); + dropdownCssAdapter = dropdownCssAdapter || _dropdownAdapter; + + if (dropdownCssClass.indexOf(':all:') !== -1) { + dropdownCssClass = dropdownCssClass.replace(':all:', ''); + + var _cssAdapter = dropdownCssAdapter; + + dropdownCssAdapter = function (clazz) { + var adapted = _cssAdapter(clazz); + + if (adapted != null) { + // Append the old one along with the adapted one + return adapted + ' ' + clazz; + } + + return clazz; + }; + } + + var dropdownCss = this.options.get('dropdownCss') || {}; + + if ($.isFunction(dropdownCss)) { + dropdownCss = dropdownCss(this.$element); + } + + CompatUtils.syncCssClasses($dropdown, this.$element, dropdownCssAdapter); + + $dropdown.css(dropdownCss); + $dropdown.addClass(dropdownCssClass); + + return $dropdown; + }; + + return DropdownCSS; +}); + +S2.define('select2/compat/initSelection',[ + 'jquery' +], function ($) { + function InitSelection (decorated, $element, options) { + if (options.get('debug') && window.console && console.warn) { + console.warn( + 'Select2: The `initSelection` option has been deprecated in favor' + + ' of a custom data adapter that overrides the `current` method. ' + + 'This method is now called multiple times instead of a single ' + + 'time when the instance is initialized. Support will be removed ' + + 'for the `initSelection` option in future versions of Select2' + ); + } + + this.initSelection = options.get('initSelection'); + this._isInitialized = false; + + decorated.call(this, $element, options); + } + + InitSelection.prototype.current = function (decorated, callback) { + var self = this; + + if (this._isInitialized) { + decorated.call(this, callback); + + return; + } + + this.initSelection.call(null, this.$element, function (data) { + self._isInitialized = true; + + if (!$.isArray(data)) { + data = [data]; + } + + callback(data); + }); + }; + + return InitSelection; +}); + +S2.define('select2/compat/inputData',[ + 'jquery' +], function ($) { + function InputData (decorated, $element, options) { + this._currentData = []; + this._valueSeparator = options.get('valueSeparator') || ','; + + if ($element.prop('type') === 'hidden') { + if (options.get('debug') && console && console.warn) { + console.warn( + 'Select2: Using a hidden input with Select2 is no longer ' + + 'supported and may stop working in the future. It is recommended ' + + 'to use a `' + + '' + ); + + this.$searchContainer = $search; + this.$search = $search.find('input'); + + var $rendered = decorated.call(this); + + this._transferTabIndex(); + + return $rendered; + }; + + Search.prototype.bind = function (decorated, container, $container) { + var self = this; + + decorated.call(this, container, $container); + + container.on('open', function () { + self.$search.trigger('focus'); + }); + + container.on('close', function () { + self.$search.val(''); + self.$search.removeAttr('aria-activedescendant'); + self.$search.trigger('focus'); + }); + + container.on('enable', function () { + self.$search.prop('disabled', false); + + self._transferTabIndex(); + }); + + container.on('disable', function () { + self.$search.prop('disabled', true); + }); + + container.on('focus', function (evt) { + self.$search.trigger('focus'); + }); + + container.on('results:focus', function (params) { + self.$search.attr('aria-activedescendant', params.id); + }); + + this.$selection.on('focusin', '.select2-search--inline', function (evt) { + self.trigger('focus', evt); + }); + + this.$selection.on('focusout', '.select2-search--inline', function (evt) { + self._handleBlur(evt); + }); + + this.$selection.on('keydown', '.select2-search--inline', function (evt) { + evt.stopPropagation(); + + self.trigger('keypress', evt); + + self._keyUpPrevented = evt.isDefaultPrevented(); + + var key = evt.which; + + if (key === KEYS.BACKSPACE && self.$search.val() === '') { + var $previousChoice = self.$searchContainer + .prev('.select2-selection__choice'); + + if ($previousChoice.length > 0) { + var item = $previousChoice.data('data'); + + self.searchRemoveChoice(item); + + evt.preventDefault(); + } + } + }); + + // Try to detect the IE version should the `documentMode` property that + // is stored on the document. This is only implemented in IE and is + // slightly cleaner than doing a user agent check. + // This property is not available in Edge, but Edge also doesn't have + // this bug. + var msie = document.documentMode; + var disableInputEvents = msie && msie <= 11; + + // Workaround for browsers which do not support the `input` event + // This will prevent double-triggering of events for browsers which support + // both the `keyup` and `input` events. + this.$selection.on( + 'input.searchcheck', + '.select2-search--inline', + function (evt) { + // IE will trigger the `input` event when a placeholder is used on a + // search box. To get around this issue, we are forced to ignore all + // `input` events in IE and keep using `keyup`. + if (disableInputEvents) { + self.$selection.off('input.search input.searchcheck'); + return; + } + + // Unbind the duplicated `keyup` event + self.$selection.off('keyup.search'); + } + ); + + this.$selection.on( + 'keyup.search input.search', + '.select2-search--inline', + function (evt) { + // IE will trigger the `input` event when a placeholder is used on a + // search box. To get around this issue, we are forced to ignore all + // `input` events in IE and keep using `keyup`. + if (disableInputEvents && evt.type === 'input') { + self.$selection.off('input.search input.searchcheck'); + return; + } + + var key = evt.which; + + // We can freely ignore events from modifier keys + if (key == KEYS.SHIFT || key == KEYS.CTRL || key == KEYS.ALT) { + return; + } + + // Tabbing will be handled during the `keydown` phase + if (key == KEYS.TAB) { + return; + } + + self.handleSearch(evt); + } + ); + }; + + /** + * This method will transfer the tabindex attribute from the rendered + * selection to the search box. This allows for the search box to be used as + * the primary focus instead of the selection container. + * + * @private + */ + Search.prototype._transferTabIndex = function (decorated) { + this.$search.attr('tabindex', this.$selection.attr('tabindex')); + this.$selection.attr('tabindex', '-1'); + }; + + Search.prototype.createPlaceholder = function (decorated, placeholder) { + this.$search.attr('placeholder', placeholder.text); + }; + + Search.prototype.update = function (decorated, data) { + var searchHadFocus = this.$search[0] == document.activeElement; + + this.$search.attr('placeholder', ''); + + decorated.call(this, data); + + this.$selection.find('.select2-selection__rendered') + .append(this.$searchContainer); + + this.resizeSearch(); + if (searchHadFocus) { + this.$search.focus(); + } + }; + + Search.prototype.handleSearch = function () { + this.resizeSearch(); + + if (!this._keyUpPrevented) { + var input = this.$search.val(); + + this.trigger('query', { + term: input + }); + } + + this._keyUpPrevented = false; + }; + + Search.prototype.searchRemoveChoice = function (decorated, item) { + this.trigger('unselect', { + data: item + }); + + this.$search.val(item.text); + this.handleSearch(); + }; + + Search.prototype.resizeSearch = function () { + this.$search.css('width', '25px'); + + var width = ''; + + if (this.$search.attr('placeholder') !== '') { + width = this.$selection.find('.select2-selection__rendered').innerWidth(); + } else { + var minimumWidth = this.$search.val().length + 1; + + width = (minimumWidth * 0.75) + 'em'; + } + + this.$search.css('width', width); + }; + + return Search; +}); + +S2.define('select2/selection/eventRelay',[ + 'jquery' +], function ($) { + function EventRelay () { } + + EventRelay.prototype.bind = function (decorated, container, $container) { + var self = this; + var relayEvents = [ + 'open', 'opening', + 'close', 'closing', + 'select', 'selecting', + 'unselect', 'unselecting' + ]; + + var preventableEvents = ['opening', 'closing', 'selecting', 'unselecting']; + + decorated.call(this, container, $container); + + container.on('*', function (name, params) { + // Ignore events that should not be relayed + if ($.inArray(name, relayEvents) === -1) { + return; + } + + // The parameters should always be an object + params = params || {}; + + // Generate the jQuery event for the Select2 event + var evt = $.Event('select2:' + name, { + params: params + }); + + self.$element.trigger(evt); + + // Only handle preventable events if it was one + if ($.inArray(name, preventableEvents) === -1) { + return; + } + + params.prevented = evt.isDefaultPrevented(); + }); + }; + + return EventRelay; +}); + +S2.define('select2/translation',[ + 'jquery', + 'require' +], function ($, require) { + function Translation (dict) { + this.dict = dict || {}; + } + + Translation.prototype.all = function () { + return this.dict; + }; + + Translation.prototype.get = function (key) { + return this.dict[key]; + }; + + Translation.prototype.extend = function (translation) { + this.dict = $.extend({}, translation.all(), this.dict); + }; + + // Static functions + + Translation._cache = {}; + + Translation.loadPath = function (path) { + if (!(path in Translation._cache)) { + var translations = require(path); + + Translation._cache[path] = translations; + } + + return new Translation(Translation._cache[path]); + }; + + return Translation; +}); + +S2.define('select2/diacritics',[ + +], function () { + var diacritics = { + '\u24B6': 'A', + '\uFF21': 'A', + '\u00C0': 'A', + '\u00C1': 'A', + '\u00C2': 'A', + '\u1EA6': 'A', + '\u1EA4': 'A', + '\u1EAA': 'A', + '\u1EA8': 'A', + '\u00C3': 'A', + '\u0100': 'A', + '\u0102': 'A', + '\u1EB0': 'A', + '\u1EAE': 'A', + '\u1EB4': 'A', + '\u1EB2': 'A', + '\u0226': 'A', + '\u01E0': 'A', + '\u00C4': 'A', + '\u01DE': 'A', + '\u1EA2': 'A', + '\u00C5': 'A', + '\u01FA': 'A', + '\u01CD': 'A', + '\u0200': 'A', + '\u0202': 'A', + '\u1EA0': 'A', + '\u1EAC': 'A', + '\u1EB6': 'A', + '\u1E00': 'A', + '\u0104': 'A', + '\u023A': 'A', + '\u2C6F': 'A', + '\uA732': 'AA', + '\u00C6': 'AE', + '\u01FC': 'AE', + '\u01E2': 'AE', + '\uA734': 'AO', + '\uA736': 'AU', + '\uA738': 'AV', + '\uA73A': 'AV', + '\uA73C': 'AY', + '\u24B7': 'B', + '\uFF22': 'B', + '\u1E02': 'B', + '\u1E04': 'B', + '\u1E06': 'B', + '\u0243': 'B', + '\u0182': 'B', + '\u0181': 'B', + '\u24B8': 'C', + '\uFF23': 'C', + '\u0106': 'C', + '\u0108': 'C', + '\u010A': 'C', + '\u010C': 'C', + '\u00C7': 'C', + '\u1E08': 'C', + '\u0187': 'C', + '\u023B': 'C', + '\uA73E': 'C', + '\u24B9': 'D', + '\uFF24': 'D', + '\u1E0A': 'D', + '\u010E': 'D', + '\u1E0C': 'D', + '\u1E10': 'D', + '\u1E12': 'D', + '\u1E0E': 'D', + '\u0110': 'D', + '\u018B': 'D', + '\u018A': 'D', + '\u0189': 'D', + '\uA779': 'D', + '\u01F1': 'DZ', + '\u01C4': 'DZ', + '\u01F2': 'Dz', + '\u01C5': 'Dz', + '\u24BA': 'E', + '\uFF25': 'E', + '\u00C8': 'E', + '\u00C9': 'E', + '\u00CA': 'E', + '\u1EC0': 'E', + '\u1EBE': 'E', + '\u1EC4': 'E', + '\u1EC2': 'E', + '\u1EBC': 'E', + '\u0112': 'E', + '\u1E14': 'E', + '\u1E16': 'E', + '\u0114': 'E', + '\u0116': 'E', + '\u00CB': 'E', + '\u1EBA': 'E', + '\u011A': 'E', + '\u0204': 'E', + '\u0206': 'E', + '\u1EB8': 'E', + '\u1EC6': 'E', + '\u0228': 'E', + '\u1E1C': 'E', + '\u0118': 'E', + '\u1E18': 'E', + '\u1E1A': 'E', + '\u0190': 'E', + '\u018E': 'E', + '\u24BB': 'F', + '\uFF26': 'F', + '\u1E1E': 'F', + '\u0191': 'F', + '\uA77B': 'F', + '\u24BC': 'G', + '\uFF27': 'G', + '\u01F4': 'G', + '\u011C': 'G', + '\u1E20': 'G', + '\u011E': 'G', + '\u0120': 'G', + '\u01E6': 'G', + '\u0122': 'G', + '\u01E4': 'G', + '\u0193': 'G', + '\uA7A0': 'G', + '\uA77D': 'G', + '\uA77E': 'G', + '\u24BD': 'H', + '\uFF28': 'H', + '\u0124': 'H', + '\u1E22': 'H', + '\u1E26': 'H', + '\u021E': 'H', + '\u1E24': 'H', + '\u1E28': 'H', + '\u1E2A': 'H', + '\u0126': 'H', + '\u2C67': 'H', + '\u2C75': 'H', + '\uA78D': 'H', + '\u24BE': 'I', + '\uFF29': 'I', + '\u00CC': 'I', + '\u00CD': 'I', + '\u00CE': 'I', + '\u0128': 'I', + '\u012A': 'I', + '\u012C': 'I', + '\u0130': 'I', + '\u00CF': 'I', + '\u1E2E': 'I', + '\u1EC8': 'I', + '\u01CF': 'I', + '\u0208': 'I', + '\u020A': 'I', + '\u1ECA': 'I', + '\u012E': 'I', + '\u1E2C': 'I', + '\u0197': 'I', + '\u24BF': 'J', + '\uFF2A': 'J', + '\u0134': 'J', + '\u0248': 'J', + '\u24C0': 'K', + '\uFF2B': 'K', + '\u1E30': 'K', + '\u01E8': 'K', + '\u1E32': 'K', + '\u0136': 'K', + '\u1E34': 'K', + '\u0198': 'K', + '\u2C69': 'K', + '\uA740': 'K', + '\uA742': 'K', + '\uA744': 'K', + '\uA7A2': 'K', + '\u24C1': 'L', + '\uFF2C': 'L', + '\u013F': 'L', + '\u0139': 'L', + '\u013D': 'L', + '\u1E36': 'L', + '\u1E38': 'L', + '\u013B': 'L', + '\u1E3C': 'L', + '\u1E3A': 'L', + '\u0141': 'L', + '\u023D': 'L', + '\u2C62': 'L', + '\u2C60': 'L', + '\uA748': 'L', + '\uA746': 'L', + '\uA780': 'L', + '\u01C7': 'LJ', + '\u01C8': 'Lj', + '\u24C2': 'M', + '\uFF2D': 'M', + '\u1E3E': 'M', + '\u1E40': 'M', + '\u1E42': 'M', + '\u2C6E': 'M', + '\u019C': 'M', + '\u24C3': 'N', + '\uFF2E': 'N', + '\u01F8': 'N', + '\u0143': 'N', + '\u00D1': 'N', + '\u1E44': 'N', + '\u0147': 'N', + '\u1E46': 'N', + '\u0145': 'N', + '\u1E4A': 'N', + '\u1E48': 'N', + '\u0220': 'N', + '\u019D': 'N', + '\uA790': 'N', + '\uA7A4': 'N', + '\u01CA': 'NJ', + '\u01CB': 'Nj', + '\u24C4': 'O', + '\uFF2F': 'O', + '\u00D2': 'O', + '\u00D3': 'O', + '\u00D4': 'O', + '\u1ED2': 'O', + '\u1ED0': 'O', + '\u1ED6': 'O', + '\u1ED4': 'O', + '\u00D5': 'O', + '\u1E4C': 'O', + '\u022C': 'O', + '\u1E4E': 'O', + '\u014C': 'O', + '\u1E50': 'O', + '\u1E52': 'O', + '\u014E': 'O', + '\u022E': 'O', + '\u0230': 'O', + '\u00D6': 'O', + '\u022A': 'O', + '\u1ECE': 'O', + '\u0150': 'O', + '\u01D1': 'O', + '\u020C': 'O', + '\u020E': 'O', + '\u01A0': 'O', + '\u1EDC': 'O', + '\u1EDA': 'O', + '\u1EE0': 'O', + '\u1EDE': 'O', + '\u1EE2': 'O', + '\u1ECC': 'O', + '\u1ED8': 'O', + '\u01EA': 'O', + '\u01EC': 'O', + '\u00D8': 'O', + '\u01FE': 'O', + '\u0186': 'O', + '\u019F': 'O', + '\uA74A': 'O', + '\uA74C': 'O', + '\u01A2': 'OI', + '\uA74E': 'OO', + '\u0222': 'OU', + '\u24C5': 'P', + '\uFF30': 'P', + '\u1E54': 'P', + '\u1E56': 'P', + '\u01A4': 'P', + '\u2C63': 'P', + '\uA750': 'P', + '\uA752': 'P', + '\uA754': 'P', + '\u24C6': 'Q', + '\uFF31': 'Q', + '\uA756': 'Q', + '\uA758': 'Q', + '\u024A': 'Q', + '\u24C7': 'R', + '\uFF32': 'R', + '\u0154': 'R', + '\u1E58': 'R', + '\u0158': 'R', + '\u0210': 'R', + '\u0212': 'R', + '\u1E5A': 'R', + '\u1E5C': 'R', + '\u0156': 'R', + '\u1E5E': 'R', + '\u024C': 'R', + '\u2C64': 'R', + '\uA75A': 'R', + '\uA7A6': 'R', + '\uA782': 'R', + '\u24C8': 'S', + '\uFF33': 'S', + '\u1E9E': 'S', + '\u015A': 'S', + '\u1E64': 'S', + '\u015C': 'S', + '\u1E60': 'S', + '\u0160': 'S', + '\u1E66': 'S', + '\u1E62': 'S', + '\u1E68': 'S', + '\u0218': 'S', + '\u015E': 'S', + '\u2C7E': 'S', + '\uA7A8': 'S', + '\uA784': 'S', + '\u24C9': 'T', + '\uFF34': 'T', + '\u1E6A': 'T', + '\u0164': 'T', + '\u1E6C': 'T', + '\u021A': 'T', + '\u0162': 'T', + '\u1E70': 'T', + '\u1E6E': 'T', + '\u0166': 'T', + '\u01AC': 'T', + '\u01AE': 'T', + '\u023E': 'T', + '\uA786': 'T', + '\uA728': 'TZ', + '\u24CA': 'U', + '\uFF35': 'U', + '\u00D9': 'U', + '\u00DA': 'U', + '\u00DB': 'U', + '\u0168': 'U', + '\u1E78': 'U', + '\u016A': 'U', + '\u1E7A': 'U', + '\u016C': 'U', + '\u00DC': 'U', + '\u01DB': 'U', + '\u01D7': 'U', + '\u01D5': 'U', + '\u01D9': 'U', + '\u1EE6': 'U', + '\u016E': 'U', + '\u0170': 'U', + '\u01D3': 'U', + '\u0214': 'U', + '\u0216': 'U', + '\u01AF': 'U', + '\u1EEA': 'U', + '\u1EE8': 'U', + '\u1EEE': 'U', + '\u1EEC': 'U', + '\u1EF0': 'U', + '\u1EE4': 'U', + '\u1E72': 'U', + '\u0172': 'U', + '\u1E76': 'U', + '\u1E74': 'U', + '\u0244': 'U', + '\u24CB': 'V', + '\uFF36': 'V', + '\u1E7C': 'V', + '\u1E7E': 'V', + '\u01B2': 'V', + '\uA75E': 'V', + '\u0245': 'V', + '\uA760': 'VY', + '\u24CC': 'W', + '\uFF37': 'W', + '\u1E80': 'W', + '\u1E82': 'W', + '\u0174': 'W', + '\u1E86': 'W', + '\u1E84': 'W', + '\u1E88': 'W', + '\u2C72': 'W', + '\u24CD': 'X', + '\uFF38': 'X', + '\u1E8A': 'X', + '\u1E8C': 'X', + '\u24CE': 'Y', + '\uFF39': 'Y', + '\u1EF2': 'Y', + '\u00DD': 'Y', + '\u0176': 'Y', + '\u1EF8': 'Y', + '\u0232': 'Y', + '\u1E8E': 'Y', + '\u0178': 'Y', + '\u1EF6': 'Y', + '\u1EF4': 'Y', + '\u01B3': 'Y', + '\u024E': 'Y', + '\u1EFE': 'Y', + '\u24CF': 'Z', + '\uFF3A': 'Z', + '\u0179': 'Z', + '\u1E90': 'Z', + '\u017B': 'Z', + '\u017D': 'Z', + '\u1E92': 'Z', + '\u1E94': 'Z', + '\u01B5': 'Z', + '\u0224': 'Z', + '\u2C7F': 'Z', + '\u2C6B': 'Z', + '\uA762': 'Z', + '\u24D0': 'a', + '\uFF41': 'a', + '\u1E9A': 'a', + '\u00E0': 'a', + '\u00E1': 'a', + '\u00E2': 'a', + '\u1EA7': 'a', + '\u1EA5': 'a', + '\u1EAB': 'a', + '\u1EA9': 'a', + '\u00E3': 'a', + '\u0101': 'a', + '\u0103': 'a', + '\u1EB1': 'a', + '\u1EAF': 'a', + '\u1EB5': 'a', + '\u1EB3': 'a', + '\u0227': 'a', + '\u01E1': 'a', + '\u00E4': 'a', + '\u01DF': 'a', + '\u1EA3': 'a', + '\u00E5': 'a', + '\u01FB': 'a', + '\u01CE': 'a', + '\u0201': 'a', + '\u0203': 'a', + '\u1EA1': 'a', + '\u1EAD': 'a', + '\u1EB7': 'a', + '\u1E01': 'a', + '\u0105': 'a', + '\u2C65': 'a', + '\u0250': 'a', + '\uA733': 'aa', + '\u00E6': 'ae', + '\u01FD': 'ae', + '\u01E3': 'ae', + '\uA735': 'ao', + '\uA737': 'au', + '\uA739': 'av', + '\uA73B': 'av', + '\uA73D': 'ay', + '\u24D1': 'b', + '\uFF42': 'b', + '\u1E03': 'b', + '\u1E05': 'b', + '\u1E07': 'b', + '\u0180': 'b', + '\u0183': 'b', + '\u0253': 'b', + '\u24D2': 'c', + '\uFF43': 'c', + '\u0107': 'c', + '\u0109': 'c', + '\u010B': 'c', + '\u010D': 'c', + '\u00E7': 'c', + '\u1E09': 'c', + '\u0188': 'c', + '\u023C': 'c', + '\uA73F': 'c', + '\u2184': 'c', + '\u24D3': 'd', + '\uFF44': 'd', + '\u1E0B': 'd', + '\u010F': 'd', + '\u1E0D': 'd', + '\u1E11': 'd', + '\u1E13': 'd', + '\u1E0F': 'd', + '\u0111': 'd', + '\u018C': 'd', + '\u0256': 'd', + '\u0257': 'd', + '\uA77A': 'd', + '\u01F3': 'dz', + '\u01C6': 'dz', + '\u24D4': 'e', + '\uFF45': 'e', + '\u00E8': 'e', + '\u00E9': 'e', + '\u00EA': 'e', + '\u1EC1': 'e', + '\u1EBF': 'e', + '\u1EC5': 'e', + '\u1EC3': 'e', + '\u1EBD': 'e', + '\u0113': 'e', + '\u1E15': 'e', + '\u1E17': 'e', + '\u0115': 'e', + '\u0117': 'e', + '\u00EB': 'e', + '\u1EBB': 'e', + '\u011B': 'e', + '\u0205': 'e', + '\u0207': 'e', + '\u1EB9': 'e', + '\u1EC7': 'e', + '\u0229': 'e', + '\u1E1D': 'e', + '\u0119': 'e', + '\u1E19': 'e', + '\u1E1B': 'e', + '\u0247': 'e', + '\u025B': 'e', + '\u01DD': 'e', + '\u24D5': 'f', + '\uFF46': 'f', + '\u1E1F': 'f', + '\u0192': 'f', + '\uA77C': 'f', + '\u24D6': 'g', + '\uFF47': 'g', + '\u01F5': 'g', + '\u011D': 'g', + '\u1E21': 'g', + '\u011F': 'g', + '\u0121': 'g', + '\u01E7': 'g', + '\u0123': 'g', + '\u01E5': 'g', + '\u0260': 'g', + '\uA7A1': 'g', + '\u1D79': 'g', + '\uA77F': 'g', + '\u24D7': 'h', + '\uFF48': 'h', + '\u0125': 'h', + '\u1E23': 'h', + '\u1E27': 'h', + '\u021F': 'h', + '\u1E25': 'h', + '\u1E29': 'h', + '\u1E2B': 'h', + '\u1E96': 'h', + '\u0127': 'h', + '\u2C68': 'h', + '\u2C76': 'h', + '\u0265': 'h', + '\u0195': 'hv', + '\u24D8': 'i', + '\uFF49': 'i', + '\u00EC': 'i', + '\u00ED': 'i', + '\u00EE': 'i', + '\u0129': 'i', + '\u012B': 'i', + '\u012D': 'i', + '\u00EF': 'i', + '\u1E2F': 'i', + '\u1EC9': 'i', + '\u01D0': 'i', + '\u0209': 'i', + '\u020B': 'i', + '\u1ECB': 'i', + '\u012F': 'i', + '\u1E2D': 'i', + '\u0268': 'i', + '\u0131': 'i', + '\u24D9': 'j', + '\uFF4A': 'j', + '\u0135': 'j', + '\u01F0': 'j', + '\u0249': 'j', + '\u24DA': 'k', + '\uFF4B': 'k', + '\u1E31': 'k', + '\u01E9': 'k', + '\u1E33': 'k', + '\u0137': 'k', + '\u1E35': 'k', + '\u0199': 'k', + '\u2C6A': 'k', + '\uA741': 'k', + '\uA743': 'k', + '\uA745': 'k', + '\uA7A3': 'k', + '\u24DB': 'l', + '\uFF4C': 'l', + '\u0140': 'l', + '\u013A': 'l', + '\u013E': 'l', + '\u1E37': 'l', + '\u1E39': 'l', + '\u013C': 'l', + '\u1E3D': 'l', + '\u1E3B': 'l', + '\u017F': 'l', + '\u0142': 'l', + '\u019A': 'l', + '\u026B': 'l', + '\u2C61': 'l', + '\uA749': 'l', + '\uA781': 'l', + '\uA747': 'l', + '\u01C9': 'lj', + '\u24DC': 'm', + '\uFF4D': 'm', + '\u1E3F': 'm', + '\u1E41': 'm', + '\u1E43': 'm', + '\u0271': 'm', + '\u026F': 'm', + '\u24DD': 'n', + '\uFF4E': 'n', + '\u01F9': 'n', + '\u0144': 'n', + '\u00F1': 'n', + '\u1E45': 'n', + '\u0148': 'n', + '\u1E47': 'n', + '\u0146': 'n', + '\u1E4B': 'n', + '\u1E49': 'n', + '\u019E': 'n', + '\u0272': 'n', + '\u0149': 'n', + '\uA791': 'n', + '\uA7A5': 'n', + '\u01CC': 'nj', + '\u24DE': 'o', + '\uFF4F': 'o', + '\u00F2': 'o', + '\u00F3': 'o', + '\u00F4': 'o', + '\u1ED3': 'o', + '\u1ED1': 'o', + '\u1ED7': 'o', + '\u1ED5': 'o', + '\u00F5': 'o', + '\u1E4D': 'o', + '\u022D': 'o', + '\u1E4F': 'o', + '\u014D': 'o', + '\u1E51': 'o', + '\u1E53': 'o', + '\u014F': 'o', + '\u022F': 'o', + '\u0231': 'o', + '\u00F6': 'o', + '\u022B': 'o', + '\u1ECF': 'o', + '\u0151': 'o', + '\u01D2': 'o', + '\u020D': 'o', + '\u020F': 'o', + '\u01A1': 'o', + '\u1EDD': 'o', + '\u1EDB': 'o', + '\u1EE1': 'o', + '\u1EDF': 'o', + '\u1EE3': 'o', + '\u1ECD': 'o', + '\u1ED9': 'o', + '\u01EB': 'o', + '\u01ED': 'o', + '\u00F8': 'o', + '\u01FF': 'o', + '\u0254': 'o', + '\uA74B': 'o', + '\uA74D': 'o', + '\u0275': 'o', + '\u01A3': 'oi', + '\u0223': 'ou', + '\uA74F': 'oo', + '\u24DF': 'p', + '\uFF50': 'p', + '\u1E55': 'p', + '\u1E57': 'p', + '\u01A5': 'p', + '\u1D7D': 'p', + '\uA751': 'p', + '\uA753': 'p', + '\uA755': 'p', + '\u24E0': 'q', + '\uFF51': 'q', + '\u024B': 'q', + '\uA757': 'q', + '\uA759': 'q', + '\u24E1': 'r', + '\uFF52': 'r', + '\u0155': 'r', + '\u1E59': 'r', + '\u0159': 'r', + '\u0211': 'r', + '\u0213': 'r', + '\u1E5B': 'r', + '\u1E5D': 'r', + '\u0157': 'r', + '\u1E5F': 'r', + '\u024D': 'r', + '\u027D': 'r', + '\uA75B': 'r', + '\uA7A7': 'r', + '\uA783': 'r', + '\u24E2': 's', + '\uFF53': 's', + '\u00DF': 's', + '\u015B': 's', + '\u1E65': 's', + '\u015D': 's', + '\u1E61': 's', + '\u0161': 's', + '\u1E67': 's', + '\u1E63': 's', + '\u1E69': 's', + '\u0219': 's', + '\u015F': 's', + '\u023F': 's', + '\uA7A9': 's', + '\uA785': 's', + '\u1E9B': 's', + '\u24E3': 't', + '\uFF54': 't', + '\u1E6B': 't', + '\u1E97': 't', + '\u0165': 't', + '\u1E6D': 't', + '\u021B': 't', + '\u0163': 't', + '\u1E71': 't', + '\u1E6F': 't', + '\u0167': 't', + '\u01AD': 't', + '\u0288': 't', + '\u2C66': 't', + '\uA787': 't', + '\uA729': 'tz', + '\u24E4': 'u', + '\uFF55': 'u', + '\u00F9': 'u', + '\u00FA': 'u', + '\u00FB': 'u', + '\u0169': 'u', + '\u1E79': 'u', + '\u016B': 'u', + '\u1E7B': 'u', + '\u016D': 'u', + '\u00FC': 'u', + '\u01DC': 'u', + '\u01D8': 'u', + '\u01D6': 'u', + '\u01DA': 'u', + '\u1EE7': 'u', + '\u016F': 'u', + '\u0171': 'u', + '\u01D4': 'u', + '\u0215': 'u', + '\u0217': 'u', + '\u01B0': 'u', + '\u1EEB': 'u', + '\u1EE9': 'u', + '\u1EEF': 'u', + '\u1EED': 'u', + '\u1EF1': 'u', + '\u1EE5': 'u', + '\u1E73': 'u', + '\u0173': 'u', + '\u1E77': 'u', + '\u1E75': 'u', + '\u0289': 'u', + '\u24E5': 'v', + '\uFF56': 'v', + '\u1E7D': 'v', + '\u1E7F': 'v', + '\u028B': 'v', + '\uA75F': 'v', + '\u028C': 'v', + '\uA761': 'vy', + '\u24E6': 'w', + '\uFF57': 'w', + '\u1E81': 'w', + '\u1E83': 'w', + '\u0175': 'w', + '\u1E87': 'w', + '\u1E85': 'w', + '\u1E98': 'w', + '\u1E89': 'w', + '\u2C73': 'w', + '\u24E7': 'x', + '\uFF58': 'x', + '\u1E8B': 'x', + '\u1E8D': 'x', + '\u24E8': 'y', + '\uFF59': 'y', + '\u1EF3': 'y', + '\u00FD': 'y', + '\u0177': 'y', + '\u1EF9': 'y', + '\u0233': 'y', + '\u1E8F': 'y', + '\u00FF': 'y', + '\u1EF7': 'y', + '\u1E99': 'y', + '\u1EF5': 'y', + '\u01B4': 'y', + '\u024F': 'y', + '\u1EFF': 'y', + '\u24E9': 'z', + '\uFF5A': 'z', + '\u017A': 'z', + '\u1E91': 'z', + '\u017C': 'z', + '\u017E': 'z', + '\u1E93': 'z', + '\u1E95': 'z', + '\u01B6': 'z', + '\u0225': 'z', + '\u0240': 'z', + '\u2C6C': 'z', + '\uA763': 'z', + '\u0386': '\u0391', + '\u0388': '\u0395', + '\u0389': '\u0397', + '\u038A': '\u0399', + '\u03AA': '\u0399', + '\u038C': '\u039F', + '\u038E': '\u03A5', + '\u03AB': '\u03A5', + '\u038F': '\u03A9', + '\u03AC': '\u03B1', + '\u03AD': '\u03B5', + '\u03AE': '\u03B7', + '\u03AF': '\u03B9', + '\u03CA': '\u03B9', + '\u0390': '\u03B9', + '\u03CC': '\u03BF', + '\u03CD': '\u03C5', + '\u03CB': '\u03C5', + '\u03B0': '\u03C5', + '\u03C9': '\u03C9', + '\u03C2': '\u03C3' + }; + + return diacritics; +}); + +S2.define('select2/data/base',[ + '../utils' +], function (Utils) { + function BaseAdapter ($element, options) { + BaseAdapter.__super__.constructor.call(this); + } + + Utils.Extend(BaseAdapter, Utils.Observable); + + BaseAdapter.prototype.current = function (callback) { + throw new Error('The `current` method must be defined in child classes.'); + }; + + BaseAdapter.prototype.query = function (params, callback) { + throw new Error('The `query` method must be defined in child classes.'); + }; + + BaseAdapter.prototype.bind = function (container, $container) { + // Can be implemented in subclasses + }; + + BaseAdapter.prototype.destroy = function () { + // Can be implemented in subclasses + }; + + BaseAdapter.prototype.generateResultId = function (container, data) { + var id = container.id + '-result-'; + + id += Utils.generateChars(4); + + if (data.id != null) { + id += '-' + data.id.toString(); + } else { + id += '-' + Utils.generateChars(4); + } + return id; + }; + + return BaseAdapter; +}); + +S2.define('select2/data/select',[ + './base', + '../utils', + 'jquery' +], function (BaseAdapter, Utils, $) { + function SelectAdapter ($element, options) { + this.$element = $element; + this.options = options; + + SelectAdapter.__super__.constructor.call(this); + } + + Utils.Extend(SelectAdapter, BaseAdapter); + + SelectAdapter.prototype.current = function (callback) { + var data = []; + var self = this; + + this.$element.find(':selected').each(function () { + var $option = $(this); + + var option = self.item($option); + + data.push(option); + }); + + callback(data); + }; + + SelectAdapter.prototype.select = function (data) { + var self = this; + + data.selected = true; + + // If data.element is a DOM node, use it instead + if ($(data.element).is('option')) { + data.element.selected = true; + + this.$element.trigger('change'); + + return; + } + + if (this.$element.prop('multiple')) { + this.current(function (currentData) { + var val = []; + + data = [data]; + data.push.apply(data, currentData); + + for (var d = 0; d < data.length; d++) { + var id = data[d].id; + + if ($.inArray(id, val) === -1) { + val.push(id); + } + } + + self.$element.val(val); + self.$element.trigger('change'); + }); + } else { + var val = data.id; + + this.$element.val(val); + this.$element.trigger('change'); + } + }; + + SelectAdapter.prototype.unselect = function (data) { + var self = this; + + if (!this.$element.prop('multiple')) { + return; + } + + data.selected = false; + + if ($(data.element).is('option')) { + data.element.selected = false; + + this.$element.trigger('change'); + + return; + } + + this.current(function (currentData) { + var val = []; + + for (var d = 0; d < currentData.length; d++) { + var id = currentData[d].id; + + if (id !== data.id && $.inArray(id, val) === -1) { + val.push(id); + } + } + + self.$element.val(val); + + self.$element.trigger('change'); + }); + }; + + SelectAdapter.prototype.bind = function (container, $container) { + var self = this; + + this.container = container; + + container.on('select', function (params) { + self.select(params.data); + }); + + container.on('unselect', function (params) { + self.unselect(params.data); + }); + }; + + SelectAdapter.prototype.destroy = function () { + // Remove anything added to child elements + this.$element.find('*').each(function () { + // Remove any custom data set by Select2 + $.removeData(this, 'data'); + }); + }; + + SelectAdapter.prototype.query = function (params, callback) { + var data = []; + var self = this; + + var $options = this.$element.children(); + + $options.each(function () { + var $option = $(this); + + if (!$option.is('option') && !$option.is('optgroup')) { + return; + } + + var option = self.item($option); + + var matches = self.matches(params, option); + + if (matches !== null) { + data.push(matches); + } + }); + + callback({ + results: data + }); + }; + + SelectAdapter.prototype.addOptions = function ($options) { + Utils.appendMany(this.$element, $options); + }; + + SelectAdapter.prototype.option = function (data) { + var option; + + if (data.children) { + option = document.createElement('optgroup'); + option.label = data.text; + } else { + option = document.createElement('option'); + + if (option.textContent !== undefined) { + option.textContent = data.text; + } else { + option.innerText = data.text; + } + } + + if (data.id) { + option.value = data.id; + } + + if (data.disabled) { + option.disabled = true; + } + + if (data.selected) { + option.selected = true; + } + + if (data.title) { + option.title = data.title; + } + + var $option = $(option); + + var normalizedData = this._normalizeItem(data); + normalizedData.element = option; + + // Override the option's data with the combined data + $.data(option, 'data', normalizedData); + + return $option; + }; + + SelectAdapter.prototype.item = function ($option) { + var data = {}; + + data = $.data($option[0], 'data'); + + if (data != null) { + return data; + } + + if ($option.is('option')) { + data = { + id: $option.val(), + text: $option.text(), + disabled: $option.prop('disabled'), + selected: $option.prop('selected'), + title: $option.prop('title') + }; + } else if ($option.is('optgroup')) { + data = { + text: $option.prop('label'), + children: [], + title: $option.prop('title') + }; + + var $children = $option.children('option'); + var children = []; + + for (var c = 0; c < $children.length; c++) { + var $child = $($children[c]); + + var child = this.item($child); + + children.push(child); + } + + data.children = children; + } + + data = this._normalizeItem(data); + data.element = $option[0]; + + $.data($option[0], 'data', data); + + return data; + }; + + SelectAdapter.prototype._normalizeItem = function (item) { + if (!$.isPlainObject(item)) { + item = { + id: item, + text: item + }; + } + + item = $.extend({}, { + text: '' + }, item); + + var defaults = { + selected: false, + disabled: false + }; + + if (item.id != null) { + item.id = item.id.toString(); + } + + if (item.text != null) { + item.text = item.text.toString(); + } + + if (item._resultId == null && item.id && this.container != null) { + item._resultId = this.generateResultId(this.container, item); + } + + return $.extend({}, defaults, item); + }; + + SelectAdapter.prototype.matches = function (params, data) { + var matcher = this.options.get('matcher'); + + return matcher(params, data); + }; + + return SelectAdapter; +}); + +S2.define('select2/data/array',[ + './select', + '../utils', + 'jquery' +], function (SelectAdapter, Utils, $) { + function ArrayAdapter ($element, options) { + var data = options.get('data') || []; + + ArrayAdapter.__super__.constructor.call(this, $element, options); + + this.addOptions(this.convertToOptions(data)); + } + + Utils.Extend(ArrayAdapter, SelectAdapter); + + ArrayAdapter.prototype.select = function (data) { + var $option = this.$element.find('option').filter(function (i, elm) { + return elm.value == data.id.toString(); + }); + + if ($option.length === 0) { + $option = this.option(data); + + this.addOptions($option); + } + + ArrayAdapter.__super__.select.call(this, data); + }; + + ArrayAdapter.prototype.convertToOptions = function (data) { + var self = this; + + var $existing = this.$element.find('option'); + var existingIds = $existing.map(function () { + return self.item($(this)).id; + }).get(); + + var $options = []; + + // Filter out all items except for the one passed in the argument + function onlyItem (item) { + return function () { + return $(this).val() == item.id; + }; + } + + for (var d = 0; d < data.length; d++) { + var item = this._normalizeItem(data[d]); + + // Skip items which were pre-loaded, only merge the data + if ($.inArray(item.id, existingIds) >= 0) { + var $existingOption = $existing.filter(onlyItem(item)); + + var existingData = this.item($existingOption); + var newData = $.extend(true, {}, item, existingData); + + var $newOption = this.option(newData); + + $existingOption.replaceWith($newOption); + + continue; + } + + var $option = this.option(item); + + if (item.children) { + var $children = this.convertToOptions(item.children); + + Utils.appendMany($option, $children); + } + + $options.push($option); + } + + return $options; + }; + + return ArrayAdapter; +}); + +S2.define('select2/data/ajax',[ + './array', + '../utils', + 'jquery' +], function (ArrayAdapter, Utils, $) { + function AjaxAdapter ($element, options) { + this.ajaxOptions = this._applyDefaults(options.get('ajax')); + + if (this.ajaxOptions.processResults != null) { + this.processResults = this.ajaxOptions.processResults; + } + + AjaxAdapter.__super__.constructor.call(this, $element, options); + } + + Utils.Extend(AjaxAdapter, ArrayAdapter); + + AjaxAdapter.prototype._applyDefaults = function (options) { + var defaults = { + data: function (params) { + return $.extend({}, params, { + q: params.term + }); + }, + transport: function (params, success, failure) { + var $request = $.ajax(params); + + $request.then(success); + $request.fail(failure); + + return $request; + } + }; + + return $.extend({}, defaults, options, true); + }; + + AjaxAdapter.prototype.processResults = function (results) { + return results; + }; + + AjaxAdapter.prototype.query = function (params, callback) { + var matches = []; + var self = this; + + if (this._request != null) { + // JSONP requests cannot always be aborted + if ($.isFunction(this._request.abort)) { + this._request.abort(); + } + + this._request = null; + } + + var options = $.extend({ + type: 'GET' + }, this.ajaxOptions); + + if (typeof options.url === 'function') { + options.url = options.url.call(this.$element, params); + } + + if (typeof options.data === 'function') { + options.data = options.data.call(this.$element, params); + } + + function request () { + var $request = options.transport(options, function (data) { + var results = self.processResults(data, params); + + if (self.options.get('debug') && window.console && console.error) { + // Check to make sure that the response included a `results` key. + if (!results || !results.results || !$.isArray(results.results)) { + console.error( + 'Select2: The AJAX results did not return an array in the ' + + '`results` key of the response.' + ); + } + } + + callback(results); + }, function () { + // Attempt to detect if a request was aborted + // Only works if the transport exposes a status property + if ($request.status && $request.status === '0') { + return; + } + + self.trigger('results:message', { + message: 'errorLoading' + }); + }); + + self._request = $request; + } + + if (this.ajaxOptions.delay && params.term != null) { + if (this._queryTimeout) { + window.clearTimeout(this._queryTimeout); + } + + this._queryTimeout = window.setTimeout(request, this.ajaxOptions.delay); + } else { + request(); + } + }; + + return AjaxAdapter; +}); + +S2.define('select2/data/tags',[ + 'jquery' +], function ($) { + function Tags (decorated, $element, options) { + var tags = options.get('tags'); + + var createTag = options.get('createTag'); + + if (createTag !== undefined) { + this.createTag = createTag; + } + + var insertTag = options.get('insertTag'); + + if (insertTag !== undefined) { + this.insertTag = insertTag; + } + + decorated.call(this, $element, options); + + if ($.isArray(tags)) { + for (var t = 0; t < tags.length; t++) { + var tag = tags[t]; + var item = this._normalizeItem(tag); + + var $option = this.option(item); + + this.$element.append($option); + } + } + } + + Tags.prototype.query = function (decorated, params, callback) { + var self = this; + + this._removeOldTags(); + + if (params.term == null || params.page != null) { + decorated.call(this, params, callback); + return; + } + + function wrapper (obj, child) { + var data = obj.results; + + for (var i = 0; i < data.length; i++) { + var option = data[i]; + + var checkChildren = ( + option.children != null && + !wrapper({ + results: option.children + }, true) + ); + + var checkText = option.text === params.term; + + if (checkText || checkChildren) { + if (child) { + return false; + } + + obj.data = data; + callback(obj); + + return; + } + } + + if (child) { + return true; + } + + var tag = self.createTag(params); + + if (tag != null) { + var $option = self.option(tag); + $option.attr('data-select2-tag', true); + + self.addOptions([$option]); + + self.insertTag(data, tag); + } + + obj.results = data; + + callback(obj); + } + + decorated.call(this, params, wrapper); + }; + + Tags.prototype.createTag = function (decorated, params) { + var term = $.trim(params.term); + + if (term === '') { + return null; + } + + return { + id: term, + text: term + }; + }; + + Tags.prototype.insertTag = function (_, data, tag) { + data.unshift(tag); + }; + + Tags.prototype._removeOldTags = function (_) { + var tag = this._lastTag; + + var $options = this.$element.find('option[data-select2-tag]'); + + $options.each(function () { + if (this.selected) { + return; + } + + $(this).remove(); + }); + }; + + return Tags; +}); + +S2.define('select2/data/tokenizer',[ + 'jquery' +], function ($) { + function Tokenizer (decorated, $element, options) { + var tokenizer = options.get('tokenizer'); + + if (tokenizer !== undefined) { + this.tokenizer = tokenizer; + } + + decorated.call(this, $element, options); + } + + Tokenizer.prototype.bind = function (decorated, container, $container) { + decorated.call(this, container, $container); + + this.$search = container.dropdown.$search || container.selection.$search || + $container.find('.select2-search__field'); + }; + + Tokenizer.prototype.query = function (decorated, params, callback) { + var self = this; + + function createAndSelect (data) { + // Normalize the data object so we can use it for checks + var item = self._normalizeItem(data); + + // Check if the data object already exists as a tag + // Select it if it doesn't + var $existingOptions = self.$element.find('option').filter(function () { + return $(this).val() === item.id; + }); + + // If an existing option wasn't found for it, create the option + if (!$existingOptions.length) { + var $option = self.option(item); + $option.attr('data-select2-tag', true); + + self._removeOldTags(); + self.addOptions([$option]); + } + + // Select the item, now that we know there is an option for it + select(item); + } + + function select (data) { + self.trigger('select', { + data: data + }); + } + + params.term = params.term || ''; + + var tokenData = this.tokenizer(params, this.options, createAndSelect); + + if (tokenData.term !== params.term) { + // Replace the search term if we have the search box + if (this.$search.length) { + this.$search.val(tokenData.term); + this.$search.focus(); + } + + params.term = tokenData.term; + } + + decorated.call(this, params, callback); + }; + + Tokenizer.prototype.tokenizer = function (_, params, options, callback) { + var separators = options.get('tokenSeparators') || []; + var term = params.term; + var i = 0; + + var createTag = this.createTag || function (params) { + return { + id: params.term, + text: params.term + }; + }; + + while (i < term.length) { + var termChar = term[i]; + + if ($.inArray(termChar, separators) === -1) { + i++; + + continue; + } + + var part = term.substr(0, i); + var partParams = $.extend({}, params, { + term: part + }); + + var data = createTag(partParams); + + if (data == null) { + i++; + continue; + } + + callback(data); + + // Reset the term to not include the tokenized portion + term = term.substr(i + 1) || ''; + i = 0; + } + + return { + term: term + }; + }; + + return Tokenizer; +}); + +S2.define('select2/data/minimumInputLength',[ + +], function () { + function MinimumInputLength (decorated, $e, options) { + this.minimumInputLength = options.get('minimumInputLength'); + + decorated.call(this, $e, options); + } + + MinimumInputLength.prototype.query = function (decorated, params, callback) { + params.term = params.term || ''; + + if (params.term.length < this.minimumInputLength) { + this.trigger('results:message', { + message: 'inputTooShort', + args: { + minimum: this.minimumInputLength, + input: params.term, + params: params + } + }); + + return; + } + + decorated.call(this, params, callback); + }; + + return MinimumInputLength; +}); + +S2.define('select2/data/maximumInputLength',[ + +], function () { + function MaximumInputLength (decorated, $e, options) { + this.maximumInputLength = options.get('maximumInputLength'); + + decorated.call(this, $e, options); + } + + MaximumInputLength.prototype.query = function (decorated, params, callback) { + params.term = params.term || ''; + + if (this.maximumInputLength > 0 && + params.term.length > this.maximumInputLength) { + this.trigger('results:message', { + message: 'inputTooLong', + args: { + maximum: this.maximumInputLength, + input: params.term, + params: params + } + }); + + return; + } + + decorated.call(this, params, callback); + }; + + return MaximumInputLength; +}); + +S2.define('select2/data/maximumSelectionLength',[ + +], function (){ + function MaximumSelectionLength (decorated, $e, options) { + this.maximumSelectionLength = options.get('maximumSelectionLength'); + + decorated.call(this, $e, options); + } + + MaximumSelectionLength.prototype.query = + function (decorated, params, callback) { + var self = this; + + this.current(function (currentData) { + var count = currentData != null ? currentData.length : 0; + if (self.maximumSelectionLength > 0 && + count >= self.maximumSelectionLength) { + self.trigger('results:message', { + message: 'maximumSelected', + args: { + maximum: self.maximumSelectionLength + } + }); + return; + } + decorated.call(self, params, callback); + }); + }; + + return MaximumSelectionLength; +}); + +S2.define('select2/dropdown',[ + 'jquery', + './utils' +], function ($, Utils) { + function Dropdown ($element, options) { + this.$element = $element; + this.options = options; + + Dropdown.__super__.constructor.call(this); + } + + Utils.Extend(Dropdown, Utils.Observable); + + Dropdown.prototype.render = function () { + var $dropdown = $( + '' + + '' + + '' + ); + + $dropdown.attr('dir', this.options.get('dir')); + + this.$dropdown = $dropdown; + + return $dropdown; + }; + + Dropdown.prototype.bind = function () { + // Should be implemented in subclasses + }; + + Dropdown.prototype.position = function ($dropdown, $container) { + // Should be implmented in subclasses + }; + + Dropdown.prototype.destroy = function () { + // Remove the dropdown from the DOM + this.$dropdown.remove(); + }; + + return Dropdown; +}); + +S2.define('select2/dropdown/search',[ + 'jquery', + '../utils' +], function ($, Utils) { + function Search () { } + + Search.prototype.render = function (decorated) { + var $rendered = decorated.call(this); + + var $search = $( + '' + + '' + + '' + ); + + this.$searchContainer = $search; + this.$search = $search.find('input'); + + $rendered.prepend($search); + + return $rendered; + }; + + Search.prototype.bind = function (decorated, container, $container) { + var self = this; + + decorated.call(this, container, $container); + + this.$search.on('keydown', function (evt) { + self.trigger('keypress', evt); + + self._keyUpPrevented = evt.isDefaultPrevented(); + }); + + // Workaround for browsers which do not support the `input` event + // This will prevent double-triggering of events for browsers which support + // both the `keyup` and `input` events. + this.$search.on('input', function (evt) { + // Unbind the duplicated `keyup` event + $(this).off('keyup'); + }); + + this.$search.on('keyup input', function (evt) { + self.handleSearch(evt); + }); + + container.on('open', function () { + self.$search.attr('tabindex', 0); + + self.$search.focus(); + + window.setTimeout(function () { + self.$search.focus(); + }, 0); + }); + + container.on('close', function () { + self.$search.attr('tabindex', -1); + + self.$search.val(''); + }); + + container.on('focus', function () { + if (container.isOpen()) { + self.$search.focus(); + } + }); + + container.on('results:all', function (params) { + if (params.query.term == null || params.query.term === '') { + var showSearch = self.showSearch(params); + + if (showSearch) { + self.$searchContainer.removeClass('select2-search--hide'); + } else { + self.$searchContainer.addClass('select2-search--hide'); + } + } + }); + }; + + Search.prototype.handleSearch = function (evt) { + if (!this._keyUpPrevented) { + var input = this.$search.val(); + + this.trigger('query', { + term: input + }); + } + + this._keyUpPrevented = false; + }; + + Search.prototype.showSearch = function (_, params) { + return true; + }; + + return Search; +}); + +S2.define('select2/dropdown/hidePlaceholder',[ + +], function () { + function HidePlaceholder (decorated, $element, options, dataAdapter) { + this.placeholder = this.normalizePlaceholder(options.get('placeholder')); + + decorated.call(this, $element, options, dataAdapter); + } + + HidePlaceholder.prototype.append = function (decorated, data) { + data.results = this.removePlaceholder(data.results); + + decorated.call(this, data); + }; + + HidePlaceholder.prototype.normalizePlaceholder = function (_, placeholder) { + if (typeof placeholder === 'string') { + placeholder = { + id: '', + text: placeholder + }; + } + + return placeholder; + }; + + HidePlaceholder.prototype.removePlaceholder = function (_, data) { + var modifiedData = data.slice(0); + + for (var d = data.length - 1; d >= 0; d--) { + var item = data[d]; + + if (this.placeholder.id === item.id) { + modifiedData.splice(d, 1); + } + } + + return modifiedData; + }; + + return HidePlaceholder; +}); + +S2.define('select2/dropdown/infiniteScroll',[ + 'jquery' +], function ($) { + function InfiniteScroll (decorated, $element, options, dataAdapter) { + this.lastParams = {}; + + decorated.call(this, $element, options, dataAdapter); + + this.$loadingMore = this.createLoadingMore(); + this.loading = false; + } + + InfiniteScroll.prototype.append = function (decorated, data) { + this.$loadingMore.remove(); + this.loading = false; + + decorated.call(this, data); + + if (this.showLoadingMore(data)) { + this.$results.append(this.$loadingMore); + } + }; + + InfiniteScroll.prototype.bind = function (decorated, container, $container) { + var self = this; + + decorated.call(this, container, $container); + + container.on('query', function (params) { + self.lastParams = params; + self.loading = true; + }); + + container.on('query:append', function (params) { + self.lastParams = params; + self.loading = true; + }); + + this.$results.on('scroll', function () { + var isLoadMoreVisible = $.contains( + document.documentElement, + self.$loadingMore[0] + ); + + if (self.loading || !isLoadMoreVisible) { + return; + } + + var currentOffset = self.$results.offset().top + + self.$results.outerHeight(false); + var loadingMoreOffset = self.$loadingMore.offset().top + + self.$loadingMore.outerHeight(false); + + if (currentOffset + 50 >= loadingMoreOffset) { + self.loadMore(); + } + }); + }; + + InfiniteScroll.prototype.loadMore = function () { + this.loading = true; + + var params = $.extend({}, {page: 1}, this.lastParams); + + params.page++; + + this.trigger('query:append', params); + }; + + InfiniteScroll.prototype.showLoadingMore = function (_, data) { + return data.pagination && data.pagination.more; + }; + + InfiniteScroll.prototype.createLoadingMore = function () { + var $option = $( + '
      • ' + ); + + var message = this.options.get('translations').get('loadingMore'); + + $option.html(message(this.lastParams)); + + return $option; + }; + + return InfiniteScroll; +}); + +S2.define('select2/dropdown/attachBody',[ + 'jquery', + '../utils' +], function ($, Utils) { + function AttachBody (decorated, $element, options) { + this.$dropdownParent = options.get('dropdownParent') || $(document.body); + + decorated.call(this, $element, options); + } + + AttachBody.prototype.bind = function (decorated, container, $container) { + var self = this; + + var setupResultsEvents = false; + + decorated.call(this, container, $container); + + container.on('open', function () { + self._showDropdown(); + self._attachPositioningHandler(container); + + if (!setupResultsEvents) { + setupResultsEvents = true; + + container.on('results:all', function () { + self._positionDropdown(); + self._resizeDropdown(); + }); + + container.on('results:append', function () { + self._positionDropdown(); + self._resizeDropdown(); + }); + } + }); + + container.on('close', function () { + self._hideDropdown(); + self._detachPositioningHandler(container); + }); + + this.$dropdownContainer.on('mousedown', function (evt) { + evt.stopPropagation(); + }); + }; + + AttachBody.prototype.destroy = function (decorated) { + decorated.call(this); + + this.$dropdownContainer.remove(); + }; + + AttachBody.prototype.position = function (decorated, $dropdown, $container) { + // Clone all of the container classes + $dropdown.attr('class', $container.attr('class')); + + $dropdown.removeClass('select2'); + $dropdown.addClass('select2-container--open'); + + $dropdown.css({ + position: 'absolute', + top: -999999 + }); + + this.$container = $container; + }; + + AttachBody.prototype.render = function (decorated) { + var $container = $(''); + + var $dropdown = decorated.call(this); + $container.append($dropdown); + + this.$dropdownContainer = $container; + + return $container; + }; + + AttachBody.prototype._hideDropdown = function (decorated) { + this.$dropdownContainer.detach(); + }; + + AttachBody.prototype._attachPositioningHandler = + function (decorated, container) { + var self = this; + + var scrollEvent = 'scroll.select2.' + container.id; + var resizeEvent = 'resize.select2.' + container.id; + var orientationEvent = 'orientationchange.select2.' + container.id; + + var $watchers = this.$container.parents().filter(Utils.hasScroll); + $watchers.each(function () { + $(this).data('select2-scroll-position', { + x: $(this).scrollLeft(), + y: $(this).scrollTop() + }); + }); + + $watchers.on(scrollEvent, function (ev) { + var position = $(this).data('select2-scroll-position'); + $(this).scrollTop(position.y); + }); + + $(window).on(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent, + function (e) { + self._positionDropdown(); + self._resizeDropdown(); + }); + }; + + AttachBody.prototype._detachPositioningHandler = + function (decorated, container) { + var scrollEvent = 'scroll.select2.' + container.id; + var resizeEvent = 'resize.select2.' + container.id; + var orientationEvent = 'orientationchange.select2.' + container.id; + + var $watchers = this.$container.parents().filter(Utils.hasScroll); + $watchers.off(scrollEvent); + + $(window).off(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent); + }; + + AttachBody.prototype._positionDropdown = function () { + var $window = $(window); + + var isCurrentlyAbove = this.$dropdown.hasClass('select2-dropdown--above'); + var isCurrentlyBelow = this.$dropdown.hasClass('select2-dropdown--below'); + + var newDirection = null; + + var offset = this.$container.offset(); + + offset.bottom = offset.top + this.$container.outerHeight(false); + + var container = { + height: this.$container.outerHeight(false) + }; + + container.top = offset.top; + container.bottom = offset.top + container.height; + + var dropdown = { + height: this.$dropdown.outerHeight(false) + }; + + var viewport = { + top: $window.scrollTop(), + bottom: $window.scrollTop() + $window.height() + }; + + var enoughRoomAbove = viewport.top < (offset.top - dropdown.height); + var enoughRoomBelow = viewport.bottom > (offset.bottom + dropdown.height); + + var css = { + left: offset.left, + top: container.bottom + }; + + // Determine what the parent element is to use for calciulating the offset + var $offsetParent = this.$dropdownParent; + + // For statically positoned elements, we need to get the element + // that is determining the offset + if ($offsetParent.css('position') === 'static') { + $offsetParent = $offsetParent.offsetParent(); + } + + var parentOffset = $offsetParent.offset(); + + css.top -= parentOffset.top; + css.left -= parentOffset.left; + + if (!isCurrentlyAbove && !isCurrentlyBelow) { + newDirection = 'below'; + } + + if (!enoughRoomBelow && enoughRoomAbove && !isCurrentlyAbove) { + newDirection = 'above'; + } else if (!enoughRoomAbove && enoughRoomBelow && isCurrentlyAbove) { + newDirection = 'below'; + } + + if (newDirection == 'above' || + (isCurrentlyAbove && newDirection !== 'below')) { + css.top = container.top - parentOffset.top - dropdown.height; + } + + if (newDirection != null) { + this.$dropdown + .removeClass('select2-dropdown--below select2-dropdown--above') + .addClass('select2-dropdown--' + newDirection); + this.$container + .removeClass('select2-container--below select2-container--above') + .addClass('select2-container--' + newDirection); + } + + this.$dropdownContainer.css(css); + }; + + AttachBody.prototype._resizeDropdown = function () { + var css = { + width: this.$container.outerWidth(false) + 'px' + }; + + if (this.options.get('dropdownAutoWidth')) { + css.minWidth = css.width; + css.position = 'relative'; + css.width = 'auto'; + } + + this.$dropdown.css(css); + }; + + AttachBody.prototype._showDropdown = function (decorated) { + this.$dropdownContainer.appendTo(this.$dropdownParent); + + this._positionDropdown(); + this._resizeDropdown(); + }; + + return AttachBody; +}); + +S2.define('select2/dropdown/minimumResultsForSearch',[ + +], function () { + function countResults (data) { + var count = 0; + + for (var d = 0; d < data.length; d++) { + var item = data[d]; + + if (item.children) { + count += countResults(item.children); + } else { + count++; + } + } + + return count; + } + + function MinimumResultsForSearch (decorated, $element, options, dataAdapter) { + this.minimumResultsForSearch = options.get('minimumResultsForSearch'); + + if (this.minimumResultsForSearch < 0) { + this.minimumResultsForSearch = Infinity; + } + + decorated.call(this, $element, options, dataAdapter); + } + + MinimumResultsForSearch.prototype.showSearch = function (decorated, params) { + if (countResults(params.data.results) < this.minimumResultsForSearch) { + return false; + } + + return decorated.call(this, params); + }; + + return MinimumResultsForSearch; +}); + +S2.define('select2/dropdown/selectOnClose',[ + +], function () { + function SelectOnClose () { } + + SelectOnClose.prototype.bind = function (decorated, container, $container) { + var self = this; + + decorated.call(this, container, $container); + + container.on('close', function (params) { + self._handleSelectOnClose(params); + }); + }; + + SelectOnClose.prototype._handleSelectOnClose = function (_, params) { + if (params && params.originalSelect2Event != null) { + var event = params.originalSelect2Event; + + // Don't select an item if the close event was triggered from a select or + // unselect event + if (event._type === 'select' || event._type === 'unselect') { + return; + } + } + + var $highlightedResults = this.getHighlightedResults(); + + // Only select highlighted results + if ($highlightedResults.length < 1) { + return; + } + + var data = $highlightedResults.data('data'); + + // Don't re-select already selected resulte + if ( + (data.element != null && data.element.selected) || + (data.element == null && data.selected) + ) { + return; + } + + this.trigger('select', { + data: data + }); + }; + + return SelectOnClose; +}); + +S2.define('select2/dropdown/closeOnSelect',[ + +], function () { + function CloseOnSelect () { } + + CloseOnSelect.prototype.bind = function (decorated, container, $container) { + var self = this; + + decorated.call(this, container, $container); + + container.on('select', function (evt) { + self._selectTriggered(evt); + }); + + container.on('unselect', function (evt) { + self._selectTriggered(evt); + }); + }; + + CloseOnSelect.prototype._selectTriggered = function (_, evt) { + var originalEvent = evt.originalEvent; + + // Don't close if the control key is being held + if (originalEvent && originalEvent.ctrlKey) { + return; + } + + this.trigger('close', { + originalEvent: originalEvent, + originalSelect2Event: evt + }); + }; + + return CloseOnSelect; +}); + +S2.define('select2/i18n/en',[],function () { + // English + return { + errorLoading: function () { + return 'The results could not be loaded.'; + }, + inputTooLong: function (args) { + var overChars = args.input.length - args.maximum; + + var message = 'Please delete ' + overChars + ' character'; + + if (overChars != 1) { + message += 's'; + } + + return message; + }, + inputTooShort: function (args) { + var remainingChars = args.minimum - args.input.length; + + var message = 'Please enter ' + remainingChars + ' or more characters'; + + return message; + }, + loadingMore: function () { + return 'Loading more results…'; + }, + maximumSelected: function (args) { + var message = 'You can only select ' + args.maximum + ' item'; + + if (args.maximum != 1) { + message += 's'; + } + + return message; + }, + noResults: function () { + return 'No results found'; + }, + searching: function () { + return 'Searching…'; + } + }; +}); + +S2.define('select2/defaults',[ + 'jquery', + 'require', + + './results', + + './selection/single', + './selection/multiple', + './selection/placeholder', + './selection/allowClear', + './selection/search', + './selection/eventRelay', + + './utils', + './translation', + './diacritics', + + './data/select', + './data/array', + './data/ajax', + './data/tags', + './data/tokenizer', + './data/minimumInputLength', + './data/maximumInputLength', + './data/maximumSelectionLength', + + './dropdown', + './dropdown/search', + './dropdown/hidePlaceholder', + './dropdown/infiniteScroll', + './dropdown/attachBody', + './dropdown/minimumResultsForSearch', + './dropdown/selectOnClose', + './dropdown/closeOnSelect', + + './i18n/en' +], function ($, require, + + ResultsList, + + SingleSelection, MultipleSelection, Placeholder, AllowClear, + SelectionSearch, EventRelay, + + Utils, Translation, DIACRITICS, + + SelectData, ArrayData, AjaxData, Tags, Tokenizer, + MinimumInputLength, MaximumInputLength, MaximumSelectionLength, + + Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll, + AttachBody, MinimumResultsForSearch, SelectOnClose, CloseOnSelect, + + EnglishTranslation) { + function Defaults () { + this.reset(); + } + + Defaults.prototype.apply = function (options) { + options = $.extend(true, {}, this.defaults, options); + + if (options.dataAdapter == null) { + if (options.ajax != null) { + options.dataAdapter = AjaxData; + } else if (options.data != null) { + options.dataAdapter = ArrayData; + } else { + options.dataAdapter = SelectData; + } + + if (options.minimumInputLength > 0) { + options.dataAdapter = Utils.Decorate( + options.dataAdapter, + MinimumInputLength + ); + } + + if (options.maximumInputLength > 0) { + options.dataAdapter = Utils.Decorate( + options.dataAdapter, + MaximumInputLength + ); + } + + if (options.maximumSelectionLength > 0) { + options.dataAdapter = Utils.Decorate( + options.dataAdapter, + MaximumSelectionLength + ); + } + + if (options.tags) { + options.dataAdapter = Utils.Decorate(options.dataAdapter, Tags); + } + + if (options.tokenSeparators != null || options.tokenizer != null) { + options.dataAdapter = Utils.Decorate( + options.dataAdapter, + Tokenizer + ); + } + + if (options.query != null) { + var Query = require(options.amdBase + 'compat/query'); + + options.dataAdapter = Utils.Decorate( + options.dataAdapter, + Query + ); + } + + if (options.initSelection != null) { + var InitSelection = require(options.amdBase + 'compat/initSelection'); + + options.dataAdapter = Utils.Decorate( + options.dataAdapter, + InitSelection + ); + } + } + + if (options.resultsAdapter == null) { + options.resultsAdapter = ResultsList; + + if (options.ajax != null) { + options.resultsAdapter = Utils.Decorate( + options.resultsAdapter, + InfiniteScroll + ); + } + + if (options.placeholder != null) { + options.resultsAdapter = Utils.Decorate( + options.resultsAdapter, + HidePlaceholder + ); + } + + if (options.selectOnClose) { + options.resultsAdapter = Utils.Decorate( + options.resultsAdapter, + SelectOnClose + ); + } + } + + if (options.dropdownAdapter == null) { + if (options.multiple) { + options.dropdownAdapter = Dropdown; + } else { + var SearchableDropdown = Utils.Decorate(Dropdown, DropdownSearch); + + options.dropdownAdapter = SearchableDropdown; + } + + if (options.minimumResultsForSearch !== 0) { + options.dropdownAdapter = Utils.Decorate( + options.dropdownAdapter, + MinimumResultsForSearch + ); + } + + if (options.closeOnSelect) { + options.dropdownAdapter = Utils.Decorate( + options.dropdownAdapter, + CloseOnSelect + ); + } + + if ( + options.dropdownCssClass != null || + options.dropdownCss != null || + options.adaptDropdownCssClass != null + ) { + var DropdownCSS = require(options.amdBase + 'compat/dropdownCss'); + + options.dropdownAdapter = Utils.Decorate( + options.dropdownAdapter, + DropdownCSS + ); + } + + options.dropdownAdapter = Utils.Decorate( + options.dropdownAdapter, + AttachBody + ); + } + + if (options.selectionAdapter == null) { + if (options.multiple) { + options.selectionAdapter = MultipleSelection; + } else { + options.selectionAdapter = SingleSelection; + } + + // Add the placeholder mixin if a placeholder was specified + if (options.placeholder != null) { + options.selectionAdapter = Utils.Decorate( + options.selectionAdapter, + Placeholder + ); + } + + if (options.allowClear) { + options.selectionAdapter = Utils.Decorate( + options.selectionAdapter, + AllowClear + ); + } + + if (options.multiple) { + options.selectionAdapter = Utils.Decorate( + options.selectionAdapter, + SelectionSearch + ); + } + + if ( + options.containerCssClass != null || + options.containerCss != null || + options.adaptContainerCssClass != null + ) { + var ContainerCSS = require(options.amdBase + 'compat/containerCss'); + + options.selectionAdapter = Utils.Decorate( + options.selectionAdapter, + ContainerCSS + ); + } + + options.selectionAdapter = Utils.Decorate( + options.selectionAdapter, + EventRelay + ); + } + + if (typeof options.language === 'string') { + // Check if the language is specified with a region + if (options.language.indexOf('-') > 0) { + // Extract the region information if it is included + var languageParts = options.language.split('-'); + var baseLanguage = languageParts[0]; + + options.language = [options.language, baseLanguage]; + } else { + options.language = [options.language]; + } + } + + if ($.isArray(options.language)) { + var languages = new Translation(); + options.language.push('en'); + + var languageNames = options.language; + + for (var l = 0; l < languageNames.length; l++) { + var name = languageNames[l]; + var language = {}; + + try { + // Try to load it with the original name + language = Translation.loadPath(name); + } catch (e) { + try { + // If we couldn't load it, check if it wasn't the full path + name = this.defaults.amdLanguageBase + name; + language = Translation.loadPath(name); + } catch (ex) { + // The translation could not be loaded at all. Sometimes this is + // because of a configuration problem, other times this can be + // because of how Select2 helps load all possible translation files. + if (options.debug && window.console && console.warn) { + console.warn( + 'Select2: The language file for "' + name + '" could not be ' + + 'automatically loaded. A fallback will be used instead.' + ); + } + + continue; + } + } + + languages.extend(language); + } + + options.translations = languages; + } else { + var baseTranslation = Translation.loadPath( + this.defaults.amdLanguageBase + 'en' + ); + var customTranslation = new Translation(options.language); + + customTranslation.extend(baseTranslation); + + options.translations = customTranslation; + } + + return options; + }; + + Defaults.prototype.reset = function () { + function stripDiacritics (text) { + // Used 'uni range + named function' from http://jsperf.com/diacritics/18 + function match(a) { + return DIACRITICS[a] || a; + } + + return text.replace(/[^\u0000-\u007E]/g, match); + } + + function matcher (params, data) { + // Always return the object if there is nothing to compare + if ($.trim(params.term) === '') { + return data; + } + + // Do a recursive check for options with children + if (data.children && data.children.length > 0) { + // Clone the data object if there are children + // This is required as we modify the object to remove any non-matches + var match = $.extend(true, {}, data); + + // Check each child of the option + for (var c = data.children.length - 1; c >= 0; c--) { + var child = data.children[c]; + + var matches = matcher(params, child); + + // If there wasn't a match, remove the object in the array + if (matches == null) { + match.children.splice(c, 1); + } + } + + // If any children matched, return the new object + if (match.children.length > 0) { + return match; + } + + // If there were no matching children, check just the plain object + return matcher(params, match); + } + + var original = stripDiacritics(data.text).toUpperCase(); + var term = stripDiacritics(params.term).toUpperCase(); + + // Check if the text contains the term + if (original.indexOf(term) > -1) { + return data; + } + + // If it doesn't contain the term, don't return anything + return null; + } + + this.defaults = { + amdBase: './', + amdLanguageBase: './i18n/', + closeOnSelect: true, + debug: false, + dropdownAutoWidth: false, + escapeMarkup: Utils.escapeMarkup, + language: EnglishTranslation, + matcher: matcher, + minimumInputLength: 0, + maximumInputLength: 0, + maximumSelectionLength: 0, + minimumResultsForSearch: 0, + selectOnClose: false, + sorter: function (data) { + return data; + }, + templateResult: function (result) { + return result.text; + }, + templateSelection: function (selection) { + return selection.text; + }, + theme: 'default', + width: 'resolve' + }; + }; + + Defaults.prototype.set = function (key, value) { + var camelKey = $.camelCase(key); + + var data = {}; + data[camelKey] = value; + + var convertedData = Utils._convertData(data); + + $.extend(this.defaults, convertedData); + }; + + var defaults = new Defaults(); + + return defaults; +}); + +S2.define('select2/options',[ + 'require', + 'jquery', + './defaults', + './utils' +], function (require, $, Defaults, Utils) { + function Options (options, $element) { + this.options = options; + + if ($element != null) { + this.fromElement($element); + } + + this.options = Defaults.apply(this.options); + + if ($element && $element.is('input')) { + var InputCompat = require(this.get('amdBase') + 'compat/inputData'); + + this.options.dataAdapter = Utils.Decorate( + this.options.dataAdapter, + InputCompat + ); + } + } + + Options.prototype.fromElement = function ($e) { + var excludedData = ['select2']; + + if (this.options.multiple == null) { + this.options.multiple = $e.prop('multiple'); + } + + if (this.options.disabled == null) { + this.options.disabled = $e.prop('disabled'); + } + + if (this.options.language == null) { + if ($e.prop('lang')) { + this.options.language = $e.prop('lang').toLowerCase(); + } else if ($e.closest('[lang]').prop('lang')) { + this.options.language = $e.closest('[lang]').prop('lang'); + } + } + + if (this.options.dir == null) { + if ($e.prop('dir')) { + this.options.dir = $e.prop('dir'); + } else if ($e.closest('[dir]').prop('dir')) { + this.options.dir = $e.closest('[dir]').prop('dir'); + } else { + this.options.dir = 'ltr'; + } + } + + $e.prop('disabled', this.options.disabled); + $e.prop('multiple', this.options.multiple); + + if ($e.data('select2Tags')) { + if (this.options.debug && window.console && console.warn) { + console.warn( + 'Select2: The `data-select2-tags` attribute has been changed to ' + + 'use the `data-data` and `data-tags="true"` attributes and will be ' + + 'removed in future versions of Select2.' + ); + } + + $e.data('data', $e.data('select2Tags')); + $e.data('tags', true); + } + + if ($e.data('ajaxUrl')) { + if (this.options.debug && window.console && console.warn) { + console.warn( + 'Select2: The `data-ajax-url` attribute has been changed to ' + + '`data-ajax--url` and support for the old attribute will be removed' + + ' in future versions of Select2.' + ); + } + + $e.attr('ajax--url', $e.data('ajaxUrl')); + $e.data('ajax--url', $e.data('ajaxUrl')); + } + + var dataset = {}; + + // Prefer the element's `dataset` attribute if it exists + // jQuery 1.x does not correctly handle data attributes with multiple dashes + if ($.fn.jquery && $.fn.jquery.substr(0, 2) == '1.' && $e[0].dataset) { + dataset = $.extend(true, {}, $e[0].dataset, $e.data()); + } else { + dataset = $e.data(); + } + + var data = $.extend(true, {}, dataset); + + data = Utils._convertData(data); + + for (var key in data) { + if ($.inArray(key, excludedData) > -1) { + continue; + } + + if ($.isPlainObject(this.options[key])) { + $.extend(this.options[key], data[key]); + } else { + this.options[key] = data[key]; + } + } + + return this; + }; + + Options.prototype.get = function (key) { + return this.options[key]; + }; + + Options.prototype.set = function (key, val) { + this.options[key] = val; + }; + + return Options; +}); + +S2.define('select2/core',[ + 'jquery', + './options', + './utils', + './keys' +], function ($, Options, Utils, KEYS) { + var Select2 = function ($element, options) { + if ($element.data('select2') != null) { + $element.data('select2').destroy(); + } + + this.$element = $element; + + this.id = this._generateId($element); + + options = options || {}; + + this.options = new Options(options, $element); + + Select2.__super__.constructor.call(this); + + // Set up the tabindex + + var tabindex = $element.attr('tabindex') || 0; + $element.data('old-tabindex', tabindex); + $element.attr('tabindex', '-1'); + + // Set up containers and adapters + + var DataAdapter = this.options.get('dataAdapter'); + this.dataAdapter = new DataAdapter($element, this.options); + + var $container = this.render(); + + this._placeContainer($container); + + var SelectionAdapter = this.options.get('selectionAdapter'); + this.selection = new SelectionAdapter($element, this.options); + this.$selection = this.selection.render(); + + this.selection.position(this.$selection, $container); + + var DropdownAdapter = this.options.get('dropdownAdapter'); + this.dropdown = new DropdownAdapter($element, this.options); + this.$dropdown = this.dropdown.render(); + + this.dropdown.position(this.$dropdown, $container); + + var ResultsAdapter = this.options.get('resultsAdapter'); + this.results = new ResultsAdapter($element, this.options, this.dataAdapter); + this.$results = this.results.render(); + + this.results.position(this.$results, this.$dropdown); + + // Bind events + + var self = this; + + // Bind the container to all of the adapters + this._bindAdapters(); + + // Register any DOM event handlers + this._registerDomEvents(); + + // Register any internal event handlers + this._registerDataEvents(); + this._registerSelectionEvents(); + this._registerDropdownEvents(); + this._registerResultsEvents(); + this._registerEvents(); + + // Set the initial state + this.dataAdapter.current(function (initialData) { + self.trigger('selection:update', { + data: initialData + }); + }); + + // Hide the original select + $element.addClass('select2-hidden-accessible'); + $element.attr('aria-hidden', 'true'); + + // Synchronize any monitored attributes + this._syncAttributes(); + + $element.data('select2', this); + }; + + Utils.Extend(Select2, Utils.Observable); + + Select2.prototype._generateId = function ($element) { + var id = ''; + + if ($element.attr('id') != null) { + id = $element.attr('id'); + } else if ($element.attr('name') != null) { + id = $element.attr('name') + '-' + Utils.generateChars(2); + } else { + id = Utils.generateChars(4); + } + + id = id.replace(/(:|\.|\[|\]|,)/g, ''); + id = 'select2-' + id; + + return id; + }; + + Select2.prototype._placeContainer = function ($container) { + $container.insertAfter(this.$element); + + var width = this._resolveWidth(this.$element, this.options.get('width')); + + if (width != null) { + $container.css('width', width); + } + }; + + Select2.prototype._resolveWidth = function ($element, method) { + var WIDTH = /^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i; + + if (method == 'resolve') { + var styleWidth = this._resolveWidth($element, 'style'); + + if (styleWidth != null) { + return styleWidth; + } + + return this._resolveWidth($element, 'element'); + } + + if (method == 'element') { + var elementWidth = $element.outerWidth(false); + + if (elementWidth <= 0) { + return 'auto'; + } + + return elementWidth + 'px'; + } + + if (method == 'style') { + var style = $element.attr('style'); + + if (typeof(style) !== 'string') { + return null; + } + + var attrs = style.split(';'); + + for (var i = 0, l = attrs.length; i < l; i = i + 1) { + var attr = attrs[i].replace(/\s/g, ''); + var matches = attr.match(WIDTH); + + if (matches !== null && matches.length >= 1) { + return matches[1]; + } + } + + return null; + } + + return method; + }; + + Select2.prototype._bindAdapters = function () { + this.dataAdapter.bind(this, this.$container); + this.selection.bind(this, this.$container); + + this.dropdown.bind(this, this.$container); + this.results.bind(this, this.$container); + }; + + Select2.prototype._registerDomEvents = function () { + var self = this; + + this.$element.on('change.select2', function () { + self.dataAdapter.current(function (data) { + self.trigger('selection:update', { + data: data + }); + }); + }); + + this.$element.on('focus.select2', function (evt) { + self.trigger('focus', evt); + }); + + this._syncA = Utils.bind(this._syncAttributes, this); + this._syncS = Utils.bind(this._syncSubtree, this); + + if (this.$element[0].attachEvent) { + this.$element[0].attachEvent('onpropertychange', this._syncA); + } + + var observer = window.MutationObserver || + window.WebKitMutationObserver || + window.MozMutationObserver + ; + + if (observer != null) { + this._observer = new observer(function (mutations) { + $.each(mutations, self._syncA); + $.each(mutations, self._syncS); + }); + this._observer.observe(this.$element[0], { + attributes: true, + childList: true, + subtree: false + }); + } else if (this.$element[0].addEventListener) { + this.$element[0].addEventListener( + 'DOMAttrModified', + self._syncA, + false + ); + this.$element[0].addEventListener( + 'DOMNodeInserted', + self._syncS, + false + ); + this.$element[0].addEventListener( + 'DOMNodeRemoved', + self._syncS, + false + ); + } + }; + + Select2.prototype._registerDataEvents = function () { + var self = this; + + this.dataAdapter.on('*', function (name, params) { + self.trigger(name, params); + }); + }; + + Select2.prototype._registerSelectionEvents = function () { + var self = this; + var nonRelayEvents = ['toggle', 'focus']; + + this.selection.on('toggle', function () { + self.toggleDropdown(); + }); + + this.selection.on('focus', function (params) { + self.focus(params); + }); + + this.selection.on('*', function (name, params) { + if ($.inArray(name, nonRelayEvents) !== -1) { + return; + } + + self.trigger(name, params); + }); + }; + + Select2.prototype._registerDropdownEvents = function () { + var self = this; + + this.dropdown.on('*', function (name, params) { + self.trigger(name, params); + }); + }; + + Select2.prototype._registerResultsEvents = function () { + var self = this; + + this.results.on('*', function (name, params) { + self.trigger(name, params); + }); + }; + + Select2.prototype._registerEvents = function () { + var self = this; + + this.on('open', function () { + self.$container.addClass('select2-container--open'); + }); + + this.on('close', function () { + self.$container.removeClass('select2-container--open'); + }); + + this.on('enable', function () { + self.$container.removeClass('select2-container--disabled'); + }); + + this.on('disable', function () { + self.$container.addClass('select2-container--disabled'); + }); + + this.on('blur', function () { + self.$container.removeClass('select2-container--focus'); + }); + + this.on('query', function (params) { + if (!self.isOpen()) { + self.trigger('open', {}); + } + + this.dataAdapter.query(params, function (data) { + self.trigger('results:all', { + data: data, + query: params + }); + }); + }); + + this.on('query:append', function (params) { + this.dataAdapter.query(params, function (data) { + self.trigger('results:append', { + data: data, + query: params + }); + }); + }); + + this.on('keypress', function (evt) { + var key = evt.which; + + if (self.isOpen()) { + if (key === KEYS.ESC || key === KEYS.TAB || + (key === KEYS.UP && evt.altKey)) { + self.close(); + + evt.preventDefault(); + } else if (key === KEYS.ENTER) { + self.trigger('results:select', {}); + + evt.preventDefault(); + } else if ((key === KEYS.SPACE && evt.ctrlKey)) { + self.trigger('results:toggle', {}); + + evt.preventDefault(); + } else if (key === KEYS.UP) { + self.trigger('results:previous', {}); + + evt.preventDefault(); + } else if (key === KEYS.DOWN) { + self.trigger('results:next', {}); + + evt.preventDefault(); + } + } else { + if (key === KEYS.ENTER || key === KEYS.SPACE || + (key === KEYS.DOWN && evt.altKey)) { + self.open(); + + evt.preventDefault(); + } + } + }); + }; + + Select2.prototype._syncAttributes = function () { + this.options.set('disabled', this.$element.prop('disabled')); + + if (this.options.get('disabled')) { + if (this.isOpen()) { + this.close(); + } + + this.trigger('disable', {}); + } else { + this.trigger('enable', {}); + } + }; + + Select2.prototype._syncSubtree = function (evt, mutations) { + var changed = false; + var self = this; + + // Ignore any mutation events raised for elements that aren't options or + // optgroups. This handles the case when the select element is destroyed + if ( + evt && evt.target && ( + evt.target.nodeName !== 'OPTION' && evt.target.nodeName !== 'OPTGROUP' + ) + ) { + return; + } + + if (!mutations) { + // If mutation events aren't supported, then we can only assume that the + // change affected the selections + changed = true; + } else if (mutations.addedNodes && mutations.addedNodes.length > 0) { + for (var n = 0; n < mutations.addedNodes.length; n++) { + var node = mutations.addedNodes[n]; + + if (node.selected) { + changed = true; + } + } + } else if (mutations.removedNodes && mutations.removedNodes.length > 0) { + changed = true; + } + + // Only re-pull the data if we think there is a change + if (changed) { + this.dataAdapter.current(function (currentData) { + self.trigger('selection:update', { + data: currentData + }); + }); + } + }; + + /** + * Override the trigger method to automatically trigger pre-events when + * there are events that can be prevented. + */ + Select2.prototype.trigger = function (name, args) { + var actualTrigger = Select2.__super__.trigger; + var preTriggerMap = { + 'open': 'opening', + 'close': 'closing', + 'select': 'selecting', + 'unselect': 'unselecting' + }; + + if (args === undefined) { + args = {}; + } + + if (name in preTriggerMap) { + var preTriggerName = preTriggerMap[name]; + var preTriggerArgs = { + prevented: false, + name: name, + args: args + }; + + actualTrigger.call(this, preTriggerName, preTriggerArgs); + + if (preTriggerArgs.prevented) { + args.prevented = true; + + return; + } + } + + actualTrigger.call(this, name, args); + }; + + Select2.prototype.toggleDropdown = function () { + if (this.options.get('disabled')) { + return; + } + + if (this.isOpen()) { + this.close(); + } else { + this.open(); + } + }; + + Select2.prototype.open = function () { + if (this.isOpen()) { + return; + } + + this.trigger('query', {}); + }; + + Select2.prototype.close = function () { + if (!this.isOpen()) { + return; + } + + this.trigger('close', {}); + }; + + Select2.prototype.isOpen = function () { + return this.$container.hasClass('select2-container--open'); + }; + + Select2.prototype.hasFocus = function () { + return this.$container.hasClass('select2-container--focus'); + }; + + Select2.prototype.focus = function (data) { + // No need to re-trigger focus events if we are already focused + if (this.hasFocus()) { + return; + } + + this.$container.addClass('select2-container--focus'); + this.trigger('focus', {}); + }; + + Select2.prototype.enable = function (args) { + if (this.options.get('debug') && window.console && console.warn) { + console.warn( + 'Select2: The `select2("enable")` method has been deprecated and will' + + ' be removed in later Select2 versions. Use $element.prop("disabled")' + + ' instead.' + ); + } + + if (args == null || args.length === 0) { + args = [true]; + } + + var disabled = !args[0]; + + this.$element.prop('disabled', disabled); + }; + + Select2.prototype.data = function () { + if (this.options.get('debug') && + arguments.length > 0 && window.console && console.warn) { + console.warn( + 'Select2: Data can no longer be set using `select2("data")`. You ' + + 'should consider setting the value instead using `$element.val()`.' + ); + } + + var data = []; + + this.dataAdapter.current(function (currentData) { + data = currentData; + }); + + return data; + }; + + Select2.prototype.val = function (args) { + if (this.options.get('debug') && window.console && console.warn) { + console.warn( + 'Select2: The `select2("val")` method has been deprecated and will be' + + ' removed in later Select2 versions. Use $element.val() instead.' + ); + } + + if (args == null || args.length === 0) { + return this.$element.val(); + } + + var newVal = args[0]; + + if ($.isArray(newVal)) { + newVal = $.map(newVal, function (obj) { + return obj.toString(); + }); + } + + this.$element.val(newVal).trigger('change'); + }; + + Select2.prototype.destroy = function () { + this.$container.remove(); + + if (this.$element[0].detachEvent) { + this.$element[0].detachEvent('onpropertychange', this._syncA); + } + + if (this._observer != null) { + this._observer.disconnect(); + this._observer = null; + } else if (this.$element[0].removeEventListener) { + this.$element[0] + .removeEventListener('DOMAttrModified', this._syncA, false); + this.$element[0] + .removeEventListener('DOMNodeInserted', this._syncS, false); + this.$element[0] + .removeEventListener('DOMNodeRemoved', this._syncS, false); + } + + this._syncA = null; + this._syncS = null; + + this.$element.off('.select2'); + this.$element.attr('tabindex', this.$element.data('old-tabindex')); + + this.$element.removeClass('select2-hidden-accessible'); + this.$element.attr('aria-hidden', 'false'); + this.$element.removeData('select2'); + + this.dataAdapter.destroy(); + this.selection.destroy(); + this.dropdown.destroy(); + this.results.destroy(); + + this.dataAdapter = null; + this.selection = null; + this.dropdown = null; + this.results = null; + }; + + Select2.prototype.render = function () { + var $container = $( + '' + + '' + + '' + + '' + ); + + $container.attr('dir', this.options.get('dir')); + + this.$container = $container; + + this.$container.addClass('select2-container--' + this.options.get('theme')); + + $container.data('element', this.$element); + + return $container; + }; + + return Select2; +}); + +S2.define('jquery-mousewheel',[ + 'jquery' +], function ($) { + // Used to shim jQuery.mousewheel for non-full builds. + return $; +}); + +S2.define('jquery.select2',[ + 'jquery', + 'jquery-mousewheel', + + './select2/core', + './select2/defaults' +], function ($, _, Select2, Defaults) { + if ($.fn.select2 == null) { + // All methods that should return the element + var thisMethods = ['open', 'close', 'destroy']; + + $.fn.select2 = function (options) { + options = options || {}; + + if (typeof options === 'object') { + this.each(function () { + var instanceOptions = $.extend(true, {}, options); + + var instance = new Select2($(this), instanceOptions); + }); + + return this; + } else if (typeof options === 'string') { + var ret; + var args = Array.prototype.slice.call(arguments, 1); + + this.each(function () { + var instance = $(this).data('select2'); + + if (instance == null && window.console && console.error) { + console.error( + 'The select2(\'' + options + '\') method was called on an ' + + 'element that is not using Select2.' + ); + } + + ret = instance[options].apply(instance, args); + }); + + // Check if we should be returning `this` + if ($.inArray(options, thisMethods) > -1) { + return this; + } + + return ret; + } else { + throw new Error('Invalid arguments for Select2: ' + options); + } + }; + } + + if ($.fn.select2.defaults == null) { + $.fn.select2.defaults = Defaults; + } + + return Select2; +}); + + // Return the AMD loader configuration so it can be used outside of this file + return { + define: S2.define, + require: S2.require + }; +}()); + + // Autoload the jQuery bindings + // We know that all of the modules exist above this, so we're safe + var select2 = S2.require('jquery.select2'); + + // Hold the AMD module references on the jQuery function that was just loaded + // This allows Select2 to use the internal loader outside of this file, such + // as in the language files. + jQuery.fn.select2.amd = S2; + + // Return the Select2 instance for anyone who is importing it. + return select2; +})); diff --git a/plugins/woocommerce/composer.json b/plugins/woocommerce/composer.json index 52052c43651..b51f7e3c195 100644 --- a/plugins/woocommerce/composer.json +++ b/plugins/woocommerce/composer.json @@ -2,7 +2,7 @@ "name": "woocommerce/woocommerce", "description": "An eCommerce toolkit that helps you sell anything. Beautifully.", "homepage": "https://woocommerce.com/", - "version": "9.3.0", + "version": "9.4.0", "type": "wordpress-plugin", "license": "GPL-3.0-or-later", "prefer-stable": true, diff --git a/plugins/woocommerce/includes/abstracts/abstract-wc-order.php b/plugins/woocommerce/includes/abstracts/abstract-wc-order.php index ac30221490b..57786b9a591 100644 --- a/plugins/woocommerce/includes/abstracts/abstract-wc-order.php +++ b/plugins/woocommerce/includes/abstracts/abstract-wc-order.php @@ -1413,7 +1413,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { private function get_temporary_coupon( WC_Order_Item_Coupon $coupon_item ): WC_Coupon { $coupon_object = new WC_Coupon(); - // Since WooCommerce 8.7 a succint 'coupon_info' line item meta entry is created + // Since WooCommerce 8.7 a succinct 'coupon_info' line item meta entry is created // whenever a coupon is applied to an order. Previously a more verbose 'coupon_data' was created. $coupon_info = $coupon_item->get_meta( 'coupon_info', true ); diff --git a/plugins/woocommerce/includes/admin/class-wc-admin-assets.php b/plugins/woocommerce/includes/admin/class-wc-admin-assets.php index cf191c483e2..f93b755c6c1 100644 --- a/plugins/woocommerce/includes/admin/class-wc-admin-assets.php +++ b/plugins/woocommerce/includes/admin/class-wc-admin-assets.php @@ -149,7 +149,7 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) : wp_register_script( 'wc-shipping-zone-methods', WC()->plugin_url() . '/assets/js/admin/wc-shipping-zone-methods' . $suffix . '.js', array( 'jquery', 'wp-util', 'underscore', 'backbone', 'jquery-ui-sortable', 'wc-backbone-modal' ), $version ); wp_register_script( 'wc-shipping-classes', WC()->plugin_url() . '/assets/js/admin/wc-shipping-classes' . $suffix . '.js', array( 'jquery', 'wp-util', 'underscore', 'backbone', 'wc-backbone-modal' ), $version, array( 'in_footer' => false ) ); wp_register_script( 'wc-clipboard', WC()->plugin_url() . '/assets/js/admin/wc-clipboard' . $suffix . '.js', array( 'jquery' ), $version ); - wp_register_script( 'select2', WC()->plugin_url() . '/assets/js/selectWoo/selectWoo.full' . $suffix . '.js', array( 'jquery' ), '1.0.6' ); + wp_register_script( 'select2', WC()->plugin_url() . '/assets/js/select2/select2.full' . $suffix . '.js', array( 'jquery' ), '4.0.3', array( 'in_footer' => false ) ); wp_register_script( 'selectWoo', WC()->plugin_url() . '/assets/js/selectWoo/selectWoo.full' . $suffix . '.js', array( 'jquery' ), '1.0.6' ); wp_register_script( 'wc-enhanced-select', WC()->plugin_url() . '/assets/js/admin/wc-enhanced-select' . $suffix . '.js', array( 'jquery', 'selectWoo' ), $version ); wp_register_script( 'js-cookie', WC()->plugin_url() . '/assets/js/js-cookie/js.cookie' . $suffix . '.js', array(), '2.1.4', true ); diff --git a/plugins/woocommerce/includes/admin/class-wc-admin-importers.php b/plugins/woocommerce/includes/admin/class-wc-admin-importers.php index 37c59e4dc5b..6dfec075b89 100644 --- a/plugins/woocommerce/includes/admin/class-wc-admin-importers.php +++ b/plugins/woocommerce/includes/admin/class-wc-admin-importers.php @@ -1,4 +1,4 @@ -importers['product_importer'] = array( @@ -96,10 +97,15 @@ class WC_Admin_Importers { */ public function product_importer() { if ( Constants::is_defined( 'WP_LOAD_IMPORTERS' ) ) { - wp_safe_redirect( admin_url( 'edit.php?post_type=product&page=product_importer' ) ); + wp_safe_redirect( admin_url( 'edit.php?post_type=product&page=product_importer&source=wordpress-importer' ) ); exit; } + // phpcs:ignore + if ( isset( $_GET['source'] ) && 'wordpress-importer' === sanitize_text_field( wp_unslash( $_GET['source'] ) ) ) { + wc_admin_record_tracks_event( 'product_importer_view_from_wp_importer' ); + } + include_once WC_ABSPATH . 'includes/import/class-wc-product-csv-importer.php'; include_once WC_ABSPATH . 'includes/admin/importers/class-wc-product-csv-importer-controller.php'; @@ -132,7 +138,9 @@ class WC_Admin_Importers { } } - require dirname( __FILE__ ) . '/importers/class-wc-tax-rate-importer.php'; + wc_admin_record_tracks_event( 'tax_rates_importer_view_from_wp_importer' ); + + require __DIR__ . '/importers/class-wc-tax-rate-importer.php'; $importer = new WC_Tax_Rate_Importer(); $importer->dispatch(); @@ -182,7 +190,9 @@ class WC_Admin_Importers { // Register the taxonomy now so that the import works! register_taxonomy( $term['domain'], + // phpcs:ignore apply_filters( 'woocommerce_taxonomy_objects_' . $term['domain'], array( 'product' ) ), + // phpcs:ignore apply_filters( 'woocommerce_taxonomy_args_' . $term['domain'], array( @@ -229,7 +239,7 @@ class WC_Admin_Importers { * * @param int $size Batch size. * - * @since + * @since 3.1.0 */ 'lines' => apply_filters( 'woocommerce_product_import_batch_size', 30 ), 'parse' => true, @@ -314,6 +324,29 @@ class WC_Admin_Importers { ); } } + + /** + * Track importer/exporter view. + * + * @return void + */ + public function track_importer_exporter_view() { + $screen = get_current_screen(); + + if ( ! isset( $screen->id ) ) { + return; + } + + // Don't track if we're in a specific import screen. + // phpcs:ignore + if ( isset( $_GET['import'] ) ) { + return; + } + + if ( 'import' === $screen->id || 'export' === $screen->id ) { + wc_admin_record_tracks_event( 'wordpress_' . $screen->id . '_view' ); + } + } } new WC_Admin_Importers(); diff --git a/plugins/woocommerce/includes/admin/class-wc-admin-marketplace-promotions.php b/plugins/woocommerce/includes/admin/class-wc-admin-marketplace-promotions.php index d1e6f667932..02aedbd9556 100644 --- a/plugins/woocommerce/includes/admin/class-wc-admin-marketplace-promotions.php +++ b/plugins/woocommerce/includes/admin/class-wc-admin-marketplace-promotions.php @@ -219,8 +219,8 @@ class WC_Admin_Marketplace_Promotions { /** * From the array of promotions, select those of a given format. * - * @param ? array $promotions Array of data about promotions of all formats. - * @param ? string $format Format we want to filter for. + * @param ?array $promotions Array of data about promotions of all formats. + * @param ?string $format Format we want to filter for. * * @return array */ diff --git a/plugins/woocommerce/includes/admin/class-wc-admin-post-types.php b/plugins/woocommerce/includes/admin/class-wc-admin-post-types.php index 108e27384ea..095893653ba 100644 --- a/plugins/woocommerce/includes/admin/class-wc-admin-post-types.php +++ b/plugins/woocommerce/includes/admin/class-wc-admin-post-types.php @@ -896,7 +896,8 @@ class WC_Admin_Post_Types { return false; } - $old_price = (float) $product->{"get_{$price_type}_price"}(); + $old_price = $product->{"get_{$price_type}_price"}(); + $old_price = '' === $old_price ? (float) $product->get_regular_price() : (float) $old_price; $price_changed = false; $change_price = absint( $request_data[ "change_{$price_type}_price" ] ); @@ -906,13 +907,17 @@ class WC_Admin_Post_Types { switch ( $change_price ) { case 1: - $new_price = $price; + if ( empty( $price ) ) { + $new_price = $product->get_regular_price(); + } else { + $new_price = $price; + } break; case 2: if ( $is_percentage ) { $percent = $price / 100; $new_price = $old_price + ( $old_price * $percent ); - } else { + } elseif ( ! empty( $price ) ) { $new_price = $old_price + $price; } break; @@ -920,7 +925,7 @@ class WC_Admin_Post_Types { if ( $is_percentage ) { $percent = $price / 100; $new_price = max( 0, $old_price - ( $old_price * $percent ) ); - } else { + } elseif ( ! empty( $price ) ) { $new_price = max( 0, $old_price - $price ); } break; diff --git a/plugins/woocommerce/includes/admin/helper/class-wc-helper-subscriptions-api.php b/plugins/woocommerce/includes/admin/helper/class-wc-helper-subscriptions-api.php index 09b1b4d1ae2..6346f8e0a4f 100644 --- a/plugins/woocommerce/includes/admin/helper/class-wc-helper-subscriptions-api.php +++ b/plugins/woocommerce/includes/admin/helper/class-wc-helper-subscriptions-api.php @@ -139,6 +139,8 @@ class WC_Helper_Subscriptions_API { */ public static function refresh() { WC_Helper::refresh_helper_subscriptions(); + WC_Helper::get_subscriptions(); + WC_Helper::get_product_usage_notice_rules(); self::get_subscriptions(); } diff --git a/plugins/woocommerce/includes/admin/helper/class-wc-helper.php b/plugins/woocommerce/includes/admin/helper/class-wc-helper.php index 18c0c7dbc28..586b47fc21f 100644 --- a/plugins/woocommerce/includes/admin/helper/class-wc-helper.php +++ b/plugins/woocommerce/includes/admin/helper/class-wc-helper.php @@ -1007,6 +1007,7 @@ class WC_Helper { self::_flush_authentication_cache(); self::_flush_subscriptions_cache(); self::_flush_updates_cache(); + self::flush_product_usage_notice_rules_cache(); } /** @@ -1589,6 +1590,7 @@ class WC_Helper { 'product-usage-notice-rules', array( 'authenticated' => false, + 'timeout' => 2, ) ); @@ -2214,6 +2216,13 @@ class WC_Helper { delete_transient( '_woocommerce_helper_subscriptions' ); } + /** + * Flush product-usage-notice-rules cache. + */ + public static function flush_product_usage_notice_rules_cache() { + delete_transient( '_woocommerce_helper_product_usage_notice_rules' ); + } + /** * Flush auth cache. */ @@ -2313,6 +2322,7 @@ class WC_Helper { self::_flush_subscriptions_cache(); self::_flush_updates_cache(); + self::flush_product_usage_notice_rules_cache(); } /** @@ -2402,6 +2412,7 @@ class WC_Helper { self::_flush_subscriptions_cache(); self::_flush_updates_cache(); + self::flush_product_usage_notice_rules_cache(); } /** diff --git a/plugins/woocommerce/includes/admin/settings/class-wc-settings-payment-gateways-react.php b/plugins/woocommerce/includes/admin/settings/class-wc-settings-payment-gateways-react.php index 178c33a4daf..88ee03acd20 100644 --- a/plugins/woocommerce/includes/admin/settings/class-wc-settings-payment-gateways-react.php +++ b/plugins/woocommerce/includes/admin/settings/class-wc-settings-payment-gateways-react.php @@ -57,6 +57,16 @@ class WC_Settings_Payment_Gateways_React extends WC_Settings_Page { //phpcs:disable WordPress.Security.NonceVerification.Recommended global $current_section; + // We don't want to output anything from the action for now. So we buffer it and discard it. + ob_start(); + /** + * Fires before the payment gateways settings fields are rendered. + * + * @since 1.5.7 + */ + do_action( 'woocommerce_admin_field_payment_gateways' ); + ob_end_clean(); + // Load gateways so we can show any global options they may have. $payment_gateways = WC()->payment_gateways->payment_gateways(); @@ -91,6 +101,11 @@ class WC_Settings_Payment_Gateways_React extends WC_Settings_Page { global $hide_save_button; $hide_save_button = true; echo '
        '; + + // Output the gateways data to the page so the React app can use it. + $controller = new WC_REST_Payment_Gateways_Controller(); + $response = $controller->get_items( new WP_REST_Request( 'GET', '/wc/v3/payment_gateways' ) ); + echo ''; } /** diff --git a/plugins/woocommerce/includes/admin/settings/class-wc-settings-products.php b/plugins/woocommerce/includes/admin/settings/class-wc-settings-products.php index 813f108ee02..e37d37d90eb 100644 --- a/plugins/woocommerce/includes/admin/settings/class-wc-settings-products.php +++ b/plugins/woocommerce/includes/admin/settings/class-wc-settings-products.php @@ -446,6 +446,20 @@ class WC_Settings_Products extends WC_Settings_Page { ), ), + array( + 'title' => __( 'Count partial downloads', 'woocommerce' ), + 'desc' => __( 'Count downloads even if only part of a file is fetched.', 'woocommerce' ), + 'id' => 'woocommerce_downloads_count_partial', + 'type' => 'checkbox', + 'default' => 'yes', + 'desc_tip' => sprintf( + /* Translators: 1: opening link tag 2: closing link tag. */ + __( 'Repeat fetches made within a reasonable window of time (by default, 30 minutes) will not be counted twice. This is a generally reasonably way to enforce download limits in relation to ranged requests. %1$sLearn more.%2$s', 'woocommerce' ), + '', + '' + ), + ), + array( 'type' => 'sectionend', 'id' => 'digital_download_options', diff --git a/plugins/woocommerce/includes/admin/views/html-admin-page-status-report.php b/plugins/woocommerce/includes/admin/views/html-admin-page-status-report.php index 1630d059b2c..76a0418a68f 100644 --- a/plugins/woocommerce/includes/admin/views/html-admin-page-status-report.php +++ b/plugins/woocommerce/includes/admin/views/html-admin-page-status-report.php @@ -1060,6 +1060,12 @@ if ( 0 < $mu_plugins_count ) : + | + + + + + diff --git a/plugins/woocommerce/includes/class-wc-cart.php b/plugins/woocommerce/includes/class-wc-cart.php index 6b036136f8a..db8fefc926b 100644 --- a/plugins/woocommerce/includes/class-wc-cart.php +++ b/plugins/woocommerce/includes/class-wc-cart.php @@ -1170,7 +1170,7 @@ class WC_Cart extends WC_Legacy_Cart { $message = apply_filters( 'woocommerce_cart_product_cannot_add_another_message', $message, $product_data ); $wp_button_class = wc_wp_theme_get_element_class_name( 'button' ) ? ' ' . wc_wp_theme_get_element_class_name( 'button' ) : ''; - throw new Exception( sprintf( '%s %s', wc_get_cart_url(), esc_attr( $wp_button_class ), __( 'View cart', 'woocommerce' ), $message ) ); + throw new Exception( sprintf( '%s %s', $message, wc_get_cart_url(), esc_attr( $wp_button_class ), __( 'View cart', 'woocommerce' ) ) ); } } @@ -1232,12 +1232,12 @@ class WC_Cart extends WC_Legacy_Cart { $wp_button_class = wc_wp_theme_get_element_class_name( 'button' ) ? ' ' . wc_wp_theme_get_element_class_name( 'button' ) : ''; $message = sprintf( - '%s %s', + '%s %s', + /* translators: 1: quantity in stock 2: current quantity */ + sprintf( __( 'You cannot add that amount to the cart — we have %1$s in stock and you already have %2$s in your cart.', 'woocommerce' ), wc_format_stock_quantity_for_display( $stock_quantity, $product_data ), wc_format_stock_quantity_for_display( $stock_quantity_in_cart, $product_data ) ), wc_get_cart_url(), esc_attr( $wp_button_class ), - __( 'View cart', 'woocommerce' ), - /* translators: 1: quantity in stock 2: current quantity */ - sprintf( __( 'You cannot add that amount to the cart — we have %1$s in stock and you already have %2$s in your cart.', 'woocommerce' ), wc_format_stock_quantity_for_display( $stock_quantity, $product_data ), wc_format_stock_quantity_for_display( $stock_quantity_in_cart, $product_data ) ) + __( 'View cart', 'woocommerce' ) ); /** diff --git a/plugins/woocommerce/includes/class-wc-checkout.php b/plugins/woocommerce/includes/class-wc-checkout.php index 943746642e8..cfc36f69e7e 100644 --- a/plugins/woocommerce/includes/class-wc-checkout.php +++ b/plugins/woocommerce/includes/class-wc-checkout.php @@ -1132,7 +1132,17 @@ class WC_Checkout { ); if ( is_wp_error( $customer_id ) ) { - throw new Exception( $customer_id->get_error_message() ); + if ( 'registration-error-email-exists' === $customer_id->get_error_code() ) { + /** + * Filter the notice shown when a customer tries to register with an existing email address. + * + * @since 3.3.0 + * @param string $message The notice. + * @param string $email The email address. + */ + throw new Exception( apply_filters( 'woocommerce_registration_error_email_exists', __( 'An account is already registered with your email address. Please log in.', 'woocommerce' ), $data['billing_email'] ) ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped + } + throw new Exception( $customer_id->get_error_message() ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped } wc_set_customer_auth_cookie( $customer_id ); @@ -1357,7 +1367,7 @@ class WC_Checkout { if ( is_callable( array( $customer_object, "get_$input" ) ) ) { $value = $customer_object->{"get_$input"}(); - } elseif ( $customer_object->meta_exists( $input ) ) { + } elseif ( is_callable( array( $customer_object, 'meta_exists' ) ) && $customer_object->meta_exists( $input ) ) { $value = $customer_object->get_meta( $input, true ); } diff --git a/plugins/woocommerce/includes/class-wc-coupon.php b/plugins/woocommerce/includes/class-wc-coupon.php index 01e4ac8a489..5c7691cf092 100644 --- a/plugins/woocommerce/includes/class-wc-coupon.php +++ b/plugins/woocommerce/includes/class-wc-coupon.php @@ -85,7 +85,7 @@ class WC_Coupon extends WC_Legacy_Coupon { * Error message. * * This property should not be considered public API, and should not be accessed directly. - * It is being added to supress PHP > 8.0 warnings against dynamic property creation, and all access + * It is being added to suppress PHP > 8.0 warnings against dynamic property creation, and all access * should be through the getter and setter methods, namely `get_error_message()` and `set_error_message()`. * In the future, the access modifier may be changed back to protected. * diff --git a/plugins/woocommerce/includes/class-wc-download-handler.php b/plugins/woocommerce/includes/class-wc-download-handler.php index 82571a9cf65..cb9d5d940a0 100644 --- a/plugins/woocommerce/includes/class-wc-download-handler.php +++ b/plugins/woocommerce/includes/class-wc-download-handler.php @@ -8,12 +8,19 @@ * @version 2.2.0 */ +use Automattic\WooCommerce\Internal\Traits\AccessiblePrivateMethods; defined( 'ABSPATH' ) || exit; /** * Download handler class. */ class WC_Download_Handler { + use AccessiblePrivateMethods; + + /** + * The hook used for deferred tracking of partial download attempts. + */ + public const TRACK_DOWNLOAD_CALLBACK = 'track_partial_download'; /** * Hook in methods. @@ -25,6 +32,7 @@ class WC_Download_Handler { add_action( 'woocommerce_download_file_redirect', array( __CLASS__, 'download_file_redirect' ), 10, 2 ); add_action( 'woocommerce_download_file_xsendfile', array( __CLASS__, 'download_file_xsendfile' ), 10, 2 ); add_action( 'woocommerce_download_file_force', array( __CLASS__, 'download_file_force' ), 10, 2 ); + self::add_action( self::TRACK_DOWNLOAD_CALLBACK, array( __CLASS__, 'track_download' ), 10, 3 ); } /** @@ -135,9 +143,13 @@ class WC_Download_Handler { // Track the download in logs and change remaining/counts. $current_user_id = get_current_user_id(); $ip_address = WC_Geolocation::get_ip_address(); - if ( ! $download_range['is_range_request'] ) { - $download->track_download( $current_user_id > 0 ? $current_user_id : null, ! empty( $ip_address ) ? $ip_address : null ); - } + + self::track_download( + $download, + $current_user_id > 0 ? $current_user_id : null, + ! empty( $ip_address ) ? $ip_address : null, + $download_range['is_range_request'] + ); self::download( $file_path, $download->get_product_id() ); } @@ -695,6 +707,76 @@ class WC_Download_Handler { } wp_die( $message, $title, array( 'response' => $status ) ); // WPCS: XSS ok. } + + /** + * Takes care of tracking download requests, with support for deferring tracking in the case of + * partial (ranged request) downloads. + * + * @param WC_Customer_Download|int $download The download to be tracked. + * @param int|null $user_id The user ID, if known. + * @param string|null $user_ip_address The download IP address, if known. + * @param bool $defer If tracking the download should be deferred. + * + * @return void + * @throws Exception If the active version of Action Scheduler is less than 3.6.0. + */ + private static function track_download( $download, $user_id = null, $user_ip_address = null, bool $defer = false ): void { + try { + // If we were supplied with an integer, convert it to a download object. + $download = new WC_Customer_Download( $download ); + + // In simple cases, we can track the download immediately. + if ( ! $defer ) { + $download->track_download( $user_id, $user_ip_address ); + return; + } + + // Counting of partial downloads may be disabled by the site operator. + if ( get_option( 'woocommerce_downloads_count_partial', 'yes' ) !== 'yes' ) { + return; + } + + /** + * Determines how long the window of time is for tracking unique download attempts, in relation to + * partial (ranged) download requests. + * + * @since 9.2.0 + * + * @param int $window_in_seconds Non-negative number of seconds. Defaults to 1800 (30 minutes). + * @param int $download_permission_id References the download permission being tracked. + */ + $window = absint( apply_filters( 'woocommerce_partial_download_tracking_window', 30 * MINUTE_IN_SECONDS, $download->get_id() ) ); + + // If we do not have Action Scheduler 3.6.0+ (this would be an unexpected scenario) then we cannot + // track partial downloads, because we require support for unique actions. + if ( version_compare( ActionScheduler_Versions::instance()->latest_version(), '3.6.0', '<' ) ) { + throw new Exception( 'Support for unique scheduled actions is not currently available.' ); + } + + as_schedule_single_action( + time() + $window, + self::TRACK_DOWNLOAD_CALLBACK, + array( + $download->get_id(), + $user_id, + $user_ip_address, + ), + 'woocommerce', + true + ); + } catch ( Exception $e ) { + wc_get_logger()->error( + 'There was a problem while tracking a product download.', + array( + 'error' => $e->getMessage(), + 'id' => $download->get_id(), + 'user_id' => $user_id, + 'ip' => $user_ip_address, + 'deferred' => $defer ? 'yes' : 'no', + ) + ); + } + } } WC_Download_Handler::init(); diff --git a/plugins/woocommerce/includes/class-wc-form-handler.php b/plugins/woocommerce/includes/class-wc-form-handler.php index 1ac35bda206..b9575b2ef95 100644 --- a/plugins/woocommerce/includes/class-wc-form-handler.php +++ b/plugins/woocommerce/includes/class-wc-form-handler.php @@ -986,7 +986,7 @@ class WC_Form_Handler { } } - // Peform the login. + // Perform the login. $user = wp_signon( apply_filters( 'woocommerce_login_credentials', $creds ), is_ssl() ); if ( is_wp_error( $user ) ) { diff --git a/plugins/woocommerce/includes/class-wc-frontend-scripts.php b/plugins/woocommerce/includes/class-wc-frontend-scripts.php index c9e1338da2d..0f97337928c 100644 --- a/plugins/woocommerce/includes/class-wc-frontend-scripts.php +++ b/plugins/woocommerce/includes/class-wc-frontend-scripts.php @@ -235,9 +235,9 @@ class WC_Frontend_Scripts { 'version' => $version, ), 'select2' => array( - 'src' => self::get_asset_url( 'assets/js/selectWoo/selectWoo.full' . $suffix . '.js' ), + 'src' => self::get_asset_url( 'assets/js/select2/select2.full' . $suffix . '.js' ), 'deps' => array( 'jquery' ), - 'version' => '1.0.9-wc.' . $version, + 'version' => '4.0.3-wc.' . $version, ), 'selectWoo' => array( 'src' => self::get_asset_url( 'assets/js/selectWoo/selectWoo.full' . $suffix . '.js' ), diff --git a/plugins/woocommerce/includes/class-wc-geo-ip.php b/plugins/woocommerce/includes/class-wc-geo-ip.php index 392025a83f7..9c5e751d270 100644 --- a/plugins/woocommerce/includes/class-wc-geo-ip.php +++ b/plugins/woocommerce/includes/class-wc-geo-ip.php @@ -633,7 +633,7 @@ class WC_Geo_IP { ); /** - * Contry names. + * Country names. * * @var array */ diff --git a/plugins/woocommerce/includes/class-wc-install.php b/plugins/woocommerce/includes/class-wc-install.php index d5184396673..e6d591d8d11 100644 --- a/plugins/woocommerce/includes/class-wc-install.php +++ b/plugins/woocommerce/includes/class-wc-install.php @@ -264,6 +264,7 @@ class WC_Install { ), '9.3.0' => array( 'wc_update_930_add_woocommerce_coming_soon_option', + 'wc_update_930_migrate_user_meta_for_launch_your_store_tour', ), ); diff --git a/plugins/woocommerce/includes/class-wc-post-data.php b/plugins/woocommerce/includes/class-wc-post-data.php index f1563c5d976..56e826b34c5 100644 --- a/plugins/woocommerce/includes/class-wc-post-data.php +++ b/plugins/woocommerce/includes/class-wc-post-data.php @@ -58,6 +58,8 @@ class WC_Post_Data { // Meta cache flushing. add_action( 'updated_post_meta', array( __CLASS__, 'flush_object_meta_cache' ), 10, 4 ); + add_action( 'added_post_meta', array( __CLASS__, 'flush_object_meta_cache' ), 10, 4 ); + add_action( 'deleted_post_meta', array( __CLASS__, 'flush_object_meta_cache' ), 10, 4 ); add_action( 'updated_order_item_meta', array( __CLASS__, 'flush_object_meta_cache' ), 10, 4 ); } diff --git a/plugins/woocommerce/includes/class-wc-structured-data.php b/plugins/woocommerce/includes/class-wc-structured-data.php index 213fb9788f3..98acb513224 100644 --- a/plugins/woocommerce/includes/class-wc-structured-data.php +++ b/plugins/woocommerce/includes/class-wc-structured-data.php @@ -214,6 +214,12 @@ class WC_Structured_Data { $markup['sku'] = $product->get_id(); } + // Add GTIN only if it's a valid number. + $gtin = $product->get_global_unique_id(); + if ( $gtin && is_numeric( $gtin ) ) { + $markup['gtin'] = $gtin; + } + if ( '' !== $product->get_price() ) { // Assume prices will be valid until the end of next year, unless on sale and there is an end date. $price_valid_until = gmdate( 'Y-12-31', time() + YEAR_IN_SECONDS ); diff --git a/plugins/woocommerce/includes/class-woocommerce.php b/plugins/woocommerce/includes/class-woocommerce.php index a8949206d52..41104fcbd65 100644 --- a/plugins/woocommerce/includes/class-woocommerce.php +++ b/plugins/woocommerce/includes/class-woocommerce.php @@ -10,6 +10,7 @@ defined( 'ABSPATH' ) || exit; use Automattic\WooCommerce\Internal\AssignDefaultCategory; use Automattic\WooCommerce\Internal\BatchProcessing\BatchProcessingController; +use Automattic\WooCommerce\Internal\ComingSoon\ComingSoonAdminBarBadge; use Automattic\WooCommerce\Internal\ComingSoon\ComingSoonCacheInvalidator; use Automattic\WooCommerce\Internal\ComingSoon\ComingSoonRequestHandler; use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController; @@ -45,7 +46,7 @@ final class WooCommerce { * * @var string */ - public $version = '9.3.0'; + public $version = '9.4.0'; /** * WooCommerce Schema version. @@ -329,6 +330,7 @@ final class WooCommerce { $container->get( WebhookUtil::class ); $container->get( Marketplace::class ); $container->get( TimeUtil::class ); + $container->get( ComingSoonAdminBarBadge::class ); $container->get( ComingSoonCacheInvalidator::class ); $container->get( ComingSoonRequestHandler::class ); @@ -710,6 +712,11 @@ final class WooCommerce { */ include_once WC_ABSPATH . 'includes/wccom-site/class-wc-wccom-site.php'; + /** + * Product Usage + */ + include_once WC_ABSPATH . 'includes/product-usage/class-wc-product-usage.php'; + /** * Libraries and packages. */ diff --git a/plugins/woocommerce/includes/cli/class-wc-cli-com-command.php b/plugins/woocommerce/includes/cli/class-wc-cli-com-command.php index 5f6f7b63144..d56ec1b29fe 100644 --- a/plugins/woocommerce/includes/cli/class-wc-cli-com-command.php +++ b/plugins/woocommerce/includes/cli/class-wc-cli-com-command.php @@ -137,7 +137,7 @@ class WC_CLI_COM_Command { * # force connecting to WCCOM even if site is already connected. * $ wp wc com connect --force * - * # Pass password to comman. + * # Pass password to command. * $ wp wc com connect --password=PASSWORD * * @param array $args Positional arguments to include when calling the command. diff --git a/plugins/woocommerce/includes/data-stores/class-wc-product-data-store-cpt.php b/plugins/woocommerce/includes/data-stores/class-wc-product-data-store-cpt.php index d4a43593e29..b5a611d6f8a 100644 --- a/plugins/woocommerce/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/plugins/woocommerce/includes/data-stores/class-wc-product-data-store-cpt.php @@ -750,7 +750,6 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da * Action to signal that the value of 'stock_quantity' for a variation has changed. * * @since 3.0 - * @since 9.2 Added $stock parameter. * * @param WC_Product $product The variation whose stock has changed. */ @@ -760,7 +759,6 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da * Action to signal that the value of 'stock_quantity' for a product has changed. * * @since 3.0 - * @since 9.2 Added $stock parameter. * * @param WC_Product $product The variation whose stock has changed. */ diff --git a/plugins/woocommerce/includes/emails/class-wc-email-new-order.php b/plugins/woocommerce/includes/emails/class-wc-email-new-order.php index 3451c64b3a3..33a49ebce34 100644 --- a/plugins/woocommerce/includes/emails/class-wc-email-new-order.php +++ b/plugins/woocommerce/includes/emails/class-wc-email-new-order.php @@ -109,10 +109,11 @@ if ( ! class_exists( 'WC_Email_New_Order' ) ) : } if ( $this->is_enabled() && $this->get_recipient() ) { - $this->send( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() ); - - $order->update_meta_data( '_new_order_email_sent', 'true' ); - $order->save(); + $email_sent_successfully = $this->send( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() ); + if ( $email_sent_successfully ) { + $order->update_meta_data( '_new_order_email_sent', 'true' ); + $order->save(); + } } $this->restore_locale(); diff --git a/plugins/woocommerce/includes/export/class-wc-product-csv-exporter.php b/plugins/woocommerce/includes/export/class-wc-product-csv-exporter.php index 1c0b976dddf..10808c09bc4 100644 --- a/plugins/woocommerce/includes/export/class-wc-product-csv-exporter.php +++ b/plugins/woocommerce/includes/export/class-wc-product-csv-exporter.php @@ -262,7 +262,7 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter { * @since 3.1.0 * * @param array $row An associative array with the data of a single row in the CSV file. - * @param WC_Product $product The product object correspnding to the current row. + * @param WC_Product $product The product object corresponding to the current row. * @param WC_Product_CSV_Exporter $exporter The instance of the CSV exporter. */ return apply_filters( 'woocommerce_product_export_row_data', $row, $product, $this ); diff --git a/plugins/woocommerce/includes/import/class-wc-product-csv-importer.php b/plugins/woocommerce/includes/import/class-wc-product-csv-importer.php index 27be278e6eb..804426ed9aa 100644 --- a/plugins/woocommerce/includes/import/class-wc-product-csv-importer.php +++ b/plugins/woocommerce/includes/import/class-wc-product-csv-importer.php @@ -198,7 +198,7 @@ class WC_Product_CSV_Importer extends WC_Product_Importer { return absint( $original_id ); } - // See if the given ID maps to a valid product allready. + // See if the given ID maps to a valid product already. $existing_id = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_type IN ( 'product', 'product_variation' ) AND ID = %d;", $id ) ); // WPCS: db call ok, cache ok. if ( $existing_id ) { diff --git a/plugins/woocommerce/includes/interfaces/class-wc-queue-interface.php b/plugins/woocommerce/includes/interfaces/class-wc-queue-interface.php index 3cfe06845ce..c4e3ef442e7 100644 --- a/plugins/woocommerce/includes/interfaces/class-wc-queue-interface.php +++ b/plugins/woocommerce/includes/interfaces/class-wc-queue-interface.php @@ -91,7 +91,7 @@ interface WC_Queue_Interface { public function cancel_all( $hook, $args = array(), $group = '' ); /** - * Get the date and time for the next scheduled occurence of an action with a given hook + * Get the date and time for the next scheduled occurrence of an action with a given hook * (an optionally that matches certain args and group), if any. * * @param string $hook The hook that the job will trigger. diff --git a/plugins/woocommerce/includes/product-usage/class-wc-product-usage-rule-set.php b/plugins/woocommerce/includes/product-usage/class-wc-product-usage-rule-set.php new file mode 100644 index 00000000000..e8ccb1bc3a2 --- /dev/null +++ b/plugins/woocommerce/includes/product-usage/class-wc-product-usage-rule-set.php @@ -0,0 +1,50 @@ +rules = $rules; + } + + /** + * Retrieve the value of a rule by name + * + * @param string $rule_name name of the rule to retrieve value. + * @return mixed|null + */ + public function get_rule( string $rule_name ) { + if ( ! isset( $this->rules[ $rule_name ] ) ) { + return null; + } + + return $this->rules[ $rule_name ]; + } +} diff --git a/plugins/woocommerce/includes/product-usage/class-wc-product-usage.php b/plugins/woocommerce/includes/product-usage/class-wc-product-usage.php new file mode 100644 index 00000000000..7e986309fc0 --- /dev/null +++ b/plugins/woocommerce/includes/product-usage/class-wc-product-usage.php @@ -0,0 +1,87 @@ + $product_id ) ); + if ( empty( $subscriptions ) ) { + return new WC_Product_Usage_Rule_Set( $rules ); + } + + // Product should only have a single connected subscription on current store. + $product_subscription = current( $subscriptions ); + if ( $product_subscription['expired'] ) { + return new WC_Product_Usage_Rule_Set( $rules ); + } + + return null; + } + + /** + * Get the product usage rule for a product. + * + * @param int $product_id product id to get feature restriction rules. + * @return array|null + * @since 9.3.0 + */ + private static function get_product_usage_restriction_rule( int $product_id ): ?array { + $rules = WC_Helper::get_product_usage_notice_rules(); + if ( empty( $rules['restricted_products'][ $product_id ] ) ) { + return null; + } + + return $rules['restricted_products'][ $product_id ]; + } +} + +WC_Product_Usage::load(); diff --git a/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-system-status-tools-v2-controller.php b/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-system-status-tools-v2-controller.php index d9c8ad8d9fe..822b2a651f1 100644 --- a/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-system-status-tools-v2-controller.php +++ b/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-system-status-tools-v2-controller.php @@ -123,42 +123,42 @@ class WC_REST_System_Status_Tools_V2_Controller extends WC_REST_Controller { */ public function get_tools() { $tools = array( - 'clear_transients' => array( + 'clear_transients' => array( 'name' => __( 'WooCommerce transients', 'woocommerce' ), 'button' => __( 'Clear transients', 'woocommerce' ), 'desc' => __( 'This tool will clear the product/shop transients cache.', 'woocommerce' ), ), - 'clear_expired_transients' => array( + 'clear_expired_transients' => array( 'name' => __( 'Expired transients', 'woocommerce' ), 'button' => __( 'Clear transients', 'woocommerce' ), 'desc' => __( 'This tool will clear ALL expired transients from WordPress.', 'woocommerce' ), ), - 'delete_orphaned_variations' => array( + 'delete_orphaned_variations' => array( 'name' => __( 'Orphaned variations', 'woocommerce' ), 'button' => __( 'Delete orphaned variations', 'woocommerce' ), 'desc' => __( 'This tool will delete all variations which have no parent.', 'woocommerce' ), ), - 'clear_expired_download_permissions' => array( + 'clear_expired_download_permissions' => array( 'name' => __( 'Used-up download permissions', 'woocommerce' ), 'button' => __( 'Clean up download permissions', 'woocommerce' ), 'desc' => __( 'This tool will delete expired download permissions and permissions with 0 remaining downloads.', 'woocommerce' ), ), - 'regenerate_product_lookup_tables' => array( + 'regenerate_product_lookup_tables' => array( 'name' => __( 'Product lookup tables', 'woocommerce' ), 'button' => __( 'Regenerate', 'woocommerce' ), 'desc' => __( 'This tool will regenerate product lookup table data. This process may take a while.', 'woocommerce' ), ), - 'recount_terms' => array( + 'recount_terms' => array( 'name' => __( 'Term counts', 'woocommerce' ), 'button' => __( 'Recount terms', 'woocommerce' ), 'desc' => __( 'This tool will recount product terms - useful when changing your settings in a way which hides products from the catalog.', 'woocommerce' ), ), - 'reset_roles' => array( + 'reset_roles' => array( 'name' => __( 'Capabilities', 'woocommerce' ), 'button' => __( 'Reset capabilities', 'woocommerce' ), 'desc' => __( 'This tool will reset the admin, customer and shop_manager roles to default. Use this if your users cannot access all of the WooCommerce admin pages.', 'woocommerce' ), ), - 'clear_sessions' => array( + 'clear_sessions' => array( 'name' => __( 'Clear customer sessions', 'woocommerce' ), 'button' => __( 'Clear', 'woocommerce' ), 'desc' => sprintf( @@ -167,7 +167,7 @@ class WC_REST_System_Status_Tools_V2_Controller extends WC_REST_Controller { __( 'This tool will delete all customer session data from the database, including current carts and saved carts in the database.', 'woocommerce' ) ), ), - 'clear_template_cache' => array( + 'clear_template_cache' => array( 'name' => __( 'Clear template cache', 'woocommerce' ), 'button' => __( 'Clear', 'woocommerce' ), 'desc' => sprintf( @@ -176,7 +176,16 @@ class WC_REST_System_Status_Tools_V2_Controller extends WC_REST_Controller { __( 'This tool will empty the template cache.', 'woocommerce' ) ), ), - 'install_pages' => array( + 'clear_system_status_theme_info_cache' => array( + 'name' => __( 'Clear system status theme info cache', 'woocommerce' ), + 'button' => __( 'Clear', 'woocommerce' ), + 'desc' => sprintf( + '%1$s %2$s', + __( 'Note:', 'woocommerce' ), + __( 'This tool will empty the system status theme info cache.', 'woocommerce' ) + ), + ), + 'install_pages' => array( 'name' => __( 'Create default WooCommerce pages', 'woocommerce' ), 'button' => __( 'Create pages', 'woocommerce' ), 'desc' => sprintf( @@ -185,7 +194,7 @@ class WC_REST_System_Status_Tools_V2_Controller extends WC_REST_Controller { __( 'This tool will install all the missing WooCommerce pages. Pages already defined and set up will not be replaced.', 'woocommerce' ) ), ), - 'delete_taxes' => array( + 'delete_taxes' => array( 'name' => __( 'Delete WooCommerce tax rates', 'woocommerce' ), 'button' => __( 'Delete tax rates', 'woocommerce' ), 'desc' => sprintf( @@ -194,12 +203,12 @@ class WC_REST_System_Status_Tools_V2_Controller extends WC_REST_Controller { __( 'This option will delete ALL of your tax rates, use with caution. This action cannot be reversed.', 'woocommerce' ) ), ), - 'regenerate_thumbnails' => array( + 'regenerate_thumbnails' => array( 'name' => __( 'Regenerate shop thumbnails', 'woocommerce' ), 'button' => __( 'Regenerate', 'woocommerce' ), 'desc' => __( 'This will regenerate all shop thumbnails to match your theme and/or image settings.', 'woocommerce' ), ), - 'db_update_routine' => array( + 'db_update_routine' => array( 'name' => __( 'Update database', 'woocommerce' ), 'button' => __( 'Update database', 'woocommerce' ), 'desc' => sprintf( @@ -567,6 +576,11 @@ class WC_REST_System_Status_Tools_V2_Controller extends WC_REST_Controller { } break; + case 'clear_system_status_theme_info_cache': + wc_clear_system_status_theme_info_cache(); + $message = __( 'System status theme info cache cleared.', 'woocommerce' ); + break; + case 'verify_db_tables': if ( ! method_exists( 'WC_Install', 'verify_base_tables' ) ) { $message = __( 'You need WooCommerce 4.2 or newer to run this tool.', 'woocommerce' ); diff --git a/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-system-status-v2-controller.php b/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-system-status-v2-controller.php index 0d663686c33..f8c244c7e68 100644 --- a/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-system-status-v2-controller.php +++ b/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-system-status-v2-controller.php @@ -373,7 +373,41 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller { 'context' => array( 'view' ), 'readonly' => true, 'items' => array( - 'type' => 'string', + 'type' => 'object', + 'properties' => array( + 'plugin' => array( + 'description' => __( 'Plugin basename. The path to the main plugin file relative to the plugins directory.', 'woocommerce' ), + 'type' => 'string', + ), + 'name' => array( + 'description' => __( 'Name of the plugin.', 'woocommerce' ), + 'type' => 'string', + ), + 'version' => array( + 'description' => __( 'Current plugin version.', 'woocommerce' ), + 'type' => 'string', + ), + 'version_latest' => array( + 'description' => __( 'Latest available plugin version.', 'woocommerce' ), + 'type' => 'string', + ), + 'url' => array( + 'description' => __( 'Plugin URL.', 'woocommerce' ), + 'type' => 'string', + ), + 'author_name' => array( + 'description' => __( 'Plugin author name.', 'woocommerce' ), + 'type' => 'string', + ), + 'author_url' => array( + 'description' => __( 'Plugin author URL.', 'woocommerce' ), + 'type' => 'string', + ), + 'network_activated' => array( + 'description' => __( 'Whether the plugin can only be activated network-wide.', 'woocommerce' ), + 'type' => 'boolean', + ), + ), ), ), 'inactive_plugins' => array( @@ -382,7 +416,41 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller { 'context' => array( 'view' ), 'readonly' => true, 'items' => array( - 'type' => 'string', + 'type' => 'object', + 'properties' => array( + 'plugin' => array( + 'description' => __( 'Plugin basename. The path to the main plugin file relative to the plugins directory.', 'woocommerce' ), + 'type' => 'string', + ), + 'name' => array( + 'description' => __( 'Name of the plugin.', 'woocommerce' ), + 'type' => 'string', + ), + 'version' => array( + 'description' => __( 'Current plugin version.', 'woocommerce' ), + 'type' => 'string', + ), + 'version_latest' => array( + 'description' => __( 'Latest available plugin version.', 'woocommerce' ), + 'type' => 'string', + ), + 'url' => array( + 'description' => __( 'Plugin URL.', 'woocommerce' ), + 'type' => 'string', + ), + 'author_name' => array( + 'description' => __( 'Plugin author name.', 'woocommerce' ), + 'type' => 'string', + ), + 'author_url' => array( + 'description' => __( 'Plugin author URL.', 'woocommerce' ), + 'type' => 'string', + ), + 'network_activated' => array( + 'description' => __( 'Whether the plugin can only be activated network-wide.', 'woocommerce' ), + 'type' => 'boolean', + ), + ), ), ), 'dropins_mu_plugins' => array( @@ -1048,7 +1116,7 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller { $active_plugins_data = array(); foreach ( $active_valid_plugins as $plugin ) { - $data = get_plugin_data( $plugin ); + $data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin ); $active_plugins_data[] = $this->format_plugin_data( $plugin, $data ); } diff --git a/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-product-reviews-controller.php b/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-product-reviews-controller.php index 9ae674c382b..0e25996d107 100644 --- a/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-product-reviews-controller.php +++ b/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-product-reviews-controller.php @@ -1065,7 +1065,7 @@ class WC_REST_Product_Reviews_Controller extends WC_REST_Controller { } /** - * Get the reivew, if the ID is valid. + * Get the review, if the ID is valid. * * @since 3.5.0 * @param int $id Supplied ID. diff --git a/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-product-shipping-classes-controller.php b/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-product-shipping-classes-controller.php index a8d37886478..f0baad08771 100644 --- a/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-product-shipping-classes-controller.php +++ b/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-product-shipping-classes-controller.php @@ -53,7 +53,7 @@ class WC_REST_Product_Shipping_Classes_Controller extends WC_REST_Product_Shippi } /** - * Callback fuction for the slug-suggestion endpoint. + * Callback function for the slug-suggestion endpoint. * * @param WP_REST_Request $request Full details about the request. * @return string The suggested slug. diff --git a/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-product-variations-controller.php b/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-product-variations-controller.php index aa298f52749..aaa06648fe1 100644 --- a/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-product-variations-controller.php +++ b/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-product-variations-controller.php @@ -446,7 +446,7 @@ class WC_REST_Product_Variations_Controller extends WC_REST_Product_Variations_V if ( is_wp_error( $upload ) ) { /** - * Filter to check if it should supress the image upload error, false by default. + * Filter to check if it should suppress the image upload error, false by default. * * @since 4.5.0 * @param bool false If it should suppress. diff --git a/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-products-controller.php b/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-products-controller.php index b4a3a933faa..f1c472a38cd 100644 --- a/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-products-controller.php +++ b/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-products-controller.php @@ -232,7 +232,7 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller { } if ( wc_product_sku_enabled() ) { - // Do a partial match for a sku. Supercedes sku parameter that does exact matching. + // Do a partial match for a sku. Supersedes sku parameter that does exact matching. if ( ! empty( $request['search_sku'] ) ) { // Store this for use in the query clause filters. $this->search_sku_in_product_lookup_table = $request['search_sku']; diff --git a/plugins/woocommerce/includes/shortcodes/class-wc-shortcode-products.php b/plugins/woocommerce/includes/shortcodes/class-wc-shortcode-products.php index 9b551b3f2e0..51eed9c2753 100644 --- a/plugins/woocommerce/includes/shortcodes/class-wc-shortcode-products.php +++ b/plugins/woocommerce/includes/shortcodes/class-wc-shortcode-products.php @@ -641,8 +641,13 @@ class WC_Shortcode_Products { do_action( "woocommerce_shortcode_before_{$this->type}_loop", $this->attributes ); - // Fire standard shop loop hooks when paginating results so we can show result counts and so on. if ( wc_string_to_bool( $this->attributes['paginate'] ) ) { + /** + * Fire the standard shop hooks when paginating so we can display result counts etc. + * If the pagination is not enabled, this hook will not be fired. + * + * @since 3.3.1 + */ do_action( 'woocommerce_before_shop_loop' ); } @@ -667,8 +672,13 @@ class WC_Shortcode_Products { $GLOBALS['post'] = $original_post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited woocommerce_product_loop_end(); - // Fire standard shop loop hooks when paginating results so we can show result counts and so on. if ( wc_string_to_bool( $this->attributes['paginate'] ) ) { + /** + * Fire the standard shop hooks when paginating so we can display the pagination. + * If the pagination is not enabled, this hook will not be fired. + * + * @since 3.3.1 + */ do_action( 'woocommerce_after_shop_loop' ); } diff --git a/plugins/woocommerce/includes/wc-cart-functions.php b/plugins/woocommerce/includes/wc-cart-functions.php index cb14a9f2cf4..d0a048d3bbb 100644 --- a/plugins/woocommerce/includes/wc-cart-functions.php +++ b/plugins/woocommerce/includes/wc-cart-functions.php @@ -123,9 +123,9 @@ function wc_add_to_cart_message( $products, $show_qty = false, $return = false ) $wp_button_class = wc_wp_theme_get_element_class_name( 'button' ) ? ' ' . wc_wp_theme_get_element_class_name( 'button' ) : ''; if ( 'yes' === get_option( 'woocommerce_cart_redirect_after_add' ) ) { $return_to = apply_filters( 'woocommerce_continue_shopping_redirect', wc_get_raw_referer() ? wp_validate_redirect( wc_get_raw_referer(), false ) : wc_get_page_permalink( 'shop' ) ); - $message = sprintf( '%s %s', esc_url( $return_to ), esc_attr( $wp_button_class ), esc_html__( 'Continue shopping', 'woocommerce' ), esc_html( $added_text ) ); + $message = sprintf( '%s %s', esc_html( $added_text ), esc_url( $return_to ), esc_attr( $wp_button_class ), esc_html__( 'Continue shopping', 'woocommerce' ) ); } else { - $message = sprintf( '%s %s', esc_url( wc_get_cart_url() ), esc_attr( $wp_button_class ), esc_html__( 'View cart', 'woocommerce' ), esc_html( $added_text ) ); + $message = sprintf( '%s %s', esc_html( $added_text ), esc_url( wc_get_cart_url() ), esc_attr( $wp_button_class ), esc_html__( 'View cart', 'woocommerce' ) ); } if ( has_filter( 'wc_add_to_cart_message' ) ) { @@ -413,7 +413,12 @@ function wc_cart_round_discount( $value, $precision ) { */ function wc_get_chosen_shipping_method_ids() { $method_ids = array(); - $chosen_methods = WC()->session->get( 'chosen_shipping_methods', array() ); + $chosen_methods = array(); + + if ( is_callable( array( WC()->session, 'get' ) ) ) { + $chosen_methods = WC()->session->get( 'chosen_shipping_methods', array() ); + } + foreach ( $chosen_methods as $chosen_method ) { if ( ! is_string( $chosen_method ) ) { continue; @@ -421,6 +426,7 @@ function wc_get_chosen_shipping_method_ids() { $chosen_method = explode( ':', $chosen_method ); $method_ids[] = current( $chosen_method ); } + return $method_ids; } diff --git a/plugins/woocommerce/includes/wc-core-functions.php b/plugins/woocommerce/includes/wc-core-functions.php index eb399b895d3..4158fd900e0 100644 --- a/plugins/woocommerce/includes/wc-core-functions.php +++ b/plugins/woocommerce/includes/wc-core-functions.php @@ -456,6 +456,15 @@ function wc_clear_template_cache() { } } +/** + * Clear the system status theme info cache. + * + * @since 9.4.0 + */ +function wc_clear_system_status_theme_info_cache() { + delete_transient( 'wc_system_status_theme_info' ); +} + /** * Get Base Currency Code. * @@ -1476,8 +1485,9 @@ function wc_transaction_query( $type = 'start', $force = false ) { */ function wc_get_cart_url() { if ( is_cart() && isset( $_SERVER['HTTP_HOST'], $_SERVER['REQUEST_URI'] ) ) { - $protocol = is_ssl() ? 'https' : 'http'; - $cart_url = esc_url_raw( $protocol . '://' . wp_unslash( $_SERVER['HTTP_HOST'] ) . wp_unslash( $_SERVER['REQUEST_URI'] ) ); + $protocol = is_ssl() ? 'https' : 'http'; + $current_url = esc_url_raw( $protocol . '://' . wp_unslash( $_SERVER['HTTP_HOST'] ) . wp_unslash( $_SERVER['REQUEST_URI'] ) ); + $cart_url = remove_query_arg( array( 'remove_item', 'add-to-cart', 'added-to-cart', 'order_again', '_wpnonce' ), $current_url ); } else { $cart_url = wc_get_page_permalink( 'cart' ); } @@ -1823,7 +1833,7 @@ function wc_uasort_comparison( $a, $b ) { } /** - * Sort values based on ascii, usefull for special chars in strings. + * Sort values based on ascii, useful for special chars in strings. * * @param string $a First value. * @param string $b Second value. @@ -2514,7 +2524,7 @@ function wc_selected( $value, $options ) { * Retrieves the MySQL server version. Based on $wpdb. * * @since 3.4.1 - * @return array Vesion information. + * @return array Version information. */ function wc_get_server_database_version() { global $wpdb; diff --git a/plugins/woocommerce/includes/wc-order-functions.php b/plugins/woocommerce/includes/wc-order-functions.php index 420f2da080d..972705ab078 100644 --- a/plugins/woocommerce/includes/wc-order-functions.php +++ b/plugins/woocommerce/includes/wc-order-functions.php @@ -540,10 +540,11 @@ function wc_create_refund( $args = array() ) { throw new Exception( __( 'Invalid order ID.', 'woocommerce' ) ); } - $remaining_refund_amount = $order->get_remaining_refund_amount(); - $remaining_refund_items = $order->get_remaining_refund_items(); - $refund_item_count = 0; - $refund = new WC_Order_Refund( $args['refund_id'] ); + $remaining_refund_amount = $order->get_remaining_refund_amount(); + $remaining_refund_items = $order->get_remaining_refund_items(); + $refund_item_count = 0; + $refund = new WC_Order_Refund( $args['refund_id'] ); + $refunded_order_and_products = array(); if ( 0 > $args['amount'] || $args['amount'] > $remaining_refund_amount ) { throw new Exception( __( 'Invalid refund amount.', 'woocommerce' ) ); @@ -576,6 +577,16 @@ function wc_create_refund( $args = array() ) { continue; } + // array of order id and product id which were refunded. + // later to be used for revoking download permission. + // checking if the item is a product, as we only need to revoke download permission for products. + if ( $item->is_type( 'line_item' ) ) { + $refunded_order_and_products[ $item_id ] = array( + 'order_id' => $order->get_id(), + 'product_id' => $item->get_product_id(), + ); + } + $class = get_class( $item ); $refunded_item = new $class( $item ); $refunded_item->set_id( 0 ); @@ -635,6 +646,19 @@ function wc_create_refund( $args = array() ) { wc_restock_refunded_items( $order, $args['line_items'] ); } + // delete downloads that were refunded using order and product id, if present. + if ( ! empty( $refunded_order_and_products ) ) { + foreach ( $refunded_order_and_products as $refunded_order_and_product ) { + $download_data_store = WC_Data_Store::load( 'customer-download' ); + $downloads = $download_data_store->get_downloads( $refunded_order_and_product ); + if ( ! empty( $downloads ) ) { + foreach ( $downloads as $download ) { + $download_data_store->delete_by_id( $download->get_id() ); + } + } + } + } + /** * Trigger notification emails. * diff --git a/plugins/woocommerce/includes/wc-rest-functions.php b/plugins/woocommerce/includes/wc-rest-functions.php index 3eeec672a27..e9b3055cc9f 100644 --- a/plugins/woocommerce/includes/wc-rest-functions.php +++ b/plugins/woocommerce/includes/wc-rest-functions.php @@ -410,8 +410,6 @@ function wc_rest_should_load_namespace( string $ns, string $rest_route = '' ): b 'wc/private', ); - // We can consider allowing filtering this list in the future. - $known_namespace_request = false; foreach ( $known_namespaces as $known_namespace ) { if ( str_starts_with( $rest_route, $known_namespace ) ) { @@ -424,5 +422,15 @@ function wc_rest_should_load_namespace( string $ns, string $rest_route = '' ): b return true; } - return str_starts_with( $rest_route, $ns ); + /** + * Filters whether a namespace should be loaded. + * + * @param bool $should_load True if the namespace should be loaded, false otherwise. + * @param string $ns The namespace to check. + * @param string $rest_route The REST route being checked. + * @param array $known_namespaces Known namespaces that we know are safe to not load if the request is not for them. + * + * @since 9.4 + */ + return apply_filters( 'wc_rest_should_load_namespace', str_starts_with( $rest_route, $ns ), $ns, $rest_route, $known_namespaces ); } diff --git a/plugins/woocommerce/includes/wc-stock-functions.php b/plugins/woocommerce/includes/wc-stock-functions.php index bc49bc61dd9..1489a0e6630 100644 --- a/plugins/woocommerce/includes/wc-stock-functions.php +++ b/plugins/woocommerce/includes/wc-stock-functions.php @@ -284,6 +284,10 @@ function wc_trigger_stock_change_notifications( $order, $changes ) { * @return void */ function wc_trigger_stock_change_actions( $product ) { + if ( true !== $product->get_manage_stock() ) { + return; + } + $no_stock_amount = absint( get_option( 'woocommerce_notify_no_stock_amount', 0 ) ); $low_stock_amount = absint( wc_get_low_stock_amount( $product ) ); $stock_quantity = $product->get_stock_quantity(); diff --git a/plugins/woocommerce/includes/wc-template-functions.php b/plugins/woocommerce/includes/wc-template-functions.php index 404232fc3bb..5187fb20817 100644 --- a/plugins/woocommerce/includes/wc-template-functions.php +++ b/plugins/woocommerce/includes/wc-template-functions.php @@ -237,11 +237,11 @@ function wc_set_loop_prop( $prop, $value = '' ) { } /** - * Set the current visbility for a product in the woocommerce_loop global. + * Set the current visibility for a product in the woocommerce_loop global. * * @since 4.4.0 - * @param int $product_id Product it to cache visbiility for. - * @param bool $value The poduct visibility value to cache. + * @param int $product_id Product it to cache visibility for. + * @param bool $value The product visibility value to cache. */ function wc_set_loop_product_visibility( $product_id, $value ) { wc_set_loop_prop( "product_visibility_$product_id", $value ); @@ -1615,25 +1615,39 @@ function wc_get_gallery_image_html( $attachment_id, $main_image = false ) { $thumbnail_srcset = wp_get_attachment_image_srcset( $attachment_id, $thumbnail_size ); $full_src = wp_get_attachment_image_src( $attachment_id, $full_size ); $alt_text = trim( wp_strip_all_tags( get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ) ) ); - $image = wp_get_attachment_image( + + /** + * Filters the attributes for the image markup. + * + * @since 3.3.2 + * + * @param array $image_attributes Attributes for the image markup. + */ + $image_params = apply_filters( + 'woocommerce_gallery_image_html_attachment_image_params', + array( + 'title' => _wp_specialchars( get_post_field( 'post_title', $attachment_id ), ENT_QUOTES, 'UTF-8', true ), + 'data-caption' => _wp_specialchars( get_post_field( 'post_excerpt', $attachment_id ), ENT_QUOTES, 'UTF-8', true ), + 'data-src' => esc_url( $full_src[0] ), + 'data-large_image' => esc_url( $full_src[0] ), + 'data-large_image_width' => esc_attr( $full_src[1] ), + 'data-large_image_height' => esc_attr( $full_src[2] ), + 'class' => esc_attr( $main_image ? 'wp-post-image' : '' ), + ), + $attachment_id, + $image_size, + $main_image + ); + + if ( isset( $image_params['title'] ) ) { + unset( $image_params['title'] ); + } + + $image = wp_get_attachment_image( $attachment_id, $image_size, false, - apply_filters( - 'woocommerce_gallery_image_html_attachment_image_params', - array( - 'title' => _wp_specialchars( get_post_field( 'post_title', $attachment_id ), ENT_QUOTES, 'UTF-8', true ), - 'data-caption' => _wp_specialchars( get_post_field( 'post_excerpt', $attachment_id ), ENT_QUOTES, 'UTF-8', true ), - 'data-src' => esc_url( $full_src[0] ), - 'data-large_image' => esc_url( $full_src[0] ), - 'data-large_image_width' => esc_attr( $full_src[1] ), - 'data-large_image_height' => esc_attr( $full_src[2] ), - 'class' => esc_attr( $main_image ? 'wp-post-image' : '' ), - ), - $attachment_id, - $image_size, - $main_image - ) + $image_params ); return ''; @@ -3791,7 +3805,7 @@ function wc_empty_cart_message() { // This adds the cart-empty classname to the notice to preserve backwards compatibility (for styling purposes etc). $notice = str_replace( 'class="woocommerce-info"', 'class="cart-empty woocommerce-info"', $notice ); - // Return the notice within a consistent wrapper element. This is targetted by some scripts such as cart.js. + // Return the notice within a consistent wrapper element. This is targeted by some scripts such as cart.js. // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo '
        ' . $notice . '
        '; } diff --git a/plugins/woocommerce/includes/wc-update-functions.php b/plugins/woocommerce/includes/wc-update-functions.php index 9879e6e5538..179cd988255 100644 --- a/plugins/woocommerce/includes/wc-update-functions.php +++ b/plugins/woocommerce/includes/wc-update-functions.php @@ -416,7 +416,7 @@ function wc_update_209_brazillian_state() { // phpcs:disable WordPress.DB.SlowDBQuery - // Update brazillian state codes. + // Update Brazilian state codes. $wpdb->update( $wpdb->postmeta, array( @@ -2598,7 +2598,7 @@ function wc_update_770_remove_multichannel_marketing_feature_options() { /** * Migrate transaction data which was being incorrectly stored in the postmeta table to HPOS tables. * - * @return bool Whether there are pending migration recrods. + * @return bool Whether there are pending migration records. */ function wc_update_810_migrate_transactional_metadata_for_hpos() { global $wpdb; @@ -2824,3 +2824,31 @@ function wc_update_910_remove_obsolete_user_meta() { function wc_update_930_add_woocommerce_coming_soon_option() { add_option( 'woocommerce_coming_soon', 'no' ); } + +/** + * Migrate Launch Your Store tour meta keys to the woocommerce_meta user data fields. + */ +function wc_update_930_migrate_user_meta_for_launch_your_store_tour() { + // Rename `woocommerce_launch_your_store_tour_hidden` meta key to `woocommerce_admin_launch_your_store_tour_hidden`. + global $wpdb; + $wpdb->query( + $wpdb->prepare( + "UPDATE {$wpdb->usermeta} + SET meta_key = %s + WHERE meta_key = %s", + 'woocommerce_admin_launch_your_store_tour_hidden', + 'woocommerce_launch_your_store_tour_hidden' + ) + ); + + // Rename `woocommerce_coming_soon_banner_dismissed` meta key to `woocommerce_admin_coming_soon_banner_dismissed`. + $wpdb->query( + $wpdb->prepare( + "UPDATE {$wpdb->usermeta} + SET meta_key = %s + WHERE meta_key = %s", + 'woocommerce_admin_coming_soon_banner_dismissed', + 'woocommerce_coming_soon_banner_dismissed' + ) + ); +} diff --git a/plugins/woocommerce/includes/wc-user-functions.php b/plugins/woocommerce/includes/wc-user-functions.php index aa6e60f318a..6b653d07e0b 100644 --- a/plugins/woocommerce/includes/wc-user-functions.php +++ b/plugins/woocommerce/includes/wc-user-functions.php @@ -43,6 +43,9 @@ if ( ! function_exists( 'wc_create_new_customer' ) ) { /** * Create a new customer. * + * @since 9.4.0 Moved woocommerce_registration_error_email_exists filter to the shortcode checkout class. + * @since 9.4.0 Removed handling for generating username/password based on settings--this is consumed at form level. Here, if data is missing it will be generated. + * * @param string $email Customer email. * @param string $username Customer username. * @param string $password Customer password. @@ -55,17 +58,24 @@ if ( ! function_exists( 'wc_create_new_customer' ) ) { } if ( email_exists( $email ) ) { - return new WP_Error( 'registration-error-email-exists', apply_filters( 'woocommerce_registration_error_email_exists', __( 'An account is already registered with your email address. Please log in.', 'woocommerce' ), $email ) ); + return new WP_Error( + 'registration-error-email-exists', + sprintf( + // Translators: %s Email address. + esc_html__( 'An account is already registered with %s. Please log in or use a different email address.', 'woocommerce' ), + esc_html( $email ) + ) + ); } - if ( 'yes' === get_option( 'woocommerce_registration_generate_username', 'yes' ) && empty( $username ) ) { + if ( empty( $username ) ) { $username = wc_create_new_customer_username( $email, $args ); } $username = sanitize_user( $username ); if ( empty( $username ) || ! validate_username( $username ) ) { - return new WP_Error( 'registration-error-invalid-username', __( 'Please enter a valid account username.', 'woocommerce' ) ); + return new WP_Error( 'registration-error-invalid-username', __( 'Please provide a valid account username.', 'woocommerce' ) ); } if ( username_exists( $username ) ) { @@ -74,35 +84,88 @@ if ( ! function_exists( 'wc_create_new_customer' ) ) { // Handle password creation. $password_generated = false; - if ( 'yes' === get_option( 'woocommerce_registration_generate_password' ) && empty( $password ) ) { + + if ( empty( $password ) ) { $password = wp_generate_password(); $password_generated = true; } if ( empty( $password ) ) { - return new WP_Error( 'registration-error-missing-password', __( 'Please enter an account password.', 'woocommerce' ) ); + return new WP_Error( 'registration-error-missing-password', __( 'Please create a password for your account.', 'woocommerce' ) ); } // Use WP_Error to handle registration errors. $errors = new WP_Error(); + /** + * Fires before a customer account is registered. + * + * This hook fires before customer accounts are created and passes the form data (username, email) and an array + * of errors. + * + * This could be used to add extra validation logic and append errors to the array. + * + * @since 7.2.0 + * + * @internal Matches filter name in WooCommerce core. + * + * @param string $username Customer username. + * @param string $user_email Customer email address. + * @param \WP_Error $errors Error object. + */ do_action( 'woocommerce_register_post', $username, $email, $errors ); + /** + * Filters registration errors before a customer account is registered. + * + * This hook filters registration errors. This can be used to manipulate the array of errors before + * they are displayed. + * + * @since 7.2.0 + * + * @internal Matches filter name in WooCommerce core. + * + * @param \WP_Error $errors Error object. + * @param string $username Customer username. + * @param string $user_email Customer email address. + * @return \WP_Error + */ $errors = apply_filters( 'woocommerce_registration_errors', $errors, $username, $email ); - if ( $errors->get_error_code() ) { + if ( is_wp_error( $errors ) && $errors->get_error_code() ) { return $errors; } + // Merged passed args with sanitized username, email, and password. + $customer_data = array_merge( + $args, + array( + 'user_login' => $username, + 'user_pass' => $password, + 'user_email' => $email, + 'role' => 'customer', + ) + ); + + /** + * Filters customer data before a customer account is registered. + * + * This hook filters customer data. It allows user data to be changed, for example, username, password, email, + * first name, last name, and role. + * + * @since 7.2.0 + * + * @param array $customer_data An array of customer (user) data. + * @return array + */ $new_customer_data = apply_filters( 'woocommerce_new_customer_data', - array_merge( - $args, + wp_parse_args( + $customer_data, array( - 'user_login' => $username, - 'user_pass' => $password, - 'user_email' => $email, - 'role' => 'customer', + 'first_name' => '', + 'last_name' => '', + 'source' => 'unknown', ) ) ); @@ -113,6 +176,24 @@ if ( ! function_exists( 'wc_create_new_customer' ) ) { return $customer_id; } + // Set account flag to remind customer to update generated password. + if ( $password_generated ) { + update_user_option( $customer_id, 'default_password_nag', true, true ); + } + + /** + * Fires after a customer account has been registered. + * + * This hook fires after customer accounts are created and passes the customer data. + * + * @since 7.2.0 + * + * @internal Matches filter name in WooCommerce core. + * + * @param integer $customer_id New customer (user) ID. + * @param array $new_customer_data Array of customer (user) data. + * @param string $password_generated The generated password for the account. + */ do_action( 'woocommerce_created_customer', $customer_id, $new_customer_data, $password_generated ); return $customer_id; @@ -232,7 +313,9 @@ function wc_set_customer_auth_cookie( $customer_id ) { wp_set_auth_cookie( $customer_id, true ); // Update session. - WC()->session->init_session_cookie(); + if ( is_callable( array( WC()->session, 'init_session_cookie' ) ) ) { + WC()->session->init_session_cookie(); + } } /** @@ -272,10 +355,10 @@ function wc_update_new_customer_past_orders( $customer_id ) { do_action( 'woocommerce_update_new_customer_past_order', $order_id, $customer ); if ( $order->get_status() === 'wc-completed' ) { - $complete++; + ++$complete; } - $linked++; + ++$linked; } } @@ -356,18 +439,18 @@ function wc_customer_bought_product( $customer_email, $user_id, $product_id ) { } if ( OrderUtil::custom_orders_table_usage_is_enabled() ) { - $statuses = array_map( + $statuses = array_map( function ( $status ) { return "wc-$status"; }, $statuses ); - $order_table = OrdersTableDataStore::get_orders_table_name(); + $order_table = OrdersTableDataStore::get_orders_table_name(); $user_id_clause = ''; if ( $user_id ) { $user_id_clause = 'OR o.customer_id = ' . absint( $user_id ); } - $sql = " + $sql = " SELECT DISTINCT im.meta_value FROM $order_table AS o INNER JOIN {$wpdb->prefix}woocommerce_order_items AS i ON o.id = i.order_id INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS im ON i.order_item_id = im.order_item_id @@ -568,17 +651,15 @@ function wc_modify_map_meta_cap( $caps, $cap, $user_id, $args ) { case 'delete_user': if ( ! isset( $args[0] ) || $args[0] === $user_id ) { break; - } else { - if ( ! wc_current_user_has_role( 'administrator' ) ) { - if ( wc_user_has_role( $args[0], 'administrator' ) ) { + } elseif ( ! wc_current_user_has_role( 'administrator' ) ) { + if ( wc_user_has_role( $args[0], 'administrator' ) ) { + $caps[] = 'do_not_allow'; + } elseif ( wc_current_user_has_role( 'shop_manager' ) ) { + // Shop managers can only edit customer info. + $userdata = get_userdata( $args[0] ); + $shop_manager_editable_roles = apply_filters( 'woocommerce_shop_manager_editable_roles', array( 'customer' ) ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment + if ( property_exists( $userdata, 'roles' ) && ! empty( $userdata->roles ) && ! array_intersect( $userdata->roles, $shop_manager_editable_roles ) ) { $caps[] = 'do_not_allow'; - } elseif ( wc_current_user_has_role( 'shop_manager' ) ) { - // Shop managers can only edit customer info. - $userdata = get_userdata( $args[0] ); - $shop_manager_editable_roles = apply_filters( 'woocommerce_shop_manager_editable_roles', array( 'customer' ) ); - if ( property_exists( $userdata, 'roles' ) && ! empty( $userdata->roles ) && ! array_intersect( $userdata->roles, $shop_manager_editable_roles ) ) { - $caps[] = 'do_not_allow'; - } } } } @@ -596,7 +677,7 @@ add_filter( 'map_meta_cap', 'wc_modify_map_meta_cap', 10, 4 ); */ function wc_get_customer_download_permissions( $customer_id ) { $data_store = WC_Data_Store::load( 'customer-download' ); - return apply_filters( 'woocommerce_permission_list', $data_store->get_downloads_for_customer( $customer_id ), $customer_id ); + return apply_filters( 'woocommerce_permission_list', $data_store->get_downloads_for_customer( $customer_id ), $customer_id ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment } /** @@ -655,6 +736,7 @@ function wc_get_customer_available_downloads( $customer_id ) { } // Download name will be 'Product Name' for products with a single downloadable file, and 'Product Name - File X' for products with multiple files. + // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment $download_name = apply_filters( 'woocommerce_downloadable_product_name', $download_file['name'], @@ -688,10 +770,11 @@ function wc_get_customer_available_downloads( $customer_id ) { ), ); - $file_number++; + ++$file_number; } } + // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment return apply_filters( 'woocommerce_customer_available_downloads', $downloads, $customer_id ); } @@ -817,7 +900,7 @@ add_action( 'profile_update', 'wc_update_profile_last_update_time', 10, 2 ); * @param mixed $_meta_value Value of the meta that was changed. */ function wc_meta_update_last_update_time( $meta_id, $user_id, $meta_key, $_meta_value ) { - $keys_to_track = apply_filters( 'woocommerce_user_last_update_fields', array( 'first_name', 'last_name' ) ); + $keys_to_track = apply_filters( 'woocommerce_user_last_update_fields', array( 'first_name', 'last_name' ) ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment $update_time = in_array( $meta_key, $keys_to_track, true ) ? true : false; $update_time = 'billing_' === substr( $meta_key, 0, 8 ) ? true : $update_time; @@ -848,7 +931,7 @@ function wc_set_user_last_update_time( $user_id ) { * @return array */ function wc_get_customer_saved_methods_list( $customer_id ) { - return apply_filters( 'woocommerce_saved_payment_methods_list', array(), $customer_id ); + return apply_filters( 'woocommerce_saved_payment_methods_list', array(), $customer_id ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment } /** diff --git a/plugins/woocommerce/lib/packages/Detection/MobileDetect.php b/plugins/woocommerce/lib/packages/Detection/MobileDetect.php index d4c2d48c7c8..f7eb621e537 100644 --- a/plugins/woocommerce/lib/packages/Detection/MobileDetect.php +++ b/plugins/woocommerce/lib/packages/Detection/MobileDetect.php @@ -789,7 +789,7 @@ class MobileDetect 'SamsungBrowser' => 'SamsungBrowser/[VER]', 'Iron' => 'Iron/[VER]', // @note: Safari 7534.48.3 is actually Version 5.1. - // @note: On BlackBerry the Version is overwriten by the OS. + // @note: On BlackBerry the Version is overwritten by the OS. 'Safari' => ['Version/[VER]', 'Safari/[VER]'], 'Skyfire' => 'Skyfire/[VER]', 'Tizen' => 'Tizen/[VER]', diff --git a/plugins/woocommerce/package.json b/plugins/woocommerce/package.json index 9dac6666749..7ed07ba6aba 100644 --- a/plugins/woocommerce/package.json +++ b/plugins/woocommerce/package.json @@ -2,7 +2,7 @@ "name": "@woocommerce/plugin-woocommerce", "private": true, "title": "WooCommerce", - "version": "9.3.0", + "version": "9.4.0", "homepage": "https://woocommerce.com/", "repository": { "type": "git", @@ -592,7 +592,7 @@ "@babel/core": "7.12.9", "@babel/preset-env": "7.12.7", "@babel/register": "7.12.1", - "@playwright/test": "^1.45.1", + "@playwright/test": "^1.46.1", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/experimental-utils": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", @@ -639,7 +639,7 @@ }, "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "browserslist": [ "> 0.1%", diff --git a/plugins/woocommerce/patterns/header-essential.php b/plugins/woocommerce/patterns/header-essential.php index 82fc3f768f6..5521b691efc 100644 --- a/plugins/woocommerce/patterns/header-essential.php +++ b/plugins/woocommerce/patterns/header-essential.php @@ -17,14 +17,14 @@
        + +
        - -
        diff --git a/plugins/woocommerce/patterns/header-minimal.php b/plugins/woocommerce/patterns/header-minimal.php index 589e9ad6f49..18ca4db4a37 100644 --- a/plugins/woocommerce/patterns/header-minimal.php +++ b/plugins/woocommerce/patterns/header-minimal.php @@ -20,9 +20,9 @@
        - - + +
        diff --git a/plugins/woocommerce/readme.txt b/plugins/woocommerce/readme.txt index 16a7b9988f7..d3730c2db5b 100644 --- a/plugins/woocommerce/readme.txt +++ b/plugins/woocommerce/readme.txt @@ -4,7 +4,7 @@ Tags: online store, ecommerce, shop, shopping cart, sell online Requires at least: 6.5 Tested up to: 6.6 Requires PHP: 7.4 -Stable tag: 9.1.4 +Stable tag: 9.2.3 License: GPLv3 License URI: https://www.gnu.org/licenses/gpl-3.0.html @@ -169,6 +169,6 @@ WooCommerce comes with some sample data you can use to see how products look; im == Changelog == -= 9.3.0 2024-XX-XX = += 9.4.0 2024-XX-XX = [See changelog for all versions](https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/changelog.txt). diff --git a/plugins/woocommerce/src/Admin/API/AI/AIEndpoint.php b/plugins/woocommerce/src/Admin/API/AI/AIEndpoint.php index ff177b2fa93..12ea82b0fc5 100644 --- a/plugins/woocommerce/src/Admin/API/AI/AIEndpoint.php +++ b/plugins/woocommerce/src/Admin/API/AI/AIEndpoint.php @@ -51,5 +51,7 @@ abstract class AIEndpoint { * * @return array */ - abstract public function get_schema(); + public function get_schema() { + return array(); + } } diff --git a/plugins/woocommerce/src/Admin/API/AI/BusinessDescription.php b/plugins/woocommerce/src/Admin/API/AI/BusinessDescription.php new file mode 100644 index 00000000000..430e695f7b0 --- /dev/null +++ b/plugins/woocommerce/src/Admin/API/AI/BusinessDescription.php @@ -0,0 +1,84 @@ +register( + array( + array( + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'update_business_description' ), + 'permission_callback' => array( Middleware::class, 'is_authorized' ), + 'args' => array( + 'business_description' => array( + 'description' => __( 'The business description for a given store.', 'woocommerce' ), + 'type' => 'string', + ), + ), + ), + 'schema' => array( $this, 'get_schema' ), + ) + ); + } + + /** + * Update the business description. + * + * @param WP_REST_Request $request Request object. + * @return WP_REST_Response|WP_Error Response object. + */ + public function update_business_description( $request ) { + + $business_description = $request->get_param( 'business_description' ); + + if ( ! $business_description ) { + return new WP_Error( + 'invalid_business_description', + __( 'Invalid business description.', 'woocommerce' ) + ); + } + + update_option( 'last_business_description_with_ai_content_generated', $business_description ); + + return rest_ensure_response( + array( + 'ai_content_generated' => true, + ) + ); + } + + /** + * Get the Business Description response. + * + * @return array + */ + public function get_schema() { + return array( + 'ai_content_generated' => true, + ); + } +} diff --git a/plugins/woocommerce/src/Admin/API/AI/Images.php b/plugins/woocommerce/src/Admin/API/AI/Images.php new file mode 100644 index 00000000000..8ac0f7023f4 --- /dev/null +++ b/plugins/woocommerce/src/Admin/API/AI/Images.php @@ -0,0 +1,105 @@ +register( + array( + array( + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'generate_images' ), + 'permission_callback' => array( Middleware::class, 'is_authorized' ), + 'args' => array( + 'business_description' => array( + 'description' => __( 'The business description for a given store.', 'woocommerce' ), + 'type' => 'string', + ), + ), + ), + ) + ); + } + + /** + * Generate Images from Pexels + * + * @param WP_REST_Request $request Request object. + * + * @return WP_Error|WP_REST_Response + */ + public function generate_images( WP_REST_Request $request ) { + + $business_description = sanitize_text_field( wp_unslash( $request['business_description'] ) ); + + if ( empty( $business_description ) ) { + $business_description = get_option( 'woo_ai_describe_store_description' ); + } + + $last_business_description = get_option( 'last_business_description_with_ai_content_generated' ); + + if ( $last_business_description === $business_description ) { + return rest_ensure_response( + array( + 'ai_content_generated' => true, + 'images' => array(), + ), + ); + } + + $ai_connection = new Connection(); + + $site_id = $ai_connection->get_site_id(); + + if ( is_wp_error( $site_id ) ) { + return $site_id; + } + + $token = $ai_connection->get_jwt_token( $site_id ); + + if ( is_wp_error( $token ) ) { + return $token; + } + + $images = ( new Pexels() )->get_images( $ai_connection, $token, $business_description ); + + if ( is_wp_error( $images ) ) { + $images = array( + 'images' => array(), + 'search_term' => '', + ); + } + + return rest_ensure_response( + array( + 'ai_content_generated' => true, + 'images' => $images, + ) + ); + } +} diff --git a/plugins/woocommerce/src/Admin/API/AI/Patterns.php b/plugins/woocommerce/src/Admin/API/AI/Patterns.php new file mode 100644 index 00000000000..224a7afb163 --- /dev/null +++ b/plugins/woocommerce/src/Admin/API/AI/Patterns.php @@ -0,0 +1,98 @@ +register( + array( + array( + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'update_patterns' ), + 'permission_callback' => array( Middleware::class, 'is_authorized' ), + 'args' => array( + 'business_description' => array( + 'description' => __( 'The business description for a given store.', 'woocommerce' ), + 'type' => 'string', + ), + 'images' => array( + 'description' => __( 'The images for a given store.', 'woocommerce' ), + 'type' => 'object', + ), + ), + ), + array( + 'methods' => \WP_REST_Server::DELETABLE, + 'callback' => array( $this, 'delete_patterns' ), + 'permission_callback' => array( Middleware::class, 'is_authorized' ), + ), + ) + ); + } + + /** + * Update patterns with the content and images powered by AI. + * + * @param WP_REST_Request $request Request object. + * + * @return WP_Error|WP_REST_Response + */ + public function update_patterns( WP_REST_Request $request ) { + $business_description = sanitize_text_field( wp_unslash( $request['business_description'] ) ); + + $ai_connection = new Connection(); + + $site_id = $ai_connection->get_site_id(); + + if ( is_wp_error( $site_id ) ) { + return $site_id; + } + + $token = $ai_connection->get_jwt_token( $site_id ); + + $images = $request['images']; + + try { + ( new UpdatePatterns() )->generate_content( $ai_connection, $token, $images, $business_description ); + return rest_ensure_response( array( 'ai_content_generated' => true ) ); + } catch ( \Exception $e ) { + return rest_ensure_response( array( 'ai_content_generated' => false ) ); + } + } + + /** + * Remove patterns generated by AI. + * + * @return WP_Error|WP_REST_Response + */ + public function delete_patterns() { + PatternsHelper::delete_patterns_ai_data_post(); + return rest_ensure_response( array( 'removed' => true ) ); + } +} diff --git a/plugins/woocommerce/src/Admin/API/AI/Product.php b/plugins/woocommerce/src/Admin/API/AI/Product.php new file mode 100644 index 00000000000..68f388cdc63 --- /dev/null +++ b/plugins/woocommerce/src/Admin/API/AI/Product.php @@ -0,0 +1,96 @@ +register( + array( + array( + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'update_product' ), + 'permission_callback' => array( Middleware::class, 'is_authorized' ), + 'args' => array( + 'products_information' => array( + 'description' => __( 'Data generated by AI for updating dummy products.', 'woocommerce' ), + 'type' => 'object', + ), + 'last_product' => array( + 'description' => __( 'Whether the product being updated is the last one in the loop', 'woocommerce' ), + 'type' => 'boolean', + ), + ), + ), + ) + ); + } + + /** + * Update product with the content and images powered by AI. + * + * @param WP_REST_Request $request Request object. + * + * @return WP_REST_Response + */ + public function update_product( WP_REST_Request $request ) { + $product_information = $request['products_information'] ?? array(); + + if ( empty( $product_information ) ) { + return rest_ensure_response( + array( + self::AI_CONTENT_GENERATED => true, + ) + ); + } + + try { + $product_updater = new UpdateProducts(); + $product_updater->update_product_content( $product_information ); + } catch ( \Exception $e ) { + return rest_ensure_response( array( 'ai_content_generated' => false ) ); + } + + $last_product_to_update = $request['last_product'] ?? false; + + if ( $last_product_to_update ) { + flush_rewrite_rules(); + } + + return rest_ensure_response( + array( + self::AI_CONTENT_GENERATED => true, + ) + ); + } +} diff --git a/plugins/woocommerce/src/Admin/API/AI/Products.php b/plugins/woocommerce/src/Admin/API/AI/Products.php new file mode 100644 index 00000000000..e764777ca76 --- /dev/null +++ b/plugins/woocommerce/src/Admin/API/AI/Products.php @@ -0,0 +1,128 @@ +register( + array( + array( + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'generate_products_content' ), + 'permission_callback' => array( Middleware::class, 'is_authorized' ), + 'args' => array( + 'business_description' => array( + 'description' => __( 'The business description for a given store.', 'woocommerce' ), + 'type' => 'string', + ), + 'images' => array( + 'description' => __( 'The images for a given store.', 'woocommerce' ), + 'type' => 'object', + ), + ), + ), + array( + 'methods' => \WP_REST_Server::DELETABLE, + 'callback' => array( $this, 'delete_products' ), + 'permission_callback' => array( Middleware::class, 'is_authorized' ), + ), + ) + ); + } + + /** + * Generate the content for the products. + * + * @param WP_REST_Request $request Request object. + * + * @return WP_Error|WP_REST_Response + */ + public function generate_products_content( WP_REST_Request $request ) { + $allow_ai_connection = get_option( 'woocommerce_blocks_allow_ai_connection' ); + + if ( ! $allow_ai_connection ) { + return rest_ensure_response( + new WP_Error( + 'ai_connection_not_allowed', + __( 'AI content generation is not allowed on this store. Update your store settings if you wish to enable this feature.', 'woocommerce' ) + ) + ); + } + + $business_description = sanitize_text_field( wp_unslash( $request['business_description'] ) ); + + if ( empty( $business_description ) ) { + $business_description = get_option( 'woo_ai_describe_store_description' ); + } + + $ai_connection = new Connection(); + + $site_id = $ai_connection->get_site_id(); + + if ( is_wp_error( $site_id ) ) { + return $site_id; + } + + $token = $ai_connection->get_jwt_token( $site_id ); + + if ( is_wp_error( $token ) ) { + return $token; + } + + $images = $request['images']; + + $populate_products = ( new UpdateProducts() )->generate_content( $ai_connection, $token, $images, $business_description ); + + if ( is_wp_error( $populate_products ) ) { + return $populate_products; + } + + if ( ! isset( $populate_products['product_content'] ) ) { + return new WP_Error( 'product_content_not_found', __( 'Product content not found.', 'woocommerce' ) ); + } + + $product_content = $populate_products['product_content']; + + $item = array( + 'ai_content_generated' => true, + 'product_content' => $product_content, + ); + + return rest_ensure_response( $item ); + } + + /** + * Remove products generated by AI. + * + * @return WP_Error|WP_REST_Response + */ + public function delete_products() { + ( new UpdateProducts() )->reset_products_content(); + return rest_ensure_response( array( 'removed' => true ) ); + } +} diff --git a/plugins/woocommerce/src/Admin/API/AI/StoreInfo.php b/plugins/woocommerce/src/Admin/API/AI/StoreInfo.php new file mode 100644 index 00000000000..0fb0fb3ab8c --- /dev/null +++ b/plugins/woocommerce/src/Admin/API/AI/StoreInfo.php @@ -0,0 +1,81 @@ +register( + array( + array( + 'methods' => \WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_response' ), + 'permission_callback' => array( Middleware::class, 'is_authorized' ), + ), + 'schema' => array( $this, 'get_schema' ), + ) + ); + } + + /** + * Update the store title powered by AI. + * + * @return WP_Error|WP_REST_Response + */ + public function get_response() { + $product_updater = new UpdateProducts(); + $patterns = PatternsHelper::get_patterns_ai_data_post(); + + $products = $product_updater->fetch_product_ids( 'dummy' ); + + if ( empty( $products ) && ! isset( $patterns ) ) { + return rest_ensure_response( + array( + 'is_ai_generated' => false, + ) + ); + } + + return rest_ensure_response( + array( + 'is_ai_generated' => true, + ) + ); + } + + /** + * Get the Business Description response. + * + * @return array + */ + public function get_schema() { + return array( + 'ai_content_generated' => true, + ); + } +} diff --git a/plugins/woocommerce/src/Admin/API/AI/StoreTitle.php b/plugins/woocommerce/src/Admin/API/AI/StoreTitle.php index fd4447bb899..9c479027098 100644 --- a/plugins/woocommerce/src/Admin/API/AI/StoreTitle.php +++ b/plugins/woocommerce/src/Admin/API/AI/StoreTitle.php @@ -45,7 +45,6 @@ class StoreTitle extends AIEndpoint { */ protected $endpoint = 'store-title'; - /** * Register routes. */ diff --git a/plugins/woocommerce/src/Admin/API/Init.php b/plugins/woocommerce/src/Admin/API/Init.php index d8c35352996..67332f10e48 100644 --- a/plugins/woocommerce/src/Admin/API/Init.php +++ b/plugins/woocommerce/src/Admin/API/Init.php @@ -87,6 +87,13 @@ class Init { 'Automattic\WooCommerce\Admin\API\MobileAppMagicLink', 'Automattic\WooCommerce\Admin\API\ShippingPartnerSuggestions', 'Automattic\WooCommerce\Admin\API\AI\StoreTitle', + 'Automattic\WooCommerce\Admin\API\AI\BusinessDescription', + 'Automattic\WooCommerce\Admin\API\AI\StoreInfo', + 'Automattic\WooCommerce\Admin\API\AI\Images', + 'Automattic\WooCommerce\Admin\API\AI\Patterns', + 'Automattic\WooCommerce\Admin\API\AI\Product', + 'Automattic\WooCommerce\Admin\API\AI\Products', + 'Automattic\WooCommerce\Admin\API\Patterns', ); } diff --git a/plugins/woocommerce/src/Admin/API/LaunchYourStore.php b/plugins/woocommerce/src/Admin/API/LaunchYourStore.php index 735e18bba64..3bc45b296c9 100644 --- a/plugins/woocommerce/src/Admin/API/LaunchYourStore.php +++ b/plugins/woocommerce/src/Admin/API/LaunchYourStore.php @@ -128,11 +128,6 @@ class LaunchYourStore { return; } - if ( false === get_option( 'woocommerce_store_pages_only', false ) ) { - // Coming soon already initialized. - return false; - } - $coming_soon = 'yes'; $store_pages_only = WCAdminHelper::is_site_fresh() ? 'no' : 'yes'; $private_link = 'no'; diff --git a/plugins/woocommerce/src/Admin/API/Notes.php b/plugins/woocommerce/src/Admin/API/Notes.php index d96d8165c6c..3c21865fcd9 100644 --- a/plugins/woocommerce/src/Admin/API/Notes.php +++ b/plugins/woocommerce/src/Admin/API/Notes.php @@ -538,7 +538,7 @@ class Notes extends \WC_REST_CRUD_Controller { * * @param string $url The URL needing a nonce. * @param string $action The nonce action. - * @param string $name The nonce anme. + * @param string $name The nonce name. * @return string A fully formed URL. */ private function maybe_add_nonce_to_url( string $url, string $action = '', string $name = '' ) : string { @@ -547,7 +547,7 @@ class Notes extends \WC_REST_CRUD_Controller { } if ( empty( $name ) ) { - // Default paramater name. + // Default parameter name. $name = '_wpnonce'; } diff --git a/plugins/woocommerce/src/Admin/API/OnboardingTasks.php b/plugins/woocommerce/src/Admin/API/OnboardingTasks.php index 5e8cfe19ca6..16b7d652b7c 100644 --- a/plugins/woocommerce/src/Admin/API/OnboardingTasks.php +++ b/plugins/woocommerce/src/Admin/API/OnboardingTasks.php @@ -37,7 +37,7 @@ class OnboardingTasks extends \WC_REST_Data_Controller { protected $rest_base = 'onboarding/tasks'; /** - * Duration to milisecond mapping. + * Duration to millisecond mapping. * * @var array */ diff --git a/plugins/woocommerce/src/Admin/API/Patterns.php b/plugins/woocommerce/src/Admin/API/Patterns.php new file mode 100644 index 00000000000..a4a21dc3596 --- /dev/null +++ b/plugins/woocommerce/src/Admin/API/Patterns.php @@ -0,0 +1,93 @@ + \WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_pattern' ), + 'permission_callback' => function () { + return is_user_logged_in(); + }, + ), + array( + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'update_patterns' ), + 'permission_callback' => function () { + return is_user_logged_in(); + }, + ), + ) + ); + } + + /** + * Fetch a single pattern from the PTK to ensure the API is available. + * + * @return WP_Error|WP_REST_Response + */ + public function get_pattern() { + $ptk_client = Package::container()->get( PTKClient::class ); + + $response = $ptk_client->fetch_patterns( + array( + 'per_page' => 1, + ) + ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + return rest_ensure_response( + array( + 'success' => true, + ) + ); + } + + /** + * Fetch the patterns from the PTK and update the transient. + * + * @return WP_REST_Response + */ + public function update_patterns() { + $ptk_patterns_store = Package::container()->get( PTKPatternsStore::class ); + + $ptk_patterns_store->fetch_patterns(); + + $block_patterns = Package::container()->get( BlockPatterns::class ); + + $block_patterns->register_ptk_patterns(); + + return rest_ensure_response( + array( + 'success' => true, + ) + ); + } +} diff --git a/plugins/woocommerce/src/Admin/API/PaymentGatewaySuggestions.php b/plugins/woocommerce/src/Admin/API/PaymentGatewaySuggestions.php index 09e23aa22ee..4fea841cdca 100644 --- a/plugins/woocommerce/src/Admin/API/PaymentGatewaySuggestions.php +++ b/plugins/woocommerce/src/Admin/API/PaymentGatewaySuggestions.php @@ -2,7 +2,7 @@ /** * REST API Payment Gateway Suggestions Controller * - * Handles requests to install and activate depedent plugins. + * Handles requests to install and activate dependent plugins. */ namespace Automattic\WooCommerce\Admin\API; diff --git a/plugins/woocommerce/src/Admin/API/Plugins.php b/plugins/woocommerce/src/Admin/API/Plugins.php index 746deb7f84e..f8b3e43150f 100644 --- a/plugins/woocommerce/src/Admin/API/Plugins.php +++ b/plugins/woocommerce/src/Admin/API/Plugins.php @@ -2,7 +2,7 @@ /** * REST API Plugins Controller * - * Handles requests to install and activate depedent plugins. + * Handles requests to install and activate dependent plugins. */ namespace Automattic\WooCommerce\Admin\API; diff --git a/plugins/woocommerce/src/Admin/API/ProductsLowInStock.php b/plugins/woocommerce/src/Admin/API/ProductsLowInStock.php index 223df3303cc..9e8ed8b9556 100644 --- a/plugins/woocommerce/src/Admin/API/ProductsLowInStock.php +++ b/plugins/woocommerce/src/Admin/API/ProductsLowInStock.php @@ -263,7 +263,7 @@ final class ProductsLowInStock extends \WC_REST_Products_Controller { /** * Return a query string for low in stock products. - * The query string incldues the following replacement strings: + * The query string includes the following replacement strings: * - :selects * - :postmeta_join * - :postmeta_wheres diff --git a/plugins/woocommerce/src/Admin/API/Reports/Categories/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Categories/Controller.php index bdfe5a78ca5..463ed1e3482 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Categories/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Categories/Controller.php @@ -9,16 +9,20 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Categories; defined( 'ABSPATH' ) || exit; -use Automattic\WooCommerce\Admin\API\Reports\Controller as ReportsController; use Automattic\WooCommerce\Admin\API\Reports\ExportableInterface; +use Automattic\WooCommerce\Admin\API\Reports\GenericController; +use Automattic\WooCommerce\Admin\API\Reports\GenericQuery; +use Automattic\WooCommerce\Admin\API\Reports\OrderAwareControllerTrait; /** * REST API Reports categories controller class. * * @internal - * @extends \Automattic\WooCommerce\Admin\API\Reports\Controller + * @extends \Automattic\WooCommerce\Admin\API\Reports\GenericController */ -class Controller extends ReportsController implements ExportableInterface { +class Controller extends GenericController implements ExportableInterface { + + use OrderAwareControllerTrait; /** * Route base. @@ -27,6 +31,19 @@ class Controller extends ReportsController implements ExportableInterface { */ protected $rest_base = 'reports/categories'; + /** + * Get data from `'categories'` GenericQuery. + * + * @override GenericController::get_datastore_data() + * + * @param array $query_args Query arguments. + * @return mixed Results from the data store. + */ + protected function get_datastore_data( $query_args = array() ) { + $query = new GenericQuery( $query_args, 'categories' ); + return $query->get_data(); + } + /** * Maps query arguments from the REST request. * @@ -52,56 +69,15 @@ class Controller extends ReportsController implements ExportableInterface { } /** - * Get all reports. + * Prepare a report data item for serialization. * - * @param WP_REST_Request $request Request data. - * @return array|WP_Error - */ - public function get_items( $request ) { - $query_args = $this->prepare_reports_query( $request ); - $categories_query = new Query( $query_args ); - $report_data = $categories_query->get_data(); - - if ( is_wp_error( $report_data ) ) { - return $report_data; - } - - if ( ! isset( $report_data->data ) || ! isset( $report_data->page_no ) || ! isset( $report_data->pages ) ) { - return new \WP_Error( 'woocommerce_rest_reports_categories_invalid_response', __( 'Invalid response from data store.', 'woocommerce' ), array( 'status' => 500 ) ); - } - - $out_data = array(); - - foreach ( $report_data->data as $datum ) { - $item = $this->prepare_item_for_response( $datum, $request ); - $out_data[] = $this->prepare_response_for_collection( $item ); - } - - return $this->add_pagination_headers( - $request, - $out_data, - (int) $report_data->total, - (int) $report_data->page_no, - (int) $report_data->pages - ); - } - - /** - * Prepare a report object for serialization. - * - * @param stdClass $report Report data. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response + * @param mixed $report Report data item as returned from Data Store. + * @param \WP_REST_Request $request Request object. + * @return \WP_REST_Response */ public function prepare_item_for_response( $report, $request ) { - $data = $report; - - $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; - $data = $this->add_additional_fields_to_object( $data, $request ); - $data = $this->filter_response_by_context( $data, $context ); - // Wrap the data in a response object. - $response = rest_ensure_response( $data ); + $response = parent::prepare_item_for_response( $report, $request ); $response->add_links( $this->prepare_links( $report ) ); /** @@ -119,7 +95,7 @@ class Controller extends ReportsController implements ExportableInterface { /** * Prepare links for the request. * - * @param \Automattic\WooCommerce\Admin\API\Reports\Query $object Object data. + * @param \Automattic\WooCommerce\Admin\API\Reports\GenericQuery $object Object data. * @return array */ protected function prepare_links( $object ) { @@ -193,59 +169,17 @@ class Controller extends ReportsController implements ExportableInterface { * @return array */ public function get_collection_params() { - $params = array(); - $params['context'] = $this->get_context_param( array( 'default' => 'view' ) ); - $params['page'] = array( - 'description' => __( 'Current page of the collection.', 'woocommerce' ), - 'type' => 'integer', - 'default' => 1, - 'sanitize_callback' => 'absint', - 'validate_callback' => 'rest_validate_request_arg', - 'minimum' => 1, + $params = parent::get_collection_params(); + $params['orderby']['default'] = 'category_id'; + $params['orderby']['enum'] = array( + 'category_id', + 'items_sold', + 'net_revenue', + 'orders_count', + 'products_count', + 'category', ); - $params['per_page'] = array( - 'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce' ), - 'type' => 'integer', - 'default' => 10, - 'minimum' => 1, - 'maximum' => 100, - 'sanitize_callback' => 'absint', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['after'] = array( - 'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'date-time', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['before'] = array( - 'description' => __( 'Limit response to resources published before a given ISO8601 compliant date.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'date-time', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['order'] = array( - 'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'desc', - 'enum' => array( 'asc', 'desc' ), - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['orderby'] = array( - 'description' => __( 'Sort collection by object attribute.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'category_id', - 'enum' => array( - 'category_id', - 'items_sold', - 'net_revenue', - 'orders_count', - 'products_count', - 'category', - ), - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['interval'] = array( + $params['interval'] = array( 'description' => __( 'Time interval to use for buckets in the returned data.', 'woocommerce' ), 'type' => 'string', 'default' => 'week', @@ -259,7 +193,7 @@ class Controller extends ReportsController implements ExportableInterface { ), 'validate_callback' => 'rest_validate_request_arg', ); - $params['status_is'] = array( + $params['status_is'] = array( 'description' => __( 'Limit result set to items that have the specified order status.', 'woocommerce' ), 'type' => 'array', 'sanitize_callback' => 'wp_parse_slug_list', @@ -269,7 +203,7 @@ class Controller extends ReportsController implements ExportableInterface { 'type' => 'string', ), ); - $params['status_is_not'] = array( + $params['status_is_not'] = array( 'description' => __( 'Limit result set to items that don\'t have the specified order status.', 'woocommerce' ), 'type' => 'array', 'sanitize_callback' => 'wp_parse_slug_list', @@ -279,7 +213,7 @@ class Controller extends ReportsController implements ExportableInterface { 'type' => 'string', ), ); - $params['categories'] = array( + $params['categories'] = array( 'description' => __( 'Limit result set to all items that have the specified term assigned in the categories taxonomy.', 'woocommerce' ), 'type' => 'array', 'sanitize_callback' => 'wp_parse_id_list', @@ -288,19 +222,13 @@ class Controller extends ReportsController implements ExportableInterface { 'type' => 'integer', ), ); - $params['extended_info'] = array( + $params['extended_info'] = array( 'description' => __( 'Add additional piece of info about each category to the report.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'sanitize_callback' => 'wc_string_to_bool', 'validate_callback' => 'rest_validate_request_arg', ); - $params['force_cache_refresh'] = array( - 'description' => __( 'Force retrieval of fresh data instead of from the cache.', 'woocommerce' ), - 'type' => 'boolean', - 'sanitize_callback' => 'wp_validate_boolean', - 'validate_callback' => 'rest_validate_request_arg', - ); return $params; } diff --git a/plugins/woocommerce/src/Admin/API/Reports/Categories/DataStore.php b/plugins/woocommerce/src/Admin/API/Reports/Categories/DataStore.php index 36a410807e8..1eac07cc3ea 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Categories/DataStore.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Categories/DataStore.php @@ -9,7 +9,6 @@ defined( 'ABSPATH' ) || exit; use Automattic\WooCommerce\Admin\API\Reports\DataStore as ReportsDataStore; use Automattic\WooCommerce\Admin\API\Reports\DataStoreInterface; -use Automattic\WooCommerce\Admin\API\Reports\TimeInterval; use Automattic\WooCommerce\Admin\API\Reports\SqlQuery; /** @@ -20,6 +19,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Table used to get the data. * + * @override ReportsDataStore::$table_name + * * @var string */ protected static $table_name = 'wc_order_product_lookup'; @@ -27,6 +28,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Cache identifier. * + * @override ReportsDataStore::$cache_key + * * @var string */ protected $cache_key = 'categories'; @@ -48,6 +51,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Mapping columns to data type to return correct response types. * + * @override ReportsDataStore::$column_types + * * @var array */ protected $column_types = array( @@ -61,12 +66,16 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Data store context used to pass to filters. * + * @override ReportsDataStore::$context + * * @var string */ protected $context = 'categories'; /** * Assign report columns once full table name has been assigned. + * + * @override ReportsDataStore::assign_report_columns() */ protected function assign_report_columns() { $table_name = self::get_db_table_name(); @@ -145,6 +154,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Maps ordering specified by the user to columns in the database/fields in the data. * + * @override ReportsDataStore::normalize_order_by() + * * @param string $order_by Sorting criterion. * @return string */ @@ -201,104 +212,99 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { } /** - * Returns the report data based on parameters supplied by the user. + * Get the default query arguments to be used by get_data(). + * These defaults are only partially applied when used via REST API, as that has its own defaults. * - * @param array $query_args Query parameters. - * @return stdClass|WP_Error Data. + * @override ReportsDataStore::get_default_query_vars() + * + * @return array Query parameters. */ - public function get_data( $query_args ) { + public function get_default_query_vars() { + $defaults = parent::get_default_query_vars(); + $defaults['category_includes'] = array(); + $defaults['extended_info'] = false; + + return $defaults; + } + + /** + * Returns the report data based on normalized parameters. + * Will be called by `get_data` if there is no data in cache. + * + * @see get_data + * @override ReportsDataStore::get_noncached_data() + * + * @param array $query_args Query parameters. + * @return stdClass|WP_Error Data object `{ totals: *, intervals: array, total: int, pages: int, page_no: int }`, or error. + */ + public function get_noncached_data( $query_args ) { global $wpdb; $table_name = self::get_db_table_name(); + $this->initialize_queries(); - // These defaults are only partially applied when used via REST API, as that has its own defaults. - $defaults = array( - 'per_page' => get_option( 'posts_per_page' ), - 'page' => 1, - 'order' => 'DESC', - 'orderby' => 'date', - 'before' => TimeInterval::default_before(), - 'after' => TimeInterval::default_after(), - 'fields' => '*', - 'category_includes' => array(), - 'extended_info' => false, + $data = (object) array( + 'data' => array(), + 'total' => 0, + 'pages' => 0, + 'page_no' => 0, ); - $query_args = wp_parse_args( $query_args, $defaults ); - $this->normalize_timezones( $query_args, $defaults ); - /* - * We need to get the cache key here because - * parent::update_intervals_sql_params() modifies $query_args. - */ - $cache_key = $this->get_cache_key( $query_args ); - $data = $this->get_cached_data( $cache_key ); + $this->subquery->add_sql_clause( 'select', $this->selected_columns( $query_args ) ); + $included_categories = $this->get_included_categories_array( $query_args ); + $this->add_sql_query_params( $query_args ); - if ( false === $data ) { - $this->initialize_queries(); + if ( count( $included_categories ) > 0 ) { + $fields = $this->get_fields( $query_args ); + $ids_table = $this->get_ids_table( $included_categories, 'category_id' ); - $data = (object) array( - 'data' => array(), - 'total' => 0, - 'pages' => 0, - 'page_no' => 0, + $this->add_sql_clause( 'select', $this->format_join_selections( array_merge( array( 'category_id' ), $fields ), array( 'category_id' ) ) ); + $this->add_sql_clause( 'from', '(' ); + $this->add_sql_clause( 'from', $this->subquery->get_query_statement() ); + $this->add_sql_clause( 'from', ") AS {$table_name}" ); + $this->add_sql_clause( + 'right_join', + "RIGHT JOIN ( {$ids_table} ) AS default_results + ON default_results.category_id = {$table_name}.category_id" ); - $this->subquery->add_sql_clause( 'select', $this->selected_columns( $query_args ) ); - $included_categories = $this->get_included_categories_array( $query_args ); - $this->add_sql_query_params( $query_args ); - - if ( count( $included_categories ) > 0 ) { - $fields = $this->get_fields( $query_args ); - $ids_table = $this->get_ids_table( $included_categories, 'category_id' ); - - $this->add_sql_clause( 'select', $this->format_join_selections( array_merge( array( 'category_id' ), $fields ), array( 'category_id' ) ) ); - $this->add_sql_clause( 'from', '(' ); - $this->add_sql_clause( 'from', $this->subquery->get_query_statement() ); - $this->add_sql_clause( 'from', ") AS {$table_name}" ); - $this->add_sql_clause( - 'right_join', - "RIGHT JOIN ( {$ids_table} ) AS default_results - ON default_results.category_id = {$table_name}.category_id" - ); - - $categories_query = $this->get_query_statement(); - } else { - $this->subquery->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); - $categories_query = $this->subquery->get_query_statement(); - } - $categories_data = $wpdb->get_results( - $categories_query, // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared - ARRAY_A - ); - - if ( null === $categories_data ) { - return new \WP_Error( 'woocommerce_analytics_categories_result_failed', __( 'Sorry, fetching revenue data failed.', 'woocommerce' ), array( 'status' => 500 ) ); - } - - $record_count = count( $categories_data ); - $total_pages = (int) ceil( $record_count / $query_args['per_page'] ); - if ( $query_args['page'] < 1 || $query_args['page'] > $total_pages ) { - return $data; - } - - $categories_data = $this->page_records( $categories_data, $query_args['page'], $query_args['per_page'] ); - $this->include_extended_info( $categories_data, $query_args ); - $categories_data = array_map( array( $this, 'cast_numbers' ), $categories_data ); - $data = (object) array( - 'data' => $categories_data, - 'total' => $record_count, - 'pages' => $total_pages, - 'page_no' => (int) $query_args['page'], - ); - - $this->set_cached_data( $cache_key, $data ); + $categories_query = $this->get_query_statement(); + } else { + $this->subquery->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); + $categories_query = $this->subquery->get_query_statement(); } + $categories_data = $wpdb->get_results( + $categories_query, // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + ARRAY_A + ); + + if ( null === $categories_data ) { + return new \WP_Error( 'woocommerce_analytics_categories_result_failed', __( 'Sorry, fetching revenue data failed.', 'woocommerce' ), array( 'status' => 500 ) ); + } + + $record_count = count( $categories_data ); + $total_pages = (int) ceil( $record_count / $query_args['per_page'] ); + if ( $query_args['page'] < 1 || $query_args['page'] > $total_pages ) { + return $data; + } + + $categories_data = $this->page_records( $categories_data, $query_args['page'], $query_args['per_page'] ); + $this->include_extended_info( $categories_data, $query_args ); + $categories_data = array_map( array( $this, 'cast_numbers' ), $categories_data ); + $data = (object) array( + 'data' => $categories_data, + 'total' => $record_count, + 'pages' => $total_pages, + 'page_no' => (int) $query_args['page'], + ); return $data; } /** * Initialize query objects. + * + * @override ReportsDataStore::initialize_queries() */ protected function initialize_queries() { global $wpdb; diff --git a/plugins/woocommerce/src/Admin/API/Reports/Categories/Query.php b/plugins/woocommerce/src/Admin/API/Reports/Categories/Query.php index a07d41a56d9..cba0dd7ff62 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Categories/Query.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Categories/Query.php @@ -21,7 +21,9 @@ defined( 'ABSPATH' ) || exit; use Automattic\WooCommerce\Admin\API\Reports\Query as ReportsQuery; /** - * API\Reports\Query + * API\Reports\Categories\Query + * + * @deprecated 9.3.0 Categories\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. */ class Query extends ReportsQuery { @@ -30,18 +32,26 @@ class Query extends ReportsQuery { /** * Valid fields for Categories report. * + * @deprecated 9.3.0 Categories\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. + * * @return array */ protected function get_default_query_vars() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + return array(); } /** * Get categories data based on the current query vars. * + * @deprecated 9.3.0 Categories\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. + * * @return array */ public function get_data() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + $args = apply_filters( 'woocommerce_analytics_categories_query_args', $this->get_query_vars() ); $results = \WC_Data_Store::load( self::REPORT_NAME )->get_data( $args ); return apply_filters( 'woocommerce_analytics_categories_select_query', $results, $args ); diff --git a/plugins/woocommerce/src/Admin/API/Reports/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Controller.php index 078d1cc15a9..6e641575c4d 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Controller.php @@ -1,8 +1,6 @@ is_valid_order( $order ) ) { - return null; - } - - if ( 'shop_order_refund' === $order->get_type() ) { - $order = wc_get_order( $order->get_parent_id() ); - - // If the parent order doesn't exist, return null. - if ( ! $this->is_valid_order( $order ) ) { - return null; - } - } - - if ( ! has_filter( 'woocommerce_order_number' ) ) { - return $order->get_id(); - } - - return $order->get_order_number(); - } - - /** - * Whether the order is valid. - * - * @param bool|WC_Order|WC_Order_Refund $order Order object. - * @return bool True if the order is valid, false otherwise. - */ - protected function is_valid_order( $order ) { - return $order instanceof \WC_Order || $order instanceof \WC_Order_Refund; - } - - /** - * Get the order total with the related currency formatting. - * Returns the parent order total if the order is actually a refund. - * - * @param int $order_id Order ID. - * @return string|null The Order Number or null if the order doesn't exist. - */ - protected function get_total_formatted( $order_id ) { - $order = wc_get_order( $order_id ); - - if ( ! $this->is_valid_order( $order ) ) { - return null; - } - - if ( 'shop_order_refund' === $order->get_type() ) { - $order = wc_get_order( $order->get_parent_id() ); - - if ( ! $this->is_valid_order( $order ) ) { - return null; - } - } - - return wp_strip_all_tags( html_entity_decode( $order->get_formatted_order_total() ), true ); - } - /** * Prepare a report object for serialization. * @@ -214,12 +152,8 @@ class Controller extends GenericController { 'path' => $report->path, ); - $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; - $data = $this->add_additional_fields_to_object( $data, $request ); - $data = $this->filter_response_by_context( $data, $context ); - // Wrap the data in a response object. - $response = rest_ensure_response( $data ); + $response = parent::prepare_item_for_response( $data, $request ); $response->add_links( array( 'self' => array( @@ -249,6 +183,8 @@ class Controller extends GenericController { /** * Get the Report's schema, conforming to JSON Schema. * + * @override WP_REST_Controller::get_item_schema() + * * @return array */ public function get_item_schema() { @@ -291,42 +227,4 @@ class Controller extends GenericController { 'context' => $this->get_context_param( array( 'default' => 'view' ) ), ); } - - /** - * Get order statuses without prefixes. - * Includes unregistered statuses that have been marked "actionable". - * - * @internal - * @return array - */ - public static function get_order_statuses() { - // Allow all statuses selected as "actionable" - this may include unregistered statuses. - // See: https://github.com/woocommerce/woocommerce-admin/issues/5592. - $actionable_statuses = get_option( 'woocommerce_actionable_order_statuses', array() ); - - // See WC_REST_Orders_V2_Controller::get_collection_params() re: any/trash statuses. - $registered_statuses = array_merge( array( 'any', 'trash' ), array_keys( self::get_order_status_labels() ) ); - - // Merge the status arrays (using flip to avoid array_unique()). - $allowed_statuses = array_keys( array_merge( array_flip( $registered_statuses ), array_flip( $actionable_statuses ) ) ); - - return $allowed_statuses; - } - - /** - * Get order statuses (and labels) without prefixes. - * - * @internal - * @return array - */ - public static function get_order_status_labels() { - $order_statuses = array(); - - foreach ( wc_get_order_statuses() as $key => $label ) { - $new_key = str_replace( 'wc-', '', $key ); - $order_statuses[ $new_key ] = $label; - } - - return $order_statuses; - } } diff --git a/plugins/woocommerce/src/Admin/API/Reports/Coupons/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Coupons/Controller.php index 5a884f23bb7..d7f1fd5d818 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Coupons/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Coupons/Controller.php @@ -11,6 +11,7 @@ defined( 'ABSPATH' ) || exit; use Automattic\WooCommerce\Admin\API\Reports\GenericController; use Automattic\WooCommerce\Admin\API\Reports\ExportableInterface; +use Automattic\WooCommerce\Admin\API\Reports\GenericQuery; use WP_REST_Request; use WP_REST_Response; @@ -29,6 +30,19 @@ class Controller extends GenericController implements ExportableInterface { */ protected $rest_base = 'reports/coupons'; + /** + * Get data from `'coupons'` GenericQuery. + * + * @override GenericController::get_datastore_data() + * + * @param array $query_args Query arguments. + * @return mixed Results from the data store. + */ + protected function get_datastore_data( $query_args = array() ) { + $query = new GenericQuery( $query_args, 'coupons' ); + return $query->get_data(); + } + /** * Maps query arguments from the REST request. * @@ -50,38 +64,11 @@ class Controller extends GenericController implements ExportableInterface { } /** - * Get all reports. + * Prepare a report data item for serialization. * - * @param WP_REST_Request $request Request data. - * @return array|WP_Error - */ - public function get_items( $request ) { - $query_args = $this->prepare_reports_query( $request ); - $coupons_query = new Query( $query_args ); - $report_data = $coupons_query->get_data(); - - $data = array(); - - foreach ( $report_data->data as $coupons_data ) { - $item = $this->prepare_item_for_response( $coupons_data, $request ); - $data[] = $this->prepare_response_for_collection( $item ); - } - - return $this->add_pagination_headers( - $request, - $data, - (int) $report_data->total, - (int) $report_data->page_no, - (int) $report_data->pages - ); - } - - /** - * Prepare a report object for serialization. - * - * @param array $report Report data. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response + * @param array $report Report data item as returned from Data Store. + * @param \WP_REST_Request $request Request object. + * @return \WP_REST_Response */ public function prepare_item_for_response( $report, $request ) { $response = parent::prepare_item_for_response( $report, $request ); diff --git a/plugins/woocommerce/src/Admin/API/Reports/Coupons/DataStore.php b/plugins/woocommerce/src/Admin/API/Reports/Coupons/DataStore.php index 40aefe535c7..150c99264da 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Coupons/DataStore.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Coupons/DataStore.php @@ -21,6 +21,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Table used to get the data. * + * @override ReportsDataStore::$table_name + * * @var string */ protected static $table_name = 'wc_order_coupon_lookup'; @@ -28,6 +30,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Cache identifier. * + * @override ReportsDataStore::$cache_key + * * @var string */ protected $cache_key = 'coupons'; @@ -35,6 +39,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Mapping columns to data type to return correct response types. * + * @override ReportsDataStore::$column_types + * * @var array */ protected $column_types = array( @@ -46,12 +52,16 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Data store context used to pass to filters. * + * @override ReportsDataStore::$context + * * @var string */ protected $context = 'coupons'; /** * Assign report columns once full table name has been assigned. + * + * @override ReportsDataStore::assign_report_columns() */ protected function assign_report_columns() { $table_name = self::get_db_table_name(); @@ -148,6 +158,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Maps ordering specified by the user to columns in the database/fields in the data. * + * @override ReportsDataStore::normalize_order_by() + * * @param string $order_by Sorting criterion. * @return string */ @@ -223,119 +235,6 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { } } - /** - * Returns the report data based on parameters supplied by the user. - * - * @param array $query_args Query parameters. - * @return stdClass|WP_Error Data. - */ - public function get_data( $query_args ) { - global $wpdb; - - $table_name = self::get_db_table_name(); - - // These defaults are only partially applied when used via REST API, as that has its own defaults. - $defaults = array( - 'per_page' => get_option( 'posts_per_page' ), - 'page' => 1, - 'order' => 'DESC', - 'orderby' => 'coupon_id', - 'before' => TimeInterval::default_before(), - 'after' => TimeInterval::default_after(), - 'fields' => '*', - 'coupons' => array(), - 'extended_info' => false, - ); - $query_args = wp_parse_args( $query_args, $defaults ); - $this->normalize_timezones( $query_args, $defaults ); - - /* - * We need to get the cache key here because - * parent::update_intervals_sql_params() modifies $query_args. - */ - $cache_key = $this->get_cache_key( $query_args ); - $data = $this->get_cached_data( $cache_key ); - - if ( false === $data ) { - $this->initialize_queries(); - - $data = (object) array( - 'data' => array(), - 'total' => 0, - 'pages' => 0, - 'page_no' => 0, - ); - - $selections = $this->selected_columns( $query_args ); - $included_coupons = $this->get_included_coupons_array( $query_args ); - $limit_params = $this->get_limit_params( $query_args ); - $this->subquery->add_sql_clause( 'select', $selections ); - $this->add_sql_query_params( $query_args ); - - if ( count( $included_coupons ) > 0 ) { - $total_results = count( $included_coupons ); - $total_pages = (int) ceil( $total_results / $limit_params['per_page'] ); - - $fields = $this->get_fields( $query_args ); - $ids_table = $this->get_ids_table( $included_coupons, 'coupon_id' ); - - $this->add_sql_clause( 'select', $this->format_join_selections( $fields, array( 'coupon_id' ) ) ); - $this->add_sql_clause( 'from', '(' ); - $this->add_sql_clause( 'from', $this->subquery->get_query_statement() ); - $this->add_sql_clause( 'from', ") AS {$table_name}" ); - $this->add_sql_clause( - 'right_join', - "RIGHT JOIN ( {$ids_table} ) AS default_results - ON default_results.coupon_id = {$table_name}.coupon_id" - ); - - $coupons_query = $this->get_query_statement(); - } else { - $this->subquery->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); - $this->subquery->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) ); - $coupons_query = $this->subquery->get_query_statement(); - - $this->subquery->clear_sql_clause( array( 'select', 'order_by', 'limit' ) ); - $this->subquery->add_sql_clause( 'select', 'coupon_id' ); - $coupon_subquery = "SELECT COUNT(*) FROM ( - {$this->subquery->get_query_statement()} - ) AS tt"; - - $db_records_count = (int) $wpdb->get_var( - $coupon_subquery // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared - ); - - $total_results = $db_records_count; - $total_pages = (int) ceil( $db_records_count / $limit_params['per_page'] ); - if ( $query_args['page'] < 1 || $query_args['page'] > $total_pages ) { - return $data; - } - } - - $coupon_data = $wpdb->get_results( - $coupons_query, // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared - ARRAY_A - ); - if ( null === $coupon_data ) { - return $data; - } - - $this->include_extended_info( $coupon_data, $query_args ); - - $coupon_data = array_map( array( $this, 'cast_numbers' ), $coupon_data ); - $data = (object) array( - 'data' => $coupon_data, - 'total' => $total_results, - 'pages' => $total_pages, - 'page_no' => (int) $query_args['page'], - ); - - $this->set_cached_data( $cache_key, $data ); - } - - return $data; - } - /** * Get coupon ID for an order. * @@ -363,6 +262,115 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { return wc_get_coupon_id_by_code( $coupon_item->get_code() ); } + /** + * Get the default query arguments to be used by get_data(). + * These defaults are only partially applied when used via REST API, as that has its own defaults. + * + * @override ReportsDataStore::get_default_query_vars() + * + * @return array Query parameters. + */ + public function get_default_query_vars() { + $defaults = parent::get_default_query_vars(); + $defaults['orderby'] = 'coupon_id'; + $defaults['coupons'] = array(); + $defaults['extended_info'] = false; + + return $defaults; + } + + + /** + * Returns the report data based on normalized parameters. + * Will be called by `get_data` if there is no data in cache. + * + * @override ReportsDataStore::get_noncached_data() + * + * @see get_data + * @param array $query_args Query parameters. + * @return stdClass|WP_Error Data object `{ totals: *, intervals: array, total: int, pages: int, page_no: int }`, or error. + */ + public function get_noncached_data( $query_args ) { + global $wpdb; + + $table_name = self::get_db_table_name(); + + $this->initialize_queries(); + + $data = (object) array( + 'data' => array(), + 'total' => 0, + 'pages' => 0, + 'page_no' => 0, + ); + + $selections = $this->selected_columns( $query_args ); + $included_coupons = $this->get_included_coupons_array( $query_args ); + $limit_params = $this->get_limit_params( $query_args ); + $this->subquery->add_sql_clause( 'select', $selections ); + $this->add_sql_query_params( $query_args ); + + if ( count( $included_coupons ) > 0 ) { + $total_results = count( $included_coupons ); + $total_pages = (int) ceil( $total_results / $limit_params['per_page'] ); + + $fields = $this->get_fields( $query_args ); + $ids_table = $this->get_ids_table( $included_coupons, 'coupon_id' ); + + $this->add_sql_clause( 'select', $this->format_join_selections( $fields, array( 'coupon_id' ) ) ); + $this->add_sql_clause( 'from', '(' ); + $this->add_sql_clause( 'from', $this->subquery->get_query_statement() ); + $this->add_sql_clause( 'from', ") AS {$table_name}" ); + $this->add_sql_clause( + 'right_join', + "RIGHT JOIN ( {$ids_table} ) AS default_results + ON default_results.coupon_id = {$table_name}.coupon_id" + ); + + $coupons_query = $this->get_query_statement(); + } else { + $this->subquery->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); + $this->subquery->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) ); + $coupons_query = $this->subquery->get_query_statement(); + + $this->subquery->clear_sql_clause( array( 'select', 'order_by', 'limit' ) ); + $this->subquery->add_sql_clause( 'select', 'coupon_id' ); + $coupon_subquery = "SELECT COUNT(*) FROM ( + {$this->subquery->get_query_statement()} + ) AS tt"; + + $db_records_count = (int) $wpdb->get_var( + $coupon_subquery // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + ); + + $total_results = $db_records_count; + $total_pages = (int) ceil( $db_records_count / $limit_params['per_page'] ); + if ( $query_args['page'] < 1 || $query_args['page'] > $total_pages ) { + return $data; + } + } + + $coupon_data = $wpdb->get_results( + $coupons_query, // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + ARRAY_A + ); + if ( null === $coupon_data ) { + return $data; + } + + $this->include_extended_info( $coupon_data, $query_args ); + + $coupon_data = array_map( array( $this, 'cast_numbers' ), $coupon_data ); + $data = (object) array( + 'data' => $coupon_data, + 'total' => $total_results, + 'pages' => $total_pages, + 'page_no' => (int) $query_args['page'], + ); + + return $data; + } + /** * Create or update an an entry in the wc_order_coupon_lookup table for an order. * diff --git a/plugins/woocommerce/src/Admin/API/Reports/Coupons/Query.php b/plugins/woocommerce/src/Admin/API/Reports/Coupons/Query.php index f81fa4b8182..690861ae4d4 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Coupons/Query.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Coupons/Query.php @@ -21,24 +21,34 @@ use Automattic\WooCommerce\Admin\API\Reports\Query as ReportsQuery; /** * API\Reports\Coupons\Query + * + * @deprecated 9.3.0 Coupons\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. */ class Query extends ReportsQuery { /** * Valid fields for Products report. * + * @deprecated 9.3.0 Coupons\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. + * * @return array */ protected function get_default_query_vars() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + return array(); } /** * Get product data based on the current query vars. * + * @deprecated 9.3.0 Coupons\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. + * * @return array */ public function get_data() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + $args = apply_filters( 'woocommerce_analytics_coupons_query_args', $this->get_query_vars() ); $data_store = \WC_Data_Store::load( 'report-coupons' ); diff --git a/plugins/woocommerce/src/Admin/API/Reports/Coupons/Stats/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Coupons/Stats/Controller.php index 88e7c2109b1..593b3c28915 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Coupons/Stats/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Coupons/Stats/Controller.php @@ -10,7 +10,7 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Coupons\Stats; defined( 'ABSPATH' ) || exit; use Automattic\WooCommerce\Admin\API\Reports\GenericStatsController; -use Automattic\WooCommerce\Admin\API\Reports\ParameterException; +use Automattic\WooCommerce\Admin\API\Reports\GenericQuery; use WP_REST_Request; use WP_REST_Response; @@ -54,51 +54,30 @@ class Controller extends GenericStatsController { } /** - * Get all reports. + * Get data from `'coupons-stats'` GenericQuery. * - * @param WP_REST_Request $request Request data. - * @return array|WP_Error + * @override GenericController::get_datastore_data() + * + * @param array $query_args Query arguments. + * @return mixed Results from the data store. */ - public function get_items( $request ) { - $query_args = $this->prepare_reports_query( $request ); - $coupons_query = new Query( $query_args ); - try { - $report_data = $coupons_query->get_data(); - } catch ( ParameterException $e ) { - return new \WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); - } - - $out_data = array( - 'totals' => get_object_vars( $report_data->totals ), - 'intervals' => array(), - ); - - foreach ( $report_data->intervals as $interval_data ) { - $item = $this->prepare_item_for_response( (object) $interval_data, $request ); - $out_data['intervals'][] = $this->prepare_response_for_collection( $item ); - } - - return $this->add_pagination_headers( - $request, - $out_data, - (int) $report_data->total, - (int) $report_data->page_no, - (int) $report_data->pages - ); + protected function get_datastore_data( $query_args = array() ) { + $query = new GenericQuery( $query_args, 'coupons-stats' ); + return $query->get_data(); } /** - * Prepare a report object for serialization. + * Prepare a report data item for serialization. * - * @param stdClass $report Report data. + * @param mixed $report Report data item as returned from Data Store. * @param WP_REST_Request $request Request object. * @return WP_REST_Response */ public function prepare_item_for_response( $report, $request ) { - $data = get_object_vars( $report ); - - $response = parent::prepare_item_for_response( $data, $request ); + $response = parent::prepare_item_for_response( $report, $request ); + // Map to `object` for backwards compatibility. + $report = (object) $report; /** * Filter a report returned from the API. * @@ -189,15 +168,6 @@ class Controller extends GenericStatsController { ), 'validate_callback' => 'rest_validate_request_arg', ); - $params['fields'] = array( - 'description' => __( 'Limit stats fields to the specified items.', 'woocommerce' ), - 'type' => 'array', - 'sanitize_callback' => 'wp_parse_slug_list', - 'validate_callback' => 'rest_validate_request_arg', - 'items' => array( - 'type' => 'string', - ), - ); return $params; } diff --git a/plugins/woocommerce/src/Admin/API/Reports/Coupons/Stats/DataStore.php b/plugins/woocommerce/src/Admin/API/Reports/Coupons/Stats/DataStore.php index 459917ec87d..f8ab7562e40 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Coupons/Stats/DataStore.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Coupons/Stats/DataStore.php @@ -9,15 +9,19 @@ defined( 'ABSPATH' ) || exit; use Automattic\WooCommerce\Admin\API\Reports\Coupons\DataStore as CouponsDataStore; use Automattic\WooCommerce\Admin\API\Reports\DataStoreInterface; use Automattic\WooCommerce\Admin\API\Reports\TimeInterval; -use Automattic\WooCommerce\Admin\API\Reports\SqlQuery; +use Automattic\WooCommerce\Admin\API\Reports\StatsDataStoreTrait; /** * API\Reports\Coupons\Stats\DataStore. */ class DataStore extends CouponsDataStore implements DataStoreInterface { + use StatsDataStoreTrait; + /** * Mapping columns to data type to return correct response types. * + * @override CouponsDataStore::$column_types + * * @var array */ protected $column_types = array( @@ -33,6 +37,8 @@ class DataStore extends CouponsDataStore implements DataStoreInterface { /** * SQL columns to select in the db query. * + * @override CouponsDataStore::$report_columns + * * @var array */ protected $report_columns; @@ -40,6 +46,8 @@ class DataStore extends CouponsDataStore implements DataStoreInterface { /** * Data store context used to pass to filters. * + * @override CouponsDataStore::$context + * * @var string */ protected $context = 'coupons_stats'; @@ -47,12 +55,16 @@ class DataStore extends CouponsDataStore implements DataStoreInterface { /** * Cache identifier. * + * @override CouponsDataStore::get_default_query_vars() + * * @var string */ protected $cache_key = 'coupons_stats'; /** * Assign report columns once full table name has been assigned. + * + * @override CouponsDataStore::assign_report_columns() */ protected function assign_report_columns() { $table_name = self::get_db_table_name(); @@ -105,145 +117,114 @@ class DataStore extends CouponsDataStore implements DataStoreInterface { } /** - * Returns the report data based on parameters supplied by the user. + * Get the default query arguments to be used by get_data(). + * These defaults are only partially applied when used via REST API, as that has its own defaults. * - * @since 3.5.0 - * @param array $query_args Query parameters. - * @return stdClass|WP_Error Data. + * @override CouponsDataStore::get_default_query_vars() + * + * @return array Query parameters. */ - public function get_data( $query_args ) { + public function get_default_query_vars() { + $defaults = parent::get_default_query_vars(); + $defaults['coupons'] = array(); + $defaults['interval'] = 'week'; + + return $defaults; + } + + /** + * Returns the report data based on normalized parameters. + * Will be called by `get_data` if there is no data in cache. + * + * @override CouponsDataStore::get_noncached_stats_data() + * + * @see get_data + * @see get_noncached_stats_data + * @param array $query_args Query parameters. + * @param array $params Query limit parameters. + * @param stdClass $data Reference to the data object to fill. + * @param int $expected_interval_count Number of expected intervals. + * @return stdClass|WP_Error Data object `{ totals: *, intervals: array, total: int, pages: int, page_no: int }`, or error. + */ + public function get_noncached_stats_data( $query_args, $params, &$data, $expected_interval_count ) { global $wpdb; $table_name = self::get_db_table_name(); - // These defaults are only partially applied when used via REST API, as that has its own defaults. - $defaults = array( - 'per_page' => get_option( 'posts_per_page' ), - 'page' => 1, - 'order' => 'DESC', - 'orderby' => 'date', - 'before' => TimeInterval::default_before(), - 'after' => TimeInterval::default_after(), - 'fields' => '*', - 'interval' => 'week', - 'coupons' => array(), + $this->initialize_queries(); + + $selections = $this->selected_columns( $query_args ); + $totals_query = array(); + $intervals_query = array(); + $limit_params = $this->get_limit_sql_params( $query_args ); + $this->update_sql_query_params( $query_args, $totals_query, $intervals_query ); + + $db_intervals = $wpdb->get_col( + // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- cache ok, DB call ok, unprepared SQL ok. + $this->interval_query->get_query_statement() ); - $query_args = wp_parse_args( $query_args, $defaults ); - $this->normalize_timezones( $query_args, $defaults ); - /* - * We need to get the cache key here because - * parent::update_intervals_sql_params() modifies $query_args. - */ - $cache_key = $this->get_cache_key( $query_args ); - $data = $this->get_cached_data( $cache_key ); + $db_interval_count = count( $db_intervals ); - if ( false === $data ) { - $this->initialize_queries(); + $this->total_query->add_sql_clause( 'select', $selections ); + $totals = $wpdb->get_results( + // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- cache ok, DB call ok, unprepared SQL ok. + $this->total_query->get_query_statement(), + ARRAY_A + ); - $data = (object) array( - 'data' => array(), - 'total' => 0, - 'pages' => 0, - 'page_no' => 0, - ); - - $selections = $this->selected_columns( $query_args ); - $totals_query = array(); - $intervals_query = array(); - $limit_params = $this->get_limit_sql_params( $query_args ); - $this->update_sql_query_params( $query_args, $totals_query, $intervals_query ); - - $db_intervals = $wpdb->get_col( - $this->interval_query->get_query_statement() - ); // WPCS: cache ok, DB call ok, unprepared SQL ok. - - $db_interval_count = count( $db_intervals ); - $expected_interval_count = TimeInterval::intervals_between( $query_args['after'], $query_args['before'], $query_args['interval'] ); - $total_pages = (int) ceil( $expected_interval_count / $limit_params['per_page'] ); - if ( $query_args['page'] < 1 || $query_args['page'] > $total_pages ) { - return $data; - } - - $this->total_query->add_sql_clause( 'select', $selections ); - $totals = $wpdb->get_results( - $this->total_query->get_query_statement(), - ARRAY_A - ); // WPCS: cache ok, DB call ok, unprepared SQL ok. - - if ( null === $totals ) { - return $data; - } - - // @todo remove these assignements when refactoring segmenter classes to use query objects. - $totals_query = array( - 'from_clause' => $this->total_query->get_sql_clause( 'join' ), - 'where_time_clause' => $this->total_query->get_sql_clause( 'where_time' ), - 'where_clause' => $this->total_query->get_sql_clause( 'where' ), - ); - $intervals_query = array( - 'select_clause' => $this->get_sql_clause( 'select' ), - 'from_clause' => $this->interval_query->get_sql_clause( 'join' ), - 'where_time_clause' => $this->interval_query->get_sql_clause( 'where_time' ), - 'where_clause' => $this->interval_query->get_sql_clause( 'where' ), - 'limit' => $this->get_sql_clause( 'limit' ), - ); - $segmenter = new Segmenter( $query_args, $this->report_columns ); - $totals[0]['segments'] = $segmenter->get_totals_segments( $totals_query, $table_name ); - $totals = (object) $this->cast_numbers( $totals[0] ); - - // Intervals. - $this->update_intervals_sql_params( $query_args, $db_interval_count, $expected_interval_count, $table_name ); - $this->interval_query->add_sql_clause( 'select', ", MAX({$table_name}.date_created) AS datetime_anchor" ); - - if ( '' !== $selections ) { - $this->interval_query->add_sql_clause( 'select', ', ' . $selections ); - } - - $intervals = $wpdb->get_results( - $this->interval_query->get_query_statement(), - ARRAY_A - ); // WPCS: cache ok, DB call ok, unprepared SQL ok. - - if ( null === $intervals ) { - return $data; - } - - $data = (object) array( - 'totals' => $totals, - 'intervals' => $intervals, - 'total' => $expected_interval_count, - 'pages' => $total_pages, - 'page_no' => (int) $query_args['page'], - ); - - if ( TimeInterval::intervals_missing( $expected_interval_count, $db_interval_count, $limit_params['per_page'], $query_args['page'], $query_args['order'], $query_args['orderby'], count( $intervals ) ) ) { - $this->fill_in_missing_intervals( $db_intervals, $query_args['adj_after'], $query_args['adj_before'], $query_args['interval'], $data ); - $this->sort_intervals( $data, $query_args['orderby'], $query_args['order'] ); - $this->remove_extra_records( $data, $query_args['page'], $limit_params['per_page'], $db_interval_count, $expected_interval_count, $query_args['orderby'], $query_args['order'] ); - } else { - $this->update_interval_boundary_dates( $query_args['after'], $query_args['before'], $query_args['interval'], $data->intervals ); - } - $segmenter->add_intervals_segments( $data, $intervals_query, $table_name ); - $this->create_interval_subtotals( $data->intervals ); - - $this->set_cached_data( $cache_key, $data ); + if ( null === $totals ) { + return $data; } + // phpcs:ignore Generic.Commenting.Todo.TaskFound + // @todo remove these assignements when refactoring segmenter classes to use query objects. + $totals_query = array( + 'from_clause' => $this->total_query->get_sql_clause( 'join' ), + 'where_time_clause' => $this->total_query->get_sql_clause( 'where_time' ), + 'where_clause' => $this->total_query->get_sql_clause( 'where' ), + ); + $intervals_query = array( + 'select_clause' => $this->get_sql_clause( 'select' ), + 'from_clause' => $this->interval_query->get_sql_clause( 'join' ), + 'where_time_clause' => $this->interval_query->get_sql_clause( 'where_time' ), + 'where_clause' => $this->interval_query->get_sql_clause( 'where' ), + 'limit' => $this->get_sql_clause( 'limit' ), + ); + $segmenter = new Segmenter( $query_args, $this->report_columns ); + $totals[0]['segments'] = $segmenter->get_totals_segments( $totals_query, $table_name ); + $totals = (object) $this->cast_numbers( $totals[0] ); + + // Intervals. + $this->update_intervals_sql_params( $query_args, $db_interval_count, $expected_interval_count, $table_name ); + $this->interval_query->add_sql_clause( 'select', ", MAX({$table_name}.date_created) AS datetime_anchor" ); + + if ( '' !== $selections ) { + $this->interval_query->add_sql_clause( 'select', ', ' . $selections ); + } + + $intervals = $wpdb->get_results( + // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- cache ok, DB call ok, unprepared SQL ok. + $this->interval_query->get_query_statement(), + ARRAY_A + ); + + if ( null === $intervals ) { + return $data; + } + + $data->totals = $totals; + $data->intervals = $intervals; + + if ( TimeInterval::intervals_missing( $expected_interval_count, $db_interval_count, $limit_params['per_page'], $query_args['page'], $query_args['order'], $query_args['orderby'], count( $intervals ) ) ) { + $this->fill_in_missing_intervals( $db_intervals, $query_args['adj_after'], $query_args['adj_before'], $query_args['interval'], $data ); + $this->sort_intervals( $data, $query_args['orderby'], $query_args['order'] ); + $this->remove_extra_records( $data, $query_args['page'], $limit_params['per_page'], $db_interval_count, $expected_interval_count, $query_args['orderby'], $query_args['order'] ); + } else { + $this->update_interval_boundary_dates( $query_args['after'], $query_args['before'], $query_args['interval'], $data->intervals ); + } + $segmenter->add_intervals_segments( $data, $intervals_query, $table_name ); + return $data; } - - /** - * Initialize query objects. - */ - protected function initialize_queries() { - $this->clear_all_clauses(); - unset( $this->subquery ); - $this->total_query = new SqlQuery( $this->context . '_total' ); - $this->total_query->add_sql_clause( 'from', self::get_db_table_name() ); - - $this->interval_query = new SqlQuery( $this->context . '_interval' ); - $this->interval_query->add_sql_clause( 'from', self::get_db_table_name() ); - $this->interval_query->add_sql_clause( 'group_by', 'time_interval' ); - } } diff --git a/plugins/woocommerce/src/Admin/API/Reports/Coupons/Stats/Query.php b/plugins/woocommerce/src/Admin/API/Reports/Coupons/Stats/Query.php index 3e538cee9e3..0defa18ad41 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Coupons/Stats/Query.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Coupons/Stats/Query.php @@ -21,24 +21,34 @@ use Automattic\WooCommerce\Admin\API\Reports\Query as ReportsQuery; /** * API\Reports\Coupons\Stats\Query + * + * @deprecated 9.3.0 Coupons\Stats\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. */ class Query extends ReportsQuery { /** * Valid fields for Products report. * + * @deprecated 9.3.0 Coupons\Stats\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. + * * @return array */ protected function get_default_query_vars() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + return array(); } /** * Get product data based on the current query vars. * + * @deprecated 9.3.0 Coupons\Stats\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. + * * @return array */ public function get_data() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + $args = apply_filters( 'woocommerce_analytics_coupons_stats_query_args', $this->get_query_vars() ); $data_store = \WC_Data_Store::load( 'report-coupons-stats' ); diff --git a/plugins/woocommerce/src/Admin/API/Reports/Customers/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Customers/Controller.php index 6502d95e2c6..003fc089b0d 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Customers/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Customers/Controller.php @@ -33,6 +33,19 @@ class Controller extends GenericController implements ExportableInterface { */ protected $rest_base = 'reports/customers'; + /** + * Get data from Customers\Query. + * + * @override GenericController::get_datastore_data() + * + * @param array $query_args Query arguments. + * @return mixed Results from the data store. + */ + protected function get_datastore_data( $query_args = array() ) { + $query = new Query( $query_args ); + return $query->get_data(); + } + /** * Maps query arguments from the REST request. * @@ -84,34 +97,6 @@ class Controller extends GenericController implements ExportableInterface { return $args; } - /** - * Get all reports. - * - * @param WP_REST_Request $request Request data. - * @return array|WP_Error - */ - public function get_items( $request ) { - $query_args = $this->prepare_reports_query( $request ); - $customers_query = new Query( $query_args ); - $report_data = $customers_query->get_data(); - - $data = array(); - - foreach ( $report_data->data as $customer_data ) { - $item = $this->prepare_item_for_response( $customer_data, $request ); - $data[] = $this->prepare_response_for_collection( $item ); - } - - return $this->add_pagination_headers( - $request, - $data, - (int) $report_data->total, - (int) $report_data->page_no, - (int) $report_data->pages - ); - } - - /** * Get one report. * @@ -139,11 +124,11 @@ class Controller extends GenericController implements ExportableInterface { } /** - * Prepare a report object for serialization. + * Prepare a report data item for serialization. * - * @param array $report Report data. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response + * @param array $report Report data item as returned from Data Store. + * @param \WP_REST_Request $request Request object. + * @return \WP_REST_Response */ public function prepare_item_for_response( $report, $request ) { $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; diff --git a/plugins/woocommerce/src/Admin/API/Reports/Customers/DataStore.php b/plugins/woocommerce/src/Admin/API/Reports/Customers/DataStore.php index 74f1a19e7cd..fac1fdaa961 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Customers/DataStore.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Customers/DataStore.php @@ -22,6 +22,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Table used to get the data. * + * @override ReportsDataStore::$table_name + * * @var string */ protected static $table_name = 'wc_customer_lookup'; @@ -29,6 +31,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Cache identifier. * + * @override ReportsDataStore::$cache_key + * * @var string */ protected $cache_key = 'customers'; @@ -36,6 +40,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Mapping columns to data type to return correct response types. * + * @override ReportsDataStore::$column_types + * * @var array */ protected $column_types = array( @@ -49,12 +55,16 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Data store context used to pass to filters. * + * @override ReportsDataStore::$context + * * @var string */ protected $context = 'customers'; /** * Assign report columns once full table name has been assigned. + * + * @override ReportsDataStore::assign_report_columns() */ protected function assign_report_columns() { global $wpdb; @@ -168,6 +178,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Maps ordering specified by the user to columns in the database/fields in the data. * + * @override ReportsDataStore::normalize_order_by() + * * @param string $order_by Sorting criterion. * @return string */ @@ -182,6 +194,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Fills WHERE clause of SQL request with date-related constraints. * + * @override ReportsDataStore::add_time_period_sql_params() + * * @param array $query_args Parameters supplied by the user. * @param string $table_name Name of the db table relevant for the date constraint. */ @@ -409,89 +423,20 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { } /** - * Returns the report data based on parameters supplied by the user. + * Get the default query arguments to be used by get_data(). + * These defaults are only partially applied when used via REST API, as that has its own defaults. * - * @param array $query_args Query parameters. - * @return stdClass|WP_Error Data. + * @override ReportsDataStore::get_default_query_vars() + * + * @return array Query parameters. */ - public function get_data( $query_args ) { - global $wpdb; + public function get_default_query_vars() { + $defaults = parent::get_default_query_vars(); + $defaults['orderby'] = 'date_registered'; + $defaults['order_before'] = TimeInterval::default_before(); + $defaults['order_after'] = TimeInterval::default_after(); - $customers_table_name = self::get_db_table_name(); - $order_stats_table_name = $wpdb->prefix . 'wc_order_stats'; - - // These defaults are only partially applied when used via REST API, as that has its own defaults. - $defaults = array( - 'per_page' => get_option( 'posts_per_page' ), - 'page' => 1, - 'order' => 'DESC', - 'orderby' => 'date_registered', - 'order_before' => TimeInterval::default_before(), - 'order_after' => TimeInterval::default_after(), - 'fields' => '*', - ); - $query_args = wp_parse_args( $query_args, $defaults ); - $this->normalize_timezones( $query_args, $defaults ); - - /* - * We need to get the cache key here because - * parent::update_intervals_sql_params() modifies $query_args. - */ - $cache_key = $this->get_cache_key( $query_args ); - $data = $this->get_cached_data( $cache_key ); - - if ( false === $data ) { - $this->initialize_queries(); - - $data = (object) array( - 'data' => array(), - 'total' => 0, - 'pages' => 0, - 'page_no' => 0, - ); - - $selections = $this->selected_columns( $query_args ); - $sql_query_params = $this->add_sql_query_params( $query_args ); - $count_query = "SELECT COUNT(*) FROM ( - {$this->subquery->get_query_statement()} - ) as tt - "; - $db_records_count = (int) $wpdb->get_var( - $count_query // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared - ); - - $params = $this->get_limit_params( $query_args ); - $total_pages = (int) ceil( $db_records_count / $params['per_page'] ); - if ( $query_args['page'] < 1 || $query_args['page'] > $total_pages ) { - return $data; - } - - $this->subquery->clear_sql_clause( 'select' ); - $this->subquery->add_sql_clause( 'select', $selections ); - $this->subquery->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); - $this->subquery->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) ); - - $customer_data = $wpdb->get_results( - $this->subquery->get_query_statement(), // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared - ARRAY_A - ); - - if ( null === $customer_data ) { - return $data; - } - - $customer_data = array_map( array( $this, 'cast_numbers' ), $customer_data ); - $data = (object) array( - 'data' => $customer_data, - 'total' => $db_records_count, - 'pages' => $total_pages, - 'page_no' => (int) $query_args['page'], - ); - - $this->set_cached_data( $cache_key, $data ); - } - - return $data; + return $defaults; } /** @@ -533,6 +478,69 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { } } + /** + * Returns the report data based on normalized parameters. + * Will be called by `get_data` if there is no data in cache. + * + * @override ReportsDataStore::get_noncached_data() + * + * @see get_data + * @param array $query_args Query parameters. + * @return stdClass|WP_Error Data object `{ totals: *, intervals: array, total: int, pages: int, page_no: int }`, or error. + */ + public function get_noncached_data( $query_args ) { + global $wpdb; + + $this->initialize_queries(); + + $data = (object) array( + 'data' => array(), + 'total' => 0, + 'pages' => 0, + 'page_no' => 0, + ); + + $selections = $this->selected_columns( $query_args ); + $sql_query_params = $this->add_sql_query_params( $query_args ); + $count_query = "SELECT COUNT(*) FROM ( + {$this->subquery->get_query_statement()} + ) as tt + "; + $db_records_count = (int) $wpdb->get_var( + $count_query // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + ); + + $params = $this->get_limit_params( $query_args ); + $total_pages = (int) ceil( $db_records_count / $params['per_page'] ); + if ( $query_args['page'] < 1 || $query_args['page'] > $total_pages ) { + return $data; + } + + $this->subquery->clear_sql_clause( 'select' ); + $this->subquery->add_sql_clause( 'select', $selections ); + $this->subquery->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); + $this->subquery->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) ); + + $customer_data = $wpdb->get_results( + $this->subquery->get_query_statement(), // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + ARRAY_A + ); + + if ( null === $customer_data ) { + return $data; + } + + $customer_data = array_map( array( $this, 'cast_numbers' ), $customer_data ); + $data = (object) array( + 'data' => $customer_data, + 'total' => $db_records_count, + 'pages' => $total_pages, + 'page_no' => (int) $query_args['page'], + ); + + return $data; + } + /** * Get or create a customer from a given order. * diff --git a/plugins/woocommerce/src/Admin/API/Reports/Customers/Query.php b/plugins/woocommerce/src/Admin/API/Reports/Customers/Query.php index c6e5c8c491d..0aa5cbe9247 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Customers/Query.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Customers/Query.php @@ -16,14 +16,23 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Customers; -defined( 'ABSPATH' ) || exit; +use Automattic\WooCommerce\Admin\API\Reports\GenericQuery; -use Automattic\WooCommerce\Admin\API\Reports\Query as ReportsQuery; +defined( 'ABSPATH' ) || exit; /** * API\Reports\Customers\Query */ -class Query extends ReportsQuery { +class Query extends GenericQuery { + + /** + * Specific query name. + * Will be used to load the `report-{name}` data store, + * and to call `woocommerce_analytics_{snake_case(name)}_*` filters. + * + * @var string + */ + protected $name = 'customers'; /** * Valid fields for Customers report. @@ -39,17 +48,4 @@ class Query extends ReportsQuery { 'fields' => '*', ); } - - /** - * Get product data based on the current query vars. - * - * @return array - */ - public function get_data() { - $args = apply_filters( 'woocommerce_analytics_customers_query_args', $this->get_query_vars() ); - - $data_store = \WC_Data_Store::load( 'report-customers' ); - $results = $data_store->get_data( $args ); - return apply_filters( 'woocommerce_analytics_customers_select_query', $results, $args ); - } } diff --git a/plugins/woocommerce/src/Admin/API/Reports/Customers/Stats/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Customers/Stats/Controller.php index 95496f7ae30..3b1b201e50f 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Customers/Stats/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Customers/Stats/Controller.php @@ -7,6 +7,8 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Customers\Stats; +use Automattic\WooCommerce\Admin\API\Reports\Customers\Query; + defined( 'ABSPATH' ) || exit; use Automattic\WooCommerce\Admin\API\Reports\TimeInterval; @@ -83,7 +85,7 @@ class Controller extends \WC_REST_Reports_Controller { */ public function get_items( $request ) { $query_args = $this->prepare_reports_query( $request ); - $customers_query = new Query( $query_args ); + $customers_query = new Query( $query_args, 'customers-stats' ); $report_data = $customers_query->get_data(); $out_data = array( 'totals' => $report_data, @@ -93,11 +95,11 @@ class Controller extends \WC_REST_Reports_Controller { } /** - * Prepare a report object for serialization. + * Prepare a report data item for serialization. * - * @param Array $report Report data. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response + * @param array $report Report data item as returned from Data Store. + * @param \WP_REST_Request $request Request object. + * @return \WP_REST_Response */ public function prepare_item_for_response( $report, $request ) { $data = $report; diff --git a/plugins/woocommerce/src/Admin/API/Reports/Customers/Stats/DataStore.php b/plugins/woocommerce/src/Admin/API/Reports/Customers/Stats/DataStore.php index a57109cfbc8..5e6798e8e2d 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Customers/Stats/DataStore.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Customers/Stats/DataStore.php @@ -8,6 +8,7 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Customers\Stats; defined( 'ABSPATH' ) || exit; use Automattic\WooCommerce\Admin\API\Reports\Customers\DataStore as CustomersDataStore; +use Automattic\WooCommerce\Admin\API\Reports\DataStore as ReportsDataStore; use Automattic\WooCommerce\Admin\API\Reports\DataStoreInterface; /** @@ -17,6 +18,8 @@ class DataStore extends CustomersDataStore implements DataStoreInterface { /** * Mapping columns to data type to return correct response types. * + * @override CustomersDataStore::$column_types + * * @var array */ protected $column_types = array( @@ -29,6 +32,8 @@ class DataStore extends CustomersDataStore implements DataStoreInterface { /** * Cache identifier. * + * @override CustomersDataStore::$cache_key + * * @var string */ protected $cache_key = 'customers_stats'; @@ -36,12 +41,16 @@ class DataStore extends CustomersDataStore implements DataStoreInterface { /** * Data store context used to pass to filters. * + * @override CustomersDataStore::$context + * * @var string */ protected $context = 'customers_stats'; /** * Assign report columns once full table name has been assigned. + * + * @override CustomersDataStore::assign_report_columns() */ protected function assign_report_columns() { $this->report_columns = array( @@ -53,76 +62,70 @@ class DataStore extends CustomersDataStore implements DataStoreInterface { } /** - * Returns the report data based on parameters supplied by the user. + * Get the default query arguments to be used by get_data(). + * These defaults are only partially applied when used via REST API, as that has its own defaults. * - * @param array $query_args Query parameters. - * @return stdClass|WP_Error Data. + * @override CustomersDataStore::get_default_query_vars() + * + * @return array Query parameters. */ - public function get_data( $query_args ) { + public function get_default_query_vars() { + $defaults = ReportsDataStore::get_default_query_vars(); + $defaults['orderby'] = 'date_registered'; + // Do not set `order_before` and `order_after` here, like in the parent class. + return $defaults; + } + + /** + * Returns the report data based on normalized parameters. + * Will be called by `get_data` if there is no data in cache. + * + * @override CustomersDataStore::get_noncached_data() + * + * @see get_data + * @param array $query_args Query parameters. + * @return stdClass|WP_Error Data object `{ totals: *, intervals: array, total: int, pages: int, page_no: int }`, or error. + */ + public function get_noncached_data( $query_args ) { global $wpdb; + $this->initialize_queries(); - $customers_table_name = self::get_db_table_name(); - - // These defaults are only partially applied when used via REST API, as that has its own defaults. - $defaults = array( - 'per_page' => get_option( 'posts_per_page' ), - 'page' => 1, - 'order' => 'DESC', - 'orderby' => 'date_registered', - 'fields' => '*', + $data = (object) array( + 'customers_count' => 0, + 'avg_orders_count' => 0, + 'avg_total_spend' => 0.0, + 'avg_avg_order_value' => 0.0, ); - $query_args = wp_parse_args( $query_args, $defaults ); - $this->normalize_timezones( $query_args, $defaults ); - /* - * We need to get the cache key here because - * parent::update_intervals_sql_params() modifies $query_args. - */ - $cache_key = $this->get_cache_key( $query_args ); - $data = $this->get_cached_data( $cache_key ); + $selections = $this->selected_columns( $query_args ); + $this->add_sql_query_params( $query_args ); + // Clear SQL clauses set for parent class queries that are different here. + $this->subquery->clear_sql_clause( 'select' ); + $this->subquery->add_sql_clause( 'select', 'SUM( total_sales ) AS total_spend,' ); + $this->subquery->add_sql_clause( + 'select', + 'SUM( CASE WHEN parent_id = 0 THEN 1 END ) as orders_count,' + ); + $this->subquery->add_sql_clause( + 'select', + 'CASE WHEN SUM( CASE WHEN parent_id = 0 THEN 1 ELSE 0 END ) = 0 THEN NULL ELSE SUM( total_sales ) / SUM( CASE WHEN parent_id = 0 THEN 1 ELSE 0 END ) END AS avg_order_value' + ); - if ( false === $data ) { - $this->initialize_queries(); + $this->clear_sql_clause( array( 'order_by', 'limit' ) ); + $this->add_sql_clause( 'select', $selections ); + $this->add_sql_clause( 'from', "({$this->subquery->get_query_statement()}) AS tt" ); - $data = (object) array( - 'customers_count' => 0, - 'avg_orders_count' => 0, - 'avg_total_spend' => 0.0, - 'avg_avg_order_value' => 0.0, - ); + $report_data = $wpdb->get_results( + $this->get_query_statement(), // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + ARRAY_A + ); - $selections = $this->selected_columns( $query_args ); - $this->add_sql_query_params( $query_args ); - // Clear SQL clauses set for parent class queries that are different here. - $this->subquery->clear_sql_clause( 'select' ); - $this->subquery->add_sql_clause( 'select', 'SUM( total_sales ) AS total_spend,' ); - $this->subquery->add_sql_clause( - 'select', - 'SUM( CASE WHEN parent_id = 0 THEN 1 END ) as orders_count,' - ); - $this->subquery->add_sql_clause( - 'select', - 'CASE WHEN SUM( CASE WHEN parent_id = 0 THEN 1 ELSE 0 END ) = 0 THEN NULL ELSE SUM( total_sales ) / SUM( CASE WHEN parent_id = 0 THEN 1 ELSE 0 END ) END AS avg_order_value' - ); - - $this->clear_sql_clause( array( 'order_by', 'limit' ) ); - $this->add_sql_clause( 'select', $selections ); - $this->add_sql_clause( 'from', "({$this->subquery->get_query_statement()}) AS tt" ); - - $report_data = $wpdb->get_results( - $this->get_query_statement(), // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared - ARRAY_A - ); - - if ( null === $report_data ) { - return $data; - } - - $data = (object) $this->cast_numbers( $report_data[0] ); - - $this->set_cached_data( $cache_key, $data ); + if ( null === $report_data ) { + return $data; } + $data = (object) $this->cast_numbers( $report_data[0] ); + return $data; } } diff --git a/plugins/woocommerce/src/Admin/API/Reports/Customers/Stats/Query.php b/plugins/woocommerce/src/Admin/API/Reports/Customers/Stats/Query.php index 291358a2cbb..d3adebde7a3 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Customers/Stats/Query.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Customers/Stats/Query.php @@ -22,15 +22,21 @@ use Automattic\WooCommerce\Admin\API\Reports\Query as ReportsQuery; /** * API\Reports\Customers\Stats\Query + * + * @deprecated 9.3.0 Customers\Stats\Query class is deprecated, please use `Reports\Customers\Query` with a custom name, `GenericQuery`, `\WC_Object_Query`, or use `DataStore` directly. */ class Query extends ReportsQuery { /** * Valid fields for Customers report. * + * @deprecated 9.3.0 Customers\Stats\Query class is deprecated, please use `Reports\Customers\Query` with a custom name, `GenericQuery`, `\WC_Object_Query`, or use `DataStore` directly. + * * @return array */ protected function get_default_query_vars() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`Reports\Customers\Query` with a custom name, `GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + return array( 'per_page' => get_option( 'posts_per_page' ), // not sure if this should be the default. 'page' => 1, @@ -43,9 +49,13 @@ class Query extends ReportsQuery { /** * Get product data based on the current query vars. * + * @deprecated 9.3.0 Customers\Stats\Query class is deprecated, please use `Reports\Customers\Query` with a custom name, `GenericQuery`, `\WC_Object_Query`, or use `DataStore` directly. + * * @return array */ public function get_data() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'x.x.x', '`Reports\Customers\Query` with a custom name, `GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + $args = apply_filters( 'woocommerce_analytics_customers_stats_query_args', $this->get_query_vars() ); $data_store = \WC_Data_Store::load( 'report-customers-stats' ); diff --git a/plugins/woocommerce/src/Admin/API/Reports/DataStore.php b/plugins/woocommerce/src/Admin/API/Reports/DataStore.php index 50a9213d9fc..4d200ab1bf9 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/DataStore.php +++ b/plugins/woocommerce/src/Admin/API/Reports/DataStore.php @@ -9,12 +9,59 @@ if ( ! defined( 'ABSPATH' ) ) { exit; } +use Automattic\WooCommerce\Admin\API\Reports\DataStoreInterface; use Automattic\WooCommerce\Admin\API\Reports\TimeInterval; /** - * Admin\API\Reports\DataStore: Common parent for custom report data stores. + * Common parent for custom report data stores. + * + * We use Report DataStores to separate DB data retrieval logic from the REST API controllers. + * + * Handles caching, data normalization, intervals-related methods, and other common functionality. + * So, in your custom report DataStore class that extends this class + * you can focus on specifics by overriding the `get_noncached_data` method. + * + * Minimalistic example: + *
        class MyDataStore extends DataStore implements DataStoreInterface {
        + *     /** Cache identifier, used by the `DataStore` class to handle caching for you. */
        + *     protected $cache_key = 'my_thing';
        + *     /** Data store context used to pass to filters. */
        + *     protected $context = 'my_thing';
        + *     /** Table used to get the data. */
        + *     protected static $table_name = 'my_table';
        + *     /**
        + *      * Method that overrides the `DataStore::get_noncached_data()` to return the report data.
        + *      * Will be called by `get_data` if there is no data in cache.
        + *      */
        + *     public function get_noncached_data( $query ) {
        + *         // Do your magic.
        + *
        + *         // Then return your data in conforming object structure.
        + *         return (object) array(
        + *             'data' => $product_data,
        + *             'total' => 1,
        + *             'page_no' => 1,
        + *             'pages' => 1,
        + *         );
        + *     }
        + * }
        + * 
        + * + * Please use the `woocommerce_data_stores` filter to add your custom data store to the list of available ones. + * Then, your store could be accessed by Controller classes ({@see GenericController::get_datastore_data() GenericController::get_datastore_data()}) + * or using {@link \WC_Data_Store::load() \WC_Data_Store::load()}. + * + * We recommend registering using the REST base name of your Controller as the key, e.g.: + *
        add_filter( 'woocommerce_data_stores', function( $stores ) {
        + *     $stores['reports/my-thing'] = 'MyExtension\Admin\Analytics\Rest_API\MyDataStore';
        + * } );
        + * 
        + * This way, `GenericController` will pick it up automatically. + * + * Note that this class is NOT {@link https://developer.woocommerce.com/docs/how-to-manage-woocommerce-data-stores/ a CRUD data store}. + * It does not implement the {@see WC_Object_Data_Store_Interface WC_Object_Data_Store_Interface} nor extend WC_Data & WC_Data_Store_WP classes. */ -class DataStore extends SqlQuery { +class DataStore extends SqlQuery implements DataStoreInterface { /** * Cache group for the reports. @@ -90,6 +137,8 @@ class DataStore extends SqlQuery { /** * Data store context used to pass to filters. * + * @override SqlQuery + * * @var string */ protected $context = 'reports'; @@ -138,6 +187,8 @@ class DataStore extends SqlQuery { /** * Class constructor. + * + * @override SqlQuery::__construct() */ public function __construct() { self::set_db_table_name(); @@ -160,6 +211,54 @@ class DataStore extends SqlQuery { } } + + /** + * Get the data based on args. + * + * Returns the report data based on parameters supplied by the user. + * Fetches it from cache or returns `get_noncached_data` result. + * + * @param array $query_args Query parameters. + * @return stdClass|WP_Error + */ + public function get_data( $query_args ) { + $defaults = $this->get_default_query_vars(); + $query_args = wp_parse_args( $query_args, $defaults ); + $this->normalize_timezones( $query_args, $defaults ); + + /* + * We need to get the cache key here because + * parent::update_intervals_sql_params() modifies $query_args. + */ + $cache_key = $this->get_cache_key( $query_args ); + $data = $this->get_cached_data( $cache_key ); + + if ( false === $data ) { + $data = $this->get_noncached_data( $query_args ); + $this->set_cached_data( $cache_key, $data ); + } + + return $data; + } + + /** + * Get the default query arguments to be used by get_data(). + * These defaults are only partially applied when used via REST API, as that has its own defaults. + * + * @return array Query parameters. + */ + public function get_default_query_vars() { + return array( + 'per_page' => get_option( 'posts_per_page' ), + 'page' => 1, + 'order' => 'DESC', + 'orderby' => 'date', + 'before' => TimeInterval::default_before(), + 'after' => TimeInterval::default_after(), + 'fields' => '*', + ); + } + /** * Get table name from database class. */ @@ -168,6 +267,19 @@ class DataStore extends SqlQuery { return isset( $wpdb->{static::$table_name} ) ? $wpdb->{static::$table_name} : $wpdb->prefix . static::$table_name; } + /** + * Returns the report data based on normalized parameters. + * Will be called by `get_data` if there is no data in cache. + * + * @see get_data + * @param array $query_args Query parameters. + * @return stdClass|WP_Error Data object `{ totals: *, intervals: array, total: int, pages: int, page_no: int }`, or error. + */ + public function get_noncached_data( $query_args ) { + /* translators: %s: Method name */ + return new \WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass.", 'woocommerce' ), __METHOD__ ), array( 'status' => 405 ) ); + } + /** * Set table name from database class. */ diff --git a/plugins/woocommerce/src/Admin/API/Reports/Downloads/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Downloads/Controller.php index 5978e7cdc0e..1330e2ad4e1 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Downloads/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Downloads/Controller.php @@ -9,16 +9,20 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Downloads; defined( 'ABSPATH' ) || exit; -use Automattic\WooCommerce\Admin\API\Reports\Controller as ReportsController; use Automattic\WooCommerce\Admin\API\Reports\ExportableInterface; +use Automattic\WooCommerce\Admin\API\Reports\GenericController; +use Automattic\WooCommerce\Admin\API\Reports\GenericQuery; +use Automattic\WooCommerce\Admin\API\Reports\OrderAwareControllerTrait; /** * REST API Reports downloads controller class. * * @internal - * @extends Automattic\WooCommerce\Admin\API\Reports\Controller + * @extends Automattic\WooCommerce\Admin\API\Reports\GenericController */ -class Controller extends ReportsController implements ExportableInterface { +class Controller extends GenericController implements ExportableInterface { + + use OrderAwareControllerTrait; /** * Route base. @@ -28,67 +32,40 @@ class Controller extends ReportsController implements ExportableInterface { protected $rest_base = 'reports/downloads'; /** - * Get items. + * Get data from `'downloads'` GenericQuery. * - * @param WP_REST_Request $request Request data. - * @return array|WP_Error + * @override GenericController::get_datastore_data() + * + * @param array $query_args Query arguments. + * @return mixed Results from the data store. */ - public function get_items( $request ) { - $args = array(); - $registered = array_keys( $this->get_collection_params() ); - foreach ( $registered as $param_name ) { - if ( isset( $request[ $param_name ] ) ) { - $args[ $param_name ] = $request[ $param_name ]; - } - } - - $reports = new Query( $args ); - $downloads_data = $reports->get_data(); - - $data = array(); - - foreach ( $downloads_data->data as $download_data ) { - $item = $this->prepare_item_for_response( $download_data, $request ); - $data[] = $this->prepare_response_for_collection( $item ); - } - - return $this->add_pagination_headers( - $request, - $data, - (int) $downloads_data->total, - (int) $downloads_data->page_no, - (int) $downloads_data->pages - ); + protected function get_datastore_data( $query_args = array() ) { + $query = new GenericQuery( $query_args, 'downloads' ); + return $query->get_data(); } /** - * Prepare a report object for serialization. + * Prepare a report data item for serialization. * - * @param Array $report Report data. + * @param Array $report Report data item as returned from Data Store. * @param WP_REST_Request $request Request object. * @return WP_REST_Response */ public function prepare_item_for_response( $report, $request ) { - $data = $report; - - $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; - $data = $this->add_additional_fields_to_object( $data, $request ); - $data = $this->filter_response_by_context( $data, $context ); - // Wrap the data in a response object. - $response = rest_ensure_response( $data ); + $response = parent::prepare_item_for_response( $report, $request ); $response->add_links( $this->prepare_links( $report ) ); - $response->data['date'] = get_date_from_gmt( $data['date_gmt'], 'Y-m-d H:i:s' ); + $response->data['date'] = get_date_from_gmt( $report['date_gmt'], 'Y-m-d H:i:s' ); // Figure out file name. // Matches https://github.com/woocommerce/woocommerce/blob/4be0018c092e617c5d2b8c46b800eb71ece9ddef/includes/class-wc-download-handler.php#L197. - $product_id = intval( $data['product_id'] ); + $product_id = intval( $report['product_id'] ); $_product = wc_get_product( $product_id ); // Make sure the product hasn't been deleted. if ( $_product ) { - $file_path = $_product->get_file_download_path( $data['download_id'] ); + $file_path = $_product->get_file_download_path( $report['download_id'] ); $filename = basename( $file_path ); $response->data['file_name'] = apply_filters( 'woocommerce_file_download_filename', $filename, $product_id ); $response->data['file_path'] = $file_path; @@ -97,9 +74,9 @@ class Controller extends ReportsController implements ExportableInterface { $response->data['file_path'] = ''; } - $customer = new \WC_Customer( $data['user_id'] ); + $customer = new \WC_Customer( $report['user_id'] ); $response->data['username'] = $customer->get_username(); - $response->data['order_number'] = $this->get_order_number( $data['order_id'] ); + $response->data['order_number'] = $this->get_order_number( $report['order_id'] ); /** * Filter a report returned from the API. @@ -130,6 +107,22 @@ class Controller extends ReportsController implements ExportableInterface { return $links; } + /** + * Maps query arguments from the REST request. + * + * @param array $request Request array. + * @return array + */ + protected function prepare_reports_query( $request ) { + $args = array(); + $registered = array_keys( $this->get_collection_params() ); + foreach ( $registered as $param_name ) { + if ( isset( $request[ $param_name ] ) ) { + $args[ $param_name ] = $request[ $param_name ]; + } + } + return $args; + } /** * Get the Report's schema, conforming to JSON Schema. * @@ -225,53 +218,10 @@ class Controller extends ReportsController implements ExportableInterface { * @return array */ public function get_collection_params() { - $params = array(); - $params['context'] = $this->get_context_param( array( 'default' => 'view' ) ); - $params['page'] = array( - 'description' => __( 'Current page of the collection.', 'woocommerce' ), - 'type' => 'integer', - 'default' => 1, - 'sanitize_callback' => 'absint', - 'validate_callback' => 'rest_validate_request_arg', - 'minimum' => 1, - ); - $params['per_page'] = array( - 'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce' ), - 'type' => 'integer', - 'default' => 10, - 'minimum' => 1, - 'maximum' => 100, - 'sanitize_callback' => 'absint', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['after'] = array( - 'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'date-time', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['before'] = array( - 'description' => __( 'Limit response to resources published before a given ISO8601 compliant date.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'date-time', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['order'] = array( - 'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'desc', - 'enum' => array( 'asc', 'desc' ), - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['orderby'] = array( - 'description' => __( 'Sort collection by object attribute.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'date', - 'enum' => array( - 'date', - 'product', - ), - 'validate_callback' => 'rest_validate_request_arg', + $params = parent::get_collection_params(); + $params['orderby']['enum'] = array( + 'date', + 'product', ); $params['match'] = array( 'description' => __( 'Indicates whether all the conditions should be true for the resulting set, or if any one of them is sufficient. Match affects the following parameters: products, orders, username, ip_address.', 'woocommerce' ), @@ -355,12 +305,6 @@ class Controller extends ReportsController implements ExportableInterface { 'type' => 'string', ), ); - $params['force_cache_refresh'] = array( - 'description' => __( 'Force retrieval of fresh data instead of from the cache.', 'woocommerce' ), - 'type' => 'boolean', - 'sanitize_callback' => 'wp_validate_boolean', - 'validate_callback' => 'rest_validate_request_arg', - ); return $params; } diff --git a/plugins/woocommerce/src/Admin/API/Reports/Downloads/DataStore.php b/plugins/woocommerce/src/Admin/API/Reports/Downloads/DataStore.php index 59b14867e61..9adeebe8e32 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Downloads/DataStore.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Downloads/DataStore.php @@ -20,6 +20,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Table used to get the data. * + * @override ReportsDataStore::$table_name + * * @var string */ protected static $table_name = 'wc_download_log'; @@ -27,6 +29,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Cache identifier. * + * @override ReportsDataStore::$cache_key + * * @var string */ protected $cache_key = 'downloads'; @@ -34,6 +38,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Mapping columns to data type to return correct response types. * + * @override ReportsDataStore::$column_types + * * @var array */ protected $column_types = array( @@ -51,12 +57,16 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Data store context used to pass to filters. * + * @override ReportsDataStore::$context + * * @var string */ protected $context = 'downloads'; /** * Assign report columns once full table name has been assigned. + * + * @override ReportsDataStore::assign_report_columns() */ protected function assign_report_columns() { $this->report_columns = array( @@ -252,6 +262,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Gets WHERE time clause of SQL request with date-related constraints. * + * @override ReportsDataStore::add_time_period_sql_params() + * * @param array $query_args Parameters supplied by the user. * @param string $table_name Name of the db table relevant for the date constraint. * @return string @@ -294,94 +306,89 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { } /** - * Returns the report data based on parameters supplied by the user. + * Get the default query arguments to be used by get_data(). + * These defaults are only partially applied when used via REST API, as that has its own defaults. * - * @param array $query_args Query parameters. - * @return stdClass|WP_Error Data. + * @override ReportsDataStore::get_default_query_vars() + * + * @return array Query parameters. */ - public function get_data( $query_args ) { + public function get_default_query_vars() { + $defaults = parent::get_default_query_vars(); + $defaults['orderby'] = 'timestamp'; + + return $defaults; + } + + /** + * Returns the report data based on normalized parameters. + * Will be called by `get_data` if there is no data in cache. + * + * @override ReportsDataStore::get_noncached_data() + * + * @see get_data + * @param array $query_args Query parameters. + * @return stdClass|WP_Error Data object `{ totals: *, intervals: array, total: int, pages: int, page_no: int }`, or error. + */ + public function get_noncached_data( $query_args ) { global $wpdb; - $table_name = self::get_db_table_name(); + $this->initialize_queries(); - // These defaults are only partially applied when used via REST API, as that has its own defaults. - $defaults = array( - 'per_page' => get_option( 'posts_per_page' ), - 'page' => 1, - 'order' => 'DESC', - 'orderby' => 'timestamp', - 'before' => TimeInterval::default_before(), - 'after' => TimeInterval::default_after(), - 'fields' => '*', + $data = (object) array( + 'data' => array(), + 'total' => 0, + 'pages' => 0, + 'page_no' => 0, ); - $query_args = wp_parse_args( $query_args, $defaults ); - $this->normalize_timezones( $query_args, $defaults ); - /* - * We need to get the cache key here because - * parent::update_intervals_sql_params() modifies $query_args. - */ - $cache_key = $this->get_cache_key( $query_args ); - $data = $this->get_cached_data( $cache_key ); + $selections = $this->selected_columns( $query_args ); + $this->add_sql_query_params( $query_args ); - if ( false === $data ) { - $this->initialize_queries(); + // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared + $db_records_count = (int) $wpdb->get_var( + "SELECT COUNT(*) FROM ( + {$this->subquery->get_query_statement()} + ) AS tt" + ); + // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared - $data = (object) array( - 'data' => array(), - 'total' => 0, - 'pages' => 0, - 'page_no' => 0, - ); - - $selections = $this->selected_columns( $query_args ); - $this->add_sql_query_params( $query_args ); - - // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared - $db_records_count = (int) $wpdb->get_var( - "SELECT COUNT(*) FROM ( - {$this->subquery->get_query_statement()} - ) AS tt" - ); - // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared - - $params = $this->get_limit_params( $query_args ); - $total_pages = (int) ceil( $db_records_count / $params['per_page'] ); - if ( $query_args['page'] < 1 || $query_args['page'] > $total_pages ) { - return $data; - } - - $this->subquery->clear_sql_clause( 'select' ); - $this->subquery->add_sql_clause( 'select', $selections ); - $this->subquery->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); - $this->subquery->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) ); - - $download_data = $wpdb->get_results( - $this->subquery->get_query_statement(), // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared - ARRAY_A - ); - - if ( null === $download_data ) { - return $data; - } - - $download_data = array_map( array( $this, 'cast_numbers' ), $download_data ); - $data = (object) array( - 'data' => $download_data, - 'total' => $db_records_count, - 'pages' => $total_pages, - 'page_no' => (int) $query_args['page'], - ); - - $this->set_cached_data( $cache_key, $data ); + $params = $this->get_limit_params( $query_args ); + $total_pages = (int) ceil( $db_records_count / $params['per_page'] ); + if ( $query_args['page'] < 1 || $query_args['page'] > $total_pages ) { + return $data; } + $this->subquery->clear_sql_clause( 'select' ); + $this->subquery->add_sql_clause( 'select', $selections ); + $this->subquery->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); + $this->subquery->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) ); + + $download_data = $wpdb->get_results( + $this->subquery->get_query_statement(), // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + ARRAY_A + ); + + if ( null === $download_data ) { + return $data; + } + + $download_data = array_map( array( $this, 'cast_numbers' ), $download_data ); + $data = (object) array( + 'data' => $download_data, + 'total' => $db_records_count, + 'pages' => $total_pages, + 'page_no' => (int) $query_args['page'], + ); + return $data; } /** * Maps ordering specified by the user to columns in the database/fields in the data. * + * @override ReportsDataStore::normalize_order_by() + * * @param string $order_by Sorting criterion. * @return string */ diff --git a/plugins/woocommerce/src/Admin/API/Reports/Downloads/Query.php b/plugins/woocommerce/src/Admin/API/Reports/Downloads/Query.php index 81d2e0c229c..d47c19deefb 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Downloads/Query.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Downloads/Query.php @@ -21,24 +21,34 @@ use Automattic\WooCommerce\Admin\API\Reports\Query as ReportsQuery; /** * API\Reports\Downloads\Query + * + * @deprecated 9.3.0 Downloads\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. */ class Query extends ReportsQuery { /** * Valid fields for downloads report. * + * @deprecated 9.3.0 Downloads\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. + * * @return array */ protected function get_default_query_vars() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + return array(); } /** * Get downloads data based on the current query vars. * + * @deprecated 9.3.0 Downloads\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. + * * @return array */ public function get_data() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + $args = apply_filters( 'woocommerce_analytics_downloads_query_args', $this->get_query_vars() ); $data_store = \WC_Data_Store::load( 'report-downloads' ); diff --git a/plugins/woocommerce/src/Admin/API/Reports/Downloads/Stats/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Downloads/Stats/Controller.php index 7d248213054..9d8e5f1bed2 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Downloads/Stats/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Downloads/Stats/Controller.php @@ -9,6 +9,7 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Downloads\Stats; defined( 'ABSPATH' ) || exit; +use Automattic\WooCommerce\Admin\API\Reports\GenericQuery; use Automattic\WooCommerce\Admin\API\Reports\GenericStatsController; use WP_REST_Request; use WP_REST_Response; @@ -59,39 +60,22 @@ class Controller extends GenericStatsController { } /** - * Get all reports. + * Get data from `'downloads-stats'` GenericQuery. * - * @param WP_REST_Request $request Request data. - * @return array|WP_Error + * @override GenericController::get_datastore_data() + * + * @param array $query_args Query arguments. + * @return mixed Results from the data store. */ - public function get_items( $request ) { - $query_args = $this->prepare_reports_query( $request ); - $downloads_query = new Query( $query_args ); - $report_data = $downloads_query->get_data(); - - $out_data = array( - 'totals' => get_object_vars( $report_data->totals ), - 'intervals' => array(), - ); - - foreach ( $report_data->intervals as $interval_data ) { - $item = $this->prepare_item_for_response( $interval_data, $request ); - $out_data['intervals'][] = $this->prepare_response_for_collection( $item ); - } - - return $this->add_pagination_headers( - $request, - $out_data, - (int) $report_data->total, - (int) $report_data->page_no, - (int) $report_data->pages - ); + protected function get_datastore_data( $query_args = array() ) { + $query = new GenericQuery( $query_args, 'downloads-stats' ); + return $query->get_data(); } /** - * Prepare a report object for serialization. + * Prepare a report data item for serialization. * - * @param array $report Report data. + * @param array $report Report data item as returned from Data Store. * @param WP_REST_Request $request Request object. * @return WP_REST_Response */ @@ -110,7 +94,6 @@ class Controller extends GenericStatsController { return apply_filters( 'woocommerce_rest_prepare_report_downloads_stats', $response, $report, $request ); } - /** * Get the Report's item properties schema. * Will be used by `get_item_schema` as `totals` and `subtotals`. @@ -129,6 +112,7 @@ class Controller extends GenericStatsController { ), ); } + /** * Get the Report's schema, conforming to JSON Schema. * It does not have the segments as in GenericStatsController. @@ -298,15 +282,6 @@ class Controller extends GenericStatsController { 'type' => 'string', ), ); - $params['fields'] = array( - 'description' => __( 'Limit stats fields to the specified items.', 'woocommerce' ), - 'type' => 'array', - 'sanitize_callback' => 'wp_parse_slug_list', - 'validate_callback' => 'rest_validate_request_arg', - 'items' => array( - 'type' => 'string', - ), - ); return $params; } diff --git a/plugins/woocommerce/src/Admin/API/Reports/Downloads/Stats/DataStore.php b/plugins/woocommerce/src/Admin/API/Reports/Downloads/Stats/DataStore.php index 97a1d8e3180..e82877bdd9b 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Downloads/Stats/DataStore.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Downloads/Stats/DataStore.php @@ -10,16 +10,19 @@ defined( 'ABSPATH' ) || exit; use Automattic\WooCommerce\Admin\API\Reports\Downloads\DataStore as DownloadsDataStore; use Automattic\WooCommerce\Admin\API\Reports\DataStoreInterface; use Automattic\WooCommerce\Admin\API\Reports\TimeInterval; -use Automattic\WooCommerce\Admin\API\Reports\SqlQuery; +use Automattic\WooCommerce\Admin\API\Reports\StatsDataStoreTrait; /** * API\Reports\Downloads\Stats\DataStore. */ class DataStore extends DownloadsDataStore implements DataStoreInterface { + use StatsDataStoreTrait; /** * Mapping columns to data type to return correct response types. * + * @override DownloadsDataStore::$column_types + * * @var array */ protected $column_types = array( @@ -29,6 +32,8 @@ class DataStore extends DownloadsDataStore implements DataStoreInterface { /** * Cache identifier. * + * @override DownloadsDataStore::$cache_key + * * @var string */ protected $cache_key = 'downloads_stats'; @@ -36,12 +41,16 @@ class DataStore extends DownloadsDataStore implements DataStoreInterface { /** * Data store context used to pass to filters. * + * @override DownloadsDataStore::$context + * * @var string */ protected $context = 'downloads_stats'; /** * Assign report columns once full table name has been assigned. + * + * @override DownloadsDataStore::assign_report_columns() */ protected function assign_report_columns() { $this->report_columns = array( @@ -50,111 +59,100 @@ class DataStore extends DownloadsDataStore implements DataStoreInterface { } /** - * Returns the report data based on parameters supplied by the user. + * Get the default query arguments to be used by get_data(). + * These defaults are only partially applied when used via REST API, as that has its own defaults. * - * @param array $query_args Query parameters. - * @return stdClass|WP_Error Data. + * @override DownloadsDataStore::default_query_args() + * + * @return array Query parameters. */ - public function get_data( $query_args ) { + public function get_default_query_vars() { + $defaults = parent::get_default_query_vars(); + $defaults['interval'] = 'week'; + + return $defaults; + } + + /** + * Returns the report data based on normalized parameters. + * Will be called by `get_data` if there is no data in cache. + * + * @override DownloadsDataStore::get_noncached_data() + * + * @see get_data + * @see get_noncached_stats_data + * @param array $query_args Query parameters. + * @param array $params Query limit parameters. + * @param stdClass $data Reference to the data object to fill. + * @param int $expected_interval_count Number of expected intervals. + * @return stdClass|WP_Error Data object `{ totals: *, intervals: array, total: int, pages: int, page_no: int }`, or error. + */ + public function get_noncached_stats_data( $query_args, $params, &$data, $expected_interval_count ) { global $wpdb; $table_name = self::get_db_table_name(); - // These defaults are only partially applied when used via REST API, as that has its own defaults. - $defaults = array( - 'per_page' => get_option( 'posts_per_page' ), - 'page' => 1, - 'order' => 'DESC', - 'orderby' => 'date', - 'fields' => '*', - 'interval' => 'week', - 'before' => TimeInterval::default_before(), - 'after' => TimeInterval::default_after(), + $this->initialize_queries(); + $selections = $this->selected_columns( $query_args ); + $this->add_sql_query_params( $query_args ); + $where_time = $this->add_time_period_sql_params( $query_args, $table_name ); + $this->add_intervals_sql_params( $query_args, $table_name ); + + $this->interval_query->add_sql_clause( 'select', $this->get_sql_clause( 'select' ) . ' AS time_interval' ); + $this->interval_query->str_replace_clause( 'select', 'date_created', 'timestamp' ); + $this->interval_query->str_replace_clause( 'where_time', 'date_created', 'timestamp' ); + + $db_intervals = $wpdb->get_col( + // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- cache ok, DB call ok, unprepared SQL ok. + $this->interval_query->get_query_statement() ); - $query_args = wp_parse_args( $query_args, $defaults ); - $this->normalize_timezones( $query_args, $defaults ); - /* - * We need to get the cache key here because - * parent::update_intervals_sql_params() modifies $query_args. - */ - $cache_key = $this->get_cache_key( $query_args ); - $data = $this->get_cached_data( $cache_key ); + $db_records_count = count( $db_intervals ); - if ( false === $data ) { - $this->initialize_queries(); - $selections = $this->selected_columns( $query_args ); - $this->add_sql_query_params( $query_args ); - $where_time = $this->add_time_period_sql_params( $query_args, $table_name ); - $this->add_intervals_sql_params( $query_args, $table_name ); + $this->update_intervals_sql_params( $query_args, $db_records_count, $expected_interval_count, $table_name ); + $this->interval_query->str_replace_clause( 'where_time', 'date_created', 'timestamp' ); + $this->total_query->add_sql_clause( 'select', $selections ); + $this->total_query->add_sql_clause( 'where', $this->interval_query->get_sql_clause( 'where' ) ); + if ( $where_time ) { + $this->total_query->add_sql_clause( 'where_time', $where_time ); + } + $totals = $wpdb->get_results( + // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- cache ok, DB call ok, unprepared SQL ok. + $this->total_query->get_query_statement(), + ARRAY_A + ); + if ( null === $totals ) { + return new \WP_Error( 'woocommerce_analytics_downloads_stats_result_failed', __( 'Sorry, fetching downloads data failed.', 'woocommerce' ) ); + } - $this->interval_query->add_sql_clause( 'select', $this->get_sql_clause( 'select' ) . ' AS time_interval' ); - $this->interval_query->str_replace_clause( 'select', 'date_created', 'timestamp' ); - $this->interval_query->str_replace_clause( 'where_time', 'date_created', 'timestamp' ); + $this->interval_query->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); + $this->interval_query->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) ); + $this->interval_query->add_sql_clause( 'select', ', MAX(timestamp) AS datetime_anchor' ); + if ( '' !== $selections ) { + $this->interval_query->add_sql_clause( 'select', ', ' . $selections ); + } - $db_intervals = $wpdb->get_col( - $this->interval_query->get_query_statement() - ); // phpcs:ignore cache ok, DB call ok, unprepared SQL ok. + $intervals = $wpdb->get_results( + // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- cache ok, DB call ok, unprepared SQL ok. + $this->interval_query->get_query_statement(), + ARRAY_A + ); - $db_records_count = count( $db_intervals ); + if ( null === $intervals ) { + return new \WP_Error( 'woocommerce_analytics_downloads_stats_result_failed', __( 'Sorry, fetching downloads data failed.', 'woocommerce' ) ); + } - $params = $this->get_limit_params( $query_args ); - $expected_interval_count = TimeInterval::intervals_between( $query_args['after'], $query_args['before'], $query_args['interval'] ); - $total_pages = (int) ceil( $expected_interval_count / $params['per_page'] ); - if ( $query_args['page'] < 1 || $query_args['page'] > $total_pages ) { - return array(); - } + $totals = (object) $this->cast_numbers( $totals[0] ); - $this->update_intervals_sql_params( $query_args, $db_records_count, $expected_interval_count, $table_name ); - $this->interval_query->str_replace_clause( 'where_time', 'date_created', 'timestamp' ); - $this->total_query->add_sql_clause( 'select', $selections ); - $this->total_query->add_sql_clause( 'where', $this->interval_query->get_sql_clause( 'where' ) ); - if ( $where_time ) { - $this->total_query->add_sql_clause( 'where_time', $where_time ); - } - $totals = $wpdb->get_results( - $this->total_query->get_query_statement(), - ARRAY_A - ); // phpcs:ignore cache ok, DB call ok, unprepared SQL ok. - if ( null === $totals ) { - return new \WP_Error( 'woocommerce_analytics_downloads_stats_result_failed', __( 'Sorry, fetching downloads data failed.', 'woocommerce' ) ); - } + $data->totals = $totals; + $data->intervals = $intervals; - $this->interval_query->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); - $this->interval_query->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) ); - $this->interval_query->add_sql_clause( 'select', ', MAX(timestamp) AS datetime_anchor' ); - if ( '' !== $selections ) { - $this->interval_query->add_sql_clause( 'select', ', ' . $selections ); - } - - $intervals = $wpdb->get_results( - $this->interval_query->get_query_statement(), - ARRAY_A - ); // phpcs:ignore cache ok, DB call ok, unprepared SQL ok. - - if ( null === $intervals ) { - return new \WP_Error( 'woocommerce_analytics_downloads_stats_result_failed', __( 'Sorry, fetching downloads data failed.', 'woocommerce' ) ); - } - - $totals = (object) $this->cast_numbers( $totals[0] ); - $data = (object) array( - 'totals' => $totals, - 'intervals' => $intervals, - 'total' => $expected_interval_count, - 'pages' => $total_pages, - 'page_no' => (int) $query_args['page'], - ); - - if ( $this->intervals_missing( $expected_interval_count, $db_records_count, $params['per_page'], $query_args['page'], $query_args['order'], $query_args['orderby'], count( $intervals ) ) ) { - $this->fill_in_missing_intervals( $db_intervals, $query_args['adj_after'], $query_args['adj_before'], $query_args['interval'], $data ); - $this->sort_intervals( $data, $query_args['orderby'], $query_args['order'] ); - $this->remove_extra_records( $data, $query_args['page'], $params['per_page'], $db_records_count, $expected_interval_count, $query_args['orderby'], $query_args['order'] ); - } else { - $this->update_interval_boundary_dates( $query_args['after'], $query_args['before'], $query_args['interval'], $data->intervals ); - } - $this->create_interval_subtotals( $data->intervals ); - - $this->set_cached_data( $cache_key, $data ); + if ( $this->intervals_missing( $expected_interval_count, $db_records_count, $params['per_page'], $query_args['page'], $query_args['order'], $query_args['orderby'], count( $intervals ) ) ) { + $this->fill_in_missing_intervals( $db_intervals, $query_args['adj_after'], $query_args['adj_before'], $query_args['interval'], $data ); + $this->sort_intervals( $data, $query_args['orderby'], $query_args['order'] ); + $this->remove_extra_records( $data, $query_args['page'], $params['per_page'], $db_records_count, $expected_interval_count, $query_args['orderby'], $query_args['order'] ); + } else { + $this->update_interval_boundary_dates( $query_args['after'], $query_args['before'], $query_args['interval'], $data->intervals ); } return $data; @@ -163,6 +161,8 @@ class DataStore extends DownloadsDataStore implements DataStoreInterface { /** * Normalizes order_by clause to match to SQL query. * + * @override DownloadsDataStore::normalize_order_by() + * * @param string $order_by Order by option requeste by user. * @return string */ @@ -173,18 +173,4 @@ class DataStore extends DownloadsDataStore implements DataStoreInterface { return $order_by; } - - /** - * Initialize query objects. - */ - protected function initialize_queries() { - $this->clear_all_clauses(); - unset( $this->subquery ); - $this->total_query = new SqlQuery( $this->context . '_total' ); - $this->total_query->add_sql_clause( 'from', self::get_db_table_name() ); - - $this->interval_query = new SqlQuery( $this->context . '_interval' ); - $this->interval_query->add_sql_clause( 'from', self::get_db_table_name() ); - $this->interval_query->add_sql_clause( 'group_by', 'time_interval' ); - } } diff --git a/plugins/woocommerce/src/Admin/API/Reports/Downloads/Stats/Query.php b/plugins/woocommerce/src/Admin/API/Reports/Downloads/Stats/Query.php index 95c56a105ec..059218e63f3 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Downloads/Stats/Query.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Downloads/Stats/Query.php @@ -11,24 +11,34 @@ use Automattic\WooCommerce\Admin\API\Reports\Query as ReportsQuery; /** * API\Reports\Downloads\Stats\Query + * + * @deprecated 9.3.0 Downloads\Stats\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. */ class Query extends ReportsQuery { /** * Valid fields for Orders report. * + * @deprecated 9.3.0 Downloads\Stats\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. + * * @return array */ protected function get_default_query_vars() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + return array(); } /** * Get revenue data based on the current query vars. * + * @deprecated 9.3.0 Downloads\Stats\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. + * * @return array */ public function get_data() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + $args = apply_filters( 'woocommerce_analytics_downloads_stats_query_args', $this->get_query_vars() ); $data_store = \WC_Data_Store::load( 'report-downloads-stats' ); diff --git a/plugins/woocommerce/src/Admin/API/Reports/FilteredGetDataTrait.php b/plugins/woocommerce/src/Admin/API/Reports/FilteredGetDataTrait.php new file mode 100644 index 00000000000..957790a8ce3 --- /dev/null +++ b/plugins/woocommerce/src/Admin/API/Reports/FilteredGetDataTrait.php @@ -0,0 +1,58 @@ +context}_query_args` and + * `woocommerce_analytics_{$this->context}_select_query` on the `get_data` method. + * + * Example: + *
        class MyStatsDataStore extends DataStore implements DataStoreInterface {
        + *     // Use the trait.
        + *     use FilteredGetDataTrait;
        + *     // Provide all the necessary properties and methods for a regular DataStore.
        + *     // ...
        + * }
        + * 
        + * + * @see DataStore + */ +trait FilteredGetDataTrait { + /** + * Get the data based on args. + * + * Filters query args, calls DataStore::get_data, and returns the filtered data. + * + * @override ReportsDataStore::get_data() + * + * @param array $query_args Query parameters. + * @return stdClass|WP_Error + */ + public function get_data( $query_args ) { + /** + * Called before the data is fetched. + * + * @since 9.3.0 + * @param array $query_args Query parameters. + */ + $args = apply_filters( "woocommerce_analytics_{$this->context}_query_args", $query_args ); + $results = parent::get_data( $args ); + /** + * Called after the data is fetched. + * The results can be modified here. + * + * @since 9.3.0 + * @param stdClass|WP_Error $results The results of the query. + */ + return apply_filters( "woocommerce_analytics_{$this->context}_select_query", $results, $args ); + } +} diff --git a/plugins/woocommerce/src/Admin/API/Reports/GenericController.php b/plugins/woocommerce/src/Admin/API/Reports/GenericController.php index 021d63b4588..2910e23b7d9 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/GenericController.php +++ b/plugins/woocommerce/src/Admin/API/Reports/GenericController.php @@ -7,10 +7,45 @@ use WP_REST_Request; use WP_REST_Response; /** - * WC REST API Reports controller extended - * to be shared as a generic base for all Analytics controllers. + * {@see WC_REST_Reports_Controller WC REST API Reports Controller} extended to be shared as a generic base for all Analytics reports controllers. + * + * Handles pagination HTTP headers and links, basic, conventional params. + * Does all the REST API plumbing as `WC_REST_Controller`. + * + * + * Minimalistic example: + *
        class MyController extends GenericController {
        + *     /** Route of your new REST endpoint. */
        + *     protected $rest_base = 'reports/my-thing';
        + *     /**
        + *      * Provide JSON schema for the response item.
        + *      * @override WC_REST_Reports_Controller::get_item_schema()
        + *      */
        + *     public function get_item_schema() {
        + *         $schema = array(
        + *             '$schema'    => 'http://json-schema.org/draft-04/schema#',
        + *             'title'      => 'report_my_thing',
        + *             'type'       => 'object',
        + *             'properties' => array(
        + *                 'product_id' => array(
        + *                     'type'        => 'integer',
        + *                     'readonly'    => true,
        + *                     'context'     => array( 'view', 'edit' ),
        + *                 'description' => __( 'Product ID.', 'my_extension' ),
        + *                 ),
        + *             ),
        + *         );
        + *         // Add additional fields from `get_additional_fields` method and apply `woocommerce_rest_' . $schema['title'] . '_schema` filter.
        + *         return $this->add_additional_fields_schema( $schema );
        + *     }
        + * }
        + * 
        + * + * The above Controller will get the data from a {@see DataStore data store} registered as `$rest_base` (`reports/my-thing`). + * (To change this behavior, override the `get_datastore_data()` method). + * + * To use the controller, please register it with the filter `woocommerce_admin_rest_controllers` filter. * - * @internal * @extends WC_REST_Reports_Controller */ abstract class GenericController extends \WC_REST_Reports_Controller { @@ -26,12 +61,12 @@ abstract class GenericController extends \WC_REST_Reports_Controller { /** * Add pagination headers and links. * - * @param WP_REST_Request $request Request data. - * @param WP_REST_Response|array $response Response data. - * @param int $total Total results. - * @param int $page Current page. - * @param int $max_pages Total amount of pages. - * @return WP_REST_Response + * @param \WP_REST_Request $request Request data. + * @param \WP_REST_Response|array $response Response data. + * @param int $total Total results. + * @param int $page Current page. + * @param int $max_pages Total amount of pages. + * @return \WP_REST_Response */ public function add_pagination_headers( $request, $response, int $total, int $page, int $max_pages ) { $response = rest_ensure_response( $response ); @@ -62,7 +97,19 @@ abstract class GenericController extends \WC_REST_Reports_Controller { } /** - * Get the query params for collections. + * Get data from `{$this->rest_base}` store, based on the given query vars. + * + * @throws Exception When the data store is not found {@see WC_Data_Store WC_Data_Store}. + * @param array $query_args Query arguments. + * @return mixed Results from the data store. + */ + protected function get_datastore_data( $query_args = array() ) { + $data_store = \WC_Data_Store::load( $this->rest_base ); + return $data_store->get_data( $query_args ); + } + + /** + * Get the query params definition for collections. * * @return array */ @@ -124,15 +171,62 @@ abstract class GenericController extends \WC_REST_Reports_Controller { return $params; } + /** - * Prepare a report object for serialization. + * Get the report data. * - * @param array $report Report data. - * @param WP_REST_Request $request Request object. + * Prepares query params, fetches the report data from the data store, + * prepares it for the response, and packs it into the convention-conforming response object. + * + * @throws \WP_Error When the queried data is invalid. + * @param \WP_REST_Request $request Request data. + * @return \WP_Error|\WP_REST_Response + */ + public function get_items( $request ) { + $query_args = $this->prepare_reports_query( $request ); + $report_data = $this->get_datastore_data( $query_args ); + + if ( is_wp_error( $report_data ) ) { + return $report_data; + } + + if ( ! isset( $report_data->data ) || ! isset( $report_data->page_no ) || ! isset( $report_data->pages ) ) { + return new \WP_Error( 'woocommerce_rest_reports_invalid_response', __( 'Invalid response from data store.', 'woocommerce' ), array( 'status' => 500 ) ); + } + + $out_data = array(); + + foreach ( $report_data->data as $datum ) { + $item = $this->prepare_item_for_response( $datum, $request ); + $out_data[] = $this->prepare_response_for_collection( $item ); + } + + return $this->add_pagination_headers( + $request, + $out_data, + (int) $report_data->total, + (int) $report_data->page_no, + (int) $report_data->pages + ); + } + + /** + * Prepare a report data item for serialization. + * + * This method is called by `get_items` to prepare a single report data item for serialization. + * Calls `add_additional_fields_to_object` and `filter_response_by_context`, + * then wpraps the data with `rest_ensure_response`. + * + * You can extend it to add or filter some fields. + * + * @override WP_REST_Posts_Controller::prepare_item_for_response() + * + * @param mixed $report_item Report data item as returned from Data Store. + * @param WP_REST_Request $request Request object. * @return WP_REST_Response */ - public function prepare_item_for_response( $report, $request ) { - $data = $report; + public function prepare_item_for_response( $report_item, $request ) { + $data = $report_item; $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; $data = $this->add_additional_fields_to_object( $data, $request ); @@ -141,4 +235,26 @@ abstract class GenericController extends \WC_REST_Reports_Controller { // Wrap the data in a response object. return rest_ensure_response( $data ); } + + /** + * Maps query arguments from the REST request, to be used to query the datastore. + * + * `WP_REST_Request` does not expose a method to return all params covering defaults, + * as it does for `$request['param']` accessor. + * Therefore, we re-implement defaults resolution. + * + * @param \WP_REST_Request $request Full request object. + * @return array Simplified array of params. + */ + protected function prepare_reports_query( $request ) { + $args = wp_parse_args( + array_intersect_key( + $request->get_query_params(), + $this->get_collection_params() + ), + $request->get_default_params() + ); + + return $args; + } } diff --git a/plugins/woocommerce/src/Admin/API/Reports/GenericQuery.php b/plugins/woocommerce/src/Admin/API/Reports/GenericQuery.php new file mode 100644 index 00000000000..8b8e4c03e2f --- /dev/null +++ b/plugins/woocommerce/src/Admin/API/Reports/GenericQuery.php @@ -0,0 +1,91 @@ +$args = array( + * 'before' => '2018-07-19 00:00:00', + * 'after' => '2018-07-05 00:00:00', + * 'page' => 2, + * ); + * $report = new GenericQuery( $args, 'coupons' ); + * $mydata = $report->get_data(); + * + * + * It uses the name provided in the class property or in the constructor call to load the `report-{name}` data store. + * + * It's used by the {@see GenericController GenericController}. + * + * @since 9.3.0 + */ +class GenericQuery extends \WC_Object_Query { + + /** + * Specific query name. + * Will be used to load the `report-{name}` data store, + * and to call `woocommerce_analytics_{snake_case(name)}_*` filters. + * + * @var string + */ + protected $name; + + /** + * Create a new query. + * + * @param array $args Criteria to query on in a format similar to WP_Query. + * @param string $name Query name. + * @extends WC_Object_Query::_construct + */ + public function __construct( $args, $name = null ) { + $this->name = $name ?? $this->name; + + return parent::__construct( $args ); // phpcs:ignore Universal.CodeAnalysis.ConstructorDestructorReturn.ReturnValueFound + } + /** + * Valid fields for Products report. + * + * @return array + */ + protected function get_default_query_vars() { + return array(); + } + + /** + * Get data from `report-{$name}` store, based on the current query vars. + * Filters query vars through `woocommerce_analytics_{snake_case(name)}_query_args` filter. + * Filters results through `woocommerce_analytics_{snake_case(name)}_select_query` filter. + * + * @return mixed filtered results from the data store. + */ + public function get_data() { + $snake_name = str_replace( '-', '_', $this->name ); + /** + * Filter query args given for the report. + * + * @since 9.3.0 + * + * @param array $query_args Query args. + */ + $args = apply_filters( "woocommerce_analytics_{$snake_name}_query_args", $this->get_query_vars() ); + + $data_store = \WC_Data_Store::load( "report-{$this->name}" ); + $results = $data_store->get_data( $args ); + /** + * Filter report query results. + * + * @since 9.3.0 + * + * @param stdClass|WP_Error $results Results from the data store. + * @param array $args Query args used to get the data (potentially filtered). + */ + return apply_filters( "woocommerce_analytics_{$snake_name}_select_query", $results, $args ); + } +} diff --git a/plugins/woocommerce/src/Admin/API/Reports/GenericStatsController.php b/plugins/woocommerce/src/Admin/API/Reports/GenericStatsController.php index 5852ec0b460..5ee255356fa 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/GenericStatsController.php +++ b/plugins/woocommerce/src/Admin/API/Reports/GenericStatsController.php @@ -6,21 +6,69 @@ defined( 'ABSPATH' ) || exit; use Automattic\WooCommerce\Admin\API\Reports\GenericController; /** - * Generic base for all Stats controllers. + * Generic base for all stats controllers. + * + * {@see GenericController Generic Controller} extended to be shared as a generic base for all Analytics stats controllers. + * + * Besides the `GenericController` functionality, it adds conventional stats-specific collection params and item schema. + * So, you may want to extend only your report-specific {@see get_item_properties_schema() get_item_properties_schema()}`. + * It also uses the stats-specific {@see get_items() get_items()} method, + * which packs report data into `totals` and `intervals`. + * + * + * Minimalistic example: + *
        class StatsController extends GenericStatsController {
        + *     /** Route of your new REST endpoint. */
        + *     protected $rest_base = 'reports/my-thing/stats';
        + *     /** Define your proeprties schema. */
        + *     protected function get_item_properties_schema() {
        + *         return array(
        + *             'my_property' => array(
        + *                 'title'       => __( 'My property', 'my-extension' ),
        + *                 'type'        => 'integer',
        + *                 'readonly'    => true,
        + *                 'context'     => array( 'view', 'edit' ),
        + *                 'description' => __( 'Amazing thing.', 'my-extension' ),
        + *                 'indicator'    => true,
        + *              ),
        + *         );
        + *     }
        + *     /** Define overall schema. You can use the defaults,
        + *      * just remember to provide your title and call `add_additional_fields_schema`
        + *      * to run the filters
        + *      */
        + *     public function get_item_schema() {
        + *         $schema          = parent::get_item_schema();
        + *         $schema['title'] = 'report_my_thing_stats';
        + *
        + *        return $this->add_additional_fields_schema( $schema );
        + *     }
        + * }
        + * 
        * - * @internal * @extends GenericController */ abstract class GenericStatsController extends GenericController { /** - * Get the query params for collections. - * Adds intervals to the generic list. + * Get the query params definition for collections. + * Adds `fields` & `intervals` to the generic list. + * + * @override GenericController::get_collection_params() * * @return array */ public function get_collection_params() { $params = parent::get_collection_params(); + $params['fields'] = array( + 'description' => __( 'Limit stats fields to the specified items.', 'woocommerce' ), + 'type' => 'array', + 'sanitize_callback' => 'wp_parse_slug_list', + 'validate_callback' => 'rest_validate_request_arg', + 'items' => array( + 'type' => 'string', + ), + ); $params['interval'] = array( 'description' => __( 'Time interval to use for buckets in the returned data.', 'woocommerce' ), 'type' => 'string', @@ -40,7 +88,7 @@ abstract class GenericStatsController extends GenericController { } /** - * Get the Report's item properties schema. + * Get the report's item properties schema. * Will be used by `get_item_schema` as `totals` and `subtotals`. * * @return array @@ -50,7 +98,7 @@ abstract class GenericStatsController extends GenericController { /** * Get the Report's schema, conforming to JSON Schema. * - * Please note, it does not call add_additional_fields_schema, + * Please note that it does not call add_additional_fields_schema, * as you may want to update the `title` first. * * @return array @@ -155,4 +203,43 @@ abstract class GenericStatsController extends GenericController { ), ); } + + /** + * Get the report data. + * + * Prepares query params, fetches the report data from the data store, + * prepares it for the response, and packs it into the convention-conforming response object. + * + * @override GenericController::get_items() + * + * @throws \WP_Error When the queried data is invalid. + * @param \WP_REST_Request $request Request data. + * @return \WP_REST_Response|\WP_Error + */ + public function get_items( $request ) { + $query_args = $this->prepare_reports_query( $request ); + try { + $report_data = $this->get_datastore_data( $query_args ); + } catch ( ParameterException $e ) { + return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); + } + + $out_data = array( + 'totals' => $report_data->totals ? get_object_vars( $report_data->totals ) : null, + 'intervals' => array(), + ); + + foreach ( $report_data->intervals as $interval_data ) { + $item = $this->prepare_item_for_response( $interval_data, $request ); + $out_data['intervals'][] = $this->prepare_response_for_collection( $item ); + } + + return $this->add_pagination_headers( + $request, + $out_data, + (int) $report_data->total, + (int) $report_data->page_no, + (int) $report_data->pages + ); + } } diff --git a/plugins/woocommerce/src/Admin/API/Reports/OrderAwareControllerTrait.php b/plugins/woocommerce/src/Admin/API/Reports/OrderAwareControllerTrait.php new file mode 100644 index 00000000000..bf1f28ba39c --- /dev/null +++ b/plugins/woocommerce/src/Admin/API/Reports/OrderAwareControllerTrait.php @@ -0,0 +1,123 @@ +is_valid_order( $order ) ) { + return null; + } + + if ( 'shop_order_refund' === $order->get_type() ) { + $order = wc_get_order( $order->get_parent_id() ); + + // If the parent order doesn't exist, return null. + if ( ! $this->is_valid_order( $order ) ) { + return null; + } + } + + if ( ! has_filter( 'woocommerce_order_number' ) ) { + return $order->get_id(); + } + + return $order->get_order_number(); + } + + /** + * Whether the order is valid. + * + * @param bool|WC_Order|WC_Order_Refund $order Order object. + * @return bool True if the order is valid, false otherwise. + */ + protected function is_valid_order( $order ) { + return $order instanceof \WC_Order || $order instanceof \WC_Order_Refund; + } + + /** + * Get the order total with the related currency formatting. + * Returns the parent order total if the order is actually a refund. + * + * @param int $order_id Order ID. + * @return string|null The Order Number or null if the order doesn't exist. + */ + protected function get_total_formatted( $order_id ) { + $order = wc_get_order( $order_id ); + + if ( ! $this->is_valid_order( $order ) ) { + return null; + } + + if ( 'shop_order_refund' === $order->get_type() ) { + $order = wc_get_order( $order->get_parent_id() ); + + if ( ! $this->is_valid_order( $order ) ) { + return null; + } + } + + return wp_strip_all_tags( html_entity_decode( $order->get_formatted_order_total() ), true ); + } + + /** + * Get order statuses without prefixes. + * Includes unregistered statuses that have been marked "actionable". + * + * @return array + */ + public static function get_order_statuses() { + // Allow all statuses selected as "actionable" - this may include unregistered statuses. + // See: https://github.com/woocommerce/woocommerce-admin/issues/5592. + $actionable_statuses = get_option( 'woocommerce_actionable_order_statuses', array() ); + + // See WC_REST_Orders_V2_Controller::get_collection_params() re: any/trash statuses. + $registered_statuses = array_merge( array( 'any', 'trash' ), array_keys( self::get_order_status_labels() ) ); + + // Merge the status arrays (using flip to avoid array_unique()). + $allowed_statuses = array_keys( array_merge( array_flip( $registered_statuses ), array_flip( $actionable_statuses ) ) ); + + return $allowed_statuses; + } + + /** + * Get order statuses (and labels) without prefixes. + * + * @internal + * @return array + */ + public static function get_order_status_labels() { + $order_statuses = array(); + + foreach ( wc_get_order_statuses() as $key => $label ) { + $new_key = str_replace( 'wc-', '', $key ); + $order_statuses[ $new_key ] = $label; + } + + return $order_statuses; + } +} diff --git a/plugins/woocommerce/src/Admin/API/Reports/Orders/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Orders/Controller.php index b48a7245d38..2a6b81fad94 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Orders/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Orders/Controller.php @@ -9,16 +9,19 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Orders; defined( 'ABSPATH' ) || exit; -use Automattic\WooCommerce\Admin\API\Reports\Controller as ReportsController; use Automattic\WooCommerce\Admin\API\Reports\ExportableInterface; +use Automattic\WooCommerce\Admin\API\Reports\GenericController; +use Automattic\WooCommerce\Admin\API\Reports\OrderAwareControllerTrait; /** * REST API Reports orders controller class. * * @internal - * @extends \Automattic\WooCommerce\Admin\API\Reports\Controller + * @extends \Automattic\WooCommerce\Admin\API\Reports\GenericController */ -class Controller extends ReportsController implements ExportableInterface { +class Controller extends GenericController implements ExportableInterface { + + use OrderAwareControllerTrait; /** * Route base. @@ -27,6 +30,19 @@ class Controller extends ReportsController implements ExportableInterface { */ protected $rest_base = 'reports/orders'; + /** + * Get data from Orders\Query. + * + * @override GenericController::get_datastore_data() + * + * @param array $query_args Query arguments. + * @return mixed Results from the data store. + */ + protected function get_datastore_data( $query_args = array() ) { + $query = new Query( $query_args ); + return $query->get_data(); + } + /** * Maps query arguments from the REST request. * @@ -65,50 +81,17 @@ class Controller extends ReportsController implements ExportableInterface { } /** - * Get all reports. + * Prepare a report data item for serialization. * - * @param WP_REST_Request $request Request data. - * @return array|WP_Error - */ - public function get_items( $request ) { - $query_args = $this->prepare_reports_query( $request ); - $orders_query = new Query( $query_args ); - $report_data = $orders_query->get_data(); - - $data = array(); - - foreach ( $report_data->data as $orders_data ) { - $orders_data['order_number'] = $this->get_order_number( $orders_data['order_id'] ); - $orders_data['total_formatted'] = $this->get_total_formatted( $orders_data['order_id'] ); - $item = $this->prepare_item_for_response( $orders_data, $request ); - $data[] = $this->prepare_response_for_collection( $item ); - } - - return $this->add_pagination_headers( - $request, - $data, - (int) $report_data->total, - (int) $report_data->page_no, - (int) $report_data->pages - ); - } - - /** - * Prepare a report object for serialization. - * - * @param stdClass $report Report data. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response + * @param array $report Report data item as returned from Data Store. + * @param \WP_REST_Request $request Request object. + * @return \WP_REST_Response */ public function prepare_item_for_response( $report, $request ) { - $data = $report; - - $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; - $data = $this->add_additional_fields_to_object( $data, $request ); - $data = $this->filter_response_by_context( $data, $context ); - + $report['order_number'] = $this->get_order_number( $report['order_id'] ); + $report['total_formatted'] = $this->get_total_formatted( $report['order_id'] ); // Wrap the data in a response object. - $response = rest_ensure_response( $data ); + $response = parent::prepare_item_for_response( $report, $request ); $response->add_links( $this->prepare_links( $report ) ); /** @@ -248,54 +231,12 @@ class Controller extends ReportsController implements ExportableInterface { * @return array */ public function get_collection_params() { - $params = array(); - $params['context'] = $this->get_context_param( array( 'default' => 'view' ) ); - $params['page'] = array( - 'description' => __( 'Current page of the collection.', 'woocommerce' ), - 'type' => 'integer', - 'default' => 1, - 'sanitize_callback' => 'absint', - 'validate_callback' => 'rest_validate_request_arg', - 'minimum' => 1, - ); - $params['per_page'] = array( - 'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce' ), - 'type' => 'integer', - 'default' => 10, - 'minimum' => 0, - 'maximum' => 100, - 'sanitize_callback' => 'absint', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['after'] = array( - 'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'date-time', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['before'] = array( - 'description' => __( 'Limit response to resources published before a given ISO8601 compliant date.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'date-time', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['order'] = array( - 'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'desc', - 'enum' => array( 'asc', 'desc' ), - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['orderby'] = array( - 'description' => __( 'Sort collection by object attribute.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'date', - 'enum' => array( - 'date', - 'num_items_sold', - 'net_total', - ), - 'validate_callback' => 'rest_validate_request_arg', + $params = parent::get_collection_params(); + $params['per_page']['minimum'] = 0; + $params['orderby']['enum'] = array( + 'date', + 'num_items_sold', + 'net_total', ); $params['product_includes'] = array( 'description' => __( 'Limit result set to items that have the specified product(s) assigned.', 'woocommerce' ), @@ -464,12 +405,6 @@ class Controller extends ReportsController implements ExportableInterface { 'default' => array(), 'validate_callback' => 'rest_validate_request_arg', ); - $params['force_cache_refresh'] = array( - 'description' => __( 'Force retrieval of fresh data instead of from the cache.', 'woocommerce' ), - 'type' => 'boolean', - 'sanitize_callback' => 'wp_validate_boolean', - 'validate_callback' => 'rest_validate_request_arg', - ); return $params; } diff --git a/plugins/woocommerce/src/Admin/API/Reports/Orders/DataStore.php b/plugins/woocommerce/src/Admin/API/Reports/Orders/DataStore.php index 007bcf16ea8..7d42995158b 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Orders/DataStore.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Orders/DataStore.php @@ -14,7 +14,6 @@ use Automattic\WooCommerce\Admin\API\Reports\DataStore as ReportsDataStore; use Automattic\WooCommerce\Admin\API\Reports\DataStoreInterface; use Automattic\WooCommerce\Admin\API\Reports\SqlQuery; use Automattic\WooCommerce\Admin\API\Reports\Cache; -use Automattic\WooCommerce\Admin\API\Reports\TimeInterval; /** @@ -25,6 +24,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Dynamically sets the date column name based on configuration + * + * @override ReportsDataStore::__construct() */ public function __construct() { $this->date_column_name = get_option( 'woocommerce_date_type', 'date_paid' ); @@ -34,6 +35,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Table used to get the data. * + * @override ReportsDataStore::$table_name + * * @var string */ protected static $table_name = 'wc_order_stats'; @@ -41,6 +44,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Cache identifier. * + * @override ReportsDataStore::$cache_key + * * @var string */ protected $cache_key = 'orders'; @@ -48,6 +53,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Mapping columns to data type to return correct response types. * + * @override ReportsDataStore::$column_types + * * @var array */ protected $column_types = array( @@ -66,16 +73,20 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Data store context used to pass to filters. * + * @override ReportsDataStore::$context + * * @var string */ protected $context = 'orders'; /** * Assign report columns once full table name has been assigned. + * + * @override ReportsDataStore::assign_report_columns() */ protected function assign_report_columns() { $table_name = self::get_db_table_name(); - // Avoid ambigious columns in SQL query. + // Avoid ambiguous columns in SQL query. $this->report_columns = array( 'order_id' => "DISTINCT {$table_name}.order_id", 'parent_id' => "{$table_name}.parent_id", @@ -213,117 +224,118 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { } /** - * Returns the report data based on parameters supplied by the user. + * Get the default query arguments to be used by get_data(). + * These defaults are only partially applied when used via REST API, as that has its own defaults. * - * @param array $query_args Query parameters. - * @return stdClass|WP_Error Data. + * @override ReportsDataStore::get_default_query_vars() + * + * @return array Query parameters. */ - public function get_data( $query_args ) { + public function get_default_query_vars() { + $defaults = array_merge( + parent::get_default_query_vars(), + array( + 'orderby' => $this->date_column_name, + 'product_includes' => array(), + 'product_excludes' => array(), + 'coupon_includes' => array(), + 'coupon_excludes' => array(), + 'tax_rate_includes' => array(), + 'tax_rate_excludes' => array(), + 'customer_type' => null, + 'status_is' => array(), + 'extended_info' => false, + 'refunds' => null, + 'order_includes' => array(), + 'order_excludes' => array(), + ) + ); + + return $defaults; + } + + /** + * Returns the report data based on normalized parameters. + * Will be called by `get_data` if there is no data in cache. + * + * @override ReportsDataStore::get_noncached_data() + * + * @see get_data + * @param array $query_args Query parameters. + * @return stdClass|WP_Error Data object `{ totals: *, intervals: array, total: int, pages: int, page_no: int }`, or error. + */ + public function get_noncached_data( $query_args ) { global $wpdb; - // These defaults are only partially applied when used via REST API, as that has its own defaults. - $defaults = array( - 'per_page' => get_option( 'posts_per_page' ), - 'page' => 1, - 'order' => 'DESC', - 'orderby' => $this->date_column_name, - 'before' => TimeInterval::default_before(), - 'after' => TimeInterval::default_after(), - 'fields' => '*', - 'product_includes' => array(), - 'product_excludes' => array(), - 'coupon_includes' => array(), - 'coupon_excludes' => array(), - 'tax_rate_includes' => array(), - 'tax_rate_excludes' => array(), - 'customer_type' => null, - 'status_is' => array(), - 'extended_info' => false, - 'refunds' => null, - 'order_includes' => array(), - 'order_excludes' => array(), + $this->initialize_queries(); + + $data = (object) array( + 'data' => array(), + 'total' => 0, + 'pages' => 0, + 'page_no' => 0, ); - $query_args = wp_parse_args( $query_args, $defaults ); - $this->normalize_timezones( $query_args, $defaults ); - /* - * We need to get the cache key here because - * parent::update_intervals_sql_params() modifies $query_args. - */ - $cache_key = $this->get_cache_key( $query_args ); - $data = $this->get_cached_data( $cache_key ); - - if ( false === $data ) { - $this->initialize_queries(); + $selections = $this->selected_columns( $query_args ); + $params = $this->get_limit_params( $query_args ); + $this->add_sql_query_params( $query_args ); + /* phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared */ + $db_records_count = (int) $wpdb->get_var( + "SELECT COUNT( DISTINCT tt.order_id ) FROM ( + {$this->subquery->get_query_statement()} + ) AS tt" + ); + /* phpcs:enable */ + if ( 0 === $params['per_page'] ) { + $total_pages = 0; + } else { + $total_pages = (int) ceil( $db_records_count / $params['per_page'] ); + } + if ( $query_args['page'] < 1 || $query_args['page'] > $total_pages ) { $data = (object) array( 'data' => array(), - 'total' => 0, + 'total' => $db_records_count, 'pages' => 0, 'page_no' => 0, ); - - $selections = $this->selected_columns( $query_args ); - $params = $this->get_limit_params( $query_args ); - $this->add_sql_query_params( $query_args ); - /* phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared */ - $db_records_count = (int) $wpdb->get_var( - "SELECT COUNT( DISTINCT tt.order_id ) FROM ( - {$this->subquery->get_query_statement()} - ) AS tt" - ); - /* phpcs:enable */ - - if ( 0 === $params['per_page'] ) { - $total_pages = 0; - } else { - $total_pages = (int) ceil( $db_records_count / $params['per_page'] ); - } - if ( $query_args['page'] < 1 || $query_args['page'] > $total_pages ) { - $data = (object) array( - 'data' => array(), - 'total' => $db_records_count, - 'pages' => 0, - 'page_no' => 0, - ); - return $data; - } - - $this->subquery->clear_sql_clause( 'select' ); - $this->subquery->add_sql_clause( 'select', $selections ); - $this->subquery->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); - $this->subquery->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) ); - /* phpcs:disable WordPress.DB.PreparedSQL.NotPrepared */ - $orders_data = $wpdb->get_results( - $this->subquery->get_query_statement(), - ARRAY_A - ); - /* phpcs:enable */ - - if ( null === $orders_data ) { - return $data; - } - - if ( $query_args['extended_info'] ) { - $this->include_extended_info( $orders_data, $query_args ); - } - - $orders_data = array_map( array( $this, 'cast_numbers' ), $orders_data ); - $data = (object) array( - 'data' => $orders_data, - 'total' => $db_records_count, - 'pages' => $total_pages, - 'page_no' => (int) $query_args['page'], - ); - - $this->set_cached_data( $cache_key, $data ); + return $data; } + + $this->subquery->clear_sql_clause( 'select' ); + $this->subquery->add_sql_clause( 'select', $selections ); + $this->subquery->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); + $this->subquery->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) ); + /* phpcs:disable WordPress.DB.PreparedSQL.NotPrepared */ + $orders_data = $wpdb->get_results( + $this->subquery->get_query_statement(), + ARRAY_A + ); + /* phpcs:enable */ + + if ( null === $orders_data ) { + return $data; + } + + if ( $query_args['extended_info'] ) { + $this->include_extended_info( $orders_data, $query_args ); + } + + $orders_data = array_map( array( $this, 'cast_numbers' ), $orders_data ); + $data = (object) array( + 'data' => $orders_data, + 'total' => $db_records_count, + 'pages' => $total_pages, + 'page_no' => (int) $query_args['page'], + ); return $data; } /** * Normalizes order_by clause to match to SQL query. * + * @override ReportsDataStore::normalize_order_by() + * * @param string $order_by Order by option requeste by user. * @return string */ diff --git a/plugins/woocommerce/src/Admin/API/Reports/Orders/Query.php b/plugins/woocommerce/src/Admin/API/Reports/Orders/Query.php index bb28600ed9f..bf21d709fb8 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Orders/Query.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Orders/Query.php @@ -19,24 +19,32 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Orders; +use Automattic\WooCommerce\Admin\API\Reports\GenericQuery; + defined( 'ABSPATH' ) || exit; -use Automattic\WooCommerce\Admin\API\Reports\Query as ReportsQuery; /** * API\Reports\Orders\Query */ -class Query extends ReportsQuery { +class Query extends GenericQuery { /** - * Get order data based on the current query vars. + * Specific query name. + * Will be used to load the `report-{name}` data store, + * and to call `woocommerce_analytics_{snake_case(name)}_*` filters. + * + * @var string + */ + protected $name = 'orders'; + + + /** + * Get the default allowed query vars. * * @return array */ - public function get_data() { - $args = apply_filters( 'woocommerce_analytics_orders_query_args', $this->get_query_vars() ); - $data_store = \WC_Data_Store::load( 'report-orders' ); - $results = $data_store->get_data( $args ); - return apply_filters( 'woocommerce_analytics_orders_select_query', $results, $args ); + protected function get_default_query_vars() { + return \WC_Object_Query::get_default_query_vars(); } } diff --git a/plugins/woocommerce/src/Admin/API/Reports/Orders/Stats/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Orders/Stats/Controller.php index 0f835bd6d14..ee5d3f24a4f 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Orders/Stats/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Orders/Stats/Controller.php @@ -9,15 +9,19 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Orders\Stats; defined( 'ABSPATH' ) || exit; -use Automattic\WooCommerce\Admin\API\Reports\ParameterException; +use Automattic\WooCommerce\Admin\API\Reports\GenericStatsController; +use Automattic\WooCommerce\Admin\API\Reports\OrderAwareControllerTrait; +use Automattic\WooCommerce\Admin\API\Reports\Orders\Stats\Query; /** * REST API Reports orders stats controller class. * * @internal - * @extends \Automattic\WooCommerce\Admin\API\Reports\Controller + * @extends \Automattic\WooCommerce\Admin\API\Reports\GenericStatsController */ -class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller { +class Controller extends GenericStatsController { + + use OrderAwareControllerTrait; /** * Route base. @@ -26,6 +30,19 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller { */ protected $rest_base = 'reports/orders/stats'; + /** + * Get data from Orders\Stats\Query. + * + * @override GenericController::get_datastore_data() + * + * @param array $query_args Query arguments. + * @return mixed Results from the data store. + */ + protected function get_datastore_data( $query_args = array() ) { + $query = new Query( $query_args ); + return $query->get_data(); + } + /** * Maps query arguments from the REST request. * @@ -70,55 +87,15 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller { } /** - * Get all reports. + * Prepare a report data item for serialization. * - * @param WP_REST_Request $request Request data. - * @return array|WP_Error - */ - public function get_items( $request ) { - $query_args = $this->prepare_reports_query( $request ); - $orders_query = new Query( $query_args ); - try { - $report_data = $orders_query->get_data(); - } catch ( ParameterException $e ) { - return new \WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); - } - - $out_data = array( - 'totals' => get_object_vars( $report_data->totals ), - 'intervals' => array(), - ); - - foreach ( $report_data->intervals as $interval_data ) { - $item = $this->prepare_item_for_response( $interval_data, $request ); - $out_data['intervals'][] = $this->prepare_response_for_collection( $item ); - } - - return $this->add_pagination_headers( - $request, - $out_data, - (int) $report_data->total, - (int) $report_data->page_no, - (int) $report_data->pages - ); - } - - /** - * Prepare a report object for serialization. - * - * @param Array $report Report data. + * @param Array $report Report data item as returned from Data Store. * @param WP_REST_Request $request Request object. * @return WP_REST_Response */ public function prepare_item_for_response( $report, $request ) { - $data = $report; - - $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; - $data = $this->add_additional_fields_to_object( $data, $request ); - $data = $this->filter_response_by_context( $data, $context ); - // Wrap the data in a response object. - $response = rest_ensure_response( $data ); + $response = parent::prepare_item_for_response( $report, $request ); /** * Filter a report returned from the API. @@ -132,13 +109,15 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller { return apply_filters( 'woocommerce_rest_prepare_report_orders_stats', $response, $report, $request ); } + /** - * Get the Report's schema, conforming to JSON Schema. + * Get the Report's item properties schema. + * Will be used by `get_item_schema` as `totals` and `subtotals`. * * @return array */ - public function get_item_schema() { - $data_values = array( + protected function get_item_properties_schema() { + return array( 'net_revenue' => array( 'description' => __( 'Net sales.', 'woocommerce' ), 'type' => 'number', @@ -199,104 +178,19 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller { 'readonly' => true, ), ); + } - $segments = array( - 'segments' => array( - 'description' => __( 'Reports data grouped by segment condition.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'segment_id' => array( - 'description' => __( 'Segment identificator.', 'woocommerce' ), - 'type' => 'integer', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'subtotals' => array( - 'description' => __( 'Interval subtotals.', 'woocommerce' ), - 'type' => 'object', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - 'properties' => $data_values, - ), - ), - ), - ), - ); - - $totals = array_merge( $data_values, $segments ); + /** + * Get the Report's schema, conforming to JSON Schema. + * + * @return array + */ + public function get_item_schema() { + $schema = parent::get_item_schema(); + $schema['title'] = 'report_orders_stats'; // Products is not shown in intervals. - unset( $data_values['products'] ); - - $intervals = array_merge( $data_values, $segments ); - - $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => 'report_orders_stats', - 'type' => 'object', - 'properties' => array( - 'totals' => array( - 'description' => __( 'Totals data.', 'woocommerce' ), - 'type' => 'object', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - 'properties' => $totals, - ), - 'intervals' => array( - 'description' => __( 'Reports data grouped by intervals.', 'woocommerce' ), - 'type' => 'array', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - 'items' => array( - 'type' => 'object', - 'properties' => array( - 'interval' => array( - 'description' => __( 'Type of interval.', 'woocommerce' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - 'enum' => array( 'day', 'week', 'month', 'year' ), - ), - 'date_start' => array( - 'description' => __( "The date the report start, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_start_gmt' => array( - 'description' => __( 'The date the report start, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_end' => array( - 'description' => __( "The date the report end, in the site's timezone.", 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'date_end_gmt' => array( - 'description' => __( 'The date the report end, as GMT.', 'woocommerce' ), - 'type' => 'date-time', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - ), - 'subtotals' => array( - 'description' => __( 'Interval subtotals.', 'woocommerce' ), - 'type' => 'object', - 'context' => array( 'view', 'edit' ), - 'readonly' => true, - 'properties' => $intervals, - ), - ), - ), - ), - ), - ); + unset( $schema['properties']['intervals']['items']['properties']['subtotals']['properties']['products'] ); return $this->add_additional_fields_schema( $schema ); } @@ -307,69 +201,12 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller { * @return array */ public function get_collection_params() { - $params = array(); - $params['context'] = $this->get_context_param( array( 'default' => 'view' ) ); - $params['page'] = array( - 'description' => __( 'Current page of the collection.', 'woocommerce' ), - 'type' => 'integer', - 'default' => 1, - 'sanitize_callback' => 'absint', - 'validate_callback' => 'rest_validate_request_arg', - 'minimum' => 1, - ); - $params['per_page'] = array( - 'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce' ), - 'type' => 'integer', - 'default' => 10, - 'minimum' => 1, - 'maximum' => 100, - 'sanitize_callback' => 'absint', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['after'] = array( - 'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'date-time', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['before'] = array( - 'description' => __( 'Limit response to resources published before a given ISO8601 compliant date.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'date-time', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['order'] = array( - 'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'desc', - 'enum' => array( 'asc', 'desc' ), - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['orderby'] = array( - 'description' => __( 'Sort collection by object attribute.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'date', - 'enum' => array( - 'date', - 'net_revenue', - 'orders_count', - 'avg_order_value', - ), - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['interval'] = array( - 'description' => __( 'Time interval to use for buckets in the returned data.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'week', - 'enum' => array( - 'hour', - 'day', - 'week', - 'month', - 'quarter', - 'year', - ), - 'validate_callback' => 'rest_validate_request_arg', + $params = parent::get_collection_params(); + $params['orderby']['enum'] = array( + 'date', + 'net_revenue', + 'orders_count', + 'avg_order_value', ); $params['match'] = array( 'description' => __( 'Indicates whether all the conditions should be true for the resulting set, or if any one of them is sufficient. Match affects the following parameters: status_is, status_is_not, product_includes, product_excludes, coupon_includes, coupon_excludes, customer, categories', 'woocommerce' ), @@ -412,7 +249,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller { 'sanitize_callback' => 'wp_parse_id_list', ); - $params['product_excludes'] = array( + $params['product_excludes'] = array( 'description' => __( 'Limit result set to items that don\'t have the specified product(s) assigned.', 'woocommerce' ), 'type' => 'array', 'items' => array( @@ -421,7 +258,8 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller { 'default' => array(), 'sanitize_callback' => 'wp_parse_id_list', ); - $params['variation_includes'] = array( + // Split assignments for PHPCS complaining on aligned. + $params['variation_includes'] = array( 'description' => __( 'Limit result set to items that have the specified variation(s) assigned.', 'woocommerce' ), 'type' => 'array', 'items' => array( @@ -431,7 +269,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller { 'sanitize_callback' => 'wp_parse_id_list', 'validate_callback' => 'rest_validate_request_arg', ); - $params['variation_excludes'] = array( + $params['variation_excludes'] = array( 'description' => __( 'Limit result set to items that don\'t have the specified variation(s) assigned.', 'woocommerce' ), 'type' => 'array', 'items' => array( @@ -441,7 +279,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller { 'validate_callback' => 'rest_validate_request_arg', 'sanitize_callback' => 'wp_parse_id_list', ); - $params['coupon_includes'] = array( + $params['coupon_includes'] = array( 'description' => __( 'Limit result set to items that have the specified coupon(s) assigned.', 'woocommerce' ), 'type' => 'array', 'items' => array( @@ -450,7 +288,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller { 'default' => array(), 'sanitize_callback' => 'wp_parse_id_list', ); - $params['coupon_excludes'] = array( + $params['coupon_excludes'] = array( 'description' => __( 'Limit result set to items that don\'t have the specified coupon(s) assigned.', 'woocommerce' ), 'type' => 'array', 'items' => array( @@ -459,7 +297,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller { 'default' => array(), 'sanitize_callback' => 'wp_parse_id_list', ); - $params['tax_rate_includes'] = array( + $params['tax_rate_includes'] = array( 'description' => __( 'Limit result set to items that have the specified tax rate(s) assigned.', 'woocommerce' ), 'type' => 'array', 'items' => array( @@ -469,7 +307,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller { 'sanitize_callback' => 'wp_parse_id_list', 'validate_callback' => 'rest_validate_request_arg', ); - $params['tax_rate_excludes'] = array( + $params['tax_rate_excludes'] = array( 'description' => __( 'Limit result set to items that don\'t have the specified tax rate(s) assigned.', 'woocommerce' ), 'type' => 'array', 'items' => array( @@ -479,7 +317,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller { 'validate_callback' => 'rest_validate_request_arg', 'sanitize_callback' => 'wp_parse_id_list', ); - $params['customer'] = array( + $params['customer'] = array( 'description' => __( 'Alias for customer_type (deprecated).', 'woocommerce' ), 'type' => 'string', 'enum' => array( @@ -488,7 +326,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller { ), 'validate_callback' => 'rest_validate_request_arg', ); - $params['customer_type'] = array( + $params['customer_type'] = array( 'description' => __( 'Limit result set to orders that have the specified customer_type', 'woocommerce' ), 'type' => 'string', 'enum' => array( @@ -497,7 +335,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller { ), 'validate_callback' => 'rest_validate_request_arg', ); - $params['refunds'] = array( + $params['refunds'] = array( 'description' => __( 'Limit result set to specific types of refunds.', 'woocommerce' ), 'type' => 'string', 'default' => '', @@ -510,7 +348,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller { ), 'validate_callback' => 'rest_validate_request_arg', ); - $params['attribute_is'] = array( + $params['attribute_is'] = array( 'description' => __( 'Limit result set to orders that include products with the specified attributes.', 'woocommerce' ), 'type' => 'array', 'items' => array( @@ -519,7 +357,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller { 'default' => array(), 'validate_callback' => 'rest_validate_request_arg', ); - $params['attribute_is_not'] = array( + $params['attribute_is_not'] = array( 'description' => __( 'Limit result set to orders that don\'t include products with the specified attributes.', 'woocommerce' ), 'type' => 'array', 'items' => array( @@ -528,7 +366,7 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller { 'default' => array(), 'validate_callback' => 'rest_validate_request_arg', ); - $params['segmentby'] = array( + $params['segmentby'] = array( 'description' => __( 'Segment the response by additional constraint.', 'woocommerce' ), 'type' => 'string', 'enum' => array( @@ -540,21 +378,8 @@ class Controller extends \Automattic\WooCommerce\Admin\API\Reports\Controller { ), 'validate_callback' => 'rest_validate_request_arg', ); - $params['fields'] = array( - 'description' => __( 'Limit stats fields to the specified items.', 'woocommerce' ), - 'type' => 'array', - 'sanitize_callback' => 'wp_parse_slug_list', - 'validate_callback' => 'rest_validate_request_arg', - 'items' => array( - 'type' => 'string', - ), - ); - $params['force_cache_refresh'] = array( - 'description' => __( 'Force retrieval of fresh data instead of from the cache.', 'woocommerce' ), - 'type' => 'boolean', - 'sanitize_callback' => 'wp_validate_boolean', - 'validate_callback' => 'rest_validate_request_arg', - ); + unset( $params['intervals'] ); + unset( $params['fields'] ); return $params; } diff --git a/plugins/woocommerce/src/Admin/API/Reports/Orders/Stats/DataStore.php b/plugins/woocommerce/src/Admin/API/Reports/Orders/Stats/DataStore.php index e99e7bfd6bb..4652a2b3a16 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Orders/Stats/DataStore.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Orders/Stats/DataStore.php @@ -14,15 +14,19 @@ use Automattic\WooCommerce\Admin\API\Reports\SqlQuery; use Automattic\WooCommerce\Admin\API\Reports\Cache as ReportsCache; use Automattic\WooCommerce\Admin\API\Reports\Customers\DataStore as CustomersDataStore; use Automattic\WooCommerce\Utilities\OrderUtil; +use Automattic\WooCommerce\Admin\API\Reports\StatsDataStoreTrait; /** * API\Reports\Orders\Stats\DataStore. */ class DataStore extends ReportsDataStore implements DataStoreInterface { + use StatsDataStoreTrait; /** * Table used to get the data. * + * @override ReportsDataStore::$table_name + * * @var string */ protected static $table_name = 'wc_order_stats'; @@ -35,6 +39,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Cache identifier. * + * @override ReportsDataStore::$cache_key + * * @var string */ protected $cache_key = 'orders_stats'; @@ -42,6 +48,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Type for each column to cast values correctly later. * + * @override ReportsDataStore::$column_types + * * @var array */ protected $column_types = array( @@ -65,12 +73,16 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Data store context used to pass to filters. * + * @override ReportsDataStore::$context + * * @var string */ protected $context = 'orders_stats'; /** * Dynamically sets the date column name based on configuration + * + * @override ReportsDataStore::__construct() */ public function __construct() { $this->date_column_name = get_option( 'woocommerce_date_type', 'date_paid' ); @@ -79,10 +91,12 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Assign report columns once full table name has been assigned. + * + * @override ReportsDataStore::assign_report_columns() */ protected function assign_report_columns() { $table_name = self::get_db_table_name(); - // Avoid ambigious columns in SQL query. + // Avoid ambiguous columns in SQL query. $refunds = "ABS( SUM( CASE WHEN {$table_name}.net_total < 0 THEN {$table_name}.net_total ELSE 0 END ) )"; $gross_sales = "( SUM({$table_name}.total_sales)" . @@ -260,176 +274,161 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { } /** - * Returns the report data based on parameters supplied by the user. + * Get the default query arguments to be used by get_data(). + * These defaults are only partially applied when used via REST API, as that has its own defaults. * - * @param array $query_args Query parameters. - * @return stdClass|WP_Error Data. + * @override ReportsDataStore::get_default_query_vars() + * + * @return array Query parameters. */ - public function get_data( $query_args ) { + public function get_default_query_vars() { + $defaults = array_merge( + parent::get_default_query_vars(), + array( + 'interval' => 'week', + 'segmentby' => '', + + 'match' => 'all', + 'status_is' => array(), + 'status_is_not' => array(), + 'product_includes' => array(), + 'product_excludes' => array(), + 'coupon_includes' => array(), + 'coupon_excludes' => array(), + 'tax_rate_includes' => array(), + 'tax_rate_excludes' => array(), + 'customer_type' => '', + 'category_includes' => array(), + ) + ); + + return $defaults; + } + + /** + * Returns the report data based on normalized parameters. + * Will be called by `get_data` if there is no data in cache. + * + * @override ReportsDataStore::get_noncached_stats_data() + * + * @see get_data + * @see get_noncached_stats_data + * @param array $query_args Query parameters. + * @param array $params Query limit parameters. + * @param stdClass $data Reference to the data object to fill. + * @param int $expected_interval_count Number of expected intervals. + * @return stdClass|WP_Error Data object `{ totals: *, intervals: array, total: int, pages: int, page_no: int }`, or error. + */ + public function get_noncached_stats_data( $query_args, $params, &$data, $expected_interval_count ) { global $wpdb; $table_name = self::get_db_table_name(); - // These defaults are only applied when not using REST API, as the API has its own defaults that overwrite these for most values (except before, after, etc). - $defaults = array( - 'per_page' => get_option( 'posts_per_page' ), - 'page' => 1, - 'order' => 'DESC', - 'orderby' => 'date', - 'before' => TimeInterval::default_before(), - 'after' => TimeInterval::default_after(), - 'interval' => 'week', - 'fields' => '*', - 'segmentby' => '', - - 'match' => 'all', - 'status_is' => array(), - 'status_is_not' => array(), - 'product_includes' => array(), - 'product_excludes' => array(), - 'coupon_includes' => array(), - 'coupon_excludes' => array(), - 'tax_rate_includes' => array(), - 'tax_rate_excludes' => array(), - 'customer_type' => '', - 'category_includes' => array(), - ); - $query_args = wp_parse_args( $query_args, $defaults ); - $this->normalize_timezones( $query_args, $defaults ); - - /* - * We need to get the cache key here because - * parent::update_intervals_sql_params() modifies $query_args. - */ - $cache_key = $this->get_cache_key( $query_args ); - $data = $this->get_cached_data( $cache_key ); - if ( isset( $query_args['date_type'] ) ) { $this->date_column_name = $query_args['date_type']; } - if ( false === $data ) { - $this->initialize_queries(); + $this->initialize_queries(); - $data = (object) array( - 'totals' => (object) array(), - 'intervals' => (object) array(), - 'total' => 0, - 'pages' => 0, - 'page_no' => 0, - ); + $selections = $this->selected_columns( $query_args ); + $this->add_time_period_sql_params( $query_args, $table_name ); + $this->add_intervals_sql_params( $query_args, $table_name ); + $this->add_order_by_sql_params( $query_args ); + $where_time = $this->get_sql_clause( 'where_time' ); + $params = $this->get_limit_sql_params( $query_args ); + $coupon_join = "LEFT JOIN ( + SELECT + order_id, + SUM(discount_amount) AS discount_amount, + COUNT(DISTINCT coupon_id) AS coupons_count + FROM + {$wpdb->prefix}wc_order_coupon_lookup + GROUP BY + order_id + ) order_coupon_lookup + ON order_coupon_lookup.order_id = {$wpdb->prefix}wc_order_stats.order_id"; - $selections = $this->selected_columns( $query_args ); - $this->add_time_period_sql_params( $query_args, $table_name ); - $this->add_intervals_sql_params( $query_args, $table_name ); - $this->add_order_by_sql_params( $query_args ); - $where_time = $this->get_sql_clause( 'where_time' ); - $params = $this->get_limit_sql_params( $query_args ); - $coupon_join = "LEFT JOIN ( - SELECT - order_id, - SUM(discount_amount) AS discount_amount, - COUNT(DISTINCT coupon_id) AS coupons_count - FROM - {$wpdb->prefix}wc_order_coupon_lookup - GROUP BY - order_id - ) order_coupon_lookup - ON order_coupon_lookup.order_id = {$wpdb->prefix}wc_order_stats.order_id"; - - // Additional filtering for Orders report. - $this->orders_stats_sql_filter( $query_args ); - $this->total_query->add_sql_clause( 'select', $selections ); - $this->total_query->add_sql_clause( 'left_join', $coupon_join ); - $this->total_query->add_sql_clause( 'where_time', $where_time ); - $totals = $wpdb->get_results( - $this->total_query->get_query_statement(), - ARRAY_A - ); // phpcs:ignore cache ok, DB call ok, unprepared SQL ok. - if ( null === $totals ) { - return new \WP_Error( 'woocommerce_analytics_revenue_result_failed', __( 'Sorry, fetching revenue data failed.', 'woocommerce' ) ); - } - - // phpcs:ignore Generic.Commenting.Todo.TaskFound - // @todo Remove these assignements when refactoring segmenter classes to use query objects. - $totals_query = array( - 'from_clause' => $this->total_query->get_sql_clause( 'join' ), - 'where_time_clause' => $where_time, - 'where_clause' => $this->total_query->get_sql_clause( 'where' ), - ); - $intervals_query = array( - 'select_clause' => $this->get_sql_clause( 'select' ), - 'from_clause' => $this->interval_query->get_sql_clause( 'join' ), - 'where_time_clause' => $where_time, - 'where_clause' => $this->interval_query->get_sql_clause( 'where' ), - 'limit' => $this->get_sql_clause( 'limit' ), - ); - - $unique_products = $this->get_unique_product_count( $totals_query['from_clause'], $totals_query['where_time_clause'], $totals_query['where_clause'] ); - $totals[0]['products'] = $unique_products; - $segmenter = new Segmenter( $query_args, $this->report_columns ); - $unique_coupons = $this->get_unique_coupon_count( $totals_query['from_clause'], $totals_query['where_time_clause'], $totals_query['where_clause'] ); - $totals[0]['coupons_count'] = $unique_coupons; - $totals[0]['segments'] = $segmenter->get_totals_segments( $totals_query, $table_name ); - $totals = (object) $this->cast_numbers( $totals[0] ); - - $this->interval_query->add_sql_clause( 'select', $this->get_sql_clause( 'select' ) . ' AS time_interval' ); - $this->interval_query->add_sql_clause( 'left_join', $coupon_join ); - $this->interval_query->add_sql_clause( 'where_time', $where_time ); - $db_intervals = $wpdb->get_col( - $this->interval_query->get_query_statement() - ); // phpcs:ignore cache ok, DB call ok, , unprepared SQL ok. - - $db_interval_count = count( $db_intervals ); - $expected_interval_count = TimeInterval::intervals_between( $query_args['after'], $query_args['before'], $query_args['interval'] ); - $total_pages = (int) ceil( $expected_interval_count / $params['per_page'] ); - - if ( $query_args['page'] < 1 || $query_args['page'] > $total_pages ) { - return $data; - } - - $this->update_intervals_sql_params( $query_args, $db_interval_count, $expected_interval_count, $table_name ); - $this->interval_query->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); - $this->interval_query->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) ); - $this->interval_query->add_sql_clause( 'select', ", MAX({$table_name}.date_created) AS datetime_anchor" ); - if ( '' !== $selections ) { - $this->interval_query->add_sql_clause( 'select', ', ' . $selections ); - } - $intervals = $wpdb->get_results( - $this->interval_query->get_query_statement(), - ARRAY_A - ); // phpcs:ignore cache ok, DB call ok, unprepared SQL ok. - - if ( null === $intervals ) { - return new \WP_Error( 'woocommerce_analytics_revenue_result_failed', __( 'Sorry, fetching revenue data failed.', 'woocommerce' ) ); - } - - if ( isset( $intervals[0] ) ) { - $unique_coupons = $this->get_unique_coupon_count( $intervals_query['from_clause'], $intervals_query['where_time_clause'], $intervals_query['where_clause'], true ); - $intervals[0]['coupons_count'] = $unique_coupons; - } - - $data = (object) array( - 'totals' => $totals, - 'intervals' => $intervals, - 'total' => $expected_interval_count, - 'pages' => $total_pages, - 'page_no' => (int) $query_args['page'], - ); - - if ( TimeInterval::intervals_missing( $expected_interval_count, $db_interval_count, $params['per_page'], $query_args['page'], $query_args['order'], $query_args['orderby'], count( $intervals ) ) ) { - $this->fill_in_missing_intervals( $db_intervals, $query_args['adj_after'], $query_args['adj_before'], $query_args['interval'], $data ); - $this->sort_intervals( $data, $query_args['orderby'], $query_args['order'] ); - $this->remove_extra_records( $data, $query_args['page'], $params['per_page'], $db_interval_count, $expected_interval_count, $query_args['orderby'], $query_args['order'] ); - } else { - $this->update_interval_boundary_dates( $query_args['after'], $query_args['before'], $query_args['interval'], $data->intervals ); - } - $segmenter->add_intervals_segments( $data, $intervals_query, $table_name ); - $this->create_interval_subtotals( $data->intervals ); - - $this->set_cached_data( $cache_key, $data ); + // Additional filtering for Orders report. + $this->orders_stats_sql_filter( $query_args ); + $this->total_query->add_sql_clause( 'select', $selections ); + $this->total_query->add_sql_clause( 'left_join', $coupon_join ); + $this->total_query->add_sql_clause( 'where_time', $where_time ); + $totals = $wpdb->get_results( + // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- cache ok, DB call ok, unprepared SQL ok. + $this->total_query->get_query_statement(), + ARRAY_A + ); + if ( null === $totals ) { + return new \WP_Error( 'woocommerce_analytics_revenue_result_failed', __( 'Sorry, fetching revenue data failed.', 'woocommerce' ) ); } + // phpcs:ignore Generic.Commenting.Todo.TaskFound + // @todo Remove these assignements when refactoring segmenter classes to use query objects. + $totals_query = array( + 'from_clause' => $this->total_query->get_sql_clause( 'join' ), + 'where_time_clause' => $where_time, + 'where_clause' => $this->total_query->get_sql_clause( 'where' ), + ); + $intervals_query = array( + 'select_clause' => $this->get_sql_clause( 'select' ), + 'from_clause' => $this->interval_query->get_sql_clause( 'join' ), + 'where_time_clause' => $where_time, + 'where_clause' => $this->interval_query->get_sql_clause( 'where' ), + 'limit' => $this->get_sql_clause( 'limit' ), + ); + + $unique_products = $this->get_unique_product_count( $totals_query['from_clause'], $totals_query['where_time_clause'], $totals_query['where_clause'] ); + $totals[0]['products'] = $unique_products; + $segmenter = new Segmenter( $query_args, $this->report_columns ); + $unique_coupons = $this->get_unique_coupon_count( $totals_query['from_clause'], $totals_query['where_time_clause'], $totals_query['where_clause'] ); + $totals[0]['coupons_count'] = $unique_coupons; + $totals[0]['segments'] = $segmenter->get_totals_segments( $totals_query, $table_name ); + $totals = (object) $this->cast_numbers( $totals[0] ); + + $this->interval_query->add_sql_clause( 'select', $this->get_sql_clause( 'select' ) . ' AS time_interval' ); + $this->interval_query->add_sql_clause( 'left_join', $coupon_join ); + $this->interval_query->add_sql_clause( 'where_time', $where_time ); + $db_intervals = $wpdb->get_col( + // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- cache ok, DB call ok, , unprepared SQL ok. + $this->interval_query->get_query_statement() + ); + + $db_interval_count = count( $db_intervals ); + + $this->update_intervals_sql_params( $query_args, $db_interval_count, $expected_interval_count, $table_name ); + $this->interval_query->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); + $this->interval_query->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) ); + $this->interval_query->add_sql_clause( 'select', ", MAX({$table_name}.date_created) AS datetime_anchor" ); + if ( '' !== $selections ) { + $this->interval_query->add_sql_clause( 'select', ', ' . $selections ); + } + $intervals = $wpdb->get_results( + // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- cache ok, DB call ok, , unprepared SQL ok. + $this->interval_query->get_query_statement(), + ARRAY_A + ); + + if ( null === $intervals ) { + return new \WP_Error( 'woocommerce_analytics_revenue_result_failed', __( 'Sorry, fetching revenue data failed.', 'woocommerce' ) ); + } + + if ( isset( $intervals[0] ) ) { + $unique_coupons = $this->get_unique_coupon_count( $intervals_query['from_clause'], $intervals_query['where_time_clause'], $intervals_query['where_clause'], true ); + $intervals[0]['coupons_count'] = $unique_coupons; + } + + $data->totals = $totals; + $data->intervals = $intervals; + + if ( TimeInterval::intervals_missing( $expected_interval_count, $db_interval_count, $params['per_page'], $query_args['page'], $query_args['order'], $query_args['orderby'], count( $intervals ) ) ) { + $this->fill_in_missing_intervals( $db_intervals, $query_args['adj_after'], $query_args['adj_before'], $query_args['interval'], $data ); + $this->sort_intervals( $data, $query_args['orderby'], $query_args['order'] ); + $this->remove_extra_records( $data, $query_args['page'], $params['per_page'], $db_interval_count, $expected_interval_count, $query_args['orderby'], $query_args['order'] ); + } else { + $this->update_interval_boundary_dates( $query_args['after'], $query_args['before'], $query_args['interval'], $data->intervals ); + } + $segmenter->add_intervals_segments( $data, $intervals_query, $table_name ); + return $data; } @@ -729,18 +728,4 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { ) ); } - - /** - * Initialize query objects. - */ - protected function initialize_queries() { - $this->clear_all_clauses(); - unset( $this->subquery ); - $this->total_query = new SqlQuery( $this->context . '_total' ); - $this->total_query->add_sql_clause( 'from', self::get_db_table_name() ); - - $this->interval_query = new SqlQuery( $this->context . '_interval' ); - $this->interval_query->add_sql_clause( 'from', self::get_db_table_name() ); - $this->interval_query->add_sql_clause( 'group_by', 'time_interval' ); - } } diff --git a/plugins/woocommerce/src/Admin/API/Reports/Orders/Stats/Query.php b/plugins/woocommerce/src/Admin/API/Reports/Orders/Stats/Query.php index 70311f89ec6..dd8a51343d4 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Orders/Stats/Query.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Orders/Stats/Query.php @@ -17,14 +17,23 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Orders\Stats; -defined( 'ABSPATH' ) || exit; +use Automattic\WooCommerce\Admin\API\Reports\GenericQuery; -use Automattic\WooCommerce\Admin\API\Reports\Query as ReportsQuery; +defined( 'ABSPATH' ) || exit; /** * API\Reports\Orders\Stats\Query */ -class Query extends ReportsQuery { +class Query extends GenericQuery { + + /** + * Specific query name. + * Will be used to load the `report-{name}` data store, + * and to call `woocommerce_analytics_{snake_case(name)}_*` filters. + * + * @var string + */ + protected $name = 'orders-stats'; /** * Valid fields for Orders report. @@ -45,17 +54,4 @@ class Query extends ReportsQuery { ), ); } - - /** - * Get revenue data based on the current query vars. - * - * @return array - */ - public function get_data() { - $args = apply_filters( 'woocommerce_analytics_orders_stats_query_args', $this->get_query_vars() ); - - $data_store = \WC_Data_Store::load( 'report-orders-stats' ); - $results = $data_store->get_data( $args ); - return apply_filters( 'woocommerce_analytics_orders_stats_select_query', $results, $args ); - } } diff --git a/plugins/woocommerce/src/Admin/API/Reports/PerformanceIndicators/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/PerformanceIndicators/Controller.php index bc5e5cb2f9c..564cf206b6a 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/PerformanceIndicators/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/PerformanceIndicators/Controller.php @@ -452,10 +452,10 @@ class Controller extends GenericController { } /** - * Prepare a report object for serialization. + * Prepare a report data item for serialization. * - * @param array $stat_data Report data. - * @param WP_REST_Request $request Request object. + * @param array $stat_data Report data item as returned from Data Store. + * @param WP_REST_Request $request Request object. * @return WP_REST_Response */ public function prepare_item_for_response( $stat_data, $request ) { @@ -478,7 +478,7 @@ class Controller extends GenericController { /** * Prepare links for the request. * - * @param \Automattic\WooCommerce\Admin\API\Reports\Query $object Object data. + * @param object $object data. * @return array */ protected function prepare_links( $object ) { diff --git a/plugins/woocommerce/src/Admin/API/Reports/Products/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Products/Controller.php index c6cc45feddd..60df83d6800 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Products/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Products/Controller.php @@ -9,8 +9,9 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Products; defined( 'ABSPATH' ) || exit; -use Automattic\WooCommerce\Admin\API\Reports\GenericController; use Automattic\WooCommerce\Admin\API\Reports\ExportableInterface; +use Automattic\WooCommerce\Admin\API\Reports\GenericController; +use Automattic\WooCommerce\Admin\API\Reports\GenericQuery; use WP_REST_Request; use WP_REST_Response; @@ -41,51 +42,22 @@ class Controller extends GenericController implements ExportableInterface { ); /** - * Get items. + * Get data from `'products'` GenericQuery. * - * @param WP_REST_Request $request Request data. + * @override GenericController::get_datastore_data() * - * @return array|WP_Error + * @param array $query_args Query arguments. + * @return mixed Results from the data store. */ - public function get_items( $request ) { - $args = array(); - $registered = array_keys( $this->get_collection_params() ); - foreach ( $registered as $param_name ) { - if ( isset( $request[ $param_name ] ) ) { - if ( isset( $this->param_mapping[ $param_name ] ) ) { - $args[ $this->param_mapping[ $param_name ] ] = $request[ $param_name ]; - } else { - $args[ $param_name ] = $request[ $param_name ]; - } - } - } - - $reports = new Query( $args ); - $products_data = $reports->get_data(); - - $data = array(); - - foreach ( $products_data->data as $product_data ) { - $item = $this->prepare_item_for_response( $product_data, $request ); - if ( isset( $item->data['extended_info']['name'] ) ) { - $item->data['extended_info']['name'] = wp_strip_all_tags( $item->data['extended_info']['name'] ); - } - $data[] = $this->prepare_response_for_collection( $item ); - } - - return $this->add_pagination_headers( - $request, - $data, - (int) $products_data->total, - (int) $products_data->page_no, - (int) $products_data->pages - ); + protected function get_datastore_data( $query_args = array() ) { + $query = new GenericQuery( $query_args, 'products' ); + return $query->get_data(); } /** - * Prepare a report object for serialization. + * Prepare a report data item for serialization. * - * @param Array $report Report data. + * @param Array $report Report data item as returned from Data Store. * @param WP_REST_Request $request Request object. * @return WP_REST_Response */ @@ -101,8 +73,36 @@ class Controller extends GenericController implements ExportableInterface { * @param WP_REST_Response $response The response object. * @param object $report The original report object. * @param WP_REST_Request $request Request used to generate the response. + * + * @since 6.5.0 */ - return apply_filters( 'woocommerce_rest_prepare_report_products', $response, $report, $request ); + $filtered_response = apply_filters( 'woocommerce_rest_prepare_report_products', $response, $report, $request ); + if ( isset( $filtered_response->data['extended_info']['name'] ) ) { + $filtered_response->data['extended_info']['name'] = wp_strip_all_tags( $filtered_response->data['extended_info']['name'] ); + } + return $filtered_response; + } + + + /** + * Maps query arguments from the REST request. + * + * @param array $request Request array. + * @return array + */ + protected function prepare_reports_query( $request ) { + $args = array(); + $registered = array_keys( $this->get_collection_params() ); + foreach ( $registered as $param_name ) { + if ( isset( $request[ $param_name ] ) ) { + if ( isset( $this->param_mapping[ $param_name ] ) ) { + $args[ $this->param_mapping[ $param_name ] ] = $request[ $param_name ]; + } else { + $args[ $param_name ] = $request[ $param_name ]; + } + } + } + return $args; } /** diff --git a/plugins/woocommerce/src/Admin/API/Reports/Products/DataStore.php b/plugins/woocommerce/src/Admin/API/Reports/Products/DataStore.php index 31ba4954873..fee11df494b 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Products/DataStore.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Products/DataStore.php @@ -21,6 +21,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Table used to get the data. * + * @override ReportsDataStore::$table_name + * * @var string */ protected static $table_name = 'wc_order_product_lookup'; @@ -28,6 +30,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Cache identifier. * + * @override ReportsDataStore::$cache_key + * * @var string */ protected $cache_key = 'products'; @@ -35,6 +39,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Mapping columns to data type to return correct response types. * + * @override ReportsDataStore::$column_types + * * @var array */ protected $column_types = array( @@ -79,12 +85,16 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Data store context used to pass to filters. * + * @override ReportsDataStore::$context + * * @var string */ protected $context = 'products'; /** * Assign report columns once full table name has been assigned. + * + * @override ReportsDataStore::assign_report_columns() */ protected function assign_report_columns() { $table_name = self::get_db_table_name(); @@ -175,6 +185,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Maps ordering specified by the user to columns in the database/fields in the data. * + * @override ReportsDataStore::normalize_order_by() + * * @param string $order_by Sorting criterion. * @return string */ @@ -256,122 +268,137 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Returns the report data based on parameters supplied by the user. * + * @override ReportsDataStore::get_data() + * * @param array $query_args Query parameters. * @return stdClass|WP_Error Data. */ public function get_data( $query_args ) { + $data = parent::get_data( $query_args ); + + /* + * Do not cache extended info -- this is required to get the latest stock data. + * `include_extended_info` checks only `extended_info` key, + * so we don't need to bother about normalizing timestamps. + */ + $defaults = $this->get_default_query_vars(); + $query_args = wp_parse_args( $query_args, $defaults ); + $this->include_extended_info( $data->data, $query_args ); + + return $data; + } + + /** + * Get the default query arguments to be used by get_data(). + * These defaults are only partially applied when used via REST API, as that has its own defaults. + * + * @override ReportsDataStore::get_default_query_vars() + * + * @return array Query parameters. + */ + public function get_default_query_vars() { + $defaults = parent::get_default_query_vars(); + $defaults['category_includes'] = array(); + $defaults['product_includes'] = array(); + $defaults['extended_info'] = false; + + return $defaults; + } + + /** + * Returns the report data based on normalized parameters. + * Will be called by `get_data` if there is no data in cache. + * + * @override ReportsDataStore::get_noncached_data() + * + * @see get_data + * @param array $query_args Query parameters. + * @return stdClass|WP_Error Data object `{ totals: *, intervals: array, total: int, pages: int, page_no: int }`, or error. + */ + public function get_noncached_data( $query_args ) { global $wpdb; $table_name = self::get_db_table_name(); - // These defaults are only partially applied when used via REST API, as that has its own defaults. - $defaults = array( - 'per_page' => get_option( 'posts_per_page' ), - 'page' => 1, - 'order' => 'DESC', - 'orderby' => 'date', - 'before' => TimeInterval::default_before(), - 'after' => TimeInterval::default_after(), - 'fields' => '*', - 'category_includes' => array(), - 'product_includes' => array(), - 'extended_info' => false, + $this->initialize_queries(); + + $data = (object) array( + 'data' => array(), + 'total' => 0, + 'pages' => 0, + 'page_no' => 0, ); - $query_args = wp_parse_args( $query_args, $defaults ); - $this->normalize_timezones( $query_args, $defaults ); - /* - * We need to get the cache key here because - * parent::update_intervals_sql_params() modifies $query_args. - */ - $cache_key = $this->get_cache_key( $query_args ); - $data = $this->get_cached_data( $cache_key ); + $selections = $this->selected_columns( $query_args ); + $included_products = $this->get_included_products_array( $query_args ); + $params = $this->get_limit_params( $query_args ); + $this->add_sql_query_params( $query_args ); - if ( false === $data ) { - $this->initialize_queries(); + if ( count( $included_products ) > 0 ) { + $filtered_products = array_diff( $included_products, array( '-1' ) ); + $total_results = count( $filtered_products ); + $total_pages = (int) ceil( $total_results / $params['per_page'] ); - $data = (object) array( - 'data' => array(), - 'total' => 0, - 'pages' => 0, - 'page_no' => 0, - ); - - $selections = $this->selected_columns( $query_args ); - $included_products = $this->get_included_products_array( $query_args ); - $params = $this->get_limit_params( $query_args ); - $this->add_sql_query_params( $query_args ); - - if ( count( $included_products ) > 0 ) { - $filtered_products = array_diff( $included_products, array( '-1' ) ); - $total_results = count( $filtered_products ); - $total_pages = (int) ceil( $total_results / $params['per_page'] ); - - if ( 'date' === $query_args['orderby'] ) { - $selections .= ", {$table_name}.date_created"; - } - - $fields = $this->get_fields( $query_args ); - $join_selections = $this->format_join_selections( $fields, array( 'product_id' ) ); - $ids_table = $this->get_ids_table( $included_products, 'product_id' ); - - $this->subquery->clear_sql_clause( 'select' ); - $this->subquery->add_sql_clause( 'select', $selections ); - $this->add_sql_clause( 'select', $join_selections ); - $this->add_sql_clause( 'from', '(' ); - $this->add_sql_clause( 'from', $this->subquery->get_query_statement() ); - $this->add_sql_clause( 'from', ") AS {$table_name}" ); - $this->add_sql_clause( - 'right_join', - "RIGHT JOIN ( {$ids_table} ) AS default_results - ON default_results.product_id = {$table_name}.product_id" - ); - $this->add_sql_clause( 'where', 'AND default_results.product_id != -1' ); - - $products_query = $this->get_query_statement(); - } else { - $count_query = "SELECT COUNT(*) FROM ( - {$this->subquery->get_query_statement()} - ) AS tt"; - $db_records_count = (int) $wpdb->get_var( - $count_query // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared - ); - - $total_results = $db_records_count; - $total_pages = (int) ceil( $db_records_count / $params['per_page'] ); - - if ( ( $query_args['page'] < 1 || $query_args['page'] > $total_pages ) ) { - return $data; - } - - $this->subquery->clear_sql_clause( 'select' ); - $this->subquery->add_sql_clause( 'select', $selections ); - $this->subquery->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); - $this->subquery->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) ); - $products_query = $this->subquery->get_query_statement(); + if ( 'date' === $query_args['orderby'] ) { + $selections .= ", {$table_name}.date_created"; } - $product_data = $wpdb->get_results( - $products_query, // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared - ARRAY_A + $fields = $this->get_fields( $query_args ); + $join_selections = $this->format_join_selections( $fields, array( 'product_id' ) ); + $ids_table = $this->get_ids_table( $included_products, 'product_id' ); + + $this->subquery->clear_sql_clause( 'select' ); + $this->subquery->add_sql_clause( 'select', $selections ); + $this->add_sql_clause( 'select', $join_selections ); + $this->add_sql_clause( 'from', '(' ); + $this->add_sql_clause( 'from', $this->subquery->get_query_statement() ); + $this->add_sql_clause( 'from', ") AS {$table_name}" ); + $this->add_sql_clause( + 'right_join', + "RIGHT JOIN ( {$ids_table} ) AS default_results + ON default_results.product_id = {$table_name}.product_id" + ); + $this->add_sql_clause( 'where', 'AND default_results.product_id != -1' ); + + $products_query = $this->get_query_statement(); + } else { + $count_query = "SELECT COUNT(*) FROM ( + {$this->subquery->get_query_statement()} + ) AS tt"; + $db_records_count = (int) $wpdb->get_var( + $count_query // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared ); - if ( null === $product_data ) { + $total_results = $db_records_count; + $total_pages = (int) ceil( $db_records_count / $params['per_page'] ); + + if ( ( $query_args['page'] < 1 || $query_args['page'] > $total_pages ) ) { return $data; } - $product_data = array_map( array( $this, 'cast_numbers' ), $product_data ); - $data = (object) array( - 'data' => $product_data, - 'total' => $total_results, - 'pages' => $total_pages, - 'page_no' => (int) $query_args['page'], - ); - - $this->set_cached_data( $cache_key, $data ); + $this->subquery->clear_sql_clause( 'select' ); + $this->subquery->add_sql_clause( 'select', $selections ); + $this->subquery->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); + $this->subquery->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) ); + $products_query = $this->subquery->get_query_statement(); } - $this->include_extended_info( $data->data, $query_args ); + $product_data = $wpdb->get_results( + $products_query, // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + ARRAY_A + ); + + if ( null === $product_data ) { + return $data; + } + + $product_data = array_map( array( $this, 'cast_numbers' ), $product_data ); + $data = (object) array( + 'data' => $product_data, + 'total' => $total_results, + 'pages' => $total_pages, + 'page_no' => (int) $query_args['page'], + ); return $data; } diff --git a/plugins/woocommerce/src/Admin/API/Reports/Products/Query.php b/plugins/woocommerce/src/Admin/API/Reports/Products/Query.php index 63810eff234..e7bbed3d199 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Products/Query.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Products/Query.php @@ -22,24 +22,34 @@ use Automattic\WooCommerce\Admin\API\Reports\Query as ReportsQuery; /** * API\Reports\Products\Query + * + * @deprecated 9.3.0 Products\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. */ class Query extends ReportsQuery { /** * Valid fields for Products report. * + * @deprecated 9.3.0 Products\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. + * * @return array */ protected function get_default_query_vars() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + return array(); } /** * Get product data based on the current query vars. * + * @deprecated 9.3.0 Products\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. + * * @return array */ public function get_data() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + $args = apply_filters( 'woocommerce_analytics_products_query_args', $this->get_query_vars() ); $data_store = \WC_Data_Store::load( 'report-products' ); diff --git a/plugins/woocommerce/src/Admin/API/Reports/Products/Stats/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Products/Stats/Controller.php index bfb24195543..acf047a37f9 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Products/Stats/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Products/Stats/Controller.php @@ -9,8 +9,8 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Products\Stats; defined( 'ABSPATH' ) || exit; +use Automattic\WooCommerce\Admin\API\Reports\GenericQuery; use Automattic\WooCommerce\Admin\API\Reports\GenericStatsController; -use Automattic\WooCommerce\Admin\API\Reports\ParameterException; use WP_REST_Request; use WP_REST_Response; @@ -48,12 +48,25 @@ class Controller extends GenericStatsController { } /** - * Get all reports. + * Get data from `'products-stats'` GenericQuery. * - * @param WP_REST_Request $request Request data. - * @return array|WP_Error + * @override GenericController::get_datastore_data() + * + * @param array $query_args Query arguments. + * @return mixed Results from the data store. */ - public function get_items( $request ) { + protected function get_datastore_data( $query_args = array() ) { + $query = new GenericQuery( $query_args, 'products-stats' ); + return $query->get_data(); + } + + /** + * Maps query arguments from the REST request to be used to query the datastore. + * + * @param \WP_REST_Request $request Full request object. + * @return array Simplified array of params. + */ + protected function prepare_reports_query( $request ) { $query_args = array( 'fields' => array( 'items_sold', @@ -75,36 +88,13 @@ class Controller extends GenericStatsController { } } - $query = new Query( $query_args ); - try { - $report_data = $query->get_data(); - } catch ( ParameterException $e ) { - return new \WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); - } - - $out_data = array( - 'totals' => get_object_vars( $report_data->totals ), - 'intervals' => array(), - ); - - foreach ( $report_data->intervals as $interval_data ) { - $item = $this->prepare_item_for_response( $interval_data, $request ); - $out_data['intervals'][] = $this->prepare_response_for_collection( $item ); - } - - return $this->add_pagination_headers( - $request, - $out_data, - (int) $report_data->total, - (int) $report_data->page_no, - (int) $report_data->pages - ); + return $query_args; } /** - * Prepare a report object for serialization. + * Prepare a report data item for serialization. * - * @param array $report Report data. + * @param array $report Report data item as returned from Data Store. * @param WP_REST_Request $request Request object. * @return WP_REST_Response */ @@ -255,15 +245,6 @@ class Controller extends GenericStatsController { ), 'validate_callback' => 'rest_validate_request_arg', ); - $params['fields'] = array( - 'description' => __( 'Limit stats fields to the specified items.', 'woocommerce' ), - 'type' => 'array', - 'sanitize_callback' => 'wp_parse_slug_list', - 'validate_callback' => 'rest_validate_request_arg', - 'items' => array( - 'type' => 'string', - ), - ); return $params; } diff --git a/plugins/woocommerce/src/Admin/API/Reports/Products/Stats/DataStore.php b/plugins/woocommerce/src/Admin/API/Reports/Products/Stats/DataStore.php index c1e75d09a7a..23fe869dbfb 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Products/Stats/DataStore.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Products/Stats/DataStore.php @@ -8,18 +8,22 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Products\Stats; defined( 'ABSPATH' ) || exit; use Automattic\WooCommerce\Admin\API\Reports\Products\DataStore as ProductsDataStore; +use Automattic\WooCommerce\Admin\API\Reports\DataStore as ReportsDataStore; use Automattic\WooCommerce\Admin\API\Reports\DataStoreInterface; use Automattic\WooCommerce\Admin\API\Reports\TimeInterval; -use Automattic\WooCommerce\Admin\API\Reports\SqlQuery; +use Automattic\WooCommerce\Admin\API\Reports\StatsDataStoreTrait; /** * API\Reports\Products\Stats\DataStore. */ class DataStore extends ProductsDataStore implements DataStoreInterface { + use StatsDataStoreTrait; /** * Mapping columns to data type to return correct response types. * + * @override ProductsDataStore::$column_types + * * @var array */ protected $column_types = array( @@ -36,6 +40,8 @@ class DataStore extends ProductsDataStore implements DataStoreInterface { /** * Cache identifier. * + * @override ProductsDataStore::$cache_key + * * @var string */ protected $cache_key = 'products_stats'; @@ -43,12 +49,16 @@ class DataStore extends ProductsDataStore implements DataStoreInterface { /** * Data store context used to pass to filters. * + * @override ProductsDataStore::$context + * * @var string */ protected $context = 'products_stats'; /** * Assign report columns once full table name has been assigned. + * + * @override ProductsDataStore::assign_report_columns() */ protected function assign_report_columns() { $table_name = self::get_db_table_name(); @@ -99,138 +109,141 @@ class DataStore extends ProductsDataStore implements DataStoreInterface { $this->interval_query->add_sql_clause( 'select', $this->get_sql_clause( 'select' ) . ' AS time_interval' ); } + /** + * Get the default query arguments to be used by get_data(). + * These defaults are only partially applied when used via REST API, as that has its own defaults. + * + * @override ProductsDataStore::get_default_query_vars() + * + * @return array Query parameters. + */ + public function get_default_query_vars() { + $defaults = parent::get_default_query_vars(); + $defaults['interval'] = 'week'; + unset( $defaults['extended_info'] ); + + return $defaults; + } + /** * Returns the report data based on parameters supplied by the user. * - * @since 3.5.0 + * @override ProductsDataStore::get_data() + * * @param array $query_args Query parameters. * @return stdClass|WP_Error Data. */ public function get_data( $query_args ) { + // Do not include extended info like `ProductsDataStore` does. + return ReportsDataStore::get_data( $query_args ); + } + + /** + * Returns the report data based on normalized parameters. + * Will be called by `get_data` if there is no data in cache. + * + * @override ProductsDataStore::get_noncached_data() + * + * @see get_data + * @see get_noncached_stats_data + * @param array $query_args Query parameters. + * @param array $params Query limit parameters. + * @param stdClass $data Reference to the data object to fill. + * @param int $expected_interval_count Number of expected intervals. + * @return stdClass|WP_Error Data object `{ totals: *, intervals: array, total: int, pages: int, page_no: int }`, or error. + */ + public function get_noncached_stats_data( $query_args, $params, &$data, $expected_interval_count ) { global $wpdb; $table_name = self::get_db_table_name(); - // These defaults are only partially applied when used via REST API, as that has its own defaults. - $defaults = array( - 'per_page' => get_option( 'posts_per_page' ), - 'page' => 1, - 'order' => 'DESC', - 'orderby' => 'date', - 'before' => TimeInterval::default_before(), - 'after' => TimeInterval::default_after(), - 'fields' => '*', - 'category_includes' => array(), - 'interval' => 'week', - 'product_includes' => array(), + $this->initialize_queries(); + + $selections = $this->selected_columns( $query_args ); + + $this->update_sql_query_params( $query_args ); + $this->get_limit_sql_params( $query_args ); + $this->interval_query->add_sql_clause( 'where_time', $this->get_sql_clause( 'where_time' ) ); + + $db_intervals = $wpdb->get_col( + // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- cache ok, DB call ok, unprepared SQL ok. + $this->interval_query->get_query_statement() ); - $query_args = wp_parse_args( $query_args, $defaults ); - $this->normalize_timezones( $query_args, $defaults ); - /* - * We need to get the cache key here because - * parent::update_intervals_sql_params() modifies $query_args. - */ - $cache_key = $this->get_cache_key( $query_args ); - $data = $this->get_cached_data( $cache_key ); + $db_interval_count = count( $db_intervals ); - if ( false === $data ) { - $this->initialize_queries(); + $intervals = array(); + $this->update_intervals_sql_params( $query_args, $db_interval_count, $expected_interval_count, $table_name ); + $this->total_query->add_sql_clause( 'select', $selections ); + $this->total_query->add_sql_clause( 'where_time', $this->get_sql_clause( 'where_time' ) ); - $selections = $this->selected_columns( $query_args ); - $params = $this->get_limit_params( $query_args ); + $totals = $wpdb->get_results( + // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- cache ok, DB call ok, unprepared SQL ok. + $this->total_query->get_query_statement(), + ARRAY_A + ); - $this->update_sql_query_params( $query_args ); - $this->get_limit_sql_params( $query_args ); - $this->interval_query->add_sql_clause( 'where_time', $this->get_sql_clause( 'where_time' ) ); + // phpcs:ignore Generic.Commenting.Todo.TaskFound + // @todo remove these assignements when refactoring segmenter classes to use query objects. + $totals_query = array( + 'from_clause' => $this->total_query->get_sql_clause( 'join' ), + 'where_time_clause' => $this->total_query->get_sql_clause( 'where_time' ), + 'where_clause' => $this->total_query->get_sql_clause( 'where' ), + ); + $intervals_query = array( + 'select_clause' => $this->get_sql_clause( 'select' ), + 'from_clause' => $this->interval_query->get_sql_clause( 'join' ), + 'where_time_clause' => $this->interval_query->get_sql_clause( 'where_time' ), + 'where_clause' => $this->interval_query->get_sql_clause( 'where' ), + 'order_by' => $this->get_sql_clause( 'order_by' ), + 'limit' => $this->get_sql_clause( 'limit' ), + ); + $segmenter = new Segmenter( $query_args, $this->report_columns ); + $totals[0]['segments'] = $segmenter->get_totals_segments( $totals_query, $table_name ); - $db_intervals = $wpdb->get_col( - $this->interval_query->get_query_statement() - ); // WPCS: cache ok, DB call ok, unprepared SQL ok. - - $db_interval_count = count( $db_intervals ); - $expected_interval_count = TimeInterval::intervals_between( $query_args['after'], $query_args['before'], $query_args['interval'] ); - $total_pages = (int) ceil( $expected_interval_count / $params['per_page'] ); - if ( $query_args['page'] < 1 || $query_args['page'] > $total_pages ) { - return array(); - } - - $intervals = array(); - $this->update_intervals_sql_params( $query_args, $db_interval_count, $expected_interval_count, $table_name ); - $this->total_query->add_sql_clause( 'select', $selections ); - $this->total_query->add_sql_clause( 'where_time', $this->get_sql_clause( 'where_time' ) ); - - $totals = $wpdb->get_results( - $this->total_query->get_query_statement(), - ARRAY_A - ); // WPCS: cache ok, DB call ok, unprepared SQL ok. - - // @todo remove these assignements when refactoring segmenter classes to use query objects. - $totals_query = array( - 'from_clause' => $this->total_query->get_sql_clause( 'join' ), - 'where_time_clause' => $this->total_query->get_sql_clause( 'where_time' ), - 'where_clause' => $this->total_query->get_sql_clause( 'where' ), - ); - $intervals_query = array( - 'select_clause' => $this->get_sql_clause( 'select' ), - 'from_clause' => $this->interval_query->get_sql_clause( 'join' ), - 'where_time_clause' => $this->interval_query->get_sql_clause( 'where_time' ), - 'where_clause' => $this->interval_query->get_sql_clause( 'where' ), - 'order_by' => $this->get_sql_clause( 'order_by' ), - 'limit' => $this->get_sql_clause( 'limit' ), - ); - $segmenter = new Segmenter( $query_args, $this->report_columns ); - $totals[0]['segments'] = $segmenter->get_totals_segments( $totals_query, $table_name ); - - if ( null === $totals ) { - return new \WP_Error( 'woocommerce_analytics_products_stats_result_failed', __( 'Sorry, fetching revenue data failed.', 'woocommerce' ) ); - } - - $this->interval_query->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); - $this->interval_query->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) ); - $this->interval_query->add_sql_clause( 'select', ", MAX({$table_name}.date_created) AS datetime_anchor" ); - if ( '' !== $selections ) { - $this->interval_query->add_sql_clause( 'select', ', ' . $selections ); - } - - $intervals = $wpdb->get_results( - $this->interval_query->get_query_statement(), - ARRAY_A - ); // WPCS: cache ok, DB call ok, unprepared SQL ok. - - if ( null === $intervals ) { - return new \WP_Error( 'woocommerce_analytics_products_stats_result_failed', __( 'Sorry, fetching revenue data failed.', 'woocommerce' ) ); - } - - $totals = (object) $this->cast_numbers( $totals[0] ); - - $data = (object) array( - 'totals' => $totals, - 'intervals' => $intervals, - 'total' => $expected_interval_count, - 'pages' => $total_pages, - 'page_no' => (int) $query_args['page'], - ); - - if ( TimeInterval::intervals_missing( $expected_interval_count, $db_interval_count, $params['per_page'], $query_args['page'], $query_args['order'], $query_args['orderby'], count( $intervals ) ) ) { - $this->fill_in_missing_intervals( $db_intervals, $query_args['adj_after'], $query_args['adj_before'], $query_args['interval'], $data ); - $this->sort_intervals( $data, $query_args['orderby'], $query_args['order'] ); - $this->remove_extra_records( $data, $query_args['page'], $params['per_page'], $db_interval_count, $expected_interval_count, $query_args['orderby'], $query_args['order'] ); - } else { - $this->update_interval_boundary_dates( $query_args['after'], $query_args['before'], $query_args['interval'], $data->intervals ); - } - $segmenter->add_intervals_segments( $data, $intervals_query, $table_name ); - $this->create_interval_subtotals( $data->intervals ); - - $this->set_cached_data( $cache_key, $data ); + if ( null === $totals ) { + return new \WP_Error( 'woocommerce_analytics_products_stats_result_failed', __( 'Sorry, fetching revenue data failed.', 'woocommerce' ) ); } + $this->interval_query->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); + $this->interval_query->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) ); + $this->interval_query->add_sql_clause( 'select', ", MAX({$table_name}.date_created) AS datetime_anchor" ); + if ( '' !== $selections ) { + $this->interval_query->add_sql_clause( 'select', ', ' . $selections ); + } + + $intervals = $wpdb->get_results( + // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- cache ok, DB call ok, unprepared SQL ok. + $this->interval_query->get_query_statement(), + ARRAY_A + ); + + if ( null === $intervals ) { + return new \WP_Error( 'woocommerce_analytics_products_stats_result_failed', __( 'Sorry, fetching revenue data failed.', 'woocommerce' ) ); + } + + $totals = (object) $this->cast_numbers( $totals[0] ); + + $data->totals = $totals; + $data->intervals = $intervals; + + if ( TimeInterval::intervals_missing( $expected_interval_count, $db_interval_count, $params['per_page'], $query_args['page'], $query_args['order'], $query_args['orderby'], count( $intervals ) ) ) { + $this->fill_in_missing_intervals( $db_intervals, $query_args['adj_after'], $query_args['adj_before'], $query_args['interval'], $data ); + $this->sort_intervals( $data, $query_args['orderby'], $query_args['order'] ); + $this->remove_extra_records( $data, $query_args['page'], $params['per_page'], $db_interval_count, $expected_interval_count, $query_args['orderby'], $query_args['order'] ); + } else { + $this->update_interval_boundary_dates( $query_args['after'], $query_args['before'], $query_args['interval'], $data->intervals ); + } + $segmenter->add_intervals_segments( $data, $intervals_query, $table_name ); + return $data; } /** * Normalizes order_by clause to match to SQL query. * + * @override ProductsDataStore::normalize_order_by() + * * @param string $order_by Order by option requeste by user. * @return string */ @@ -241,18 +254,4 @@ class DataStore extends ProductsDataStore implements DataStoreInterface { return $order_by; } - - /** - * Initialize query objects. - */ - protected function initialize_queries() { - $this->clear_all_clauses(); - unset( $this->subquery ); - $this->total_query = new SqlQuery( $this->context . '_total' ); - $this->total_query->add_sql_clause( 'from', self::get_db_table_name() ); - - $this->interval_query = new SqlQuery( $this->context . '_interval' ); - $this->interval_query->add_sql_clause( 'from', self::get_db_table_name() ); - $this->interval_query->add_sql_clause( 'group_by', 'time_interval' ); - } } diff --git a/plugins/woocommerce/src/Admin/API/Reports/Products/Stats/Query.php b/plugins/woocommerce/src/Admin/API/Reports/Products/Stats/Query.php index dc089601772..d6245c8f732 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Products/Stats/Query.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Products/Stats/Query.php @@ -22,24 +22,34 @@ use Automattic\WooCommerce\Admin\API\Reports\Query as ReportsQuery; /** * API\Reports\Products\Stats\Query + * + * @deprecated 9.3.0 Products\Stats\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. */ class Query extends ReportsQuery { /** * Valid fields for Products report. * + * @deprecated 9.3.0 Products\Stats\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. + * * @return array */ protected function get_default_query_vars() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + return array(); } /** * Get product data based on the current query vars. * + * @deprecated 9.3.0 Products\Stats\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. + * * @return array */ public function get_data() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + $args = apply_filters( 'woocommerce_analytics_products_stats_query_args', $this->get_query_vars() ); $data_store = \WC_Data_Store::load( 'report-products-stats' ); diff --git a/plugins/woocommerce/src/Admin/API/Reports/Query.php b/plugins/woocommerce/src/Admin/API/Reports/Query.php index c287159beeb..a6d2a346de9 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Query.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Query.php @@ -9,15 +9,32 @@ defined( 'ABSPATH' ) || exit; /** * Admin\API\Reports\Query + * + * @deprecated 9.3.0 Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. */ abstract class Query extends \WC_Object_Query { + /** + * Create a new query. + * + * @deprecated 9.3.0 Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. + * + * @param array $args Criteria to query on in a format similar to WP_Query. + */ + public function __construct( $args = array() ) { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + parent::__construct( $args ); + } + /** * Get report data matching the current query vars. * + * @deprecated 9.3.0 Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. + * * @return array|object of WC_Product objects */ public function get_data() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); /* translators: %s: Method name */ return new \WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass.", 'woocommerce' ), __METHOD__ ), array( 'status' => 405 ) ); } diff --git a/plugins/woocommerce/src/Admin/API/Reports/Revenue/Query.php b/plugins/woocommerce/src/Admin/API/Reports/Revenue/Query.php index 6a36261e328..f5f4bb5bd90 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Revenue/Query.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Revenue/Query.php @@ -16,12 +16,15 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Revenue; defined( 'ABSPATH' ) || exit; -use Automattic\WooCommerce\Admin\API\Reports\Query as ReportsQuery; - /** * API\Reports\Revenue\Query + * + * This query uses inconsistent names: + * - `report-revenue-stats` data store + * - `woocommerce_analytics_revenue_*` filters + * So, for backward compatibility, we cannot use GenericQuery. */ -class Query extends ReportsQuery { +class Query extends \WC_Object_Query { /** * Valid fields for Revenue report. diff --git a/plugins/woocommerce/src/Admin/API/Reports/Revenue/Stats/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Revenue/Stats/Controller.php index 077fb917305..826153a3b60 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Revenue/Stats/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Revenue/Stats/Controller.php @@ -13,7 +13,6 @@ use Automattic\WooCommerce\Admin\API\Reports\GenericStatsController; use Automattic\WooCommerce\Admin\API\Reports\Revenue\Query as RevenueQuery; use Automattic\WooCommerce\Admin\API\Reports\ExportableInterface; use Automattic\WooCommerce\Admin\API\Reports\ExportableTraits; -use Automattic\WooCommerce\Admin\API\Reports\ParameterException; use WP_REST_Request; use WP_REST_Response; @@ -60,37 +59,16 @@ class Controller extends GenericStatsController implements ExportableInterface { } /** - * Get all reports. + * Get data from RevenueQuery. * - * @param WP_REST_Request $request Request data. - * @return WP_REST_Response|WP_Error + * @override GenericController::get_datastore_data() + * + * @param array $query_args Query arguments. + * @return mixed Results from the data store. */ - public function get_items( $request ) { - $query_args = $this->prepare_reports_query( $request ); - $reports_revenue = new RevenueQuery( $query_args ); - try { - $report_data = $reports_revenue->get_data(); - } catch ( ParameterException $e ) { - return new \WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); - } - - $out_data = array( - 'totals' => get_object_vars( $report_data->totals ), - 'intervals' => array(), - ); - - foreach ( $report_data->intervals as $interval_data ) { - $item = $this->prepare_item_for_response( $interval_data, $request ); - $out_data['intervals'][] = $this->prepare_response_for_collection( $item ); - } - - return $this->add_pagination_headers( - $request, - $out_data, - (int) $report_data->total, - (int) $report_data->page_no, - (int) $report_data->pages - ); + protected function get_datastore_data( $query_args = array() ) { + $query = new RevenueQuery( $query_args ); + return $query->get_data(); } /** @@ -112,9 +90,9 @@ class Controller extends GenericStatsController implements ExportableInterface { } /** - * Prepare a report object for serialization. + * Prepare a report data item for serialization. * - * @param array $report Report data. + * @param array $report Report data item as returned from Data Store. * @param WP_REST_Request $request Request object. * @return WP_REST_Response */ @@ -279,6 +257,7 @@ class Controller extends GenericStatsController implements ExportableInterface { ), 'validate_callback' => 'rest_validate_request_arg', ); + unset( $params['fields'] ); return $params; } diff --git a/plugins/woocommerce/src/Admin/API/Reports/StatsDataStoreTrait.php b/plugins/woocommerce/src/Admin/API/Reports/StatsDataStoreTrait.php new file mode 100644 index 00000000000..f05204336aa --- /dev/null +++ b/plugins/woocommerce/src/Admin/API/Reports/StatsDataStoreTrait.php @@ -0,0 +1,120 @@ +class MyStatsDataStore extends DataStore implements DataStoreInterface { + * // Use the trait. + * use StatsDataStoreTrait; + * // Provide all the necessary properties and methods for a regular DataStore. + * // ... + * /** + * * Return your results with the help of the interval & total methods and queries. + * * @return stdClass|WP_Error $data filled with your results. + * */ + * public function get_noncached_stats_data( $query_args, $params, &$data, $expected_interval_count ) { + * $this->initialize_queries(); + * // Do your magic ... + * // ... with a help of things like: + * $this->update_intervals_sql_params( $query_args, $db_interval_count, $expected_interval_count, $table_name ); + * $this->total_query->add_sql_clause( 'where_time', $this->get_sql_clause( 'where_time' ) ); + * + * $totals = $wpdb->get_results( + * $this->total_query->get_query_statement(), + * ARRAY_A + * ); + * + * $intervals = $wpdb->get_results( + * $this->interval_query->get_query_statement(), + * ARRAY_A + * ); + * + * $data->totals = (object) $this->cast_numbers( $totals[0] ); + * $data->intervals = $intervals; + * + * if ( TimeInterval::intervals_missing( $expected_interval_count, $db_interval_count, $params['per_page'], $query_args['page'], $query_args['order'], $query_args['orderby'], count( $intervals ) ) ) { + * $this->fill_in_missing_intervals( $db_intervals, $query_args['adj_after'], $query_args['adj_before'], $query_args['interval'], $data ); + * $this->sort_intervals( $data, $query_args['orderby'], $query_args['order'] ); + * $this->remove_extra_records( $data, $query_args['page'], $params['per_page'], $db_interval_count, $expected_interval_count, $query_args['orderby'], $query_args['order'] ); + * } else { + * $this->update_interval_boundary_dates( $query_args['after'], $query_args['before'], $query_args['interval'], $data->intervals ); + * } + * + * return $data; + * } + * } + * + * + * @see DataStore + */ +trait StatsDataStoreTrait { + /** + * Initialize query objects. + */ + protected function initialize_queries() { + $this->clear_all_clauses(); + unset( $this->subquery ); + $table_name = self::get_db_table_name(); + + $this->total_query = new SqlQuery( $this->context . '_total' ); + $this->total_query->add_sql_clause( 'from', $table_name ); + + $this->interval_query = new SqlQuery( $this->context . '_interval' ); + $this->interval_query->add_sql_clause( 'from', $table_name ); + $this->interval_query->add_sql_clause( 'group_by', 'time_interval' ); + } + + /** + * Returns the stats report data based on normalized parameters. + * Prepares the basic intervals and object structure + * Will be called by `get_data` if there is no data in cache. + * Will call `get_noncached_stats_data` to fetch the actual data. + * + * @see get_data + * @param array $query_args Query parameters. + * @return stdClass|WP_Error Data object, or error. + */ + public function get_noncached_data( $query_args ) { + $params = $this->get_limit_params( $query_args ); + $expected_interval_count = TimeInterval::intervals_between( $query_args['after'], $query_args['before'], $query_args['interval'] ); + $total_pages = (int) ceil( $expected_interval_count / $params['per_page'] ); + + // Default, empty data object. + $data = (object) array( + 'totals' => null, + 'intervals' => array(), + 'total' => $expected_interval_count, + 'pages' => $total_pages, + 'page_no' => (int) $query_args['page'], + ); + // If the requested page is out off range, return the default empty object. + if ( $query_args['page'] >= 1 && $query_args['page'] <= $total_pages ) { + // Fetch the actual data. + $data = $this->get_noncached_stats_data( $query_args, $params, $data, $expected_interval_count ); + + if ( ! is_wp_error( $data ) && is_array( $data->intervals ) ) { + $this->create_interval_subtotals( $data->intervals ); + } + } + + return $data; + } +} diff --git a/plugins/woocommerce/src/Admin/API/Reports/Stock/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Stock/Controller.php index 72cb2f66c74..11524a422f0 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Stock/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Stock/Controller.php @@ -276,9 +276,9 @@ class Controller extends GenericController implements ExportableInterface { } /** - * Prepare a report object for serialization. + * Prepare a report data item for serialization. * - * @param WC_Product $product Report data. + * @param WC_Product $product Report data item as returned from Data Store. * @param WP_REST_Request $request Request object. * @return WP_REST_Response */ diff --git a/plugins/woocommerce/src/Admin/API/Reports/Stock/Stats/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Stock/Stats/Controller.php index 6e51ac2dd98..3cf4d2ee8c2 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Stock/Stats/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Stock/Stats/Controller.php @@ -47,9 +47,9 @@ class Controller extends \WC_REST_Reports_Controller { } /** - * Prepare a report object for serialization. + * Prepare a report data item for serialization. * - * @param WC_Product $report Report data. + * @param WC_Product $report Report data item as returned from Data Store. * @param WP_REST_Request $request Request object. * @return WP_REST_Response */ diff --git a/plugins/woocommerce/src/Admin/API/Reports/Stock/Stats/DataStore.php b/plugins/woocommerce/src/Admin/API/Reports/Stock/Stats/DataStore.php index 3af2c08162b..9af9159a1a0 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Stock/Stats/DataStore.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Stock/Stats/DataStore.php @@ -18,6 +18,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Get stock counts for the whole store. * + * @override ReportsDataStore::get_data() + * * @param array $query Not used for the stock stats data store, but needed for the interface. * @return array Array of counts. */ diff --git a/plugins/woocommerce/src/Admin/API/Reports/Stock/Stats/Query.php b/plugins/woocommerce/src/Admin/API/Reports/Stock/Stats/Query.php index f485a96b2b7..b4886e62340 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Stock/Stats/Query.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Stock/Stats/Query.php @@ -10,12 +10,11 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Stock\Stats; defined( 'ABSPATH' ) || exit; -use Automattic\WooCommerce\Admin\API\Reports\Query as ReportsQuery; - /** * API\Reports\Stock\Stats\Query + * This query takes no arguments, so we do not inherit from GenericQuery. */ -class Query extends ReportsQuery { +class Query extends \WC_Object_Query { /** * Get product data based on the current query vars. diff --git a/plugins/woocommerce/src/Admin/API/Reports/Taxes/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Taxes/Controller.php index 762056e1f5d..9d6cbd54364 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Taxes/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Taxes/Controller.php @@ -9,9 +9,10 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Taxes; defined( 'ABSPATH' ) || exit; -use Automattic\WooCommerce\Admin\API\Reports\GenericController; use Automattic\WooCommerce\Admin\API\Reports\ExportableInterface; use Automattic\WooCommerce\Admin\API\Reports\ExportableTraits; +use Automattic\WooCommerce\Admin\API\Reports\GenericController; +use Automattic\WooCommerce\Admin\API\Reports\GenericQuery; use WP_REST_Request; use WP_REST_Response; @@ -34,6 +35,19 @@ class Controller extends GenericController implements ExportableInterface { */ protected $rest_base = 'reports/taxes'; + /** + * Get data from `'taxes'` GenericQuery. + * + * @override GenericController::get_datastore_data() + * + * @param array $query_args Query arguments. + * @return mixed Results from the data store. + */ + protected function get_datastore_data( $query_args = array() ) { + $query = new GenericQuery( $query_args, 'taxes' ); + return $query->get_data(); + } + /** * Maps query arguments from the REST request. * @@ -55,41 +69,17 @@ class Controller extends GenericController implements ExportableInterface { } /** - * Get all reports. + * Prepare a report data item for serialization. * - * @param WP_REST_Request $request Request data. - * @return array|WP_Error - */ - public function get_items( $request ) { - $query_args = $this->prepare_reports_query( $request ); - $taxes_query = new Query( $query_args ); - $report_data = $taxes_query->get_data(); - - $data = array(); - - foreach ( $report_data->data as $tax_data ) { - $item = $this->prepare_item_for_response( (object) $tax_data, $request ); - $data[] = $this->prepare_response_for_collection( $item ); - } - - return $this->add_pagination_headers( - $request, - $data, - (int) $report_data->total, - (int) $report_data->page_no, - (int) $report_data->pages - ); - } - - /** - * Prepare a report object for serialization. - * - * @param stdClass $report Report data. + * @param mixed $report Report data item as returned from Data Store. * @param WP_REST_Request $request Request object. * @return WP_REST_Response */ public function prepare_item_for_response( $report, $request ) { $response = parent::prepare_item_for_response( $report, $request ); + + // Map to `object` for backwards compatibility. + $report = (object) $report; $response->add_links( $this->prepare_links( $report ) ); /** diff --git a/plugins/woocommerce/src/Admin/API/Reports/Taxes/DataStore.php b/plugins/woocommerce/src/Admin/API/Reports/Taxes/DataStore.php index 2f9a430e7cb..d808b8170fb 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Taxes/DataStore.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Taxes/DataStore.php @@ -21,6 +21,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Table used to get the data. * + * @override ReportsDataStore::$table_name + * * @var string */ protected static $table_name = 'wc_order_tax_lookup'; @@ -28,6 +30,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Cache identifier. * + * @override ReportsDataStore::$cache_key + * * @var string */ protected $cache_key = 'taxes'; @@ -35,6 +39,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Mapping columns to data type to return correct response types. * + * @override ReportsDataStore::$column_types + * * @var array */ protected $column_types = array( @@ -53,12 +59,16 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Data store context used to pass to filters. * + * @override ReportsDataStore::$context + * * @var string */ protected $context = 'taxes'; /** * Assign report columns once full table name has been assigned. + * + * @override ReportsDataStore::assign_report_columns() */ protected function assign_report_columns() { global $wpdb; @@ -138,100 +148,97 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { } /** - * Returns the report data based on parameters supplied by the user. + * Get the default query arguments to be used by get_data(). + * These defaults are only partially applied when used via REST API, as that has its own defaults. * - * @param array $query_args Query parameters. - * @return stdClass|WP_Error Data. + * @override ReportsDataStore::get_default_query_vars() + * + * @return array Query parameters. */ - public function get_data( $query_args ) { + public function get_default_query_vars() { + $defaults = parent::get_default_query_vars(); + $defaults['orderby'] = 'tax_rate_id'; + $defaults['taxes'] = array(); + + return $defaults; + } + + /** + * Returns the report data based on normalized parameters. + * Will be called by `get_data` if there is no data in cache. + * + * @override ReportsDataStore::get_noncached_data() + * + * @see get_data + * @param array $query_args Query parameters. + * @return stdClass|WP_Error Data object `{ totals: *, intervals: array, total: int, pages: int, page_no: int }`, or error. + */ + public function get_noncached_data( $query_args ) { global $wpdb; - // These defaults are only partially applied when used via REST API, as that has its own defaults. - $defaults = array( - 'per_page' => get_option( 'posts_per_page' ), - 'page' => 1, - 'order' => 'DESC', - 'orderby' => 'tax_rate_id', - 'before' => TimeInterval::default_before(), - 'after' => TimeInterval::default_after(), - 'fields' => '*', - 'taxes' => array(), + $this->initialize_queries(); + + $data = (object) array( + 'data' => array(), + 'total' => 0, + 'pages' => 0, + 'page_no' => 0, ); - $query_args = wp_parse_args( $query_args, $defaults ); - $this->normalize_timezones( $query_args, $defaults ); - /* - * We need to get the cache key here because - * parent::update_intervals_sql_params() modifies $query_args. - */ - $cache_key = $this->get_cache_key( $query_args ); - $data = $this->get_cached_data( $cache_key ); + $this->add_sql_query_params( $query_args ); + $params = $this->get_limit_params( $query_args ); - if ( false === $data ) { - $this->initialize_queries(); - - $data = (object) array( - 'data' => array(), - 'total' => 0, - 'pages' => 0, - 'page_no' => 0, + if ( isset( $query_args['taxes'] ) && is_array( $query_args['taxes'] ) && ! empty( $query_args['taxes'] ) ) { + $total_results = count( $query_args['taxes'] ); + $total_pages = (int) ceil( $total_results / $params['per_page'] ); + } else { + $db_records_count = (int) $wpdb->get_var( + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- cache ok, DB call ok, unprepared SQL ok. + "SELECT COUNT(*) FROM ( {$this->subquery->get_query_statement()} ) AS tt" ); - $this->add_sql_query_params( $query_args ); - $params = $this->get_limit_params( $query_args ); + $total_results = $db_records_count; + $total_pages = (int) ceil( $db_records_count / $params['per_page'] ); - if ( isset( $query_args['taxes'] ) && is_array( $query_args['taxes'] ) && ! empty( $query_args['taxes'] ) ) { - $total_results = count( $query_args['taxes'] ); - $total_pages = (int) ceil( $total_results / $params['per_page'] ); - } else { - $db_records_count = (int) $wpdb->get_var( - "SELECT COUNT(*) FROM ( - {$this->subquery->get_query_statement()} - ) AS tt" - ); // WPCS: cache ok, DB call ok, unprepared SQL ok. - - $total_results = $db_records_count; - $total_pages = (int) ceil( $db_records_count / $params['per_page'] ); - - if ( $query_args['page'] < 1 || $query_args['page'] > $total_pages ) { - return $data; - } - } - - $this->subquery->clear_sql_clause( 'select' ); - $this->subquery->add_sql_clause( 'select', $this->selected_columns( $query_args ) ); - $this->subquery->add_sql_clause( 'group_by', ", {$wpdb->prefix}woocommerce_order_items.order_item_name, {$wpdb->prefix}woocommerce_order_itemmeta.meta_value" ); - $this->subquery->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); - $this->subquery->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) ); - - $taxes_query = $this->subquery->get_query_statement(); - - $tax_data = $wpdb->get_results( - $taxes_query, - ARRAY_A - ); // WPCS: cache ok, DB call ok, unprepared SQL ok. - - if ( null === $tax_data ) { + if ( $query_args['page'] < 1 || $query_args['page'] > $total_pages ) { return $data; } - - $tax_data = array_map( array( $this, 'cast_numbers' ), $tax_data ); - $data = (object) array( - 'data' => $tax_data, - 'total' => $total_results, - 'pages' => $total_pages, - 'page_no' => (int) $query_args['page'], - ); - - $this->set_cached_data( $cache_key, $data ); } + $this->subquery->clear_sql_clause( 'select' ); + $this->subquery->add_sql_clause( 'select', $this->selected_columns( $query_args ) ); + $this->subquery->add_sql_clause( 'group_by', ", {$wpdb->prefix}woocommerce_order_items.order_item_name, {$wpdb->prefix}woocommerce_order_itemmeta.meta_value" ); + $this->subquery->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); + $this->subquery->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) ); + + $taxes_query = $this->subquery->get_query_statement(); + + $tax_data = $wpdb->get_results( + // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- cache ok, DB call ok, unprepared SQL ok. + $taxes_query, + ARRAY_A + ); + + if ( null === $tax_data ) { + return $data; + } + + $tax_data = array_map( array( $this, 'cast_numbers' ), $tax_data ); + $data = (object) array( + 'data' => $tax_data, + 'total' => $total_results, + 'pages' => $total_pages, + 'page_no' => (int) $query_args['page'], + ); + return $data; } /** * Maps ordering specified by the user to columns in the database/fields in the data. * + * @override ReportsDataStore::normalize_order_by() + * * @param string $order_by Sorting criterion. * @return string */ diff --git a/plugins/woocommerce/src/Admin/API/Reports/Taxes/Query.php b/plugins/woocommerce/src/Admin/API/Reports/Taxes/Query.php index 3323b65845e..bcc5ced65d7 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Taxes/Query.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Taxes/Query.php @@ -21,24 +21,34 @@ use Automattic\WooCommerce\Admin\API\Reports\Query as ReportsQuery; /** * API\Reports\Taxes\Query + * + * @deprecated 9.3.0 Taxes\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. */ class Query extends ReportsQuery { /** * Valid fields for Taxes report. * + * @deprecated 9.3.0 Taxes\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. + * * @return array */ protected function get_default_query_vars() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + return array(); } /** * Get product data based on the current query vars. * + * @deprecated 9.3.0 Taxes\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. + * * @return array */ public function get_data() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + $args = apply_filters( 'woocommerce_analytics_taxes_query_args', $this->get_query_vars() ); $data_store = \WC_Data_Store::load( 'report-taxes' ); diff --git a/plugins/woocommerce/src/Admin/API/Reports/Taxes/Stats/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Taxes/Stats/Controller.php index 426ea8c2432..f551d54d04e 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Taxes/Stats/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Taxes/Stats/Controller.php @@ -9,6 +9,7 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Taxes\Stats; defined( 'ABSPATH' ) || exit; +use Automattic\WooCommerce\Admin\API\Reports\GenericQuery; use Automattic\WooCommerce\Admin\API\Reports\GenericStatsController; use WP_REST_Request; use WP_REST_Response; @@ -83,47 +84,30 @@ class Controller extends GenericStatsController { } /** - * Get all reports. + * Get data from `'taxes-stats'` GenericQuery. * - * @param WP_REST_Request $request Request data. - * @return array|WP_Error + * @override GenericController::get_datastore_data() + * + * @param array $query_args Query arguments. + * @return mixed Results from the data store. */ - public function get_items( $request ) { - $query_args = $this->prepare_reports_query( $request ); - $taxes_query = new Query( $query_args ); - $report_data = $taxes_query->get_data(); - - $out_data = array( - 'totals' => get_object_vars( $report_data->totals ), - 'intervals' => array(), - ); - - foreach ( $report_data->intervals as $interval_data ) { - $item = $this->prepare_item_for_response( (object) $interval_data, $request ); - $out_data['intervals'][] = $this->prepare_response_for_collection( $item ); - } - - return $this->add_pagination_headers( - $request, - $out_data, - (int) $report_data->total, - (int) $report_data->page_no, - (int) $report_data->pages - ); + protected function get_datastore_data( $query_args = array() ) { + $query = new GenericQuery( $query_args, 'taxes-stats' ); + return $query->get_data(); } /** - * Prepare a report object for serialization. + * Prepare a report data item for serialization. * - * @param stdClass $report Report data. + * @param mixed $report Report data item as returned from Data Store. * @param WP_REST_Request $request Request object. * @return WP_REST_Response */ public function prepare_item_for_response( $report, $request ) { - $data = get_object_vars( $report ); - - $response = parent::prepare_item_for_response( $data, $request ); + $response = parent::prepare_item_for_response( $report, $request ); + // Map to `object` for backwards compatibility. + $report = (object) $report; /** * Filter a report returned from the API. * @@ -226,15 +210,6 @@ class Controller extends GenericStatsController { ), 'validate_callback' => 'rest_validate_request_arg', ); - $params['fields'] = array( - 'description' => __( 'Limit stats fields to the specified items.', 'woocommerce' ), - 'type' => 'array', - 'sanitize_callback' => 'wp_parse_slug_list', - 'validate_callback' => 'rest_validate_request_arg', - 'items' => array( - 'type' => 'string', - ), - ); return $params; } diff --git a/plugins/woocommerce/src/Admin/API/Reports/Taxes/Stats/DataStore.php b/plugins/woocommerce/src/Admin/API/Reports/Taxes/Stats/DataStore.php index 486bfaee488..2173f6b859c 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Taxes/Stats/DataStore.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Taxes/Stats/DataStore.php @@ -10,16 +10,19 @@ defined( 'ABSPATH' ) || exit; use Automattic\WooCommerce\Admin\API\Reports\DataStore as ReportsDataStore; use Automattic\WooCommerce\Admin\API\Reports\DataStoreInterface; use Automattic\WooCommerce\Admin\API\Reports\TimeInterval; -use Automattic\WooCommerce\Admin\API\Reports\SqlQuery; +use Automattic\WooCommerce\Admin\API\Reports\StatsDataStoreTrait; /** * API\Reports\Taxes\Stats\DataStore. */ class DataStore extends ReportsDataStore implements DataStoreInterface { + use StatsDataStoreTrait; /** * Table used to get the data. * + * @override ReportsDataStore::$table_name + * * @var string */ protected static $table_name = 'wc_order_tax_lookup'; @@ -27,6 +30,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Cache identifier. * + * @override ReportsDataStore::$cache_key + * * @var string */ protected $cache_key = 'taxes_stats'; @@ -34,6 +39,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Mapping columns to data type to return correct response types. * + * @override ReportsDataStore::$column_types + * * @var array */ protected $column_types = array( @@ -47,12 +54,16 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Data store context used to pass to filters. * + * @override ReportsDataStore::$context + * * @var string */ protected $context = 'taxes_stats'; /** * Assign report columns once full table name has been assigned. + * + * @override ReportsDataStore::assign_report_columns() */ protected function assign_report_columns() { $table_name = self::get_db_table_name(); @@ -107,12 +118,12 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { public static function get_taxes( $args ) { global $wpdb; $query = " - SELECT - tax_rate_id, - tax_rate_country, - tax_rate_state, - tax_rate_name, - tax_rate_priority + SELECT + tax_rate_id, + tax_rate_country, + tax_rate_state, + tax_rate_name, + tax_rate_priority FROM {$wpdb->prefix}woocommerce_tax_rates "; if ( ! empty( $args['include'] ) ) { @@ -126,146 +137,116 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { } /** - * Returns the report data based on parameters supplied by the user. + * Get the default query arguments to be used by get_data(). + * These defaults are only partially applied when used via REST API, as that has its own defaults. * - * @param array $query_args Query parameters. - * @return stdClass|WP_Error Data. + * @override ReportsDataStore::get_default_query_vars() + * + * @return array Query parameters. */ - public function get_data( $query_args ) { + public function get_default_query_vars() { + $defaults = parent::get_default_query_vars(); + $defaults['orderby'] = 'tax_rate_id'; + $defaults['taxes'] = array(); + + return $defaults; + } + + /** + * Returns the report data based on normalized parameters. + * Will be called by `get_data` if there is no data in cache. + * + * @override ReportsDataStore::get_noncached_data() + * + * @see get_data + * @see get_noncached_stats_data + * @param array $query_args Query parameters. + * @param array $params Query limit parameters. + * @param stdClass $data Reference to the data object to fill. + * @param int $expected_interval_count Number of expected intervals. + * @return stdClass|WP_Error Data object `{ totals: *, intervals: array, total: int, pages: int, page_no: int }`, or error. + */ + public function get_noncached_stats_data( $query_args, $params, &$data, $expected_interval_count ) { global $wpdb; $table_name = self::get_db_table_name(); - // These defaults are only partially applied when used via REST API, as that has its own defaults. - $defaults = array( - 'per_page' => get_option( 'posts_per_page' ), - 'page' => 1, - 'order' => 'DESC', - 'orderby' => 'tax_rate_id', - 'before' => TimeInterval::default_before(), - 'after' => TimeInterval::default_after(), - 'fields' => '*', - 'taxes' => array(), + $this->initialize_queries(); + + $selections = $this->selected_columns( $query_args ); + $order_stats_join = "JOIN {$wpdb->prefix}wc_order_stats ON {$table_name}.order_id = {$wpdb->prefix}wc_order_stats.order_id"; + $this->update_sql_query_params( $query_args ); + $this->interval_query->add_sql_clause( 'join', $order_stats_join ); + + $db_intervals = $wpdb->get_col( + // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- cache ok, DB call ok, unprepared SQL ok. + $this->interval_query->get_query_statement() ); - $query_args = wp_parse_args( $query_args, $defaults ); - $this->normalize_timezones( $query_args, $defaults ); + $db_interval_count = count( $db_intervals ); - /* - * We need to get the cache key here because - * parent::update_intervals_sql_params() modifies $query_args. - */ - $cache_key = $this->get_cache_key( $query_args ); - $data = $this->get_cached_data( $cache_key ); + $this->total_query->add_sql_clause( 'select', $selections ); + $this->total_query->add_sql_clause( 'join', $order_stats_join ); + $this->total_query->add_sql_clause( 'where_time', $this->get_sql_clause( 'where_time' ) ); - if ( false === $data ) { - $this->initialize_queries(); + $totals = $wpdb->get_results( + // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- cache ok, DB call ok, unprepared SQL ok. + $this->total_query->get_query_statement(), + ARRAY_A + ); - $data = (object) array( - 'totals' => (object) array(), - 'intervals' => (object) array(), - 'total' => 0, - 'pages' => 0, - 'page_no' => 0, - ); - - $selections = $this->selected_columns( $query_args ); - $params = $this->get_limit_params( $query_args ); - $order_stats_join = "JOIN {$wpdb->prefix}wc_order_stats ON {$table_name}.order_id = {$wpdb->prefix}wc_order_stats.order_id"; - $this->update_sql_query_params( $query_args ); - $this->interval_query->add_sql_clause( 'join', $order_stats_join ); - - $db_intervals = $wpdb->get_col( - $this->interval_query->get_query_statement() - ); // WPCS: cache ok, DB call ok, unprepared SQL ok. - $db_interval_count = count( $db_intervals ); - $expected_interval_count = TimeInterval::intervals_between( $query_args['after'], $query_args['before'], $query_args['interval'] ); - $total_pages = (int) ceil( $expected_interval_count / $params['per_page'] ); - - if ( $query_args['page'] < 1 || $query_args['page'] > $total_pages ) { - return $data; - } - $this->total_query->add_sql_clause( 'select', $selections ); - $this->total_query->add_sql_clause( 'join', $order_stats_join ); - $this->total_query->add_sql_clause( 'where_time', $this->get_sql_clause( 'where_time' ) ); - - $totals = $wpdb->get_results( - $this->total_query->get_query_statement(), - ARRAY_A - ); // WPCS: cache ok, DB call ok, unprepared SQL ok. - - if ( null === $totals ) { - return new \WP_Error( 'woocommerce_analytics_taxes_stats_result_failed', __( 'Sorry, fetching revenue data failed.', 'woocommerce' ) ); - } - - // @todo remove these assignements when refactoring segmenter classes to use query objects. - $totals_query = array( - 'from_clause' => $this->total_query->get_sql_clause( 'join' ), - 'where_time_clause' => $this->total_query->get_sql_clause( 'where_time' ), - 'where_clause' => $this->total_query->get_sql_clause( 'where' ), - ); - $intervals_query = array( - 'select_clause' => $this->get_sql_clause( 'select' ), - 'from_clause' => $this->interval_query->get_sql_clause( 'join' ), - 'where_time_clause' => $this->interval_query->get_sql_clause( 'where_time' ), - 'where_clause' => $this->interval_query->get_sql_clause( 'where' ), - ); - $segmenter = new Segmenter( $query_args, $this->report_columns ); - $totals[0]['segments'] = $segmenter->get_totals_segments( $totals_query, $table_name ); - - $this->update_intervals_sql_params( $query_args, $db_interval_count, $expected_interval_count, $table_name ); - - if ( '' !== $selections ) { - $this->interval_query->add_sql_clause( 'select', ', ' . $selections ); - } - - $this->interval_query->add_sql_clause( 'select', ", MAX({$table_name}.date_created) AS datetime_anchor" ); - $this->interval_query->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); - $this->interval_query->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) ); - - $intervals = $wpdb->get_results( - $this->interval_query->get_query_statement(), - ARRAY_A - ); // WPCS: cache ok, DB call ok, unprepared SQL ok. - - if ( null === $intervals ) { - return new \WP_Error( 'woocommerce_analytics_taxes_stats_result_failed', __( 'Sorry, fetching tax data failed.', 'woocommerce' ) ); - } - - $totals = (object) $this->cast_numbers( $totals[0] ); - - $data = (object) array( - 'totals' => $totals, - 'intervals' => $intervals, - 'total' => $expected_interval_count, - 'pages' => $total_pages, - 'page_no' => (int) $query_args['page'], - ); - - if ( TimeInterval::intervals_missing( $expected_interval_count, $db_interval_count, $params['per_page'], $query_args['page'], $query_args['order'], $query_args['orderby'], count( $intervals ) ) ) { - $this->fill_in_missing_intervals( $db_intervals, $query_args['adj_after'], $query_args['adj_before'], $query_args['interval'], $data ); - $this->sort_intervals( $data, $query_args['orderby'], $query_args['order'] ); - $this->remove_extra_records( $data, $query_args['page'], $params['per_page'], $db_interval_count, $expected_interval_count, $query_args['orderby'], $query_args['order'] ); - } else { - $this->update_interval_boundary_dates( $query_args['after'], $query_args['before'], $query_args['interval'], $data->intervals ); - } - $segmenter->add_intervals_segments( $data, $intervals_query, $table_name ); - $this->create_interval_subtotals( $data->intervals ); - $this->set_cached_data( $cache_key, $data ); + if ( null === $totals ) { + return new \WP_Error( 'woocommerce_analytics_taxes_stats_result_failed', __( 'Sorry, fetching revenue data failed.', 'woocommerce' ) ); } + + // phpcs:ignore Generic.Commenting.Todo.TaskFound + // @todo remove these assignements when refactoring segmenter classes to use query objects. + $totals_query = array( + 'from_clause' => $this->total_query->get_sql_clause( 'join' ), + 'where_time_clause' => $this->total_query->get_sql_clause( 'where_time' ), + 'where_clause' => $this->total_query->get_sql_clause( 'where' ), + ); + $intervals_query = array( + 'select_clause' => $this->get_sql_clause( 'select' ), + 'from_clause' => $this->interval_query->get_sql_clause( 'join' ), + 'where_time_clause' => $this->interval_query->get_sql_clause( 'where_time' ), + 'where_clause' => $this->interval_query->get_sql_clause( 'where' ), + ); + $segmenter = new Segmenter( $query_args, $this->report_columns ); + $totals[0]['segments'] = $segmenter->get_totals_segments( $totals_query, $table_name ); + + $this->update_intervals_sql_params( $query_args, $db_interval_count, $expected_interval_count, $table_name ); + + if ( '' !== $selections ) { + $this->interval_query->add_sql_clause( 'select', ', ' . $selections ); + } + + $this->interval_query->add_sql_clause( 'select', ", MAX({$table_name}.date_created) AS datetime_anchor" ); + $this->interval_query->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); + $this->interval_query->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) ); + + $intervals = $wpdb->get_results( + // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- cache ok, DB call ok, unprepared SQL ok. + $this->interval_query->get_query_statement(), + ARRAY_A + ); + + if ( null === $intervals ) { + return new \WP_Error( 'woocommerce_analytics_taxes_stats_result_failed', __( 'Sorry, fetching tax data failed.', 'woocommerce' ) ); + } + + $totals = (object) $this->cast_numbers( $totals[0] ); + + $data->totals = $totals; + $data->intervals = $intervals; + + if ( TimeInterval::intervals_missing( $expected_interval_count, $db_interval_count, $params['per_page'], $query_args['page'], $query_args['order'], $query_args['orderby'], count( $intervals ) ) ) { + $this->fill_in_missing_intervals( $db_intervals, $query_args['adj_after'], $query_args['adj_before'], $query_args['interval'], $data ); + $this->sort_intervals( $data, $query_args['orderby'], $query_args['order'] ); + $this->remove_extra_records( $data, $query_args['page'], $params['per_page'], $db_interval_count, $expected_interval_count, $query_args['orderby'], $query_args['order'] ); + } else { + $this->update_interval_boundary_dates( $query_args['after'], $query_args['before'], $query_args['interval'], $data->intervals ); + } + $segmenter->add_intervals_segments( $data, $intervals_query, $table_name ); return $data; } - - /** - * Initialize query objects. - */ - protected function initialize_queries() { - $this->clear_all_clauses(); - unset( $this->subquery ); - $this->total_query = new SqlQuery( $this->context . '_total' ); - $this->total_query->add_sql_clause( 'from', self::get_db_table_name() ); - - $this->interval_query = new SqlQuery( $this->context . '_interval' ); - $this->interval_query->add_sql_clause( 'from', self::get_db_table_name() ); - $this->interval_query->add_sql_clause( 'group_by', 'time_interval' ); - } } diff --git a/plugins/woocommerce/src/Admin/API/Reports/Taxes/Stats/Query.php b/plugins/woocommerce/src/Admin/API/Reports/Taxes/Stats/Query.php index 8ff96321bd9..98301fc1b82 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Taxes/Stats/Query.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Taxes/Stats/Query.php @@ -22,24 +22,34 @@ use Automattic\WooCommerce\Admin\API\Reports\Query as ReportsQuery; /** * API\Reports\Taxes\Stats\Query + * + * @deprecated 9.3.0 Taxes\Stats\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. */ class Query extends ReportsQuery { /** * Valid fields for Taxes report. * + * @deprecated 9.3.0 Taxes\Stats\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. + * * @return array */ protected function get_default_query_vars() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + return array(); } /** * Get tax stats data based on the current query vars. * + * @deprecated 9.3.0 Taxes\Stats\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. + * * @return array */ public function get_data() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + $args = apply_filters( 'woocommerce_analytics_taxes_stats_query_args', $this->get_query_vars() ); $data_store = \WC_Data_Store::load( 'report-taxes-stats' ); diff --git a/plugins/woocommerce/src/Admin/API/Reports/Variations/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Variations/Controller.php index 03231e9674d..43c499e2636 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Variations/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Variations/Controller.php @@ -9,17 +9,24 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Variations; defined( 'ABSPATH' ) || exit; -use Automattic\WooCommerce\Admin\API\Reports\Controller as ReportsController; use Automattic\WooCommerce\Admin\API\Reports\ExportableInterface; use Automattic\WooCommerce\Admin\API\Reports\ExportableTraits; +use Automattic\WooCommerce\Admin\API\Reports\GenericController; +use Automattic\WooCommerce\Admin\API\Reports\GenericQuery; +use Automattic\WooCommerce\Admin\API\Reports\OrderAwareControllerTrait; + /** * REST API Reports products controller class. * * @internal - * @extends ReportsController + * @extends GenericController */ -class Controller extends ReportsController implements ExportableInterface { +class Controller extends GenericController implements ExportableInterface { + + // The controller does not use this trait. It's here for API backward compatibility. + use OrderAwareControllerTrait; + /** * Exportable traits. */ @@ -43,13 +50,52 @@ class Controller extends ReportsController implements ExportableInterface { ); /** - * Get items. + * Get data from `'variations'` GenericQuery. * - * @param WP_REST_Request $request Request data. + * @override GenericController::get_datastore_data() * - * @return array|WP_Error + * @param array $query_args Query arguments. + * @return mixed Results from the data store. */ - public function get_items( $request ) { + protected function get_datastore_data( $query_args = array() ) { + $query = new GenericQuery( $query_args, 'variations' ); + return $query->get_data(); + } + + /** + * Prepare a report data item for serialization. + * + * @param array $report Report data item as returned from Data Store. + * @param WP_REST_Request $request Request object. + * @return WP_REST_Response + */ + public function prepare_item_for_response( $report, $request ) { + // Wrap the data in a response object. + $response = parent::prepare_item_for_response( $report, $request ); + + $response->add_links( $this->prepare_links( $report ) ); + + /** + * Filter a report returned from the API. + * + * Allows modification of the report data right before it is returned. + * + * @since 6.5.0 + * + * @param WP_REST_Response $response The response object. + * @param object $report The original report object. + * @param WP_REST_Request $request Request used to generate the response. + */ + return apply_filters( 'woocommerce_rest_prepare_report_variations', $response, $report, $request ); + } + + /** + * Maps query arguments from the REST request. + * + * @param array $request Request array. + * @return array + */ + protected function prepare_reports_query( $request ) { $args = array(); /** * Experimental: Filter the list of parameters provided when querying data from the data store. @@ -57,6 +103,8 @@ class Controller extends ReportsController implements ExportableInterface { * @ignore * * @param array $collection_params List of parameters. + * + * @since 6.5.0 */ $collection_params = apply_filters( 'experimental_woocommerce_analytics_variations_collection_params', @@ -72,54 +120,7 @@ class Controller extends ReportsController implements ExportableInterface { } } } - - $reports = new Query( $args ); - $products_data = $reports->get_data(); - - $data = array(); - - foreach ( $products_data->data as $product_data ) { - $item = $this->prepare_item_for_response( $product_data, $request ); - $data[] = $this->prepare_response_for_collection( $item ); - } - - return $this->add_pagination_headers( - $request, - $data, - (int) $products_data->total, - (int) $products_data->page_no, - (int) $products_data->pages - ); - } - - /** - * Prepare a report object for serialization. - * - * @param array $report Report data. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response - */ - public function prepare_item_for_response( $report, $request ) { - $data = $report; - - $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; - $data = $this->add_additional_fields_to_object( $data, $request ); - $data = $this->filter_response_by_context( $data, $context ); - - // Wrap the data in a response object. - $response = rest_ensure_response( $data ); - $response->add_links( $this->prepare_links( $report ) ); - - /** - * Filter a report returned from the API. - * - * Allows modification of the report data right before it is returned. - * - * @param WP_REST_Response $response The response object. - * @param object $report The original report object. - * @param WP_REST_Request $request Request used to generate the response. - */ - return apply_filters( 'woocommerce_rest_prepare_report_variations', $response, $report, $request ); + return $args; } /** @@ -244,38 +245,15 @@ class Controller extends ReportsController implements ExportableInterface { * @return array */ public function get_collection_params() { - $params = array(); - $params['context'] = $this->get_context_param( array( 'default' => 'view' ) ); - $params['page'] = array( - 'description' => __( 'Current page of the collection.', 'woocommerce' ), - 'type' => 'integer', - 'default' => 1, - 'sanitize_callback' => 'absint', - 'validate_callback' => 'rest_validate_request_arg', - 'minimum' => 1, + $params = parent::get_collection_params(); + $params['orderby']['enum'] = array( + 'date', + 'net_revenue', + 'orders_count', + 'items_sold', + 'sku', ); - $params['per_page'] = array( - 'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce' ), - 'type' => 'integer', - 'default' => 10, - 'minimum' => 1, - 'maximum' => 100, - 'sanitize_callback' => 'absint', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['after'] = array( - 'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'date-time', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['before'] = array( - 'description' => __( 'Limit response to resources published before a given ISO8601 compliant date.', 'woocommerce' ), - 'type' => 'string', - 'format' => 'date-time', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['match'] = array( + $params['match'] = array( 'description' => __( 'Indicates whether all the conditions should be true for the resulting set, or if any one of them is sufficient. Match affects the following parameters: status_is, status_is_not, product_includes, product_excludes, coupon_includes, coupon_excludes, customer, categories', 'woocommerce' ), 'type' => 'string', 'default' => 'all', @@ -285,27 +263,7 @@ class Controller extends ReportsController implements ExportableInterface { ), 'validate_callback' => 'rest_validate_request_arg', ); - $params['order'] = array( - 'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'desc', - 'enum' => array( 'asc', 'desc' ), - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['orderby'] = array( - 'description' => __( 'Sort collection by object attribute.', 'woocommerce' ), - 'type' => 'string', - 'default' => 'date', - 'enum' => array( - 'date', - 'net_revenue', - 'orders_count', - 'items_sold', - 'sku', - ), - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['product_includes'] = array( + $params['product_includes'] = array( 'description' => __( 'Limit result set to items that have the specified parent product(s).', 'woocommerce' ), 'type' => 'array', 'items' => array( @@ -315,7 +273,7 @@ class Controller extends ReportsController implements ExportableInterface { 'sanitize_callback' => 'wp_parse_id_list', 'validate_callback' => 'rest_validate_request_arg', ); - $params['product_excludes'] = array( + $params['product_excludes'] = array( 'description' => __( 'Limit result set to items that don\'t have the specified parent product(s).', 'woocommerce' ), 'type' => 'array', 'items' => array( @@ -325,7 +283,7 @@ class Controller extends ReportsController implements ExportableInterface { 'validate_callback' => 'rest_validate_request_arg', 'sanitize_callback' => 'wp_parse_id_list', ); - $params['variations'] = array( + $params['variations'] = array( 'description' => __( 'Limit result to items with specified variation ids.', 'woocommerce' ), 'type' => 'array', 'sanitize_callback' => 'wp_parse_id_list', @@ -334,14 +292,14 @@ class Controller extends ReportsController implements ExportableInterface { 'type' => 'integer', ), ); - $params['extended_info'] = array( + $params['extended_info'] = array( 'description' => __( 'Add additional piece of info about each variation to the report.', 'woocommerce' ), 'type' => 'boolean', 'default' => false, 'sanitize_callback' => 'wc_string_to_bool', 'validate_callback' => 'rest_validate_request_arg', ); - $params['attribute_is'] = array( + $params['attribute_is'] = array( 'description' => __( 'Limit result set to variations that include the specified attributes.', 'woocommerce' ), 'type' => 'array', 'items' => array( @@ -350,7 +308,7 @@ class Controller extends ReportsController implements ExportableInterface { 'default' => array(), 'validate_callback' => 'rest_validate_request_arg', ); - $params['attribute_is_not'] = array( + $params['attribute_is_not'] = array( 'description' => __( 'Limit result set to variations that don\'t include the specified attributes.', 'woocommerce' ), 'type' => 'array', 'items' => array( @@ -359,7 +317,7 @@ class Controller extends ReportsController implements ExportableInterface { 'default' => array(), 'validate_callback' => 'rest_validate_request_arg', ); - $params['category_includes'] = array( + $params['category_includes'] = array( 'description' => __( 'Limit result set to variations in the specified categories.', 'woocommerce' ), 'type' => 'array', 'sanitize_callback' => 'wp_parse_id_list', @@ -368,7 +326,7 @@ class Controller extends ReportsController implements ExportableInterface { 'type' => 'integer', ), ); - $params['category_excludes'] = array( + $params['category_excludes'] = array( 'description' => __( 'Limit result set to variations not in the specified categories.', 'woocommerce' ), 'type' => 'array', 'sanitize_callback' => 'wp_parse_id_list', @@ -377,13 +335,7 @@ class Controller extends ReportsController implements ExportableInterface { 'type' => 'integer', ), ); - $params['force_cache_refresh'] = array( - 'description' => __( 'Force retrieval of fresh data instead of from the cache.', 'woocommerce' ), - 'type' => 'boolean', - 'sanitize_callback' => 'wp_validate_boolean', - 'validate_callback' => 'rest_validate_request_arg', - ); - $params['products'] = array( + $params['products'] = array( 'description' => __( 'Limit result to items with specified product ids.', 'woocommerce' ), 'type' => 'array', 'sanitize_callback' => 'wp_parse_id_list', diff --git a/plugins/woocommerce/src/Admin/API/Reports/Variations/DataStore.php b/plugins/woocommerce/src/Admin/API/Reports/Variations/DataStore.php index ddcb6e048bf..5a12872ad66 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Variations/DataStore.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Variations/DataStore.php @@ -9,7 +9,6 @@ defined( 'ABSPATH' ) || exit; use Automattic\WooCommerce\Admin\API\Reports\DataStore as ReportsDataStore; use Automattic\WooCommerce\Admin\API\Reports\DataStoreInterface; -use Automattic\WooCommerce\Admin\API\Reports\TimeInterval; use Automattic\WooCommerce\Admin\API\Reports\SqlQuery; /** @@ -20,6 +19,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Table used to get the data. * + * @override ReportsDataStore::$table_name + * * @var string */ protected static $table_name = 'wc_order_product_lookup'; @@ -27,6 +28,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Cache identifier. * + * @override ReportsDataStore::$cache_key + * * @var string */ protected $cache_key = 'variations'; @@ -34,6 +37,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Mapping columns to data type to return correct response types. * + * @override ReportsDataStore::$column_types + * * @var array */ protected $column_types = array( @@ -70,12 +75,16 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Data store context used to pass to filters. * + * @override ReportsDataStore::$context + * * @var string */ protected $context = 'variations'; /** * Assign report columns once full table name has been assigned. + * + * @override ReportsDataStore::assign_report_columns() */ protected function assign_report_columns() { $table_name = self::get_db_table_name(); @@ -209,6 +218,8 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { /** * Maps ordering specified by the user to columns in the database/fields in the data. * + * @override ReportsDataStore::normalize_order_by() + * * @param string $order_by Sorting criterion. * * @return string @@ -372,146 +383,139 @@ class DataStore extends ReportsDataStore implements DataStoreInterface { } /** - * Returns the report data based on parameters supplied by the user. + * Get the default query arguments to be used by get_data(). + * These defaults are only partially applied when used via REST API, as that has its own defaults. * - * @param array $query_args Query parameters. + * @override ReportsDataStore::get_default_query_vars() * - * @return stdClass|WP_Error Data. + * @return array Query parameters. */ - public function get_data( $query_args ) { + public function get_default_query_vars() { + $defaults = parent::get_default_query_vars(); + $defaults['product_includes'] = array(); + $defaults['variation_includes'] = array(); + $defaults['extended_info'] = false; + + return $defaults; + } + + /** + * Returns the report data based on normalized parameters. + * Will be called by `get_data` if there is no data in cache. + * + * @override ReportsDataStore::get_noncached_data() + * + * @see get_data + * @param array $query_args Query parameters. + * @return stdClass|WP_Error Data object `{ totals: *, intervals: array, total: int, pages: int, page_no: int }`, or error. + */ + public function get_noncached_data( $query_args ) { global $wpdb; $table_name = self::get_db_table_name(); - // These defaults are only partially applied when used via REST API, as that has its own defaults. - $defaults = array( - 'per_page' => get_option( 'posts_per_page' ), - 'page' => 1, - 'order' => 'DESC', - 'orderby' => 'date', - 'before' => TimeInterval::default_before(), - 'after' => TimeInterval::default_after(), - 'fields' => '*', - 'product_includes' => array(), - 'variation_includes' => array(), - 'extended_info' => false, + $this->initialize_queries(); + + $data = (object) array( + 'data' => array(), + 'total' => 0, + 'pages' => 0, + 'page_no' => 0, ); - $query_args = wp_parse_args( $query_args, $defaults ); - $this->normalize_timezones( $query_args, $defaults ); - /* - * We need to get the cache key here because - * parent::update_intervals_sql_params() modifies $query_args. - */ - $cache_key = $this->get_cache_key( $query_args ); - $data = $this->get_cached_data( $cache_key ); + $selections = $this->selected_columns( $query_args ); + $included_variations = + ( isset( $query_args['variation_includes'] ) && is_array( $query_args['variation_includes'] ) ) + ? $query_args['variation_includes'] + : array(); + $params = $this->get_limit_params( $query_args ); + $this->add_sql_query_params( $query_args ); - if ( false === $data ) { - $this->initialize_queries(); + if ( count( $included_variations ) > 0 ) { + $total_results = count( $included_variations ); + $total_pages = (int) ceil( $total_results / $params['per_page'] ); - $data = (object) array( - 'data' => array(), - 'total' => 0, - 'pages' => 0, - 'page_no' => 0, - ); + $this->subquery->clear_sql_clause( 'select' ); + $this->subquery->add_sql_clause( 'select', $selections ); - $selections = $this->selected_columns( $query_args ); - $included_variations = - ( isset( $query_args['variation_includes'] ) && is_array( $query_args['variation_includes'] ) ) - ? $query_args['variation_includes'] - : array(); - $params = $this->get_limit_params( $query_args ); - $this->add_sql_query_params( $query_args ); - - if ( count( $included_variations ) > 0 ) { - $total_results = count( $included_variations ); - $total_pages = (int) ceil( $total_results / $params['per_page'] ); - - $this->subquery->clear_sql_clause( 'select' ); - $this->subquery->add_sql_clause( 'select', $selections ); - - if ( 'date' === $query_args['orderby'] ) { - $this->subquery->add_sql_clause( 'select', ", {$table_name}.date_created" ); - } - - $fields = $this->get_fields( $query_args ); - $join_selections = $this->format_join_selections( $fields, array( 'variation_id' ) ); - $ids_table = $this->get_ids_table( $included_variations, 'variation_id' ); - - $this->add_sql_clause( 'select', $join_selections ); - $this->add_sql_clause( 'from', '(' ); - $this->add_sql_clause( 'from', $this->subquery->get_query_statement() ); - $this->add_sql_clause( 'from', ") AS {$table_name}" ); - $this->add_sql_clause( - 'right_join', - "RIGHT JOIN ( {$ids_table} ) AS default_results - ON default_results.variation_id = {$table_name}.variation_id" - ); - - $variations_query = $this->get_query_statement(); - } else { - - $this->subquery->clear_sql_clause( 'select' ); - $this->subquery->add_sql_clause( 'select', $selections ); - - /** - * Experimental: Filter the Variations SQL query allowing extensions to add additional SQL clauses. - * - * @since 7.4.0 - * @param array $query_args Query parameters. - * @param SqlQuery $subquery Variations query class. - */ - apply_filters( 'experimental_woocommerce_analytics_variations_additional_clauses', $query_args, $this->subquery ); - - /* phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared */ - $db_records_count = (int) $wpdb->get_var( - "SELECT COUNT(*) FROM ( - {$this->subquery->get_query_statement()} - ) AS tt" - ); - /* phpcs:enable */ - - $total_results = $db_records_count; - $total_pages = (int) ceil( $db_records_count / $params['per_page'] ); - - if ( $query_args['page'] < 1 || $query_args['page'] > $total_pages ) { - return $data; - } - - $this->subquery->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); - $this->subquery->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) ); - $variations_query = $this->subquery->get_query_statement(); + if ( 'date' === $query_args['orderby'] ) { + $this->subquery->add_sql_clause( 'select', ", {$table_name}.date_created" ); } - /* phpcs:disable WordPress.DB.PreparedSQL.NotPrepared */ - $product_data = $wpdb->get_results( - $variations_query, - ARRAY_A + $fields = $this->get_fields( $query_args ); + $join_selections = $this->format_join_selections( $fields, array( 'variation_id' ) ); + $ids_table = $this->get_ids_table( $included_variations, 'variation_id' ); + + $this->add_sql_clause( 'select', $join_selections ); + $this->add_sql_clause( 'from', '(' ); + $this->add_sql_clause( 'from', $this->subquery->get_query_statement() ); + $this->add_sql_clause( 'from', ") AS {$table_name}" ); + $this->add_sql_clause( + 'right_join', + "RIGHT JOIN ( {$ids_table} ) AS default_results + ON default_results.variation_id = {$table_name}.variation_id" + ); + + $variations_query = $this->get_query_statement(); + } else { + + $this->subquery->clear_sql_clause( 'select' ); + $this->subquery->add_sql_clause( 'select', $selections ); + + /** + * Experimental: Filter the Variations SQL query allowing extensions to add additional SQL clauses. + * + * @since 7.4.0 + * @param array $query_args Query parameters. + * @param SqlQuery $subquery Variations query class. + */ + apply_filters( 'experimental_woocommerce_analytics_variations_additional_clauses', $query_args, $this->subquery ); + + /* phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared */ + $db_records_count = (int) $wpdb->get_var( + "SELECT COUNT(*) FROM ( + {$this->subquery->get_query_statement()} + ) AS tt" ); /* phpcs:enable */ - if ( null === $product_data ) { + $total_results = $db_records_count; + $total_pages = (int) ceil( $db_records_count / $params['per_page'] ); + + if ( $query_args['page'] < 1 || $query_args['page'] > $total_pages ) { return $data; } - $this->include_extended_info( $product_data, $query_args ); - - if ( $query_args['extended_info'] ) { - $this->fill_deleted_product_name( $product_data ); - } - - $product_data = array_map( array( $this, 'cast_numbers' ), $product_data ); - $data = (object) array( - 'data' => $product_data, - 'total' => $total_results, - 'pages' => $total_pages, - 'page_no' => (int) $query_args['page'], - ); - - $this->set_cached_data( $cache_key, $data ); + $this->subquery->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); + $this->subquery->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) ); + $variations_query = $this->subquery->get_query_statement(); } + /* phpcs:disable WordPress.DB.PreparedSQL.NotPrepared */ + $product_data = $wpdb->get_results( + $variations_query, + ARRAY_A + ); + /* phpcs:enable */ + + if ( null === $product_data ) { + return $data; + } + + $this->include_extended_info( $product_data, $query_args ); + + if ( $query_args['extended_info'] ) { + $this->fill_deleted_product_name( $product_data ); + } + + $product_data = array_map( array( $this, 'cast_numbers' ), $product_data ); + $data = (object) array( + 'data' => $product_data, + 'total' => $total_results, + 'pages' => $total_pages, + 'page_no' => (int) $query_args['page'], + ); + return $data; } diff --git a/plugins/woocommerce/src/Admin/API/Reports/Variations/Query.php b/plugins/woocommerce/src/Admin/API/Reports/Variations/Query.php index 906e9c70a0f..da0d19ec78a 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Variations/Query.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Variations/Query.php @@ -22,24 +22,34 @@ use Automattic\WooCommerce\Admin\API\Reports\Query as ReportsQuery; /** * API\Reports\Variations\Query + * + * @deprecated 9.3.0 Variations\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. */ class Query extends ReportsQuery { /** * Valid fields for Products report. * + * @deprecated 9.3.0 Variations\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. + * * @return array */ protected function get_default_query_vars() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + return array(); } /** * Get product data based on the current query vars. * + * @deprecated 9.3.0 Variations\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. + * * @return array */ public function get_data() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + $args = apply_filters( 'woocommerce_analytics_variations_query_args', $this->get_query_vars() ); $data_store = \WC_Data_Store::load( 'report-variations' ); diff --git a/plugins/woocommerce/src/Admin/API/Reports/Variations/Stats/Controller.php b/plugins/woocommerce/src/Admin/API/Reports/Variations/Stats/Controller.php index 7aa2b7f7016..68e6590dce7 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Variations/Stats/Controller.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Variations/Stats/Controller.php @@ -9,8 +9,8 @@ namespace Automattic\WooCommerce\Admin\API\Reports\Variations\Stats; defined( 'ABSPATH' ) || exit; +use Automattic\WooCommerce\Admin\API\Reports\GenericQuery; use Automattic\WooCommerce\Admin\API\Reports\GenericStatsController; -use Automattic\WooCommerce\Admin\API\Reports\ParameterException; use WP_REST_Request; use WP_REST_Response; @@ -46,12 +46,25 @@ class Controller extends GenericStatsController { } /** - * Get all reports. + * Get data from `'variations-stats'` GenericQuery. * - * @param WP_REST_Request $request Request data. - * @return array|WP_Error + * @override GenericController::get_datastore_data() + * + * @param array $query_args Query arguments. + * @return mixed Results from the data store. */ - public function get_items( $request ) { + protected function get_datastore_data( $query_args = array() ) { + $query = new GenericQuery( $query_args, 'variations-stats' ); + return $query->get_data(); + } + + /** + * Maps query arguments from the REST request, to be fed to Query. + * + * @param \WP_REST_Request $request Full request object. + * @return array Simplified array of params. + */ + protected function prepare_reports_query( $request ) { $query_args = array( 'fields' => array( 'items_sold', @@ -79,36 +92,13 @@ class Controller extends GenericStatsController { } } - $query = new Query( $query_args ); - try { - $report_data = $query->get_data(); - } catch ( ParameterException $e ) { - return new \WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) ); - } - - $out_data = array( - 'totals' => get_object_vars( $report_data->totals ), - 'intervals' => array(), - ); - - foreach ( $report_data->intervals as $interval_data ) { - $item = $this->prepare_item_for_response( $interval_data, $request ); - $out_data['intervals'][] = $this->prepare_response_for_collection( $item ); - } - - return $this->add_pagination_headers( - $request, - $out_data, - (int) $report_data->total, - (int) $report_data->page_no, - (int) $report_data->pages - ); + return $query_args; } /** - * Prepare a report object for serialization. + * Prepare a report data item for serialization. * - * @param array $report Report data. + * @param array $report Report data item as returned from Data Store. * @param WP_REST_Request $request Request object. * @return WP_REST_Response */ @@ -288,15 +278,6 @@ class Controller extends GenericStatsController { ), 'validate_callback' => 'rest_validate_request_arg', ); - $params['fields'] = array( - 'description' => __( 'Limit stats fields to the specified items.', 'woocommerce' ), - 'type' => 'array', - 'sanitize_callback' => 'wp_parse_slug_list', - 'validate_callback' => 'rest_validate_request_arg', - 'items' => array( - 'type' => 'string', - ), - ); $params['attribute_is'] = array( 'description' => __( 'Limit result set to orders that include products with the specified attributes.', 'woocommerce' ), 'type' => 'array', diff --git a/plugins/woocommerce/src/Admin/API/Reports/Variations/Stats/DataStore.php b/plugins/woocommerce/src/Admin/API/Reports/Variations/Stats/DataStore.php index 0f928ddd98d..7ddfd78177b 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Variations/Stats/DataStore.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Variations/Stats/DataStore.php @@ -10,16 +10,19 @@ defined( 'ABSPATH' ) || exit; use Automattic\WooCommerce\Admin\API\Reports\Variations\DataStore as VariationsDataStore; use Automattic\WooCommerce\Admin\API\Reports\DataStoreInterface; use Automattic\WooCommerce\Admin\API\Reports\TimeInterval; -use Automattic\WooCommerce\Admin\API\Reports\SqlQuery; +use Automattic\WooCommerce\Admin\API\Reports\StatsDataStoreTrait; /** * API\Reports\Variations\Stats\DataStore. */ class DataStore extends VariationsDataStore implements DataStoreInterface { + use StatsDataStoreTrait; /** * Mapping columns to data type to return correct response types. * + * @override VariationsDataStore::$column_types + * * @var array */ protected $column_types = array( @@ -32,6 +35,8 @@ class DataStore extends VariationsDataStore implements DataStoreInterface { /** * Cache identifier. * + * @override VariationsDataStore::$cache_key + * * @var string */ protected $cache_key = 'variations_stats'; @@ -39,12 +44,16 @@ class DataStore extends VariationsDataStore implements DataStoreInterface { /** * Data store context used to pass to filters. * + * @override VariationsDataStore::$context + * * @var string */ protected $context = 'variations_stats'; /** * Assign report columns once full table name has been assigned. + * + * @override VariationsDataStore::assign_report_columns() */ protected function assign_report_columns() { $table_name = self::get_db_table_name(); @@ -133,144 +142,131 @@ class DataStore extends VariationsDataStore implements DataStoreInterface { } /** - * Returns the report data based on parameters supplied by the user. + * Get the default query arguments to be used by get_data(). + * These defaults are only partially applied when used via REST API, as that has its own defaults. * - * @since 3.5.0 - * @param array $query_args Query parameters. - * @return stdClass|WP_Error Data. + * @override VariationsDataStore::get_default_query_vars() + * + * @return array Query parameters. */ - public function get_data( $query_args ) { + public function get_default_query_vars() { + $defaults = parent::get_default_query_vars(); + $defaults['category_includes'] = array(); + $defaults['interval'] = 'week'; + unset( $defaults['extended_info'] ); + + return $defaults; + } + + /** + * Returns the report data based on normalized parameters. + * Will be called by `get_data` if there is no data in cache. + * + * @override VariationsDataStore::get_noncached_stats_data() + * + * @see get_data + * @see get_noncached_stats_data + * @param array $query_args Query parameters. + * @param array $params Query limit parameters. + * @param stdClass $data Reference to the data object to fill. + * @param int $expected_interval_count Number of expected intervals. + * @return stdClass|WP_Error Data object `{ totals: *, intervals: array, total: int, pages: int, page_no: int }`, or error. + */ + public function get_noncached_stats_data( $query_args, $params, &$data, $expected_interval_count ) { global $wpdb; $table_name = self::get_db_table_name(); - // These defaults are only partially applied when used via REST API, as that has its own defaults. - $defaults = array( - 'per_page' => get_option( 'posts_per_page' ), - 'page' => 1, - 'order' => 'DESC', - 'orderby' => 'date', - 'before' => TimeInterval::default_before(), - 'after' => TimeInterval::default_after(), - 'fields' => '*', - 'category_includes' => array(), - 'interval' => 'week', - 'product_includes' => array(), - 'variation_includes' => array(), + $this->initialize_queries(); + + $selections = $this->selected_columns( $query_args ); + + $this->update_sql_query_params( $query_args ); + $this->get_limit_sql_params( $query_args ); + $this->interval_query->add_sql_clause( 'where_time', $this->get_sql_clause( 'where_time' ) ); + + /* phpcs:disable WordPress.DB.PreparedSQL.NotPrepared */ + $db_intervals = $wpdb->get_col( + $this->interval_query->get_query_statement() ); - $query_args = wp_parse_args( $query_args, $defaults ); - $this->normalize_timezones( $query_args, $defaults ); + /* phpcs:enable */ - /* - * We need to get the cache key here because - * parent::update_intervals_sql_params() modifies $query_args. - */ - $cache_key = $this->get_cache_key( $query_args ); - $data = $this->get_cached_data( $cache_key ); + $db_interval_count = count( $db_intervals ); - if ( false === $data ) { - $this->initialize_queries(); + $intervals = array(); + $this->update_intervals_sql_params( $query_args, $db_interval_count, $expected_interval_count, $table_name ); + $this->total_query->add_sql_clause( 'select', $selections ); + $this->total_query->add_sql_clause( 'where_time', $this->get_sql_clause( 'where_time' ) ); - $selections = $this->selected_columns( $query_args ); - $params = $this->get_limit_params( $query_args ); + /* phpcs:disable WordPress.DB.PreparedSQL.NotPrepared */ + $totals = $wpdb->get_results( + $this->total_query->get_query_statement(), + ARRAY_A + ); + /* phpcs:enable */ - $this->update_sql_query_params( $query_args ); - $this->get_limit_sql_params( $query_args ); - $this->interval_query->add_sql_clause( 'where_time', $this->get_sql_clause( 'where_time' ) ); + // phpcs:ignore Generic.Commenting.Todo.TaskFound + // @todo remove these assignements when refactoring segmenter classes to use query objects. + $totals_query = array( + 'from_clause' => $this->total_query->get_sql_clause( 'join' ), + 'where_time_clause' => $this->total_query->get_sql_clause( 'where_time' ), + 'where_clause' => $this->total_query->get_sql_clause( 'where' ), + ); + $intervals_query = array( + 'select_clause' => $this->get_sql_clause( 'select' ), + 'from_clause' => $this->interval_query->get_sql_clause( 'join' ), + 'where_time_clause' => $this->interval_query->get_sql_clause( 'where_time' ), + 'where_clause' => $this->interval_query->get_sql_clause( 'where' ), + 'order_by' => $this->get_sql_clause( 'order_by' ), + 'limit' => $this->get_sql_clause( 'limit' ), + ); + $segmenter = new Segmenter( $query_args, $this->report_columns ); + $totals[0]['segments'] = $segmenter->get_totals_segments( $totals_query, $table_name ); - /* phpcs:disable WordPress.DB.PreparedSQL.NotPrepared */ - $db_intervals = $wpdb->get_col( - $this->interval_query->get_query_statement() - ); - /* phpcs:enable */ - - $db_interval_count = count( $db_intervals ); - $expected_interval_count = TimeInterval::intervals_between( $query_args['after'], $query_args['before'], $query_args['interval'] ); - $total_pages = (int) ceil( $expected_interval_count / $params['per_page'] ); - if ( $query_args['page'] < 1 || $query_args['page'] > $total_pages ) { - return array(); - } - - $intervals = array(); - $this->update_intervals_sql_params( $query_args, $db_interval_count, $expected_interval_count, $table_name ); - $this->total_query->add_sql_clause( 'select', $selections ); - $this->total_query->add_sql_clause( 'where_time', $this->get_sql_clause( 'where_time' ) ); - - /* phpcs:disable WordPress.DB.PreparedSQL.NotPrepared */ - $totals = $wpdb->get_results( - $this->total_query->get_query_statement(), - ARRAY_A - ); - /* phpcs:enable */ - - // @todo remove these assignements when refactoring segmenter classes to use query objects. - $totals_query = array( - 'from_clause' => $this->total_query->get_sql_clause( 'join' ), - 'where_time_clause' => $this->total_query->get_sql_clause( 'where_time' ), - 'where_clause' => $this->total_query->get_sql_clause( 'where' ), - ); - $intervals_query = array( - 'select_clause' => $this->get_sql_clause( 'select' ), - 'from_clause' => $this->interval_query->get_sql_clause( 'join' ), - 'where_time_clause' => $this->interval_query->get_sql_clause( 'where_time' ), - 'where_clause' => $this->interval_query->get_sql_clause( 'where' ), - 'order_by' => $this->get_sql_clause( 'order_by' ), - 'limit' => $this->get_sql_clause( 'limit' ), - ); - $segmenter = new Segmenter( $query_args, $this->report_columns ); - $totals[0]['segments'] = $segmenter->get_totals_segments( $totals_query, $table_name ); - - if ( null === $totals ) { - return new \WP_Error( 'woocommerce_analytics_variations_stats_result_failed', __( 'Sorry, fetching revenue data failed.', 'woocommerce' ) ); - } - - $this->interval_query->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); - $this->interval_query->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) ); - $this->interval_query->add_sql_clause( 'select', ", MAX({$table_name}.date_created) AS datetime_anchor" ); - if ( '' !== $selections ) { - $this->interval_query->add_sql_clause( 'select', ', ' . $selections ); - } - - /* phpcs:disable WordPress.DB.PreparedSQL.NotPrepared */ - $intervals = $wpdb->get_results( - $this->interval_query->get_query_statement(), - ARRAY_A - ); - /* phpcs:enable */ - - if ( null === $intervals ) { - return new \WP_Error( 'woocommerce_analytics_variations_stats_result_failed', __( 'Sorry, fetching revenue data failed.', 'woocommerce' ) ); - } - - $totals = (object) $this->cast_numbers( $totals[0] ); - - $data = (object) array( - 'totals' => $totals, - 'intervals' => $intervals, - 'total' => $expected_interval_count, - 'pages' => $total_pages, - 'page_no' => (int) $query_args['page'], - ); - - if ( TimeInterval::intervals_missing( $expected_interval_count, $db_interval_count, $params['per_page'], $query_args['page'], $query_args['order'], $query_args['orderby'], count( $intervals ) ) ) { - $this->fill_in_missing_intervals( $db_intervals, $query_args['adj_after'], $query_args['adj_before'], $query_args['interval'], $data ); - $this->sort_intervals( $data, $query_args['orderby'], $query_args['order'] ); - $this->remove_extra_records( $data, $query_args['page'], $params['per_page'], $db_interval_count, $expected_interval_count, $query_args['orderby'], $query_args['order'] ); - } else { - $this->update_interval_boundary_dates( $query_args['after'], $query_args['before'], $query_args['interval'], $data->intervals ); - } - $segmenter->add_intervals_segments( $data, $intervals_query, $table_name ); - $this->create_interval_subtotals( $data->intervals ); - - $this->set_cached_data( $cache_key, $data ); + if ( null === $totals ) { + return new \WP_Error( 'woocommerce_analytics_variations_stats_result_failed', __( 'Sorry, fetching revenue data failed.', 'woocommerce' ) ); } + $this->interval_query->add_sql_clause( 'order_by', $this->get_sql_clause( 'order_by' ) ); + $this->interval_query->add_sql_clause( 'limit', $this->get_sql_clause( 'limit' ) ); + $this->interval_query->add_sql_clause( 'select', ", MAX({$table_name}.date_created) AS datetime_anchor" ); + if ( '' !== $selections ) { + $this->interval_query->add_sql_clause( 'select', ', ' . $selections ); + } + + /* phpcs:disable WordPress.DB.PreparedSQL.NotPrepared */ + $intervals = $wpdb->get_results( + $this->interval_query->get_query_statement(), + ARRAY_A + ); + /* phpcs:enable */ + + if ( null === $intervals ) { + return new \WP_Error( 'woocommerce_analytics_variations_stats_result_failed', __( 'Sorry, fetching revenue data failed.', 'woocommerce' ) ); + } + + $totals = (object) $this->cast_numbers( $totals[0] ); + + $data->totals = $totals; + $data->intervals = $intervals; + + if ( TimeInterval::intervals_missing( $expected_interval_count, $db_interval_count, $params['per_page'], $query_args['page'], $query_args['order'], $query_args['orderby'], count( $intervals ) ) ) { + $this->fill_in_missing_intervals( $db_intervals, $query_args['adj_after'], $query_args['adj_before'], $query_args['interval'], $data ); + $this->sort_intervals( $data, $query_args['orderby'], $query_args['order'] ); + $this->remove_extra_records( $data, $query_args['page'], $params['per_page'], $db_interval_count, $expected_interval_count, $query_args['orderby'], $query_args['order'] ); + } else { + $this->update_interval_boundary_dates( $query_args['after'], $query_args['before'], $query_args['interval'], $data->intervals ); + } + $segmenter->add_intervals_segments( $data, $intervals_query, $table_name ); + return $data; } /** * Normalizes order_by clause to match to SQL query. * + * @override VariationsDataStore::normalize_order_by() + * * @param string $order_by Order by option requeste by user. * @return string */ @@ -281,18 +277,4 @@ class DataStore extends VariationsDataStore implements DataStoreInterface { return $order_by; } - - /** - * Initialize query objects. - */ - protected function initialize_queries() { - $this->clear_all_clauses(); - unset( $this->subquery ); - $this->total_query = new SqlQuery( $this->context . '_total' ); - $this->total_query->add_sql_clause( 'from', self::get_db_table_name() ); - - $this->interval_query = new SqlQuery( $this->context . '_interval' ); - $this->interval_query->add_sql_clause( 'from', self::get_db_table_name() ); - $this->interval_query->add_sql_clause( 'group_by', 'time_interval' ); - } } diff --git a/plugins/woocommerce/src/Admin/API/Reports/Variations/Stats/Query.php b/plugins/woocommerce/src/Admin/API/Reports/Variations/Stats/Query.php index 092b356fb9d..0613472bb38 100644 --- a/plugins/woocommerce/src/Admin/API/Reports/Variations/Stats/Query.php +++ b/plugins/woocommerce/src/Admin/API/Reports/Variations/Stats/Query.php @@ -22,24 +22,34 @@ use Automattic\WooCommerce\Admin\API\Reports\Query as ReportsQuery; /** * API\Reports\Variations\Stats\Query + * + * @deprecated 9.3.0 Variations\Stats\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. */ class Query extends ReportsQuery { /** * Valid fields for Products report. * + * @deprecated 9.3.0 Variations\Stats\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. + * * @return array */ protected function get_default_query_vars() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + return array(); } /** * Get variations data based on the current query vars. * + * @deprecated 9.3.0 Variations\Stats\Query class is deprecated. Please use `GenericQuery`, \WC_Object_Query`, or use `DataStore` directly. + * * @return array */ public function get_data() { + wc_deprecated_function( __CLASS__ . '::' . __FUNCTION__, '9.3.0', '`GenericQuery`, `\WC_Object_Query`, or direct `DataStore` use' ); + $args = apply_filters( 'woocommerce_analytics_variations_stats_query_args', $this->get_query_vars() ); $data_store = \WC_Data_Store::load( 'report-variations-stats' ); diff --git a/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCSettings.php b/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCSettings.php index 1a8ac50c233..97f02b7452e 100644 --- a/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCSettings.php +++ b/plugins/woocommerce/src/Admin/Features/Blueprint/Exporters/ExportWCSettings.php @@ -198,7 +198,7 @@ class ExportWCSettings implements StepExporter, HasAlias { $option_info['woocommerce_store_pages_only'] = array( 'location' => 'site_visibility.general', - 'title' => 'Restrict to store pages only', + 'title' => 'Apply to store pages only', ); return compact( 'options', 'pages', 'option_info' ); diff --git a/plugins/woocommerce/src/Admin/Features/Features.php b/plugins/woocommerce/src/Admin/Features/Features.php index 330a33cb906..90ed9b28dfc 100644 --- a/plugins/woocommerce/src/Admin/Features/Features.php +++ b/plugins/woocommerce/src/Admin/Features/Features.php @@ -27,7 +27,6 @@ class Features { */ protected static $optional_features = array( 'navigation' => array( 'default' => 'no' ), - 'settings' => array( 'default' => 'no' ), 'analytics' => array( 'default' => 'yes' ), 'remote-inbox-notifications' => array( 'default' => 'yes' ), ); @@ -353,7 +352,6 @@ class Features { 'Automattic\WooCommerce\Internal\Admin\Marketing' => 'Automattic\WooCommerce\Admin\Features\Marketing', 'Automattic\WooCommerce\Internal\Admin\MobileAppBanner' => 'Automattic\WooCommerce\Admin\Features\MobileAppBanner', 'Automattic\WooCommerce\Internal\Admin\RemoteInboxNotifications' => 'Automattic\WooCommerce\Admin\Features\RemoteInboxNotifications', - 'Automattic\WooCommerce\Internal\Admin\SettingsNavigationFeature' => 'Automattic\WooCommerce\Admin\Features\Settings', 'Automattic\WooCommerce\Internal\Admin\ShippingLabelBanner' => 'Automattic\WooCommerce\Admin\Features\ShippingLabelBanner', 'Automattic\WooCommerce\Internal\Admin\ShippingLabelBannerDisplayRules' => 'Automattic\WooCommerce\Admin\Features\ShippingLabelBannerDisplayRules', 'Automattic\WooCommerce\Internal\Admin\WcPayWelcomePage' => 'Automattic\WooCommerce\Admin\Features\WcPayWelcomePage', diff --git a/plugins/woocommerce/src/Admin/Features/LaunchYourStore.php b/plugins/woocommerce/src/Admin/Features/LaunchYourStore.php index cc83a73dd15..3bfff4518fe 100644 --- a/plugins/woocommerce/src/Admin/Features/LaunchYourStore.php +++ b/plugins/woocommerce/src/Admin/Features/LaunchYourStore.php @@ -5,12 +5,13 @@ namespace Automattic\WooCommerce\Admin\Features; use Automattic\WooCommerce\Admin\PageController; use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils; use Automattic\WooCommerce\Admin\WCAdminHelper; +use Automattic\WooCommerce\Internal\Admin\WCAdminUser; /** * Takes care of Launch Your Store related actions. */ class LaunchYourStore { - const BANNER_DISMISS_USER_META_KEY = 'woocommerce_coming_soon_banner_dismissed'; + const BANNER_DISMISS_USER_META_KEY = 'coming_soon_banner_dismissed'; /** * Constructor. */ @@ -21,6 +22,7 @@ class LaunchYourStore { add_action( 'init', array( $this, 'register_launch_your_store_user_meta_fields' ) ); add_filter( 'woocommerce_tracks_event_properties', array( $this, 'append_coming_soon_global_tracks' ), 10, 2 ); add_action( 'wp_login', array( $this, 'reset_woocommerce_coming_soon_banner_dismissed' ), 10, 2 ); + add_filter( 'woocommerce_admin_get_user_data_fields', array( $this, 'add_user_data_fields' ) ); } /** @@ -160,7 +162,10 @@ class LaunchYourStore { return false; } - if ( get_user_meta( $current_user_id, self::BANNER_DISMISS_USER_META_KEY, true ) === 'yes' ) { + $has_dismissed_banner = WCAdminUser::get_user_data_field( $current_user_id, self::BANNER_DISMISS_USER_META_KEY ) + // Remove this check in WC 9.4. + || get_user_meta( $current_user_id, 'woocommerce_' . self::BANNER_DISMISS_USER_META_KEY, true ) === 'yes'; + if ( $has_dismissed_banner ) { return false; } @@ -198,6 +203,8 @@ class LaunchYourStore { /** * Register user meta fields for Launch Your Store. + * + * This should be removed in WC 9.4. */ public function register_launch_your_store_user_meta_fields() { if ( ! $this->is_manager_or_admin() ) { @@ -217,7 +224,7 @@ class LaunchYourStore { register_meta( 'user', - self::BANNER_DISMISS_USER_META_KEY, + 'woocommerce_coming_soon_banner_dismissed', array( 'type' => 'string', 'description' => 'Indicate whether the user has dismissed the coming soon notice or not.', @@ -227,6 +234,22 @@ class LaunchYourStore { ); } + /** + * Register user meta fields for Launch Your Store. + * + * @param array $user_data_fields user data fields. + * @return array + */ + public function add_user_data_fields( $user_data_fields ) { + return array_merge( + $user_data_fields, + array( + 'launch_your_store_tour_hidden', + self::BANNER_DISMISS_USER_META_KEY, + ) + ); + } + /** * Reset 'woocommerce_coming_soon_banner_dismissed' user meta to 'no'. * @@ -236,9 +259,9 @@ class LaunchYourStore { * @param object $user user object. */ public function reset_woocommerce_coming_soon_banner_dismissed( $user_login, $user ) { - $existing_meta = get_user_meta( $user->ID, self::BANNER_DISMISS_USER_META_KEY, true ); + $existing_meta = WCAdminUser::get_user_data_field( $user->ID, self::BANNER_DISMISS_USER_META_KEY ); if ( 'yes' === $existing_meta ) { - update_user_meta( $user->ID, self::BANNER_DISMISS_USER_META_KEY, 'no' ); + WCAdminUser::update_user_data_field( $user->ID, self::BANNER_DISMISS_USER_META_KEY, 'no' ); } } } diff --git a/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Task.php b/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Task.php index cfc54d4569c..063e01fd1a5 100644 --- a/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Task.php +++ b/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Task.php @@ -61,7 +61,7 @@ abstract class Task { protected $task_list; /** - * Duration to milisecond mapping. + * Duration to millisecond mapping. * * @var string */ diff --git a/plugins/woocommerce/src/Admin/Features/OnboardingTasks/TaskLists.php b/plugins/woocommerce/src/Admin/Features/OnboardingTasks/TaskLists.php index 96fb26f73cd..a60515131c6 100644 --- a/plugins/woocommerce/src/Admin/Features/OnboardingTasks/TaskLists.php +++ b/plugins/woocommerce/src/Admin/Features/OnboardingTasks/TaskLists.php @@ -297,7 +297,6 @@ class TaskLists { $task_list->add_task( $task ); } } - } /** @@ -318,8 +317,8 @@ class TaskLists { public static function get_lists_by_ids( $ids ) { return array_filter( self::$lists, - function( $list ) use ( $ids ) { - return in_array( $list->get_list_id(), $ids, true ); + function ( $task_list ) use ( $ids ) { + return in_array( $task_list->get_list_id(), $ids, true ); } ); } @@ -404,25 +403,31 @@ class TaskLists { /** * Return number of setup tasks remaining * - * @return number + * This is not updated immediately when a task is completed, but rather when task is marked as complete in the database to reduce performance impact. + * + * @return int|null */ public static function setup_tasks_remaining() { $setup_list = self::get_list( 'setup' ); - if ( ! $setup_list || $setup_list->is_hidden() || $setup_list->has_previously_completed() || $setup_list->is_complete() ) { + if ( ! $setup_list || $setup_list->is_hidden() || $setup_list->has_previously_completed() ) { return; } - $remaining_tasks = array_values( + $viewable_tasks = $setup_list->get_viewable_tasks(); + $completed_tasks = get_option( Task::COMPLETED_OPTION, array() ); + if ( ! is_array( $completed_tasks ) ) { + $completed_tasks = array(); + } + + return count( array_filter( - $setup_list->get_viewable_tasks(), - function( $task ) { - return ! $task->is_complete(); + $viewable_tasks, + function ( $task ) use ( $completed_tasks ) { + return ! in_array( $task->get_id(), $completed_tasks, true ); } ) ); - - return count( $remaining_tasks ); } /** @@ -443,7 +448,6 @@ class TaskLists { break; } } - } /** diff --git a/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/AdditionalPayments.php b/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/AdditionalPayments.php index 672b9a4a932..f5cdd68fedd 100644 --- a/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/AdditionalPayments.php +++ b/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/AdditionalPayments.php @@ -188,7 +188,7 @@ class AdditionalPayments extends Payments { */ private static function get_suggestion_gateways( $filter_by = 'category_additional' ) { $country = wc_get_base_location()['country']; - $plugin_suggestions = Init::get_suggestions(); + $plugin_suggestions = Init::get_cached_or_default_suggestions(); $plugin_suggestions = array_filter( $plugin_suggestions, function( $plugin ) use ( $country, $filter_by ) { diff --git a/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/CustomizeStore.php b/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/CustomizeStore.php index fe6a2f28b7a..91fc334318f 100644 --- a/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/CustomizeStore.php +++ b/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/CustomizeStore.php @@ -7,6 +7,8 @@ use WP_Post; /** * Customize Your Store Task + * + * @internal */ class CustomizeStore extends Task { /** diff --git a/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/Tax.php b/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/Tax.php index 662d4fec80b..ba33d9b2362 100644 --- a/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/Tax.php +++ b/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/Tax.php @@ -121,7 +121,7 @@ class Tax extends Task { } /** - * Addtional data. + * Additional data. * * @return array */ @@ -129,9 +129,11 @@ class Tax extends Task { return array( 'avalara_activated' => PluginsHelper::is_plugin_active( 'woocommerce-avatax' ), 'tax_jar_activated' => class_exists( 'WC_Taxjar' ), + 'stripe_tax_activated' => PluginsHelper::is_plugin_active( 'stripe-tax-for-woocommerce' ), 'woocommerce_tax_activated' => PluginsHelper::is_plugin_active( 'woocommerce-tax' ), 'woocommerce_shipping_activated' => PluginsHelper::is_plugin_active( 'woocommerce-shipping' ), 'woocommerce_tax_countries' => self::get_automated_support_countries(), + 'stripe_tax_countries' => self::get_stripe_tax_support_countries(), ); } @@ -162,4 +164,54 @@ class Tax extends Task { return $tax_supported_countries; } + + /** + * Get an array of countries that support Stripe tax. + * + * @return array + */ + private static function get_stripe_tax_support_countries() { + // https://docs.stripe.com/tax/supported-countries#supported-countries accurate as of 2024-08-26. + // countries with remote sales not included. + return array( + 'AU', + 'AT', + 'BE', + 'BG', + 'CA', + 'HR', + 'CY', + 'CZ', + 'DK', + 'EE', + 'FI', + 'FR', + 'DE', + 'GR', + 'HK', + 'HU', + 'IE', + 'IT', + 'JP', + 'LV', + 'LT', + 'LU', + 'MT', + 'NL', + 'NZ', + 'NO', + 'PL', + 'PT', + 'RO', + 'SG', + 'SK', + 'SI', + 'ES', + 'SE', + 'CH', + 'AE', + 'GB', + 'US', + ); + } } diff --git a/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/WooCommercePayments.php b/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/WooCommercePayments.php index 5a269ca620f..5c6d45255f6 100644 --- a/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/WooCommercePayments.php +++ b/plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/WooCommercePayments.php @@ -147,11 +147,9 @@ class WooCommercePayments extends Task { * @return bool */ public static function is_connected() { - if ( class_exists( '\WC_Payments' ) ) { - $wc_payments_gateway = \WC_Payments::get_gateway(); - return method_exists( $wc_payments_gateway, 'is_connected' ) - ? $wc_payments_gateway->is_connected() - : false; + $wc_payments_gateway = self::get_woo_payments_gateway(); + if ( $wc_payments_gateway && method_exists( $wc_payments_gateway, 'is_connected' ) ) { + return $wc_payments_gateway->is_connected(); } return false; @@ -164,11 +162,9 @@ class WooCommercePayments extends Task { * @return bool */ public static function is_account_partially_onboarded() { - if ( class_exists( '\WC_Payments' ) ) { - $wc_payments_gateway = \WC_Payments::get_gateway(); - return method_exists( $wc_payments_gateway, 'is_account_partially_onboarded' ) - ? $wc_payments_gateway->is_account_partially_onboarded() - : false; + $wc_payments_gateway = self::get_woo_payments_gateway(); + if ( $wc_payments_gateway && method_exists( $wc_payments_gateway, 'is_account_partially_onboarded' ) ) { + return $wc_payments_gateway->is_account_partially_onboarded(); } return false; @@ -195,4 +191,17 @@ class WooCommercePayments extends Task { } return false; } + + /** + * Get the WooPayments gateway. + * + * @return \WC_Payments|null + */ + private static function get_woo_payments_gateway() { + $payment_gateways = WC()->payment_gateways->payment_gateways(); + if ( isset( $payment_gateways['woocommerce_payments'] ) ) { + return $payment_gateways['woocommerce_payments']; + } + return null; + } } diff --git a/plugins/woocommerce/src/Admin/Features/PaymentGatewaySuggestions/Init.php b/plugins/woocommerce/src/Admin/Features/PaymentGatewaySuggestions/Init.php index 581f86b80b8..b6a7712aa18 100644 --- a/plugins/woocommerce/src/Admin/Features/PaymentGatewaySuggestions/Init.php +++ b/plugins/woocommerce/src/Admin/Features/PaymentGatewaySuggestions/Init.php @@ -61,6 +61,31 @@ class Init extends RemoteSpecsEngine { return $specs_to_return; } + /** + * Gets either cached or default suggestions. + * + * @return array + */ + public static function get_cached_or_default_suggestions() { + $specs = 'no' === get_option( 'woocommerce_show_marketplace_suggestions', 'yes' ) + ? DefaultPaymentGateways::get_all() + : PaymentGatewaySuggestionsDataSourcePoller::get_instance()->get_cached_specs(); + + if ( ! is_array( $specs ) || 0 === count( $specs ) ) { + $specs = DefaultPaymentGateways::get_all(); + } + /** + * Allows filtering of payment gateway suggestion specs + * + * @since 6.4.0 + * + * @param array Gateway specs. + */ + $specs = apply_filters( 'woocommerce_admin_payment_gateway_suggestion_specs', $specs ); + $results = EvaluateSuggestion::evaluate_specs( $specs ); + return $results['suggestions']; + } + /** * Delete the specs transient. */ diff --git a/plugins/woocommerce/src/Admin/Features/PaymentGatewaySuggestions/PaymentGatewaySuggestionsDataSourcePoller.php b/plugins/woocommerce/src/Admin/Features/PaymentGatewaySuggestions/PaymentGatewaySuggestionsDataSourcePoller.php index 5e3608b635d..d53ac724afd 100644 --- a/plugins/woocommerce/src/Admin/Features/PaymentGatewaySuggestions/PaymentGatewaySuggestionsDataSourcePoller.php +++ b/plugins/woocommerce/src/Admin/Features/PaymentGatewaySuggestions/PaymentGatewaySuggestionsDataSourcePoller.php @@ -2,7 +2,7 @@ namespace Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions; -use Automattic\WooCommerce\Admin\DataSourcePoller; +use Automattic\WooCommerce\Admin\RemoteSpecs\DataSourcePoller; /** * Specs data source poller class for payment gateway suggestions. diff --git a/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/Init.php b/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/Init.php index d31f8cf0e04..7e274815e91 100644 --- a/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/Init.php +++ b/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/Init.php @@ -172,7 +172,7 @@ class Init { if ( ! PageController::is_admin_page() ) { return; } - // Dequeing this to avoid conflicts, until we remove the 'woocommerce-page' class. + // Dequeuing this to avoid conflicts, until we remove the 'woocommerce-page' class. wp_dequeue_style( 'woocommerce-blocktheme' ); } diff --git a/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/ProductFormsController.php b/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/ProductFormsController.php index d5b586d7ad0..f9fc30bc84e 100644 --- a/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/ProductFormsController.php +++ b/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/ProductFormsController.php @@ -59,7 +59,7 @@ class ProductFormsController { } /** - * Create ot update a product_form post for each product form template. + * Create or update a product_form post for each product form template. * If the post already exists, it will be updated. * If the post does not exist, it will be created even if the action is `update`. * diff --git a/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/RedirectionController.php b/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/RedirectionController.php index c5e2e135243..be9cb5e4d61 100644 --- a/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/RedirectionController.php +++ b/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/RedirectionController.php @@ -96,7 +96,7 @@ class RedirectionController { /** * Check if a product is supported by the new experience. * - * @param array $product_templates The registered product teamplates. + * @param array $product_templates The registered product templates. */ public function set_product_templates( array $product_templates ): void { $this->product_templates = $product_templates; diff --git a/plugins/woocommerce/src/Admin/Features/ProductDataViews/Init.php b/plugins/woocommerce/src/Admin/Features/ProductDataViews/Init.php new file mode 100644 index 00000000000..26909eff3ce --- /dev/null +++ b/plugins/woocommerce/src/Admin/Features/ProductDataViews/Init.php @@ -0,0 +1,145 @@ +has_data_views_support() ) { + add_action( 'admin_menu', array( $this, 'woocommerce_add_new_products_dashboard' ) ); + add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) ); + add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); + + if ( $this->is_product_data_view_page() ) { + add_filter( + 'admin_body_class', + static function ( $classes ) { + return "$classes is-fullscreen-mode"; + } + ); + } + } + } + + /** + * Returns true if we are on a JS powered admin page. + */ + private static function is_product_data_view_page() { + // phpcs:disable WordPress.Security.NonceVerification + return isset( $_GET['page'] ) && 'woocommerce-products-dashboard' === $_GET['page']; + // phpcs:enable WordPress.Security.NonceVerification + } + + /** + * Checks for data views support. + */ + private function has_data_views_support() { + if ( Utils::wp_version_compare( '6.6', '>=' ) ) { + return true; + } + + if ( is_plugin_active( 'gutenberg/gutenberg.php' ) ) { + $gutenberg_version = ''; + + if ( defined( 'GUTENBERG_VERSION' ) ) { + $gutenberg_version = GUTENBERG_VERSION; + } + + if ( ! $gutenberg_version ) { + $gutenberg_data = get_file_data( + WP_PLUGIN_DIR . '/gutenberg/gutenberg.php', + array( 'Version' => 'Version' ) + ); + $gutenberg_version = $gutenberg_data['Version']; + } + return version_compare( $gutenberg_version, '19.0', '>=' ); + } + + return false; + } + + /** + * Enqueue styles needed for the rich text editor. + */ + public function enqueue_styles() { + if ( ! $this->is_product_data_view_page() ) { + return; + } + wp_enqueue_style( 'wc-product-editor' ); + } + + /** + * Enqueue scripts needed for the product form block editor. + */ + public function enqueue_scripts() { + if ( ! $this->is_product_data_view_page() ) { + return; + } + + $script_handle = 'wc-admin-edit-product'; + wp_register_script( $script_handle, '', array( 'wp-blocks' ), '0.1.0', true ); + wp_enqueue_script( $script_handle ); + wp_enqueue_media(); + wp_register_style( 'wc-global-presets', false ); // phpcs:ignore + wp_add_inline_style( 'wc-global-presets', wp_get_global_stylesheet( array( 'presets' ) ) ); + wp_enqueue_style( 'wc-global-presets' ); + } + + /** + * Replaces the default posts menu item with the new posts dashboard. + */ + public function woocommerce_add_new_products_dashboard() { + $gutenberg_experiments = get_option( 'gutenberg-experiments' ); + if ( ! $gutenberg_experiments ) { + return; + } + $ptype_obj = get_post_type_object( 'product' ); + add_submenu_page( + 'woocommerce', + $ptype_obj->labels->name, + esc_html__( 'All Products', 'woocommerce' ), + 'manage_woocommerce', + 'woocommerce-products-dashboard', + array( $this, 'woocommerce_products_dashboard' ), + 1 + ); + } + + /** + * Renders the new posts dashboard page. + */ + public function woocommerce_products_dashboard() { + $suffix = Constants::is_true( 'SCRIPT_DEBUG' ) ? '' : '.min'; + $version = Constants::get_constant( 'WC_VERSION' ); + if ( function_exists( 'gutenberg_url' ) ) { + // phpcs:disable WordPress.WP.EnqueuedResourceParameters.MissingVersion + wp_register_style( + 'wp-gutenberg-posts-dashboard', + gutenberg_url( 'build/edit-site/posts.css', __FILE__ ), + array( 'wp-components' ), + ); + // phpcs:enable WordPress.WP.EnqueuedResourceParameters.MissingVersion + wp_enqueue_style( 'wp-gutenberg-posts-dashboard' ); + } + WCAdminAssets::get_instance(); + wp_enqueue_script( 'wc-admin-product-editor', WC()->plugin_url() . '/assets/js/admin/product-editor' . $suffix . '.js', array( 'wc-product-editor' ), $version, false ); + wp_add_inline_script( 'wp-edit-site', 'window.wc.productEditor.initializeProductsDashboard( "woocommerce-products-dashboard" );', 'after' ); + wp_enqueue_script( 'wp-edit-site' ); + + echo '
        '; + } +} diff --git a/plugins/woocommerce/src/Internal/Admin/SettingsNavigationFeature.php b/plugins/woocommerce/src/Admin/Features/Settings.php similarity index 81% rename from plugins/woocommerce/src/Internal/Admin/SettingsNavigationFeature.php rename to plugins/woocommerce/src/Admin/Features/Settings.php index 49a14b96b28..34ab3577686 100644 --- a/plugins/woocommerce/src/Internal/Admin/SettingsNavigationFeature.php +++ b/plugins/woocommerce/src/Admin/Features/Settings.php @@ -1,21 +1,16 @@ - __( 'Settings', 'woocommerce' ), - 'desc' => __( - 'Adds the new WooCommerce settings UI.', - 'woocommerce' - ), - 'id' => 'woocommerce_settings_enabled', - 'type' => 'checkbox', - ); - - return $features; - } - /** * Registers settings pages. */ diff --git a/plugins/woocommerce/src/Admin/Notes/Note.php b/plugins/woocommerce/src/Admin/Notes/Note.php index 8191b6aede7..de8cde59dc7 100644 --- a/plugins/woocommerce/src/Admin/Notes/Note.php +++ b/plugins/woocommerce/src/Admin/Notes/Note.php @@ -676,7 +676,7 @@ class Note extends \WC_Data { * * @param string $note_action_name Name of action to add a nonce to. * @param string $nonce_action The nonce action. - * @param string $nonce_name The nonce Name. This is used as the paramater name in the resulting URL for the action. + * @param string $nonce_name The nonce Name. This is used as the parameter name in the resulting URL for the action. * @return void * @throws \Exception If note name cannot be found. */ diff --git a/plugins/woocommerce/src/Admin/PageController.php b/plugins/woocommerce/src/Admin/PageController.php index 16d8b7125a2..0d8738d1ba5 100644 --- a/plugins/woocommerce/src/Admin/PageController.php +++ b/plugins/woocommerce/src/Admin/PageController.php @@ -220,7 +220,7 @@ class PageController { */ public function get_current_page() { // If 'current_screen' hasn't fired yet, the current page calculation - // will fail which causes `false` to be returned for all subsquent calls. + // will fail which causes `false` to be returned for all subsequent calls. if ( ! did_action( 'current_screen' ) ) { _doing_it_wrong( __FUNCTION__, esc_html__( 'Current page retrieval should be called on or after the `current_screen` hook.', 'woocommerce' ), '0.16.0' ); } @@ -394,7 +394,7 @@ class PageController { } /** - * Returns true if we are on a page registed with this controller. + * Returns true if we are on a page registered with this controller. * * @return boolean */ @@ -530,7 +530,7 @@ class PageController { */ public function remove_app_entry_page_menu_item() { global $submenu; - // User does not have capabilites to see the submenu. + // User does not have capabilities to see the submenu. if ( ! current_user_can( 'manage_woocommerce' ) || empty( $submenu['woocommerce'] ) ) { return; } diff --git a/plugins/woocommerce/src/Admin/PluginsHelper.php b/plugins/woocommerce/src/Admin/PluginsHelper.php index cc5d302908b..efcf9152f9e 100644 --- a/plugins/woocommerce/src/Admin/PluginsHelper.php +++ b/plugins/woocommerce/src/Admin/PluginsHelper.php @@ -365,7 +365,7 @@ class PluginsHelper { } /** - * Callback regsitered by OnboardingPlugins::install_and_activate_async. + * Callback registered by OnboardingPlugins::install_and_activate_async. * * It is used to call install_plugins and activate_plugins with a custom logger. * diff --git a/plugins/woocommerce/src/Admin/PluginsInstallLoggers/AsyncPluginsInstallLogger.php b/plugins/woocommerce/src/Admin/PluginsInstallLoggers/AsyncPluginsInstallLogger.php index 1e884328851..87faf90aa7a 100644 --- a/plugins/woocommerce/src/Admin/PluginsInstallLoggers/AsyncPluginsInstallLogger.php +++ b/plugins/woocommerce/src/Admin/PluginsInstallLoggers/AsyncPluginsInstallLogger.php @@ -32,7 +32,7 @@ class AsyncPluginsInstallLogger implements PluginsInstallLogger { 'no' ); - // Set status as failed in case we run out of exectuion time. + // Set status as failed in case we run out of execution time. register_shutdown_function( function () { $error = error_get_last(); @@ -57,7 +57,7 @@ class AsyncPluginsInstallLogger implements PluginsInstallLogger { } /** - * Retreive the option. + * Retrieve the option. * * @return false|mixed|void */ diff --git a/plugins/woocommerce/src/Admin/RemoteSpecs/DataSourcePoller.php b/plugins/woocommerce/src/Admin/RemoteSpecs/DataSourcePoller.php index 0769d65ffa6..d0812fd5ffa 100644 --- a/plugins/woocommerce/src/Admin/RemoteSpecs/DataSourcePoller.php +++ b/plugins/woocommerce/src/Admin/RemoteSpecs/DataSourcePoller.php @@ -126,6 +126,29 @@ abstract class DataSourcePoller { return false !== $specs ? $specs : array(); } + /** + * Gets specs from cache if it exists. + * + * @return array list of specs. + */ + public function get_cached_specs() { + $locale = get_user_locale(); + $specs_group = get_transient( $this->args['transient_name'] ) ?? array(); + $specs = isset( $specs_group[ $locale ] ) ? $specs_group[ $locale ] : null; + + /** + * Filter specs. + * + * @param array $specs List of specs. + * @param string $this->id Spec identifier. + * + * @since 8.8.0 + */ + $specs = apply_filters( self::FILTER_NAME_SPECS, $specs, $this->id ); + + return false !== $specs ? $specs : array(); + } + /** * Reads the data sources for specs and persists those specs. * diff --git a/plugins/woocommerce/src/Admin/RemoteSpecs/RuleProcessors/Transformers/TransformerService.php b/plugins/woocommerce/src/Admin/RemoteSpecs/RuleProcessors/Transformers/TransformerService.php index ec3fc8c9c5b..9519627f374 100644 --- a/plugins/woocommerce/src/Admin/RemoteSpecs/RuleProcessors/Transformers/TransformerService.php +++ b/plugins/woocommerce/src/Admin/RemoteSpecs/RuleProcessors/Transformers/TransformerService.php @@ -39,7 +39,7 @@ class TransformerService { * @param bool $is_default_set flag on is default value set. * @param string $default_value default value. * - * @throws InvalidArgumentException Throws when one of the requried arguments is missing. + * @throws InvalidArgumentException Throws when one of the required arguments is missing. * @return mixed|null */ public static function apply( $target_value, array $transformer_configs, $is_default_set, $default_value ) { diff --git a/plugins/woocommerce/src/Admin/WCAdminHelper.php b/plugins/woocommerce/src/Admin/WCAdminHelper.php index 5c02a93d30b..9e234762eb8 100644 --- a/plugins/woocommerce/src/Admin/WCAdminHelper.php +++ b/plugins/woocommerce/src/Admin/WCAdminHelper.php @@ -150,6 +150,18 @@ class WCAdminHelper { } $normalized_path = self::get_normalized_url_path( $url ); + $params = array( + 'post_type' => 'product', + ); + + parse_str( wp_parse_url( $url, PHP_URL_QUERY ), $url_params ); + + foreach ( $params as $key => $param ) { + if ( isset( $url_params[ $key ] ) && $url_params[ $key ] === $param ) { + return true; + } + } + // WC store pages. $store_pages = array( 'shop' => wc_get_page_id( 'shop' ), @@ -185,6 +197,7 @@ class WCAdminHelper { } $permalink = get_permalink( $page_id ); + if ( ! $permalink ) { continue; } @@ -236,7 +249,7 @@ class WCAdminHelper { * @param bool $is_store_page Whether or not the URL is a store page. * @param string $url URL to check. */ - $is_store_page = apply_filters( 'woocommerce_is_store_page', false, $url ); + $is_store_page = apply_filters( 'woocommerce_is_extension_store_page', false, $url ); return filter_var( $is_store_page, FILTER_VALIDATE_BOOL ); } diff --git a/plugins/woocommerce/src/Blocks/AI/Configuration.php b/plugins/woocommerce/src/Blocks/AI/Configuration.php index cd60cdd7f25..426a672db06 100644 --- a/plugins/woocommerce/src/Blocks/AI/Configuration.php +++ b/plugins/woocommerce/src/Blocks/AI/Configuration.php @@ -8,6 +8,8 @@ use Automattic\Jetpack\Connection\Utils; /** * Class Configuration + * + * @internal */ class Configuration { diff --git a/plugins/woocommerce/src/Blocks/AI/Connection.php b/plugins/woocommerce/src/Blocks/AI/Connection.php index cedd08b8cd2..0a20ddf10f8 100644 --- a/plugins/woocommerce/src/Blocks/AI/Connection.php +++ b/plugins/woocommerce/src/Blocks/AI/Connection.php @@ -10,6 +10,8 @@ use WpOrg\Requests\Requests; /** * Class Connection + * + * @internal */ class Connection { const TEXT_COMPLETION_API_URL = 'https://public-api.wordpress.com/wpcom/v2/text-completion'; diff --git a/plugins/woocommerce/src/Blocks/AIContent/ContentProcessor.php b/plugins/woocommerce/src/Blocks/AIContent/ContentProcessor.php index 55b202c0e3c..1f34077fb26 100644 --- a/plugins/woocommerce/src/Blocks/AIContent/ContentProcessor.php +++ b/plugins/woocommerce/src/Blocks/AIContent/ContentProcessor.php @@ -10,6 +10,8 @@ use WP_Error; * ContentProcessor class. * * Process images for content + * + * @internal */ class ContentProcessor { diff --git a/plugins/woocommerce/src/Blocks/AIContent/PatternsDictionary.php b/plugins/woocommerce/src/Blocks/AIContent/PatternsDictionary.php index cb87c48b8ea..9a881e47787 100644 --- a/plugins/woocommerce/src/Blocks/AIContent/PatternsDictionary.php +++ b/plugins/woocommerce/src/Blocks/AIContent/PatternsDictionary.php @@ -5,6 +5,8 @@ namespace Automattic\WooCommerce\Blocks\AIContent; /** * Patterns Dictionary class. + * + * @internal */ class PatternsDictionary { /** diff --git a/plugins/woocommerce/src/Blocks/AIContent/PatternsHelper.php b/plugins/woocommerce/src/Blocks/AIContent/PatternsHelper.php index d6eceadb97e..560d7ec831b 100644 --- a/plugins/woocommerce/src/Blocks/AIContent/PatternsHelper.php +++ b/plugins/woocommerce/src/Blocks/AIContent/PatternsHelper.php @@ -6,6 +6,8 @@ use WP_Error; /** * Patterns Helper class. + * + * @internal */ class PatternsHelper { /** diff --git a/plugins/woocommerce/src/Blocks/AIContent/UpdatePatterns.php b/plugins/woocommerce/src/Blocks/AIContent/UpdatePatterns.php index d9e792e85eb..d2e0228ca5f 100644 --- a/plugins/woocommerce/src/Blocks/AIContent/UpdatePatterns.php +++ b/plugins/woocommerce/src/Blocks/AIContent/UpdatePatterns.php @@ -7,6 +7,8 @@ use WP_Error; /** * Pattern Images class. + * + * @internal */ class UpdatePatterns { diff --git a/plugins/woocommerce/src/Blocks/AIContent/UpdateProducts.php b/plugins/woocommerce/src/Blocks/AIContent/UpdateProducts.php index 20e0549f8e3..571cb08029e 100644 --- a/plugins/woocommerce/src/Blocks/AIContent/UpdateProducts.php +++ b/plugins/woocommerce/src/Blocks/AIContent/UpdateProducts.php @@ -6,6 +6,8 @@ use Automattic\WooCommerce\Blocks\AI\Connection; use WP_Error; /** * Pattern Images class. + * + * @internal */ class UpdateProducts { diff --git a/plugins/woocommerce/src/Blocks/AssetsController.php b/plugins/woocommerce/src/Blocks/AssetsController.php index 1bb55c0f0c1..13236dc8145 100644 --- a/plugins/woocommerce/src/Blocks/AssetsController.php +++ b/plugins/woocommerce/src/Blocks/AssetsController.php @@ -64,11 +64,11 @@ final class AssetsController { $this->api->register_script( 'wc-price-format', 'assets/client/blocks/price-format.js', array(), false ); // Vendor scripts for blocks frontends (not including cart and checkout). - $this->api->register_script( 'wc-blocks-frontend-vendors', $this->api->get_block_asset_build_path( 'wc-blocks-frontend-vendors-frontend' ), array(), false ); + $this->api->register_script( 'wc-blocks-frontend-vendors', $this->api->get_block_asset_build_path( 'wc-blocks-frontend-vendors-frontend' ), array(), true ); // Cart and checkout frontend scripts. - $this->api->register_script( 'wc-cart-checkout-vendors', $this->api->get_block_asset_build_path( 'wc-cart-checkout-vendors-frontend' ), array(), false ); - $this->api->register_script( 'wc-cart-checkout-base', $this->api->get_block_asset_build_path( 'wc-cart-checkout-base-frontend' ), array(), false ); + $this->api->register_script( 'wc-cart-checkout-vendors', $this->api->get_block_asset_build_path( 'wc-cart-checkout-vendors-frontend' ), array(), true ); + $this->api->register_script( 'wc-cart-checkout-base', $this->api->get_block_asset_build_path( 'wc-cart-checkout-base-frontend' ), array(), true ); $this->api->register_script( 'wc-blocks-checkout', 'assets/client/blocks/blocks-checkout.js' ); $this->api->register_script( 'wc-blocks-components', 'assets/client/blocks/blocks-components.js' ); diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/CustomerAccount.php b/plugins/woocommerce/src/Blocks/BlockTypes/CustomerAccount.php index 80bd799b224..8d201c1051f 100644 --- a/plugins/woocommerce/src/Blocks/BlockTypes/CustomerAccount.php +++ b/plugins/woocommerce/src/Blocks/BlockTypes/CustomerAccount.php @@ -68,6 +68,7 @@ class CustomerAccount extends AbstractBlock { public function modify_hooked_block_attributes( $parsed_hooked_block, $hooked_block_type, $relative_position, $parsed_anchor_block, $context ) { $parsed_hooked_block['attrs']['displayStyle'] = 'icon_only'; $parsed_hooked_block['attrs']['iconStyle'] = 'line'; + $parsed_hooked_block['attrs']['iconClass'] = 'wc-block-customer-account__account-icon'; /* * The Mini Cart block (which is hooked into the header) has a margin of 0.5em on the left side. diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/OrderConfirmation/Summary.php b/plugins/woocommerce/src/Blocks/BlockTypes/OrderConfirmation/Summary.php index e36c1d77f74..3560292ab02 100644 --- a/plugins/woocommerce/src/Blocks/BlockTypes/OrderConfirmation/Summary.php +++ b/plugins/woocommerce/src/Blocks/BlockTypes/OrderConfirmation/Summary.php @@ -29,11 +29,11 @@ class Summary extends AbstractOrderConfirmationBlock { } $content = '
          '; - $content .= $this->render_summary_row( __( 'Order number:', 'woocommerce' ), $order->get_order_number() ); + $content .= $this->render_summary_row( __( 'Order #:', 'woocommerce' ), $order->get_order_number() ); $content .= $this->render_summary_row( __( 'Date:', 'woocommerce' ), wc_format_datetime( $order->get_date_created() ) ); $content .= $this->render_summary_row( __( 'Total:', 'woocommerce' ), $order->get_formatted_order_total() ); $content .= $this->render_summary_row( __( 'Email:', 'woocommerce' ), $order->get_billing_email() ); - $content .= $this->render_summary_row( __( 'Payment method:', 'woocommerce' ), $order->get_payment_method_title() ); + $content .= $this->render_summary_row( __( 'Payment:', 'woocommerce' ), $order->get_payment_method_title() ); $content .= '
        '; return $content; diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection.php index 45f5ab2b48e..f2f9f72328a 100644 --- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection.php +++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductCollection.php @@ -47,6 +47,18 @@ class ProductCollection extends AbstractBlock { protected $custom_order_opts = array( 'popularity', 'rating' ); + /** + * The render state of the product collection block. + * + * These props are runtime-based and reinitialize for every block on a page. + * + * @var array + */ + private $render_state = array( + 'has_results' => false, + 'has_no_results_block' => false, + ); + /** * Initialize this block type. * @@ -80,8 +92,30 @@ class ProductCollection extends AbstractBlock { // Provide location context into block's context. add_filter( 'render_block_context', array( $this, 'provide_location_context_for_inner_blocks' ), 11, 1 ); + // Disable block render if the ProductTemplate block is empty. + add_filter( + 'render_block_woocommerce/product-template', + function ( $html ) { + $this->render_state['has_results'] = ! empty( $html ); + return $html; + }, + 100, + 1 + ); + + // Enable block render if the ProductCollectionNoResults block is rendered. + add_filter( + 'render_block_woocommerce/product-collection-no-results', + function ( $html ) { + $this->render_state['has_no_results_block'] = ! empty( $html ); + return $html; + }, + 100, + 1 + ); + // Interactivity API: Add navigation directives to the product collection block. - add_filter( 'render_block_woocommerce/product-collection', array( $this, 'enhance_product_collection_with_interactivity' ), 10, 2 ); + add_filter( 'render_block_woocommerce/product-collection', array( $this, 'handle_rendering' ), 10, 2 ); add_filter( 'render_block_core/query-pagination', array( $this, 'add_navigation_link_directives' ), 10, 3 ); add_filter( 'posts_clauses', array( $this, 'add_price_range_filter_posts_clauses' ), 10, 2 ); @@ -90,6 +124,46 @@ class ProductCollection extends AbstractBlock { add_filter( 'render_block_data', array( $this, 'disable_enhanced_pagination' ), 10, 1 ); } + /** + * Handle the rendering of the block. + * + * @param string $block_content The block content about to be rendered. + * @param array $block The block being rendered. + * + * @return string + */ + public function handle_rendering( $block_content, $block ) { + if ( $this->should_prevent_render() ) { + return ''; // Prevent rendering. + } + + // Reset the render state for the next render. + $this->reset_render_state(); + + return $this->enhance_product_collection_with_interactivity( $block_content, $block ); + } + + /** + * Check if the block should be prevented from rendering. + * + * @return bool + */ + private function should_prevent_render() { + return ! $this->render_state['has_results'] && ! $this->render_state['has_no_results_block']; + } + + /** + * Reset the render state. + */ + private function reset_render_state() { + $this->render_state = array( + 'has_results' => false, + 'has_no_results_block' => false, + ); + } + + + /** * Provides the location context to each inner block of the product collection block. * Hint: Only blocks using the 'query' context will be affected. @@ -324,7 +398,7 @@ class ProductCollection extends AbstractBlock { $is_enhanced_pagination_enabled = ! ( $this->parsed_block['attrs']['forcePageReload'] ?? false ); // Only proceed if the block is a product collection block, - // enhaced pagination is enabled and query IDs match. + // enhanced pagination is enabled and query IDs match. if ( $is_product_collection_block && $is_enhanced_pagination_enabled && $query_id === $parsed_query_id ) { $block_content = $this->process_pagination_links( $block_content ); } @@ -414,8 +488,8 @@ class ProductCollection extends AbstractBlock { /** * Check inner blocks of Product Collection block if there's one - * incompatible with Interactivity API and if so, disable client-side - * naviagtion. + * incompatible with the Interactivity API and if so, disable client-side + * navigation. * * @param array $parsed_block The block being rendered. * @return string Returns the parsed block, unmodified. @@ -867,7 +941,7 @@ class ProductCollection extends AbstractBlock { * - For array items with numeric keys, we merge them as normal. * - For array items with string keys: * - * - If the value isn't array, we'll use the value comming from the merge array. + * - If the value isn't array, we'll use the value coming from the merge array. * $base = ['orderby' => 'date'] * $new = ['orderby' => 'meta_value_num'] * Result: ['orderby' => 'meta_value_num'] diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductDetails.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductDetails.php index c512966d6ad..907d198dde9 100644 --- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductDetails.php +++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductDetails.php @@ -1,6 +1,7 @@ render_tabs(); + if ( $hide_tab_title ) { + remove_filter( 'woocommerce_product_description_heading', '__return_empty_string' ); + remove_filter( 'woocommerce_product_additional_information_heading', '__return_empty_string' ); + remove_filter( 'woocommerce_reviews_title', '__return_empty_string' ); + + // Remove the first `h2` of every `.wc-tab`. This is required for the Reviews tabs when there are no reviews and for plugin tabs. + $tabs_html = new WP_HTML_Tag_Processor( $tabs ); + while ( $tabs_html->next_tag( array( 'class_name' => 'wc-tab' ) ) ) { + if ( $tabs_html->next_tag( 'h2' ) ) { + $tabs_html->set_attribute( 'hidden', 'true' ); + } + } + $tabs = $tabs_html->get_updated_html(); + } + $classname = $attributes['className'] ?? ''; $classes_and_styles = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes ); diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterPrice.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterPrice.php index 708868018c2..97b73763b65 100644 --- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterPrice.php +++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductFilterPrice.php @@ -171,7 +171,8 @@ final class ProductFilterPrice extends AbstractBlock { type="text" value="%s" data-wc-bind--value="state.formattedMinPrice" - data-wc-on--change="actions.updateProducts" + data-wc-on--input="actions.updateProducts" + data-wc-on--focus="actions.selectInputContent" pattern="" />', wp_strip_all_tags( $formatted_min_price ) @@ -189,7 +190,8 @@ final class ProductFilterPrice extends AbstractBlock { type="text" value="%s" data-wc-bind--value="state.formattedMaxPrice" - data-wc-on--change="actions.updateProducts" + data-wc-on--input="actions.updateProducts" + data-wc-on--focus="actions.selectInputContent" />', wp_strip_all_tags( $formatted_max_price ) ) : sprintf( @@ -232,6 +234,7 @@ final class ProductFilterPrice extends AbstractBlock { data-wc-bind--max="context.maxRange" data-wc-bind--value="context.minPrice" data-wc-on--change="actions.updateProducts" + data-wc-on--input="actions.updateRange" >
        diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductQuery.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductQuery.php index ec5b78d9f6d..8183c1b9f5e 100644 --- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductQuery.php +++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductQuery.php @@ -778,7 +778,7 @@ class ProductQuery extends AbstractBlock { * - For array items with numeric keys, we merge them as normal. * - For array items with string keys: * - * - If the value isn't array, we'll use the value comming from the merge array. + * - If the value isn't array, we'll use the value coming from the merge array. * $base = ['orderby' => 'date'] * $new = ['orderby' => 'meta_value_num'] * Result: ['orderby' => 'meta_value_num'] diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductRating.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductRating.php index 55e95ccca87..1d729677171 100644 --- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductRating.php +++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductRating.php @@ -114,7 +114,9 @@ class ProductRating extends AbstractBlock { $post_id = isset( $block->context['postId'] ) ? $block->context['postId'] : ''; $product = wc_get_product( $post_id ); - if ( $product && $product->get_review_count() > 0 ) { + if ( $product && $product->get_review_count() > 0 + && $product->get_reviews_allowed() + && wc_reviews_enabled() ) { $product_reviews_count = $product->get_review_count(); $product_rating = $product->get_average_rating(); $parsed_attributes = $this->parse_attributes( $attributes ); diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/ProductSKU.php b/plugins/woocommerce/src/Blocks/BlockTypes/ProductSKU.php index 1475a005217..50e748f646e 100644 --- a/plugins/woocommerce/src/Blocks/BlockTypes/ProductSKU.php +++ b/plugins/woocommerce/src/Blocks/BlockTypes/ProductSKU.php @@ -69,14 +69,27 @@ class ProductSKU extends AbstractBlock { $styles_and_classes = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes ); + $prefix = isset( $attributes['prefix'] ) ? wp_kses_post( ( $attributes['prefix'] ) ) : __( 'SKU: ', 'woocommerce' ); + if ( ! empty( $prefix ) ) { + $prefix = sprintf( '%s', $prefix ); + } + + $suffix = isset( $attributes['suffix'] ) ? wp_kses_post( ( $attributes['suffix'] ) ) : ''; + if ( ! empty( $suffix ) ) { + $suffix = sprintf( '%s', $suffix ); + } + return sprintf( '
        - SKU: - %3$s + %3$s + %4$s + %5$s
        ', esc_attr( $styles_and_classes['classes'] ), esc_attr( $styles_and_classes['styles'] ?? '' ), - $product_sku + $prefix, + $product_sku, + $suffix ); } } diff --git a/plugins/woocommerce/src/Blocks/BlockTypesController.php b/plugins/woocommerce/src/Blocks/BlockTypesController.php index ac2f5240c14..f6d84ecc051 100644 --- a/plugins/woocommerce/src/Blocks/BlockTypesController.php +++ b/plugins/woocommerce/src/Blocks/BlockTypesController.php @@ -227,20 +227,28 @@ final class BlockTypesController { * @return string */ public function add_data_attributes( $content, $block ) { - $block_name = $block['blockName']; - if ( ! $this->block_should_have_data_attributes( $block_name ) ) { + $content = trim( $content ); + + if ( ! $this->block_should_have_data_attributes( $block['blockName'] ) ) { return $content; } - $attributes = (array) $block['attrs']; - $exclude_attributes = array( 'className', 'align' ); - $escaped_data_attributes = array( - 'data-block-name="' . esc_attr( $block['blockName'] ) . '"', - ); + $attributes = (array) $block['attrs']; + $exclude_attributes = array( 'className', 'align' ); - foreach ( $attributes as $key => $value ) { - if ( in_array( $key, $exclude_attributes, true ) ) { + $processor = new \WP_HTML_Tag_Processor( $content ); + + if ( + false === $processor->next_token() || + 'DIV' !== $processor->get_token_name() || + $processor->is_tag_closer() + ) { + return $content; + } + + foreach ( $attributes as $key => $value ) { + if ( ! is_string( $key ) || in_array( $key, $exclude_attributes, true ) ) { continue; } if ( is_bool( $value ) ) { @@ -249,10 +257,16 @@ final class BlockTypesController { if ( ! is_scalar( $value ) ) { $value = wp_json_encode( $value ); } - $escaped_data_attributes[] = 'data-' . esc_attr( strtolower( preg_replace( '/(?set_attribute( "data-{$key}", $value ); } - return preg_replace( '/^
        set_attribute( 'data-block-name', $block['blockName'] ); + return $processor->get_updated_html(); } /** @@ -271,7 +285,7 @@ final class BlockTypesController { * and prevent them from showing as an option in the Legacy Widget block. * * @param array $widget_types An array of widgets hidden in core. - * @return array $widget_types An array inluding the WooCommerce widgets to hide. + * @return array $widget_types An array including the WooCommerce widgets to hide. */ public function hide_legacy_widgets_with_block_equivalent( $widget_types ) { array_push( diff --git a/plugins/woocommerce/src/Blocks/Domain/Bootstrap.php b/plugins/woocommerce/src/Blocks/Domain/Bootstrap.php index 7966386aceb..dbf929aa3fb 100644 --- a/plugins/woocommerce/src/Blocks/Domain/Bootstrap.php +++ b/plugins/woocommerce/src/Blocks/Domain/Bootstrap.php @@ -169,8 +169,6 @@ class Bootstrap { $this->container->get( BlockPatterns::class ); $this->container->get( BlockTypesController::class ); $this->container->get( ClassicTemplatesCompatibility::class ); - $this->container->get( ArchiveProductTemplatesCompatibility::class )->init(); - $this->container->get( SingleProductTemplateCompatibility::class )->init(); $this->container->get( Notices::class )->init(); $this->container->get( PTKPatternsStore::class ); $this->container->get( TemplateOptions::class )->init(); @@ -276,18 +274,6 @@ class Bootstrap { return new ClassicTemplatesCompatibility( $asset_data_registry ); } ); - $this->container->register( - ArchiveProductTemplatesCompatibility::class, - function () { - return new ArchiveProductTemplatesCompatibility(); - } - ); - $this->container->register( - SingleProductTemplateCompatibility::class, - function () { - return new SingleProductTemplateCompatibility(); - } - ); $this->container->register( DraftOrders::class, function ( Container $container ) { diff --git a/plugins/woocommerce/src/Blocks/Domain/Services/CheckoutFields.php b/plugins/woocommerce/src/Blocks/Domain/Services/CheckoutFields.php index ab9e4c19279..dcf2e0351f8 100644 --- a/plugins/woocommerce/src/Blocks/Domain/Services/CheckoutFields.php +++ b/plugins/woocommerce/src/Blocks/Domain/Services/CheckoutFields.php @@ -508,6 +508,7 @@ class CheckoutFields { 'hidden' => false, 'autocomplete' => 'email', 'autocapitalize' => 'none', + 'type' => 'email', 'index' => 0, ], 'country' => [ diff --git a/plugins/woocommerce/src/Blocks/Patterns/AIPatterns.php b/plugins/woocommerce/src/Blocks/Patterns/AIPatterns.php index 8bcf093fd1f..6982fad05a7 100644 --- a/plugins/woocommerce/src/Blocks/Patterns/AIPatterns.php +++ b/plugins/woocommerce/src/Blocks/Patterns/AIPatterns.php @@ -8,6 +8,8 @@ use Automattic\WooCommerce\Blocks\Images\Pexels; /** * AIPatterns class. + * + * @internal */ class AIPatterns { const PATTERNS_AI_DATA_POST_TYPE = 'patterns_ai_data'; diff --git a/plugins/woocommerce/src/Blocks/Patterns/PTKClient.php b/plugins/woocommerce/src/Blocks/Patterns/PTKClient.php index e319c22ffed..2b86b3c857c 100644 --- a/plugins/woocommerce/src/Blocks/Patterns/PTKClient.php +++ b/plugins/woocommerce/src/Blocks/Patterns/PTKClient.php @@ -5,6 +5,8 @@ use WP_Error; /** * PatternsToolkit class. + * + * @internal */ class PTKClient { /** diff --git a/plugins/woocommerce/src/Blocks/Patterns/PTKPatternsStore.php b/plugins/woocommerce/src/Blocks/Patterns/PTKPatternsStore.php index 2b24e1a9657..1ce873b39f8 100644 --- a/plugins/woocommerce/src/Blocks/Patterns/PTKPatternsStore.php +++ b/plugins/woocommerce/src/Blocks/Patterns/PTKPatternsStore.php @@ -7,6 +7,8 @@ use WP_Upgrader; /** * PTKPatterns class. + * + * @internal */ class PTKPatternsStore { const TRANSIENT_NAME = 'ptk_patterns'; diff --git a/plugins/woocommerce/src/Blocks/Patterns/PatternRegistry.php b/plugins/woocommerce/src/Blocks/Patterns/PatternRegistry.php index 9a79a95cc5b..3af03be5200 100644 --- a/plugins/woocommerce/src/Blocks/Patterns/PatternRegistry.php +++ b/plugins/woocommerce/src/Blocks/Patterns/PatternRegistry.php @@ -5,6 +5,8 @@ use Automattic\WooCommerce\Admin\Features\Features; /** * PatternRegistry class. + * + * @internal */ class PatternRegistry { const SLUG_REGEX = '/^[A-z0-9\/_-]+$/'; diff --git a/plugins/woocommerce/src/Blocks/QueryFilters.php b/plugins/woocommerce/src/Blocks/QueryFilters.php index 76489d8a8cd..274566be8b0 100644 --- a/plugins/woocommerce/src/Blocks/QueryFilters.php +++ b/plugins/woocommerce/src/Blocks/QueryFilters.php @@ -18,7 +18,7 @@ final class QueryFilters { } /** - * Filter the posts clauses of the main query to suport global filters. + * Filter the posts clauses of the main query to support global filters. * * @param array $args Query args. * @param \WP_Query $wp_query WP_Query object. diff --git a/plugins/woocommerce/src/Blocks/Templates/AbstractTemplateCompatibility.php b/plugins/woocommerce/src/Blocks/Templates/AbstractTemplateCompatibility.php index 3d622a345c2..cbd427547b0 100644 --- a/plugins/woocommerce/src/Blocks/Templates/AbstractTemplateCompatibility.php +++ b/plugins/woocommerce/src/Blocks/Templates/AbstractTemplateCompatibility.php @@ -21,10 +21,6 @@ abstract class AbstractTemplateCompatibility { * Initialization method. */ public function init() { - if ( ! wc_current_theme_is_fse_theme() ) { - return; - } - $this->set_hook_data(); add_filter( @@ -61,9 +57,9 @@ abstract class AbstractTemplateCompatibility { * @since 7.6.0 * @param boolean. */ - $is_disabled_compatility_layer = apply_filters( 'woocommerce_disable_compatibility_layer', false ); + $is_disabled_compatibility_layer = apply_filters( 'woocommerce_disable_compatibility_layer', false ); - if ( $is_disabled_compatility_layer ) { + if ( $is_disabled_compatibility_layer ) { return $block_content; } diff --git a/plugins/woocommerce/src/Blocks/Templates/ArchiveProductTemplatesCompatibility.php b/plugins/woocommerce/src/Blocks/Templates/ArchiveProductTemplatesCompatibility.php index 74f36155582..6fba40e7149 100644 --- a/plugins/woocommerce/src/Blocks/Templates/ArchiveProductTemplatesCompatibility.php +++ b/plugins/woocommerce/src/Blocks/Templates/ArchiveProductTemplatesCompatibility.php @@ -320,7 +320,7 @@ class ArchiveProductTemplatesCompatibility extends AbstractTemplateCompatibility } /** - * Check if block is within the product-query namespace + * Check whether block is within the product-query namespace. * * @param array $block Parsed block data. */ @@ -331,7 +331,7 @@ class ArchiveProductTemplatesCompatibility extends AbstractTemplateCompatibility } /** - * Check if block has isInherited attribute asigned + * Check whether block has isInherited attribute assigned. * * @param array $block Parsed block data. */ @@ -357,7 +357,7 @@ class ArchiveProductTemplatesCompatibility extends AbstractTemplateCompatibility } /** - * Check if block is a Post template + * Check whether block is a Post template. * * @param string $block_name Block name. */ @@ -366,7 +366,7 @@ class ArchiveProductTemplatesCompatibility extends AbstractTemplateCompatibility } /** - * Check if block is a Product Template + * Check whether block is a Product Template. * * @param string $block_name Block name. */ @@ -375,7 +375,7 @@ class ArchiveProductTemplatesCompatibility extends AbstractTemplateCompatibility } /** - * Check if block is eaither a Post template or Product Template + * Check if block is either a Post template or a Product Template * * @param string $block_name Block name. */ diff --git a/plugins/woocommerce/src/Blocks/Templates/ProductAttributeTemplate.php b/plugins/woocommerce/src/Blocks/Templates/ProductAttributeTemplate.php index 828133704a3..153eadb3c9f 100644 --- a/plugins/woocommerce/src/Blocks/Templates/ProductAttributeTemplate.php +++ b/plugins/woocommerce/src/Blocks/Templates/ProductAttributeTemplate.php @@ -2,6 +2,7 @@ namespace Automattic\WooCommerce\Blocks\Templates; +use Automattic\WooCommerce\Blocks\Templates\ArchiveProductTemplatesCompatibility; use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils; /** @@ -61,6 +62,9 @@ class ProductAttributeTemplate extends AbstractTemplate { } if ( isset( $queried_object->taxonomy ) && taxonomy_is_product_attribute( $queried_object->taxonomy ) ) { + $compatibility_layer = new ArchiveProductTemplatesCompatibility(); + $compatibility_layer->init(); + $templates = get_block_templates( array( 'slug__in' => array( self::SLUG ) ) ); if ( isset( $templates[0] ) && BlockTemplateUtils::template_has_legacy_template_block( $templates[0] ) ) { diff --git a/plugins/woocommerce/src/Blocks/Templates/ProductCatalogTemplate.php b/plugins/woocommerce/src/Blocks/Templates/ProductCatalogTemplate.php index 2f3d3383ccb..7059ed379b9 100644 --- a/plugins/woocommerce/src/Blocks/Templates/ProductCatalogTemplate.php +++ b/plugins/woocommerce/src/Blocks/Templates/ProductCatalogTemplate.php @@ -2,6 +2,7 @@ namespace Automattic\WooCommerce\Blocks\Templates; +use Automattic\WooCommerce\Blocks\Templates\ArchiveProductTemplatesCompatibility; use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils; /** @@ -49,6 +50,9 @@ class ProductCatalogTemplate extends AbstractTemplate { */ public function render_block_template() { if ( ! is_embed() && ( is_post_type_archive( 'product' ) || is_page( wc_get_page_id( 'shop' ) ) ) ) { + $compatibility_layer = new ArchiveProductTemplatesCompatibility(); + $compatibility_layer->init(); + $templates = get_block_templates( array( 'slug__in' => array( self::SLUG ) ) ); if ( isset( $templates[0] ) && BlockTemplateUtils::template_has_legacy_template_block( $templates[0] ) ) { diff --git a/plugins/woocommerce/src/Blocks/Templates/ProductCategoryTemplate.php b/plugins/woocommerce/src/Blocks/Templates/ProductCategoryTemplate.php index 604cbb51aa7..233d7c2a877 100644 --- a/plugins/woocommerce/src/Blocks/Templates/ProductCategoryTemplate.php +++ b/plugins/woocommerce/src/Blocks/Templates/ProductCategoryTemplate.php @@ -2,6 +2,7 @@ namespace Automattic\WooCommerce\Blocks\Templates; +use Automattic\WooCommerce\Blocks\Templates\ArchiveProductTemplatesCompatibility; use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils; /** @@ -55,6 +56,9 @@ class ProductCategoryTemplate extends AbstractTemplate { */ public function render_block_template() { if ( ! is_embed() && is_product_taxonomy() && is_tax( 'product_cat' ) ) { + $compatibility_layer = new ArchiveProductTemplatesCompatibility(); + $compatibility_layer->init(); + $templates = get_block_templates( array( 'slug__in' => array( self::SLUG ) ) ); if ( isset( $templates[0] ) && BlockTemplateUtils::template_has_legacy_template_block( $templates[0] ) ) { diff --git a/plugins/woocommerce/src/Blocks/Templates/ProductSearchResultsTemplate.php b/plugins/woocommerce/src/Blocks/Templates/ProductSearchResultsTemplate.php index ea8daab4768..c421fa7a0a0 100644 --- a/plugins/woocommerce/src/Blocks/Templates/ProductSearchResultsTemplate.php +++ b/plugins/woocommerce/src/Blocks/Templates/ProductSearchResultsTemplate.php @@ -1,6 +1,7 @@ init(); + $templates = get_block_templates( array( 'slug__in' => array( self::SLUG ) ) ); if ( isset( $templates[0] ) && BlockTemplateUtils::template_has_legacy_template_block( $templates[0] ) ) { diff --git a/plugins/woocommerce/src/Blocks/Templates/ProductTagTemplate.php b/plugins/woocommerce/src/Blocks/Templates/ProductTagTemplate.php index 6049f429dcd..fef90fdc5ad 100644 --- a/plugins/woocommerce/src/Blocks/Templates/ProductTagTemplate.php +++ b/plugins/woocommerce/src/Blocks/Templates/ProductTagTemplate.php @@ -2,6 +2,7 @@ namespace Automattic\WooCommerce\Blocks\Templates; +use Automattic\WooCommerce\Blocks\Templates\ArchiveProductTemplatesCompatibility; use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils; /** @@ -55,6 +56,9 @@ class ProductTagTemplate extends AbstractTemplate { */ public function render_block_template() { if ( ! is_embed() && is_product_taxonomy() && is_tax( 'product_tag' ) ) { + $compatibility_layer = new ArchiveProductTemplatesCompatibility(); + $compatibility_layer->init(); + $templates = get_block_templates( array( 'slug__in' => array( self::SLUG ) ) ); if ( isset( $templates[0] ) && BlockTemplateUtils::template_has_legacy_template_block( $templates[0] ) ) { diff --git a/plugins/woocommerce/src/Blocks/Templates/SingleProductTemplate.php b/plugins/woocommerce/src/Blocks/Templates/SingleProductTemplate.php index 942f807422f..839ed177e09 100644 --- a/plugins/woocommerce/src/Blocks/Templates/SingleProductTemplate.php +++ b/plugins/woocommerce/src/Blocks/Templates/SingleProductTemplate.php @@ -23,7 +23,7 @@ class SingleProductTemplate extends AbstractTemplate { */ public function init() { add_action( 'template_redirect', array( $this, 'render_block_template' ) ); - add_filter( 'get_block_templates', array( $this, 'update_single_product_content' ), 11, 3 ); + add_filter( 'get_block_templates', array( $this, 'update_single_product_content' ), 11, 1 ); } /** @@ -51,13 +51,34 @@ class SingleProductTemplate extends AbstractTemplate { if ( ! is_embed() && is_singular( 'product' ) ) { global $post; - $valid_slugs = array( self::SLUG ); - if ( 'product' === $post->post_type && $post->post_name ) { + $compatibility_layer = new SingleProductTemplateCompatibility(); + $compatibility_layer->init(); + + $valid_slugs = array( self::SLUG ); + $single_product_slug = 'product' === $post->post_type && $post->post_name ? 'single-product-' . $post->post_name : ''; + if ( $single_product_slug ) { $valid_slugs[] = 'single-product-' . $post->post_name; } $templates = get_block_templates( array( 'slug__in' => $valid_slugs ) ); - if ( isset( $templates[0] ) && BlockTemplateUtils::template_has_legacy_template_block( $templates[0] ) ) { + if ( count( $templates ) === 0 ) { + return; + } + + // Use the first template by default. + $template = $templates[0]; + + // Check if there is a template matching the slug `single-product-{post_name}`. + if ( count( $valid_slugs ) > 1 && count( $templates ) > 1 ) { + foreach ( $templates as $t ) { + if ( $single_product_slug === $t->slug ) { + $template = $t; + break; + } + } + } + + if ( isset( $template ) && BlockTemplateUtils::template_has_legacy_template_block( $template ) ) { add_filter( 'woocommerce_disable_compatibility_layer', '__return_true' ); } @@ -68,12 +89,10 @@ class SingleProductTemplate extends AbstractTemplate { /** * Add the block template objects to be used. * - * @param array $query_result Array of template objects. - * @param array $query Optional. Arguments to retrieve templates. - * @param string $template_type wp_template or wp_template_part. + * @param array $query_result Array of template objects. * @return array */ - public function update_single_product_content( $query_result, $query, $template_type ) { + public function update_single_product_content( $query_result ) { $query_result = array_map( function ( $template ) { if ( str_contains( $template->slug, self::SLUG ) ) { diff --git a/plugins/woocommerce/src/Blocks/Templates/SingleProductTemplateCompatibility.php b/plugins/woocommerce/src/Blocks/Templates/SingleProductTemplateCompatibility.php index 05191bcbdbb..caa7b691576 100644 --- a/plugins/woocommerce/src/Blocks/Templates/SingleProductTemplateCompatibility.php +++ b/plugins/woocommerce/src/Blocks/Templates/SingleProductTemplateCompatibility.php @@ -1,6 +1,8 @@ get_registered( $block['attrs']['slug'] ); + $pattern_blocks = parse_blocks( $pattern['content'] ); + + if ( self::has_block_including_patterns( $block_names, $pattern_blocks ) ) { + return true; + } + } + } + + return false; + } + /** * Returns whether the passed `$template` has the legacy template block. * @@ -703,7 +736,13 @@ class BlockTemplateUtils { * @return boolean */ public static function template_has_legacy_template_block( $template ) { - return has_block( 'woocommerce/legacy-template', $template->content ); + if ( has_block( 'woocommerce/legacy-template', $template->content ) ) { + return true; + } + + $blocks = parse_blocks( $template->content ); + + return self::has_block_including_patterns( array( 'woocommerce/legacy-template' ), $blocks ); } /** diff --git a/plugins/woocommerce/src/Caching/ObjectCache.php b/plugins/woocommerce/src/Caching/ObjectCache.php index 925509d17b4..646308a3c32 100644 --- a/plugins/woocommerce/src/Caching/ObjectCache.php +++ b/plugins/woocommerce/src/Caching/ObjectCache.php @@ -166,7 +166,7 @@ abstract class ObjectCache { * @param object|array $object The new object that will replace the already cached one. * @param int|string|null $id Id of the object to be cached, if null, get_object_id will be used to get it. * @param int $expiration Expiration of the cached data in seconds from the current time, or DEFAULT_EXPIRATION to use the default value. - * @return bool True on success, false on error or if no object wiith the supplied id was cached. + * @return bool True on success, false on error or if no object with the supplied id was cached. * @throws CacheException Invalid parameter, or null id was passed and get_object_id returns null too. */ public function update_if_cached( $object, $id = null, int $expiration = self::DEFAULT_EXPIRATION ): bool { diff --git a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php index aa804aef3a8..ab60693811a 100644 --- a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php +++ b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php @@ -330,7 +330,7 @@ class CLIRunner { * --- * * [--order-types] - * : Comma seperated list of order types that needs to be verified. For example, --order-types=shop_order,shop_order_refund + * : Comma-separated list of order types that needs to be verified. For example, --order-types=shop_order,shop_order_refund * --- * default: Output of function `wc_get_order_types( 'cot-migration' )` * @@ -385,7 +385,7 @@ class CLIRunner { if ( 0 === count( $order_types ) ) { return WP_CLI::error( sprintf( - /* Translators: %s is the comma seperated list of order types. */ + /* Translators: %s is the comma-separated list of order types. */ __( 'Passed order type does not match any registered order types. Following order types are registered: %s', 'woocommerce' ), implode( ',', wc_get_order_types( 'cot-migration' ) ) ) @@ -927,7 +927,7 @@ ORDER BY $meta_table.order_id ASC, $meta_table.meta_key ASC; * # Cleanup post data for order 314. * $ wp wc hpos cleanup 314 * - * # Cleanup postmeta for orders with IDs betweeen 10 and 100 and order 314. + * # Cleanup postmeta for orders with IDs between 10 and 100 and order 314. * $ wp wc hpos cleanup 10-100 314 * * # Cleanup postmeta for all orders. @@ -1161,10 +1161,10 @@ ORDER BY $meta_table.order_id ASC, $meta_table.meta_key ASC; * --- * * [--meta_keys=] - * : Comma separated list of meta keys to backfill. + * : Comma-separated list of meta keys to backfill. * * [--props=] - * : Comma separated list of order properties to backfill. + * : Comma-separated list of order properties to backfill. * * @since 8.6.0 * diff --git a/plugins/woocommerce/src/Database/Migrations/MetaToCustomTableMigrator.php b/plugins/woocommerce/src/Database/Migrations/MetaToCustomTableMigrator.php index 1aa9740e146..573fdedace3 100644 --- a/plugins/woocommerce/src/Database/Migrations/MetaToCustomTableMigrator.php +++ b/plugins/woocommerce/src/Database/Migrations/MetaToCustomTableMigrator.php @@ -134,7 +134,7 @@ abstract class MetaToCustomTableMigrator extends TableMigrator { * @return string Generated queries for batch update. Would be of the form: * INSERT INTO $table ( $columns ) VALUES * ($value for row 1) - * ($valye for row 2) + * ($value for row 2) * ... * ON DUPLICATE KEY UPDATE * $column1 = VALUES($column1) diff --git a/plugins/woocommerce/src/Database/Migrations/TableMigrator.php b/plugins/woocommerce/src/Database/Migrations/TableMigrator.php index 4d6665c08f8..e9b2f0a283b 100644 --- a/plugins/woocommerce/src/Database/Migrations/TableMigrator.php +++ b/plugins/woocommerce/src/Database/Migrations/TableMigrator.php @@ -14,7 +14,7 @@ namespace Automattic\WooCommerce\Database\Migrations; abstract class TableMigrator { /** - * An array of cummulated error messages. + * An array of cumulated error messages. * * @var array */ diff --git a/plugins/woocommerce/src/Internal/Admin/Homescreen.php b/plugins/woocommerce/src/Internal/Admin/Homescreen.php index 3356ed5c67f..1a9cd90f776 100644 --- a/plugins/woocommerce/src/Internal/Admin/Homescreen.php +++ b/plugins/woocommerce/src/Internal/Admin/Homescreen.php @@ -63,7 +63,7 @@ class Homescreen { /** * Set free shipping in the same country as the store default - * Flag rate in all other countries when any of the following conditions are ture + * Flag rate in all other countries when any of the following conditions are true * * - The store sells physical products, has JP and WCS installed and connected, and is located in the US. * - The store sells physical products, and is not located in US/Canada/Australia/UK (irrelevant if JP is installed or not). @@ -242,7 +242,7 @@ class Homescreen { */ public function update_link_structure() { global $submenu; - // User does not have capabilites to see the submenu. + // User does not have capabilities to see the submenu. if ( ! current_user_can( 'manage_woocommerce' ) || empty( $submenu['woocommerce'] ) ) { return; } diff --git a/plugins/woocommerce/src/Internal/Admin/Loader.php b/plugins/woocommerce/src/Internal/Admin/Loader.php index 41e11ae613d..21caf32b9ba 100644 --- a/plugins/woocommerce/src/Internal/Admin/Loader.php +++ b/plugins/woocommerce/src/Internal/Admin/Loader.php @@ -111,7 +111,7 @@ class Loader { /** * Set up a div for the header embed to render into. - * The initial contents here are meant as a place loader for when the PHP page initialy loads. + * The initial contents here are meant as a place loader for when the PHP page initially loads. */ public static function embed_page_header() { if ( ! PageController::is_admin_page() && ! PageController::is_embed_page() ) { @@ -541,7 +541,7 @@ class Loader { } /** - * Return an object defining the currecy options for the site's current currency + * Return an object defining the currency options for the site's current currency * * @return array Settings for the current currency { * Array of settings. diff --git a/plugins/woocommerce/src/Internal/Admin/Marketing.php b/plugins/woocommerce/src/Internal/Admin/Marketing.php index b9efa6461f6..2c0d4348bd8 100644 --- a/plugins/woocommerce/src/Internal/Admin/Marketing.php +++ b/plugins/woocommerce/src/Internal/Admin/Marketing.php @@ -158,7 +158,7 @@ class Marketing { } /** - * Order marketing menu items alphabeticaly. + * Order marketing menu items alphabetically. * Overview should be first, and Coupons should be second, followed by other marketing menu items. * * @return void @@ -178,7 +178,7 @@ class Marketing { if ( false === $overview_key ) { /* - * If Overview is not found we may be on a site witha different language. + * If Overview is not found, we may be on a site with a different language. * We can use a fallback and try to find the overview page by its path. */ $overview_key = array_search( 'admin.php?page=wc-admin&path=/marketing', array_column( $marketing_submenu, self::SUBMENU_LOCATION_KEY ), true ); @@ -194,7 +194,7 @@ class Marketing { if ( false === $coupons_key ) { /* - * If Coupons is not found we may be on a site witha different language. + * If Coupons is not found, we may be on a site with a different language. * We can use a fallback and try to find the coupons page by its path. */ $coupons_key = array_search( 'edit.php?post_type=shop_coupon', array_column( $marketing_submenu, self::SUBMENU_LOCATION_KEY ), true ); diff --git a/plugins/woocommerce/src/Internal/Admin/Onboarding/OnboardingThemes.php b/plugins/woocommerce/src/Internal/Admin/Onboarding/OnboardingThemes.php index e5569f0f42a..e6f71d90601 100644 --- a/plugins/woocommerce/src/Internal/Admin/Onboarding/OnboardingThemes.php +++ b/plugins/woocommerce/src/Internal/Admin/Onboarding/OnboardingThemes.php @@ -78,10 +78,10 @@ class OnboardingThemes { usort( $themes, function ( $product_1, $product_2 ) { - if ( ! property_exists( $product_1, 'id' ) || ! property_exists( $product_1, 'slug' ) ) { + if ( ! is_object( $product_1 ) || ! property_exists( $product_1, 'id' ) || ! property_exists( $product_1, 'slug' ) ) { return 1; } - if ( ! property_exists( $product_2, 'id' ) || ! property_exists( $product_2, 'slug' ) ) { + if ( ! is_object( $product_2 ) || ! property_exists( $product_2, 'id' ) || ! property_exists( $product_2, 'slug' ) ) { return 1; } if ( in_array( 'Storefront', array( $product_1->slug, $product_2->slug ), true ) ) { @@ -110,16 +110,22 @@ class OnboardingThemes { $themes = array(); if ( ! is_wp_error( $theme_data ) ) { - $theme_data = json_decode( $theme_data['body'] ); - $woo_themes = property_exists( $theme_data, 'products' ) ? $theme_data->products : array(); - $sorted_themes = self::sort_woocommerce_themes( $woo_themes ); + $theme_data = json_decode( $theme_data['body'] ); - foreach ( $sorted_themes as $theme ) { - $slug = sanitize_title_with_dashes( $theme->slug ); - $themes[ $slug ] = (array) $theme; - $themes[ $slug ]['is_installed'] = false; - $themes[ $slug ]['has_woocommerce_support'] = true; - $themes[ $slug ]['slug'] = $slug; + if ( $theme_data ) { + $woo_themes = property_exists( $theme_data, 'products' ) ? $theme_data->products : array(); + $sorted_themes = self::sort_woocommerce_themes( $woo_themes ); + + foreach ( $sorted_themes as $theme ) { + if ( ! isset( $theme->slug ) ) { + continue; + } + $slug = sanitize_title_with_dashes( $theme->slug ); + $themes[ $slug ] = (array) $theme; + $themes[ $slug ]['is_installed'] = false; + $themes[ $slug ]['has_woocommerce_support'] = true; + $themes[ $slug ]['slug'] = $slug; + } } } diff --git a/plugins/woocommerce/src/Internal/Admin/Orders/Edit.php b/plugins/woocommerce/src/Internal/Admin/Orders/Edit.php index fdf819dda31..fad1838532a 100644 --- a/plugins/woocommerce/src/Internal/Admin/Orders/Edit.php +++ b/plugins/woocommerce/src/Internal/Admin/Orders/Edit.php @@ -170,7 +170,7 @@ class Edit { * * @since 7.4.0 * - * @oaram WC_Order $order The order being edited. + * @param WC_Order $order The order being edited. */ do_action( 'add_meta_boxes_' . $this->screen_id, $this->order ); diff --git a/plugins/woocommerce/src/Internal/Admin/ProductReviews/ReviewsListTable.php b/plugins/woocommerce/src/Internal/Admin/ProductReviews/ReviewsListTable.php index 131af7c87b4..fd6683672ec 100644 --- a/plugins/woocommerce/src/Internal/Admin/ProductReviews/ReviewsListTable.php +++ b/plugins/woocommerce/src/Internal/Admin/ProductReviews/ReviewsListTable.php @@ -632,7 +632,7 @@ class ReviewsListTable extends WP_List_Table { /** * Gets the name of the default primary column. * - * @return string Name of the primary colum. + * @return string Name of the primary column. */ protected function get_primary_column_name() : string { return 'comment'; diff --git a/plugins/woocommerce/src/Internal/Admin/Schedulers/ImportScheduler.php b/plugins/woocommerce/src/Internal/Admin/Schedulers/ImportScheduler.php index 2f90a8d5d80..9922d19b5ad 100644 --- a/plugins/woocommerce/src/Internal/Admin/Schedulers/ImportScheduler.php +++ b/plugins/woocommerce/src/Internal/Admin/Schedulers/ImportScheduler.php @@ -60,7 +60,7 @@ abstract class ImportScheduler implements ImportInterface { * Get batch sizes. * * @internal - * @retun array + * @return array */ public static function get_batch_sizes() { return array_merge( @@ -96,7 +96,7 @@ abstract class ImportScheduler implements ImportInterface { * * @internal * @param integer|boolean $days Number of days to import. - * @param boolean $skip_existing Skip exisiting records. + * @param boolean $skip_existing Skip existing records. */ public static function import_batch_init( $days, $skip_existing ) { $batch_size = static::get_batch_size( 'import' ); @@ -117,7 +117,7 @@ abstract class ImportScheduler implements ImportInterface { * @internal * @param int $batch_number Batch number to import (essentially a query page number). * @param int|bool $days Number of days to import. - * @param bool $skip_existing Skip exisiting records. + * @param bool $skip_existing Skip existing records. * @return void */ public static function import_batch( $batch_number, $days, $skip_existing ) { diff --git a/plugins/woocommerce/src/Internal/Admin/Settings.php b/plugins/woocommerce/src/Internal/Admin/Settings.php index 2d47e46e0c3..a72ae43f098 100644 --- a/plugins/woocommerce/src/Internal/Admin/Settings.php +++ b/plugins/woocommerce/src/Internal/Admin/Settings.php @@ -78,7 +78,7 @@ class Settings { } /** - * Return an object defining the currecy options for the site's current currency + * Return an object defining the currency options for the site's current currency * * @return array Settings for the current currency { * Array of settings. @@ -258,7 +258,7 @@ class Settings { } /** - * Removes non necesary feature properties for the client side. + * Removes non-necessary feature properties for the client side. * * @return array */ diff --git a/plugins/woocommerce/src/Internal/Admin/ShippingLabelBanner.php b/plugins/woocommerce/src/Internal/Admin/ShippingLabelBanner.php index 3c9ae79d398..c26301bbddc 100644 --- a/plugins/woocommerce/src/Internal/Admin/ShippingLabelBanner.php +++ b/plugins/woocommerce/src/Internal/Admin/ShippingLabelBanner.php @@ -6,6 +6,8 @@ namespace Automattic\WooCommerce\Internal\Admin; use Automattic\Jetpack\Connection\Manager as Jetpack_Connection_Manager; +use Automattic\WooCommerce\Utilities\OrderUtil; +use function WP_CLI\Utils\get_plugin_name; /** * Shows print shipping label banner on edit order page. @@ -19,6 +21,9 @@ class ShippingLabelBanner { */ private $shipping_label_banner_display_rules; + private const MIN_COMPATIBLE_WCST_VERSION = '2.7.0'; + private const MIN_COMPATIBLE_WCSHIPPING_VERSION = '1.1.0'; + /** * Constructor */ @@ -36,40 +41,26 @@ class ShippingLabelBanner { */ private function should_show_meta_box() { if ( ! $this->shipping_label_banner_display_rules ) { - $jetpack_version = null; - $jetpack_connected = null; - $wcs_version = null; - $wcs_tos_accepted = null; - - if ( defined( 'JETPACK__VERSION' ) ) { - $jetpack_version = JETPACK__VERSION; - } + $dotcom_connected = null; + $wcs_version = null; if ( class_exists( Jetpack_Connection_Manager::class ) ) { - $jetpack_connected = ( new Jetpack_Connection_Manager() )->has_connected_owner(); + $dotcom_connected = ( new Jetpack_Connection_Manager() )->has_connected_owner(); } - if ( class_exists( '\WC_Connect_Loader' ) ) { - $wcs_version = \WC_Connect_Loader::get_wcs_version(); - } - - if ( class_exists( '\WC_Connect_Options' ) ) { - $wcs_tos_accepted = \WC_Connect_Options::get_option( 'tos_accepted' ); + if ( class_exists( '\Automattic\WCShipping\Utils' ) ) { + $wcs_version = \Automattic\WCShipping\Utils::get_wcshipping_version(); } $incompatible_plugins = class_exists( '\WC_Shipping_Fedex_Init' ) || class_exists( '\WC_Shipping_UPS_Init' ) || class_exists( '\WC_Integration_ShippingEasy' ) || - class_exists( '\WC_ShipStation_Integration' ) || - class_exists( '\Automattic\WCShipping\Loader' ) || - class_exists( '\Automattic\WCTax\Loader' ); + class_exists( '\WC_ShipStation_Integration' ); $this->shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( - $jetpack_version, - $jetpack_connected, + $dotcom_connected, $wcs_version, - $wcs_tos_accepted, $incompatible_plugins ); } @@ -79,15 +70,12 @@ class ShippingLabelBanner { /** * Add metabox to order page. - * - * @param string $post_type current post type. - * @param \WP_Post $post Current post object. */ - public function add_meta_boxes( $post_type, $post ) { - if ( 'shop_order' !== $post_type ) { + public function add_meta_boxes() { + if ( ! OrderUtil::is_order_edit_screen() ) { return; } - $order = wc_get_order( $post ); + if ( $this->should_show_meta_box() ) { add_meta_box( 'woocommerce-admin-print-label', @@ -98,8 +86,6 @@ class ShippingLabelBanner { 'high', array( 'context' => 'shipping_label', - 'order' => $post->ID, - 'items' => $this->count_shippable_items( $order ), ) ); add_action( 'admin_enqueue_scripts', array( $this, 'add_print_shipping_label_script' ) ); @@ -132,14 +118,30 @@ class ShippingLabelBanner { public function add_print_shipping_label_script( $hook ) { WCAdminAssets::register_style( 'print-shipping-label-banner', 'style', array( 'wp-components' ) ); WCAdminAssets::register_script( 'wp-admin-scripts', 'print-shipping-label-banner', true ); + $wcst_version = null; + $wcshipping_installed_version = null; + $order = wc_get_order(); + if ( class_exists( '\WC_Connect_Loader' ) ) { + $wcst_version = \WC_Connect_Loader::get_wcs_version(); + } + + $wc_shipping_plugin_file = WP_PLUGIN_DIR . '/woocommerce-shipping/woocommerce-shipping.php'; + if ( file_exists( $wc_shipping_plugin_file ) ) { + $plugin_data = get_plugin_data( $wc_shipping_plugin_file ); + $wcshipping_installed_version = $plugin_data['Version']; + } $payload = array( - 'nonce' => wp_create_nonce( 'wp_rest' ), - 'baseURL' => get_rest_url(), - 'wcs_server_connection' => true, + // If WCS&T is not installed, it's considered compatible. + 'is_wcst_compatible' => $wcst_version ? (int) version_compare( $wcst_version, self::MIN_COMPATIBLE_WCST_VERSION, '>=' ) : 1, + 'order_id' => $order ? $order->get_id() : null, + // The banner is shown if the plugin is installed but not active, so we need to check if the installed version is compatible. + 'is_incompatible_wcshipping_installed' => $wcshipping_installed_version ? + (int) version_compare( $wcshipping_installed_version, self::MIN_COMPATIBLE_WCSHIPPING_VERSION, '<' ) + : 0, ); - wp_localize_script( 'print-shipping-label-banner', 'wcConnectData', $payload ); + wp_localize_script( 'wc-admin-print-shipping-label-banner', 'wcShippingCoreData', $payload ); } /** diff --git a/plugins/woocommerce/src/Internal/Admin/ShippingLabelBannerDisplayRules.php b/plugins/woocommerce/src/Internal/Admin/ShippingLabelBannerDisplayRules.php index 470596b2d5e..649c6670af5 100644 --- a/plugins/woocommerce/src/Internal/Admin/ShippingLabelBannerDisplayRules.php +++ b/plugins/woocommerce/src/Internal/Admin/ShippingLabelBannerDisplayRules.php @@ -6,24 +6,23 @@ namespace Automattic\WooCommerce\Internal\Admin; /** - * Determines whether or not the Shipping Label Banner should be displayed + * Determines whether the Shipping Label Banner should be displayed */ class ShippingLabelBannerDisplayRules { /** - * Holds the installed Jetpack version. - * - * @var string - */ - private $jetpack_version; - - - /** - * Whether or not the installed Jetpack is connected. + * Whether the site is connected to wordpress.com. * * @var bool */ - private $jetpack_connected; + private $dotcom_connected; + + /** + * Whether installed plugins are incompatible with the banner. + * + * @var bool + */ + private $no_incompatible_plugins_installed; /** * Holds the installed WooCommerce Shipping & Tax version. @@ -32,34 +31,6 @@ class ShippingLabelBannerDisplayRules { */ private $wcs_version; - /** - * Whether or not there're plugins installed incompatible with the banner. - * - * @var bool - */ - private $no_incompatible_plugins_installed; - - /** - * Whether or not the WooCommerce Shipping & Tax ToS has been accepted. - * - * @var bool - */ - private $wcs_tos_accepted; - - /** - * Minimum supported Jetpack version. - * - * @var string - */ - private $min_jetpack_version = '4.4'; - - /** - * Minimum supported WooCommerce Shipping & Tax version. - * - * @var string - */ - private $min_wcs_version = '1.22.5'; - /** * Supported countries by USPS, see: https://webpmt.usps.gov/pmt010.cfm * @@ -78,17 +49,13 @@ class ShippingLabelBannerDisplayRules { /** * Constructor. * - * @param string $jetpack_version Installed Jetpack version to check. - * @param bool $jetpack_connected Is Jetpack connected?. - * @param string $wcs_version Installed WooCommerce Shipping & Tax version to check. - * @param bool $wcs_tos_accepted WooCommerce Shipping & Tax Terms of Service accepted?. - * @param bool $incompatible_plugins_installed Are there any incompatible plugins installed?. + * @param bool $dotcom_connected Is site connected to wordpress.com?. + * @param string|null $wcs_version Installed WooCommerce Shipping version to check, null if not installed. + * @param bool $incompatible_plugins_installed Are there any incompatible plugins installed?. */ - public function __construct( $jetpack_version, $jetpack_connected, $wcs_version, $wcs_tos_accepted, $incompatible_plugins_installed ) { - $this->jetpack_version = $jetpack_version; - $this->jetpack_connected = $jetpack_connected; + public function __construct( $dotcom_connected, $wcs_version, $incompatible_plugins_installed ) { + $this->dotcom_connected = $dotcom_connected; $this->wcs_version = $wcs_version; - $this->wcs_tos_accepted = $wcs_tos_accepted; $this->no_incompatible_plugins_installed = ! $incompatible_plugins_installed; } @@ -97,15 +64,11 @@ class ShippingLabelBannerDisplayRules { */ public function should_display_banner() { return $this->banner_not_dismissed() && - $this->jetpack_installed_and_active() && - $this->jetpack_up_to_date() && - $this->jetpack_connected && + $this->dotcom_connected && $this->no_incompatible_plugins_installed && $this->order_has_shippable_products() && $this->store_in_us_and_usd() && - ( $this->wcs_not_installed() || ( - $this->wcs_up_to_date() && ! $this->wcs_tos_accepted - ) ); + $this->wcs_not_installed(); } /** @@ -129,36 +92,13 @@ class ShippingLabelBannerDisplayRules { return ! $dismissed_for_good && ! $dismissed_24h; } - /** - * Checks if jetpack is installed and active. - * - * @return bool - */ - private function jetpack_installed_and_active() { - return ! ! $this->jetpack_version; - } - - /** - * Checks if Jetpack version is supported. - * - * @return bool - */ - private function jetpack_up_to_date() { - return version_compare( $this->jetpack_version, $this->min_jetpack_version, '>=' ); - } - /** * Checks if there's a shippable product in the current order. * * @return bool */ private function order_has_shippable_products() { - $post = get_post(); - if ( ! $post ) { - return false; - } - - $order = wc_get_order( get_post()->ID ); + $order = wc_get_order(); if ( ! $order ) { return false; @@ -197,11 +137,4 @@ class ShippingLabelBannerDisplayRules { private function wcs_not_installed() { return ! $this->wcs_version; } - - /** - * Checks if WooCommerce Shipping & Tax is up to date. - */ - private function wcs_up_to_date() { - return $this->wcs_version && version_compare( $this->wcs_version, $this->min_wcs_version, '>=' ); - } } diff --git a/plugins/woocommerce/src/Internal/ComingSoon/ComingSoonAdminBarBadge.php b/plugins/woocommerce/src/Internal/ComingSoon/ComingSoonAdminBarBadge.php new file mode 100644 index 00000000000..0b65041e322 --- /dev/null +++ b/plugins/woocommerce/src/Internal/ComingSoon/ComingSoonAdminBarBadge.php @@ -0,0 +1,108 @@ + __( 'Coming soon', 'woocommerce' ), + 'store-coming-soon' => __( 'Store coming soon', 'woocommerce' ), + 'live' => __( 'Live', 'woocommerce' ), + ); + + if ( get_option( 'woocommerce_coming_soon' ) === 'yes' ) { + if ( get_option( 'woocommerce_store_pages_only' ) === 'yes' ) { + $key = 'store-coming-soon'; + } else { + $key = 'coming-soon'; + } + } else { + $key = 'live'; + } + + $args = array( + 'id' => 'woocommerce-site-visibility-badge', + 'title' => $labels[ $key ], + 'href' => admin_url( 'admin.php?page=wc-settings&tab=site-visibility' ), + 'meta' => array( + 'class' => 'woocommerce-site-status-badge-' . $key, + ), + ); + $wp_admin_bar->add_node( $args ); + } + + /** + * Output CSS for site visibility badge. + * + * @internal + */ + public function output_css() { + if ( is_admin_bar_showing() ) { + echo ''; + } + } +} diff --git a/plugins/woocommerce/src/Internal/ComingSoon/ComingSoonRequestHandler.php b/plugins/woocommerce/src/Internal/ComingSoon/ComingSoonRequestHandler.php index f0465b18d0f..d72074780f1 100644 --- a/plugins/woocommerce/src/Internal/ComingSoon/ComingSoonRequestHandler.php +++ b/plugins/woocommerce/src/Internal/ComingSoon/ComingSoonRequestHandler.php @@ -113,7 +113,7 @@ class ComingSoonRequestHandler { return false; } - // Do not show coming soon on 404 pages when restrict to store pages only. + // Do not show coming soon on 404 pages when applied to store pages only. if ( $this->coming_soon_helper->is_store_coming_soon() && is_404() ) { return false; } diff --git a/plugins/woocommerce/src/Internal/DataStores/Orders/DataSynchronizer.php b/plugins/woocommerce/src/Internal/DataStores/Orders/DataSynchronizer.php index 2cdc657d408..ea472d9af76 100644 --- a/plugins/woocommerce/src/Internal/DataStores/Orders/DataSynchronizer.php +++ b/plugins/woocommerce/src/Internal/DataStores/Orders/DataSynchronizer.php @@ -16,7 +16,7 @@ use Automattic\WooCommerce\Proxies\LegacyProxy; defined( 'ABSPATH' ) || exit; /** - * This class handles the database structure creation and the data synchronization for the custom orders tables. Its responsibilites are: + * This class handles the database structure creation and the data synchronization for the custom orders tables. Its responsibilities are: * * - Providing entry points for creating and deleting the required database tables. * - Synchronizing changes between the custom orders tables and the posts table whenever changes in orders happen. diff --git a/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableDataStore.php b/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableDataStore.php index e0fb6c2b4c7..9602bd4f6b7 100644 --- a/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableDataStore.php +++ b/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableDataStore.php @@ -98,7 +98,7 @@ class OrdersTableDataStore extends \Abstract_WC_Order_Data_Store_CPT implements ); /** - * Meta keys that are considered ephemereal and do not trigger a full save (updating modified date) when changed. + * Meta keys that are considered ephemeral and do not trigger a full save (updating modified date) when changed. * * @var string[] */ @@ -2644,7 +2644,7 @@ FROM $order_meta_table } /** - * Proxy to udpating order meta. Here for backward compatibility reasons. + * Proxy to updating order meta. Here for backward compatibility reasons. * * @param \WC_Order $order Order object. * diff --git a/plugins/woocommerce/src/Internal/DependencyManagement/ServiceProviders/ComingSoonServiceProvider.php b/plugins/woocommerce/src/Internal/DependencyManagement/ServiceProviders/ComingSoonServiceProvider.php index ba544bfaba0..10dc40ac8aa 100644 --- a/plugins/woocommerce/src/Internal/DependencyManagement/ServiceProviders/ComingSoonServiceProvider.php +++ b/plugins/woocommerce/src/Internal/DependencyManagement/ServiceProviders/ComingSoonServiceProvider.php @@ -3,6 +3,7 @@ namespace Automattic\WooCommerce\Internal\DependencyManagement\ServiceProviders; use Automattic\WooCommerce\Internal\DependencyManagement\AbstractServiceProvider; +use Automattic\WooCommerce\Internal\ComingSoon\ComingSoonAdminBarBadge; use Automattic\WooCommerce\Internal\ComingSoon\ComingSoonCacheInvalidator; use Automattic\WooCommerce\Internal\ComingSoon\ComingSoonRequestHandler; use Automattic\WooCommerce\Internal\ComingSoon\ComingSoonHelper; @@ -18,6 +19,7 @@ class ComingSoonServiceProvider extends AbstractServiceProvider { * @var array */ protected $provides = array( + ComingSoonAdminBarBadge::class, ComingSoonCacheInvalidator::class, ComingSoonHelper::class, ComingSoonRequestHandler::class, @@ -27,6 +29,7 @@ class ComingSoonServiceProvider extends AbstractServiceProvider { * Register the classes. */ public function register() { + $this->add( ComingSoonAdminBarBadge::class ); $this->add( ComingSoonCacheInvalidator::class ); $this->add( ComingSoonHelper::class ); $this->add( ComingSoonRequestHandler::class )->addArgument( ComingSoonHelper::class ); diff --git a/plugins/woocommerce/src/Internal/Logging/RemoteLogger.php b/plugins/woocommerce/src/Internal/Logging/RemoteLogger.php index 01e71d40e62..3043cd110fe 100644 --- a/plugins/woocommerce/src/Internal/Logging/RemoteLogger.php +++ b/plugins/woocommerce/src/Internal/Logging/RemoteLogger.php @@ -20,11 +20,10 @@ use WC_Log_Levels; * @package WooCommerce\Classes */ class RemoteLogger extends \WC_Log_Handler { - const LOG_ENDPOINT = 'https://public-api.wordpress.com/rest/v1.1/logstash'; - const RATE_LIMIT_ID = 'woocommerce_remote_logging'; - const RATE_LIMIT_DELAY = 60; // 1 minute. - const WC_LATEST_VERSION_TRANSIENT = 'latest_woocommerce_version'; - const FETCH_LATEST_VERSION_RETRY = 'fetch_latest_woocommerce_version_retry'; + const LOG_ENDPOINT = 'https://public-api.wordpress.com/rest/v1.1/logstash'; + const RATE_LIMIT_ID = 'woocommerce_remote_logging'; + const RATE_LIMIT_DELAY = 60; // 1 minute. + const WC_NEW_VERSION_TRANSIENT = 'woocommerce_new_version'; /** * Handle a log entry. @@ -71,7 +70,7 @@ class RemoteLogger extends \WC_Log_Handler { 'wc_version' => WC()->version, 'php_version' => phpversion(), 'wp_version' => get_bloginfo( 'version' ), - 'request_uri' => filter_input( INPUT_SERVER, 'REQUEST_URI', FILTER_SANITIZE_URL ), + 'request_uri' => $this->sanitize_request_uri( filter_input( INPUT_SERVER, 'REQUEST_URI', FILTER_SANITIZE_URL ) ), ), ); @@ -150,7 +149,7 @@ class RemoteLogger extends \WC_Log_Handler { return false; } - if ( ! $this->is_latest_woocommerce_version() ) { + if ( ! $this->should_current_version_be_logged() ) { return false; } @@ -221,7 +220,7 @@ class RemoteLogger extends \WC_Log_Handler { self::LOG_ENDPOINT, array( 'body' => wp_json_encode( $body ), - 'timeout' => 2, + 'timeout' => 3, 'headers' => array( 'Content-Type' => 'application/json', ), @@ -256,14 +255,22 @@ class RemoteLogger extends \WC_Log_Handler { * * @return bool */ - private function is_latest_woocommerce_version() { - $latest_wc_version = $this->fetch_latest_woocommerce_version(); + private function should_current_version_be_logged() { + $new_version = get_site_transient( self::WC_NEW_VERSION_TRANSIENT ); - if ( is_null( $latest_wc_version ) ) { - return false; + if ( false === $new_version ) { + $new_version = $this->fetch_new_woocommerce_version(); + // Cache the new version for a week since we want to keep logging in with the same version for a while even if the new version is available. + set_site_transient( self::WC_NEW_VERSION_TRANSIENT, $new_version, WEEK_IN_SECONDS ); } - return version_compare( WC()->version, $latest_wc_version, '>=' ); + if ( ! is_string( $new_version ) || '' === $new_version ) { + // If the new version is not available, we consider the current version to be the latest. + return true; + } + + // If the current version is the latest, we don't want to log errors. + return version_compare( WC()->version, $new_version, '>=' ); } /** @@ -316,45 +323,34 @@ class RemoteLogger extends \WC_Log_Handler { } /** - * Fetch the latest WooCommerce version using the WordPress API and cache it. + * Fetch the new version of WooCommerce from the WordPress API. * - * @return string|null + * @return string|null New version if an update is available, null otherwise. */ - private function fetch_latest_woocommerce_version() { - $cached_version = get_transient( self::WC_LATEST_VERSION_TRANSIENT ); - if ( $cached_version ) { - return $cached_version; + private function fetch_new_woocommerce_version() { + if ( ! function_exists( 'get_plugins' ) ) { + require_once ABSPATH . 'wp-admin/includes/plugin.php'; + } + if ( ! function_exists( 'get_plugin_updates' ) ) { + require_once ABSPATH . 'wp-admin/includes/update.php'; } - $retry_count = get_transient( self::FETCH_LATEST_VERSION_RETRY ); - if ( false === $retry_count || ! is_numeric( $retry_count ) ) { - $retry_count = 0; - } + $plugin_updates = get_plugin_updates(); - if ( $retry_count >= 3 ) { + // Check if WooCommerce plugin update information is available. + if ( ! is_array( $plugin_updates ) || ! isset( $plugin_updates[ WC_PLUGIN_BASENAME ] ) ) { return null; } - if ( ! function_exists( 'plugins_api' ) ) { - require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; - } - // Fetch the latest version from the WordPress API. - $plugin_info = plugins_api( 'plugin_information', array( 'slug' => 'woocommerce' ) ); + $wc_plugin_update = $plugin_updates[ WC_PLUGIN_BASENAME ]; - if ( is_wp_error( $plugin_info ) ) { - ++$retry_count; - set_transient( self::FETCH_LATEST_VERSION_RETRY, $retry_count, HOUR_IN_SECONDS ); + // Ensure the update object exists and has the required information. + if ( ! $wc_plugin_update || ! isset( $wc_plugin_update->update->new_version ) ) { return null; } - if ( ! empty( $plugin_info->version ) ) { - $latest_version = $plugin_info->version; - set_transient( self::WC_LATEST_VERSION_TRANSIENT, $latest_version, WEEK_IN_SECONDS ); - delete_transient( self::FETCH_LATEST_VERSION_RETRY ); - return $latest_version; - } - - return null; + $new_version = $wc_plugin_update->update->new_version; + return is_string( $new_version ) ? $new_version : null; } /** @@ -435,4 +431,63 @@ class RemoteLogger extends \WC_Log_Handler { protected function is_dev_or_local_environment() { return in_array( wp_get_environment_type(), array( 'development', 'local' ), true ); } + /** + * Sanitize the request URI to only allow certain query parameters. + * + * @param string $request_uri The request URI to sanitize. + * @return string The sanitized request URI. + */ + private function sanitize_request_uri( $request_uri ) { + $default_whitelist = array( + 'path', + 'page', + 'step', + 'task', + 'tab', + 'section', + 'status', + 'post_type', + 'taxonomy', + 'action', + ); + + /** + * Filter to allow other plugins to whitelist request_uri query parameter values for unmasked remote logging. + * + * @since 9.4.0 + * + * @param string $default_whitelist The default whitelist of query parameters. + */ + $whitelist = apply_filters( 'woocommerce_remote_logger_request_uri_whitelist', $default_whitelist ); + + $parsed_url = wp_parse_url( $request_uri ); + if ( ! isset( $parsed_url['query'] ) ) { + return $request_uri; + } + + parse_str( $parsed_url['query'], $query_params ); + + foreach ( $query_params as $key => &$value ) { + if ( ! in_array( $key, $whitelist, true ) ) { + $value = 'xxxxxx'; + } + } + + $parsed_url['query'] = http_build_query( $query_params ); + return $this->build_url( $parsed_url ); + } + + /** + * Build a URL from its parsed components. + * + * @param array $parsed_url The parsed URL components. + * @return string The built URL. + */ + private function build_url( $parsed_url ) { + $path = $parsed_url['path'] ?? ''; + $query = isset( $parsed_url['query'] ) ? "?{$parsed_url['query']}" : ''; + $fragment = isset( $parsed_url['fragment'] ) ? "#{$parsed_url['fragment']}" : ''; + + return "$path$query$fragment"; + } } diff --git a/plugins/woocommerce/src/Internal/ProductDownloads/ApprovedDirectories/Synchronize.php b/plugins/woocommerce/src/Internal/ProductDownloads/ApprovedDirectories/Synchronize.php index e643f439336..5d214b54335 100644 --- a/plugins/woocommerce/src/Internal/ProductDownloads/ApprovedDirectories/Synchronize.php +++ b/plugins/woocommerce/src/Internal/ProductDownloads/ApprovedDirectories/Synchronize.php @@ -130,7 +130,7 @@ class Synchronize { } /** - * Runs the syncronization task. + * Runs the synchronization task. */ public function run() { $products = $this->get_next_set_of_downloadable_products(); diff --git a/plugins/woocommerce/src/Internal/Traits/OrderAttributionMeta.php b/plugins/woocommerce/src/Internal/Traits/OrderAttributionMeta.php index 1bd7ea2cd4f..4a09f58c82b 100644 --- a/plugins/woocommerce/src/Internal/Traits/OrderAttributionMeta.php +++ b/plugins/woocommerce/src/Internal/Traits/OrderAttributionMeta.php @@ -19,7 +19,7 @@ use WP_Post; trait OrderAttributionMeta { /** - * The default fields and their sourcebuster accesors, + * The default fields and their sourcebuster accessors, * to show in the source data metabox. * * @var string[] diff --git a/plugins/woocommerce/src/Internal/Utilities/URL.php b/plugins/woocommerce/src/Internal/Utilities/URL.php index 60963b09ffa..dea63a76cc8 100644 --- a/plugins/woocommerce/src/Internal/Utilities/URL.php +++ b/plugins/woocommerce/src/Internal/Utilities/URL.php @@ -281,7 +281,7 @@ class URL { $double_dots = array_keys( $this->path_parts, '..', true ); $max_dot_index = max( array_merge( $single_dots, $double_dots ) ); - // Prepend the required number of traversals and discard unnessary trailing segments. + // Prepend the required number of traversals and discard unnecessary trailing segments. $last_traversal = $max_dot_index + ( $this->is_non_root_directory ? 1 : 0 ); $parent_path = str_repeat( '../', $level ) . join( '/', array_slice( $this->path_parts, 0, $last_traversal ) ); } elseif ( $parent_path_parts_to_keep < 0 ) { diff --git a/plugins/woocommerce/src/Packages.php b/plugins/woocommerce/src/Packages.php index 46117f7d74d..b9255a4580c 100644 --- a/plugins/woocommerce/src/Packages.php +++ b/plugins/woocommerce/src/Packages.php @@ -32,7 +32,7 @@ class Packages { /** * Array of package names and their main package classes. * - * One a package has been merged into WooCommerce Core it should be moved fron the package list and placed in + * One a package has been merged into WooCommerce Core it should be moved from the package list and placed in * this list. This will ensure that the feature plugin is disabled as well as provide the class to handle * initialization for the now-merged feature plugin. * diff --git a/plugins/woocommerce/src/StoreApi/Routes/V1/AI/BusinessDescription.php b/plugins/woocommerce/src/StoreApi/Routes/V1/AI/BusinessDescription.php deleted file mode 100644 index 79e5a221380..00000000000 --- a/plugins/woocommerce/src/StoreApi/Routes/V1/AI/BusinessDescription.php +++ /dev/null @@ -1,97 +0,0 @@ - \WP_REST_Server::CREATABLE, - 'callback' => [ $this, 'get_response' ], - 'permission_callback' => [ Middleware::class, 'is_authorized' ], - 'args' => [ - 'business_description' => [ - 'description' => __( 'The business description for a given store.', 'woocommerce' ), - 'type' => 'string', - ], - ], - ], - 'schema' => [ $this->schema, 'get_public_item_schema' ], - 'allow_batch' => [ 'v1' => true ], - ]; - } - - /** - * Update the last business description. - * - * @param \WP_REST_Request $request Request object. - * - * @return bool|string|\WP_Error|\WP_REST_Response - */ - protected function get_route_post_response( \WP_REST_Request $request ) { - - $business_description = $request->get_param( 'business_description' ); - - if ( ! $business_description ) { - return $this->error_to_response( - new \WP_Error( - 'invalid_business_description', - __( 'Invalid business description.', 'woocommerce' ) - ) - ); - } - - update_option( 'last_business_description_with_ai_content_generated', $business_description ); - - return rest_ensure_response( - array( - 'ai_content_generated' => true, - ) - ); - } - -} diff --git a/plugins/woocommerce/src/StoreApi/Routes/V1/AI/Images.php b/plugins/woocommerce/src/StoreApi/Routes/V1/AI/Images.php deleted file mode 100644 index 51cf7d2abc1..00000000000 --- a/plugins/woocommerce/src/StoreApi/Routes/V1/AI/Images.php +++ /dev/null @@ -1,130 +0,0 @@ - \WP_REST_Server::CREATABLE, - 'callback' => [ $this, 'get_response' ], - 'permission_callback' => [ Middleware::class, 'is_authorized' ], - 'args' => [ - 'business_description' => [ - 'description' => __( 'The business description for a given store.', 'woocommerce' ), - 'type' => 'string', - ], - ], - ], - 'schema' => [ $this->schema, 'get_public_item_schema' ], - 'allow_batch' => [ 'v1' => true ], - ]; - } - - /** - * Generate Images from Pexels - * - * @param \WP_REST_Request $request Request object. - * - * @return bool|string|\WP_Error|\WP_REST_Response - */ - protected function get_route_post_response( \WP_REST_Request $request ) { - - $business_description = sanitize_text_field( wp_unslash( $request['business_description'] ) ); - - if ( empty( $business_description ) ) { - $business_description = get_option( 'woo_ai_describe_store_description' ); - } - - $last_business_description = get_option( 'last_business_description_with_ai_content_generated' ); - - if ( $last_business_description === $business_description ) { - return rest_ensure_response( - $this->prepare_item_for_response( - [ - 'ai_content_generated' => true, - 'images' => array(), - ], - $request - ) - ); - } - - $ai_connection = new Connection(); - - $site_id = $ai_connection->get_site_id(); - - if ( is_wp_error( $site_id ) ) { - return $this->error_to_response( $site_id ); - } - - $token = $ai_connection->get_jwt_token( $site_id ); - - if ( is_wp_error( $token ) ) { - return $this->error_to_response( $token ); - } - - $images = ( new Pexels() )->get_images( $ai_connection, $token, $business_description ); - - if ( is_wp_error( $images ) ) { - $images = array( - 'images' => array(), - 'search_term' => '', - ); - } - - return rest_ensure_response( - $this->prepare_item_for_response( - [ - 'ai_content_generated' => true, - 'images' => $images, - ], - $request - ) - ); - } -} diff --git a/plugins/woocommerce/src/StoreApi/Routes/V1/AI/Middleware.php b/plugins/woocommerce/src/StoreApi/Routes/V1/AI/Middleware.php deleted file mode 100644 index 33a2d8ece7f..00000000000 --- a/plugins/woocommerce/src/StoreApi/Routes/V1/AI/Middleware.php +++ /dev/null @@ -1,50 +0,0 @@ -getErrorCode(), - $error->getMessage(), - array( 'status' => $error->getCode() ) - ); - } - - $allow_ai_connection = get_option( 'woocommerce_blocks_allow_ai_connection' ); - - if ( ! $allow_ai_connection ) { - try { - throw new RouteException( 'ai_connection_not_allowed', __( 'AI content generation is not allowed on this store. Update your store settings if you wish to enable this feature.', 'woocommerce' ), 403 ); - } catch ( RouteException $error ) { - return new \WP_Error( - $error->getErrorCode(), - $error->getMessage(), - array( 'status' => $error->getCode() ) - ); - } - } - - return true; - } -} diff --git a/plugins/woocommerce/src/StoreApi/Routes/V1/AI/Patterns.php b/plugins/woocommerce/src/StoreApi/Routes/V1/AI/Patterns.php deleted file mode 100644 index f818c7860a0..00000000000 --- a/plugins/woocommerce/src/StoreApi/Routes/V1/AI/Patterns.php +++ /dev/null @@ -1,122 +0,0 @@ - \WP_REST_Server::CREATABLE, - 'callback' => [ $this, 'get_response' ], - 'permission_callback' => [ Middleware::class, 'is_authorized' ], - 'args' => [ - 'business_description' => [ - 'description' => __( 'The business description for a given store.', 'woocommerce' ), - 'type' => 'string', - ], - 'images' => [ - 'description' => __( 'The images for a given store.', 'woocommerce' ), - 'type' => 'object', - ], - ], - ], - [ - 'methods' => \WP_REST_Server::DELETABLE, - 'callback' => [ $this, 'get_response' ], - 'permission_callback' => [ Middleware::class, 'is_authorized' ], - ], - 'schema' => [ $this->schema, 'get_public_item_schema' ], - 'allow_batch' => [ 'v1' => true ], - ]; - } - - /** - * Update patterns with the content and images powered by AI. - * - * @param \WP_REST_Request $request Request object. - * - * @return WP_Error|\WP_HTTP_Response|\WP_REST_Response - */ - protected function get_route_post_response( \WP_REST_Request $request ) { - $business_description = sanitize_text_field( wp_unslash( $request['business_description'] ) ); - - $ai_connection = new Connection(); - - $site_id = $ai_connection->get_site_id(); - - if ( is_wp_error( $site_id ) ) { - return $this->error_to_response( $site_id ); - } - - $token = $ai_connection->get_jwt_token( $site_id ); - - $images = $request['images']; - - try { - ( new UpdatePatterns() )->generate_content( $ai_connection, $token, $images, $business_description ); - return rest_ensure_response( array( 'ai_content_generated' => true ) ); - } catch ( WP_Error $e ) { - return $this->error_to_response( $e ); - } - } - - /** - * Remove patterns generated by AI. - * - * @param \WP_REST_Request $request Request object. - * - * @return bool|string|WP_Error|\WP_REST_Response - */ - protected function get_route_delete_response( \WP_REST_Request $request ) { - PatternsHelper::delete_patterns_ai_data_post(); - return rest_ensure_response( array( 'removed' => true ) ); - } -} diff --git a/plugins/woocommerce/src/StoreApi/Routes/V1/AI/Product.php b/plugins/woocommerce/src/StoreApi/Routes/V1/AI/Product.php deleted file mode 100644 index 46b76355e71..00000000000 --- a/plugins/woocommerce/src/StoreApi/Routes/V1/AI/Product.php +++ /dev/null @@ -1,107 +0,0 @@ - \WP_REST_Server::CREATABLE, - 'callback' => [ $this, 'get_response' ], - 'permission_callback' => [ Middleware::class, 'is_authorized' ], - 'args' => [ - 'products_information' => [ - 'description' => __( 'Data generated by AI for updating dummy products.', 'woocommerce' ), - 'type' => 'object', - ], - 'last_product' => [ - 'description' => __( 'Whether the product being updated is the last one in the loop', 'woocommerce' ), - 'type' => 'boolean', - ], - ], - ], - 'schema' => [ $this->schema, 'get_public_item_schema' ], - 'allow_batch' => [ 'v1' => true ], - ]; - } - - /** - * Update product with the content and image powered by AI. - * - * @param \WP_REST_Request $request Request object. - * - * @return bool|string|\WP_Error|\WP_REST_Response - */ - protected function get_route_post_response( \WP_REST_Request $request ) { - $product_updater = new UpdateProducts(); - $product_information = $request['products_information'] ?? array(); - - if ( empty( $product_information ) ) { - return rest_ensure_response( - array( - 'ai_content_generated' => true, - ) - ); - } - - $product_updater->update_product_content( $product_information ); - - $last_product_to_update = $request['last_product'] ?? false; - - if ( $last_product_to_update ) { - flush_rewrite_rules(); - } - - return rest_ensure_response( - array( - 'ai_content_generated' => true, - ) - ); - } - -} diff --git a/plugins/woocommerce/src/StoreApi/Routes/V1/AI/Products.php b/plugins/woocommerce/src/StoreApi/Routes/V1/AI/Products.php deleted file mode 100644 index 086b8a61636..00000000000 --- a/plugins/woocommerce/src/StoreApi/Routes/V1/AI/Products.php +++ /dev/null @@ -1,153 +0,0 @@ - \WP_REST_Server::CREATABLE, - 'callback' => [ $this, 'get_response' ], - 'permission_callback' => [ Middleware::class, 'is_authorized' ], - 'args' => [ - 'business_description' => [ - 'description' => __( 'The business description for a given store.', 'woocommerce' ), - 'type' => 'string', - ], - 'images' => [ - 'description' => __( 'The images for a given store.', 'woocommerce' ), - 'type' => 'object', - ], - ], - ], - [ - 'methods' => \WP_REST_Server::DELETABLE, - 'callback' => [ $this, 'get_response' ], - 'permission_callback' => [ Middleware::class, 'is_authorized' ], - ], - 'schema' => [ $this->schema, 'get_public_item_schema' ], - 'allow_batch' => [ 'v1' => true ], - ]; - } - - /** - * Generate the content for the products. - * - * @param \WP_REST_Request $request Request object. - * - * @return bool|string|\WP_Error|\WP_REST_Response - */ - protected function get_route_post_response( \WP_REST_Request $request ) { - $allow_ai_connection = get_option( 'woocommerce_blocks_allow_ai_connection' ); - - if ( ! $allow_ai_connection ) { - return rest_ensure_response( - $this->error_to_response( - new \WP_Error( - 'ai_connection_not_allowed', - __( 'AI content generation is not allowed on this store. Update your store settings if you wish to enable this feature.', 'woocommerce' ) - ) - ) - ); - } - - $business_description = sanitize_text_field( wp_unslash( $request['business_description'] ) ); - - if ( empty( $business_description ) ) { - $business_description = get_option( 'woo_ai_describe_store_description' ); - } - - $ai_connection = new Connection(); - - $site_id = $ai_connection->get_site_id(); - - if ( is_wp_error( $site_id ) ) { - return $this->error_to_response( $site_id ); - } - - $token = $ai_connection->get_jwt_token( $site_id ); - - if ( is_wp_error( $token ) ) { - return $this->error_to_response( $token ); - } - - $images = $request['images']; - - $populate_products = ( new UpdateProducts() )->generate_content( $ai_connection, $token, $images, $business_description ); - - if ( is_wp_error( $populate_products ) ) { - return $this->error_to_response( $populate_products ); - } - - if ( ! isset( $populate_products['product_content'] ) ) { - return $this->error_to_response( new \WP_Error( 'product_content_not_found', __( 'Product content not found.', 'woocommerce' ) ) ); - } - - $product_content = $populate_products['product_content']; - - $item = array( - 'ai_content_generated' => true, - 'product_content' => $product_content, - ); - - return rest_ensure_response( $item ); - } - - /** - * Remove products generated by AI. - * - * @param \WP_REST_Request $request Request object. - * - * @return bool|string|\WP_Error|\WP_REST_Response - */ - protected function get_route_delete_response( \WP_REST_Request $request ) { - ( new UpdateProducts() )->reset_products_content(); - return rest_ensure_response( array( 'removed' => true ) ); - } -} diff --git a/plugins/woocommerce/src/StoreApi/Routes/V1/AI/StoreInfo.php b/plugins/woocommerce/src/StoreApi/Routes/V1/AI/StoreInfo.php deleted file mode 100644 index c3b28d61676..00000000000 --- a/plugins/woocommerce/src/StoreApi/Routes/V1/AI/StoreInfo.php +++ /dev/null @@ -1,94 +0,0 @@ - \WP_REST_Server::READABLE, - 'callback' => [ $this, 'get_response' ], - 'permission_callback' => [ Middleware::class, 'is_authorized' ], - ], - 'schema' => [ $this->schema, 'get_public_item_schema' ], - 'allow_batch' => [ 'v1' => true ], - ]; - } - - /** - * Update the store title powered by AI. - * - * @param \WP_REST_Request $request Request object. - * - * @return bool|string|\WP_Error|\WP_REST_Response - */ - protected function get_route_response( \WP_REST_Request $request ) { - $product_updater = new UpdateProducts(); - $patterns = PatternsHelper::get_patterns_ai_data_post(); - - $products = $product_updater->fetch_product_ids( 'dummy' ); - - if ( empty( $products ) && ! isset( $patterns ) ) { - return rest_ensure_response( - array( - 'is_ai_generated' => false, - ) - ); - } - - return rest_ensure_response( - array( - 'is_ai_generated' => true, - ) - ); - - } - - -} diff --git a/plugins/woocommerce/src/StoreApi/Routes/V1/Checkout.php b/plugins/woocommerce/src/StoreApi/Routes/V1/Checkout.php index 243403488f5..69e72e54806 100644 --- a/plugins/woocommerce/src/StoreApi/Routes/V1/Checkout.php +++ b/plugins/woocommerce/src/StoreApi/Routes/V1/Checkout.php @@ -1,14 +1,12 @@ should_create_customer_account( $request ) ) { - $customer_id = $this->create_customer_account( - $request['billing_address']['email'], - $request['billing_address']['first_name'], - $request['billing_address']['last_name'], - $request['customer_password'] + if ( $this->should_create_customer_account( $request ) ) { + $customer_id = wc_create_new_customer( + $request['billing_address']['email'], + '', + $request['customer_password'], + [ + 'first_name' => $request['billing_address']['first_name'], + 'last_name' => $request['billing_address']['last_name'], + 'source' => 'store-api', + ] + ); + + if ( is_wp_error( $customer_id ) ) { + throw new RouteException( + esc_html( $customer_id->get_error_code() ), + esc_html( $customer_id->get_error_message() ), + 400 ); - - // Associate customer with the order. This is done before login to ensure the order is associated with - // the correct customer if login fails. - $this->order->set_customer_id( $customer_id ); - $this->order->save(); - - // Log the customer in to WordPress. Doing this inline instead of using `wc_set_customer_auth_cookie` - // because wc_set_customer_auth_cookie forces the use of session cookie. - wp_set_current_user( $customer_id ); - wp_set_auth_cookie( $customer_id, true ); - - // Init session cookie if the session cookie handler exists. - if ( is_callable( [ WC()->session, 'init_session_cookie' ] ) ) { - WC()->session->init_session_cookie(); - } - } - } catch ( \Exception $error ) { - switch ( $error->getMessage() ) { - case 'registration-error-invalid-email': - throw new RouteException( - 'registration-error-invalid-email', - esc_html__( 'Please provide a valid email address.', 'woocommerce' ), - 400 - ); - case 'registration-error-email-exists': - throw new RouteException( - 'registration-error-email-exists', - sprintf( - // Translators: %s Email address. - esc_html__( 'An account is already registered with %s. Please log in or use a different email address.', 'woocommerce' ), - esc_html( $request['billing_address']['email'] ) - ), - 400 - ); - case 'registration-error-empty-password': - throw new RouteException( - 'registration-error-empty-password', - esc_html__( 'Please create a password for your account.', 'woocommerce' ), - 400 - ); } + + // Associate customer with the order. + $this->order->set_customer_id( $customer_id ); + $this->order->save(); + + // Set the customer auth cookie. + wc_set_customer_auth_cookie( $customer_id ); } // Persist customer address data to account. @@ -673,159 +648,4 @@ class Checkout extends AbstractCartRoute { return false; } - - /** - * Create a new account for a customer. - * - * The account is created with a generated username. The customer is sent - * an email notifying them about the account and containing a link to set - * their (initial) password. - * - * Intended as a replacement for wc_create_new_customer in WC core. - * - * @throws \Exception If an error is encountered when creating the user account. - * - * @param string $user_email The email address to use for the new account. - * @param string $first_name The first name to use for the new account. - * @param string $last_name The last name to use for the new account. - * @param string $password The password to use for the new account. If empty, a password will be generated. - * - * @return int User id if successful - */ - private function create_customer_account( $user_email, $first_name, $last_name, $password = '' ) { - if ( empty( $user_email ) || ! is_email( $user_email ) ) { - throw new \Exception( 'registration-error-invalid-email' ); - } - - if ( email_exists( $user_email ) ) { - throw new \Exception( 'registration-error-email-exists' ); - } - - // Handle password creation if not provided. - if ( empty( $password ) ) { - $password = wp_generate_password(); - $password_generated = true; - } else { - $password_generated = false; - } - - // This ensures `wp_generate_password` returned something (it is filterable and could be empty string). - if ( empty( $password ) ) { - throw new \Exception( 'registration-error-empty-password' ); - } - - $username = wc_create_new_customer_username( $user_email ); - - // Use WP_Error to handle registration errors. - $errors = new \WP_Error(); - - /** - * Fires before a customer account is registered. - * - * This hook fires before customer accounts are created and passes the form data (username, email) and an array - * of errors. - * - * This could be used to add extra validation logic and append errors to the array. - * - * @since 7.2.0 - * - * @internal Matches filter name in WooCommerce core. - * - * @param string $username Customer username. - * @param string $user_email Customer email address. - * @param \WP_Error $errors Error object. - */ - do_action( 'woocommerce_register_post', $username, $user_email, $errors ); - - /** - * Filters registration errors before a customer account is registered. - * - * This hook filters registration errors. This can be used to manipulate the array of errors before - * they are displayed. - * - * @since 7.2.0 - * - * @internal Matches filter name in WooCommerce core. - * - * @param \WP_Error $errors Error object. - * @param string $username Customer username. - * @param string $user_email Customer email address. - * @return \WP_Error - */ - $errors = apply_filters( 'woocommerce_registration_errors', $errors, $username, $user_email ); - - if ( is_wp_error( $errors ) && $errors->get_error_code() ) { - throw new \Exception( $errors->get_error_code() ); - } - - /** - * Filters customer data before a customer account is registered. - * - * This hook filters customer data. It allows user data to be changed, for example, username, password, email, - * first name, last name, and role. - * - * @since 7.2.0 - * - * @param array $customer_data An array of customer (user) data. - * @return array - */ - $new_customer_data = apply_filters( - 'woocommerce_new_customer_data', - array( - 'user_login' => $username, - 'user_pass' => $password, - 'user_email' => $user_email, - 'first_name' => $first_name, - 'last_name' => $last_name, - 'role' => 'customer', - 'source' => 'store-api', - ) - ); - - $customer_id = wp_insert_user( $new_customer_data ); - - if ( is_wp_error( $customer_id ) ) { - throw $this->map_create_account_error( $customer_id ); - } - - // Set account flag to remind customer to update generated password. - update_user_option( $customer_id, 'default_password_nag', true, true ); - - /** - * Fires after a customer account has been registered. - * - * This hook fires after customer accounts are created and passes the customer data. - * - * @since 7.2.0 - * - * @internal Matches filter name in WooCommerce core. - * - * @param integer $customer_id New customer (user) ID. - * @param array $new_customer_data Array of customer (user) data. - * @param string $password_generated The generated password for the account. - */ - do_action( 'woocommerce_created_customer', $customer_id, $new_customer_data, $password_generated ); - - return $customer_id; - } - - /** - * Convert an account creation error to an exception. - * - * @param \WP_Error $error An error object. - * @return \Exception. - */ - private function map_create_account_error( \WP_Error $error ) { - switch ( $error->get_error_code() ) { - // WordPress core error codes. - case 'empty_username': - case 'invalid_username': - case 'empty_email': - case 'invalid_email': - case 'email_exists': - case 'registerfail': - return new \Exception( 'woocommerce_rest_checkout_create_account_failure' ); - } - return new \Exception( 'woocommerce_rest_checkout_create_account_failure' ); - } } diff --git a/plugins/woocommerce/src/StoreApi/RoutesController.php b/plugins/woocommerce/src/StoreApi/RoutesController.php index 986c29e925f..502cf1e4ddd 100644 --- a/plugins/woocommerce/src/StoreApi/RoutesController.php +++ b/plugins/woocommerce/src/StoreApi/RoutesController.php @@ -36,7 +36,7 @@ class RoutesController { public function __construct( SchemaController $schema_controller ) { $this->schema_controller = $schema_controller; $this->routes = [ - 'v1' => [ + 'v1' => [ Routes\V1\Batch::IDENTIFIER => Routes\V1\Batch::class, Routes\V1\Cart::IDENTIFIER => Routes\V1\Cart::class, Routes\V1\CartAddItem::IDENTIFIER => Routes\V1\CartAddItem::class, @@ -66,16 +66,6 @@ class RoutesController { Routes\V1\ProductsById::IDENTIFIER => Routes\V1\ProductsById::class, Routes\V1\ProductsBySlug::IDENTIFIER => Routes\V1\ProductsBySlug::class, ], - // @todo Migrate internal AI routes to WooCommerce Core codebase. - 'private' => [ - Routes\V1\AI\Images::IDENTIFIER => Routes\V1\AI\Images::class, - Routes\V1\AI\Patterns::IDENTIFIER => Routes\V1\AI\Patterns::class, - Routes\V1\AI\Product::IDENTIFIER => Routes\V1\AI\Product::class, - Routes\V1\AI\Products::IDENTIFIER => Routes\V1\AI\Products::class, - Routes\V1\AI\BusinessDescription::IDENTIFIER => Routes\V1\AI\BusinessDescription::class, - Routes\V1\AI\StoreInfo::IDENTIFIER => Routes\V1\AI\StoreInfo::class, - Routes\V1\Patterns::IDENTIFIER => Routes\V1\Patterns::class, - ], ]; } diff --git a/plugins/woocommerce/src/StoreApi/SchemaController.php b/plugins/woocommerce/src/StoreApi/SchemaController.php index e0a1cf843cc..0454c4d416f 100644 --- a/plugins/woocommerce/src/StoreApi/SchemaController.php +++ b/plugins/woocommerce/src/StoreApi/SchemaController.php @@ -54,13 +54,6 @@ class SchemaController { Schemas\V1\ProductCategorySchema::IDENTIFIER => Schemas\V1\ProductCategorySchema::class, Schemas\V1\ProductCollectionDataSchema::IDENTIFIER => Schemas\V1\ProductCollectionDataSchema::class, Schemas\V1\ProductReviewSchema::IDENTIFIER => Schemas\V1\ProductReviewSchema::class, - Schemas\V1\AI\ImagesSchema::IDENTIFIER => Schemas\V1\AI\ImagesSchema::class, - Schemas\V1\AI\PatternsSchema::IDENTIFIER => Schemas\V1\AI\PatternsSchema::class, - Schemas\V1\AI\ProductSchema::IDENTIFIER => Schemas\V1\AI\ProductSchema::class, - Schemas\V1\AI\ProductsSchema::IDENTIFIER => Schemas\V1\AI\ProductsSchema::class, - Schemas\V1\AI\BusinessDescriptionSchema::IDENTIFIER => Schemas\V1\AI\BusinessDescriptionSchema::class, - Schemas\V1\AI\StoreInfoSchema::IDENTIFIER => Schemas\V1\AI\StoreInfoSchema::class, - Schemas\V1\PatternsSchema::IDENTIFIER => Schemas\V1\PatternsSchema::class, ], ]; } diff --git a/plugins/woocommerce/src/StoreApi/Schemas/V1/AI/BusinessDescriptionSchema.php b/plugins/woocommerce/src/StoreApi/Schemas/V1/AI/BusinessDescriptionSchema.php deleted file mode 100644 index b628fe9c33a..00000000000 --- a/plugins/woocommerce/src/StoreApi/Schemas/V1/AI/BusinessDescriptionSchema.php +++ /dev/null @@ -1,47 +0,0 @@ - true, - ]; - } -} diff --git a/plugins/woocommerce/src/StoreApi/Schemas/V1/AI/ImagesSchema.php b/plugins/woocommerce/src/StoreApi/Schemas/V1/AI/ImagesSchema.php deleted file mode 100644 index 317e4c028f6..00000000000 --- a/plugins/woocommerce/src/StoreApi/Schemas/V1/AI/ImagesSchema.php +++ /dev/null @@ -1,45 +0,0 @@ - true, - ]; - } -} diff --git a/plugins/woocommerce/src/StoreApi/Schemas/V1/AI/ProductSchema.php b/plugins/woocommerce/src/StoreApi/Schemas/V1/AI/ProductSchema.php deleted file mode 100644 index d2c4aa4ccd4..00000000000 --- a/plugins/woocommerce/src/StoreApi/Schemas/V1/AI/ProductSchema.php +++ /dev/null @@ -1,47 +0,0 @@ - true, - ]; - } -} diff --git a/plugins/woocommerce/src/StoreApi/Schemas/V1/AI/ProductsSchema.php b/plugins/woocommerce/src/StoreApi/Schemas/V1/AI/ProductsSchema.php deleted file mode 100644 index 114df7807af..00000000000 --- a/plugins/woocommerce/src/StoreApi/Schemas/V1/AI/ProductsSchema.php +++ /dev/null @@ -1,48 +0,0 @@ - $item['ai_content_generated'], - 'product_content' => $item['product_content'], - ]; - } -} diff --git a/plugins/woocommerce/src/StoreApi/Schemas/V1/AI/StoreInfoSchema.php b/plugins/woocommerce/src/StoreApi/Schemas/V1/AI/StoreInfoSchema.php deleted file mode 100644 index 251e702d9a9..00000000000 --- a/plugins/woocommerce/src/StoreApi/Schemas/V1/AI/StoreInfoSchema.php +++ /dev/null @@ -1,34 +0,0 @@ - true, - ]; - } -} diff --git a/plugins/woocommerce/src/StoreApi/Utilities/DraftOrderTrait.php b/plugins/woocommerce/src/StoreApi/Utilities/DraftOrderTrait.php index 4e80f2695f8..dedc60ae418 100644 --- a/plugins/woocommerce/src/StoreApi/Utilities/DraftOrderTrait.php +++ b/plugins/woocommerce/src/StoreApi/Utilities/DraftOrderTrait.php @@ -60,9 +60,8 @@ trait DraftOrderTrait { return true; } - // Failed orders and those needing payment can be retried if the cart hasn't changed. - // Pending orders are excluded from this check since they may be awaiting an update from the payment processor. - if ( $order_object->needs_payment() && ! $order_object->has_status( 'pending' ) && $order_object->has_cart_hash( wc()->cart->get_cart_hash() ) ) { + // Pending and failed orders can be retried if the cart hasn't changed. + if ( $order_object->needs_payment() && $order_object->has_cart_hash( wc()->cart->get_cart_hash() ) ) { return true; } diff --git a/plugins/woocommerce/src/Utilities/PluginUtil.php b/plugins/woocommerce/src/Utilities/PluginUtil.php index e3c229a64fb..d206e0e2e32 100644 --- a/plugins/woocommerce/src/Utilities/PluginUtil.php +++ b/plugins/woocommerce/src/Utilities/PluginUtil.php @@ -78,7 +78,7 @@ class PluginUtil { * Note that the doc block for `wp_get_active_and_valid_plugins` says it returns "Array of paths to plugin files * relative to the plugins directory", but it actually returns absolute paths. * - * @return string[] Array of absolute paths to plugin files. + * @return string[] Array of plugin basenames (paths relative to the plugin directory). */ public function get_all_active_valid_plugins() { $local = wp_get_active_and_valid_plugins(); @@ -91,8 +91,11 @@ class PluginUtil { } $all = array_merge( $local, $network ); + $all = array_unique( $all ); + $all = array_map( 'plugin_basename', $all ); + sort( $all ); - return array_unique( $all ); + return $all; } /** diff --git a/plugins/woocommerce/templates/global/quantity-input.php b/plugins/woocommerce/templates/global/quantity-input.php index 61dbe181f10..2ff50286e4f 100644 --- a/plugins/woocommerce/templates/global/quantity-input.php +++ b/plugins/woocommerce/templates/global/quantity-input.php @@ -12,7 +12,7 @@ * * @see https://woocommerce.com/document/template-structure/ * @package WooCommerce\Templates - * @version 7.8.0 + * @version 9.4.0 * * @var bool $readonly If the input should be set to readonly mode. * @var string $type The input type attribute. @@ -42,7 +42,9 @@ $label = ! empty( $args['product_name'] ) ? sprintf( esc_html__( '%s quantity', name="" value="" aria-label="" - size="4" + + size="4" + min="" max="" diff --git a/plugins/woocommerce/templates/single-product/add-to-cart/variation.php b/plugins/woocommerce/templates/single-product/add-to-cart/variation.php index 82158b0e88b..38b3002d2aa 100644 --- a/plugins/woocommerce/templates/single-product/add-to-cart/variation.php +++ b/plugins/woocommerce/templates/single-product/add-to-cart/variation.php @@ -7,7 +7,7 @@ * * @see https://woocommerce.com/document/template-structure/ * @package WooCommerce\Templates - * @version 2.5.0 + * @version 9.3.0 */ defined( 'ABSPATH' ) || exit; @@ -19,5 +19,5 @@ defined( 'ABSPATH' ) || exit;
        {{{ data.variation.availability_html }}}
        diff --git a/plugins/woocommerce/tests/e2e-pw/bin/test-env-setup.sh b/plugins/woocommerce/tests/e2e-pw/bin/test-env-setup.sh index 95bff22a0d8..4f71a820241 100755 --- a/plugins/woocommerce/tests/e2e-pw/bin/test-env-setup.sh +++ b/plugins/woocommerce/tests/e2e-pw/bin/test-env-setup.sh @@ -1,24 +1,24 @@ #!/usr/bin/env bash echo -e 'Activate default theme \n' -wp theme activate twentytwentythree +wp-env run tests-cli wp theme activate twentytwentythree echo -e 'Update URL structure \n' -wp rewrite structure '/%postname%/' --hard +wp-env run tests-cli wp rewrite structure '/%postname%/' --hard echo -e 'Activate Filter Setter utility plugin \n' -wp plugin activate filter-setter +wp-env run tests-cli wp plugin activate filter-setter # This plugin allows you to process queued scheduled actions immediately. # It's used in the analytics e2e tests so that order numbers are shown in Analytics. echo -e 'Activate Process Waiting Actions utility plugin \n' -wp plugin activate process-waiting-actions +wp-env run tests-cli wp plugin activate process-waiting-actions echo -e 'Activate Test Helper APIs utility plugin \n' -wp plugin activate test-helper-apis +wp-env run tests-cli wp plugin activate test-helper-apis echo -e 'Add Customer user \n' -wp user create customer customer@woocommercecoree2etestsuite.com \ +wp-env run tests-cli wp user create customer customer@woocommercecoree2etestsuite.com \ --user_pass=password \ --role=customer \ --first_name='Jane' \ @@ -26,20 +26,17 @@ wp user create customer customer@woocommercecoree2etestsuite.com \ --user_registered='2022-01-01 12:23:45' echo -e 'Update Blog Name \n' -wp option update blogname 'WooCommerce Core E2E Test Suite' +wp-env run tests-cli wp option update blogname 'WooCommerce Core E2E Test Suite' echo -e 'Preparing Test Files \n' -sudo cp /var/www/html/wp-content/plugins/woocommerce/tests/legacy/unit-tests/importer/sample.csv /var/www/sample.csv +wp-env run tests-cli sudo cp /var/www/html/wp-content/plugins/woocommerce/tests/legacy/unit-tests/importer/sample.csv /var/www/sample.csv ENABLE_TRACKING="${ENABLE_TRACKING:-0}" if [ $ENABLE_TRACKING == 1 ]; then echo -e 'Enable tracking\n' - wp option update woocommerce_allow_tracking 'yes' + wp-env run tests-cli wp option update woocommerce_allow_tracking 'yes' fi -echo -e 'Disabling coming soon option\n' -wp option update woocommerce_coming_soon 'no' - echo -e 'Upload test images \n' -wp media import './test-data/images/image-01.png' './test-data/images/image-02.png' './test-data/images/image-03.png' +wp-env run tests-cli wp media import './test-data/images/image-01.png' './test-data/images/image-02.png' './test-data/images/image-03.png' diff --git a/plugins/woocommerce/tests/e2e-pw/envs/default-pressable/playwright.config.js b/plugins/woocommerce/tests/e2e-pw/envs/default-pressable/playwright.config.js index 29e31e30715..b0747f37c30 100644 --- a/plugins/woocommerce/tests/e2e-pw/envs/default-pressable/playwright.config.js +++ b/plugins/woocommerce/tests/e2e-pw/envs/default-pressable/playwright.config.js @@ -7,7 +7,22 @@ config = { { name: 'default pressable', use: { ...devices[ 'Desktop Chrome' ] }, - testMatch: '**basic.spec.js', + testMatch: [ + '**/basic.spec.js', + '**/merchant/products/add-variable-product/**/*.spec.js', + '**/activate-and-setup/**/*.spec.js', + '**/merchant/products/block-editor/**/*.spec.js', + '**/admin-analytics/**/*.spec.js', + '**/admin-marketing/**/*.spec.js', + '**/admin-tasks/**/*.spec.js', + '**/customize-store/**/*.spec.js', + '**/merchant/command-palette.spec.js', + '**/merchant/create-cart-block.spec.js', + '**/merchant/create-checkout-block.spec.js', + '**/merchant/create-coupon.spec.js', + '**/merchant/create-order.spec.js', + ], + grepInvert: /@skip-on-default-pressable/, }, ], }; diff --git a/plugins/woocommerce/tests/e2e-pw/tests/activate-and-setup/basic-setup.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/activate-and-setup/basic-setup.spec.js index 6720b5bf4b1..38635d1880d 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/activate-and-setup/basic-setup.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/activate-and-setup/basic-setup.spec.js @@ -1,10 +1,26 @@ const { test, expect } = require( '@playwright/test' ); +const wcApi = require( '@woocommerce/woocommerce-rest-api' ).default; test.describe( 'Store owner can finish initial store setup', { tag: [ '@gutenberg', '@payments', '@services' ] }, () => { test.use( { storageState: process.env.ADMINSTATE } ); + + test.beforeAll( async ( { baseURL } ) => { + const api = new wcApi( { + url: baseURL, + consumerKey: process.env.CONSUMER_KEY, + consumerSecret: process.env.CONSUMER_SECRET, + version: 'wc/v3', + } ); + await api.put( 'settings/general/woocommerce_calc_taxes', { + data: { + value: 'no', + }, + } ); + } ); + test( 'can enable tax rates and calculations', async ( { page } ) => { await page.goto( 'wp-admin/admin.php?page=wc-settings' ); // Check the enable taxes checkbox @@ -28,9 +44,7 @@ test.describe( .fill( '/product/' ); await page.locator( '#submit' ).click(); // Verify that settings have been saved - await expect( - page.locator( '#setting-error-settings_updated' ) - ).toContainText( 'Permalink structure updated.' ); + await page.reload(); await expect( page.locator( '#permalink_structure' ) ).toHaveValue( '/%postname%/' ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/activate-and-setup/core-profiler.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/activate-and-setup/core-profiler.spec.js index 9537c6a5e34..f0cf8016e40 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/activate-and-setup/core-profiler.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/activate-and-setup/core-profiler.spec.js @@ -1,468 +1,552 @@ -const { test, expect } = require( '@playwright/test' ); +const { test, expect, request } = require( '@playwright/test' ); +const { setOption } = require( '../../utils/options' ); -test.describe( 'Store owner can complete the core profiler', () => { - test.use( { storageState: process.env.ADMINSTATE } ); +test.describe( + 'Store owner can complete the core profiler', + { tag: '@skip-on-default-pressable' }, + () => { + test.use( { storageState: process.env.ADMINSTATE } ); - test( 'Can complete the core profiler skipping extension install', async ( { - page, - } ) => { - await page.goto( - 'wp-admin/admin.php?page=wc-admin&path=%2Fsetup-wizard' - ); - - await test.step( 'Intro page and opt in to data sharing', async () => { - await expect( - page.getByRole( 'heading', { name: 'Welcome to Woo!' } ) - ).toBeVisible(); - await page - .getByRole( 'checkbox', { name: 'I agree to share my data' } ) - .uncheck(); - await page - .getByRole( 'button', { name: 'Set up my store' } ) - .click(); - } ); - - await test.step( 'User profile information', async () => { - await expect( - page.getByRole( 'heading', { - name: 'Which one of these best describes you?', - } ) - ).toBeVisible(); - await page - .getByRole( 'radio', { name: "I'm just starting my business" } ) - .first() - .click(); - await page.getByRole( 'button', { name: 'Continue' } ).click(); - } ); - - await test.step( 'Business Information', async () => { - await expect( - page.getByRole( 'heading', { - name: 'Tell us a bit about your store', - } ) - ).toBeVisible(); - await expect( - page.getByPlaceholder( 'Ex. My awesome store' ) - ).toHaveValue( 'WooCommerce Core E2E Test Suite' ); - await page - .locator( - 'form.woocommerce-profiler-business-information-form > div > div > div > div > input' - ) - .first() - .click(); - // select clothing and accessories - await page - .getByRole( 'option', { name: 'Clothing and accessories' } ) - .click(); - // select a WooPayments compatible location - await page - .locator( - 'form.woocommerce-profiler-business-information-form > div > div > div > div > input' - ) - .last() - .click(); - await page - .getByRole( 'option', { - name: 'Australia — Northern Territory', - } ) - .click(); - - await page - .getByPlaceholder( 'wordpress@example.com' ) - .fill( 'merchant@example.com' ); - await page.getByLabel( 'Opt-in to receive tips,' ).uncheck(); - await page.getByRole( 'button', { name: 'Continue' } ).click(); - } ); - - await test.step( 'Extensions -- do not install any', async () => { - await expect( - page.getByRole( 'heading', { - name: 'Get a boost with our free features', - } ) - ).toBeVisible(); - // check that WooPayments is displayed because Australia is a supported country - await expect( - page.getByRole( 'heading', { - name: 'Get paid with WooPayments', - } ) - ).toBeVisible(); - // skip this step so that no extensions are installed - await page - .getByRole( 'button', { name: 'Skip this step' } ) - .click(); - } ); - - await test.step( 'Confirm that core profiler was completed and no extensions installed', async () => { - // intermediate page shown - await expect( - page.getByRole( 'heading', { name: 'Turning on the lights' } ) - ).toBeVisible(); - await expect( - page.locator( '.woocommerce-onboarding-progress-bar__filler' ) - ).toBeVisible(); - // dashboard shown - await expect( - page.getByRole( 'heading', { - name: 'Welcome to WooCommerce Core E2E Test Suite', - } ) - ).toBeVisible(); - - // go to the plugins page to make sure that extensions weren't installed - await page.goto( 'wp-admin/plugins.php' ); - await expect( - page.getByRole( 'heading', { name: 'Plugins', exact: true } ) - ).toBeVisible(); - // confirm that some of the optional extensions aren't present - await expect( - page.getByText( 'MailPoet for WooCommerce', { exact: true } ) - ).toBeHidden(); - await expect( - page.getByText( 'Pinterest for WooCommerce', { exact: true } ) - ).toBeHidden(); - await expect( - page.getByText( 'Google for WooCommerce', { exact: true } ) - ).toBeHidden(); - } ); - - await test.step( 'Confirm that information from core profiler saved', async () => { - await page.goto( 'wp-admin/admin.php?page=wc-settings' ); - await expect( - page.getByRole( 'textbox', { - name: 'Australia — Northern Territory', - } ) - ).toBeVisible(); - await expect( - page.getByRole( 'textbox', { name: 'Australian dollar ($)' } ) - ).toBeVisible(); - await expect( - page.getByRole( 'textbox', { name: 'Left' } ) - ).toBeVisible(); - await expect( - page.getByLabel( 'Thousand separator', { exact: true } ) - ).toHaveValue( ',' ); - await expect( - page.getByLabel( 'Decimal separator', { exact: true } ) - ).toHaveValue( '.' ); - await expect( page.getByLabel( 'Number of decimals' ) ).toHaveValue( - '2' - ); - } ); - } ); - - test( 'Can complete the core profiler installing default extensions', async ( { - page, - } ) => { - await page.goto( - 'wp-admin/admin.php?page=wc-admin&path=%2Fsetup-wizard' - ); - - await test.step( 'Intro page and opt in to data sharing', async () => { - await expect( - page.getByRole( 'heading', { name: 'Welcome to Woo!' } ) - ).toBeVisible(); - await page - .getByRole( 'checkbox', { name: 'I agree to share my data' } ) - .uncheck(); - await page - .getByRole( 'button', { name: 'Set up my store' } ) - .click(); - } ); - - await test.step( 'User profile information', async () => { - await expect( - page.getByRole( 'heading', { - name: 'Which one of these best describes you?', - } ) - ).toBeVisible(); - await page - .getByRole( 'radio', { name: "I'm already selling" } ) - .first() - .click(); - await page.getByLabel( 'Select an option' ).click(); - await page - .getByRole( 'option', { name: "No, I'm selling offline" } ) - .click(); - await page.getByRole( 'button', { name: 'Continue' } ).click(); - } ); - - await test.step( 'Business Information', async () => { - await expect( - page.getByRole( 'heading', { - name: 'Tell us a bit about your store', - } ) - ).toBeVisible(); - await expect( - page.getByPlaceholder( 'Ex. My awesome store' ) - ).toHaveValue( 'WooCommerce Core E2E Test Suite' ); - await page - .locator( - 'form.woocommerce-profiler-business-information-form > div > div > div > div > input' - ) - .first() - .click(); - // select food and drink - await page - .getByRole( 'option', { name: 'Food and drink' } ) - .click(); - // select a WooPayments incompatible location - await page - .locator( - 'form.woocommerce-profiler-business-information-form > div > div > div > div > input' - ) - .last() - .click(); - await page.getByRole( 'option', { name: 'Afghanistan' } ).click(); - - await page - .getByPlaceholder( 'wordpress@example.com' ) - .fill( 'merchant@example.com' ); - await page.getByLabel( 'Opt-in to receive tips,' ).uncheck(); - await page.getByRole( 'button', { name: 'Continue' } ).click(); - } ); - - await test.step( 'Extensions -- install some suggested extensions', async () => { - await expect( - page.getByRole( 'heading', { - name: 'Get a boost with our free features', - } ) - ).toBeVisible(); - // check that WooPayments is not displayed because Afghanistan is not a supported country - await expect( - page.getByRole( 'heading', { - name: 'Get paid with WooPayments', - } ) - ).not.toBeAttached(); - - // select and install the rest of the extentions + test.beforeAll( async ( { baseURL } ) => { try { - await page - .getByText( - 'Boost content creation with Jetpack AI AssistantSave time on content creation' - ) - .getByRole( 'checkbox' ) - .uncheck( { timeout: 2000 } ); - } catch ( e ) { - console.log( 'Checkbox not present for Jetpack AI Assistant' ); + await setOption( + request, + baseURL, + 'woocommerce_coming_soon', + 'no' + ); + } catch ( error ) { + console.log( error ); } - try { + } ); + + test( 'Can complete the core profiler skipping extension install', async ( { + page, + } ) => { + await page.goto( + 'wp-admin/admin.php?page=wc-admin&path=%2Fsetup-wizard' + ); + + await test.step( 'Intro page and opt in to data sharing', async () => { + await expect( + page.getByRole( 'heading', { name: 'Welcome to Woo!' } ) + ).toBeVisible(); + await page + .getByRole( 'checkbox', { + name: 'I agree to share my data', + } ) + .uncheck(); + await page + .getByRole( 'button', { name: 'Set up my store' } ) + .click(); + } ); + + await test.step( 'User profile information', async () => { + await expect( + page.getByRole( 'heading', { + name: 'Which one of these best describes you?', + } ) + ).toBeVisible(); + await page + .getByRole( 'radio' ) + .filter( { hasText: 'just starting my business' } ) + .click(); + await page.getByRole( 'button', { name: 'Continue' } ).click(); + } ); + + await test.step( 'Business Information', async () => { + await expect( + page.getByRole( 'heading', { + name: 'Tell us a bit about your store', + } ) + ).toBeVisible(); + await expect( + page.getByPlaceholder( 'Ex. My awesome store' ) + ).toHaveValue( 'WooCommerce Core E2E Test Suite' ); + await page + .locator( + 'form.woocommerce-profiler-business-information-form > div > div > div > div > input' + ) + .first() + .click(); + // select clothing and accessories + await page + .getByRole( 'option', { name: 'Clothing and accessories' } ) + .click(); + // select a WooPayments compatible location + await page + .locator( + 'form.woocommerce-profiler-business-information-form > div > div > div > div > input' + ) + .last() + .click(); + await page + .getByRole( 'option', { + name: 'Australia — Northern Territory', + } ) + .click(); + + await page + .getByPlaceholder( 'wordpress@example.com' ) + .fill( 'merchant@example.com' ); + await page.getByLabel( 'Opt-in to receive tips,' ).uncheck(); + await page.getByRole( 'button', { name: 'Continue' } ).click(); + } ); + + await test.step( 'Extensions -- do not install any', async () => { + await expect( + page.getByRole( 'heading', { + name: 'Get a boost with our free features', + } ) + ).toBeVisible(); + // check that WooPayments is displayed because Australia is a supported country + await expect( + page.getByRole( 'heading', { + name: 'Get paid with WooPayments', + } ) + ).toBeVisible(); + // skip this step so that no extensions are installed + await page + .getByRole( 'button', { name: 'Skip this step' } ) + .click(); + } ); + + await test.step( 'Confirm that core profiler was completed and no extensions installed', async () => { + // intermediate page shown + await expect( + page.getByRole( 'heading', { + name: 'Turning on the lights', + } ) + ).toBeVisible(); + await expect( + page.locator( + '.woocommerce-onboarding-progress-bar__filler' + ) + ).toBeVisible(); + // dashboard shown + await expect( + page.getByRole( 'heading', { + name: 'Welcome to WooCommerce Core E2E Test Suite', + } ) + ).toBeVisible(); + + // go to the plugins page to make sure that extensions weren't installed + await page.goto( 'wp-admin/plugins.php?plugin_status=active' ); + await expect( + page.getByRole( 'heading', { + name: 'Plugins', + exact: true, + } ) + ).toBeVisible(); + // confirm that some of the optional extensions aren't present + await expect( + page.getByText( 'MailPoet for WooCommerce', { + exact: true, + } ) + ).toBeHidden(); + await expect( + page.getByText( 'Pinterest for WooCommerce', { + exact: true, + } ) + ).toBeHidden(); + await expect( + page.getByText( 'Google for WooCommerce', { exact: true } ) + ).toBeHidden(); + } ); + + await test.step( 'Confirm that information from core profiler saved', async () => { + await page.goto( 'wp-admin/admin.php?page=wc-settings' ); + await expect( + page.getByRole( 'textbox', { + name: 'Australia — Northern Territory', + } ) + ).toBeVisible(); + await expect( + page.getByRole( 'textbox', { + name: 'Australian dollar ($)', + } ) + ).toBeVisible(); + await expect( + page.getByRole( 'textbox', { name: 'Left' } ) + ).toBeVisible(); + await expect( + page.getByLabel( 'Thousand separator', { exact: true } ) + ).toHaveValue( ',' ); + await expect( + page.getByLabel( 'Decimal separator', { exact: true } ) + ).toHaveValue( '.' ); + await expect( + page.getByLabel( 'Number of decimals' ) + ).toHaveValue( '2' ); + } ); + } ); + + test( 'Can complete the core profiler installing default extensions', async ( { + page, + } ) => { + await page.goto( + 'wp-admin/admin.php?page=wc-admin&path=%2Fsetup-wizard' + ); + + await test.step( 'Intro page and opt in to data sharing', async () => { + await expect( + page.getByRole( 'heading', { name: 'Welcome to Woo!' } ) + ).toBeVisible(); + await page + .getByRole( 'checkbox', { + name: 'I agree to share my data', + } ) + .uncheck(); + await page + .getByRole( 'button', { name: 'Set up my store' } ) + .click(); + } ); + + await test.step( 'User profile information', async () => { + await expect( + page.getByRole( 'heading', { + name: 'Which one of these best describes you?', + } ) + ).toBeVisible(); + await page + .getByRole( 'radio' ) + .filter( { hasText: 'already selling' } ) + .click(); + await page.getByLabel( 'Select an option' ).click(); + await page + .getByRole( 'option' ) + .filter( { hasText: 'selling offline' } ) + .click(); + await page.getByRole( 'button', { name: 'Continue' } ).click(); + } ); + + await test.step( 'Business Information', async () => { + await expect( + page.getByRole( 'heading', { + name: 'Tell us a bit about your store', + } ) + ).toBeVisible(); + await expect( + page.getByPlaceholder( 'Ex. My awesome store' ) + ).toHaveValue( 'WooCommerce Core E2E Test Suite' ); + await page + .locator( + 'form.woocommerce-profiler-business-information-form > div > div > div > div > input' + ) + .first() + .click(); + // select food and drink + await page + .getByRole( 'option', { name: 'Food and drink' } ) + .click(); + // select a WooPayments incompatible location + await page + .locator( + 'form.woocommerce-profiler-business-information-form > div > div > div > div > input' + ) + .last() + .click(); + await page + .getByRole( 'option', { name: 'Afghanistan' } ) + .click(); + + await page + .getByPlaceholder( 'wordpress@example.com' ) + .fill( 'merchant@example.com' ); + await page.getByLabel( 'Opt-in to receive tips,' ).uncheck(); + await page.getByRole( 'button', { name: 'Continue' } ).click(); + } ); + + await test.step( 'Extensions -- install some suggested extensions', async () => { + await expect( + page.getByRole( 'heading', { + name: 'Get a boost with our free features', + } ) + ).toBeVisible(); + // check that WooPayments is not displayed because Afghanistan is not a supported country + await expect( + page.getByRole( 'heading', { + name: 'Get paid with WooPayments', + } ) + ).not.toBeAttached(); + + // select and install the rest of the extentions + try { + await page + .getByText( + 'Boost content creation with Jetpack AI AssistantSave time on content creation' + ) + .getByRole( 'checkbox' ) + .uncheck( { timeout: 2000 } ); + } catch ( e ) { + console.log( + 'Checkbox not present for Jetpack AI Assistant' + ); + } + try { + await page + .getByText( + 'Showcase your products with PinterestGet your products in front of a highly' + ) + .getByRole( 'checkbox' ) + .check( { timeout: 2000 } ); + } catch ( e ) { + console.log( 'Checkbox not present for Pinterest' ); + } + try { + await page + .getByText( + 'Reach your customers with MailPoetSend purchase follow-up emails, newsletters,' + ) + .getByRole( 'checkbox' ) + .uncheck( { timeout: 2000 } ); + } catch ( e ) { + console.log( 'Checkbox not present for MailPoet' ); + } + await page .getByText( - 'Showcase your products with PinterestGet your products in front of a highly' + 'Drive sales with Google for WooCommerceReach millions of active shoppers across' ) .getByRole( 'checkbox' ) .check( { timeout: 2000 } ); - } catch ( e ) { - console.log( 'Checkbox not present for Pinterest' ); - } - try { - await page - .getByText( - 'Reach your customers with MailPoetSend purchase follow-up emails, newsletters,' - ) - .getByRole( 'checkbox' ) - .uncheck( { timeout: 2000 } ); - } catch ( e ) { - console.log( 'Checkbox not present for MailPoet' ); - } + await page.getByRole( 'button', { name: 'Continue' } ).click(); + } ); - await page - .getByText( - 'Drive sales with Google for WooCommerceReach millions of active shoppers across' - ) - .getByRole( 'checkbox' ) - .check( { timeout: 2000 } ); - await page.getByRole( 'button', { name: 'Continue' } ).click(); + await test.step( 'Confirm that core profiler was completed and a couple of default extensions installed', async () => { + page.on( 'dialog', ( dialog ) => dialog.accept() ); + // intermediate page shown + // the next two are soft assertions because depending on the extensions selected, they may or may not appear + // and we want the test to complete in order for cleanup to happen + await expect + .soft( + page + .getByRole( 'heading' ) + .filter( { hasText: 'get your features ready' } ) + ) + .toBeVisible( { timeout: 30000 } ); + await expect + .soft( + page + .getByRole( 'heading' ) + .filter( { hasText: 'Extending your store' } ) + ) + .toBeVisible( { timeout: 30000 } ); + // dashboard shown + await expect( + page.getByRole( 'heading', { + name: 'Welcome to WooCommerce Core E2E Test Suite', + } ) + ).toBeVisible( { timeout: 30000 } ); + // go to the plugins page to make sure that extensions were installed + await page.goto( 'wp-admin/plugins.php?plugin_status=active' ); + await expect( + page.getByRole( 'heading', { + name: 'Plugins', + exact: true, + } ) + ).toBeVisible(); + // confirm that the optional plugins are present + await expect( + page.locator( '.plugin-title', { + hasText: 'Pinterest for WooCommerce', + } ) + ).toBeVisible(); + await expect( + page.locator( '.plugin-title', { + hasText: /Google for WooCommerce|Google Listings & Ads/, + } ) + ).toBeVisible(); + + await expect( + page.locator( '.plugin-title', { + hasText: 'MailPoet', + } ) + ).toBeHidden(); + await expect( + page.locator( '.plugin-title', { + hasText: 'Jetpack', + } ) + ).toBeHidden(); + } ); + + await test.step( 'Confirm that information from core profiler saved', async () => { + await page.goto( 'wp-admin/admin.php?page=wc-settings' ); + await expect( + page.getByRole( 'textbox', { name: 'Afghanistan' } ) + ).toBeVisible(); + await expect( + page.getByRole( 'textbox', { name: 'Afghan afghani (؋)' } ) + ).toBeVisible(); + await expect( + page.getByRole( 'textbox', { name: 'Left with space' } ) + ).toBeVisible(); + await expect( + page.getByLabel( 'Thousand separator', { exact: true } ) + ).toHaveValue( '.' ); + await expect( + page.getByLabel( 'Decimal separator', { exact: true } ) + ).toHaveValue( ',' ); + await expect( + page.getByLabel( 'Number of decimals' ) + ).toHaveValue( '0' ); + } ); + + await test.step( 'Clean up installed extensions', async () => { + await page.goto( 'wp-admin/plugins.php' ); + await page.getByLabel( 'Deactivate Google' ).click(); + await expect( + page.getByText( 'Plugin deactivated.' ) + ).toBeVisible(); + // delete plugin regularly or, if attempted, accept deleting data as well + try { + await page.getByLabel( 'Delete Google' ).click(); + await expect( + page.getByText( 'was successfully deleted.' ) + ).toBeVisible( { timeout: 5000 } ); + } catch ( e ) { + await page + .getByText( 'Yes, delete these files and data' ) + .click(); + await page + .getByText( 'The selected plugin has been deleted.' ) + .waitFor(); + } + await expect( page.getByLabel( 'Delete Google' ) ).toBeHidden(); + await page.getByLabel( 'Deactivate Pinterest for' ).click(); + await expect( + page.getByText( 'Plugin deactivated.' ) + ).toBeVisible(); + // delete plugin regularly or, if attempted, accept deleting data as well + try { + await page.getByLabel( 'Delete Pinterest for' ).click(); + await expect( + page.getByText( 'was successfully deleted.' ) + ).toBeVisible( { timeout: 5000 } ); + } catch ( e ) { + await page + .getByText( 'Yes, delete these files and data' ) + .click(); + await page + .getByText( 'The selected plugin has been deleted.' ) + .waitFor(); + } + await expect( + page.getByLabel( 'Delete Pinterest for' ) + ).toBeHidden(); + } ); + + await test.step( 'Confirm that the store is in coming soon mode after completing the core profiler', async () => { + await page.goto( 'wp-admin/admin.php?page=wc-admin' ); + await expect( + page + .getByRole( 'menuitem' ) + .filter( { hasText: 'coming soon' } ) + ).toBeVisible(); + } ); + } ); + } +); + +test.describe( + 'Store owner can skip the core profiler', + { tag: '@skip-on-default-pressable' }, + () => { + test.use( { storageState: process.env.ADMINSTATE } ); + + test.beforeAll( async ( { baseURL } ) => { + try { + await setOption( + request, + baseURL, + 'woocommerce_coming_soon', + 'no' + ); + } catch ( error ) { + console.log( error ); + } } ); - await test.step( 'Confirm that core profiler was completed and a couple of default extensions installed', async () => { - page.on( 'dialog', ( dialog ) => dialog.accept() ); - // intermediate page shown - // the next two are soft assertions because depending on the extensions selected, they may or may not appear - // and we want the test to complete in order for cleanup to happen - await expect - .soft( - page.getByRole( 'heading', { - name: "Woo! Let's get your features ready", - } ) - ) - .toBeVisible( { timeout: 30000 } ); - await expect - .soft( - page.getByRole( 'heading', { - name: "Extending your store's capabilities", - } ) - ) - .toBeVisible( { timeout: 30000 } ); - // dashboard shown + test( 'Can click skip guided setup', async ( { page } ) => { + await page.goto( + 'wp-admin/admin.php?page=wc-admin&path=%2Fsetup-wizard' + ); + + await page + .getByRole( 'button', { name: 'Skip guided setup' } ) + .click(); + + await expect( + page.getByRole( 'heading', { + name: 'Where is your business located?', + } ) + ).toBeVisible(); + await page.getByLabel( 'Select country/region' ).click(); + await page + .getByRole( 'option', { + name: 'United States (US) — California', + } ) + .click(); + await page + .getByRole( 'button', { name: 'Go to my store' } ) + .click(); + + await expect( + page.getByRole( 'heading', { name: 'Turning on the lights' } ) + ).toBeVisible(); + await expect( page.getByRole( 'heading', { name: 'Welcome to WooCommerce Core E2E Test Suite', } ) - ).toBeVisible( { timeout: 30000 } ); - // go to the plugins page to make sure that extensions were installed - await page.goto( 'wp-admin/plugins.php' ); - await expect( - page.getByRole( 'heading', { name: 'Plugins', exact: true } ) - ).toBeVisible(); - // confirm that the optional plugins are present - await expect( - page.locator( '.plugin-title', { - hasText: 'Pinterest for WooCommerce', - } ) - ).toBeVisible(); - await expect( - page.locator( '.plugin-title', { - hasText: /Google for WooCommerce|Google Listings & Ads/, - } ) ).toBeVisible(); - await expect( - page.locator( '.plugin-title', { - hasText: 'MailPoet', - } ) - ).toBeHidden(); - await expect( - page.locator( '.plugin-title', { - hasText: 'Jetpack', - } ) - ).toBeHidden(); - } ); - - await test.step( 'Confirm that information from core profiler saved', async () => { - await page.goto( 'wp-admin/admin.php?page=wc-settings' ); - await expect( - page.getByRole( 'textbox', { name: 'Afghanistan' } ) - ).toBeVisible(); - await expect( - page.getByRole( 'textbox', { name: 'Afghan afghani (؋)' } ) - ).toBeVisible(); - await expect( - page.getByRole( 'textbox', { name: 'Left with space' } ) - ).toBeVisible(); - await expect( - page.getByLabel( 'Thousand separator', { exact: true } ) - ).toHaveValue( '.' ); - await expect( - page.getByLabel( 'Decimal separator', { exact: true } ) - ).toHaveValue( ',' ); - await expect( page.getByLabel( 'Number of decimals' ) ).toHaveValue( - '0' - ); - } ); - - await test.step( 'Clean up installed extensions', async () => { - await page.goto( 'wp-admin/plugins.php' ); - await page.getByLabel( 'Deactivate Google' ).click(); - await expect( - page.getByText( 'Plugin deactivated.' ) - ).toBeVisible(); - // delete plugin regularly or, if attempted, accept deleting data as well - try { - await page.getByLabel( 'Delete Google' ).click(); + await test.step( 'Confirm that the store is in coming soon mode after skipping the core profiler', async () => { + await page.goto( 'wp-admin/admin.php?page=wc-admin' ); await expect( - page.getByText( 'was successfully deleted.' ) - ).toBeVisible( { timeout: 5000 } ); - } catch ( e ) { + page + .getByRole( 'menuitem' ) + .filter( { hasText: 'coming soon' } ) + ).toBeVisible(); + } ); + } ); + + test( 'Can connect to WooCommerce.com', async ( { page } ) => { + await test.step( 'Go to WC Home and make sure the total sales is visible', async () => { + await page.goto( 'wp-admin/admin.php?page=wc-admin' ); await page - .getByText( 'Yes, delete these files and data' ) - .click(); - await page - .getByText( 'The selected plugin has been deleted.' ) - .waitFor(); - } - await expect( page.getByLabel( 'Delete Google' ) ).toBeHidden(); - await page.getByLabel( 'Deactivate Pinterest for' ).click(); - await expect( - page.getByText( 'Plugin deactivated.' ) - ).toBeVisible(); - // delete plugin regularly or, if attempted, accept deleting data as well - try { - await page.getByLabel( 'Delete Pinterest for' ).click(); + .getByRole( 'menuitem', { name: 'Total sales' } ) + .waitFor( { state: 'visible' } ); + } ); + + await test.step( 'Go to the extensions tab and connect store', async () => { + await page.goto( + 'wp-admin/admin.php?page=wc-admin&tab=my-subscriptions&path=%2Fextensions' + ); await expect( - page.getByText( 'was successfully deleted.' ) - ).toBeVisible( { timeout: 5000 } ); - } catch ( e ) { + page.getByText( + 'Hundreds of vetted products and services. Unlimited potential.' + ) + ).toBeVisible(); + await expect( + page.getByRole( 'button', { name: 'My Subscriptions' } ) + ).toBeVisible(); + await expect( + page.getByRole( 'link', { name: 'Connect your store' } ) + ).toBeVisible(); await page - .getByText( 'Yes, delete these files and data' ) + .getByRole( 'link', { name: 'Connect your store' } ) .click(); - await page - .getByText( 'The selected plugin has been deleted.' ) - .waitFor(); - } - await expect( - page.getByLabel( 'Delete Pinterest for' ) - ).toBeHidden(); + } ); + + await test.step( 'Check that we are sent to wp.com', async () => { + await expect( page.url() ).toContain( 'wordpress.com/log-in' ); + await expect( + page.getByRole( 'heading', { + name: 'Log in to your account', + } ) + ).toBeVisible( { timeout: 30000 } ); + } ); } ); - } ); -} ); - -test.describe( 'Store owner can skip the core profiler', () => { - test.use( { storageState: process.env.ADMINSTATE } ); - - test( 'Can click skip guided setup', async ( { page } ) => { - await page.goto( - 'wp-admin/admin.php?page=wc-admin&path=%2Fsetup-wizard' - ); - - await page.getByRole( 'button', { name: 'Skip guided setup' } ).click(); - - await expect( - page.getByRole( 'heading', { - name: 'Where is your business located?', - } ) - ).toBeVisible(); - await page.getByLabel( 'Select country/region' ).click(); - await page - .getByRole( 'option', { name: 'United States (US) — California' } ) - .click(); - await page.getByRole( 'button', { name: 'Go to my store' } ).click(); - - await expect( - page.getByRole( 'heading', { name: 'Turning on the lights' } ) - ).toBeVisible(); - - await expect( - page.getByRole( 'heading', { - name: 'Welcome to WooCommerce Core E2E Test Suite', - } ) - ).toBeVisible(); - } ); - - test( 'Can connect to WooCommerce.com', async ( { page } ) => { - await test.step( 'Go to WC Home and make sure the total sales is visible', async () => { - await page.goto( 'wp-admin/admin.php?page=wc-admin' ); - await page - .getByRole( 'menuitem', { name: 'Total sales' } ) - .waitFor( { state: 'visible' } ); - } ); - - await test.step( 'Go to the extensions tab and connect store', async () => { - await page.goto( - 'wp-admin/admin.php?page=wc-admin&tab=my-subscriptions&path=%2Fextensions' - ); - await expect( - page.getByText( - 'Hundreds of vetted products and services. Unlimited potential.' - ) - ).toBeVisible(); - await expect( - page.getByRole( 'button', { name: 'My Subscriptions' } ) - ).toBeVisible(); - await expect( - page.getByRole( 'link', { name: 'Connect your store' } ) - ).toBeVisible(); - await page - .getByRole( 'link', { name: 'Connect your store' } ) - .click(); - } ); - - await test.step( 'Check that we are sent to wp.com', async () => { - await expect( page.url() ).toContain( 'wordpress.com/log-in' ); - await expect( - page.getByRole( 'heading', { - name: 'Log in to your account', - } ) - ).toBeVisible( { timeout: 30000 } ); - } ); - } ); -} ); + } +); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/admin-analytics/analytics-data.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/admin-analytics/analytics-data.spec.js index fe893b0808c..6cfd1d35fd2 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/admin-analytics/analytics-data.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/admin-analytics/analytics-data.spec.js @@ -25,7 +25,7 @@ const test = baseTest.extend( { test.describe( 'Analytics-related tests', - { tag: [ '@payments', '@services' ] }, + { tag: [ '@payments', '@services', '@skip-on-default-pressable' ] }, () => { let categoryIds, productIds, orderIds, setupPage; diff --git a/plugins/woocommerce/tests/e2e-pw/tests/admin-marketing/overview.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/admin-marketing/overview.spec.js index cc69f347113..2ababaf97ec 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/admin-marketing/overview.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/admin-marketing/overview.spec.js @@ -27,11 +27,6 @@ test.describe( 'Marketing page', () => { ).toBeVisible(); // Sections present - await expect( - page.getByText( - 'Reach new customers and increase sales without leaving WooCommerce' - ) - ).toBeVisible(); await expect( page.getByText( 'Channels', { exact: true } ) ).toBeVisible(); @@ -55,38 +50,44 @@ test.describe( 'Marketing page', () => { ).toBeVisible(); } ); - test( 'Introduction can be dismissed', async ( { page } ) => { - // Go to the Marketing page. - await page.goto( 'wp-admin/admin.php?page=wc-admin&path=%2Fmarketing' ); + test( + 'Introduction can be dismissed', + { tag: '@skip-on-default-pressable' }, + async ( { page } ) => { + // Go to the Marketing page. + await page.goto( + 'wp-admin/admin.php?page=wc-admin&path=%2Fmarketing' + ); - // Dismiss the introduction (if it's visible) - try { - await page - .locator( - '.woocommerce-marketing-introduction-banner-illustration > .components-button' + // Dismiss the introduction (if it's visible) + try { + await page + .locator( + '.woocommerce-marketing-introduction-banner-illustration > .components-button' + ) + .click( { timeout: 2000 } ); + } catch ( e ) { + console.log( 'Info: introduction already hidden' ); + } + + // The introduction should be hidden. + await expect( + page.getByText( + 'Reach new customers and increase sales without leaving WooCommerce' ) - .click( { timeout: 2000 } ); - } catch ( e ) { - console.log( 'Info: introduction already hidden' ); + ).toBeHidden(); + + // Refresh the page to make sure the state is saved. + await page.reload(); + + // The introduction should still be hidden. + await expect( + page.getByText( + 'Reach new customers and increase sales without leaving WooCommerce' + ) + ).toBeHidden(); } - - // The introduction should be hidden. - await expect( - page.getByText( - 'Reach new customers and increase sales without leaving WooCommerce' - ) - ).toBeHidden(); - - // Refresh the page to make sure the state is saved. - await page.reload(); - - // The introduction should still be hidden. - await expect( - page.getByText( - 'Reach new customers and increase sales without leaving WooCommerce' - ) - ).toBeHidden(); - } ); + ); test( 'Learning section can be expanded', async ( { page } ) => { // Go to the Dashboard page (this adds time for posts to be created) diff --git a/plugins/woocommerce/tests/e2e-pw/tests/basic.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/basic.spec.js index 4c164d7fec1..833eaa92dae 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/basic.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/basic.spec.js @@ -1,6 +1,15 @@ -const { test, expect } = require( '@playwright/test' ); +const { test, expect, request } = require( '@playwright/test' ); const { logIn } = require( '../utils/login' ); const { admin, customer } = require( '../test-data/data' ); +const { setOption } = require( '../utils/options' ); + +test.beforeAll( async ( { baseURL } ) => { + try { + await setOption( request, baseURL, 'woocommerce_coming_soon', 'no' ); + } catch ( error ) { + console.log( error ); + } +} ); test( 'Load the home page', async ( { page } ) => { await page.goto( '/' ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/customize-store/assembler/color-picker.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/customize-store/assembler/color-picker.spec.js index 02d002926e2..f28ae181628 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/customize-store/assembler/color-picker.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/customize-store/assembler/color-picker.spec.js @@ -412,105 +412,105 @@ test.describe( 'Assembler -> Color Pickers', { tag: '@gutenberg' }, () => { await expect( colorPicker ).toHaveClass( /is-active/ ); } ); - test( 'Selected color palette should be applied on the frontend', async ( { - assemblerPageObject, - page, - baseURL, - } ) => { - const assembler = await assemblerPageObject.getAssembler(); - const colorPicker = assembler - .locator( - '.woocommerce-customize-store_global-styles-variations_item' - ) - .last(); + test( + 'Selected color palette should be applied on the frontend', + { tag: '@skip-on-default-pressable' }, + async ( { assemblerPageObject, page, baseURL } ) => { + const assembler = await assemblerPageObject.getAssembler(); + const colorPicker = assembler + .locator( + '.woocommerce-customize-store_global-styles-variations_item' + ) + .last(); - await colorPicker.click(); + await colorPicker.click(); - await assembler.locator( '[aria-label="Back"]' ).click(); + await assembler.locator( '[aria-label="Back"]' ).click(); - const saveButton = assembler.getByText( 'Finish customizing' ); + const saveButton = assembler.getByText( 'Finish customizing' ); - const waitResponseGlobalStyles = page.waitForResponse( - ( response ) => - response.url().includes( 'wp-json/wp/v2/global-styles' ) && - response.status() === 200 - ); - - const wordPressVersion = await getInstalledWordPressVersion(); - - await saveButton.click(); - - await Promise.all( [ - waitResponseGlobalStyles, - wordPressVersion < 6.6 - ? page.waitForResponse( - ( response ) => - response.url().includes( - // When CYS will support all block themes, this URL will change. - 'wp-json/wp/v2/templates/twentytwentyfour//home' - ) && response.status() === 200 - ) - : Promise.resolve(), - ] ); - - await page.goto( baseURL ); - - const paragraphs = await page - .locator( - 'p.wp-block.wp-block-paragraph:not([aria-label="Empty block; start writing or type forward slash to choose a block"])' - ) - .evaluateAll( ( elements ) => - elements.map( ( element ) => { - const style = window.getComputedStyle( element ); - return { - background: style.backgroundColor, - color: style.color, - }; - } ) + const waitResponseGlobalStyles = page.waitForResponse( + ( response ) => + response.url().includes( 'wp-json/wp/v2/global-styles' ) && + response.status() === 200 ); - const buttons = await page - .locator( '.wp-block-button > .wp-block-button__link' ) - .evaluateAll( ( elements ) => - elements.map( ( element ) => { - const style = window.getComputedStyle( element ); - return { - background: style.backgroundColor, - color: style.color, - }; - } ) - ); + const wordPressVersion = await getInstalledWordPressVersion(); - const headers = await page - .locator( 'h1, h2, h3, h4, h5, h6' ) - .evaluateAll( ( elements ) => - elements.map( ( element ) => { - const style = window.getComputedStyle( element ); - return { - background: style.backgroundColor, - color: style.color, - }; - } ) - ); + await saveButton.click(); - for ( const element of buttons ) { - await expect( element.background ).toEqual( - colorPalette.Slate.button.background - ); + await Promise.all( [ + waitResponseGlobalStyles, + wordPressVersion < 6.6 + ? page.waitForResponse( + ( response ) => + response.url().includes( + // When CYS will support all block themes, this URL will change. + 'wp-json/wp/v2/templates/twentytwentyfour//home' + ) && response.status() === 200 + ) + : Promise.resolve(), + ] ); + + await page.goto( baseURL ); + + const paragraphs = await page + .locator( + 'p.wp-block.wp-block-paragraph:not([aria-label="Empty block; start writing or type forward slash to choose a block"])' + ) + .evaluateAll( ( elements ) => + elements.map( ( element ) => { + const style = window.getComputedStyle( element ); + return { + background: style.backgroundColor, + color: style.color, + }; + } ) + ); + + const buttons = await page + .locator( '.wp-block-button > .wp-block-button__link' ) + .evaluateAll( ( elements ) => + elements.map( ( element ) => { + const style = window.getComputedStyle( element ); + return { + background: style.backgroundColor, + color: style.color, + }; + } ) + ); + + const headers = await page + .locator( 'h1, h2, h3, h4, h5, h6' ) + .evaluateAll( ( elements ) => + elements.map( ( element ) => { + const style = window.getComputedStyle( element ); + return { + background: style.backgroundColor, + color: style.color, + }; + } ) + ); + + for ( const element of buttons ) { + await expect( element.background ).toEqual( + colorPalette.Slate.button.background + ); + } + + for ( const element of paragraphs ) { + expect( + colorPalette.Slate.paragraph.color.includes( element.color ) + ).toBe( true ); + } + + for ( const element of headers ) { + expect( + colorPalette.Slate.header.color.includes( element.color ) + ).toBe( true ); + } } - - for ( const element of paragraphs ) { - expect( - colorPalette.Slate.paragraph.color.includes( element.color ) - ).toBe( true ); - } - - for ( const element of headers ) { - expect( - colorPalette.Slate.header.color.includes( element.color ) - ).toBe( true ); - } - } ); + ); test( 'Create "your own" pickers should be visible', async ( { assemblerPageObject, diff --git a/plugins/woocommerce/tests/e2e-pw/tests/customize-store/assembler/full-composability.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/customize-store/assembler/full-composability.spec.js index 01e56c90c70..4d8f95f4869 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/customize-store/assembler/full-composability.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/customize-store/assembler/full-composability.spec.js @@ -343,32 +343,35 @@ test.describe( 'Assembler -> Full composability', { tag: '@gutenberg' }, () => { await expect( defaultPattern ).toBeVisible(); } ); - test( 'Clicking opt-in new patterns should be available', async ( { - pageObject, - baseURL, - } ) => { - await prepareAssembler( pageObject, baseURL ); - const assembler = await pageObject.getAssembler(); + test( + 'Clicking opt-in new patterns should be available', + { tag: '@skip-on-default-pressable' }, + async ( { pageObject, baseURL } ) => { + await prepareAssembler( pageObject, baseURL ); + const assembler = await pageObject.getAssembler(); - await assembler.getByText( 'Usage tracking' ).click(); - await expect( - assembler.getByText( 'Access more patterns' ) - ).toBeVisible(); + await assembler.getByText( 'Usage tracking' ).click(); + await expect( + assembler.getByText( 'Access more patterns' ) + ).toBeVisible(); - await assembler.getByRole( 'button', { name: 'Opt in' } ).click(); + await assembler.getByRole( 'button', { name: 'Opt in' } ).click(); - await assembler - .getByText( 'Access more patterns' ) - .waitFor( { state: 'hidden' } ); + await assembler + .getByText( 'Access more patterns' ) + .waitFor( { state: 'hidden' } ); - const sidebarPattern = assembler.locator( - '.block-editor-block-patterns-list' - ); + const sidebarPattern = assembler.locator( + '.block-editor-block-patterns-list' + ); - await sidebarPattern.waitFor( { state: 'visible' } ); + await sidebarPattern.waitFor( { state: 'visible' } ); - await expect( - assembler.locator( '.block-editor-block-patterns-list__list-item' ) - ).toHaveCount( 10 ); - } ); + await expect( + assembler.locator( + '.block-editor-block-patterns-list__list-item' + ) + ).toHaveCount( 10 ); + } + ); } ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/customize-store/assembler/homepage.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/customize-store/assembler/homepage.spec.js index 167705d0776..8be49d4945a 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/customize-store/assembler/homepage.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/customize-store/assembler/homepage.spec.js @@ -254,7 +254,7 @@ test.describe( 'Homepage tracking banner', () => { } ) => { await setOption( request, baseURL, 'woocommerce_allow_tracking', 'no' ); - await page.route( '**/wp-json/wc/private/patterns*', ( route ) => { + await page.route( '**/wp-json/wc-admin/patterns*', ( route ) => { route.fulfill( { status: 500, } ); @@ -314,22 +314,23 @@ test.describe( 'Homepage tracking banner', () => { ).toBeVisible(); } ); - test( 'Should not show the "Want more patterns?" banner when tracking is allowed', async ( { - baseURL, - pageObject, - } ) => { - await setOption( - request, - baseURL, - 'woocommerce_allow_tracking', - 'yes' - ); + test( + 'Should not show the "Want more patterns?" banner when tracking is allowed', + { tag: '@skip-on-default-pressable' }, + async ( { baseURL, pageObject } ) => { + await setOption( + request, + baseURL, + 'woocommerce_allow_tracking', + 'yes' + ); - await prepareAssembler( pageObject, baseURL ); + await prepareAssembler( pageObject, baseURL ); - const assembler = await pageObject.getAssembler(); - await expect( - assembler.getByText( 'Want more patterns?' ) - ).toBeHidden(); - } ); + const assembler = await pageObject.getAssembler(); + await expect( + assembler.getByText( 'Want more patterns?' ) + ).toBeHidden(); + } + ); } ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/customize-store/assembler/logo-picker/logo-picker.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/customize-store/assembler/logo-picker/logo-picker.spec.js index a2c5c306407..7428e9dcc83 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/customize-store/assembler/logo-picker/logo-picker.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/customize-store/assembler/logo-picker/logo-picker.spec.js @@ -235,9 +235,10 @@ test.describe( 'Assembler -> Logo Picker', { tag: '@gutenberg' }, () => { // alternative way to verify new site icon on the site // verifying site icon shown in the new tab is impossible in headless mode const date = new Date(); + const month = ( date.getMonth() + 1 ).toString().padStart( 2, '0' ); await expect( page.goto( - `/wp-content/uploads/${ date.getFullYear() }/${ date.getMonth() }/image-03-100x100.png` + `/wp-content/uploads/${ date.getFullYear() }/${ month }/image-03-100x100.png` ) ).toBeTruthy(); } ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/customize-store/intro.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/customize-store/intro.spec.js index 449add25331..ffe3f97b831 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/customize-store/intro.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/customize-store/intro.spec.js @@ -115,34 +115,38 @@ test.describe( ).toBeVisible(); } ); - test( 'it shows the "non default block theme" banner when the theme is a block theme different than TT4', async ( { - page, - } ) => { - await activateTheme( 'twentytwentythree' ); + test( + 'it shows the "non default block theme" banner when the theme is a block theme different than TT4', + { tag: '@skip-on-default-pressable' }, + async ( { page } ) => { + await activateTheme( 'twentytwentythree' ); - await page.goto( CUSTOMIZE_STORE_URL ); + await page.goto( CUSTOMIZE_STORE_URL ); - await expect( page.locator( 'h1' ) ).toHaveText( - 'Customize your theme' - ); - await expect( - page.getByRole( 'button', { name: 'Go to the Editor' } ) - ).toBeVisible(); - } ); + await expect( page.locator( 'h1' ) ).toHaveText( + 'Customize your theme' + ); + await expect( + page.getByRole( 'button', { name: 'Go to the Editor' } ) + ).toBeVisible(); + } + ); - test( 'clicking on "Go to the Customizer" with a classic theme should go to the customizer', async ( { - page, - } ) => { - await activateTheme( 'twentytwenty' ); + test( + 'clicking on "Go to the Customizer" with a classic theme should go to the customizer', + { tag: '@skip-on-default-pressable' }, + async ( { page } ) => { + await activateTheme( 'twentytwenty' ); - await page.goto( CUSTOMIZE_STORE_URL ); + await page.goto( CUSTOMIZE_STORE_URL ); - await page - .getByRole( 'button', { name: 'Go to the Customizer' } ) - .click(); + await page + .getByRole( 'button', { name: 'Go to the Customizer' } ) + .click(); - await page.waitForNavigation(); - await expect( page.url() ).toContain( 'customize.php' ); - } ); + await page.waitForNavigation(); + await expect( page.url() ).toContain( 'customize.php' ); + } + ); } ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-cart-block.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-cart-block.spec.js index b262e740846..1f612946ce5 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-cart-block.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-cart-block.spec.js @@ -5,6 +5,7 @@ const { insertBlock, transformIntoBlocks, publishPage, + closeChoosePatternModal, } = require( '../../utils/editor' ); const { getInstalledWordPressVersion } = require( '../../utils/wordpress' ); @@ -23,6 +24,8 @@ test.describe( } ) => { await goToPageEditor( { page } ); + await closeChoosePatternModal( { page } ); + await fillPageTitle( page, testPage.title ); const wordPressVersion = await getInstalledWordPressVersion(); await insertBlock( page, 'Classic Cart', wordPressVersion ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-checkout-block.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-checkout-block.spec.js index 23317b1a834..53479325a1b 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-checkout-block.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-checkout-block.spec.js @@ -7,6 +7,7 @@ const { transformIntoBlocks, publishPage, openEditorSettings, + closeChoosePatternModal, } = require( '../../utils/editor' ); const { getInstalledWordPressVersion } = require( '../../utils/wordpress' ); @@ -74,6 +75,8 @@ test.describe( } ) => { await goToPageEditor( { page } ); + await closeChoosePatternModal( { page } ); + await fillPageTitle( page, testPage.title ); const wordPressVersion = await getInstalledWordPressVersion(); await insertBlock( page, 'Classic Checkout', wordPressVersion ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-coupon.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-coupon.spec.js index 2630674a42b..785f5c0d6b3 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-coupon.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-coupon.spec.js @@ -35,7 +35,15 @@ const test = baseTest.extend( { coupon: async ( { api }, use ) => { const coupon = {}; await use( coupon ); - await api.delete( `coupons/${ coupon.id }`, { force: true } ); + await api + .delete( `coupons/${ coupon.id }`, { force: true } ) + .then( ( response ) => { + console.log( 'Delete successful:', response.data ); + } ) + .catch( ( error ) => { + console.log( 'Error response data:', error.response.data ); + throw new Error( error.response.data ); + } ); }, } ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-order.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-order.spec.js index aa33c49f167..087e95556b7 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-order.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-order.spec.js @@ -299,10 +299,17 @@ test.describe( await page .getByRole( 'textbox', { name: 'Postcode' } ) .fill( '12345' ); - await page - .getByRole( 'textbox', { name: 'Select an option…' } ) - .click(); - await page.getByRole( 'option', { name: 'Florida' } ).click(); + // eslint-disable-next-line playwright/no-conditional-in-test + if ( + await page + .getByRole( 'textbox', { name: 'Select an option…' } ) + .isVisible() + ) { + await page + .getByRole( 'textbox', { name: 'Select an option…' } ) + .click(); + await page.getByRole( 'option', { name: 'Florida' } ).click(); + } await page .getByRole( 'textbox', { name: 'Email address' } ) .fill( 'elbarto@example.com' ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-restricted-coupons.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-restricted-coupons.spec.js index 7a94c6deb02..dd6941c6cc9 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-restricted-coupons.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-restricted-coupons.spec.js @@ -96,7 +96,7 @@ const test = baseTest.extend( { }, } ); -test.describe( 'Restricted coupon management', { tag: '@services' }, () => { +test.describe( 'Restricted coupon management', { tag: [ '@services' ] }, () => { for ( const couponType of Object.keys( couponData ) ) { test( `can create new ${ couponType } coupon`, async ( { page, diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/customer-payment-page.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/customer-payment-page.spec.js index 7aceae0f0fd..db105a99a48 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/merchant/customer-payment-page.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/customer-payment-page.spec.js @@ -114,7 +114,7 @@ test.describe( page.getByText( 'Your order has been received' ) ).toBeVisible(); await expect( - page.getByText( `Order number: ${ orderId }` ) + page.getByText( `Order #: ${ orderId }` ) ).toBeVisible(); await expect( await page.getByText( `Total: $${ productPrice }` ).count() diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/launch-your-store.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/launch-your-store.spec.js index b8e778c39db..cfef43102a9 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/merchant/launch-your-store.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/launch-your-store.spec.js @@ -132,7 +132,7 @@ test.describe( // The store only checkbox should not be on the page. await expect( page.getByRole( 'checkbox', { - name: 'Restrict to store pages only', + name: 'Apply to store pages only', } ) ).toHaveCount( 0 ); @@ -156,14 +156,14 @@ test.describe( // The store only checkbox should be visible. await expect( page.getByRole( 'checkbox', { - name: 'Restrict to store pages only', + name: 'Apply to store pages only', } ) ).toBeVisible(); // The store only checkbox should not be checked. await expect( page.getByRole( 'checkbox', { - name: 'Restrict to store pages only', + name: 'Apply to store pages only', } ) ).not.toBeChecked(); @@ -212,24 +212,11 @@ test.describe( await page.goto( '/wp-admin/admin.php?page=wc-admin' ); await expect( - page.getByRole( 'button', { + page.getByRole( 'menuitem', { name: 'Store coming soon', exact: true, } ) ).toBeVisible(); - - page.getByRole( 'button', { - name: 'Store coming soon', - exact: true, - } ).click(); - - await expect( - page.getByText( 'Manage site visibility' ) - ).toBeVisible(); - - await expect( - page.getByText( 'Customize "Coming soon" page' ) - ).toBeVisible(); } ); test( 'Homescreen badge coming soon entire store', async ( { @@ -257,24 +244,11 @@ test.describe( await page.goto( '/wp-admin/admin.php?page=wc-admin' ); await expect( - page.getByRole( 'button', { - name: 'Site coming soon', + page.getByRole( 'menuitem', { + name: 'Coming soon', exact: true, } ) ).toBeVisible(); - - page.getByRole( 'button', { - name: 'Site coming soon', - exact: true, - } ).click(); - - await expect( - page.getByText( 'Manage site visibility' ) - ).toBeVisible(); - - await expect( - page.getByText( 'Customize "Coming soon" page' ) - ).toBeVisible(); } ); test( 'Homescreen badge live', async ( { page, baseURL } ) => { @@ -299,24 +273,11 @@ test.describe( await page.goto( '/wp-admin/admin.php?page=wc-admin' ); await expect( - page.getByRole( 'button', { + page.getByRole( 'menuitem', { name: 'Live', exact: true, } ) ).toBeVisible(); - - page.getByRole( 'button', { - name: 'Live', - exact: true, - } ).click(); - - await expect( - page.getByText( 'Manage site visibility' ) - ).toBeVisible(); - - await expect( - page.getByText( 'Customize "Coming soon" page' ) - ).toBeHidden(); } ); } ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/lost-password.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/lost-password.spec.js index e13f2d7c9ba..c38e848c541 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/merchant/lost-password.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/lost-password.spec.js @@ -19,20 +19,16 @@ test.describe( 'Can go to lost password page and submit the form', () => { .fill( admin.username ); await page.getByRole( 'button', { name: 'Get New Password' } ).click(); - try { - // For local testing, the email might not be sent, so we can ignore this error. - await expect( - page.getByText( - /The email could not be sent. Your site may not be correctly configured to send emails/i - ) - ).toBeVisible(); - } catch ( e ) { - // eslint-disable-next-line jest/no-try-expect - await page.waitForURL( '**/wp-login.php?checkemail=confirm' ); - // eslint-disable-next-line jest/no-try-expect - await expect( - page.getByText( /Check your email for the confirmation link/i ) - ).toBeVisible(); - } + const emailSentMessage = page.getByText( + 'check your email for the confirmation link' + ); + const emailNotSentMessage = page.getByText( + 'the email could not be sent' + ); + + // We don't have to care if the email was sent, we just want to know the button click attempts a reset. + await expect( + emailSentMessage.or( emailNotSentMessage ) + ).toBeVisible(); } ); } ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/page-loads.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/page-loads.spec.js index 50f86d283fb..d2ff9e34b11 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/merchant/page-loads.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/page-loads.spec.js @@ -206,7 +206,7 @@ for ( const currentPage of wcPages ) { } ) => { await page .locator( - `li.wp-menu-open > ul.wp-submenu > li:has-text("${ currentPage.subpages[ i ].name }")` + `li.wp-menu-open > ul.wp-submenu > li a:text-is("${ currentPage.subpages[ i ].name }")` ) .click(); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/product-edit.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/product-edit.spec.js index 4babc2fec0b..e94595088b6 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/merchant/product-edit.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/product-edit.spec.js @@ -185,3 +185,216 @@ test( } ); } ); + +test( + 'can restore regular price when bulk editing products', + { tag: [ '@gutenberg', '@services' ] }, + async ( { page, products } ) => { + await page.goto( `wp-admin/edit.php?post_type=product` ); + + const salePriceDecrease = 10; + + await test.step( 'select and bulk edit the products', async () => { + for ( const product of products ) { + await page.getByLabel( `Select ${ product.name }` ).click(); + } + + await page + .locator( '#bulk-action-selector-top' ) + .selectOption( 'Edit' ); + await page.locator( '#doaction' ).click(); + + await expect( + await page.locator( '#bulk-titles-list li' ).count() + ).toEqual( products.length ); + } ); + + await test.step( 'update the sale price', async () => { + await page + .locator( 'select[name="change_sale_price"]' ) + .selectOption( + 'Set to regular price decreased by (fixed amount or %):' + ); + await page + .getByPlaceholder( 'Enter sale price ($)' ) + .fill( `${ salePriceDecrease }%` ); + } ); + + await test.step( 'save the updates', async () => { + await page.getByRole( 'button', { name: 'Update' } ).click(); + } ); + + await test.step( 'verify the changes', async () => { + for ( const product of products ) { + await page.goto( `product/${ product.slug }` ); + + const expectedRegularPrice = product.regular_price; + + const expectedSalePrice = ( + expectedRegularPrice * + ( 1 - salePriceDecrease / 100 ) + ).toFixed( 2 ); + + await expect + .soft( + await page + .locator( 'ins' ) + .getByText( `$${ expectedSalePrice }` ) + .count() + ) + .toBeGreaterThan( 0 ); + } + } ); + + await test.step( 'Update products leaving the "Sale > Change to" empty', async () => { + await page.goto( `wp-admin/edit.php?post_type=product` ); + + for ( const product of products ) { + await page.getByLabel( `Select ${ product.name }` ).click(); + } + + await page + .locator( '#bulk-action-selector-top' ) + .selectOption( 'Edit' ); + await page.locator( '#doaction' ).click(); + + await page + .locator( 'select[name="change_sale_price"]' ) + .selectOption( 'Change to:' ); + + await page.getByRole( 'button', { name: 'Update' } ).click(); + } ); + + await test.step( 'Verify products have their regular price again', async () => { + for ( const product of products ) { + await page.goto( `product/${ product.slug }` ); + + const expectedRegularPrice = product.regular_price; + + await expect + .soft( await page.locator( 'ins' ).count() ) + .toBe( 0 ); + + await expect + .soft( await page.locator( 'bdi' ).first() ) + .toContainText( expectedRegularPrice ); + } + } ); + } +); + +test( + 'can decrease the sale price if the product was not previously in sale when bulk editing products', + { tag: [ '@gutenberg', '@services' ] }, + async ( { page, products } ) => { + await page.goto( `wp-admin/edit.php?post_type=product` ); + + const salePriceDecrease = 10; + + await test.step( 'Update products with the "Sale > Decrease existing sale price" option', async () => { + await page.goto( `wp-admin/edit.php?post_type=product` ); + + for ( const product of products ) { + await page.getByLabel( `Select ${ product.name }` ).click(); + } + + await page + .locator( '#bulk-action-selector-top' ) + .selectOption( 'Edit' ); + await page.locator( '#doaction' ).click(); + + await page + .locator( 'select[name="change_sale_price"]' ) + .selectOption( + 'Decrease existing sale price by (fixed amount or %):' + ); + await page + .getByPlaceholder( 'Enter sale price ($)' ) + .fill( `${ salePriceDecrease }%` ); + + await page.getByRole( 'button', { name: 'Update' } ).click(); + } ); + + await test.step( 'Verify products have a sale price', async () => { + for ( const product of products ) { + await page.goto( `product/${ product.slug }` ); + + const expectedSalePrice = ( + product.regular_price * + ( 1 - salePriceDecrease / 100 ) + ).toFixed( 2 ); + + await expect + .soft( + await page + .locator( 'ins' ) + .getByText( `$${ expectedSalePrice }` ) + .count() + ) + .toBeGreaterThan( 0 ); + } + } ); + } +); + +test( + 'increasing the sale price from 0 does not change the sale price when bulk editing products', + { tag: [ '@gutenberg', '@services' ] }, + async ( { page, api } ) => { + let product; + await api + .post( 'products', { + id: 0, + name: `Product _${ Date.now() }`, + type: 'simple', + regular_price: '100', + sale_price: '0', + manage_stock: true, + stock_quantity: 10, + stock_status: 'instock', + } ) + .then( ( response ) => { + product = response.data; + } ); + + const salePriceIncrease = 10; + + await test.step( 'Update products with the "Sale > Increase existing sale price" option', async () => { + await page.goto( `wp-admin/edit.php?post_type=product` ); + + await page.getByLabel( `Select ${ product.name }` ).click(); + + await page + .locator( '#bulk-action-selector-top' ) + .selectOption( 'Edit' ); + await page.locator( '#doaction' ).click(); + + await page + .locator( 'select[name="change_sale_price"]' ) + .selectOption( + 'Increase existing sale price by (fixed amount or %):' + ); + + await page + .getByPlaceholder( 'Enter sale price ($)' ) + .fill( `${ salePriceIncrease }%` ); + + await page.getByRole( 'button', { name: 'Update' } ).click(); + } ); + + await test.step( 'Verify products have a sale price', async () => { + await page.goto( `product/${ product.slug }` ); + + const expectedSalePrice = '$0.00'; + + await expect + .soft( + await page + .locator( 'ins' ) + .getByText( expectedSalePrice ) + .count() + ) + .toBeGreaterThan( 0 ); + } ); + } +); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/product-images.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/product-images.spec.js index 6d4e57fb2fd..e874730d438 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/merchant/product-images.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/product-images.spec.js @@ -110,7 +110,9 @@ test.describe( // Verify image in store frontend await page.goto( product.permalink ); - await expect( page.getByTitle( `image-01` ) ).toBeVisible(); + await expect( + page.locator( `img.wp-post-image[src*="image-01"]` ) + ).toBeVisible(); } ); } ); @@ -150,7 +152,9 @@ test.describe( // Verify image in store frontend await page.goto( productWithImage.permalink ); - await expect( page.getByTitle( `image-02` ) ).toBeVisible(); + await expect( + page.locator( `img.wp-post-image[src*="image-02"]` ) + ).toBeVisible(); } ); } ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/product-reviews.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/product-reviews.spec.js index 3c0359693c5..8b024315391 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/merchant/product-reviews.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/product-reviews.spec.js @@ -48,7 +48,7 @@ const test = baseTest.extend( { } ); test.describe( - 'Product Reviews > Edit Product Review', + 'Product Reviews', { tag: [ '@gutenberg', '@services' ] }, () => { test( 'can view products reviews list', async ( { page, reviews } ) => { @@ -77,6 +77,8 @@ test.describe( .first() ).toContainText( review.product_name ); } + + expect( reviews.length ).toBeGreaterThan( 0 ); } ); test( 'can filter the reviews by product', async ( { @@ -139,7 +141,6 @@ test.describe( .getByRole( 'button', { name: 'Update Comment' } ) .click(); - // Verify that the edited comment is there await expect( reviewRow.getByText( updatedQuickReview ) ).toBeVisible(); @@ -148,20 +149,17 @@ test.describe( test( 'can edit a product review', async ( { page, reviews } ) => { const review = reviews[ 0 ]; - // Go to the Edit page of the review await page.goto( `wp-admin/comment.php?action=editcomment&c=${ review.id }` ); await expect( page.getByText( 'Edit Comment' ) ).toBeVisible(); - // Create new comment and edit the review with it const updatedReview = `(edited ${ Date.now() })`; await page .locator( '.wp-editor-area' ) .first() .fill( updatedReview ); - // Generate another random rating and edit the review with it await page.click( '#rating' ); const updatedRating = ( Math.random() * ( 5 - 1 ) + 1 ).toFixed( 0 @@ -171,7 +169,6 @@ test.describe( } ); await page.getByRole( 'button', { name: 'Update' } ).click(); - // Verify that the edited comment is in the product reviews list await page.goto( `wp-admin/edit.php?post_type=product&page=product-reviews` ); @@ -185,7 +182,6 @@ test.describe( reviewRow.getByLabel( `${ updatedRating } out of 5` ) ).toBeVisible(); - // Verify that the edited comment is in the shop's product page await reviewRow.locator( 'a.comments-view-item-link' ).click(); await page.click( '#tab-reviews' ); await expect( @@ -196,7 +192,31 @@ test.describe( ).toBeVisible(); } ); - test( 'can delete a product review', async ( { page, reviews } ) => { + test( 'can approve a product review', async ( { page, reviews } ) => { + const review = reviews[ 0 ]; // Select the first review for approval + + await page.goto( + `wp-admin/edit.php?post_type=product&page=product-reviews` + ); + + const reviewRow = page.locator( `#comment-${ review.id }` ); + + const approveButton = reviewRow.getByRole( 'button', { + name: 'Approve', + } ); + + await reviewRow.hover(); + await approveButton.click(); + const unapproveButton = reviewRow.getByRole( 'button', { + name: 'Unapprove', + } ); + await expect( unapproveButton ).toBeVisible(); + } ); + + test( 'can mark a product review as spam', async ( { + page, + reviews, + } ) => { const review = reviews[ 0 ]; await page.goto( @@ -206,7 +226,59 @@ test.describe( const reviewRow = page.locator( `#comment-${ review.id }` ); await reviewRow.hover(); - // Select Trash action, check confirmation prompt and undo + await reviewRow.getByRole( 'button', { name: 'Spam' } ).click(); + + await expect( + page.locator( `#comment-${ review.id }` ) + ).toBeHidden(); + + await page.click( 'a[href*="comment_status=spam"]' ); + + await expect( + page.locator( `#comment-${ review.id }` ) + ).toBeVisible(); + } ); + + test( 'can reply to a product review', async ( { page, reviews } ) => { + const review = reviews[ 0 ]; + + await page.goto( + 'wp-admin/edit.php?post_type=product&page=product-reviews' + ); + + const reviewRow = page.locator( `#comment-${ review.id }` ); + await reviewRow.hover(); + await reviewRow.getByRole( 'button', { name: 'Reply' } ).click(); + const replyTextArea = page.locator( 'textarea#replycontent' ); + + await expect( replyTextArea ).toBeVisible(); + + const replyText = `Thank you for your feedback! (replied ${ Date.now() })`; + await replyTextArea.fill( replyText ); + + await page.locator( 'button.save.button.button-primary' ).click(); + + const productLink = await reviewRow + .locator( 'a.comments-view-item-link' ) + .getAttribute( 'href' ); + await page.goto( productLink ); + await page.click( '#tab-reviews' ); + + const replyReviews = page.locator( + `div.comment_container:has-text("${ replyText }")` + ); + await expect( replyReviews ).toBeVisible(); + } ); + + test( 'can delete a product review', async ( { page, reviews } ) => { + const review = reviews[ 0 ]; + + await page.goto( + `wp-admin/edit.php?post_type=product&page=product-reviews` + ); + const reviewRow = page.locator( `#comment-${ review.id }` ); + await reviewRow.hover(); + await reviewRow.getByRole( 'button', { name: 'Trash' } ).click(); await expect( page.getByText( @@ -215,26 +287,24 @@ test.describe( ).toBeVisible(); await page.getByRole( 'button', { name: 'Undo' } ).click(); - // Verify that the review has been restored await expect( reviewRow.getByRole( 'cell', { name: review.review } ) ).toBeVisible(); - // Select Trash action and delete it permanently await reviewRow.getByRole( 'button', { name: 'Trash' } ).click(); + await expect( page.getByText( `Comment by ${ review.reviewer } moved to the Trash` ) ).toBeVisible(); - // Verify that the review in the trash await page.click( 'a[href*="comment_status=trash"]' ); + await expect( reviewRow.getByRole( 'cell', { name: review.review } ) ).toBeVisible(); - // Check that the review is in the trash via URL await page.goto( `wp-admin/comment.php?action=editcomment&c=${ review.id }` ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/add-variable-product/create-product-attributes.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/add-variable-product/create-product-attributes.spec.js index 94225f9c4bd..77db0953f5c 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/add-variable-product/create-product-attributes.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/add-variable-product/create-product-attributes.spec.js @@ -133,7 +133,10 @@ test.describe( 'Add product attributes', { tag: '@gutenberg' }, () => { 'options=woocommerce_task_list_reminder_bar_hidden' ) ); - await page.getByRole( 'button', { name: 'Update' } ).click(); + await page + .locator( '#publishing-action' ) + .getByRole( 'button', { name: 'Update' } ) + .click(); const response = await finalRequestResolution; expect( response.ok() ).toBeTruthy(); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/create-variable-product-block-editor.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/create-variable-product-block-editor.spec.js index 8417a8264f3..1c1ff9c0459 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/create-variable-product-block-editor.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/create-variable-product-block-editor.spec.js @@ -367,10 +367,16 @@ test.describe( 'Variations tab', { tag: '@gutenberg' }, () => { await page.getByLabel( 'Delete variation' ).click(); - const element = page.locator( 'div.components-snackbar__content' ); - await expect( await element.innerText() ).toMatch( - '1 variation deleted.' - ); + await expect( + page + .getByLabel( 'Dismiss this notice' ) + .getByText( '1 variation deleted.' ) + ).toBeVisible(); + + await page.waitForSelector( + 'div.woocommerce-product-variations-pagination__info', + { timeout: 20000 } + ); // test was timing out before page loaded await expect( await page diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/linked-product-tab-product-block-editor.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/linked-product-tab-product-block-editor.spec.js index e5b4cb7ea39..777c933b3fb 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/linked-product-tab-product-block-editor.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/linked-product-tab-product-block-editor.spec.js @@ -2,15 +2,19 @@ const { test } = require( '../../../../fixtures/block-editor-fixtures' ); const { expect } = require( '@playwright/test' ); const { clickOnTab } = require( '../../../../utils/simple-products' ); -const { api } = require( '../../../../utils' ); +const { helpers } = require( '../../../../utils' ); const NEW_EDITOR_ADD_PRODUCT_URL = 'wp-admin/admin.php?page=wc-admin&path=%2Fadd-product'; const isTrackingSupposedToBeEnabled = !! process.env.ENABLE_TRACKING; +const uniqueId = helpers.random(); +let categoryId = 0; +const categoryName = `cat_${ uniqueId }`; +const productName = `Product ${ uniqueId }`; const productData = { - name: `Linked product Name ${ new Date().getTime().toString() }`, + name: `Linked ${ productName }`, summary: 'This is a product summary', }; @@ -20,27 +24,45 @@ let productId = 0; test.describe( 'General tab', { tag: '@gutenberg' }, () => { test.describe( 'Linked product', () => { - test.beforeAll( async () => { + test.beforeAll( async ( { api } ) => { + await api + .post( 'products/categories', { + name: categoryName, + } ) + .then( ( response ) => { + categoryId = response.data.id; + } ); + for ( let i = 1; i <= 5; i++ ) { const product = { - name: `Product name ${ i } ${ new Date() - .getTime() - .toString() }`, - productPrice: `${ i }00`, + name: `Product ${ uniqueId } ${ i }`, + regular_price: `${ i }0000`, + sale_price: `${ i }000`, type: 'simple', + categories: [ { id: categoryId } ], }; - linkedProductsData.push( product ); - const id = await api.create.product( product ); - productIds.push( id ); + await api.post( 'products', product ).then( ( response ) => { + productIds.push( response.data.id ); + linkedProductsData.push( product ); + } ); } } ); - test.afterAll( async () => { + test.afterAll( async ( { api } ) => { for ( const aProductId of productIds ) { - await api.deletePost.product( aProductId ); + await api.delete( `products/${ aProductId }`, { + force: true, + } ); } - await api.deletePost.product( productId ); + await api.delete( `products/${ productId }`, { + force: true, + } ); + + await api.delete( `products/categories/${ categoryId }`, { + force: true, + } ); } ); + test.skip( isTrackingSupposedToBeEnabled, 'The block product editor is not being tested' @@ -61,7 +83,30 @@ test.describe( 'General tab', { tag: '@gutenberg' }, () => { .last() .fill( productData.summary ); + // Include in category + await clickOnTab( 'Organization', page ); + const waitForCategoriesResponse = page.waitForResponse( + ( response ) => + response.url().includes( '/wp-json/wp/v2/product_cat' ) && + response.status() === 200 + ); + await page.getByLabel( 'Categories' ).click(); + await waitForCategoriesResponse; + await page.getByLabel( categoryName ).check(); + await page.getByLabel( `Remove Uncategorized` ).click(); + await expect( + page.getByLabel( `Remove ${ categoryName }` ) + ).toBeVisible(); + + const waitForProductsSearchResponse = page.waitForResponse( + ( response ) => + response + .url() + .includes( '/wp-json/wc/v3/products?search' ) && + response.status() === 200 + ); await clickOnTab( 'Linked products', page ); + await waitForProductsSearchResponse; await expect( page.getByRole( 'heading', { @@ -75,7 +120,7 @@ test.describe( 'General tab', { tag: '@gutenberg' }, () => { ) .first() .getByRole( 'combobox' ) - .fill( linkedProductsData[ 0 ].name ); + .fill( productName ); await page.getByText( linkedProductsData[ 0 ].name ).click(); @@ -92,7 +137,7 @@ test.describe( 'General tab', { tag: '@gutenberg' }, () => { await chooseProductsResponsePromise; await expect( - page.getByRole( 'row', { name: 'Product name' } ) + page.getByRole( 'row', { name: productName } ) ).toHaveCount( 4 ); const upsellsRows = page.locator( diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/product-attributes-block-editor.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/product-attributes-block-editor.spec.js index df494986a71..b80922b34f1 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/product-attributes-block-editor.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/product-attributes-block-editor.spec.js @@ -373,6 +373,13 @@ test( await expect( async () => { await page.reload(); + // Waiting for the "Block: Product attributes" will ensure + // that test will pass against the Pressable environment + await expect( + page.locator( + `[aria-label="Block: Product attributes"]` + ) + ).toBeVisible(); await page.getByRole( 'button', { name: 'Edit' } ).click(); await expect( page.locator( diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/product-images-block-editor.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/product-images-block-editor.spec.js index 60453a017b1..57d27bacfa7 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/product-images-block-editor.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/product-images-block-editor.spec.js @@ -107,7 +107,11 @@ test( 'can add images', { tag: '@gutenberg' }, async ( { page, product } ) => { await page.goto( product.permalink ); for ( const image of images ) { - await expect( page.getByTitle( image ) ).toBeVisible(); + await expect( + // By scopping the locator to the link, we exclude the zoom image and + // ensure the correct image is displayed. + page.locator( `a > img[src*="${ image }"]` ) + ).toBeVisible(); } } ); } ); @@ -160,7 +164,11 @@ test( // Verify image in store frontend await page.goto( productWithGallery.permalink ); - await expect( page.getByTitle( newImageName ) ).toBeVisible(); + await expect( + // By scopping the locator to the link, we exclude the zoom image and + // ensure the correct image is displayed. + page.locator( `a > img[src*="${ newImageName }"]` ) + ).toBeVisible(); } ); } ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-block-calculate-shipping.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-block-calculate-shipping.spec.js index 8649bd7e246..2b736893759 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-block-calculate-shipping.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-block-calculate-shipping.spec.js @@ -130,157 +130,173 @@ test.describe( } ); } ); - test( 'allows customer to calculate Free Shipping in cart block if in Netherlands', async ( { - page, - context, - cartBlockPage, - } ) => { - await context.clearCookies(); + test( + 'allows customer to calculate Free Shipping in cart block if in Netherlands', + { tag: [ '@could-be-unit-test' ] }, + async ( { page, context, cartBlockPage } ) => { + await context.clearCookies(); - await addAProductToCart( page, product1Id ); - await page.goto( cartBlockPage.slug ); + await addAProductToCart( page, product1Id ); + await page.goto( cartBlockPage.slug ); - // Set shipping country to Netherlands - await page.getByLabel( 'Add an address for shipping' ).click(); - await page - .getByRole( 'combobox' ) - .first() - .selectOption( 'Netherlands' ); - await page.getByLabel( 'Postal code' ).fill( '1011AA' ); - await page.getByLabel( 'City' ).fill( 'Amsterdam' ); - await page.getByRole( 'button', { name: 'Update' } ).click(); + // Set shipping country to Netherlands + await page.getByLabel( 'Add an address for shipping' ).click(); + await page + .getByRole( 'combobox' ) + .first() + .selectOption( 'Netherlands' ); + await page.getByLabel( 'Postal code' ).fill( '1011AA' ); + await page.getByLabel( 'City' ).fill( 'Amsterdam' ); + await page.getByRole( 'button', { name: 'Update' } ).click(); - // Verify shipping costs - await expect( - page.getByRole( 'group' ).getByText( 'Free shipping' ) - ).toBeVisible(); - await expect( - page.getByRole( 'strong' ).getByText( 'Free', { exact: true } ) - ).toBeVisible(); - await expect( page.getByText( '$' ).nth( 2 ) ).toContainText( - firstProductPrice - ); - } ); + // Verify shipping costs + await expect( + page.getByRole( 'group' ).getByText( 'Free shipping' ) + ).toBeVisible(); + await expect( + page + .getByRole( 'strong' ) + .getByText( 'Free', { exact: true } ) + ).toBeVisible(); + await expect( page.getByText( '$' ).nth( 2 ) ).toContainText( + firstProductPrice + ); + } + ); - test( 'allows customer to calculate Flat rate and Local pickup in cart block if in Portugal', async ( { - page, - context, - cartBlockPage, - } ) => { - await context.clearCookies(); + test( + 'allows customer to calculate Flat rate and Local pickup in cart block if in Portugal', + { tag: [ '@could-be-unit-test' ] }, + async ( { page, context, cartBlockPage } ) => { + await context.clearCookies(); - await addAProductToCart( page, product1Id ); - await page.goto( cartBlockPage.slug ); + await addAProductToCart( page, product1Id ); + await page.goto( cartBlockPage.slug ); - // Set shipping country to Portugal - await page.getByLabel( 'Add an address for shipping' ).click(); - await page - .getByRole( 'combobox' ) - .first() - .selectOption( 'Portugal' ); - await page.getByLabel( 'Postal code' ).fill( '1000-001' ); - await page.getByLabel( 'City' ).fill( 'Lisbon' ); - await page.getByRole( 'button', { name: 'Update' } ).click(); + // Set shipping country to Portugal + await page.getByLabel( 'Add an address for shipping' ).click(); + await page + .getByRole( 'combobox' ) + .first() + .selectOption( 'Portugal' ); + await page.getByLabel( 'Postal code' ).fill( '1000-001' ); + await page.getByLabel( 'City' ).fill( 'Lisbon' ); + await page.getByRole( 'button', { name: 'Update' } ).click(); - // Verify shipping costs - await expect( - page.getByRole( 'group' ).getByText( 'Flat rate' ) - ).toBeVisible(); - await expect( page.getByText( 'Shipping$5.00Flat' ) ).toBeVisible(); - await expect( - page.getByText( `$${ firstProductWithFlatRate }` ) - ).toBeVisible(); + // Verify shipping costs + await expect( + page.getByRole( 'group' ).getByText( 'Flat rate' ) + ).toBeVisible(); + await expect( + page.getByText( 'Shipping$5.00Flat' ) + ).toBeVisible(); + await expect( + page.getByText( `$${ firstProductWithFlatRate }` ) + ).toBeVisible(); - // Set shipping to local pickup instead of flat rate - await page.getByRole( 'group' ).getByText( 'Local pickup' ).click(); + // Set shipping to local pickup instead of flat rate + await page + .getByRole( 'group' ) + .getByText( 'Local pickup' ) + .click(); - // Verify updated shipping costs - await expect( page.getByText( 'ShippingFreeLocal' ) ).toBeVisible(); - await expect( page.getByText( '$' ).nth( 2 ) ).toContainText( - firstProductPrice - ); - } ); + // Verify updated shipping costs + await expect( + page.getByText( 'ShippingFreeLocal' ) + ).toBeVisible(); + await expect( page.getByText( '$' ).nth( 2 ) ).toContainText( + firstProductPrice + ); + } + ); - test( 'should show correct total cart block price after updating quantity', async ( { - page, - context, - cartBlockPage, - } ) => { - await context.clearCookies(); + test( + 'should show correct total cart block price after updating quantity', + { tag: [ '@could-be-unit-test' ] }, + async ( { page, context, cartBlockPage } ) => { + await context.clearCookies(); - await addAProductToCart( page, product1Id ); - await page.goto( cartBlockPage.slug ); + await addAProductToCart( page, product1Id ); + await page.goto( cartBlockPage.slug ); - // Set shipping country to Portugal - await page.getByLabel( 'Add an address for shipping' ).click(); - await page - .getByRole( 'combobox' ) - .first() - .selectOption( 'Portugal' ); - await page.getByLabel( 'Postal code' ).fill( '1000-001' ); - await page.getByLabel( 'City' ).fill( 'Lisbon' ); - await page.getByRole( 'button', { name: 'Update' } ).click(); + // Set shipping country to Portugal + await page.getByLabel( 'Add an address for shipping' ).click(); + await page + .getByRole( 'combobox' ) + .first() + .selectOption( 'Portugal' ); + await page.getByLabel( 'Postal code' ).fill( '1000-001' ); + await page.getByLabel( 'City' ).fill( 'Lisbon' ); + await page.getByRole( 'button', { name: 'Update' } ).click(); - // Increase product quantity and verify the updated price - await page.getByLabel( 'Increase quantity of First' ).click(); - await expect( - page.getByText( - `$${ - parseInt( firstProductPrice, 10 ) + - parseInt( firstProductPrice, 10 ) + - 5 - }`.toString() - ) - ).toBeVisible(); - } ); + // Increase product quantity and verify the updated price + await page.getByLabel( 'Increase quantity of First' ).click(); + await expect( + page.getByText( + `$${ + parseInt( firstProductPrice, 10 ) + + parseInt( firstProductPrice, 10 ) + + 5 + }`.toString() + ) + ).toBeVisible(); + } + ); - test( 'should show correct total cart block price with 2 different products and flat rate/local pickup', async ( { - page, - context, - cartBlockPage, - } ) => { - await context.clearCookies(); + test( + 'should show correct total cart block price with 2 different products and flat rate/local pickup', + { tag: [ '@could-be-unit-test' ] }, + async ( { page, context, cartBlockPage } ) => { + await context.clearCookies(); - await addAProductToCart( page, product1Id ); - await addAProductToCart( page, product2Id ); - await page.goto( cartBlockPage.slug ); + await addAProductToCart( page, product1Id ); + await addAProductToCart( page, product2Id ); + await page.goto( cartBlockPage.slug ); - // Set shipping country to Portugal - await page.getByLabel( 'Add an address for shipping' ).click(); - await page - .getByRole( 'combobox' ) - .first() - .selectOption( 'Portugal' ); - await page.getByLabel( 'Postal code' ).fill( '1000-001' ); - await page.getByLabel( 'City' ).fill( 'Lisbon' ); - await page.getByRole( 'button', { name: 'Update' } ).click(); + // Set shipping country to Portugal + await page.getByLabel( 'Add an address for shipping' ).click(); + await page + .getByRole( 'combobox' ) + .first() + .selectOption( 'Portugal' ); + await page.getByLabel( 'Postal code' ).fill( '1000-001' ); + await page.getByLabel( 'City' ).fill( 'Lisbon' ); + await page.getByRole( 'button', { name: 'Update' } ).click(); - // Verify shipping costs - await expect( - page.getByRole( 'group' ).getByText( 'Flat rate' ) - ).toBeVisible(); - await expect( page.getByText( 'Shipping$5.00Flat' ) ).toBeVisible(); - await expect( - page.getByText( - `$${ - parseInt( firstProductPrice, 10 ) + - parseInt( secondProductPrice, 10 ) + - 5 - }`.toString() - ) - ).toBeVisible(); + // Verify shipping costs + await expect( + page.getByRole( 'group' ).getByText( 'Flat rate' ) + ).toBeVisible(); + await expect( + page.getByText( 'Shipping$5.00Flat' ) + ).toBeVisible(); + await expect( + page.getByText( + `$${ + parseInt( firstProductPrice, 10 ) + + parseInt( secondProductPrice, 10 ) + + 5 + }`.toString() + ) + ).toBeVisible(); - // Set shipping to local pickup instead of flat rate - await page.getByRole( 'group' ).getByText( 'Local pickup' ).click(); + // Set shipping to local pickup instead of flat rate + await page + .getByRole( 'group' ) + .getByText( 'Local pickup' ) + .click(); - // Verify updated shipping costs - await expect( page.getByText( 'ShippingFreeLocal' ) ).toBeVisible(); - await expect( - page - .locator( 'div' ) - .filter( { hasText: /^\$30\.00$/ } ) - .locator( 'span' ) - ).toBeVisible(); - } ); + // Verify updated shipping costs + await expect( + page.getByText( 'ShippingFreeLocal' ) + ).toBeVisible(); + await expect( + page + .locator( 'div' ) + .filter( { hasText: /^\$30\.00$/ } ) + .locator( 'span' ) + ).toBeVisible(); + } + ); } ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-block-coupons.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-block-coupons.spec.js index 62d8d6f2cdc..9d709e40ea9 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-block-coupons.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-block-coupons.spec.js @@ -127,17 +127,111 @@ test.describe( } ); } ); - test( 'allows cart block to apply coupon of any type', async ( { - page, - } ) => { - const totals = [ '$50.00', '$27.50', '$45.00' ]; + test( + 'allows cart block to apply coupon of any type', + { tag: [ '@could-be-unit-test' ] }, + async ( { page } ) => { + const totals = [ '$50.00', '$27.50', '$45.00' ]; - // apply all coupon types - for ( let i = 0; i < coupons.length; i++ ) { + // apply all coupon types + for ( let i = 0; i < coupons.length; i++ ) { + await page + .getByRole( 'button', { name: 'Add a coupon' } ) + .click(); + await page + .getByLabel( 'Enter code' ) + .fill( coupons[ i ].code ); + await page.getByText( 'Apply', { exact: true } ).click(); + await expect( + page + .locator( + '.wc-block-components-notice-banner__content' + ) + .getByText( + `Coupon code "${ coupons[ i ].code }" has been applied to your cart.` + ) + ).toBeVisible(); + await expect( + page.locator( + '.wc-block-components-totals-footer-item > .wc-block-components-totals-item__value' + ) + ).toHaveText( totals[ i ] ); + await page + .getByLabel( `Remove coupon "${ coupons[ i ].code }"` ) + .click(); + await expect( + page + .locator( + '.wc-block-components-notice-banner__content' + ) + .getByText( + `Coupon code "${ coupons[ i ].code }" has been removed from your cart.` + ) + ).toBeVisible(); + } + } + ); + + test( + 'allows cart block to apply multiple coupons', + { tag: [ '@could-be-unit-test' ] }, + async ( { page } ) => { + const totals = [ '$50.00', '$22.50', '$12.50' ]; + const totalsReverse = [ '$17.50', '$45.00', '$55.00' ]; + const discounts = [ '-$5.00', '-$32.50', '-$42.50' ]; + + // add all coupons and verify prices + for ( let i = 0; i < coupons.length; i++ ) { + await page + .getByRole( 'button', { name: 'Add a coupon' } ) + .click(); + await page + .getByLabel( 'Enter code' ) + .fill( coupons[ i ].code ); + await page.getByText( 'Apply', { exact: true } ).click(); + await expect( + page + .locator( + '.wc-block-components-notice-banner__content' + ) + .getByText( + `Coupon code "${ coupons[ i ].code }" has been applied to your cart.` + ) + ).toBeVisible(); + await expect( + page.locator( + '.wc-block-components-totals-discount > .wc-block-components-totals-item__value' + ) + ).toHaveText( discounts[ i ] ); + await expect( + page.locator( + '.wc-block-components-totals-footer-item > .wc-block-components-totals-item__value' + ) + ).toHaveText( totals[ i ] ); + } + + for ( let i = 0; i < coupons.length; i++ ) { + await page + .getByLabel( `Remove coupon "${ coupons[ i ].code }"` ) + .click(); + await expect( + page.locator( + '.wc-block-components-totals-footer-item > .wc-block-components-totals-item__value' + ) + ).toHaveText( totalsReverse[ i ] ); + } + } + ); + + test( + 'prevents cart block applying same coupon twice', + { tag: [ '@could-be-unit-test' ] }, + async ( { page } ) => { + // try to add two same coupons and verify the error message await page .getByRole( 'button', { name: 'Add a coupon' } ) .click(); - await page.getByLabel( 'Enter code' ).fill( coupons[ i ].code ); + await page.getByLabel( 'Enter code' ).fill( coupons[ 0 ].code ); await page.getByText( 'Apply', { exact: true } ).click(); await expect( page @@ -145,114 +239,40 @@ test.describe( '.wc-block-components-notice-banner__content' ) .getByText( - `Coupon code "${ coupons[ i ].code }" has been applied to your cart.` + `Coupon code "${ coupons[ 0 ].code }" has been applied to your cart.` ) ).toBeVisible(); - await expect( - page.locator( - '.wc-block-components-totals-footer-item > .wc-block-components-totals-item__value' - ) - ).toHaveText( totals[ i ] ); - await page - .getByLabel( `Remove coupon "${ coupons[ i ].code }"` ) - .click(); - await expect( - page - .locator( - '.wc-block-components-notice-banner__content' - ) - .getByText( - `Coupon code "${ coupons[ i ].code }" has been removed from your cart.` - ) - ).toBeVisible(); - } - } ); - - test( 'allows cart block to apply multiple coupons', async ( { - page, - } ) => { - const totals = [ '$50.00', '$22.50', '$12.50' ]; - const totalsReverse = [ '$17.50', '$45.00', '$55.00' ]; - const discounts = [ '-$5.00', '-$32.50', '-$42.50' ]; - - // add all coupons and verify prices - for ( let i = 0; i < coupons.length; i++ ) { await page .getByRole( 'button', { name: 'Add a coupon' } ) .click(); - await page.getByLabel( 'Enter code' ).fill( coupons[ i ].code ); + await page.getByLabel( 'Enter code' ).fill( coupons[ 0 ].code ); await page.getByText( 'Apply', { exact: true } ).click(); await expect( page - .locator( - '.wc-block-components-notice-banner__content' - ) + .getByRole( 'alert' ) .getByText( - `Coupon code "${ coupons[ i ].code }" has been applied to your cart.` + `Coupon code "${ coupons[ 0 ].code }" has already been applied.` ) ).toBeVisible(); - await expect( - page.locator( - '.wc-block-components-totals-discount > .wc-block-components-totals-item__value' - ) - ).toHaveText( discounts[ i ] ); - await expect( - page.locator( - '.wc-block-components-totals-footer-item > .wc-block-components-totals-item__value' - ) - ).toHaveText( totals[ i ] ); } + ); - for ( let i = 0; i < coupons.length; i++ ) { + test( + 'prevents cart block applying coupon with usage limit', + { tag: [ '@could-be-unit-test' ] }, + async ( { page } ) => { + // add coupon with usage limit await page - .getByLabel( `Remove coupon "${ coupons[ i ].code }"` ) + .getByRole( 'button', { name: 'Add a coupon' } ) .click(); + await page.getByLabel( 'Enter code' ).fill( couponLimitedCode ); + await page.getByText( 'Apply', { exact: true } ).click(); await expect( - page.locator( - '.wc-block-components-totals-footer-item > .wc-block-components-totals-item__value' - ) - ).toHaveText( totalsReverse[ i ] ); + page + .getByRole( 'alert' ) + .getByText( 'Coupon usage limit has been reached.' ) + ).toBeVisible(); } - } ); - - test( 'prevents cart block applying same coupon twice', async ( { - page, - } ) => { - // try to add two same coupons and verify the error message - await page.getByRole( 'button', { name: 'Add a coupon' } ).click(); - await page.getByLabel( 'Enter code' ).fill( coupons[ 0 ].code ); - await page.getByText( 'Apply', { exact: true } ).click(); - await expect( - page - .locator( '.wc-block-components-notice-banner__content' ) - .getByText( - `Coupon code "${ coupons[ 0 ].code }" has been applied to your cart.` - ) - ).toBeVisible(); - await page.getByRole( 'button', { name: 'Add a coupon' } ).click(); - await page.getByLabel( 'Enter code' ).fill( coupons[ 0 ].code ); - await page.getByText( 'Apply', { exact: true } ).click(); - await expect( - page - .getByRole( 'alert' ) - .getByText( - `Coupon code "${ coupons[ 0 ].code }" has already been applied.` - ) - ).toBeVisible(); - } ); - - test( 'prevents cart block applying coupon with usage limit', async ( { - page, - } ) => { - // add coupon with usage limit - await page.getByRole( 'button', { name: 'Add a coupon' } ).click(); - await page.getByLabel( 'Enter code' ).fill( couponLimitedCode ); - await page.getByText( 'Apply', { exact: true } ).click(); - await expect( - page - .getByRole( 'alert' ) - .getByText( 'Coupon usage limit has been reached.' ) - ).toBeVisible(); - } ); + ); } ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-block.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-block.spec.js index 11ab6abe7d9..3ce134b9e9c 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-block.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-block.spec.js @@ -73,116 +73,132 @@ test.describe( 'Cart Block page', { tag: [ '@payments', '@services' ] }, () => { } ); } ); - test( 'can see empty cart, add and remove simple & cross sell product, increase to max quantity', async ( { - page, - testPage, - } ) => { - await goToPageEditor( { page } ); - await fillPageTitle( page, testPage.title ); - await insertBlockByShortcut( page, 'Cart' ); - await publishPage( page, testPage.title ); + test( + 'can see empty cart, add and remove simple & cross sell product, increase to max quantity', + { tag: [ '@could-be-unit-test' ] }, + async ( { page, testPage } ) => { + await goToPageEditor( { page } ); + await fillPageTitle( page, testPage.title ); + await insertBlockByShortcut( page, 'Cart' ); + await publishPage( page, testPage.title ); - // go to the page to test empty cart block - await page.goto( testPage.slug ); - await expect( - page.getByRole( 'heading', { name: testPage.title } ) - ).toBeVisible(); - await expect( - await page.getByText( 'Your cart is currently empty!' ).count() - ).toBeGreaterThan( 0 ); - await expect( - page.getByRole( 'link', { name: 'Browse store' } ) - ).toBeVisible(); - await page.getByRole( 'link', { name: 'Browse store' } ).click(); - await expect( - page.getByRole( 'heading', { name: 'Shop' } ) - ).toBeVisible(); + // go to the page to test empty cart block + await page.goto( testPage.slug ); + await expect( + page.getByRole( 'heading', { name: testPage.title } ) + ).toBeVisible(); + await expect( + await page.getByText( 'Your cart is currently empty!' ).count() + ).toBeGreaterThan( 0 ); + await expect( + page.getByRole( 'link', { name: 'Browse store' } ) + ).toBeVisible(); + await page.getByRole( 'link', { name: 'Browse store' } ).click(); + await expect( + page.getByRole( 'heading', { name: 'Shop' } ) + ).toBeVisible(); - await addAProductToCart( page, product1Id ); - await page.goto( testPage.slug ); - await expect( - page.getByRole( 'heading', { name: testPage.title } ) - ).toBeVisible(); - await expect( - page.getByRole( 'link', { name: simpleProductName, exact: true } ) - ).toBeVisible(); - await expect( page.getByText( simpleProductDesc ) ).toBeVisible(); - await expect( - page.getByText( `Save $${ singleProductSalePrice }` ) - ).toBeVisible(); + await addAProductToCart( page, product1Id ); + await page.goto( testPage.slug ); + await expect( + page.getByRole( 'heading', { name: testPage.title } ) + ).toBeVisible(); + await expect( + page.getByRole( 'link', { + name: simpleProductName, + exact: true, + } ) + ).toBeVisible(); + await expect( page.getByText( simpleProductDesc ) ).toBeVisible(); + await expect( + page.getByText( `Save $${ singleProductSalePrice }` ) + ).toBeVisible(); - // increase product quantity to its maximum - await expect( page.getByText( '2 left in stock' ) ).toBeVisible(); - await page - .getByRole( 'button' ) - .filter( { hasText: '+', exact: true } ) - .click(); - await expect( - page.locator( - '.wc-block-components-totals-footer-item > .wc-block-components-totals-item__value' - ) - ).toContainText( `$${ doubleProductsPrice.toString() }` ); - await expect( - page.getByRole( 'button' ).filter( { hasText: '+', exact: true } ) - ).toBeDisabled(); + // increase product quantity to its maximum + await expect( page.getByText( '2 left in stock' ) ).toBeVisible(); + await page + .getByRole( 'button' ) + .filter( { hasText: '+', exact: true } ) + .click(); + await expect( + page.locator( + '.wc-block-components-totals-footer-item > .wc-block-components-totals-item__value' + ) + ).toContainText( `$${ doubleProductsPrice.toString() }` ); + await expect( + page + .getByRole( 'button' ) + .filter( { hasText: '+', exact: true } ) + ).toBeDisabled(); - // add cross-sell products to cart - await expect( - page.getByRole( 'heading', { name: 'You may be interested in…' } ) - ).toBeVisible(); - await page - .getByLabel( `Add to cart: “${ simpleProductName } Cross-Sell 1”` ) - .click(); - await expect( - page - .locator( '.wc-block-cart-items' ) - .getByText( `${ simpleProductName } Cross-Sell 1` ) - ).toBeVisible(); - await page - .getByLabel( `Add to cart: “${ simpleProductName } Cross-Sell 2”` ) - .click(); - await expect( - page - .locator( '.wc-block-cart-items' ) - .getByText( `${ simpleProductName } Cross-Sell 2` ) - ).toBeVisible(); + // add cross-sell products to cart + await expect( + page.getByRole( 'heading', { + name: 'You may be interested in…', + } ) + ).toBeVisible(); + await page + .getByLabel( + `Add to cart: “${ simpleProductName } Cross-Sell 1”` + ) + .click(); + await expect( + page + .locator( '.wc-block-cart-items' ) + .getByText( `${ simpleProductName } Cross-Sell 1` ) + ).toBeVisible(); + await page + .getByLabel( + `Add to cart: “${ simpleProductName } Cross-Sell 2”` + ) + .click(); + await expect( + page + .locator( '.wc-block-cart-items' ) + .getByText( `${ simpleProductName } Cross-Sell 2` ) + ).toBeVisible(); - await page.goto( testPage.slug ); - await expect( - page.getByRole( 'heading', { name: testPage.title } ) - ).toBeVisible(); - await expect( - page.getByRole( 'heading', { name: 'You may be interested in…' } ) - ).toBeHidden(); - await expect( - page.locator( - '.wc-block-components-totals-footer-item > .wc-block-components-totals-item__value' - ) - ).toContainText( - `$${ singleProductWithCrossSellProducts.toString() }` - ); + await page.goto( testPage.slug ); + await expect( + page.getByRole( 'heading', { name: testPage.title } ) + ).toBeVisible(); + await expect( + page.getByRole( 'heading', { + name: 'You may be interested in…', + } ) + ).toBeHidden(); + await expect( + page.locator( + '.wc-block-components-totals-footer-item > .wc-block-components-totals-item__value' + ) + ).toContainText( + `$${ singleProductWithCrossSellProducts.toString() }` + ); - // remove cross-sell products from cart - await page.locator( ':nth-match(:text("Remove item"), 3)' ).click(); - await page.locator( ':nth-match(:text("Remove item"), 2)' ).click(); - await expect( - page.getByRole( 'heading', { name: 'You may be interested in…' } ) - ).toBeVisible(); + // remove cross-sell products from cart + await page.locator( ':nth-match(:text("Remove item"), 3)' ).click(); + await page.locator( ':nth-match(:text("Remove item"), 2)' ).click(); + await expect( + page.getByRole( 'heading', { + name: 'You may be interested in…', + } ) + ).toBeVisible(); - // check if the link to proceed to the checkout exists - await expect( - page.getByRole( 'link', { - name: 'Proceed to Checkout', - } ) - ).toBeVisible(); + // check if the link to proceed to the checkout exists + await expect( + page.getByRole( 'link', { + name: 'Proceed to Checkout', + } ) + ).toBeVisible(); - // remove product from cart - await page.locator( ':text("Remove item")' ).click(); - await expect( - page.getByText( 'Your cart is currently empty!' ) - ).toBeVisible(); - await expect( - page.getByRole( 'link', { name: 'Browse store' } ) - ).toBeVisible(); - } ); + // remove product from cart + await page.locator( ':text("Remove item")' ).click(); + await expect( + page.getByText( 'Your cart is currently empty!' ) + ).toBeVisible(); + await expect( + page.getByRole( 'link', { name: 'Browse store' } ) + ).toBeVisible(); + } + ); } ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-calculate-shipping.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-calculate-shipping.spec.js index 62be5e273a3..4f3d0026b88 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-calculate-shipping.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-calculate-shipping.spec.js @@ -125,108 +125,120 @@ test.describe( } ); } ); - test( 'allows customer to calculate Free Shipping if in Germany', async ( { - page, - } ) => { - await page.goto( '/cart/' ); - // Set shipping country to Germany - await page.locator( 'a.shipping-calculator-button' ).click(); - await page - .locator( '#calc_shipping_country' ) - .selectOption( shippingCountryDE ); - await page.locator( 'button[name="calc_shipping"]' ).click(); + test( + 'allows customer to calculate Free Shipping if in Germany', + { tag: [ '@could-be-unit-test' ] }, + async ( { page } ) => { + await page.goto( '/cart/' ); + // Set shipping country to Germany + await page.locator( 'a.shipping-calculator-button' ).click(); + await page + .locator( '#calc_shipping_country' ) + .selectOption( shippingCountryDE ); + await page.locator( 'button[name="calc_shipping"]' ).click(); - // Verify shipping costs - await expect( - page.locator( '.shipping ul#shipping_method > li' ) - ).toContainText( 'Free shipping' ); - await expect( - page.locator( '.order-total .amount' ) - ).toContainText( firstProductPrice ); - } ); + // Verify shipping costs + await expect( + page.locator( '.shipping ul#shipping_method > li' ) + ).toContainText( 'Free shipping' ); + await expect( + page.locator( '.order-total .amount' ) + ).toContainText( firstProductPrice ); + } + ); - test( 'allows customer to calculate Flat rate and Local pickup if in France', async ( { - page, - } ) => { - await page.goto( '/cart/' ); - // Set shipping country to France - await page.locator( 'a.shipping-calculator-button' ).click(); - await page - .locator( '#calc_shipping_country' ) - .selectOption( shippingCountryFR ); - await page.locator( 'button[name="calc_shipping"]' ).click(); + test( + 'allows customer to calculate Flat rate and Local pickup if in France', + { tag: [ '@could-be-unit-test' ] }, + async ( { page } ) => { + await page.goto( '/cart/' ); + // Set shipping country to France + await page.locator( 'a.shipping-calculator-button' ).click(); + await page + .locator( '#calc_shipping_country' ) + .selectOption( shippingCountryFR ); + await page.locator( 'button[name="calc_shipping"]' ).click(); - // Verify shipping costs - await expect( page.locator( '.shipping .amount' ) ).toContainText( - '$5.00' - ); - await expect( - page.locator( '.order-total .amount' ) - ).toContainText( `$${ firstProductWithFlatRate }` ); + // Verify shipping costs + await expect( + page.locator( '.shipping .amount' ) + ).toContainText( '$5.00' ); + await expect( + page.locator( '.order-total .amount' ) + ).toContainText( `$${ firstProductWithFlatRate }` ); - // Set shipping to local pickup instead of flat rate - await page.locator( 'text=Local pickup' ).click(); + // Set shipping to local pickup instead of flat rate + await page.locator( 'text=Local pickup' ).click(); - // Verify updated shipping costs - await expect( - page.locator( '.order-total .amount' ).first() - ).toContainText( `$${ firstProductPrice }` ); - } ); + // Verify updated shipping costs + await expect( + page.locator( '.order-total .amount' ).first() + ).toContainText( `$${ firstProductPrice }` ); + } + ); - test( 'should show correct total cart price after updating quantity', async ( { - page, - } ) => { - await page.goto( '/cart/' ); - await page.locator( 'input.qty' ).fill( '4' ); - await page.locator( 'text=Update cart' ).click(); + test( + 'should show correct total cart price after updating quantity', + { tag: [ '@could-be-unit-test' ] }, + async ( { page } ) => { + await page.goto( '/cart/' ); + await page.locator( 'input.qty' ).fill( '4' ); + await page.locator( 'text=Update cart' ).click(); - // Set shipping country to France - await page.locator( 'a.shipping-calculator-button' ).click(); - await page - .locator( '#calc_shipping_country' ) - .selectOption( shippingCountryFR ); - await page.locator( 'button[name="calc_shipping"]' ).click(); + // Set shipping country to France + await page.locator( 'a.shipping-calculator-button' ).click(); + await page + .locator( '#calc_shipping_country' ) + .selectOption( shippingCountryFR ); + await page.locator( 'button[name="calc_shipping"]' ).click(); - await expect( - page.locator( '.order-total .amount' ) - ).toContainText( `$${ fourProductsWithFlatRate }` ); - } ); + await expect( + page.locator( '.order-total .amount' ) + ).toContainText( `$${ fourProductsWithFlatRate }` ); + } + ); - test( 'should show correct total cart price with 2 products and flat rate', async ( { - page, - } ) => { - await addAProductToCart( page, secondProductId ); + test( + 'should show correct total cart price with 2 products and flat rate', + { tag: [ '@could-be-unit-test' ] }, + async ( { page } ) => { + await addAProductToCart( page, secondProductId ); - await page.goto( '/cart/' ); - await page.locator( 'a.shipping-calculator-button' ).click(); - await page - .locator( '#calc_shipping_country' ) - .selectOption( shippingCountryFR ); - await page.locator( 'button[name="calc_shipping"]' ).click(); + await page.goto( '/cart/' ); + await page.locator( 'a.shipping-calculator-button' ).click(); + await page + .locator( '#calc_shipping_country' ) + .selectOption( shippingCountryFR ); + await page.locator( 'button[name="calc_shipping"]' ).click(); - await expect( page.locator( '.shipping .amount' ) ).toContainText( - '$5.00' - ); - await expect( - page.locator( '.order-total .amount' ) - ).toContainText( `$${ twoProductsWithFlatRate }` ); - } ); + await expect( + page.locator( '.shipping .amount' ) + ).toContainText( '$5.00' ); + await expect( + page.locator( '.order-total .amount' ) + ).toContainText( `$${ twoProductsWithFlatRate }` ); + } + ); - test( 'should show correct total cart price with 2 products without flat rate', async ( { - page, - } ) => { - await addAProductToCart( page, secondProductId ); + test( + 'should show correct total cart price with 2 products without flat rate', + { tag: [ '@could-be-unit-test' ] }, + async ( { page } ) => { + await addAProductToCart( page, secondProductId ); - // Set shipping country to Spain - await page.goto( '/cart/' ); - await page.locator( 'a.shipping-calculator-button' ).click(); - await page.locator( '#calc_shipping_country' ).selectOption( 'ES' ); - await page.locator( 'button[name="calc_shipping"]' ).click(); + // Set shipping country to Spain + await page.goto( '/cart/' ); + await page.locator( 'a.shipping-calculator-button' ).click(); + await page + .locator( '#calc_shipping_country' ) + .selectOption( 'ES' ); + await page.locator( 'button[name="calc_shipping"]' ).click(); - // Verify shipping costs - await expect( - page.locator( '.order-total .amount' ) - ).toContainText( `$${ twoProductsTotal }` ); - } ); + // Verify shipping costs + await expect( + page.locator( '.order-total .amount' ) + ).toContainText( `$${ twoProductsTotal }` ); + } + ); } ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-checkout-block-calculate-tax.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-checkout-block-calculate-tax.spec.js index f449ebe1b3a..eb189618973 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-checkout-block-calculate-tax.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-checkout-block-calculate-tax.spec.js @@ -38,7 +38,7 @@ let productId, test.describe( 'Shopper Cart & Checkout Block Tax Display', - { tag: [ '@payments', '@services', '@hpos' ] }, + { tag: [ '@payments', '@services', '@hpos', '@could-be-unit-test' ] }, () => { test.use( { storageState: process.env.ADMINSTATE } ); test.beforeAll( async ( { baseURL } ) => { @@ -240,7 +240,7 @@ test.describe( test.describe( 'Shopper Cart & Checkout Block Tax Rounding', - { tag: [ '@payments', '@services' ] }, + { tag: [ '@payments', '@services', '@could-be-unit-test' ] }, () => { test.beforeAll( async ( { baseURL } ) => { const api = new wcApi( { @@ -484,7 +484,7 @@ test.describe( test.describe( 'Shopper Cart & Checkout Block Tax Levels', - { tag: [ '@payments', '@services' ] }, + { tag: [ '@payments', '@services', '@could-be-unit-test' ] }, () => { test.beforeAll( async ( { baseURL } ) => { const api = new wcApi( { @@ -809,7 +809,7 @@ test.describe( test.describe( 'Shipping Cart & Checkout Block Tax', - { tag: [ '@payments', '@services' ] }, + { tag: [ '@payments', '@services', '@could-be-unit-test' ] }, () => { test.beforeAll( async ( { baseURL } ) => { const api = new wcApi( { diff --git a/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-checkout-calculate-tax.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-checkout-calculate-tax.spec.js index bfcc1218a69..f5f1b416487 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-checkout-calculate-tax.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-checkout-calculate-tax.spec.js @@ -24,7 +24,7 @@ let productId, test.describe.serial( 'Tax rates in the cart and checkout', - { tag: [ '@payments', '@services', '@hpos' ] }, + { tag: [ '@payments', '@services', '@hpos', '@could-be-unit-test' ] }, () => { test.beforeAll( async ( { baseURL } ) => { const api = new wcApi( { diff --git a/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-checkout-coupons.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-checkout-coupons.spec.js index 813b3623236..6301a750e90 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-checkout-coupons.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-checkout-coupons.spec.js @@ -306,60 +306,65 @@ test.describe( } ); } ); - test( 'restores total when coupons are removed', async ( { - page, - context, - } ) => { - await test.step( 'Load cart page and try restoring total when removed coupons', async () => { - await addAProductToCart( page, firstProductId ); + test( + 'restores total when coupons are removed', + { tag: [ '@could-be-unit-test' ] }, + async ( { page, context } ) => { + await test.step( 'Load cart page and try restoring total when removed coupons', async () => { + await addAProductToCart( page, firstProductId ); - await page.goto( '/cart/' ); - await page.locator( '#coupon_code' ).fill( coupons[ 0 ].code ); - await page - .getByRole( 'button', { name: 'Apply coupon' } ) - .click(); + await page.goto( '/cart/' ); + await page + .locator( '#coupon_code' ) + .fill( coupons[ 0 ].code ); + await page + .getByRole( 'button', { name: 'Apply coupon' } ) + .click(); - // confirm numbers - await expect( - page.locator( '.cart-discount .amount' ) - ).toContainText( discounts[ 0 ] ); - await expect( - page.locator( '.order-total .amount' ) - ).toContainText( totals[ 0 ] ); + // confirm numbers + await expect( + page.locator( '.cart-discount .amount' ) + ).toContainText( discounts[ 0 ] ); + await expect( + page.locator( '.order-total .amount' ) + ).toContainText( totals[ 0 ] ); - await page.locator( 'a.woocommerce-remove-coupon' ).click(); + await page.locator( 'a.woocommerce-remove-coupon' ).click(); - await expect( - page.locator( '.order-total .amount' ) - ).toContainText( '$20.00' ); - } ); + await expect( + page.locator( '.order-total .amount' ) + ).toContainText( '$20.00' ); + } ); - await context.clearCookies(); + await context.clearCookies(); - await test.step( 'Load checkout page and try restoring total when removed coupons', async () => { - await addAProductToCart( page, firstProductId ); + await test.step( 'Load checkout page and try restoring total when removed coupons', async () => { + await addAProductToCart( page, firstProductId ); - await page.goto( '/checkout/' ); - await page - .locator( 'text=Click here to enter your code' ) - .click(); - await page.locator( '#coupon_code' ).fill( coupons[ 0 ].code ); - await page.locator( 'text=Apply coupon' ).click(); + await page.goto( '/checkout/' ); + await page + .locator( 'text=Click here to enter your code' ) + .click(); + await page + .locator( '#coupon_code' ) + .fill( coupons[ 0 ].code ); + await page.locator( 'text=Apply coupon' ).click(); - // confirm numbers - await expect( - page.locator( '.cart-discount .amount' ) - ).toContainText( discounts[ 0 ] ); - await expect( - page.locator( '.order-total .amount' ) - ).toContainText( totals[ 0 ] ); + // confirm numbers + await expect( + page.locator( '.cart-discount .amount' ) + ).toContainText( discounts[ 0 ] ); + await expect( + page.locator( '.order-total .amount' ) + ).toContainText( totals[ 0 ] ); - await page.locator( 'a.woocommerce-remove-coupon' ).click(); + await page.locator( 'a.woocommerce-remove-coupon' ).click(); - await expect( - page.locator( '.order-total .amount' ) - ).toContainText( '$20.00' ); - } ); - } ); + await expect( + page.locator( '.order-total .amount' ) + ).toContainText( '$20.00' ); + } ); + } + ); } ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-checkout-restricted-coupons.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-checkout-restricted-coupons.spec.js index a67bdadb715..1fed03600c7 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-checkout-restricted-coupons.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-checkout-restricted-coupons.spec.js @@ -8,9 +8,20 @@ const excludedProductName = 'Excluded test product'; const includedCategoryName = 'Included Category'; const excludedCategoryName = 'Excluded Category'; +// This applies a coupon and waits for the result to prevent flakyness. +const applyCoupon = async ( page ) => { + const responsePromise = page.waitForResponse( + ( response ) => + response.url().includes( '?wc-ajax=apply_coupon' ) && + response.status() === 200 + ); + await page.getByRole( 'button', { name: 'Apply coupon' } ).click(); + await responsePromise; +}; + test.describe( 'Cart & Checkout Restricted Coupons', - { tag: [ '@payments', '@services', '@hpos' ] }, + { tag: [ '@payments', '@services', '@hpos', '@could-be-unit-test' ] }, () => { let firstProductId, secondProductId, @@ -195,9 +206,7 @@ test.describe( await page .getByPlaceholder( 'Coupon code' ) .fill( 'expired-coupon' ); - await page - .getByRole( 'button', { name: 'Apply coupon' } ) - .click(); + await applyCoupon( page ); await expect( page.getByText( 'This coupon has expired.' ) ).toBeVisible(); @@ -217,9 +226,7 @@ test.describe( await page .getByPlaceholder( 'Coupon code' ) .fill( 'expired-coupon' ); - await page - .getByRole( 'button', { name: 'Apply coupon' } ) - .click(); + await applyCoupon( page ); await expect( page.getByText( 'This coupon has expired.' ) ).toBeVisible(); @@ -237,9 +244,7 @@ test.describe( await page .getByPlaceholder( 'Coupon code' ) .fill( 'min-max-spend-individual' ); - await page - .getByRole( 'button', { name: 'Apply coupon' } ) - .click(); + await applyCoupon( page ); // failed because we need to have at least $50 in cart (single product is only $20) await expect( page.getByText( @@ -255,9 +260,7 @@ test.describe( await page .getByPlaceholder( 'Coupon code' ) .fill( 'min-max-spend-individual' ); - await page - .getByRole( 'button', { name: 'Apply coupon' } ) - .click(); + await applyCoupon( page ); await expect( page.getByText( 'Coupon code applied successfully.' ) ).toBeVisible(); @@ -267,9 +270,7 @@ test.describe( await page .getByPlaceholder( 'Coupon code' ) .fill( 'no-sale-use-limit' ); - await page - .getByRole( 'button', { name: 'Apply coupon' } ) - .click(); + await applyCoupon( page ); await expect( page.getByText( 'Sorry, coupon "min-max-spend-individual" has already been applied and cannot be used in conjunction with other coupons.' @@ -291,9 +292,7 @@ test.describe( await page .getByPlaceholder( 'Coupon code' ) .fill( 'min-max-spend-individual' ); - await page - .getByRole( 'button', { name: 'Apply coupon' } ) - .click(); + await applyCoupon( page ); // failed because we need to have at least $50 in cart (single product is only $20) await expect( page.getByText( @@ -314,9 +313,7 @@ test.describe( await page .getByPlaceholder( 'Coupon code' ) .fill( 'min-max-spend-individual' ); - await page - .getByRole( 'button', { name: 'Apply coupon' } ) - .click(); + await applyCoupon( page ); await expect( page.getByText( 'Coupon code applied successfully.' ) ).toBeVisible(); @@ -331,9 +328,7 @@ test.describe( await page .getByPlaceholder( 'Coupon code' ) .fill( 'no-sale-use-limit' ); - await page - .getByRole( 'button', { name: 'Apply coupon' } ) - .click(); + await applyCoupon( page ); await expect( page.getByText( 'Sorry, coupon "min-max-spend-individual" has already been applied and cannot be used in conjunction with other coupons.' @@ -353,10 +348,8 @@ test.describe( await page .getByPlaceholder( 'Coupon code' ) .fill( 'no-sale-use-limit' ); - await page - .getByRole( 'button', { name: 'Apply coupon' } ) - .click(); - // failed because this product is on sale + await applyCoupon( page ); + // failed because this product is on sale. await expect( page.getByText( 'Sorry, this coupon is not valid for sale items.' @@ -378,9 +371,7 @@ test.describe( await page .getByPlaceholder( 'Coupon code' ) .fill( 'no-sale-use-limit' ); - await page - .getByRole( 'button', { name: 'Apply coupon' } ) - .click(); + await applyCoupon( page ); // failed because this product is on sale await expect( page.getByText( @@ -437,9 +428,7 @@ test.describe( await page .getByPlaceholder( 'Coupon code' ) .fill( 'no-sale-use-limit' ); - await page - .getByRole( 'button', { name: 'Apply coupon' } ) - .click(); + await applyCoupon( page ); // failed because this coupon code has been used too much await expect( page.getByText( @@ -462,9 +451,7 @@ test.describe( await page .getByPlaceholder( 'Coupon code' ) .fill( 'no-sale-use-limit' ); - await page - .getByRole( 'button', { name: 'Apply coupon' } ) - .click(); + await applyCoupon( page ); // failed because this coupon code has been used too much await expect( page.getByText( @@ -489,9 +476,7 @@ test.describe( await page .getByPlaceholder( 'Coupon code' ) .fill( 'product-and-category-included' ); - await page - .getByRole( 'button', { name: 'Apply coupon' } ) - .click(); + await applyCoupon( page ); // failed because this product is not included for coupon await expect( page.getByText( @@ -514,9 +499,7 @@ test.describe( await page .getByPlaceholder( 'Coupon code' ) .fill( 'product-and-category-included' ); - await page - .getByRole( 'button', { name: 'Apply coupon' } ) - .click(); + await applyCoupon( page ); // failed because this product is not included for coupon await expect( page.getByText( @@ -537,9 +520,7 @@ test.describe( await page .getByPlaceholder( 'Coupon code' ) .fill( 'product-and-category-included' ); - await page - .getByRole( 'button', { name: 'Apply coupon' } ) - .click(); + await applyCoupon( page ); // succeeded await expect( page.getByText( 'Coupon code applied successfully.' ) @@ -560,9 +541,7 @@ test.describe( await page .getByPlaceholder( 'Coupon code' ) .fill( 'product-and-category-included' ); - await page - .getByRole( 'button', { name: 'Apply coupon' } ) - .click(); + await applyCoupon( page ); // succeeded await expect( page.getByText( 'Coupon code applied successfully.' ) @@ -581,9 +560,7 @@ test.describe( await page .getByPlaceholder( 'Coupon code' ) .fill( 'product-and-category-included' ); - await page - .getByRole( 'button', { name: 'Apply coupon' } ) - .click(); + await applyCoupon( page ); // failed because this product is excluded from coupon await expect( page.getByText( @@ -606,9 +583,7 @@ test.describe( await page .getByPlaceholder( 'Coupon code' ) .fill( 'product-and-category-included' ); - await page - .getByRole( 'button', { name: 'Apply coupon' } ) - .click(); + await applyCoupon( page ); // failed because this product is excluded from coupon await expect( page.getByText( @@ -629,9 +604,7 @@ test.describe( await page .getByPlaceholder( 'Coupon code' ) .fill( 'product-and-category-included' ); - await page - .getByRole( 'button', { name: 'Apply coupon' } ) - .click(); + await applyCoupon( page ); // succeeded await expect( page.getByText( 'Coupon code applied successfully.' ) @@ -652,9 +625,7 @@ test.describe( await page .getByPlaceholder( 'Coupon code' ) .fill( 'product-and-category-included' ); - await page - .getByRole( 'button', { name: 'Apply coupon' } ) - .click(); + await applyCoupon( page ); // succeeded await expect( page.getByText( 'Coupon code applied successfully.' ) @@ -671,8 +642,7 @@ test.describe( await page .getByPlaceholder( 'Coupon code' ) .fill( 'email-restricted' ); - await page.getByRole( 'button', { name: 'Apply coupon' } ).click(); - + await applyCoupon( page ); await expect( page.getByText( 'Please enter a valid email at checkout to use coupon code "email-restricted".' @@ -710,8 +680,7 @@ test.describe( await page .getByPlaceholder( 'Coupon code' ) .fill( 'email-restricted' ); - await page.getByRole( 'button', { name: 'Apply coupon' } ).click(); - + await applyCoupon( page ); await expect( page.getByText( 'Please enter a valid email to use coupon code "email-restricted".' @@ -757,7 +726,7 @@ test.describe( await page .getByPlaceholder( 'Coupon code' ) .fill( 'email-restricted' ); - await page.getByRole( 'button', { name: 'Apply coupon' } ).click(); + await applyCoupon( page ); await expect( page.getByText( 'Coupon code applied successfully.' ) ).toBeVisible(); @@ -797,7 +766,7 @@ test.describe( await page .getByPlaceholder( 'Coupon code' ) .fill( 'email-restricted' ); - await page.getByRole( 'button', { name: 'Apply coupon' } ).click(); + await applyCoupon( page ); await expect( page.getByText( 'Coupon code applied successfully.' ) ).toBeVisible(); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-redirection.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-redirection.spec.js index 869a9cd0c5f..186b7965e3e 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-redirection.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-redirection.spec.js @@ -3,7 +3,7 @@ const wcApi = require( '@woocommerce/woocommerce-rest-api' ).default; test.describe( 'Cart > Redirect to cart from shop', - { tag: [ '@payments', '@services' ] }, + { tag: [ '@payments', '@services', '@not-e2e' ] }, () => { let productId; const productName = 'A redirect product test'; diff --git a/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart.spec.js index f1dcdeeebd5..58b7a58c68f 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart.spec.js @@ -81,100 +81,116 @@ test.describe( 'Cart page', { tag: [ '@payments', '@services' ] }, () => { await responsePromise; } - test( 'should display no item in the cart', async ( { page } ) => { - await page.goto( '/cart/' ); - await expect( - page.getByText( 'Your cart is currently empty.' ) - ).toBeVisible(); - } ); - - test( 'should add the product to the cart from the shop page', async ( { - page, - } ) => { - await goToShopPageAndAddProductToCart( page, productName ); - - await page.goto( '/cart/' ); - await expect( page.locator( 'td.product-name' ) ).toContainText( - productName - ); - } ); - - test( 'should increase item quantity when "Add to cart" of the same product is clicked', async ( { - page, - } ) => { - let qty = 2; - while ( qty-- ) { - await goToShopPageAndAddProductToCart( page, productName ); + test( + 'should display no item in the cart', + { tag: [ '@could-be-unit-test' ] }, + async ( { page } ) => { + await page.goto( '/cart/' ); + await expect( + page.getByText( 'Your cart is currently empty.' ) + ).toBeVisible(); } + ); - await page.goto( '/cart/' ); - await expect( page.locator( 'input.qty' ) ).toHaveValue( '2' ); - } ); + test( + 'should add the product to the cart from the shop page', + { tag: [ '@could-be-unit-test' ] }, + async ( { page } ) => { + await goToShopPageAndAddProductToCart( page, productName ); - test( 'should update quantity when updated via quantity input', async ( { - page, - } ) => { - await goToShopPageAndAddProductToCart( page, productName ); + await page.goto( '/cart/' ); + await expect( page.locator( 'td.product-name' ) ).toContainText( + productName + ); + } + ); - await page.goto( '/cart/' ); - await page.locator( 'input.qty' ).fill( '2' ); - await page.locator( 'text=Update cart' ).click(); + test( + 'should increase item quantity when "Add to cart" of the same product is clicked', + { tag: [ '@could-be-unit-test' ] }, + async ( { page } ) => { + let qty = 2; + while ( qty-- ) { + await goToShopPageAndAddProductToCart( page, productName ); + } - await expect( page.locator( '.order-total .amount' ) ).toContainText( - `$${ twoProductPrice }` - ); - } ); + await page.goto( '/cart/' ); + await expect( page.locator( 'input.qty' ) ).toHaveValue( '2' ); + } + ); - test( 'should remove the item from the cart when remove is clicked', async ( { - page, - } ) => { - await goToShopPageAndAddProductToCart( page, productName ); - await page.goto( '/cart/' ); + test( + 'should update quantity when updated via quantity input', + { tag: [ '@could-be-unit-test' ] }, + async ( { page } ) => { + await goToShopPageAndAddProductToCart( page, productName ); - // make sure that the product is in the cart - await expect( page.locator( '.order-total .amount' ) ).toContainText( - `$${ productPrice }` - ); + await page.goto( '/cart/' ); + await page.locator( 'input.qty' ).fill( '2' ); + await page.locator( 'text=Update cart' ).click(); - await page.locator( 'a.remove' ).click(); + await expect( + page.locator( '.order-total .amount' ) + ).toContainText( `$${ twoProductPrice }` ); + } + ); - await expect( - page.getByText( `“${ productName }” removed` ) - ).toBeVisible(); - await expect( - page.getByText( 'Your cart is currently empty' ) - ).toBeVisible(); - } ); + test( + 'should remove the item from the cart when remove is clicked', + { tag: [ '@could-be-unit-test' ] }, + async ( { page } ) => { + await goToShopPageAndAddProductToCart( page, productName ); + await page.goto( '/cart/' ); - test( 'should update subtotal in cart totals when adding product to the cart', async ( { - page, - } ) => { - await goToShopPageAndAddProductToCart( page, productName ); + // make sure that the product is in the cart + await expect( + page.locator( '.order-total .amount' ) + ).toContainText( `$${ productPrice }` ); - await page.goto( '/cart/' ); - await expect( page.locator( '.cart-subtotal .amount' ) ).toContainText( - `$${ productPrice }` - ); + await page.locator( 'a.remove' ).click(); - await page.locator( 'input.qty' ).fill( '2' ); - await page.locator( 'text=Update cart' ).click(); + await expect( + page.getByText( `“${ productName }” removed` ) + ).toBeVisible(); + await expect( + page.getByText( 'Your cart is currently empty' ) + ).toBeVisible(); + } + ); - await expect( page.locator( '.order-total .amount' ) ).toContainText( - `$${ twoProductPrice }` - ); - } ); + test( + 'should update subtotal in cart totals when adding product to the cart', + { tag: [ '@could-be-unit-test' ] }, + async ( { page } ) => { + await goToShopPageAndAddProductToCart( page, productName ); - test( 'should go to the checkout page when "Proceed to Checkout" is clicked', async ( { - page, - } ) => { - await goToShopPageAndAddProductToCart( page, productName ); + await page.goto( '/cart/' ); + await expect( + page.locator( '.cart-subtotal .amount' ) + ).toContainText( `$${ productPrice }` ); - await page.goto( '/cart/' ); + await page.locator( 'input.qty' ).fill( '2' ); + await page.locator( 'text=Update cart' ).click(); - await page.locator( '.checkout-button' ).click(); + await expect( + page.locator( '.order-total .amount' ) + ).toContainText( `$${ twoProductPrice }` ); + } + ); - await expect( page.locator( '#order_review' ) ).toBeVisible(); - } ); + test( + 'should go to the checkout page when "Proceed to Checkout" is clicked', + { tag: [ '@could-be-unit-test' ] }, + async ( { page } ) => { + await goToShopPageAndAddProductToCart( page, productName ); + + await page.goto( '/cart/' ); + + await page.locator( '.checkout-button' ).click(); + + await expect( page.locator( '#order_review' ) ).toBeVisible(); + } + ); test( 'can manage cross-sell products and maximum item quantity', async ( { page, diff --git a/plugins/woocommerce/tests/e2e-pw/tests/shopper/launch-your-store.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/shopper/launch-your-store.spec.js index 0953328ce3a..5f85238a598 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/shopper/launch-your-store.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/shopper/launch-your-store.spec.js @@ -72,51 +72,43 @@ async function runComingSoonTests( themeContext = '' ) { } ); } -test.describe( - 'Launch Your Store front end - logged out', - { tag: [ '@payments', '@services' ] }, - () => { - test.afterAll( async ( { baseURL } ) => { - try { - await setOption( - request, - baseURL, - 'woocommerce_coming_soon', - 'no' - ); - } catch ( error ) { - console.log( error ); - } - } ); - - test.describe( 'Block Theme (Twenty Twenty Four)', () => { - test.beforeAll( async () => { - await activateTheme( 'twentytwentyfour' ); - } ); - - test.afterAll( async () => { - // Reset theme to the default. - await activateTheme( DEFAULT_THEME ); - } ); - - runComingSoonTests( test.step, test.use ); - } ); - - test.describe( 'Classic Theme (Storefront)', () => { - test.beforeAll( async () => { - await activateTheme( 'storefront' ); - } ); - - test.afterAll( async () => { - // Reset theme to the default. - await activateTheme( DEFAULT_THEME ); - } ); - - runComingSoonTests( - test.step, - test.use, - 'Classic Theme (Storefront)' +test.describe( 'Launch Your Store front end - logged out', () => { + test.afterAll( async ( { baseURL } ) => { + try { + await setOption( + request, + baseURL, + 'woocommerce_coming_soon', + 'no' ); + } catch ( error ) { + console.log( error ); + } + } ); + + test.describe( 'Block Theme (Twenty Twenty Four)', () => { + test.beforeAll( async () => { + await activateTheme( 'twentytwentyfour' ); } ); - } -); + + test.afterAll( async () => { + // Reset theme to the default. + await activateTheme( DEFAULT_THEME ); + } ); + + runComingSoonTests( test.step, test.use ); + } ); + + test.describe( 'Classic Theme (Storefront)', () => { + test.beforeAll( async () => { + await activateTheme( 'storefront' ); + } ); + + test.afterAll( async () => { + // Reset theme to the default. + await activateTheme( DEFAULT_THEME ); + } ); + + runComingSoonTests( test.step, test.use, 'Classic Theme (Storefront)' ); + } ); +} ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/shopper/mini-cart.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/shopper/mini-cart.spec.js index 573510cac3d..502c1c2068a 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/shopper/mini-cart.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/shopper/mini-cart.spec.js @@ -2,6 +2,7 @@ const { test, expect } = require( '@playwright/test' ); const { disableWelcomeModal, openEditorSettings, + getCanvas, } = require( '../../utils/editor' ); const wcApi = require( '@woocommerce/woocommerce-rest-api' ).default; const { random } = require( '../../utils/helpers' ); @@ -136,11 +137,13 @@ test.describe( await disableWelcomeModal( { page } ); + const canvas = await getCanvas( page ); + // add page title and mini cart block - await page + await canvas .getByRole( 'textbox', { name: 'Add title' } ) .fill( miniCartPageTitle ); - await page.getByLabel( 'Add block' ).click(); + await canvas.getByLabel( 'Add block' ).click(); await page .getByLabel( 'Search for blocks and patterns' ) .fill( '/mini cart' ); @@ -148,13 +151,15 @@ test.describe( .getByRole( 'option' ) .filter( { hasText: 'Mini-Cart' } ) .click(); - await expect( page.getByLabel( 'Block: Mini-Cart' ) ).toBeVisible(); + await expect( + canvas.getByLabel( 'Block: Mini-Cart' ) + ).toBeVisible(); // Open Settings sidebar if closed await openEditorSettings( { page } ); // customize mini cart block - await page.getByLabel( 'Block: Mini-Cart' ).click(); + await canvas.getByLabel( 'Block: Mini-Cart' ).click(); // display total price await page.getByLabel( 'Display total price' ).click(); // open drawer when a product diff --git a/plugins/woocommerce/tests/e2e-pw/tests/shopper/product-tags-attributes.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/shopper/product-tags-attributes.spec.js index acb5651b7af..5221f51f19c 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/shopper/product-tags-attributes.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/shopper/product-tags-attributes.spec.js @@ -1,7 +1,7 @@ const { test, expect, request } = require( '@playwright/test' ); const { admin } = require( '../../test-data/data' ); const pageTitle = 'Product Showcase'; -const { goToPageEditor } = require( '../../utils/editor' ); +const { goToPageEditor, getCanvas } = require( '../../utils/editor' ); const wcApi = require( '@woocommerce/woocommerce-rest-api' ).default; const singleProductPrice1 = '5.00'; @@ -282,15 +282,17 @@ test.describe( // create as a merchant a new page with Product Collection block await goToPageEditor( { page } ); - await page + const canvas = await getCanvas( page ); + + await canvas .getByRole( 'textbox', { name: 'Add Title' } ) .fill( pageTitle ); - await page + await canvas .getByRole( 'button', { name: 'Add default block' } ) .click(); - await page + await canvas .getByRole( 'document', { name: 'Empty block; start writing or type forward slash to choose a block', } ) @@ -298,7 +300,7 @@ test.describe( await page.keyboard.press( 'Enter' ); // Product Collection requires choosing some collection. - await page + await canvas .locator( '[data-type="woocommerce/product-collection"] .components-placeholder' ) @@ -331,7 +333,7 @@ test.describe( await expect( page.getByRole( 'heading', { name: pageTitle } ) ).toBeVisible(); - await expect( + expect( await page .getByRole( 'button', { name: 'Add to cart' } ) .count() diff --git a/plugins/woocommerce/tests/e2e-pw/tests/shopper/shop-title-after-deletion.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/shopper/shop-title-after-deletion.spec.js index d967cff9d13..5c361b1ed50 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/shopper/shop-title-after-deletion.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/shopper/shop-title-after-deletion.spec.js @@ -3,7 +3,7 @@ const { test, expect } = require( '@playwright/test' ); // test case for bug https://github.com/woocommerce/woocommerce/pull/46429 test.describe( 'Check the title of the shop page after the page has been deleted', - { tag: [ '@payments', '@services' ] }, + { tag: [ '@payments', '@services', '@could-be-unit-test' ] }, () => { test.use( { storageState: process.env.ADMINSTATE } ); test.beforeEach( async ( { page } ) => { diff --git a/plugins/woocommerce/tests/e2e-pw/tests/shopper/wordpress-post.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/shopper/wordpress-post.spec.js index 6a1baa7c95d..fd6f46e0497 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/shopper/wordpress-post.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/shopper/wordpress-post.spec.js @@ -6,7 +6,7 @@ const test = baseTest.extend( { test( 'logged-in customer can comment on a post', - { tag: [ '@gutenberg', '@payments', '@services' ] }, + { tag: [ '@non-critical' ] }, async ( { page } ) => { await page.goto( 'hello-world/' ); await expect( diff --git a/plugins/woocommerce/tests/e2e-pw/utils/editor.js b/plugins/woocommerce/tests/e2e-pw/utils/editor.js index 18c73af4714..d2cf9d6dfe3 100644 --- a/plugins/woocommerce/tests/e2e-pw/utils/editor.js +++ b/plugins/woocommerce/tests/e2e-pw/utils/editor.js @@ -39,21 +39,23 @@ const getCanvas = async ( page ) => { }; const goToPageEditor = async ( { page } ) => { - await page.goto( 'wp-admin/post-new.php?post_type=page' ); - await disableWelcomeModal( { page } ); - await page.waitForResponse( + const responsePromise = page.waitForResponse( ( response ) => response.url().includes( '//page' ) && response.status() === 200 ); + await page.goto( 'wp-admin/post-new.php?post_type=page' ); + await disableWelcomeModal( { page } ); + await responsePromise; }; const goToPostEditor = async ( { page } ) => { - await page.goto( 'wp-admin/post-new.php' ); - await disableWelcomeModal( { page } ); - await page.waitForResponse( + const responsePromise = page.waitForResponse( ( response ) => response.url().includes( '//single' ) && response.status() === 200 ); + await page.goto( 'wp-admin/post-new.php' ); + await disableWelcomeModal( { page } ); + await responsePromise; }; const fillPageTitle = async ( page, title ) => { @@ -104,7 +106,7 @@ const insertBlockByShortcut = async ( page, blockName ) => { ).toBeVisible(); await page.getByRole( 'option', { name: blockName, exact: true } ).click(); await expect( - page.getByLabel( `Block: ${ blockName }` ).first() + canvas.getByLabel( `Block: ${ blockName }` ).first() ).toBeVisible(); }; diff --git a/plugins/woocommerce/tests/legacy/unit-tests/cart/functions.php b/plugins/woocommerce/tests/legacy/unit-tests/cart/functions.php index 3e71605b21e..742e6a34cc9 100644 --- a/plugins/woocommerce/tests/legacy/unit-tests/cart/functions.php +++ b/plugins/woocommerce/tests/legacy/unit-tests/cart/functions.php @@ -141,21 +141,21 @@ class WC_Tests_Cart_Functions extends WC_Unit_Test_Case { $wp_button_class = esc_attr( wc_wp_theme_get_element_class_name( 'button' ) ? ' ' . wc_wp_theme_get_element_class_name( 'button' ) : '' ); $message = wc_add_to_cart_message( array( $product->get_id() => 1 ), false, true ); - $this->assertEquals( 'View cart “Dummy Product” has been added to your cart.', $message ); + $this->assertEquals( '“Dummy Product” has been added to your cart. View cart', $message ); $message = wc_add_to_cart_message( array( $product->get_id() => 3 ), false, true ); - $this->assertEquals( 'View cart “Dummy Product” has been added to your cart.', $message ); + $this->assertEquals( '“Dummy Product” has been added to your cart. View cart', $message ); $message = wc_add_to_cart_message( array( $product->get_id() => 1 ), true, true ); - $this->assertEquals( 'View cart “Dummy Product” has been added to your cart.', $message ); + $this->assertEquals( '“Dummy Product” has been added to your cart. View cart', $message ); $message = wc_add_to_cart_message( array( $product->get_id() => 3 ), true, true ); - $this->assertEquals( 'View cart 3 × “Dummy Product” have been added to your cart.', $message ); + $this->assertEquals( '3 × “Dummy Product” have been added to your cart. View cart', $message ); $message = wc_add_to_cart_message( $product->get_id(), false, true ); - $this->assertEquals( 'View cart “Dummy Product” has been added to your cart.', $message ); + $this->assertEquals( '“Dummy Product” has been added to your cart. View cart', $message ); $message = wc_add_to_cart_message( $product->get_id(), true, true ); - $this->assertEquals( 'View cart “Dummy Product” has been added to your cart.', $message ); + $this->assertEquals( '“Dummy Product” has been added to your cart. View cart', $message ); } } diff --git a/plugins/woocommerce/tests/legacy/unit-tests/customer/functions.php b/plugins/woocommerce/tests/legacy/unit-tests/customer/functions.php index c3c33acd987..0ebfa691475 100644 --- a/plugins/woocommerce/tests/legacy/unit-tests/customer/functions.php +++ b/plugins/woocommerce/tests/legacy/unit-tests/customer/functions.php @@ -46,7 +46,7 @@ class WC_Tests_Customer_Functions extends WC_Unit_Test_Case { $id = wc_create_new_customer( 'test@example.com', 'testuser', 'testpassword' ); $this->assertInstanceOf( 'WP_Error', $id ); - // Empty username. + // Empty email. $id = wc_create_new_customer( '', 'testuser', 'testpassword' ); $this->assertInstanceOf( 'WP_Error', $id ); @@ -58,20 +58,6 @@ class WC_Tests_Customer_Functions extends WC_Unit_Test_Case { $id = wc_create_new_customer( 'test2@example.com', 'testuser', 'testpassword' ); $this->assertInstanceOf( 'WP_Error', $id ); - // Username with auto-generation. - update_option( 'woocommerce_registration_generate_username', 'yes' ); - $id = wc_create_new_customer( 'fred@example.com', '', 'testpassword' ); - $userdata = get_userdata( $id ); - $this->assertEquals( 'fred', $userdata->user_login ); - $id = wc_create_new_customer( 'fred@mail.com', '', 'testpassword' ); - $userdata = get_userdata( $id ); - $this->assertNotEquals( 'fred', $userdata->user_login ); - $this->assertStringContainsString( 'fred', $userdata->user_login ); - $id = wc_create_new_customer( 'fred@test.com', '', 'testpassword' ); - $userdata = get_userdata( $id ); - $this->assertNotEquals( 'fred', $userdata->user_login ); - $this->assertStringContainsString( 'fred', $userdata->user_login ); - // Test extra arguments to generate display_name. $id = wc_create_new_customer( 'john.doe@example.com', @@ -85,13 +71,20 @@ class WC_Tests_Customer_Functions extends WC_Unit_Test_Case { $userdata = get_userdata( $id ); $this->assertEquals( 'John Doe', $userdata->display_name ); - // No password. - update_option( 'woocommerce_registration_generate_password', 'no' ); - $id = wc_create_new_customer( 'joe@example.com', 'joecustomer', '' ); - $this->assertInstanceOf( 'WP_Error', $id ); + // Username with auto-generation. + $id = wc_create_new_customer( 'fred@example.com', '', 'testpassword' ); + $userdata = get_userdata( $id ); + $this->assertEquals( 'fred', $userdata->user_login ); + $id = wc_create_new_customer( 'fred@mail.com', '', 'testpassword' ); + $userdata = get_userdata( $id ); + $this->assertNotEquals( 'fred', $userdata->user_login ); + $this->assertStringContainsString( 'fred', $userdata->user_login ); + $id = wc_create_new_customer( 'fred@test.com', '', 'testpassword' ); + $userdata = get_userdata( $id ); + $this->assertNotEquals( 'fred', $userdata->user_login ); + $this->assertStringContainsString( 'fred', $userdata->user_login ); // Auto-generated password. - update_option( 'woocommerce_registration_generate_password', 'yes' ); $id = wc_create_new_customer( 'joe@example.com', 'joecustomer', '' ); $this->assertTrue( is_numeric( $id ) && $id > 0 ); } diff --git a/plugins/woocommerce/tests/legacy/unit-tests/rest-api/Tests/Version2/system-status.php b/plugins/woocommerce/tests/legacy/unit-tests/rest-api/Tests/Version2/system-status.php index a8548813c13..58738a44f15 100644 --- a/plugins/woocommerce/tests/legacy/unit-tests/rest-api/Tests/Version2/system-status.php +++ b/plugins/woocommerce/tests/legacy/unit-tests/rest-api/Tests/Version2/system-status.php @@ -154,9 +154,8 @@ class WC_Tests_REST_System_Status_V2 extends WC_REST_Unit_Test_Case { * @since 3.0.0 */ public function test_get_system_status_info_active_plugins() { - $this->skip_on_php_8_1(); - wp_set_current_user( self::$administrator_user ); + delete_transient( 'wc_system_status_active_plugins' ); $actual_plugins = array( 'hello.php' ); update_option( 'active_plugins', $actual_plugins ); @@ -165,9 +164,20 @@ class WC_Tests_REST_System_Status_V2 extends WC_REST_Unit_Test_Case { $data = $response->get_data(); $plugins = (array) $data['active_plugins']; - $this->assertEquals( 1, count( $plugins ) ); - $this->assertEquals( 'Hello Dolly', $plugins[0]['name'] ); + + $plugin = reset( $plugins ); + $this->assertArrayHasKey( 'plugin', $plugin ); + $this->assertEquals( 'hello.php', $plugin['plugin'] ); + $this->assertArrayHasKey( 'name', $plugin ); + $this->assertEquals( 'Hello Dolly', $plugin['name'] ); + $this->assertArrayHasKey( 'version', $plugin ); + $this->assertArrayHasKey( 'version_latest', $plugin ); + $this->assertArrayHasKey( 'url', $plugin ); + $this->assertArrayHasKey( 'author_name', $plugin ); + $this->assertArrayHasKey( 'author_url', $plugin ); + $this->assertArrayHasKey( 'network_activated', $plugin ); + $this->assertEquals( false, $plugin['network_activated'] ); } /** diff --git a/plugins/woocommerce/tests/legacy/unit-tests/rest-api/Tests/Version3/system-status.php b/plugins/woocommerce/tests/legacy/unit-tests/rest-api/Tests/Version3/system-status.php index 54c81fea123..c78cb349c1c 100644 --- a/plugins/woocommerce/tests/legacy/unit-tests/rest-api/Tests/Version3/system-status.php +++ b/plugins/woocommerce/tests/legacy/unit-tests/rest-api/Tests/Version3/system-status.php @@ -183,6 +183,8 @@ class WC_Tests_REST_System_Status extends WC_REST_Unit_Test_Case { */ public function test_get_system_status_info_active_plugins() { wp_set_current_user( self::$administrator_user ); + delete_transient( 'wc_system_status_active_plugins' ); + $actual_plugins = array( 'hello.php' ); update_option( 'active_plugins', $actual_plugins ); $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v3/system_status' ) ); @@ -190,9 +192,20 @@ class WC_Tests_REST_System_Status extends WC_REST_Unit_Test_Case { $data = $response->get_data(); $plugins = (array) $data['active_plugins']; - $this->assertEquals( 1, count( $plugins ) ); - $this->assertEquals( 'Hello Dolly', $plugins[0]['name'] ); + + $plugin = reset( $plugins ); + $this->assertArrayHasKey( 'plugin', $plugin ); + $this->assertEquals( 'hello.php', $plugin['plugin'] ); + $this->assertArrayHasKey( 'name', $plugin ); + $this->assertEquals( 'Hello Dolly', $plugin['name'] ); + $this->assertArrayHasKey( 'version', $plugin ); + $this->assertArrayHasKey( 'version_latest', $plugin ); + $this->assertArrayHasKey( 'url', $plugin ); + $this->assertArrayHasKey( 'author_name', $plugin ); + $this->assertArrayHasKey( 'author_url', $plugin ); + $this->assertArrayHasKey( 'network_activated', $plugin ); + $this->assertEquals( false, $plugin['network_activated'] ); } /** diff --git a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/features/class-wc-tests-shipping-label-banner-display-rules.php b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/features/class-wc-tests-shipping-label-banner-display-rules.php index e0daecd2d80..7daefcc8777 100644 --- a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/features/class-wc-tests-shipping-label-banner-display-rules.php +++ b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/features/class-wc-tests-shipping-label-banner-display-rules.php @@ -12,13 +12,6 @@ use Automattic\WooCommerce\Internal\Admin\ShippingLabelBannerDisplayRules; */ class WC_Admin_Tests_Shipping_Label_Banner_Display_Rules extends WC_Unit_Test_Case { - /** - * Jetpack version to test the display manager. - * - * @var string - */ - private $valid_jetpack_version = '4.4'; - /** * Stores the default WordPress options stored in the database. * @@ -66,7 +59,6 @@ class WC_Admin_Tests_Shipping_Label_Banner_Display_Rules extends WC_Unit_Test_Ca /** * Test if the banner is displayed when all conditions are satisfied: * - Banner NOT dismissed - * - Jetpack >= 4.4 installed and active * - Jetpack Connected * - No incompatible extensions installed: * - Shipstation not installed @@ -76,13 +68,12 @@ class WC_Admin_Tests_Shipping_Label_Banner_Display_Rules extends WC_Unit_Test_Ca * - Order contains physical products which need to be shipped (we should check that the order status is not set to complete) * - Store is located in US * - Store currency is set to USD - * - WCS plugin not installed OR WCS is installed *AND* ToS have NOT been accepted *AND* WCS version is 1.22.5 or greater - * (The 1.22.5 or greater requirement is so we can launch the shipping modal from the banner) + * - WCS plugin not installed OR WCS is installed */ public function test_display_banner_if_all_conditions_are_met() { $this->with_order( - function( $that ) { - $shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( '4.4', true, '1.22.5', false, false ); + function ( $that ) { + $shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( true, null, false ); $that->assertEquals( $shipping_label_banner_display_rules->should_display_banner(), true ); } @@ -93,7 +84,7 @@ class WC_Admin_Tests_Shipping_Label_Banner_Display_Rules extends WC_Unit_Test_Ca * Test if the banner is hidden when Jetpack is not active. */ public function test_if_banner_hidden_when_jetpack_disconnected() { - $shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( null, null, null, null, null ); + $shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( null, null, null ); $this->assertEquals( $shipping_label_banner_display_rules->should_display_banner(), false ); } @@ -103,7 +94,7 @@ class WC_Admin_Tests_Shipping_Label_Banner_Display_Rules extends WC_Unit_Test_Ca */ public function test_if_banner_hidden_when_dismiss_option_enabled() { update_option( 'woocommerce_shipping_dismissed_timestamp', -1 ); - $shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( '4.4', true, '1.22.5', false, false ); + $shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( true, '1.22.5', false ); $this->assertEquals( $shipping_label_banner_display_rules->should_display_banner(), false ); } @@ -115,7 +106,7 @@ class WC_Admin_Tests_Shipping_Label_Banner_Display_Rules extends WC_Unit_Test_Ca $two_hours_from_ago = ( time() - 2 * 60 * 60 ) * 1000; update_option( 'woocommerce_shipping_dismissed_timestamp', $two_hours_from_ago ); - $shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( '4.4', true, '1.22.5', false, false ); + $shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( true, '1.22.5', false ); $this->assertEquals( $shipping_label_banner_display_rules->should_display_banner(), false ); } @@ -128,8 +119,8 @@ class WC_Admin_Tests_Shipping_Label_Banner_Display_Rules extends WC_Unit_Test_Ca update_option( 'woocommerce_shipping_dismissed_timestamp', $twenty_four_hours_one_sec_ago ); $this->with_order( - function( $that ) { - $shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( '4.4', true, '1.22.5', false, false ); + function ( $that ) { + $shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( true, null, false, false ); $that->assertEquals( $shipping_label_banner_display_rules->should_display_banner(), true ); } @@ -140,7 +131,7 @@ class WC_Admin_Tests_Shipping_Label_Banner_Display_Rules extends WC_Unit_Test_Ca * Test if the banner is hidden when no shippable product available. */ public function test_if_banner_hidden_when_no_shippable_product() { - $shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( '4.4', true, '1.22.5', false, false ); + $shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( true, '1.22.5', false ); $this->assertEquals( $shipping_label_banner_display_rules->should_display_banner(), false ); } @@ -151,8 +142,8 @@ class WC_Admin_Tests_Shipping_Label_Banner_Display_Rules extends WC_Unit_Test_Ca public function test_if_banner_hidden_when_store_is_not_in_us() { update_option( 'woocommerce_default_country', 'ES' ); $this->with_order( - function( $that ) { - $shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( '4.4', true, '1.22.5', false, false ); + function ( $that ) { + $shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( true, '1.22.5', false, false ); $that->assertEquals( $shipping_label_banner_display_rules->should_display_banner(), false ); } @@ -165,8 +156,8 @@ class WC_Admin_Tests_Shipping_Label_Banner_Display_Rules extends WC_Unit_Test_Ca public function test_if_banner_hidden_when_currency_is_not_usd() { update_option( 'woocommerce_currency', 'EUR' ); $this->with_order( - function( $that ) { - $shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( '4.4', true, '1.22.5', false, false ); + function ( $that ) { + $shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( true, '1.22.5', false ); $that->assertEquals( $shipping_label_banner_display_rules->should_display_banner(), false ); } @@ -178,34 +169,8 @@ class WC_Admin_Tests_Shipping_Label_Banner_Display_Rules extends WC_Unit_Test_Ca */ public function test_if_banner_hidden_when_incompatible_plugin_installed() { $this->with_order( - function( $that ) { - $shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( '4.4', true, '1.22.5', false, true ); - - $that->assertEquals( $shipping_label_banner_display_rules->should_display_banner(), false ); - } - ); - } - - /** - * Test if the banner is hidden when Jetpack version is not at least 4.4. - */ - public function test_if_banner_hidden_when_jetpack_version_is_old() { - $this->with_order( - function( $that ) { - $shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( '4.3', true, '1.22.5', false, false ); - - $that->assertEquals( $shipping_label_banner_display_rules->should_display_banner(), false ); - } - ); - } - - /** - * Test if the banner is hidden when the WooCommerce Shipping & Tax Terms of Service has been already accepted. - */ - public function test_if_banner_hidden_when_wcs_tos_accepted() { - $this->with_order( - function( $that ) { - $shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( '4.4', true, '1.22.5', true, false ); + function ( $that ) { + $shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( true, '1.22.5', false, true ); $that->assertEquals( $shipping_label_banner_display_rules->should_display_banner(), false ); } @@ -217,8 +182,8 @@ class WC_Admin_Tests_Shipping_Label_Banner_Display_Rules extends WC_Unit_Test_Ca */ public function test_if_banner_hidden_when_wcs_not_installed() { $this->with_order( - function( $that ) { - $shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( '4.4', true, '1.22.4', false, false ); + function ( $that ) { + $shipping_label_banner_display_rules = new ShippingLabelBannerDisplayRules( true, '1.22.4', false ); $that->assertEquals( $shipping_label_banner_display_rules->should_display_banner(), false ); } @@ -230,20 +195,10 @@ class WC_Admin_Tests_Shipping_Label_Banner_Display_Rules extends WC_Unit_Test_Ca */ private function create_order() { $product = WC_Helper_Product::create_simple_product(); - $product->set_props( array( 'virtual' => true ) ); - - $order = new WC_Order(); - $order_item = new WC_Order_Item_Product(); - $order_item->set_props( array( 'product' => $product ) ); - $order->add_item( $order_item ); - $order->save(); - - global $post; - - // phpcs:disable WordPress.WP.GlobalVariablesOverride.Prohibited - $post = new \stdClass(); - $post->ID = $order->get_id(); + $order = WC_Helper_Order::create_order( 1, $product ); + global $theorder; + $theorder = $order; return $order; } @@ -274,5 +229,4 @@ class WC_Admin_Tests_Shipping_Label_Banner_Display_Rules extends WC_Unit_Test_Ca $this->destroy_order( $order ); } - } diff --git a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/features/onboarding-tasks/task-lists.php b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/features/onboarding-tasks/task-lists.php index e37548dac5b..175d41d3aab 100644 --- a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/features/onboarding-tasks/task-lists.php +++ b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/features/onboarding-tasks/task-lists.php @@ -10,8 +10,9 @@ */ require_once __DIR__ . '/test-task.php'; -use Automattic\WooCommerce\Admin\Features\OnboardingTasks\TaskList; use Automattic\WooCommerce\Admin\Features\OnboardingTasks\TaskLists; +use Automattic\WooCommerce\Admin\Features\OnboardingTasks\TaskList; +use Automattic\WooCommerce\Admin\Features\OnboardingTasks\Task; /** * Class WC_Tests_OnboardingTasks_TaskLists @@ -42,8 +43,8 @@ class WC_Tests_OnboardingTasks_TaskLists extends WC_Unit_Test_Case { // Filter the default task lists. add_filter( 'woocommerce_admin_experimental_onboarding_tasklists', - function( - $task_lists + function ( + $task_lists ) { $this->assertIsArray( $task_lists ); @@ -138,4 +139,58 @@ class WC_Tests_OnboardingTasks_TaskLists extends WC_Unit_Test_Case { // Assert we have the task we added. $this->assertEquals( 'wc-unit-test_tasklists_get_json_visible_list', $json['tasks'][0]['id'] ); } + + /** + * Tests the setup_tasks_remaining method. + */ + public function test_setup_tasks_remaining() { + // Initialize the default task lists. + TaskLists::add_list( + array( + 'id' => 'setup', + 'title' => 'Setup', + 'tasks' => array(), + 'display_progress_header' => true, + 'event_prefix' => 'tasklist_', + 'options' => array( + 'use_completed_title' => true, + ), + 'visible' => true, + ) + ); + + $setup_list = TaskLists::get_list( 'setup' ); + + for ( $i = 1; $i <= 3; $i++ ) { + TaskLists::add_task( + 'setup', + new TestTask( + $setup_list, + array( + 'id' => "setup-task-{$i}", + ) + ) + ); + } + + // Test when no tasks are completed. + $this->assertEquals( 3, TaskLists::setup_tasks_remaining() ); + + // Complete one task. + update_option( Task::COMPLETED_OPTION, array( 'setup-task-1' ) ); + $this->assertEquals( 2, TaskLists::setup_tasks_remaining() ); + + // Complete all tasks. + update_option( Task::COMPLETED_OPTION, array( 'setup-task-1', 'setup-task-2', 'setup-task-3' ) ); + $this->assertEquals( 0, TaskLists::setup_tasks_remaining() ); + + // Test when the setup list is hidden. + $setup_list->hide(); + $this->assertNull( TaskLists::setup_tasks_remaining() ); + + // Test when the setup list has been previously completed. + $setup_list->unhide(); + update_option( TaskList::COMPLETED_OPTION, array( 'setup' ) ); + $this->assertNull( TaskLists::setup_tasks_remaining() ); + } } diff --git a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/features/onboarding-tasks/tasks/woocommerce-payments.php b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/features/onboarding-tasks/tasks/woocommerce-payments.php new file mode 100644 index 00000000000..dc4e1e3e28b --- /dev/null +++ b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/features/onboarding-tasks/tasks/woocommerce-payments.php @@ -0,0 +1,169 @@ +task = new WooCommercePayments( new TaskList() ); + $this->fake_gateway = new Fake_WC_Payments_Gateway(); + } + + /** + * Teardown after each test. + */ + public function tearDown(): void { + parent::tearDown(); + remove_filter( 'woocommerce_payment_gateways', array( $this, 'inject_fake_gateway' ) ); + } + + /** + * Test is_connected method when WooCommerce Payments is connected. + */ + public function test_is_connected_when_wcpay_is_connected() { + $this->mock_wc_payments_gateway( true ); + $this->assertTrue( WooCommercePayments::is_connected() ); + } + + /** + * Test is_connected method when WooCommerce Payments is not connected. + */ + public function test_is_connected_when_wcpay_is_not_connected() { + $this->mock_wc_payments_gateway( false ); + $this->assertFalse( WooCommercePayments::is_connected() ); + } + + /** + * Test is_account_partially_onboarded method when account is partially onboarded. + */ + public function test_is_account_partially_onboarded_when_true() { + $this->mock_wc_payments_gateway( true, true ); + $this->assertTrue( WooCommercePayments::is_account_partially_onboarded() ); + } + + /** + * Test is_account_partially_onboarded method when account is fully onboarded. + */ + public function test_is_account_partially_onboarded_when_false() { + $this->mock_wc_payments_gateway( true, false ); + $this->assertFalse( WooCommercePayments::is_account_partially_onboarded() ); + } + + /** + * Mock the WC_Payments gateway using filters. + * + * @param bool $is_connected Whether the gateway is connected. + * @param bool $is_partially_onboarded Whether the account is partially onboarded. + */ + private function mock_wc_payments_gateway( $is_connected, $is_partially_onboarded = false ) { + $this->fake_gateway->set_connected( $is_connected ); + $this->fake_gateway->set_partially_onboarded( $is_partially_onboarded ); + add_filter( 'woocommerce_payment_gateways', array( $this, 'inject_fake_gateway' ) ); + WC()->payment_gateways()->init(); + } + + /** + * Inject fake gateway filter callback. + * + * @param array $gateways Existing gateways. + * @return array Modified gateways. + */ + public function inject_fake_gateway( $gateways ) { + return array( 'woocommerce_payments' => $this->fake_gateway ); + } +} + +// phpcs:disable Generic.Files.OneObjectStructurePerFile.MultipleFound + +/** + * Fake WooCommerce Payments Gateway class for testing. + */ +class Fake_WC_Payments_Gateway extends WC_Payment_Gateway { + /** + * Whether the gateway is connected. + * + * @var bool + */ + private $connected = false; + + /** + * Whether the account is partially onboarded. + * + * @var bool + */ + private $partially_onboarded = false; + + /** + * Constructor. + */ + public function __construct() { + $this->id = 'woocommerce_payments'; + } + + /** + * Check if the gateway is connected. + * + * @return bool + */ + public function is_connected() { + return $this->connected; + } + + /** + * Check if the account is partially onboarded. + * + * @return bool + */ + public function is_account_partially_onboarded() { + return $this->partially_onboarded; + } + + /** + * Set the connected status. + * + * @param bool $connected Whether the gateway is connected. + */ + public function set_connected( $connected ) { + $this->connected = $connected; + } + + /** + * Set the partially onboarded status. + * + * @param bool $partially_onboarded Whether the account is partially onboarded. + */ + public function set_partially_onboarded( $partially_onboarded ) { + $this->partially_onboarded = $partially_onboarded; + } +} diff --git a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/features/payment-gateway-suggestions/payment-gateway-suggestions.php b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/features/payment-gateway-suggestions/payment-gateway-suggestions.php index 74f18eb6f30..728c78ce286 100644 --- a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/features/payment-gateway-suggestions/payment-gateway-suggestions.php +++ b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/features/payment-gateway-suggestions/payment-gateway-suggestions.php @@ -117,6 +117,69 @@ class WC_Admin_Tests_PaymentGatewaySuggestions_Init extends WC_Unit_Test_Case { $this->assertEquals( $expected_suggestions[1]['id'], $suggestions[1]->id ); } + /** + * Test that specs are read from cache when they exist. + */ + public function test_cached_or_default_suggestions_when_cache_exist() { + // Arrange. + $expected_suggestions = array( + array( + 'id' => 'mock-gateway1', + ), + array( + 'id' => 'mock-gateway2', + ), + ); + set_transient( + 'woocommerce_admin_' . PaymentGatewaySuggestionsDataSourcePoller::ID . '_specs', + array( + 'en_US' => $expected_suggestions, + ) + ); + + // Act. + $suggestions = PaymentGatewaySuggestions::get_cached_or_default_suggestions(); + + // Assert. + $this->assertCount( count( $expected_suggestions ), $suggestions ); + $this->assertEquals( $expected_suggestions[0]['id'], $suggestions[0]->id ); + $this->assertEquals( $expected_suggestions[1]['id'], $suggestions[1]->id ); + } + + /** + * Test that specs are read from default when cache is empty. + */ + public function test_cached_or_default_suggestions_when_cache_empty() { + // Arrange. + PaymentGatewaySuggestionsDataSourcePoller::get_instance()->delete_specs_transient(); + + // Act. + $suggestions = PaymentGatewaySuggestions::get_cached_or_default_suggestions(); + + // Assert. + $default_suggestions = EvaluateSuggestion::evaluate_specs( DefaultPaymentGateways::get_all() )['suggestions']; + + $this->assertEquals( $default_suggestions, $suggestions ); + } + + + /** + * Test that default gateways are provided when remote sources don't exist. + */ + public function test_cached_or_default_suggestions_when_marketplace_suggestions_off() { + // Arrange. + update_option( 'woocommerce_show_marketplace_suggestions', 'no' ); + PaymentGatewaySuggestionsDataSourcePoller::get_instance()->delete_specs_transient(); + + // Act. + $suggestions = PaymentGatewaySuggestions::get_cached_or_default_suggestions(); + $default_suggestions = EvaluateSuggestion::evaluate_specs( DefaultPaymentGateways::get_all() )['suggestions']; + + // Assert. + $this->assertEquals( $suggestions, $default_suggestions ); + } + + /** * Test that non-matched suggestions are not shown. */ diff --git a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/notes/class-wc-tests-customizing-product-catalog.php b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/notes/class-wc-tests-customizing-product-catalog.php index e682a69286e..8a652546b90 100644 --- a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/notes/class-wc-tests-customizing-product-catalog.php +++ b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/notes/class-wc-tests-customizing-product-catalog.php @@ -55,7 +55,7 @@ class WC_Admin_Tests_Customizing_Product_Catalog extends WC_Unit_Test_Case { * When get_note() is called * Then it should return null */ - public function test_it_does_not_add_note_if_prouduct_is_less_than_a_day_old() { + public function test_it_does_not_add_note_if_product_is_less_than_a_day_old() { // Given. wp_insert_post( array( diff --git a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/reports/class-wc-tests-reports-coupons-stats.php b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/reports/class-wc-tests-reports-coupons-stats.php index 76e25f8a0f0..04e1c98698d 100644 --- a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/reports/class-wc-tests-reports-coupons-stats.php +++ b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/reports/class-wc-tests-reports-coupons-stats.php @@ -6,7 +6,7 @@ */ use Automattic\WooCommerce\Admin\API\Reports\Coupons\Stats\DataStore as CouponsStatsDataStore; -use Automattic\WooCommerce\Admin\API\Reports\Coupons\Stats\Query as CouponsStatsQuery; +use Automattic\WooCommerce\Admin\API\Reports\GenericQuery; /** * Class WC_Admin_Tests_Reports_Coupons_Stats @@ -100,8 +100,8 @@ class WC_Admin_Tests_Reports_Coupons_Stats extends WC_Unit_Test_Case { ); $this->assertEquals( $expected_data, $data ); - // Test retrieving the stats through the query class. - $query = new CouponsStatsQuery( $args ); + // Test retrieving the stats through the generic query class. + $query = new GenericQuery( $args, 'coupons-stats' ); $this->assertEquals( $expected_data, $query->get_data() ); } @@ -143,8 +143,8 @@ class WC_Admin_Tests_Reports_Coupons_Stats extends WC_Unit_Test_Case { 'interval' => 'day', ); - // Test retrieving the stats through the query class. - $query = new CouponsStatsQuery( $args ); + // Test retrieving the stats through the generic query class. + $query = new GenericQuery( $args, 'coupons-stats' ); $start_datetime = new DateTime( $start_time ); $end_datetime = new DateTime( $end_time ); $expected_data = (object) array( diff --git a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/reports/class-wc-tests-reports-coupons.php b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/reports/class-wc-tests-reports-coupons.php index 45adb8769f9..867de990e28 100644 --- a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/reports/class-wc-tests-reports-coupons.php +++ b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/reports/class-wc-tests-reports-coupons.php @@ -5,9 +5,9 @@ * @package WooCommerce\Admin\Tests\Coupons */ +use Automattic\WooCommerce\Admin\API\Reports\GenericQuery; use Automattic\WooCommerce\Admin\ReportCSVExporter; use Automattic\WooCommerce\Admin\API\Reports\Coupons\DataStore as CouponsDataStore; -use Automattic\WooCommerce\Admin\API\Reports\Coupons\Query as CouponsQuery; use Automattic\WooCommerce\Admin\API\Reports\TimeInterval; /** @@ -96,8 +96,8 @@ class WC_Admin_Tests_Reports_Coupons extends WC_Unit_Test_Case { ); $this->assertEquals( $expected_data, $data ); - // Test retrieving the stats through the query class. - $query = new CouponsQuery( $args ); + // Test retrieving the stats through the generic query class. + $query = new GenericQuery( $args, 'coupons' ); $this->assertEquals( $expected_data, $query->get_data() ); // Test order by orders_count DESC. diff --git a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/reports/class-wc-tests-reports-orders.php b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/reports/class-wc-tests-reports-orders.php index 165b69cd93c..078da5f087b 100644 --- a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/reports/class-wc-tests-reports-orders.php +++ b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/reports/class-wc-tests-reports-orders.php @@ -6,8 +6,6 @@ */ use Automattic\WooCommerce\Admin\API\Reports\Orders\DataStore as OrdersDataStore; -use Automattic\WooCommerce\Admin\API\Reports\Orders\Query as OrdersQuery; -use Automattic\WooCommerce\Admin\API\Reports\TimeInterval; /** * Class WC_Admin_Tests_Reports_Orders diff --git a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/reports/class-wc-tests-reports-products.php b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/reports/class-wc-tests-reports-products.php index 1abbcfcb17a..e088c2f47e4 100644 --- a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/reports/class-wc-tests-reports-products.php +++ b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/reports/class-wc-tests-reports-products.php @@ -6,8 +6,8 @@ * @todo Finish up unit testing to verify bug-free product reports. */ +use Automattic\WooCommerce\Admin\API\Reports\GenericQuery; use Automattic\WooCommerce\Admin\API\Reports\Products\DataStore as ProductsDataStore; -use Automattic\WooCommerce\Admin\API\Reports\Products\Query as ProductsQuery; use Automattic\WooCommerce\Admin\ReportCSVExporter; /** @@ -70,8 +70,8 @@ class WC_Admin_Tests_Reports_Products extends WC_Unit_Test_Case { ); $this->assertEquals( $expected_data, $data ); - // Test retrieving the stats through the query class. - $query = new ProductsQuery( $args ); + // Test retrieving the stats through the generic query class. + $query = new GenericQuery( $args, 'products' ); $this->assertEquals( $expected_data, $query->get_data() ); } @@ -186,8 +186,8 @@ class WC_Admin_Tests_Reports_Products extends WC_Unit_Test_Case { ); $this->assertEquals( $expected_data, $data ); - // Test retrieving the stats through the query class. - $query = new ProductsQuery( $args ); + // Test retrieving the stats through the generic query class. + $query = new GenericQuery( $args, 'products' ); $this->assertEquals( $expected_data, $query->get_data() ); } @@ -411,8 +411,8 @@ class WC_Admin_Tests_Reports_Products extends WC_Unit_Test_Case { ); $this->assertEquals( $expected_data, $data ); - // Test retrieving the stats through the query class. - $query = new ProductsQuery( $args ); + // Test retrieving the stats through the generic query class. + $query = new GenericQuery( $args, 'products' ); $this->assertEquals( $expected_data, $query->get_data() ); } diff --git a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/reports/class-wc-tests-reports-variations.php b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/reports/class-wc-tests-reports-variations.php index 132a180b36e..66e55d262d1 100644 --- a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/reports/class-wc-tests-reports-variations.php +++ b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/reports/class-wc-tests-reports-variations.php @@ -6,8 +6,8 @@ * @todo Finish up unit testing to verify bug-free order reports. */ +use Automattic\WooCommerce\Admin\API\Reports\GenericQuery; use Automattic\WooCommerce\Admin\API\Reports\Variations\DataStore as VariationsDataStore; -use Automattic\WooCommerce\Admin\API\Reports\Variations\Query as VariationsQuery; /** * Reports order stats tests class. @@ -71,8 +71,8 @@ class WC_Admin_Tests_Reports_Variations extends WC_Unit_Test_Case { ); $this->assertEquals( $expected_data, $data ); - // Test retrieving the stats through the query class. - $query = new VariationsQuery( $args ); + // Test retrieving the stats through the generic query class. + $query = new GenericQuery( $args, 'variations' ); $this->assertEquals( $expected_data, $query->get_data() ); } diff --git a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/wc-admin-helper.php b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/wc-admin-helper.php index 46608767664..c7a712d197c 100644 --- a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/wc-admin-helper.php +++ b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/wc-admin-helper.php @@ -257,6 +257,19 @@ class WC_Admin_Tests_Admin_Helper extends WC_Unit_Test_Case { } } + /** + * Test is_store_page with the defined post_type param. + */ + public function test_is_store_page_with_post_type() { + // Test with post_type=product. + $this->assertTrue( WCAdminHelper::is_store_page( 'https://example.com/?post_type=product' ) ); + // Test with post_type=product and other params. + $this->assertTrue( WCAdminHelper::is_store_page( 'https://example.com/test?param1=value1&post_type=product¶m2=value2' ) ); + + // should return false if post_type is not product. + $this->assertFalse( WCAdminHelper::is_store_page( 'https://example.com/test?param1=value1¶m2=value2' ) ); + } + /** Test product archive link is store page even if shop page not set. */ public function test_is_store_page_even_if_shop_page_not_set() { $shop_page_id = get_option( 'woocommerce_shop_page_id' ); diff --git a/plugins/woocommerce/tests/php/includes/class-wc-checkout-test.php b/plugins/woocommerce/tests/php/includes/class-wc-checkout-test.php index ef7a26d47c7..2c8b7406ad9 100644 --- a/plugins/woocommerce/tests/php/includes/class-wc-checkout-test.php +++ b/plugins/woocommerce/tests/php/includes/class-wc-checkout-test.php @@ -46,12 +46,11 @@ class WC_Checkout_Test extends \WC_Unit_Test_Case { * @param bool $expect_error_message_for_shipping_country True to expect an error to be generated for the shipping country. */ public function test_validate_posted_data_adds_error_for_non_existing_country( $ship_to_different_address, $expect_error_message_for_shipping_country ) { - $_POST = array( + $data = array( 'billing_country' => 'XX', 'shipping_country' => 'YY', 'ship_to_different_address' => $ship_to_different_address, ); - $data = $_POST; // phpcs:ignore WordPress.Security.NonceVerification.Missing add_filter( 'woocommerce_cart_needs_shipping_address', @@ -75,12 +74,11 @@ class WC_Checkout_Test extends \WC_Unit_Test_Case { * @testdox the customer notes are correctly sanitized. */ public function test_order_notes() { - $_POST = array( + $data = array( 'ship_to_different_address' => false, 'order_comments' => 'This text should not save inside an anchor.', 'payment_method' => 'bacs', ); - $data = $_POST; // phpcs:ignore WordPress.Security.NonceVerification.Missing $errors = new WP_Error(); @@ -108,12 +106,11 @@ class WC_Checkout_Test extends \WC_Unit_Test_Case { * @param bool $ship_to_different_address True to simulate shipping to a different address than the billing address. */ public function test_validate_posted_data_does_not_add_error_for_existing_country( $ship_to_different_address ) { - $_POST = array( + $data = array( 'billing_country' => 'ES', 'shipping_country' => 'ES', 'ship_to_different_address' => $ship_to_different_address, ); - $data = $_POST; // phpcs:ignore WordPress.Security.NonceVerification.Missing $errors = new WP_Error(); @@ -132,12 +129,11 @@ class WC_Checkout_Test extends \WC_Unit_Test_Case { * @param bool $ship_to_different_address True to simulate shipping to a different address than the billing address. */ public function test_validate_posted_data_does_not_add_error_for_empty_country( $ship_to_different_address ) { - $_POST = array( + $data = array( 'billing_country' => '', 'shipping_country' => '', 'ship_to_different_address' => $ship_to_different_address, ); - $data = $_POST; // phpcs:ignore WordPress.Security.NonceVerification.Missing $errors = new WP_Error(); @@ -186,12 +182,11 @@ class WC_Checkout_Test extends \WC_Unit_Test_Case { ) ); - $_POST = array( + $data = array( 'billing_country' => $country, 'shipping_country' => $country, 'ship_to_different_address' => false, ); - $data = $_POST; // phpcs:ignore WordPress.Security.NonceVerification.Missing $errors = new WP_Error(); @@ -202,5 +197,20 @@ class WC_Checkout_Test extends \WC_Unit_Test_Case { $errors->get_error_message( 'shipping' ) ); } + + /** + * @testdox If the WooCommerce class's customer object is null (like if WC has not been fully initialized yet), + * calling WC_Checkout::get_value should not throw an error. + */ + public function test_get_value_no_error_on_null_customer() { + $sut = WC_Checkout::instance(); + + $orig_customer = WC()->customer; + WC()->customer = null; + + $this->assertNull( $sut->get_value( 'billing_country' ) ); + + WC()->customer = $orig_customer; + } } diff --git a/plugins/woocommerce/tests/php/includes/class-wc-download-handler-tests.php b/plugins/woocommerce/tests/php/includes/class-wc-download-handler-tests.php index 3f4c7cae3cf..53d04b481a7 100644 --- a/plugins/woocommerce/tests/php/includes/class-wc-download-handler-tests.php +++ b/plugins/woocommerce/tests/php/includes/class-wc-download-handler-tests.php @@ -74,7 +74,7 @@ class WC_Download_Handler_Tests extends \WC_Unit_Test_Case { $approved_directories->add_approved_directory( 'https://always.trusted' ); $approved_directory_rule_id = $approved_directories->add_approved_directory( 'https://new.supplier' ); - $product = WC_Helper_Product::create_downloadable_product( + list( $product, $order ) = $this->build_downloadable_product_and_order_one( array( array( 'name' => 'Book 1', @@ -87,12 +87,7 @@ class WC_Download_Handler_Tests extends \WC_Unit_Test_Case { ) ); - $customer = WC_Helper_Customer::create_customer(); - $email = 'admin@example.org'; - $order = WC_Helper_Order::create_order( $customer->get_id(), $product ); - $order->set_status( 'completed' ); - $order->save(); - + $email = 'admin@example.org'; $product_id = $product->get_id(); $downloads = $product->get_downloads(); $download_keys = array_keys( $downloads ); @@ -139,6 +134,111 @@ class WC_Download_Handler_Tests extends \WC_Unit_Test_Case { self::restore_download_handlers(); } + /** + * @testdox The remaining downloads count should iterate accurately. + */ + public function test_downloads_remaining_count(): void { + self::remove_download_handlers(); + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents -- Ok for unit tests. + file_put_contents( WP_CONTENT_DIR . '/uploads/woocommerce_uploads/supersheet-123.ods', str_pad( '', 100 ) ); + + list( $product, $order ) = $this->build_downloadable_product_and_order_one( + array( + array( + 'name' => 'Supersheet 123', + 'file' => content_url( 'uploads/woocommerce_uploads/supersheet-123.ods' ), + ), + ) + ); + + $product_id = $product->get_id(); + $downloads = $product->get_downloads(); + $download_keys = array_keys( $downloads ); + $email = 'admin@example.org'; + $download = current( WC_Data_Store::load( 'customer-download' )->get_downloads( array( 'product_id' => $product_id ) ) ); + + $download->set_downloads_remaining( 10 ); + $download->save(); + + // phpcs:disable WordPress.Security.NonceVerification.Recommended WordPress.Security.ValidatedSanitizedInput.InputNotValidated + $_GET = array( + 'download_file' => $product_id, + 'order' => $order->get_order_key(), + 'email' => $email, + 'uid' => hash( 'sha256', $email ), + 'key' => $download_keys[0], + ); + + WC_Download_Handler::download_product(); + $download = new WC_Customer_Download( $download->get_id() ); + $this->assertEquals( + 9, + $download->get_downloads_remaining(), + 'In relation to "normal" download requests, we should see a reduction in the downloads remaining count.' + ); + + // Let's simulate a ranged request (partial download). + $_SERVER['HTTP_RANGE'] = 'bytes=10-50'; + WC_Download_Handler::download_product(); + $download = new WC_Customer_Download( $download->get_id() ); + $this->assertEquals( + 9, + $download->get_downloads_remaining(), + 'In relation to "ranged" (partial) download requests, we should not see an immediate reduction in the downloads remaining count.' + ); + + // Repeat (HTTP_RANGE is still set). + WC_Download_Handler::download_product(); + $download = new WC_Customer_Download( $download->get_id() ); + $this->assertEquals( + 9, + $download->get_downloads_remaining(), + 'In relation to "ranged" (partial) download requests, we should not see an immediate reduction in the downloads remaining count.' + ); + + // Find the deferred download tracking action. + $deferred_download_tracker = current( + WC_Queue::instance()->search( + array( 'hook' => WC_Download_Handler::TRACK_DOWNLOAD_CALLBACK ) + ) + ); + + // Let it do it's thing, and confirm that a further decrement happened (of just 1 unit). + do_action_ref_array( $deferred_download_tracker->get_hook(), $deferred_download_tracker->get_args() ); + $download = new WC_Customer_Download( $download->get_id() ); + $this->assertEquals( + 8, + $download->get_downloads_remaining(), + 'In relation to "ranged" (partial) download requests, the deferred update to the downloads remaining count functioned as expected.' + ); + + self::restore_download_handlers(); + } + + /** + * Creates a downloadable product, and then places (and completes) an order for that + * object. + * + * @param array[] $downloadable_files Array of arrays, with each inner array specifying the 'name' and 'file'. + * + * @return array { + * WC_Product, + * WC_Order + * } + */ + private function build_downloadable_product_and_order_one( array $downloadable_files ): array { + $product = WC_Helper_Product::create_downloadable_product( $downloadable_files ); + $customer = WC_Helper_Customer::create_customer(); + $order = WC_Helper_Order::create_order( $customer->get_id(), $product ); + $order->set_status( 'completed' ); + $order->save(); + + return array( + $product, + $order, + ); + } + /** * Unregister download handlers to prevent unwanted output and side-effects. */ diff --git a/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-data-store-cpt-test.php b/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-data-store-cpt-test.php index 9b98872ab31..b8d3e97fdf8 100644 --- a/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-data-store-cpt-test.php +++ b/plugins/woocommerce/tests/php/includes/data-stores/class-wc-product-data-store-cpt-test.php @@ -114,4 +114,67 @@ class WC_Product_Data_Store_CPT_Test extends WC_Unit_Test_Case { $product2->save(); $product3->save(); } + + /** + * @testDox Test that meta cache key is changed on direct post meta add. + */ + public function test_get_meta_data_is_busted_on_post_meta_add() { + $product = new WC_Product(); + $product->save(); + + // Set the cache. + $product->get_meta_data(); + + $object_id_cache_key = WC_Cache_Helper::get_cache_prefix( 'object_' . $product->get_id() ); + add_post_meta( $product->get_id(), 'test', 'value' ); + + $r_object_id_cache_key = WC_Cache_Helper::get_cache_prefix( 'object_' . $product->get_id() ); + $this->assertNotEquals( $object_id_cache_key, $r_object_id_cache_key ); + + $product = wc_get_product( $product->get_id() ); + $this->assertEquals( 'value', $product->get_meta( 'test', true ) ); + } + + + /** + * @testDox Test that meta cache key is changed on direct post meta update. + */ + public function test_get_meta_data_is_busted_on_post_meta_update() { + $product = new WC_Product(); + $product->add_meta_data( 'test', 'value' ); + $product->save(); + + // Set the cache. + $product->get_meta_data(); + + $object_id_cache_key = WC_Cache_Helper::get_cache_prefix( 'object_' . $product->get_id() ); + update_post_meta( $product->get_id(), 'test', 'value2' ); + + $r_object_id_cache_key = WC_Cache_Helper::get_cache_prefix( 'object_' . $product->get_id() ); + $this->assertNotEquals( $object_id_cache_key, $r_object_id_cache_key ); + + $product = wc_get_product( $product->get_id() ); + $this->assertEquals( 'value2', $product->get_meta( 'test', true ) ); + } + + /** + * @testDox Test that meta cache key is changed on direct post meta delete. + */ + public function test_get_meta_data_is_busted_on_post_meta_delete() { + $product = new WC_Product(); + $product->add_meta_data( 'test', 'value' ); + $product->save(); + + // Set the cache. + $product->get_meta_data(); + + $object_id_cache_key = WC_Cache_Helper::get_cache_prefix( 'object_' . $product->get_id() ); + delete_post_meta( $product->get_id(), 'test' ); + + $r_object_id_cache_key = WC_Cache_Helper::get_cache_prefix( 'object_' . $product->get_id() ); + $this->assertNotEquals( $object_id_cache_key, $r_object_id_cache_key ); + + $product = wc_get_product( $product->get_id() ); + $this->assertEmpty( $product->get_meta( 'test', true ) ); + } } diff --git a/plugins/woocommerce/tests/php/includes/settings/class-wc-settings-products-test.php b/plugins/woocommerce/tests/php/includes/settings/class-wc-settings-products-test.php index 800618d4c8a..b98e3cde301 100644 --- a/plugins/woocommerce/tests/php/includes/settings/class-wc-settings-products-test.php +++ b/plugins/woocommerce/tests/php/includes/settings/class-wc-settings-products-test.php @@ -148,6 +148,7 @@ class WC_Settings_Products_Test extends WC_Settings_Unit_Test_Case { 'woocommerce_downloads_grant_access_after_payment' => 'checkbox', 'woocommerce_downloads_add_hash_to_filename' => 'checkbox', 'woocommerce_downloads_deliver_inline' => 'checkbox', + 'woocommerce_downloads_count_partial' => 'checkbox', ); $this->assertEquals( $expected, $settings_ids_and_types ); diff --git a/plugins/woocommerce/tests/php/includes/wc-order-functions-test.php b/plugins/woocommerce/tests/php/includes/wc-order-functions-test.php index 75d99e32986..3ef3723e4ad 100644 --- a/plugins/woocommerce/tests/php/includes/wc-order-functions-test.php +++ b/plugins/woocommerce/tests/php/includes/wc-order-functions-test.php @@ -39,7 +39,7 @@ class WC_Order_Functions_Test extends \WC_Unit_Test_Case { $line_item = reset( $items ); // Force a restock of one item. - $refunded_items = array(); + $refunded_items = array(); $refunded_items[ $line_item->get_id() ] = array( 'qty' => 1, ); @@ -174,5 +174,4 @@ class WC_Order_Functions_Test extends \WC_Unit_Test_Case { $this->assertEquals( 1, $order->get_data_store()->get_recorded_coupon_usage_counts( $order ) ); $this->assertEquals( 1, ( new WC_Coupon( $coupon ) )->get_usage_count() ); } - } diff --git a/plugins/woocommerce/tests/php/includes/wc-rest-functions-test.php b/plugins/woocommerce/tests/php/includes/wc-rest-functions-test.php index d5938d7fbcb..41d2dd8dfc4 100644 --- a/plugins/woocommerce/tests/php/includes/wc-rest-functions-test.php +++ b/plugins/woocommerce/tests/php/includes/wc-rest-functions-test.php @@ -27,4 +27,22 @@ class WCRestFunctionsTest extends WC_Unit_Test_Case { $this->assertFalse( wc_rest_should_load_namespace( 'wc-analytics', 'wc/v2' ) ); $this->assertTrue( wc_rest_should_load_namespace( 'wc/v2', 'wc/v2' ) ); } + + /** + * @testDox Test wc_rest_should_load_namespace known works with preload. + */ + public function test_wc_rest_should_load_namespace_known_works_with_preload() { + $memo = rest_preload_api_request( array(), '/wc/store/v1/cart' ); + $this->assertArrayHasKey( '/wc/store/v1/cart', $memo ); + } + + /** + * @testDox Test wc_rest_should_load_namespace filter. + */ + public function test_wc_rest_should_load_namespace_filter() { + $this->assertFalse( wc_rest_should_load_namespace( 'wc/v1', 'wc/v2' ) ); + add_filter( 'wc_rest_should_load_namespace', '__return_true' ); + $this->assertTrue( wc_rest_should_load_namespace( 'wc/v1', 'wc/v2' ) ); + remove_filter( 'wc_rest_should_load_namespace', '__return_true' ); + } } diff --git a/plugins/woocommerce/tests/php/includes/wc-stock-functions-tests.php b/plugins/woocommerce/tests/php/includes/wc-stock-functions-tests.php index 72ec9a59875..90f312be98b 100644 --- a/plugins/woocommerce/tests/php/includes/wc-stock-functions-tests.php +++ b/plugins/woocommerce/tests/php/includes/wc-stock-functions-tests.php @@ -432,4 +432,28 @@ class WC_Stock_Functions_Tests extends \WC_Unit_Test_Case { remove_action( 'woocommerce_no_stock', $callback ); } + + /** + * @testdox The wc_trigger_stock_change_actions function should only trigger actions if the product is set + * to manage stock. + */ + public function test_wc_trigger_stock_change_actions_bails_early_for_unmanaged_stock() { + $action_fired = false; + $callback = function () use ( &$action_fired ) { + $action_fired = true; + }; + add_action( 'woocommerce_no_stock', $callback ); + + $product = WC_Helper_Product::create_simple_product(); + + $this->assertFalse( $action_fired ); + + $product->set_manage_stock( true ); + $product->set_stock_quantity( 0 ); + $product->save(); + + $this->assertTrue( $action_fired ); + + remove_action( 'woocommerce_no_stock', $callback ); + } } diff --git a/plugins/woocommerce/tests/php/includes/wc-user-functions-test.php b/plugins/woocommerce/tests/php/includes/wc-user-functions-test.php index a2149955f27..163e977a555 100644 --- a/plugins/woocommerce/tests/php/includes/wc-user-functions-test.php +++ b/plugins/woocommerce/tests/php/includes/wc-user-functions-test.php @@ -1,6 +1,7 @@ assertFalse( wc_customer_bought_product( 'test@example.com', $customer_id_1, $product_id_2 ) ); $this->assertFalse( wc_customer_bought_product( 'test2@example.com', $customer_id_2, $product_id_1 ) ); } + + /** + * Test test_wc_get_customer_available_downloads_for_partial_refunds. + * + * @since 9.3 + */ + public function test_wc_get_customer_available_downloads_for_partial_refunds(): void { + /** @var Download_Directories $download_directories */ + $download_directories = wc_get_container()->get( Download_Directories::class ); + $download_directories->set_mode( Download_Directories::MODE_ENABLED ); + $download_directories->add_approved_directory( 'https://always.trusted' ); + + $test_file = 'https://always.trusted/123.pdf'; + + $customer_id = wc_create_new_customer( 'test@example.com', 'testuser', 'testpassword' ); + + $prod_download = new WC_Product_Download(); + $prod_download->set_file( $test_file ); + $prod_download->set_id( 1 ); + + $prod_download2 = new WC_Product_Download(); + $prod_download2->set_file( $test_file ); + $prod_download2->set_id( 2 ); + + $product = new WC_Product_Simple(); + $product->set_regular_price( 10 ); + $product->set_downloadable( 'yes' ); + $product->set_downloads( array( $prod_download ) ); + $product->save(); + + $product2 = new WC_Product_Simple(); + $product2->set_regular_price( 20 ); + $product2->set_downloadable( 'yes' ); + $product2->set_downloads( array( $prod_download2 ) ); + $product2->save(); + + $order = new WC_Order(); + $order->set_customer_id( $customer_id ); + + $item = new WC_Order_Item_Product(); + $item->set_product( $product ); + $item->set_order_id( $order->get_id() ); + $item->set_total( 10 ); + $item->save(); + $order->add_item( $item ); + + $item2 = new WC_Order_Item_Product(); + $item2->set_product( $product2 ); + $item2->set_order_id( $order->get_id() ); + $item->set_total( 20 ); + $item2->save(); + $order->add_item( $item2 ); + + $order->set_total( 30 ); // 10 + 20 + $order->set_status( 'completed' ); + $order->save(); + + $args = array( + 'amount' => 10, + 'order_id' => $order->get_id(), + 'line_items' => array( + $item->get_id() => array( + 'qty' => 1, + 'refund_total' => 10, + ), + ), + ); + + wc_create_refund( $args ); + + $downloads = wc_get_customer_available_downloads( $customer_id ); + $this->assertEquals( 1, count( $downloads ) ); + + $download = current( $downloads ); + $this->assertEquals( $prod_download2->get_id(), $download['download_id'] ); + $this->assertEquals( $order->get_id(), $download['order_id'] ); + $this->assertEquals( $product2->get_id(), $download['product_id'] ); + } } diff --git a/plugins/woocommerce/tests/php/src/Admin/API/PatternsTest.php b/plugins/woocommerce/tests/php/src/Admin/API/PatternsTest.php new file mode 100644 index 00000000000..93df53e208f --- /dev/null +++ b/plugins/woocommerce/tests/php/src/Admin/API/PatternsTest.php @@ -0,0 +1,121 @@ +factory->user->create( array( 'role' => 'administrator' ) ); + wp_set_current_user( $user ); + } + + /** + * Test the post endpoint when tracking is not allowed. + * + * @return void + */ + public function test_post_endpoint_when_tracking_is_not_allowed() { + update_option( 'woocommerce_allow_tracking', 'no' ); + + $response = rest_get_server()->dispatch( new \WP_REST_Request( 'POST', '/wc-admin/patterns' ) ); + $data = $response->get_data(); + + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( true, $data['success'] ); + + $patterns = get_transient( PTKPatternsStore::TRANSIENT_NAME ); + $this->assertFalse( $patterns ); + } + + /** + * Test the post endpoint when tracking is allowed. + * + * @return void + */ + public function test_post_endpoint_when_tracking_is_allowed() { + update_option( 'woocommerce_allow_tracking', 'yes' ); + + $response = rest_get_server()->dispatch( new \WP_REST_Request( 'POST', '/wc-admin/patterns' ) ); + $data = $response->get_data(); + + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( true, $data['success'] ); + + $patterns = get_transient( PTKPatternsStore::TRANSIENT_NAME ); + $this->assertNotFalse( $patterns ); + } + + /** + * Test the get endpoint when tracking is not allowed. + * + * @return void + */ + public function test_get_endpoint_when_tracking_is_not_allowed() { + update_option( 'woocommerce_allow_tracking', 'no' ); + + $response = rest_get_server()->dispatch( new \WP_REST_Request( 'GET', '/wc-admin/patterns' ) ); + $data = $response->get_data(); + + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( true, $data['success'] ); + + $patterns = get_transient( PTKPatternsStore::TRANSIENT_NAME ); + $this->assertFalse( $patterns ); + } + + /** + * Test the get endpoint when tracking is allowed. + * + * @return void + */ + public function test_get_endpoint_when_tracking_is_allowed() { + update_option( 'woocommerce_allow_tracking', 'yes' ); + + rest_get_server()->dispatch( new \WP_REST_Request( 'POST', '/wc-admin/patterns' ) ); + + $response = rest_get_server()->dispatch( new \WP_REST_Request( 'GET', '/wc-admin/patterns' ) ); + $data = $response->get_data(); + + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( true, $data['success'] ); + + $patterns = get_transient( PTKPatternsStore::TRANSIENT_NAME ); + $this->assertNotFalse( $patterns ); + } + + /** + * Test the get endpoint when the PTK client fails. + * + * @return void + */ + public function test_get_endpoint_when_ptk_client_fails() { + update_option( 'woocommerce_allow_tracking', 'yes' ); + + $error = function () { + return new \WP_Error( 'error', 'An error message' ); + }; + add_filter( 'pre_http_request', $error, 10, 3 ); + + $response = rest_get_server()->dispatch( new \WP_REST_Request( 'GET', '/wc-admin/patterns' ) ); + $data = $response->get_data(); + + $this->assertEquals( 500, $response->get_status() ); + $this->assertEquals( 'patterns_toolkit_api_error', $data['code'] ); + $this->assertEquals( 'Failed to connect with the Patterns Toolkit API: try again later.', $data['message'] ); + + $patterns = get_transient( PTKPatternsStore::TRANSIENT_NAME ); + $this->assertFalse( $patterns ); + } +} diff --git a/plugins/woocommerce/tests/php/src/Blocks/Helpers/FixtureData.php b/plugins/woocommerce/tests/php/src/Blocks/Helpers/FixtureData.php index f1342196129..e31a24cf7ea 100644 --- a/plugins/woocommerce/tests/php/src/Blocks/Helpers/FixtureData.php +++ b/plugins/woocommerce/tests/php/src/Blocks/Helpers/FixtureData.php @@ -194,6 +194,22 @@ class FixtureData { return $return; } + /** + * Create a product category and return the result. + * + * @param array $props Category props. + * @return array + */ + public function get_product_category( $props ) { + $category_name = $props['name'] ?? 'Test Category'; + + return wp_insert_term( + $category_name, + 'product_cat', + $props + ); + } + /** * Create a coupon and return the result. * diff --git a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/Patterns.php b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/Patterns.php deleted file mode 100644 index cf60a8e704f..00000000000 --- a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/Patterns.php +++ /dev/null @@ -1,56 +0,0 @@ -factory->user->create( array( 'role' => 'administrator' ) ); - wp_set_current_user( $user ); - } - - /** - * Test the post endpoint when tracking is not allowed. - * - * @return void - */ - public function test_post_endpoint_when_tracking_is_not_allowed() { - update_option( 'woocommerce_allow_tracking', 'no' ); - - $response = rest_get_server()->dispatch( new \WP_REST_Request( 'POST', '/wc/private/patterns' ) ); - $data = $response->get_data(); - - $this->assertEquals( 200, $response->get_status() ); - $this->assertEquals( true, $data['success'] ); - - $patterns = get_transient( PTKPatternsStore::TRANSIENT_NAME ); - $this->assertFalse( $patterns ); - } - - /** - * Test the post endpoint when tracking is allowed. - * - * @return void - */ - public function test_post_endpoint_when_tracking_is_allowed() { - update_option( 'woocommerce_allow_tracking', 'yes' ); - - $response = rest_get_server()->dispatch( new \WP_REST_Request( 'POST', '/wc/private/patterns' ) ); - $data = $response->get_data(); - - $this->assertEquals( 200, $response->get_status() ); - $this->assertEquals( true, $data['success'] ); - - $patterns = get_transient( PTKPatternsStore::TRANSIENT_NAME ); - $this->assertNotFalse( $patterns ); - } -} diff --git a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/ProductReviews.php b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/ProductReviews.php new file mode 100644 index 00000000000..e11497c5cbb --- /dev/null +++ b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/ProductReviews.php @@ -0,0 +1,129 @@ +product_category = $fixtures->get_product_category( + array( + 'name' => 'Test Category 1', + ) + ); + + $this->products = array( + $fixtures->get_simple_product( + array( + 'name' => 'Test Product 1', + 'regular_price' => 10, + ) + ), + $fixtures->get_simple_product( + array( + 'name' => 'Test Product 2', + 'regular_price' => 100, + 'category_ids' => array( $this->product_category['term_id'] ), + ) + ), + ); + + $fixtures->add_product_review( $this->products[0]->get_id(), 5 ); + $fixtures->add_product_review( $this->products[1]->get_id(), 4 ); + } + + /** + * Test getting reviews. + */ + public function test_get_items() { + $response = rest_get_server()->dispatch( new \WP_REST_Request( 'GET', '/wc/store/v1/products/reviews' ) ); + $data = $response->get_data(); + + // Assert correct response format. + $this->assertSame( 200, $response->get_status(), 'Unexpected status code.' ); + $this->assertSame( 2, count( $data ), 'Unexpected item count.' ); + + // Assert response items contain the correct properties. + $this->assertArrayHasKey( 'id', $data[0] ); + $this->assertArrayHasKey( 'date_created', $data[0] ); + $this->assertArrayHasKey( 'formatted_date_created', $data[0] ); + $this->assertArrayHasKey( 'date_created_gmt', $data[0] ); + $this->assertArrayHasKey( 'product_id', $data[0] ); + $this->assertArrayHasKey( 'product_name', $data[0] ); + $this->assertArrayHasKey( 'product_permalink', $data[0] ); + $this->assertArrayHasKey( 'product_image', $data[0] ); + $this->assertArrayHasKey( 'product_permalink', $data[0] ); + $this->assertArrayHasKey( 'reviewer', $data[0] ); + $this->assertArrayHasKey( 'review', $data[0] ); + $this->assertArrayHasKey( 'rating', $data[0] ); + $this->assertArrayHasKey( 'verified', $data[0] ); + $this->assertArrayHasKey( 'reviewer_avatar_urls', $data[0] ); + + // Assert response items contain the correct review data. + $this->assertSame( 'Test Product 2', $data[0]['product_name'] ); + $this->assertSame( 4, $data[0]['rating'] ); + $this->assertSame( 'Test Product 1', $data[1]['product_name'] ); + $this->assertSame( 5, $data[1]['rating'] ); + } + + /** + * Test getting reviews with specific order and per_page parameters. + */ + public function test_get_items_with_order_params() { + $request = new \WP_REST_Request( 'GET', '/wc/store/v1/products/reviews' ); + $request->set_param( 'per_page', 1 ); + $request->set_param( 'orderby', 'rating' ); + $request->set_param( 'order', 'desc' ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + $this->assertSame( 200, $response->get_status(), 'Unexpected status code.' ); + $this->assertCount( 1, $data, 'Unexpected item count.' ); + $this->assertSame( 5, $data[0]['rating'] ); + } + + /** + * Test getting reviews from a specific product. + */ + public function test_get_items_with_product_id_param() { + $request = new \WP_REST_Request( 'GET', '/wc/store/v1/products/reviews' ); + $request->set_param( 'product_id', (string) $this->products[0]->get_id() ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + $this->assertSame( 200, $response->get_status(), 'Unexpected status code.' ); + $this->assertCount( 1, $data, 'Unexpected item count.' ); + $this->assertSame( 5, $data[0]['rating'] ); + } + + /** + * Test getting reviews from a specific category. + */ + public function test_get_items_with_category_id_param() { + $request = new \WP_REST_Request( 'GET', '/wc/store/v1/products/reviews' ); + $request->set_param( 'category_id', (string) $this->product_category['term_id'] ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + $this->assertSame( 200, $response->get_status(), 'Unexpected status code.' ); + $this->assertCount( 1, $data, 'Unexpected item count.' ); + $this->assertSame( 4, $data[0]['rating'] ); + } +} diff --git a/plugins/woocommerce/tests/php/src/Blocks/Templates/SingleProductTemplateCompatibilityTests.php b/plugins/woocommerce/tests/php/src/Blocks/Templates/SingleProductTemplateCompatibilityTests.php index 4ab7388ed9c..e67d08b5563 100644 --- a/plugins/woocommerce/tests/php/src/Blocks/Templates/SingleProductTemplateCompatibilityTests.php +++ b/plugins/woocommerce/tests/php/src/Blocks/Templates/SingleProductTemplateCompatibilityTests.php @@ -37,10 +37,10 @@ class SingleProductTemplateCompatibilityTests extends WP_UnitTestCase { $result = SingleProductTemplateCompatibility::add_compatibility_layer( $default_single_product_template ); - $result_without_withespace = preg_replace( '/\s+/', '', $result ); + $result_without_whitespace = preg_replace( '/\s+/', '', $result ); $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); - $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); + $this->assertEquals( $result_without_whitespace, $expected_single_product_template_without_whitespace, '' ); } /** @@ -72,10 +72,10 @@ class SingleProductTemplateCompatibilityTests extends WP_UnitTestCase { $result = SingleProductTemplateCompatibility::add_compatibility_layer( $default_single_product_template ); - $result_without_withespace = preg_replace( '/\s+/', '', $result ); + $result_without_whitespace = preg_replace( '/\s+/', '', $result ); $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); - $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); + $this->assertEquals( $result_without_whitespace, $expected_single_product_template_without_whitespace, '' ); } /** @@ -155,10 +155,80 @@ class SingleProductTemplateCompatibilityTests extends WP_UnitTestCase { $result = SingleProductTemplateCompatibility::add_compatibility_layer( $default_single_product_template ); - $result_without_withespace = preg_replace( '/\s+/', '', $result ); + $result_without_whitespace = preg_replace( '/\s+/', '', $result ); $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); - $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); + $this->assertEquals( $result_without_whitespace, $expected_single_product_template_without_whitespace, '' ); + } + + /** + * Test that the Single Product Template is wrapped in a div with the correct class if it contains a pattern that contains a block related to the Single Product Template. + */ + public function test_add_compatibility_layer_if_contains_pattern_with_single_product_blocks() { + register_block_pattern( + 'test-pattern', + array( + 'title' => 'Test Pattern', + 'description' => 'Test Pattern Description', + 'content' => '', + ) + ); + + $default_single_product_template = ' + + + '; + + $expected_single_product_template = ' + + +
        + +
        + + '; + + $result = SingleProductTemplateCompatibility::add_compatibility_layer( $default_single_product_template ); + + $result_without_whitespace = preg_replace( '/\s+/', '', $result ); + $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); + + $this->assertEquals( $result_without_whitespace, $expected_single_product_template_without_whitespace, '' ); + } + + /** + * Test that the Single Product Template is wrapped in a div with the correct class if it contains a pattern that contains an inner block related to the Single Product Template. + */ + public function test_add_compatibility_layer_if_contains_pattern_with_inner_single_product_blocks() { + register_block_pattern( + 'test-pattern', + array( + 'title' => 'Test Pattern', + 'description' => 'Test Pattern Description', + 'content' => '
        ', + ) + ); + + $default_single_product_template = ' + + + '; + + $expected_single_product_template = ' + + +
        + +
        + + '; + + $result = SingleProductTemplateCompatibility::add_compatibility_layer( $default_single_product_template ); + + $result_without_whitespace = preg_replace( '/\s+/', '', $result ); + $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); + + $this->assertEquals( $result_without_whitespace, $expected_single_product_template_without_whitespace, '' ); } /** @@ -222,10 +292,10 @@ class SingleProductTemplateCompatibilityTests extends WP_UnitTestCase { $result = SingleProductTemplateCompatibility::add_compatibility_layer( $default_single_product_template ); - $result_without_withespace = preg_replace( '/\s+/', '', $result ); + $result_without_whitespace = preg_replace( '/\s+/', '', $result ); $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); - $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); + $this->assertEquals( $result_without_whitespace, $expected_single_product_template_without_whitespace, '' ); } /** @@ -251,10 +321,10 @@ class SingleProductTemplateCompatibilityTests extends WP_UnitTestCase { $result = SingleProductTemplateCompatibility::add_compatibility_layer( $default_single_product_template ); - $result_without_withespace = preg_replace( '/\s+/', '', $result ); + $result_without_whitespace = preg_replace( '/\s+/', '', $result ); $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); - $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); + $this->assertEquals( $result_without_whitespace, $expected_single_product_template_without_whitespace, '' ); } @@ -281,10 +351,10 @@ class SingleProductTemplateCompatibilityTests extends WP_UnitTestCase { $result = SingleProductTemplateCompatibility::add_compatibility_layer( $default_single_product_template ); - $result_without_withespace = preg_replace( '/\s+/', '', $result ); + $result_without_whitespace = preg_replace( '/\s+/', '', $result ); $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); - $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); + $this->assertEquals( $result_without_whitespace, $expected_single_product_template_without_whitespace, '' ); } /** @@ -322,10 +392,10 @@ class SingleProductTemplateCompatibilityTests extends WP_UnitTestCase { $result = SingleProductTemplateCompatibility::add_compatibility_layer( $default_single_product_template ); - $result_without_withespace = preg_replace( '/\s+/', '', $result ); + $result_without_whitespace = preg_replace( '/\s+/', '', $result ); $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); - $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); + $this->assertEquals( $result_without_whitespace, $expected_single_product_template_without_whitespace, '' ); } /** @@ -359,10 +429,10 @@ class SingleProductTemplateCompatibilityTests extends WP_UnitTestCase { $result = SingleProductTemplateCompatibility::add_compatibility_layer( $default_single_product_template ); - $result_without_withespace = preg_replace( '/\s+/', '', $result ); + $result_without_whitespace = preg_replace( '/\s+/', '', $result ); $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); - $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); + $this->assertEquals( $result_without_whitespace, $expected_single_product_template_without_whitespace, '' ); } /** @@ -398,9 +468,9 @@ class SingleProductTemplateCompatibilityTests extends WP_UnitTestCase { $result = SingleProductTemplateCompatibility::add_compatibility_layer( $default_single_product_template ); - $result_without_withespace = preg_replace( '/\s+/', '', $result ); + $result_without_whitespace = preg_replace( '/\s+/', '', $result ); $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', $expected_single_product_template ); - $this->assertEquals( $result_without_withespace, $expected_single_product_template_without_whitespace, '' ); + $this->assertEquals( $result_without_whitespace, $expected_single_product_template_without_whitespace, '' ); } } diff --git a/plugins/woocommerce/tests/php/src/Blocks/Templates/SingleProductTemplateTests.php b/plugins/woocommerce/tests/php/src/Blocks/Templates/SingleProductTemplateTests.php index 7649173e24a..634df6d181c 100644 --- a/plugins/woocommerce/tests/php/src/Blocks/Templates/SingleProductTemplateTests.php +++ b/plugins/woocommerce/tests/php/src/Blocks/Templates/SingleProductTemplateTests.php @@ -11,6 +11,203 @@ use \WP_UnitTestCase; */ class SingleProductTemplateTests extends WP_UnitTestCase { + /** + * Test that the Product Catalog template content isn't updated mistakenly. + * In other words, make sure the Single Product template logic doesn't leak + * into other templates. + * + */ + public function test_dont_update_single_product_content_for_other_templates() { + $single_product_template = new SingleProductTemplate(); + $default_product_catalog_template_content = ' + + + '; + + $template = new \WP_Block_Template(); + $template->slug = 'archive-product'; + $template->title = 'Product Catalog'; + $template->content = $default_product_catalog_template_content; + $template->type = 'wp_template'; + + $result = $single_product_template->update_single_product_content( + array( + $template, + ), + ); + + $this->assertEquals( + $default_product_catalog_template_content, + $result[0]->content + ); + } + + /** + * Test that the Single Product template content isn't updated if it + * contains the Legacy Template block. + * + */ + public function test_dont_update_single_product_content_with_legacy_template() { + $single_product_template = new SingleProductTemplate(); + $default_single_product_template_content = ' + + + '; + + $template = new \WP_Block_Template(); + $template->slug = 'single-product'; + $template->title = 'Single Product'; + $template->content = $default_single_product_template_content; + $template->type = 'wp_template'; + + $result = $single_product_template->update_single_product_content( + array( + $template, + ), + ); + + $this->assertEquals( + $default_single_product_template_content, + $result[0]->content + ); + } + + /** + * Test that the Single Product template content is updated if it doesn't + * contain the Legacy Template block. + * + */ + public function test_update_single_product_content_with_legacy_template() { + $single_product_template = new SingleProductTemplate(); + $default_single_product_template_content = ' + + + '; + $expected_single_product_template_content = ' + + +
        + +
        + + '; + + $template = new \WP_Block_Template(); + $template->slug = 'single-product'; + $template->title = 'Single Product'; + $template->content = $default_single_product_template_content; + $template->type = 'wp_template'; + + $result = $single_product_template->update_single_product_content( + array( + $template, + ), + ); + + $expected_single_product_template_without_whitespace = preg_replace( + '/\s+/', + '', + $expected_single_product_template_content + ); + $result_without_whitespace = preg_replace( '/\s+/', '', $result[0]->content ); + + $this->assertEquals( + $expected_single_product_template_without_whitespace, + $result_without_whitespace + ); + } + + /** + * Test that the Single Product template content isn't updated if it + * contains a pattern with the Legacy Template block. + * + */ + public function test_dont_update_single_product_content_with_legacy_template_inside_a_pattern() { + register_block_pattern( + 'test-pattern', + array( + 'title' => 'Test Pattern', + 'description' => 'Test Pattern Description', + 'content' => '', + ) + ); + $single_product_template = new SingleProductTemplate(); + $default_single_product_template_content = ' + + + '; + + $template = new \WP_Block_Template(); + $template->slug = 'single-product'; + $template->title = 'Single Product'; + $template->content = $default_single_product_template_content; + $template->type = 'wp_template'; + + $result = $single_product_template->update_single_product_content( + array( + $template, + ), + ); + + $this->assertEquals( + $default_single_product_template_content, + $result[0]->content + ); + } + + /** + * Test that the Single Product template content is updated if it doesn't + * contain the Legacy Template block. + * + */ + public function test_update_single_product_content_with_legacy_template_inside_a_pattern() { + register_block_pattern( + 'test-pattern', + array( + 'title' => 'Test Pattern', + 'description' => 'Test Pattern Description', + 'content' => '', + ) + ); + $single_product_template = new SingleProductTemplate(); + $default_single_product_template_content = ' + + + '; + $expected_single_product_template_content = ' + + +
        + +
        + + '; + + $template = new \WP_Block_Template(); + $template->slug = 'single-product'; + $template->title = 'Single Product'; + $template->content = $default_single_product_template_content; + $template->type = 'wp_template'; + + $result = $single_product_template->update_single_product_content( + array( + $template, + ), + ); + + $expected_single_product_template_without_whitespace = preg_replace( + '/\s+/', + '', + $expected_single_product_template_content + ); + $result_without_whitespace = preg_replace( '/\s+/', '', $result[0]->content ); + + $this->assertEquals( + $expected_single_product_template_without_whitespace, + $result_without_whitespace + ); + } + /** * Test that the password form isn't added to the Single Product Template. * @@ -38,7 +235,7 @@ class SingleProductTemplateTests extends WP_UnitTestCase { $default_single_product_template ); - $result_without_withespace = preg_replace( '/\s+/', '', $result ); + $result_without_whitespace = preg_replace( '/\s+/', '', $result ); $expected_single_product_template_without_whitespace = preg_replace( '/\s+/', '', @@ -46,9 +243,8 @@ class SingleProductTemplateTests extends WP_UnitTestCase { ); $this->assertEquals( - $result_without_withespace, - $expected_single_product_template_without_whitespace, - '' + $result_without_whitespace, + $expected_single_product_template_without_whitespace ); } @@ -81,11 +277,11 @@ class SingleProductTemplateTests extends WP_UnitTestCase { $default_single_product_template ); - $result_without_withespace = preg_replace( '/\s+/', '', $result ); - $result_without_withespace_without_custom_pwbox_ids = preg_replace( + $result_without_whitespace = preg_replace( '/\s+/', '', $result ); + $result_without_whitespace_without_custom_pwbox_ids = preg_replace( '/pwbox-\d+/', '', - $result_without_withespace + $result_without_whitespace ); $expected_single_product_template_without_whitespace = preg_replace( @@ -101,9 +297,8 @@ class SingleProductTemplateTests extends WP_UnitTestCase { ); $this->assertEquals( - $result_without_withespace_without_custom_pwbox_ids, - $expected_single_product_template_without_whitespace_without_custom_pwbox_ids, - '' + $result_without_whitespace_without_custom_pwbox_ids, + $expected_single_product_template_without_whitespace_without_custom_pwbox_ids ); } @@ -219,11 +414,11 @@ class SingleProductTemplateTests extends WP_UnitTestCase { $default_single_product_template ); - $result_without_withespace = preg_replace( '/\s+/', '', $result ); - $result_without_withespace_without_custom_pwbox_ids = preg_replace( + $result_without_whitespace = preg_replace( '/\s+/', '', $result ); + $result_without_whitespace_without_custom_pwbox_ids = preg_replace( '/pwbox-\d+/', '', - $result_without_withespace + $result_without_whitespace ); $expected_single_product_template_without_whitespace = preg_replace( @@ -239,9 +434,8 @@ class SingleProductTemplateTests extends WP_UnitTestCase { ); $this->assertEquals( - $result_without_withespace_without_custom_pwbox_ids, - $expected_single_product_template_without_whitespace_without_custom_pwbox_ids, - '' + $result_without_whitespace_without_custom_pwbox_ids, + $expected_single_product_template_without_whitespace_without_custom_pwbox_ids ); } } diff --git a/plugins/woocommerce/tests/php/src/Internal/Logging/RemoteLoggerTest.php b/plugins/woocommerce/tests/php/src/Internal/Logging/RemoteLoggerTest.php index d4af6c893fe..1133880bad2 100644 --- a/plugins/woocommerce/tests/php/src/Internal/Logging/RemoteLoggerTest.php +++ b/plugins/woocommerce/tests/php/src/Internal/Logging/RemoteLoggerTest.php @@ -1,469 +1,646 @@ sut = wc_get_container()->get( RemoteLogger::class ); - WC()->version = '9.2.0'; - } + class RemoteLoggerTest extends \WC_Unit_Test_Case { + /** + * System under test. + * + * @var RemoteLogger + */ + private $sut; - /** - * Tear down. - * - * @return void - */ - public function tearDown(): void { - $this->cleanup_filters(); - delete_option( 'woocommerce_feature_remote_logging_enabled' ); - delete_transient( RemoteLogger::WC_LATEST_VERSION_TRANSIENT ); - global $wpdb; - $wpdb->query( "DELETE FROM {$wpdb->prefix}wc_rate_limits" ); - WC_Cache_Helper::invalidate_cache_group( WC_Rate_Limiter::CACHE_GROUP ); - } - - /** - * Cleanup filters used in tests. - * - * @return void - */ - private function cleanup_filters() { - $filters = array( - 'option_woocommerce_admin_remote_feature_enabled', - 'option_woocommerce_allow_tracking', - 'option_woocommerce_version', - 'option_woocommerce_remote_variant_assignment', - 'plugins_api', - 'pre_http_request', - 'woocommerce_remote_logger_formatted_log_data', - ); - foreach ( $filters as $filter ) { - remove_all_filters( $filter ); + /** + * Set up test + * + * @return void + */ + public function setUp(): void { + parent::setUp(); + $this->sut = wc_get_container()->get( RemoteLogger::class ); } - } - /** - * @testdox Remote logging is allowed when all conditions are met - */ - public function test_remote_logging_allowed() { - $this->setup_remote_logging_conditions( true ); - $this->assertTrue( $this->sut->is_remote_logging_allowed() ); - } - - /** - * @testdox Remote logging is not allowed under various conditions - * @dataProvider remote_logging_disallowed_provider - * - * @param string $condition The condition being tested. - * @param callable $setup_callback Callback to set up the test condition. - */ - public function test_remote_logging_not_allowed( $condition, $setup_callback ) { - $this->setup_remote_logging_conditions( true ); - $setup_callback( $this ); - $this->assertFalse( $this->sut->is_remote_logging_allowed() ); - } - - /** - * Data provider for test_remote_logging_not_allowed. - * - * @return array[] Test cases with conditions and setup callbacks. - */ - public function remote_logging_disallowed_provider() { - return array( - 'feature flag disabled' => array( - 'condition' => 'feature flag disabled', - 'setup' => fn() => update_option( 'woocommerce_feature_remote_logging_enabled', 'no' ), - ), - 'tracking opted out' => array( - 'condition' => 'tracking opted out', - 'setup' => fn() => add_filter( 'option_woocommerce_allow_tracking', fn() => 'no' ), - ), - 'outdated version' => array( - 'condition' => 'outdated version', - 'setup' => fn() => WC()->version = '9.0.0', - ), - 'high variant assignment' => array( - 'condition' => 'high variant assignment', - 'setup' => fn() => add_filter( 'option_woocommerce_remote_variant_assignment', fn() => 15 ), - ), - ); - } - - /** - * @testdox Fetch latest WooCommerce version retries on API failure - */ - public function test_fetch_latest_woocommerce_version_retry() { - $this->setup_remote_logging_conditions( true ); - add_filter( 'plugins_api', fn() => new \WP_Error(), 10, 3 ); - - for ( $i = 1; $i <= 4; $i++ ) { - $this->sut->is_remote_logging_allowed(); - $retry_count = get_transient( RemoteLogger::FETCH_LATEST_VERSION_RETRY ); - $this->assertEquals( min( $i, 3 ), $retry_count ); + /** + * Tear down. + * + * @return void + */ + public function tearDown(): void { + $this->cleanup_filters(); + delete_option( 'woocommerce_feature_remote_logging_enabled' ); + delete_transient( RemoteLogger::WC_NEW_VERSION_TRANSIENT ); + global $wpdb; + $wpdb->query( "DELETE FROM {$wpdb->prefix}wc_rate_limits" ); + WC_Cache_Helper::invalidate_cache_group( WC_Rate_Limiter::CACHE_GROUP ); } - } - /** - * @testdox get_formatted_log method returns expected array structure - * @dataProvider get_formatted_log_provider - * - * @param string $level The log level. - * @param string $message The log message. - * @param array $context The log context. - * @param array $expected The expected formatted log array. - */ - public function test_get_formatted_log( $level, $message, $context, $expected ) { - $formatted_log = $this->sut->get_formatted_log( $level, $message, $context ); - foreach ( $expected as $key => $value ) { - $this->assertArrayHasKey( $key, $formatted_log ); - $this->assertEquals( $value, $formatted_log[ $key ] ); + /** + * Cleanup filters used in tests. + * + * @return void + */ + private function cleanup_filters() { + $filters = array( + 'option_woocommerce_admin_remote_feature_enabled', + 'option_woocommerce_allow_tracking', + 'option_woocommerce_version', + 'option_woocommerce_remote_variant_assignment', + 'plugins_api', + 'pre_http_request', + 'woocommerce_remote_logger_formatted_log_data', + 'pre_site_transient_update_plugins', + 'woocommerce_remote_logger_request_uri_whitelist', + ); + foreach ( $filters as $filter ) { + remove_all_filters( $filter ); + } } - } - /** - * Data provider for test_get_formatted_log. - * - * @return array[] Test cases with log data and expected formatted output. - */ - public function get_formatted_log_provider() { - return array( - 'basic log data' => array( - 'error', - 'Fatal error occurred at line 123 in ' . ABSPATH . 'wp-content/file.php', - array( 'tags' => array( 'tag1', 'tag2' ) ), - array( - 'feature' => 'woocommerce_core', - 'severity' => 'error', - 'message' => 'Fatal error occurred at line 123 in **/wp-content/file.php', - 'tags' => array( 'woocommerce', 'php', 'tag1', 'tag2' ), + /** + * @testdox Remote logging is allowed when all conditions are met + */ + public function test_remote_logging_allowed() { + $this->setup_remote_logging_conditions( true ); + $this->assertTrue( $this->sut->is_remote_logging_allowed() ); + } + + /** + * @testdox Remote logging is not allowed under various conditions + * @dataProvider remote_logging_disallowed_provider + * + * @param string $condition The condition being tested. + * @param callable $setup_callback Callback to set up the test condition. + */ + public function test_remote_logging_not_allowed( $condition, $setup_callback ) { + $this->setup_remote_logging_conditions( true ); + $setup_callback( $this ); + $this->assertFalse( $this->sut->is_remote_logging_allowed() ); + } + + /** + * Data provider for test_remote_logging_not_allowed. + * + * @return array[] Test cases with conditions and setup callbacks. + */ + public function remote_logging_disallowed_provider() { + return array( + 'feature flag disabled' => array( + 'condition' => 'feature flag disabled', + 'setup' => fn() => update_option( 'woocommerce_feature_remote_logging_enabled', 'no' ), ), - ), - 'log with backtrace' => array( - 'error', - 'Test error message', - array( 'backtrace' => ABSPATH . 'wp-content/plugins/woocommerce/file.php' ), - array( 'trace' => '**/woocommerce/file.php' ), - ), - 'log with extra attributes' => array( - 'error', - 'Test error message', - array( - 'extra' => array( - 'key1' => 'value1', - 'key2' => 'value2', + 'tracking opted out' => array( + 'condition' => 'tracking opted out', + 'setup' => fn() => add_filter( 'option_woocommerce_allow_tracking', fn() => 'no' ), + ), + 'high variant assignment' => array( + 'condition' => 'high variant assignment', + 'setup' => fn() => add_filter( 'option_woocommerce_remote_variant_assignment', fn() => 15 ), + ), + 'outdated version' => array( + 'condition' => 'outdated version', + 'setup' => function () { + $version = WC()->version; + // Next major version. (e.g. 9.0.1 -> 10.0.0). + $next_version = implode( + '.', + array_map( + function ( $n, $i ) { + return 0 === $i ? $n + 1 : 0; + }, + explode( '.', $version ), + array_keys( explode( '.', $version ) ) + ) + ); + + set_site_transient( RemoteLogger::WC_NEW_VERSION_TRANSIENT, $next_version, WEEK_IN_SECONDS ); + }, + ), + ); + } + + /** + * @testdox should_current_version_be_logged method behaves correctly + * @dataProvider should_current_version_be_logged_provider + * + * @param string $current_version The current WooCommerce version. + * @param string $new_version The new WooCommerce version. + * @param string $transient_value The value of the transient. + * @param bool $expected The expected result. + */ + public function test_should_current_version_be_logged( $current_version, $new_version, $transient_value, $expected ) { + $wc_version = WC()->version; + WC()->version = $current_version; + + // Set up the transient. + if ( null !== $transient_value ) { + set_site_transient( RemoteLogger::WC_NEW_VERSION_TRANSIENT, $transient_value, WEEK_IN_SECONDS ); + } else { + delete_site_transient( RemoteLogger::WC_NEW_VERSION_TRANSIENT ); + + $this->setup_mock_plugin_updates( $new_version ); + } + + $result = $this->invoke_private_method( $this->sut, 'should_current_version_be_logged', array() ); + $this->assertEquals( $expected, $result ); + + // Clean up. + delete_site_transient( RemoteLogger::WC_NEW_VERSION_TRANSIENT ); + + WC()->version = $wc_version; + } + + /** + * Data provider for test_should_current_version_be_logged. + */ + public function should_current_version_be_logged_provider() { + return array( + 'current version is latest (transient set)' => array( '9.2.0', '9.2.0', '9.2.0', true ), + 'current version is newer (transient set)' => array( '9.3.0', '9.2.0', '9.2.0', true ), + 'current version is older (transient set)' => array( '9.1.0', '9.2.0', '9.2.0', false ), + 'new version is null (transient set)' => array( '9.2.0', null, null, true ), + 'transient not set, current version is latest' => array( '9.2.0', '9.2.0', null, true ), + 'transient not set, current version is newer' => array( '9.3.0', '9.2.0', null, true ), + 'transient not set, current version is older' => array( '9.1.0', '9.2.0', null, false ), + 'transient not set, new version is null' => array( '9.2.0', null, null, true ), + ); + } + + /** + * @testdox fetch_new_woocommerce_version method returns correct version + */ + public function test_fetch_new_woocommerce_version() { + $this->setup_mock_plugin_updates( '9.3.0' ); + + $result = $this->invoke_private_method( $this->sut, 'fetch_new_woocommerce_version', array() ); + $this->assertEquals( '9.3.0', $result, 'The result should be the latest version when an update is available.' ); + } + + /** + * @testdox fetch_new_woocommerce_version method returns null when no update is available + */ + public function test_fetch_new_woocommerce_version_no_update() { + add_filter( 'pre_site_transient_update_plugins', fn() => array() ); + + $result = $this->invoke_private_method( $this->sut, 'fetch_new_woocommerce_version', array() ); + $this->assertNull( $result, 'The result should be null when no update is available.' ); + } + + + /** + * @testdox get_formatted_log method returns expected array structure + * @dataProvider get_formatted_log_provider + * + * @param string $level The log level. + * @param string $message The log message. + * @param array $context The log context. + * @param array $expected The expected formatted log array. + */ + public function test_get_formatted_log( $level, $message, $context, $expected ) { + $formatted_log = $this->sut->get_formatted_log( $level, $message, $context ); + foreach ( $expected as $key => $value ) { + $this->assertArrayHasKey( $key, $formatted_log ); + $this->assertEquals( $value, $formatted_log[ $key ] ); + } + } + + /** + * Data provider for test_get_formatted_log. + * + * @return array[] Test cases with log data and expected formatted output. + */ + public function get_formatted_log_provider() { + return array( + 'basic log data' => array( + 'error', + 'Fatal error occurred at line 123 in ' . ABSPATH . 'wp-content/file.php', + array( 'tags' => array( 'tag1', 'tag2' ) ), + array( + 'feature' => 'woocommerce_core', + 'severity' => 'error', + 'message' => 'Fatal error occurred at line 123 in **/wp-content/file.php', + 'tags' => array( 'woocommerce', 'php', 'tag1', 'tag2' ), ), ), - array( - 'extra' => array( - 'key1' => 'value1', - 'key2' => 'value2', + 'log with backtrace' => array( + 'error', + 'Test error message', + array( 'backtrace' => ABSPATH . 'wp-content/plugins/woocommerce/file.php' ), + array( 'trace' => '**/woocommerce/file.php' ), + ), + 'log with extra attributes' => array( + 'error', + 'Test error message', + array( + 'extra' => array( + 'key1' => 'value1', + 'key2' => 'value2', + ), + ), + array( + 'extra' => array( + 'key1' => 'value1', + 'key2' => 'value2', + ), ), ), - ), - ); - } + ); + } - /** - * @testdox should_handle method behaves correctly under different conditions - * @dataProvider should_handle_provider - * - * @param callable $setup Function to set up the test environment. - * @param string $level Log level to test. - * @param bool $expected Expected result of should_handle method. - */ - public function test_should_handle( $setup, $level, $expected ) { - $this->sut = $this->getMockBuilder( RemoteLogger::class ) + + /** + * @testdox get_formatted_log method correctly sanitizes request URI + */ + public function test_get_formatted_log_sanitizes_request_uri() { + global $mock_filter_input, $mock_return; + $mock_filter_input = true; + $mock_return = '/shop?path=home&user=admin&token=abc123'; + + $formatted_log = $this->sut->get_formatted_log( 'error', 'Test message', array() ); + + $mock_filter_input = false; + + $this->assertArrayHasKey( 'properties', $formatted_log ); + $this->assertArrayHasKey( 'request_uri', $formatted_log['properties'] ); + $this->assertNotNull( $formatted_log['properties']['request_uri'], 'Request URI should not be null' ); + $this->assertStringContainsString( 'path=home', $formatted_log['properties']['request_uri'] ); + $this->assertStringContainsString( 'user=xxxxxx', $formatted_log['properties']['request_uri'] ); + $this->assertStringContainsString( 'token=xxxxxx', $formatted_log['properties']['request_uri'] ); + } + + /** + * @testdox sanitize_request_uri method respects whitelist filter + */ + public function test_sanitize_request_uri_respects_whitelist_filter() { + add_filter( + 'woocommerce_remote_logger_request_uri_whitelist', + function ( $whitelist ) { + $whitelist[] = 'custom_param'; + return $whitelist; + } + ); + + $request_uri = '/shop?path=home&custom_param=value&token=abc123'; + $sanitized_uri = $this->invoke_private_method( $this->sut, 'sanitize_request_uri', array( $request_uri ) ); + $this->assertStringContainsString( 'path=home', $sanitized_uri ); + $this->assertStringContainsString( 'custom_param=value', $sanitized_uri ); + $this->assertStringContainsString( 'token=xxxxxx', $sanitized_uri ); + } + + /** + * @testdox sanitize_request_uri method correctly sanitizes request URIs + */ + public function test_sanitize_request_uri() { + $reflection = new \ReflectionClass( $this->sut ); + $method = $reflection->getMethod( 'sanitize_request_uri' ); + $method->setAccessible( true ); + + // Test with whitelisted parameters. + $request_uri = '/shop?path=home&page=2&step=1&task=checkout'; + $sanitized_uri = $method->invokeArgs( $this->sut, array( $request_uri ) ); + $this->assertStringContainsString( 'path=home', $sanitized_uri ); + $this->assertStringContainsString( 'page=2', $sanitized_uri ); + $this->assertStringContainsString( 'step=1', $sanitized_uri ); + $this->assertStringContainsString( 'task=checkout', $sanitized_uri ); + + // Test with non-whitelisted parameters. + $request_uri = '/shop?path=home&user=admin&token=abc123'; + $sanitized_uri = $method->invokeArgs( $this->sut, array( $request_uri ) ); + $this->assertStringContainsString( 'path=home', $sanitized_uri ); + $this->assertStringContainsString( 'user=xxxxxx', $sanitized_uri ); + $this->assertStringContainsString( 'token=xxxxxx', $sanitized_uri ); + + // Test with mixed parameters. + $request_uri = '/shop?path=home&page=2&user=admin&step=1&token=abc123'; + $sanitized_uri = $method->invokeArgs( $this->sut, array( $request_uri ) ); + $this->assertStringContainsString( 'path=home', $sanitized_uri ); + $this->assertStringContainsString( 'page=2', $sanitized_uri ); + $this->assertStringContainsString( 'step=1', $sanitized_uri ); + $this->assertStringContainsString( 'user=xxxxxx', $sanitized_uri ); + $this->assertStringContainsString( 'token=xxxxxx', $sanitized_uri ); + } + + /** + * @testdox should_handle method behaves correctly under different conditions + * @dataProvider should_handle_provider + * + * @param callable $setup Function to set up the test environment. + * @param string $level Log level to test. + * @param bool $expected Expected result of should_handle method. + */ + public function test_should_handle( $setup, $level, $expected ) { + $this->sut = $this->getMockBuilder( RemoteLogger::class ) ->onlyMethods( array( 'is_remote_logging_allowed', 'is_third_party_error' ) ) ->getMock(); - $this->sut->method( 'is_remote_logging_allowed' )->willReturn( true ); - $this->sut->method( 'is_third_party_error' )->willReturn( false ); + $this->sut->method( 'is_remote_logging_allowed' )->willReturn( true ); + $this->sut->method( 'is_third_party_error' )->willReturn( false ); - $setup( $this ); + $setup( $this ); - $result = $this->invoke_private_method( $this->sut, 'should_handle', array( $level, 'Test message', array() ) ); - $this->assertEquals( $expected, $result ); - } + $result = $this->invoke_private_method( $this->sut, 'should_handle', array( $level, 'Test message', array() ) ); + $this->assertEquals( $expected, $result ); + } - /** - * Data provider for test_should_handle method. - * - * @return array Test cases for should_handle method. - */ - public function should_handle_provider() { - return array( - 'throttled' => array( - fn() => WC_Rate_Limiter::set_rate_limit( RemoteLogger::RATE_LIMIT_ID, 10 ), - 'critical', - false, - ), - 'less severe than critical' => array( - fn() => null, - 'error', - false, - ), - 'critical level' => array( - fn() => null, - 'critical', - true, - ), - ); - } + /** + * Data provider for test_should_handle method. + * + * @return array Test cases for should_handle method. + */ + public function should_handle_provider() { + return array( + 'throttled' => array( + fn() => WC_Rate_Limiter::set_rate_limit( RemoteLogger::RATE_LIMIT_ID, 10 ), + 'critical', + false, + ), + 'less severe than critical' => array( + fn() => null, + 'error', + false, + ), + 'critical level' => array( + fn() => null, + 'critical', + true, + ), + ); + } - /** - * @testdox handle method applies filter and doesn't send logs when filtered to null - */ - public function test_handle_filtered_log_null() { - $this->sut = $this->getMockBuilder( RemoteLogger::class ) + /** + * @testdox handle method applies filter and doesn't send logs when filtered to null + */ + public function test_handle_filtered_log_null() { + $this->sut = $this->getMockBuilder( RemoteLogger::class ) ->onlyMethods( array( 'is_remote_logging_allowed' ) ) ->getMock(); - $this->sut->method( 'is_remote_logging_allowed' )->willReturn( true ); + $this->sut->method( 'is_remote_logging_allowed' )->willReturn( true ); - add_filter( 'woocommerce_remote_logger_formatted_log_data', fn() => null, 10, 4 ); - add_filter( 'pre_http_request', fn() => $this->fail( 'wp_safe_remote_post should not be called' ), 10, 3 ); + add_filter( 'woocommerce_remote_logger_formatted_log_data', fn() => null, 10, 4 ); + add_filter( 'pre_http_request', fn() => $this->fail( 'wp_safe_remote_post should not be called' ), 10, 3 ); - $this->assertFalse( $this->sut->handle( time(), 'error', 'Test message', array() ) ); - } + $this->assertFalse( $this->sut->handle( time(), 'error', 'Test message', array() ) ); + } - /** - * @testdox handle method does not send logs in dev environment - */ - public function test_handle_does_not_send_logs_in_dev_environment() { - $this->sut = $this->getMockBuilder( RemoteLoggerWithEnvironmentOverride::class ) + /** + * @testdox handle method does not send logs in dev environment + */ + public function test_handle_does_not_send_logs_in_dev_environment() { + $this->sut = $this->getMockBuilder( RemoteLoggerWithEnvironmentOverride::class ) ->onlyMethods( array( 'is_remote_logging_allowed' ) ) ->getMock(); - $this->sut->set_is_dev_or_local( true ); - $this->sut->method( 'is_remote_logging_allowed' )->willReturn( true ); + $this->sut->set_is_dev_or_local( true ); + $this->sut->method( 'is_remote_logging_allowed' )->willReturn( true ); - $this->assertFalse( $this->sut->handle( time(), 'error', 'Test message', array() ) ); - } + $this->assertFalse( $this->sut->handle( time(), 'error', 'Test message', array() ) ); + } - /** - * @testdox handle method successfully sends log - */ - public function test_handle_successful() { - $this->sut = $this->getMockBuilder( RemoteLoggerWithEnvironmentOverride::class ) + /** + * @testdox handle method successfully sends log + */ + public function test_handle_successful() { + $this->sut = $this->getMockBuilder( RemoteLoggerWithEnvironmentOverride::class ) ->onlyMethods( array( 'is_remote_logging_allowed' ) ) ->getMock(); - $this->sut->set_is_dev_or_local( false ); - $this->sut->method( 'is_remote_logging_allowed' )->willReturn( true ); + $this->sut->set_is_dev_or_local( false ); + $this->sut->method( 'is_remote_logging_allowed' )->willReturn( true ); - add_filter( - 'pre_http_request', - function ( $preempt, $args ) { - $this->assertArrayHasKey( 'body', $args ); - $this->assertArrayHasKey( 'headers', $args ); - return array( - 'response' => array( - 'code' => 200, - 'message' => 'OK', + add_filter( + 'pre_http_request', + function ( $preempt, $args ) { + $this->assertArrayHasKey( 'body', $args ); + $this->assertArrayHasKey( 'headers', $args ); + return array( + 'response' => array( + 'code' => 200, + 'message' => 'OK', + ), + 'body' => wp_json_encode( array( 'success' => true ) ), + ); + }, + 10, + 3 + ); + + $this->assertTrue( $this->sut->handle( time(), 'critical', 'Test message', array() ) ); + $this->assertTrue( WC_Rate_Limiter::retried_too_soon( RemoteLogger::RATE_LIMIT_ID ) ); + } + + /** + * @testdox handle method handles remote logging failure + */ + public function test_handle_remote_logging_failure() { + $this->sut = $this->getMockBuilder( RemoteLoggerWithEnvironmentOverride::class ) + ->onlyMethods( array( 'is_remote_logging_allowed' ) ) + ->getMock(); + + $this->sut->set_is_dev_or_local( false ); + $this->sut->method( 'is_remote_logging_allowed' )->willReturn( true ); + + add_filter( + 'pre_http_request', + function ( $preempt, $args, $url ) { + if ( 'https://public-api.wordpress.com/rest/v1.1/logstash' === $url ) { + throw new \Exception( 'Remote logging failed: A valid URL was not provided.' ); + } + return $preempt; + }, + 10, + 3 + ); + + $this->assertFalse( $this->sut->handle( time(), 'critical', 'Test message', array() ) ); + $this->assertTrue( WC_Rate_Limiter::retried_too_soon( RemoteLogger::RATE_LIMIT_ID ) ); + } + + /** + * @testdox is_third_party_error method correctly identifies third-party errors + * @dataProvider is_third_party_error_provider + * @param string $message The error message to check. + * @param array $context The context of the error. + * @param bool $expected_result The expected result of the check. + */ + public function test_is_third_party_error( $message, $context, $expected_result ) { + $result = $this->invoke_private_method( $this->sut, 'is_third_party_error', array( $message, $context ) ); + $this->assertEquals( $expected_result, $result ); + } + + /** + * Data provider for test_is_third_party_error. + * + * @return array[] Test cases. + */ + public function is_third_party_error_provider() { + return array( + array( 'Fatal error in ' . WC_ABSPATH . 'file.php', array(), false ), + array( 'Fatal error in /wp-content/file.php', array(), false ), + array( 'Fatal error in /wp-content/file.php', array( 'source' => 'fatal-errors' ), false ), + array( + 'Fatal error in /wp-content/plugins/3rd-plugin/file.php', + array( + 'source' => 'fatal-errors', + 'backtrace' => array( '/wp-content/plugins/3rd-plugin/file.php', WC_ABSPATH . 'file.php' ), ), - 'body' => wp_json_encode( array( 'success' => true ) ), - ); - }, - 10, - 3 - ); - - $this->assertTrue( $this->sut->handle( time(), 'critical', 'Test message', array() ) ); - $this->assertTrue( WC_Rate_Limiter::retried_too_soon( RemoteLogger::RATE_LIMIT_ID ) ); - } - - /** - * @testdox handle method handles remote logging failure - */ - public function test_handle_remote_logging_failure() { - $this->sut = $this->getMockBuilder( RemoteLoggerWithEnvironmentOverride::class ) - ->onlyMethods( array( 'is_remote_logging_allowed' ) ) - ->getMock(); - - $this->sut->set_is_dev_or_local( false ); - $this->sut->method( 'is_remote_logging_allowed' )->willReturn( true ); - - add_filter( - 'pre_http_request', - function ( $preempt, $args, $url ) { - if ( 'https://public-api.wordpress.com/rest/v1.1/logstash' === $url ) { - throw new \Exception( 'Remote logging failed: A valid URL was not provided.' ); - } - return $preempt; - }, - 10, - 3 - ); - - $this->assertFalse( $this->sut->handle( time(), 'critical', 'Test message', array() ) ); - $this->assertTrue( WC_Rate_Limiter::retried_too_soon( RemoteLogger::RATE_LIMIT_ID ) ); - } - - /** - * @testdox is_third_party_error method correctly identifies third-party errors - * @dataProvider is_third_party_error_provider - * @param string $message The error message to check. - * @param array $context The context of the error. - * @param bool $expected_result The expected result of the check. - */ - public function test_is_third_party_error( $message, $context, $expected_result ) { - $result = $this->invoke_private_method( $this->sut, 'is_third_party_error', array( $message, $context ) ); - $this->assertEquals( $expected_result, $result ); - } - - /** - * Data provider for test_is_third_party_error. - * - * @return array[] Test cases. - */ - public function is_third_party_error_provider() { - return array( - array( 'Fatal error in ' . WC_ABSPATH . 'file.php', array(), false ), - array( 'Fatal error in /wp-content/file.php', array(), false ), - array( 'Fatal error in /wp-content/file.php', array( 'source' => 'fatal-errors' ), false ), - array( - 'Fatal error in /wp-content/plugins/3rd-plugin/file.php', - array( - 'source' => 'fatal-errors', - 'backtrace' => array( '/wp-content/plugins/3rd-plugin/file.php', WC_ABSPATH . 'file.php' ), + false, ), - false, - ), - array( - 'Fatal error in /wp-content/plugins/woocommerce-3rd-plugin/file.php', array( - 'source' => 'fatal-errors', - 'backtrace' => array( WP_PLUGIN_DIR . 'woocommerce-3rd-plugin/file.php' ), + 'Fatal error in /wp-content/plugins/woocommerce-3rd-plugin/file.php', + array( + 'source' => 'fatal-errors', + 'backtrace' => array( WP_PLUGIN_DIR . 'woocommerce-3rd-plugin/file.php' ), + ), + true, ), - true, - ), - array( - 'Fatal error in /wp-content/plugins/3rd-plugin/file.php', array( - 'source' => 'fatal-errors', - 'backtrace' => array( WP_PLUGIN_DIR . '3rd-plugin/file.php' ), + 'Fatal error in /wp-content/plugins/3rd-plugin/file.php', + array( + 'source' => 'fatal-errors', + 'backtrace' => array( WP_PLUGIN_DIR . '3rd-plugin/file.php' ), + ), + true, ), - true, - ), - array( - 'Fatal error in /wp-content/plugins/3rd-plugin/file.php', array( - 'source' => 'fatal-errors', - 'backtrace' => array( array( 'file' => WP_PLUGIN_DIR . '3rd-plugin/file.php' ) ), + 'Fatal error in /wp-content/plugins/3rd-plugin/file.php', + array( + 'source' => 'fatal-errors', + 'backtrace' => array( array( 'file' => WP_PLUGIN_DIR . '3rd-plugin/file.php' ) ), + ), + true, ), - true, - ), - ); - } + ); + } - /** - * @testdox sanitize method correctly sanitizes paths - */ - public function test_sanitize() { - $message = WC_ABSPATH . 'includes/class-wc-test.php on line 123'; - $expected = '**/woocommerce/includes/class-wc-test.php on line 123'; - $result = $this->invoke_private_method( $this->sut, 'sanitize', array( $message ) ); - $this->assertEquals( $expected, $result ); - } + /** + * @testdox sanitize method correctly sanitizes paths + */ + public function test_sanitize() { + $message = WC_ABSPATH . 'includes/class-wc-test.php on line 123'; + $expected = '**/woocommerce/includes/class-wc-test.php on line 123'; + $result = $this->invoke_private_method( $this->sut, 'sanitize', array( $message ) ); + $this->assertEquals( $expected, $result ); + } - /** - * @testdox sanitize_trace method correctly sanitizes stack traces - */ - public function test_sanitize_trace() { - $trace = array( - WC_ABSPATH . 'includes/class-wc-test.php:123', - ABSPATH . 'wp-includes/plugin.php:456', - ); - $expected = "**/woocommerce/includes/class-wc-test.php:123\n**/wp-includes/plugin.php:456"; - $result = $this->invoke_private_method( $this->sut, 'sanitize_trace', array( $trace ) ); - $this->assertEquals( $expected, $result ); - } + /** + * @testdox sanitize_trace method correctly sanitizes stack traces + */ + public function test_sanitize_trace() { + $trace = array( + WC_ABSPATH . 'includes/class-wc-test.php:123', + ABSPATH . 'wp-includes/plugin.php:456', + ); + $expected = "**/woocommerce/includes/class-wc-test.php:123\n**/wp-includes/plugin.php:456"; + $result = $this->invoke_private_method( $this->sut, 'sanitize_trace', array( $trace ) ); + $this->assertEquals( $expected, $result ); + } - /** - * Setup common conditions for remote logging tests. - * - * @param bool $enabled Whether remote logging is enabled. - */ - private function setup_remote_logging_conditions( $enabled = true ) { - update_option( 'woocommerce_feature_remote_logging_enabled', $enabled ? 'yes' : 'no' ); - add_filter( 'option_woocommerce_allow_tracking', fn() => 'yes' ); - add_filter( 'option_woocommerce_remote_variant_assignment', fn() => 5 ); - add_filter( - 'plugins_api', - function ( $result, $action, $args ) { - if ( 'plugin_information' === $action && 'woocommerce' === $args->slug ) { - return (object) array( 'version' => '9.2.0' ); - } - return $result; - }, - 10, - 3 - ); - } + /** + * Setup common conditions for remote logging tests. + * + * @param bool $enabled Whether remote logging is enabled. + */ + private function setup_remote_logging_conditions( $enabled = true ) { + update_option( 'woocommerce_feature_remote_logging_enabled', $enabled ? 'yes' : 'no' ); + add_filter( 'option_woocommerce_allow_tracking', fn() => 'yes' ); + add_filter( 'option_woocommerce_remote_variant_assignment', fn() => 5 ); + $this->setup_mock_plugin_updates( $enabled ? WC()->version : '9.0.0' ); + } - /** - * Helper method to invoke private methods. - * - * @param object $obj Object instance. - * @param string $method_name Name of the private method. - * @param array $parameters Parameters to pass to the method. - * @return mixed - */ - private function invoke_private_method( $obj, $method_name, $parameters = array() ) { - $reflection = new \ReflectionClass( get_class( $obj ) ); - $method = $reflection->getMethod( $method_name ); - $method->setAccessible( true ); - return $method->invokeArgs( $obj, $parameters ); + /** + * Set up mock plugin updates. + * + * @param string $new_version The new version of WooCommerce to simulate. + */ + private function setup_mock_plugin_updates( $new_version ) { + $update_plugins = (object) array( + 'response' => array( + WC_PLUGIN_BASENAME => (object) array( + 'new_version' => $new_version, + 'package' => 'https://downloads.wordpress.org/plugin/woocommerce.zip', + 'slug' => 'woocommerce', + ), + ), + ); + add_filter( 'pre_site_transient_update_plugins', fn() => $update_plugins ); + } + + /** + * Helper method to invoke private methods. + * + * @param object $obj Object instance. + * @param string $method_name Name of the private method. + * @param array $parameters Parameters to pass to the method. + * @return mixed + */ + private function invoke_private_method( $obj, $method_name, $parameters = array() ) { + $reflection = new \ReflectionClass( get_class( $obj ) ); + $method = $reflection->getMethod( $method_name ); + $method->setAccessible( true ); + return $method->invokeArgs( $obj, $parameters ); + } } -} //phpcs:disable Generic.Files.OneObjectStructurePerFile.MultipleFound, Squiz.Classes.ClassFileName.NoMatch, Suin.Classes.PSR4.IncorrectClassName -/** - * Mock class that extends RemoteLogger to allow overriding is_dev_or_local_environment. - */ -class RemoteLoggerWithEnvironmentOverride extends RemoteLogger { /** - * The is_dev_or_local value. - * - * @var bool + * Mock class that extends RemoteLogger to allow overriding is_dev_or_local_environment. */ - private $is_dev_or_local = false; + class RemoteLoggerWithEnvironmentOverride extends RemoteLogger { + /** + * The is_dev_or_local value. + * + * @var bool + */ + private $is_dev_or_local = false; - /** - * Set the is_dev_or_local value. - * - * @param bool $value The value to set. - */ - public function set_is_dev_or_local( $value ) { - $this->is_dev_or_local = $value; + /** + * Set the is_dev_or_local value. + * + * @param bool $value The value to set. + */ + public function set_is_dev_or_local( $value ) { + $this->is_dev_or_local = $value; + } + + /** + * @inheritDoc + */ + protected function is_dev_or_local_environment() { + return $this->is_dev_or_local; + } } +//phpcs:enable Generic.Files.OneObjectStructurePerFile.MultipleFound, Squiz.Classes.ClassFileName.NoMatch, Suin.Classes.PSR4.IncorrectClassName +} +/** + * Mocks for global functions used in RemoteLogger.php + */ +namespace Automattic\WooCommerce\Internal\Logging { /** - * @inheritDoc + * The filter_input function will return NULL if we change the $_SERVER variables at runtime, so we + * need to override it in RemoteLogger's namespace when we want it to return a specific value for testing. + * + * @return mixed */ - protected function is_dev_or_local_environment() { - return $this->is_dev_or_local; + function filter_input() { + global $mock_filter_input, $mock_return; + + if ( true === $mock_filter_input ) { + return $mock_return; + } else { + return call_user_func_array( '\filter_input', func_get_args() ); + } } } -//phpcs:enable Generic.Files.OneObjectStructurePerFile.MultipleFound, Squiz.Classes.ClassFileName.NoMatch, Suin.Classes.PSR4.IncorrectClassName diff --git a/plugins/woocommerce/tests/php/src/Utilities/PluginUtilTests.php b/plugins/woocommerce/tests/php/src/Utilities/PluginUtilTests.php index ad0f44c40da..c41cb54094f 100644 --- a/plugins/woocommerce/tests/php/src/Utilities/PluginUtilTests.php +++ b/plugins/woocommerce/tests/php/src/Utilities/PluginUtilTests.php @@ -64,12 +64,12 @@ class PluginUtilTests extends \WC_Unit_Test_Case { if ( is_multisite() ) { $this->assertCount( 2, $active_valid_plugins ); - $this->assertContains( WP_PLUGIN_DIR . '/test3/test3.php', $active_valid_plugins ); + $this->assertContains( 'test3/test3.php', $active_valid_plugins ); } else { $this->assertCount( 1, $active_valid_plugins ); } - $this->assertContains( WP_PLUGIN_DIR . '/test1/test1.php', $active_valid_plugins ); + $this->assertContains( 'test1/test1.php', $active_valid_plugins ); if ( false === $orig_local_plugins ) { delete_option( 'active_plugins' ); diff --git a/plugins/woocommerce/woocommerce.php b/plugins/woocommerce/woocommerce.php index 391f2f454d9..b03085e7ac6 100644 --- a/plugins/woocommerce/woocommerce.php +++ b/plugins/woocommerce/woocommerce.php @@ -3,7 +3,7 @@ * Plugin Name: WooCommerce * Plugin URI: https://woocommerce.com/ * Description: An ecommerce toolkit that helps you sell anything. Beautifully. - * Version: 9.3.0-dev + * Version: 9.4.0-dev * Author: Automattic * Author URI: https://woocommerce.com * Text Domain: woocommerce diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9f69bf408ca..ec49d327648 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -256,7 +256,7 @@ importers: version: 10.5.0(sass@1.69.5)(webpack@5.89.0(webpack-cli@3.3.12)) ts-jest: specifier: ~29.1.1 - version: 29.1.1(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@16.18.68)(typescript@5.3.3)))(typescript@5.3.3) + version: 29.1.1(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.3)))(typescript@5.3.3) typescript: specifier: ^5.3.3 version: 5.3.3 @@ -292,7 +292,7 @@ importers: version: 2.3.2 debug: specifier: ^4.3.4 - version: 4.3.4(supports-color@9.4.0) + version: 4.3.4(supports-color@8.1.1) dompurify: specifier: ^2.4.7 version: 2.4.7 @@ -453,7 +453,7 @@ importers: version: 27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.3)) ts-jest: specifier: ~29.1.1 - version: 29.1.1(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@16.18.68)(typescript@5.3.3)))(typescript@5.3.3) + version: 29.1.1(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.3)))(typescript@5.3.3) typescript: specifier: ^5.3.3 version: 5.3.3 @@ -1446,7 +1446,7 @@ importers: version: 0.2.0 '@woocommerce/e2e-utils': specifier: ^0.1.6 - version: 0.1.6(@woocommerce/api@0.2.0)(encoding@0.1.13)(jest@29.7.0(@types/node@20.14.2)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.3.3)))(puppeteer@17.1.3(encoding@0.1.13))(react-native@0.73.0(@babel/core@7.12.9)(@babel/preset-env@7.12.7(@babel/core@7.12.9))(encoding@0.1.13)(react@18.3.1)) + version: 0.1.6(@woocommerce/api@0.2.0)(encoding@0.1.13)(jest@29.7.0(@types/node@22.4.0)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.4.0)(typescript@5.3.3)))(puppeteer@17.1.3(encoding@0.1.13))(react-native@0.73.0(@babel/core@7.12.9)(@babel/preset-env@7.12.7(@babel/core@7.12.9))(encoding@0.1.13)(react@18.3.1)) '@wordpress/deprecated': specifier: wp-6.0 version: 3.6.1 @@ -2474,7 +2474,7 @@ importers: version: 7.23.5 '@storybook/addon-knobs': specifier: ^7.0.2 - version: 7.0.2(@storybook/addons@7.5.2(react-dom@18.3.1(react@17.0.2))(react@17.0.2))(@storybook/api@7.6.4(react-dom@18.3.1(react@17.0.2))(react@17.0.2))(@storybook/components@7.6.4(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2))(@storybook/core-events@7.6.4)(@storybook/theming@7.6.4(react-dom@18.3.1(react@17.0.2))(react@17.0.2))(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + version: 7.0.2(@storybook/addons@7.5.2(react-dom@18.3.1(react@17.0.2))(react@17.0.2))(@storybook/api@7.6.4(react-dom@18.3.1(react@17.0.2))(react@17.0.2))(@storybook/components@7.6.4(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2))(@storybook/core-events@7.6.4)(@storybook/theming@7.6.4(react-dom@18.3.1(react@17.0.2))(react@17.0.2))(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) '@testing-library/react': specifier: 12.1.3 version: 12.1.3(react-dom@18.3.1(react@17.0.2))(react@17.0.2) @@ -2616,6 +2616,9 @@ importers: '@wordpress/data': specifier: wp-6.0 version: 6.6.1(react@17.0.2) + '@wordpress/dataviews': + specifier: ^4.2.0 + version: 4.2.0(@emotion/is-prop-valid@1.2.1)(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/date': specifier: wp-6.0 version: 4.6.1 @@ -2641,8 +2644,8 @@ importers: specifier: wp-6.0 version: 4.6.1 '@wordpress/icons': - specifier: wp-6.0 - version: 8.2.3 + specifier: 10.6.0 + version: 10.6.0(react@17.0.2) '@wordpress/interface': specifier: wp-6.0 version: 4.5.6(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react-with-direction@1.4.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react@17.0.2) @@ -2661,6 +2664,9 @@ importers: '@wordpress/preferences': specifier: wp-6.0 version: 1.2.5(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react-with-direction@1.4.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react@17.0.2) + '@wordpress/private-apis': + specifier: ^1.6.0 + version: 1.6.0 '@wordpress/url': specifier: wp-6.0 version: 3.7.1 @@ -2875,7 +2881,7 @@ importers: version: 5.0.5 ts-jest: specifier: ~29.1.1 - version: 29.1.1(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@16.18.68)(typescript@5.3.3)))(typescript@5.3.3) + version: 29.1.1(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.3)))(typescript@5.3.3) typescript: specifier: ^5.3.3 version: 5.3.3 @@ -2887,7 +2893,7 @@ importers: dependencies: debug: specifier: ^4.3.4 - version: 4.3.4(supports-color@9.4.0) + version: 4.3.4(supports-color@8.1.1) devDependencies: '@babel/core': specifier: ^7.23.5 @@ -2996,7 +3002,7 @@ importers: version: 1.2.5(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react-with-direction@1.4.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react@17.0.2) debug: specifier: ^4.3.4 - version: 4.3.4(supports-color@9.4.0) + version: 4.3.4(supports-color@8.1.1) prop-types: specifier: ^15.8.1 version: 15.8.1 @@ -3117,8 +3123,8 @@ importers: specifier: 7.12.1 version: 7.12.1(@babel/core@7.12.9) '@playwright/test': - specifier: ^1.45.1 - version: 1.45.1 + specifier: ^1.46.1 + version: 1.46.1 '@typescript-eslint/eslint-plugin': specifier: ^5.62.0 version: 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.55.0)(typescript@5.3.3))(eslint@8.55.0)(typescript@5.3.3) @@ -3160,7 +3166,7 @@ importers: version: 3.0.2(@babel/core@7.12.9) '@wordpress/e2e-test-utils-playwright': specifier: wp-6.6 - version: 1.0.1(@playwright/test@1.45.1)(encoding@0.1.13)(typescript@5.3.3) + version: 1.0.1(@playwright/test@1.46.1)(encoding@0.1.13)(typescript@5.3.3) '@wordpress/env': specifier: ^10.1.0 version: 10.5.0 @@ -3313,7 +3319,7 @@ importers: version: 3.6.1 '@wordpress/edit-site': specifier: 5.15.0 - version: 5.15.0(patch_hash=6y3l6gxu33zybfmvbjd23dtqda)(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@preact/signals-core@1.5.1)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + version: 5.15.0(patch_hash=6y3l6gxu33zybfmvbjd23dtqda)(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@preact/signals-core@1.5.1)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/element': specifier: wp-6.0 version: 4.4.1 @@ -3331,7 +3337,7 @@ importers: version: 8.2.3 '@wordpress/interface': specifier: ^5.24.0 - version: 5.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + version: 5.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/keycodes': specifier: wp-6.0 version: 3.6.1 @@ -3373,7 +3379,7 @@ importers: version: 3.34.0 debug: specifier: ^4.3.4 - version: 4.3.4(supports-color@9.4.0) + version: 4.3.4(supports-color@8.1.1) dompurify: specifier: ^2.4.7 version: 2.4.7 @@ -3470,7 +3476,7 @@ importers: version: 0.5.11(@types/webpack@4.41.38)(react-refresh@0.14.0)(type-fest@2.19.0)(webpack-dev-server@4.15.1(debug@4.3.4)(webpack-cli@4.10.0)(webpack@5.89.0))(webpack-hot-middleware@2.25.4)(webpack@5.89.0(webpack-cli@4.10.0)) '@statelyai/inspect': specifier: ^0.3.1 - version: 0.3.1(ws@8.17.0)(xstate@4.37.1) + version: 0.3.1(ws@8.18.0)(xstate@4.37.1) '@testing-library/dom': specifier: 8.11.3 version: 8.11.3 @@ -3650,7 +3656,7 @@ importers: version: 21.36.0(postcss@8.4.32)(stylelint@14.16.1) '@xstate/inspect': specifier: 0.8.0 - version: 0.8.0(@types/ws@8.5.10)(ws@8.17.0)(xstate@4.37.1) + version: 0.8.0(@types/ws@8.5.10)(ws@8.18.0)(xstate@4.37.1) '@xstate/test': specifier: 0.5.1 version: 0.5.1(xstate@4.37.1) @@ -4083,7 +4089,7 @@ importers: version: '@wordpress/components@14.2.0(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react-with-direction@1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(reakit-utils@0.15.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(redux@4.2.1)' wordpress-components-slotfill: specifier: npm:@wordpress/components@wp-6.5 - version: '@wordpress/components@26.0.6(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)' + version: '@wordpress/components@26.0.6(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)' optionalDependencies: ndb: specifier: 1.1.5 @@ -4120,8 +4126,8 @@ importers: specifier: 5.0.0 version: 5.0.0 '@playwright/test': - specifier: ^1.45.1 - version: 1.45.1 + specifier: ^1.46.1 + version: 1.46.1 '@storybook/addon-a11y': specifier: 7.5.2 version: 7.5.2(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -4226,7 +4232,7 @@ importers: version: 2.2.0 '@types/wordpress__editor': specifier: ^13.6.7 - version: 13.6.7(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 13.6.7(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/wordpress__notices': specifier: ^3.27.6 version: 3.27.6(react@18.3.1) @@ -4298,7 +4304,7 @@ importers: version: 10.12.0(encoding@0.1.13)(jest@29.7.0(@types/node@16.18.68)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.2)))(puppeteer-core@21.6.0(encoding@0.1.13)) '@wordpress/e2e-test-utils-playwright': specifier: wp-6.6 - version: 1.0.1(@playwright/test@1.45.1)(encoding@0.1.13)(typescript@5.3.2) + version: 1.0.1(@playwright/test@1.46.1)(encoding@0.1.13)(typescript@5.3.2) '@wordpress/e2e-tests': specifier: ^4.9.2 version: 4.9.2(@swc/core@1.3.100)(@types/webpack@4.41.38)(encoding@0.1.13)(esbuild@0.18.20)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.56.0(eslint@8.55.0)(typescript@5.3.2))(eslint-import-resolver-webpack@0.13.2)(eslint-plugin-import@2.28.1)(eslint@8.55.0))(eslint-import-resolver-webpack@0.13.2(eslint-plugin-import@2.28.1)(webpack@5.91.0(@swc/core@1.3.100)(esbuild@0.18.20)(webpack-cli@5.1.4)))(file-loader@6.2.0(webpack@5.91.0(@swc/core@1.3.100)(esbuild@0.18.20)(webpack-cli@5.1.4)))(jest@29.7.0(@types/node@16.18.68)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.2)))(node-notifier@8.0.2)(puppeteer-core@21.6.0(encoding@0.1.13))(puppeteer@17.1.3(encoding@0.1.13))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.2))(type-fest@2.19.0)(typescript@5.3.2)(webpack-hot-middleware@2.25.4) @@ -4663,7 +4669,7 @@ importers: version: 27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.3)) ts-jest: specifier: ~29.1.1 - version: 29.1.1(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@16.18.68)(typescript@5.3.3)))(typescript@5.3.3) + version: 29.1.1(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.3)))(typescript@5.3.3) ts-node: specifier: ^10.9.2 version: 10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.3) @@ -4848,7 +4854,7 @@ importers: version: 1.2.2 ts-jest: specifier: ~29.1.1 - version: 29.1.1(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@16.18.68)(typescript@5.3.3)))(typescript@5.3.3) + version: 29.1.1(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.3)))(typescript@5.3.3) ts-loader: specifier: ^9.5.1 version: 9.5.1(typescript@5.3.3)(webpack@5.89.0(webpack-cli@3.3.12)) @@ -5004,67 +5010,64 @@ importers: version: 7.23.5 '@storybook/addon-a11y': specifier: 6.5.17-alpha.0 - version: 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + version: 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/addon-actions': specifier: 6.5.17-alpha.0 - version: 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + version: 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/addon-console': specifier: ^1.2.3 - version: 1.2.3(@storybook/addon-actions@6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2)) + version: 1.2.3(@storybook/addon-actions@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) '@storybook/addon-controls': specifier: 6.5.17-alpha.0 - version: 6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3) + version: 6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3) '@storybook/addon-docs': specifier: 6.5.17-alpha.0 - version: 6.5.17-alpha.0(@babel/core@7.24.7)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)(webpack@5.89.0) + version: 6.5.17-alpha.0(@babel/core@7.24.7)(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3)(webpack@5.89.0) '@storybook/addon-knobs': specifier: ^6.4.0 - version: 6.4.0(@storybook/addons@6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(@storybook/api@6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(@storybook/components@6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(@storybook/core-events@6.5.17-alpha.0)(@storybook/theming@6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + version: 6.4.0(@storybook/addons@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/api@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/components@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/core-events@6.5.17-alpha.0)(@storybook/theming@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/addon-links': specifier: 6.5.17-alpha.0 - version: 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + version: 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/addon-storysource': specifier: 6.5.17-alpha.0 - version: 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + version: 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/addon-viewport': specifier: 6.5.17-alpha.0 - version: 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + version: 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/addons': specifier: 6.5.17-alpha.0 - version: 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + version: 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/api': specifier: 6.5.17-alpha.0 - version: 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + version: 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/builder-webpack5': specifier: 6.5.17-alpha.0 - version: 6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3) + version: 6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3) '@storybook/components': specifier: 6.5.17-alpha.0 - version: 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + version: 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/core-events': specifier: 6.5.17-alpha.0 version: 6.5.17-alpha.0 '@storybook/manager-webpack5': specifier: 6.5.17-alpha.0 - version: 6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3) + version: 6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3) '@storybook/react': specifier: 6.5.17-alpha.0 - version: 6.5.17-alpha.0(@babel/core@7.24.7)(@storybook/builder-webpack5@6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3))(@storybook/manager-webpack5@6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3))(@types/webpack@4.41.38)(encoding@0.1.13)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(require-from-string@2.0.2)(type-fest@2.19.0)(typescript@5.3.3)(webpack-dev-server@4.15.1(webpack@5.89.0))(webpack-hot-middleware@2.25.4) + version: 6.5.17-alpha.0(@babel/core@7.24.7)(@storybook/builder-webpack5@6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3))(@storybook/manager-webpack5@6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3))(@types/webpack@4.41.38)(encoding@0.1.13)(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(require-from-string@2.0.2)(type-fest@2.19.0)(typescript@5.3.3)(webpack-dev-server@4.15.1(webpack@5.89.0))(webpack-hot-middleware@2.25.4) '@storybook/theming': specifier: 6.5.17-alpha.0 - version: 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + version: 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@woocommerce/eslint-plugin': specifier: workspace:* version: link:../../packages/js/eslint-plugin react: - specifier: ^17.0.2 - version: 17.0.2 + specifier: 18.3.1 + version: 18.3.1 react-dom: - specifier: ^17.0.2 - version: 17.0.2(react@17.0.2) - react18: - specifier: npm:react@^18.3.0 - version: react@18.3.1 + specifier: 18.3.1 + version: 18.3.1(react@18.3.1) typescript: specifier: ^5.3.3 version: 5.3.3 @@ -5098,26 +5101,30 @@ packages: resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} engines: {node: '>=6.0.0'} + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + '@ariakit/core@0.3.11': resolution: {integrity: sha512-+MnOeqnA4FLI/7vqsZLbZQHHN4ofd9kvkNjz44fNi0gqmD+ZbMWiDkFAvZII75dYnxYw5ZPpWjA4waK22VBWig==} - '@ariakit/core@0.3.8': - resolution: {integrity: sha512-LlSCwbyyozMX4ZEobpYGcv1LFqOdBTdTYPZw3lAVgLcFSNivsazi3NkKM9qNWNIu00MS+xTa0+RuIcuWAjlB2Q==} - '@ariakit/core@0.4.5': resolution: {integrity: sha512-e294+bEcyzt/H/kO4fS5/czLAlkF7PY+Kul3q2z54VY+GGay8NlVs9UezAB7L4jUBlYRAXwp7/1Sq3R7b+MZ7w==} + '@ariakit/core@0.4.9': + resolution: {integrity: sha512-nV0B/OTK/0iB+P9RC7fudznYZ8eR6rR1F912Zc54e3+wSW5RrRvNOiRxyMrgENidd4R7cCMDw77XJLSBLKgEPQ==} + '@ariakit/react-core@0.3.14': resolution: {integrity: sha512-16Qj6kDPglpdWtU5roY9q+G66naOjauTY5HvUIaL2aLY0187ATaRrABIKoMMzTtJyhvsud4jFlzivz+/zCQ8yw==} peerDependencies: react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - '@ariakit/react-core@0.3.9': - resolution: {integrity: sha512-K1Rcxr6FpF0n3L7Uvo+e5hm+zqoZmXLRcYF/skI+/j+ole+uNbnsnfGhG1avqJlklqH4bmkFkjZzmMdOnUC0Ig==} + '@ariakit/react-core@0.4.10': + resolution: {integrity: sha512-r6DZmtHBmSoOj848+RpBwdZy/55YxPhMhfH14JIO2OLn1F6iSFkQwR7AAGpIrlYycWJFSF7KrQu50O+SSfFJdQ==} peerDependencies: - react: ^17.0.0 || ^18.0.0 - react-dom: ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 '@ariakit/react-core@0.4.5': resolution: {integrity: sha512-ciTYPwpj/+mdA+EstveEnoygbx5e4PXQJxfkLKy4lkTkDJJUS9GcbYhdnIFJVUta6P1YFvzkIKo+/y9mcbMKJg==} @@ -5131,11 +5138,11 @@ packages: react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - '@ariakit/react@0.3.9': - resolution: {integrity: sha512-gC+gibh2go8wvBqzYXavlHKwAfmee5GUMrPSQ9WBBLIfm9nQElujxcHJydaRx+ULR5FbOnbZVC3vU2ic8hSrNw==} + '@ariakit/react@0.4.10': + resolution: {integrity: sha512-c1+6sNLj57aAXrBZMCVGG+OXeFrPAG0TV1jT7oPJcN/KLRs3aCuO3CCJVep/eKepFzzK01kNRGYX3wPT1TXPNw==} peerDependencies: - react: ^17.0.0 || ^18.0.0 - react-dom: ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 '@ariakit/react@0.4.5': resolution: {integrity: sha512-GUHxaOY1JZrJUHkuV20IY4NWcgknhqTQM0qCQcVZDCi+pJiWchUjTG+UyIr/Of02hU569qnQ7yovskCf+V3tNg==} @@ -5276,6 +5283,10 @@ packages: resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.25.2': + resolution: {integrity: sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==} + engines: {node: '>=6.9.0'} + '@babel/core@7.12.9': resolution: {integrity: sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==} engines: {node: '>=6.9.0'} @@ -5292,6 +5303,10 @@ packages: resolution: {integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==} engines: {node: '>=6.9.0'} + '@babel/core@7.25.2': + resolution: {integrity: sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==} + engines: {node: '>=6.9.0'} + '@babel/eslint-parser@7.23.3': resolution: {integrity: sha512-9bTuNlyx7oSstodm1cR1bECj4fkiknsDa1YniISkJemMY3DGhJNYBECbe6QD/q54mp2J8VO66jW3/7uP//iFCw==} engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0} @@ -5307,6 +5322,10 @@ packages: resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==} engines: {node: '>=6.9.0'} + '@babel/generator@7.25.0': + resolution: {integrity: sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==} + engines: {node: '>=6.9.0'} + '@babel/helper-annotate-as-pure@7.22.5': resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} engines: {node: '>=6.9.0'} @@ -5327,6 +5346,10 @@ packages: resolution: {integrity: sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==} engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.25.2': + resolution: {integrity: sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==} + engines: {node: '>=6.9.0'} + '@babel/helper-create-class-features-plugin@7.23.5': resolution: {integrity: sha512-QELlRWxSpgdwdJzSJn4WAhKC+hvw/AtHbbrIoncKHkhKKR/luAlKkgBDcri1EzWAo8f8VvYVryEHN4tax/V67A==} engines: {node: '>=6.9.0'} @@ -5339,8 +5362,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-create-class-features-plugin@7.24.7': - resolution: {integrity: sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg==} + '@babel/helper-create-class-features-plugin@7.25.0': + resolution: {integrity: sha512-GYM6BxeQsETc9mnct+nIIpf63SAyzvyYN7UB/IlTyd+MBg06afFGp0mIeUqGyWgS2mxad6vqbMrHVlaL3m70sQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -5351,8 +5374,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-create-regexp-features-plugin@7.24.7': - resolution: {integrity: sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==} + '@babel/helper-create-regexp-features-plugin@7.25.2': + resolution: {integrity: sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -5405,8 +5428,8 @@ packages: resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==} engines: {node: '>=6.9.0'} - '@babel/helper-member-expression-to-functions@7.24.7': - resolution: {integrity: sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==} + '@babel/helper-member-expression-to-functions@7.24.8': + resolution: {integrity: sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA==} engines: {node: '>=6.9.0'} '@babel/helper-module-imports@7.22.15': @@ -5429,6 +5452,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-module-transforms@7.25.2': + resolution: {integrity: sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-optimise-call-expression@7.22.5': resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==} engines: {node: '>=6.9.0'} @@ -5448,14 +5477,18 @@ packages: resolution: {integrity: sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==} engines: {node: '>=6.9.0'} + '@babel/helper-plugin-utils@7.24.8': + resolution: {integrity: sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==} + engines: {node: '>=6.9.0'} + '@babel/helper-remap-async-to-generator@7.22.20': resolution: {integrity: sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-remap-async-to-generator@7.24.7': - resolution: {integrity: sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==} + '@babel/helper-remap-async-to-generator@7.25.0': + resolution: {integrity: sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -5466,8 +5499,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-replace-supers@7.24.7': - resolution: {integrity: sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==} + '@babel/helper-replace-supers@7.25.0': + resolution: {integrity: sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -5504,6 +5537,10 @@ packages: resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.24.8': + resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.22.20': resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} engines: {node: '>=6.9.0'} @@ -5520,12 +5557,16 @@ packages: resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.24.8': + resolution: {integrity: sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==} + engines: {node: '>=6.9.0'} + '@babel/helper-wrap-function@7.22.20': resolution: {integrity: sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==} engines: {node: '>=6.9.0'} - '@babel/helper-wrap-function@7.24.7': - resolution: {integrity: sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==} + '@babel/helper-wrap-function@7.25.0': + resolution: {integrity: sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ==} engines: {node: '>=6.9.0'} '@babel/helpers@7.23.5': @@ -5536,6 +5577,10 @@ packages: resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==} engines: {node: '>=6.9.0'} + '@babel/helpers@7.25.0': + resolution: {integrity: sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==} + engines: {node: '>=6.9.0'} + '@babel/highlight@7.23.4': resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} engines: {node: '>=6.9.0'} @@ -5554,6 +5599,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.25.3': + resolution: {integrity: sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3': resolution: {integrity: sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==} engines: {node: '>=6.9.0'} @@ -5908,8 +5958,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-block-scoping@7.24.7': - resolution: {integrity: sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==} + '@babel/plugin-transform-block-scoping@7.25.0': + resolution: {integrity: sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -5932,8 +5982,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-classes@7.24.7': - resolution: {integrity: sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw==} + '@babel/plugin-transform-classes@7.25.0': + resolution: {integrity: sha512-xyi6qjr/fYU304fiRwFbekzkqVJZ6A7hOjWZd+89FVcBqPV3S9Wuozz82xdpLspckeaafntbzglaW4pqpzvtSw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -5956,8 +6006,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-destructuring@7.24.7': - resolution: {integrity: sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw==} + '@babel/plugin-transform-destructuring@7.24.8': + resolution: {integrity: sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -5998,8 +6048,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-flow-strip-types@7.24.7': - resolution: {integrity: sha512-cjRKJ7FobOH2eakx7Ja+KpJRj8+y+/SiB3ooYm/n2UJfxu0oEaOoxOinitkJcPqv9KxS0kxTGPUaR7L2XcXDXA==} + '@babel/plugin-transform-flow-strip-types@7.25.2': + resolution: {integrity: sha512-InBZ0O8tew5V0K6cHcQ+wgxlrjOw1W4wDXLkOTjLRD8GYhTSkxTVBtdy3MMtvYBrbAWa1Qm3hNoTc1620Yj+Mg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -6022,8 +6072,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-function-name@7.24.7': - resolution: {integrity: sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==} + '@babel/plugin-transform-function-name@7.25.1': + resolution: {integrity: sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -6040,8 +6090,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-literals@7.24.7': - resolution: {integrity: sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==} + '@babel/plugin-transform-literals@7.25.2': + resolution: {integrity: sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -6070,8 +6120,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-commonjs@7.24.7': - resolution: {integrity: sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ==} + '@babel/plugin-transform-modules-commonjs@7.24.8': + resolution: {integrity: sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -6226,8 +6276,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx@7.24.7': - resolution: {integrity: sha512-+Dj06GDZEFRYvclU6k4bme55GKBEWUmByM/eoKuqg4zTNQHiApWRhQph5fxQB2wAEFvRzL1tOEj1RJ19wJrhoA==} + '@babel/plugin-transform-react-jsx@7.25.2': + resolution: {integrity: sha512-KQsqEAVBpU82NM/B/N9j9WOdphom1SZH3R+2V7INrQUH+V9EBFwZsEJl8eBIVeQE62FxJCc70jzEZwqU7RcVqA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -6322,8 +6372,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typescript@7.24.7': - resolution: {integrity: sha512-iLD3UNkgx2n/HrjBesVbYX6j0yqn/sJktvbtKKgcaLIQ4bTTQ8obAypc1VpyHPD2y4Phh9zHOaAt8e/L14wCpw==} + '@babel/plugin-transform-typescript@7.25.2': + resolution: {integrity: sha512-lBwRvjSmqiMYe/pS0+1gggjJleUJi7NzjvQ1Fkqtt69hBa/0t1YuW/MLQMAPixfwaQOHUXsd6jeU3Z+vdGv3+A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -6460,6 +6510,10 @@ packages: resolution: {integrity: sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==} engines: {node: '>=6.9.0'} + '@babel/runtime@7.25.0': + resolution: {integrity: sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==} + engines: {node: '>=6.9.0'} + '@babel/template@7.22.15': resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} engines: {node: '>=6.9.0'} @@ -6468,6 +6522,10 @@ packages: resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} engines: {node: '>=6.9.0'} + '@babel/template@7.25.0': + resolution: {integrity: sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==} + engines: {node: '>=6.9.0'} + '@babel/traverse@7.23.5': resolution: {integrity: sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==} engines: {node: '>=6.9.0'} @@ -6476,6 +6534,10 @@ packages: resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==} engines: {node: '>=6.9.0'} + '@babel/traverse@7.25.3': + resolution: {integrity: sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==} + engines: {node: '>=6.9.0'} + '@babel/types@7.23.5': resolution: {integrity: sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==} engines: {node: '>=6.9.0'} @@ -6488,6 +6550,10 @@ packages: resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==} engines: {node: '>=6.9.0'} + '@babel/types@7.25.2': + resolution: {integrity: sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==} + engines: {node: '>=6.9.0'} + '@bartekbp/typescript-checkstyle@5.0.0': resolution: {integrity: sha512-c4HUbr7V/6M5W7dGpEkLtjfAM6scvGr2/9OLw1Hv7ohfsnrtd7X43bjcHjX1AmRH27PmTpv1YjihbwU72MTPcw==} hasBin: true @@ -6855,18 +6921,12 @@ packages: '@floating-ui/core@0.6.2': resolution: {integrity: sha512-jktYRmZwmau63adUG3GKOAVCofBXkk55S/zQ94XOorAHhwqFIOFAy1rSp2N0Wp6/tGbe9V3u/ExlGZypyY17rg==} - '@floating-ui/core@0.7.3': - resolution: {integrity: sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg==} - '@floating-ui/core@1.5.2': resolution: {integrity: sha512-Ii3MrfY/GAIN3OhXNzpCKaLxHQfJF9qvwq/kEJYdqDxeIHa01K8sldugal6TmeeXl+WMvhv9cnVzUTaFFJF09A==} '@floating-ui/dom@0.4.5': resolution: {integrity: sha512-b+prvQgJt8pieaKYMSJBXHxX/DYwdLsAWxKYqnO5dO2V4oo/TYBZJAUQCVNjTWWsrs6o4VDrNcP9+E70HAhJdw==} - '@floating-ui/dom@0.5.4': - resolution: {integrity: sha512-419BMceRLq0RrmTSDxn8hf9R3VCJv2K9PUfugh5JyEFmdjzDo+e8U5EdR8nzKq8Yj1htzLm3b6eQEEam3/rrtg==} - '@floating-ui/dom@1.5.3': resolution: {integrity: sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==} @@ -6876,12 +6936,6 @@ packages: react: '>=16.8.0' react-dom: '>=16.8.0' - '@floating-ui/react-dom@0.7.2': - resolution: {integrity: sha512-1T0sJcpHgX/u4I1OzIEhlcrvkUN8ln39nz7fMoE/2HDHrPiMFoOGR7++GYyfUmIQHkkrTinaeQsO3XWubjSvGg==} - peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' - '@floating-ui/react-dom@1.3.0': resolution: {integrity: sha512-htwHm67Ji5E/pROEAr7f8IKFShuiCKHwUC/UY4vC3I5jiSvGFAYnSYiZO5MlGmads+QqvUkR9ANHEguGrDv72g==} peerDependencies: @@ -7668,8 +7722,8 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@playwright/test@1.45.1': - resolution: {integrity: sha512-Wo1bWTzQvGA7LyKGIZc8nFSTFf2TkthGIFBR+QVNilvwouGzFd4PYukZe3rvf5PSqjHi1+1NyKSDZKcQWETzaA==} + '@playwright/test@1.46.1': + resolution: {integrity: sha512-Fq6SwLujA/DOIvNC2EL/SojJnkKf/rAwJ//APpJJHRyMi1PdKrY3Az+4XNQ51N4RTbItbIByQ0jgd1tayq1aeA==} engines: {node: '>=18'} hasBin: true @@ -7737,12 +7791,6 @@ packages: '@radix-ui/primitive@1.0.1': resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} - '@radix-ui/react-arrow@1.0.2': - resolution: {integrity: sha512-fqYwhhI9IarZ0ll2cUSfKuXHlJK0qE4AfnRrPBbRwEH/4mGQn04/QFGomLi8TXWIdv9WJk//KgGm+aDxVIr1wA==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-arrow@1.0.3': resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==} peerDependencies: @@ -7756,12 +7804,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collection@1.0.2': - resolution: {integrity: sha512-s8WdQQ6wNXpaxdZ308KSr8fEWGrg4un8i4r/w7fhiS4ElRNjk5rRcl0/C6TANG2LvLOGIxtzo/jAg6Qf73TEBw==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-collection@1.0.3': resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} peerDependencies: @@ -7809,11 +7851,6 @@ packages: react: ^16.8 || ^17.0 || ^18.0 react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-direction@1.0.0': - resolution: {integrity: sha512-2HV05lGUgYcA6xgLQ4BKPDmtL+QbIZYH5fCOTAOOcJ5O0QbWS3i9lKaurLzliYUDhORI2Qr3pyjhJh44lKA3rQ==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-direction@1.0.1': resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==} peerDependencies: @@ -7829,12 +7866,6 @@ packages: react: ^16.8 || ^17.0 || ^18.0 react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-dismissable-layer@1.0.3': - resolution: {integrity: sha512-nXZOvFjOuHS1ovumntGV7NNoLaEp9JEvTht3MBjP44NSW5hUKj/8OnfN3+8WmB+CEhN44XaGhpHoSsUIEl5P7Q==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-dismissable-layer@1.0.4': resolution: {integrity: sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==} peerDependencies: @@ -7848,12 +7879,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dropdown-menu@2.0.4': - resolution: {integrity: sha512-y6AT9+MydyXcByivdK1+QpjWoKaC7MLjkS/cH1Q3keEyMvDkiY85m8o2Bi6+Z1PPUlCsMULopxagQOSfN0wahg==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-focus-guards@1.0.0': resolution: {integrity: sha512-UagjDk4ijOAnGu4WMUPj9ahi7/zJJqNZ9ZAiGPp7waUWJO0O1aWXi/udPphI0IUjvrhBsZJGSN66dR2dsueLWQ==} peerDependencies: @@ -7874,12 +7899,6 @@ packages: react: ^16.8 || ^17.0 || ^18.0 react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-focus-scope@1.0.2': - resolution: {integrity: sha512-spwXlNTfeIprt+kaEWE/qYuYT3ZAqJiAGjN/JgdvgVDTu8yc+HuX+WOWXrKliKnLnwck0F6JDkqIERncnih+4A==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-focus-scope@1.0.3': resolution: {integrity: sha512-upXdPfqI4islj2CslyfUBNlaJCPybbqRHAi1KER7Isel9Q2AtSJ0zRBZv8mWQiFXD2nyAJ4BhC3yXgZ6kMBSrQ==} peerDependencies: @@ -7907,18 +7926,6 @@ packages: '@types/react': optional: true - '@radix-ui/react-menu@2.0.4': - resolution: {integrity: sha512-mzKR47tZ1t193trEqlQoJvzY4u9vYfVH16ryBrVrCAGZzkgyWnMQYEZdUkM7y8ak9mrkKtJiqB47TlEnubeOFQ==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - - '@radix-ui/react-popper@1.1.1': - resolution: {integrity: sha512-keYDcdMPNMjSC8zTsZ8wezUMiWM9Yj14wtF3s0PTIs9srnEPC9Kt2Gny1T3T81mmSeyDjZxsD9N5WCwNNb712w==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-popper@1.1.2': resolution: {integrity: sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==} peerDependencies: @@ -7938,12 +7945,6 @@ packages: react: ^16.8 || ^17.0 || ^18.0 react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-portal@1.0.2': - resolution: {integrity: sha512-swu32idoCW7KA2VEiUZGBSu9nB6qwGdV6k6HYhUoOo3M1FFpD+VgLzUqtt3mwL1ssz7r2x8MggpLSQach2Xy/Q==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-portal@1.0.3': resolution: {integrity: sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==} peerDependencies: @@ -7969,12 +7970,6 @@ packages: react: ^16.8 || ^17.0 || ^18.0 react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-primitive@1.0.2': - resolution: {integrity: sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-primitive@1.0.3': resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==} peerDependencies: @@ -7988,12 +7983,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-roving-focus@1.0.3': - resolution: {integrity: sha512-stjCkIoMe6h+1fWtXlA6cRfikdBzCLp3SnVk7c48cv/uy3DTGoXhN76YaOYUJuy3aEDvDIKwKR5KSmvrtPvQPQ==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-roving-focus@1.0.4': resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==} peerDependencies: @@ -8038,11 +8027,6 @@ packages: peerDependencies: react: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-slot@1.0.1': - resolution: {integrity: sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-slot@1.0.2': resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} peerDependencies: @@ -8124,11 +8108,6 @@ packages: peerDependencies: react: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-use-escape-keydown@1.0.2': - resolution: {integrity: sha512-DXGim3x74WgUv+iMNCF+cAo8xUHHeqvjx8zs7trKf+FkQKPQXLk2sX7Gx1ysH7Q76xCpZuxIJE7HLPxRE+Q+GA==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-use-escape-keydown@1.0.3': resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==} peerDependencies: @@ -8161,11 +8140,6 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-rect@1.0.0': - resolution: {integrity: sha512-TB7pID8NRMEHxb/qQJpvSt3hQU4sqNPM1VCTjTRjEOa7cEop/QMuq8S6fb/5Tsz64kqSvB9WnwsDHtjnrM9qew==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-use-rect@1.0.1': resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==} peerDependencies: @@ -8175,11 +8149,6 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-size@1.0.0': - resolution: {integrity: sha512-imZ3aYcoYCKhhgNpkNDh/aTiU05qw9hX+HHI1QDBTyIlcFjgeFlKKySNGMwTp7nYFLQg/j0VA2FmCY4WPDDHMg==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 - '@radix-ui/react-use-size@1.0.1': resolution: {integrity: sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==} peerDependencies: @@ -8202,9 +8171,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/rect@1.0.0': - resolution: {integrity: sha512-d0O68AYy/9oeEy1DdC07bz1/ZXX+DqCskRd3i4JzLSTXwefzaepQrKjXC7aNM8lTHjFLDO0pDgaEiQ7jEk+HVg==} - '@radix-ui/rect@1.0.1': resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==} @@ -8217,8 +8183,8 @@ packages: '@react-native-community/cli-debugger-ui@12.1.1': resolution: {integrity: sha512-q427jvbJ0WdDuS6HNdc3EbmUu/dX/+FWCcZI60xB7m1i/8p+LzmrsoR2yIJCricsAIV3hhiFOGfquZDgrbF27Q==} - '@react-native-community/cli-debugger-ui@12.3.6': - resolution: {integrity: sha512-SjUKKsx5FmcK9G6Pb6UBFT0s9JexVStK5WInmANw75Hm7YokVvHEgtprQDz2Uvy5znX5g2ujzrkIU//T15KQzA==} + '@react-native-community/cli-debugger-ui@12.3.7': + resolution: {integrity: sha512-UHUFrRdcjWSCdWG9KIp2QjuRIahBQnb9epnQI7JCq6NFbFHYfEI4rI7msjMn+gG8/tSwKTV2PTPuPmZ5wWlE7Q==} '@react-native-community/cli-doctor@12.1.1': resolution: {integrity: sha512-IUZJ/KUCuz+IzL9GdHUlIf6zF93XadxCBDPseUYb0ucIS+rEb3RmYC+IukYhUWwN3y4F/yxipYy3ytKrQ33AxA==} @@ -8238,14 +8204,14 @@ packages: '@react-native-community/cli-server-api@12.1.1': resolution: {integrity: sha512-dUqqEmtEiCMyqFd6LF1UqH0WwXirK2tpU7YhyFsBbigBj3hPz2NmzghCe7DRIcC9iouU0guBxhgmiLtmUEPduQ==} - '@react-native-community/cli-server-api@12.3.6': - resolution: {integrity: sha512-80NIMzo8b2W+PL0Jd7NjiJW9mgaT8Y8wsIT/lh6mAvYH7mK0ecDJUYUTAAv79Tbo1iCGPAr3T295DlVtS8s4yQ==} + '@react-native-community/cli-server-api@12.3.7': + resolution: {integrity: sha512-LYETs3CCjrLn1ZU0kYv44TywiIl5IPFHZGeXhAh2TtgOk4mo3kvXxECDil9CdO3bmDra6qyiG61KHvzr8IrHdg==} '@react-native-community/cli-tools@12.1.1': resolution: {integrity: sha512-c9vjDVojZnivGsLoVoTZsJjHnwBEI785yV8mgyKTVFx1sciK8lCsIj1Lke7jNpz7UAE1jW94nI7de2B1aQ9rbA==} - '@react-native-community/cli-tools@12.3.6': - resolution: {integrity: sha512-FPEvZn19UTMMXUp/piwKZSh8cMEfO8G3KDtOwo53O347GTcwNrKjgZGtLSPELBX2gr+YlzEft3CoRv2Qmo83fQ==} + '@react-native-community/cli-tools@12.3.7': + resolution: {integrity: sha512-7NL/1/i+wzd4fBr/FSr3ypR05tiU/Kv9l/M1sL1c6jfcDtWXAL90R161gQkQFK7shIQ8Idp0dQX1rq49tSyfQw==} '@react-native-community/cli-types@12.1.1': resolution: {integrity: sha512-B9lFEIc1/H2GjiyRCk6ISJNn06h5j0cWuokNm3FmeyGOoGIfm4XYUbnM6IpGlIDdQpTtUzZfNq8CL4CIJZXF0g==} @@ -8275,8 +8241,8 @@ packages: peerDependencies: '@babel/preset-env': ^7.1.6 - '@react-native/community-cli-plugin@0.73.17': - resolution: {integrity: sha512-F3PXZkcHg+1ARIr6FRQCQiB7ZAA+MQXGmq051metRscoLvgYJwj7dgC8pvgy0kexzUkHu5BNKrZeySzUft3xuQ==} + '@react-native/community-cli-plugin@0.73.18': + resolution: {integrity: sha512-RN8piDh/eF+QT6YYmrj3Zd9uiaDsRY/kMT0FYR42j8/M/boE4hs4Xn0u91XzT8CAkU9q/ilyo3wJsXIJo2teww==} engines: {node: '>=18'} '@react-native/debugger-frontend@0.73.3': @@ -9920,8 +9886,8 @@ packages: '@types/node@18.19.3': resolution: {integrity: sha512-k5fggr14DwAytoA/t8rPrIz++lXK7/DqckthCmoZOKNsEbJkId4Z//BqgApXBUGrGddrigYa1oqheo/7YmW4rg==} - '@types/node@20.14.2': - resolution: {integrity: sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q==} + '@types/node@22.4.0': + resolution: {integrity: sha512-49AbMDwYUz7EXxKU/r7mXOsxwFr4BYbvB7tWYxVuLdb2ibd30ijjXINSMAHiEEZk5PCRBmW1gUeisn2VMKt3cQ==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -9974,6 +9940,9 @@ packages: '@types/react-dom@18.0.10': resolution: {integrity: sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg==} + '@types/react-dom@18.3.0': + resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} + '@types/react-outside-click-handler@1.3.3': resolution: {integrity: sha512-fF7x4dHf/IPIne8kkt3rlCGuWFrWkFJmzQm4JkxSBzXJIM9WDLob++VnmGpE3ToVWrW3Xw9D5TxcUWrwqe04Gg==} @@ -10366,11 +10335,19 @@ packages: '@use-gesture/core@10.3.0': resolution: {integrity: sha512-rh+6MND31zfHcy9VU3dOZCqGY511lvGcfyJenN4cWZe0u1BH6brBpBddLVXhF2r4BMqWbvxfsbL7D287thJU2A==} + '@use-gesture/core@10.3.1': + resolution: {integrity: sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==} + '@use-gesture/react@10.3.0': resolution: {integrity: sha512-3zc+Ve99z4usVP6l9knYVbVnZgfqhKah7sIG+PS2w+vpig2v2OLct05vs+ZXMzwxdNCMka8B+8WlOo0z6Pn6DA==} peerDependencies: react: '>= 16.8.0' + '@use-gesture/react@10.3.1': + resolution: {integrity: sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==} + peerDependencies: + react: '>= 16.8.0' + '@webassemblyjs/ast@1.11.1': resolution: {integrity: sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==} @@ -10627,6 +10604,10 @@ packages: resolution: {integrity: sha512-mOQtwpY5hUt4vMLyshZPPV1x9MBRF2FimUjIImfYJb1x8o6jY4npikzWplAfWYQUJJjWfw/1NmfqD7vUOh9+ww==} engines: {node: '>=12'} + '@wordpress/a11y@4.6.0': + resolution: {integrity: sha512-dSYGLgntqQCAiHBnNxttLOUZnH26m/BrIQdCXtb9JVJy5p68JAdFHbr6qFoOfOoTCvwUqE8cNS7K4GWfAJwT0w==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/api-fetch@3.23.1': resolution: {integrity: sha512-dmeigLuvqYAzpQ2hWUQT1P5VQAjkj9hS1z7PgNi1CcULFPbY8BWW+KiBETUu6Wm+rlSbUL2dC8qrA4JDv9ja5A==} @@ -10861,13 +10842,6 @@ packages: react: ^17.0.0 react-dom: ^17.0.0 - '@wordpress/components@25.13.0': - resolution: {integrity: sha512-Ym/5Xv7NnkJu40jCSmt/t6B8vT2ue2vobwDEz1FKlB0xGm5bzzh5589m2nZqqY459/Qm9dl5R4BKSdvKqKB2MQ==} - engines: {node: '>=12'} - peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 - '@wordpress/components@25.16.0': resolution: {integrity: sha512-voQuMsO5JbH+JW33TnWurwwvpSb8IQ4XU5wyVMubX4TUwadt+/2ToNJbZIDXoaJPei7vbM81Ft+pH+zGlN8CyA==} engines: {node: '>=12'} @@ -10889,6 +10863,13 @@ packages: react: ^18.0.0 react-dom: ^18.0.0 + '@wordpress/components@28.6.0': + resolution: {integrity: sha512-9YmA+7Tmz19oOfKifOF/VxcwJwyyLK8Y2LupK7ge6Oue0P1bMLs/9LBgZUBizoKMWmXYdzBm8pXf9Eyqq3PG0Q==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + '@wordpress/compose@3.25.3': resolution: {integrity: sha512-tCO2EnJCkCH548OqA0uU8V1k/1skz2QwBlHs8ZQSpimqUS4OWWsAlndCEFe4U4vDTqFt2ow7tzAir+05Cw8MAg==} @@ -10926,6 +10907,12 @@ packages: peerDependencies: react: ^18.0.0 + '@wordpress/compose@7.6.0': + resolution: {integrity: sha512-4ukiLfCOUkb0zmdFpPSVOnQkpNHTWqQUOCgpMykjKO0gRfa/rZ6dxcZUQ/KEYT5EKZkGCo9bR4lBhxjNVrgfug==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + peerDependencies: + react: ^18.0.0 + '@wordpress/core-commands@0.7.0': resolution: {integrity: sha512-kMfyANcDUmA2+4EfEZuDVNFOWKEOJe7oEaZtC6tFRR1wYAlPYOzaQJxbtQMBzqhvHlQMORaxDQNhaoJ8+ac8MQ==} engines: {node: '>=12'} @@ -10976,6 +10963,12 @@ packages: peerDependencies: react: ^17.0.0 + '@wordpress/data@10.6.0': + resolution: {integrity: sha512-u6g1IeK3Vv0Ulr/0jPWU5wpde+flWH1SDvqgc50GjG2v03NWqzie8zTGGeHo8Fque7s/UNbGYKlzrbM3+dPl5g==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + peerDependencies: + react: ^18.0.0 + '@wordpress/data@4.27.3': resolution: {integrity: sha512-5763NgNV9IIa1CC3Q80dAvrH6108tJtj3IrHfUCZmUk1atSNsOMBCkLdQ7tGTTi2JFejeGEMg1LJI22JD5zM6Q==} @@ -11015,14 +11008,16 @@ packages: peerDependencies: react: ^18.0.0 + '@wordpress/dataviews@4.2.0': + resolution: {integrity: sha512-rCnMbEVXKZYgQmJO7S448KPVh78DTHgfJ+B5H937l/HX8+Gd0OlkpbKi4C4UZUj0k/xwY7ccKERYurq3W8/NFg==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + peerDependencies: + react: ^18.0.0 + '@wordpress/date@4.44.0': resolution: {integrity: sha512-WrSAg+gbRN5YB/YZhQnJMNKj80efc+6taVYq3VjSzp27CPxh75qTE5N56TJWGKZbB8mqCIEWy6eOXhIoBW19mQ==} engines: {node: '>=12'} - '@wordpress/date@4.47.0': - resolution: {integrity: sha512-HIruX+wMaQWKYLCFIu6JeEEoqRYkhpL4cWfZ1lJG78wNsgq3vRiHzXQaXHcbmJQCq0PZOxtmeSzldPiUMFVNpg==} - engines: {node: '>=12'} - '@wordpress/date@4.57.0': resolution: {integrity: sha512-azUXRQDhxoCkME7c+0Cw/aCZmyoQeTXhWJYtZBFyPU5wsIXSv/Ucp3WggJR7OSKFnE5rSp5qpCt/nihfLLfZWQ==} engines: {node: '>=12'} @@ -11031,6 +11026,10 @@ packages: resolution: {integrity: sha512-7/w2pzCDvzbidqAl2Rhd/FeA6QZhZmb03Y7rPIO0eJR33L8QWnLiyw+r4Et2DLji8A7N8/gcc+hsRL6lcEsGMA==} engines: {node: '>=12'} + '@wordpress/date@5.6.0': + resolution: {integrity: sha512-uB/FaNHudbs4DgaPGld+Ckvoo8kYvxcDhVyJ6Io3MgONMcsDr4KR3lOc50MprbNZPbXG2KB0CTgHA+PHNxP9iQ==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/dependency-extraction-webpack-plugin@2.9.0': resolution: {integrity: sha512-Eo8ByPd3iZ6az4UmdLD2xYLp1/7os/H80l28Y5OlS4DozkD3vcWCBReynWoBax74u3oJ9wWN5b/8oSxGwIKXYQ==} peerDependencies: @@ -11067,6 +11066,10 @@ packages: resolution: {integrity: sha512-ilOkjXejcnJMxnq1gTVkBnDPP9W+XjlEe1TIfaMKcCwKsfsNy6bgURxWl1qIM2dPjH+5KK65bPjW0XELTMJy4w==} engines: {node: '>=12'} + '@wordpress/deprecated@4.6.0': + resolution: {integrity: sha512-XQbF7SIb43I4Ey7nEDqowm7YJgzoUpdmZfNBN01/UXKUZ0FNaKzf2LCNjOCwfEfRE7AroyUgMR40qWVBBs+GKQ==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/dom-ready@3.27.0': resolution: {integrity: sha512-X7yVAm/JL5UKNfttAN2Ak3suEyOag/MPfr/aX8L2k/od71a6zJBkpMcdKaVPVfIPj9HcrW6ROrfINySPtoGCLA==} engines: {node: '>=12'} @@ -11083,6 +11086,10 @@ packages: resolution: {integrity: sha512-G6OnfLLC0MIWi9efTW6DMNEtEoy7tCoV0MxD19gUuG3/rDOi8RgHYwuNCvt6ieQMISiLiHnsg4tZc4D45zdfZA==} engines: {node: '>=12'} + '@wordpress/dom-ready@4.6.0': + resolution: {integrity: sha512-3fX1O1abmp3++FpZMPnDQygeygUggqfEvWQQQ80di/ksMEo6DXvIdtXolwDQt9WIC1WetLdI7Mf3KKVJnruyxg==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/dom@2.18.0': resolution: {integrity: sha512-tM2WeQuSObl3nzWjUTF0/dyLnA7sdl/MXaSe32D64OF89bjSyJvjUipI7gjKzI3kJ7ddGhwcTggGvSB06MOoCQ==} @@ -11090,10 +11097,6 @@ packages: resolution: {integrity: sha512-ympP0cK4ErQSFCRyrhjg8wAK7Wb5NqTUyiw1kV+2TQ35PKNG+TCXjYkk19Wc0kxiYZPFtbxk8OPp40e8Up7y7g==} engines: {node: '>=12'} - '@wordpress/dom@3.47.0': - resolution: {integrity: sha512-SY6wfAc4yrXYil8fm/uyeKQnPjGuc0G9Q1/5pUKO6dssst8fClsrxy+hXNl0FYFGWnAZBqg5ccrwYydrFt5k/g==} - engines: {node: '>=12'} - '@wordpress/dom@3.57.0': resolution: {integrity: sha512-3vJ1Z5Lzb7kfMoB8ni275vFGIRrljWFQ2XsVfO6oA/HeoIfHAGVcR58GmbjyxwEgClrizMGIkbs9ubrRpontLQ==} engines: {node: '>=12'} @@ -11102,6 +11105,10 @@ packages: resolution: {integrity: sha512-wdWBzfxU8iUPpxxTACkFpYbEoC0f+Hqs24IYOkhn/8ERp2LFpUdFcwF7/DmY6agSpUs8iWT/2hSGdUz9Lw2f0w==} engines: {node: '>=12'} + '@wordpress/dom@4.6.0': + resolution: {integrity: sha512-ZCjMOya5dTkzgp/vTq7w1qpvVQDPoF7sJpalARUUQjeMUkUw/PTLYvvXJ3gARBCgaEdD85QjLorpxnJVz1XNng==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/e2e-test-utils-playwright@1.0.1': resolution: {integrity: sha512-DNR45Q0px6p3XLnJzRXANIXSQ1OKLdWCwQLQctuSmhVyqSyKS0VZApiYVoaPTKLEdxl+WeJ7jN153q1vUa5Lcg==} engines: {node: '>=18.12.0', npm: '>=8.19.2'} @@ -11207,6 +11214,10 @@ packages: resolution: {integrity: sha512-/d/lWBDYYgzE2yeXYvPnjMSDG1EdQs5TSLdjM/drQVJMxWayFqAPaF/pVczLHCPYfjgyJN4Zc+bneAKj6dEiLw==} engines: {node: '>=12'} + '@wordpress/element@6.6.0': + resolution: {integrity: sha512-IvSocvmd0fNus/XZo7K1EU4UD7aOKUdi3Y7pFUW2ljBbL3vuXk3E+6bwYahCjUIlBhpgGuCjemWTdg2Awzfmiw==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/env@10.5.0': resolution: {integrity: sha512-Hx+fi6qTEAuycznulkuMi4d5RDPZ6lPPAxaylpCwXNX2hgx5jrrpgnY4Zn0chBgZMpShO7BbA+zNDq2E6evvTw==} engines: {node: '>=18.12.0', npm: '>=8.19.2'} @@ -11223,6 +11234,10 @@ packages: resolution: {integrity: sha512-DkTDo1Qhvs9rfobBpg5vXAOKaev3Jox8R5ryvYIhql5chrkj/V5k2ZzwUChFXxYmivVkWacCwDGmDmwe2ex/ag==} engines: {node: '>=12'} + '@wordpress/escape-html@3.6.0': + resolution: {integrity: sha512-NY9As0uJ81TPTogBzD6G/m7L4+sjvkjTEKkNsHLD5aEYxRX+RHlPYPyyd6y4CmlOkttwymbV9eKNP+LrfX5zZQ==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/eslint-plugin@12.9.0': resolution: {integrity: sha512-R6dTvD4uFYeoUJFZNUhm1CSwthC0Pl0RIY057Y9oUvGSqjjm7RqRIwKrMlw3dO0P9KoBGGHUox8NUj6EciRXww==} engines: {node: '>=12', npm: '>=6.9'} @@ -11292,8 +11307,8 @@ packages: resolution: {integrity: sha512-4sIngmH64M1jzcprfkffo1GHsQbd/QNbTweq6cSPIJNorKfE63Inf59NQ6r0pq6+Nz+cuq64eMz5v4eyngjZ/A==} engines: {node: '>=12'} - '@wordpress/hooks@4.4.0': - resolution: {integrity: sha512-KO0gUx0KLhH3XCatg9ZOU1TH0fgyQUccAEIM8liErfgmrabHl8JhDoR2Uk5k0jNKZNPog7XxvKgPFVtCzvzQig==} + '@wordpress/hooks@4.6.0': + resolution: {integrity: sha512-FWJhubBXeyRhx12YUmxT9pNoV9Azvx8nkynhduV+RNgA+F2SXoOf15pr+USPV//m3Bx031GN/wPHjgUCbC6+XA==} engines: {node: '>=18.12.0', npm: '>=8.19.2'} '@wordpress/html-entities@3.24.0': @@ -11312,6 +11327,10 @@ packages: resolution: {integrity: sha512-Nb0nCYIdTEehWJ6HoA76bxpseKDY/12rYZ10eqf5OSr6oMvtyJ5j4fkNMKuHFQ00Mhppl9fkYWp2c8ZzBcp5Vw==} engines: {node: '>=12'} + '@wordpress/html-entities@4.6.0': + resolution: {integrity: sha512-ypTlGwDKw7jpmu9rneErkkq9dFHXzju8SGdEWkVAeqhRS9Ifri9DvmrovASB2c5IPY+Ijwh4YlVkx1yNBRHr5w==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/i18n@3.20.0': resolution: {integrity: sha512-SIoOJFB4UrrYAScS4H91CYCLW9dX3Ghv8pBKc/yHGculb1AdGr6gRMlmJxZV62Cn3CZ4Ga86c+FfR+GiBu0JPg==} hasBin: true @@ -11326,11 +11345,6 @@ packages: engines: {node: '>=12'} hasBin: true - '@wordpress/i18n@4.54.0': - resolution: {integrity: sha512-gSKBopBN9rY9GhNy3CXLK3n4D5viuBTObvcu3blu4SFqkHl+Ws1Gx0tHbpypfV80ESrOyMXHJIAqWgBD8d4Hew==} - engines: {node: '>=12'} - hasBin: true - '@wordpress/i18n@4.57.0': resolution: {integrity: sha512-VYWYHE+7NxnZvE9Swhhe4leQcn0jHNkzRAEV36TkfAL/MvrQYCRh71KLTvKhsilG96HUQdBwjH0VPLmYEmR3sg==} engines: {node: '>=12'} @@ -11346,6 +11360,15 @@ packages: engines: {node: '>=18.12.0', npm: '>=8.19.2'} hasBin: true + '@wordpress/i18n@5.6.0': + resolution: {integrity: sha512-xTpwuRh0owYFlgRHUbUAQIWr8ye3FC0ZsjDIOskJaNkrheAU9ZWKJDcmQmPvi01Udml4g9LUIaffkcRd2kyW2g==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + hasBin: true + + '@wordpress/icons@10.6.0': + resolution: {integrity: sha512-dy58bQFVee2izXA65Ptar1f8mVhL1hilOJI3BWbLWmxHr9H4VjI0ohjW4ZkAhahBG2yIvKZja/HaFMTs5O/7Xg==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/icons@4.1.0': resolution: {integrity: sha512-1FpEjT9kJbr0cWbgdgIwd2DoeerWijcVx3qCZ/WMFKNElBH9lfZLuWPI1hpX102HGWFcEi3VlbVpdBGeCeYQWg==} engines: {node: '>=12'} @@ -11366,10 +11389,6 @@ packages: resolution: {integrity: sha512-QkJRDNgSJzfU3OCVr5X9P3Au3MIag2yT4dzM3Ej6VfrF0SPfFgMwroXKSdNEHmCCG7AwtzGOjaqjpQ3y9vRMkA==} engines: {node: '>=12'} - '@wordpress/icons@9.38.0': - resolution: {integrity: sha512-K+rSZM1eKuWh+rXeMWNLj4dT0a3RJSzoUUh9UDQZCSdnThyAyZECGEKfHSCfd28/yabxLKaziXrb5/MVBrPjZw==} - engines: {node: '>=12'} - '@wordpress/icons@9.48.0': resolution: {integrity: sha512-47efXMuqX8Qbf7sFyYeUJ0TPjs3tNqnjHUn3WGc7Gq1IIYD6EGYFmCzPAfciUIXwRBhez2oC4y6IAXl5GP3KBw==} engines: {node: '>=12'} @@ -11407,6 +11426,10 @@ packages: resolution: {integrity: sha512-qUMqZMLlunwY2J31HG6NZwD2kBIqcwvIDBmdQYvVuQ2aDGeB2Z6sVPXyHCqGfh2ynFfaIL8bDtjW5UtYGPUI4A==} engines: {node: '>=12'} + '@wordpress/is-shallow-equal@5.6.0': + resolution: {integrity: sha512-WjxXleJePz9scpTXMTl//mn3AgEBqdHd56pWtaDgz9Ub7O5H8AMNa2BU4VDK8OOQ3iwpAUgqGhaTRK5GjbaeSA==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/jest-console@3.10.0': resolution: {integrity: sha512-iS1GSO+o7+p2PhvScOquD+IK7WqmVxa2s9uTUQyNEo06f9EUv6KNw0B1iZ00DpbgLqDCiczfdCNapC816UXIIA==} engines: {node: '>=8'} @@ -11506,6 +11529,10 @@ packages: resolution: {integrity: sha512-GLKho4gAFbqgmP3GxEPP5iSS2WwOtqX0xL0zVjElNC/uHKCULyZ2UlyDAc2clN5wiVNf3hC4A1BsxzKeKIMNFQ==} engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/keycodes@4.6.0': + resolution: {integrity: sha512-7jmKM1BLyoQPLXFl+3FPaKBrLEe7kUIkBMGS88083SQtXXFcW8sYQt5jd6E1yY6EAnniGveUNrv0C9Lbaipx3w==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/lazy-import@1.34.0': resolution: {integrity: sha512-ZF4YhWDJtvlev1GqZ7FRr2CPg5Vssw6lb4gn2OH56/KWuHf/LrBPVdshXR6ujDPvgUMnNFRf39ofHIENoj7JPA==} engines: {npm: '>=6.9.0'} @@ -11657,6 +11684,12 @@ packages: resolution: {integrity: sha512-4vMhlu40+qxkt6lyCv2KWCx9bP7hcpPC9GXj9Kq3gwKIzSSHoqbYs3V8HYeGWrG9g7JWMFN9Pkdy8Bm61ZsKuQ==} engines: {node: '>=12'} + '@wordpress/primitives@4.6.0': + resolution: {integrity: sha512-uu4ANmgwslB2YOyIBQDSwKTQXXqGDL9Gz5INe+UeJZBMt2uU/TGEjKcZ63dqbuM8mqlPAcdVGL52RCt7mIKEhQ==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + peerDependencies: + react: ^18.0.0 + '@wordpress/priority-queue@1.11.2': resolution: {integrity: sha512-ulwmUOklY3orn1xXpcPnTyGWV5B/oycxI+cHZ6EevBVgM5sq+BW3xo0PKLR/MMm6UNBtFTu/71QAJrNZcD6V1g==} @@ -11668,6 +11701,10 @@ packages: resolution: {integrity: sha512-g4Oka9aQFVPQUhXkKhHT6BoyTEdCG6S0TUvP4SP16PbkhbvIFwZ25GRQb2ERCVTdseCuDIM5YP0kwZd3NqTlGg==} engines: {node: '>=12'} + '@wordpress/priority-queue@3.6.0': + resolution: {integrity: sha512-r2cyisWaqDLesIqC8BqWoXyNIxt1lwjvevw5Kijl9zxzxfYBsNQlu7RI1JNYgnjbDQQirWukFgprt7tdzhwssQ==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/private-apis@0.20.0': resolution: {integrity: sha512-byyPRUNAD8/ca9N8gP2rUr8DHuMbzSoXO03nP8g3cecTN6iCOWqFDm6adkrbiAX527N9Ip+GOrRJd7Tta4kRIg==} engines: {node: '>=12'} @@ -11688,6 +11725,10 @@ packages: resolution: {integrity: sha512-JGn7ngLHjbIWcaRNiZP2githQXcLRYS55UpnWa8WemM5vM/Lfql+mBwo1B9/GVvxiGgTy0K3Fv4SsGhZwMjCMg==} engines: {node: '>=12'} + '@wordpress/private-apis@1.6.0': + resolution: {integrity: sha512-gQ978Fh3QbwzcWLkljYYyqMIbj1zNK/4tjt6zwzjzCxwwh2qcChDVfVt358wjKJ5sGdhYKIbqP8NzQQV9Un0Iw==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/react-i18n@3.55.0': resolution: {integrity: sha512-1mbLk3MeK4qmY0yaqozXfNap9H6pKbTY5u5ZI3rdO7ZHYkrDH+mH0BnmkmkMDFGg3la8z7xQ82EvkPewXEUJlA==} engines: {node: '>=12'} @@ -11707,6 +11748,12 @@ packages: peerDependencies: redux: '>=4' + '@wordpress/redux-routine@5.6.0': + resolution: {integrity: sha512-CQkO+JZefPJLNBh5iBup2DRCXfUoPfEZeo2mhO91tSbBmrP08v1Pdk6YLsa8gNDXp4qJbFhNHMGCqRzEioMOhA==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + peerDependencies: + redux: '>=4' + '@wordpress/reusable-blocks@3.20.0': resolution: {integrity: sha512-2Wp1W704eYfTdCrYx+EKr5VbW/Z0AX24M8+FxWmhFlGjWpdzGl9shuMKv6cLfXeLDitU8fyHILXAVAXsvRvK3A==} engines: {node: '>=12'} @@ -11737,15 +11784,15 @@ packages: peerDependencies: react: ^17.0.0 - '@wordpress/rich-text@6.24.0': - resolution: {integrity: sha512-RkvzK8zvLgpd7i5dlL6zs+Dig1lZNSZf/3sYyjX6RalISXNuxF6Zn8Or7kBcq7EcYmey0LMlVIl5FTZ2l7HSIA==} + '@wordpress/rich-text@6.34.0': + resolution: {integrity: sha512-qeHPgSaI6UolAA9s8ShlbqjWtlh1kTIOMKATDZD6GOACZurXh9ZVJxxsE95FSmLEu4SDmYJ0b2sZlh92yJuaPw==} engines: {node: '>=12'} peerDependencies: react: ^18.0.0 - '@wordpress/rich-text@6.34.0': - resolution: {integrity: sha512-qeHPgSaI6UolAA9s8ShlbqjWtlh1kTIOMKATDZD6GOACZurXh9ZVJxxsE95FSmLEu4SDmYJ0b2sZlh92yJuaPw==} - engines: {node: '>=12'} + '@wordpress/rich-text@7.6.0': + resolution: {integrity: sha512-XxlfrlwfCPX7f3u9DMinouYNM9PDBMeGZb4MlK2Fbrc8ympaTZOdH4U74VR3jgv0Eusx6vxFEA5JVVXpW/xS2w==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} peerDependencies: react: ^18.0.0 @@ -11856,6 +11903,10 @@ packages: resolution: {integrity: sha512-WhMKX/ETGUJr2GkaPgGwFF8gTU/PgikfvE2b2ZDjUglxIPYnujBa9S6w+kQPzwGniGJutHL1DFK+TmAaxoci9A==} engines: {node: '>=12'} + '@wordpress/undo-manager@1.6.0': + resolution: {integrity: sha512-Sl2rG/7t5zTQOgp+jOPn5m27sKd1DJIX/EGhM6LtRcjXZqa0rLDJXal1xWfkZk5oghaqW1TAwXJsg9UdAlh7Nw==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/url@2.22.2': resolution: {integrity: sha512-aqpYKQXzyzkCOm+GzZRYlLb+wh58g0cwR1PaKAl0UXaBS4mdS+X6biMriylb4P8CVC/RR7CSw5XI20JC24KDwQ==} @@ -11908,6 +11959,10 @@ packages: resolution: {integrity: sha512-Xs37x0IkvNewPNKs1A8cnw5xLb+AqwUqqCsH4+5Sjat5GDqP86mHgLfRIlE4d6fBYg+q6tO7DVPG49TT3/wzgA==} engines: {node: '>=12'} + '@wordpress/warning@3.6.0': + resolution: {integrity: sha512-pm57z1LZkzfQsXsji6yxcP0XSymKbvP087vJLlMkmLf+MoNVyTD6UvFpXl8hRSH6C6pySoJSgGFXaH81CRuO2Q==} + engines: {node: '>=18.12.0', npm: '>=8.19.2'} + '@wordpress/widgets@3.24.0': resolution: {integrity: sha512-bgjUoBjHKhyM2u7QrTScll7hCFDrHw0OxZWGbPXOGfE0VUgaej/d8QV5re7I+sOIi0g8+XLYQE0fwEyANt1iUg==} peerDependencies: @@ -12050,6 +12105,11 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + address@1.2.2: resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==} engines: {node: '>= 10.0.0'} @@ -12712,8 +12772,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - babel-plugin-polyfill-corejs3@0.10.4: - resolution: {integrity: sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==} + babel-plugin-polyfill-corejs3@0.10.6: + resolution: {integrity: sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 @@ -12842,6 +12902,10 @@ packages: resolution: {integrity: sha512-QHX8HLlncOLpy54mh+k/sWIFd0ThmRqwe9ZjELybGZK+tZ8rUb9VO0saKJUROTbE+KhzDUT7xziGpGrW8Kmd+g==} engines: {node: '>=10.0.0'} + basic-ftp@5.0.5: + resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} + engines: {node: '>=10.0.0'} + batch-processor@1.0.0: resolution: {integrity: sha512-xoLQD8gmmR32MeuBHgH0Tzd5PuSZx71ZsbhVxOCRbgktZEPe4SQy7s9Z50uPp0F/f7iw2XmkHN2xkgbMfckMDA==} @@ -13021,6 +13085,11 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + browserslist@4.23.3: + resolution: {integrity: sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + bs-logger@0.2.6: resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} engines: {node: '>= 6'} @@ -13201,6 +13270,9 @@ packages: caniuse-lite@1.0.30001629: resolution: {integrity: sha512-c3dl911slnQhmxUIT4HhYzT7wnBK/XYpGnYLOj4nJBaRiw52Ibe7YxlDaAeRECvA786zCuExhxIUJ2K7nHMrBw==} + caniuse-lite@1.0.30001651: + resolution: {integrity: sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==} + canvas-confetti@1.9.2: resolution: {integrity: sha512-6Xi7aHHzKwxZsem4mCKoqP6YwUG3HamaHHAlz1hTNQPCqXhARFpSXnkC9TWlahHY5CG6hSL5XexNjxK8irVErg==} @@ -13848,8 +13920,8 @@ packages: core-js-compat@3.34.0: resolution: {integrity: sha512-4ZIyeNbW/Cn1wkMMDy+mvrRUxrwFNjKwbhCfQpDd+eLgYipDqp8oGFGtLmhh18EDPKA0g3VUBYOxQGGwvWLVpA==} - core-js-compat@3.37.1: - resolution: {integrity: sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==} + core-js-compat@3.38.0: + resolution: {integrity: sha512-75LAicdLa4OJVwFxFbQR3NdnZjNgX6ILpVcVzcC4T2smerB5lELMrJQQQoWV6TiuC/vlaFqgU2tKQx9w5s0e0A==} core-js-pure@3.34.0: resolution: {integrity: sha512-pmhivkYXkymswFfbXsANmBAewXx86UBfmagP+w0wkK06kLsLlTK5oQmsURPivzMkIBQiYq2cjamcZExIwlFQIg==} @@ -14202,6 +14274,10 @@ packages: resolution: {integrity: sha512-MZd3VlchQkp8rdend6vrx7MmVDJzSNTBvghvKjirLkD+WTChA3KUf0jkE68Q4UyctNqI11zZO9/x2Yx+ub5Cvg==} engines: {node: '>= 14'} + data-uri-to-buffer@6.0.2: + resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} + engines: {node: '>= 14'} + data-urls@1.1.0: resolution: {integrity: sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==} @@ -14225,8 +14301,8 @@ packages: dateformat@4.6.3: resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} - dayjs@1.11.11: - resolution: {integrity: sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==} + dayjs@1.11.12: + resolution: {integrity: sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg==} debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} @@ -14288,6 +14364,15 @@ packages: supports-color: optional: true + debug@4.3.6: + resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + debuglog@1.0.1: resolution: {integrity: sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==} deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. @@ -14772,6 +14857,9 @@ packages: electron-to-chromium@1.4.795: resolution: {integrity: sha512-hHo4lK/8wb4NUa+NJYSFyJ0xedNHiR6ylilDtb8NUW9d4dmBFmGiecYEKCEbti1wTNzbKXLfl4hPWEkAFbHYlw==} + electron-to-chromium@1.5.9: + resolution: {integrity: sha512-HfkT8ndXR0SEkU8gBQQM3rz035bpE/hxkZ1YIt4KJPEFES68HfIU6LzKukH0H794Lm83WJtkSAMfEToxCs15VA==} + element-resize-detector@1.2.4: resolution: {integrity: sha512-Fl5Ftk6WwXE0wqCgNoseKWndjzZlDCwuPTcoVZfCP9R3EHQF8qUtr3YUPNETegRBOKqQKPW3n4kiIWngGi8tKg==} @@ -15589,8 +15677,8 @@ packages: fast-sort@3.4.0: resolution: {integrity: sha512-c/cMBGA5mH3OYjaXedtLIM3hQjv+KuZuiD2QEH5GofNOZeQVDIYIN7Okc2AW1KPhk44g5PTZnXp8t2lOMl8qhQ==} - fast-xml-parser@4.2.5: - resolution: {integrity: sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==} + fast-xml-parser@4.4.1: + resolution: {integrity: sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==} hasBin: true fastest-levenshtein@1.0.16: @@ -15975,6 +16063,20 @@ packages: react-dom: optional: true + framer-motion@11.3.30: + resolution: {integrity: sha512-9VmqGe9OIjfMoCcs+ZsKXlv6JaG5QagKX2F1uSbkG3Z33wgjnz60Kw+CngC1M49rDYau+Y9aL+8jGagAwrbVyw==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 + react-dom: ^18.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + framer-motion@6.5.1: resolution: {integrity: sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==} peerDependencies: @@ -16006,6 +16108,10 @@ packages: resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} engines: {node: '>=14.14'} + fs-extra@11.2.0: + resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + engines: {node: '>=14.14'} + fs-extra@6.0.1: resolution: {integrity: sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==} @@ -16171,6 +16277,10 @@ packages: resolution: {integrity: sha512-5KLucCJobh8vBY1K07EFV4+cPZH3mrV9YeAruUseCQKHB58SGjjT2l9/eA9LD082IiuMjSlFJEcdJ27TXvbZNw==} engines: {node: '>= 14'} + get-uri@6.0.3: + resolution: {integrity: sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==} + engines: {node: '>= 14'} + get-value@2.0.6: resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==} engines: {node: '>=0.10.0'} @@ -16666,14 +16776,14 @@ packages: hermes-estree@0.15.0: resolution: {integrity: sha512-lLYvAd+6BnOqWdnNbP/Q8xfl8LOGw4wVjfrNd9Gt8eoFzhNBRVD95n4l2ksfMVOoxuVyegs85g83KS9QOsxbVQ==} - hermes-estree@0.20.1: - resolution: {integrity: sha512-SQpZK4BzR48kuOg0v4pb3EAGNclzIlqMj3Opu/mu7bbAoFw6oig6cEt/RAi0zTFW/iW6Iz9X9ggGuZTAZ/yZHg==} + hermes-estree@0.23.0: + resolution: {integrity: sha512-Rkp0PNLGpORw4ktsttkVbpYJbrYKS3hAnkxu8D9nvQi6LvSbuPa+tYw/t2u3Gjc35lYd/k95YkjqyTcN4zspag==} hermes-parser@0.15.0: resolution: {integrity: sha512-Q1uks5rjZlE9RjMMjSUCkGrEIPI5pKJILeCtK1VmTj7U4pf3wVPoo+cxfu+s4cBAPy2JzikIIdCZgBoR6x7U1Q==} - hermes-parser@0.20.1: - resolution: {integrity: sha512-BL5P83cwCogI8D7rrDCgsFY0tdYUtmFP9XaXtl2IQjC+2Xo+4okjfXintlTxcIwl4qeGddEl28Z11kbVIw0aNA==} + hermes-parser@0.23.0: + resolution: {integrity: sha512-xLwM4ylfHGwrm+2qXfO1JT/fnqEDGSnpS/9hQ4VLtqTexSviu2ZpBgz07U8jVtndq67qdb/ps0qvaWDZ3fkTyg==} hermes-profile-transformer@0.0.6: resolution: {integrity: sha512-cnN7bQUm65UWOy6cbGcCcZ3rpwW8Q/j4OP5aWRhEry4Z2t2aR1cjrbp0BS+KiBN0smvP1caBgAuxutvyvJILzQ==} @@ -16891,8 +17001,8 @@ packages: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} - https-proxy-agent@7.0.4: - resolution: {integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==} + https-proxy-agent@7.0.5: + resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} engines: {node: '>= 14'} human-signals@1.1.1: @@ -18358,8 +18468,8 @@ packages: joi@17.11.0: resolution: {integrity: sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==} - joi@17.13.1: - resolution: {integrity: sha512-vaBlIKCyo4FCUtCm7Eu4QZd/q02bWcxfUO6YSXAZOWF6gzcLBeba8kwotUdYJjDLW8Cz8RywsSOqiNJZW0mNvg==} + joi@17.13.3: + resolution: {integrity: sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==} jpeg-js@0.4.4: resolution: {integrity: sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==} @@ -19295,61 +19405,61 @@ packages: resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} engines: {node: '>= 0.6'} - metro-babel-transformer@0.80.9: - resolution: {integrity: sha512-d76BSm64KZam1nifRZlNJmtwIgAeZhZG3fi3K+EmPOlrR8rDtBxQHDSN3fSGeNB9CirdTyabTMQCkCup6BXFSQ==} + metro-babel-transformer@0.80.10: + resolution: {integrity: sha512-GXHueUzgzcazfzORDxDzWS9jVVRV6u+cR6TGvHOfGdfLzJCj7/D0PretLfyq+MwN20twHxLW+BUXkoaB8sCQBg==} engines: {node: '>=18'} - metro-cache-key@0.80.9: - resolution: {integrity: sha512-hRcYGhEiWIdM87hU0fBlcGr+tHDEAT+7LYNCW89p5JhErFt/QaAkVx4fb5bW3YtXGv5BTV7AspWPERoIb99CXg==} + metro-cache-key@0.80.10: + resolution: {integrity: sha512-57qBhO3zQfoU/hP4ZlLW5hVej2jVfBX6B4NcSfMj4LgDPL3YknWg80IJBxzQfjQY/m+fmMLmPy8aUMHzUp/guA==} engines: {node: '>=18'} - metro-cache@0.80.9: - resolution: {integrity: sha512-ujEdSI43QwI+Dj2xuNax8LMo8UgKuXJEdxJkzGPU6iIx42nYa1byQ+aADv/iPh5sh5a//h5FopraW5voXSgm2w==} + metro-cache@0.80.10: + resolution: {integrity: sha512-8CBtDJwMguIE5RvV3PU1QtxUG8oSSX54mIuAbRZmcQ0MYiOl9JdrMd4JCBvIyhiZLoSStph425SMyCSnjtJsdA==} engines: {node: '>=18'} - metro-config@0.80.9: - resolution: {integrity: sha512-28wW7CqS3eJrunRGnsibWldqgwRP9ywBEf7kg+uzUHkSFJNKPM1K3UNSngHmH0EZjomizqQA2Zi6/y6VdZMolg==} + metro-config@0.80.10: + resolution: {integrity: sha512-0GYAw0LkmGbmA81FepKQepL1KU/85Cyv7sAiWm6QWeV6AcVCpsKg6jGLqGHJ0LLPL60rWzA4TV1DQAlzdJAEtA==} engines: {node: '>=18'} - metro-core@0.80.9: - resolution: {integrity: sha512-tbltWQn+XTdULkGdzHIxlxk4SdnKxttvQQV3wpqqFbHDteR4gwCyTR2RyYJvxgU7HELfHtrVbqgqAdlPByUSbg==} + metro-core@0.80.10: + resolution: {integrity: sha512-nwBB6HbpGlNsZMuzxVqxqGIOsn5F3JKpsp8PziS7Z4mV8a/jA1d44mVOgYmDa2q5WlH5iJfRIIhdz24XRNDlLA==} engines: {node: '>=18'} - metro-file-map@0.80.9: - resolution: {integrity: sha512-sBUjVtQMHagItJH/wGU9sn3k2u0nrCl0CdR4SFMO1tksXLKbkigyQx4cbpcyPVOAmGTVuy3jyvBlELaGCAhplQ==} + metro-file-map@0.80.10: + resolution: {integrity: sha512-ytsUq8coneaN7ZCVk1IogojcGhLIbzWyiI2dNmw2nnBgV/0A+M5WaTTgZ6dJEz3dzjObPryDnkqWPvIGLCPtiw==} engines: {node: '>=18'} - metro-minify-terser@0.80.9: - resolution: {integrity: sha512-FEeCeFbkvvPuhjixZ1FYrXtO0araTpV6UbcnGgDUpH7s7eR5FG/PiJz3TsuuPP/HwCK19cZtQydcA2QrCw446A==} + metro-minify-terser@0.80.10: + resolution: {integrity: sha512-Xyv9pEYpOsAerrld7cSLIcnCCpv8ItwysOmTA+AKf1q4KyE9cxrH2O2SA0FzMCkPzwxzBWmXwHUr+A89BpEM6g==} engines: {node: '>=18'} - metro-resolver@0.80.9: - resolution: {integrity: sha512-wAPIjkN59BQN6gocVsAvvpZ1+LQkkqUaswlT++cJafE/e54GoVkMNCmrR4BsgQHr9DknZ5Um/nKueeN7kaEz9w==} + metro-resolver@0.80.10: + resolution: {integrity: sha512-EYC5CL7f+bSzrqdk1bylKqFNGabfiI5PDctxoPx70jFt89Jz+ThcOscENog8Jb4LEQFG6GkOYlwmPpsi7kx3QA==} engines: {node: '>=18'} - metro-runtime@0.80.9: - resolution: {integrity: sha512-8PTVIgrVcyU+X/rVCy/9yxNlvXsBCk5JwwkbAm/Dm+Abo6NBGtNjWF0M1Xo/NWCb4phamNWcD7cHdR91HhbJvg==} + metro-runtime@0.80.10: + resolution: {integrity: sha512-Xh0N589ZmSIgJYAM+oYwlzTXEHfASZac9TYPCNbvjNTn0EHKqpoJ/+Im5G3MZT4oZzYv4YnvzRtjqS5k0tK94A==} engines: {node: '>=18'} - metro-source-map@0.80.9: - resolution: {integrity: sha512-RMn+XS4VTJIwMPOUSj61xlxgBvPeY4G6s5uIn6kt6HB6A/k9ekhr65UkkDD7WzHYs3a9o869qU8tvOZvqeQzgw==} + metro-source-map@0.80.10: + resolution: {integrity: sha512-EyZswqJW8Uukv/HcQr6K19vkMXW1nzHAZPWJSEyJFKIbgp708QfRZ6vnZGmrtFxeJEaFdNup4bGnu8/mIOYlyA==} engines: {node: '>=18'} - metro-symbolicate@0.80.9: - resolution: {integrity: sha512-Ykae12rdqSs98hg41RKEToojuIW85wNdmSe/eHUgMkzbvCFNVgcC0w3dKZEhSsqQOXapXRlLtHkaHLil0UD/EA==} + metro-symbolicate@0.80.10: + resolution: {integrity: sha512-qAoVUoSxpfZ2DwZV7IdnQGXCSsf2cAUExUcZyuCqGlY5kaWBb0mx2BL/xbMFDJ4wBp3sVvSBPtK/rt4J7a0xBA==} engines: {node: '>=18'} hasBin: true - metro-transform-plugins@0.80.9: - resolution: {integrity: sha512-UlDk/uc8UdfLNJhPbF3tvwajyuuygBcyp+yBuS/q0z3QSuN/EbLllY3rK8OTD9n4h00qZ/qgxGv/lMFJkwP4vg==} + metro-transform-plugins@0.80.10: + resolution: {integrity: sha512-leAx9gtA+2MHLsCeWK6XTLBbv2fBnNFu/QiYhWzMq8HsOAP4u1xQAU0tSgPs8+1vYO34Plyn79xTLUtQCRSSUQ==} engines: {node: '>=18'} - metro-transform-worker@0.80.9: - resolution: {integrity: sha512-c/IrzMUVnI0hSVVit4TXzt3A1GiUltGVlzCmLJWxNrBGHGrJhvgePj38+GXl1Xf4Fd4vx6qLUkKMQ3ux73bFLQ==} + metro-transform-worker@0.80.10: + resolution: {integrity: sha512-zNfNLD8Rz99U+JdOTqtF2o7iTjcDMMYdVS90z6+81Tzd2D0lDWVpls7R1hadS6xwM+ymgXFQTjM6V6wFoZaC0g==} engines: {node: '>=18'} - metro@0.80.9: - resolution: {integrity: sha512-Bc57Xf3GO2Xe4UWQsBj/oW6YfLPABEu8jfDVDiNmJvoQW4CO34oDPuYKe4KlXzXhcuNsqOtSxpbjCRRVjhhREg==} + metro@0.80.10: + resolution: {integrity: sha512-FDPi0X7wpafmDREXe1lgg3WzETxtXh6Kpq8+IwsG35R2tMyp2kFIqDdshdohuvDt1J/qDARcEPq7V/jElTb1kA==} engines: {node: '>=18'} hasBin: true @@ -19780,6 +19890,9 @@ packages: node-releases@2.0.14: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + node-releases@2.0.18: + resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + node-stream-zip@1.15.0: resolution: {integrity: sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==} engines: {node: '>=0.12.0'} @@ -19971,8 +20084,8 @@ packages: oauth-sign@0.9.0: resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==} - ob1@0.80.9: - resolution: {integrity: sha512-v9yOxowkZbxWhKOaaTyLjIm1aLy4ebMNcSn4NYJKOAI/Qv+SkfEfszpLr2GIxsccmb2Y2HA9qtsqiIJ80ucpVA==} + ob1@0.80.10: + resolution: {integrity: sha512-dJHyB0S6JkMorUSfSGcYGkkg9kmq3qDUu3ygZUKIfkr47XOPuG35r2Sk6tbwtHXbdKIXmcMvM8DF2CwgdyaHfQ==} engines: {node: '>=18'} object-assign@4.1.1: @@ -20262,10 +20375,18 @@ packages: resolution: {integrity: sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==} engines: {node: '>= 14'} + pac-proxy-agent@7.0.2: + resolution: {integrity: sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==} + engines: {node: '>= 14'} + pac-resolver@7.0.0: resolution: {integrity: sha512-Fd9lT9vJbHYRACT8OhCbZBbxr6KRSawSovFpy8nDGshaK99S/EBhVIHp9+crhxrsZOuvLpgL1n23iyPg6Rl2hg==} engines: {node: '>= 14'} + pac-resolver@7.0.1: + resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} + engines: {node: '>= 14'} + package-json@4.0.1: resolution: {integrity: sha512-q/R5GrMek0vzgoomq6rm9OX+3PQve8sLwTirmK30YB3Cu0Bbt9OX9M/SIUnroN5BGJkzwGsFwDaRGD9EwBOlCA==} engines: {node: '>=4'} @@ -20555,13 +20676,13 @@ packages: resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==} engines: {node: '>=14.16'} - playwright-core@1.45.1: - resolution: {integrity: sha512-LF4CUUtrUu2TCpDw4mcrAIuYrEjVDfT1cHbJMfwnE2+1b8PZcFzPNgvZCvq2JfQ4aTjRCCHw5EJ2tmr2NSzdPg==} + playwright-core@1.46.1: + resolution: {integrity: sha512-h9LqIQaAv+CYvWzsZ+h3RsrqCStkBHlgo6/TJlFst3cOTlLghBQlJwPOZKQJTKNaD3QIB7aAVQ+gfWbN3NXB7A==} engines: {node: '>=18'} hasBin: true - playwright@1.45.1: - resolution: {integrity: sha512-Hjrgae4kpSQBr98nhCj3IScxVeVUixqj+5oyif8TdIn2opTCPEzqAqNMeK42i3cWDCVu9MI+ZsGWw+gVR4ISBg==} + playwright@1.46.1: + resolution: {integrity: sha512-oPcr1yqoXLCkgKtD5eNUPLiN40rYEM39odNpIb6VE6S7/15gJmA1NzVv6zJYusV0e7tzvkU/utBFNa/Kpxmwng==} engines: {node: '>=18'} hasBin: true @@ -21807,7 +21928,7 @@ packages: react-with-direction@1.4.0: resolution: {integrity: sha512-ybHNPiAmaJpoWwugwqry9Hd1Irl2hnNXlo/2SXQBwbLn/jGMauMS2y9jw+ydyX5V9ICryCqObNSthNt5R94xpg==} peerDependencies: - react: ^0.14 || ^15 || ^16 + react: ^17.0.2 react-dom: ^0.14 || ^15 || ^16 react-with-styles-interface-css@4.0.3: @@ -22567,6 +22688,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + send@0.18.0: resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} engines: {node: '>= 0.8.0'} @@ -22784,6 +22910,10 @@ packages: resolution: {integrity: sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A==} engines: {node: '>= 14'} + socks-proxy-agent@8.0.4: + resolution: {integrity: sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==} + engines: {node: '>= 14'} + socks@2.7.1: resolution: {integrity: sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==} engines: {node: '>= 10.13.0', npm: '>= 3.0.0'} @@ -23532,8 +23662,8 @@ packages: engines: {node: '>=10'} hasBin: true - terser@5.31.1: - resolution: {integrity: sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg==} + terser@5.31.6: + resolution: {integrity: sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==} engines: {node: '>=10'} hasBin: true @@ -23974,6 +24104,9 @@ packages: undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + undici-types@6.19.6: + resolution: {integrity: sha512-e/vggGopEfTKSvj4ihnOLTsqhrKRN3LeO6qSN/GxohhuRv8qH9bNQ4B8W7e/vFL+0XTnmHPB4/kegunZGA4Org==} + undici@5.28.2: resolution: {integrity: sha512-wh1pHJHnUeQV5Xa8/kyQhO7WFa8M34l026L5P/+2TYiakvGy5Rdc8jWZVyG7ieht/0WgJLEd3kcU5gKx+6GC8w==} engines: {node: '>=14.0'} @@ -24157,6 +24290,12 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + update-browserslist-db@1.1.0: + resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + update-notifier@2.5.0: resolution: {integrity: sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==} engines: {node: '>=4'} @@ -24952,8 +25091,8 @@ packages: resolution: {integrity: sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==} engines: {node: '>=4'} - ws@5.2.3: - resolution: {integrity: sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA==} + ws@5.2.4: + resolution: {integrity: sha512-fFCejsuC8f9kOSu9FYaOw8CdO68O3h5v0lg4p74o8JqWpwTf9tniOD+nOB78aWoVSS6WptVUmDrp/KPsMVBWFQ==} peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ^5.0.2 @@ -24974,6 +25113,17 @@ packages: utf-8-validate: optional: true + ws@6.2.3: + resolution: {integrity: sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + ws@7.4.6: resolution: {integrity: sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==} engines: {node: '>=8.3.0'} @@ -24986,6 +25136,18 @@ packages: utf-8-validate: optional: true + ws@7.5.10: + resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + ws@7.5.9: resolution: {integrity: sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==} engines: {node: '>=8.3.0'} @@ -25034,8 +25196,8 @@ packages: utf-8-validate: optional: true - ws@8.17.0: - resolution: {integrity: sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==} + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -25169,6 +25331,11 @@ packages: engines: {node: '>= 14'} hasBin: true + yaml@2.5.0: + resolution: {integrity: sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==} + engines: {node: '>= 14'} + hasBin: true + yargs-parser@13.1.2: resolution: {integrity: sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==} @@ -25291,12 +25458,17 @@ snapshots: '@jridgewell/gen-mapping': 0.3.3 '@jridgewell/trace-mapping': 0.3.20 + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + '@ariakit/core@0.3.11': {} - '@ariakit/core@0.3.8': {} - '@ariakit/core@0.4.5': {} + '@ariakit/core@0.4.9': {} + '@ariakit/react-core@0.3.14(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: '@ariakit/core': 0.3.11 @@ -25313,9 +25485,9 @@ snapshots: react-dom: 18.3.1(react@18.3.1) use-sync-external-store: 1.2.0(react@18.3.1) - '@ariakit/react-core@0.3.9(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@ariakit/react-core@0.4.10(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@ariakit/core': 0.3.8 + '@ariakit/core': 0.4.9 '@floating-ui/dom': 1.5.3 react: 17.0.2 react-dom: 17.0.2(react@17.0.2) @@ -25341,9 +25513,9 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@ariakit/react@0.3.9(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@ariakit/react@0.4.10(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@ariakit/react-core': 0.3.9(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@ariakit/react-core': 0.4.10(react-dom@17.0.2(react@17.0.2))(react@17.0.2) react: 17.0.2 react-dom: 17.0.2(react@17.0.2) @@ -25469,7 +25641,7 @@ snapshots: '@automattic/load-script@1.0.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 debug: 3.2.7 transitivePeerDependencies: - supports-color @@ -25530,7 +25702,7 @@ snapshots: '@wordpress/primitives': 3.55.0 '@wordpress/react-i18n': 3.55.0 classnames: 2.3.2 - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) react: 17.0.2 react-dom: 17.0.2(react@17.0.2) react-popper: 2.3.0(@popperjs/core@2.11.8)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) @@ -25621,6 +25793,8 @@ snapshots: '@babel/compat-data@7.24.7': {} + '@babel/compat-data@7.25.2': {} + '@babel/core@7.12.9': dependencies: '@babel/code-frame': 7.23.5 @@ -25632,7 +25806,7 @@ snapshots: '@babel/traverse': 7.23.5 '@babel/types': 7.23.5 convert-source-map: 1.9.0 - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 lodash: 4.17.21 @@ -25655,7 +25829,7 @@ snapshots: '@babel/traverse': 7.23.5 '@babel/types': 7.23.5 convert-source-map: 2.0.0 - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -25675,7 +25849,7 @@ snapshots: '@babel/traverse': 7.23.5 '@babel/types': 7.23.5 convert-source-map: 2.0.0 - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -25702,6 +25876,26 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/core@7.25.2': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.25.0 + '@babel/helper-compilation-targets': 7.25.2 + '@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2) + '@babel/helpers': 7.25.0 + '@babel/parser': 7.25.3 + '@babel/template': 7.25.0 + '@babel/traverse': 7.25.3 + '@babel/types': 7.25.2 + convert-source-map: 2.0.0 + debug: 4.3.6 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + '@babel/eslint-parser@7.23.3(@babel/core@7.12.9)(eslint@8.55.0)': dependencies: '@babel/core': 7.12.9 @@ -25748,13 +25942,20 @@ snapshots: '@jridgewell/trace-mapping': 0.3.25 jsesc: 2.5.2 + '@babel/generator@7.25.0': + dependencies: + '@babel/types': 7.25.2 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 2.5.2 + '@babel/helper-annotate-as-pure@7.22.5': dependencies: '@babel/types': 7.23.5 '@babel/helper-annotate-as-pure@7.24.7': dependencies: - '@babel/types': 7.24.7 + '@babel/types': 7.25.2 '@babel/helper-builder-binary-assignment-operator-visitor@7.22.15': dependencies: @@ -25776,6 +25977,14 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 + '@babel/helper-compilation-targets@7.25.2': + dependencies: + '@babel/compat-data': 7.25.2 + '@babel/helper-validator-option': 7.24.8 + browserslist: 4.23.3 + lru-cache: 5.1.1 + semver: 6.3.1 + '@babel/helper-create-class-features-plugin@7.23.5(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 @@ -25867,47 +26076,54 @@ snapshots: '@babel/helper-split-export-declaration': 7.24.7 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.24.7(@babel/core@7.12.9)': + '@babel/helper-create-class-features-plugin@7.23.6(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-function-name': 7.24.7 + '@babel/helper-member-expression-to-functions': 7.23.0 + '@babel/helper-optimise-call-expression': 7.22.5 + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.25.2) + '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 + '@babel/helper-split-export-declaration': 7.24.7 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.25.0(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-function-name': 7.24.7 - '@babel/helper-member-expression-to-functions': 7.24.7 + '@babel/helper-member-expression-to-functions': 7.24.8 '@babel/helper-optimise-call-expression': 7.24.7 - '@babel/helper-replace-supers': 7.24.7(@babel/core@7.12.9) + '@babel/helper-replace-supers': 7.25.0(@babel/core@7.12.9) '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 - '@babel/helper-split-export-declaration': 7.24.7 + '@babel/traverse': 7.25.3 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/helper-create-class-features-plugin@7.24.7(@babel/core@7.23.5)': + '@babel/helper-create-class-features-plugin@7.25.0(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-function-name': 7.24.7 - '@babel/helper-member-expression-to-functions': 7.24.7 + '@babel/helper-member-expression-to-functions': 7.24.8 '@babel/helper-optimise-call-expression': 7.24.7 - '@babel/helper-replace-supers': 7.24.7(@babel/core@7.23.5) + '@babel/helper-replace-supers': 7.25.0(@babel/core@7.23.5) '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 - '@babel/helper-split-export-declaration': 7.24.7 + '@babel/traverse': 7.25.3 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/helper-create-class-features-plugin@7.24.7(@babel/core@7.24.7)': + '@babel/helper-create-class-features-plugin@7.25.0(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.2 '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-function-name': 7.24.7 - '@babel/helper-member-expression-to-functions': 7.24.7 + '@babel/helper-member-expression-to-functions': 7.24.8 '@babel/helper-optimise-call-expression': 7.24.7 - '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7) + '@babel/helper-replace-supers': 7.25.0(@babel/core@7.25.2) '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 - '@babel/helper-split-export-declaration': 7.24.7 + '@babel/traverse': 7.25.3 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -25933,14 +26149,14 @@ snapshots: regexpu-core: 5.3.2 semver: 6.3.1 - '@babel/helper-create-regexp-features-plugin@7.24.7(@babel/core@7.12.9)': + '@babel/helper-create-regexp-features-plugin@7.25.2(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 '@babel/helper-annotate-as-pure': 7.24.7 regexpu-core: 5.3.2 semver: 6.3.1 - '@babel/helper-create-regexp-features-plugin@7.24.7(@babel/core@7.23.5)': + '@babel/helper-create-regexp-features-plugin@7.25.2(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-annotate-as-pure': 7.24.7 @@ -25954,7 +26170,7 @@ snapshots: '@babel/helper-module-imports': 7.24.7 '@babel/helper-plugin-utils': 7.22.5 '@babel/traverse': 7.24.7 - debug: 4.3.5 + debug: 4.3.6 lodash.debounce: 4.0.8 resolve: 1.22.8 semver: 6.3.1 @@ -25966,7 +26182,7 @@ snapshots: '@babel/core': 7.12.9 '@babel/helper-compilation-targets': 7.24.7 '@babel/helper-plugin-utils': 7.22.5 - debug: 4.3.5 + debug: 4.3.6 lodash.debounce: 4.0.8 resolve: 1.22.8 transitivePeerDependencies: @@ -25977,7 +26193,7 @@ snapshots: '@babel/core': 7.23.5 '@babel/helper-compilation-targets': 7.24.7 '@babel/helper-plugin-utils': 7.22.5 - debug: 4.3.5 + debug: 4.3.6 lodash.debounce: 4.0.8 resolve: 1.22.8 transitivePeerDependencies: @@ -25988,7 +26204,7 @@ snapshots: '@babel/core': 7.24.7 '@babel/helper-compilation-targets': 7.24.7 '@babel/helper-plugin-utils': 7.22.5 - debug: 4.3.5 + debug: 4.3.6 lodash.debounce: 4.0.8 resolve: 1.22.8 transitivePeerDependencies: @@ -25999,7 +26215,7 @@ snapshots: '@babel/core': 7.23.5 '@babel/helper-compilation-targets': 7.24.7 '@babel/helper-plugin-utils': 7.22.5 - debug: 4.3.5 + debug: 4.3.6 lodash.debounce: 4.0.8 resolve: 1.22.8 transitivePeerDependencies: @@ -26010,7 +26226,7 @@ snapshots: '@babel/core': 7.24.7 '@babel/helper-compilation-targets': 7.24.7 '@babel/helper-plugin-utils': 7.22.5 - debug: 4.3.5 + debug: 4.3.6 lodash.debounce: 4.0.8 resolve: 1.22.8 transitivePeerDependencies: @@ -26019,9 +26235,9 @@ snapshots: '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-compilation-targets': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - debug: 4.3.5 + '@babel/helper-compilation-targets': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + debug: 4.3.6 lodash.debounce: 4.0.8 resolve: 1.22.8 transitivePeerDependencies: @@ -26030,9 +26246,9 @@ snapshots: '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 - '@babel/helper-compilation-targets': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - debug: 4.3.5 + '@babel/helper-compilation-targets': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + debug: 4.3.6 lodash.debounce: 4.0.8 resolve: 1.22.8 transitivePeerDependencies: @@ -26066,10 +26282,10 @@ snapshots: dependencies: '@babel/types': 7.24.7 - '@babel/helper-member-expression-to-functions@7.24.7': + '@babel/helper-member-expression-to-functions@7.24.8': dependencies: - '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 + '@babel/traverse': 7.25.3 + '@babel/types': 7.25.2 transitivePeerDependencies: - supports-color @@ -26155,13 +26371,43 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-transforms@7.25.2(@babel/core@7.12.9)': + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-simple-access': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 + '@babel/traverse': 7.25.3 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.25.2(@babel/core@7.23.5)': + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-simple-access': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 + '@babel/traverse': 7.25.3 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.25.2(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-simple-access': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 + '@babel/traverse': 7.25.3 + transitivePeerDependencies: + - supports-color + '@babel/helper-optimise-call-expression@7.22.5': dependencies: '@babel/types': 7.24.7 '@babel/helper-optimise-call-expression@7.24.7': dependencies: - '@babel/types': 7.24.7 + '@babel/types': 7.25.2 '@babel/helper-plugin-utils@7.10.4': {} @@ -26169,6 +26415,8 @@ snapshots: '@babel/helper-plugin-utils@7.24.7': {} + '@babel/helper-plugin-utils@7.24.8': {} + '@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 @@ -26190,21 +26438,21 @@ snapshots: '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-wrap-function': 7.22.20 - '@babel/helper-remap-async-to-generator@7.24.7(@babel/core@7.12.9)': + '@babel/helper-remap-async-to-generator@7.25.0(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-wrap-function': 7.24.7 + '@babel/helper-wrap-function': 7.25.0 + '@babel/traverse': 7.25.3 transitivePeerDependencies: - supports-color - '@babel/helper-remap-async-to-generator@7.24.7(@babel/core@7.23.5)': + '@babel/helper-remap-async-to-generator@7.25.0(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-wrap-function': 7.24.7 + '@babel/helper-wrap-function': 7.25.0 + '@babel/traverse': 7.25.3 transitivePeerDependencies: - supports-color @@ -26236,30 +26484,37 @@ snapshots: '@babel/helper-member-expression-to-functions': 7.23.0 '@babel/helper-optimise-call-expression': 7.22.5 - '@babel/helper-replace-supers@7.24.7(@babel/core@7.12.9)': + '@babel/helper-replace-supers@7.22.20(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-member-expression-to-functions': 7.23.0 + '@babel/helper-optimise-call-expression': 7.22.5 + + '@babel/helper-replace-supers@7.25.0(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-member-expression-to-functions': 7.24.7 + '@babel/helper-member-expression-to-functions': 7.24.8 '@babel/helper-optimise-call-expression': 7.24.7 + '@babel/traverse': 7.25.3 transitivePeerDependencies: - supports-color - '@babel/helper-replace-supers@7.24.7(@babel/core@7.23.5)': + '@babel/helper-replace-supers@7.25.0(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-member-expression-to-functions': 7.24.7 + '@babel/helper-member-expression-to-functions': 7.24.8 '@babel/helper-optimise-call-expression': 7.24.7 + '@babel/traverse': 7.25.3 transitivePeerDependencies: - supports-color - '@babel/helper-replace-supers@7.24.7(@babel/core@7.24.7)': + '@babel/helper-replace-supers@7.25.0(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-member-expression-to-functions': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-member-expression-to-functions': 7.24.8 '@babel/helper-optimise-call-expression': 7.24.7 + '@babel/traverse': 7.25.3 transitivePeerDependencies: - supports-color @@ -26280,8 +26535,8 @@ snapshots: '@babel/helper-skip-transparent-expression-wrappers@7.24.7': dependencies: - '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 + '@babel/traverse': 7.25.3 + '@babel/types': 7.25.2 transitivePeerDependencies: - supports-color @@ -26297,6 +26552,8 @@ snapshots: '@babel/helper-string-parser@7.24.7': {} + '@babel/helper-string-parser@7.24.8': {} + '@babel/helper-validator-identifier@7.22.20': {} '@babel/helper-validator-identifier@7.24.7': {} @@ -26305,18 +26562,19 @@ snapshots: '@babel/helper-validator-option@7.24.7': {} + '@babel/helper-validator-option@7.24.8': {} + '@babel/helper-wrap-function@7.22.20': dependencies: '@babel/helper-function-name': 7.24.7 '@babel/template': 7.24.7 '@babel/types': 7.24.7 - '@babel/helper-wrap-function@7.24.7': + '@babel/helper-wrap-function@7.25.0': dependencies: - '@babel/helper-function-name': 7.24.7 - '@babel/template': 7.24.7 - '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 + '@babel/template': 7.25.0 + '@babel/traverse': 7.25.3 + '@babel/types': 7.25.2 transitivePeerDependencies: - supports-color @@ -26333,6 +26591,11 @@ snapshots: '@babel/template': 7.24.7 '@babel/types': 7.24.7 + '@babel/helpers@7.25.0': + dependencies: + '@babel/template': 7.25.0 + '@babel/types': 7.25.2 + '@babel/highlight@7.23.4': dependencies: '@babel/helper-validator-identifier': 7.24.7 @@ -26354,6 +26617,10 @@ snapshots: dependencies: '@babel/types': 7.24.7 + '@babel/parser@7.25.3': + dependencies: + '@babel/types': 7.25.2 + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 @@ -26448,6 +26715,12 @@ snapshots: '@babel/helper-create-class-features-plugin': 7.23.6(@babel/core@7.24.7) '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-create-class-features-plugin': 7.23.6(@babel/core@7.25.2) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-proposal-decorators@7.23.5(@babel/core@7.24.7)': dependencies: '@babel/core': 7.24.7 @@ -26472,13 +26745,13 @@ snapshots: '@babel/plugin-proposal-export-default-from@7.24.7(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-export-default-from': 7.24.7(@babel/core@7.12.9) '@babel/plugin-proposal-export-default-from@7.24.7(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-export-default-from': 7.24.7(@babel/core@7.23.5) '@babel/plugin-proposal-export-namespace-from@7.18.9(@babel/core@7.12.9)': @@ -26517,6 +26790,12 @@ snapshots: '@babel/helper-plugin-utils': 7.22.5 '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.25.2) + '@babel/plugin-proposal-numeric-separator@7.18.6(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 @@ -26603,6 +26882,13 @@ snapshots: '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.25.2) + '@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 @@ -26758,12 +27044,12 @@ snapshots: '@babel/plugin-syntax-export-default-from@7.24.7(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-export-default-from@7.24.7(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.12.9)': dependencies: @@ -26798,17 +27084,17 @@ snapshots: '@babel/plugin-syntax-flow@7.24.7(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-flow@7.24.7(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-flow@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-import-assertions@7.23.3(@babel/core@7.12.9)': dependencies: @@ -26918,17 +27204,17 @@ snapshots: '@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.12.9)': dependencies: @@ -26970,6 +27256,11 @@ snapshots: '@babel/core': 7.24.7 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 @@ -27050,6 +27341,11 @@ snapshots: '@babel/core': 7.24.7 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 @@ -27115,6 +27411,11 @@ snapshots: '@babel/core': 7.24.7 '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-typescript@7.24.7(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.7 + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 @@ -27151,12 +27452,12 @@ snapshots: '@babel/plugin-transform-arrow-functions@7.24.7(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-transform-arrow-functions@7.24.7(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-transform-async-generator-functions@7.23.4(@babel/core@7.12.9)': dependencies: @@ -27207,8 +27508,8 @@ snapshots: dependencies: '@babel/core': 7.12.9 '@babel/helper-module-imports': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.12.9) + '@babel/helper-plugin-utils': 7.24.8 + '@babel/helper-remap-async-to-generator': 7.25.0(@babel/core@7.12.9) transitivePeerDependencies: - supports-color @@ -27216,8 +27517,8 @@ snapshots: dependencies: '@babel/core': 7.23.5 '@babel/helper-module-imports': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.23.5) + '@babel/helper-plugin-utils': 7.24.8 + '@babel/helper-remap-async-to-generator': 7.25.0(@babel/core@7.23.5) transitivePeerDependencies: - supports-color @@ -27251,15 +27552,15 @@ snapshots: '@babel/core': 7.24.7 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-transform-block-scoping@7.24.7(@babel/core@7.12.9)': + '@babel/plugin-transform-block-scoping@7.25.0(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-block-scoping@7.24.7(@babel/core@7.23.5)': + '@babel/plugin-transform-block-scoping@7.25.0(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-transform-class-properties@7.23.3(@babel/core@7.12.9)': dependencies: @@ -27339,30 +27640,26 @@ snapshots: '@babel/helper-split-export-declaration': 7.24.7 globals: 11.12.0 - '@babel/plugin-transform-classes@7.24.7(@babel/core@7.12.9)': + '@babel/plugin-transform-classes@7.25.0(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-compilation-targets': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-function-name': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/helper-replace-supers': 7.24.7(@babel/core@7.12.9) - '@babel/helper-split-export-declaration': 7.24.7 + '@babel/helper-compilation-targets': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/helper-replace-supers': 7.25.0(@babel/core@7.12.9) + '@babel/traverse': 7.25.3 globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-classes@7.24.7(@babel/core@7.23.5)': + '@babel/plugin-transform-classes@7.25.0(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-compilation-targets': 7.24.7 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-function-name': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/helper-replace-supers': 7.24.7(@babel/core@7.23.5) - '@babel/helper-split-export-declaration': 7.24.7 + '@babel/helper-compilation-targets': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/helper-replace-supers': 7.25.0(@babel/core@7.23.5) + '@babel/traverse': 7.25.3 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -27388,14 +27685,14 @@ snapshots: '@babel/plugin-transform-computed-properties@7.24.7(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/template': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/template': 7.25.0 '@babel/plugin-transform-computed-properties@7.24.7(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/template': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/template': 7.25.0 '@babel/plugin-transform-destructuring@7.23.3(@babel/core@7.12.9)': dependencies: @@ -27412,15 +27709,15 @@ snapshots: '@babel/core': 7.24.7 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-transform-destructuring@7.24.7(@babel/core@7.12.9)': + '@babel/plugin-transform-destructuring@7.24.8(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-destructuring@7.24.7(@babel/core@7.23.5)': + '@babel/plugin-transform-destructuring@7.24.8(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-transform-dotall-regex@7.23.3(@babel/core@7.12.9)': dependencies: @@ -27527,23 +27824,23 @@ snapshots: '@babel/helper-plugin-utils': 7.22.5 '@babel/plugin-syntax-flow': 7.23.3(@babel/core@7.24.7) - '@babel/plugin-transform-flow-strip-types@7.24.7(@babel/core@7.12.9)': + '@babel/plugin-transform-flow-strip-types@7.25.2(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.12.9) - '@babel/plugin-transform-flow-strip-types@7.24.7(@babel/core@7.23.5)': + '@babel/plugin-transform-flow-strip-types@7.25.2(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.23.5) - '@babel/plugin-transform-flow-strip-types@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-flow-strip-types@7.25.2(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.24.7) + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.25.2) '@babel/plugin-transform-for-of@7.23.3(@babel/core@7.12.9)': dependencies: @@ -27593,19 +27890,23 @@ snapshots: '@babel/helper-function-name': 7.24.7 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-transform-function-name@7.24.7(@babel/core@7.12.9)': + '@babel/plugin-transform-function-name@7.25.1(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-compilation-targets': 7.24.7 - '@babel/helper-function-name': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-compilation-targets': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/traverse': 7.25.3 + transitivePeerDependencies: + - supports-color - '@babel/plugin-transform-function-name@7.24.7(@babel/core@7.23.5)': + '@babel/plugin-transform-function-name@7.25.1(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 - '@babel/helper-compilation-targets': 7.24.7 - '@babel/helper-function-name': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-compilation-targets': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/traverse': 7.25.3 + transitivePeerDependencies: + - supports-color '@babel/plugin-transform-json-strings@7.23.4(@babel/core@7.12.9)': dependencies: @@ -27640,15 +27941,15 @@ snapshots: '@babel/core': 7.24.7 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-transform-literals@7.24.7(@babel/core@7.12.9)': + '@babel/plugin-transform-literals@7.25.2(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-transform-literals@7.24.7(@babel/core@7.23.5)': + '@babel/plugin-transform-literals@7.25.2(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-transform-logical-assignment-operators@7.23.4(@babel/core@7.12.9)': dependencies: @@ -27743,29 +28044,29 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-commonjs@7.24.7(@babel/core@7.12.9)': + '@babel/plugin-transform-modules-commonjs@7.24.8(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-module-transforms': 7.24.7(@babel/core@7.12.9) - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-module-transforms': 7.25.2(@babel/core@7.12.9) + '@babel/helper-plugin-utils': 7.24.8 '@babel/helper-simple-access': 7.24.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-commonjs@7.24.7(@babel/core@7.23.5)': + '@babel/plugin-transform-modules-commonjs@7.24.8(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 - '@babel/helper-module-transforms': 7.24.7(@babel/core@7.23.5) - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-module-transforms': 7.25.2(@babel/core@7.23.5) + '@babel/helper-plugin-utils': 7.24.8 '@babel/helper-simple-access': 7.24.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-commonjs@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-modules-commonjs@7.24.8(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.7 + '@babel/core': 7.25.2 + '@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2) + '@babel/helper-plugin-utils': 7.24.8 '@babel/helper-simple-access': 7.24.7 transitivePeerDependencies: - supports-color @@ -27845,14 +28146,14 @@ snapshots: '@babel/plugin-transform-named-capturing-groups-regex@7.24.7(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.12.9) - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.12.9) + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-transform-named-capturing-groups-regex@7.24.7(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 - '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.23.5) - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.23.5) + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-transform-new-target@7.23.3(@babel/core@7.12.9)': dependencies: @@ -28007,12 +28308,12 @@ snapshots: '@babel/plugin-transform-parameters@7.24.7(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-transform-parameters@7.24.7(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-transform-private-methods@7.23.3(@babel/core@7.12.9)': dependencies: @@ -28035,16 +28336,16 @@ snapshots: '@babel/plugin-transform-private-methods@7.24.7(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.12.9) - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-create-class-features-plugin': 7.25.0(@babel/core@7.12.9) + '@babel/helper-plugin-utils': 7.24.8 transitivePeerDependencies: - supports-color '@babel/plugin-transform-private-methods@7.24.7(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 - '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.23.5) - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-create-class-features-plugin': 7.25.0(@babel/core@7.23.5) + '@babel/helper-plugin-utils': 7.24.8 transitivePeerDependencies: - supports-color @@ -28076,8 +28377,8 @@ snapshots: dependencies: '@babel/core': 7.12.9 '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.12.9) - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-create-class-features-plugin': 7.25.0(@babel/core@7.12.9) + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.12.9) transitivePeerDependencies: - supports-color @@ -28086,8 +28387,8 @@ snapshots: dependencies: '@babel/core': 7.23.5 '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.23.5) - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-create-class-features-plugin': 7.25.0(@babel/core@7.23.5) + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.5) transitivePeerDependencies: - supports-color @@ -28135,12 +28436,12 @@ snapshots: '@babel/plugin-transform-react-display-name@7.24.7(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-transform-react-display-name@7.24.7(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-transform-react-jsx-development@7.22.5(@babel/core@7.23.2)': dependencies: @@ -28160,22 +28461,22 @@ snapshots: '@babel/plugin-transform-react-jsx-self@7.24.7(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-transform-react-jsx-self@7.24.7(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-transform-react-jsx-source@7.24.7(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-transform-react-jsx-source@7.24.7(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-transform-react-jsx@7.23.4(@babel/core@7.12.9)': dependencies: @@ -28213,25 +28514,25 @@ snapshots: '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.24.7) '@babel/types': 7.23.5 - '@babel/plugin-transform-react-jsx@7.24.7(@babel/core@7.12.9)': + '@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 '@babel/helper-annotate-as-pure': 7.24.7 '@babel/helper-module-imports': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.12.9) - '@babel/types': 7.24.7 + '@babel/types': 7.25.2 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx@7.24.7(@babel/core@7.23.5)': + '@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-annotate-as-pure': 7.24.7 '@babel/helper-module-imports': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.23.5) - '@babel/types': 7.24.7 + '@babel/types': 7.25.2 transitivePeerDependencies: - supports-color @@ -28326,9 +28627,9 @@ snapshots: dependencies: '@babel/core': 7.12.9 '@babel/helper-module-imports': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.12.9) - babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.12.9) + babel-plugin-polyfill-corejs3: 0.10.6(@babel/core@7.12.9) babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.12.9) semver: 6.3.1 transitivePeerDependencies: @@ -28338,9 +28639,9 @@ snapshots: dependencies: '@babel/core': 7.23.5 '@babel/helper-module-imports': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.23.5) - babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.23.5) + babel-plugin-polyfill-corejs3: 0.10.6(@babel/core@7.23.5) babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.23.5) semver: 6.3.1 transitivePeerDependencies: @@ -28364,12 +28665,12 @@ snapshots: '@babel/plugin-transform-shorthand-properties@7.24.7(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-transform-shorthand-properties@7.24.7(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-transform-spread@7.23.3(@babel/core@7.12.9)': dependencies: @@ -28392,7 +28693,7 @@ snapshots: '@babel/plugin-transform-spread@7.24.7(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 transitivePeerDependencies: - supports-color @@ -28400,7 +28701,7 @@ snapshots: '@babel/plugin-transform-spread@7.24.7(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 transitivePeerDependencies: - supports-color @@ -28423,12 +28724,12 @@ snapshots: '@babel/plugin-transform-sticky-regex@7.24.7(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-transform-sticky-regex@7.24.7(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-transform-template-literals@7.23.3(@babel/core@7.12.9)': dependencies: @@ -28492,33 +28793,36 @@ snapshots: '@babel/helper-plugin-utils': 7.22.5 '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.24.7) - '@babel/plugin-transform-typescript@7.24.7(@babel/core@7.12.9)': + '@babel/plugin-transform-typescript@7.25.2(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.12.9) - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-create-class-features-plugin': 7.25.0(@babel/core@7.12.9) + '@babel/helper-plugin-utils': 7.24.8 + '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 '@babel/plugin-syntax-typescript': 7.24.7(@babel/core@7.12.9) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-typescript@7.24.7(@babel/core@7.23.5)': + '@babel/plugin-transform-typescript@7.25.2(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.23.5) - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-create-class-features-plugin': 7.25.0(@babel/core@7.23.5) + '@babel/helper-plugin-utils': 7.24.8 + '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 '@babel/plugin-syntax-typescript': 7.24.7(@babel/core@7.23.5) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-typescript@7.24.7(@babel/core@7.24.7)': + '@babel/plugin-transform-typescript@7.25.2(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.2 '@babel/helper-annotate-as-pure': 7.24.7 - '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7) - '@babel/helper-plugin-utils': 7.24.7 - '@babel/plugin-syntax-typescript': 7.24.7(@babel/core@7.24.7) + '@babel/helper-create-class-features-plugin': 7.25.0(@babel/core@7.25.2) + '@babel/helper-plugin-utils': 7.24.8 + '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + '@babel/plugin-syntax-typescript': 7.24.7(@babel/core@7.25.2) transitivePeerDependencies: - supports-color @@ -28576,14 +28880,14 @@ snapshots: '@babel/plugin-transform-unicode-regex@7.24.7(@babel/core@7.12.9)': dependencies: '@babel/core': 7.12.9 - '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.12.9) - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.12.9) + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-transform-unicode-regex@7.24.7(@babel/core@7.23.5)': dependencies: '@babel/core': 7.23.5 - '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.23.5) - '@babel/helper-plugin-utils': 7.24.7 + '@babel/helper-create-regexp-features-plugin': 7.25.2(@babel/core@7.23.5) + '@babel/helper-plugin-utils': 7.24.8 '@babel/plugin-transform-unicode-sets-regex@7.23.3(@babel/core@7.12.9)': dependencies: @@ -29131,12 +29435,12 @@ snapshots: '@babel/helper-validator-option': 7.23.5 '@babel/plugin-transform-flow-strip-types': 7.23.3(@babel/core@7.24.7) - '@babel/preset-flow@7.24.7(@babel/core@7.24.7)': + '@babel/preset-flow@7.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/helper-validator-option': 7.24.7 - '@babel/plugin-transform-flow-strip-types': 7.24.7(@babel/core@7.24.7) + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/helper-validator-option': 7.24.8 + '@babel/plugin-transform-flow-strip-types': 7.25.2(@babel/core@7.25.2) '@babel/preset-modules@0.1.6(@babel/core@7.12.9)': dependencies: @@ -29242,14 +29546,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/preset-typescript@7.24.7(@babel/core@7.24.7)': + '@babel/preset-typescript@7.24.7(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 - '@babel/helper-plugin-utils': 7.24.7 - '@babel/helper-validator-option': 7.24.7 - '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.24.7) - '@babel/plugin-transform-typescript': 7.24.7(@babel/core@7.24.7) + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + '@babel/helper-validator-option': 7.24.8 + '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-modules-commonjs': 7.24.8(@babel/core@7.25.2) + '@babel/plugin-transform-typescript': 7.25.2(@babel/core@7.25.2) transitivePeerDependencies: - supports-color @@ -29289,9 +29593,9 @@ snapshots: pirates: 4.0.6 source-map-support: 0.5.21 - '@babel/register@7.24.6(@babel/core@7.24.7)': + '@babel/register@7.24.6(@babel/core@7.25.2)': dependencies: - '@babel/core': 7.24.7 + '@babel/core': 7.25.2 clone-deep: 4.0.1 find-cache-dir: 2.1.0 make-dir: 2.1.0 @@ -29317,6 +29621,10 @@ snapshots: dependencies: regenerator-runtime: 0.14.1 + '@babel/runtime@7.25.0': + dependencies: + regenerator-runtime: 0.14.1 + '@babel/template@7.22.15': dependencies: '@babel/code-frame': 7.23.5 @@ -29329,6 +29637,12 @@ snapshots: '@babel/parser': 7.24.7 '@babel/types': 7.24.7 + '@babel/template@7.25.0': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/parser': 7.25.3 + '@babel/types': 7.25.2 + '@babel/traverse@7.23.5': dependencies: '@babel/code-frame': 7.24.7 @@ -29354,7 +29668,19 @@ snapshots: '@babel/helper-split-export-declaration': 7.24.7 '@babel/parser': 7.24.7 '@babel/types': 7.24.7 - debug: 4.3.5 + debug: 4.3.6 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/traverse@7.25.3': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.25.0 + '@babel/parser': 7.25.3 + '@babel/template': 7.25.0 + '@babel/types': 7.25.2 + debug: 4.3.6 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -29377,6 +29703,12 @@ snapshots: '@babel/helper-validator-identifier': 7.24.7 to-fast-properties: 2.0.0 + '@babel/types@7.25.2': + dependencies: + '@babel/helper-string-parser': 7.24.8 + '@babel/helper-validator-identifier': 7.24.7 + to-fast-properties: 2.0.0 + '@bartekbp/typescript-checkstyle@5.0.0': dependencies: '@aivenio/tsc-output-parser': 2.1.1 @@ -29475,7 +29807,7 @@ snapshots: '@emotion/babel-plugin@11.11.0': dependencies: '@babel/helper-module-imports': 7.24.7 - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@emotion/hash': 0.9.1 '@emotion/memoize': 0.8.1 '@emotion/serialize': 1.1.2 @@ -29505,7 +29837,7 @@ snapshots: '@emotion/core@10.3.1(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@emotion/cache': 10.0.29 '@emotion/css': 10.0.27 '@emotion/serialize': 0.11.16 @@ -29515,6 +29847,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@emotion/core@10.3.1(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.0 + '@emotion/cache': 10.0.29 + '@emotion/css': 10.0.27 + '@emotion/serialize': 0.11.16 + '@emotion/sheet': 0.9.4 + '@emotion/utils': 0.11.3 + react: 18.3.1 + transitivePeerDependencies: + - supports-color + '@emotion/css@10.0.27': dependencies: '@emotion/serialize': 0.11.16 @@ -29604,7 +29948,7 @@ snapshots: '@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@17.0.71)(react@17.0.2))(@types/react@17.0.71)(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@emotion/babel-plugin': 11.11.0 '@emotion/is-prop-valid': 1.2.1 '@emotion/react': 11.11.1(@types/react@17.0.71)(react@17.0.2) @@ -29619,7 +29963,7 @@ snapshots: '@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@17.0.71)(react@18.3.1))(@types/react@17.0.71)(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@emotion/babel-plugin': 11.11.0 '@emotion/is-prop-valid': 1.2.1 '@emotion/react': 11.11.1(@types/react@17.0.71)(react@18.3.1) @@ -29750,7 +30094,7 @@ snapshots: '@eslint/eslintrc@0.4.3': dependencies: ajv: 6.12.6 - debug: 4.3.5 + debug: 4.3.6 espree: 7.3.1 globals: 13.24.0 ignore: 4.0.6 @@ -29787,8 +30131,6 @@ snapshots: '@floating-ui/core@0.6.2': {} - '@floating-ui/core@0.7.3': {} - '@floating-ui/core@1.5.2': dependencies: '@floating-ui/utils': 0.1.6 @@ -29797,10 +30139,6 @@ snapshots: dependencies: '@floating-ui/core': 0.6.2 - '@floating-ui/dom@0.5.4': - dependencies: - '@floating-ui/core': 0.7.3 - '@floating-ui/dom@1.5.3': dependencies: '@floating-ui/core': 1.5.2 @@ -29824,27 +30162,12 @@ snapshots: transitivePeerDependencies: - '@types/react' - '@floating-ui/react-dom@0.7.2(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': - dependencies: - '@floating-ui/dom': 0.5.4 - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - use-isomorphic-layout-effect: 1.1.2(@types/react@17.0.71)(react@17.0.2) - transitivePeerDependencies: - - '@types/react' - '@floating-ui/react-dom@1.3.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: '@floating-ui/dom': 1.5.3 react: 17.0.2 react-dom: 17.0.2(react@17.0.2) - '@floating-ui/react-dom@2.0.4(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': - dependencies: - '@floating-ui/dom': 1.5.3 - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - '@floating-ui/react-dom@2.0.4(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': dependencies: '@floating-ui/dom': 1.5.3 @@ -29907,7 +30230,7 @@ snapshots: '@humanwhocodes/config-array@0.5.0': dependencies: '@humanwhocodes/object-schema': 1.2.1 - debug: 4.3.5 + debug: 4.3.6 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -30205,7 +30528,7 @@ snapshots: - supports-color - ts-node - '@jest/core@29.7.0(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.3.3))': + '@jest/core@29.7.0(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.4.0)(typescript@5.3.3))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0(node-notifier@8.0.2) @@ -30219,7 +30542,7 @@ snapshots: exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@16.18.68)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.3.3)) + jest-config: 29.7.0(@types/node@16.18.68)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.4.0)(typescript@5.3.3)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -30827,7 +31150,7 @@ snapshots: '@kwsites/file-exists@1.1.1': dependencies: - debug: 4.3.5 + debug: 4.3.6 transitivePeerDependencies: - supports-color @@ -30863,6 +31186,10 @@ snapshots: dependencies: react: 17.0.2 + '@mdx-js/react@1.6.22(react@18.3.1)': + dependencies: + react: 18.3.1 + '@mdx-js/react@2.3.0(react@18.3.1)': dependencies: '@types/mdx': 2.0.10 @@ -31211,7 +31538,7 @@ snapshots: '@oclif/color': 1.0.13 '@oclif/core': 2.15.0(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.3) chalk: 4.1.2 - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) fs-extra: 9.1.0 http-call: 5.3.0 load-json-file: 5.3.0 @@ -31230,7 +31557,7 @@ snapshots: dependencies: '@oclif/core': 2.15.0(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.3) chalk: 4.1.2 - debug: 4.3.5 + debug: 4.3.6 http-call: 5.3.0 lodash.template: 4.5.0 semver: 7.6.2 @@ -31569,9 +31896,9 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@playwright/test@1.45.1': + '@playwright/test@1.46.1': dependencies: - playwright: 1.45.1 + playwright: 1.46.1 '@pmmmwh/react-refresh-webpack-plugin@0.5.11(@types/webpack@4.41.38)(react-refresh@0.10.0)(type-fest@2.19.0)(webpack-dev-server@4.15.1(webpack-cli@4.10.0)(webpack@5.91.0))(webpack-hot-middleware@2.25.4)(webpack@5.91.0(@swc/core@1.3.100)(esbuild@0.18.20)(webpack-cli@4.10.0))': dependencies: @@ -31681,7 +32008,7 @@ snapshots: '@puppeteer/browsers@1.4.6(typescript@5.3.2)': dependencies: - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) extract-zip: 2.0.1 progress: 2.0.3 proxy-agent: 6.3.0 @@ -31695,7 +32022,7 @@ snapshots: '@puppeteer/browsers@1.4.6(typescript@5.3.3)': dependencies: - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) extract-zip: 2.0.1 progress: 2.0.3 proxy-agent: 6.3.0 @@ -31709,7 +32036,7 @@ snapshots: '@puppeteer/browsers@1.9.0': dependencies: - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) extract-zip: 2.0.1 progress: 2.0.3 proxy-agent: 6.3.1 @@ -31721,36 +32048,19 @@ snapshots: '@radix-ui/number@1.0.1': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/primitive@1.0.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/primitive@1.0.1': dependencies: - '@babel/runtime': 7.24.7 - - '@radix-ui/react-arrow@1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-primitive': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - - '@radix-ui/react-arrow@1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - react: 17.0.2 - react-dom: 18.3.1(react@17.0.2) - optionalDependencies: - '@types/react': 17.0.71 - '@types/react-dom': 18.0.10 + '@babel/runtime': 7.25.0 '@radix-ui/react-arrow@1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -31758,32 +32068,19 @@ snapshots: '@types/react': 17.0.71 '@types/react-dom': 18.0.10 - '@radix-ui/react-collection@1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@radix-ui/react-arrow@1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-compose-refs': 1.0.0(react@17.0.2) - '@radix-ui/react-context': 1.0.0(react@17.0.2) - '@radix-ui/react-primitive': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-slot': 1.0.1(react@17.0.2) - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - - '@radix-ui/react-collection@1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-context': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - '@radix-ui/react-slot': 1.0.2(@types/react@17.0.71)(react@17.0.2) + '@babel/runtime': 7.25.0 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) react: 17.0.2 react-dom: 18.3.1(react@17.0.2) optionalDependencies: '@types/react': 17.0.71 - '@types/react-dom': 18.0.10 + '@types/react-dom': 18.3.0 '@radix-ui/react-collection@1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-compose-refs': 1.0.1(@types/react@17.0.71)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@17.0.71)(react@18.3.1) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -31794,57 +32091,70 @@ snapshots: '@types/react': 17.0.71 '@types/react-dom': 18.0.10 + '@radix-ui/react-collection@1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': + dependencies: + '@babel/runtime': 7.25.0 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-context': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@radix-ui/react-slot': 1.0.2(@types/react@17.0.71)(react@17.0.2) + react: 17.0.2 + react-dom: 18.3.1(react@17.0.2) + optionalDependencies: + '@types/react': 17.0.71 + '@types/react-dom': 18.3.0 + '@radix-ui/react-compose-refs@1.0.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 react: 17.0.2 '@radix-ui/react-compose-refs@1.0.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 react: 18.3.1 '@radix-ui/react-compose-refs@1.0.1(@types/react@17.0.71)(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 react: 17.0.2 optionalDependencies: '@types/react': 17.0.71 '@radix-ui/react-compose-refs@1.0.1(@types/react@17.0.71)(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 react: 18.3.1 optionalDependencies: '@types/react': 17.0.71 '@radix-ui/react-context@1.0.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 react: 17.0.2 '@radix-ui/react-context@1.0.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 react: 18.3.1 '@radix-ui/react-context@1.0.1(@types/react@17.0.71)(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 react: 17.0.2 optionalDependencies: '@types/react': 17.0.71 '@radix-ui/react-context@1.0.1(@types/react@17.0.71)(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 react: 18.3.1 optionalDependencies: '@types/react': 17.0.71 '@radix-ui/react-dialog@1.0.0(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/primitive': 1.0.0 '@radix-ui/react-compose-refs': 1.0.0(react@17.0.2) '@radix-ui/react-context': 1.0.0(react@17.0.2) @@ -31866,7 +32176,7 @@ snapshots: '@radix-ui/react-dialog@1.0.0(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/primitive': 1.0.0 '@radix-ui/react-compose-refs': 1.0.0(react@18.3.1) '@radix-ui/react-context': 1.0.0(react@18.3.1) @@ -31886,28 +32196,23 @@ snapshots: transitivePeerDependencies: - '@types/react' - '@radix-ui/react-direction@1.0.0(react@17.0.2)': - dependencies: - '@babel/runtime': 7.24.7 - react: 17.0.2 - '@radix-ui/react-direction@1.0.1(@types/react@17.0.71)(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 react: 17.0.2 optionalDependencies: '@types/react': 17.0.71 '@radix-ui/react-direction@1.0.1(@types/react@17.0.71)(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 react: 18.3.1 optionalDependencies: '@types/react': 17.0.71 '@radix-ui/react-dismissable-layer@1.0.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/primitive': 1.0.0 '@radix-ui/react-compose-refs': 1.0.0(react@17.0.2) '@radix-ui/react-primitive': 1.0.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) @@ -31918,7 +32223,7 @@ snapshots: '@radix-ui/react-dismissable-layer@1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/primitive': 1.0.0 '@radix-ui/react-compose-refs': 1.0.0(react@18.3.1) '@radix-ui/react-primitive': 1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -31927,34 +32232,9 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@radix-ui/react-dismissable-layer@1.0.3(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/primitive': 1.0.0 - '@radix-ui/react-compose-refs': 1.0.0(react@17.0.2) - '@radix-ui/react-primitive': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-use-callback-ref': 1.0.0(react@17.0.2) - '@radix-ui/react-use-escape-keydown': 1.0.2(react@17.0.2) - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - - '@radix-ui/react-dismissable-layer@1.0.4(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@17.0.71)(react@17.0.2) - react: 17.0.2 - react-dom: 18.3.1(react@17.0.2) - optionalDependencies: - '@types/react': 17.0.71 - '@types/react-dom': 18.0.10 - '@radix-ui/react-dismissable-layer@1.0.4(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@17.0.71)(react@18.3.1) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -31966,48 +32246,47 @@ snapshots: '@types/react': 17.0.71 '@types/react-dom': 18.0.10 - '@radix-ui/react-dropdown-menu@2.0.4(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@radix-ui/react-dismissable-layer@1.0.4(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/primitive': 1.0.0 - '@radix-ui/react-compose-refs': 1.0.0(react@17.0.2) - '@radix-ui/react-context': 1.0.0(react@17.0.2) - '@radix-ui/react-id': 1.0.0(react@17.0.2) - '@radix-ui/react-menu': 2.0.4(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-primitive': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-use-controllable-state': 1.0.0(react@17.0.2) + '@babel/runtime': 7.25.0 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@17.0.71)(react@17.0.2) react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - transitivePeerDependencies: - - '@types/react' + react-dom: 18.3.1(react@17.0.2) + optionalDependencies: + '@types/react': 17.0.71 + '@types/react-dom': 18.3.0 '@radix-ui/react-focus-guards@1.0.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 react: 17.0.2 '@radix-ui/react-focus-guards@1.0.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 react: 18.3.1 '@radix-ui/react-focus-guards@1.0.1(@types/react@17.0.71)(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 react: 17.0.2 optionalDependencies: '@types/react': 17.0.71 '@radix-ui/react-focus-guards@1.0.1(@types/react@17.0.71)(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 react: 18.3.1 optionalDependencies: '@types/react': 17.0.71 '@radix-ui/react-focus-scope@1.0.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-compose-refs': 1.0.0(react@17.0.2) '@radix-ui/react-primitive': 1.0.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@radix-ui/react-use-callback-ref': 1.0.0(react@17.0.2) @@ -32016,37 +32295,16 @@ snapshots: '@radix-ui/react-focus-scope@1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-compose-refs': 1.0.0(react@18.3.1) '@radix-ui/react-primitive': 1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-use-callback-ref': 1.0.0(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@radix-ui/react-focus-scope@1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-compose-refs': 1.0.0(react@17.0.2) - '@radix-ui/react-primitive': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-use-callback-ref': 1.0.0(react@17.0.2) - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - - '@radix-ui/react-focus-scope@1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@17.0.71)(react@17.0.2) - react: 17.0.2 - react-dom: 18.3.1(react@17.0.2) - optionalDependencies: - '@types/react': 17.0.71 - '@types/react-dom': 18.0.10 - '@radix-ui/react-focus-scope@1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-compose-refs': 1.0.1(@types/react@17.0.71)(react@18.3.1) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@17.0.71)(react@18.3.1) @@ -32056,21 +32314,33 @@ snapshots: '@types/react': 17.0.71 '@types/react-dom': 18.0.10 + '@radix-ui/react-focus-scope@1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': + dependencies: + '@babel/runtime': 7.25.0 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@17.0.71)(react@17.0.2) + react: 17.0.2 + react-dom: 18.3.1(react@17.0.2) + optionalDependencies: + '@types/react': 17.0.71 + '@types/react-dom': 18.3.0 + '@radix-ui/react-id@1.0.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-use-layout-effect': 1.0.0(react@17.0.2) react: 17.0.2 '@radix-ui/react-id@1.0.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-use-layout-effect': 1.0.0(react@18.3.1) react: 18.3.1 '@radix-ui/react-id@1.0.1(@types/react@17.0.71)(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@17.0.71)(react@17.0.2) react: 17.0.2 optionalDependencies: @@ -32078,78 +32348,15 @@ snapshots: '@radix-ui/react-id@1.0.1(@types/react@17.0.71)(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@17.0.71)(react@18.3.1) react: 18.3.1 optionalDependencies: '@types/react': 17.0.71 - '@radix-ui/react-menu@2.0.4(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/primitive': 1.0.0 - '@radix-ui/react-collection': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-compose-refs': 1.0.0(react@17.0.2) - '@radix-ui/react-context': 1.0.0(react@17.0.2) - '@radix-ui/react-direction': 1.0.0(react@17.0.2) - '@radix-ui/react-dismissable-layer': 1.0.3(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-focus-guards': 1.0.0(react@17.0.2) - '@radix-ui/react-focus-scope': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-id': 1.0.0(react@17.0.2) - '@radix-ui/react-popper': 1.1.1(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-portal': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-presence': 1.0.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-primitive': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-roving-focus': 1.0.3(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-slot': 1.0.1(react@17.0.2) - '@radix-ui/react-use-callback-ref': 1.0.0(react@17.0.2) - aria-hidden: 1.2.3 - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - react-remove-scroll: 2.5.5(@types/react@17.0.71)(react@17.0.2) - transitivePeerDependencies: - - '@types/react' - - '@radix-ui/react-popper@1.1.1(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.24.7 - '@floating-ui/react-dom': 0.7.2(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-arrow': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-compose-refs': 1.0.0(react@17.0.2) - '@radix-ui/react-context': 1.0.0(react@17.0.2) - '@radix-ui/react-primitive': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-use-callback-ref': 1.0.0(react@17.0.2) - '@radix-ui/react-use-layout-effect': 1.0.0(react@17.0.2) - '@radix-ui/react-use-rect': 1.0.0(react@17.0.2) - '@radix-ui/react-use-size': 1.0.0(react@17.0.2) - '@radix-ui/rect': 1.0.0 - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - transitivePeerDependencies: - - '@types/react' - - '@radix-ui/react-popper@1.1.2(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.24.7 - '@floating-ui/react-dom': 2.0.4(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - '@radix-ui/react-compose-refs': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-context': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-use-rect': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-use-size': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/rect': 1.0.1 - react: 17.0.2 - react-dom: 18.3.1(react@17.0.2) - optionalDependencies: - '@types/react': 17.0.71 - '@types/react-dom': 18.0.10 - '@radix-ui/react-popper@1.1.2(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@floating-ui/react-dom': 2.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-compose-refs': 1.0.1(@types/react@17.0.71)(react@18.3.1) @@ -32166,40 +32373,42 @@ snapshots: '@types/react': 17.0.71 '@types/react-dom': 18.0.10 + '@radix-ui/react-popper@1.1.2(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': + dependencies: + '@babel/runtime': 7.25.0 + '@floating-ui/react-dom': 2.0.4(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-context': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-use-rect': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-use-size': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/rect': 1.0.1 + react: 17.0.2 + react-dom: 18.3.1(react@17.0.2) + optionalDependencies: + '@types/react': 17.0.71 + '@types/react-dom': 18.3.0 + '@radix-ui/react-portal@1.0.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-primitive': 1.0.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) react: 17.0.2 react-dom: 17.0.2(react@17.0.2) '@radix-ui/react-portal@1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-primitive': 1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@radix-ui/react-portal@1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-primitive': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - - '@radix-ui/react-portal@1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - react: 17.0.2 - react-dom: 18.3.1(react@17.0.2) - optionalDependencies: - '@types/react': 17.0.71 - '@types/react-dom': 18.0.10 - '@radix-ui/react-portal@1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -32207,9 +32416,19 @@ snapshots: '@types/react': 17.0.71 '@types/react-dom': 18.0.10 + '@radix-ui/react-portal@1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': + dependencies: + '@babel/runtime': 7.25.0 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + react: 17.0.2 + react-dom: 18.3.1(react@17.0.2) + optionalDependencies: + '@types/react': 17.0.71 + '@types/react-dom': 18.3.0 + '@radix-ui/react-presence@1.0.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-compose-refs': 1.0.0(react@17.0.2) '@radix-ui/react-use-layout-effect': 1.0.0(react@17.0.2) react: 17.0.2 @@ -32217,7 +32436,7 @@ snapshots: '@radix-ui/react-presence@1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-compose-refs': 1.0.0(react@18.3.1) '@radix-ui/react-use-layout-effect': 1.0.0(react@18.3.1) react: 18.3.1 @@ -32225,38 +32444,21 @@ snapshots: '@radix-ui/react-primitive@1.0.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-slot': 1.0.0(react@17.0.2) react: 17.0.2 react-dom: 17.0.2(react@17.0.2) '@radix-ui/react-primitive@1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-slot': 1.0.0(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@radix-ui/react-primitive@1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-slot': 1.0.1(react@17.0.2) - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - - '@radix-ui/react-primitive@1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-slot': 1.0.2(@types/react@17.0.71)(react@17.0.2) - react: 17.0.2 - react-dom: 18.3.1(react@17.0.2) - optionalDependencies: - '@types/react': 17.0.71 - '@types/react-dom': 18.0.10 - '@radix-ui/react-primitive@1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-slot': 1.0.2(@types/react@17.0.71)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -32264,42 +32466,19 @@ snapshots: '@types/react': 17.0.71 '@types/react-dom': 18.0.10 - '@radix-ui/react-roving-focus@1.0.3(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@radix-ui/react-primitive@1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/primitive': 1.0.0 - '@radix-ui/react-collection': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-compose-refs': 1.0.0(react@17.0.2) - '@radix-ui/react-context': 1.0.0(react@17.0.2) - '@radix-ui/react-direction': 1.0.0(react@17.0.2) - '@radix-ui/react-id': 1.0.0(react@17.0.2) - '@radix-ui/react-primitive': 1.0.2(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-use-callback-ref': 1.0.0(react@17.0.2) - '@radix-ui/react-use-controllable-state': 1.0.0(react@17.0.2) - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - - '@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - '@radix-ui/react-compose-refs': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-context': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-direction': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-id': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@babel/runtime': 7.25.0 + '@radix-ui/react-slot': 1.0.2(@types/react@17.0.71)(react@17.0.2) react: 17.0.2 react-dom: 18.3.1(react@17.0.2) optionalDependencies: '@types/react': 17.0.71 - '@types/react-dom': 18.0.10 + '@types/react-dom': 18.3.0 '@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-compose-refs': 1.0.1(@types/react@17.0.71)(react@18.3.1) @@ -32315,39 +32494,27 @@ snapshots: '@types/react': 17.0.71 '@types/react-dom': 18.0.10 - '@radix-ui/react-select@1.2.2(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': + '@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/number': 1.0.1 + '@babel/runtime': 7.25.0 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) '@radix-ui/react-compose-refs': 1.0.1(@types/react@17.0.71)(react@17.0.2) '@radix-ui/react-context': 1.0.1(@types/react@17.0.71)(react@17.0.2) '@radix-ui/react-direction': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-dismissable-layer': 1.0.4(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - '@radix-ui/react-focus-guards': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-focus-scope': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) '@radix-ui/react-id': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-popper': 1.1.2(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - '@radix-ui/react-portal': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - '@radix-ui/react-slot': 1.0.2(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@17.0.71)(react@17.0.2) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-use-previous': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - aria-hidden: 1.2.3 react: 17.0.2 react-dom: 18.3.1(react@17.0.2) - react-remove-scroll: 2.5.5(@types/react@17.0.71)(react@17.0.2) optionalDependencies: '@types/react': 17.0.71 - '@types/react-dom': 18.0.10 + '@types/react-dom': 18.3.0 '@radix-ui/react-select@1.2.2(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/number': 1.0.1 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -32375,19 +32542,39 @@ snapshots: '@types/react': 17.0.71 '@types/react-dom': 18.0.10 - '@radix-ui/react-separator@1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': + '@radix-ui/react-select@1.2.2(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@babel/runtime': 7.25.0 + '@radix-ui/number': 1.0.1 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-context': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-direction': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-dismissable-layer': 1.0.4(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-focus-scope': 1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@radix-ui/react-id': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-popper': 1.1.2(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@radix-ui/react-portal': 1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@radix-ui/react-slot': 1.0.2(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-use-previous': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + aria-hidden: 1.2.3 react: 17.0.2 react-dom: 18.3.1(react@17.0.2) + react-remove-scroll: 2.5.5(@types/react@17.0.71)(react@17.0.2) optionalDependencies: '@types/react': 17.0.71 - '@types/react-dom': 18.0.10 + '@types/react-dom': 18.3.0 '@radix-ui/react-separator@1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -32395,27 +32582,31 @@ snapshots: '@types/react': 17.0.71 '@types/react-dom': 18.0.10 + '@radix-ui/react-separator@1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': + dependencies: + '@babel/runtime': 7.25.0 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + react: 17.0.2 + react-dom: 18.3.1(react@17.0.2) + optionalDependencies: + '@types/react': 17.0.71 + '@types/react-dom': 18.3.0 + '@radix-ui/react-slot@1.0.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-compose-refs': 1.0.0(react@17.0.2) react: 17.0.2 '@radix-ui/react-slot@1.0.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-compose-refs': 1.0.0(react@18.3.1) react: 18.3.1 - '@radix-ui/react-slot@1.0.1(react@17.0.2)': - dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-compose-refs': 1.0.0(react@17.0.2) - react: 17.0.2 - '@radix-ui/react-slot@1.0.2(@types/react@17.0.71)(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-compose-refs': 1.0.1(@types/react@17.0.71)(react@17.0.2) react: 17.0.2 optionalDependencies: @@ -32423,31 +32614,15 @@ snapshots: '@radix-ui/react-slot@1.0.2(@types/react@17.0.71)(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-compose-refs': 1.0.1(@types/react@17.0.71)(react@18.3.1) react: 18.3.1 optionalDependencies: '@types/react': 17.0.71 - '@radix-ui/react-toggle-group@1.0.4(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-context': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-direction': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - '@radix-ui/react-toggle': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@17.0.71)(react@17.0.2) - react: 17.0.2 - react-dom: 18.3.1(react@17.0.2) - optionalDependencies: - '@types/react': 17.0.71 - '@types/react-dom': 18.0.10 - '@radix-ui/react-toggle-group@1.0.4(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-context': 1.0.1(@types/react@17.0.71)(react@18.3.1) '@radix-ui/react-direction': 1.0.1(@types/react@17.0.71)(react@18.3.1) @@ -32461,21 +32636,25 @@ snapshots: '@types/react': 17.0.71 '@types/react-dom': 18.0.10 - '@radix-ui/react-toggle@1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': + '@radix-ui/react-toggle-group@1.0.4(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@radix-ui/react-context': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-direction': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@radix-ui/react-toggle': 1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@17.0.71)(react@17.0.2) react: 17.0.2 react-dom: 18.3.1(react@17.0.2) optionalDependencies: '@types/react': 17.0.71 - '@types/react-dom': 18.0.10 + '@types/react-dom': 18.3.0 '@radix-ui/react-toggle@1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@17.0.71)(react@18.3.1) @@ -32485,25 +32664,21 @@ snapshots: '@types/react': 17.0.71 '@types/react-dom': 18.0.10 - '@radix-ui/react-toolbar@1.0.4(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': + '@radix-ui/react-toggle@1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-context': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-direction': 1.0.1(@types/react@17.0.71)(react@17.0.2) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - '@radix-ui/react-separator': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - '@radix-ui/react-toggle-group': 1.0.4(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@17.0.71)(react@17.0.2) react: 17.0.2 react-dom: 18.3.1(react@17.0.2) optionalDependencies: '@types/react': 17.0.71 - '@types/react-dom': 18.0.10 + '@types/react-dom': 18.3.0 '@radix-ui/react-toolbar@1.0.4(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-context': 1.0.1(@types/react@17.0.71)(react@18.3.1) '@radix-ui/react-direction': 1.0.1(@types/react@17.0.71)(react@18.3.1) @@ -32517,45 +32692,61 @@ snapshots: '@types/react': 17.0.71 '@types/react-dom': 18.0.10 + '@radix-ui/react-toolbar@1.0.4(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': + dependencies: + '@babel/runtime': 7.25.0 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-context': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-direction': 1.0.1(@types/react@17.0.71)(react@17.0.2) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@radix-ui/react-separator': 1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@radix-ui/react-toggle-group': 1.0.4(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + react: 17.0.2 + react-dom: 18.3.1(react@17.0.2) + optionalDependencies: + '@types/react': 17.0.71 + '@types/react-dom': 18.3.0 + '@radix-ui/react-use-callback-ref@1.0.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 react: 17.0.2 '@radix-ui/react-use-callback-ref@1.0.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 react: 18.3.1 '@radix-ui/react-use-callback-ref@1.0.1(@types/react@17.0.71)(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 react: 17.0.2 optionalDependencies: '@types/react': 17.0.71 '@radix-ui/react-use-callback-ref@1.0.1(@types/react@17.0.71)(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 react: 18.3.1 optionalDependencies: '@types/react': 17.0.71 '@radix-ui/react-use-controllable-state@1.0.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-use-callback-ref': 1.0.0(react@17.0.2) react: 17.0.2 '@radix-ui/react-use-controllable-state@1.0.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-use-callback-ref': 1.0.0(react@18.3.1) react: 18.3.1 '@radix-ui/react-use-controllable-state@1.0.1(@types/react@17.0.71)(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@17.0.71)(react@17.0.2) react: 17.0.2 optionalDependencies: @@ -32563,7 +32754,7 @@ snapshots: '@radix-ui/react-use-controllable-state@1.0.1(@types/react@17.0.71)(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@17.0.71)(react@18.3.1) react: 18.3.1 optionalDependencies: @@ -32571,25 +32762,19 @@ snapshots: '@radix-ui/react-use-escape-keydown@1.0.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-use-callback-ref': 1.0.0(react@17.0.2) react: 17.0.2 '@radix-ui/react-use-escape-keydown@1.0.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-use-callback-ref': 1.0.0(react@18.3.1) react: 18.3.1 - '@radix-ui/react-use-escape-keydown@1.0.2(react@17.0.2)': - dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-use-callback-ref': 1.0.0(react@17.0.2) - react: 17.0.2 - '@radix-ui/react-use-escape-keydown@1.0.3(@types/react@17.0.71)(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@17.0.71)(react@17.0.2) react: 17.0.2 optionalDependencies: @@ -32597,7 +32782,7 @@ snapshots: '@radix-ui/react-use-escape-keydown@1.0.3(@types/react@17.0.71)(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@17.0.71)(react@18.3.1) react: 18.3.1 optionalDependencies: @@ -32605,51 +32790,45 @@ snapshots: '@radix-ui/react-use-layout-effect@1.0.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 react: 17.0.2 '@radix-ui/react-use-layout-effect@1.0.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 react: 18.3.1 '@radix-ui/react-use-layout-effect@1.0.1(@types/react@17.0.71)(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 react: 17.0.2 optionalDependencies: '@types/react': 17.0.71 '@radix-ui/react-use-layout-effect@1.0.1(@types/react@17.0.71)(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 react: 18.3.1 optionalDependencies: '@types/react': 17.0.71 '@radix-ui/react-use-previous@1.0.1(@types/react@17.0.71)(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 react: 17.0.2 optionalDependencies: '@types/react': 17.0.71 '@radix-ui/react-use-previous@1.0.1(@types/react@17.0.71)(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 react: 18.3.1 optionalDependencies: '@types/react': 17.0.71 - '@radix-ui/react-use-rect@1.0.0(react@17.0.2)': - dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/rect': 1.0.0 - react: 17.0.2 - '@radix-ui/react-use-rect@1.0.1(@types/react@17.0.71)(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/rect': 1.0.1 react: 17.0.2 optionalDependencies: @@ -32657,21 +32836,15 @@ snapshots: '@radix-ui/react-use-rect@1.0.1(@types/react@17.0.71)(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/rect': 1.0.1 react: 18.3.1 optionalDependencies: '@types/react': 17.0.71 - '@radix-ui/react-use-size@1.0.0(react@17.0.2)': - dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-use-layout-effect': 1.0.0(react@17.0.2) - react: 17.0.2 - '@radix-ui/react-use-size@1.0.1(@types/react@17.0.71)(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@17.0.71)(react@17.0.2) react: 17.0.2 optionalDependencies: @@ -32679,25 +32852,15 @@ snapshots: '@radix-ui/react-use-size@1.0.1(@types/react@17.0.71)(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@17.0.71)(react@18.3.1) react: 18.3.1 optionalDependencies: '@types/react': 17.0.71 - '@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': - dependencies: - '@babel/runtime': 7.24.7 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - react: 17.0.2 - react-dom: 18.3.1(react@17.0.2) - optionalDependencies: - '@types/react': 17.0.71 - '@types/react-dom': 18.0.10 - '@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -32705,13 +32868,19 @@ snapshots: '@types/react': 17.0.71 '@types/react-dom': 18.0.10 - '@radix-ui/rect@1.0.0': + '@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + react: 17.0.2 + react-dom: 18.3.1(react@17.0.2) + optionalDependencies: + '@types/react': 17.0.71 + '@types/react-dom': 18.3.0 '@radix-ui/rect@1.0.1': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@react-native-community/cli-clean@12.1.1(encoding@0.1.13)': dependencies: @@ -32728,7 +32897,7 @@ snapshots: cosmiconfig: 5.2.1 deepmerge: 4.3.1 glob: 7.2.3 - joi: 17.13.1 + joi: 17.13.3 transitivePeerDependencies: - encoding @@ -32738,7 +32907,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@react-native-community/cli-debugger-ui@12.3.6': + '@react-native-community/cli-debugger-ui@12.3.7': dependencies: serve-static: 1.15.0 transitivePeerDependencies: @@ -32759,10 +32928,10 @@ snapshots: ip: 1.1.9 node-stream-zip: 1.15.0 ora: 5.4.1 - semver: 7.6.2 + semver: 7.6.3 strip-ansi: 5.2.0 wcwidth: 1.0.1 - yaml: 2.4.3 + yaml: 2.5.0 transitivePeerDependencies: - encoding @@ -32781,7 +32950,7 @@ snapshots: '@react-native-community/cli-tools': 12.1.1(encoding@0.1.13) chalk: 4.1.2 execa: 5.1.1 - fast-xml-parser: 4.2.5 + fast-xml-parser: 4.4.1 glob: 7.2.3 logkitty: 0.7.1 transitivePeerDependencies: @@ -32792,7 +32961,7 @@ snapshots: '@react-native-community/cli-tools': 12.1.1(encoding@0.1.13) chalk: 4.1.2 execa: 5.1.1 - fast-xml-parser: 4.2.5 + fast-xml-parser: 4.4.1 glob: 7.2.3 ora: 5.4.1 transitivePeerDependencies: @@ -32810,24 +32979,24 @@ snapshots: nocache: 3.0.4 pretty-format: 26.6.2 serve-static: 1.15.0 - ws: 7.5.9 + ws: 7.5.10 transitivePeerDependencies: - bufferutil - encoding - supports-color - utf-8-validate - '@react-native-community/cli-server-api@12.3.6(encoding@0.1.13)': + '@react-native-community/cli-server-api@12.3.7(encoding@0.1.13)': dependencies: - '@react-native-community/cli-debugger-ui': 12.3.6 - '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-debugger-ui': 12.3.7 + '@react-native-community/cli-tools': 12.3.7(encoding@0.1.13) compression: 1.7.4 connect: 3.7.0 errorhandler: 1.5.1 nocache: 3.0.4 pretty-format: 26.6.2 serve-static: 1.15.0 - ws: 7.5.9 + ws: 7.5.10 transitivePeerDependencies: - bufferutil - encoding @@ -32843,13 +33012,13 @@ snapshots: node-fetch: 2.7.0(encoding@0.1.13) open: 6.4.0 ora: 5.4.1 - semver: 7.6.2 + semver: 7.6.3 shell-quote: 1.8.1 sudo-prompt: 9.2.1 transitivePeerDependencies: - encoding - '@react-native-community/cli-tools@12.3.6(encoding@0.1.13)': + '@react-native-community/cli-tools@12.3.7(encoding@0.1.13)': dependencies: appdirsjs: 1.2.7 chalk: 4.1.2 @@ -32858,7 +33027,7 @@ snapshots: node-fetch: 2.7.0(encoding@0.1.13) open: 6.4.0 ora: 5.4.1 - semver: 7.6.2 + semver: 7.6.3 shell-quote: 1.8.1 sudo-prompt: 9.2.1 transitivePeerDependencies: @@ -32866,7 +33035,7 @@ snapshots: '@react-native-community/cli-types@12.1.1': dependencies: - joi: 17.13.1 + joi: 17.13.3 '@react-native-community/cli@12.1.1(encoding@0.1.13)': dependencies: @@ -32887,7 +33056,7 @@ snapshots: fs-extra: 8.1.0 graceful-fs: 4.2.11 prompts: 2.4.2 - semver: 7.6.2 + semver: 7.6.3 transitivePeerDependencies: - bufferutil - encoding @@ -32928,29 +33097,29 @@ snapshots: '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.12.9) '@babel/plugin-transform-arrow-functions': 7.24.7(@babel/core@7.12.9) '@babel/plugin-transform-async-to-generator': 7.24.7(@babel/core@7.12.9) - '@babel/plugin-transform-block-scoping': 7.24.7(@babel/core@7.12.9) - '@babel/plugin-transform-classes': 7.24.7(@babel/core@7.12.9) + '@babel/plugin-transform-block-scoping': 7.25.0(@babel/core@7.12.9) + '@babel/plugin-transform-classes': 7.25.0(@babel/core@7.12.9) '@babel/plugin-transform-computed-properties': 7.24.7(@babel/core@7.12.9) - '@babel/plugin-transform-destructuring': 7.24.7(@babel/core@7.12.9) - '@babel/plugin-transform-flow-strip-types': 7.24.7(@babel/core@7.12.9) - '@babel/plugin-transform-function-name': 7.24.7(@babel/core@7.12.9) - '@babel/plugin-transform-literals': 7.24.7(@babel/core@7.12.9) - '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.12.9) + '@babel/plugin-transform-destructuring': 7.24.8(@babel/core@7.12.9) + '@babel/plugin-transform-flow-strip-types': 7.25.2(@babel/core@7.12.9) + '@babel/plugin-transform-function-name': 7.25.1(@babel/core@7.12.9) + '@babel/plugin-transform-literals': 7.25.2(@babel/core@7.12.9) + '@babel/plugin-transform-modules-commonjs': 7.24.8(@babel/core@7.12.9) '@babel/plugin-transform-named-capturing-groups-regex': 7.24.7(@babel/core@7.12.9) '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.12.9) '@babel/plugin-transform-private-methods': 7.24.7(@babel/core@7.12.9) '@babel/plugin-transform-private-property-in-object': 7.24.7(@babel/core@7.12.9) '@babel/plugin-transform-react-display-name': 7.24.7(@babel/core@7.12.9) - '@babel/plugin-transform-react-jsx': 7.24.7(@babel/core@7.12.9) + '@babel/plugin-transform-react-jsx': 7.25.2(@babel/core@7.12.9) '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.12.9) '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.12.9) '@babel/plugin-transform-runtime': 7.24.7(@babel/core@7.12.9) '@babel/plugin-transform-shorthand-properties': 7.24.7(@babel/core@7.12.9) '@babel/plugin-transform-spread': 7.24.7(@babel/core@7.12.9) '@babel/plugin-transform-sticky-regex': 7.24.7(@babel/core@7.12.9) - '@babel/plugin-transform-typescript': 7.24.7(@babel/core@7.12.9) + '@babel/plugin-transform-typescript': 7.25.2(@babel/core@7.12.9) '@babel/plugin-transform-unicode-regex': 7.24.7(@babel/core@7.12.9) - '@babel/template': 7.24.7 + '@babel/template': 7.25.0 '@react-native/babel-plugin-codegen': 0.73.4(@babel/preset-env@7.12.7(@babel/core@7.12.9)) babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.12.9) react-refresh: 0.14.2 @@ -32976,29 +33145,29 @@ snapshots: '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.5) '@babel/plugin-transform-arrow-functions': 7.24.7(@babel/core@7.23.5) '@babel/plugin-transform-async-to-generator': 7.24.7(@babel/core@7.23.5) - '@babel/plugin-transform-block-scoping': 7.24.7(@babel/core@7.23.5) - '@babel/plugin-transform-classes': 7.24.7(@babel/core@7.23.5) + '@babel/plugin-transform-block-scoping': 7.25.0(@babel/core@7.23.5) + '@babel/plugin-transform-classes': 7.25.0(@babel/core@7.23.5) '@babel/plugin-transform-computed-properties': 7.24.7(@babel/core@7.23.5) - '@babel/plugin-transform-destructuring': 7.24.7(@babel/core@7.23.5) - '@babel/plugin-transform-flow-strip-types': 7.24.7(@babel/core@7.23.5) - '@babel/plugin-transform-function-name': 7.24.7(@babel/core@7.23.5) - '@babel/plugin-transform-literals': 7.24.7(@babel/core@7.23.5) - '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.23.5) + '@babel/plugin-transform-destructuring': 7.24.8(@babel/core@7.23.5) + '@babel/plugin-transform-flow-strip-types': 7.25.2(@babel/core@7.23.5) + '@babel/plugin-transform-function-name': 7.25.1(@babel/core@7.23.5) + '@babel/plugin-transform-literals': 7.25.2(@babel/core@7.23.5) + '@babel/plugin-transform-modules-commonjs': 7.24.8(@babel/core@7.23.5) '@babel/plugin-transform-named-capturing-groups-regex': 7.24.7(@babel/core@7.23.5) '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.23.5) '@babel/plugin-transform-private-methods': 7.24.7(@babel/core@7.23.5) '@babel/plugin-transform-private-property-in-object': 7.24.7(@babel/core@7.23.5) '@babel/plugin-transform-react-display-name': 7.24.7(@babel/core@7.23.5) - '@babel/plugin-transform-react-jsx': 7.24.7(@babel/core@7.23.5) + '@babel/plugin-transform-react-jsx': 7.25.2(@babel/core@7.23.5) '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.23.5) '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.23.5) '@babel/plugin-transform-runtime': 7.24.7(@babel/core@7.23.5) '@babel/plugin-transform-shorthand-properties': 7.24.7(@babel/core@7.23.5) '@babel/plugin-transform-spread': 7.24.7(@babel/core@7.23.5) '@babel/plugin-transform-sticky-regex': 7.24.7(@babel/core@7.23.5) - '@babel/plugin-transform-typescript': 7.24.7(@babel/core@7.23.5) + '@babel/plugin-transform-typescript': 7.25.2(@babel/core@7.23.5) '@babel/plugin-transform-unicode-regex': 7.24.7(@babel/core@7.23.5) - '@babel/template': 7.24.7 + '@babel/template': 7.25.0 '@react-native/babel-plugin-codegen': 0.73.4(@babel/preset-env@7.23.6(@babel/core@7.23.5)) babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.23.5) react-refresh: 0.14.2 @@ -33008,7 +33177,7 @@ snapshots: '@react-native/codegen@0.73.3(@babel/preset-env@7.12.7(@babel/core@7.12.9))': dependencies: - '@babel/parser': 7.24.7 + '@babel/parser': 7.25.3 '@babel/preset-env': 7.12.7(@babel/core@7.12.9) flow-parser: 0.206.0 glob: 7.2.3 @@ -33021,7 +33190,7 @@ snapshots: '@react-native/codegen@0.73.3(@babel/preset-env@7.23.6(@babel/core@7.23.5))': dependencies: - '@babel/parser': 7.24.7 + '@babel/parser': 7.25.3 '@babel/preset-env': 7.23.6(@babel/core@7.23.5) flow-parser: 0.206.0 glob: 7.2.3 @@ -33032,17 +33201,17 @@ snapshots: transitivePeerDependencies: - supports-color - '@react-native/community-cli-plugin@0.73.17(@babel/core@7.12.9)(@babel/preset-env@7.12.7(@babel/core@7.12.9))(encoding@0.1.13)': + '@react-native/community-cli-plugin@0.73.18(@babel/core@7.12.9)(@babel/preset-env@7.12.7(@babel/core@7.12.9))(encoding@0.1.13)': dependencies: - '@react-native-community/cli-server-api': 12.3.6(encoding@0.1.13) - '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-server-api': 12.3.7(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.7(encoding@0.1.13) '@react-native/dev-middleware': 0.73.8(encoding@0.1.13) '@react-native/metro-babel-transformer': 0.73.15(@babel/core@7.12.9)(@babel/preset-env@7.12.7(@babel/core@7.12.9)) chalk: 4.1.2 execa: 5.1.1 - metro: 0.80.9(encoding@0.1.13) - metro-config: 0.80.9(encoding@0.1.13) - metro-core: 0.80.9 + metro: 0.80.10(encoding@0.1.13) + metro-config: 0.80.10(encoding@0.1.13) + metro-core: 0.80.10 node-fetch: 2.7.0(encoding@0.1.13) readline: 1.3.0 transitivePeerDependencies: @@ -33053,17 +33222,17 @@ snapshots: - supports-color - utf-8-validate - '@react-native/community-cli-plugin@0.73.17(@babel/core@7.23.5)(@babel/preset-env@7.23.6(@babel/core@7.23.5))(encoding@0.1.13)': + '@react-native/community-cli-plugin@0.73.18(@babel/core@7.23.5)(@babel/preset-env@7.23.6(@babel/core@7.23.5))(encoding@0.1.13)': dependencies: - '@react-native-community/cli-server-api': 12.3.6(encoding@0.1.13) - '@react-native-community/cli-tools': 12.3.6(encoding@0.1.13) + '@react-native-community/cli-server-api': 12.3.7(encoding@0.1.13) + '@react-native-community/cli-tools': 12.3.7(encoding@0.1.13) '@react-native/dev-middleware': 0.73.8(encoding@0.1.13) '@react-native/metro-babel-transformer': 0.73.15(@babel/core@7.23.5)(@babel/preset-env@7.23.6(@babel/core@7.23.5)) chalk: 4.1.2 execa: 5.1.1 - metro: 0.80.9(encoding@0.1.13) - metro-config: 0.80.9(encoding@0.1.13) - metro-core: 0.80.9 + metro: 0.80.10(encoding@0.1.13) + metro-config: 0.80.10(encoding@0.1.13) + metro-core: 0.80.10 node-fetch: 2.7.0(encoding@0.1.13) readline: 1.3.0 transitivePeerDependencies: @@ -33088,7 +33257,7 @@ snapshots: open: 7.4.2 serve-static: 1.15.0 temp-dir: 2.0.0 - ws: 6.2.2 + ws: 6.2.3 transitivePeerDependencies: - bufferutil - encoding @@ -33344,10 +33513,10 @@ snapshots: transitivePeerDependencies: - debug - '@statelyai/inspect@0.3.1(ws@8.17.0)(xstate@4.37.1)': + '@statelyai/inspect@0.3.1(ws@8.18.0)(xstate@4.37.1)': dependencies: fast-safe-stringify: 2.1.1 - isomorphic-ws: 5.0.0(ws@8.17.0) + isomorphic-ws: 5.0.0(ws@8.18.0) partysocket: 0.0.25 safe-stable-stringify: 2.4.3 superjson: 1.13.3 @@ -33356,16 +33525,16 @@ snapshots: transitivePeerDependencies: - ws - '@storybook/addon-a11y@6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@storybook/addon-a11y@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@storybook/addons': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/api': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/addons': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/api': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/channels': 6.5.17-alpha.0 '@storybook/client-logger': 6.5.17-alpha.0 - '@storybook/components': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/components': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/core-events': 6.5.17-alpha.0 '@storybook/csf': 0.0.2--canary.4566f4d.1 - '@storybook/theming': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/theming': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) axe-core: 4.8.2 core-js: 3.34.0 global: 4.4.0 @@ -33375,8 +33544,8 @@ snapshots: ts-dedent: 2.2.0 util-deprecate: 1.0.2 optionalDependencies: - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) '@storybook/addon-a11y@7.5.2(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: @@ -33425,6 +33594,31 @@ snapshots: react: 17.0.2 react-dom: 17.0.2(react@17.0.2) + '@storybook/addon-actions@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@storybook/addons': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/api': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/client-logger': 6.5.17-alpha.0 + '@storybook/components': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/core-events': 6.5.17-alpha.0 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/theming': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + core-js: 3.34.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.17.21 + polished: 4.2.2 + prop-types: 15.8.1 + react-inspector: 5.1.1(react@18.3.1) + regenerator-runtime: 0.13.11 + telejson: 6.0.8 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + uuid-browser: 3.1.0 + optionalDependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + '@storybook/addon-actions@7.5.2(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@storybook/client-logger': 7.5.2 @@ -33483,30 +33677,10 @@ snapshots: '@storybook/addon-actions': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) global: 4.4.0 - '@storybook/addon-controls@6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)': + '@storybook/addon-console@1.2.3(@storybook/addon-actions@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': dependencies: - '@storybook/addons': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/api': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/client-logger': 6.5.17-alpha.0 - '@storybook/components': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/core-common': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3) - '@storybook/csf': 0.0.2--canary.4566f4d.1 - '@storybook/node-logger': 6.5.17-alpha.0 - '@storybook/store': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/theming': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - core-js: 3.34.0 - lodash: 4.17.21 - ts-dedent: 2.2.0 - optionalDependencies: - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - transitivePeerDependencies: - - eslint - - supports-color - - typescript - - vue-template-compiler - - webpack-cli - - webpack-command + '@storybook/addon-actions': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + global: 4.4.0 '@storybook/addon-controls@6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)(webpack-cli@3.3.12(webpack@5.89.0))': dependencies: @@ -33533,6 +33707,31 @@ snapshots: - webpack-cli - webpack-command + '@storybook/addon-controls@6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3)': + dependencies: + '@storybook/addons': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/api': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/client-logger': 6.5.17-alpha.0 + '@storybook/components': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/core-common': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3) + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/node-logger': 6.5.17-alpha.0 + '@storybook/store': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/theming': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + core-js: 3.34.0 + lodash: 4.17.21 + ts-dedent: 2.2.0 + optionalDependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - eslint + - supports-color + - typescript + - vue-template-compiler + - webpack-cli + - webpack-command + '@storybook/addon-controls@7.5.2(@types/react-dom@18.0.10)(@types/react@17.0.71)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@storybook/blocks': 7.5.2(@types/react-dom@18.0.10)(@types/react@17.0.71)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -33599,26 +33798,26 @@ snapshots: - webpack-cli - webpack-command - '@storybook/addon-docs@6.5.17-alpha.0(@babel/core@7.24.7)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)(webpack@5.89.0)': + '@storybook/addon-docs@6.5.17-alpha.0(@babel/core@7.24.7)(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3)(webpack@5.89.0)': dependencies: '@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.24.7) '@babel/preset-env': 7.23.5(@babel/core@7.24.7) '@jest/transform': 26.6.2 - '@mdx-js/react': 1.6.22(react@17.0.2) - '@storybook/addons': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/api': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/components': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/core-common': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3) + '@mdx-js/react': 1.6.22(react@18.3.1) + '@storybook/addons': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/api': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/components': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/core-common': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3) '@storybook/core-events': 6.5.17-alpha.0 '@storybook/csf': 0.0.2--canary.4566f4d.1 - '@storybook/docs-tools': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/docs-tools': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/mdx1-csf': 0.0.1(@babel/core@7.24.7) '@storybook/node-logger': 6.5.17-alpha.0 '@storybook/postinstall': 6.5.17-alpha.0 - '@storybook/preview-web': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/source-loader': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/store': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/theming': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/preview-web': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/source-loader': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/store': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/theming': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) babel-loader: 8.3.0(@babel/core@7.24.7)(webpack@5.89.0) core-js: 3.34.0 fast-deep-equal: 3.1.3 @@ -33630,8 +33829,8 @@ snapshots: ts-dedent: 2.2.0 util-deprecate: 1.0.2 optionalDependencies: - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) transitivePeerDependencies: - '@babel/core' - eslint @@ -33754,11 +33953,35 @@ snapshots: transitivePeerDependencies: - supports-color - '@storybook/addon-knobs@7.0.2(@storybook/addons@7.5.2(react-dom@18.3.1(react@17.0.2))(react@17.0.2))(@storybook/api@7.6.4(react-dom@18.3.1(react@17.0.2))(react@17.0.2))(@storybook/components@7.6.4(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2))(@storybook/core-events@7.6.4)(@storybook/theming@7.6.4(react-dom@18.3.1(react@17.0.2))(react@17.0.2))(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': + '@storybook/addon-knobs@6.4.0(@storybook/addons@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/api@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/components@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@storybook/core-events@6.5.17-alpha.0)(@storybook/theming@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@storybook/addons': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/api': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/components': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/core-events': 6.5.17-alpha.0 + '@storybook/theming': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + copy-to-clipboard: 3.3.3 + core-js: 3.34.0 + escape-html: 1.0.3 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.17.21 + prop-types: 15.8.1 + qs: 6.11.2 + react-colorful: 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-lifecycles-compat: 3.0.4 + react-select: 3.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + optionalDependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - supports-color + + '@storybook/addon-knobs@7.0.2(@storybook/addons@7.5.2(react-dom@18.3.1(react@17.0.2))(react@17.0.2))(@storybook/api@7.6.4(react-dom@18.3.1(react@17.0.2))(react@17.0.2))(@storybook/components@7.6.4(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2))(@storybook/core-events@7.6.4)(@storybook/theming@7.6.4(react-dom@18.3.1(react@17.0.2))(react@17.0.2))(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': dependencies: '@storybook/addons': 7.5.2(react-dom@18.3.1(react@17.0.2))(react@17.0.2) '@storybook/api': 7.6.4(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - '@storybook/components': 7.6.4(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@storybook/components': 7.6.4(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) '@storybook/core-events': 7.6.4 '@storybook/theming': 7.6.4(react-dom@18.3.1(react@17.0.2))(react@17.0.2) copy-to-clipboard: 3.3.3 @@ -33797,6 +34020,24 @@ snapshots: react: 17.0.2 react-dom: 17.0.2(react@17.0.2) + '@storybook/addon-links@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@storybook/addons': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/client-logger': 6.5.17-alpha.0 + '@storybook/core-events': 6.5.17-alpha.0 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/router': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@types/qs': 6.9.10 + core-js: 3.34.0 + global: 4.4.0 + prop-types: 15.8.1 + qs: 6.11.2 + regenerator-runtime: 0.13.11 + ts-dedent: 2.2.0 + optionalDependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + '@storybook/addon-links@7.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@storybook/client-logger': 7.5.2 @@ -33847,24 +34088,24 @@ snapshots: - '@types/react' - '@types/react-dom' - '@storybook/addon-storysource@6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@storybook/addon-storysource@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@storybook/addons': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/api': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/addons': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/api': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/client-logger': 6.5.17-alpha.0 - '@storybook/components': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/router': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/source-loader': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/theming': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/components': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/router': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/source-loader': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/theming': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) core-js: 3.34.0 estraverse: 5.3.0 loader-utils: 2.0.4 prop-types: 15.8.1 - react-syntax-highlighter: 15.5.0(react@17.0.2) + react-syntax-highlighter: 15.5.0(react@18.3.1) regenerator-runtime: 0.13.11 optionalDependencies: - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) '@storybook/addon-storysource@7.5.2(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: @@ -33905,22 +34146,22 @@ snapshots: - '@types/react' - '@types/react-dom' - '@storybook/addon-viewport@6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@storybook/addon-viewport@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@storybook/addons': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/api': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/addons': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/api': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/client-logger': 6.5.17-alpha.0 - '@storybook/components': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/components': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/core-events': 6.5.17-alpha.0 - '@storybook/theming': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/theming': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) core-js: 3.34.0 global: 4.4.0 memoizerific: 1.11.3 prop-types: 15.8.1 regenerator-runtime: 0.13.11 optionalDependencies: - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) '@storybook/addon-viewport@7.5.2(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: @@ -33956,6 +34197,22 @@ snapshots: react-dom: 17.0.2(react@17.0.2) regenerator-runtime: 0.13.11 + '@storybook/addons@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@storybook/api': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/channels': 6.5.17-alpha.0 + '@storybook/client-logger': 6.5.17-alpha.0 + '@storybook/core-events': 6.5.17-alpha.0 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/router': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/theming': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@types/webpack-env': 1.18.4 + core-js: 3.34.0 + global: 4.4.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + regenerator-runtime: 0.13.11 + '@storybook/addons@7.5.2(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': dependencies: '@storybook/manager-api': 7.5.2(react-dom@18.3.1(react@17.0.2))(react@17.0.2) @@ -33994,6 +34251,28 @@ snapshots: ts-dedent: 2.2.0 util-deprecate: 1.0.2 + '@storybook/api@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@storybook/channels': 6.5.17-alpha.0 + '@storybook/client-logger': 6.5.17-alpha.0 + '@storybook/core-events': 6.5.17-alpha.0 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/router': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/semver': 7.3.2 + '@storybook/theming': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + core-js: 3.34.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.17.21 + memoizerific: 1.11.3 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + regenerator-runtime: 0.13.11 + store2: 2.14.2 + telejson: 6.0.8 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + '@storybook/api@7.6.4(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': dependencies: '@storybook/client-logger': 7.6.4 @@ -34090,67 +34369,6 @@ snapshots: - encoding - supports-color - '@storybook/builder-webpack4@6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)': - dependencies: - '@babel/core': 7.24.7 - '@storybook/addons': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/api': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/channel-postmessage': 6.5.17-alpha.0 - '@storybook/channels': 6.5.17-alpha.0 - '@storybook/client-api': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/client-logger': 6.5.17-alpha.0 - '@storybook/components': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/core-common': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3) - '@storybook/core-events': 6.5.17-alpha.0 - '@storybook/node-logger': 6.5.17-alpha.0 - '@storybook/preview-web': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/router': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/semver': 7.3.2 - '@storybook/store': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/theming': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/ui': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@types/node': 16.18.68 - '@types/webpack': 4.41.38 - autoprefixer: 9.8.6 - babel-loader: 8.3.0(@babel/core@7.24.7)(webpack@4.47.0) - case-sensitive-paths-webpack-plugin: 2.4.0 - core-js: 3.34.0 - css-loader: 3.6.0(webpack@4.47.0) - file-loader: 6.2.0(webpack@4.47.0) - find-up: 5.0.0 - fork-ts-checker-webpack-plugin: 4.1.6(eslint@8.55.0)(typescript@5.3.3)(webpack@4.47.0) - glob: 7.2.3 - glob-promise: 3.4.0(glob@7.2.3) - global: 4.4.0 - html-webpack-plugin: 4.5.2(webpack@4.47.0) - pnp-webpack-plugin: 1.6.4(typescript@5.3.3) - postcss: 7.0.39 - postcss-flexbugs-fixes: 4.2.1 - postcss-loader: 4.3.0(postcss@7.0.39)(webpack@4.47.0) - raw-loader: 4.0.2(webpack@4.47.0) - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - stable: 0.1.8 - style-loader: 1.3.0(webpack@4.47.0) - terser-webpack-plugin: 4.2.3(webpack@4.47.0) - ts-dedent: 2.2.0 - url-loader: 4.1.1(file-loader@6.2.0(webpack@5.89.0))(webpack@4.47.0) - util-deprecate: 1.0.2 - webpack: 4.47.0 - webpack-dev-middleware: 3.7.3(webpack@4.47.0) - webpack-filter-warnings-plugin: 1.2.1(webpack@4.47.0) - webpack-hot-middleware: 2.25.4 - webpack-virtual-modules: 0.2.2 - optionalDependencies: - typescript: 5.3.3 - transitivePeerDependencies: - - bluebird - - eslint - - supports-color - - vue-template-compiler - - webpack-cli - - webpack-command - '@storybook/builder-webpack4@6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)(webpack-cli@3.3.12(webpack@5.89.0))': dependencies: '@babel/core': 7.24.7 @@ -34195,7 +34413,7 @@ snapshots: style-loader: 1.3.0(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))) terser-webpack-plugin: 4.2.3(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))) ts-dedent: 2.2.0 - url-loader: 4.1.1(file-loader@6.2.0(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))))(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))) + url-loader: 4.1.1(file-loader@6.2.0(webpack@5.89.0(webpack-cli@3.3.12)))(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))) util-deprecate: 1.0.2 webpack: 4.47.0(webpack-cli@3.3.12(webpack@5.89.0)) webpack-dev-middleware: 3.7.3(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))) @@ -34212,24 +34430,85 @@ snapshots: - webpack-cli - webpack-command - '@storybook/builder-webpack5@6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)': + '@storybook/builder-webpack4@6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3)': dependencies: - '@babel/core': 7.23.5 - '@storybook/addons': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/api': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@babel/core': 7.24.7 + '@storybook/addons': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/api': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/channel-postmessage': 6.5.17-alpha.0 '@storybook/channels': 6.5.17-alpha.0 - '@storybook/client-api': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/client-api': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/client-logger': 6.5.17-alpha.0 - '@storybook/components': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/core-common': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3) + '@storybook/components': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/core-common': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3) '@storybook/core-events': 6.5.17-alpha.0 '@storybook/node-logger': 6.5.17-alpha.0 - '@storybook/preview-web': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/router': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/preview-web': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/router': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/semver': 7.3.2 - '@storybook/store': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/theming': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/store': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/theming': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/ui': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@types/node': 16.18.68 + '@types/webpack': 4.41.38 + autoprefixer: 9.8.6 + babel-loader: 8.3.0(@babel/core@7.24.7)(webpack@4.47.0) + case-sensitive-paths-webpack-plugin: 2.4.0 + core-js: 3.34.0 + css-loader: 3.6.0(webpack@4.47.0) + file-loader: 6.2.0(webpack@4.47.0) + find-up: 5.0.0 + fork-ts-checker-webpack-plugin: 4.1.6(eslint@8.55.0)(typescript@5.3.3)(webpack@4.47.0) + glob: 7.2.3 + glob-promise: 3.4.0(glob@7.2.3) + global: 4.4.0 + html-webpack-plugin: 4.5.2(webpack@4.47.0) + pnp-webpack-plugin: 1.6.4(typescript@5.3.3) + postcss: 7.0.39 + postcss-flexbugs-fixes: 4.2.1 + postcss-loader: 4.3.0(postcss@7.0.39)(webpack@4.47.0) + raw-loader: 4.0.2(webpack@4.47.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + stable: 0.1.8 + style-loader: 1.3.0(webpack@4.47.0) + terser-webpack-plugin: 4.2.3(webpack@4.47.0) + ts-dedent: 2.2.0 + url-loader: 4.1.1(file-loader@6.2.0(webpack@4.47.0))(webpack@4.47.0) + util-deprecate: 1.0.2 + webpack: 4.47.0 + webpack-dev-middleware: 3.7.3(webpack@4.47.0) + webpack-filter-warnings-plugin: 1.2.1(webpack@4.47.0) + webpack-hot-middleware: 2.25.4 + webpack-virtual-modules: 0.2.2 + optionalDependencies: + typescript: 5.3.3 + transitivePeerDependencies: + - bluebird + - eslint + - supports-color + - vue-template-compiler + - webpack-cli + - webpack-command + + '@storybook/builder-webpack5@6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3)': + dependencies: + '@babel/core': 7.23.5 + '@storybook/addons': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/api': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/channel-postmessage': 6.5.17-alpha.0 + '@storybook/channels': 6.5.17-alpha.0 + '@storybook/client-api': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/client-logger': 6.5.17-alpha.0 + '@storybook/components': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/core-common': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3) + '@storybook/core-events': 6.5.17-alpha.0 + '@storybook/node-logger': 6.5.17-alpha.0 + '@storybook/preview-web': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/router': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/semver': 7.3.2 + '@storybook/store': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/theming': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/node': 16.18.68 babel-loader: 8.3.0(@babel/core@7.23.5)(webpack@5.91.0) babel-plugin-named-exports-order: 0.0.2 @@ -34243,8 +34522,8 @@ snapshots: html-webpack-plugin: 5.5.4(webpack@5.91.0) path-browserify: 1.0.1 process: 0.11.10 - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) stable: 0.1.8 style-loader: 2.0.0(webpack@5.91.0) terser-webpack-plugin: 5.3.6(webpack@5.91.0) @@ -34431,6 +34710,31 @@ snapshots: ts-dedent: 2.2.0 util-deprecate: 1.0.2 + '@storybook/client-api@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@storybook/addons': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/channel-postmessage': 6.5.17-alpha.0 + '@storybook/channels': 6.5.17-alpha.0 + '@storybook/client-logger': 6.5.17-alpha.0 + '@storybook/core-events': 6.5.17-alpha.0 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/store': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@types/qs': 6.9.10 + '@types/webpack-env': 1.18.4 + core-js: 3.34.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.17.21 + memoizerific: 1.11.3 + qs: 6.11.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + regenerator-runtime: 0.13.11 + store2: 2.14.2 + synchronous-promise: 2.0.17 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + '@storybook/client-api@7.5.2': dependencies: '@storybook/client-logger': 7.5.2 @@ -34481,6 +34785,19 @@ snapshots: regenerator-runtime: 0.13.11 util-deprecate: 1.0.2 + '@storybook/components@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@storybook/client-logger': 6.5.17-alpha.0 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/theming': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + core-js: 3.34.0 + memoizerific: 1.11.3 + qs: 6.11.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + regenerator-runtime: 0.13.11 + util-deprecate: 1.0.2 + '@storybook/components@7.5.2(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/react-select': 1.2.2(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -34499,24 +34816,6 @@ snapshots: - '@types/react' - '@types/react-dom' - '@storybook/components@7.6.4(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': - dependencies: - '@radix-ui/react-select': 1.2.2(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - '@radix-ui/react-toolbar': 1.0.4(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - '@storybook/client-logger': 7.6.4 - '@storybook/csf': 0.1.2 - '@storybook/global': 5.0.0 - '@storybook/theming': 7.6.4(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - '@storybook/types': 7.6.4 - memoizerific: 1.11.3 - react: 17.0.2 - react-dom: 18.3.1(react@17.0.2) - use-resize-observer: 9.1.0(react-dom@18.3.1(react@17.0.2))(react@17.0.2) - util-deprecate: 1.0.2 - transitivePeerDependencies: - - '@types/react' - - '@types/react-dom' - '@storybook/components@7.6.4(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/react-select': 1.2.2(@types/react-dom@18.0.10)(@types/react@17.0.71)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -34535,6 +34834,24 @@ snapshots: - '@types/react' - '@types/react-dom' + '@storybook/components@7.6.4(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': + dependencies: + '@radix-ui/react-select': 1.2.2(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@radix-ui/react-toolbar': 1.0.4(@types/react-dom@18.3.0)(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@storybook/client-logger': 7.6.4 + '@storybook/csf': 0.1.2 + '@storybook/global': 5.0.0 + '@storybook/theming': 7.6.4(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + '@storybook/types': 7.6.4 + memoizerific: 1.11.3 + react: 17.0.2 + react-dom: 18.3.1(react@17.0.2) + use-resize-observer: 9.1.0(react-dom@18.3.1(react@17.0.2))(react@17.0.2) + util-deprecate: 1.0.2 + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + '@storybook/core-client@6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0)))': dependencies: '@storybook/addons': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) @@ -34563,34 +34880,6 @@ snapshots: optionalDependencies: typescript: 5.3.3 - '@storybook/core-client@6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)(webpack@4.47.0)': - dependencies: - '@storybook/addons': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/channel-postmessage': 6.5.17-alpha.0 - '@storybook/channel-websocket': 6.5.17-alpha.0 - '@storybook/client-api': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/client-logger': 6.5.17-alpha.0 - '@storybook/core-events': 6.5.17-alpha.0 - '@storybook/csf': 0.0.2--canary.4566f4d.1 - '@storybook/preview-web': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/store': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/ui': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - airbnb-js-shims: 2.2.1 - ansi-to-html: 0.6.15 - core-js: 3.34.0 - global: 4.4.0 - lodash: 4.17.21 - qs: 6.11.2 - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - regenerator-runtime: 0.13.11 - ts-dedent: 2.2.0 - unfetch: 4.2.0 - util-deprecate: 1.0.2 - webpack: 4.47.0 - optionalDependencies: - typescript: 5.3.3 - '@storybook/core-client@6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)(webpack@5.91.0(webpack-cli@3.3.12(webpack@5.89.0)))': dependencies: '@storybook/addons': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) @@ -34619,26 +34908,54 @@ snapshots: optionalDependencies: typescript: 5.3.3 - '@storybook/core-client@6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)(webpack@5.91.0)': + '@storybook/core-client@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3)(webpack@4.47.0)': dependencies: - '@storybook/addons': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/addons': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/channel-postmessage': 6.5.17-alpha.0 '@storybook/channel-websocket': 6.5.17-alpha.0 - '@storybook/client-api': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/client-api': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/client-logger': 6.5.17-alpha.0 '@storybook/core-events': 6.5.17-alpha.0 '@storybook/csf': 0.0.2--canary.4566f4d.1 - '@storybook/preview-web': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/store': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/ui': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/preview-web': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/store': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/ui': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) airbnb-js-shims: 2.2.1 ansi-to-html: 0.6.15 core-js: 3.34.0 global: 4.4.0 lodash: 4.17.21 qs: 6.11.2 - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + regenerator-runtime: 0.13.11 + ts-dedent: 2.2.0 + unfetch: 4.2.0 + util-deprecate: 1.0.2 + webpack: 4.47.0 + optionalDependencies: + typescript: 5.3.3 + + '@storybook/core-client@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3)(webpack@5.91.0)': + dependencies: + '@storybook/addons': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/channel-postmessage': 6.5.17-alpha.0 + '@storybook/channel-websocket': 6.5.17-alpha.0 + '@storybook/client-api': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/client-logger': 6.5.17-alpha.0 + '@storybook/core-events': 6.5.17-alpha.0 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/preview-web': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/store': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/ui': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + airbnb-js-shims: 2.2.1 + ansi-to-html: 0.6.15 + core-js: 3.34.0 + global: 4.4.0 + lodash: 4.17.21 + qs: 6.11.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) regenerator-runtime: 0.13.11 ts-dedent: 2.2.0 unfetch: 4.2.0 @@ -34657,69 +34974,6 @@ snapshots: '@storybook/client-logger': 7.6.4 '@storybook/preview-api': 7.6.4 - '@storybook/core-common@6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)': - dependencies: - '@babel/core': 7.24.7 - '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.24.7) - '@babel/plugin-proposal-decorators': 7.23.5(@babel/core@7.24.7) - '@babel/plugin-proposal-export-default-from': 7.23.3(@babel/core@7.24.7) - '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.24.7) - '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.24.7) - '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.24.7) - '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.24.7) - '@babel/plugin-proposal-private-property-in-object': 7.21.11(@babel/core@7.24.7) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.7) - '@babel/plugin-transform-arrow-functions': 7.23.3(@babel/core@7.24.7) - '@babel/plugin-transform-block-scoping': 7.23.4(@babel/core@7.24.7) - '@babel/plugin-transform-classes': 7.23.5(@babel/core@7.24.7) - '@babel/plugin-transform-destructuring': 7.23.3(@babel/core@7.24.7) - '@babel/plugin-transform-for-of': 7.23.3(@babel/core@7.24.7) - '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.24.7) - '@babel/plugin-transform-shorthand-properties': 7.23.3(@babel/core@7.24.7) - '@babel/plugin-transform-spread': 7.23.3(@babel/core@7.24.7) - '@babel/preset-env': 7.23.5(@babel/core@7.24.7) - '@babel/preset-react': 7.23.3(@babel/core@7.24.7) - '@babel/preset-typescript': 7.23.3(@babel/core@7.24.7) - '@babel/register': 7.12.1(@babel/core@7.24.7) - '@storybook/node-logger': 6.5.17-alpha.0 - '@storybook/semver': 7.3.2 - '@types/node': 16.18.68 - '@types/pretty-hrtime': 1.0.3 - babel-loader: 8.3.0(@babel/core@7.24.7)(webpack@4.47.0) - babel-plugin-macros: 3.1.0 - babel-plugin-polyfill-corejs3: 0.1.7(@babel/core@7.24.7) - chalk: 4.1.2 - core-js: 3.34.0 - express: 4.18.2 - file-system-cache: 1.1.0 - find-up: 5.0.0 - fork-ts-checker-webpack-plugin: 6.5.3(eslint@8.55.0)(typescript@5.3.3)(webpack@4.47.0) - fs-extra: 9.1.0 - glob: 7.2.3 - handlebars: 4.7.8 - interpret: 2.2.0 - json5: 2.2.3 - lazy-universal-dotenv: 3.0.1 - picomatch: 2.3.1 - pkg-dir: 5.0.0 - pretty-hrtime: 1.0.3 - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - resolve-from: 5.0.0 - slash: 3.0.0 - telejson: 6.0.8 - ts-dedent: 2.2.0 - util-deprecate: 1.0.2 - webpack: 4.47.0 - optionalDependencies: - typescript: 5.3.3 - transitivePeerDependencies: - - eslint - - supports-color - - vue-template-compiler - - webpack-cli - - webpack-command - '@storybook/core-common@6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)(webpack-cli@3.3.12(webpack@5.89.0))': dependencies: '@babel/core': 7.24.7 @@ -34783,6 +35037,69 @@ snapshots: - webpack-cli - webpack-command + '@storybook/core-common@6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3)': + dependencies: + '@babel/core': 7.24.7 + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.24.7) + '@babel/plugin-proposal-decorators': 7.23.5(@babel/core@7.24.7) + '@babel/plugin-proposal-export-default-from': 7.23.3(@babel/core@7.24.7) + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.24.7) + '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.24.7) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.24.7) + '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.24.7) + '@babel/plugin-proposal-private-property-in-object': 7.21.11(@babel/core@7.24.7) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-transform-arrow-functions': 7.23.3(@babel/core@7.24.7) + '@babel/plugin-transform-block-scoping': 7.23.4(@babel/core@7.24.7) + '@babel/plugin-transform-classes': 7.23.5(@babel/core@7.24.7) + '@babel/plugin-transform-destructuring': 7.23.3(@babel/core@7.24.7) + '@babel/plugin-transform-for-of': 7.23.3(@babel/core@7.24.7) + '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.24.7) + '@babel/plugin-transform-shorthand-properties': 7.23.3(@babel/core@7.24.7) + '@babel/plugin-transform-spread': 7.23.3(@babel/core@7.24.7) + '@babel/preset-env': 7.23.5(@babel/core@7.24.7) + '@babel/preset-react': 7.23.3(@babel/core@7.24.7) + '@babel/preset-typescript': 7.23.3(@babel/core@7.24.7) + '@babel/register': 7.12.1(@babel/core@7.24.7) + '@storybook/node-logger': 6.5.17-alpha.0 + '@storybook/semver': 7.3.2 + '@types/node': 16.18.68 + '@types/pretty-hrtime': 1.0.3 + babel-loader: 8.3.0(@babel/core@7.24.7)(webpack@4.47.0) + babel-plugin-macros: 3.1.0 + babel-plugin-polyfill-corejs3: 0.1.7(@babel/core@7.24.7) + chalk: 4.1.2 + core-js: 3.34.0 + express: 4.18.2 + file-system-cache: 1.1.0 + find-up: 5.0.0 + fork-ts-checker-webpack-plugin: 6.5.3(eslint@8.55.0)(typescript@5.3.3)(webpack@4.47.0) + fs-extra: 9.1.0 + glob: 7.2.3 + handlebars: 4.7.8 + interpret: 2.2.0 + json5: 2.2.3 + lazy-universal-dotenv: 3.0.1 + picomatch: 2.3.1 + pkg-dir: 5.0.0 + pretty-hrtime: 1.0.3 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + resolve-from: 5.0.0 + slash: 3.0.0 + telejson: 6.0.8 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + webpack: 4.47.0 + optionalDependencies: + typescript: 5.3.3 + transitivePeerDependencies: + - eslint + - supports-color + - vue-template-compiler + - webpack-cli + - webpack-command + '@storybook/core-common@7.5.2(encoding@0.1.13)': dependencies: '@storybook/core-events': 7.5.2 @@ -34853,20 +35170,20 @@ snapshots: dependencies: ts-dedent: 2.2.0 - '@storybook/core-server@6.5.17-alpha.0(@storybook/builder-webpack5@6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3))(@storybook/manager-webpack5@6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3))(encoding@0.1.13)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)': + '@storybook/core-server@6.5.17-alpha.0(@storybook/builder-webpack5@6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3))(@storybook/manager-webpack5@6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3))(encoding@0.1.13)(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3)': dependencies: '@discoveryjs/json-ext': 0.5.7 - '@storybook/builder-webpack4': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3) - '@storybook/core-client': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)(webpack@4.47.0) - '@storybook/core-common': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3) + '@storybook/builder-webpack4': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3) + '@storybook/core-client': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3)(webpack@4.47.0) + '@storybook/core-common': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3) '@storybook/core-events': 6.5.17-alpha.0 '@storybook/csf': 0.0.2--canary.4566f4d.1 '@storybook/csf-tools': 6.5.17-alpha.0 - '@storybook/manager-webpack4': 6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3) + '@storybook/manager-webpack4': 6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3) '@storybook/node-logger': 6.5.17-alpha.0 '@storybook/semver': 7.3.2 - '@storybook/store': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/telemetry': 6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3) + '@storybook/store': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/telemetry': 6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3) '@types/node': 16.18.68 '@types/node-fetch': 2.6.9 '@types/pretty-hrtime': 1.0.3 @@ -34890,8 +35207,8 @@ snapshots: open: 8.4.2 pretty-hrtime: 1.0.3 prompts: 2.4.2 - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) regenerator-runtime: 0.13.11 serve-favicon: 2.5.0 slash: 3.0.0 @@ -34900,11 +35217,11 @@ snapshots: util-deprecate: 1.0.2 watchpack: 2.4.0 webpack: 4.47.0 - ws: 8.17.0 + ws: 8.18.0 x-default-browser: 0.4.0 optionalDependencies: - '@storybook/builder-webpack5': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3) - '@storybook/manager-webpack5': 6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3) + '@storybook/builder-webpack5': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3) + '@storybook/manager-webpack5': 6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3) typescript: 5.3.3 transitivePeerDependencies: - '@storybook/mdx2-csf' @@ -34965,7 +35282,7 @@ snapshots: util-deprecate: 1.0.2 watchpack: 2.4.0 webpack: 4.47.0(webpack-cli@3.3.12(webpack@5.89.0)) - ws: 8.17.0 + ws: 8.18.0 x-default-browser: 0.4.0 optionalDependencies: typescript: 5.3.3 @@ -35023,7 +35340,7 @@ snapshots: util: 0.12.5 util-deprecate: 1.0.2 watchpack: 2.4.0 - ws: 8.17.0 + ws: 8.18.0 transitivePeerDependencies: - bufferutil - encoding @@ -35041,16 +35358,16 @@ snapshots: - encoding - supports-color - '@storybook/core@6.5.17-alpha.0(@storybook/builder-webpack5@6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3))(@storybook/manager-webpack5@6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3))(encoding@0.1.13)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)(webpack@5.91.0)': + '@storybook/core@6.5.17-alpha.0(@storybook/builder-webpack5@6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3))(@storybook/manager-webpack5@6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3))(encoding@0.1.13)(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3)(webpack@5.91.0)': dependencies: - '@storybook/core-client': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)(webpack@5.91.0) - '@storybook/core-server': 6.5.17-alpha.0(@storybook/builder-webpack5@6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3))(@storybook/manager-webpack5@6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3))(encoding@0.1.13)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3) - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) + '@storybook/core-client': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3)(webpack@5.91.0) + '@storybook/core-server': 6.5.17-alpha.0(@storybook/builder-webpack5@6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3))(@storybook/manager-webpack5@6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3))(encoding@0.1.13)(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) webpack: 5.91.0 optionalDependencies: - '@storybook/builder-webpack5': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3) - '@storybook/manager-webpack5': 6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3) + '@storybook/builder-webpack5': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3) + '@storybook/manager-webpack5': 6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3) typescript: 5.3.3 transitivePeerDependencies: - '@storybook/mdx2-csf' @@ -35174,6 +35491,20 @@ snapshots: - react-dom - supports-color + '@storybook/docs-tools@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/core': 7.24.7 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/store': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + core-js: 3.34.0 + doctrine: 3.0.0 + lodash: 4.17.21 + regenerator-runtime: 0.13.11 + transitivePeerDependencies: + - react + - react-dom + - supports-color + '@storybook/docs-tools@7.5.2(encoding@0.1.13)': dependencies: '@storybook/core-common': 7.5.2(encoding@0.1.13) @@ -35283,56 +35614,6 @@ snapshots: - react - react-dom - '@storybook/manager-webpack4@6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)': - dependencies: - '@babel/core': 7.24.7 - '@babel/plugin-transform-template-literals': 7.23.3(@babel/core@7.24.7) - '@babel/preset-react': 7.23.3(@babel/core@7.24.7) - '@storybook/addons': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/core-client': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)(webpack@4.47.0) - '@storybook/core-common': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3) - '@storybook/node-logger': 6.5.17-alpha.0 - '@storybook/theming': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/ui': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@types/node': 16.18.68 - '@types/webpack': 4.41.38 - babel-loader: 8.3.0(@babel/core@7.24.7)(webpack@4.47.0) - case-sensitive-paths-webpack-plugin: 2.4.0 - chalk: 4.1.2 - core-js: 3.34.0 - css-loader: 3.6.0(webpack@4.47.0) - express: 4.18.2 - file-loader: 6.2.0(webpack@4.47.0) - find-up: 5.0.0 - fs-extra: 9.1.0 - html-webpack-plugin: 4.5.2(webpack@4.47.0) - node-fetch: 2.7.0(encoding@0.1.13) - pnp-webpack-plugin: 1.6.4(typescript@5.3.3) - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - read-pkg-up: 7.0.1 - regenerator-runtime: 0.13.11 - resolve-from: 5.0.0 - style-loader: 1.3.0(webpack@4.47.0) - telejson: 6.0.8 - terser-webpack-plugin: 4.2.3(webpack@4.47.0) - ts-dedent: 2.2.0 - url-loader: 4.1.1(file-loader@6.2.0(webpack@5.89.0))(webpack@4.47.0) - util-deprecate: 1.0.2 - webpack: 4.47.0 - webpack-dev-middleware: 3.7.3(webpack@4.47.0) - webpack-virtual-modules: 0.2.2 - optionalDependencies: - typescript: 5.3.3 - transitivePeerDependencies: - - bluebird - - encoding - - eslint - - supports-color - - vue-template-compiler - - webpack-cli - - webpack-command - '@storybook/manager-webpack4@6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)(webpack-cli@3.3.12(webpack@5.89.0))': dependencies: '@babel/core': 7.24.7 @@ -35367,7 +35648,7 @@ snapshots: telejson: 6.0.8 terser-webpack-plugin: 4.2.3(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))) ts-dedent: 2.2.0 - url-loader: 4.1.1(file-loader@6.2.0(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))))(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))) + url-loader: 4.1.1(file-loader@6.2.0(webpack@5.89.0(webpack-cli@3.3.12)))(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))) util-deprecate: 1.0.2 webpack: 4.47.0(webpack-cli@3.3.12(webpack@5.89.0)) webpack-dev-middleware: 3.7.3(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))) @@ -35383,17 +35664,67 @@ snapshots: - webpack-cli - webpack-command - '@storybook/manager-webpack5@6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)': + '@storybook/manager-webpack4@6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3)': + dependencies: + '@babel/core': 7.24.7 + '@babel/plugin-transform-template-literals': 7.23.3(@babel/core@7.24.7) + '@babel/preset-react': 7.23.3(@babel/core@7.24.7) + '@storybook/addons': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/core-client': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3)(webpack@4.47.0) + '@storybook/core-common': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3) + '@storybook/node-logger': 6.5.17-alpha.0 + '@storybook/theming': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/ui': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@types/node': 16.18.68 + '@types/webpack': 4.41.38 + babel-loader: 8.3.0(@babel/core@7.24.7)(webpack@4.47.0) + case-sensitive-paths-webpack-plugin: 2.4.0 + chalk: 4.1.2 + core-js: 3.34.0 + css-loader: 3.6.0(webpack@4.47.0) + express: 4.18.2 + file-loader: 6.2.0(webpack@4.47.0) + find-up: 5.0.0 + fs-extra: 9.1.0 + html-webpack-plugin: 4.5.2(webpack@4.47.0) + node-fetch: 2.7.0(encoding@0.1.13) + pnp-webpack-plugin: 1.6.4(typescript@5.3.3) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + read-pkg-up: 7.0.1 + regenerator-runtime: 0.13.11 + resolve-from: 5.0.0 + style-loader: 1.3.0(webpack@4.47.0) + telejson: 6.0.8 + terser-webpack-plugin: 4.2.3(webpack@4.47.0) + ts-dedent: 2.2.0 + url-loader: 4.1.1(file-loader@6.2.0(webpack@4.47.0))(webpack@4.47.0) + util-deprecate: 1.0.2 + webpack: 4.47.0 + webpack-dev-middleware: 3.7.3(webpack@4.47.0) + webpack-virtual-modules: 0.2.2 + optionalDependencies: + typescript: 5.3.3 + transitivePeerDependencies: + - bluebird + - encoding + - eslint + - supports-color + - vue-template-compiler + - webpack-cli + - webpack-command + + '@storybook/manager-webpack5@6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3)': dependencies: '@babel/core': 7.23.5 '@babel/plugin-transform-template-literals': 7.23.3(@babel/core@7.23.5) '@babel/preset-react': 7.23.3(@babel/core@7.23.5) - '@storybook/addons': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/core-client': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)(webpack@5.91.0) - '@storybook/core-common': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3) + '@storybook/addons': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/core-client': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3)(webpack@5.91.0) + '@storybook/core-common': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3) '@storybook/node-logger': 6.5.17-alpha.0 - '@storybook/theming': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@storybook/ui': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/theming': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/ui': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/node': 16.18.68 babel-loader: 8.3.0(@babel/core@7.23.5)(webpack@5.91.0) case-sensitive-paths-webpack-plugin: 2.4.0 @@ -35406,8 +35737,8 @@ snapshots: html-webpack-plugin: 5.5.4(webpack@5.91.0) node-fetch: 2.7.0(encoding@0.1.13) process: 0.11.10 - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) read-pkg-up: 7.0.1 regenerator-runtime: 0.13.11 resolve-from: 5.0.0 @@ -35583,11 +35914,32 @@ snapshots: unfetch: 4.2.0 util-deprecate: 1.0.2 + '@storybook/preview-web@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@storybook/addons': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/channel-postmessage': 6.5.17-alpha.0 + '@storybook/client-logger': 6.5.17-alpha.0 + '@storybook/core-events': 6.5.17-alpha.0 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + '@storybook/store': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + ansi-to-html: 0.6.15 + core-js: 3.34.0 + global: 4.4.0 + lodash: 4.17.21 + qs: 6.11.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + regenerator-runtime: 0.13.11 + synchronous-promise: 2.0.17 + ts-dedent: 2.2.0 + unfetch: 4.2.0 + util-deprecate: 1.0.2 + '@storybook/preview@7.6.4': {} '@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.3.2)(webpack@5.91.0(@swc/core@1.3.100)(esbuild@0.18.20)(webpack-cli@5.1.4))': dependencies: - debug: 4.3.5 + debug: 4.3.6 endent: 2.1.0 find-cache-dir: 3.3.2 flat-cache: 3.2.0 @@ -35727,21 +36079,21 @@ snapshots: - webpack-hot-middleware - webpack-plugin-serve - '@storybook/react@6.5.17-alpha.0(@babel/core@7.24.7)(@storybook/builder-webpack5@6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3))(@storybook/manager-webpack5@6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3))(@types/webpack@4.41.38)(encoding@0.1.13)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(require-from-string@2.0.2)(type-fest@2.19.0)(typescript@5.3.3)(webpack-dev-server@4.15.1(webpack@5.89.0))(webpack-hot-middleware@2.25.4)': + '@storybook/react@6.5.17-alpha.0(@babel/core@7.24.7)(@storybook/builder-webpack5@6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3))(@storybook/manager-webpack5@6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3))(@types/webpack@4.41.38)(encoding@0.1.13)(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(require-from-string@2.0.2)(type-fest@2.19.0)(typescript@5.3.3)(webpack-dev-server@4.15.1(webpack@5.89.0))(webpack-hot-middleware@2.25.4)': dependencies: '@babel/preset-flow': 7.23.3(@babel/core@7.24.7) '@babel/preset-react': 7.23.3(@babel/core@7.24.7) '@pmmmwh/react-refresh-webpack-plugin': 0.5.11(@types/webpack@4.41.38)(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-dev-server@4.15.1(webpack@5.89.0))(webpack-hot-middleware@2.25.4)(webpack@5.91.0) - '@storybook/addons': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/addons': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/client-logger': 6.5.17-alpha.0 - '@storybook/core': 6.5.17-alpha.0(@storybook/builder-webpack5@6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3))(@storybook/manager-webpack5@6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3))(encoding@0.1.13)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)(webpack@5.91.0) - '@storybook/core-common': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3) + '@storybook/core': 6.5.17-alpha.0(@storybook/builder-webpack5@6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3))(@storybook/manager-webpack5@6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3))(encoding@0.1.13)(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3)(webpack@5.91.0) + '@storybook/core-common': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3) '@storybook/csf': 0.0.2--canary.4566f4d.1 - '@storybook/docs-tools': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/docs-tools': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/node-logger': 6.5.17-alpha.0 '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.630821.0(typescript@5.3.3)(webpack@5.91.0) '@storybook/semver': 7.3.2 - '@storybook/store': 6.5.17-alpha.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@storybook/store': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/estree': 0.0.51 '@types/node': 16.18.68 '@types/webpack-env': 1.18.4 @@ -35757,9 +36109,9 @@ snapshots: html-tags: 3.3.1 lodash: 4.17.21 prop-types: 15.8.1 - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) - react-element-to-jsx-string: 14.3.4(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-element-to-jsx-string: 14.3.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-refresh: 0.11.0 read-pkg-up: 7.0.1 regenerator-runtime: 0.13.11 @@ -35769,8 +36121,8 @@ snapshots: webpack: 5.91.0 optionalDependencies: '@babel/core': 7.24.7 - '@storybook/builder-webpack5': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3) - '@storybook/manager-webpack5': 6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3) + '@storybook/builder-webpack5': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3) + '@storybook/manager-webpack5': 6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3) typescript: 5.3.3 transitivePeerDependencies: - '@storybook/mdx2-csf' @@ -35865,6 +36217,16 @@ snapshots: react-dom: 17.0.2(react@17.0.2) regenerator-runtime: 0.13.11 + '@storybook/router@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@storybook/client-logger': 6.5.17-alpha.0 + core-js: 3.34.0 + memoizerific: 1.11.3 + qs: 6.11.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + regenerator-runtime: 0.13.11 + '@storybook/router@7.5.2(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': dependencies: '@storybook/client-logger': 7.5.2 @@ -35907,6 +36269,21 @@ snapshots: react-dom: 17.0.2(react@17.0.2) regenerator-runtime: 0.13.11 + '@storybook/source-loader@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@storybook/addons': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/client-logger': 6.5.17-alpha.0 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + core-js: 3.34.0 + estraverse: 5.3.0 + global: 4.4.0 + loader-utils: 2.0.4 + lodash: 4.17.21 + prettier: 2.3.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + regenerator-runtime: 0.13.11 + '@storybook/source-loader@7.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@storybook/csf': 0.1.2 @@ -35937,10 +36314,30 @@ snapshots: ts-dedent: 2.2.0 util-deprecate: 1.0.2 - '@storybook/telemetry@6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)': + '@storybook/store@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@storybook/addons': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/client-logger': 6.5.17-alpha.0 + '@storybook/core-events': 6.5.17-alpha.0 + '@storybook/csf': 0.0.2--canary.4566f4d.1 + core-js: 3.34.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.17.21 + memoizerific: 1.11.3 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + regenerator-runtime: 0.13.11 + slash: 3.0.0 + stable: 0.1.8 + synchronous-promise: 2.0.17 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + + '@storybook/telemetry@6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)(webpack-cli@3.3.12(webpack@5.89.0))': dependencies: '@storybook/client-logger': 6.5.17-alpha.0 - '@storybook/core-common': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3) + '@storybook/core-common': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)(webpack-cli@3.3.12(webpack@5.89.0)) chalk: 4.1.2 core-js: 3.34.0 detect-package-manager: 2.0.1 @@ -35962,10 +36359,10 @@ snapshots: - webpack-cli - webpack-command - '@storybook/telemetry@6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)(webpack-cli@3.3.12(webpack@5.89.0))': + '@storybook/telemetry@6.5.17-alpha.0(encoding@0.1.13)(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3)': dependencies: '@storybook/client-logger': 6.5.17-alpha.0 - '@storybook/core-common': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@5.3.3)(webpack-cli@3.3.12(webpack@5.89.0)) + '@storybook/core-common': 6.5.17-alpha.0(eslint@8.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.3.3) chalk: 4.1.2 core-js: 3.34.0 detect-package-manager: 2.0.1 @@ -36010,6 +36407,15 @@ snapshots: react-dom: 17.0.2(react@17.0.2) regenerator-runtime: 0.13.11 + '@storybook/theming@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@storybook/client-logger': 6.5.17-alpha.0 + core-js: 3.34.0 + memoizerific: 1.11.3 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + regenerator-runtime: 0.13.11 + '@storybook/theming@7.5.2(react-dom@18.3.1(react@17.0.2))(react@17.0.2)': dependencies: '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@17.0.2) @@ -36079,6 +36485,25 @@ snapshots: regenerator-runtime: 0.13.11 resolve-from: 5.0.0 + '@storybook/ui@6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@storybook/addons': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/api': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/channels': 6.5.17-alpha.0 + '@storybook/client-logger': 6.5.17-alpha.0 + '@storybook/components': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/core-events': 6.5.17-alpha.0 + '@storybook/router': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/semver': 7.3.2 + '@storybook/theming': 6.5.17-alpha.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + core-js: 3.34.0 + memoizerific: 1.11.3 + qs: 6.11.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + regenerator-runtime: 0.13.11 + resolve-from: 5.0.0 + '@stylelint/postcss-css-in-js@0.37.3(postcss-syntax@0.36.2(postcss-html@0.36.0)(postcss-less@3.1.4)(postcss-scss@2.1.1)(postcss@7.0.39))(postcss@7.0.39)': dependencies: '@babel/core': 7.24.7 @@ -36409,7 +36834,7 @@ snapshots: '@testing-library/dom@10.1.0': dependencies: '@babel/code-frame': 7.24.7 - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@types/aria-query': 5.0.4 aria-query: 5.3.0 chalk: 4.1.2 @@ -36888,9 +37313,9 @@ snapshots: dependencies: undici-types: 5.26.5 - '@types/node@20.14.2': + '@types/node@22.4.0': dependencies: - undici-types: 5.26.5 + undici-types: 6.19.6 optional: true '@types/normalize-package-data@2.4.4': {} @@ -36941,6 +37366,10 @@ snapshots: dependencies: '@types/react': 17.0.71 + '@types/react-dom@18.3.0': + dependencies: + '@types/react': 17.0.71 + '@types/react-outside-click-handler@1.3.3': dependencies: '@types/react': 17.0.71 @@ -37254,7 +37683,7 @@ snapshots: '@types/react': 17.0.71 '@types/wordpress__block-editor': 7.0.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@types/wordpress__blocks': 11.0.9(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@types/wordpress__components': 19.10.5(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@types/wordpress__components': 23.0.10(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@types/wordpress__core-data': 2.4.5 '@types/wordpress__data': 6.0.2 '@types/wordpress__media-utils': 3.0.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2) @@ -37263,14 +37692,14 @@ snapshots: - react - react-dom - '@types/wordpress__editor@13.6.7(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@types/wordpress__editor@13.6.7(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@types/react': 17.0.71 '@types/wordpress__block-editor': 7.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/wordpress__blocks': 11.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/wordpress__components': 23.0.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/wordpress__media-utils': 3.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@wordpress/core-data': 6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@wordpress/core-data': 6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@wordpress/data': 9.17.0(react@18.3.1) '@wordpress/element': 5.22.0 transitivePeerDependencies: @@ -37380,7 +37809,7 @@ snapshots: '@typescript-eslint/experimental-utils': 4.33.0(eslint@7.32.0)(typescript@5.3.3) '@typescript-eslint/parser': 4.33.0(eslint@7.32.0)(typescript@5.3.3) '@typescript-eslint/scope-manager': 4.33.0 - debug: 4.3.5 + debug: 4.3.6 eslint: 7.32.0 functional-red-black-tree: 1.0.1 ignore: 5.3.0 @@ -37399,7 +37828,7 @@ snapshots: '@typescript-eslint/scope-manager': 5.56.0 '@typescript-eslint/type-utils': 5.56.0(eslint@8.55.0)(typescript@5.3.2) '@typescript-eslint/utils': 5.56.0(eslint@8.55.0)(typescript@5.3.2) - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.55.0 grapheme-splitter: 1.0.4 ignore: 5.3.0 @@ -37418,7 +37847,7 @@ snapshots: '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/type-utils': 5.62.0(eslint@8.55.0)(typescript@5.3.2) '@typescript-eslint/utils': 5.62.0(eslint@8.55.0)(typescript@5.3.2) - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.55.0 graphemer: 1.4.0 ignore: 5.3.0 @@ -37437,7 +37866,7 @@ snapshots: '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/type-utils': 5.62.0(eslint@8.55.0)(typescript@5.3.3) '@typescript-eslint/utils': 5.62.0(eslint@8.55.0)(typescript@5.3.3) - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.55.0 graphemer: 1.4.0 ignore: 5.3.0 @@ -37505,7 +37934,7 @@ snapshots: '@typescript-eslint/scope-manager': 4.33.0 '@typescript-eslint/types': 4.33.0 '@typescript-eslint/typescript-estree': 4.33.0(typescript@5.3.3) - debug: 4.3.5 + debug: 4.3.6 eslint: 7.32.0 optionalDependencies: typescript: 5.3.3 @@ -37517,7 +37946,7 @@ snapshots: '@typescript-eslint/scope-manager': 5.56.0 '@typescript-eslint/types': 5.56.0 '@typescript-eslint/typescript-estree': 5.56.0(typescript@5.3.2) - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.55.0 optionalDependencies: typescript: 5.3.2 @@ -37529,7 +37958,7 @@ snapshots: '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.3.2) - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.55.0 optionalDependencies: typescript: 5.3.2 @@ -37541,7 +37970,7 @@ snapshots: '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.3.3) - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.55.0 optionalDependencies: typescript: 5.3.3 @@ -37567,7 +37996,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 5.56.0(typescript@5.3.2) '@typescript-eslint/utils': 5.56.0(eslint@8.55.0)(typescript@5.3.2) - debug: 4.3.5 + debug: 4.3.6 eslint: 8.55.0 tsutils: 3.21.0(typescript@5.3.2) optionalDependencies: @@ -37607,7 +38036,7 @@ snapshots: '@typescript-eslint/typescript-estree@2.34.0(typescript@5.3.3)': dependencies: - debug: 4.3.5 + debug: 4.3.6 eslint-visitor-keys: 1.3.0 glob: 7.2.3 is-glob: 4.0.3 @@ -37623,7 +38052,7 @@ snapshots: dependencies: '@typescript-eslint/types': 4.33.0 '@typescript-eslint/visitor-keys': 4.33.0 - debug: 4.3.5 + debug: 4.3.6 globby: 11.1.0 is-glob: 4.0.3 semver: 7.6.2 @@ -37637,7 +38066,7 @@ snapshots: dependencies: '@typescript-eslint/types': 5.56.0 '@typescript-eslint/visitor-keys': 5.56.0 - debug: 4.3.5 + debug: 4.3.6 globby: 11.1.0 is-glob: 4.0.3 semver: 7.6.2 @@ -37739,6 +38168,8 @@ snapshots: '@use-gesture/core@10.3.0': {} + '@use-gesture/core@10.3.1': {} + '@use-gesture/react@10.3.0(react@17.0.2)': dependencies: '@use-gesture/core': 10.3.0 @@ -37749,6 +38180,16 @@ snapshots: '@use-gesture/core': 10.3.0 react: 18.3.1 + '@use-gesture/react@10.3.1(react@17.0.2)': + dependencies: + '@use-gesture/core': 10.3.1 + react: 17.0.2 + + '@use-gesture/react@10.3.1(react@18.3.1)': + dependencies: + '@use-gesture/core': 10.3.1 + react: 18.3.1 + '@webassemblyjs/ast@1.11.1': dependencies: '@webassemblyjs/helper-numbers': 1.11.1 @@ -38189,11 +38630,11 @@ snapshots: - supports-color - utf-8-validate - '@woocommerce/e2e-utils@0.1.6(@woocommerce/api@0.2.0)(encoding@0.1.13)(jest@29.7.0(@types/node@20.14.2)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.3.3)))(puppeteer@17.1.3(encoding@0.1.13))(react-native@0.73.0(@babel/core@7.12.9)(@babel/preset-env@7.12.7(@babel/core@7.12.9))(encoding@0.1.13)(react@18.3.1))': + '@woocommerce/e2e-utils@0.1.6(@woocommerce/api@0.2.0)(encoding@0.1.13)(jest@29.7.0(@types/node@22.4.0)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.4.0)(typescript@5.3.3)))(puppeteer@17.1.3(encoding@0.1.13))(react-native@0.73.0(@babel/core@7.12.9)(@babel/preset-env@7.12.7(@babel/core@7.12.9))(encoding@0.1.13)(react@18.3.1))': dependencies: '@woocommerce/api': 0.2.0 '@wordpress/deprecated': 2.12.3 - '@wordpress/e2e-test-utils': 4.16.1(encoding@0.1.13)(jest@29.7.0(@types/node@20.14.2)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.3.3)))(puppeteer@17.1.3(encoding@0.1.13))(react-native@0.73.0(@babel/core@7.12.9)(@babel/preset-env@7.12.7(@babel/core@7.12.9))(encoding@0.1.13)(react@18.3.1)) + '@wordpress/e2e-test-utils': 4.16.1(encoding@0.1.13)(jest@29.7.0(@types/node@22.4.0)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.4.0)(typescript@5.3.3)))(puppeteer@17.1.3(encoding@0.1.13))(react-native@0.73.0(@babel/core@7.12.9)(@babel/preset-env@7.12.7(@babel/core@7.12.9))(encoding@0.1.13)(react@18.3.1)) config: 3.3.3 faker: 5.5.3 fishery: 1.4.0 @@ -38218,13 +38659,13 @@ snapshots: '@wordpress/a11y@3.47.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/dom-ready': 3.47.0 '@wordpress/i18n': 4.47.0 '@wordpress/a11y@3.57.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/dom-ready': 3.57.0 '@wordpress/i18n': 4.57.0 @@ -38234,9 +38675,15 @@ snapshots: '@wordpress/dom-ready': 3.27.0 '@wordpress/i18n': 4.6.1 + '@wordpress/a11y@4.6.0': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/dom-ready': 4.6.0 + '@wordpress/i18n': 5.6.0 + '@wordpress/api-fetch@3.23.1(react-native@0.73.0(@babel/core@7.23.5)(@babel/preset-env@7.23.6(@babel/core@7.23.5))(encoding@0.1.13)(react@17.0.2))': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/i18n': 3.20.0 '@wordpress/url': 2.22.2(react-native@0.73.0(@babel/core@7.23.5)(@babel/preset-env@7.23.6(@babel/core@7.23.5))(encoding@0.1.13)(react@17.0.2)) transitivePeerDependencies: @@ -38244,7 +38691,7 @@ snapshots: '@wordpress/api-fetch@4.0.0(react-native@0.73.0(@babel/core@7.23.5)(@babel/preset-env@7.23.6(@babel/core@7.23.5))(encoding@0.1.13)(react@17.0.2))': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/i18n': 3.20.0 '@wordpress/url': 2.22.2(react-native@0.73.0(@babel/core@7.23.5)(@babel/preset-env@7.23.6(@babel/core@7.23.5))(encoding@0.1.13)(react@17.0.2)) transitivePeerDependencies: @@ -38252,9 +38699,9 @@ snapshots: '@wordpress/api-fetch@5.2.7': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/i18n': 4.47.0 - '@wordpress/url': 3.13.0 + '@wordpress/url': 3.7.1 '@wordpress/api-fetch@6.21.0': dependencies: @@ -38270,13 +38717,13 @@ snapshots: '@wordpress/api-fetch@6.44.0': dependencies: - '@babel/runtime': 7.24.7 - '@wordpress/i18n': 4.47.0 + '@babel/runtime': 7.25.0 + '@wordpress/i18n': 4.57.0 '@wordpress/url': 3.48.0 '@wordpress/api-fetch@7.0.1': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/i18n': 5.0.1 '@wordpress/url': 4.0.1 @@ -38286,7 +38733,7 @@ snapshots: '@wordpress/autop@3.47.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/babel-plugin-import-jsx-pragma@1.1.3(@babel/core@7.12.9)': dependencies: @@ -38333,7 +38780,7 @@ snapshots: '@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.24.7) '@babel/plugin-transform-runtime': 7.23.4(@babel/core@7.24.7) '@babel/preset-env': 7.23.5(@babel/core@7.24.7) - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/babel-plugin-import-jsx-pragma': 2.7.0(@babel/core@7.24.7) '@wordpress/browserslist-config': 2.7.0 '@wordpress/element': 2.20.3 @@ -38383,7 +38830,7 @@ snapshots: '@babel/plugin-transform-runtime': 7.23.4(@babel/core@7.24.7) '@babel/preset-env': 7.23.5(@babel/core@7.24.7) '@babel/preset-typescript': 7.23.3(@babel/core@7.24.7) - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/babel-plugin-import-jsx-pragma': 4.30.0(@babel/core@7.24.7) '@wordpress/browserslist-config': 5.30.0 '@wordpress/warning': 2.47.0 @@ -38405,7 +38852,7 @@ snapshots: '@wordpress/blob@3.47.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/blob@3.6.1': dependencies: @@ -38413,7 +38860,7 @@ snapshots: '@wordpress/block-editor@10.5.0(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@react-spring/web': 9.7.3(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/a11y': 3.47.0 '@wordpress/api-fetch': 6.21.0 @@ -38459,40 +38906,40 @@ snapshots: - '@types/react' - supports-color - '@wordpress/block-editor@12.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@wordpress/block-editor@12.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@emotion/react': 11.11.1(@types/react@17.0.71)(react@17.0.2) '@emotion/styled': 11.11.0(@emotion/react@11.11.1(@types/react@17.0.71)(react@17.0.2))(@types/react@17.0.71)(react@17.0.2) '@react-spring/web': 9.7.3(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/a11y': 3.47.0 + '@wordpress/a11y': 3.57.0 '@wordpress/api-fetch': 6.44.0 '@wordpress/blob': 3.47.0 '@wordpress/blocks': 12.24.0(react@17.0.2) - '@wordpress/commands': 0.18.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/compose': 6.24.0(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) - '@wordpress/date': 4.47.0 - '@wordpress/deprecated': 3.47.0 - '@wordpress/dom': 3.47.0 - '@wordpress/element': 5.24.0 - '@wordpress/escape-html': 2.47.0 + '@wordpress/commands': 0.18.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/compose': 6.34.0(react@17.0.2) + '@wordpress/data': 9.27.0(react@17.0.2) + '@wordpress/date': 4.57.0 + '@wordpress/deprecated': 3.57.0 + '@wordpress/dom': 3.57.0 + '@wordpress/element': 5.34.0 + '@wordpress/escape-html': 2.57.0 '@wordpress/hooks': 3.57.0 - '@wordpress/html-entities': 3.47.0 - '@wordpress/i18n': 4.47.0 + '@wordpress/html-entities': 3.57.0 + '@wordpress/i18n': 4.57.0 '@wordpress/icons': 9.48.0 - '@wordpress/is-shallow-equal': 4.47.0 + '@wordpress/is-shallow-equal': 4.57.0 '@wordpress/keyboard-shortcuts': 4.24.0(react@17.0.2) '@wordpress/keycodes': 3.57.0 '@wordpress/notices': 4.15.0(react@17.0.2) - '@wordpress/preferences': 3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/preferences': 3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/private-apis': 0.29.0 - '@wordpress/rich-text': 6.24.0(react@17.0.2) + '@wordpress/rich-text': 6.34.0(react@17.0.2) '@wordpress/style-engine': 1.30.0 '@wordpress/token-list': 2.47.0 '@wordpress/url': 3.48.0 - '@wordpress/warning': 2.47.0 + '@wordpress/warning': 2.57.0 '@wordpress/wordcount': 3.47.0 change-case: 4.1.2 classnames: 2.3.2 @@ -38520,40 +38967,40 @@ snapshots: - supports-color - vite - '@wordpress/block-editor@12.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@wordpress/block-editor@12.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@emotion/react': 11.11.1(@types/react@17.0.71)(react@18.3.1) '@emotion/styled': 11.11.0(@emotion/react@11.11.1(@types/react@17.0.71)(react@18.3.1))(@types/react@17.0.71)(react@18.3.1) '@react-spring/web': 9.7.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@wordpress/a11y': 3.47.0 + '@wordpress/a11y': 3.57.0 '@wordpress/api-fetch': 6.44.0 '@wordpress/blob': 3.47.0 '@wordpress/blocks': 12.24.0(react@18.3.1) - '@wordpress/commands': 0.18.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@wordpress/compose': 6.24.0(react@18.3.1) - '@wordpress/data': 9.17.0(react@18.3.1) - '@wordpress/date': 4.47.0 - '@wordpress/deprecated': 3.47.0 - '@wordpress/dom': 3.47.0 - '@wordpress/element': 5.24.0 - '@wordpress/escape-html': 2.47.0 + '@wordpress/commands': 0.18.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@wordpress/compose': 6.34.0(react@18.3.1) + '@wordpress/data': 9.27.0(react@18.3.1) + '@wordpress/date': 4.57.0 + '@wordpress/deprecated': 3.57.0 + '@wordpress/dom': 3.57.0 + '@wordpress/element': 5.34.0 + '@wordpress/escape-html': 2.57.0 '@wordpress/hooks': 3.57.0 - '@wordpress/html-entities': 3.47.0 - '@wordpress/i18n': 4.47.0 + '@wordpress/html-entities': 3.57.0 + '@wordpress/i18n': 4.57.0 '@wordpress/icons': 9.48.0 - '@wordpress/is-shallow-equal': 4.47.0 + '@wordpress/is-shallow-equal': 4.57.0 '@wordpress/keyboard-shortcuts': 4.24.0(react@18.3.1) '@wordpress/keycodes': 3.57.0 '@wordpress/notices': 4.15.0(react@18.3.1) - '@wordpress/preferences': 3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@wordpress/preferences': 3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@wordpress/private-apis': 0.29.0 - '@wordpress/rich-text': 6.24.0(react@18.3.1) + '@wordpress/rich-text': 6.34.0(react@18.3.1) '@wordpress/style-engine': 1.30.0 '@wordpress/token-list': 2.47.0 '@wordpress/url': 3.48.0 - '@wordpress/warning': 2.47.0 + '@wordpress/warning': 2.57.0 '@wordpress/wordcount': 3.47.0 change-case: 4.1.2 classnames: 2.3.2 @@ -38593,7 +39040,7 @@ snapshots: '@wordpress/compose': 5.5.0(react@18.3.1) '@wordpress/data': 6.15.0(react@18.3.1) '@wordpress/deprecated': 3.41.0 - '@wordpress/dom': 3.27.0 + '@wordpress/dom': 3.6.1 '@wordpress/element': 4.20.0 '@wordpress/hooks': 3.6.1 '@wordpress/html-entities': 3.24.0 @@ -38629,7 +39076,7 @@ snapshots: '@wordpress/block-editor@8.6.0(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react-with-direction@1.4.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@react-spring/web': 9.7.3(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/a11y': 3.47.0 '@wordpress/api-fetch': 6.21.0 @@ -38766,36 +39213,36 @@ snapshots: - '@types/react' - supports-color - '@wordpress/block-library@8.24.1(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@preact/signals-core@1.5.1)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@wordpress/block-library@8.24.1(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@preact/signals-core@1.5.1)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 - '@wordpress/a11y': 3.47.0 + '@babel/runtime': 7.25.0 + '@wordpress/a11y': 3.57.0 '@wordpress/api-fetch': 6.44.0 '@wordpress/autop': 3.47.0 '@wordpress/blob': 3.47.0 - '@wordpress/block-editor': 12.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/block-editor': 12.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/blocks': 12.24.0(react@17.0.2) - '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/compose': 6.24.0(react@17.0.2) - '@wordpress/core-data': 6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) - '@wordpress/date': 4.47.0 - '@wordpress/deprecated': 3.47.0 - '@wordpress/dom': 3.47.0 - '@wordpress/element': 5.24.0 - '@wordpress/escape-html': 2.47.0 + '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/compose': 6.34.0(react@17.0.2) + '@wordpress/core-data': 6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/data': 9.27.0(react@17.0.2) + '@wordpress/date': 4.57.0 + '@wordpress/deprecated': 3.57.0 + '@wordpress/dom': 3.57.0 + '@wordpress/element': 5.34.0 + '@wordpress/escape-html': 2.57.0 '@wordpress/hooks': 3.57.0 - '@wordpress/html-entities': 3.47.0 - '@wordpress/i18n': 4.47.0 + '@wordpress/html-entities': 3.57.0 + '@wordpress/i18n': 4.57.0 '@wordpress/icons': 9.48.0 '@wordpress/interactivity': 3.0.1(@preact/signals-core@1.5.1) '@wordpress/keycodes': 3.57.0 '@wordpress/notices': 4.15.0(react@17.0.2) - '@wordpress/primitives': 3.45.0 + '@wordpress/primitives': 3.55.0 '@wordpress/private-apis': 0.29.0 - '@wordpress/reusable-blocks': 4.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/rich-text': 6.24.0(react@17.0.2) - '@wordpress/server-side-render': 4.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/reusable-blocks': 4.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/rich-text': 6.34.0(react@17.0.2) + '@wordpress/server-side-render': 4.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/url': 3.48.0 '@wordpress/viewport': 5.24.0(react@17.0.2) '@wordpress/wordcount': 3.47.0 @@ -38825,7 +39272,7 @@ snapshots: '@wordpress/block-serialization-default-parser@4.47.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/blocks@11.1.5(react@18.3.1)': dependencies: @@ -38836,7 +39283,7 @@ snapshots: '@wordpress/compose': 5.5.0(react@18.3.1) '@wordpress/data': 6.15.0(react@18.3.1) '@wordpress/deprecated': 3.41.0 - '@wordpress/dom': 3.27.0 + '@wordpress/dom': 3.6.1 '@wordpress/element': 4.20.0 '@wordpress/hooks': 3.6.1 '@wordpress/html-entities': 3.24.0 @@ -38920,7 +39367,7 @@ snapshots: '@wordpress/compose': 6.24.0(react@17.0.2) '@wordpress/data': 9.17.0(react@17.0.2) '@wordpress/deprecated': 3.47.0 - '@wordpress/dom': 3.47.0 + '@wordpress/dom': 3.57.0 '@wordpress/element': 5.24.0 '@wordpress/hooks': 3.57.0 '@wordpress/html-entities': 3.47.0 @@ -38950,7 +39397,7 @@ snapshots: '@wordpress/compose': 6.24.0(react@18.3.1) '@wordpress/data': 9.17.0(react@18.3.1) '@wordpress/deprecated': 3.47.0 - '@wordpress/dom': 3.47.0 + '@wordpress/dom': 3.57.0 '@wordpress/element': 5.24.0 '@wordpress/hooks': 3.57.0 '@wordpress/html-entities': 3.47.0 @@ -38979,11 +39426,11 @@ snapshots: '@wordpress/browserslist-config@5.30.0': {} - '@wordpress/commands@0.18.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@wordpress/commands@0.18.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 - '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) + '@babel/runtime': 7.25.0 + '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/data': 9.27.0(react@17.0.2) '@wordpress/element': 5.34.0 '@wordpress/i18n': 4.57.0 '@wordpress/icons': 9.48.0 @@ -39003,11 +39450,11 @@ snapshots: - supports-color - vite - '@wordpress/commands@0.18.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@wordpress/commands@0.18.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@wordpress/data': 9.17.0(react@18.3.1) + '@babel/runtime': 7.25.0 + '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@wordpress/data': 9.27.0(react@18.3.1) '@wordpress/element': 5.34.0 '@wordpress/i18n': 4.57.0 '@wordpress/icons': 9.48.0 @@ -39027,13 +39474,13 @@ snapshots: - supports-color - vite - '@wordpress/commands@0.9.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@wordpress/commands@0.9.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 - '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) + '@babel/runtime': 7.25.0 + '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/data': 9.27.0(react@17.0.2) '@wordpress/element': 5.22.0 - '@wordpress/i18n': 4.47.0 + '@wordpress/i18n': 4.57.0 '@wordpress/icons': 9.36.0 '@wordpress/keyboard-shortcuts': 4.24.0(react@17.0.2) '@wordpress/private-apis': 0.20.0 @@ -39062,7 +39509,7 @@ snapshots: '@wordpress/compose': 4.2.0(react@18.3.1) '@wordpress/date': 4.44.0 '@wordpress/deprecated': 3.41.0 - '@wordpress/dom': 3.27.0 + '@wordpress/dom': 3.6.1 '@wordpress/element': 3.2.0 '@wordpress/hooks': 3.6.1 '@wordpress/i18n': 4.47.0 @@ -39210,7 +39657,7 @@ snapshots: '@wordpress/compose': 5.4.1(react@17.0.2) '@wordpress/date': 4.44.0 '@wordpress/deprecated': 3.41.0 - '@wordpress/dom': 3.27.0 + '@wordpress/dom': 3.6.1 '@wordpress/element': 4.4.1 '@wordpress/escape-html': 2.47.0 '@wordpress/hooks': 3.6.1 @@ -39258,7 +39705,7 @@ snapshots: '@wordpress/compose': 5.4.1(react@17.0.2) '@wordpress/date': 4.44.0 '@wordpress/deprecated': 3.41.0 - '@wordpress/dom': 3.27.0 + '@wordpress/dom': 3.6.1 '@wordpress/element': 4.4.1 '@wordpress/escape-html': 2.47.0 '@wordpress/hooks': 3.6.1 @@ -39294,7 +39741,7 @@ snapshots: '@wordpress/components@20.0.0(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@emotion/cache': 11.11.0 '@emotion/css': 11.11.2 '@emotion/react': 11.11.1(@types/react@17.0.71)(react@17.0.2) @@ -39315,7 +39762,7 @@ snapshots: '@wordpress/icons': 9.36.0 '@wordpress/is-shallow-equal': 4.24.0 '@wordpress/keycodes': 3.47.0 - '@wordpress/primitives': 3.45.0 + '@wordpress/primitives': 3.55.0 '@wordpress/rich-text': 5.20.0(react@17.0.2) '@wordpress/warning': 2.47.0 change-case: 4.1.2 @@ -39343,7 +39790,7 @@ snapshots: '@wordpress/components@22.1.0(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@emotion/cache': 11.11.0 '@emotion/css': 11.11.2 '@emotion/react': 11.11.1(@types/react@17.0.71)(react@17.0.2) @@ -39364,7 +39811,7 @@ snapshots: '@wordpress/icons': 9.36.0 '@wordpress/is-shallow-equal': 4.24.0 '@wordpress/keycodes': 3.47.0 - '@wordpress/primitives': 3.45.0 + '@wordpress/primitives': 3.55.0 '@wordpress/rich-text': 5.20.0(react@17.0.2) '@wordpress/warning': 2.47.0 change-case: 4.1.2 @@ -39391,84 +39838,20 @@ snapshots: - '@types/react' - supports-color - '@wordpress/components@25.13.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': - dependencies: - '@ariakit/react': 0.3.9(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@babel/runtime': 7.24.7 - '@emotion/cache': 11.11.0 - '@emotion/css': 11.11.2 - '@emotion/react': 11.11.1(@types/react@17.0.71)(react@17.0.2) - '@emotion/serialize': 1.1.2 - '@emotion/styled': 11.11.0(@emotion/react@11.11.1(@types/react@17.0.71)(react@17.0.2))(@types/react@17.0.71)(react@17.0.2) - '@emotion/utils': 1.2.1 - '@floating-ui/react-dom': 2.0.4(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@radix-ui/react-dropdown-menu': 2.0.4(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@types/gradient-parser': 0.1.3 - '@types/highlight-words-core': 1.2.1 - '@use-gesture/react': 10.3.0(react@17.0.2) - '@wordpress/a11y': 3.47.0 - '@wordpress/compose': 6.24.0(react@17.0.2) - '@wordpress/date': 4.47.0 - '@wordpress/deprecated': 3.47.0 - '@wordpress/dom': 3.47.0 - '@wordpress/element': 5.24.0 - '@wordpress/escape-html': 2.47.0 - '@wordpress/hooks': 3.57.0 - '@wordpress/html-entities': 3.47.0 - '@wordpress/i18n': 4.54.0 - '@wordpress/icons': 9.48.0 - '@wordpress/is-shallow-equal': 4.47.0 - '@wordpress/keycodes': 3.57.0 - '@wordpress/primitives': 3.45.0 - '@wordpress/private-apis': 0.29.0 - '@wordpress/rich-text': 6.24.0(react@17.0.2) - '@wordpress/warning': 2.47.0 - change-case: 4.1.2 - classnames: 2.3.2 - colord: 2.9.3 - date-fns: 2.30.0 - deepmerge: 4.3.1 - dom-scroll-into-view: 1.2.1 - downshift: 6.1.12(react@17.0.2) - fast-deep-equal: 3.1.3 - framer-motion: 10.16.16(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - gradient-parser: 0.1.5 - highlight-words-core: 1.2.2 - is-plain-object: 5.0.0 - memize: 2.1.0 - path-to-regexp: 6.2.1 - re-resizable: 6.9.11(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - react: 17.0.2 - react-colorful: 5.6.1(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - react-dom: 17.0.2(react@17.0.2) - reakit: 1.3.11(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - remove-accents: 0.5.0 - use-lilius: 2.0.3(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - uuid: 9.0.1 - valtio: 1.7.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(babel-plugin-macros@3.1.0)(react@17.0.2) - transitivePeerDependencies: - - '@babel/helper-module-imports' - - '@babel/types' - - '@types/react' - - aslemammad-vite-plugin-macro - - babel-plugin-macros - - supports-color - - vite - - '@wordpress/components@25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@wordpress/components@25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: '@ariakit/react': 0.3.14(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@emotion/cache': 11.11.0 '@emotion/css': 11.11.2 '@emotion/react': 11.11.1(@types/react@17.0.71)(react@17.0.2) '@emotion/serialize': 1.1.2 '@emotion/styled': 11.11.0(@emotion/react@11.11.1(@types/react@17.0.71)(react@17.0.2))(@types/react@17.0.71)(react@17.0.2) '@emotion/utils': 1.2.1 - '@floating-ui/react-dom': 2.0.4(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@floating-ui/react-dom': 2.0.9(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@types/gradient-parser': 0.1.3 '@types/highlight-words-core': 1.2.1 - '@use-gesture/react': 10.3.0(react@17.0.2) + '@use-gesture/react': 10.3.1(react@17.0.2) '@wordpress/a11y': 3.57.0 '@wordpress/compose': 6.34.0(react@17.0.2) '@wordpress/date': 4.57.0 @@ -39506,9 +39889,9 @@ snapshots: react-dom: 17.0.2(react@17.0.2) reakit: 1.3.11(react-dom@17.0.2(react@17.0.2))(react@17.0.2) remove-accents: 0.5.0 - use-lilius: 2.0.3(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + use-lilius: 2.0.5(react-dom@17.0.2(react@17.0.2))(react@17.0.2) uuid: 9.0.1 - valtio: 1.7.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(babel-plugin-macros@3.1.0)(react@17.0.2) + valtio: 1.7.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(babel-plugin-macros@3.1.0)(react@17.0.2) transitivePeerDependencies: - '@babel/helper-module-imports' - '@babel/types' @@ -39518,20 +39901,20 @@ snapshots: - supports-color - vite - '@wordpress/components@25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@wordpress/components@25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@ariakit/react': 0.3.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@emotion/cache': 11.11.0 '@emotion/css': 11.11.2 '@emotion/react': 11.11.1(@types/react@17.0.71)(react@18.3.1) '@emotion/serialize': 1.1.2 '@emotion/styled': 11.11.0(@emotion/react@11.11.1(@types/react@17.0.71)(react@18.3.1))(@types/react@17.0.71)(react@18.3.1) '@emotion/utils': 1.2.1 - '@floating-ui/react-dom': 2.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@floating-ui/react-dom': 2.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/gradient-parser': 0.1.3 '@types/highlight-words-core': 1.2.1 - '@use-gesture/react': 10.3.0(react@18.3.1) + '@use-gesture/react': 10.3.1(react@18.3.1) '@wordpress/a11y': 3.57.0 '@wordpress/compose': 6.34.0(react@18.3.1) '@wordpress/date': 4.57.0 @@ -39569,9 +39952,9 @@ snapshots: react-dom: 18.3.1(react@18.3.1) reakit: 1.3.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1) remove-accents: 0.5.0 - use-lilius: 2.0.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + use-lilius: 2.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) uuid: 9.0.1 - valtio: 1.7.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(babel-plugin-macros@3.1.0)(react@18.3.1) + valtio: 1.7.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(babel-plugin-macros@3.1.0)(react@18.3.1) transitivePeerDependencies: - '@babel/helper-module-imports' - '@babel/types' @@ -39581,7 +39964,7 @@ snapshots: - supports-color - vite - '@wordpress/components@26.0.6(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@wordpress/components@26.0.6(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@ariakit/react': 0.3.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@babel/runtime': 7.23.6 @@ -39633,7 +40016,7 @@ snapshots: remove-accents: 0.5.0 use-lilius: 2.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) uuid: 9.0.1 - valtio: 1.7.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(babel-plugin-macros@3.1.0)(react@18.3.1) + valtio: 1.7.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(babel-plugin-macros@3.1.0)(react@18.3.1) transitivePeerDependencies: - '@babel/helper-module-imports' - '@babel/types' @@ -39646,7 +40029,7 @@ snapshots: '@wordpress/components@27.5.0(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: '@ariakit/react': 0.3.14(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@emotion/cache': 11.11.0 '@emotion/css': 11.11.2 '@emotion/react': 11.11.1(@types/react@17.0.71)(react@17.0.2) @@ -39656,7 +40039,7 @@ snapshots: '@floating-ui/react-dom': 2.0.9(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@types/gradient-parser': 0.1.3 '@types/highlight-words-core': 1.2.1 - '@use-gesture/react': 10.3.0(react@17.0.2) + '@use-gesture/react': 10.3.1(react@17.0.2) '@wordpress/a11y': 3.57.0 '@wordpress/compose': 6.34.0(react@17.0.2) '@wordpress/date': 4.57.0 @@ -39698,9 +40081,64 @@ snapshots: - '@types/react' - supports-color + '@wordpress/components@28.6.0(@emotion/is-prop-valid@1.2.1)(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + dependencies: + '@ariakit/react': 0.4.10(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@babel/runtime': 7.25.0 + '@emotion/cache': 11.11.0 + '@emotion/css': 11.11.2 + '@emotion/react': 11.11.1(@types/react@17.0.71)(react@17.0.2) + '@emotion/serialize': 1.1.2 + '@emotion/styled': 11.11.0(@emotion/react@11.11.1(@types/react@17.0.71)(react@17.0.2))(@types/react@17.0.71)(react@17.0.2) + '@emotion/utils': 1.2.1 + '@floating-ui/react-dom': 2.0.9(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@types/gradient-parser': 0.1.3 + '@types/highlight-words-core': 1.2.1 + '@use-gesture/react': 10.3.1(react@17.0.2) + '@wordpress/a11y': 4.6.0 + '@wordpress/compose': 7.6.0(react@17.0.2) + '@wordpress/date': 5.6.0 + '@wordpress/deprecated': 4.6.0 + '@wordpress/dom': 4.6.0 + '@wordpress/element': 6.6.0 + '@wordpress/escape-html': 3.6.0 + '@wordpress/hooks': 4.6.0 + '@wordpress/html-entities': 4.6.0 + '@wordpress/i18n': 5.6.0 + '@wordpress/icons': 10.6.0(react@17.0.2) + '@wordpress/is-shallow-equal': 5.6.0 + '@wordpress/keycodes': 4.6.0 + '@wordpress/primitives': 4.6.0(react@17.0.2) + '@wordpress/private-apis': 1.6.0 + '@wordpress/rich-text': 7.6.0(react@17.0.2) + '@wordpress/warning': 3.6.0 + change-case: 4.1.2 + clsx: 2.1.1 + colord: 2.9.3 + date-fns: 3.6.0 + deepmerge: 4.3.1 + fast-deep-equal: 3.1.3 + framer-motion: 11.3.30(@emotion/is-prop-valid@1.2.1)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + gradient-parser: 0.1.5 + highlight-words-core: 1.2.2 + is-plain-object: 5.0.0 + memize: 2.1.0 + path-to-regexp: 6.2.1 + re-resizable: 6.9.11(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + react: 17.0.2 + react-colorful: 5.6.1(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + react-dom: 17.0.2(react@17.0.2) + remove-accents: 0.5.0 + use-lilius: 2.0.5(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + uuid: 9.0.1 + transitivePeerDependencies: + - '@emotion/is-prop-valid' + - '@types/react' + - supports-color + '@wordpress/compose@3.25.3(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/deprecated': 2.12.3 '@wordpress/dom': 2.18.0 '@wordpress/element': 2.20.3 @@ -39718,11 +40156,11 @@ snapshots: '@wordpress/compose@4.2.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@types/lodash': 4.14.149 '@types/mousetrap': 1.6.15 - '@wordpress/deprecated': 3.41.0 - '@wordpress/dom': 3.27.0 + '@wordpress/deprecated': 3.6.1 + '@wordpress/dom': 3.6.1 '@wordpress/element': 3.2.0 '@wordpress/is-shallow-equal': 4.24.0 '@wordpress/keycodes': 3.47.0 @@ -39737,10 +40175,10 @@ snapshots: '@wordpress/compose@5.20.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@types/mousetrap': 1.6.15 '@wordpress/deprecated': 3.47.0 - '@wordpress/dom': 3.47.0 + '@wordpress/dom': 3.27.0 '@wordpress/element': 4.20.0 '@wordpress/is-shallow-equal': 4.47.0 '@wordpress/keycodes': 3.47.0 @@ -39753,10 +40191,10 @@ snapshots: '@wordpress/compose@5.20.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@types/mousetrap': 1.6.15 '@wordpress/deprecated': 3.47.0 - '@wordpress/dom': 3.47.0 + '@wordpress/dom': 3.27.0 '@wordpress/element': 4.20.0 '@wordpress/is-shallow-equal': 4.47.0 '@wordpress/keycodes': 3.47.0 @@ -39773,7 +40211,7 @@ snapshots: '@types/lodash': 4.14.202 '@types/mousetrap': 1.6.15 '@wordpress/deprecated': 3.6.1 - '@wordpress/dom': 3.27.0 + '@wordpress/dom': 3.6.1 '@wordpress/element': 4.4.1 '@wordpress/is-shallow-equal': 4.24.0 '@wordpress/keycodes': 3.6.1 @@ -39787,7 +40225,7 @@ snapshots: '@wordpress/compose@5.5.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.23.6 + '@babel/runtime': 7.25.0 '@types/lodash': 4.14.202 '@types/mousetrap': 1.6.15 '@wordpress/deprecated': 3.41.0 @@ -39805,7 +40243,7 @@ snapshots: '@wordpress/compose@5.5.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.23.6 + '@babel/runtime': 7.25.0 '@types/lodash': 4.14.202 '@types/mousetrap': 1.6.15 '@wordpress/deprecated': 3.41.0 @@ -39823,11 +40261,11 @@ snapshots: '@wordpress/compose@6.24.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@types/mousetrap': 1.6.15 - '@wordpress/deprecated': 3.47.0 - '@wordpress/dom': 3.47.0 - '@wordpress/element': 5.24.0 + '@wordpress/deprecated': 3.57.0 + '@wordpress/dom': 3.57.0 + '@wordpress/element': 5.34.0 '@wordpress/is-shallow-equal': 4.47.0 '@wordpress/keycodes': 3.47.0 '@wordpress/priority-queue': 2.47.0 @@ -39840,11 +40278,11 @@ snapshots: '@wordpress/compose@6.24.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@types/mousetrap': 1.6.15 - '@wordpress/deprecated': 3.47.0 - '@wordpress/dom': 3.47.0 - '@wordpress/element': 5.24.0 + '@wordpress/deprecated': 3.57.0 + '@wordpress/dom': 3.57.0 + '@wordpress/element': 5.34.0 '@wordpress/is-shallow-equal': 4.47.0 '@wordpress/keycodes': 3.47.0 '@wordpress/priority-queue': 2.47.0 @@ -39857,7 +40295,7 @@ snapshots: '@wordpress/compose@6.34.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@types/mousetrap': 1.6.15 '@wordpress/deprecated': 3.57.0 '@wordpress/dom': 3.57.0 @@ -39874,7 +40312,7 @@ snapshots: '@wordpress/compose@6.34.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@types/mousetrap': 1.6.15 '@wordpress/deprecated': 3.57.0 '@wordpress/dom': 3.57.0 @@ -39889,12 +40327,29 @@ snapshots: react: 18.3.1 use-memo-one: 1.1.3(react@18.3.1) - '@wordpress/core-commands@0.7.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@wordpress/compose@7.6.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 - '@wordpress/commands': 0.9.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/core-data': 6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) + '@babel/runtime': 7.25.0 + '@types/mousetrap': 1.6.15 + '@wordpress/deprecated': 4.6.0 + '@wordpress/dom': 4.6.0 + '@wordpress/element': 6.6.0 + '@wordpress/is-shallow-equal': 5.6.0 + '@wordpress/keycodes': 4.6.0 + '@wordpress/priority-queue': 3.6.0 + '@wordpress/undo-manager': 1.6.0 + change-case: 4.1.2 + clipboard: 2.0.11 + mousetrap: 1.6.5 + react: 17.0.2 + use-memo-one: 1.1.3(react@17.0.2) + + '@wordpress/core-commands@0.7.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/commands': 0.9.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/core-data': 6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/data': 9.27.0(react@17.0.2) '@wordpress/element': 5.22.0 '@wordpress/i18n': 4.47.0 '@wordpress/icons': 9.36.0 @@ -39975,21 +40430,21 @@ snapshots: rememo: 4.0.2 uuid: 8.3.2 - '@wordpress/core-data@6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@wordpress/core-data@6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/api-fetch': 6.44.0 - '@wordpress/block-editor': 12.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/block-editor': 12.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/blocks': 12.24.0(react@17.0.2) - '@wordpress/compose': 6.24.0(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) - '@wordpress/deprecated': 3.47.0 - '@wordpress/element': 5.24.0 - '@wordpress/html-entities': 3.47.0 - '@wordpress/i18n': 4.47.0 - '@wordpress/is-shallow-equal': 4.47.0 + '@wordpress/compose': 6.34.0(react@17.0.2) + '@wordpress/data': 9.27.0(react@17.0.2) + '@wordpress/deprecated': 3.57.0 + '@wordpress/element': 5.34.0 + '@wordpress/html-entities': 3.57.0 + '@wordpress/i18n': 4.57.0 + '@wordpress/is-shallow-equal': 4.57.0 '@wordpress/private-apis': 0.29.0 - '@wordpress/rich-text': 6.24.0(react@17.0.2) + '@wordpress/rich-text': 6.34.0(react@17.0.2) '@wordpress/sync': 0.9.0 '@wordpress/undo-manager': 0.7.0 '@wordpress/url': 3.48.0 @@ -40012,21 +40467,21 @@ snapshots: - utf-8-validate - vite - '@wordpress/core-data@6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@wordpress/core-data@6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/api-fetch': 6.44.0 - '@wordpress/block-editor': 12.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@wordpress/block-editor': 12.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@wordpress/blocks': 12.24.0(react@18.3.1) - '@wordpress/compose': 6.24.0(react@18.3.1) - '@wordpress/data': 9.17.0(react@18.3.1) - '@wordpress/deprecated': 3.47.0 - '@wordpress/element': 5.24.0 - '@wordpress/html-entities': 3.47.0 - '@wordpress/i18n': 4.47.0 - '@wordpress/is-shallow-equal': 4.47.0 + '@wordpress/compose': 6.34.0(react@18.3.1) + '@wordpress/data': 9.27.0(react@18.3.1) + '@wordpress/deprecated': 3.57.0 + '@wordpress/element': 5.34.0 + '@wordpress/html-entities': 3.57.0 + '@wordpress/i18n': 4.57.0 + '@wordpress/is-shallow-equal': 4.57.0 '@wordpress/private-apis': 0.29.0 - '@wordpress/rich-text': 6.24.0(react@18.3.1) + '@wordpress/rich-text': 6.34.0(react@18.3.1) '@wordpress/sync': 0.9.0 '@wordpress/undo-manager': 0.7.0 '@wordpress/url': 3.48.0 @@ -40072,7 +40527,7 @@ snapshots: '@wordpress/data-controls@1.21.3(react-native@0.73.0(@babel/core@7.23.5)(@babel/preset-env@7.23.6(@babel/core@7.23.5))(encoding@0.1.13)(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/api-fetch': 4.0.0(react-native@0.73.0(@babel/core@7.23.5)(@babel/preset-env@7.23.6(@babel/core@7.23.5))(encoding@0.1.13)(react@17.0.2)) '@wordpress/data': 4.27.3(react@17.0.2) '@wordpress/deprecated': 2.12.3 @@ -40097,9 +40552,28 @@ snapshots: '@wordpress/deprecated': 3.41.0 react: 17.0.2 + '@wordpress/data@10.6.0(react@17.0.2)': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/compose': 7.6.0(react@17.0.2) + '@wordpress/deprecated': 4.6.0 + '@wordpress/element': 6.6.0 + '@wordpress/is-shallow-equal': 5.6.0 + '@wordpress/priority-queue': 3.6.0 + '@wordpress/private-apis': 1.6.0 + '@wordpress/redux-routine': 5.6.0(redux@4.2.1) + deepmerge: 4.3.1 + equivalent-key-map: 0.2.2 + is-plain-object: 5.0.0 + is-promise: 4.0.0 + react: 17.0.2 + redux: 4.2.1 + rememo: 4.0.2 + use-memo-one: 1.1.3(react@17.0.2) + '@wordpress/data@4.27.3(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/compose': 3.25.3(react@17.0.2) '@wordpress/deprecated': 2.12.3 '@wordpress/element': 2.20.3 @@ -40118,13 +40592,13 @@ snapshots: '@wordpress/data@5.2.0(react@18.3.1)(redux@4.2.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/compose': 4.2.0(react@18.3.1) '@wordpress/deprecated': 3.6.1 '@wordpress/element': 3.2.0 '@wordpress/is-shallow-equal': 4.24.0 - '@wordpress/priority-queue': 2.47.0 - '@wordpress/redux-routine': 4.47.0(redux@4.2.1) + '@wordpress/priority-queue': 2.57.0 + '@wordpress/redux-routine': 4.57.0(redux@4.2.1) equivalent-key-map: 0.2.2 is-promise: 4.0.0 lodash: 4.17.21 @@ -40137,7 +40611,7 @@ snapshots: '@wordpress/data@6.15.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/compose': 5.20.0(react@17.0.2) '@wordpress/deprecated': 3.41.0 '@wordpress/element': 4.20.0 @@ -40207,7 +40681,7 @@ snapshots: '@wordpress/data@7.6.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/compose': 5.20.0(react@17.0.2) '@wordpress/deprecated': 3.41.0 '@wordpress/element': 4.20.0 @@ -40225,7 +40699,7 @@ snapshots: '@wordpress/data@7.6.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/compose': 5.20.0(react@18.3.1) '@wordpress/deprecated': 3.41.0 '@wordpress/element': 4.20.0 @@ -40281,7 +40755,7 @@ snapshots: '@wordpress/data@9.27.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/compose': 6.34.0(react@17.0.2) '@wordpress/deprecated': 3.57.0 '@wordpress/element': 5.34.0 @@ -40300,7 +40774,7 @@ snapshots: '@wordpress/data@9.27.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/compose': 6.34.0(react@18.3.1) '@wordpress/deprecated': 3.57.0 '@wordpress/element': 5.34.0 @@ -40317,6 +40791,28 @@ snapshots: rememo: 4.0.2 use-memo-one: 1.1.3(react@18.3.1) + '@wordpress/dataviews@4.2.0(@emotion/is-prop-valid@1.2.1)(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + dependencies: + '@ariakit/react': 0.4.10(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@babel/runtime': 7.25.0 + '@wordpress/components': 28.6.0(@emotion/is-prop-valid@1.2.1)(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/compose': 7.6.0(react@17.0.2) + '@wordpress/data': 10.6.0(react@17.0.2) + '@wordpress/element': 6.6.0 + '@wordpress/i18n': 5.6.0 + '@wordpress/icons': 10.6.0(react@17.0.2) + '@wordpress/primitives': 4.6.0(react@17.0.2) + '@wordpress/private-apis': 1.6.0 + '@wordpress/warning': 3.6.0 + clsx: 2.1.1 + react: 17.0.2 + remove-accents: 0.5.0 + transitivePeerDependencies: + - '@emotion/is-prop-valid' + - '@types/react' + - react-dom + - supports-color + '@wordpress/date@4.44.0': dependencies: '@babel/runtime': 7.23.5 @@ -40324,16 +40820,9 @@ snapshots: moment: 2.29.4 moment-timezone: 0.5.43 - '@wordpress/date@4.47.0': - dependencies: - '@babel/runtime': 7.24.7 - '@wordpress/deprecated': 3.47.0 - moment: 2.29.4 - moment-timezone: 0.5.43 - '@wordpress/date@4.57.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/deprecated': 3.57.0 moment: 2.29.4 moment-timezone: 0.5.43 @@ -40344,6 +40833,13 @@ snapshots: moment: 2.29.4 moment-timezone: 0.5.43 + '@wordpress/date@5.6.0': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/deprecated': 4.6.0 + moment: 2.29.4 + moment-timezone: 0.5.43 + '@wordpress/dependency-extraction-webpack-plugin@2.9.0(webpack@4.47.0(webpack-cli@3.3.12))': dependencies: json2php: 0.0.4 @@ -40382,7 +40878,7 @@ snapshots: '@wordpress/deprecated@2.12.3': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/hooks': 2.12.3 '@wordpress/deprecated@3.41.0': @@ -40392,12 +40888,12 @@ snapshots: '@wordpress/deprecated@3.47.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/hooks': 3.57.0 '@wordpress/deprecated@3.57.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/hooks': 3.57.0 '@wordpress/deprecated@3.6.1': @@ -40405,25 +40901,34 @@ snapshots: '@babel/runtime': 7.23.5 '@wordpress/hooks': 3.6.1 + '@wordpress/deprecated@4.6.0': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/hooks': 4.6.0 + '@wordpress/dom-ready@3.27.0': dependencies: '@babel/runtime': 7.23.5 '@wordpress/dom-ready@3.47.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/dom-ready@3.57.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/dom-ready@3.6.1': dependencies: '@babel/runtime': 7.23.5 + '@wordpress/dom-ready@4.6.0': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/dom@2.18.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 lodash: 4.17.21 '@wordpress/dom@3.27.0': @@ -40431,14 +40936,9 @@ snapshots: '@babel/runtime': 7.23.5 '@wordpress/deprecated': 3.41.0 - '@wordpress/dom@3.47.0': - dependencies: - '@babel/runtime': 7.24.7 - '@wordpress/deprecated': 3.47.0 - '@wordpress/dom@3.57.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/deprecated': 3.57.0 '@wordpress/dom@3.6.1': @@ -40446,9 +40946,14 @@ snapshots: '@babel/runtime': 7.23.5 lodash: 4.17.21 - '@wordpress/e2e-test-utils-playwright@1.0.1(@playwright/test@1.45.1)(encoding@0.1.13)(typescript@5.3.2)': + '@wordpress/dom@4.6.0': dependencies: - '@playwright/test': 1.45.1 + '@babel/runtime': 7.25.0 + '@wordpress/deprecated': 4.6.0 + + '@wordpress/e2e-test-utils-playwright@1.0.1(@playwright/test@1.46.1)(encoding@0.1.13)(typescript@5.3.2)': + dependencies: + '@playwright/test': 1.46.1 '@wordpress/api-fetch': 7.0.1 '@wordpress/keycodes': 4.0.1 '@wordpress/url': 4.0.1 @@ -40465,9 +40970,9 @@ snapshots: - typescript - utf-8-validate - '@wordpress/e2e-test-utils-playwright@1.0.1(@playwright/test@1.45.1)(encoding@0.1.13)(typescript@5.3.3)': + '@wordpress/e2e-test-utils-playwright@1.0.1(@playwright/test@1.46.1)(encoding@0.1.13)(typescript@5.3.3)': dependencies: - '@playwright/test': 1.45.1 + '@playwright/test': 1.46.1 '@wordpress/api-fetch': 7.0.1 '@wordpress/keycodes': 4.0.1 '@wordpress/url': 4.0.1 @@ -40500,7 +41005,7 @@ snapshots: '@wordpress/e2e-test-utils@3.0.0(jest@24.9.0)(puppeteer@2.1.1)(react-native@0.73.0(@babel/core@7.12.9)(@babel/preset-env@7.12.7(@babel/core@7.12.9))(encoding@0.1.13)(react@18.3.1))': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/keycodes': 2.19.3 '@wordpress/url': 2.22.2(react-native@0.73.0(@babel/core@7.12.9)(@babel/preset-env@7.12.7(@babel/core@7.12.9))(encoding@0.1.13)(react@18.3.1)) jest: 24.9.0 @@ -40512,7 +41017,7 @@ snapshots: '@wordpress/e2e-test-utils@3.0.0(jest@24.9.0)(puppeteer@2.1.1)(react-native@0.73.0(@babel/core@7.23.5)(@babel/preset-env@7.23.6(@babel/core@7.23.5))(encoding@0.1.13)(react@18.3.1))': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/keycodes': 2.19.3 '@wordpress/url': 2.22.2(react-native@0.73.0(@babel/core@7.23.5)(@babel/preset-env@7.23.6(@babel/core@7.23.5))(encoding@0.1.13)(react@18.3.1)) jest: 24.9.0 @@ -40548,12 +41053,12 @@ snapshots: - encoding - react-native - '@wordpress/e2e-test-utils@4.16.1(encoding@0.1.13)(jest@29.7.0(@types/node@20.14.2)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.3.3)))(puppeteer@17.1.3(encoding@0.1.13))(react-native@0.73.0(@babel/core@7.12.9)(@babel/preset-env@7.12.7(@babel/core@7.12.9))(encoding@0.1.13)(react@18.3.1))': + '@wordpress/e2e-test-utils@4.16.1(encoding@0.1.13)(jest@29.7.0(@types/node@22.4.0)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.4.0)(typescript@5.3.3)))(puppeteer@17.1.3(encoding@0.1.13))(react-native@0.73.0(@babel/core@7.12.9)(@babel/preset-env@7.12.7(@babel/core@7.12.9))(encoding@0.1.13)(react@18.3.1))': dependencies: '@babel/runtime': 7.23.5 '@wordpress/keycodes': 2.19.3 '@wordpress/url': 2.22.2(react-native@0.73.0(@babel/core@7.12.9)(@babel/preset-env@7.12.7(@babel/core@7.12.9))(encoding@0.1.13)(react@18.3.1)) - jest: 29.7.0(@types/node@20.14.2)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.3.3)) + jest: 29.7.0(@types/node@22.4.0)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.4.0)(typescript@5.3.3)) lodash: 4.17.21 node-fetch: 2.7.0(encoding@0.1.13) puppeteer: 17.1.3(encoding@0.1.13) @@ -40563,7 +41068,7 @@ snapshots: '@wordpress/e2e-test-utils@7.11.0(encoding@0.1.13)(jest@29.7.0(@types/node@16.18.68)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.2)))(puppeteer-core@21.6.0(encoding@0.1.13))': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/api-fetch': 6.21.0 '@wordpress/keycodes': 3.47.0 '@wordpress/url': 3.48.0 @@ -40654,7 +41159,7 @@ snapshots: '@wordpress/element': 4.4.1 '@wordpress/hooks': 3.6.1 '@wordpress/i18n': 4.6.1 - '@wordpress/icons': 8.2.3 + '@wordpress/icons': 8.4.0 '@wordpress/interface': 4.5.6(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react-with-direction@1.4.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react@17.0.2) '@wordpress/keyboard-shortcuts': 3.4.1(react@17.0.2) '@wordpress/keycodes': 3.6.1 @@ -40676,45 +41181,45 @@ snapshots: - react-with-direction - supports-color - '@wordpress/edit-site@5.15.0(patch_hash=6y3l6gxu33zybfmvbjd23dtqda)(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@preact/signals-core@1.5.1)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@wordpress/edit-site@5.15.0(patch_hash=6y3l6gxu33zybfmvbjd23dtqda)(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@preact/signals-core@1.5.1)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.23.5 - '@wordpress/a11y': 3.47.0 + '@babel/runtime': 7.25.0 + '@wordpress/a11y': 3.57.0 '@wordpress/api-fetch': 6.44.0 - '@wordpress/block-editor': 12.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/block-library': 8.24.1(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@preact/signals-core@1.5.1)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/block-editor': 12.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/block-library': 8.24.1(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@preact/signals-core@1.5.1)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/blocks': 12.24.0(react@17.0.2) - '@wordpress/commands': 0.9.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/components': 25.13.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/compose': 6.24.0(react@17.0.2) - '@wordpress/core-commands': 0.7.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/core-data': 6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) + '@wordpress/commands': 0.9.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/compose': 6.34.0(react@17.0.2) + '@wordpress/core-commands': 0.7.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/core-data': 6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/data': 9.27.0(react@17.0.2) '@wordpress/date': 4.44.0 '@wordpress/deprecated': 3.41.0 - '@wordpress/dom': 3.47.0 - '@wordpress/editor': 13.24.1(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/dom': 3.57.0 + '@wordpress/editor': 13.24.1(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/element': 5.22.0 - '@wordpress/escape-html': 2.47.0 + '@wordpress/escape-html': 2.57.0 '@wordpress/hooks': 3.57.0 - '@wordpress/html-entities': 3.47.0 - '@wordpress/i18n': 4.47.0 + '@wordpress/html-entities': 3.57.0 + '@wordpress/i18n': 4.57.0 '@wordpress/icons': 9.36.0 - '@wordpress/interface': 5.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/interface': 5.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/keyboard-shortcuts': 4.24.0(react@17.0.2) - '@wordpress/keycodes': 3.47.0 + '@wordpress/keycodes': 3.57.0 '@wordpress/media-utils': 4.38.0 '@wordpress/notices': 4.15.0(react@17.0.2) - '@wordpress/plugins': 6.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/preferences': 3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/primitives': 3.45.0 + '@wordpress/plugins': 6.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/preferences': 3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/primitives': 3.55.0 '@wordpress/private-apis': 0.20.0 - '@wordpress/reusable-blocks': 4.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/reusable-blocks': 4.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/router': 0.7.0(react@17.0.2) '@wordpress/style-engine': 1.30.0 '@wordpress/url': 3.48.0 '@wordpress/viewport': 5.24.0(react@17.0.2) - '@wordpress/widgets': 3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/widgets': 3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/wordcount': 3.47.0 change-case: 4.1.2 classnames: 2.3.2 @@ -40759,7 +41264,7 @@ snapshots: '@wordpress/hooks': 3.6.1 '@wordpress/html-entities': 3.6.1 '@wordpress/i18n': 4.6.1 - '@wordpress/icons': 8.2.3 + '@wordpress/icons': 8.4.0 '@wordpress/keyboard-shortcuts': 3.4.1(react@17.0.2) '@wordpress/keycodes': 3.6.1 '@wordpress/media-utils': 3.4.1 @@ -40782,36 +41287,36 @@ snapshots: - react-with-direction - supports-color - '@wordpress/editor@13.24.1(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@wordpress/editor@13.24.1(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 - '@wordpress/a11y': 3.47.0 + '@babel/runtime': 7.25.0 + '@wordpress/a11y': 3.57.0 '@wordpress/api-fetch': 6.44.0 '@wordpress/blob': 3.47.0 - '@wordpress/block-editor': 12.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/block-editor': 12.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/blocks': 12.24.0(react@17.0.2) - '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/compose': 6.24.0(react@17.0.2) - '@wordpress/core-data': 6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) - '@wordpress/date': 4.47.0 - '@wordpress/deprecated': 3.47.0 - '@wordpress/dom': 3.47.0 - '@wordpress/element': 5.24.0 + '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/compose': 6.34.0(react@17.0.2) + '@wordpress/core-data': 6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/data': 9.27.0(react@17.0.2) + '@wordpress/date': 4.57.0 + '@wordpress/deprecated': 3.57.0 + '@wordpress/dom': 3.57.0 + '@wordpress/element': 5.34.0 '@wordpress/hooks': 3.57.0 - '@wordpress/html-entities': 3.47.0 - '@wordpress/i18n': 4.47.0 + '@wordpress/html-entities': 3.57.0 + '@wordpress/i18n': 4.57.0 '@wordpress/icons': 9.48.0 '@wordpress/keyboard-shortcuts': 4.24.0(react@17.0.2) '@wordpress/keycodes': 3.57.0 '@wordpress/media-utils': 4.38.0 '@wordpress/notices': 4.15.0(react@17.0.2) - '@wordpress/patterns': 1.8.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/preferences': 3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/patterns': 1.8.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/preferences': 3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/private-apis': 0.29.0 - '@wordpress/reusable-blocks': 4.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/rich-text': 6.24.0(react@17.0.2) - '@wordpress/server-side-render': 4.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/reusable-blocks': 4.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/rich-text': 6.34.0(react@17.0.2) + '@wordpress/server-side-render': 4.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/url': 3.48.0 '@wordpress/wordcount': 3.47.0 classnames: 2.3.2 @@ -40835,7 +41340,7 @@ snapshots: '@wordpress/element@2.20.3': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@types/react': 17.0.71 '@types/react-dom': 16.9.24 '@wordpress/escape-html': 1.12.2 @@ -40887,7 +41392,7 @@ snapshots: '@wordpress/element@5.24.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@types/react': 17.0.71 '@types/react-dom': 18.0.10 '@wordpress/escape-html': 2.47.0 @@ -40898,7 +41403,7 @@ snapshots: '@wordpress/element@5.34.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@types/react': 17.0.71 '@types/react-dom': 18.0.10 '@wordpress/escape-html': 2.57.0 @@ -40907,6 +41412,17 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + '@wordpress/element@6.6.0': + dependencies: + '@babel/runtime': 7.25.0 + '@types/react': 17.0.71 + '@types/react-dom': 18.3.0 + '@wordpress/escape-html': 3.6.0 + change-case: 4.1.2 + is-plain-object: 5.0.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + '@wordpress/env@10.5.0': dependencies: chalk: 4.1.2 @@ -40926,7 +41442,7 @@ snapshots: '@wordpress/escape-html@1.12.2': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/escape-html@2.47.0': dependencies: @@ -40934,7 +41450,11 @@ snapshots: '@wordpress/escape-html@2.57.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 + + '@wordpress/escape-html@3.6.0': + dependencies: + '@babel/runtime': 7.25.0 '@wordpress/eslint-plugin@12.9.0(@babel/core@7.24.7)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.56.0(eslint@8.55.0)(typescript@5.3.2))(eslint-import-resolver-webpack@0.13.2)(eslint-plugin-import@2.28.1)(eslint@8.55.0))(eslint-import-resolver-webpack@0.13.2(eslint-plugin-import@2.28.1)(webpack@5.91.0(@swc/core@1.3.100)(esbuild@0.18.20)(webpack-cli@5.1.4)))(eslint@8.55.0)(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.2)))(typescript@5.3.2)(wp-prettier@2.6.2)': dependencies: @@ -41101,19 +41621,19 @@ snapshots: '@wordpress/hooks@2.12.3': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/hooks@3.57.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/hooks@3.6.1': dependencies: '@babel/runtime': 7.24.7 - '@wordpress/hooks@4.4.0': + '@wordpress/hooks@4.6.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/html-entities@3.24.0': dependencies: @@ -41121,19 +41641,23 @@ snapshots: '@wordpress/html-entities@3.47.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/html-entities@3.57.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/html-entities@3.6.1': dependencies: '@babel/runtime': 7.23.5 + '@wordpress/html-entities@4.6.0': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/i18n@3.20.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/hooks': 2.12.3 gettext-parser: 1.4.0 lodash: 4.17.21 @@ -41159,18 +41683,9 @@ snapshots: sprintf-js: 1.1.3 tannin: 1.2.0 - '@wordpress/i18n@4.54.0': - dependencies: - '@babel/runtime': 7.24.7 - '@wordpress/hooks': 3.57.0 - gettext-parser: 1.4.0 - memize: 2.1.0 - sprintf-js: 1.1.3 - tannin: 1.2.0 - '@wordpress/i18n@4.57.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/hooks': 3.57.0 gettext-parser: 1.4.0 memize: 2.1.0 @@ -41189,22 +41704,39 @@ snapshots: '@wordpress/i18n@5.0.1': dependencies: - '@babel/runtime': 7.24.7 - '@wordpress/hooks': 4.4.0 + '@babel/runtime': 7.25.0 + '@wordpress/hooks': 4.6.0 gettext-parser: 1.4.0 memize: 2.1.0 sprintf-js: 1.1.3 tannin: 1.2.0 + '@wordpress/i18n@5.6.0': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/hooks': 4.6.0 + gettext-parser: 1.4.0 + memize: 2.1.0 + sprintf-js: 1.1.3 + tannin: 1.2.0 + + '@wordpress/icons@10.6.0(react@17.0.2)': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/element': 6.6.0 + '@wordpress/primitives': 4.6.0(react@17.0.2) + transitivePeerDependencies: + - react + '@wordpress/icons@4.1.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/element': 3.2.0 '@wordpress/primitives': 2.2.0 '@wordpress/icons@7.0.1': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/element': 4.20.0 '@wordpress/primitives': 3.4.1 @@ -41226,15 +41758,9 @@ snapshots: '@wordpress/element': 5.22.0 '@wordpress/primitives': 3.45.0 - '@wordpress/icons@9.38.0': - dependencies: - '@babel/runtime': 7.24.7 - '@wordpress/element': 5.24.0 - '@wordpress/primitives': 3.45.0 - '@wordpress/icons@9.48.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/element': 5.34.0 '@wordpress/primitives': 3.55.0 @@ -41257,7 +41783,7 @@ snapshots: '@wordpress/deprecated': 3.6.1 '@wordpress/element': 4.4.1 '@wordpress/i18n': 4.6.1 - '@wordpress/icons': 8.2.3 + '@wordpress/icons': 8.4.0 '@wordpress/plugins': 4.4.3(react@17.0.2) '@wordpress/preferences': 1.2.5(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react-with-direction@1.4.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react@17.0.2) '@wordpress/viewport': 4.20.0(react@17.0.2) @@ -41270,19 +41796,19 @@ snapshots: - react-with-direction - supports-color - '@wordpress/interface@5.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@wordpress/interface@5.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.23.5 - '@wordpress/a11y': 3.47.0 - '@wordpress/components': 25.13.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/compose': 6.24.0(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) - '@wordpress/deprecated': 3.47.0 - '@wordpress/element': 5.24.0 - '@wordpress/i18n': 4.47.0 - '@wordpress/icons': 9.38.0 - '@wordpress/plugins': 6.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/preferences': 3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@babel/runtime': 7.25.0 + '@wordpress/a11y': 3.57.0 + '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/compose': 6.34.0(react@17.0.2) + '@wordpress/data': 9.27.0(react@17.0.2) + '@wordpress/deprecated': 3.57.0 + '@wordpress/element': 5.34.0 + '@wordpress/i18n': 4.57.0 + '@wordpress/icons': 9.48.0 + '@wordpress/plugins': 6.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/preferences': 3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/viewport': 5.24.0(react@17.0.2) classnames: 2.3.2 react: 17.0.2 @@ -41298,7 +41824,7 @@ snapshots: '@wordpress/is-shallow-equal@3.1.3': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/is-shallow-equal@4.24.0': dependencies: @@ -41306,36 +41832,40 @@ snapshots: '@wordpress/is-shallow-equal@4.47.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/is-shallow-equal@4.57.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 + + '@wordpress/is-shallow-equal@5.6.0': + dependencies: + '@babel/runtime': 7.25.0 '@wordpress/jest-console@3.10.0(jest@25.5.4)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 jest: 25.5.4 jest-matcher-utils: 25.5.0 lodash: 4.17.21 '@wordpress/jest-console@4.1.1(jest@25.5.4)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 jest: 25.5.4 jest-matcher-utils: 26.6.2 lodash: 4.17.21 '@wordpress/jest-console@4.1.1(jest@26.6.3(ts-node@10.9.2(@types/node@16.18.68)(typescript@5.3.3)))': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 jest: 26.6.3(ts-node@10.9.2(@types/node@16.18.68)(typescript@5.3.3)) jest-matcher-utils: 26.6.2 lodash: 4.17.21 '@wordpress/jest-console@4.1.1(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@16.18.68)(typescript@5.3.3)))': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 jest: 27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.3)) jest-matcher-utils: 26.6.2 lodash: 4.17.21 @@ -41360,7 +41890,7 @@ snapshots: '@wordpress/jest-console@6.11.0(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.2)))': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 jest: 27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.2)) jest-matcher-utils: 27.5.1 @@ -41461,14 +41991,14 @@ snapshots: '@wordpress/jest-puppeteer-axe@4.1.0(jest@29.7.0(@types/node@16.18.68)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.2)))(puppeteer@17.1.3(encoding@0.1.13))': dependencies: '@axe-core/puppeteer': 4.8.2(puppeteer@17.1.3(encoding@0.1.13)) - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 jest: 29.7.0(@types/node@16.18.68)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.2)) optionalDependencies: puppeteer: 17.1.3(encoding@0.1.13) '@wordpress/keyboard-shortcuts@3.20.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/data': 7.6.0(react@17.0.2) '@wordpress/element': 4.20.0 '@wordpress/keycodes': 3.47.0 @@ -41477,9 +42007,9 @@ snapshots: '@wordpress/keyboard-shortcuts@3.4.1(react@17.0.2)': dependencies: - '@babel/runtime': 7.23.6 + '@babel/runtime': 7.25.0 '@wordpress/data': 6.6.1(react@17.0.2) - '@wordpress/element': 4.4.1 + '@wordpress/element': 4.20.0 '@wordpress/keycodes': 3.6.1 lodash: 4.17.21 react: 17.0.2 @@ -41487,9 +42017,9 @@ snapshots: '@wordpress/keyboard-shortcuts@3.4.1(react@18.3.1)': dependencies: - '@babel/runtime': 7.23.6 + '@babel/runtime': 7.25.0 '@wordpress/data': 6.6.1(react@18.3.1) - '@wordpress/element': 4.4.1 + '@wordpress/element': 4.20.0 '@wordpress/keycodes': 3.6.1 lodash: 4.17.21 react: 18.3.1 @@ -41497,8 +42027,8 @@ snapshots: '@wordpress/keyboard-shortcuts@4.24.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 - '@wordpress/data': 9.17.0(react@17.0.2) + '@babel/runtime': 7.25.0 + '@wordpress/data': 9.27.0(react@17.0.2) '@wordpress/element': 5.34.0 '@wordpress/keycodes': 3.57.0 react: 17.0.2 @@ -41506,8 +42036,8 @@ snapshots: '@wordpress/keyboard-shortcuts@4.24.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@wordpress/data': 9.17.0(react@18.3.1) + '@babel/runtime': 7.25.0 + '@wordpress/data': 9.27.0(react@18.3.1) '@wordpress/element': 5.34.0 '@wordpress/keycodes': 3.57.0 react: 18.3.1 @@ -41515,7 +42045,7 @@ snapshots: '@wordpress/keycodes@2.19.3': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/i18n': 3.20.0 lodash: 4.17.21 @@ -41527,7 +42057,7 @@ snapshots: '@wordpress/keycodes@3.57.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/i18n': 4.57.0 '@wordpress/keycodes@3.6.1': @@ -41538,9 +42068,14 @@ snapshots: '@wordpress/keycodes@4.0.1': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/i18n': 5.0.1 + '@wordpress/keycodes@4.6.0': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/i18n': 5.6.0 + '@wordpress/lazy-import@1.34.0': dependencies: execa: 4.1.0 @@ -41558,29 +42093,29 @@ snapshots: '@wordpress/media-utils@4.38.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/api-fetch': 6.44.0 '@wordpress/blob': 3.47.0 - '@wordpress/element': 5.24.0 + '@wordpress/element': 5.34.0 '@wordpress/i18n': 4.47.0 '@wordpress/notices@3.12.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/a11y': 3.47.0 '@wordpress/data': 6.15.0(react@17.0.2) react: 17.0.2 '@wordpress/notices@3.12.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/a11y': 3.47.0 '@wordpress/data': 6.15.0(react@18.3.1) react: 18.3.1 '@wordpress/notices@3.31.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/a11y': 3.47.0 '@wordpress/data': 9.17.0(react@17.0.2) transitivePeerDependencies: @@ -41596,16 +42131,16 @@ snapshots: '@wordpress/notices@4.15.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 - '@wordpress/a11y': 3.47.0 - '@wordpress/data': 9.17.0(react@17.0.2) + '@babel/runtime': 7.25.0 + '@wordpress/a11y': 3.57.0 + '@wordpress/data': 9.27.0(react@17.0.2) react: 17.0.2 '@wordpress/notices@4.15.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@wordpress/a11y': 3.47.0 - '@wordpress/data': 9.17.0(react@18.3.1) + '@babel/runtime': 7.25.0 + '@wordpress/a11y': 3.57.0 + '@wordpress/data': 9.27.0(react@18.3.1) react: 18.3.1 '@wordpress/npm-package-json-lint-config@3.1.0(npm-package-json-lint@5.4.2)': @@ -41616,18 +42151,18 @@ snapshots: dependencies: npm-package-json-lint: 5.4.2 - '@wordpress/patterns@1.8.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@wordpress/patterns@1.8.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/a11y': 3.47.0 - '@wordpress/block-editor': 12.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/block-editor': 12.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/blocks': 12.24.0(react@17.0.2) - '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/compose': 6.34.0(react@17.0.2) - '@wordpress/core-data': 6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) + '@wordpress/core-data': 6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/data': 9.27.0(react@17.0.2) '@wordpress/element': 5.34.0 - '@wordpress/html-entities': 3.47.0 + '@wordpress/html-entities': 3.57.0 '@wordpress/i18n': 4.57.0 '@wordpress/icons': 9.48.0 '@wordpress/notices': 4.15.0(react@17.0.2) @@ -41667,15 +42202,15 @@ snapshots: memize: 1.1.0 react: 17.0.2 - '@wordpress/plugins@6.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@wordpress/plugins@6.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 - '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/compose': 6.24.0(react@17.0.2) - '@wordpress/element': 5.24.0 + '@babel/runtime': 7.25.0 + '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/compose': 6.34.0(react@17.0.2) + '@wordpress/element': 5.34.0 '@wordpress/hooks': 3.57.0 '@wordpress/icons': 9.48.0 - '@wordpress/is-shallow-equal': 4.47.0 + '@wordpress/is-shallow-equal': 4.57.0 memize: 2.1.0 react: 17.0.2 react-dom: 17.0.2(react@17.0.2) @@ -41742,12 +42277,12 @@ snapshots: - react-with-direction - supports-color - '@wordpress/preferences@3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@wordpress/preferences@3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 - '@wordpress/a11y': 3.47.0 - '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) + '@babel/runtime': 7.25.0 + '@wordpress/a11y': 3.57.0 + '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/data': 9.27.0(react@17.0.2) '@wordpress/element': 5.34.0 '@wordpress/i18n': 4.57.0 '@wordpress/icons': 9.48.0 @@ -41763,12 +42298,12 @@ snapshots: - supports-color - vite - '@wordpress/preferences@3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@wordpress/preferences@3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 - '@wordpress/a11y': 3.47.0 - '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@wordpress/data': 9.17.0(react@18.3.1) + '@babel/runtime': 7.25.0 + '@wordpress/a11y': 3.57.0 + '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@wordpress/data': 9.27.0(react@18.3.1) '@wordpress/element': 5.34.0 '@wordpress/i18n': 4.57.0 '@wordpress/icons': 9.48.0 @@ -41808,7 +42343,7 @@ snapshots: '@wordpress/primitives@2.2.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/element': 3.2.0 classnames: 2.3.2 @@ -41826,60 +42361,76 @@ snapshots: '@wordpress/primitives@3.45.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/element': 5.34.0 classnames: 2.3.2 '@wordpress/primitives@3.55.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/element': 5.34.0 classnames: 2.3.2 + '@wordpress/primitives@4.6.0(react@17.0.2)': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/element': 6.6.0 + clsx: 2.1.1 + react: 17.0.2 + '@wordpress/priority-queue@1.11.2': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/priority-queue@2.47.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 requestidlecallback: 0.3.0 '@wordpress/priority-queue@2.57.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 + requestidlecallback: 0.3.0 + + '@wordpress/priority-queue@3.6.0': + dependencies: + '@babel/runtime': 7.25.0 requestidlecallback: 0.3.0 '@wordpress/private-apis@0.20.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/private-apis@0.29.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/private-apis@0.32.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/private-apis@0.33.1': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/private-apis@0.39.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 + + '@wordpress/private-apis@1.6.0': + dependencies: + '@babel/runtime': 7.25.0 '@wordpress/react-i18n@3.55.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/element': 5.34.0 '@wordpress/i18n': 4.57.0 utility-types: 3.10.0 '@wordpress/redux-routine@3.14.2': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 is-promise: 4.0.0 lodash: 4.17.21 rungen: 0.3.2 @@ -41894,7 +42445,15 @@ snapshots: '@wordpress/redux-routine@4.57.0(redux@4.2.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 + is-plain-object: 5.0.0 + is-promise: 4.0.0 + redux: 4.2.1 + rungen: 0.3.2 + + '@wordpress/redux-routine@5.6.0(redux@4.2.1)': + dependencies: + '@babel/runtime': 7.25.0 is-plain-object: 5.0.0 is-promise: 4.0.0 redux: 4.2.1 @@ -41918,16 +42477,16 @@ snapshots: - '@types/react' - supports-color - '@wordpress/reusable-blocks@4.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@wordpress/reusable-blocks@4.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 - '@wordpress/block-editor': 12.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@babel/runtime': 7.25.0 + '@wordpress/block-editor': 12.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/blocks': 12.24.0(react@17.0.2) - '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/core-data': 6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) - '@wordpress/element': 5.24.0 - '@wordpress/i18n': 4.47.0 + '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/core-data': 6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/data': 9.27.0(react@17.0.2) + '@wordpress/element': 5.34.0 + '@wordpress/i18n': 4.57.0 '@wordpress/icons': 9.48.0 '@wordpress/notices': 4.15.0(react@17.0.2) '@wordpress/private-apis': 0.29.0 @@ -41947,12 +42506,12 @@ snapshots: '@wordpress/rich-text@4.2.0(react@18.3.1)(redux@4.2.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/compose': 4.2.0(react@18.3.1) '@wordpress/data': 5.2.0(react@18.3.1)(redux@4.2.1) - '@wordpress/dom': 3.27.0 + '@wordpress/dom': 3.6.1 '@wordpress/element': 3.2.0 - '@wordpress/escape-html': 2.47.0 + '@wordpress/escape-html': 2.57.0 '@wordpress/is-shallow-equal': 4.24.0 '@wordpress/keycodes': 3.47.0 classnames: 2.3.2 @@ -41965,7 +42524,7 @@ snapshots: '@wordpress/rich-text@5.20.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/a11y': 3.47.0 '@wordpress/compose': 5.20.0(react@17.0.2) '@wordpress/data': 7.6.0(react@17.0.2) @@ -41980,7 +42539,7 @@ snapshots: '@wordpress/rich-text@5.20.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/a11y': 3.47.0 '@wordpress/compose': 5.20.0(react@18.3.1) '@wordpress/data': 7.6.0(react@18.3.1) @@ -42023,39 +42582,9 @@ snapshots: react: 18.3.1 rememo: 3.0.0 - '@wordpress/rich-text@6.24.0(react@17.0.2)': - dependencies: - '@babel/runtime': 7.24.7 - '@wordpress/a11y': 3.47.0 - '@wordpress/compose': 6.34.0(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) - '@wordpress/deprecated': 3.47.0 - '@wordpress/element': 5.34.0 - '@wordpress/escape-html': 2.47.0 - '@wordpress/i18n': 4.57.0 - '@wordpress/keycodes': 3.57.0 - memize: 2.1.0 - react: 17.0.2 - rememo: 4.0.2 - - '@wordpress/rich-text@6.24.0(react@18.3.1)': - dependencies: - '@babel/runtime': 7.24.7 - '@wordpress/a11y': 3.47.0 - '@wordpress/compose': 6.34.0(react@18.3.1) - '@wordpress/data': 9.17.0(react@18.3.1) - '@wordpress/deprecated': 3.47.0 - '@wordpress/element': 5.34.0 - '@wordpress/escape-html': 2.47.0 - '@wordpress/i18n': 4.57.0 - '@wordpress/keycodes': 3.57.0 - memize: 2.1.0 - react: 18.3.1 - rememo: 4.0.2 - '@wordpress/rich-text@6.34.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/a11y': 3.57.0 '@wordpress/compose': 6.34.0(react@17.0.2) '@wordpress/data': 9.27.0(react@17.0.2) @@ -42069,7 +42598,7 @@ snapshots: '@wordpress/rich-text@6.34.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/a11y': 3.57.0 '@wordpress/compose': 6.34.0(react@18.3.1) '@wordpress/data': 9.27.0(react@18.3.1) @@ -42081,9 +42610,23 @@ snapshots: memize: 2.1.0 react: 18.3.1 + '@wordpress/rich-text@7.6.0(react@17.0.2)': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/a11y': 4.6.0 + '@wordpress/compose': 7.6.0(react@17.0.2) + '@wordpress/data': 10.6.0(react@17.0.2) + '@wordpress/deprecated': 4.6.0 + '@wordpress/element': 6.6.0 + '@wordpress/escape-html': 3.6.0 + '@wordpress/i18n': 5.6.0 + '@wordpress/keycodes': 4.6.0 + memize: 2.1.0 + react: 17.0.2 + '@wordpress/router@0.7.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.23.5 + '@babel/runtime': 7.25.0 '@wordpress/element': 5.22.0 '@wordpress/private-apis': 0.20.0 '@wordpress/url': 3.48.0 @@ -42405,7 +42948,7 @@ snapshots: '@wordpress/server-side-render@3.10.0(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react-with-direction@1.4.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.23.5 + '@babel/runtime': 7.25.0 '@wordpress/api-fetch': 6.21.0 '@wordpress/blocks': 11.21.0(react@17.0.2) '@wordpress/components': 19.17.0(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react-with-direction@1.4.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2))(react@17.0.2) @@ -42445,7 +42988,7 @@ snapshots: '@wordpress/server-side-render@3.20.0(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/api-fetch': 6.21.0 '@wordpress/blocks': 11.21.0(react@17.0.2) '@wordpress/components': 22.1.0(@types/react@17.0.71)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) @@ -42462,15 +43005,15 @@ snapshots: - '@types/react' - supports-color - '@wordpress/server-side-render@4.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@wordpress/server-side-render@4.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/api-fetch': 6.44.0 '@wordpress/blocks': 12.24.0(react@17.0.2) - '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/compose': 6.34.0(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) - '@wordpress/deprecated': 3.47.0 + '@wordpress/data': 9.27.0(react@17.0.2) + '@wordpress/deprecated': 3.57.0 '@wordpress/element': 5.34.0 '@wordpress/i18n': 4.57.0 '@wordpress/url': 3.48.0 @@ -42493,17 +43036,17 @@ snapshots: '@wordpress/style-engine@0.15.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 lodash: 4.17.21 '@wordpress/style-engine@0.2.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 lodash: 4.17.21 '@wordpress/style-engine@0.6.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 lodash: 4.17.21 '@wordpress/style-engine@1.30.0': @@ -42536,7 +43079,7 @@ snapshots: '@wordpress/sync@0.9.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@types/simple-peer': 9.11.8 '@wordpress/url': 3.48.0 import-locals: 2.0.0 @@ -42557,17 +43100,22 @@ snapshots: '@wordpress/undo-manager@0.17.0': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/is-shallow-equal': 4.57.0 '@wordpress/undo-manager@0.7.0': dependencies: - '@babel/runtime': 7.24.7 - '@wordpress/is-shallow-equal': 4.47.0 + '@babel/runtime': 7.25.0 + '@wordpress/is-shallow-equal': 4.57.0 + + '@wordpress/undo-manager@1.6.0': + dependencies: + '@babel/runtime': 7.25.0 + '@wordpress/is-shallow-equal': 5.6.0 '@wordpress/url@2.22.2(react-native@0.73.0(@babel/core@7.12.9)(@babel/preset-env@7.12.7(@babel/core@7.12.9))(encoding@0.1.13)(react@18.3.1))': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 lodash: 4.17.21 react-native-url-polyfill: 1.3.0(react-native@0.73.0(@babel/core@7.12.9)(@babel/preset-env@7.12.7(@babel/core@7.12.9))(encoding@0.1.13)(react@18.3.1)) transitivePeerDependencies: @@ -42575,7 +43123,7 @@ snapshots: '@wordpress/url@2.22.2(react-native@0.73.0(@babel/core@7.23.5)(@babel/preset-env@7.23.6(@babel/core@7.23.5))(encoding@0.1.13)(react@17.0.2))': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 lodash: 4.17.21 react-native-url-polyfill: 1.3.0(react-native@0.73.0(@babel/core@7.23.5)(@babel/preset-env@7.23.6(@babel/core@7.23.5))(encoding@0.1.13)(react@17.0.2)) transitivePeerDependencies: @@ -42583,7 +43131,7 @@ snapshots: '@wordpress/url@2.22.2(react-native@0.73.0(@babel/core@7.23.5)(@babel/preset-env@7.23.6(@babel/core@7.23.5))(encoding@0.1.13)(react@18.3.1))': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 lodash: 4.17.21 react-native-url-polyfill: 1.3.0(react-native@0.73.0(@babel/core@7.23.5)(@babel/preset-env@7.23.6(@babel/core@7.23.5))(encoding@0.1.13)(react@18.3.1)) transitivePeerDependencies: @@ -42606,12 +43154,12 @@ snapshots: '@wordpress/url@4.0.1': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 remove-accents: 0.5.0 '@wordpress/viewport@4.20.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/compose': 5.20.0(react@17.0.2) '@wordpress/data': 7.6.0(react@17.0.2) react: 17.0.2 @@ -42626,10 +43174,10 @@ snapshots: '@wordpress/viewport@5.24.0(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 - '@wordpress/compose': 6.24.0(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) - '@wordpress/element': 5.24.0 + '@babel/runtime': 7.25.0 + '@wordpress/compose': 6.34.0(react@17.0.2) + '@wordpress/data': 9.27.0(react@17.0.2) + '@wordpress/element': 5.34.0 react: 17.0.2 '@wordpress/warning@1.4.2': {} @@ -42640,18 +43188,20 @@ snapshots: '@wordpress/warning@2.6.1': {} - '@wordpress/widgets@3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@wordpress/warning@3.6.0': {} + + '@wordpress/widgets@3.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@wordpress/api-fetch': 6.44.0 - '@wordpress/block-editor': 12.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/block-editor': 12.15.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@wordpress/blocks': 12.24.0(react@17.0.2) - '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/compose': 6.24.0(react@17.0.2) - '@wordpress/core-data': 6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@wordpress/data': 9.17.0(react@17.0.2) - '@wordpress/element': 5.24.0 - '@wordpress/i18n': 4.47.0 + '@wordpress/components': 25.16.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/compose': 6.34.0(react@17.0.2) + '@wordpress/core-data': 6.24.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(@types/react@17.0.71)(babel-plugin-macros@3.1.0)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@wordpress/data': 9.27.0(react@17.0.2) + '@wordpress/element': 5.34.0 + '@wordpress/i18n': 4.57.0 '@wordpress/icons': 9.48.0 '@wordpress/notices': 4.15.0(react@17.0.2) classnames: 2.3.2 @@ -42676,10 +43226,10 @@ snapshots: dependencies: xstate: 4.37.1 - '@xstate/inspect@0.8.0(@types/ws@8.5.10)(ws@8.17.0)(xstate@4.37.1)': + '@xstate/inspect@0.8.0(@types/ws@8.5.10)(ws@8.18.0)(xstate@4.37.1)': dependencies: fast-safe-stringify: 2.1.1 - ws: 8.17.0 + ws: 8.18.0 xstate: 4.37.1 optionalDependencies: '@types/ws': 8.5.10 @@ -42786,6 +43336,8 @@ snapshots: acorn@8.11.3: {} + acorn@8.12.1: {} + address@1.2.2: {} adm-zip@0.5.10: {} @@ -42798,19 +43350,19 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.3.5 + debug: 4.3.6 transitivePeerDependencies: - supports-color agent-base@7.1.0: dependencies: - debug: 4.3.5 + debug: 4.3.6 transitivePeerDependencies: - supports-color agent-base@7.1.1: dependencies: - debug: 4.3.5 + debug: 4.3.6 transitivePeerDependencies: - supports-color @@ -43380,6 +43932,10 @@ snapshots: dependencies: '@babel/core': 7.24.7 + babel-core@7.0.0-bridge.0(@babel/core@7.25.2): + dependencies: + '@babel/core': 7.25.2 + babel-eslint@10.1.0(eslint@7.32.0): dependencies: '@babel/code-frame': 7.23.5 @@ -43779,13 +44335,13 @@ snapshots: babel-plugin-macros@2.8.0: dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 cosmiconfig: 6.0.0 resolve: 1.22.8 babel-plugin-macros@3.1.0: dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 cosmiconfig: 7.1.0 resolve: 1.22.8 @@ -43793,7 +44349,7 @@ snapshots: babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.12.9): dependencies: - '@babel/compat-data': 7.24.7 + '@babel/compat-data': 7.25.2 '@babel/core': 7.12.9 '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.12.9) semver: 6.3.1 @@ -43802,7 +44358,7 @@ snapshots: babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.23.5): dependencies: - '@babel/compat-data': 7.24.7 + '@babel/compat-data': 7.25.2 '@babel/core': 7.23.5 '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.23.5) semver: 6.3.1 @@ -43862,19 +44418,19 @@ snapshots: transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs3@0.10.4(@babel/core@7.12.9): + babel-plugin-polyfill-corejs3@0.10.6(@babel/core@7.12.9): dependencies: '@babel/core': 7.12.9 '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.12.9) - core-js-compat: 3.37.1 + core-js-compat: 3.38.0 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs3@0.10.4(@babel/core@7.23.5): + babel-plugin-polyfill-corejs3@0.10.6(@babel/core@7.23.5): dependencies: '@babel/core': 7.23.5 '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.23.5) - core-js-compat: 3.37.1 + core-js-compat: 3.38.0 transitivePeerDependencies: - supports-color @@ -44227,6 +44783,8 @@ snapshots: basic-ftp@5.0.3: {} + basic-ftp@5.0.5: {} + batch-processor@1.0.0: {} batch@0.6.1: {} @@ -44392,7 +44950,7 @@ snapshots: broadcast-channel@3.7.0: dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 detect-node: 2.1.0 js-sha3: 0.8.0 microseconds: 0.2.0 @@ -44489,6 +45047,13 @@ snapshots: node-releases: 2.0.14 update-browserslist-db: 1.0.16(browserslist@4.23.0) + browserslist@4.23.3: + dependencies: + caniuse-lite: 1.0.30001651 + electron-to-chromium: 1.5.9 + node-releases: 2.0.18 + update-browserslist-db: 1.1.0(browserslist@4.23.3) + bs-logger@0.2.6: dependencies: fast-json-stable-stringify: 2.1.0 @@ -44760,6 +45325,8 @@ snapshots: caniuse-lite@1.0.30001629: {} + caniuse-lite@1.0.30001651: {} + canvas-confetti@1.9.2: {} capital-case@1.0.4: @@ -45536,9 +46103,9 @@ snapshots: dependencies: browserslist: 4.22.2 - core-js-compat@3.37.1: + core-js-compat@3.38.0: dependencies: - browserslist: 4.23.0 + browserslist: 4.23.3 core-js-pure@3.34.0: {} @@ -45662,13 +46229,13 @@ snapshots: - supports-color - ts-node - create-jest@29.7.0(@types/node@20.14.2)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.3.3)): + create-jest@29.7.0(@types/node@22.4.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.4.0)(typescript@5.3.3)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.14.2)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.3.3)) + jest-config: 29.7.0(@types/node@22.4.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.4.0)(typescript@5.3.3)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -46168,6 +46735,8 @@ snapshots: data-uri-to-buffer@6.0.1: {} + data-uri-to-buffer@6.0.2: {} + data-urls@1.1.0: dependencies: abab: 2.0.6 @@ -46184,7 +46753,7 @@ snapshots: date-fns@2.30.0: dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 date-fns@3.6.0: {} @@ -46192,7 +46761,7 @@ snapshots: dateformat@4.6.3: {} - dayjs@1.11.11: {} + dayjs@1.11.12: {} debug@2.6.9(supports-color@6.1.0): dependencies: @@ -46240,6 +46809,10 @@ snapshots: dependencies: ms: 2.1.2 + debug@4.3.6: + dependencies: + ms: 2.1.2 + debuglog@1.0.1: {} decamelize-keys@1.1.1: @@ -46456,7 +47029,7 @@ snapshots: detect-port@1.5.1: dependencies: address: 1.2.2 - debug: 4.3.5 + debug: 4.3.6 transitivePeerDependencies: - supports-color @@ -46545,7 +47118,7 @@ snapshots: dom-helpers@5.2.1: dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 csstype: 3.1.3 dom-scroll-into-view@1.2.1: {} @@ -46706,6 +47279,8 @@ snapshots: electron-to-chromium@1.4.795: {} + electron-to-chromium@1.5.9: {} + element-resize-detector@1.2.4: dependencies: batch-processor: 1.0.0 @@ -47011,7 +47586,7 @@ snapshots: esbuild-register@3.5.0(esbuild@0.18.20): dependencies: - debug: 4.3.5 + debug: 4.3.6 esbuild: 0.18.20 transitivePeerDependencies: - supports-color @@ -47100,7 +47675,7 @@ snapshots: eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.56.0(eslint@8.55.0)(typescript@5.3.2))(eslint-import-resolver-webpack@0.13.2)(eslint-plugin-import@2.28.1)(eslint@8.55.0): dependencies: - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) enhanced-resolve: 5.15.0 eslint: 8.55.0 eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.56.0(eslint@8.55.0)(typescript@5.3.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.56.0(eslint@8.55.0)(typescript@5.3.2))(eslint-import-resolver-webpack@0.13.2)(eslint-plugin-import@2.28.1)(eslint@8.55.0))(eslint-import-resolver-webpack@0.13.2(eslint-plugin-import@2.28.1)(webpack@5.91.0(@swc/core@1.3.100)(esbuild@0.18.20)(webpack-cli@5.1.4)))(eslint@8.55.0) @@ -47117,7 +47692,7 @@ snapshots: eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.55.0)(typescript@5.3.3))(eslint-import-resolver-webpack@0.13.8)(eslint-plugin-import@2.29.0)(eslint@8.55.0): dependencies: - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) enhanced-resolve: 5.15.0 eslint: 8.55.0 eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0(eslint@8.55.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.55.0)(typescript@5.3.3))(eslint-import-resolver-webpack@0.13.8)(eslint-plugin-import@2.29.0)(eslint@8.55.0))(eslint-import-resolver-webpack@0.13.8(eslint-plugin-import@2.29.0)(webpack@5.89.0(webpack-cli@4.10.0)))(eslint@8.55.0) @@ -47396,7 +47971,7 @@ snapshots: eslint-plugin-jsdoc@30.7.13(eslint@7.32.0): dependencies: comment-parser: 0.7.6 - debug: 4.3.5 + debug: 4.3.6 eslint: 7.32.0 jsdoctypeparser: 9.0.0 lodash: 4.17.21 @@ -47410,7 +47985,7 @@ snapshots: dependencies: '@es-joy/jsdoccomment': 0.10.8 comment-parser: 1.2.4 - debug: 4.3.5 + debug: 4.3.6 eslint: 7.32.0 esquery: 1.5.0 jsdoc-type-pratt-parser: 1.2.0 @@ -47425,7 +48000,7 @@ snapshots: dependencies: '@es-joy/jsdoccomment': 0.20.1 comment-parser: 1.3.0 - debug: 4.3.5 + debug: 4.3.6 escape-string-regexp: 4.0.0 eslint: 8.55.0 esquery: 1.5.0 @@ -47439,7 +48014,7 @@ snapshots: dependencies: '@es-joy/jsdoccomment': 0.36.1 comment-parser: 1.3.1 - debug: 4.3.5 + debug: 4.3.6 escape-string-regexp: 4.0.0 eslint: 8.55.0 esquery: 1.5.0 @@ -47450,7 +48025,7 @@ snapshots: eslint-plugin-jsx-a11y@6.8.0(eslint@7.32.0): dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 aria-query: 5.3.0 array-includes: 3.1.7 array.prototype.flatmap: 1.3.2 @@ -47470,7 +48045,7 @@ snapshots: eslint-plugin-jsx-a11y@6.8.0(eslint@8.55.0): dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 aria-query: 5.3.0 array-includes: 3.1.7 array.prototype.flatmap: 1.3.2 @@ -47668,7 +48243,7 @@ snapshots: ajv: 6.12.6 chalk: 2.4.2 cross-spawn: 6.0.5 - debug: 4.3.5 + debug: 4.3.6 doctrine: 3.0.0 eslint-scope: 4.0.3 eslint-utils: 1.4.3 @@ -47803,7 +48378,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -48144,7 +48719,7 @@ snapshots: extract-zip@2.0.1: dependencies: - debug: 4.3.5 + debug: 4.3.6 get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -48201,7 +48776,7 @@ snapshots: fast-sort@3.4.0: {} - fast-xml-parser@4.2.5: + fast-xml-parser@4.4.1: dependencies: strnum: 1.0.5 @@ -48410,7 +48985,7 @@ snapshots: dependencies: chalk: 4.1.2 commander: 5.1.0 - debug: 4.3.5 + debug: 4.3.6 transitivePeerDependencies: - supports-color @@ -48531,7 +49106,7 @@ snapshots: follow-redirects@1.15.6(debug@4.3.4): optionalDependencies: - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) follow-redirects@1.5.10: dependencies: @@ -48752,6 +49327,14 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + framer-motion@11.3.30(@emotion/is-prop-valid@1.2.1)(react-dom@17.0.2(react@17.0.2))(react@17.0.2): + dependencies: + tslib: 2.6.3 + optionalDependencies: + '@emotion/is-prop-valid': 1.2.1 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + framer-motion@6.5.1(react-dom@17.0.2(react@17.0.2))(react@17.0.2): dependencies: '@motionone/dom': 10.12.0 @@ -48818,6 +49401,12 @@ snapshots: jsonfile: 6.1.0 universalify: 2.0.1 + fs-extra@11.2.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + fs-extra@6.0.1: dependencies: graceful-fs: 4.2.11 @@ -48981,11 +49570,20 @@ snapshots: dependencies: basic-ftp: 5.0.3 data-uri-to-buffer: 6.0.1 - debug: 4.3.5 + debug: 4.3.6 fs-extra: 8.1.0 transitivePeerDependencies: - supports-color + get-uri@6.0.3: + dependencies: + basic-ftp: 5.0.5 + data-uri-to-buffer: 6.0.2 + debug: 4.3.6 + fs-extra: 11.2.0 + transitivePeerDependencies: + - supports-color + get-value@2.0.6: {} getobject@1.0.2: {} @@ -49013,7 +49611,7 @@ snapshots: dependencies: colorette: 2.0.20 defu: 6.1.3 - https-proxy-agent: 7.0.4 + https-proxy-agent: 7.0.5 mri: 1.2.0 node-fetch-native: 1.4.1 pathe: 1.1.1 @@ -49661,15 +50259,15 @@ snapshots: hermes-estree@0.15.0: {} - hermes-estree@0.20.1: {} + hermes-estree@0.23.0: {} hermes-parser@0.15.0: dependencies: hermes-estree: 0.15.0 - hermes-parser@0.20.1: + hermes-parser@0.23.0: dependencies: - hermes-estree: 0.20.1 + hermes-estree: 0.23.0 hermes-profile-transformer@0.0.6: dependencies: @@ -49865,7 +50463,7 @@ snapshots: http-call@5.3.0: dependencies: content-type: 1.0.5 - debug: 4.3.5 + debug: 4.3.6 is-retry-allowed: 1.2.0 is-stream: 2.0.1 parse-json: 4.0.0 @@ -49898,7 +50496,7 @@ snapshots: dependencies: '@tootallnate/once': 1.1.2 agent-base: 6.0.2 - debug: 4.3.5 + debug: 4.3.6 transitivePeerDependencies: - supports-color @@ -49906,14 +50504,14 @@ snapshots: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.3.5 + debug: 4.3.6 transitivePeerDependencies: - supports-color http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.1 - debug: 4.3.5 + debug: 4.3.6 transitivePeerDependencies: - supports-color @@ -49974,21 +50572,21 @@ snapshots: https-proxy-agent@5.0.0: dependencies: agent-base: 6.0.2 - debug: 4.3.5 + debug: 4.3.6 transitivePeerDependencies: - supports-color https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.3.5 + debug: 4.3.6 transitivePeerDependencies: - supports-color - https-proxy-agent@7.0.4: + https-proxy-agent@7.0.5: dependencies: agent-base: 7.1.0 - debug: 4.3.5 + debug: 4.3.6 transitivePeerDependencies: - supports-color @@ -50008,7 +50606,7 @@ snapshots: i18n-calypso@5.0.0(react@17.0.2): dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@tannin/sprintf': 1.2.0 '@wordpress/compose': 3.25.3(react@17.0.2) debug: 4.3.5 @@ -50026,7 +50624,7 @@ snapshots: i18n-calypso@7.0.0(@types/react@17.0.71)(react@17.0.2): dependencies: '@automattic/interpolate-components': 1.2.1(@types/react@17.0.71)(react@17.0.2) - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@tannin/sprintf': 1.2.0 '@wordpress/compose': 6.34.0(react@17.0.2) debug: 4.3.5 @@ -50620,9 +51218,9 @@ snapshots: transitivePeerDependencies: - encoding - isomorphic-ws@5.0.0(ws@8.17.0): + isomorphic-ws@5.0.0(ws@8.18.0): dependencies: - ws: 8.17.0 + ws: 8.18.0 isomorphic.js@0.2.5: {} @@ -50738,7 +51336,7 @@ snapshots: istanbul-lib-source-maps@3.0.6: dependencies: - debug: 4.3.5 + debug: 4.3.6 istanbul-lib-coverage: 2.0.5 make-dir: 2.1.0 rimraf: 2.7.1 @@ -50748,7 +51346,7 @@ snapshots: istanbul-lib-source-maps@4.0.1: dependencies: - debug: 4.3.5 + debug: 4.3.6 istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: @@ -51079,16 +51677,16 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(@types/node@20.14.2)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.3.3)): + jest-cli@29.7.0(@types/node@22.4.0)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.4.0)(typescript@5.3.3)): dependencies: - '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.3.3)) + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.4.0)(typescript@5.3.3)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.14.2)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.3.3)) + create-jest: 29.7.0(@types/node@22.4.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.4.0)(typescript@5.3.3)) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.14.2)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.3.3)) + jest-config: 29.7.0(@types/node@22.4.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.4.0)(typescript@5.3.3)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -51278,7 +51876,7 @@ snapshots: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@16.18.68)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.3.3)): + jest-config@29.7.0(@types/node@16.18.68)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.4.0)(typescript@5.3.3)): dependencies: '@babel/core': 7.24.7 '@jest/test-sequencer': 29.7.0 @@ -51304,12 +51902,12 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 16.18.68 - ts-node: 10.9.2(@types/node@20.14.2)(typescript@5.3.3) + ts-node: 10.9.2(@types/node@22.4.0)(typescript@5.3.3) transitivePeerDependencies: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.14.2)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.3.3)): + jest-config@29.7.0(@types/node@22.4.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.4.0)(typescript@5.3.3)): dependencies: '@babel/core': 7.24.7 '@jest/test-sequencer': 29.7.0 @@ -51334,8 +51932,8 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 20.14.2 - ts-node: 10.9.2(@types/node@20.14.2)(typescript@5.3.3) + '@types/node': 22.4.0 + ts-node: 10.9.2(@types/node@22.4.0)(typescript@5.3.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -51739,7 +52337,9 @@ snapshots: pretty-format: 24.9.0 throat: 4.1.0 transitivePeerDependencies: + - bufferutil - supports-color + - utf-8-validate jest-jasmine2@25.5.4: dependencies: @@ -52786,12 +53386,12 @@ snapshots: - supports-color - ts-node - jest@29.7.0(@types/node@20.14.2)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.3.3)): + jest@29.7.0(@types/node@22.4.0)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.4.0)(typescript@5.3.3)): dependencies: - '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.3.3)) + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.4.0)(typescript@5.3.3)) '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.14.2)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.3.3)) + jest-cli: 29.7.0(@types/node@22.4.0)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.4.0)(typescript@5.3.3)) optionalDependencies: node-notifier: 8.0.2 transitivePeerDependencies: @@ -52810,7 +53410,7 @@ snapshots: '@sideway/formula': 3.0.1 '@sideway/pinpoint': 2.0.0 - joi@17.13.1: + joi@17.13.3: dependencies: '@hapi/hoek': 9.3.0 '@hapi/topo': 5.1.0 @@ -52890,17 +53490,17 @@ snapshots: jscodeshift@0.14.0(@babel/preset-env@7.12.7(@babel/core@7.12.9)): dependencies: - '@babel/core': 7.24.7 - '@babel/parser': 7.24.7 - '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.24.7) - '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.24.7) - '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.24.7) - '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.24.7) + '@babel/core': 7.25.2 + '@babel/parser': 7.25.3 + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.25.2) + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.25.2) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.25.2) + '@babel/plugin-transform-modules-commonjs': 7.24.8(@babel/core@7.25.2) '@babel/preset-env': 7.12.7(@babel/core@7.12.9) - '@babel/preset-flow': 7.24.7(@babel/core@7.24.7) - '@babel/preset-typescript': 7.24.7(@babel/core@7.24.7) - '@babel/register': 7.24.6(@babel/core@7.24.7) - babel-core: 7.0.0-bridge.0(@babel/core@7.24.7) + '@babel/preset-flow': 7.24.7(@babel/core@7.25.2) + '@babel/preset-typescript': 7.24.7(@babel/core@7.25.2) + '@babel/register': 7.24.6(@babel/core@7.25.2) + babel-core: 7.0.0-bridge.0(@babel/core@7.25.2) chalk: 4.1.2 flow-parser: 0.223.3 graceful-fs: 4.2.11 @@ -52915,17 +53515,17 @@ snapshots: jscodeshift@0.14.0(@babel/preset-env@7.23.6(@babel/core@7.23.5)): dependencies: - '@babel/core': 7.24.7 - '@babel/parser': 7.24.7 - '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.24.7) - '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.24.7) - '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.24.7) - '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.24.7) + '@babel/core': 7.25.2 + '@babel/parser': 7.25.3 + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.25.2) + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.25.2) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.25.2) + '@babel/plugin-transform-modules-commonjs': 7.24.8(@babel/core@7.25.2) '@babel/preset-env': 7.23.6(@babel/core@7.23.5) - '@babel/preset-flow': 7.24.7(@babel/core@7.24.7) - '@babel/preset-typescript': 7.24.7(@babel/core@7.24.7) - '@babel/register': 7.24.6(@babel/core@7.24.7) - babel-core: 7.0.0-bridge.0(@babel/core@7.24.7) + '@babel/preset-flow': 7.24.7(@babel/core@7.25.2) + '@babel/preset-typescript': 7.24.7(@babel/core@7.25.2) + '@babel/register': 7.24.6(@babel/core@7.25.2) + babel-core: 7.0.0-bridge.0(@babel/core@7.25.2) chalk: 4.1.2 flow-parser: 0.223.3 graceful-fs: 4.2.11 @@ -53001,7 +53601,7 @@ snapshots: whatwg-encoding: 1.0.5 whatwg-mimetype: 2.3.0 whatwg-url: 6.5.0 - ws: 5.2.3 + ws: 5.2.4 xml-name-validator: 3.0.0 transitivePeerDependencies: - bufferutil @@ -53033,7 +53633,7 @@ snapshots: whatwg-encoding: 1.0.5 whatwg-mimetype: 2.3.0 whatwg-url: 7.1.0 - ws: 7.5.9 + ws: 7.5.10 xml-name-validator: 3.0.0 transitivePeerDependencies: - bufferutil @@ -53066,7 +53666,7 @@ snapshots: whatwg-encoding: 1.0.5 whatwg-mimetype: 2.3.0 whatwg-url: 8.7.0 - ws: 7.5.9 + ws: 7.5.10 xml-name-validator: 3.0.0 transitivePeerDependencies: - bufferutil @@ -53237,7 +53837,7 @@ snapshots: lazy-universal-dotenv@3.0.1: dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 app-root-dir: 1.0.2 core-js: 3.34.0 dotenv: 8.6.0 @@ -53411,7 +54011,7 @@ snapshots: chalk: 5.2.0 cli-truncate: 3.1.0 commander: 10.0.1 - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) execa: 7.2.0 lilconfig: 2.1.0 listr2: 5.0.8(enquirer@2.4.1) @@ -53653,7 +54253,7 @@ snapshots: logkitty@0.7.1: dependencies: ansi-fragments: 0.2.1 - dayjs: 1.11.11 + dayjs: 1.11.12 yargs: 15.4.1 lolex@5.1.2: @@ -53940,7 +54540,7 @@ snapshots: match-sorter@6.3.1: dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 remove-accents: 0.4.2 mathml-tag-names@2.1.3: {} @@ -54147,46 +54747,53 @@ snapshots: methods@1.1.2: {} - metro-babel-transformer@0.80.9: + metro-babel-transformer@0.80.10: dependencies: - '@babel/core': 7.24.7 - hermes-parser: 0.20.1 + '@babel/core': 7.25.2 + flow-enums-runtime: 0.0.6 + hermes-parser: 0.23.0 nullthrows: 1.1.1 transitivePeerDependencies: - supports-color - metro-cache-key@0.80.9: {} - - metro-cache@0.80.9: + metro-cache-key@0.80.10: dependencies: - metro-core: 0.80.9 - rimraf: 3.0.2 + flow-enums-runtime: 0.0.6 - metro-config@0.80.9(encoding@0.1.13): + metro-cache@0.80.10: + dependencies: + exponential-backoff: 3.1.1 + flow-enums-runtime: 0.0.6 + metro-core: 0.80.10 + + metro-config@0.80.10(encoding@0.1.13): dependencies: connect: 3.7.0 cosmiconfig: 5.2.1 + flow-enums-runtime: 0.0.6 jest-validate: 29.7.0 - metro: 0.80.9(encoding@0.1.13) - metro-cache: 0.80.9 - metro-core: 0.80.9 - metro-runtime: 0.80.9 + metro: 0.80.10(encoding@0.1.13) + metro-cache: 0.80.10 + metro-core: 0.80.10 + metro-runtime: 0.80.10 transitivePeerDependencies: - bufferutil - encoding - supports-color - utf-8-validate - metro-core@0.80.9: + metro-core@0.80.10: dependencies: + flow-enums-runtime: 0.0.6 lodash.throttle: 4.1.1 - metro-resolver: 0.80.9 + metro-resolver: 0.80.10 - metro-file-map@0.80.9: + metro-file-map@0.80.10: dependencies: anymatch: 3.1.3 debug: 2.6.9(supports-color@6.1.0) fb-watchman: 2.0.2 + flow-enums-runtime: 0.0.6 graceful-fs: 4.2.11 invariant: 2.2.4 jest-worker: 29.7.0 @@ -54199,33 +54806,39 @@ snapshots: transitivePeerDependencies: - supports-color - metro-minify-terser@0.80.9: + metro-minify-terser@0.80.10: dependencies: - terser: 5.31.1 + flow-enums-runtime: 0.0.6 + terser: 5.31.6 - metro-resolver@0.80.9: {} - - metro-runtime@0.80.9: + metro-resolver@0.80.10: dependencies: - '@babel/runtime': 7.24.7 + flow-enums-runtime: 0.0.6 - metro-source-map@0.80.9: + metro-runtime@0.80.10: dependencies: - '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 + '@babel/runtime': 7.25.0 + flow-enums-runtime: 0.0.6 + + metro-source-map@0.80.10: + dependencies: + '@babel/traverse': 7.25.3 + '@babel/types': 7.25.2 + flow-enums-runtime: 0.0.6 invariant: 2.2.4 - metro-symbolicate: 0.80.9 + metro-symbolicate: 0.80.10 nullthrows: 1.1.1 - ob1: 0.80.9 + ob1: 0.80.10 source-map: 0.5.7 vlq: 1.0.1 transitivePeerDependencies: - supports-color - metro-symbolicate@0.80.9: + metro-symbolicate@0.80.10: dependencies: + flow-enums-runtime: 0.0.6 invariant: 2.2.4 - metro-source-map: 0.80.9 + metro-source-map: 0.80.10 nullthrows: 1.1.1 source-map: 0.5.7 through2: 2.0.5 @@ -54233,29 +54846,31 @@ snapshots: transitivePeerDependencies: - supports-color - metro-transform-plugins@0.80.9: + metro-transform-plugins@0.80.10: dependencies: - '@babel/core': 7.24.7 - '@babel/generator': 7.24.7 - '@babel/template': 7.24.7 - '@babel/traverse': 7.24.7 + '@babel/core': 7.25.2 + '@babel/generator': 7.25.0 + '@babel/template': 7.25.0 + '@babel/traverse': 7.25.3 + flow-enums-runtime: 0.0.6 nullthrows: 1.1.1 transitivePeerDependencies: - supports-color - metro-transform-worker@0.80.9(encoding@0.1.13): + metro-transform-worker@0.80.10(encoding@0.1.13): dependencies: - '@babel/core': 7.24.7 - '@babel/generator': 7.24.7 - '@babel/parser': 7.24.7 - '@babel/types': 7.24.7 - metro: 0.80.9(encoding@0.1.13) - metro-babel-transformer: 0.80.9 - metro-cache: 0.80.9 - metro-cache-key: 0.80.9 - metro-minify-terser: 0.80.9 - metro-source-map: 0.80.9 - metro-transform-plugins: 0.80.9 + '@babel/core': 7.25.2 + '@babel/generator': 7.25.0 + '@babel/parser': 7.25.3 + '@babel/types': 7.25.2 + flow-enums-runtime: 0.0.6 + metro: 0.80.10(encoding@0.1.13) + metro-babel-transformer: 0.80.10 + metro-cache: 0.80.10 + metro-cache-key: 0.80.10 + metro-minify-terser: 0.80.10 + metro-source-map: 0.80.10 + metro-transform-plugins: 0.80.10 nullthrows: 1.1.1 transitivePeerDependencies: - bufferutil @@ -54263,15 +54878,15 @@ snapshots: - supports-color - utf-8-validate - metro@0.80.9(encoding@0.1.13): + metro@0.80.10(encoding@0.1.13): dependencies: '@babel/code-frame': 7.24.7 - '@babel/core': 7.24.7 - '@babel/generator': 7.24.7 - '@babel/parser': 7.24.7 - '@babel/template': 7.24.7 - '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 + '@babel/core': 7.25.2 + '@babel/generator': 7.25.0 + '@babel/parser': 7.25.3 + '@babel/template': 7.25.0 + '@babel/traverse': 7.25.3 + '@babel/types': 7.25.2 accepts: 1.3.8 chalk: 4.1.2 ci-info: 2.0.0 @@ -54279,34 +54894,34 @@ snapshots: debug: 2.6.9(supports-color@6.1.0) denodeify: 1.2.1 error-stack-parser: 2.1.4 + flow-enums-runtime: 0.0.6 graceful-fs: 4.2.11 - hermes-parser: 0.20.1 + hermes-parser: 0.23.0 image-size: 1.1.1 invariant: 2.2.4 jest-worker: 29.7.0 jsc-safe-url: 0.2.4 lodash.throttle: 4.1.1 - metro-babel-transformer: 0.80.9 - metro-cache: 0.80.9 - metro-cache-key: 0.80.9 - metro-config: 0.80.9(encoding@0.1.13) - metro-core: 0.80.9 - metro-file-map: 0.80.9 - metro-resolver: 0.80.9 - metro-runtime: 0.80.9 - metro-source-map: 0.80.9 - metro-symbolicate: 0.80.9 - metro-transform-plugins: 0.80.9 - metro-transform-worker: 0.80.9(encoding@0.1.13) + metro-babel-transformer: 0.80.10 + metro-cache: 0.80.10 + metro-cache-key: 0.80.10 + metro-config: 0.80.10(encoding@0.1.13) + metro-core: 0.80.10 + metro-file-map: 0.80.10 + metro-resolver: 0.80.10 + metro-runtime: 0.80.10 + metro-source-map: 0.80.10 + metro-symbolicate: 0.80.10 + metro-transform-plugins: 0.80.10 + metro-transform-worker: 0.80.10(encoding@0.1.13) mime-types: 2.1.35 node-fetch: 2.7.0(encoding@0.1.13) nullthrows: 1.1.1 - rimraf: 3.0.2 serialize-error: 2.1.0 source-map: 0.5.7 strip-ansi: 6.0.1 throat: 5.0.0 - ws: 7.5.9 + ws: 7.5.10 yargs: 17.7.2 transitivePeerDependencies: - bufferutil @@ -54318,7 +54933,7 @@ snapshots: micromark@2.11.4: dependencies: - debug: 4.3.5 + debug: 4.3.6 parse-entities: 2.0.0 transitivePeerDependencies: - supports-color @@ -54700,7 +55315,7 @@ snapshots: dependencies: carlo: 0.9.46 chokidar: 3.5.3 - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) isbinaryfile: 3.0.3 mime: 2.6.0 opn: 5.5.0 @@ -54870,6 +55485,8 @@ snapshots: node-releases@2.0.14: {} + node-releases@2.0.18: {} + node-stream-zip@1.15.0: {} node-watch@0.7.4: {} @@ -55109,7 +55726,9 @@ snapshots: oauth-sign@0.9.0: {} - ob1@0.80.9: {} + ob1@0.80.10: + dependencies: + flow-enums-runtime: 0.0.6 object-assign@4.1.1: {} @@ -55224,7 +55843,7 @@ snapshots: '@oclif/plugin-warn-if-update-available': 2.1.1(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.3) aws-sdk: 2.1515.0 concurrently: 7.6.0 - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) find-yarn-workspace-root: 2.0.0 fs-extra: 8.1.0 github-slugger: 1.5.0 @@ -55453,7 +56072,7 @@ snapshots: p-transform@1.3.0: dependencies: - debug: 4.3.5 + debug: 4.3.6 p-queue: 6.6.2 transitivePeerDependencies: - supports-color @@ -55464,21 +56083,39 @@ snapshots: dependencies: '@tootallnate/quickjs-emscripten': 0.23.0 agent-base: 7.1.1 - debug: 4.3.5 + debug: 4.3.6 get-uri: 6.0.2 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.4 + https-proxy-agent: 7.0.5 pac-resolver: 7.0.0 socks-proxy-agent: 8.0.3 transitivePeerDependencies: - supports-color + pac-proxy-agent@7.0.2: + dependencies: + '@tootallnate/quickjs-emscripten': 0.23.0 + agent-base: 7.1.1 + debug: 4.3.6 + get-uri: 6.0.3 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.5 + pac-resolver: 7.0.1 + socks-proxy-agent: 8.0.4 + transitivePeerDependencies: + - supports-color + pac-resolver@7.0.0: dependencies: degenerator: 5.0.1 ip: 1.1.9 netmask: 2.0.2 + pac-resolver@7.0.1: + dependencies: + degenerator: 5.0.1 + netmask: 2.0.2 + package-json@4.0.1: dependencies: got: 6.7.1 @@ -55761,9 +56398,9 @@ snapshots: photon@4.0.0: dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 crc32: 0.2.2 - debug: 4.3.5 + debug: 4.3.6 seed-random: 2.2.0 transitivePeerDependencies: - supports-color @@ -55812,11 +56449,11 @@ snapshots: dependencies: find-up: 6.3.0 - playwright-core@1.45.1: {} + playwright-core@1.46.1: {} - playwright@1.45.1: + playwright@1.46.1: dependencies: - playwright-core: 1.45.1 + playwright-core: 1.46.1 optionalDependencies: fsevents: 2.3.2 @@ -55834,7 +56471,7 @@ snapshots: polished@4.2.2: dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 popmotion@11.0.3: dependencies: @@ -56611,9 +57248,9 @@ snapshots: proxy-agent@6.3.0: dependencies: agent-base: 7.1.1 - debug: 4.3.5 + debug: 4.3.6 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.4 + https-proxy-agent: 7.0.5 lru-cache: 7.18.3 pac-proxy-agent: 7.0.1 proxy-from-env: 1.1.0 @@ -56624,13 +57261,13 @@ snapshots: proxy-agent@6.3.1: dependencies: agent-base: 7.1.1 - debug: 4.3.5 + debug: 4.3.6 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.4 + https-proxy-agent: 7.0.5 lru-cache: 7.18.3 - pac-proxy-agent: 7.0.1 + pac-proxy-agent: 7.0.2 proxy-from-env: 1.1.0 - socks-proxy-agent: 8.0.3 + socks-proxy-agent: 8.0.4 transitivePeerDependencies: - supports-color @@ -56683,14 +57320,14 @@ snapshots: puppeteer-core@1.12.2: dependencies: - debug: 4.3.5 + debug: 4.3.6 extract-zip: 1.7.0 https-proxy-agent: 2.2.4 mime: 2.6.0 progress: 2.0.3 proxy-from-env: 1.1.0 rimraf: 2.7.1 - ws: 6.2.2 + ws: 6.2.3 transitivePeerDependencies: - bufferutil - supports-color @@ -56718,7 +57355,7 @@ snapshots: puppeteer-core@13.7.0(encoding@0.1.13): dependencies: cross-fetch: 3.1.5(encoding@0.1.13) - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) devtools-protocol: 0.0.981744 extract-zip: 2.0.1 https-proxy-agent: 5.0.1 @@ -56738,7 +57375,7 @@ snapshots: puppeteer-core@2.1.1: dependencies: '@types/mime-types': 2.1.4 - debug: 4.3.5 + debug: 4.3.6 extract-zip: 1.7.0 https-proxy-agent: 4.0.0 mime: 2.6.0 @@ -56746,7 +57383,7 @@ snapshots: progress: 2.0.3 proxy-from-env: 1.1.0 rimraf: 2.7.1 - ws: 6.2.2 + ws: 6.2.3 transitivePeerDependencies: - bufferutil - supports-color @@ -56757,7 +57394,7 @@ snapshots: '@puppeteer/browsers': 1.4.6(typescript@5.3.2) chromium-bidi: 0.4.16(devtools-protocol@0.0.1147663) cross-fetch: 4.0.0(encoding@0.1.13) - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) devtools-protocol: 0.0.1147663 ws: 8.13.0 optionalDependencies: @@ -56773,7 +57410,7 @@ snapshots: '@puppeteer/browsers': 1.4.6(typescript@5.3.3) chromium-bidi: 0.4.16(devtools-protocol@0.0.1147663) cross-fetch: 4.0.0(encoding@0.1.13) - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) devtools-protocol: 0.0.1147663 ws: 8.13.0 optionalDependencies: @@ -56789,7 +57426,7 @@ snapshots: '@puppeteer/browsers': 1.9.0 chromium-bidi: 0.5.1(devtools-protocol@0.0.1203626) cross-fetch: 4.0.0(encoding@0.1.13) - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) devtools-protocol: 0.0.1203626 ws: 8.14.2 transitivePeerDependencies: @@ -56801,7 +57438,7 @@ snapshots: puppeteer-core@3.0.0: dependencies: '@types/mime-types': 2.1.4 - debug: 4.3.5 + debug: 4.3.6 extract-zip: 2.0.1 https-proxy-agent: 4.0.0 mime: 2.6.0 @@ -56811,7 +57448,7 @@ snapshots: rimraf: 3.0.2 tar-fs: 2.1.1 unbzip2-stream: 1.4.3 - ws: 7.5.9 + ws: 7.5.10 transitivePeerDependencies: - bufferutil - supports-color @@ -56825,7 +57462,7 @@ snapshots: puppeteer@17.1.3(encoding@0.1.13): dependencies: cross-fetch: 3.1.5(encoding@0.1.13) - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) devtools-protocol: 0.0.1036444 extract-zip: 2.0.1 https-proxy-agent: 5.0.1 @@ -56865,7 +57502,7 @@ snapshots: qqjs@0.3.11: dependencies: chalk: 2.4.2 - debug: 4.3.5 + debug: 4.3.6 execa: 0.10.0 fs-extra: 6.0.1 get-stream: 5.2.0 @@ -57152,14 +57789,14 @@ snapshots: react-devtools-core@4.28.5: dependencies: shell-quote: 1.8.1 - ws: 7.5.9 + ws: 7.5.10 transitivePeerDependencies: - bufferutil - utf-8-validate react-docgen-typescript-plugin@1.0.5(typescript@5.3.2)(webpack@5.91.0(@swc/core@1.3.100)(esbuild@0.18.20)(webpack-cli@5.1.4)): dependencies: - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) endent: 2.1.0 find-cache-dir: 3.3.2 flat-cache: 3.2.0 @@ -57183,7 +57820,7 @@ snapshots: dependencies: '@babel/core': 7.24.7 '@babel/generator': 7.24.7 - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 ast-types: 0.14.2 commander: 2.20.3 doctrine: 3.0.0 @@ -57280,6 +57917,14 @@ snapshots: react-dom: 17.0.2(react@17.0.2) react-is: 17.0.2 + react-element-to-jsx-string@14.3.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@base2/pretty-print-object': 1.0.1 + is-plain-object: 5.0.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 17.0.2 + react-element-to-jsx-string@15.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@base2/pretty-print-object': 1.0.1 @@ -57305,12 +57950,24 @@ snapshots: prop-types: 15.8.1 react: 17.0.2 + react-input-autosize@3.0.0(react@18.3.1): + dependencies: + prop-types: 15.8.1 + react: 18.3.1 + react-inspector@5.1.1(react@17.0.2): + dependencies: + '@babel/runtime': 7.25.0 + is-dom: 1.1.0 + prop-types: 15.8.1 + react: 17.0.2 + + react-inspector@5.1.1(react@18.3.1): dependencies: '@babel/runtime': 7.24.7 is-dom: 1.1.0 prop-types: 15.8.1 - react: 17.0.2 + react: 18.3.1 react-inspector@6.0.2(react@18.3.1): dependencies: @@ -57364,7 +58021,7 @@ snapshots: '@react-native-community/cli-platform-ios': 12.1.1(encoding@0.1.13) '@react-native/assets-registry': 0.73.1 '@react-native/codegen': 0.73.3(@babel/preset-env@7.12.7(@babel/core@7.12.9)) - '@react-native/community-cli-plugin': 0.73.17(@babel/core@7.12.9)(@babel/preset-env@7.12.7(@babel/core@7.12.9))(encoding@0.1.13) + '@react-native/community-cli-plugin': 0.73.18(@babel/core@7.12.9)(@babel/preset-env@7.12.7(@babel/core@7.12.9))(encoding@0.1.13) '@react-native/gradle-plugin': 0.73.4 '@react-native/js-polyfills': 0.73.1 '@react-native/normalize-colors': 0.73.2 @@ -57380,8 +58037,8 @@ snapshots: jest-environment-node: 29.7.0 jsc-android: 250231.0.0 memoize-one: 5.2.1 - metro-runtime: 0.80.9 - metro-source-map: 0.80.9 + metro-runtime: 0.80.10 + metro-source-map: 0.80.10 mkdirp: 0.5.6 nullthrows: 1.1.1 pretty-format: 26.6.2 @@ -57394,7 +58051,7 @@ snapshots: scheduler: 0.24.0-canary-efb381bbf-20230505 stacktrace-parser: 0.1.10 whatwg-fetch: 3.6.20 - ws: 6.2.2 + ws: 6.2.3 yargs: 17.7.2 transitivePeerDependencies: - '@babel/core' @@ -57412,7 +58069,7 @@ snapshots: '@react-native-community/cli-platform-ios': 12.1.1(encoding@0.1.13) '@react-native/assets-registry': 0.73.1 '@react-native/codegen': 0.73.3(@babel/preset-env@7.23.6(@babel/core@7.23.5)) - '@react-native/community-cli-plugin': 0.73.17(@babel/core@7.23.5)(@babel/preset-env@7.23.6(@babel/core@7.23.5))(encoding@0.1.13) + '@react-native/community-cli-plugin': 0.73.18(@babel/core@7.23.5)(@babel/preset-env@7.23.6(@babel/core@7.23.5))(encoding@0.1.13) '@react-native/gradle-plugin': 0.73.4 '@react-native/js-polyfills': 0.73.1 '@react-native/normalize-colors': 0.73.2 @@ -57428,8 +58085,8 @@ snapshots: jest-environment-node: 29.7.0 jsc-android: 250231.0.0 memoize-one: 5.2.1 - metro-runtime: 0.80.9 - metro-source-map: 0.80.9 + metro-runtime: 0.80.10 + metro-source-map: 0.80.10 mkdirp: 0.5.6 nullthrows: 1.1.1 pretty-format: 26.6.2 @@ -57442,7 +58099,7 @@ snapshots: scheduler: 0.24.0-canary-efb381bbf-20230505 stacktrace-parser: 0.1.10 whatwg-fetch: 3.6.20 - ws: 6.2.2 + ws: 6.2.3 yargs: 17.7.2 transitivePeerDependencies: - '@babel/core' @@ -57460,7 +58117,7 @@ snapshots: '@react-native-community/cli-platform-ios': 12.1.1(encoding@0.1.13) '@react-native/assets-registry': 0.73.1 '@react-native/codegen': 0.73.3(@babel/preset-env@7.23.6(@babel/core@7.23.5)) - '@react-native/community-cli-plugin': 0.73.17(@babel/core@7.23.5)(@babel/preset-env@7.23.6(@babel/core@7.23.5))(encoding@0.1.13) + '@react-native/community-cli-plugin': 0.73.18(@babel/core@7.23.5)(@babel/preset-env@7.23.6(@babel/core@7.23.5))(encoding@0.1.13) '@react-native/gradle-plugin': 0.73.4 '@react-native/js-polyfills': 0.73.1 '@react-native/normalize-colors': 0.73.2 @@ -57476,8 +58133,8 @@ snapshots: jest-environment-node: 29.7.0 jsc-android: 250231.0.0 memoize-one: 5.2.1 - metro-runtime: 0.80.9 - metro-source-map: 0.80.9 + metro-runtime: 0.80.10 + metro-source-map: 0.80.10 mkdirp: 0.5.6 nullthrows: 1.1.1 pretty-format: 26.6.2 @@ -57490,7 +58147,7 @@ snapshots: scheduler: 0.24.0-canary-efb381bbf-20230505 stacktrace-parser: 0.1.10 whatwg-fetch: 3.6.20 - ws: 6.2.2 + ws: 6.2.3 yargs: 17.7.2 transitivePeerDependencies: - '@babel/core' @@ -57688,7 +58345,7 @@ snapshots: react-select@3.2.0(react-dom@17.0.2(react@17.0.2))(react@17.0.2): dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 '@emotion/cache': 10.0.29 '@emotion/core': 10.3.1(react@17.0.2) '@emotion/css': 10.0.27 @@ -57701,9 +58358,24 @@ snapshots: transitivePeerDependencies: - supports-color - react-select@5.8.0(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2): + react-select@3.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.24.7 + '@emotion/cache': 10.0.29 + '@emotion/core': 10.3.1(react@18.3.1) + '@emotion/css': 10.0.27 + memoize-one: 5.2.1 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-input-autosize: 3.0.0(react@18.3.1) + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + transitivePeerDependencies: + - supports-color + + react-select@5.8.0(@types/react@17.0.71)(react-dom@18.3.1(react@17.0.2))(react@17.0.2): + dependencies: + '@babel/runtime': 7.25.0 '@emotion/cache': 11.11.0 '@emotion/react': 11.11.1(@types/react@17.0.71)(react@17.0.2) '@floating-ui/dom': 1.5.3 @@ -57744,7 +58416,7 @@ snapshots: react-spring@8.0.27(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 prop-types: 15.8.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -57767,18 +58439,9 @@ snapshots: optionalDependencies: '@types/react': 17.0.71 - react-syntax-highlighter@15.5.0(react@17.0.2): - dependencies: - '@babel/runtime': 7.24.7 - highlight.js: 10.7.3 - lowlight: 1.20.0 - prismjs: 1.29.0 - react: 17.0.2 - refractor: 3.6.0 - react-syntax-highlighter@15.5.0(react@18.3.1): dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 highlight.js: 10.7.3 lowlight: 1.20.0 prismjs: 1.29.0 @@ -57826,7 +58489,7 @@ snapshots: react-transition-group@4.4.5(react-dom@17.0.2(react@17.0.2))(react@17.0.2): dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 @@ -57835,7 +58498,7 @@ snapshots: react-transition-group@4.4.5(react-dom@18.3.1(react@17.0.2))(react@17.0.2): dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 @@ -57844,7 +58507,7 @@ snapshots: react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 @@ -58281,7 +58944,7 @@ snapshots: regenerator-transform@0.15.2: dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 regex-not@1.0.2: dependencies: @@ -58906,6 +59569,8 @@ snapshots: semver@7.6.2: {} + semver@7.6.3: {} + send@0.18.0: dependencies: debug: 2.6.9(supports-color@6.1.0) @@ -59087,7 +59752,7 @@ snapshots: dependencies: '@kwsites/file-exists': 1.1.1 '@kwsites/promise-deferred': 1.1.1 - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -59096,7 +59761,7 @@ snapshots: simple-peer@9.11.1: dependencies: buffer: 6.0.3 - debug: 4.3.5 + debug: 4.3.6 err-code: 3.0.1 get-browser-rtc: 1.1.0 queue-microtask: 1.2.3 @@ -59193,7 +59858,7 @@ snapshots: socks-proxy-agent@6.2.1: dependencies: agent-base: 6.0.2 - debug: 4.3.5 + debug: 4.3.6 socks: 2.7.1 transitivePeerDependencies: - supports-color @@ -59201,7 +59866,7 @@ snapshots: socks-proxy-agent@7.0.0: dependencies: agent-base: 6.0.2 - debug: 4.3.5 + debug: 4.3.6 socks: 2.7.1 transitivePeerDependencies: - supports-color @@ -59209,7 +59874,15 @@ snapshots: socks-proxy-agent@8.0.3: dependencies: agent-base: 7.1.1 - debug: 4.3.5 + debug: 4.3.6 + socks: 2.8.3 + transitivePeerDependencies: + - supports-color + + socks-proxy-agent@8.0.4: + dependencies: + agent-base: 7.1.1 + debug: 4.3.6 socks: 2.8.3 transitivePeerDependencies: - supports-color @@ -59338,7 +60011,7 @@ snapshots: spdy-transport@3.0.0: dependencies: - debug: 4.3.5 + debug: 4.3.6 detect-node: 2.1.0 hpack.js: 2.1.6 obuf: 1.1.2 @@ -59349,7 +60022,7 @@ snapshots: spdy@4.0.2: dependencies: - debug: 4.3.5 + debug: 4.3.6 handle-thing: 2.0.1 http-deceiver: 1.2.7 select-hose: 2.0.0 @@ -59827,7 +60500,7 @@ snapshots: colord: 2.9.3 cosmiconfig: 7.1.0 css-functions-list: 3.2.1 - debug: 4.3.4(supports-color@9.4.0) + debug: 4.3.4(supports-color@8.1.1) fast-glob: 3.3.2 fastest-levenshtein: 1.0.16 file-entry-cache: 6.0.1 @@ -59875,7 +60548,7 @@ snapshots: dependencies: component-emitter: 1.3.1 cookiejar: 2.1.4 - debug: 4.3.5 + debug: 4.3.6 fast-safe-stringify: 2.1.1 form-data: 4.0.0 formidable: 2.1.2 @@ -60372,10 +61045,10 @@ snapshots: commander: 2.20.3 source-map-support: 0.5.21 - terser@5.31.1: + terser@5.31.6: dependencies: '@jridgewell/source-map': 0.3.6 - acorn: 8.11.3 + acorn: 8.12.1 commander: 2.20.3 source-map-support: 0.5.21 @@ -60600,7 +61273,7 @@ snapshots: '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.23.5) - ts-jest@29.1.1(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@16.18.68)(typescript@5.3.3)))(typescript@5.3.3): + ts-jest@29.1.1(@babel/core@7.24.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.7))(jest@27.5.1(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.3.100)(@types/node@16.18.68)(typescript@5.3.3)))(typescript@5.3.3): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 @@ -60678,14 +61351,14 @@ snapshots: optionalDependencies: '@swc/core': 1.3.100 - ts-node@10.9.2(@types/node@20.14.2)(typescript@5.3.3): + ts-node@10.9.2(@types/node@22.4.0)(typescript@5.3.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.9 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.14.2 + '@types/node': 22.4.0 acorn: 8.11.2 acorn-walk: 8.3.1 arg: 4.1.3 @@ -60733,7 +61406,7 @@ snapshots: tuf-js@1.1.7: dependencies: '@tufjs/models': 1.0.4 - debug: 4.3.5 + debug: 4.3.6 make-fetch-happen: 11.1.1 transitivePeerDependencies: - supports-color @@ -60861,6 +61534,9 @@ snapshots: undici-types@5.26.5: {} + undici-types@6.19.6: + optional: true + undici@5.28.2: dependencies: '@fastify/busboy': 2.1.0 @@ -61022,7 +61698,7 @@ snapshots: unload@2.2.0: dependencies: - '@babel/runtime': 7.24.7 + '@babel/runtime': 7.25.0 detect-node: 2.1.0 unpipe@1.0.0: {} @@ -61065,6 +61741,12 @@ snapshots: escalade: 3.1.2 picocolors: 1.0.1 + update-browserslist-db@1.1.0(browserslist@4.23.3): + dependencies: + browserslist: 4.23.3 + escalade: 3.1.2 + picocolors: 1.0.1 + update-notifier@2.5.0: dependencies: boxen: 1.3.0 @@ -61123,14 +61805,14 @@ snapshots: optionalDependencies: file-loader: 6.2.0(webpack@5.89.0(webpack-cli@4.10.0)) - url-loader@4.1.1(file-loader@6.2.0(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))))(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))): + url-loader@4.1.1(file-loader@6.2.0(webpack@4.47.0))(webpack@4.47.0): dependencies: loader-utils: 2.0.4 mime-types: 2.1.35 schema-utils: 3.3.0 - webpack: 4.47.0(webpack-cli@3.3.12(webpack@5.89.0)) + webpack: 4.47.0 optionalDependencies: - file-loader: 6.2.0(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))) + file-loader: 6.2.0(webpack@4.47.0) url-loader@4.1.1(file-loader@6.2.0(webpack@5.89.0(uglify-js@3.17.4)(webpack-cli@4.10.0)))(webpack@5.89.0(uglify-js@3.17.4)(webpack-cli@4.10.0)): dependencies: @@ -61141,14 +61823,14 @@ snapshots: optionalDependencies: file-loader: 6.2.0(webpack@5.89.0(uglify-js@3.17.4)(webpack-cli@4.10.0)) - url-loader@4.1.1(file-loader@6.2.0(webpack@5.89.0))(webpack@4.47.0): + url-loader@4.1.1(file-loader@6.2.0(webpack@5.89.0(webpack-cli@3.3.12)))(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))): dependencies: loader-utils: 2.0.4 mime-types: 2.1.35 schema-utils: 3.3.0 - webpack: 4.47.0 + webpack: 4.47.0(webpack-cli@3.3.12(webpack@5.89.0)) optionalDependencies: - file-loader: 6.2.0(webpack@4.47.0) + file-loader: 6.2.0(webpack@4.47.0(webpack-cli@3.3.12(webpack@5.89.0))) url-loader@4.1.1(file-loader@6.2.0(webpack@5.91.0(@swc/core@1.3.100)(esbuild@0.18.20)(webpack-cli@5.1.4)))(webpack@5.91.0(@swc/core@1.3.100)(esbuild@0.18.20)(webpack-cli@4.10.0)): dependencies: @@ -61216,12 +61898,6 @@ snapshots: react: 17.0.2 react-dom: 17.0.2(react@17.0.2) - use-lilius@2.0.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - date-fns: 2.30.0 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - use-lilius@2.0.5(react-dom@17.0.2(react@17.0.2))(react@17.0.2): dependencies: date-fns: 3.6.0 @@ -61406,23 +62082,23 @@ snapshots: '@types/react': 17.0.71 react: 17.0.2 - valtio@1.7.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(babel-plugin-macros@3.1.0)(react@17.0.2): + valtio@1.7.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(babel-plugin-macros@3.1.0)(react@17.0.2): dependencies: proxy-compare: 2.3.0 use-sync-external-store: 1.2.0(react@17.0.2) optionalDependencies: '@babel/helper-module-imports': 7.24.7 - '@babel/types': 7.24.7 + '@babel/types': 7.25.2 babel-plugin-macros: 3.1.0 react: 17.0.2 - valtio@1.7.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.24.7)(babel-plugin-macros@3.1.0)(react@18.3.1): + valtio@1.7.0(@babel/helper-module-imports@7.24.7)(@babel/types@7.25.2)(babel-plugin-macros@3.1.0)(react@18.3.1): dependencies: proxy-compare: 2.3.0 use-sync-external-store: 1.2.0(react@18.3.1) optionalDependencies: '@babel/helper-module-imports': 7.24.7 - '@babel/types': 7.24.7 + '@babel/types': 7.25.2 babel-plugin-macros: 3.1.0 react: 18.3.1 @@ -61530,7 +62206,7 @@ snapshots: dependencies: chalk: 2.4.2 commander: 3.0.2 - debug: 4.3.5 + debug: 4.3.6 transitivePeerDependencies: - supports-color @@ -62746,7 +63422,7 @@ snapshots: dependencies: mkdirp: 0.5.6 - ws@5.2.3: + ws@5.2.4: dependencies: async-limiter: 1.0.1 @@ -62754,8 +63430,14 @@ snapshots: dependencies: async-limiter: 1.0.1 + ws@6.2.3: + dependencies: + async-limiter: 1.0.1 + ws@7.4.6: {} + ws@7.5.10: {} + ws@7.5.9: {} ws@8.13.0: {} @@ -62764,7 +63446,7 @@ snapshots: ws@8.15.0: {} - ws@8.17.0: {} + ws@8.18.0: {} ws@8.5.0: {} @@ -62820,7 +63502,7 @@ snapshots: y-protocols: 1.0.6(yjs@13.6.10) yjs: 13.6.10 optionalDependencies: - ws: 8.17.0 + ws: 8.18.0 transitivePeerDependencies: - bufferutil - supports-color @@ -62844,6 +63526,8 @@ snapshots: yaml@2.4.3: {} + yaml@2.5.0: {} + yargs-parser@13.1.2: dependencies: camelcase: 5.3.1 @@ -62979,7 +63663,7 @@ snapshots: cli-table: 0.3.11 commander: 7.1.0 dateformat: 4.6.3 - debug: 4.3.5 + debug: 4.3.6 diff: 5.1.0 error: 10.4.0 escape-string-regexp: 4.0.0 @@ -63016,7 +63700,7 @@ snapshots: dependencies: chalk: 4.1.2 dargs: 7.0.0 - debug: 4.3.5 + debug: 4.3.6 execa: 5.1.1 github-username: 6.0.0(encoding@0.1.13) lodash: 4.17.21 diff --git a/tools/code-analyzer/package.json b/tools/code-analyzer/package.json index 778644ae498..83210833493 100644 --- a/tools/code-analyzer/package.json +++ b/tools/code-analyzer/package.json @@ -40,7 +40,7 @@ }, "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "config": { "ci": { diff --git a/tools/code-analyzer/src/lib/__tests__/hook-changes-test.ts b/tools/code-analyzer/src/lib/__tests__/hook-changes-test.ts index 55e3c7dff7f..16ed18dd870 100644 --- a/tools/code-analyzer/src/lib/__tests__/hook-changes-test.ts +++ b/tools/code-analyzer/src/lib/__tests__/hook-changes-test.ts @@ -8,7 +8,7 @@ import fs from 'fs'; */ import { scanForHookChanges } from '../hook-changes'; -describe( 'scanForHookChages', () => { +describe( 'scanForHookChanges', () => { it( 'should return a Map with multiple entries in patches with multiple hook changes for the same file.', async () => { // load the text file as a string variable const content = fs.readFileSync( diff --git a/tools/compare-perf/package.json b/tools/compare-perf/package.json index 33c901973a3..430d64146e5 100644 --- a/tools/compare-perf/package.json +++ b/tools/compare-perf/package.json @@ -19,6 +19,6 @@ }, "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" } } diff --git a/tools/monorepo-merge/package.json b/tools/monorepo-merge/package.json index b5fe6b5a8b9..3a54f6badc3 100644 --- a/tools/monorepo-merge/package.json +++ b/tools/monorepo-merge/package.json @@ -63,7 +63,7 @@ }, "engines": { "node": "^20.11.1", - "pnpm": "^9.1.0" + "pnpm": "9.1.3" }, "types": "dist/index.d.ts", "config": { diff --git a/tools/monorepo-utils/dist/index.js b/tools/monorepo-utils/dist/index.js index 2b4523c66b8..4a8bc32f062 100644 --- a/tools/monorepo-utils/dist/index.js +++ b/tools/monorepo-utils/dist/index.js @@ -1,2 +1,2 @@ /*! For license information please see index.js.LICENSE.txt */ -(()=>{var __webpack_modules__={4797:function(e,t,i){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,i,s){void 0===s&&(s=i),Object.defineProperty(e,s,{enumerable:!0,get:function(){return t[i]}})}:function(e,t,i,s){void 0===s&&(s=i),e[s]=t[i]}),r=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)"default"!==i&&Object.hasOwnProperty.call(e,i)&&s(t,e,i);return r(t,e),t};Object.defineProperty(t,"__esModule",{value:!0}),t.issue=t.issueCommand=void 0;const o=n(i(22037)),a=i(54106);function c(e,t,i){const s=new l(e,t,i);process.stdout.write(s.toString()+o.EOL)}t.issueCommand=c,t.issue=function(e,t=""){c(e,{},t)};class l{constructor(e,t,i){e||(e="missing.command"),this.command=e,this.properties=t,this.message=i}toString(){let e="::"+this.command;if(this.properties&&Object.keys(this.properties).length>0){e+=" ";let i=!0;for(const s in this.properties)if(this.properties.hasOwnProperty(s)){const r=this.properties[s];r&&(i?i=!1:e+=",",e+=`${s}=${t=r,a.toCommandValue(t).replace(/%/g,"%25").replace(/\r/g,"%0D").replace(/\n/g,"%0A").replace(/:/g,"%3A").replace(/,/g,"%2C")}`)}}var t;return e+=`::${function(e){return a.toCommandValue(e).replace(/%/g,"%25").replace(/\r/g,"%0D").replace(/\n/g,"%0A")}(this.message)}`,e}}},57995:function(e,t,i){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,i,s){void 0===s&&(s=i),Object.defineProperty(e,s,{enumerable:!0,get:function(){return t[i]}})}:function(e,t,i,s){void 0===s&&(s=i),e[s]=t[i]}),r=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)"default"!==i&&Object.hasOwnProperty.call(e,i)&&s(t,e,i);return r(t,e),t},o=this&&this.__awaiter||function(e,t,i,s){return new(i||(i=Promise))((function(r,n){function o(e){try{c(s.next(e))}catch(e){n(e)}}function a(e){try{c(s.throw(e))}catch(e){n(e)}}function c(e){var t;e.done?r(e.value):(t=e.value,t instanceof i?t:new i((function(e){e(t)}))).then(o,a)}c((s=s.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:!0}),t.getIDToken=t.getState=t.saveState=t.group=t.endGroup=t.startGroup=t.info=t.notice=t.warning=t.error=t.debug=t.isDebug=t.setFailed=t.setCommandEcho=t.setOutput=t.getBooleanInput=t.getMultilineInput=t.getInput=t.addPath=t.setSecret=t.exportVariable=t.ExitCode=void 0;const a=i(4797),c=i(8096),l=i(54106),p=n(i(22037)),A=n(i(71017)),u=i(271);var d;function h(e,t){const i=process.env[`INPUT_${e.replace(/ /g,"_").toUpperCase()}`]||"";if(t&&t.required&&!i)throw new Error(`Input required and not supplied: ${e}`);return t&&!1===t.trimWhitespace?i:i.trim()}function m(e,t={}){a.issueCommand("error",l.toCommandProperties(t),e instanceof Error?e.toString():e)}function g(e){a.issue("group",e)}function f(){a.issue("endgroup")}!function(e){e[e.Success=0]="Success",e[e.Failure=1]="Failure"}(d=t.ExitCode||(t.ExitCode={})),t.exportVariable=function(e,t){const i=l.toCommandValue(t);if(process.env[e]=i,process.env.GITHUB_ENV)return c.issueFileCommand("ENV",c.prepareKeyValueMessage(e,t));a.issueCommand("set-env",{name:e},i)},t.setSecret=function(e){a.issueCommand("add-mask",{},e)},t.addPath=function(e){process.env.GITHUB_PATH?c.issueFileCommand("PATH",e):a.issueCommand("add-path",{},e),process.env.PATH=`${e}${A.delimiter}${process.env.PATH}`},t.getInput=h,t.getMultilineInput=function(e,t){const i=h(e,t).split("\n").filter((e=>""!==e));return t&&!1===t.trimWhitespace?i:i.map((e=>e.trim()))},t.getBooleanInput=function(e,t){const i=h(e,t);if(["true","True","TRUE"].includes(i))return!0;if(["false","False","FALSE"].includes(i))return!1;throw new TypeError(`Input does not meet YAML 1.2 "Core Schema" specification: ${e}\nSupport boolean input list: \`true | True | TRUE | false | False | FALSE\``)},t.setOutput=function(e,t){if(process.env.GITHUB_OUTPUT)return c.issueFileCommand("OUTPUT",c.prepareKeyValueMessage(e,t));process.stdout.write(p.EOL),a.issueCommand("set-output",{name:e},l.toCommandValue(t))},t.setCommandEcho=function(e){a.issue("echo",e?"on":"off")},t.setFailed=function(e){process.exitCode=d.Failure,m(e)},t.isDebug=function(){return"1"===process.env.RUNNER_DEBUG},t.debug=function(e){a.issueCommand("debug",{},e)},t.error=m,t.warning=function(e,t={}){a.issueCommand("warning",l.toCommandProperties(t),e instanceof Error?e.toString():e)},t.notice=function(e,t={}){a.issueCommand("notice",l.toCommandProperties(t),e instanceof Error?e.toString():e)},t.info=function(e){process.stdout.write(e+p.EOL)},t.startGroup=g,t.endGroup=f,t.group=function(e,t){return o(this,void 0,void 0,(function*(){let i;g(e);try{i=yield t()}finally{f()}return i}))},t.saveState=function(e,t){if(process.env.GITHUB_STATE)return c.issueFileCommand("STATE",c.prepareKeyValueMessage(e,t));a.issueCommand("save-state",{name:e},l.toCommandValue(t))},t.getState=function(e){return process.env[`STATE_${e}`]||""},t.getIDToken=function(e){return o(this,void 0,void 0,(function*(){return yield u.OidcClient.getIDToken(e)}))};var E=i(26163);Object.defineProperty(t,"summary",{enumerable:!0,get:function(){return E.summary}});var C=i(26163);Object.defineProperty(t,"markdownSummary",{enumerable:!0,get:function(){return C.markdownSummary}});var y=i(56520);Object.defineProperty(t,"toPosixPath",{enumerable:!0,get:function(){return y.toPosixPath}}),Object.defineProperty(t,"toWin32Path",{enumerable:!0,get:function(){return y.toWin32Path}}),Object.defineProperty(t,"toPlatformPath",{enumerable:!0,get:function(){return y.toPlatformPath}})},8096:function(e,t,i){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,i,s){void 0===s&&(s=i),Object.defineProperty(e,s,{enumerable:!0,get:function(){return t[i]}})}:function(e,t,i,s){void 0===s&&(s=i),e[s]=t[i]}),r=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)"default"!==i&&Object.hasOwnProperty.call(e,i)&&s(t,e,i);return r(t,e),t};Object.defineProperty(t,"__esModule",{value:!0}),t.prepareKeyValueMessage=t.issueFileCommand=void 0;const o=n(i(57147)),a=n(i(22037)),c=i(68040),l=i(54106);t.issueFileCommand=function(e,t){const i=process.env[`GITHUB_${e}`];if(!i)throw new Error(`Unable to find environment variable for file command ${e}`);if(!o.existsSync(i))throw new Error(`Missing file at path: ${i}`);o.appendFileSync(i,`${l.toCommandValue(t)}${a.EOL}`,{encoding:"utf8"})},t.prepareKeyValueMessage=function(e,t){const i=`ghadelimiter_${c.v4()}`,s=l.toCommandValue(t);if(e.includes(i))throw new Error(`Unexpected input: name should not contain the delimiter "${i}"`);if(s.includes(i))throw new Error(`Unexpected input: value should not contain the delimiter "${i}"`);return`${e}<<${i}${a.EOL}${s}${a.EOL}${i}`}},271:function(e,t,i){"use strict";var s=this&&this.__awaiter||function(e,t,i,s){return new(i||(i=Promise))((function(r,n){function o(e){try{c(s.next(e))}catch(e){n(e)}}function a(e){try{c(s.throw(e))}catch(e){n(e)}}function c(e){var t;e.done?r(e.value):(t=e.value,t instanceof i?t:new i((function(e){e(t)}))).then(o,a)}c((s=s.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:!0}),t.OidcClient=void 0;const r=i(48543),n=i(65343),o=i(57995);class a{static createHttpClient(e=!0,t=10){const i={allowRetries:e,maxRetries:t};return new r.HttpClient("actions/oidc-client",[new n.BearerCredentialHandler(a.getRequestToken())],i)}static getRequestToken(){const e=process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN;if(!e)throw new Error("Unable to get ACTIONS_ID_TOKEN_REQUEST_TOKEN env variable");return e}static getIDTokenUrl(){const e=process.env.ACTIONS_ID_TOKEN_REQUEST_URL;if(!e)throw new Error("Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable");return e}static getCall(e){var t;return s(this,void 0,void 0,(function*(){const i=a.createHttpClient(),s=yield i.getJson(e).catch((e=>{throw new Error(`Failed to get ID Token. \n \n Error Code : ${e.statusCode}\n \n Error Message: ${e.message}`)})),r=null===(t=s.result)||void 0===t?void 0:t.value;if(!r)throw new Error("Response json body do not have ID Token field");return r}))}static getIDToken(e){return s(this,void 0,void 0,(function*(){try{let t=a.getIDTokenUrl();e&&(t=`${t}&audience=${encodeURIComponent(e)}`),o.debug(`ID token url is ${t}`);const i=yield a.getCall(t);return o.setSecret(i),i}catch(e){throw new Error(`Error message: ${e.message}`)}}))}}t.OidcClient=a},56520:function(e,t,i){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,i,s){void 0===s&&(s=i),Object.defineProperty(e,s,{enumerable:!0,get:function(){return t[i]}})}:function(e,t,i,s){void 0===s&&(s=i),e[s]=t[i]}),r=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)"default"!==i&&Object.hasOwnProperty.call(e,i)&&s(t,e,i);return r(t,e),t};Object.defineProperty(t,"__esModule",{value:!0}),t.toPlatformPath=t.toWin32Path=t.toPosixPath=void 0;const o=n(i(71017));t.toPosixPath=function(e){return e.replace(/[\\]/g,"/")},t.toWin32Path=function(e){return e.replace(/[/]/g,"\\")},t.toPlatformPath=function(e){return e.replace(/[/\\]/g,o.sep)}},26163:function(e,t,i){"use strict";var s=this&&this.__awaiter||function(e,t,i,s){return new(i||(i=Promise))((function(r,n){function o(e){try{c(s.next(e))}catch(e){n(e)}}function a(e){try{c(s.throw(e))}catch(e){n(e)}}function c(e){var t;e.done?r(e.value):(t=e.value,t instanceof i?t:new i((function(e){e(t)}))).then(o,a)}c((s=s.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:!0}),t.summary=t.markdownSummary=t.SUMMARY_DOCS_URL=t.SUMMARY_ENV_VAR=void 0;const r=i(22037),n=i(57147),{access:o,appendFile:a,writeFile:c}=n.promises;t.SUMMARY_ENV_VAR="GITHUB_STEP_SUMMARY",t.SUMMARY_DOCS_URL="https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary";const l=new class{constructor(){this._buffer=""}filePath(){return s(this,void 0,void 0,(function*(){if(this._filePath)return this._filePath;const e=process.env[t.SUMMARY_ENV_VAR];if(!e)throw new Error(`Unable to find environment variable for $${t.SUMMARY_ENV_VAR}. Check if your runtime environment supports job summaries.`);try{yield o(e,n.constants.R_OK|n.constants.W_OK)}catch(t){throw new Error(`Unable to access summary file: '${e}'. Check if the file has correct read/write permissions.`)}return this._filePath=e,this._filePath}))}wrap(e,t,i={}){const s=Object.entries(i).map((([e,t])=>` ${e}="${t}"`)).join("");return t?`<${e}${s}>${t}`:`<${e}${s}>`}write(e){return s(this,void 0,void 0,(function*(){const t=!!(null==e?void 0:e.overwrite),i=yield this.filePath(),s=t?c:a;return yield s(i,this._buffer,{encoding:"utf8"}),this.emptyBuffer()}))}clear(){return s(this,void 0,void 0,(function*(){return this.emptyBuffer().write({overwrite:!0})}))}stringify(){return this._buffer}isEmptyBuffer(){return 0===this._buffer.length}emptyBuffer(){return this._buffer="",this}addRaw(e,t=!1){return this._buffer+=e,t?this.addEOL():this}addEOL(){return this.addRaw(r.EOL)}addCodeBlock(e,t){const i=Object.assign({},t&&{lang:t}),s=this.wrap("pre",this.wrap("code",e),i);return this.addRaw(s).addEOL()}addList(e,t=!1){const i=t?"ol":"ul",s=e.map((e=>this.wrap("li",e))).join(""),r=this.wrap(i,s);return this.addRaw(r).addEOL()}addTable(e){const t=e.map((e=>{const t=e.map((e=>{if("string"==typeof e)return this.wrap("td",e);const{header:t,data:i,colspan:s,rowspan:r}=e,n=t?"th":"td",o=Object.assign(Object.assign({},s&&{colspan:s}),r&&{rowspan:r});return this.wrap(n,i,o)})).join("");return this.wrap("tr",t)})).join(""),i=this.wrap("table",t);return this.addRaw(i).addEOL()}addDetails(e,t){const i=this.wrap("details",this.wrap("summary",e)+t);return this.addRaw(i).addEOL()}addImage(e,t,i){const{width:s,height:r}=i||{},n=Object.assign(Object.assign({},s&&{width:s}),r&&{height:r}),o=this.wrap("img",null,Object.assign({src:e,alt:t},n));return this.addRaw(o).addEOL()}addHeading(e,t){const i=`h${t}`,s=["h1","h2","h3","h4","h5","h6"].includes(i)?i:"h1",r=this.wrap(s,e);return this.addRaw(r).addEOL()}addSeparator(){const e=this.wrap("hr",null);return this.addRaw(e).addEOL()}addBreak(){const e=this.wrap("br",null);return this.addRaw(e).addEOL()}addQuote(e,t){const i=Object.assign({},t&&{cite:t}),s=this.wrap("blockquote",e,i);return this.addRaw(s).addEOL()}addLink(e,t){const i=this.wrap("a",e,{href:t});return this.addRaw(i).addEOL()}};t.markdownSummary=l,t.summary=l},54106:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.toCommandProperties=t.toCommandValue=void 0,t.toCommandValue=function(e){return null==e?"":"string"==typeof e||e instanceof String?e:JSON.stringify(e)},t.toCommandProperties=function(e){return Object.keys(e).length?{title:e.title,file:e.file,line:e.startLine,endLine:e.endLine,col:e.startColumn,endColumn:e.endColumn}:{}}},65343:function(e,t){"use strict";var i=this&&this.__awaiter||function(e,t,i,s){return new(i||(i=Promise))((function(r,n){function o(e){try{c(s.next(e))}catch(e){n(e)}}function a(e){try{c(s.throw(e))}catch(e){n(e)}}function c(e){var t;e.done?r(e.value):(t=e.value,t instanceof i?t:new i((function(e){e(t)}))).then(o,a)}c((s=s.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:!0}),t.PersonalAccessTokenCredentialHandler=t.BearerCredentialHandler=t.BasicCredentialHandler=void 0,t.BasicCredentialHandler=class{constructor(e,t){this.username=e,this.password=t}prepareRequest(e){if(!e.headers)throw Error("The request has no headers");e.headers.Authorization=`Basic ${Buffer.from(`${this.username}:${this.password}`).toString("base64")}`}canHandleAuthentication(){return!1}handleAuthentication(){return i(this,void 0,void 0,(function*(){throw new Error("not implemented")}))}},t.BearerCredentialHandler=class{constructor(e){this.token=e}prepareRequest(e){if(!e.headers)throw Error("The request has no headers");e.headers.Authorization=`Bearer ${this.token}`}canHandleAuthentication(){return!1}handleAuthentication(){return i(this,void 0,void 0,(function*(){throw new Error("not implemented")}))}},t.PersonalAccessTokenCredentialHandler=class{constructor(e){this.token=e}prepareRequest(e){if(!e.headers)throw Error("The request has no headers");e.headers.Authorization=`Basic ${Buffer.from(`PAT:${this.token}`).toString("base64")}`}canHandleAuthentication(){return!1}handleAuthentication(){return i(this,void 0,void 0,(function*(){throw new Error("not implemented")}))}}},48543:function(e,t,i){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,i,s){void 0===s&&(s=i);var r=Object.getOwnPropertyDescriptor(t,i);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,s,r)}:function(e,t,i,s){void 0===s&&(s=i),e[s]=t[i]}),r=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)"default"!==i&&Object.prototype.hasOwnProperty.call(e,i)&&s(t,e,i);return r(t,e),t},o=this&&this.__awaiter||function(e,t,i,s){return new(i||(i=Promise))((function(r,n){function o(e){try{c(s.next(e))}catch(e){n(e)}}function a(e){try{c(s.throw(e))}catch(e){n(e)}}function c(e){var t;e.done?r(e.value):(t=e.value,t instanceof i?t:new i((function(e){e(t)}))).then(o,a)}c((s=s.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:!0}),t.HttpClient=t.isHttps=t.HttpClientResponse=t.HttpClientError=t.getProxyUrl=t.MediaTypes=t.Headers=t.HttpCodes=void 0;const a=n(i(13685)),c=n(i(95687)),l=n(i(20359)),p=n(i(42785)),A=i(90083);var u,d,h;!function(e){e[e.OK=200]="OK",e[e.MultipleChoices=300]="MultipleChoices",e[e.MovedPermanently=301]="MovedPermanently",e[e.ResourceMoved=302]="ResourceMoved",e[e.SeeOther=303]="SeeOther",e[e.NotModified=304]="NotModified",e[e.UseProxy=305]="UseProxy",e[e.SwitchProxy=306]="SwitchProxy",e[e.TemporaryRedirect=307]="TemporaryRedirect",e[e.PermanentRedirect=308]="PermanentRedirect",e[e.BadRequest=400]="BadRequest",e[e.Unauthorized=401]="Unauthorized",e[e.PaymentRequired=402]="PaymentRequired",e[e.Forbidden=403]="Forbidden",e[e.NotFound=404]="NotFound",e[e.MethodNotAllowed=405]="MethodNotAllowed",e[e.NotAcceptable=406]="NotAcceptable",e[e.ProxyAuthenticationRequired=407]="ProxyAuthenticationRequired",e[e.RequestTimeout=408]="RequestTimeout",e[e.Conflict=409]="Conflict",e[e.Gone=410]="Gone",e[e.TooManyRequests=429]="TooManyRequests",e[e.InternalServerError=500]="InternalServerError",e[e.NotImplemented=501]="NotImplemented",e[e.BadGateway=502]="BadGateway",e[e.ServiceUnavailable=503]="ServiceUnavailable",e[e.GatewayTimeout=504]="GatewayTimeout"}(u||(t.HttpCodes=u={})),function(e){e.Accept="accept",e.ContentType="content-type"}(d||(t.Headers=d={})),function(e){e.ApplicationJson="application/json"}(h||(t.MediaTypes=h={})),t.getProxyUrl=function(e){const t=l.getProxyUrl(new URL(e));return t?t.href:""};const m=[u.MovedPermanently,u.ResourceMoved,u.SeeOther,u.TemporaryRedirect,u.PermanentRedirect],g=[u.BadGateway,u.ServiceUnavailable,u.GatewayTimeout],f=["OPTIONS","GET","DELETE","HEAD"];class E extends Error{constructor(e,t){super(e),this.name="HttpClientError",this.statusCode=t,Object.setPrototypeOf(this,E.prototype)}}t.HttpClientError=E;class C{constructor(e){this.message=e}readBody(){return o(this,void 0,void 0,(function*(){return new Promise((e=>o(this,void 0,void 0,(function*(){let t=Buffer.alloc(0);this.message.on("data",(e=>{t=Buffer.concat([t,e])})),this.message.on("end",(()=>{e(t.toString())}))}))))}))}readBodyBuffer(){return o(this,void 0,void 0,(function*(){return new Promise((e=>o(this,void 0,void 0,(function*(){const t=[];this.message.on("data",(e=>{t.push(e)})),this.message.on("end",(()=>{e(Buffer.concat(t))}))}))))}))}}t.HttpClientResponse=C,t.isHttps=function(e){return"https:"===new URL(e).protocol},t.HttpClient=class{constructor(e,t,i){this._ignoreSslError=!1,this._allowRedirects=!0,this._allowRedirectDowngrade=!1,this._maxRedirects=50,this._allowRetries=!1,this._maxRetries=1,this._keepAlive=!1,this._disposed=!1,this.userAgent=e,this.handlers=t||[],this.requestOptions=i,i&&(null!=i.ignoreSslError&&(this._ignoreSslError=i.ignoreSslError),this._socketTimeout=i.socketTimeout,null!=i.allowRedirects&&(this._allowRedirects=i.allowRedirects),null!=i.allowRedirectDowngrade&&(this._allowRedirectDowngrade=i.allowRedirectDowngrade),null!=i.maxRedirects&&(this._maxRedirects=Math.max(i.maxRedirects,0)),null!=i.keepAlive&&(this._keepAlive=i.keepAlive),null!=i.allowRetries&&(this._allowRetries=i.allowRetries),null!=i.maxRetries&&(this._maxRetries=i.maxRetries))}options(e,t){return o(this,void 0,void 0,(function*(){return this.request("OPTIONS",e,null,t||{})}))}get(e,t){return o(this,void 0,void 0,(function*(){return this.request("GET",e,null,t||{})}))}del(e,t){return o(this,void 0,void 0,(function*(){return this.request("DELETE",e,null,t||{})}))}post(e,t,i){return o(this,void 0,void 0,(function*(){return this.request("POST",e,t,i||{})}))}patch(e,t,i){return o(this,void 0,void 0,(function*(){return this.request("PATCH",e,t,i||{})}))}put(e,t,i){return o(this,void 0,void 0,(function*(){return this.request("PUT",e,t,i||{})}))}head(e,t){return o(this,void 0,void 0,(function*(){return this.request("HEAD",e,null,t||{})}))}sendStream(e,t,i,s){return o(this,void 0,void 0,(function*(){return this.request(e,t,i,s)}))}getJson(e,t={}){return o(this,void 0,void 0,(function*(){t[d.Accept]=this._getExistingOrDefaultHeader(t,d.Accept,h.ApplicationJson);const i=yield this.get(e,t);return this._processResponse(i,this.requestOptions)}))}postJson(e,t,i={}){return o(this,void 0,void 0,(function*(){const s=JSON.stringify(t,null,2);i[d.Accept]=this._getExistingOrDefaultHeader(i,d.Accept,h.ApplicationJson),i[d.ContentType]=this._getExistingOrDefaultHeader(i,d.ContentType,h.ApplicationJson);const r=yield this.post(e,s,i);return this._processResponse(r,this.requestOptions)}))}putJson(e,t,i={}){return o(this,void 0,void 0,(function*(){const s=JSON.stringify(t,null,2);i[d.Accept]=this._getExistingOrDefaultHeader(i,d.Accept,h.ApplicationJson),i[d.ContentType]=this._getExistingOrDefaultHeader(i,d.ContentType,h.ApplicationJson);const r=yield this.put(e,s,i);return this._processResponse(r,this.requestOptions)}))}patchJson(e,t,i={}){return o(this,void 0,void 0,(function*(){const s=JSON.stringify(t,null,2);i[d.Accept]=this._getExistingOrDefaultHeader(i,d.Accept,h.ApplicationJson),i[d.ContentType]=this._getExistingOrDefaultHeader(i,d.ContentType,h.ApplicationJson);const r=yield this.patch(e,s,i);return this._processResponse(r,this.requestOptions)}))}request(e,t,i,s){return o(this,void 0,void 0,(function*(){if(this._disposed)throw new Error("Client has already been disposed.");const r=new URL(t);let n=this._prepareRequest(e,r,s);const o=this._allowRetries&&f.includes(e)?this._maxRetries+1:1;let a,c=0;do{if(a=yield this.requestRaw(n,i),a&&a.message&&a.message.statusCode===u.Unauthorized){let e;for(const t of this.handlers)if(t.canHandleAuthentication(a)){e=t;break}return e?e.handleAuthentication(this,n,i):a}let t=this._maxRedirects;for(;a.message.statusCode&&m.includes(a.message.statusCode)&&this._allowRedirects&&t>0;){const o=a.message.headers.location;if(!o)break;const c=new URL(o);if("https:"===r.protocol&&r.protocol!==c.protocol&&!this._allowRedirectDowngrade)throw new Error("Redirect from HTTPS to HTTP protocol. This downgrade is not allowed for security reasons. If you want to allow this behavior, set the allowRedirectDowngrade option to true.");if(yield a.readBody(),c.hostname!==r.hostname)for(const e in s)"authorization"===e.toLowerCase()&&delete s[e];n=this._prepareRequest(e,c,s),a=yield this.requestRaw(n,i),t--}if(!a.message.statusCode||!g.includes(a.message.statusCode))return a;c+=1,c{this.requestRawWithCallback(e,t,(function(e,t){e?s(e):t?i(t):s(new Error("Unknown error"))}))}))}))}requestRawWithCallback(e,t,i){"string"==typeof t&&(e.options.headers||(e.options.headers={}),e.options.headers["Content-Length"]=Buffer.byteLength(t,"utf8"));let s=!1;function r(e,t){s||(s=!0,i(e,t))}const n=e.httpModule.request(e.options,(e=>{r(void 0,new C(e))}));let o;n.on("socket",(e=>{o=e})),n.setTimeout(this._socketTimeout||18e4,(()=>{o&&o.end(),r(new Error(`Request timeout: ${e.options.path}`))})),n.on("error",(function(e){r(e)})),t&&"string"==typeof t&&n.write(t,"utf8"),t&&"string"!=typeof t?(t.on("close",(function(){n.end()})),t.pipe(n)):n.end()}getAgent(e){const t=new URL(e);return this._getAgent(t)}getAgentDispatcher(e){const t=new URL(e),i=l.getProxyUrl(t);if(i&&i.hostname)return this._getProxyAgentDispatcher(t,i)}_prepareRequest(e,t,i){const s={};s.parsedUrl=t;const r="https:"===s.parsedUrl.protocol;s.httpModule=r?c:a;const n=r?443:80;if(s.options={},s.options.host=s.parsedUrl.hostname,s.options.port=s.parsedUrl.port?parseInt(s.parsedUrl.port):n,s.options.path=(s.parsedUrl.pathname||"")+(s.parsedUrl.search||""),s.options.method=e,s.options.headers=this._mergeHeaders(i),null!=this.userAgent&&(s.options.headers["user-agent"]=this.userAgent),s.options.agent=this._getAgent(s.parsedUrl),this.handlers)for(const e of this.handlers)e.prepareRequest(s.options);return s}_mergeHeaders(e){return this.requestOptions&&this.requestOptions.headers?Object.assign({},y(this.requestOptions.headers),y(e||{})):y(e||{})}_getExistingOrDefaultHeader(e,t,i){let s;return this.requestOptions&&this.requestOptions.headers&&(s=y(this.requestOptions.headers)[t]),e[t]||s||i}_getAgent(e){let t;const i=l.getProxyUrl(e),s=i&&i.hostname;if(this._keepAlive&&s&&(t=this._proxyAgent),this._keepAlive&&!s&&(t=this._agent),t)return t;const r="https:"===e.protocol;let n=100;if(this.requestOptions&&(n=this.requestOptions.maxSockets||a.globalAgent.maxSockets),i&&i.hostname){const e={maxSockets:n,keepAlive:this._keepAlive,proxy:Object.assign(Object.assign({},(i.username||i.password)&&{proxyAuth:`${i.username}:${i.password}`}),{host:i.hostname,port:i.port})};let s;const o="https:"===i.protocol;s=r?o?p.httpsOverHttps:p.httpsOverHttp:o?p.httpOverHttps:p.httpOverHttp,t=s(e),this._proxyAgent=t}if(this._keepAlive&&!t){const e={keepAlive:this._keepAlive,maxSockets:n};t=r?new c.Agent(e):new a.Agent(e),this._agent=t}return t||(t=r?c.globalAgent:a.globalAgent),r&&this._ignoreSslError&&(t.options=Object.assign(t.options||{},{rejectUnauthorized:!1})),t}_getProxyAgentDispatcher(e,t){let i;if(this._keepAlive&&(i=this._proxyAgentDispatcher),i)return i;const s="https:"===e.protocol;return i=new A.ProxyAgent(Object.assign({uri:t.href,pipelining:this._keepAlive?1:0},(t.username||t.password)&&{token:`${t.username}:${t.password}`})),this._proxyAgentDispatcher=i,s&&this._ignoreSslError&&(i.options=Object.assign(i.options.requestTls||{},{rejectUnauthorized:!1})),i}_performExponentialBackoff(e){return o(this,void 0,void 0,(function*(){e=Math.min(10,e);const t=5*Math.pow(2,e);return new Promise((e=>setTimeout((()=>e()),t)))}))}_processResponse(e,t){return o(this,void 0,void 0,(function*(){return new Promise(((i,s)=>o(this,void 0,void 0,(function*(){const r=e.message.statusCode||0,n={statusCode:r,result:null,headers:{}};let o,a;r===u.NotFound&&i(n);try{a=yield e.readBody(),a&&a.length>0&&(o=t&&t.deserializeDates?JSON.parse(a,(function(e,t){if("string"==typeof t){const e=new Date(t);if(!isNaN(e.valueOf()))return e}return t})):JSON.parse(a),n.result=o),n.headers=e.message.headers}catch(e){}if(r>299){let e;e=o&&o.message?o.message:a&&a.length>0?a:`Failed request: (${r})`;const t=new E(e,r);t.result=n.result,s(t)}else i(n)}))))}))}};const y=e=>Object.keys(e).reduce(((t,i)=>(t[i.toLowerCase()]=e[i],t)),{})},20359:(e,t)=>{"use strict";function i(e){if(!e.hostname)return!1;if(function(e){const t=e.toLowerCase();return"localhost"===t||t.startsWith("127.")||t.startsWith("[::1]")||t.startsWith("[0:0:0:0:0:0:0:1]")}(e.hostname))return!0;const t=process.env.no_proxy||process.env.NO_PROXY||"";if(!t)return!1;let i;e.port?i=Number(e.port):"http:"===e.protocol?i=80:"https:"===e.protocol&&(i=443);const s=[e.hostname.toUpperCase()];"number"==typeof i&&s.push(`${s[0]}:${i}`);for(const e of t.split(",").map((e=>e.trim().toUpperCase())).filter((e=>e)))if("*"===e||s.some((t=>t===e||t.endsWith(`.${e}`)||e.startsWith(".")&&t.endsWith(`${e}`))))return!0;return!1}Object.defineProperty(t,"__esModule",{value:!0}),t.checkBypass=t.getProxyUrl=void 0,t.getProxyUrl=function(e){const t="https:"===e.protocol;if(i(e))return;const s=t?process.env.https_proxy||process.env.HTTPS_PROXY:process.env.http_proxy||process.env.HTTP_PROXY;if(s)try{return new URL(s)}catch(e){if(!s.startsWith("http://")&&!s.startsWith("https://"))return new URL(`http://${s}`)}},t.checkBypass=i},55763:(e,t,i)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),function(e){for(var i in e)t.hasOwnProperty(i)||(t[i]=e[i])}(i(20536))},20536:function(e,t,i){"use strict";var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});const r=i(57147),n=s(i(8856)).default("@kwsites/file-exists");t.exists=function(e,i=t.READABLE){return function(e,t,i){n("checking %s",e);try{const s=r.statSync(e);return s.isFile()&&t?(n("[OK] path represents a file"),!0):s.isDirectory()&&i?(n("[OK] path represents a directory"),!0):(n("[FAIL] path represents something other than a file or directory"),!1)}catch(e){if("ENOENT"===e.code)return n("[FAIL] path is not accessible: %o",e),!1;throw n("[FATAL] %o",e),e}}(e,(i&t.FILE)>0,(i&t.FOLDER)>0)},t.FILE=1,t.FOLDER=2,t.READABLE=t.FILE+t.FOLDER},39487:(e,t)=>{"use strict";function i(){let e,t,i="pending";return{promise:new Promise(((i,s)=>{e=i,t=s})),done(t){"pending"===i&&(i="resolved",e(t))},fail(e){"pending"===i&&(i="rejected",t(e))},get fulfilled(){return"pending"!==i},get status(){return i}}}Object.defineProperty(t,"__esModule",{value:!0}),t.createDeferred=t.deferred=void 0,t.deferred=i,t.createDeferred=i,t.default=i},61786:(e,t,i)=>{"use strict";var s,r=Object.create,n=Object.defineProperty,o=Object.getOwnPropertyDescriptor,a=Object.getOwnPropertyNames,c=Object.getPrototypeOf,l=Object.prototype.hasOwnProperty,p=(e,t,i,s)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let r of a(t))l.call(e,r)||r===i||n(e,r,{get:()=>t[r],enumerable:!(s=o(t,r))||s.enumerable});return e},A={};((e,t)=>{for(var i in t)n(e,i,{get:t[i],enumerable:!0})})(A,{createAppAuth:()=>S,createOAuthUserAuth:()=>D.createOAuthUserAuth}),e.exports=(s=A,p(n({},"__esModule",{value:!0}),s));var u=i(21375),d=i(65683),h=i(62419),m=i(57751),g=(((e,t,i)=>{i=null!=e?r(c(e)):{},p(e&&e.__esModule?i:n(i,"default",{value:e,enumerable:!0}),e)})(i(62419)),i(67478));async function f({appId:e,privateKey:t,timeDifference:i}){try{const s=await(0,g.githubAppJwt)({id:+e,privateKey:t,now:i&&Math.floor(Date.now()/1e3)+i});return{type:"app",token:s.token,appId:s.appId,expiresAt:new Date(1e3*s.expiration).toISOString()}}catch(e){throw"-----BEGIN RSA PRIVATE KEY-----"===t?new Error("The 'privateKey` option contains only the first line '-----BEGIN RSA PRIVATE KEY-----'. If you are setting it using a `.env` file, make sure it is set on a single line with newlines replaced by '\n'"):e}}var E=i(65649);function C({installationId:e,permissions:t={},repositoryIds:i=[],repositoryNames:s=[]}){const r=Object.keys(t).sort().map((e=>"read"===t[e]?e:`${e}!`)).join(",");return[e,i.sort().join(","),s.join(","),r].filter(Boolean).join("|")}function y({installationId:e,token:t,createdAt:i,expiresAt:s,repositorySelection:r,permissions:n,repositoryIds:o,repositoryNames:a,singleFileName:c}){return Object.assign({type:"token",tokenType:"installation",token:t,installationId:e,permissions:n,createdAt:i,expiresAt:s,repositorySelection:r},o?{repositoryIds:o}:null,a?{repositoryNames:a}:null,c?{singleFileName:c}:null)}async function v(e,t,i){const s=Number(t.installationId||e.installationId);if(!s)throw new Error("[@octokit/auth-app] installationId option is required for installation authentication.");if(t.factory){const{type:i,factory:s,oauthApp:r,...n}={...e,...t};return s(n)}const r=Object.assign({installationId:s},t);if(!t.refresh){const t=await async function(e,t){const i=C(t),s=await e.get(i);if(!s)return;const[r,n,o,a,c,l]=s.split("|");return{token:r,createdAt:n,expiresAt:o,permissions:t.permissions||c.split(/,/).reduce(((e,t)=>(/!$/.test(t)?e[t.slice(0,-1)]="write":e[t]="read",e)),{}),repositoryIds:t.repositoryIds,repositoryNames:t.repositoryNames,singleFileName:l,repositorySelection:a}}(e.cache,r);if(t){const{token:e,createdAt:i,expiresAt:r,permissions:n,repositoryIds:o,repositoryNames:a,singleFileName:c,repositorySelection:l}=t;return y({installationId:s,token:e,createdAt:i,expiresAt:r,permissions:n,repositorySelection:l,repositoryIds:o,repositoryNames:a,singleFileName:c})}}const n=await f(e),o=i||e.request,{data:{token:a,expires_at:c,repositories:l,permissions:p,repository_selection:A,single_file:u}}=await o("POST /app/installations/{installation_id}/access_tokens",{installation_id:s,repository_ids:t.repositoryIds,repositories:t.repositoryNames,permissions:t.permissions,mediaType:{previews:["machine-man"]},headers:{authorization:`bearer ${n.token}`}}),d=p||{},h=A||"all",m=l?l.map((e=>e.id)):void 0,g=l?l.map((e=>e.name)):void 0,E=(new Date).toISOString();return await async function(e,t,i){const s=C(t),r=t.permissions?"":Object.keys(i.permissions).map((e=>`${e}${"write"===i.permissions[e]?"!":""}`)).join(","),n=[i.token,i.createdAt,i.expiresAt,i.repositorySelection,r,i.singleFileName].join("|");await e.set(s,n)}(e.cache,r,{token:a,createdAt:E,expiresAt:c,repositorySelection:h,permissions:d,repositoryIds:m,repositoryNames:g,singleFileName:u}),y({installationId:s,token:a,createdAt:E,expiresAt:c,repositorySelection:h,permissions:d,repositoryIds:m,repositoryNames:g,singleFileName:u})}async function I(e,t){switch(t.type){case"app":return f(e);case"oauth":e.log.warn(new m.Deprecation('[@octokit/auth-app] {type: "oauth"} is deprecated. Use {type: "oauth-app"} instead'));case"oauth-app":return e.oauthApp({type:"oauth-app"});case"installation":return v(e,{...t,type:"installation"});case"oauth-user":return e.oauthApp(t);default:throw new Error(`Invalid auth type: ${t.type}`)}}var B=i(3360);i(98838);var w=function(e){const t=`^(?:${["/app","/app/hook/config","/app/hook/deliveries","/app/hook/deliveries/{delivery_id}","/app/hook/deliveries/{delivery_id}/attempts","/app/installations","/app/installations/{installation_id}","/app/installations/{installation_id}/access_tokens","/app/installations/{installation_id}/suspended","/app/installation-requests","/marketplace_listing/accounts/{account_id}","/marketplace_listing/plan","/marketplace_listing/plans","/marketplace_listing/plans/{plan_id}/accounts","/marketplace_listing/stubbed/accounts/{account_id}","/marketplace_listing/stubbed/plan","/marketplace_listing/stubbed/plans","/marketplace_listing/stubbed/plans/{plan_id}/accounts","/orgs/{org}/installation","/repos/{owner}/{repo}/installation","/users/{username}/installation"].map((e=>e.split("/").map((e=>e.startsWith("{")?"(?:.+?)":e)).join("/"))).map((e=>`(?:${e})`)).join("|")})$`;return new RegExp(t,"i")}(),b=5e3;async function Q(e,t,i,s){const r=t.endpoint.merge(i,s),n=r.url;if(/\/login\/oauth\/access_token$/.test(n))return t(r);if(function(e){return!!e&&w.test(e.split("?")[0])}(n.replace(t.endpoint.DEFAULTS.baseUrl,""))){const{token:i}=await f(e);let s;r.headers.authorization=`bearer ${i}`;try{s=await t(r)}catch(i){if(function(e){return!(e.message.match(/'Expiration time' claim \('exp'\) must be a numeric value representing the future time at which the assertion expires/)||e.message.match(/'Issued at' claim \('iat'\) must be an Integer representing the time that the assertion was issued/))}(i))throw i;if(void 0===i.response.headers.date)throw i;const s=Math.floor((Date.parse(i.response.headers.date)-Date.parse((new Date).toString()))/1e3);e.log.warn(i.message),e.log.warn(`[@octokit/auth-app] GitHub API time and system time are different by ${s} seconds. Retrying request with the difference accounted for.`);const{token:n}=await f({...e,timeDifference:s});return r.headers.authorization=`bearer ${n}`,t(r)}return s}if((0,B.requiresBasicAuth)(n)){const i=await e.oauthApp({type:"oauth-app"});return r.headers.authorization=i.headers.authorization,t(r)}const{token:o,createdAt:a}=await v(e,{},t);return r.headers.authorization=`token ${o}`,x(e,t,r,a)}async function x(e,t,i,s,r=0){const n=+new Date-+new Date(s);try{return await t(i)}catch(o){if(401!==o.status)throw o;if(n>=b)throw r>0&&(o.message=`After ${r} retries within ${n/1e3}s of creating the installation access token, the response remains 401. At this point, the cause may be an authentication problem or a system outage. Please check https://www.githubstatus.com for status information`),o;const a=1e3*++r;return e.log.warn(`[@octokit/auth-app] Retrying after 401 response to account for token replication delay (retry: ${r}, wait: ${a/1e3}s)`),await new Promise((e=>setTimeout(e,a))),x(e,t,i,s,r)}}var k="6.1.1",D=i(3360);function S(e){if(!e.appId)throw new Error("[@octokit/auth-app] appId option is required");if(!Number.isFinite(+e.appId))throw new Error("[@octokit/auth-app] appId option must be a number or numeric string");if(!e.privateKey)throw new Error("[@octokit/auth-app] privateKey option is required");if("installationId"in e&&!e.installationId)throw new Error("[@octokit/auth-app] installationId is set to a falsy value");const t=Object.assign({warn:console.warn.bind(console)},e.log),i=e.request||d.request.defaults({headers:{"user-agent":`octokit-auth-app.js/${k} ${(0,u.getUserAgent)()}`}}),s=Object.assign({request:i,cache:new E.LRUCache({max:15e3,ttl:354e4})},e,e.installationId?{installationId:Number(e.installationId)}:{},{log:t,oauthApp:(0,h.createOAuthAppAuth)({clientType:"github-app",clientId:e.clientId||"",clientSecret:e.clientSecret||"",request:i})});return Object.assign(I.bind(null,s),{hook:Q.bind(null,s)})}},62419:(e,t,i)=>{"use strict";i.r(t),i.d(t,{createOAuthAppAuth:()=>u,createOAuthUserAuth:()=>a.createOAuthUserAuth});var s=i(21375),r=i(65683),n=i(96756),o=i.n(n),a=i(3360);async function c(e,t){if("oauth-app"===t.type)return{type:"oauth-app",clientId:e.clientId,clientSecret:e.clientSecret,clientType:e.clientType,headers:{authorization:`basic ${o()(`${e.clientId}:${e.clientSecret}`)}`}};if("factory"in t){const{type:i,...s}={...t,...e};return t.factory(s)}const i={clientId:e.clientId,clientSecret:e.clientSecret,request:e.request,...t};return(e.clientType,await(0,a.createOAuthUserAuth)({...i,clientType:e.clientType}))()}var l=i(90420);async function p(e,t,i,s){let r=t.endpoint.merge(i,s);if(/\/login\/(oauth\/access_token|device\/code)$/.test(r.url))return t(r);if("github-app"===e.clientType&&!(0,l.X)(r.url))throw new Error(`[@octokit/auth-oauth-app] GitHub Apps cannot use their client ID/secret for basic authentication for endpoints other than "/applications/{client_id}/**". "${r.method} ${r.url}" is not supported.`);const n=o()(`${e.clientId}:${e.clientSecret}`);r.headers.authorization=`basic ${n}`;try{return await t(r)}catch(e){if(401!==e.status)throw e;throw e.message=`[@octokit/auth-oauth-app] "${r.method} ${r.url}" does not support clientId/clientSecret basic authentication.`,e}}const A="7.1.0";function u(e){const t=Object.assign({request:r.request.defaults({headers:{"user-agent":`octokit-auth-oauth-app.js/${A} ${(0,s.getUserAgent)()}`}}),clientType:"oauth-app"},e);return Object.assign(c.bind(null,t),{hook:p.bind(null,t)})}},3360:(e,t,i)=>{"use strict";i.r(t),i.d(t,{createOAuthUserAuth:()=>Q,requiresBasicAuth:()=>w.X});var s=i(21375),r=i(65683);const n="4.1.0";var o=i(63855),a=i(85579);async function c(e,t){const i=function(e,t){if(!0===t.refresh)return!1;if(!e.authentication)return!1;if("github-app"===e.clientType)return e.authentication;const i=e.authentication;return("scopes"in t&&t.scopes||e.scopes).join(" ")===i.scopes.join(" ")&&i}(e,t.auth);if(i)return i;const{data:s}=await(0,o.T)({clientType:e.clientType,clientId:e.clientId,request:t.request||e.request,scopes:t.auth.scopes||e.scopes});await e.onVerification(s);const r=await p(t.request||e.request,e.clientId,e.clientType,s);return e.authentication=r,r}async function l(e){await new Promise((t=>setTimeout(t,1e3*e)))}async function p(e,t,i,s){try{const r={clientId:t,request:e,code:s.device_code},{authentication:n}="oauth-app"===i?await(0,a.i)({...r,clientType:"oauth-app"}):await(0,a.i)({...r,clientType:"github-app"});return{type:"token",tokenType:"oauth",...n}}catch(r){if(!r.response)throw r;const n=r.response.data.error;if("authorization_pending"===n)return await l(s.interval),p(e,t,i,s);if("slow_down"===n)return await l(s.interval+5),p(e,t,i,s);throw r}}async function A(e,t){return c(e,{auth:t})}async function u(e,t,i,s){let r=t.endpoint.merge(i,s);if(/\/login\/(oauth\/access_token|device\/code)$/.test(r.url))return t(r);const{token:n}=await c(e,{request:t,auth:{type:"oauth"}});return r.headers.authorization=`token ${n}`,t(r)}const d="6.1.0";function h(e){const t=e.request||r.request.defaults({headers:{"user-agent":`octokit-auth-oauth-device.js/${d} ${(0,s.getUserAgent)()}`}}),{request:i=t,...n}=e,o="github-app"===e.clientType?{...n,clientType:"github-app",request:i}:{...n,clientType:"oauth-app",request:i,scopes:e.scopes||[]};if(!e.clientId)throw new Error('[@octokit/auth-oauth-device] "clientId" option must be set (https://github.com/octokit/auth-oauth-device.js#usage)');if(!e.onVerification)throw new Error('[@octokit/auth-oauth-device] "onVerification" option must be a function (https://github.com/octokit/auth-oauth-device.js#usage)');return Object.assign(A.bind(null,o),{hook:u.bind(null,o)})}var m=i(71060);var g=i(27550),f=i(4923),E=i(9147),C=i(46886),y=i(62616);async function v(e,t={}){if(e.authentication||(e.authentication=(e.clientType,await async function(e){if("code"in e.strategyOptions){const{authentication:t}=await(0,m.y)({clientId:e.clientId,clientSecret:e.clientSecret,clientType:e.clientType,onTokenCreated:e.onTokenCreated,...e.strategyOptions,request:e.request});return{type:"token",tokenType:"oauth",...t}}if("onVerification"in e.strategyOptions){const t=h({clientType:e.clientType,clientId:e.clientId,onTokenCreated:e.onTokenCreated,...e.strategyOptions,request:e.request}),i=await t({type:"oauth"});return{clientSecret:e.clientSecret,...i}}if("token"in e.strategyOptions)return{type:"token",tokenType:"oauth",clientId:e.clientId,clientSecret:e.clientSecret,clientType:e.clientType,onTokenCreated:e.onTokenCreated,...e.strategyOptions};throw new Error("[@octokit/auth-oauth-user] Invalid strategy options")}(e))),e.authentication.invalid)throw new Error("[@octokit/auth-oauth-user] Token is invalid");const i=e.authentication;if("expiresAt"in i&&("refresh"===t.type||new Date(i.expiresAt){"use strict";i.d(t,{X:()=>r});const s=/\/applications\/[^/]+\/(token|grant)s?/;function r(e){return e&&s.test(e)}},37573:(e,t,i)=>{"use strict";async function s(e){return{type:"unauthenticated",reason:e}}i.r(t),i.d(t,{createUnauthenticatedAuth:()=>o});var r=/\babuse\b/i;async function n(e,t,i,s){const n=t.endpoint.merge(i,s);return t(n).catch((t=>{if(404===t.status)throw t.message=`Not found. May be due to lack of authentication. Reason: ${e}`,t;if(function(e){return 403===e.status&&!!e.response&&"0"===e.response.headers["x-ratelimit-remaining"]}(t))throw t.message=`API rate limit exceeded. This maybe caused by the lack of authentication. Reason: ${e}`,t;if(function(e){return 403===e.status&&r.test(e.message)}(t))throw t.message=`You have triggered an abuse detection mechanism. This maybe caused by the lack of authentication. Reason: ${e}`,t;if(401===t.status)throw t.message=`Unauthorized. "${n.method} ${n.url}" failed most likely due to lack of authentication. Reason: ${e}`,t;throw t.status>=400&&t.status<500&&(t.message=t.message.replace(/\.?$/,`. May be caused by lack of authentication (${e}).`)),t}))}var o=function(e){if(!e||!e.reason)throw new Error("[@octokit/auth-unauthenticated] No reason passed to createUnauthenticatedAuth");return Object.assign(s.bind(null,e.reason),{hook:n.bind(null,e.reason)})}},96049:(e,t,i)=>{"use strict";i.r(t),i.d(t,{Octokit:()=>I});var s=i(21375),r=i(8903),n=i(65683),o=class extends Error{constructor(e,t,i){super("Request failed due to following response errors:\n"+i.errors.map((e=>` - ${e.message}`)).join("\n")),this.request=e,this.headers=t,this.response=i,this.name="GraphqlResponseError",this.errors=i.errors,this.data=i.data,Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor)}},a=["method","baseUrl","url","headers","request","query","mediaType"],c=["query","method","url"],l=/\/api\/v3\/?$/;function p(e,t){const i=e.defaults(t);return Object.assign(((e,t)=>function(e,t,i){if(i){if("string"==typeof t&&"query"in i)return Promise.reject(new Error('[@octokit/graphql] "query" cannot be used as variable name'));for(const e in i)if(c.includes(e))return Promise.reject(new Error(`[@octokit/graphql] "${e}" cannot be used as variable name`))}const s="string"==typeof t?Object.assign({query:t},i):t,r=Object.keys(s).reduce(((e,t)=>a.includes(t)?(e[t]=s[t],e):(e.variables||(e.variables={}),e.variables[t]=s[t],e)),{}),n=s.baseUrl||e.endpoint.DEFAULTS.baseUrl;return l.test(n)&&(r.url=n.replace(l,"/api/graphql")),e(r).then((e=>{if(e.data.errors){const t={};for(const i of Object.keys(e.headers))t[i]=e.headers[i];throw new o(r,t,e.data)}return e.data.data}))}(i,e,t)),{defaults:p.bind(null,i),endpoint:i.endpoint})}p(n.request,{headers:{"user-agent":`octokit-graphql.js/7.1.0 ${(0,s.getUserAgent)()}`},method:"POST",url:"/graphql"});const A=/^v1\./,u=/^ghs_/,d=/^ghu_/;async function h(e){const t=3===e.split(/\./).length,i=A.test(e)||u.test(e),s=d.test(e);return{type:"token",token:e,tokenType:t?"app":i?"installation":s?"user-to-server":"oauth"}}async function m(e,t,i,s){const r=t.endpoint.merge(i,s);return r.headers.authorization=function(e){return 3===e.split(/\./).length?`bearer ${e}`:`token ${e}`}(e),t(r)}const g=function(e){if(!e)throw new Error("[@octokit/auth-token] No token passed to createTokenAuth");if("string"!=typeof e)throw new Error("[@octokit/auth-token] Token passed to createTokenAuth is not a string");return e=e.replace(/^(token|bearer) +/i,""),Object.assign(h.bind(null,e),{hook:m.bind(null,e)})};var f="5.2.0",E=()=>{},C=console.warn.bind(console),y=console.error.bind(console),v=`octokit-core.js/${f} ${(0,s.getUserAgent)()}`,I=class{static{this.VERSION=f}static defaults(e){return class extends(this){constructor(...t){const i=t[0]||{};super("function"!=typeof e?Object.assign({},e,i,i.userAgent&&e.userAgent?{userAgent:`${i.userAgent} ${e.userAgent}`}:null):e(i))}}}static{this.plugins=[]}static plugin(...e){const t=this.plugins;return class extends(this){static{this.plugins=t.concat(e.filter((e=>!t.includes(e))))}}}constructor(e={}){const t=new r.Collection,i={baseUrl:n.request.endpoint.DEFAULTS.baseUrl,headers:{},request:Object.assign({},e.request,{hook:t.bind(null,"request")}),mediaType:{previews:[],format:""}};var s;if(i.headers["user-agent"]=e.userAgent?`${e.userAgent} ${v}`:v,e.baseUrl&&(i.baseUrl=e.baseUrl),e.previews&&(i.mediaType.previews=e.previews),e.timeZone&&(i.headers["time-zone"]=e.timeZone),this.request=n.request.defaults(i),this.graphql=(s=this.request,p(s,{method:"POST",url:"/graphql"})).defaults(i),this.log=Object.assign({debug:E,info:E,warn:C,error:y},e.log),this.hook=t,e.authStrategy){const{authStrategy:i,...s}=e,r=i(Object.assign({request:this.request,log:this.log,octokit:this,octokitOptions:s},e.auth));t.wrap("request",r.hook),this.auth=r}else if(e.auth){const i=g(e.auth);t.wrap("request",i.hook),this.auth=i}else this.auth=async()=>({type:"unauthenticated"});const o=this.constructor;for(let t=0;t{"use strict";function s(e){return"[object Object]"===Object.prototype.toString.call(e)}function r(e){var t,i;return!1!==s(e)&&(void 0===(t=e.constructor)||!1!==s(i=t.prototype)&&!1!==i.hasOwnProperty("isPrototypeOf"))}i.r(t),i.d(t,{GraphqlResponseError:()=>Qe,graphql:()=>_e,withCustomRequest:()=>Re});var n=i(21375);function o(e,t){const i=Object.assign({},e);return Object.keys(t).forEach((s=>{r(t[s])?s in e?i[s]=o(e[s],t[s]):Object.assign(i,{[s]:t[s]}):Object.assign(i,{[s]:t[s]})})),i}function a(e){for(const t in e)void 0===e[t]&&delete e[t];return e}function c(e,t,i){if("string"==typeof t){let[e,s]=t.split(" ");i=Object.assign(s?{method:e,url:s}:{url:e},i)}else i=Object.assign({},t);var s;i.headers=(s=i.headers)?Object.keys(s).reduce(((e,t)=>(e[t.toLowerCase()]=s[t],e)),{}):{},a(i),a(i.headers);const r=o(e||{},i);return e&&e.mediaType.previews.length&&(r.mediaType.previews=e.mediaType.previews.filter((e=>!r.mediaType.previews.includes(e))).concat(r.mediaType.previews)),r.mediaType.previews=r.mediaType.previews.map((e=>e.replace(/-preview/,""))),r}const l=/\{[^}]+\}/g;function p(e){return e.replace(/^\W+|\W+$/g,"").split(/,/)}function A(e,t){return Object.keys(e).filter((e=>!t.includes(e))).reduce(((t,i)=>(t[i]=e[i],t)),{})}function u(e){return e.split(/(%[0-9A-Fa-f]{2})/g).map((function(e){return/%[0-9A-Fa-f]/.test(e)||(e=encodeURI(e).replace(/%5B/g,"[").replace(/%5D/g,"]")),e})).join("")}function d(e){return encodeURIComponent(e).replace(/[!'()*]/g,(function(e){return"%"+e.charCodeAt(0).toString(16).toUpperCase()}))}function h(e,t,i){return t="+"===e||"#"===e?u(t):d(t),i?d(i)+"="+t:t}function m(e){return null!=e}function g(e){return";"===e||"&"===e||"?"===e}function f(e,t){var i=["+","#",".","/",";","?","&"];return e.replace(/\{([^\{\}]+)\}|([^\{\}]+)/g,(function(e,s,r){if(s){let e="";const r=[];if(-1!==i.indexOf(s.charAt(0))&&(e=s.charAt(0),s=s.substr(1)),s.split(/,/g).forEach((function(i){var s=/([^:\*]*)(?::(\d+)|(\*))?/.exec(i);r.push(function(e,t,i,s){var r=e[i],n=[];if(m(r)&&""!==r)if("string"==typeof r||"number"==typeof r||"boolean"==typeof r)r=r.toString(),s&&"*"!==s&&(r=r.substring(0,parseInt(s,10))),n.push(h(t,r,g(t)?i:""));else if("*"===s)Array.isArray(r)?r.filter(m).forEach((function(e){n.push(h(t,e,g(t)?i:""))})):Object.keys(r).forEach((function(e){m(r[e])&&n.push(h(t,r[e],e))}));else{const e=[];Array.isArray(r)?r.filter(m).forEach((function(i){e.push(h(t,i))})):Object.keys(r).forEach((function(i){m(r[i])&&(e.push(d(i)),e.push(h(t,r[i].toString())))})),g(t)?n.push(d(i)+"="+e.join(",")):0!==e.length&&n.push(e.join(","))}else";"===t?m(r)&&n.push(d(i)):""!==r||"&"!==t&&"?"!==t?""===r&&n.push(""):n.push(d(i)+"=");return n}(t,e,s[1],s[2]||s[3]))})),e&&"+"!==e){var n=",";return"?"===e?n="&":"#"!==e&&(n=e),(0!==r.length?e:"")+r.join(n)}return r.join(",")}return u(r)}))}function E(e){let t,i=e.method.toUpperCase(),s=(e.url||"/").replace(/:([a-z]\w+)/g,"{$1}"),r=Object.assign({},e.headers),n=A(e,["method","baseUrl","url","headers","request","mediaType"]);const o=function(e){const t=e.match(l);return t?t.map(p).reduce(((e,t)=>e.concat(t)),[]):[]}(s);var a;s=(a=s,{expand:f.bind(null,a)}).expand(n),/^http/.test(s)||(s=e.baseUrl+s);const c=A(n,Object.keys(e).filter((e=>o.includes(e))).concat("baseUrl"));if(!/application\/octet-stream/i.test(r.accept)&&(e.mediaType.format&&(r.accept=r.accept.split(/,/).map((t=>t.replace(/application\/vnd(\.\w+)(\.v3)?(\.\w+)?(\+json)?$/,`application/vnd$1$2.${e.mediaType.format}`))).join(",")),e.mediaType.previews.length)){const t=r.accept.match(/[\w-]+(?=-preview)/g)||[];r.accept=t.concat(e.mediaType.previews).map((t=>`application/vnd.github.${t}-preview${e.mediaType.format?`.${e.mediaType.format}`:"+json"}`)).join(",")}return["GET","HEAD"].includes(i)?s=function(e,t){const i=/\?/.test(e)?"&":"?",s=Object.keys(t);return 0===s.length?e:e+i+s.map((e=>"q"===e?"q="+t.q.split("+").map(encodeURIComponent).join("+"):`${e}=${encodeURIComponent(t[e])}`)).join("&")}(s,c):"data"in c?t=c.data:Object.keys(c).length?t=c:r["content-length"]=0,r["content-type"]||void 0===t||(r["content-type"]="application/json; charset=utf-8"),["PATCH","PUT"].includes(i)&&void 0===t&&(t=""),Object.assign({method:i,url:s,headers:r},void 0!==t?{body:t}:null,e.request?{request:e.request}:null)}function C(e,t,i){return E(c(e,t,i))}const y=function e(t,i){const s=c(t,i),r=C.bind(null,s);return Object.assign(r,{DEFAULTS:s,defaults:e.bind(null,s),merge:c.bind(null,s),parse:E})}(null,{method:"GET",baseUrl:"https://api.github.com",headers:{accept:"application/vnd.github.v3+json","user-agent":`octokit-endpoint.js/6.0.12 ${(0,n.getUserAgent)()}`},mediaType:{format:"",previews:[]}});var v=i(12781),I=i(13685),B=i(57310),w=i(89897),b=i(95687),Q=i(59796);const x=v.Readable,k=Symbol("buffer"),D=Symbol("type");class S{constructor(){this[D]="";const e=arguments[0],t=arguments[1],i=[];let s=0;if(e){const t=e,r=Number(t.length);for(let e=0;e1&&void 0!==arguments[1]?arguments[1]:{},s=i.size;let r=void 0===s?0:s;var n=i.timeout;let o=void 0===n?0:n;null==e?e=null:O(e)?e=Buffer.from(e.toString()):M(e)||Buffer.isBuffer(e)||("[object ArrayBuffer]"===Object.prototype.toString.call(e)?e=Buffer.from(e):ArrayBuffer.isView(e)?e=Buffer.from(e.buffer,e.byteOffset,e.byteLength):e instanceof v||(e=Buffer.from(String(e)))),this[T]={body:e,disturbed:!1,error:null},this.size=r,this.timeout=o,e instanceof v&&e.on("error",(function(e){const i="AbortError"===e.name?e:new _(`Invalid response body while trying to fetch ${t.url}: ${e.message}`,"system",e);t[T].error=i}))}function L(){var e=this;if(this[T].disturbed)return N.Promise.reject(new TypeError(`body used already for: ${this.url}`));if(this[T].disturbed=!0,this[T].error)return N.Promise.reject(this[T].error);let t=this.body;if(null===t)return N.Promise.resolve(Buffer.alloc(0));if(M(t)&&(t=t.stream()),Buffer.isBuffer(t))return N.Promise.resolve(t);if(!(t instanceof v))return N.Promise.resolve(Buffer.alloc(0));let i=[],s=0,r=!1;return new N.Promise((function(n,o){let a;e.timeout&&(a=setTimeout((function(){r=!0,o(new _(`Response timeout while trying to fetch ${e.url} (over ${e.timeout}ms)`,"body-timeout"))}),e.timeout)),t.on("error",(function(t){"AbortError"===t.name?(r=!0,o(t)):o(new _(`Invalid response body while trying to fetch ${e.url}: ${t.message}`,"system",t))})),t.on("data",(function(t){if(!r&&null!==t){if(e.size&&s+t.length>e.size)return r=!0,void o(new _(`content size at ${e.url} over limit: ${e.size}`,"max-size"));s+=t.length,i.push(t)}})),t.on("end",(function(){if(!r){clearTimeout(a);try{n(Buffer.concat(i,s))}catch(t){o(new _(`Could not create Buffer from response body for ${e.url}: ${t.message}`,"system",t))}}}))}))}function O(e){return"object"==typeof e&&"function"==typeof e.append&&"function"==typeof e.delete&&"function"==typeof e.get&&"function"==typeof e.getAll&&"function"==typeof e.has&&"function"==typeof e.set&&("URLSearchParams"===e.constructor.name||"[object URLSearchParams]"===Object.prototype.toString.call(e)||"function"==typeof e.sort)}function M(e){return"object"==typeof e&&"function"==typeof e.arrayBuffer&&"string"==typeof e.type&&"function"==typeof e.stream&&"function"==typeof e.constructor&&"string"==typeof e.constructor.name&&/^(Blob|File)$/.test(e.constructor.name)&&/^(Blob|File)$/.test(e[Symbol.toStringTag])}function U(e){let t,i,s=e.body;if(e.bodyUsed)throw new Error("cannot clone body after it is used");return s instanceof v&&"function"!=typeof s.getBoundary&&(t=new F,i=new F,s.pipe(t),s.pipe(i),e[T].body=t,s=i),s}function P(e){return null===e?null:"string"==typeof e?"text/plain;charset=UTF-8":O(e)?"application/x-www-form-urlencoded;charset=UTF-8":M(e)?e.type||null:Buffer.isBuffer(e)||"[object ArrayBuffer]"===Object.prototype.toString.call(e)||ArrayBuffer.isView(e)?null:"function"==typeof e.getBoundary?`multipart/form-data;boundary=${e.getBoundary()}`:e instanceof v?null:"text/plain;charset=UTF-8"}function G(e){const t=e.body;return null===t?0:M(t)?t.size:Buffer.isBuffer(t)?t.length:t&&"function"==typeof t.getLengthSync&&(t._lengthRetrievers&&0==t._lengthRetrievers.length||t.hasKnownLength&&t.hasKnownLength())?t.getLengthSync():null}N.prototype={get body(){return this[T].body},get bodyUsed(){return this[T].disturbed},arrayBuffer(){return L.call(this).then((function(e){return e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength)}))},blob(){let e=this.headers&&this.headers.get("content-type")||"";return L.call(this).then((function(t){return Object.assign(new S([],{type:e.toLowerCase()}),{[k]:t})}))},json(){var e=this;return L.call(this).then((function(t){try{return JSON.parse(t.toString())}catch(t){return N.Promise.reject(new _(`invalid json response body at ${e.url} reason: ${t.message}`,"invalid-json"))}}))},text(){return L.call(this).then((function(e){return e.toString()}))},buffer(){return L.call(this)},textConverted(){var e=this;return L.call(this).then((function(t){return function(e,t){if("function"!=typeof R)throw new Error("The package `encoding` must be installed to use the textConverted() function");const i=t.get("content-type");let s,r,n="utf-8";return i&&(s=/charset=([^;]*)/i.exec(i)),r=e.slice(0,1024).toString(),!s&&r&&(s=/0&&void 0!==arguments[0]?arguments[0]:void 0;if(this[Y]=Object.create(null),e instanceof W){const t=e.raw(),i=Object.keys(t);for(const e of i)for(const i of t[e])this.append(e,i)}else if(null==e);else{if("object"!=typeof e)throw new TypeError("Provided initializer must be an object");{const t=e[Symbol.iterator];if(null!=t){if("function"!=typeof t)throw new TypeError("Header pairs must be iterable");const i=[];for(const t of e){if("object"!=typeof t||"function"!=typeof t[Symbol.iterator])throw new TypeError("Each header pair must be iterable");i.push(Array.from(t))}for(const e of i){if(2!==e.length)throw new TypeError("Each header pair must be a name/value tuple");this.append(e[0],e[1])}}else for(const t of Object.keys(e)){const i=e[t];this.append(t,i)}}}}get(e){H(e=`${e}`);const t=q(this[Y],e);return void 0===t?null:this[Y][t].join(", ")}forEach(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0,i=z(this),s=0;for(;s1&&void 0!==arguments[1]?arguments[1]:"key+value";return Object.keys(e[Y]).sort().map("key"===t?function(e){return e.toLowerCase()}:"value"===t?function(t){return e[Y][t].join(", ")}:function(t){return[t.toLowerCase(),e[Y][t].join(", ")]})}W.prototype.entries=W.prototype[Symbol.iterator],Object.defineProperty(W.prototype,Symbol.toStringTag,{value:"Headers",writable:!1,enumerable:!1,configurable:!0}),Object.defineProperties(W.prototype,{get:{enumerable:!0},forEach:{enumerable:!0},set:{enumerable:!0},append:{enumerable:!0},has:{enumerable:!0},delete:{enumerable:!0},keys:{enumerable:!0},values:{enumerable:!0},entries:{enumerable:!0}});const $=Symbol("internal");function X(e,t){const i=Object.create(K);return i[$]={target:e,kind:t,index:0},i}const K=Object.setPrototypeOf({next(){if(!this||Object.getPrototypeOf(this)!==K)throw new TypeError("Value of `this` is not a HeadersIterator");var e=this[$];const t=e.target,i=e.kind,s=e.index,r=z(t,i);return s>=r.length?{value:void 0,done:!0}:(this[$].index=s+1,{value:r[s],done:!1})}},Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())));function Z(e){const t=Object.assign({__proto__:null},e[Y]),i=q(e[Y],"Host");return void 0!==i&&(t[i]=t[i][0]),t}Object.defineProperty(K,Symbol.toStringTag,{value:"HeadersIterator",writable:!1,enumerable:!1,configurable:!0});const ee=Symbol("Response internals"),te=I.STATUS_CODES;class ie{constructor(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};N.call(this,e,t);const i=t.status||200,s=new W(t.headers);if(null!=e&&!s.has("Content-Type")){const t=P(e);t&&s.append("Content-Type",t)}this[ee]={url:t.url,status:i,statusText:t.statusText||te[i],headers:s,counter:t.counter}}get url(){return this[ee].url||""}get status(){return this[ee].status}get ok(){return this[ee].status>=200&&this[ee].status<300}get redirected(){return this[ee].counter>0}get statusText(){return this[ee].statusText}get headers(){return this[ee].headers}clone(){return new ie(U(this),{url:this.url,status:this.status,statusText:this.statusText,headers:this.headers,ok:this.ok,redirected:this.redirected})}}N.mixIn(ie.prototype),Object.defineProperties(ie.prototype,{url:{enumerable:!0},status:{enumerable:!0},ok:{enumerable:!0},redirected:{enumerable:!0},statusText:{enumerable:!0},headers:{enumerable:!0},clone:{enumerable:!0}}),Object.defineProperty(ie.prototype,Symbol.toStringTag,{value:"Response",writable:!1,enumerable:!1,configurable:!0});const se=Symbol("Request internals"),re=B.URL||w.URL,ne=B.parse,oe=B.format;function ae(e){return/^[a-zA-Z][a-zA-Z\d+\-.]*:/.exec(e)&&(e=new re(e).toString()),ne(e)}const ce="destroy"in v.Readable.prototype;function le(e){return"object"==typeof e&&"object"==typeof e[se]}class pe{constructor(e){let t,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};le(e)?t=ae(e.url):(t=e&&e.href?ae(e.href):ae(`${e}`),e={});let s=i.method||e.method||"GET";if(s=s.toUpperCase(),(null!=i.body||le(e)&&null!==e.body)&&("GET"===s||"HEAD"===s))throw new TypeError("Request with GET/HEAD method cannot have body");let r=null!=i.body?i.body:le(e)&&null!==e.body?U(e):null;N.call(this,r,{timeout:i.timeout||e.timeout||0,size:i.size||e.size||0});const n=new W(i.headers||e.headers||{});if(null!=r&&!n.has("Content-Type")){const e=P(r);e&&n.append("Content-Type",e)}let o=le(e)?e.signal:null;if("signal"in i&&(o=i.signal),null!=o&&!function(e){const t=e&&"object"==typeof e&&Object.getPrototypeOf(e);return!(!t||"AbortSignal"!==t.constructor.name)}(o))throw new TypeError("Expected signal to be an instanceof AbortSignal");this[se]={method:s,redirect:i.redirect||e.redirect||"follow",headers:n,parsedURL:t,signal:o},this.follow=void 0!==i.follow?i.follow:void 0!==e.follow?e.follow:20,this.compress=void 0!==i.compress?i.compress:void 0===e.compress||e.compress,this.counter=i.counter||e.counter||0,this.agent=i.agent||e.agent}get method(){return this[se].method}get url(){return oe(this[se].parsedURL)}get headers(){return this[se].headers}get redirect(){return this[se].redirect}get signal(){return this[se].signal}clone(){return new pe(this)}}function Ae(e){Error.call(this,e),this.type="aborted",this.message=e,Error.captureStackTrace(this,this.constructor)}N.mixIn(pe.prototype),Object.defineProperty(pe.prototype,Symbol.toStringTag,{value:"Request",writable:!1,enumerable:!1,configurable:!0}),Object.defineProperties(pe.prototype,{method:{enumerable:!0},url:{enumerable:!0},headers:{enumerable:!0},redirect:{enumerable:!0},clone:{enumerable:!0},signal:{enumerable:!0}}),Ae.prototype=Object.create(Error.prototype),Ae.prototype.constructor=Ae,Ae.prototype.name="AbortError";const ue=B.URL||w.URL,de=v.PassThrough;function he(e,t){if(!he.Promise)throw new Error("native promise missing, set fetch.Promise to your favorite alternative");return N.Promise=he.Promise,new he.Promise((function(i,s){const r=new pe(e,t),n=function(e){const t=e[se].parsedURL,i=new W(e[se].headers);if(i.has("Accept")||i.set("Accept","*/*"),!t.protocol||!t.hostname)throw new TypeError("Only absolute URLs are supported");if(!/^https?:$/.test(t.protocol))throw new TypeError("Only HTTP(S) protocols are supported");if(e.signal&&e.body instanceof v.Readable&&!ce)throw new Error("Cancellation of streamed requests with AbortSignal is not supported in node < 8");let s=null;if(null==e.body&&/^(POST|PUT)$/i.test(e.method)&&(s="0"),null!=e.body){const t=G(e);"number"==typeof t&&(s=String(t))}s&&i.set("Content-Length",s),i.has("User-Agent")||i.set("User-Agent","node-fetch/1.0 (+https://github.com/bitinn/node-fetch)"),e.compress&&!i.has("Accept-Encoding")&&i.set("Accept-Encoding","gzip,deflate");let r=e.agent;return"function"==typeof r&&(r=r(t)),Object.assign({},t,{method:e.method,headers:Z(i),agent:r})}(r),o=("https:"===n.protocol?b:I).request,a=r.signal;let c=null;const l=function(){let e=new Ae("The user aborted a request.");s(e),r.body&&r.body instanceof v.Readable&&me(r.body,e),c&&c.body&&c.body.emit("error",e)};if(a&&a.aborted)return void l();const p=function(){l(),d()},A=o(n);let u;function d(){A.abort(),a&&a.removeEventListener("abort",p),clearTimeout(u)}a&&a.addEventListener("abort",p),r.timeout&&A.once("socket",(function(e){u=setTimeout((function(){s(new _(`network timeout at: ${r.url}`,"request-timeout")),d()}),r.timeout)})),A.on("error",(function(e){s(new _(`request to ${r.url} failed, reason: ${e.message}`,"system",e)),c&&c.body&&me(c.body,e),d()})),function(e,t){let i;e.on("socket",(function(e){i=e})),e.on("response",(function(e){const s=e.headers;"chunked"!==s["transfer-encoding"]||s["content-length"]||e.once("close",(function(e){if(i&&i.listenerCount("data")>0&&!e){const e=new Error("Premature close");e.code="ERR_STREAM_PREMATURE_CLOSE",t(e)}}))}))}(A,(function(e){a&&a.aborted||c&&c.body&&me(c.body,e)})),parseInt(process.version.substring(1))<14&&A.on("socket",(function(e){e.addListener("close",(function(t){const i=e.listenerCount("data")>0;if(c&&i&&!t&&(!a||!a.aborted)){const e=new Error("Premature close");e.code="ERR_STREAM_PREMATURE_CLOSE",c.body.emit("error",e)}}))})),A.on("response",(function(e){clearTimeout(u);const t=function(e){const t=new W;for(const i of Object.keys(e))if(!V.test(i))if(Array.isArray(e[i]))for(const s of e[i])j.test(s)||(void 0===t[Y][i]?t[Y][i]=[s]:t[Y][i].push(s));else j.test(e[i])||(t[Y][i]=[e[i]]);return t}(e.headers);if(he.isRedirect(e.statusCode)){const o=t.get("Location");let a=null;try{a=null===o?null:new ue(o,r.url).toString()}catch(e){if("manual"!==r.redirect)return s(new _(`uri requested responds with an invalid redirect URL: ${o}`,"invalid-redirect")),void d()}switch(r.redirect){case"error":return s(new _(`uri requested responds with a redirect, redirect mode is set to error: ${r.url}`,"no-redirect")),void d();case"manual":if(null!==a)try{t.set("Location",a)}catch(e){s(e)}break;case"follow":if(null===a)break;if(r.counter>=r.follow)return s(new _(`maximum redirect reached at: ${r.url}`,"max-redirect")),void d();const o={headers:new W(r.headers),follow:r.follow,counter:r.counter+1,agent:r.agent,compress:r.compress,method:r.method,body:r.body,signal:r.signal,timeout:r.timeout,size:r.size};if(!function(e,t){const i=new ue(t).hostname,s=new ue(e).hostname;return i===s||"."===i[i.length-s.length-1]&&i.endsWith(s)}(r.url,a)||(n=r.url,new ue(a).protocol!==new ue(n).protocol))for(const e of["authorization","www-authenticate","cookie","cookie2"])o.headers.delete(e);return 303!==e.statusCode&&r.body&&null===G(r)?(s(new _("Cannot follow redirect with body being a readable stream","unsupported-redirect")),void d()):(303!==e.statusCode&&(301!==e.statusCode&&302!==e.statusCode||"POST"!==r.method)||(o.method="GET",o.body=void 0,o.headers.delete("content-length")),i(he(new pe(a,o))),void d())}}var n;e.once("end",(function(){a&&a.removeEventListener("abort",p)}));let o=e.pipe(new de);const l={url:r.url,status:e.statusCode,statusText:e.statusMessage,headers:t,size:r.size,timeout:r.timeout,counter:r.counter},A=t.get("Content-Encoding");if(!r.compress||"HEAD"===r.method||null===A||204===e.statusCode||304===e.statusCode)return c=new ie(o,l),void i(c);const h={flush:Q.Z_SYNC_FLUSH,finishFlush:Q.Z_SYNC_FLUSH};if("gzip"==A||"x-gzip"==A)return o=o.pipe(Q.createGunzip(h)),c=new ie(o,l),void i(c);if("deflate"==A||"x-deflate"==A){const t=e.pipe(new de);return t.once("data",(function(e){o=8==(15&e[0])?o.pipe(Q.createInflate()):o.pipe(Q.createInflateRaw()),c=new ie(o,l),i(c)})),void t.on("end",(function(){c||(c=new ie(o,l),i(c))}))}if("br"==A&&"function"==typeof Q.createBrotliDecompress)return o=o.pipe(Q.createBrotliDecompress()),c=new ie(o,l),void i(c);c=new ie(o,l),i(c)})),function(e,t){const i=t.body;null===i?e.end():M(i)?i.stream().pipe(e):Buffer.isBuffer(i)?(e.write(i),e.end()):i.pipe(e)}(A,r)}))}function me(e,t){e.destroy?e.destroy(t):(e.emit("error",t),e.end())}he.isRedirect=function(e){return 301===e||302===e||303===e||307===e||308===e},he.Promise=global.Promise;const ge=he;var fe=i(57751),Ee=i(36219),Ce=i.n(Ee);const ye=Ce()((e=>console.warn(e))),ve=Ce()((e=>console.warn(e)));class Ie extends Error{constructor(e,t,i){let s;super(e),Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor),this.name="HttpError",this.status=t,"headers"in i&&void 0!==i.headers&&(s=i.headers),"response"in i&&(this.response=i.response,s=i.response.headers);const r=Object.assign({},i.request);i.request.headers.authorization&&(r.headers=Object.assign({},i.request.headers,{authorization:i.request.headers.authorization.replace(/ .*$/," [REDACTED]")})),r.url=r.url.replace(/\bclient_secret=\w+/g,"client_secret=[REDACTED]").replace(/\baccess_token=\w+/g,"access_token=[REDACTED]"),this.request=r,Object.defineProperty(this,"code",{get:()=>(ye(new fe.Deprecation("[@octokit/request-error] `error.code` is deprecated, use `error.status`.")),t)}),Object.defineProperty(this,"headers",{get:()=>(ve(new fe.Deprecation("[@octokit/request-error] `error.headers` is deprecated, use `error.response.headers`.")),s||{})})}}function Be(e){const t=e.request&&e.request.log?e.request.log:console;(r(e.body)||Array.isArray(e.body))&&(e.body=JSON.stringify(e.body));let i,s,n={};return(e.request&&e.request.fetch||ge)(e.url,Object.assign({method:e.method,body:e.body,headers:e.headers,redirect:e.redirect},e.request)).then((async r=>{s=r.url,i=r.status;for(const e of r.headers)n[e[0]]=e[1];if("deprecation"in n){const i=n.link&&n.link.match(/<([^>]+)>; rel="deprecation"/),s=i&&i.pop();t.warn(`[@octokit/request] "${e.method} ${e.url}" is deprecated. It is scheduled to be removed on ${n.sunset}${s?`. See ${s}`:""}`)}if(204!==i&&205!==i){if("HEAD"===e.method){if(i<400)return;throw new Ie(r.statusText,i,{response:{url:s,status:i,headers:n,data:void 0},request:e})}if(304===i)throw new Ie("Not modified",i,{response:{url:s,status:i,headers:n,data:await we(r)},request:e});if(i>=400){const t=await we(r),o=new Ie(function(e){return"string"==typeof e?e:"message"in e?Array.isArray(e.errors)?`${e.message}: ${e.errors.map(JSON.stringify).join(", ")}`:e.message:`Unknown error: ${JSON.stringify(e)}`}(t),i,{response:{url:s,status:i,headers:n,data:t},request:e});throw o}return we(r)}})).then((e=>({status:i,url:s,headers:n,data:e}))).catch((t=>{if(t instanceof Ie)throw t;throw new Ie(t.message,500,{request:e})}))}async function we(e){const t=e.headers.get("content-type");return/application\/json/.test(t)?e.json():!t||/^text\/|charset=utf-8$/.test(t)?e.text():function(e){return e.arrayBuffer()}(e)}const be=function e(t,i){const s=t.defaults(i);return Object.assign((function(t,i){const r=s.merge(t,i);if(!r.request||!r.request.hook)return Be(s.parse(r));const n=(e,t)=>Be(s.parse(s.merge(e,t)));return Object.assign(n,{endpoint:s,defaults:e.bind(null,s)}),r.request.hook(n,r)}),{endpoint:s,defaults:e.bind(null,s)})}(y,{headers:{"user-agent":`octokit-request.js/5.6.3 ${(0,n.getUserAgent)()}`}});class Qe extends Error{constructor(e,t,i){super("Request failed due to following response errors:\n"+i.errors.map((e=>` - ${e.message}`)).join("\n")),this.request=e,this.headers=t,this.response=i,this.name="GraphqlResponseError",this.errors=i.errors,this.data=i.data,Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor)}}const xe=["method","baseUrl","url","headers","request","query","mediaType"],ke=["query","method","url"],De=/\/api\/v3\/?$/;function Se(e,t){const i=e.defaults(t);return Object.assign(((e,t)=>function(e,t,i){if(i){if("string"==typeof t&&"query"in i)return Promise.reject(new Error('[@octokit/graphql] "query" cannot be used as variable name'));for(const e in i)if(ke.includes(e))return Promise.reject(new Error(`[@octokit/graphql] "${e}" cannot be used as variable name`))}const s="string"==typeof t?Object.assign({query:t},i):t,r=Object.keys(s).reduce(((e,t)=>xe.includes(t)?(e[t]=s[t],e):(e.variables||(e.variables={}),e.variables[t]=s[t],e)),{}),n=s.baseUrl||e.endpoint.DEFAULTS.baseUrl;return De.test(n)&&(r.url=n.replace(De,"/api/graphql")),e(r).then((e=>{if(e.data.errors){const t={};for(const i of Object.keys(e.headers))t[i]=e.headers[i];throw new Qe(r,t,e.data)}return e.data.data}))}(i,e,t)),{defaults:Se.bind(null,i),endpoint:be.endpoint})}const _e=Se(be,{headers:{"user-agent":`octokit-graphql.js/4.8.0 ${(0,n.getUserAgent)()}`},method:"POST",url:"/graphql"});function Re(e){return Se(e,{method:"POST",url:"/graphql"})}},32546:(e,t,i)=>{"use strict";var s,r=Object.create,n=Object.defineProperty,o=Object.getOwnPropertyDescriptor,a=Object.getOwnPropertyNames,c=Object.getPrototypeOf,l=Object.prototype.hasOwnProperty,p=(e,t,i,s)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let r of a(t))l.call(e,r)||r===i||n(e,r,{get:()=>t[r],enumerable:!(s=o(t,r))||s.enumerable});return e},A=(e,t,i)=>(i=null!=e?r(c(e)):{},p(!t&&e&&e.__esModule?i:n(i,"default",{value:e,enumerable:!0}),e)),u={};((e,t)=>{for(var i in t)n(e,i,{get:t[i],enumerable:!0})})(u,{OAuthApp:()=>z,createAWSLambdaAPIGatewayV2Handler:()=>W,createNodeMiddleware:()=>q,createWebWorkerHandler:()=>Y,handleRequest:()=>H,sendNodeResponse:()=>J,unknownRouteResponse:()=>j}),e.exports=(s=u,p(n({},"__esModule",{value:!0}),s));var d=i(62419),h="6.1.0";function m(e,t,i){if(Array.isArray(t))for(const s of t)m(e,s,i);else e.eventHandlers[t]||(e.eventHandlers[t]=[]),e.eventHandlers[t].push(i)}var g=i(96049),f=i(21375),E=g.Octokit.defaults({userAgent:`octokit-oauth-app.js/${h} ${(0,f.getUserAgent)()}`}),C=i(3360);async function y(e,t){const{name:i,action:s}=t;if(e.eventHandlers[`${i}.${s}`])for(const r of e.eventHandlers[`${i}.${s}`])await r(t);if(e.eventHandlers[i])for(const s of e.eventHandlers[i])await s(t)}async function v(e,t){return e.octokit.auth({type:"oauth-user",...t,async factory(t){const i=new e.Octokit({authStrategy:C.createOAuthUserAuth,auth:t}),s=await i.auth({type:"get"});return await y(e,{name:"token",action:"created",token:s.token,scopes:s.scopes,authentication:s,octokit:i}),i}})}var I=A(i(11393));function B(e,t){const i={clientId:e.clientId,request:e.octokit.request,...t,allowSignup:e.allowSignup??t.allowSignup,redirectUrl:t.redirectUrl??e.redirectUrl,scopes:t.scopes??e.defaultScopes};return I.getWebFlowAuthorizationUrl({clientType:e.clientType,...i})}var w=A(i(62419));async function b(e,t){const i=await e.octokit.auth({type:"oauth-user",...t});return await y(e,{name:"token",action:"created",token:i.token,scopes:i.scopes,authentication:i,octokit:new e.Octokit({authStrategy:w.createOAuthUserAuth,auth:{clientType:e.clientType,clientId:e.clientId,clientSecret:e.clientSecret,token:i.token,scopes:i.scopes,refreshToken:i.refreshToken,expiresAt:i.expiresAt,refreshTokenExpiresAt:i.refreshTokenExpiresAt}})}),{authentication:i}}var Q=A(i(11393));async function x(e,t){const i=await Q.checkToken({clientType:e.clientType,clientId:e.clientId,clientSecret:e.clientSecret,request:e.octokit.request,...t});return Object.assign(i.authentication,{type:"token",tokenType:"oauth"}),i}var k=A(i(11393)),D=i(3360);async function S(e,t){const i={clientId:e.clientId,clientSecret:e.clientSecret,request:e.octokit.request,...t};if("oauth-app"===e.clientType){const t=await k.resetToken({clientType:"oauth-app",...i}),s=Object.assign(t.authentication,{type:"token",tokenType:"oauth"});return await y(e,{name:"token",action:"reset",token:t.authentication.token,scopes:t.authentication.scopes||void 0,authentication:s,octokit:new e.Octokit({authStrategy:D.createOAuthUserAuth,auth:{clientType:e.clientType,clientId:e.clientId,clientSecret:e.clientSecret,token:t.authentication.token,scopes:t.authentication.scopes}})}),{...t,authentication:s}}const s=await k.resetToken({clientType:"github-app",...i}),r=Object.assign(s.authentication,{type:"token",tokenType:"oauth"});return await y(e,{name:"token",action:"reset",token:s.authentication.token,authentication:r,octokit:new e.Octokit({authStrategy:D.createOAuthUserAuth,auth:{clientType:e.clientType,clientId:e.clientId,clientSecret:e.clientSecret,token:s.authentication.token}})}),{...s,authentication:r}}var _=A(i(11393)),R=i(3360);async function T(e,t){if("oauth-app"===e.clientType)throw new Error("[@octokit/oauth-app] app.refreshToken() is not supported for OAuth Apps");const i=await _.refreshToken({clientType:"github-app",clientId:e.clientId,clientSecret:e.clientSecret,request:e.octokit.request,refreshToken:t.refreshToken}),s=Object.assign(i.authentication,{type:"token",tokenType:"oauth"});return await y(e,{name:"token",action:"refreshed",token:i.authentication.token,authentication:s,octokit:new e.Octokit({authStrategy:R.createOAuthUserAuth,auth:{clientType:e.clientType,clientId:e.clientId,clientSecret:e.clientSecret,token:i.authentication.token}})}),{...i,authentication:s}}var F=A(i(11393)),N=i(3360);async function L(e,t){if("oauth-app"===e.clientType)throw new Error("[@octokit/oauth-app] app.scopeToken() is not supported for OAuth Apps");const i=await F.scopeToken({clientType:"github-app",clientId:e.clientId,clientSecret:e.clientSecret,request:e.octokit.request,...t}),s=Object.assign(i.authentication,{type:"token",tokenType:"oauth"});return await y(e,{name:"token",action:"scoped",token:i.authentication.token,authentication:s,octokit:new e.Octokit({authStrategy:N.createOAuthUserAuth,auth:{clientType:e.clientType,clientId:e.clientId,clientSecret:e.clientSecret,token:i.authentication.token}})}),{...i,authentication:s}}var O=A(i(11393)),M=i(37573);async function U(e,t){const i={clientId:e.clientId,clientSecret:e.clientSecret,request:e.octokit.request,...t},s="oauth-app"===e.clientType?await O.deleteToken({clientType:"oauth-app",...i}):await O.deleteToken({clientType:"github-app",...i});return await y(e,{name:"token",action:"deleted",token:t.token,octokit:new e.Octokit({authStrategy:M.createUnauthenticatedAuth,auth:{reason:'Handling "token.deleted" event. The access for the token has been revoked.'}})}),s}var P=A(i(11393)),G=i(37573);async function V(e,t){const i={clientId:e.clientId,clientSecret:e.clientSecret,request:e.octokit.request,...t},s="oauth-app"===e.clientType?await P.deleteAuthorization({clientType:"oauth-app",...i}):await P.deleteAuthorization({clientType:"github-app",...i});return await y(e,{name:"token",action:"deleted",token:t.token,octokit:new e.Octokit({authStrategy:G.createUnauthenticatedAuth,auth:{reason:'Handling "token.deleted" event. The access for the token has been revoked.'}})}),await y(e,{name:"authorization",action:"deleted",token:t.token,octokit:new e.Octokit({authStrategy:G.createUnauthenticatedAuth,auth:{reason:'Handling "authorization.deleted" event. The access for the app has been revoked.'}})}),s}function j(e){return{status:404,headers:{"content-type":"application/json"},text:JSON.stringify({error:`Unknown route: ${e.method} ${e.url}`})}}async function H(e,{pathPrefix:t="/api/github/oauth"},i){if("OPTIONS"===i.method)return{status:200,headers:{"access-control-allow-origin":"*","access-control-allow-methods":"*","access-control-allow-headers":"Content-Type, User-Agent, Authorization"}};let{pathname:s}=new URL(i.url,"http://localhost");if(!s.startsWith(`${t}/`))return;s=s.slice(t.length+1);const r=[i.method,s].join(" "),n={getLogin:"GET login",getCallback:"GET callback",createToken:"POST token",getToken:"GET token",patchToken:"PATCH token",patchRefreshToken:"PATCH refresh-token",scopeToken:"POST token/scoped",deleteToken:"DELETE token",deleteGrant:"DELETE grant"};if(!Object.values(n).includes(r))return j(i);let o;try{const e=await i.text();o=e?JSON.parse(e):{}}catch(e){return{status:400,headers:{"content-type":"application/json","access-control-allow-origin":"*"},text:JSON.stringify({error:"[@octokit/oauth-app] request error"})}}const{searchParams:a}=new URL(i.url,"http://localhost"),c=Object.fromEntries(a),l=i.headers;try{if(r===n.getLogin){const{url:t}=e.getWebFlowAuthorizationUrl({state:c.state,scopes:c.scopes?c.scopes.split(","):void 0,allowSignup:c.allowSignup?"true"===c.allowSignup:void 0,redirectUrl:c.redirectUrl});return{status:302,headers:{location:t}}}if(r===n.getCallback){if(c.error)throw new Error(`[@octokit/oauth-app] ${c.error} ${c.error_description}`);if(!c.code)throw new Error('[@octokit/oauth-app] "code" parameter is required');const{authentication:{token:t}}=await e.createToken({code:c.code});return{status:200,headers:{"content-type":"text/html"},text:`

        Token created successfully

        \n\n

        Your token is: ${t}. Copy it now as it cannot be shown again.

        `}}if(r===n.createToken){const{code:t,redirectUrl:i}=o;if(!t)throw new Error('[@octokit/oauth-app] "code" parameter is required');const s=await e.createToken({code:t,redirectUrl:i});return delete s.authentication.clientSecret,{status:201,headers:{"content-type":"application/json","access-control-allow-origin":"*"},text:JSON.stringify(s)}}if(r===n.getToken){const t=l.authorization?.substr(6);if(!t)throw new Error('[@octokit/oauth-app] "Authorization" header is required');const i=await e.checkToken({token:t});return delete i.authentication.clientSecret,{status:200,headers:{"content-type":"application/json","access-control-allow-origin":"*"},text:JSON.stringify(i)}}if(r===n.patchToken){const t=l.authorization?.substr(6);if(!t)throw new Error('[@octokit/oauth-app] "Authorization" header is required');const i=await e.resetToken({token:t});return delete i.authentication.clientSecret,{status:200,headers:{"content-type":"application/json","access-control-allow-origin":"*"},text:JSON.stringify(i)}}if(r===n.patchRefreshToken){const t=l.authorization?.substr(6);if(!t)throw new Error('[@octokit/oauth-app] "Authorization" header is required');const{refreshToken:i}=o;if(!i)throw new Error("[@octokit/oauth-app] refreshToken must be sent in request body");const s=await e.refreshToken({refreshToken:i});return delete s.authentication.clientSecret,{status:200,headers:{"content-type":"application/json","access-control-allow-origin":"*"},text:JSON.stringify(s)}}if(r===n.scopeToken){const t=l.authorization?.substr(6);if(!t)throw new Error('[@octokit/oauth-app] "Authorization" header is required');const i=await e.scopeToken({token:t,...o});return delete i.authentication.clientSecret,{status:200,headers:{"content-type":"application/json","access-control-allow-origin":"*"},text:JSON.stringify(i)}}if(r===n.deleteToken){const t=l.authorization?.substr(6);if(!t)throw new Error('[@octokit/oauth-app] "Authorization" header is required');return await e.deleteToken({token:t}),{status:204,headers:{"access-control-allow-origin":"*"}}}const t=l.authorization?.substr(6);if(!t)throw new Error('[@octokit/oauth-app] "Authorization" header is required');return await e.deleteAuthorization({token:t}),{status:204,headers:{"access-control-allow-origin":"*"}}}catch(e){return{status:400,headers:{"content-type":"application/json","access-control-allow-origin":"*"},text:JSON.stringify({error:e.message})}}}function J(e,t){t.writeHead(e.status,e.headers),t.end(e.text)}function q(e,t={}){return async function(i,s,r){const n=await function(e){const{method:t,url:i,headers:s}=e;return{method:t,url:i,headers:s,text:async function(){return await new Promise(((t,i)=>{let s=[];e.on("error",i).on("data",(e=>s.push(e))).on("end",(()=>t(Buffer.concat(s).toString())))}))}}}(i),o=await H(e,t,n);return o?(J(o,s),!0):(r?.(),!1)}}function Y(e,t={}){return async function(i){const s=await function(e){const t=Object.fromEntries(e.headers.entries());return{method:e.method,url:e.url,headers:t,text:()=>e.text()}}(i),r=await H(e,t,s);return r?function(e){return new Response(e.text,{status:e.status,headers:e.headers})}(r):void 0}}function W(e,t={}){return async function(i){const s=function(e){const{method:t}=e.requestContext.http;let i=e.rawPath;const{stage:s}=e.requestContext;return i.startsWith("/"+s)&&(i=i.substring(s.length+1)),e.rawQueryString&&(i+="?"+e.rawQueryString),{method:t,url:i,headers:e.headers,text:async()=>e.body||""}}(i),r=await H(e,t,s);return r?{statusCode:(n=r).status,headers:n.headers,body:n.text}:void 0;var n}}var z=class{static{this.VERSION=h}static defaults(e){return class extends(this){constructor(...t){super({...e,...t[0]})}}}constructor(e){const t=e.Octokit||E;this.type=e.clientType||"oauth-app";const i=new t({authStrategy:d.createOAuthAppAuth,auth:{clientType:this.type,clientId:e.clientId,clientSecret:e.clientSecret}}),s={clientType:this.type,clientId:e.clientId,clientSecret:e.clientSecret,defaultScopes:e.defaultScopes||[],allowSignup:e.allowSignup,baseUrl:e.baseUrl,redirectUrl:e.redirectUrl,log:e.log,Octokit:t,octokit:i,eventHandlers:{}};this.on=m.bind(null,s),this.octokit=i,this.getUserOctokit=v.bind(null,s),this.getWebFlowAuthorizationUrl=B.bind(null,s),this.createToken=b.bind(null,s),this.checkToken=x.bind(null,s),this.resetToken=S.bind(null,s),this.refreshToken=T.bind(null,s),this.scopeToken=L.bind(null,s),this.deleteToken=U.bind(null,s),this.deleteAuthorization=V.bind(null,s)}}},4923:(e,t,i)=>{"use strict";i.d(t,{a:()=>o});var s=i(65683),r=i(96756),n=i.n(r);async function o(e){const t=e.request||s.request,i=await t("POST /applications/{client_id}/token",{headers:{authorization:`basic ${n()(`${e.clientId}:${e.clientSecret}`)}`},client_id:e.clientId,access_token:e.token}),r={clientType:e.clientType,clientId:e.clientId,clientSecret:e.clientSecret,token:e.token,scopes:i.data.scopes};return i.data.expires_at&&(r.expiresAt=i.data.expires_at),"github-app"===e.clientType&&delete r.scopes,{...i,authentication:r}}},63855:(e,t,i)=>{"use strict";i.d(t,{T:()=>n});var s=i(65683),r=i(21216);async function n(e){const t=e.request||s.request,i={client_id:e.clientId};return"scopes"in e&&Array.isArray(e.scopes)&&(i.scope=e.scopes.join(" ")),(0,r.d)(t,"POST /login/device/code",i)}},62616:(e,t,i)=>{"use strict";i.d(t,{s:()=>o});var s=i(65683),r=i(96756),n=i.n(r);async function o(e){return(e.request||s.request)("DELETE /applications/{client_id}/grant",{headers:{authorization:`basic ${n()(`${e.clientId}:${e.clientSecret}`)}`},client_id:e.clientId,access_token:e.token})}},46886:(e,t,i)=>{"use strict";i.d(t,{p:()=>o});var s=i(65683),r=i(96756),n=i.n(r);async function o(e){return(e.request||s.request)("DELETE /applications/{client_id}/token",{headers:{authorization:`basic ${n()(`${e.clientId}:${e.clientSecret}`)}`},client_id:e.clientId,access_token:e.token})}},85579:(e,t,i)=>{"use strict";i.d(t,{i:()=>n});var s=i(65683),r=i(21216);async function n(e){const t=e.request||s.request,i=await(0,r.d)(t,"POST /login/oauth/access_token",{client_id:e.clientId,device_code:e.code,grant_type:"urn:ietf:params:oauth:grant-type:device_code"}),n={clientType:e.clientType,clientId:e.clientId,token:i.data.access_token,scopes:i.data.scope.split(/\s+/).filter(Boolean)};if("clientSecret"in e&&(n.clientSecret=e.clientSecret),"github-app"===e.clientType){if("refresh_token"in i.data){const e=new Date(i.headers.date).getTime();n.refreshToken=i.data.refresh_token,n.expiresAt=o(e,i.data.expires_in),n.refreshTokenExpiresAt=o(e,i.data.refresh_token_expires_in)}delete n.scopes}return{...i,authentication:n}}function o(e,t){return new Date(e+1e3*t).toISOString()}},71060:(e,t,i)=>{"use strict";i.d(t,{y:()=>n});var s=i(65683),r=i(21216);async function n(e){const t=e.request||s.request,i=await(0,r.d)(t,"POST /login/oauth/access_token",{client_id:e.clientId,client_secret:e.clientSecret,code:e.code,redirect_uri:e.redirectUrl}),n={clientType:e.clientType,clientId:e.clientId,clientSecret:e.clientSecret,token:i.data.access_token,scopes:i.data.scope.split(/\s+/).filter(Boolean)};if("github-app"===e.clientType){if("refresh_token"in i.data){const e=new Date(i.headers.date).getTime();n.refreshToken=i.data.refresh_token,n.expiresAt=o(e,i.data.expires_in),n.refreshTokenExpiresAt=o(e,i.data.refresh_token_expires_in)}delete n.scopes}return{...i,authentication:n}}function o(e,t){return new Date(e+1e3*t).toISOString()}},11393:(e,t,i)=>{"use strict";i.r(t),i.d(t,{VERSION:()=>s,checkToken:()=>p.a,createDeviceCode:()=>c.T,deleteAuthorization:()=>f.s,deleteToken:()=>g.p,exchangeDeviceCode:()=>l.i,exchangeWebFlowCode:()=>a.y,getWebFlowAuthorizationUrl:()=>o,refreshToken:()=>A.g,resetToken:()=>m.E,scopeToken:()=>h});const s="4.1.0";var r=i(65683),n=i(21216);function o({request:e=r.request,...t}){return function(e){const t=e.clientType||"oauth-app",i=e.baseUrl||"https://github.com",s={clientType:t,allowSignup:!1!==e.allowSignup,clientId:e.clientId,login:e.login||null,redirectUrl:e.redirectUrl||null,state:e.state||Math.random().toString(36).substr(2),url:""};if("oauth-app"===t){const t="scopes"in e?e.scopes:[];s.scopes="string"==typeof t?t.split(/[,\s]+/).filter(Boolean):t}return s.url=function(e,t){const i={allowSignup:"allow_signup",clientId:"client_id",login:"login",redirectUrl:"redirect_uri",scopes:"scope",state:"state"};let s=e;return Object.keys(i).filter((e=>null!==t[e])).filter((e=>"scopes"!==e||"github-app"!==t.clientType&&(!Array.isArray(t[e])||t[e].length>0))).map((e=>[i[e],`${t[e]}`])).forEach((([e,t],i)=>{s+=0===i?"?":"&",s+=`${e}=${encodeURIComponent(t)}`})),s}(`${i}/login/oauth/authorize`,s),s}({...t,baseUrl:(0,n.l)(e)})}var a=i(71060),c=i(63855),l=i(85579),p=i(4923),A=i(27550),u=i(96756),d=i.n(u);async function h(e){const{request:t,clientType:i,clientId:s,clientSecret:n,token:o,...a}=e,c=t||r.request,l=await c("POST /applications/{client_id}/token/scoped",{headers:{authorization:`basic ${d()(`${s}:${n}`)}`},client_id:s,access_token:o,...a}),p=Object.assign({clientType:i,clientId:s,clientSecret:n,token:l.data.token},l.data.expires_at?{expiresAt:l.data.expires_at}:{});return{...l,authentication:p}}var m=i(9147),g=i(46886),f=i(62616)},27550:(e,t,i)=>{"use strict";i.d(t,{g:()=>n});var s=i(65683),r=i(21216);async function n(e){const t=e.request||s.request,i=await(0,r.d)(t,"POST /login/oauth/access_token",{client_id:e.clientId,client_secret:e.clientSecret,grant_type:"refresh_token",refresh_token:e.refreshToken}),n=new Date(i.headers.date).getTime(),a={clientType:"github-app",clientId:e.clientId,clientSecret:e.clientSecret,token:i.data.access_token,refreshToken:i.data.refresh_token,expiresAt:o(n,i.data.expires_in),refreshTokenExpiresAt:o(n,i.data.refresh_token_expires_in)};return{...i,authentication:a}}function o(e,t){return new Date(e+1e3*t).toISOString()}},9147:(e,t,i)=>{"use strict";i.d(t,{E:()=>o});var s=i(65683),r=i(96756),n=i.n(r);async function o(e){const t=e.request||s.request,i=n()(`${e.clientId}:${e.clientSecret}`),r=await t("PATCH /applications/{client_id}/token",{headers:{authorization:`basic ${i}`},client_id:e.clientId,access_token:e.token}),o={clientType:e.clientType,clientId:e.clientId,clientSecret:e.clientSecret,token:r.data.token,scopes:r.data.scopes};return r.data.expires_at&&(o.expiresAt=r.data.expires_at),"github-app"===e.clientType&&delete o.scopes,{...r,authentication:o}}},21216:(e,t,i)=>{"use strict";i.d(t,{d:()=>n,l:()=>r});var s=i(98838);function r(e){const t=e.endpoint.DEFAULTS;return/^https:\/\/(api\.)?github\.com$/.test(t.baseUrl)?"https://github.com":t.baseUrl.replace("/api/v3","")}async function n(e,t,i){const n={baseUrl:r(e),headers:{accept:"application/json"},...i},o=await e(t,n);if("error"in o.data){const i=new s.RequestError(`${o.data.error_description} (${o.data.error}, ${o.data.error_uri})`,400,{request:e.endpoint.merge(t,n),headers:o.headers});throw i.response=o,i}return o}},98838:(e,t,i)=>{"use strict";i.r(t),i.d(t,{RequestError:()=>c});var s=i(57751),r=i(36219),n=i.n(r);const o=n()((e=>console.warn(e))),a=n()((e=>console.warn(e)));class c extends Error{constructor(e,t,i){let r;super(e),Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor),this.name="HttpError",this.status=t,"headers"in i&&void 0!==i.headers&&(r=i.headers),"response"in i&&(this.response=i.response,r=i.response.headers);const n=Object.assign({},i.request);i.request.headers.authorization&&(n.headers=Object.assign({},i.request.headers,{authorization:i.request.headers.authorization.replace(/ .*$/," [REDACTED]")})),n.url=n.url.replace(/\bclient_secret=\w+/g,"client_secret=[REDACTED]").replace(/\baccess_token=\w+/g,"access_token=[REDACTED]"),this.request=n,Object.defineProperty(this,"code",{get:()=>(o(new s.Deprecation("[@octokit/request-error] `error.code` is deprecated, use `error.status`.")),t)}),Object.defineProperty(this,"headers",{get:()=>(a(new s.Deprecation("[@octokit/request-error] `error.headers` is deprecated, use `error.response.headers`.")),r||{})})}}},65683:(e,t,i)=>{"use strict";function s(e,t){const i=Object.assign({},e);return Object.keys(t).forEach((r=>{!function(e){if("object"!=typeof e||null===e)return!1;if("[object Object]"!==Object.prototype.toString.call(e))return!1;const t=Object.getPrototypeOf(e);if(null===t)return!0;const i=Object.prototype.hasOwnProperty.call(t,"constructor")&&t.constructor;return"function"==typeof i&&i instanceof i&&Function.prototype.call(i)===Function.prototype.call(e)}(t[r])?Object.assign(i,{[r]:t[r]}):r in e?i[r]=s(e[r],t[r]):Object.assign(i,{[r]:t[r]})})),i}function r(e){for(const t in e)void 0===e[t]&&delete e[t];return e}function n(e,t,i){if("string"==typeof t){let[e,s]=t.split(" ");i=Object.assign(s?{method:e,url:s}:{url:e},i)}else i=Object.assign({},t);var n;i.headers=(n=i.headers)?Object.keys(n).reduce(((e,t)=>(e[t.toLowerCase()]=n[t],e)),{}):{},r(i),r(i.headers);const o=s(e||{},i);return"/graphql"===i.url&&(e&&e.mediaType.previews?.length&&(o.mediaType.previews=e.mediaType.previews.filter((e=>!o.mediaType.previews.includes(e))).concat(o.mediaType.previews)),o.mediaType.previews=(o.mediaType.previews||[]).map((e=>e.replace(/-preview/,"")))),o}i.r(t),i.d(t,{request:()=>I});const o=/\{[^}]+\}/g;function a(e){return e.replace(/^\W+|\W+$/g,"").split(/,/)}function c(e,t){const i={__proto__:null};for(const s of Object.keys(e))-1===t.indexOf(s)&&(i[s]=e[s]);return i}function l(e){return e.split(/(%[0-9A-Fa-f]{2})/g).map((function(e){return/%[0-9A-Fa-f]/.test(e)||(e=encodeURI(e).replace(/%5B/g,"[").replace(/%5D/g,"]")),e})).join("")}function p(e){return encodeURIComponent(e).replace(/[!'()*]/g,(function(e){return"%"+e.charCodeAt(0).toString(16).toUpperCase()}))}function A(e,t,i){return t="+"===e||"#"===e?l(t):p(t),i?p(i)+"="+t:t}function u(e){return null!=e}function d(e){return";"===e||"&"===e||"?"===e}function h(e,t){var i=["+","#",".","/",";","?","&"];return e=e.replace(/\{([^\{\}]+)\}|([^\{\}]+)/g,(function(e,s,r){if(s){let e="";const r=[];if(-1!==i.indexOf(s.charAt(0))&&(e=s.charAt(0),s=s.substr(1)),s.split(/,/g).forEach((function(i){var s=/([^:\*]*)(?::(\d+)|(\*))?/.exec(i);r.push(function(e,t,i,s){var r=e[i],n=[];if(u(r)&&""!==r)if("string"==typeof r||"number"==typeof r||"boolean"==typeof r)r=r.toString(),s&&"*"!==s&&(r=r.substring(0,parseInt(s,10))),n.push(A(t,r,d(t)?i:""));else if("*"===s)Array.isArray(r)?r.filter(u).forEach((function(e){n.push(A(t,e,d(t)?i:""))})):Object.keys(r).forEach((function(e){u(r[e])&&n.push(A(t,r[e],e))}));else{const e=[];Array.isArray(r)?r.filter(u).forEach((function(i){e.push(A(t,i))})):Object.keys(r).forEach((function(i){u(r[i])&&(e.push(p(i)),e.push(A(t,r[i].toString())))})),d(t)?n.push(p(i)+"="+e.join(",")):0!==e.length&&n.push(e.join(","))}else";"===t?u(r)&&n.push(p(i)):""!==r||"&"!==t&&"?"!==t?""===r&&n.push(""):n.push(p(i)+"=");return n}(t,e,s[1],s[2]||s[3]))})),e&&"+"!==e){var n=",";return"?"===e?n="&":"#"!==e&&(n=e),(0!==r.length?e:"")+r.join(n)}return r.join(",")}return l(r)})),"/"===e?e:e.replace(/\/$/,"")}function m(e){let t,i=e.method.toUpperCase(),s=(e.url||"/").replace(/:([a-z]\w+)/g,"{$1}"),r=Object.assign({},e.headers),n=c(e,["method","baseUrl","url","headers","request","mediaType"]);const l=function(e){const t=e.match(o);return t?t.map(a).reduce(((e,t)=>e.concat(t)),[]):[]}(s);var p;s=(p=s,{expand:h.bind(null,p)}).expand(n),/^http/.test(s)||(s=e.baseUrl+s);const A=c(n,Object.keys(e).filter((e=>l.includes(e))).concat("baseUrl"));if(!/application\/octet-stream/i.test(r.accept)&&(e.mediaType.format&&(r.accept=r.accept.split(/,/).map((t=>t.replace(/application\/vnd(\.\w+)(\.v3)?(\.\w+)?(\+json)?$/,`application/vnd$1$2.${e.mediaType.format}`))).join(",")),s.endsWith("/graphql")&&e.mediaType.previews?.length)){const t=r.accept.match(/[\w-]+(?=-preview)/g)||[];r.accept=t.concat(e.mediaType.previews).map((t=>`application/vnd.github.${t}-preview${e.mediaType.format?`.${e.mediaType.format}`:"+json"}`)).join(",")}return["GET","HEAD"].includes(i)?s=function(e,t){const i=/\?/.test(e)?"&":"?",s=Object.keys(t);return 0===s.length?e:e+i+s.map((e=>"q"===e?"q="+t.q.split("+").map(encodeURIComponent).join("+"):`${e}=${encodeURIComponent(t[e])}`)).join("&")}(s,A):"data"in A?t=A.data:Object.keys(A).length&&(t=A),r["content-type"]||void 0===t||(r["content-type"]="application/json; charset=utf-8"),["PATCH","PUT"].includes(i)&&void 0===t&&(t=""),Object.assign({method:i,url:s,headers:r},void 0!==t?{body:t}:null,e.request?{request:e.request}:null)}function g(e,t,i){return m(n(e,t,i))}var f=i(21375);const E=function e(t,i){const s=n(t,i),r=g.bind(null,s);return Object.assign(r,{DEFAULTS:s,defaults:e.bind(null,s),merge:n.bind(null,s),parse:m})}(null,{method:"GET",baseUrl:"https://api.github.com",headers:{accept:"application/vnd.github.v3+json","user-agent":`octokit-endpoint.js/9.0.5 ${(0,f.getUserAgent)()}`},mediaType:{format:""}});var C=i(98838);function y(e){const t=e.request&&e.request.log?e.request.log:console,i=!1!==e.request?.parseSuccessResponseBody;(function(e){if("object"!=typeof e||null===e)return!1;if("[object Object]"!==Object.prototype.toString.call(e))return!1;const t=Object.getPrototypeOf(e);if(null===t)return!0;const i=Object.prototype.hasOwnProperty.call(t,"constructor")&&t.constructor;return"function"==typeof i&&i instanceof i&&Function.prototype.call(i)===Function.prototype.call(e)}(e.body)||Array.isArray(e.body))&&(e.body=JSON.stringify(e.body));let s,r,n={},{fetch:o}=globalThis;if(e.request?.fetch&&(o=e.request.fetch),!o)throw new Error("fetch is not set. Please pass a fetch implementation as new Octokit({ request: { fetch }}). Learn more at https://github.com/octokit/octokit.js/#fetch-missing");return o(e.url,{method:e.method,body:e.body,redirect:e.request?.redirect,headers:e.headers,signal:e.request?.signal,...e.body&&{duplex:"half"}}).then((async o=>{r=o.url,s=o.status;for(const e of o.headers)n[e[0]]=e[1];if("deprecation"in n){const i=n.link&&n.link.match(/<([^>]+)>; rel="deprecation"/),s=i&&i.pop();t.warn(`[@octokit/request] "${e.method} ${e.url}" is deprecated. It is scheduled to be removed on ${n.sunset}${s?`. See ${s}`:""}`)}if(204!==s&&205!==s){if("HEAD"===e.method){if(s<400)return;throw new C.RequestError(o.statusText,s,{response:{url:r,status:s,headers:n,data:void 0},request:e})}if(304===s)throw new C.RequestError("Not modified",s,{response:{url:r,status:s,headers:n,data:await v(o)},request:e});if(s>=400){const t=await v(o),i=new C.RequestError(function(e){if("string"==typeof e)return e;let t;return t="documentation_url"in e?` - ${e.documentation_url}`:"","message"in e?Array.isArray(e.errors)?`${e.message}: ${e.errors.map(JSON.stringify).join(", ")}${t}`:`${e.message}${t}`:`Unknown error: ${JSON.stringify(e)}`}(t),s,{response:{url:r,status:s,headers:n,data:t},request:e});throw i}return i?await v(o):o.body}})).then((e=>({status:s,url:r,headers:n,data:e}))).catch((t=>{if(t instanceof C.RequestError)throw t;if("AbortError"===t.name)throw t;let i=t.message;throw"TypeError"===t.name&&"cause"in t&&(t.cause instanceof Error?i=t.cause.message:"string"==typeof t.cause&&(i=t.cause)),new C.RequestError(i,500,{request:e})}))}async function v(e){const t=e.headers.get("content-type");return/application\/json/.test(t)?e.json().catch((()=>e.text())).catch((()=>"")):!t||/^text\/|charset=utf-8$/.test(t)?e.text():function(e){return e.arrayBuffer()}(e)}const I=function e(t,i){const s=t.defaults(i);return Object.assign((function(t,i){const r=s.merge(t,i);if(!r.request||!r.request.hook)return y(s.parse(r));const n=(e,t)=>y(s.parse(s.merge(e,t)));return Object.assign(n,{endpoint:s,defaults:e.bind(null,s)}),r.request.hook(n,r)}),{endpoint:s,defaults:e.bind(null,s)})}(E,{headers:{"user-agent":`octokit-request.js/8.4.0 ${(0,f.getUserAgent)()}`}})},94832:(e,t)=>{"use strict";var i;Object.defineProperty(t,"__esModule",{value:!0}),t.ConsoleLogger=t.LogLevel=void 0,function(e){e.ERROR="error",e.WARN="warn",e.INFO="info",e.DEBUG="debug"}(i=t.LogLevel||(t.LogLevel={}));class s{constructor(){this.level=i.INFO,this.name=""}getLevel(){return this.level}setLevel(e){this.level=e}setName(e){this.name=e}debug(...e){s.isMoreOrEqualSevere(i.DEBUG,this.level)&&console.debug(s.labels.get(i.DEBUG),this.name,...e)}info(...e){s.isMoreOrEqualSevere(i.INFO,this.level)&&console.info(s.labels.get(i.INFO),this.name,...e)}warn(...e){s.isMoreOrEqualSevere(i.WARN,this.level)&&console.warn(s.labels.get(i.WARN),this.name,...e)}error(...e){s.isMoreOrEqualSevere(i.ERROR,this.level)&&console.error(s.labels.get(i.ERROR),this.name,...e)}static isMoreOrEqualSevere(e,t){return s.severity[e]>=s.severity[t]}}t.ConsoleLogger=s,s.labels=(()=>{const e=Object.entries(i).map((([e,t])=>[t,`[${e}] `]));return new Map(e)})(),s.severity={[i.ERROR]:400,[i.WARN]:300,[i.INFO]:200,[i.DEBUG]:100}},73247:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0})},69134:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0})},8702:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0})},52866:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0})},91939:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0})},92523:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0})},17578:function(e,t,i){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,i,s){void 0===s&&(s=i);var r=Object.getOwnPropertyDescriptor(t,i);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,s,r)}:function(e,t,i,s){void 0===s&&(s=i),e[s]=t[i]}),r=this&&this.__exportStar||function(e,t){for(var i in e)"default"===i||Object.prototype.hasOwnProperty.call(t,i)||s(t,e,i)};Object.defineProperty(t,"__esModule",{value:!0}),r(i(91939),t),r(i(92523),t),r(i(33144),t),r(i(88851),t),r(i(69700),t),r(i(69134),t),r(i(8702),t),r(i(73247),t),r(i(52866),t)},88851:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0})},33144:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0})},69700:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0})},11055:function(e,t,i){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,i,s){void 0===s&&(s=i);var r=Object.getOwnPropertyDescriptor(t,i);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,s,r)}:function(e,t,i,s){void 0===s&&(s=i),e[s]=t[i]}),r=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)"default"!==i&&Object.prototype.hasOwnProperty.call(e,i)&&s(t,e,i);return r(t,e),t},o=this&&this.__await||function(e){return this instanceof o?(this.v=e,this):new o(e)},a=this&&this.__asyncGenerator||function(e,t,i){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var s,r=i.apply(e,t||[]),n=[];return s={},a("next"),a("throw"),a("return"),s[Symbol.asyncIterator]=function(){return this},s;function a(e){r[e]&&(s[e]=function(t){return new Promise((function(i,s){n.push([e,t,i,s])>1||c(e,t)}))})}function c(e,t){try{(i=r[e](t)).value instanceof o?Promise.resolve(i.value.v).then(l,p):A(n[0][2],i)}catch(e){A(n[0][3],e)}var i}function l(e){c("next",e)}function p(e){c("throw",e)}function A(e,t){e(t),n.shift(),n.length&&c(n[0][0],n[0][1])}},c=this&&this.__asyncValues||function(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t,i=e[Symbol.asyncIterator];return i?i.call(e):(e="function"==typeof __values?__values(e):e[Symbol.iterator](),t={},s("next"),s("throw"),s("return"),t[Symbol.asyncIterator]=function(){return this},t);function s(i){t[i]=e[i]&&function(t){return new Promise((function(s,r){!function(e,t,i,s){Promise.resolve(s).then((function(t){e({value:t,done:i})}),t)}(s,r,(t=e[i](t)).done,t.value)}))}}},l=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.buildThreadTsWarningMessage=t.WebClient=t.WebClientEvent=void 0;const p=i(63477),A=i(71017),u=l(i(59796)),d=i(73837),h=l(i(42412)),m=l(i(88464)),g=n(i(32656)),f=l(i(34425)),E=l(i(90504)),C=l(i(30547)),y=i(25242),v=i(57390),I=i(47056),B=i(94139),w=i(39847),b=l(i(83313)),Q=i(28666),x=()=>{};var k;!function(e){e.RATE_LIMITED="rate_limited"}(k=t.WebClientEvent||(t.WebClientEvent={}));class D extends y.Methods{constructor(e,{slackApiUrl:t="https://slack.com/api/",logger:i,logLevel:s,maxRequestConcurrency:r=100,retryConfig:n=w.tenRetriesInAboutThirtyMinutes,agent:o,tls:a,timeout:c=0,rejectRateLimitedCalls:l=!1,headers:p={},teamId:A}={}){super(),this.token=e,this.slackApiUrl=t,this.retryConfig=n,this.requestQueue=new m.default({concurrency:r}),this.tlsConfig=void 0!==a?a:{},this.rejectRateLimitedCalls=l,this.teamId=A,void 0!==i?(this.logger=i,void 0!==s&&this.logger.debug("The logLevel given to WebClient was ignored as you also gave logger")):this.logger=(0,B.getLogger)(D.loggerName,null!=s?s:B.LogLevel.INFO,i),this.token&&!p.Authorization&&(p.Authorization=`Bearer ${this.token}`),this.axios=f.default.create({timeout:c,baseURL:t,headers:(0,C.default)()?p:Object.assign({"User-Agent":(0,v.getUserAgent)()},p),httpAgent:o,httpsAgent:o,transformRequest:[this.serializeApiCallOptions.bind(this)],validateStatus:()=>!0,maxRedirects:0,proxy:!1}),delete this.axios.defaults.headers.post["Content-Type"],this.logger.debug("initialized")}async apiCall(e,t={}){if(this.logger.debug(`apiCall('${e}') start`),function(e,t){const i=["channels.","groups.","im.","mpim."].some((t=>new RegExp(`^${t}`).test(e))),s=["admin.conversations.whitelist.","stars."].some((t=>new RegExp(`^${t}`).test(e)));i?t.warn(`${e} is deprecated. Please use the Conversations API instead. For more info, go to https://api.slack.com/changelog/2020-01-deprecating-antecedents-to-the-conversations-api`):s&&t.warn(`${e} is deprecated. Please check on https://api.slack.com/methods for an alternative.`)}(e,this.logger),function(e,t,i){const s=e=>void 0===e.text||null===e.text||""===e.text,r=()=>`The top-level \`text\` argument is missing in the request payload for a ${e} call - It's a best practice to always provide a \`text\` argument when posting a message. The \`text\` is used in places where the content cannot be rendered such as: system push notifications, assistive technology such as screen readers, etc.`;var n;["chat.postEphemeral","chat.postMessage","chat.scheduleMessage","chat.update"].includes(e)&&"object"==typeof i&&(n=i,Array.isArray(n.attachments)&&n.attachments.length?(e=>Array.isArray(e.attachments)&&e.attachments.some((e=>!e.fallback||""===e.fallback.trim())))(i)&&s(i)&&(t.warn(r()),t.warn(`Additionally, the attachment-level \`fallback\` argument is missing in the request payload for a ${e} call - To avoid this warning, it is recommended to always provide a top-level \`text\` argument when posting a message. Alternatively, you can provide an attachment-level \`fallback\` argument, though this is now considered a legacy field (see https://api.slack.com/reference/messaging/attachments#legacy_fields for more details).`)):s(i)&&t.warn(r()))}(e,this.logger,t),function(e,t,i){["chat.postEphemeral","chat.postMessage","chat.scheduleMessage","files.upload"].includes(e)&&void 0!==(null==i?void 0:i.thread_ts)&&"string"!=typeof(null==i?void 0:i.thread_ts)&&t.warn(R(e))}(e,this.logger,t),"string"==typeof t||"number"==typeof t||"boolean"==typeof t)throw new TypeError("Expected an options argument but instead received a "+typeof t);if((0,Q.warnIfNotUsingFilesUploadV2)(e,this.logger),"files.uploadV2"===e)return this.filesUploadV2(t);const i={};t.token&&(i.Authorization=`Bearer ${t.token}`);const s=await this.makeRequest(e,Object.assign({team_id:this.teamId},t),i),r=await this.buildResult(s);if(this.logger.debug(`http request result: ${JSON.stringify(r)}`),void 0!==r.response_metadata&&void 0!==r.response_metadata.warnings&&r.response_metadata.warnings.forEach(this.logger.warn.bind(this.logger)),void 0!==r.response_metadata&&void 0!==r.response_metadata.messages&&r.response_metadata.messages.forEach((e=>{const t=/\[ERROR\](.*)/,i=/\[WARN\](.*)/;if(t.test(e)){const i=e.match(t);null!=i&&this.logger.error(i[1].trim())}else if(i.test(e)){const t=e.match(i);null!=t&&this.logger.warn(t[1].trim())}})),!r.ok&&"application/gzip"!==s.headers["content-type"])throw(0,I.platformErrorFromResult)(r);if("ok"in r&&!1===r.ok)throw(0,I.platformErrorFromResult)(r);return this.logger.debug(`apiCall('${e}') end`),r}paginate(e,t,i,s){y.cursorPaginationEnabledMethods.has(e)||this.logger.warn(`paginate() called with method ${e}, which is not known to be cursor pagination enabled.`);const r=(()=>{if(void 0!==t&&"number"==typeof t.limit){const{limit:e}=t;return delete t.limit,e}return 200})();function n(){return a(this,arguments,(function*(){let i,s={limit:r};for(void 0!==t&&void 0!==t.cursor&&(s.cursor=t.cursor);void 0===i||void 0!==s;)i=yield o(this.apiCall(e,Object.assign(void 0!==t?t:{},s))),yield yield o(i),s=S(i,r)}))}if(void 0===i)return n.call(this);const l=void 0!==s?s:x;let p=0;return(async()=>{var e,t,s,r;const o=n.call(this),a=(await o.next(void 0)).value;let A=l(void 0,a,p);if(p+=1,i(a))return A;try{for(var u,d=!0,h=c(o);!(e=(u=await h.next()).done);){r=u.value,d=!1;try{const e=r;if(A=l(A,e,p),i(e))return A;p+=1}finally{d=!0}}}catch(e){t={error:e}}finally{try{d||e||!(s=h.return)||await s.call(h)}finally{if(t)throw t.error}}return A})()}async filesUploadV2(e){this.logger.debug("files.uploadV2() start");const t=await this.getAllFileUploads(e);return(await this.fetchAllUploadURLExternal(t)).forEach(((e,i)=>{t[i].upload_url=e.upload_url,t[i].file_id=e.file_id})),await this.postFileUploadsToExternalURL(t,e),{ok:!0,files:await this.completeFileUploads(t)}}async fetchAllUploadURLExternal(e){return Promise.all(e.map((e=>{const t={filename:e.filename,length:e.length,alt_text:e.alt_text,snippet_type:e.snippet_type};return this.files.getUploadURLExternal(t)})))}async completeFileUploads(e){const t=Object.values((0,Q.getAllFileUploadsToComplete)(e));return Promise.all(t.map((e=>this.files.completeUploadExternal(e))))}async postFileUploadsToExternalURL(e,t){return Promise.all(e.map((async e=>{const{upload_url:i,file_id:s,filename:r,data:n}=e,o=n;if(i){const e={};t.token&&(e.Authorization=`Bearer ${t.token}`);const n=await this.makeRequest(i,{body:o},e);if(200!==n.status)return Promise.reject(Error(`Failed to upload file (id:${s}, filename: ${r})`));const a={ok:!0,body:n.data};return Promise.resolve(a)}return Promise.reject(Error(`No upload url found for file (id: ${s}, filename: ${r}`))})))}async getAllFileUploads(e){let t=[];return(e.file||e.content)&&t.push(await(0,Q.getFileUploadJob)(e,this.logger)),e.file_uploads&&(t=t.concat(await(0,Q.getMultipleFileUploadJobs)(e,this.logger))),t}async makeRequest(e,t,i={}){return(0,g.default)((()=>this.requestQueue.add((async()=>{const s=e.startsWith("https")?e:`${this.axios.getUri()+e}`;this.logger.debug(`http request url: ${s}`),this.logger.debug(`http request body: ${JSON.stringify(T(t))}`),this.logger.debug(`http request headers: ${JSON.stringify(T(i))}`);try{const s=Object.assign({headers:i},this.tlsConfig);e.endsWith("admin.analytics.getFile")&&(s.responseType="arraybuffer");const r=await this.axios.post(e,t,s);if(this.logger.debug("http response received"),429===r.status){const i=_(r);if(void 0!==i){if(this.emit(k.RATE_LIMITED,i,{url:e,body:t}),this.rejectRateLimitedCalls)throw new g.AbortError((0,I.rateLimitedErrorWithDelay)(i));throw this.logger.info(`API Call failed due to rate limiting. Will retry in ${i} seconds.`),this.requestQueue.pause(),await(0,b.default)(1e3*i),this.requestQueue.start(),Error(`A rate limit was exceeded (url: ${e}, retry-after: ${i})`)}throw new g.AbortError(new Error(`Retry header did not contain a valid timeout (url: ${e}, retry-after header: ${r.headers["retry-after"]})`))}if(200!==r.status)throw(0,I.httpErrorFromResponse)(r);return r}catch(e){const t=e;if(this.logger.warn("http request failed",t.message),t.request)throw(0,I.requestErrorWithOriginal)(t);throw e}}))),this.retryConfig)}serializeApiCallOptions(e,t){let i=!1;const s=Object.entries(e).map((([e,t])=>{if(null==t)return[];let s=t;return Buffer.isBuffer(t)||(0,h.default)(t)?i=!0:"string"!=typeof t&&"number"!=typeof t&&"boolean"!=typeof t&&(s=JSON.stringify(t)),[e,s]}));if(i){this.logger.debug("Request arguments contain binary data");const e=s.reduce(((e,[t,i])=>{if(Buffer.isBuffer(i)||(0,h.default)(i)){const s={};s.filename=(()=>{const e=i;return"string"==typeof e.name?(0,A.basename)(e.name):"string"==typeof e.path?(0,A.basename)(e.path):"Untitled"})(),e.append(t,i,s)}else void 0!==t&&void 0!==i&&e.append(t,i);return e}),new E.default);return Object.entries(e.getHeaders()).forEach((([e,i])=>{t[e]=i})),e}return t["Content-Type"]="application/x-www-form-urlencoded",(0,p.stringify)(s.reduce(((e,[t,i])=>(void 0!==t&&void 0!==i&&(e[t]=i),e)),{}))}async buildResult(e){let{data:t}=e;const i="application/gzip"===e.headers["content-type"];if(i)try{const e=await new Promise(((e,i)=>{u.default.unzip(t,((t,s)=>t?i(t):e(s.toString().split("\n"))))})).then((e=>e)).catch((e=>{throw e})),i=[];Array.isArray(e)&&e.forEach((e=>{e&&e.length>0&&i.push(JSON.parse(e))})),t={file_data:i}}catch(e){t={ok:!1,error:e}}else i||"/api/admin.analytics.getFile"!==e.request.path||(t=JSON.parse((new d.TextDecoder).decode(t)));if("string"==typeof t)try{t=JSON.parse(t)}catch(e){t={ok:!1,error:t}}void 0===t.response_metadata&&(t.response_metadata={}),void 0!==e.headers["x-oauth-scopes"]&&(t.response_metadata.scopes=e.headers["x-oauth-scopes"].trim().split(/\s*,\s*/)),void 0!==e.headers["x-accepted-oauth-scopes"]&&(t.response_metadata.acceptedScopes=e.headers["x-accepted-oauth-scopes"].trim().split(/\s*,\s*/));const s=_(e);return void 0!==s&&(t.response_metadata.retryAfter=s),t}}function S(e,t){if(void 0!==e&&void 0!==e.response_metadata&&void 0!==e.response_metadata.next_cursor&&""!==e.response_metadata.next_cursor)return{limit:t,cursor:e.response_metadata.next_cursor}}function _(e){if(void 0!==e.headers["retry-after"]){const t=parseInt(e.headers["retry-after"],10);if(!Number.isNaN(t))return t}}function R(e){return`The given thread_ts value in the request payload for a ${e} call is a float value. We highly recommend using a string value instead.`}function T(e){return Object.entries(e).map((([e,t])=>{if(null==t)return[];let i=t;return(null!==e.match(/.*token.*/)||e.match(/[Aa]uthorization/))&&(i="[[REDACTED]]"),Buffer.isBuffer(t)||(0,h.default)(t)?i="[[BINARY VALUE OMITTED]]":"string"!=typeof t&&"number"!=typeof t&&"boolean"!=typeof t&&(i=JSON.stringify(t)),[e,i]})).reduce(((e,[t,i])=>(void 0!==t&&void 0!==i&&(e[t]=i),e)),{})}t.WebClient=D,D.loggerName="WebClient",t.default=D,t.buildThreadTsWarningMessage=R},47056:(e,t)=>{"use strict";var i;function s(e,t){const i=e;return i.code=t,i}Object.defineProperty(t,"__esModule",{value:!0}),t.rateLimitedErrorWithDelay=t.platformErrorFromResult=t.httpErrorFromResponse=t.requestErrorWithOriginal=t.errorWithCode=t.ErrorCode=void 0,function(e){e.RequestError="slack_webapi_request_error",e.HTTPError="slack_webapi_http_error",e.PlatformError="slack_webapi_platform_error",e.RateLimitedError="slack_webapi_rate_limited_error",e.FileUploadInvalidArgumentsError="slack_webapi_file_upload_invalid_args_error",e.FileUploadReadFileDataError="slack_webapi_file_upload_read_file_data_error"}(i=t.ErrorCode||(t.ErrorCode={})),t.errorWithCode=s,t.requestErrorWithOriginal=function(e){const t=s(new Error(`A request error occurred: ${e.message}`),i.RequestError);return t.original=e,t},t.httpErrorFromResponse=function(e){const t=s(new Error(`An HTTP protocol error occurred: statusCode = ${e.status}`),i.HTTPError);t.statusCode=e.status,t.statusMessage=e.statusText;const r={};return Object.keys(e.headers).forEach((t=>{t&&e.headers[t]&&(r[t]=e.headers[t])})),t.headers=r,t.body=e.data,t},t.platformErrorFromResult=function(e){const t=s(new Error(`An API error occurred: ${e.error}`),i.PlatformError);return t.data=e,t},t.rateLimitedErrorWithDelay=function(e){const t=s(new Error(`A rate-limit has been reached, you may retry this request in ${e} seconds`),i.RateLimitedError);return t.retryAfter=e,t}},28666:(e,t,i)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.buildInvalidFilesUploadParamError=t.buildMultipleChannelsErrorMsg=t.buildChannelsWarning=t.buildFilesUploadMissingMessage=t.buildGeneralFilesUploadWarning=t.buildLegacyMethodWarning=t.buildMissingExtensionWarning=t.buildMissingFileNameWarning=t.buildLegacyFileTypeWarning=t.buildFileSizeErrorMsg=t.buildMissingFileIdError=t.warnIfLegacyFileType=t.warnIfMissingOrInvalidFileNameAndDefault=t.errorIfInvalidOrMissingFileData=t.errorIfChannelsCsv=t.warnIfChannels=t.warnIfNotUsingFilesUploadV2=t.getAllFileUploadsToComplete=t.getFileDataAsStream=t.getFileDataLength=t.getFileData=t.getMultipleFileUploadJobs=t.getFileUploadJob=void 0;const s=i(57147),r=i(12781),n=i(47056);async function o(e,t){var i,s,r,n;h(e,t),p(e,t),A(e);const o=d(e,t),l=await a(e),u=c(l);return{alt_text:e.alt_text,channel_id:null!==(i=e.channels)&&void 0!==i?i:e.channel_id,content:e.content,file:e.file,filename:null!==(s=e.filename)&&void 0!==s?s:o,initial_comment:e.initial_comment,snippet_type:e.snippet_type,thread_ts:e.thread_ts,title:null!==(r=e.title)&&void 0!==r?r:null!==(n=e.filename)&&void 0!==n?n:o,data:l,length:u}}async function a(e){u(e);const{file:t,content:i}=e;if(t){if(Buffer.isBuffer(t))return t;if("string"==typeof t)try{return(0,s.readFileSync)(t)}catch(e){throw(0,n.errorWithCode)(new Error(`Unable to resolve file data for ${t}. Please supply a filepath string, or binary data Buffer or String directly.`),n.ErrorCode.FileUploadInvalidArgumentsError)}const e=await l(t);if(e)return e}if(i)return Buffer.from(i);throw(0,n.errorWithCode)(new Error("There was an issue getting the file data for the file or content supplied"),n.ErrorCode.FileUploadReadFileDataError)}function c(e){if(e)return Buffer.byteLength(e,"utf8");throw(0,n.errorWithCode)(new Error("There was an issue calculating the size of your file"),n.ErrorCode.FileUploadReadFileDataError)}async function l(e){const t=[];return new Promise(((i,s)=>{e.on("readable",(()=>{let i;for(;null!==(i=e.read());)t.push(i)})),e.on("end",(()=>{if(t.length>0){const e=Buffer.concat(t);i(e)}else s(Error("No data in supplied file"))}))}))}function p(e,t){e.channels&&t.warn("Although the 'channels' parameter is still supported for smoother migration from legacy files.upload, we recommend using the new channel_id parameter with a single str value instead (e.g. 'C12345').")}function A(e){if((e.channels?e.channels.split(","):[]).length>1)throw(0,n.errorWithCode)(new Error("Sharing files with multiple channels is no longer supported in v2. Share files in each channel separately instead."),n.ErrorCode.FileUploadInvalidArgumentsError)}function u(e){const{file:t,content:i}=e;if(!t&&!i||t&&i)throw(0,n.errorWithCode)(new Error("Either a file or content field is required for valid file upload. You cannot supply both"),n.ErrorCode.FileUploadInvalidArgumentsError);if(t&&!("string"==typeof t||Buffer.isBuffer(t)||t instanceof r.Readable))throw(0,n.errorWithCode)(new Error("file must be a valid string path, buffer or Readable"),n.ErrorCode.FileUploadInvalidArgumentsError);if(i&&"string"!=typeof i)throw(0,n.errorWithCode)(new Error("content must be a string"),n.ErrorCode.FileUploadInvalidArgumentsError)}function d(e,t){var i;const s=`file.${null!==(i=e.filetype)&&void 0!==i?i:"txt"}`,{filename:r}=e;return r?(r.split(".").length<2&&t.warn(m(r)),r):(t.warn("filename is a required field for files.uploadV2. \n For backwards compatibility and ease of migration, defaulting the filename. For best experience and consistent unfurl behavior, you should set the filename property with correct file extension, e.g. image.png, text.txt"),s)}function h(e,t){e.filetype&&t.warn("filetype is no longer a supported field in files.uploadV2. \nPlease remove this field. To indicate file type, please do so via the required filename property using the appropriate file extension, e.g. image.png, text.txt")}function m(e){return`filename supplied '${e}' may be missing a proper extension. Missing extenions may result in unexpected unfurl behavior when shared`}function g(e){return`${e} may cause some issues like timeouts for relatively large files.`}t.getFileUploadJob=o,t.getMultipleFileUploadJobs=async function(e,t){if(e.file_uploads)return Promise.all(e.file_uploads.map((i=>{const{channel_id:s,channels:r,initial_comment:a,thread_ts:c}=i;if(s||r||a||c)throw(0,n.errorWithCode)(new Error("You may supply file_uploads only for a single channel, comment, thread respectively. Therefore, please supply any channel_id, initial_comment, thread_ts in the top-layer."),n.ErrorCode.FileUploadInvalidArgumentsError);return o(Object.assign(Object.assign({},i),{channels:e.channels,channel_id:e.channel_id,initial_comment:e.initial_comment,thread_ts:e.thread_ts}),t)})));throw new Error("Something went wrong with processing file_uploads")},t.getFileData=a,t.getFileDataLength=c,t.getFileDataAsStream=l,t.getAllFileUploadsToComplete=function(e){const t={};return e.forEach((e=>{const{channel_id:i,thread_ts:s,initial_comment:r,file_id:n,title:o}=e;if(!n)throw new Error("Missing required file id for file upload completion");{const e=`:::${i}:::${s}:::${r}`;Object.prototype.hasOwnProperty.call(t,e)?t[e].files.push({id:n,title:o}):t[e]={files:[{id:n,title:o}],channel_id:i,initial_comment:r,thread_ts:s}}})),t},t.warnIfNotUsingFilesUploadV2=function(e,t){const i=["files.upload"].includes(e);"files.upload"===e&&t.warn(g(e)),i&&t.info("Our latest recommendation is to use client.files.uploadV2() method, which is mostly compatible and much stabler, instead.")},t.warnIfChannels=p,t.errorIfChannelsCsv=A,t.errorIfInvalidOrMissingFileData=u,t.warnIfMissingOrInvalidFileNameAndDefault=d,t.warnIfLegacyFileType=h,t.buildMissingFileIdError=function(){return"Missing required file id for file upload completion"},t.buildFileSizeErrorMsg=function(){return"There was an issue calculating the size of your file"},t.buildLegacyFileTypeWarning=function(){return"filetype is no longer a supported field in files.uploadV2. \nPlease remove this field. To indicate file type, please do so via the required filename property using the appropriate file extension, e.g. image.png, text.txt"},t.buildMissingFileNameWarning=function(){return"filename is a required field for files.uploadV2. \n For backwards compatibility and ease of migration, defaulting the filename. For best experience and consistent unfurl behavior, you should set the filename property with correct file extension, e.g. image.png, text.txt"},t.buildMissingExtensionWarning=m,t.buildLegacyMethodWarning=g,t.buildGeneralFilesUploadWarning=function(){return"Our latest recommendation is to use client.files.uploadV2() method, which is mostly compatible and much stabler, instead."},t.buildFilesUploadMissingMessage=function(){return"Something went wrong with processing file_uploads"},t.buildChannelsWarning=function(){return"Although the 'channels' parameter is still supported for smoother migration from legacy files.upload, we recommend using the new channel_id parameter with a single str value instead (e.g. 'C12345')."},t.buildMultipleChannelsErrorMsg=function(){return"Sharing files with multiple channels is no longer supported in v2. Share files in each channel separately instead."},t.buildInvalidFilesUploadParamError=function(){return"You may supply file_uploads only for a single channel, comment, thread respectively. Therefore, please supply any channel_id, initial_comment, thread_ts in the top-layer."}},83313:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return new Promise((t=>{setTimeout(t,e)}))}},85314:function(e,t,i){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,i,s){void 0===s&&(s=i);var r=Object.getOwnPropertyDescriptor(t,i);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,s,r)}:function(e,t,i,s){void 0===s&&(s=i),e[s]=t[i]}),r=this&&this.__exportStar||function(e,t){for(var i in e)"default"===i||Object.prototype.hasOwnProperty.call(t,i)||s(t,e,i)},n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.addAppMetadata=t.retryPolicies=t.ErrorCode=t.LogLevel=t.WebClientEvent=t.WebClient=void 0;var o=i(11055);Object.defineProperty(t,"WebClient",{enumerable:!0,get:function(){return o.WebClient}}),Object.defineProperty(t,"WebClientEvent",{enumerable:!0,get:function(){return o.WebClientEvent}});var a=i(94139);Object.defineProperty(t,"LogLevel",{enumerable:!0,get:function(){return a.LogLevel}});var c=i(47056);Object.defineProperty(t,"ErrorCode",{enumerable:!0,get:function(){return c.ErrorCode}});var l=i(39847);Object.defineProperty(t,"retryPolicies",{enumerable:!0,get:function(){return n(l).default}});var p=i(57390);Object.defineProperty(t,"addAppMetadata",{enumerable:!0,get:function(){return p.addAppMetadata}}),r(i(25242),t),r(i(82356),t)},57390:function(e,t,i){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,i,s){void 0===s&&(s=i);var r=Object.getOwnPropertyDescriptor(t,i);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,s,r)}:function(e,t,i,s){void 0===s&&(s=i),e[s]=t[i]}),r=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)"default"!==i&&Object.prototype.hasOwnProperty.call(e,i)&&s(t,e,i);return r(t,e),t};Object.defineProperty(t,"__esModule",{value:!0}),t.getUserAgent=t.addAppMetadata=void 0;const o=n(i(22037)),a=i(71017),c=i(10191);function l(e){return e.replace("/",":")}const p=`${l(c.name)}/${c.version} ${(0,a.basename)(process.title)}/${process.version.replace("v","")} ${o.platform()}/${o.release()}`,A={};t.addAppMetadata=function({name:e,version:t}){A[l(e)]=t},t.getUserAgent=function(){const e=Object.entries(A).map((([e,t])=>`${e}/${t}`)).join(" ");return(e.length>0?`${e} `:"")+p}},94139:(e,t,i)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getLogger=t.LogLevel=void 0;const s=i(94832);var r=i(94832);Object.defineProperty(t,"LogLevel",{enumerable:!0,get:function(){return r.LogLevel}});let n=0;t.getLogger=function(e,t,i){const r=n;n+=1;const o=void 0!==i?i:new s.ConsoleLogger;return o.setName(`web-api:${e}:${r}`),void 0!==t&&o.setLevel(t),o}},25242:function(e,t,i){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,i,s){void 0===s&&(s=i);var r=Object.getOwnPropertyDescriptor(t,i);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,s,r)}:function(e,t,i,s){void 0===s&&(s=i),e[s]=t[i]}),r=this&&this.__exportStar||function(e,t){for(var i in e)"default"===i||Object.prototype.hasOwnProperty.call(t,i)||s(t,e,i)};Object.defineProperty(t,"__esModule",{value:!0}),t.cursorPaginationEnabledMethods=t.Methods=void 0;const n=i(91906),o=i(11055);function a(e,t){return e.apiCall.bind(e,t)}function c(e){return e.filesUploadV2.bind(e)}class l extends n.EventEmitter{constructor(){if(super(),this.admin={analytics:{getFile:a(this,"admin.analytics.getFile")},apps:{approve:a(this,"admin.apps.approve"),approved:{list:a(this,"admin.apps.approved.list")},clearResolution:a(this,"admin.apps.clearResolution"),requests:{cancel:a(this,"admin.apps.requests.cancel"),list:a(this,"admin.apps.requests.list")},restrict:a(this,"admin.apps.restrict"),restricted:{list:a(this,"admin.apps.restricted.list")},uninstall:a(this,"admin.apps.uninstall"),activities:{list:a(this,"admin.apps.activities.list")}},auth:{policy:{assignEntities:a(this,"admin.auth.policy.assignEntities"),getEntities:a(this,"admin.auth.policy.getEntities"),removeEntities:a(this,"admin.auth.policy.removeEntities")}},barriers:{create:a(this,"admin.barriers.create"),delete:a(this,"admin.barriers.delete"),list:a(this,"admin.barriers.list"),update:a(this,"admin.barriers.update")},conversations:{archive:a(this,"admin.conversations.archive"),bulkArchive:a(this,"admin.conversations.bulkArchive"),bulkDelete:a(this,"admin.conversations.bulkDelete"),bulkMove:a(this,"admin.conversations.bulkMove"),convertToPrivate:a(this,"admin.conversations.convertToPrivate"),convertToPublic:a(this,"admin.conversations.convertToPublic"),create:a(this,"admin.conversations.create"),delete:a(this,"admin.conversations.delete"),disconnectShared:a(this,"admin.conversations.disconnectShared"),ekm:{listOriginalConnectedChannelInfo:a(this,"admin.conversations.ekm.listOriginalConnectedChannelInfo")},getConversationPrefs:a(this,"admin.conversations.getConversationPrefs"),getTeams:a(this,"admin.conversations.getTeams"),invite:a(this,"admin.conversations.invite"),rename:a(this,"admin.conversations.rename"),restrictAccess:{addGroup:a(this,"admin.conversations.restrictAccess.addGroup"),listGroups:a(this,"admin.conversations.restrictAccess.listGroups"),removeGroup:a(this,"admin.conversations.restrictAccess.removeGroup")},getCustomRetention:a(this,"admin.conversations.getCustomRetention"),setCustomRetention:a(this,"admin.conversations.setCustomRetention"),removeCustomRetention:a(this,"admin.conversations.removeCustomRetention"),lookup:a(this,"admin.conversations.lookup"),search:a(this,"admin.conversations.search"),setConversationPrefs:a(this,"admin.conversations.setConversationPrefs"),setTeams:a(this,"admin.conversations.setTeams"),unarchive:a(this,"admin.conversations.unarchive")},emoji:{add:a(this,"admin.emoji.add"),addAlias:a(this,"admin.emoji.addAlias"),list:a(this,"admin.emoji.list"),remove:a(this,"admin.emoji.remove"),rename:a(this,"admin.emoji.rename")},functions:{list:a(this,"admin.functions.list"),permissions:{lookup:a(this,"admin.functions.permissions.lookup"),set:a(this,"admin.functions.permissions.set")}},inviteRequests:{approve:a(this,"admin.inviteRequests.approve"),approved:{list:a(this,"admin.inviteRequests.approved.list")},denied:{list:a(this,"admin.inviteRequests.denied.list")},deny:a(this,"admin.inviteRequests.deny"),list:a(this,"admin.inviteRequests.list")},teams:{admins:{list:a(this,"admin.teams.admins.list")},create:a(this,"admin.teams.create"),list:a(this,"admin.teams.list"),owners:{list:a(this,"admin.teams.owners.list")},settings:{info:a(this,"admin.teams.settings.info"),setDefaultChannels:a(this,"admin.teams.settings.setDefaultChannels"),setDescription:a(this,"admin.teams.settings.setDescription"),setDiscoverability:a(this,"admin.teams.settings.setDiscoverability"),setIcon:a(this,"admin.teams.settings.setIcon"),setName:a(this,"admin.teams.settings.setName")}},roles:{addAssignments:a(this,"admin.roles.addAssignments"),listAssignments:a(this,"admin.roles.listAssignments"),removeAssignments:a(this,"admin.roles.removeAssignments")},usergroups:{addChannels:a(this,"admin.usergroups.addChannels"),addTeams:a(this,"admin.usergroups.addTeams"),listChannels:a(this,"admin.usergroups.listChannels"),removeChannels:a(this,"admin.usergroups.removeChannels")},users:{assign:a(this,"admin.users.assign"),invite:a(this,"admin.users.invite"),list:a(this,"admin.users.list"),remove:a(this,"admin.users.remove"),session:{list:a(this,"admin.users.session.list"),reset:a(this,"admin.users.session.reset"),resetBulk:a(this,"admin.users.session.resetBulk"),invalidate:a(this,"admin.users.session.invalidate"),getSettings:a(this,"admin.users.session.getSettings"),setSettings:a(this,"admin.users.session.setSettings"),clearSettings:a(this,"admin.users.session.clearSettings")},unsupportedVersions:{export:a(this,"admin.users.unsupportedVersions.export")},setAdmin:a(this,"admin.users.setAdmin"),setExpiration:a(this,"admin.users.setExpiration"),setOwner:a(this,"admin.users.setOwner"),setRegular:a(this,"admin.users.setRegular")},workflows:{search:a(this,"admin.workflows.search"),unpublish:a(this,"admin.workflows.unpublish"),collaborators:{add:a(this,"admin.workflows.collaborators.add"),remove:a(this,"admin.workflows.collaborators.remove")},permissions:{lookup:a(this,"admin.workflows.permissions.lookup")}}},this.api={test:a(this,"api.test")},this.apps={connections:{open:a(this,"apps.connections.open")},event:{authorizations:{list:a(this,"apps.event.authorizations.list")}},manifest:{create:a(this,"apps.manifest.create"),delete:a(this,"apps.manifest.delete"),export:a(this,"apps.manifest.export"),update:a(this,"apps.manifest.update"),validate:a(this,"apps.manifest.validate")},uninstall:a(this,"apps.uninstall")},this.auth={revoke:a(this,"auth.revoke"),teams:{list:a(this,"auth.teams.list")},test:a(this,"auth.test")},this.bots={info:a(this,"bots.info")},this.bookmarks={add:a(this,"bookmarks.add"),edit:a(this,"bookmarks.edit"),list:a(this,"bookmarks.list"),remove:a(this,"bookmarks.remove")},this.calls={add:a(this,"calls.add"),end:a(this,"calls.end"),info:a(this,"calls.info"),update:a(this,"calls.update"),participants:{add:a(this,"calls.participants.add"),remove:a(this,"calls.participants.remove")}},this.chat={delete:a(this,"chat.delete"),deleteScheduledMessage:a(this,"chat.deleteScheduledMessage"),getPermalink:a(this,"chat.getPermalink"),meMessage:a(this,"chat.meMessage"),postEphemeral:a(this,"chat.postEphemeral"),postMessage:a(this,"chat.postMessage"),scheduleMessage:a(this,"chat.scheduleMessage"),scheduledMessages:{list:a(this,"chat.scheduledMessages.list")},unfurl:a(this,"chat.unfurl"),update:a(this,"chat.update")},this.conversations={acceptSharedInvite:a(this,"conversations.acceptSharedInvite"),approveSharedInvite:a(this,"conversations.approveSharedInvite"),archive:a(this,"conversations.archive"),close:a(this,"conversations.close"),create:a(this,"conversations.create"),declineSharedInvite:a(this,"conversations.declineSharedInvite"),history:a(this,"conversations.history"),info:a(this,"conversations.info"),invite:a(this,"conversations.invite"),inviteShared:a(this,"conversations.inviteShared"),join:a(this,"conversations.join"),kick:a(this,"conversations.kick"),leave:a(this,"conversations.leave"),list:a(this,"conversations.list"),listConnectInvites:a(this,"conversations.listConnectInvites"),mark:a(this,"conversations.mark"),members:a(this,"conversations.members"),open:a(this,"conversations.open"),rename:a(this,"conversations.rename"),replies:a(this,"conversations.replies"),setPurpose:a(this,"conversations.setPurpose"),setTopic:a(this,"conversations.setTopic"),unarchive:a(this,"conversations.unarchive")},this.dialog={open:a(this,"dialog.open")},this.dnd={endDnd:a(this,"dnd.endDnd"),endSnooze:a(this,"dnd.endSnooze"),info:a(this,"dnd.info"),setSnooze:a(this,"dnd.setSnooze"),teamInfo:a(this,"dnd.teamInfo")},this.emoji={list:a(this,"emoji.list")},this.files={delete:a(this,"files.delete"),info:a(this,"files.info"),list:a(this,"files.list"),revokePublicURL:a(this,"files.revokePublicURL"),sharedPublicURL:a(this,"files.sharedPublicURL"),upload:a(this,"files.upload"),uploadV2:c(this),getUploadURLExternal:a(this,"files.getUploadURLExternal"),completeUploadExternal:a(this,"files.completeUploadExternal"),comments:{delete:a(this,"files.comments.delete")},remote:{info:a(this,"files.remote.info"),list:a(this,"files.remote.list"),add:a(this,"files.remote.add"),update:a(this,"files.remote.update"),remove:a(this,"files.remote.remove"),share:a(this,"files.remote.share")}},this.migration={exchange:a(this,"migration.exchange")},this.oauth={access:a(this,"oauth.access"),v2:{access:a(this,"oauth.v2.access"),exchange:a(this,"oauth.v2.exchange")}},this.openid={connect:{token:a(this,"openid.connect.token"),userInfo:a(this,"openid.connect.userInfo")}},this.pins={add:a(this,"pins.add"),list:a(this,"pins.list"),remove:a(this,"pins.remove")},this.reactions={add:a(this,"reactions.add"),get:a(this,"reactions.get"),list:a(this,"reactions.list"),remove:a(this,"reactions.remove")},this.reminders={add:a(this,"reminders.add"),complete:a(this,"reminders.complete"),delete:a(this,"reminders.delete"),info:a(this,"reminders.info"),list:a(this,"reminders.list")},this.rtm={connect:a(this,"rtm.connect"),start:a(this,"rtm.start")},this.search={all:a(this,"search.all"),files:a(this,"search.files"),messages:a(this,"search.messages")},this.stars={add:a(this,"stars.add"),list:a(this,"stars.list"),remove:a(this,"stars.remove")},this.team={accessLogs:a(this,"team.accessLogs"),billableInfo:a(this,"team.billableInfo"),billing:{info:a(this,"team.billing.info")},info:a(this,"team.info"),integrationLogs:a(this,"team.integrationLogs"),preferences:{list:a(this,"team.preferences.list")},profile:{get:a(this,"team.profile.get")}},this.tooling={tokens:{rotate:a(this,"tooling.tokens.rotate")}},this.usergroups={create:a(this,"usergroups.create"),disable:a(this,"usergroups.disable"),enable:a(this,"usergroups.enable"),list:a(this,"usergroups.list"),update:a(this,"usergroups.update"),users:{list:a(this,"usergroups.users.list"),update:a(this,"usergroups.users.update")}},this.users={conversations:a(this,"users.conversations"),deletePhoto:a(this,"users.deletePhoto"),getPresence:a(this,"users.getPresence"),identity:a(this,"users.identity"),info:a(this,"users.info"),list:a(this,"users.list"),lookupByEmail:a(this,"users.lookupByEmail"),setPhoto:a(this,"users.setPhoto"),setPresence:a(this,"users.setPresence"),profile:{get:a(this,"users.profile.get"),set:a(this,"users.profile.set")}},this.views={open:a(this,"views.open"),publish:a(this,"views.publish"),push:a(this,"views.push"),update:a(this,"views.update")},this.workflows={stepCompleted:a(this,"workflows.stepCompleted"),stepFailed:a(this,"workflows.stepFailed"),updateStep:a(this,"workflows.updateStep")},this.channels={archive:a(this,"channels.archive"),create:a(this,"channels.create"),history:a(this,"channels.history"),info:a(this,"channels.info"),invite:a(this,"channels.invite"),join:a(this,"channels.join"),kick:a(this,"channels.kick"),leave:a(this,"channels.leave"),list:a(this,"channels.list"),mark:a(this,"channels.mark"),rename:a(this,"channels.rename"),replies:a(this,"channels.replies"),setPurpose:a(this,"channels.setPurpose"),setTopic:a(this,"channels.setTopic"),unarchive:a(this,"channels.unarchive")},this.groups={archive:a(this,"groups.archive"),create:a(this,"groups.create"),createChild:a(this,"groups.createChild"),history:a(this,"groups.history"),info:a(this,"groups.info"),invite:a(this,"groups.invite"),kick:a(this,"groups.kick"),leave:a(this,"groups.leave"),list:a(this,"groups.list"),mark:a(this,"groups.mark"),open:a(this,"groups.open"),rename:a(this,"groups.rename"),replies:a(this,"groups.replies"),setPurpose:a(this,"groups.setPurpose"),setTopic:a(this,"groups.setTopic"),unarchive:a(this,"groups.unarchive")},this.im={close:a(this,"im.close"),history:a(this,"im.history"),list:a(this,"im.list"),mark:a(this,"im.mark"),open:a(this,"im.open"),replies:a(this,"im.replies")},this.mpim={close:a(this,"mpim.close"),history:a(this,"mpim.history"),list:a(this,"mpim.list"),mark:a(this,"mpim.mark"),open:a(this,"mpim.open"),replies:a(this,"mpim.replies")},new.target!==o.WebClient&&!(new.target.prototype instanceof o.WebClient))throw new Error("Attempt to inherit from WebClient methods without inheriting from WebClient")}}t.Methods=l,t.cursorPaginationEnabledMethods=new Set,t.cursorPaginationEnabledMethods.add("admin.apps.approved.list"),t.cursorPaginationEnabledMethods.add("admin.apps.requests.list"),t.cursorPaginationEnabledMethods.add("admin.apps.restricted.list"),t.cursorPaginationEnabledMethods.add("admin.apps.activities.list"),t.cursorPaginationEnabledMethods.add("admin.auth.policy.getEntities"),t.cursorPaginationEnabledMethods.add("admin.barriers.list"),t.cursorPaginationEnabledMethods.add("admin.conversations.lookup"),t.cursorPaginationEnabledMethods.add("admin.conversations.ekm.listOriginalConnectedChannelInfo"),t.cursorPaginationEnabledMethods.add("admin.conversations.getTeams"),t.cursorPaginationEnabledMethods.add("admin.conversations.search"),t.cursorPaginationEnabledMethods.add("admin.emoji.list"),t.cursorPaginationEnabledMethods.add("admin.inviteRequests.approved.list"),t.cursorPaginationEnabledMethods.add("admin.inviteRequests.denied.list"),t.cursorPaginationEnabledMethods.add("admin.inviteRequests.list"),t.cursorPaginationEnabledMethods.add("admin.roles.listAssignments"),t.cursorPaginationEnabledMethods.add("admin.inviteRequests.list"),t.cursorPaginationEnabledMethods.add("admin.teams.admins.list"),t.cursorPaginationEnabledMethods.add("admin.teams.list"),t.cursorPaginationEnabledMethods.add("admin.teams.owners.list"),t.cursorPaginationEnabledMethods.add("admin.users.list"),t.cursorPaginationEnabledMethods.add("admin.users.session.list"),t.cursorPaginationEnabledMethods.add("admin.worfklows.search"),t.cursorPaginationEnabledMethods.add("apps.event.authorizations.list"),t.cursorPaginationEnabledMethods.add("auth.teams.list"),t.cursorPaginationEnabledMethods.add("channels.list"),t.cursorPaginationEnabledMethods.add("chat.scheduledMessages.list"),t.cursorPaginationEnabledMethods.add("conversations.history"),t.cursorPaginationEnabledMethods.add("conversations.list"),t.cursorPaginationEnabledMethods.add("conversations.listConnectInvites"),t.cursorPaginationEnabledMethods.add("conversations.members"),t.cursorPaginationEnabledMethods.add("conversations.replies"),t.cursorPaginationEnabledMethods.add("files.info"),t.cursorPaginationEnabledMethods.add("files.remote.list"),t.cursorPaginationEnabledMethods.add("groups.list"),t.cursorPaginationEnabledMethods.add("im.list"),t.cursorPaginationEnabledMethods.add("mpim.list"),t.cursorPaginationEnabledMethods.add("reactions.list"),t.cursorPaginationEnabledMethods.add("stars.list"),t.cursorPaginationEnabledMethods.add("team.accessLogs"),t.cursorPaginationEnabledMethods.add("users.conversations"),t.cursorPaginationEnabledMethods.add("users.list"),r(i(17578),t)},82356:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0})},39847:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.rapidRetryPolicy=t.fiveRetriesInFiveMinutes=t.tenRetriesInAboutThirtyMinutes=void 0,t.tenRetriesInAboutThirtyMinutes={retries:10,factor:1.96821,randomize:!0},t.fiveRetriesInFiveMinutes={retries:5,factor:3.86},t.rapidRetryPolicy={minTimeout:0,maxTimeout:1};const i={tenRetriesInAboutThirtyMinutes:t.tenRetriesInAboutThirtyMinutes,fiveRetriesInFiveMinutes:t.fiveRetriesInFiveMinutes,rapidRetryPolicy:t.rapidRetryPolicy};t.default=i},14686:(e,t,i)=>{"use strict";const s=i(96066),r=i(53072);class n extends Error{constructor(e){if(!Array.isArray(e))throw new TypeError("Expected input to be an Array, got "+typeof e);let t=(e=[...e].map((e=>e instanceof Error?e:null!==e&&"object"==typeof e?Object.assign(new Error(e.message),e):new Error(e)))).map((e=>"string"==typeof e.stack?r(e.stack).replace(/\s+at .*aggregate-error\/index.js:\d+:\d+\)?/g,""):String(e))).join("\n");t="\n"+s(t,4),super(t),this.name="AggregateError",Object.defineProperty(this,"_errors",{value:e})}*[Symbol.iterator](){for(const e of this._errors)yield e}}e.exports=n},86873:e=>{"use strict";e.exports=({onlyFirst:e=!1}={})=>{const t=["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)","(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))"].join("|");return new RegExp(t,e?void 0:"g")}},78413:(e,t,i)=>{"use strict";e=i.nmd(e);const s=(e,t)=>(...i)=>`[${e(...i)+t}m`,r=(e,t)=>(...i)=>{const s=e(...i);return`[${38+t};5;${s}m`},n=(e,t)=>(...i)=>{const s=e(...i);return`[${38+t};2;${s[0]};${s[1]};${s[2]}m`},o=e=>e,a=(e,t,i)=>[e,t,i],c=(e,t,i)=>{Object.defineProperty(e,t,{get:()=>{const s=i();return Object.defineProperty(e,t,{value:s,enumerable:!0,configurable:!0}),s},enumerable:!0,configurable:!0})};let l;const p=(e,t,s,r)=>{void 0===l&&(l=i(60398));const n=r?10:0,o={};for(const[i,r]of Object.entries(l)){const a="ansi16"===i?"ansi":i;i===t?o[a]=e(s,n):"object"==typeof r&&(o[a]=e(r[t],n))}return o};Object.defineProperty(e,"exports",{enumerable:!0,get:function(){const e=new Map,t={modifier:{reset:[0,0],bold:[1,22],dim:[2,22],italic:[3,23],underline:[4,24],inverse:[7,27],hidden:[8,28],strikethrough:[9,29]},color:{black:[30,39],red:[31,39],green:[32,39],yellow:[33,39],blue:[34,39],magenta:[35,39],cyan:[36,39],white:[37,39],blackBright:[90,39],redBright:[91,39],greenBright:[92,39],yellowBright:[93,39],blueBright:[94,39],magentaBright:[95,39],cyanBright:[96,39],whiteBright:[97,39]},bgColor:{bgBlack:[40,49],bgRed:[41,49],bgGreen:[42,49],bgYellow:[43,49],bgBlue:[44,49],bgMagenta:[45,49],bgCyan:[46,49],bgWhite:[47,49],bgBlackBright:[100,49],bgRedBright:[101,49],bgGreenBright:[102,49],bgYellowBright:[103,49],bgBlueBright:[104,49],bgMagentaBright:[105,49],bgCyanBright:[106,49],bgWhiteBright:[107,49]}};t.color.gray=t.color.blackBright,t.bgColor.bgGray=t.bgColor.bgBlackBright,t.color.grey=t.color.blackBright,t.bgColor.bgGrey=t.bgColor.bgBlackBright;for(const[i,s]of Object.entries(t)){for(const[i,r]of Object.entries(s))t[i]={open:`[${r[0]}m`,close:`[${r[1]}m`},s[i]=t[i],e.set(r[0],r[1]);Object.defineProperty(t,i,{value:s,enumerable:!1})}return Object.defineProperty(t,"codes",{value:e,enumerable:!1}),t.color.close="",t.bgColor.close="",c(t.color,"ansi",(()=>p(s,"ansi16",o,!1))),c(t.color,"ansi256",(()=>p(r,"ansi256",o,!1))),c(t.color,"ansi16m",(()=>p(n,"rgb",a,!1))),c(t.bgColor,"ansi",(()=>p(s,"ansi16",o,!0))),c(t.bgColor,"ansi256",(()=>p(r,"ansi256",o,!0))),c(t.bgColor,"ansi16m",(()=>p(n,"rgb",a,!0))),t}})},62720:(e,t,i)=>{e.exports={parallel:i(61286),serial:i(74694),serialOrdered:i(87458)}},34653:e=>{function t(e){"function"==typeof this.jobs[e]&&this.jobs[e]()}e.exports=function(e){Object.keys(e.jobs).forEach(t.bind(e)),e.jobs={}}},5209:(e,t,i)=>{var s=i(45623);e.exports=function(e){var t=!1;return s((function(){t=!0})),function(i,r){t?e(i,r):s((function(){e(i,r)}))}}},45623:e=>{e.exports=function(e){var t="function"==typeof setImmediate?setImmediate:"object"==typeof process&&"function"==typeof process.nextTick?process.nextTick:null;t?t(e):setTimeout(e,0)}},28773:(e,t,i)=>{var s=i(5209),r=i(34653);e.exports=function(e,t,i,n){var o=i.keyedList?i.keyedList[i.index]:i.index;i.jobs[o]=function(e,t,i,r){return 2==e.length?e(i,s(r)):e(i,t,s(r))}(t,o,e[o],(function(e,t){o in i.jobs&&(delete i.jobs[o],e?r(i):i.results[o]=t,n(e,i.results))}))}},67630:e=>{e.exports=function(e,t){var i=!Array.isArray(e),s={index:0,keyedList:i||t?Object.keys(e):null,jobs:{},results:i?{}:[],size:i?Object.keys(e).length:e.length};return t&&s.keyedList.sort(i?t:function(i,s){return t(e[i],e[s])}),s}},45067:(e,t,i)=>{var s=i(34653),r=i(5209);e.exports=function(e){Object.keys(this.jobs).length&&(this.index=this.size,s(this),r(e)(null,this.results))}},61286:(e,t,i)=>{var s=i(28773),r=i(67630),n=i(45067);e.exports=function(e,t,i){for(var o=r(e);o.index<(o.keyedList||e).length;)s(e,t,o,(function(e,t){e?i(e,t):0!==Object.keys(o.jobs).length||i(null,o.results)})),o.index++;return n.bind(o,i)}},74694:(e,t,i)=>{var s=i(87458);e.exports=function(e,t,i){return s(e,t,null,i)}},87458:(e,t,i)=>{var s=i(28773),r=i(67630),n=i(45067);function o(e,t){return et?1:0}e.exports=function(e,t,i,o){var a=r(e,i);return s(e,t,a,(function i(r,n){r?o(r,n):(a.index++,a.index<(a.keyedList||e).length?s(e,t,a,i):o(null,a.results))})),n.bind(a,o)},e.exports.ascending=o,e.exports.descending=function(e,t){return-1*o(e,t)}},72547:e=>{"use strict";function t(e,t,r){e instanceof RegExp&&(e=i(e,r)),t instanceof RegExp&&(t=i(t,r));var n=s(e,t,r);return n&&{start:n[0],end:n[1],pre:r.slice(0,n[0]),body:r.slice(n[0]+e.length,n[1]),post:r.slice(n[1]+t.length)}}function i(e,t){var i=t.match(e);return i?i[0]:null}function s(e,t,i){var s,r,n,o,a,c=i.indexOf(e),l=i.indexOf(t,c+1),p=c;if(c>=0&&l>0){if(e===t)return[c,l];for(s=[],n=i.length;p>=0&&!a;)p==c?(s.push(p),c=i.indexOf(e,p+1)):1==s.length?a=[s.pop(),l]:((r=s.pop())=0?c:l;s.length&&(a=[n,o])}return a}e.exports=t,t.range=s},8903:(e,t,i)=>{var s=i(46459),r=i(32361),n=i(62235),o=Function.bind,a=o.bind(o);function c(e,t,i){var s=a(n,null).apply(null,i?[t,i]:[t]);e.api={remove:s},e.remove=s,["before","error","after","wrap"].forEach((function(s){var n=i?[t,s,i]:[t,s];e[s]=e.api[s]=a(r,null).apply(null,n)}))}function l(){var e={registry:{}},t=s.bind(null,e);return c(t,e),t}var p=!1;function A(){return p||(console.warn('[before-after-hook]: "Hook()" repurposing warning, use "Hook.Collection()". Read more: https://git.io/upgrade-before-after-hook-to-1.4'),p=!0),l()}A.Singular=function(){var e={registry:{}},t=s.bind(null,e,"h");return c(t,e,"h"),t}.bind(),A.Collection=l.bind(),e.exports=A,e.exports.Hook=A,e.exports.Singular=A.Singular,e.exports.Collection=A.Collection},32361:e=>{e.exports=function(e,t,i,s){var r=s;e.registry[i]||(e.registry[i]=[]),"before"===t&&(s=function(e,t){return Promise.resolve().then(r.bind(null,t)).then(e.bind(null,t))}),"after"===t&&(s=function(e,t){var i;return Promise.resolve().then(e.bind(null,t)).then((function(e){return r(i=e,t)})).then((function(){return i}))}),"error"===t&&(s=function(e,t){return Promise.resolve().then(e.bind(null,t)).catch((function(e){return r(e,t)}))}),e.registry[i].push({hook:s,orig:r})}},46459:e=>{e.exports=function e(t,i,s,r){if("function"!=typeof s)throw new Error("method for before hook must be a function");return r||(r={}),Array.isArray(i)?i.reverse().reduce((function(i,s){return e.bind(null,t,s,i,r)}),s)():Promise.resolve().then((function(){return t.registry[i]?t.registry[i].reduce((function(e,t){return t.hook.bind(null,e,r)}),s)():s(r)}))}},62235:e=>{e.exports=function(e,t,i){if(e.registry[t]){var s=e.registry[t].map((function(e){return e.orig})).indexOf(i);-1!==s&&e.registry[t].splice(s,1)}}},20276:(e,t,i)=>{"use strict";const{Buffer:s}=i(14300),r=Symbol.for("BufferList");function n(e){if(!(this instanceof n))return new n(e);n._init.call(this,e)}n._init=function(e){Object.defineProperty(this,r,{value:!0}),this._bufs=[],this.length=0,e&&this.append(e)},n.prototype._new=function(e){return new n(e)},n.prototype._offset=function(e){if(0===e)return[0,0];let t=0;for(let i=0;ithis.length||e<0)return;const t=this._offset(e);return this._bufs[t[0]][t[1]]},n.prototype.slice=function(e,t){return"number"==typeof e&&e<0&&(e+=this.length),"number"==typeof t&&t<0&&(t+=this.length),this.copy(null,0,e,t)},n.prototype.copy=function(e,t,i,r){if(("number"!=typeof i||i<0)&&(i=0),("number"!=typeof r||r>this.length)&&(r=this.length),i>=this.length)return e||s.alloc(0);if(r<=0)return e||s.alloc(0);const n=!!e,o=this._offset(i),a=r-i;let c=a,l=n&&t||0,p=o[1];if(0===i&&r===this.length){if(!n)return 1===this._bufs.length?this._bufs[0]:s.concat(this._bufs,this.length);for(let t=0;ti)){this._bufs[t].copy(e,l,p,p+c),l+=i;break}this._bufs[t].copy(e,l,p),l+=i,c-=i,p&&(p=0)}return e.length>l?e.slice(0,l):e},n.prototype.shallowSlice=function(e,t){if(e=e||0,t="number"!=typeof t?this.length:t,e<0&&(e+=this.length),t<0&&(t+=this.length),e===t)return this._new();const i=this._offset(e),s=this._offset(t),r=this._bufs.slice(i[0],s[0]+1);return 0===s[1]?r.pop():r[r.length-1]=r[r.length-1].slice(0,s[1]),0!==i[1]&&(r[0]=r[0].slice(i[1])),this._new(r)},n.prototype.toString=function(e,t,i){return this.slice(t,i).toString(e)},n.prototype.consume=function(e){if(e=Math.trunc(e),Number.isNaN(e)||e<=0)return this;for(;this._bufs.length;){if(!(e>=this._bufs[0].length)){this._bufs[0]=this._bufs[0].slice(e),this.length-=e;break}e-=this._bufs[0].length,this.length-=this._bufs[0].length,this._bufs.shift()}return this},n.prototype.duplicate=function(){const e=this._new();for(let t=0;tthis.length?this.length:t;const r=this._offset(t);let n=r[0],o=r[1];for(;n=e.length){const i=t.indexOf(e,o);if(-1!==i)return this._reverseOffset([n,i]);o=t.length-e.length+1}else{const t=this._reverseOffset([n,o]);if(this._match(t,e))return t;o++}o=0}return-1},n.prototype._match=function(e,t){if(this.length-e{"use strict";const s=i(65945).Duplex,r=i(44236),n=i(20276);function o(e){if(!(this instanceof o))return new o(e);if("function"==typeof e){this._callback=e;const t=function(e){this._callback&&(this._callback(e),this._callback=null)}.bind(this);this.on("pipe",(function(e){e.on("error",t)})),this.on("unpipe",(function(e){e.removeListener("error",t)})),e=null}n._init.call(this,e),s.call(this)}r(o,s),Object.assign(o.prototype,n.prototype),o.prototype._new=function(e){return new o(e)},o.prototype._write=function(e,t,i){this._appendBuffer(e),"function"==typeof i&&i()},o.prototype._read=function(e){if(!this.length)return this.push(null);e=Math.min(e,this.length),this.push(this.slice(0,e)),this.consume(e)},o.prototype.end=function(e){s.prototype.end.call(this,e),this._callback&&(this._callback(null,this.slice()),this._callback=null)},o.prototype._destroy=function(e,t){this._bufs.length=0,this.length=0,t(e)},o.prototype._isBufferList=function(e){return e instanceof o||e instanceof n||o.isBufferList(e)},o.isBufferList=n.isBufferList,e.exports=o,e.exports.BufferListStream=o,e.exports.BufferList=n},32722:function(e){var t;t=function(){"use strict";var e,t,i="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},s={load:function(e,t,i={}){var s,r,n;for(s in t)n=t[s],i[s]=null!=(r=e[s])?r:n;return i},overwrite:function(e,t,i={}){var s,r;for(s in e)r=e[s],void 0!==t[s]&&(i[s]=r);return i}},r=class{constructor(e,t){this.incr=e,this.decr=t,this._first=null,this._last=null,this.length=0}push(e){var t;this.length++,"function"==typeof this.incr&&this.incr(),t={value:e,prev:this._last,next:null},null!=this._last?(this._last.next=t,this._last=t):this._first=this._last=t}shift(){var e;if(null!=this._first)return this.length--,"function"==typeof this.decr&&this.decr(),e=this._first.value,null!=(this._first=this._first.next)?this._first.prev=null:this._last=null,e}first(){if(null!=this._first)return this._first.value}getArray(){var e,t,i;for(e=this._first,i=[];null!=e;)i.push((t=e,e=e.next,t.value));return i}forEachShift(e){var t;for(t=this.shift();null!=t;)e(t),t=this.shift()}debug(){var e,t,i,s,r;for(e=this._first,r=[];null!=e;)r.push((t=e,e=e.next,{value:t.value,prev:null!=(i=t.prev)?i.value:void 0,next:null!=(s=t.next)?s.value:void 0}));return r}},n=class{constructor(e){if(this.instance=e,this._events={},null!=this.instance.on||null!=this.instance.once||null!=this.instance.removeAllListeners)throw new Error("An Emitter already exists for this object");this.instance.on=(e,t)=>this._addListener(e,"many",t),this.instance.once=(e,t)=>this._addListener(e,"once",t),this.instance.removeAllListeners=(e=null)=>null!=e?delete this._events[e]:this._events={}}_addListener(e,t,i){var s;return null==(s=this._events)[e]&&(s[e]=[]),this._events[e].push({cb:i,status:t}),this.instance}listenerCount(e){return null!=this._events[e]?this._events[e].length:0}async trigger(e,...t){var i,s;try{if("debug"!==e&&this.trigger("debug",`Event triggered: ${e}`,t),null==this._events[e])return;return this._events[e]=this._events[e].filter((function(e){return"none"!==e.status})),s=this._events[e].map((async e=>{var i,s;if("none"!==e.status){"once"===e.status&&(e.status="none");try{return"function"==typeof(null!=(s="function"==typeof e.cb?e.cb(...t):void 0)?s.then:void 0)?await s:s}catch(e){return i=e,this.trigger("error",i),null}}})),(await Promise.all(s)).find((function(e){return null!=e}))}catch(e){return i=e,this.trigger("error",i),null}}};e=r,t=n;var o,a,c=class extends Error{};a=s,o=c;var l,p,A=class{constructor(e,t,i,s,r,n,o,c){this.task=e,this.args=t,this.rejectOnDrop=r,this.Events=n,this._states=o,this.Promise=c,this.options=a.load(i,s),this.options.priority=this._sanitizePriority(this.options.priority),this.options.id===s.id&&(this.options.id=`${this.options.id}-${this._randomIndex()}`),this.promise=new this.Promise(((e,t)=>{this._resolve=e,this._reject=t})),this.retryCount=0}_sanitizePriority(e){var t;return(t=~~e!==e?5:e)<0?0:t>9?9:t}_randomIndex(){return Math.random().toString(36).slice(2)}doDrop({error:e,message:t="This job has been dropped by Bottleneck"}={}){return!!this._states.remove(this.options.id)&&(this.rejectOnDrop&&this._reject(null!=e?e:new o(t)),this.Events.trigger("dropped",{args:this.args,options:this.options,task:this.task,promise:this.promise}),!0)}_assertStatus(e){var t;if((t=this._states.jobStatus(this.options.id))!==e&&("DONE"!==e||null!==t))throw new o(`Invalid job status ${t}, expected ${e}. Please open an issue at https://github.com/SGrondin/bottleneck/issues`)}doReceive(){return this._states.start(this.options.id),this.Events.trigger("received",{args:this.args,options:this.options})}doQueue(e,t){return this._assertStatus("RECEIVED"),this._states.next(this.options.id),this.Events.trigger("queued",{args:this.args,options:this.options,reachedHWM:e,blocked:t})}doRun(){return 0===this.retryCount?(this._assertStatus("QUEUED"),this._states.next(this.options.id)):this._assertStatus("EXECUTING"),this.Events.trigger("scheduled",{args:this.args,options:this.options})}async doExecute(e,t,i,s){var r,n,o;0===this.retryCount?(this._assertStatus("RUNNING"),this._states.next(this.options.id)):this._assertStatus("EXECUTING"),n={args:this.args,options:this.options,retryCount:this.retryCount},this.Events.trigger("executing",n);try{if(o=await(null!=e?e.schedule(this.options,this.task,...this.args):this.task(...this.args)),t())return this.doDone(n),await s(this.options,n),this._assertStatus("DONE"),this._resolve(o)}catch(e){return r=e,this._onFailure(r,n,t,i,s)}}doExpire(e,t,i){var s,r;return this._states.jobStatus("RUNNING"===this.options.id)&&this._states.next(this.options.id),this._assertStatus("EXECUTING"),r={args:this.args,options:this.options,retryCount:this.retryCount},s=new o(`This job timed out after ${this.options.expiration} ms.`),this._onFailure(s,r,e,t,i)}async _onFailure(e,t,i,s,r){var n,o;if(i())return null!=(n=await this.Events.trigger("failed",e,t))?(o=~~n,this.Events.trigger("retry",`Retrying ${this.options.id} after ${o} ms`,t),this.retryCount++,s(o)):(this.doDone(t),await r(this.options,t),this._assertStatus("DONE"),this._reject(e))}doDone(e){return this._assertStatus("EXECUTING"),this._states.next(this.options.id),this.Events.trigger("done",e)}};p=s,l=c;var u;u=c;var d;d=r;var h,m,g,f,E,C="2.19.5",y={version:C},v=Object.freeze({version:C,default:y}),I=()=>console.log("You must import the full version of Bottleneck in order to use this feature."),B=()=>console.log("You must import the full version of Bottleneck in order to use this feature.");E=s,h=n,g=I,m=B,f=()=>console.log("You must import the full version of Bottleneck in order to use this feature.");var w,b,Q=function(){class e{constructor(e={}){this.deleteKey=this.deleteKey.bind(this),this.limiterOptions=e,E.load(this.limiterOptions,this.defaults,this),this.Events=new h(this),this.instances={},this.Bottleneck=U,this._startAutoCleanup(),this.sharedConnection=null!=this.connection,null==this.connection&&("redis"===this.limiterOptions.datastore?this.connection=new g(Object.assign({},this.limiterOptions,{Events:this.Events})):"ioredis"===this.limiterOptions.datastore&&(this.connection=new m(Object.assign({},this.limiterOptions,{Events:this.Events}))))}key(e=""){var t;return null!=(t=this.instances[e])?t:(()=>{var t;return t=this.instances[e]=new this.Bottleneck(Object.assign(this.limiterOptions,{id:`${this.id}-${e}`,timeout:this.timeout,connection:this.connection})),this.Events.trigger("created",t,e),t})()}async deleteKey(e=""){var t,i;return i=this.instances[e],this.connection&&(t=await this.connection.__runCommand__(["del",...f.allKeys(`${this.id}-${e}`)])),null!=i&&(delete this.instances[e],await i.disconnect()),null!=i||t>0}limiters(){var e,t,i,s;for(e in i=[],t=this.instances)s=t[e],i.push({key:e,limiter:s});return i}keys(){return Object.keys(this.instances)}async clusterKeys(){var e,t,i,s,r,n,o,a;if(null==this.connection)return this.Promise.resolve(this.keys());for(r=[],e=null,a=`b_${this.id}-`.length;0!==e;)for([o,t]=await this.connection.__runCommand__(["scan",null!=e?e:0,"match",`b_${this.id}-*_settings`,"count",1e4]),e=~~o,i=0,n=t.length;i{var e,t,i,s,r,n;for(t in r=Date.now(),s=[],i=this.instances){n=i[t];try{await n._store.__groupCheck__(r)?s.push(this.deleteKey(t)):s.push(void 0)}catch(t){e=t,s.push(n.Events.trigger("error",e))}}return s}),this.timeout/2)).unref?e.unref():void 0}updateSettings(e={}){if(E.overwrite(e,this.defaults,this),E.overwrite(e,e,this.limiterOptions),null!=e.timeout)return this._startAutoCleanup()}disconnect(e=!0){var t;if(!this.sharedConnection)return null!=(t=this.connection)?t.disconnect(e):void 0}}return e.prototype.defaults={timeout:3e5,connection:null,Promise,id:"group-key"},e}.call(i);b=s,w=n;var x,k,D,S,_,R,T,F,N,L=function(){class e{constructor(e={}){this.options=e,b.load(this.options,this.defaults,this),this.Events=new w(this),this._arr=[],this._resetPromise(),this._lastFlush=Date.now()}_resetPromise(){return this._promise=new this.Promise(((e,t)=>this._resolve=e))}_flush(){return clearTimeout(this._timeout),this._lastFlush=Date.now(),this._resolve(),this.Events.trigger("batch",this._arr),this._arr=[],this._resetPromise()}add(e){var t;return this._arr.push(e),t=this._promise,this._arr.length===this.maxSize?this._flush():null!=this.maxTime&&1===this._arr.length&&(this._timeout=setTimeout((()=>this._flush()),this.maxTime)),t}}return e.prototype.defaults={maxTime:null,maxSize:null,Promise},e}.call(i),O=(x=v)&&x.default||x,M=[].splice;N=s,_=class{constructor(i){this.Events=new t(this),this._length=0,this._lists=function(){var t,s,r;for(r=[],t=1,s=i;1<=s?t<=s:t>=s;1<=s?++t:--t)r.push(new e((()=>this.incr()),(()=>this.decr())));return r}.call(this)}incr(){if(0==this._length++)return this.Events.trigger("leftzero")}decr(){if(0==--this._length)return this.Events.trigger("zero")}push(e){return this._lists[e.options.priority].push(e)}queued(e){return null!=e?this._lists[e].length:this._length}shiftAll(e){return this._lists.forEach((function(t){return t.forEachShift(e)}))}getFirst(e=this._lists){var t,i,s;for(t=0,i=e.length;t0)return s;return[]}shiftLastFrom(e){return this.getFirst(this._lists.slice(e).reverse()).shift()}},D=A,S=class{constructor(e,t,i){this.instance=e,this.storeOptions=t,this.clientId=this.instance._randomIndex(),p.load(i,i,this),this._nextRequest=this._lastReservoirRefresh=this._lastReservoirIncrease=Date.now(),this._running=0,this._done=0,this._unblockTime=0,this.ready=this.Promise.resolve(),this.clients={},this._startHeartbeat()}_startHeartbeat(){var e;return null==this.heartbeat&&(null!=this.storeOptions.reservoirRefreshInterval&&null!=this.storeOptions.reservoirRefreshAmount||null!=this.storeOptions.reservoirIncreaseInterval&&null!=this.storeOptions.reservoirIncreaseAmount)?"function"==typeof(e=this.heartbeat=setInterval((()=>{var e,t,i,s,r;if(s=Date.now(),null!=this.storeOptions.reservoirRefreshInterval&&s>=this._lastReservoirRefresh+this.storeOptions.reservoirRefreshInterval&&(this._lastReservoirRefresh=s,this.storeOptions.reservoir=this.storeOptions.reservoirRefreshAmount,this.instance._drainAll(this.computeCapacity())),null!=this.storeOptions.reservoirIncreaseInterval&&s>=this._lastReservoirIncrease+this.storeOptions.reservoirIncreaseInterval&&(({reservoirIncreaseAmount:e,reservoirIncreaseMaximum:i,reservoir:r}=this.storeOptions),this._lastReservoirIncrease=s,(t=null!=i?Math.min(e,i-r):e)>0))return this.storeOptions.reservoir+=t,this.instance._drainAll(this.computeCapacity())}),this.heartbeatInterval)).unref?e.unref():void 0:clearInterval(this.heartbeat)}async __publish__(e){return await this.yieldLoop(),this.instance.Events.trigger("message",e.toString())}async __disconnect__(e){return await this.yieldLoop(),clearInterval(this.heartbeat),this.Promise.resolve()}yieldLoop(e=0){return new this.Promise((function(t,i){return setTimeout(t,e)}))}computePenalty(){var e;return null!=(e=this.storeOptions.penalty)?e:15*this.storeOptions.minTime||5e3}async __updateSettings__(e){return await this.yieldLoop(),p.overwrite(e,e,this.storeOptions),this._startHeartbeat(),this.instance._drainAll(this.computeCapacity()),!0}async __running__(){return await this.yieldLoop(),this._running}async __queued__(){return await this.yieldLoop(),this.instance.queued()}async __done__(){return await this.yieldLoop(),this._done}async __groupCheck__(e){return await this.yieldLoop(),this._nextRequest+this.timeout=e}check(e,t){return this.conditionsCheck(e)&&this._nextRequest-t<=0}async __check__(e){var t;return await this.yieldLoop(),t=Date.now(),this.check(e,t)}async __register__(e,t,i){var s,r;return await this.yieldLoop(),s=Date.now(),this.conditionsCheck(t)?(this._running+=t,null!=this.storeOptions.reservoir&&(this.storeOptions.reservoir-=t),r=Math.max(this._nextRequest-s,0),this._nextRequest=s+r+this.storeOptions.minTime,{success:!0,wait:r,reservoir:this.storeOptions.reservoir}):{success:!1}}strategyIsBlock(){return 3===this.storeOptions.strategy}async __submit__(e,t){var i,s,r;if(await this.yieldLoop(),null!=this.storeOptions.maxConcurrent&&t>this.storeOptions.maxConcurrent)throw new l(`Impossible to add a job having a weight of ${t} to a limiter having a maxConcurrent setting of ${this.storeOptions.maxConcurrent}`);return s=Date.now(),r=null!=this.storeOptions.highWater&&e===this.storeOptions.highWater&&!this.check(t,s),(i=this.strategyIsBlock()&&(r||this.isBlocked(s)))&&(this._unblockTime=s+this.computePenalty(),this._nextRequest=this._unblockTime+this.storeOptions.minTime,this.instance._dropAllQueued()),{reachedHWM:r,blocked:i,strategy:this.storeOptions.strategy}}async __free__(e,t){return await this.yieldLoop(),this._running-=t,this._done+=t,this.instance._drainAll(this.computeCapacity()),{running:this._running}}},R=()=>console.log("You must import the full version of Bottleneck in order to use this feature."),k=n,T=class{constructor(e){this.status=e,this._jobs={},this.counts=this.status.map((function(){return 0}))}next(e){var t,i;return i=(t=this._jobs[e])+1,null!=t&&i(e[this.status[i]]=t,e)),{})}},F=class{constructor(e,t){this.schedule=this.schedule.bind(this),this.name=e,this.Promise=t,this._running=0,this._queue=new d}isEmpty(){return 0===this._queue.length}async _tryToRun(){var e,t,i,s,r,n,o;if(this._running<1&&this._queue.length>0)return this._running++,({task:o,args:e,resolve:r,reject:s}=this._queue.shift()),t=await async function(){try{return n=await o(...e),function(){return r(n)}}catch(e){return i=e,function(){return s(i)}}}(),this._running--,this._tryToRun(),t()}schedule(e,...t){var i,s,r;return r=s=null,i=new this.Promise((function(e,t){return r=e,s=t})),this._queue.push({task:e,args:t,resolve:r,reject:s}),this._tryToRun(),i}};var U=function(){class e{constructor(t={},...i){var s,r;this._addToQueue=this._addToQueue.bind(this),this._validateOptions(t,i),N.load(t,this.instanceDefaults,this),this._queues=new _(10),this._scheduled={},this._states=new T(["RECEIVED","QUEUED","RUNNING","EXECUTING"].concat(this.trackDoneStatus?["DONE"]:[])),this._limiter=null,this.Events=new k(this),this._submitLock=new F("submit",this.Promise),this._registerLock=new F("register",this.Promise),r=N.load(t,this.storeDefaults,{}),this._store=function(){if("redis"===this.datastore||"ioredis"===this.datastore||null!=this.connection)return s=N.load(t,this.redisStoreDefaults,{}),new R(this,r,s);if("local"===this.datastore)return s=N.load(t,this.localStoreDefaults,{}),new S(this,r,s);throw new e.prototype.BottleneckError(`Invalid datastore type: ${this.datastore}`)}.call(this),this._queues.on("leftzero",(()=>{var e;return null!=(e=this._store.heartbeat)&&"function"==typeof e.ref?e.ref():void 0})),this._queues.on("zero",(()=>{var e;return null!=(e=this._store.heartbeat)&&"function"==typeof e.unref?e.unref():void 0}))}_validateOptions(t,i){if(null==t||"object"!=typeof t||0!==i.length)throw new e.prototype.BottleneckError("Bottleneck v2 takes a single object argument. Refer to https://github.com/SGrondin/bottleneck#upgrading-to-v2 if you're upgrading from Bottleneck v1.")}ready(){return this._store.ready}clients(){return this._store.clients}channel(){return`b_${this.id}`}channel_client(){return`b_${this.id}_${this._store.clientId}`}publish(e){return this._store.__publish__(e)}disconnect(e=!0){return this._store.__disconnect__(e)}chain(e){return this._limiter=e,this}queued(e){return this._queues.queued(e)}clusterQueued(){return this._store.__queued__()}empty(){return 0===this.queued()&&this._submitLock.isEmpty()}running(){return this._store.__running__()}done(){return this._store.__done__()}jobStatus(e){return this._states.jobStatus(e)}jobs(e){return this._states.statusJobs(e)}counts(){return this._states.statusCounts()}_randomIndex(){return Math.random().toString(36).slice(2)}check(e=1){return this._store.__check__(e)}_clearGlobalState(e){return null!=this._scheduled[e]&&(clearTimeout(this._scheduled[e].expiration),delete this._scheduled[e],!0)}async _free(e,t,i,s){var r,n;try{if(({running:n}=await this._store.__free__(e,i.weight)),this.Events.trigger("debug",`Freed ${i.id}`,s),0===n&&this.empty())return this.Events.trigger("idle")}catch(e){return r=e,this.Events.trigger("error",r)}}_run(e,t,i){var s,r,n;return t.doRun(),s=this._clearGlobalState.bind(this,e),n=this._run.bind(this,e,t),r=this._free.bind(this,e,t),this._scheduled[e]={timeout:setTimeout((()=>t.doExecute(this._limiter,s,n,r)),i),expiration:null!=t.options.expiration?setTimeout((function(){return t.doExpire(s,n,r)}),i+t.options.expiration):void 0,job:t}}_drainOne(e){return this._registerLock.schedule((()=>{var t,i,s,r,n;return 0===this.queued()?this.Promise.resolve(null):(n=this._queues.getFirst(),({options:r,args:t}=s=n.first()),null!=e&&r.weight>e?this.Promise.resolve(null):(this.Events.trigger("debug",`Draining ${r.id}`,{args:t,options:r}),i=this._randomIndex(),this._store.__register__(i,r.weight,r.expiration).then((({success:e,wait:o,reservoir:a})=>{var c;return this.Events.trigger("debug",`Drained ${r.id}`,{success:e,args:t,options:r}),e?(n.shift(),(c=this.empty())&&this.Events.trigger("empty"),0===a&&this.Events.trigger("depleted",c),this._run(i,s,o),this.Promise.resolve(r.weight)):this.Promise.resolve(null)}))))}))}_drainAll(e,t=0){return this._drainOne(e).then((i=>{var s;return null!=i?(s=null!=e?e-i:e,this._drainAll(s,t+i)):this.Promise.resolve(t)})).catch((e=>this.Events.trigger("error",e)))}_dropAllQueued(e){return this._queues.shiftAll((function(t){return t.doDrop({message:e})}))}stop(t={}){var i,s;return t=N.load(t,this.stopDefaults),s=e=>{var t;return t=()=>{var t;return(t=this._states.counts)[0]+t[1]+t[2]+t[3]===e},new this.Promise(((e,i)=>t()?e():this.on("done",(()=>{if(t())return this.removeAllListeners("done"),e()}))))},i=t.dropWaitingJobs?(this._run=function(e,i){return i.doDrop({message:t.dropErrorMessage})},this._drainOne=()=>this.Promise.resolve(null),this._registerLock.schedule((()=>this._submitLock.schedule((()=>{var e,i,r;for(e in i=this._scheduled)r=i[e],"RUNNING"===this.jobStatus(r.job.options.id)&&(clearTimeout(r.timeout),clearTimeout(r.expiration),r.job.doDrop({message:t.dropErrorMessage}));return this._dropAllQueued(t.dropErrorMessage),s(0)}))))):this.schedule({priority:9,weight:0},(()=>s(1))),this._receive=function(i){return i._reject(new e.prototype.BottleneckError(t.enqueueErrorMessage))},this.stop=()=>this.Promise.reject(new e.prototype.BottleneckError("stop() has already been called")),i}async _addToQueue(t){var i,s,r,n,o,a,c;({args:i,options:n}=t);try{({reachedHWM:o,blocked:s,strategy:c}=await this._store.__submit__(this.queued(),n.weight))}catch(e){return r=e,this.Events.trigger("debug",`Could not queue ${n.id}`,{args:i,options:n,error:r}),t.doDrop({error:r}),!1}return s?(t.doDrop(),!0):o&&(null!=(a=c===e.prototype.strategy.LEAK?this._queues.shiftLastFrom(n.priority):c===e.prototype.strategy.OVERFLOW_PRIORITY?this._queues.shiftLastFrom(n.priority+1):c===e.prototype.strategy.OVERFLOW?t:void 0)&&a.doDrop(),null==a||c===e.prototype.strategy.OVERFLOW)?(null==a&&t.doDrop(),o):(t.doQueue(o,s),this._queues.push(t),await this._drainAll(),o)}_receive(t){return null!=this._states.jobStatus(t.options.id)?(t._reject(new e.prototype.BottleneckError(`A job with the same id already exists (id=${t.options.id})`)),!1):(t.doReceive(),this._submitLock.schedule(this._addToQueue,t))}submit(...e){var t,i,s,r,n,o,a;return"function"==typeof e[0]?(n=e,[i,...e]=n,[t]=M.call(e,-1),r=N.load({},this.jobDefaults)):(o=e,[r,i,...e]=o,[t]=M.call(e,-1),r=N.load(r,this.jobDefaults)),a=(...e)=>new this.Promise((function(t,s){return i(...e,(function(...e){return(null!=e[0]?s:t)(e)}))})),(s=new D(a,e,r,this.jobDefaults,this.rejectOnDrop,this.Events,this._states,this.Promise)).promise.then((function(e){return"function"==typeof t?t(...e):void 0})).catch((function(e){return Array.isArray(e)?"function"==typeof t?t(...e):void 0:"function"==typeof t?t(e):void 0})),this._receive(s)}schedule(...e){var t,i,s;return"function"==typeof e[0]?([s,...e]=e,i={}):[i,s,...e]=e,t=new D(s,e,i,this.jobDefaults,this.rejectOnDrop,this.Events,this._states,this.Promise),this._receive(t),t.promise}wrap(e){var t,i;return t=this.schedule.bind(this),(i=function(...i){return t(e.bind(this),...i)}).withOptions=function(i,...s){return t(i,e,...s)},i}async updateSettings(e={}){return await this._store.__updateSettings__(N.overwrite(e,this.storeDefaults)),N.overwrite(e,this.instanceDefaults,this),this}currentReservoir(){return this._store.__currentReservoir__()}incrementReservoir(e=0){return this._store.__incrementReservoir__(e)}}return e.default=e,e.Events=k,e.version=e.prototype.version=O.version,e.strategy=e.prototype.strategy={LEAK:1,OVERFLOW:2,OVERFLOW_PRIORITY:4,BLOCK:3},e.BottleneckError=e.prototype.BottleneckError=c,e.Group=e.prototype.Group=Q,e.RedisConnection=e.prototype.RedisConnection=I,e.IORedisConnection=e.prototype.IORedisConnection=B,e.Batcher=e.prototype.Batcher=L,e.prototype.jobDefaults={priority:5,weight:1,expiration:null,id:""},e.prototype.storeDefaults={maxConcurrent:null,minTime:0,highWater:null,strategy:e.prototype.strategy.LEAK,penalty:null,reservoir:null,reservoirRefreshInterval:null,reservoirRefreshAmount:null,reservoirIncreaseInterval:null,reservoirIncreaseAmount:null,reservoirIncreaseMaximum:null},e.prototype.localStoreDefaults={Promise,timeout:null,heartbeatInterval:250},e.prototype.redisStoreDefaults={Promise,timeout:null,heartbeatInterval:5e3,clientTimeout:1e4,Redis:null,clientOptions:{},clusterNodes:null,clearDatastore:!1,connection:null},e.prototype.instanceDefaults={datastore:"local",connection:null,id:"",rejectOnDrop:!0,trackDoneStatus:!1,Promise},e.prototype.stopDefaults={enqueueErrorMessage:"This limiter has been stopped and cannot accept new jobs.",dropWaitingJobs:!0,dropErrorMessage:"This limiter has been stopped."},e}.call(i);return U},e.exports=t()},58389:(e,t,i)=>{var s=i(72547);e.exports=function(e){return e?("{}"===e.substr(0,2)&&(e="\\{\\}"+e.substr(2)),g(function(e){return e.split("\\\\").join(r).split("\\{").join(n).split("\\}").join(o).split("\\,").join(a).split("\\.").join(c)}(e),!0).map(p)):[]};var r="\0SLASH"+Math.random()+"\0",n="\0OPEN"+Math.random()+"\0",o="\0CLOSE"+Math.random()+"\0",a="\0COMMA"+Math.random()+"\0",c="\0PERIOD"+Math.random()+"\0";function l(e){return parseInt(e,10)==e?parseInt(e,10):e.charCodeAt(0)}function p(e){return e.split(r).join("\\").split(n).join("{").split(o).join("}").split(a).join(",").split(c).join(".")}function A(e){if(!e)return[""];var t=[],i=s("{","}",e);if(!i)return e.split(",");var r=i.pre,n=i.body,o=i.post,a=r.split(",");a[a.length-1]+="{"+n+"}";var c=A(o);return o.length&&(a[a.length-1]+=c.shift(),a.push.apply(a,c)),t.push.apply(t,a),t}function u(e){return"{"+e+"}"}function d(e){return/^-?0\d/.test(e)}function h(e,t){return e<=t}function m(e,t){return e>=t}function g(e,t){var i=[],r=s("{","}",e);if(!r)return[e];var n=r.pre,a=r.post.length?g(r.post,!1):[""];if(/\$$/.test(r.pre))for(var c=0;c=0;if(!v&&!I)return r.post.match(/,.*\}/)?g(e=r.pre+"{"+r.body+o+r.post):[e];if(v)f=r.body.split(/\.\./);else if(1===(f=A(r.body)).length&&1===(f=g(f[0],!1).map(u)).length)return a.map((function(e){return r.pre+f[0]+e}));if(v){var B=l(f[0]),w=l(f[1]),b=Math.max(f[0].length,f[1].length),Q=3==f.length?Math.abs(l(f[2])):1,x=h;w0){var R=new Array(_+1).join("0");S=D<0?"-"+R+S.slice(1):R+S}}E.push(S)}}else{E=[];for(var T=0;T{e.exports=function(e){return new Buffer(e).toString("base64")}},47309:(e,t,i)=>{"use strict";const s=i(78413),{stdout:r,stderr:n}=i(10447),{stringReplaceAll:o,stringEncaseCRLFWithFirstIndex:a}=i(63370),{isArray:c}=Array,l=["ansi","ansi","ansi256","ansi16m"],p=Object.create(null);class A{constructor(e){return u(e)}}const u=e=>{const t={};return((e,t={})=>{if(t.level&&!(Number.isInteger(t.level)&&t.level>=0&&t.level<=3))throw new Error("The `level` option should be an integer from 0 to 3");const i=r?r.level:0;e.level=void 0===t.level?i:t.level})(t,e),t.template=(...e)=>y(t.template,...e),Object.setPrototypeOf(t,d.prototype),Object.setPrototypeOf(t.template,t),t.template.constructor=()=>{throw new Error("`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.")},t.template.Instance=A,t.template};function d(e){return u(e)}for(const[e,t]of Object.entries(s))p[e]={get(){const i=f(this,g(t.open,t.close,this._styler),this._isEmpty);return Object.defineProperty(this,e,{value:i}),i}};p.visible={get(){const e=f(this,this._styler,!0);return Object.defineProperty(this,"visible",{value:e}),e}};const h=["rgb","hex","keyword","hsl","hsv","hwb","ansi","ansi256"];for(const e of h)p[e]={get(){const{level:t}=this;return function(...i){const r=g(s.color[l[t]][e](...i),s.color.close,this._styler);return f(this,r,this._isEmpty)}}};for(const e of h)p["bg"+e[0].toUpperCase()+e.slice(1)]={get(){const{level:t}=this;return function(...i){const r=g(s.bgColor[l[t]][e](...i),s.bgColor.close,this._styler);return f(this,r,this._isEmpty)}}};const m=Object.defineProperties((()=>{}),{...p,level:{enumerable:!0,get(){return this._generator.level},set(e){this._generator.level=e}}}),g=(e,t,i)=>{let s,r;return void 0===i?(s=e,r=t):(s=i.openAll+e,r=t+i.closeAll),{open:e,close:t,openAll:s,closeAll:r,parent:i}},f=(e,t,i)=>{const s=(...e)=>c(e[0])&&c(e[0].raw)?E(s,y(s,...e)):E(s,1===e.length?""+e[0]:e.join(" "));return Object.setPrototypeOf(s,m),s._generator=e,s._styler=t,s._isEmpty=i,s},E=(e,t)=>{if(e.level<=0||!t)return e._isEmpty?"":t;let i=e._styler;if(void 0===i)return t;const{openAll:s,closeAll:r}=i;if(-1!==t.indexOf(""))for(;void 0!==i;)t=o(t,i.close,i.open),i=i.parent;const n=t.indexOf("\n");return-1!==n&&(t=a(t,r,s,n)),s+t+r};let C;const y=(e,...t)=>{const[s]=t;if(!c(s)||!c(s.raw))return t.join(" ");const r=t.slice(1),n=[s.raw[0]];for(let e=1;e{"use strict";const t=/(?:\\(u(?:[a-f\d]{4}|\{[a-f\d]{1,6}\})|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi,i=/(?:^|\.)(\w+)(?:\(([^)]*)\))?/g,s=/^(['"])((?:\\.|(?!\1)[^\\])*)\1$/,r=/\\(u(?:[a-f\d]{4}|{[a-f\d]{1,6}})|x[a-f\d]{2}|.)|([^\\])/gi,n=new Map([["n","\n"],["r","\r"],["t","\t"],["b","\b"],["f","\f"],["v","\v"],["0","\0"],["\\","\\"],["e",""],["a",""]]);function o(e){const t="u"===e[0],i="{"===e[1];return t&&!i&&5===e.length||"x"===e[0]&&3===e.length?String.fromCharCode(parseInt(e.slice(1),16)):t&&i?String.fromCodePoint(parseInt(e.slice(2,-1),16)):n.get(e)||e}function a(e,t){const i=[],n=t.trim().split(/\s*,\s*/g);let a;for(const t of n){const n=Number(t);if(Number.isNaN(n)){if(!(a=t.match(s)))throw new Error(`Invalid Chalk template style argument: ${t} (in style '${e}')`);i.push(a[2].replace(r,((e,t,i)=>t?o(t):i)))}else i.push(n)}return i}function c(e){i.lastIndex=0;const t=[];let s;for(;null!==(s=i.exec(e));){const e=s[1];if(s[2]){const i=a(e,s[2]);t.push([e].concat(i))}else t.push([e])}return t}function l(e,t){const i={};for(const e of t)for(const t of e.styles)i[t[0]]=e.inverse?null:t.slice(1);let s=e;for(const[e,t]of Object.entries(i))if(Array.isArray(t)){if(!(e in s))throw new Error(`Unknown Chalk style: ${e}`);s=t.length>0?s[e](...t):s[e]}return s}e.exports=(e,i)=>{const s=[],r=[];let n=[];if(i.replace(t,((t,i,a,p,A,u)=>{if(i)n.push(o(i));else if(p){const t=n.join("");n=[],r.push(0===s.length?t:l(e,s)(t)),s.push({inverse:a,styles:c(p)})}else if(A){if(0===s.length)throw new Error("Found extraneous } in Chalk template literal");r.push(l(e,s)(n.join(""))),n=[],s.pop()}else n.push(u)})),r.push(n.join("")),s.length>0){const e=`Chalk template literal is missing ${s.length} closing bracket${1===s.length?"":"s"} (\`}\`)`;throw new Error(e)}return r.join("")}},63370:e=>{"use strict";e.exports={stringReplaceAll:(e,t,i)=>{let s=e.indexOf(t);if(-1===s)return e;const r=t.length;let n=0,o="";do{o+=e.substr(n,s-n)+t+i,n=s+r,s=e.indexOf(t,n)}while(-1!==s);return o+=e.substr(n),o},stringEncaseCRLFWithFirstIndex:(e,t,i,s)=>{let r=0,n="";do{const o="\r"===e[s-1];n+=e.substr(r,(o?s-1:s)-r)+t+(o?"\r\n":"\n")+i,r=s+1,s=e.indexOf("\n",r)}while(-1!==s);return n+=e.substr(r),n}}},53072:(e,t,i)=>{"use strict";const s=i(22037),r=/\s+at.*(?:\(|\s)(.*)\)?/,n=/^(?:(?:(?:node|(?:internal\/[\w/]*|.*node_modules\/(?:babel-polyfill|pirates)\/.*)?\w+)\.js:\d+:\d+)|native)/,o=void 0===s.homedir?"":s.homedir();e.exports=(e,t)=>(t=Object.assign({pretty:!1},t),e.replace(/\\/g,"/").split("\n").filter((e=>{const t=e.match(r);if(null===t||!t[1])return!0;const i=t[1];return!i.includes(".app/Contents/Resources/electron.asar")&&!i.includes(".app/Contents/Resources/default_app.asar")&&!n.test(i)})).filter((e=>""!==e.trim())).map((e=>t.pretty?e.replace(r,((e,t)=>e.replace(t,t.replace(o,"~")))):e)).join("\n"))},55693:(e,t,i)=>{"use strict";const s=i(60350);let r=!1;t.show=(e=process.stderr)=>{e.isTTY&&(r=!1,e.write("[?25h"))},t.hide=(e=process.stderr)=>{e.isTTY&&(s(),r=!0,e.write("[?25l"))},t.toggle=(e,i)=>{void 0!==e&&(r=e),r?t.show(i):t.hide(i)}},60881:(e,t,i)=>{"use strict";const s=Object.assign({},i(36432)),r=Object.keys(s);Object.defineProperty(s,"random",{get(){const e=Math.floor(Math.random()*r.length),t=r[e];return s[t]}}),e.exports=s},91969:(e,t,i)=>{var s=i(27072),r=i(34045),n=r.repeat,o=r.truncate,a=r.pad;function c(e){if(this.options=r.options({chars:{top:"─","top-mid":"┬","top-left":"┌","top-right":"┐",bottom:"─","bottom-mid":"┴","bottom-left":"└","bottom-right":"┘",left:"│","left-mid":"├",mid:"─","mid-mid":"┼",right:"│","right-mid":"┤",middle:"│"},truncate:"…",colWidths:[],colAligns:[],style:{"padding-left":1,"padding-right":1,head:["red"],border:["grey"],compact:!1},head:[]},e),e&&e.rows)for(var t=0;tr&&(r=n),s.push({contents:i,height:n})}));var c=new Array(r);s.forEach((function(e,s){e.contents.forEach((function(e,r){c[r]||(c[r]=[]),(i||a&&0===s&&t.style.head)&&(e=C(t.style.head,e)),c[r].push(e)}));for(var n=e.height,o=r;n0&&(p+="\n"+C(t.style.border,l.left)),p+=e.join(C(t.style.border,l.middle))+C(t.style.border,l.right)})),C(t.style.border,l.left)+p}function C(e,t){return t?(e.forEach((function(e){t=s[e](t)})),t):""}function y(e,s){e=String("object"==typeof e&&e.text?e.text:e);var c=r.strlen(e),l=A[s]-(i["padding-left"]||0)-(i["padding-right"]||0),u=t.colAligns[s]||"left";return n(" ",i["padding-left"]||0)+(c==l?e:c{t.repeat=function(e,t){return Array(t+1).join(e)},t.pad=function(e,t,i,s){if(t+1>=e.length)switch(s){case"left":e=Array(t+1-e.length).join(i)+e;break;case"both":var r=Math.ceil((padlen=t-e.length)/2),n=padlen-r;e=Array(n+1).join(i)+e+Array(r+1).join(i);break;default:e+=Array(t+1-e.length).join(i)}return e},t.truncate=function(e,t,i){return i=i||"…",e.length>=t?e.substr(0,t-i.length)+i:e},t.options=function e(t,i){for(var s in i)"__proto__"!==s&&"constructor"!==s&&"prototype"!==s&&(i[s]&&i[s].constructor&&i[s].constructor===Object?(t[s]=t[s]||{},e(t[s],i[s])):t[s]=i[s]);return t},t.strlen=function(e){return(""+e).replace(/\u001b\[(?:\d*;){0,5}\d*m/g,"").split("\n").reduce((function(e,t){return t.length>e?t.length:e}),0)}},54256:e=>{var t=function(){"use strict";function e(t,s,r,n){"object"==typeof s&&(r=s.depth,n=s.prototype,s.filter,s=s.circular);var o=[],a=[],c="undefined"!=typeof Buffer;return void 0===s&&(s=!0),void 0===r&&(r=1/0),function t(r,l){if(null===r)return null;if(0==l)return r;var p,A;if("object"!=typeof r)return r;if(e.__isArray(r))p=[];else if(e.__isRegExp(r))p=new RegExp(r.source,i(r)),r.lastIndex&&(p.lastIndex=r.lastIndex);else if(e.__isDate(r))p=new Date(r.getTime());else{if(c&&Buffer.isBuffer(r))return p=Buffer.allocUnsafe?Buffer.allocUnsafe(r.length):new Buffer(r.length),r.copy(p),p;void 0===n?(A=Object.getPrototypeOf(r),p=Object.create(A)):(p=Object.create(n),A=n)}if(s){var u=o.indexOf(r);if(-1!=u)return a[u];o.push(r),a.push(p)}for(var d in r){var h;A&&(h=Object.getOwnPropertyDescriptor(A,d)),h&&null==h.set||(p[d]=t(r[d],l-1))}return p}(t,r)}function t(e){return Object.prototype.toString.call(e)}function i(e){var t="";return e.global&&(t+="g"),e.ignoreCase&&(t+="i"),e.multiline&&(t+="m"),t}return e.clonePrototype=function(e){if(null===e)return null;var t=function(){};return t.prototype=e,new t},e.__objToStr=t,e.__isDate=function(e){return"object"==typeof e&&"[object Date]"===t(e)},e.__isArray=function(e){return"object"==typeof e&&"[object Array]"===t(e)},e.__isRegExp=function(e){return"object"==typeof e&&"[object RegExp]"===t(e)},e.__getRegExpFlags=i,e}();e.exports&&(e.exports=t)},38097:(e,t,i)=>{const s=i(61821),r={};for(const e of Object.keys(s))r[s[e]]=e;const n={rgb:{channels:3,labels:"rgb"},hsl:{channels:3,labels:"hsl"},hsv:{channels:3,labels:"hsv"},hwb:{channels:3,labels:"hwb"},cmyk:{channels:4,labels:"cmyk"},xyz:{channels:3,labels:"xyz"},lab:{channels:3,labels:"lab"},lch:{channels:3,labels:"lch"},hex:{channels:1,labels:["hex"]},keyword:{channels:1,labels:["keyword"]},ansi16:{channels:1,labels:["ansi16"]},ansi256:{channels:1,labels:["ansi256"]},hcg:{channels:3,labels:["h","c","g"]},apple:{channels:3,labels:["r16","g16","b16"]},gray:{channels:1,labels:["gray"]}};e.exports=n;for(const e of Object.keys(n)){if(!("channels"in n[e]))throw new Error("missing channels property: "+e);if(!("labels"in n[e]))throw new Error("missing channel labels property: "+e);if(n[e].labels.length!==n[e].channels)throw new Error("channel and label counts mismatch: "+e);const{channels:t,labels:i}=n[e];delete n[e].channels,delete n[e].labels,Object.defineProperty(n[e],"channels",{value:t}),Object.defineProperty(n[e],"labels",{value:i})}n.rgb.hsl=function(e){const t=e[0]/255,i=e[1]/255,s=e[2]/255,r=Math.min(t,i,s),n=Math.max(t,i,s),o=n-r;let a,c;n===r?a=0:t===n?a=(i-s)/o:i===n?a=2+(s-t)/o:s===n&&(a=4+(t-i)/o),a=Math.min(60*a,360),a<0&&(a+=360);const l=(r+n)/2;return c=n===r?0:l<=.5?o/(n+r):o/(2-n-r),[a,100*c,100*l]},n.rgb.hsv=function(e){let t,i,s,r,n;const o=e[0]/255,a=e[1]/255,c=e[2]/255,l=Math.max(o,a,c),p=l-Math.min(o,a,c),A=function(e){return(l-e)/6/p+.5};return 0===p?(r=0,n=0):(n=p/l,t=A(o),i=A(a),s=A(c),o===l?r=s-i:a===l?r=1/3+t-s:c===l&&(r=2/3+i-t),r<0?r+=1:r>1&&(r-=1)),[360*r,100*n,100*l]},n.rgb.hwb=function(e){const t=e[0],i=e[1];let s=e[2];const r=n.rgb.hsl(e)[0],o=1/255*Math.min(t,Math.min(i,s));return s=1-1/255*Math.max(t,Math.max(i,s)),[r,100*o,100*s]},n.rgb.cmyk=function(e){const t=e[0]/255,i=e[1]/255,s=e[2]/255,r=Math.min(1-t,1-i,1-s);return[100*((1-t-r)/(1-r)||0),100*((1-i-r)/(1-r)||0),100*((1-s-r)/(1-r)||0),100*r]},n.rgb.keyword=function(e){const t=r[e];if(t)return t;let i,n=1/0;for(const t of Object.keys(s)){const r=(a=s[t],((o=e)[0]-a[0])**2+(o[1]-a[1])**2+(o[2]-a[2])**2);r.04045?((t+.055)/1.055)**2.4:t/12.92,i=i>.04045?((i+.055)/1.055)**2.4:i/12.92,s=s>.04045?((s+.055)/1.055)**2.4:s/12.92,[100*(.4124*t+.3576*i+.1805*s),100*(.2126*t+.7152*i+.0722*s),100*(.0193*t+.1192*i+.9505*s)]},n.rgb.lab=function(e){const t=n.rgb.xyz(e);let i=t[0],s=t[1],r=t[2];return i/=95.047,s/=100,r/=108.883,i=i>.008856?i**(1/3):7.787*i+16/116,s=s>.008856?s**(1/3):7.787*s+16/116,r=r>.008856?r**(1/3):7.787*r+16/116,[116*s-16,500*(i-s),200*(s-r)]},n.hsl.rgb=function(e){const t=e[0]/360,i=e[1]/100,s=e[2]/100;let r,n,o;if(0===i)return o=255*s,[o,o,o];r=s<.5?s*(1+i):s+i-s*i;const a=2*s-r,c=[0,0,0];for(let e=0;e<3;e++)n=t+1/3*-(e-1),n<0&&n++,n>1&&n--,o=6*n<1?a+6*(r-a)*n:2*n<1?r:3*n<2?a+(r-a)*(2/3-n)*6:a,c[e]=255*o;return c},n.hsl.hsv=function(e){const t=e[0];let i=e[1]/100,s=e[2]/100,r=i;const n=Math.max(s,.01);return s*=2,i*=s<=1?s:2-s,r*=n<=1?n:2-n,[t,100*(0===s?2*r/(n+r):2*i/(s+i)),(s+i)/2*100]},n.hsv.rgb=function(e){const t=e[0]/60,i=e[1]/100;let s=e[2]/100;const r=Math.floor(t)%6,n=t-Math.floor(t),o=255*s*(1-i),a=255*s*(1-i*n),c=255*s*(1-i*(1-n));switch(s*=255,r){case 0:return[s,c,o];case 1:return[a,s,o];case 2:return[o,s,c];case 3:return[o,a,s];case 4:return[c,o,s];case 5:return[s,o,a]}},n.hsv.hsl=function(e){const t=e[0],i=e[1]/100,s=e[2]/100,r=Math.max(s,.01);let n,o;o=(2-i)*s;const a=(2-i)*r;return n=i*r,n/=a<=1?a:2-a,n=n||0,o/=2,[t,100*n,100*o]},n.hwb.rgb=function(e){const t=e[0]/360;let i=e[1]/100,s=e[2]/100;const r=i+s;let n;r>1&&(i/=r,s/=r);const o=Math.floor(6*t),a=1-s;n=6*t-o,0!=(1&o)&&(n=1-n);const c=i+n*(a-i);let l,p,A;switch(o){default:case 6:case 0:l=a,p=c,A=i;break;case 1:l=c,p=a,A=i;break;case 2:l=i,p=a,A=c;break;case 3:l=i,p=c,A=a;break;case 4:l=c,p=i,A=a;break;case 5:l=a,p=i,A=c}return[255*l,255*p,255*A]},n.cmyk.rgb=function(e){const t=e[0]/100,i=e[1]/100,s=e[2]/100,r=e[3]/100;return[255*(1-Math.min(1,t*(1-r)+r)),255*(1-Math.min(1,i*(1-r)+r)),255*(1-Math.min(1,s*(1-r)+r))]},n.xyz.rgb=function(e){const t=e[0]/100,i=e[1]/100,s=e[2]/100;let r,n,o;return r=3.2406*t+-1.5372*i+-.4986*s,n=-.9689*t+1.8758*i+.0415*s,o=.0557*t+-.204*i+1.057*s,r=r>.0031308?1.055*r**(1/2.4)-.055:12.92*r,n=n>.0031308?1.055*n**(1/2.4)-.055:12.92*n,o=o>.0031308?1.055*o**(1/2.4)-.055:12.92*o,r=Math.min(Math.max(0,r),1),n=Math.min(Math.max(0,n),1),o=Math.min(Math.max(0,o),1),[255*r,255*n,255*o]},n.xyz.lab=function(e){let t=e[0],i=e[1],s=e[2];return t/=95.047,i/=100,s/=108.883,t=t>.008856?t**(1/3):7.787*t+16/116,i=i>.008856?i**(1/3):7.787*i+16/116,s=s>.008856?s**(1/3):7.787*s+16/116,[116*i-16,500*(t-i),200*(i-s)]},n.lab.xyz=function(e){let t,i,s;i=(e[0]+16)/116,t=e[1]/500+i,s=i-e[2]/200;const r=i**3,n=t**3,o=s**3;return i=r>.008856?r:(i-16/116)/7.787,t=n>.008856?n:(t-16/116)/7.787,s=o>.008856?o:(s-16/116)/7.787,t*=95.047,i*=100,s*=108.883,[t,i,s]},n.lab.lch=function(e){const t=e[0],i=e[1],s=e[2];let r;return r=360*Math.atan2(s,i)/2/Math.PI,r<0&&(r+=360),[t,Math.sqrt(i*i+s*s),r]},n.lch.lab=function(e){const t=e[0],i=e[1],s=e[2]/360*2*Math.PI;return[t,i*Math.cos(s),i*Math.sin(s)]},n.rgb.ansi16=function(e,t=null){const[i,s,r]=e;let o=null===t?n.rgb.hsv(e)[2]:t;if(o=Math.round(o/50),0===o)return 30;let a=30+(Math.round(r/255)<<2|Math.round(s/255)<<1|Math.round(i/255));return 2===o&&(a+=60),a},n.hsv.ansi16=function(e){return n.rgb.ansi16(n.hsv.rgb(e),e[2])},n.rgb.ansi256=function(e){const t=e[0],i=e[1],s=e[2];return t===i&&i===s?t<8?16:t>248?231:Math.round((t-8)/247*24)+232:16+36*Math.round(t/255*5)+6*Math.round(i/255*5)+Math.round(s/255*5)},n.ansi16.rgb=function(e){let t=e%10;if(0===t||7===t)return e>50&&(t+=3.5),t=t/10.5*255,[t,t,t];const i=.5*(1+~~(e>50));return[(1&t)*i*255,(t>>1&1)*i*255,(t>>2&1)*i*255]},n.ansi256.rgb=function(e){if(e>=232){const t=10*(e-232)+8;return[t,t,t]}let t;return e-=16,[Math.floor(e/36)/5*255,Math.floor((t=e%36)/6)/5*255,t%6/5*255]},n.rgb.hex=function(e){const t=(((255&Math.round(e[0]))<<16)+((255&Math.round(e[1]))<<8)+(255&Math.round(e[2]))).toString(16).toUpperCase();return"000000".substring(t.length)+t},n.hex.rgb=function(e){const t=e.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!t)return[0,0,0];let i=t[0];3===t[0].length&&(i=i.split("").map((e=>e+e)).join(""));const s=parseInt(i,16);return[s>>16&255,s>>8&255,255&s]},n.rgb.hcg=function(e){const t=e[0]/255,i=e[1]/255,s=e[2]/255,r=Math.max(Math.max(t,i),s),n=Math.min(Math.min(t,i),s),o=r-n;let a,c;return a=o<1?n/(1-o):0,c=o<=0?0:r===t?(i-s)/o%6:r===i?2+(s-t)/o:4+(t-i)/o,c/=6,c%=1,[360*c,100*o,100*a]},n.hsl.hcg=function(e){const t=e[1]/100,i=e[2]/100,s=i<.5?2*t*i:2*t*(1-i);let r=0;return s<1&&(r=(i-.5*s)/(1-s)),[e[0],100*s,100*r]},n.hsv.hcg=function(e){const t=e[1]/100,i=e[2]/100,s=t*i;let r=0;return s<1&&(r=(i-s)/(1-s)),[e[0],100*s,100*r]},n.hcg.rgb=function(e){const t=e[0]/360,i=e[1]/100,s=e[2]/100;if(0===i)return[255*s,255*s,255*s];const r=[0,0,0],n=t%1*6,o=n%1,a=1-o;let c=0;switch(Math.floor(n)){case 0:r[0]=1,r[1]=o,r[2]=0;break;case 1:r[0]=a,r[1]=1,r[2]=0;break;case 2:r[0]=0,r[1]=1,r[2]=o;break;case 3:r[0]=0,r[1]=a,r[2]=1;break;case 4:r[0]=o,r[1]=0,r[2]=1;break;default:r[0]=1,r[1]=0,r[2]=a}return c=(1-i)*s,[255*(i*r[0]+c),255*(i*r[1]+c),255*(i*r[2]+c)]},n.hcg.hsv=function(e){const t=e[1]/100,i=t+e[2]/100*(1-t);let s=0;return i>0&&(s=t/i),[e[0],100*s,100*i]},n.hcg.hsl=function(e){const t=e[1]/100,i=e[2]/100*(1-t)+.5*t;let s=0;return i>0&&i<.5?s=t/(2*i):i>=.5&&i<1&&(s=t/(2*(1-i))),[e[0],100*s,100*i]},n.hcg.hwb=function(e){const t=e[1]/100,i=t+e[2]/100*(1-t);return[e[0],100*(i-t),100*(1-i)]},n.hwb.hcg=function(e){const t=e[1]/100,i=1-e[2]/100,s=i-t;let r=0;return s<1&&(r=(i-s)/(1-s)),[e[0],100*s,100*r]},n.apple.rgb=function(e){return[e[0]/65535*255,e[1]/65535*255,e[2]/65535*255]},n.rgb.apple=function(e){return[e[0]/255*65535,e[1]/255*65535,e[2]/255*65535]},n.gray.rgb=function(e){return[e[0]/100*255,e[0]/100*255,e[0]/100*255]},n.gray.hsl=function(e){return[0,0,e[0]]},n.gray.hsv=n.gray.hsl,n.gray.hwb=function(e){return[0,100,e[0]]},n.gray.cmyk=function(e){return[0,0,0,e[0]]},n.gray.lab=function(e){return[e[0],0,0]},n.gray.hex=function(e){const t=255&Math.round(e[0]/100*255),i=((t<<16)+(t<<8)+t).toString(16).toUpperCase();return"000000".substring(i.length)+i},n.rgb.gray=function(e){return[(e[0]+e[1]+e[2])/3/255*100]}},60398:(e,t,i)=>{const s=i(38097),r=i(14657),n={};Object.keys(s).forEach((e=>{n[e]={},Object.defineProperty(n[e],"channels",{value:s[e].channels}),Object.defineProperty(n[e],"labels",{value:s[e].labels});const t=r(e);Object.keys(t).forEach((i=>{const s=t[i];n[e][i]=function(e){const t=function(...t){const i=t[0];if(null==i)return i;i.length>1&&(t=i);const s=e(t);if("object"==typeof s)for(let e=s.length,t=0;t1&&(t=i),e(t))};return"conversion"in e&&(t.conversion=e.conversion),t}(s)}))})),e.exports=n},14657:(e,t,i)=>{const s=i(38097);function r(e,t){return function(i){return t(e(i))}}function n(e,t){const i=[t[e].parent,e];let n=s[t[e].parent][e],o=t[e].parent;for(;t[o].parent;)i.unshift(t[o].parent),n=r(s[t[o].parent][o],n),o=t[o].parent;return n.conversion=i,n}e.exports=function(e){const t=function(e){const t=function(){const e={},t=Object.keys(s);for(let i=t.length,s=0;s{"use strict";e.exports={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]}},92604:(e,t,i)=>{var s={};e.exports=s,s.themes={};var r=s.styles=i(76375),n=Object.defineProperties;s.supportsColor=i(33578),void 0===s.enabled&&(s.enabled=s.supportsColor),s.stripColors=s.strip=function(e){return(""+e).replace(/\x1B\[\d+m/g,"")},s.stylize=function(e,t){return r[t].open+e+r[t].close};var o=/[|\\{}()[\]^$+*?.]/g;function a(e){var t=function e(){return A.apply(e,arguments)};return t._styles=e,t.__proto__=p,t}var c,l=(c={},r.grey=r.gray,Object.keys(r).forEach((function(e){r[e].closeRe=new RegExp(function(e){if("string"!=typeof e)throw new TypeError("Expected a string");return e.replace(o,"\\$&")}(r[e].close),"g"),c[e]={get:function(){return a(this._styles.concat(e))}}})),c),p=n((function(){}),l);function A(){var e=arguments,t=e.length,i=0!==t&&String(arguments[0]);if(t>1)for(var n=1;n{e.exports=function(e,t){var i="";e=(e=e||"Run the trap, drop the bass").split("");var s={a:["@","Ą","Ⱥ","Ʌ","Δ","Λ","Д"],b:["ß","Ɓ","Ƀ","ɮ","β","฿"],c:["©","Ȼ","Ͼ"],d:["Ð","Ɗ","Ԁ","ԁ","Ԃ","ԃ"],e:["Ë","ĕ","Ǝ","ɘ","Σ","ξ","Ҽ","੬"],f:["Ӻ"],g:["ɢ"],h:["Ħ","ƕ","Ң","Һ","Ӈ","Ԋ"],i:["༏"],j:["Ĵ"],k:["ĸ","Ҡ","Ӄ","Ԟ"],l:["Ĺ"],m:["ʍ","Ӎ","ӎ","Ԡ","ԡ","൩"],n:["Ñ","ŋ","Ɲ","Ͷ","Π","Ҋ"],o:["Ø","õ","ø","Ǿ","ʘ","Ѻ","ם","۝","๏"],p:["Ƿ","Ҏ"],q:["্"],r:["®","Ʀ","Ȑ","Ɍ","ʀ","Я"],s:["§","Ϟ","ϟ","Ϩ"],t:["Ł","Ŧ","ͳ"],u:["Ʊ","Ս"],v:["ט"],w:["Ш","Ѡ","Ѽ","൰"],x:["Ҳ","Ӿ","Ӽ","ӽ"],y:["¥","Ұ","Ӌ"],z:["Ƶ","ɀ"]};return e.forEach((function(e){e=e.toLowerCase();var t=s[e]||[" "],r=Math.floor(Math.random()*t.length);i+=void 0!==s[e]?s[e][r]:e})),i}},16893:e=>{e.exports=function(e,t){e=e||" he is here ";var i={up:["̍","̎","̄","̅","̿","̑","̆","̐","͒","͗","͑","̇","̈","̊","͂","̓","̈","͊","͋","͌","̃","̂","̌","͐","̀","́","̋","̏","̒","̓","̔","̽","̉","ͣ","ͤ","ͥ","ͦ","ͧ","ͨ","ͩ","ͪ","ͫ","ͬ","ͭ","ͮ","ͯ","̾","͛","͆","̚"],down:["̖","̗","̘","̙","̜","̝","̞","̟","̠","̤","̥","̦","̩","̪","̫","̬","̭","̮","̯","̰","̱","̲","̳","̹","̺","̻","̼","ͅ","͇","͈","͉","͍","͎","͓","͔","͕","͖","͙","͚","̣"],mid:["̕","̛","̀","́","͘","̡","̢","̧","̨","̴","̵","̶","͜","͝","͞","͟","͠","͢","̸","̷","͡"," ҉"]},s=[].concat(i.up,i.down,i.mid);function r(e){return Math.floor(Math.random()*e)}function n(e){var t=!1;return s.filter((function(i){t=i===e})),t}return function(e,t){var s,o,a="";for(o in(t=t||{}).up=t.up||!0,t.mid=t.mid||!0,t.down=t.down||!0,t.size=t.size||"maxi",e=e.split(""))if(!n(o)){switch(a+=e[o],s={up:0,down:0,mid:0},t.size){case"mini":s.up=r(8),s.min=r(2),s.down=r(8);break;case"maxi":s.up=r(16)+3,s.min=r(4)+1,s.down=r(64)+3;break;default:s.up=r(8)+1,s.mid=r(6)/2,s.down=r(8)+1}var c=["up","mid","down"];for(var l in c)for(var p=c[l],A=0;A<=s[p];A++)t[p]&&(a+=i[p][r(i[p].length)])}return a}(e)}},70418:(e,t,i)=>{var s=i(92604);e.exports=function(e,t,i){if(" "===e)return e;switch(t%3){case 0:return s.red(e);case 1:return s.white(e);case 2:return s.blue(e)}}},22430:(e,t,i)=>{var s,r=i(92604);e.exports=(s=["red","yellow","green","blue","magenta"],function(e,t,i){return" "===e?e:r[s[t++%s.length]](e)})},35041:(e,t,i)=>{var s,r=i(92604);e.exports=(s=["underline","inverse","grey","yellow","red","green","blue","white","cyan","magenta"],function(e,t,i){return" "===e?e:r[s[Math.round(Math.random()*(s.length-1))]](e)})},76492:(e,t,i)=>{var s=i(92604);e.exports=function(e,t,i){return t%2==0?e:s.inverse(e)}},76375:e=>{var t={};e.exports=t;var i={reset:[0,0],bold:[1,22],dim:[2,22],italic:[3,23],underline:[4,24],inverse:[7,27],hidden:[8,28],strikethrough:[9,29],black:[30,39],red:[31,39],green:[32,39],yellow:[33,39],blue:[34,39],magenta:[35,39],cyan:[36,39],white:[37,39],gray:[90,39],grey:[90,39],bgBlack:[40,49],bgRed:[41,49],bgGreen:[42,49],bgYellow:[43,49],bgBlue:[44,49],bgMagenta:[45,49],bgCyan:[46,49],bgWhite:[47,49],blackBG:[40,49],redBG:[41,49],greenBG:[42,49],yellowBG:[43,49],blueBG:[44,49],magentaBG:[45,49],cyanBG:[46,49],whiteBG:[47,49]};Object.keys(i).forEach((function(e){var s=i[e],r=t[e]=[];r.open="["+s[0]+"m",r.close="["+s[1]+"m"}))},33578:e=>{var t=process.argv;e.exports=-1===t.indexOf("--no-color")&&-1===t.indexOf("--color=false")&&(-1!==t.indexOf("--color")||-1!==t.indexOf("--color=true")||-1!==t.indexOf("--color=always")||!(process.stdout&&!process.stdout.isTTY)&&("win32"===process.platform||"COLORTERM"in process.env||"dumb"!==process.env.TERM&&!!/^screen|^xterm|^vt100|color|ansi|cygwin|linux/i.test(process.env.TERM)))},28130:e=>{function t(e){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}t.keys=()=>[],t.resolve=t,t.id=28130,e.exports=t},27072:(e,t,i)=>{var s=i(92604);e.exports=s},14598:(e,t,i)=>{var s=i(73837),r=i(12781).Stream,n=i(65239);function o(){this.writable=!1,this.readable=!0,this.dataSize=0,this.maxDataSize=2097152,this.pauseStreams=!0,this._released=!1,this._streams=[],this._currentStream=null,this._insideLoop=!1,this._pendingNext=!1}e.exports=o,s.inherits(o,r),o.create=function(e){var t=new this;for(var i in e=e||{})t[i]=e[i];return t},o.isStreamLike=function(e){return"function"!=typeof e&&"string"!=typeof e&&"boolean"!=typeof e&&"number"!=typeof e&&!Buffer.isBuffer(e)},o.prototype.append=function(e){if(o.isStreamLike(e)){if(!(e instanceof n)){var t=n.create(e,{maxDataSize:1/0,pauseStream:this.pauseStreams});e.on("data",this._checkDataSize.bind(this)),e=t}this._handleErrors(e),this.pauseStreams&&e.pause()}return this._streams.push(e),this},o.prototype.pipe=function(e,t){return r.prototype.pipe.call(this,e,t),this.resume(),e},o.prototype._getNext=function(){if(this._currentStream=null,this._insideLoop)this._pendingNext=!0;else{this._insideLoop=!0;try{do{this._pendingNext=!1,this._realGetNext()}while(this._pendingNext)}finally{this._insideLoop=!1}}},o.prototype._realGetNext=function(){var e=this._streams.shift();void 0!==e?"function"==typeof e?e(function(e){o.isStreamLike(e)&&(e.on("data",this._checkDataSize.bind(this)),this._handleErrors(e)),this._pipeNext(e)}.bind(this)):this._pipeNext(e):this.end()},o.prototype._pipeNext=function(e){if(this._currentStream=e,o.isStreamLike(e))return e.on("end",this._getNext.bind(this)),void e.pipe(this,{end:!1});var t=e;this.write(t),this._getNext()},o.prototype._handleErrors=function(e){var t=this;e.on("error",(function(e){t._emitError(e)}))},o.prototype.write=function(e){this.emit("data",e)},o.prototype.pause=function(){this.pauseStreams&&(this.pauseStreams&&this._currentStream&&"function"==typeof this._currentStream.pause&&this._currentStream.pause(),this.emit("pause"))},o.prototype.resume=function(){this._released||(this._released=!0,this.writable=!0,this._getNext()),this.pauseStreams&&this._currentStream&&"function"==typeof this._currentStream.resume&&this._currentStream.resume(),this.emit("resume")},o.prototype.end=function(){this._reset(),this.emit("end")},o.prototype.destroy=function(){this._reset(),this.emit("close")},o.prototype._reset=function(){this.writable=!1,this._streams=[],this._currentStream=null},o.prototype._checkDataSize=function(){if(this._updateDataSize(),!(this.dataSize<=this.maxDataSize)){var e="DelayedStream#maxDataSize of "+this.maxDataSize+" bytes exceeded.";this._emitError(new Error(e))}},o.prototype._updateDataSize=function(){this.dataSize=0;var e=this;this._streams.forEach((function(t){t.dataSize&&(e.dataSize+=t.dataSize)})),this._currentStream&&this._currentStream.dataSize&&(this.dataSize+=this._currentStream.dataSize)},o.prototype._emitError=function(e){this._reset(),this.emit("error",e)}},95146:(e,t,i)=>{t.formatArgs=function(t){if(t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+e.exports.humanize(this.diff),!this.useColors)return;const i="color: "+this.color;t.splice(1,0,i,"color: inherit");let s=0,r=0;t[0].replace(/%[a-zA-Z%]/g,(e=>{"%%"!==e&&(s++,"%c"===e&&(r=s))})),t.splice(r,0,i)},t.save=function(e){try{e?t.storage.setItem("debug",e):t.storage.removeItem("debug")}catch(e){}},t.load=function(){let e;try{e=t.storage.getItem("debug")}catch(e){}return!e&&"undefined"!=typeof process&&"env"in process&&(e=process.env.DEBUG),e},t.useColors=function(){return!("undefined"==typeof window||!window.process||"renderer"!==window.process.type&&!window.process.__nwjs)||("undefined"==typeof navigator||!navigator.userAgent||!navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))&&("undefined"!=typeof document&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||"undefined"!=typeof window&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/))},t.storage=function(){try{return localStorage}catch(e){}}(),t.destroy=(()=>{let e=!1;return()=>{e||(e=!0,console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."))}})(),t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"],t.log=console.debug||console.log||(()=>{}),e.exports=i(17498)(t);const{formatters:s}=e.exports;s.j=function(e){try{return JSON.stringify(e)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}},17498:(e,t,i)=>{e.exports=function(e){function t(e){let i,r,n,o=null;function a(...e){if(!a.enabled)return;const s=a,r=Number(new Date),n=r-(i||r);s.diff=n,s.prev=i,s.curr=r,i=r,e[0]=t.coerce(e[0]),"string"!=typeof e[0]&&e.unshift("%O");let o=0;e[0]=e[0].replace(/%([a-zA-Z%])/g,((i,r)=>{if("%%"===i)return"%";o++;const n=t.formatters[r];if("function"==typeof n){const t=e[o];i=n.call(s,t),e.splice(o,1),o--}return i})),t.formatArgs.call(s,e),(s.log||t.log).apply(s,e)}return a.namespace=e,a.useColors=t.useColors(),a.color=t.selectColor(e),a.extend=s,a.destroy=t.destroy,Object.defineProperty(a,"enabled",{enumerable:!0,configurable:!1,get:()=>null!==o?o:(r!==t.namespaces&&(r=t.namespaces,n=t.enabled(e)),n),set:e=>{o=e}}),"function"==typeof t.init&&t.init(a),a}function s(e,i){const s=t(this.namespace+(void 0===i?":":i)+e);return s.log=this.log,s}function r(e){return e.toString().substring(2,e.toString().length-2).replace(/\.\*\?$/,"*")}return t.debug=t,t.default=t,t.coerce=function(e){return e instanceof Error?e.stack||e.message:e},t.disable=function(){const e=[...t.names.map(r),...t.skips.map(r).map((e=>"-"+e))].join(",");return t.enable(""),e},t.enable=function(e){let i;t.save(e),t.namespaces=e,t.names=[],t.skips=[];const s=("string"==typeof e?e:"").split(/[\s,]+/),r=s.length;for(i=0;i{t[i]=e[i]})),t.names=[],t.skips=[],t.formatters={},t.selectColor=function(e){let i=0;for(let t=0;t{"undefined"==typeof process||"renderer"===process.type||!0===process.browser||process.__nwjs?e.exports=i(95146):e.exports=i(46072)},46072:(e,t,i)=>{const s=i(76224),r=i(73837);t.init=function(e){e.inspectOpts={};const i=Object.keys(t.inspectOpts);for(let s=0;s{}),"Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."),t.colors=[6,2,3,4,5,1];try{const e=i(56778);e&&(e.stderr||e).level>=2&&(t.colors=[20,21,26,27,32,33,38,39,40,41,42,43,44,45,56,57,62,63,68,69,74,75,76,77,78,79,80,81,92,93,98,99,112,113,128,129,134,135,148,149,160,161,162,163,164,165,166,167,168,169,170,171,172,173,178,179,184,185,196,197,198,199,200,201,202,203,204,205,206,207,208,209,214,215,220,221])}catch(e){}t.inspectOpts=Object.keys(process.env).filter((e=>/^debug_/i.test(e))).reduce(((e,t)=>{const i=t.substring(6).toLowerCase().replace(/_([a-z])/g,((e,t)=>t.toUpperCase()));let s=process.env[t];return s=!!/^(yes|on|true|enabled)$/i.test(s)||!/^(no|off|false|disabled)$/i.test(s)&&("null"===s?null:Number(s)),e[i]=s,e}),{}),e.exports=i(17498)(t);const{formatters:n}=e.exports;n.o=function(e){return this.inspectOpts.colors=this.useColors,r.inspect(e,this.inspectOpts).split("\n").map((e=>e.trim())).join(" ")},n.O=function(e){return this.inspectOpts.colors=this.useColors,r.inspect(e,this.inspectOpts)}},70596:(e,t,i)=>{var s=i(54256);e.exports=function(e,t){return e=e||{},Object.keys(t).forEach((function(i){void 0===e[i]&&(e[i]=s(t[i]))})),e}},65239:(e,t,i)=>{var s=i(12781).Stream,r=i(73837);function n(){this.source=null,this.dataSize=0,this.maxDataSize=1048576,this.pauseStream=!0,this._maxDataSizeExceeded=!1,this._released=!1,this._bufferedEvents=[]}e.exports=n,r.inherits(n,s),n.create=function(e,t){var i=new this;for(var s in t=t||{})i[s]=t[s];i.source=e;var r=e.emit;return e.emit=function(){return i._handleEmit(arguments),r.apply(e,arguments)},e.on("error",(function(){})),i.pauseStream&&e.pause(),i},Object.defineProperty(n.prototype,"readable",{configurable:!0,enumerable:!0,get:function(){return this.source.readable}}),n.prototype.setEncoding=function(){return this.source.setEncoding.apply(this.source,arguments)},n.prototype.resume=function(){this._released||this.release(),this.source.resume()},n.prototype.pause=function(){this.source.pause()},n.prototype.release=function(){this._released=!0,this._bufferedEvents.forEach(function(e){this.emit.apply(this,e)}.bind(this)),this._bufferedEvents=[]},n.prototype.pipe=function(){var e=s.prototype.pipe.apply(this,arguments);return this.resume(),e},n.prototype._handleEmit=function(e){this._released?this.emit.apply(this,e):("data"===e[0]&&(this.dataSize+=e[1].length,this._checkIfMaxDataSizeExceeded()),this._bufferedEvents.push(e))},n.prototype._checkIfMaxDataSizeExceeded=function(){if(!(this._maxDataSizeExceeded||this.dataSize<=this.maxDataSize)){this._maxDataSizeExceeded=!0;var e="DelayedStream#maxDataSize of "+this.maxDataSize+" bytes exceeded.";this.emit("error",new Error(e))}}},57751:(e,t,i)=>{"use strict";i.r(t),i.d(t,{Deprecation:()=>s});class s extends Error{constructor(e){super(e),Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor),this.name="Deprecation"}}},90772:(e,t,i)=>{const s=i(57147),r=i(71017),n=i(22037);function o(e){console.log(`[dotenv][DEBUG] ${e}`)}const a=/^\s*([\w.-]+)\s*=\s*(.*)?\s*$/,c=/\\n/g,l=/\r\n|\n|\r/;function p(e,t){const i=Boolean(t&&t.debug),s={};return e.toString().split(l).forEach((function(e,t){const r=e.match(a);if(null!=r){const e=r[1];let t=r[2]||"";const i=t.length-1,n='"'===t[0]&&'"'===t[i];"'"===t[0]&&"'"===t[i]||n?(t=t.substring(1,i),n&&(t=t.replace(c,"\n"))):t=t.trim(),s[e]=t}else i&&o(`did not match key and value when parsing line ${t+1}: ${e}`)})),s}e.exports.config=function(e){let t=r.resolve(process.cwd(),".env"),i="utf8",a=!1;var c;e&&(null!=e.path&&(t="~"===(c=e.path)[0]?r.join(n.homedir(),c.slice(1)):c),null!=e.encoding&&(i=e.encoding),null!=e.debug&&(a=!0));try{const e=p(s.readFileSync(t,{encoding:i}),{debug:a});return Object.keys(e).forEach((function(t){Object.prototype.hasOwnProperty.call(process.env,t)?a&&o(`"${t}" is already defined in \`process.env\` and will not be overwritten`):process.env[t]=e[t]})),{parsed:e}}catch(e){return{error:e}}},e.exports.parse=p},87491:function(e){var t;t=function(){return function(e){var t={};function i(s){if(t[s])return t[s].exports;var r=t[s]={exports:{},id:s,loaded:!1};return e[s].call(r.exports,r,r.exports,i),r.loaded=!0,r.exports}return i.m=e,i.c=t,i.p="",i(0)}([function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var s=i(1),r=i(3),n=i(8),o=i(15);function a(e,t,i){var o=null,a=function(e,t){i&&i(e,t),o&&o.visit(e,t)},c="function"==typeof i?a:null,l=!1;if(t){l="boolean"==typeof t.comment&&t.comment;var p="boolean"==typeof t.attachComment&&t.attachComment;(l||p)&&((o=new s.CommentHandler).attach=p,t.comment=!0,c=a)}var A,u=!1;t&&"string"==typeof t.sourceType&&(u="module"===t.sourceType),A=t&&"boolean"==typeof t.jsx&&t.jsx?new r.JSXParser(e,t,c):new n.Parser(e,t,c);var d=u?A.parseModule():A.parseScript();return l&&o&&(d.comments=o.comments),A.config.tokens&&(d.tokens=A.tokens),A.config.tolerant&&(d.errors=A.errorHandler.errors),d}t.parse=a,t.parseModule=function(e,t,i){var s=t||{};return s.sourceType="module",a(e,s,i)},t.parseScript=function(e,t,i){var s=t||{};return s.sourceType="script",a(e,s,i)},t.tokenize=function(e,t,i){var s,r=new o.Tokenizer(e,t);s=[];try{for(;;){var n=r.getNextToken();if(!n)break;i&&(n=i(n)),s.push(n)}}catch(e){r.errorHandler.tolerate(e)}return r.errorHandler.tolerant&&(s.errors=r.errors()),s};var c=i(2);t.Syntax=c.Syntax,t.version="4.0.1"},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var s=i(2),r=function(){function e(){this.attach=!1,this.comments=[],this.stack=[],this.leading=[],this.trailing=[]}return e.prototype.insertInnerComments=function(e,t){if(e.type===s.Syntax.BlockStatement&&0===e.body.length){for(var i=[],r=this.leading.length-1;r>=0;--r){var n=this.leading[r];t.end.offset>=n.start&&(i.unshift(n.comment),this.leading.splice(r,1),this.trailing.splice(r,1))}i.length&&(e.innerComments=i)}},e.prototype.findTrailingComments=function(e){var t=[];if(this.trailing.length>0){for(var i=this.trailing.length-1;i>=0;--i){var s=this.trailing[i];s.start>=e.end.offset&&t.unshift(s.comment)}return this.trailing.length=0,t}var r=this.stack[this.stack.length-1];if(r&&r.node.trailingComments){var n=r.node.trailingComments[0];n&&n.range[0]>=e.end.offset&&(t=r.node.trailingComments,delete r.node.trailingComments)}return t},e.prototype.findLeadingComments=function(e){for(var t,i=[];this.stack.length>0&&(n=this.stack[this.stack.length-1])&&n.start>=e.start.offset;)t=n.node,this.stack.pop();if(t){for(var s=(t.leadingComments?t.leadingComments.length:0)-1;s>=0;--s){var r=t.leadingComments[s];r.range[1]<=e.start.offset&&(i.unshift(r),t.leadingComments.splice(s,1))}return t.leadingComments&&0===t.leadingComments.length&&delete t.leadingComments,i}for(s=this.leading.length-1;s>=0;--s){var n;(n=this.leading[s]).start<=e.start.offset&&(i.unshift(n.comment),this.leading.splice(s,1))}return i},e.prototype.visitNode=function(e,t){if(!(e.type===s.Syntax.Program&&e.body.length>0)){this.insertInnerComments(e,t);var i=this.findTrailingComments(t),r=this.findLeadingComments(t);r.length>0&&(e.leadingComments=r),i.length>0&&(e.trailingComments=i),this.stack.push({node:e,start:t.start.offset})}},e.prototype.visitComment=function(e,t){var i="L"===e.type[0]?"Line":"Block",s={type:i,value:e.value};if(e.range&&(s.range=e.range),e.loc&&(s.loc=e.loc),this.comments.push(s),this.attach){var r={comment:{type:i,value:e.value,range:[t.start.offset,t.end.offset]},start:t.start.offset};e.loc&&(r.comment.loc=e.loc),e.type=i,this.leading.push(r),this.trailing.push(r)}},e.prototype.visit=function(e,t){"LineComment"===e.type||"BlockComment"===e.type?this.visitComment(e,t):this.attach&&this.visitNode(e,t)},e}();t.CommentHandler=r},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Syntax={AssignmentExpression:"AssignmentExpression",AssignmentPattern:"AssignmentPattern",ArrayExpression:"ArrayExpression",ArrayPattern:"ArrayPattern",ArrowFunctionExpression:"ArrowFunctionExpression",AwaitExpression:"AwaitExpression",BlockStatement:"BlockStatement",BinaryExpression:"BinaryExpression",BreakStatement:"BreakStatement",CallExpression:"CallExpression",CatchClause:"CatchClause",ClassBody:"ClassBody",ClassDeclaration:"ClassDeclaration",ClassExpression:"ClassExpression",ConditionalExpression:"ConditionalExpression",ContinueStatement:"ContinueStatement",DoWhileStatement:"DoWhileStatement",DebuggerStatement:"DebuggerStatement",EmptyStatement:"EmptyStatement",ExportAllDeclaration:"ExportAllDeclaration",ExportDefaultDeclaration:"ExportDefaultDeclaration",ExportNamedDeclaration:"ExportNamedDeclaration",ExportSpecifier:"ExportSpecifier",ExpressionStatement:"ExpressionStatement",ForStatement:"ForStatement",ForOfStatement:"ForOfStatement",ForInStatement:"ForInStatement",FunctionDeclaration:"FunctionDeclaration",FunctionExpression:"FunctionExpression",Identifier:"Identifier",IfStatement:"IfStatement",ImportDeclaration:"ImportDeclaration",ImportDefaultSpecifier:"ImportDefaultSpecifier",ImportNamespaceSpecifier:"ImportNamespaceSpecifier",ImportSpecifier:"ImportSpecifier",Literal:"Literal",LabeledStatement:"LabeledStatement",LogicalExpression:"LogicalExpression",MemberExpression:"MemberExpression",MetaProperty:"MetaProperty",MethodDefinition:"MethodDefinition",NewExpression:"NewExpression",ObjectExpression:"ObjectExpression",ObjectPattern:"ObjectPattern",Program:"Program",Property:"Property",RestElement:"RestElement",ReturnStatement:"ReturnStatement",SequenceExpression:"SequenceExpression",SpreadElement:"SpreadElement",Super:"Super",SwitchCase:"SwitchCase",SwitchStatement:"SwitchStatement",TaggedTemplateExpression:"TaggedTemplateExpression",TemplateElement:"TemplateElement",TemplateLiteral:"TemplateLiteral",ThisExpression:"ThisExpression",ThrowStatement:"ThrowStatement",TryStatement:"TryStatement",UnaryExpression:"UnaryExpression",UpdateExpression:"UpdateExpression",VariableDeclaration:"VariableDeclaration",VariableDeclarator:"VariableDeclarator",WhileStatement:"WhileStatement",WithStatement:"WithStatement",YieldExpression:"YieldExpression"}},function(e,t,i){"use strict";var s,r=this&&this.__extends||(s=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var i in t)t.hasOwnProperty(i)&&(e[i]=t[i])},function(e,t){function i(){this.constructor=e}s(e,t),e.prototype=null===t?Object.create(t):(i.prototype=t.prototype,new i)});Object.defineProperty(t,"__esModule",{value:!0});var n=i(4),o=i(5),a=i(6),c=i(7),l=i(8),p=i(13),A=i(14);function u(e){var t;switch(e.type){case a.JSXSyntax.JSXIdentifier:t=e.name;break;case a.JSXSyntax.JSXNamespacedName:var i=e;t=u(i.namespace)+":"+u(i.name);break;case a.JSXSyntax.JSXMemberExpression:var s=e;t=u(s.object)+"."+u(s.property)}return t}p.TokenName[100]="JSXIdentifier",p.TokenName[101]="JSXText";var d=function(e){function t(t,i,s){return e.call(this,t,i,s)||this}return r(t,e),t.prototype.parsePrimaryExpression=function(){return this.match("<")?this.parseJSXRoot():e.prototype.parsePrimaryExpression.call(this)},t.prototype.startJSX=function(){this.scanner.index=this.startMarker.index,this.scanner.lineNumber=this.startMarker.line,this.scanner.lineStart=this.startMarker.index-this.startMarker.column},t.prototype.finishJSX=function(){this.nextToken()},t.prototype.reenterJSX=function(){this.startJSX(),this.expectJSX("}"),this.config.tokens&&this.tokens.pop()},t.prototype.createJSXNode=function(){return this.collectComments(),{index:this.scanner.index,line:this.scanner.lineNumber,column:this.scanner.index-this.scanner.lineStart}},t.prototype.createJSXChildNode=function(){return{index:this.scanner.index,line:this.scanner.lineNumber,column:this.scanner.index-this.scanner.lineStart}},t.prototype.scanXHTMLEntity=function(e){for(var t="&",i=!0,s=!1,r=!1,o=!1;!this.scanner.eof()&&i&&!s;){var a=this.scanner.source[this.scanner.index];if(a===e)break;if(s=";"===a,t+=a,++this.scanner.index,!s)switch(t.length){case 2:r="#"===a;break;case 3:r&&(i=(o="x"===a)||n.Character.isDecimalDigit(a.charCodeAt(0)),r=r&&!o);break;default:i=(i=i&&!(r&&!n.Character.isDecimalDigit(a.charCodeAt(0))))&&!(o&&!n.Character.isHexDigit(a.charCodeAt(0)))}}if(i&&s&&t.length>2){var c=t.substr(1,t.length-2);r&&c.length>1?t=String.fromCharCode(parseInt(c.substr(1),10)):o&&c.length>2?t=String.fromCharCode(parseInt("0"+c.substr(1),16)):r||o||!A.XHTMLEntities[c]||(t=A.XHTMLEntities[c])}return t},t.prototype.lexJSX=function(){var e=this.scanner.source.charCodeAt(this.scanner.index);if(60===e||62===e||47===e||58===e||61===e||123===e||125===e)return{type:7,value:a=this.scanner.source[this.scanner.index++],lineNumber:this.scanner.lineNumber,lineStart:this.scanner.lineStart,start:this.scanner.index-1,end:this.scanner.index};if(34===e||39===e){for(var t=this.scanner.index,i=this.scanner.source[this.scanner.index++],s="";!this.scanner.eof()&&(c=this.scanner.source[this.scanner.index++])!==i;)s+="&"===c?this.scanXHTMLEntity(i):c;return{type:8,value:s,lineNumber:this.scanner.lineNumber,lineStart:this.scanner.lineStart,start:t,end:this.scanner.index}}if(46===e){var r=this.scanner.source.charCodeAt(this.scanner.index+1),o=this.scanner.source.charCodeAt(this.scanner.index+2),a=46===r&&46===o?"...":".";return t=this.scanner.index,this.scanner.index+=a.length,{type:7,value:a,lineNumber:this.scanner.lineNumber,lineStart:this.scanner.lineStart,start:t,end:this.scanner.index}}if(96===e)return{type:10,value:"",lineNumber:this.scanner.lineNumber,lineStart:this.scanner.lineStart,start:this.scanner.index,end:this.scanner.index};if(n.Character.isIdentifierStart(e)&&92!==e){for(t=this.scanner.index,++this.scanner.index;!this.scanner.eof();){var c=this.scanner.source.charCodeAt(this.scanner.index);if(n.Character.isIdentifierPart(c)&&92!==c)++this.scanner.index;else{if(45!==c)break;++this.scanner.index}}return{type:100,value:this.scanner.source.slice(t,this.scanner.index),lineNumber:this.scanner.lineNumber,lineStart:this.scanner.lineStart,start:t,end:this.scanner.index}}return this.scanner.lex()},t.prototype.nextJSXToken=function(){this.collectComments(),this.startMarker.index=this.scanner.index,this.startMarker.line=this.scanner.lineNumber,this.startMarker.column=this.scanner.index-this.scanner.lineStart;var e=this.lexJSX();return this.lastMarker.index=this.scanner.index,this.lastMarker.line=this.scanner.lineNumber,this.lastMarker.column=this.scanner.index-this.scanner.lineStart,this.config.tokens&&this.tokens.push(this.convertToken(e)),e},t.prototype.nextJSXText=function(){this.startMarker.index=this.scanner.index,this.startMarker.line=this.scanner.lineNumber,this.startMarker.column=this.scanner.index-this.scanner.lineStart;for(var e=this.scanner.index,t="";!this.scanner.eof();){var i=this.scanner.source[this.scanner.index];if("{"===i||"<"===i)break;++this.scanner.index,t+=i,n.Character.isLineTerminator(i.charCodeAt(0))&&(++this.scanner.lineNumber,"\r"===i&&"\n"===this.scanner.source[this.scanner.index]&&++this.scanner.index,this.scanner.lineStart=this.scanner.index)}this.lastMarker.index=this.scanner.index,this.lastMarker.line=this.scanner.lineNumber,this.lastMarker.column=this.scanner.index-this.scanner.lineStart;var s={type:101,value:t,lineNumber:this.scanner.lineNumber,lineStart:this.scanner.lineStart,start:e,end:this.scanner.index};return t.length>0&&this.config.tokens&&this.tokens.push(this.convertToken(s)),s},t.prototype.peekJSXToken=function(){var e=this.scanner.saveState();this.scanner.scanComments();var t=this.lexJSX();return this.scanner.restoreState(e),t},t.prototype.expectJSX=function(e){var t=this.nextJSXToken();7===t.type&&t.value===e||this.throwUnexpectedToken(t)},t.prototype.matchJSX=function(e){var t=this.peekJSXToken();return 7===t.type&&t.value===e},t.prototype.parseJSXIdentifier=function(){var e=this.createJSXNode(),t=this.nextJSXToken();return 100!==t.type&&this.throwUnexpectedToken(t),this.finalize(e,new o.JSXIdentifier(t.value))},t.prototype.parseJSXElementName=function(){var e=this.createJSXNode(),t=this.parseJSXIdentifier();if(this.matchJSX(":")){var i=t;this.expectJSX(":");var s=this.parseJSXIdentifier();t=this.finalize(e,new o.JSXNamespacedName(i,s))}else if(this.matchJSX("."))for(;this.matchJSX(".");){var r=t;this.expectJSX(".");var n=this.parseJSXIdentifier();t=this.finalize(e,new o.JSXMemberExpression(r,n))}return t},t.prototype.parseJSXAttributeName=function(){var e,t=this.createJSXNode(),i=this.parseJSXIdentifier();if(this.matchJSX(":")){var s=i;this.expectJSX(":");var r=this.parseJSXIdentifier();e=this.finalize(t,new o.JSXNamespacedName(s,r))}else e=i;return e},t.prototype.parseJSXStringLiteralAttribute=function(){var e=this.createJSXNode(),t=this.nextJSXToken();8!==t.type&&this.throwUnexpectedToken(t);var i=this.getTokenRaw(t);return this.finalize(e,new c.Literal(t.value,i))},t.prototype.parseJSXExpressionAttribute=function(){var e=this.createJSXNode();this.expectJSX("{"),this.finishJSX(),this.match("}")&&this.tolerateError("JSX attributes must only be assigned a non-empty expression");var t=this.parseAssignmentExpression();return this.reenterJSX(),this.finalize(e,new o.JSXExpressionContainer(t))},t.prototype.parseJSXAttributeValue=function(){return this.matchJSX("{")?this.parseJSXExpressionAttribute():this.matchJSX("<")?this.parseJSXElement():this.parseJSXStringLiteralAttribute()},t.prototype.parseJSXNameValueAttribute=function(){var e=this.createJSXNode(),t=this.parseJSXAttributeName(),i=null;return this.matchJSX("=")&&(this.expectJSX("="),i=this.parseJSXAttributeValue()),this.finalize(e,new o.JSXAttribute(t,i))},t.prototype.parseJSXSpreadAttribute=function(){var e=this.createJSXNode();this.expectJSX("{"),this.expectJSX("..."),this.finishJSX();var t=this.parseAssignmentExpression();return this.reenterJSX(),this.finalize(e,new o.JSXSpreadAttribute(t))},t.prototype.parseJSXAttributes=function(){for(var e=[];!this.matchJSX("/")&&!this.matchJSX(">");){var t=this.matchJSX("{")?this.parseJSXSpreadAttribute():this.parseJSXNameValueAttribute();e.push(t)}return e},t.prototype.parseJSXOpeningElement=function(){var e=this.createJSXNode();this.expectJSX("<");var t=this.parseJSXElementName(),i=this.parseJSXAttributes(),s=this.matchJSX("/");return s&&this.expectJSX("/"),this.expectJSX(">"),this.finalize(e,new o.JSXOpeningElement(t,s,i))},t.prototype.parseJSXBoundaryElement=function(){var e=this.createJSXNode();if(this.expectJSX("<"),this.matchJSX("/")){this.expectJSX("/");var t=this.parseJSXElementName();return this.expectJSX(">"),this.finalize(e,new o.JSXClosingElement(t))}var i=this.parseJSXElementName(),s=this.parseJSXAttributes(),r=this.matchJSX("/");return r&&this.expectJSX("/"),this.expectJSX(">"),this.finalize(e,new o.JSXOpeningElement(i,r,s))},t.prototype.parseJSXEmptyExpression=function(){var e=this.createJSXChildNode();return this.collectComments(),this.lastMarker.index=this.scanner.index,this.lastMarker.line=this.scanner.lineNumber,this.lastMarker.column=this.scanner.index-this.scanner.lineStart,this.finalize(e,new o.JSXEmptyExpression)},t.prototype.parseJSXExpressionContainer=function(){var e,t=this.createJSXNode();return this.expectJSX("{"),this.matchJSX("}")?(e=this.parseJSXEmptyExpression(),this.expectJSX("}")):(this.finishJSX(),e=this.parseAssignmentExpression(),this.reenterJSX()),this.finalize(t,new o.JSXExpressionContainer(e))},t.prototype.parseJSXChildren=function(){for(var e=[];!this.scanner.eof();){var t=this.createJSXChildNode(),i=this.nextJSXText();if(i.start0))break;n=this.finalize(e.node,new o.JSXElement(e.opening,e.children,e.closing)),(e=t[t.length-1]).children.push(n),t.pop()}}return e},t.prototype.parseJSXElement=function(){var e=this.createJSXNode(),t=this.parseJSXOpeningElement(),i=[],s=null;if(!t.selfClosing){var r=this.parseComplexJSXElement({node:e,opening:t,closing:s,children:i});i=r.children,s=r.closing}return this.finalize(e,new o.JSXElement(t,i,s))},t.prototype.parseJSXRoot=function(){this.config.tokens&&this.tokens.pop(),this.startJSX();var e=this.parseJSXElement();return this.finishJSX(),e},t.prototype.isStartOfExpression=function(){return e.prototype.isStartOfExpression.call(this)||this.match("<")},t}(l.Parser);t.JSXParser=d},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i={NonAsciiIdentifierStart:/[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]/,NonAsciiIdentifierPart:/[\xAA\xB5\xB7\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0-\u08B4\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDCA-\uDDCC\uDDD0-\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE37\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDDD8-\uDDDD\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDCA0-\uDCE9\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF]/};t.Character={fromCodePoint:function(e){return e<65536?String.fromCharCode(e):String.fromCharCode(55296+(e-65536>>10))+String.fromCharCode(56320+(e-65536&1023))},isWhiteSpace:function(e){return 32===e||9===e||11===e||12===e||160===e||e>=5760&&[5760,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8239,8287,12288,65279].indexOf(e)>=0},isLineTerminator:function(e){return 10===e||13===e||8232===e||8233===e},isIdentifierStart:function(e){return 36===e||95===e||e>=65&&e<=90||e>=97&&e<=122||92===e||e>=128&&i.NonAsciiIdentifierStart.test(t.Character.fromCodePoint(e))},isIdentifierPart:function(e){return 36===e||95===e||e>=65&&e<=90||e>=97&&e<=122||e>=48&&e<=57||92===e||e>=128&&i.NonAsciiIdentifierPart.test(t.Character.fromCodePoint(e))},isDecimalDigit:function(e){return e>=48&&e<=57},isHexDigit:function(e){return e>=48&&e<=57||e>=65&&e<=70||e>=97&&e<=102},isOctalDigit:function(e){return e>=48&&e<=55}}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var s=i(6);t.JSXClosingElement=function(e){this.type=s.JSXSyntax.JSXClosingElement,this.name=e};t.JSXElement=function(e,t,i){this.type=s.JSXSyntax.JSXElement,this.openingElement=e,this.children=t,this.closingElement=i};t.JSXEmptyExpression=function(){this.type=s.JSXSyntax.JSXEmptyExpression};t.JSXExpressionContainer=function(e){this.type=s.JSXSyntax.JSXExpressionContainer,this.expression=e};t.JSXIdentifier=function(e){this.type=s.JSXSyntax.JSXIdentifier,this.name=e};t.JSXMemberExpression=function(e,t){this.type=s.JSXSyntax.JSXMemberExpression,this.object=e,this.property=t};t.JSXAttribute=function(e,t){this.type=s.JSXSyntax.JSXAttribute,this.name=e,this.value=t};t.JSXNamespacedName=function(e,t){this.type=s.JSXSyntax.JSXNamespacedName,this.namespace=e,this.name=t};t.JSXOpeningElement=function(e,t,i){this.type=s.JSXSyntax.JSXOpeningElement,this.name=e,this.selfClosing=t,this.attributes=i};t.JSXSpreadAttribute=function(e){this.type=s.JSXSyntax.JSXSpreadAttribute,this.argument=e};t.JSXText=function(e,t){this.type=s.JSXSyntax.JSXText,this.value=e,this.raw=t}},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.JSXSyntax={JSXAttribute:"JSXAttribute",JSXClosingElement:"JSXClosingElement",JSXElement:"JSXElement",JSXEmptyExpression:"JSXEmptyExpression",JSXExpressionContainer:"JSXExpressionContainer",JSXIdentifier:"JSXIdentifier",JSXMemberExpression:"JSXMemberExpression",JSXNamespacedName:"JSXNamespacedName",JSXOpeningElement:"JSXOpeningElement",JSXSpreadAttribute:"JSXSpreadAttribute",JSXText:"JSXText"}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var s=i(2);t.ArrayExpression=function(e){this.type=s.Syntax.ArrayExpression,this.elements=e};t.ArrayPattern=function(e){this.type=s.Syntax.ArrayPattern,this.elements=e};t.ArrowFunctionExpression=function(e,t,i){this.type=s.Syntax.ArrowFunctionExpression,this.id=null,this.params=e,this.body=t,this.generator=!1,this.expression=i,this.async=!1};t.AssignmentExpression=function(e,t,i){this.type=s.Syntax.AssignmentExpression,this.operator=e,this.left=t,this.right=i};t.AssignmentPattern=function(e,t){this.type=s.Syntax.AssignmentPattern,this.left=e,this.right=t};t.AsyncArrowFunctionExpression=function(e,t,i){this.type=s.Syntax.ArrowFunctionExpression,this.id=null,this.params=e,this.body=t,this.generator=!1,this.expression=i,this.async=!0};t.AsyncFunctionDeclaration=function(e,t,i){this.type=s.Syntax.FunctionDeclaration,this.id=e,this.params=t,this.body=i,this.generator=!1,this.expression=!1,this.async=!0};t.AsyncFunctionExpression=function(e,t,i){this.type=s.Syntax.FunctionExpression,this.id=e,this.params=t,this.body=i,this.generator=!1,this.expression=!1,this.async=!0};t.AwaitExpression=function(e){this.type=s.Syntax.AwaitExpression,this.argument=e};t.BinaryExpression=function(e,t,i){var r="||"===e||"&&"===e;this.type=r?s.Syntax.LogicalExpression:s.Syntax.BinaryExpression,this.operator=e,this.left=t,this.right=i};t.BlockStatement=function(e){this.type=s.Syntax.BlockStatement,this.body=e};t.BreakStatement=function(e){this.type=s.Syntax.BreakStatement,this.label=e};t.CallExpression=function(e,t){this.type=s.Syntax.CallExpression,this.callee=e,this.arguments=t};t.CatchClause=function(e,t){this.type=s.Syntax.CatchClause,this.param=e,this.body=t};t.ClassBody=function(e){this.type=s.Syntax.ClassBody,this.body=e};t.ClassDeclaration=function(e,t,i){this.type=s.Syntax.ClassDeclaration,this.id=e,this.superClass=t,this.body=i};t.ClassExpression=function(e,t,i){this.type=s.Syntax.ClassExpression,this.id=e,this.superClass=t,this.body=i};t.ComputedMemberExpression=function(e,t){this.type=s.Syntax.MemberExpression,this.computed=!0,this.object=e,this.property=t};t.ConditionalExpression=function(e,t,i){this.type=s.Syntax.ConditionalExpression,this.test=e,this.consequent=t,this.alternate=i};t.ContinueStatement=function(e){this.type=s.Syntax.ContinueStatement,this.label=e};t.DebuggerStatement=function(){this.type=s.Syntax.DebuggerStatement};t.Directive=function(e,t){this.type=s.Syntax.ExpressionStatement,this.expression=e,this.directive=t};t.DoWhileStatement=function(e,t){this.type=s.Syntax.DoWhileStatement,this.body=e,this.test=t};t.EmptyStatement=function(){this.type=s.Syntax.EmptyStatement};t.ExportAllDeclaration=function(e){this.type=s.Syntax.ExportAllDeclaration,this.source=e};t.ExportDefaultDeclaration=function(e){this.type=s.Syntax.ExportDefaultDeclaration,this.declaration=e};t.ExportNamedDeclaration=function(e,t,i){this.type=s.Syntax.ExportNamedDeclaration,this.declaration=e,this.specifiers=t,this.source=i};t.ExportSpecifier=function(e,t){this.type=s.Syntax.ExportSpecifier,this.exported=t,this.local=e};t.ExpressionStatement=function(e){this.type=s.Syntax.ExpressionStatement,this.expression=e};t.ForInStatement=function(e,t,i){this.type=s.Syntax.ForInStatement,this.left=e,this.right=t,this.body=i,this.each=!1};t.ForOfStatement=function(e,t,i){this.type=s.Syntax.ForOfStatement,this.left=e,this.right=t,this.body=i};t.ForStatement=function(e,t,i,r){this.type=s.Syntax.ForStatement,this.init=e,this.test=t,this.update=i,this.body=r};t.FunctionDeclaration=function(e,t,i,r){this.type=s.Syntax.FunctionDeclaration,this.id=e,this.params=t,this.body=i,this.generator=r,this.expression=!1,this.async=!1};t.FunctionExpression=function(e,t,i,r){this.type=s.Syntax.FunctionExpression,this.id=e,this.params=t,this.body=i,this.generator=r,this.expression=!1,this.async=!1};t.Identifier=function(e){this.type=s.Syntax.Identifier,this.name=e};t.IfStatement=function(e,t,i){this.type=s.Syntax.IfStatement,this.test=e,this.consequent=t,this.alternate=i};t.ImportDeclaration=function(e,t){this.type=s.Syntax.ImportDeclaration,this.specifiers=e,this.source=t};t.ImportDefaultSpecifier=function(e){this.type=s.Syntax.ImportDefaultSpecifier,this.local=e};t.ImportNamespaceSpecifier=function(e){this.type=s.Syntax.ImportNamespaceSpecifier,this.local=e};t.ImportSpecifier=function(e,t){this.type=s.Syntax.ImportSpecifier,this.local=e,this.imported=t};t.LabeledStatement=function(e,t){this.type=s.Syntax.LabeledStatement,this.label=e,this.body=t};t.Literal=function(e,t){this.type=s.Syntax.Literal,this.value=e,this.raw=t};t.MetaProperty=function(e,t){this.type=s.Syntax.MetaProperty,this.meta=e,this.property=t};t.MethodDefinition=function(e,t,i,r,n){this.type=s.Syntax.MethodDefinition,this.key=e,this.computed=t,this.value=i,this.kind=r,this.static=n};t.Module=function(e){this.type=s.Syntax.Program,this.body=e,this.sourceType="module"};t.NewExpression=function(e,t){this.type=s.Syntax.NewExpression,this.callee=e,this.arguments=t};t.ObjectExpression=function(e){this.type=s.Syntax.ObjectExpression,this.properties=e};t.ObjectPattern=function(e){this.type=s.Syntax.ObjectPattern,this.properties=e};t.Property=function(e,t,i,r,n,o){this.type=s.Syntax.Property,this.key=t,this.computed=i,this.value=r,this.kind=e,this.method=n,this.shorthand=o};t.RegexLiteral=function(e,t,i,r){this.type=s.Syntax.Literal,this.value=e,this.raw=t,this.regex={pattern:i,flags:r}};t.RestElement=function(e){this.type=s.Syntax.RestElement,this.argument=e};t.ReturnStatement=function(e){this.type=s.Syntax.ReturnStatement,this.argument=e};t.Script=function(e){this.type=s.Syntax.Program,this.body=e,this.sourceType="script"};t.SequenceExpression=function(e){this.type=s.Syntax.SequenceExpression,this.expressions=e};t.SpreadElement=function(e){this.type=s.Syntax.SpreadElement,this.argument=e};t.StaticMemberExpression=function(e,t){this.type=s.Syntax.MemberExpression,this.computed=!1,this.object=e,this.property=t};t.Super=function(){this.type=s.Syntax.Super};t.SwitchCase=function(e,t){this.type=s.Syntax.SwitchCase,this.test=e,this.consequent=t};t.SwitchStatement=function(e,t){this.type=s.Syntax.SwitchStatement,this.discriminant=e,this.cases=t};t.TaggedTemplateExpression=function(e,t){this.type=s.Syntax.TaggedTemplateExpression,this.tag=e,this.quasi=t};t.TemplateElement=function(e,t){this.type=s.Syntax.TemplateElement,this.value=e,this.tail=t};t.TemplateLiteral=function(e,t){this.type=s.Syntax.TemplateLiteral,this.quasis=e,this.expressions=t};t.ThisExpression=function(){this.type=s.Syntax.ThisExpression};t.ThrowStatement=function(e){this.type=s.Syntax.ThrowStatement,this.argument=e};t.TryStatement=function(e,t,i){this.type=s.Syntax.TryStatement,this.block=e,this.handler=t,this.finalizer=i};t.UnaryExpression=function(e,t){this.type=s.Syntax.UnaryExpression,this.operator=e,this.argument=t,this.prefix=!0};t.UpdateExpression=function(e,t,i){this.type=s.Syntax.UpdateExpression,this.operator=e,this.argument=t,this.prefix=i};t.VariableDeclaration=function(e,t){this.type=s.Syntax.VariableDeclaration,this.declarations=e,this.kind=t};t.VariableDeclarator=function(e,t){this.type=s.Syntax.VariableDeclarator,this.id=e,this.init=t};t.WhileStatement=function(e,t){this.type=s.Syntax.WhileStatement,this.test=e,this.body=t};t.WithStatement=function(e,t){this.type=s.Syntax.WithStatement,this.object=e,this.body=t};t.YieldExpression=function(e,t){this.type=s.Syntax.YieldExpression,this.argument=e,this.delegate=t}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var s=i(9),r=i(10),n=i(11),o=i(7),a=i(12),c=i(2),l=i(13),p="ArrowParameterPlaceHolder",A=function(){function e(e,t,i){void 0===t&&(t={}),this.config={range:"boolean"==typeof t.range&&t.range,loc:"boolean"==typeof t.loc&&t.loc,source:null,tokens:"boolean"==typeof t.tokens&&t.tokens,comment:"boolean"==typeof t.comment&&t.comment,tolerant:"boolean"==typeof t.tolerant&&t.tolerant},this.config.loc&&t.source&&null!==t.source&&(this.config.source=String(t.source)),this.delegate=i,this.errorHandler=new r.ErrorHandler,this.errorHandler.tolerant=this.config.tolerant,this.scanner=new a.Scanner(e,this.errorHandler),this.scanner.trackComment=this.config.comment,this.operatorPrecedence={")":0,";":0,",":0,"=":0,"]":0,"||":1,"&&":2,"|":3,"^":4,"&":5,"==":6,"!=":6,"===":6,"!==":6,"<":7,">":7,"<=":7,">=":7,"<<":8,">>":8,">>>":8,"+":9,"-":9,"*":11,"/":11,"%":11},this.lookahead={type:2,value:"",lineNumber:this.scanner.lineNumber,lineStart:0,start:0,end:0},this.hasLineTerminator=!1,this.context={isModule:!1,await:!1,allowIn:!0,allowStrictDirective:!0,allowYield:!0,firstCoverInitializedNameError:null,isAssignmentTarget:!1,isBindingElement:!1,inFunctionBody:!1,inIteration:!1,inSwitch:!1,labelSet:{},strict:!1},this.tokens=[],this.startMarker={index:0,line:this.scanner.lineNumber,column:0},this.lastMarker={index:0,line:this.scanner.lineNumber,column:0},this.nextToken(),this.lastMarker={index:this.scanner.index,line:this.scanner.lineNumber,column:this.scanner.index-this.scanner.lineStart}}return e.prototype.throwError=function(e){for(var t=[],i=1;i0&&this.delegate)for(var t=0;t>="===e||">>>="===e||"&="===e||"^="===e||"|="===e},e.prototype.isolateCoverGrammar=function(e){var t=this.context.isBindingElement,i=this.context.isAssignmentTarget,s=this.context.firstCoverInitializedNameError;this.context.isBindingElement=!0,this.context.isAssignmentTarget=!0,this.context.firstCoverInitializedNameError=null;var r=e.call(this);return null!==this.context.firstCoverInitializedNameError&&this.throwUnexpectedToken(this.context.firstCoverInitializedNameError),this.context.isBindingElement=t,this.context.isAssignmentTarget=i,this.context.firstCoverInitializedNameError=s,r},e.prototype.inheritCoverGrammar=function(e){var t=this.context.isBindingElement,i=this.context.isAssignmentTarget,s=this.context.firstCoverInitializedNameError;this.context.isBindingElement=!0,this.context.isAssignmentTarget=!0,this.context.firstCoverInitializedNameError=null;var r=e.call(this);return this.context.isBindingElement=this.context.isBindingElement&&t,this.context.isAssignmentTarget=this.context.isAssignmentTarget&&i,this.context.firstCoverInitializedNameError=s||this.context.firstCoverInitializedNameError,r},e.prototype.consumeSemicolon=function(){this.match(";")?this.nextToken():this.hasLineTerminator||(2===this.lookahead.type||this.match("}")||this.throwUnexpectedToken(this.lookahead),this.lastMarker.index=this.startMarker.index,this.lastMarker.line=this.startMarker.line,this.lastMarker.column=this.startMarker.column)},e.prototype.parsePrimaryExpression=function(){var e,t,i,s=this.createNode();switch(this.lookahead.type){case 3:(this.context.isModule||this.context.await)&&"await"===this.lookahead.value&&this.tolerateUnexpectedToken(this.lookahead),e=this.matchAsyncFunction()?this.parseFunctionExpression():this.finalize(s,new o.Identifier(this.nextToken().value));break;case 6:case 8:this.context.strict&&this.lookahead.octal&&this.tolerateUnexpectedToken(this.lookahead,n.Messages.StrictOctalLiteral),this.context.isAssignmentTarget=!1,this.context.isBindingElement=!1,t=this.nextToken(),i=this.getTokenRaw(t),e=this.finalize(s,new o.Literal(t.value,i));break;case 1:this.context.isAssignmentTarget=!1,this.context.isBindingElement=!1,t=this.nextToken(),i=this.getTokenRaw(t),e=this.finalize(s,new o.Literal("true"===t.value,i));break;case 5:this.context.isAssignmentTarget=!1,this.context.isBindingElement=!1,t=this.nextToken(),i=this.getTokenRaw(t),e=this.finalize(s,new o.Literal(null,i));break;case 10:e=this.parseTemplateLiteral();break;case 7:switch(this.lookahead.value){case"(":this.context.isBindingElement=!1,e=this.inheritCoverGrammar(this.parseGroupExpression);break;case"[":e=this.inheritCoverGrammar(this.parseArrayInitializer);break;case"{":e=this.inheritCoverGrammar(this.parseObjectInitializer);break;case"/":case"/=":this.context.isAssignmentTarget=!1,this.context.isBindingElement=!1,this.scanner.index=this.startMarker.index,t=this.nextRegexToken(),i=this.getTokenRaw(t),e=this.finalize(s,new o.RegexLiteral(t.regex,i,t.pattern,t.flags));break;default:e=this.throwUnexpectedToken(this.nextToken())}break;case 4:!this.context.strict&&this.context.allowYield&&this.matchKeyword("yield")?e=this.parseIdentifierName():!this.context.strict&&this.matchKeyword("let")?e=this.finalize(s,new o.Identifier(this.nextToken().value)):(this.context.isAssignmentTarget=!1,this.context.isBindingElement=!1,this.matchKeyword("function")?e=this.parseFunctionExpression():this.matchKeyword("this")?(this.nextToken(),e=this.finalize(s,new o.ThisExpression)):e=this.matchKeyword("class")?this.parseClassExpression():this.throwUnexpectedToken(this.nextToken()));break;default:e=this.throwUnexpectedToken(this.nextToken())}return e},e.prototype.parseSpreadElement=function(){var e=this.createNode();this.expect("...");var t=this.inheritCoverGrammar(this.parseAssignmentExpression);return this.finalize(e,new o.SpreadElement(t))},e.prototype.parseArrayInitializer=function(){var e=this.createNode(),t=[];for(this.expect("[");!this.match("]");)if(this.match(","))this.nextToken(),t.push(null);else if(this.match("...")){var i=this.parseSpreadElement();this.match("]")||(this.context.isAssignmentTarget=!1,this.context.isBindingElement=!1,this.expect(",")),t.push(i)}else t.push(this.inheritCoverGrammar(this.parseAssignmentExpression)),this.match("]")||this.expect(",");return this.expect("]"),this.finalize(e,new o.ArrayExpression(t))},e.prototype.parsePropertyMethod=function(e){this.context.isAssignmentTarget=!1,this.context.isBindingElement=!1;var t=this.context.strict,i=this.context.allowStrictDirective;this.context.allowStrictDirective=e.simple;var s=this.isolateCoverGrammar(this.parseFunctionSourceElements);return this.context.strict&&e.firstRestricted&&this.tolerateUnexpectedToken(e.firstRestricted,e.message),this.context.strict&&e.stricted&&this.tolerateUnexpectedToken(e.stricted,e.message),this.context.strict=t,this.context.allowStrictDirective=i,s},e.prototype.parsePropertyMethodFunction=function(){var e=this.createNode(),t=this.context.allowYield;this.context.allowYield=!0;var i=this.parseFormalParameters(),s=this.parsePropertyMethod(i);return this.context.allowYield=t,this.finalize(e,new o.FunctionExpression(null,i.params,s,!1))},e.prototype.parsePropertyMethodAsyncFunction=function(){var e=this.createNode(),t=this.context.allowYield,i=this.context.await;this.context.allowYield=!1,this.context.await=!0;var s=this.parseFormalParameters(),r=this.parsePropertyMethod(s);return this.context.allowYield=t,this.context.await=i,this.finalize(e,new o.AsyncFunctionExpression(null,s.params,r))},e.prototype.parseObjectPropertyKey=function(){var e,t=this.createNode(),i=this.nextToken();switch(i.type){case 8:case 6:this.context.strict&&i.octal&&this.tolerateUnexpectedToken(i,n.Messages.StrictOctalLiteral);var s=this.getTokenRaw(i);e=this.finalize(t,new o.Literal(i.value,s));break;case 3:case 1:case 5:case 4:e=this.finalize(t,new o.Identifier(i.value));break;case 7:"["===i.value?(e=this.isolateCoverGrammar(this.parseAssignmentExpression),this.expect("]")):e=this.throwUnexpectedToken(i);break;default:e=this.throwUnexpectedToken(i)}return e},e.prototype.isPropertyKey=function(e,t){return e.type===c.Syntax.Identifier&&e.name===t||e.type===c.Syntax.Literal&&e.value===t},e.prototype.parseObjectProperty=function(e){var t,i=this.createNode(),s=this.lookahead,r=null,a=null,c=!1,l=!1,p=!1,A=!1;if(3===s.type){var u=s.value;this.nextToken(),c=this.match("["),r=(A=!(this.hasLineTerminator||"async"!==u||this.match(":")||this.match("(")||this.match("*")||this.match(",")))?this.parseObjectPropertyKey():this.finalize(i,new o.Identifier(u))}else this.match("*")?this.nextToken():(c=this.match("["),r=this.parseObjectPropertyKey());var d=this.qualifiedPropertyName(this.lookahead);if(3===s.type&&!A&&"get"===s.value&&d)t="get",c=this.match("["),r=this.parseObjectPropertyKey(),this.context.allowYield=!1,a=this.parseGetterMethod();else if(3===s.type&&!A&&"set"===s.value&&d)t="set",c=this.match("["),r=this.parseObjectPropertyKey(),a=this.parseSetterMethod();else if(7===s.type&&"*"===s.value&&d)t="init",c=this.match("["),r=this.parseObjectPropertyKey(),a=this.parseGeneratorMethod(),l=!0;else if(r||this.throwUnexpectedToken(this.lookahead),t="init",this.match(":")&&!A)!c&&this.isPropertyKey(r,"__proto__")&&(e.value&&this.tolerateError(n.Messages.DuplicateProtoProperty),e.value=!0),this.nextToken(),a=this.inheritCoverGrammar(this.parseAssignmentExpression);else if(this.match("("))a=A?this.parsePropertyMethodAsyncFunction():this.parsePropertyMethodFunction(),l=!0;else if(3===s.type)if(u=this.finalize(i,new o.Identifier(s.value)),this.match("=")){this.context.firstCoverInitializedNameError=this.lookahead,this.nextToken(),p=!0;var h=this.isolateCoverGrammar(this.parseAssignmentExpression);a=this.finalize(i,new o.AssignmentPattern(u,h))}else p=!0,a=u;else this.throwUnexpectedToken(this.nextToken());return this.finalize(i,new o.Property(t,r,c,a,l,p))},e.prototype.parseObjectInitializer=function(){var e=this.createNode();this.expect("{");for(var t=[],i={value:!1};!this.match("}");)t.push(this.parseObjectProperty(i)),this.match("}")||this.expectCommaSeparator();return this.expect("}"),this.finalize(e,new o.ObjectExpression(t))},e.prototype.parseTemplateHead=function(){s.assert(this.lookahead.head,"Template literal must start with a template head");var e=this.createNode(),t=this.nextToken(),i=t.value,r=t.cooked;return this.finalize(e,new o.TemplateElement({raw:i,cooked:r},t.tail))},e.prototype.parseTemplateElement=function(){10!==this.lookahead.type&&this.throwUnexpectedToken();var e=this.createNode(),t=this.nextToken(),i=t.value,s=t.cooked;return this.finalize(e,new o.TemplateElement({raw:i,cooked:s},t.tail))},e.prototype.parseTemplateLiteral=function(){var e=this.createNode(),t=[],i=[],s=this.parseTemplateHead();for(i.push(s);!s.tail;)t.push(this.parseExpression()),s=this.parseTemplateElement(),i.push(s);return this.finalize(e,new o.TemplateLiteral(i,t))},e.prototype.reinterpretExpressionAsPattern=function(e){switch(e.type){case c.Syntax.Identifier:case c.Syntax.MemberExpression:case c.Syntax.RestElement:case c.Syntax.AssignmentPattern:break;case c.Syntax.SpreadElement:e.type=c.Syntax.RestElement,this.reinterpretExpressionAsPattern(e.argument);break;case c.Syntax.ArrayExpression:e.type=c.Syntax.ArrayPattern;for(var t=0;t")||this.expect("=>"),e={type:p,params:[],async:!1};else{var t=this.lookahead,i=[];if(this.match("..."))e=this.parseRestElement(i),this.expect(")"),this.match("=>")||this.expect("=>"),e={type:p,params:[e],async:!1};else{var s=!1;if(this.context.isBindingElement=!0,e=this.inheritCoverGrammar(this.parseAssignmentExpression),this.match(",")){var r=[];for(this.context.isAssignmentTarget=!1,r.push(e);2!==this.lookahead.type&&this.match(",");){if(this.nextToken(),this.match(")")){this.nextToken();for(var n=0;n")||this.expect("=>"),this.context.isBindingElement=!1,n=0;n")&&(e.type===c.Syntax.Identifier&&"yield"===e.name&&(s=!0,e={type:p,params:[e],async:!1}),!s)){if(this.context.isBindingElement||this.throwUnexpectedToken(this.lookahead),e.type===c.Syntax.SequenceExpression)for(n=0;n")){for(var c=0;c0){this.nextToken(),this.context.isAssignmentTarget=!1,this.context.isBindingElement=!1;for(var r=[e,this.lookahead],n=t,a=this.isolateCoverGrammar(this.parseExponentiationExpression),c=[n,i.value,a],l=[s];!((s=this.binaryPrecedence(this.lookahead))<=0);){for(;c.length>2&&s<=l[l.length-1];){a=c.pop();var p=c.pop();l.pop(),n=c.pop(),r.pop();var A=this.startNode(r[r.length-1]);c.push(this.finalize(A,new o.BinaryExpression(p,n,a)))}c.push(this.nextToken().value),l.push(s),r.push(this.lookahead),c.push(this.isolateCoverGrammar(this.parseExponentiationExpression))}var u=c.length-1;t=c[u];for(var d=r.pop();u>1;){var h=r.pop(),m=d&&d.lineStart;A=this.startNode(h,m),p=c[u-1],t=this.finalize(A,new o.BinaryExpression(p,c[u-2],t)),u-=2,d=h}}return t},e.prototype.parseConditionalExpression=function(){var e=this.lookahead,t=this.inheritCoverGrammar(this.parseBinaryExpression);if(this.match("?")){this.nextToken();var i=this.context.allowIn;this.context.allowIn=!0;var s=this.isolateCoverGrammar(this.parseAssignmentExpression);this.context.allowIn=i,this.expect(":");var r=this.isolateCoverGrammar(this.parseAssignmentExpression);t=this.finalize(this.startNode(e),new o.ConditionalExpression(t,s,r)),this.context.isAssignmentTarget=!1,this.context.isBindingElement=!1}return t},e.prototype.checkPatternParam=function(e,t){switch(t.type){case c.Syntax.Identifier:this.validateParam(e,t,t.name);break;case c.Syntax.RestElement:this.checkPatternParam(e,t.argument);break;case c.Syntax.AssignmentPattern:this.checkPatternParam(e,t.left);break;case c.Syntax.ArrayPattern:for(var i=0;i")){this.context.isAssignmentTarget=!1,this.context.isBindingElement=!1;var r=e.async,a=this.reinterpretAsCoverFormalsList(e);if(a){this.hasLineTerminator&&this.tolerateUnexpectedToken(this.lookahead),this.context.firstCoverInitializedNameError=null;var l=this.context.strict,A=this.context.allowStrictDirective;this.context.allowStrictDirective=a.simple;var u=this.context.allowYield,d=this.context.await;this.context.allowYield=!0,this.context.await=r;var h=this.startNode(t);this.expect("=>");var m=void 0;if(this.match("{")){var g=this.context.allowIn;this.context.allowIn=!0,m=this.parseFunctionSourceElements(),this.context.allowIn=g}else m=this.isolateCoverGrammar(this.parseAssignmentExpression);var f=m.type!==c.Syntax.BlockStatement;this.context.strict&&a.firstRestricted&&this.throwUnexpectedToken(a.firstRestricted,a.message),this.context.strict&&a.stricted&&this.tolerateUnexpectedToken(a.stricted,a.message),e=r?this.finalize(h,new o.AsyncArrowFunctionExpression(a.params,m,f)):this.finalize(h,new o.ArrowFunctionExpression(a.params,m,f)),this.context.strict=l,this.context.allowStrictDirective=A,this.context.allowYield=u,this.context.await=d}}else if(this.matchAssign()){if(this.context.isAssignmentTarget||this.tolerateError(n.Messages.InvalidLHSInAssignment),this.context.strict&&e.type===c.Syntax.Identifier){var E=e;this.scanner.isRestrictedWord(E.name)&&this.tolerateUnexpectedToken(i,n.Messages.StrictLHSAssignment),this.scanner.isStrictModeReservedWord(E.name)&&this.tolerateUnexpectedToken(i,n.Messages.StrictReservedWord)}this.match("=")?this.reinterpretExpressionAsPattern(e):(this.context.isAssignmentTarget=!1,this.context.isBindingElement=!1);var C=(i=this.nextToken()).value,y=this.isolateCoverGrammar(this.parseAssignmentExpression);e=this.finalize(this.startNode(t),new o.AssignmentExpression(C,e,y)),this.context.firstCoverInitializedNameError=null}}return e},e.prototype.parseExpression=function(){var e=this.lookahead,t=this.isolateCoverGrammar(this.parseAssignmentExpression);if(this.match(",")){var i=[];for(i.push(t);2!==this.lookahead.type&&this.match(",");)this.nextToken(),i.push(this.isolateCoverGrammar(this.parseAssignmentExpression));t=this.finalize(this.startNode(e),new o.SequenceExpression(i))}return t},e.prototype.parseStatementListItem=function(){var e;if(this.context.isAssignmentTarget=!0,this.context.isBindingElement=!0,4===this.lookahead.type)switch(this.lookahead.value){case"export":this.context.isModule||this.tolerateUnexpectedToken(this.lookahead,n.Messages.IllegalExportDeclaration),e=this.parseExportDeclaration();break;case"import":this.context.isModule||this.tolerateUnexpectedToken(this.lookahead,n.Messages.IllegalImportDeclaration),e=this.parseImportDeclaration();break;case"const":e=this.parseLexicalDeclaration({inFor:!1});break;case"function":e=this.parseFunctionDeclaration();break;case"class":e=this.parseClassDeclaration();break;case"let":e=this.isLexicalDeclaration()?this.parseLexicalDeclaration({inFor:!1}):this.parseStatement();break;default:e=this.parseStatement()}else e=this.parseStatement();return e},e.prototype.parseBlock=function(){var e=this.createNode();this.expect("{");for(var t=[];!this.match("}");)t.push(this.parseStatementListItem());return this.expect("}"),this.finalize(e,new o.BlockStatement(t))},e.prototype.parseLexicalBinding=function(e,t){var i=this.createNode(),s=this.parsePattern([],e);this.context.strict&&s.type===c.Syntax.Identifier&&this.scanner.isRestrictedWord(s.name)&&this.tolerateError(n.Messages.StrictVarName);var r=null;return"const"===e?this.matchKeyword("in")||this.matchContextualKeyword("of")||(this.match("=")?(this.nextToken(),r=this.isolateCoverGrammar(this.parseAssignmentExpression)):this.throwError(n.Messages.DeclarationMissingInitializer,"const")):(!t.inFor&&s.type!==c.Syntax.Identifier||this.match("="))&&(this.expect("="),r=this.isolateCoverGrammar(this.parseAssignmentExpression)),this.finalize(i,new o.VariableDeclarator(s,r))},e.prototype.parseBindingList=function(e,t){for(var i=[this.parseLexicalBinding(e,t)];this.match(",");)this.nextToken(),i.push(this.parseLexicalBinding(e,t));return i},e.prototype.isLexicalDeclaration=function(){var e=this.scanner.saveState();this.scanner.scanComments();var t=this.scanner.lex();return this.scanner.restoreState(e),3===t.type||7===t.type&&"["===t.value||7===t.type&&"{"===t.value||4===t.type&&"let"===t.value||4===t.type&&"yield"===t.value},e.prototype.parseLexicalDeclaration=function(e){var t=this.createNode(),i=this.nextToken().value;s.assert("let"===i||"const"===i,"Lexical declaration must be either let or const");var r=this.parseBindingList(i,e);return this.consumeSemicolon(),this.finalize(t,new o.VariableDeclaration(r,i))},e.prototype.parseBindingRestElement=function(e,t){var i=this.createNode();this.expect("...");var s=this.parsePattern(e,t);return this.finalize(i,new o.RestElement(s))},e.prototype.parseArrayPattern=function(e,t){var i=this.createNode();this.expect("[");for(var s=[];!this.match("]");)if(this.match(","))this.nextToken(),s.push(null);else{if(this.match("...")){s.push(this.parseBindingRestElement(e,t));break}s.push(this.parsePatternWithDefault(e,t)),this.match("]")||this.expect(",")}return this.expect("]"),this.finalize(i,new o.ArrayPattern(s))},e.prototype.parsePropertyPattern=function(e,t){var i,s,r=this.createNode(),n=!1,a=!1;if(3===this.lookahead.type){var c=this.lookahead;i=this.parseVariableIdentifier();var l=this.finalize(r,new o.Identifier(c.value));if(this.match("=")){e.push(c),a=!0,this.nextToken();var p=this.parseAssignmentExpression();s=this.finalize(this.startNode(c),new o.AssignmentPattern(l,p))}else this.match(":")?(this.expect(":"),s=this.parsePatternWithDefault(e,t)):(e.push(c),a=!0,s=l)}else n=this.match("["),i=this.parseObjectPropertyKey(),this.expect(":"),s=this.parsePatternWithDefault(e,t);return this.finalize(r,new o.Property("init",i,n,s,!1,a))},e.prototype.parseObjectPattern=function(e,t){var i=this.createNode(),s=[];for(this.expect("{");!this.match("}");)s.push(this.parsePropertyPattern(e,t)),this.match("}")||this.expect(",");return this.expect("}"),this.finalize(i,new o.ObjectPattern(s))},e.prototype.parsePattern=function(e,t){var i;return this.match("[")?i=this.parseArrayPattern(e,t):this.match("{")?i=this.parseObjectPattern(e,t):(!this.matchKeyword("let")||"const"!==t&&"let"!==t||this.tolerateUnexpectedToken(this.lookahead,n.Messages.LetInLexicalBinding),e.push(this.lookahead),i=this.parseVariableIdentifier(t)),i},e.prototype.parsePatternWithDefault=function(e,t){var i=this.lookahead,s=this.parsePattern(e,t);if(this.match("=")){this.nextToken();var r=this.context.allowYield;this.context.allowYield=!0;var n=this.isolateCoverGrammar(this.parseAssignmentExpression);this.context.allowYield=r,s=this.finalize(this.startNode(i),new o.AssignmentPattern(s,n))}return s},e.prototype.parseVariableIdentifier=function(e){var t=this.createNode(),i=this.nextToken();return 4===i.type&&"yield"===i.value?this.context.strict?this.tolerateUnexpectedToken(i,n.Messages.StrictReservedWord):this.context.allowYield||this.throwUnexpectedToken(i):3!==i.type?this.context.strict&&4===i.type&&this.scanner.isStrictModeReservedWord(i.value)?this.tolerateUnexpectedToken(i,n.Messages.StrictReservedWord):(this.context.strict||"let"!==i.value||"var"!==e)&&this.throwUnexpectedToken(i):(this.context.isModule||this.context.await)&&3===i.type&&"await"===i.value&&this.tolerateUnexpectedToken(i),this.finalize(t,new o.Identifier(i.value))},e.prototype.parseVariableDeclaration=function(e){var t=this.createNode(),i=this.parsePattern([],"var");this.context.strict&&i.type===c.Syntax.Identifier&&this.scanner.isRestrictedWord(i.name)&&this.tolerateError(n.Messages.StrictVarName);var s=null;return this.match("=")?(this.nextToken(),s=this.isolateCoverGrammar(this.parseAssignmentExpression)):i.type===c.Syntax.Identifier||e.inFor||this.expect("="),this.finalize(t,new o.VariableDeclarator(i,s))},e.prototype.parseVariableDeclarationList=function(e){var t={inFor:e.inFor},i=[];for(i.push(this.parseVariableDeclaration(t));this.match(",");)this.nextToken(),i.push(this.parseVariableDeclaration(t));return i},e.prototype.parseVariableStatement=function(){var e=this.createNode();this.expectKeyword("var");var t=this.parseVariableDeclarationList({inFor:!1});return this.consumeSemicolon(),this.finalize(e,new o.VariableDeclaration(t,"var"))},e.prototype.parseEmptyStatement=function(){var e=this.createNode();return this.expect(";"),this.finalize(e,new o.EmptyStatement)},e.prototype.parseExpressionStatement=function(){var e=this.createNode(),t=this.parseExpression();return this.consumeSemicolon(),this.finalize(e,new o.ExpressionStatement(t))},e.prototype.parseIfClause=function(){return this.context.strict&&this.matchKeyword("function")&&this.tolerateError(n.Messages.StrictFunction),this.parseStatement()},e.prototype.parseIfStatement=function(){var e,t=this.createNode(),i=null;this.expectKeyword("if"),this.expect("(");var s=this.parseExpression();return!this.match(")")&&this.config.tolerant?(this.tolerateUnexpectedToken(this.nextToken()),e=this.finalize(this.createNode(),new o.EmptyStatement)):(this.expect(")"),e=this.parseIfClause(),this.matchKeyword("else")&&(this.nextToken(),i=this.parseIfClause())),this.finalize(t,new o.IfStatement(s,e,i))},e.prototype.parseDoWhileStatement=function(){var e=this.createNode();this.expectKeyword("do");var t=this.context.inIteration;this.context.inIteration=!0;var i=this.parseStatement();this.context.inIteration=t,this.expectKeyword("while"),this.expect("(");var s=this.parseExpression();return!this.match(")")&&this.config.tolerant?this.tolerateUnexpectedToken(this.nextToken()):(this.expect(")"),this.match(";")&&this.nextToken()),this.finalize(e,new o.DoWhileStatement(i,s))},e.prototype.parseWhileStatement=function(){var e,t=this.createNode();this.expectKeyword("while"),this.expect("(");var i=this.parseExpression();if(!this.match(")")&&this.config.tolerant)this.tolerateUnexpectedToken(this.nextToken()),e=this.finalize(this.createNode(),new o.EmptyStatement);else{this.expect(")");var s=this.context.inIteration;this.context.inIteration=!0,e=this.parseStatement(),this.context.inIteration=s}return this.finalize(t,new o.WhileStatement(i,e))},e.prototype.parseForStatement=function(){var e,t,i,s=null,r=null,a=null,l=!0,p=this.createNode();if(this.expectKeyword("for"),this.expect("("),this.match(";"))this.nextToken();else if(this.matchKeyword("var")){s=this.createNode(),this.nextToken();var A=this.context.allowIn;this.context.allowIn=!1;var u=this.parseVariableDeclarationList({inFor:!0});if(this.context.allowIn=A,1===u.length&&this.matchKeyword("in")){var d=u[0];d.init&&(d.id.type===c.Syntax.ArrayPattern||d.id.type===c.Syntax.ObjectPattern||this.context.strict)&&this.tolerateError(n.Messages.ForInOfLoopInitializer,"for-in"),s=this.finalize(s,new o.VariableDeclaration(u,"var")),this.nextToken(),e=s,t=this.parseExpression(),s=null}else 1===u.length&&null===u[0].init&&this.matchContextualKeyword("of")?(s=this.finalize(s,new o.VariableDeclaration(u,"var")),this.nextToken(),e=s,t=this.parseAssignmentExpression(),s=null,l=!1):(s=this.finalize(s,new o.VariableDeclaration(u,"var")),this.expect(";"))}else if(this.matchKeyword("const")||this.matchKeyword("let")){s=this.createNode();var h=this.nextToken().value;this.context.strict||"in"!==this.lookahead.value?(A=this.context.allowIn,this.context.allowIn=!1,u=this.parseBindingList(h,{inFor:!0}),this.context.allowIn=A,1===u.length&&null===u[0].init&&this.matchKeyword("in")?(s=this.finalize(s,new o.VariableDeclaration(u,h)),this.nextToken(),e=s,t=this.parseExpression(),s=null):1===u.length&&null===u[0].init&&this.matchContextualKeyword("of")?(s=this.finalize(s,new o.VariableDeclaration(u,h)),this.nextToken(),e=s,t=this.parseAssignmentExpression(),s=null,l=!1):(this.consumeSemicolon(),s=this.finalize(s,new o.VariableDeclaration(u,h)))):(s=this.finalize(s,new o.Identifier(h)),this.nextToken(),e=s,t=this.parseExpression(),s=null)}else{var m=this.lookahead;if(A=this.context.allowIn,this.context.allowIn=!1,s=this.inheritCoverGrammar(this.parseAssignmentExpression),this.context.allowIn=A,this.matchKeyword("in"))this.context.isAssignmentTarget&&s.type!==c.Syntax.AssignmentExpression||this.tolerateError(n.Messages.InvalidLHSInForIn),this.nextToken(),this.reinterpretExpressionAsPattern(s),e=s,t=this.parseExpression(),s=null;else if(this.matchContextualKeyword("of"))this.context.isAssignmentTarget&&s.type!==c.Syntax.AssignmentExpression||this.tolerateError(n.Messages.InvalidLHSInForLoop),this.nextToken(),this.reinterpretExpressionAsPattern(s),e=s,t=this.parseAssignmentExpression(),s=null,l=!1;else{if(this.match(",")){for(var g=[s];this.match(",");)this.nextToken(),g.push(this.isolateCoverGrammar(this.parseAssignmentExpression));s=this.finalize(this.startNode(m),new o.SequenceExpression(g))}this.expect(";")}}if(void 0===e&&(this.match(";")||(r=this.parseExpression()),this.expect(";"),this.match(")")||(a=this.parseExpression())),!this.match(")")&&this.config.tolerant)this.tolerateUnexpectedToken(this.nextToken()),i=this.finalize(this.createNode(),new o.EmptyStatement);else{this.expect(")");var f=this.context.inIteration;this.context.inIteration=!0,i=this.isolateCoverGrammar(this.parseStatement),this.context.inIteration=f}return void 0===e?this.finalize(p,new o.ForStatement(s,r,a,i)):l?this.finalize(p,new o.ForInStatement(e,t,i)):this.finalize(p,new o.ForOfStatement(e,t,i))},e.prototype.parseContinueStatement=function(){var e=this.createNode();this.expectKeyword("continue");var t=null;if(3===this.lookahead.type&&!this.hasLineTerminator){var i=this.parseVariableIdentifier();t=i;var s="$"+i.name;Object.prototype.hasOwnProperty.call(this.context.labelSet,s)||this.throwError(n.Messages.UnknownLabel,i.name)}return this.consumeSemicolon(),null!==t||this.context.inIteration||this.throwError(n.Messages.IllegalContinue),this.finalize(e,new o.ContinueStatement(t))},e.prototype.parseBreakStatement=function(){var e=this.createNode();this.expectKeyword("break");var t=null;if(3===this.lookahead.type&&!this.hasLineTerminator){var i=this.parseVariableIdentifier(),s="$"+i.name;Object.prototype.hasOwnProperty.call(this.context.labelSet,s)||this.throwError(n.Messages.UnknownLabel,i.name),t=i}return this.consumeSemicolon(),null!==t||this.context.inIteration||this.context.inSwitch||this.throwError(n.Messages.IllegalBreak),this.finalize(e,new o.BreakStatement(t))},e.prototype.parseReturnStatement=function(){this.context.inFunctionBody||this.tolerateError(n.Messages.IllegalReturn);var e=this.createNode();this.expectKeyword("return");var t=(this.match(";")||this.match("}")||this.hasLineTerminator||2===this.lookahead.type)&&8!==this.lookahead.type&&10!==this.lookahead.type?null:this.parseExpression();return this.consumeSemicolon(),this.finalize(e,new o.ReturnStatement(t))},e.prototype.parseWithStatement=function(){this.context.strict&&this.tolerateError(n.Messages.StrictModeWith);var e,t=this.createNode();this.expectKeyword("with"),this.expect("(");var i=this.parseExpression();return!this.match(")")&&this.config.tolerant?(this.tolerateUnexpectedToken(this.nextToken()),e=this.finalize(this.createNode(),new o.EmptyStatement)):(this.expect(")"),e=this.parseStatement()),this.finalize(t,new o.WithStatement(i,e))},e.prototype.parseSwitchCase=function(){var e,t=this.createNode();this.matchKeyword("default")?(this.nextToken(),e=null):(this.expectKeyword("case"),e=this.parseExpression()),this.expect(":");for(var i=[];!(this.match("}")||this.matchKeyword("default")||this.matchKeyword("case"));)i.push(this.parseStatementListItem());return this.finalize(t,new o.SwitchCase(e,i))},e.prototype.parseSwitchStatement=function(){var e=this.createNode();this.expectKeyword("switch"),this.expect("(");var t=this.parseExpression();this.expect(")");var i=this.context.inSwitch;this.context.inSwitch=!0;var s=[],r=!1;for(this.expect("{");!this.match("}");){var a=this.parseSwitchCase();null===a.test&&(r&&this.throwError(n.Messages.MultipleDefaultsInSwitch),r=!0),s.push(a)}return this.expect("}"),this.context.inSwitch=i,this.finalize(e,new o.SwitchStatement(t,s))},e.prototype.parseLabelledStatement=function(){var e,t=this.createNode(),i=this.parseExpression();if(i.type===c.Syntax.Identifier&&this.match(":")){this.nextToken();var s=i,r="$"+s.name;Object.prototype.hasOwnProperty.call(this.context.labelSet,r)&&this.throwError(n.Messages.Redeclaration,"Label",s.name),this.context.labelSet[r]=!0;var a=void 0;if(this.matchKeyword("class"))this.tolerateUnexpectedToken(this.lookahead),a=this.parseClassDeclaration();else if(this.matchKeyword("function")){var l=this.lookahead,p=this.parseFunctionDeclaration();this.context.strict?this.tolerateUnexpectedToken(l,n.Messages.StrictFunction):p.generator&&this.tolerateUnexpectedToken(l,n.Messages.GeneratorInLegacyContext),a=p}else a=this.parseStatement();delete this.context.labelSet[r],e=new o.LabeledStatement(s,a)}else this.consumeSemicolon(),e=new o.ExpressionStatement(i);return this.finalize(t,e)},e.prototype.parseThrowStatement=function(){var e=this.createNode();this.expectKeyword("throw"),this.hasLineTerminator&&this.throwError(n.Messages.NewlineAfterThrow);var t=this.parseExpression();return this.consumeSemicolon(),this.finalize(e,new o.ThrowStatement(t))},e.prototype.parseCatchClause=function(){var e=this.createNode();this.expectKeyword("catch"),this.expect("("),this.match(")")&&this.throwUnexpectedToken(this.lookahead);for(var t=[],i=this.parsePattern(t),s={},r=0;r0&&this.tolerateError(n.Messages.BadGetterArity);var s=this.parsePropertyMethod(i);return this.context.allowYield=t,this.finalize(e,new o.FunctionExpression(null,i.params,s,!1))},e.prototype.parseSetterMethod=function(){var e=this.createNode(),t=this.context.allowYield;this.context.allowYield=!0;var i=this.parseFormalParameters();1!==i.params.length?this.tolerateError(n.Messages.BadSetterArity):i.params[0]instanceof o.RestElement&&this.tolerateError(n.Messages.BadSetterRestParameter);var s=this.parsePropertyMethod(i);return this.context.allowYield=t,this.finalize(e,new o.FunctionExpression(null,i.params,s,!1))},e.prototype.parseGeneratorMethod=function(){var e=this.createNode(),t=this.context.allowYield;this.context.allowYield=!0;var i=this.parseFormalParameters();this.context.allowYield=!1;var s=this.parsePropertyMethod(i);return this.context.allowYield=t,this.finalize(e,new o.FunctionExpression(null,i.params,s,!0))},e.prototype.isStartOfExpression=function(){var e=!0,t=this.lookahead.value;switch(this.lookahead.type){case 7:e="["===t||"("===t||"{"===t||"+"===t||"-"===t||"!"===t||"~"===t||"++"===t||"--"===t||"/"===t||"/="===t;break;case 4:e="class"===t||"delete"===t||"function"===t||"let"===t||"new"===t||"super"===t||"this"===t||"typeof"===t||"void"===t||"yield"===t}return e},e.prototype.parseYieldExpression=function(){var e=this.createNode();this.expectKeyword("yield");var t=null,i=!1;if(!this.hasLineTerminator){var s=this.context.allowYield;this.context.allowYield=!1,(i=this.match("*"))?(this.nextToken(),t=this.parseAssignmentExpression()):this.isStartOfExpression()&&(t=this.parseAssignmentExpression()),this.context.allowYield=s}return this.finalize(e,new o.YieldExpression(t,i))},e.prototype.parseClassElement=function(e){var t=this.lookahead,i=this.createNode(),s="",r=null,a=null,c=!1,l=!1,p=!1,A=!1;if(this.match("*"))this.nextToken();else if(c=this.match("["),"static"===(r=this.parseObjectPropertyKey()).name&&(this.qualifiedPropertyName(this.lookahead)||this.match("*"))&&(t=this.lookahead,p=!0,c=this.match("["),this.match("*")?this.nextToken():r=this.parseObjectPropertyKey()),3===t.type&&!this.hasLineTerminator&&"async"===t.value){var u=this.lookahead.value;":"!==u&&"("!==u&&"*"!==u&&(A=!0,t=this.lookahead,r=this.parseObjectPropertyKey(),3===t.type&&"constructor"===t.value&&this.tolerateUnexpectedToken(t,n.Messages.ConstructorIsAsync))}var d=this.qualifiedPropertyName(this.lookahead);return 3===t.type?"get"===t.value&&d?(s="get",c=this.match("["),r=this.parseObjectPropertyKey(),this.context.allowYield=!1,a=this.parseGetterMethod()):"set"===t.value&&d&&(s="set",c=this.match("["),r=this.parseObjectPropertyKey(),a=this.parseSetterMethod()):7===t.type&&"*"===t.value&&d&&(s="init",c=this.match("["),r=this.parseObjectPropertyKey(),a=this.parseGeneratorMethod(),l=!0),!s&&r&&this.match("(")&&(s="init",a=A?this.parsePropertyMethodAsyncFunction():this.parsePropertyMethodFunction(),l=!0),s||this.throwUnexpectedToken(this.lookahead),"init"===s&&(s="method"),c||(p&&this.isPropertyKey(r,"prototype")&&this.throwUnexpectedToken(t,n.Messages.StaticPrototype),!p&&this.isPropertyKey(r,"constructor")&&(("method"!==s||!l||a&&a.generator)&&this.throwUnexpectedToken(t,n.Messages.ConstructorSpecialMethod),e.value?this.throwUnexpectedToken(t,n.Messages.DuplicateConstructor):e.value=!0,s="constructor")),this.finalize(i,new o.MethodDefinition(r,c,a,s,p))},e.prototype.parseClassElementList=function(){var e=[],t={value:!1};for(this.expect("{");!this.match("}");)this.match(";")?this.nextToken():e.push(this.parseClassElement(t));return this.expect("}"),e},e.prototype.parseClassBody=function(){var e=this.createNode(),t=this.parseClassElementList();return this.finalize(e,new o.ClassBody(t))},e.prototype.parseClassDeclaration=function(e){var t=this.createNode(),i=this.context.strict;this.context.strict=!0,this.expectKeyword("class");var s=e&&3!==this.lookahead.type?null:this.parseVariableIdentifier(),r=null;this.matchKeyword("extends")&&(this.nextToken(),r=this.isolateCoverGrammar(this.parseLeftHandSideExpressionAllowCall));var n=this.parseClassBody();return this.context.strict=i,this.finalize(t,new o.ClassDeclaration(s,r,n))},e.prototype.parseClassExpression=function(){var e=this.createNode(),t=this.context.strict;this.context.strict=!0,this.expectKeyword("class");var i=3===this.lookahead.type?this.parseVariableIdentifier():null,s=null;this.matchKeyword("extends")&&(this.nextToken(),s=this.isolateCoverGrammar(this.parseLeftHandSideExpressionAllowCall));var r=this.parseClassBody();return this.context.strict=t,this.finalize(e,new o.ClassExpression(i,s,r))},e.prototype.parseModule=function(){this.context.strict=!0,this.context.isModule=!0,this.scanner.isModule=!0;for(var e=this.createNode(),t=this.parseDirectivePrologues();2!==this.lookahead.type;)t.push(this.parseStatementListItem());return this.finalize(e,new o.Module(t))},e.prototype.parseScript=function(){for(var e=this.createNode(),t=this.parseDirectivePrologues();2!==this.lookahead.type;)t.push(this.parseStatementListItem());return this.finalize(e,new o.Script(t))},e.prototype.parseModuleSpecifier=function(){var e=this.createNode();8!==this.lookahead.type&&this.throwError(n.Messages.InvalidModuleSpecifier);var t=this.nextToken(),i=this.getTokenRaw(t);return this.finalize(e,new o.Literal(t.value,i))},e.prototype.parseImportSpecifier=function(){var e,t,i=this.createNode();return 3===this.lookahead.type?(t=e=this.parseVariableIdentifier(),this.matchContextualKeyword("as")&&(this.nextToken(),t=this.parseVariableIdentifier())):(t=e=this.parseIdentifierName(),this.matchContextualKeyword("as")?(this.nextToken(),t=this.parseVariableIdentifier()):this.throwUnexpectedToken(this.nextToken())),this.finalize(i,new o.ImportSpecifier(t,e))},e.prototype.parseNamedImports=function(){this.expect("{");for(var e=[];!this.match("}");)e.push(this.parseImportSpecifier()),this.match("}")||this.expect(",");return this.expect("}"),e},e.prototype.parseImportDefaultSpecifier=function(){var e=this.createNode(),t=this.parseIdentifierName();return this.finalize(e,new o.ImportDefaultSpecifier(t))},e.prototype.parseImportNamespaceSpecifier=function(){var e=this.createNode();this.expect("*"),this.matchContextualKeyword("as")||this.throwError(n.Messages.NoAsAfterImportNamespace),this.nextToken();var t=this.parseIdentifierName();return this.finalize(e,new o.ImportNamespaceSpecifier(t))},e.prototype.parseImportDeclaration=function(){this.context.inFunctionBody&&this.throwError(n.Messages.IllegalImportDeclaration);var e,t=this.createNode();this.expectKeyword("import");var i=[];if(8===this.lookahead.type)e=this.parseModuleSpecifier();else{if(this.match("{")?i=i.concat(this.parseNamedImports()):this.match("*")?i.push(this.parseImportNamespaceSpecifier()):this.isIdentifierName(this.lookahead)&&!this.matchKeyword("default")?(i.push(this.parseImportDefaultSpecifier()),this.match(",")&&(this.nextToken(),this.match("*")?i.push(this.parseImportNamespaceSpecifier()):this.match("{")?i=i.concat(this.parseNamedImports()):this.throwUnexpectedToken(this.lookahead))):this.throwUnexpectedToken(this.nextToken()),!this.matchContextualKeyword("from")){var s=this.lookahead.value?n.Messages.UnexpectedToken:n.Messages.MissingFromClause;this.throwError(s,this.lookahead.value)}this.nextToken(),e=this.parseModuleSpecifier()}return this.consumeSemicolon(),this.finalize(t,new o.ImportDeclaration(i,e))},e.prototype.parseExportSpecifier=function(){var e=this.createNode(),t=this.parseIdentifierName(),i=t;return this.matchContextualKeyword("as")&&(this.nextToken(),i=this.parseIdentifierName()),this.finalize(e,new o.ExportSpecifier(t,i))},e.prototype.parseExportDeclaration=function(){this.context.inFunctionBody&&this.throwError(n.Messages.IllegalExportDeclaration);var e,t=this.createNode();if(this.expectKeyword("export"),this.matchKeyword("default"))if(this.nextToken(),this.matchKeyword("function")){var i=this.parseFunctionDeclaration(!0);e=this.finalize(t,new o.ExportDefaultDeclaration(i))}else this.matchKeyword("class")?(i=this.parseClassDeclaration(!0),e=this.finalize(t,new o.ExportDefaultDeclaration(i))):this.matchContextualKeyword("async")?(i=this.matchAsyncFunction()?this.parseFunctionDeclaration(!0):this.parseAssignmentExpression(),e=this.finalize(t,new o.ExportDefaultDeclaration(i))):(this.matchContextualKeyword("from")&&this.throwError(n.Messages.UnexpectedToken,this.lookahead.value),i=this.match("{")?this.parseObjectInitializer():this.match("[")?this.parseArrayInitializer():this.parseAssignmentExpression(),this.consumeSemicolon(),e=this.finalize(t,new o.ExportDefaultDeclaration(i)));else if(this.match("*")){if(this.nextToken(),!this.matchContextualKeyword("from")){var s=this.lookahead.value?n.Messages.UnexpectedToken:n.Messages.MissingFromClause;this.throwError(s,this.lookahead.value)}this.nextToken();var r=this.parseModuleSpecifier();this.consumeSemicolon(),e=this.finalize(t,new o.ExportAllDeclaration(r))}else if(4===this.lookahead.type){switch(i=void 0,this.lookahead.value){case"let":case"const":i=this.parseLexicalDeclaration({inFor:!1});break;case"var":case"class":case"function":i=this.parseStatementListItem();break;default:this.throwUnexpectedToken(this.lookahead)}e=this.finalize(t,new o.ExportNamedDeclaration(i,[],null))}else if(this.matchAsyncFunction())i=this.parseFunctionDeclaration(),e=this.finalize(t,new o.ExportNamedDeclaration(i,[],null));else{var a=[],c=null,l=!1;for(this.expect("{");!this.match("}");)l=l||this.matchKeyword("default"),a.push(this.parseExportSpecifier()),this.match("}")||this.expect(",");this.expect("}"),this.matchContextualKeyword("from")?(this.nextToken(),c=this.parseModuleSpecifier(),this.consumeSemicolon()):l?(s=this.lookahead.value?n.Messages.UnexpectedToken:n.Messages.MissingFromClause,this.throwError(s,this.lookahead.value)):this.consumeSemicolon(),e=this.finalize(t,new o.ExportNamedDeclaration(null,a,c))}return e},e}();t.Parser=A},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.assert=function(e,t){if(!e)throw new Error("ASSERT: "+t)}},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(){this.errors=[],this.tolerant=!1}return e.prototype.recordError=function(e){this.errors.push(e)},e.prototype.tolerate=function(e){if(!this.tolerant)throw e;this.recordError(e)},e.prototype.constructError=function(e,t){var i=new Error(e);try{throw i}catch(e){Object.create&&Object.defineProperty&&(i=Object.create(e),Object.defineProperty(i,"column",{value:t}))}return i},e.prototype.createError=function(e,t,i,s){var r="Line "+t+": "+s,n=this.constructError(r,i);return n.index=e,n.lineNumber=t,n.description=s,n},e.prototype.throwError=function(e,t,i,s){throw this.createError(e,t,i,s)},e.prototype.tolerateError=function(e,t,i,s){var r=this.createError(e,t,i,s);if(!this.tolerant)throw r;this.recordError(r)},e}();t.ErrorHandler=i},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Messages={BadGetterArity:"Getter must not have any formal parameters",BadSetterArity:"Setter must have exactly one formal parameter",BadSetterRestParameter:"Setter function argument must not be a rest parameter",ConstructorIsAsync:"Class constructor may not be an async method",ConstructorSpecialMethod:"Class constructor may not be an accessor",DeclarationMissingInitializer:"Missing initializer in %0 declaration",DefaultRestParameter:"Unexpected token =",DuplicateBinding:"Duplicate binding %0",DuplicateConstructor:"A class may only have one constructor",DuplicateProtoProperty:"Duplicate __proto__ fields are not allowed in object literals",ForInOfLoopInitializer:"%0 loop variable declaration may not have an initializer",GeneratorInLegacyContext:"Generator declarations are not allowed in legacy contexts",IllegalBreak:"Illegal break statement",IllegalContinue:"Illegal continue statement",IllegalExportDeclaration:"Unexpected token",IllegalImportDeclaration:"Unexpected token",IllegalLanguageModeDirective:"Illegal 'use strict' directive in function with non-simple parameter list",IllegalReturn:"Illegal return statement",InvalidEscapedReservedWord:"Keyword must not contain escaped characters",InvalidHexEscapeSequence:"Invalid hexadecimal escape sequence",InvalidLHSInAssignment:"Invalid left-hand side in assignment",InvalidLHSInForIn:"Invalid left-hand side in for-in",InvalidLHSInForLoop:"Invalid left-hand side in for-loop",InvalidModuleSpecifier:"Unexpected token",InvalidRegExp:"Invalid regular expression",LetInLexicalBinding:"let is disallowed as a lexically bound name",MissingFromClause:"Unexpected token",MultipleDefaultsInSwitch:"More than one default clause in switch statement",NewlineAfterThrow:"Illegal newline after throw",NoAsAfterImportNamespace:"Unexpected token",NoCatchOrFinally:"Missing catch or finally after try",ParameterAfterRestParameter:"Rest parameter must be last formal parameter",Redeclaration:"%0 '%1' has already been declared",StaticPrototype:"Classes may not have static property named prototype",StrictCatchVariable:"Catch variable may not be eval or arguments in strict mode",StrictDelete:"Delete of an unqualified identifier in strict mode.",StrictFunction:"In strict mode code, functions can only be declared at top level or inside a block",StrictFunctionName:"Function name may not be eval or arguments in strict mode",StrictLHSAssignment:"Assignment to eval or arguments is not allowed in strict mode",StrictLHSPostfix:"Postfix increment/decrement may not have eval or arguments operand in strict mode",StrictLHSPrefix:"Prefix increment/decrement may not have eval or arguments operand in strict mode",StrictModeWith:"Strict mode code may not include a with statement",StrictOctalLiteral:"Octal literals are not allowed in strict mode.",StrictParamDupe:"Strict mode function may not have duplicate parameter names",StrictParamName:"Parameter name eval or arguments is not allowed in strict mode",StrictReservedWord:"Use of future reserved word in strict mode",StrictVarName:"Variable name may not be eval or arguments in strict mode",TemplateOctalLiteral:"Octal literals are not allowed in template strings.",UnexpectedEOS:"Unexpected end of input",UnexpectedIdentifier:"Unexpected identifier",UnexpectedNumber:"Unexpected number",UnexpectedReserved:"Unexpected reserved word",UnexpectedString:"Unexpected string",UnexpectedTemplate:"Unexpected quasi %0",UnexpectedToken:"Unexpected token %0",UnexpectedTokenIllegal:"Unexpected token ILLEGAL",UnknownLabel:"Undefined label '%0'",UnterminatedRegExp:"Invalid regular expression: missing /"}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var s=i(9),r=i(4),n=i(11);function o(e){return"0123456789abcdef".indexOf(e.toLowerCase())}function a(e){return"01234567".indexOf(e)}var c=function(){function e(e,t){this.source=e,this.errorHandler=t,this.trackComment=!1,this.isModule=!1,this.length=e.length,this.index=0,this.lineNumber=e.length>0?1:0,this.lineStart=0,this.curlyStack=[]}return e.prototype.saveState=function(){return{index:this.index,lineNumber:this.lineNumber,lineStart:this.lineStart}},e.prototype.restoreState=function(e){this.index=e.index,this.lineNumber=e.lineNumber,this.lineStart=e.lineStart},e.prototype.eof=function(){return this.index>=this.length},e.prototype.throwUnexpectedToken=function(e){return void 0===e&&(e=n.Messages.UnexpectedTokenIllegal),this.errorHandler.throwError(this.index,this.lineNumber,this.index-this.lineStart+1,e)},e.prototype.tolerateUnexpectedToken=function(e){void 0===e&&(e=n.Messages.UnexpectedTokenIllegal),this.errorHandler.tolerateError(this.index,this.lineNumber,this.index-this.lineStart+1,e)},e.prototype.skipSingleLineComment=function(e){var t,i,s=[];for(this.trackComment&&(s=[],t=this.index-e,i={start:{line:this.lineNumber,column:this.index-this.lineStart-e},end:{}});!this.eof();){var n=this.source.charCodeAt(this.index);if(++this.index,r.Character.isLineTerminator(n)){if(this.trackComment){i.end={line:this.lineNumber,column:this.index-this.lineStart-1};var o={multiLine:!1,slice:[t+e,this.index-1],range:[t,this.index-1],loc:i};s.push(o)}return 13===n&&10===this.source.charCodeAt(this.index)&&++this.index,++this.lineNumber,this.lineStart=this.index,s}}return this.trackComment&&(i.end={line:this.lineNumber,column:this.index-this.lineStart},o={multiLine:!1,slice:[t+e,this.index],range:[t,this.index],loc:i},s.push(o)),s},e.prototype.skipMultiLineComment=function(){var e,t,i=[];for(this.trackComment&&(i=[],e=this.index-2,t={start:{line:this.lineNumber,column:this.index-this.lineStart-2},end:{}});!this.eof();){var s=this.source.charCodeAt(this.index);if(r.Character.isLineTerminator(s))13===s&&10===this.source.charCodeAt(this.index+1)&&++this.index,++this.lineNumber,++this.index,this.lineStart=this.index;else if(42===s){if(47===this.source.charCodeAt(this.index+1)){if(this.index+=2,this.trackComment){t.end={line:this.lineNumber,column:this.index-this.lineStart};var n={multiLine:!0,slice:[e+2,this.index-2],range:[e,this.index],loc:t};i.push(n)}return i}++this.index}else++this.index}return this.trackComment&&(t.end={line:this.lineNumber,column:this.index-this.lineStart},n={multiLine:!0,slice:[e+2,this.index],range:[e,this.index],loc:t},i.push(n)),this.tolerateUnexpectedToken(),i},e.prototype.scanComments=function(){var e;this.trackComment&&(e=[]);for(var t=0===this.index;!this.eof();){var i=this.source.charCodeAt(this.index);if(r.Character.isWhiteSpace(i))++this.index;else if(r.Character.isLineTerminator(i))++this.index,13===i&&10===this.source.charCodeAt(this.index)&&++this.index,++this.lineNumber,this.lineStart=this.index,t=!0;else if(47===i)if(47===(i=this.source.charCodeAt(this.index+1))){this.index+=2;var s=this.skipSingleLineComment(2);this.trackComment&&(e=e.concat(s)),t=!0}else{if(42!==i)break;this.index+=2,s=this.skipMultiLineComment(),this.trackComment&&(e=e.concat(s))}else if(t&&45===i){if(45!==this.source.charCodeAt(this.index+1)||62!==this.source.charCodeAt(this.index+2))break;this.index+=3,s=this.skipSingleLineComment(3),this.trackComment&&(e=e.concat(s))}else{if(60!==i||this.isModule)break;if("!--"!==this.source.slice(this.index+1,this.index+4))break;this.index+=4,s=this.skipSingleLineComment(4),this.trackComment&&(e=e.concat(s))}}return e},e.prototype.isFutureReservedWord=function(e){switch(e){case"enum":case"export":case"import":case"super":return!0;default:return!1}},e.prototype.isStrictModeReservedWord=function(e){switch(e){case"implements":case"interface":case"package":case"private":case"protected":case"public":case"static":case"yield":case"let":return!0;default:return!1}},e.prototype.isRestrictedWord=function(e){return"eval"===e||"arguments"===e},e.prototype.isKeyword=function(e){switch(e.length){case 2:return"if"===e||"in"===e||"do"===e;case 3:return"var"===e||"for"===e||"new"===e||"try"===e||"let"===e;case 4:return"this"===e||"else"===e||"case"===e||"void"===e||"with"===e||"enum"===e;case 5:return"while"===e||"break"===e||"catch"===e||"throw"===e||"const"===e||"yield"===e||"class"===e||"super"===e;case 6:return"return"===e||"typeof"===e||"delete"===e||"switch"===e||"export"===e||"import"===e;case 7:return"default"===e||"finally"===e||"extends"===e;case 8:return"function"===e||"continue"===e||"debugger"===e;case 10:return"instanceof"===e;default:return!1}},e.prototype.codePointAt=function(e){var t=this.source.charCodeAt(e);if(t>=55296&&t<=56319){var i=this.source.charCodeAt(e+1);i>=56320&&i<=57343&&(t=1024*(t-55296)+i-56320+65536)}return t},e.prototype.scanHexEscape=function(e){for(var t="u"===e?4:2,i=0,s=0;s1114111||"}"!==e)&&this.throwUnexpectedToken(),r.Character.fromCodePoint(t)},e.prototype.getIdentifier=function(){for(var e=this.index++;!this.eof();){var t=this.source.charCodeAt(this.index);if(92===t)return this.index=e,this.getComplexIdentifier();if(t>=55296&&t<57343)return this.index=e,this.getComplexIdentifier();if(!r.Character.isIdentifierPart(t))break;++this.index}return this.source.slice(e,this.index)},e.prototype.getComplexIdentifier=function(){var e,t=this.codePointAt(this.index),i=r.Character.fromCodePoint(t);for(this.index+=i.length,92===t&&(117!==this.source.charCodeAt(this.index)&&this.throwUnexpectedToken(),++this.index,"{"===this.source[this.index]?(++this.index,e=this.scanUnicodeCodePointEscape()):null!==(e=this.scanHexEscape("u"))&&"\\"!==e&&r.Character.isIdentifierStart(e.charCodeAt(0))||this.throwUnexpectedToken(),i=e);!this.eof()&&(t=this.codePointAt(this.index),r.Character.isIdentifierPart(t));)i+=e=r.Character.fromCodePoint(t),this.index+=e.length,92===t&&(i=i.substr(0,i.length-1),117!==this.source.charCodeAt(this.index)&&this.throwUnexpectedToken(),++this.index,"{"===this.source[this.index]?(++this.index,e=this.scanUnicodeCodePointEscape()):null!==(e=this.scanHexEscape("u"))&&"\\"!==e&&r.Character.isIdentifierPart(e.charCodeAt(0))||this.throwUnexpectedToken(),i+=e);return i},e.prototype.octalToDecimal=function(e){var t="0"!==e,i=a(e);return!this.eof()&&r.Character.isOctalDigit(this.source.charCodeAt(this.index))&&(t=!0,i=8*i+a(this.source[this.index++]),"0123".indexOf(e)>=0&&!this.eof()&&r.Character.isOctalDigit(this.source.charCodeAt(this.index))&&(i=8*i+a(this.source[this.index++]))),{code:i,octal:t}},e.prototype.scanIdentifier=function(){var e,t=this.index,i=92===this.source.charCodeAt(t)?this.getComplexIdentifier():this.getIdentifier();if(3!=(e=1===i.length?3:this.isKeyword(i)?4:"null"===i?5:"true"===i||"false"===i?1:3)&&t+i.length!==this.index){var s=this.index;this.index=t,this.tolerateUnexpectedToken(n.Messages.InvalidEscapedReservedWord),this.index=s}return{type:e,value:i,lineNumber:this.lineNumber,lineStart:this.lineStart,start:t,end:this.index}},e.prototype.scanPunctuator=function(){var e=this.index,t=this.source[this.index];switch(t){case"(":case"{":"{"===t&&this.curlyStack.push("{"),++this.index;break;case".":++this.index,"."===this.source[this.index]&&"."===this.source[this.index+1]&&(this.index+=2,t="...");break;case"}":++this.index,this.curlyStack.pop();break;case")":case";":case",":case"[":case"]":case":":case"?":case"~":++this.index;break;default:">>>="===(t=this.source.substr(this.index,4))?this.index+=4:"==="===(t=t.substr(0,3))||"!=="===t||">>>"===t||"<<="===t||">>="===t||"**="===t?this.index+=3:"&&"===(t=t.substr(0,2))||"||"===t||"=="===t||"!="===t||"+="===t||"-="===t||"*="===t||"/="===t||"++"===t||"--"===t||"<<"===t||">>"===t||"&="===t||"|="===t||"^="===t||"%="===t||"<="===t||">="===t||"=>"===t||"**"===t?this.index+=2:(t=this.source[this.index],"<>=!+-*%&|^/".indexOf(t)>=0&&++this.index)}return this.index===e&&this.throwUnexpectedToken(),{type:7,value:t,lineNumber:this.lineNumber,lineStart:this.lineStart,start:e,end:this.index}},e.prototype.scanHexLiteral=function(e){for(var t="";!this.eof()&&r.Character.isHexDigit(this.source.charCodeAt(this.index));)t+=this.source[this.index++];return 0===t.length&&this.throwUnexpectedToken(),r.Character.isIdentifierStart(this.source.charCodeAt(this.index))&&this.throwUnexpectedToken(),{type:6,value:parseInt("0x"+t,16),lineNumber:this.lineNumber,lineStart:this.lineStart,start:e,end:this.index}},e.prototype.scanBinaryLiteral=function(e){for(var t,i="";!this.eof()&&("0"===(t=this.source[this.index])||"1"===t);)i+=this.source[this.index++];return 0===i.length&&this.throwUnexpectedToken(),this.eof()||(t=this.source.charCodeAt(this.index),(r.Character.isIdentifierStart(t)||r.Character.isDecimalDigit(t))&&this.throwUnexpectedToken()),{type:6,value:parseInt(i,2),lineNumber:this.lineNumber,lineStart:this.lineStart,start:e,end:this.index}},e.prototype.scanOctalLiteral=function(e,t){var i="",s=!1;for(r.Character.isOctalDigit(e.charCodeAt(0))?(s=!0,i="0"+this.source[this.index++]):++this.index;!this.eof()&&r.Character.isOctalDigit(this.source.charCodeAt(this.index));)i+=this.source[this.index++];return s||0!==i.length||this.throwUnexpectedToken(),(r.Character.isIdentifierStart(this.source.charCodeAt(this.index))||r.Character.isDecimalDigit(this.source.charCodeAt(this.index)))&&this.throwUnexpectedToken(),{type:6,value:parseInt(i,8),octal:s,lineNumber:this.lineNumber,lineStart:this.lineStart,start:t,end:this.index}},e.prototype.isImplicitOctalLiteral=function(){for(var e=this.index+1;e=0&&(i=i.replace(/\\u\{([0-9a-fA-F]+)\}|\\u([a-fA-F0-9]{4})/g,(function(e,t,i){var r=parseInt(t||i,16);return r>1114111&&s.throwUnexpectedToken(n.Messages.InvalidRegExp),r<=65535?String.fromCharCode(r):"￿"})).replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,"￿"));try{RegExp(i)}catch(e){this.throwUnexpectedToken(n.Messages.InvalidRegExp)}try{return new RegExp(e,t)}catch(e){return null}},e.prototype.scanRegExpBody=function(){var e=this.source[this.index];s.assert("/"===e,"Regular expression literal must start with a slash");for(var t=this.source[this.index++],i=!1,o=!1;!this.eof();)if(t+=e=this.source[this.index++],"\\"===e)e=this.source[this.index++],r.Character.isLineTerminator(e.charCodeAt(0))&&this.throwUnexpectedToken(n.Messages.UnterminatedRegExp),t+=e;else if(r.Character.isLineTerminator(e.charCodeAt(0)))this.throwUnexpectedToken(n.Messages.UnterminatedRegExp);else if(i)"]"===e&&(i=!1);else{if("/"===e){o=!0;break}"["===e&&(i=!0)}return o||this.throwUnexpectedToken(n.Messages.UnterminatedRegExp),t.substr(1,t.length-2)},e.prototype.scanRegExpFlags=function(){for(var e="";!this.eof();){var t=this.source[this.index];if(!r.Character.isIdentifierPart(t.charCodeAt(0)))break;if(++this.index,"\\"!==t||this.eof())e+=t;else if("u"===(t=this.source[this.index])){++this.index;var i=this.index,s=this.scanHexEscape("u");if(null!==s)for(e+=s;i=55296&&e<57343&&r.Character.isIdentifierStart(this.codePointAt(this.index))?this.scanIdentifier():this.scanPunctuator()},e}();t.Scanner=c},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.TokenName={},t.TokenName[1]="Boolean",t.TokenName[2]="",t.TokenName[3]="Identifier",t.TokenName[4]="Keyword",t.TokenName[5]="Null",t.TokenName[6]="Numeric",t.TokenName[7]="Punctuator",t.TokenName[8]="String",t.TokenName[9]="RegularExpression",t.TokenName[10]="Template"},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.XHTMLEntities={quot:'"',amp:"&",apos:"'",gt:">",nbsp:" ",iexcl:"¡",cent:"¢",pound:"£",curren:"¤",yen:"¥",brvbar:"¦",sect:"§",uml:"¨",copy:"©",ordf:"ª",laquo:"«",not:"¬",shy:"­",reg:"®",macr:"¯",deg:"°",plusmn:"±",sup2:"²",sup3:"³",acute:"´",micro:"µ",para:"¶",middot:"·",cedil:"¸",sup1:"¹",ordm:"º",raquo:"»",frac14:"¼",frac12:"½",frac34:"¾",iquest:"¿",Agrave:"À",Aacute:"Á",Acirc:"Â",Atilde:"Ã",Auml:"Ä",Aring:"Å",AElig:"Æ",Ccedil:"Ç",Egrave:"È",Eacute:"É",Ecirc:"Ê",Euml:"Ë",Igrave:"Ì",Iacute:"Í",Icirc:"Î",Iuml:"Ï",ETH:"Ð",Ntilde:"Ñ",Ograve:"Ò",Oacute:"Ó",Ocirc:"Ô",Otilde:"Õ",Ouml:"Ö",times:"×",Oslash:"Ø",Ugrave:"Ù",Uacute:"Ú",Ucirc:"Û",Uuml:"Ü",Yacute:"Ý",THORN:"Þ",szlig:"ß",agrave:"à",aacute:"á",acirc:"â",atilde:"ã",auml:"ä",aring:"å",aelig:"æ",ccedil:"ç",egrave:"è",eacute:"é",ecirc:"ê",euml:"ë",igrave:"ì",iacute:"í",icirc:"î",iuml:"ï",eth:"ð",ntilde:"ñ",ograve:"ò",oacute:"ó",ocirc:"ô",otilde:"õ",ouml:"ö",divide:"÷",oslash:"ø",ugrave:"ù",uacute:"ú",ucirc:"û",uuml:"ü",yacute:"ý",thorn:"þ",yuml:"ÿ",OElig:"Œ",oelig:"œ",Scaron:"Š",scaron:"š",Yuml:"Ÿ",fnof:"ƒ",circ:"ˆ",tilde:"˜",Alpha:"Α",Beta:"Β",Gamma:"Γ",Delta:"Δ",Epsilon:"Ε",Zeta:"Ζ",Eta:"Η",Theta:"Θ",Iota:"Ι",Kappa:"Κ",Lambda:"Λ",Mu:"Μ",Nu:"Ν",Xi:"Ξ",Omicron:"Ο",Pi:"Π",Rho:"Ρ",Sigma:"Σ",Tau:"Τ",Upsilon:"Υ",Phi:"Φ",Chi:"Χ",Psi:"Ψ",Omega:"Ω",alpha:"α",beta:"β",gamma:"γ",delta:"δ",epsilon:"ε",zeta:"ζ",eta:"η",theta:"θ",iota:"ι",kappa:"κ",lambda:"λ",mu:"μ",nu:"ν",xi:"ξ",omicron:"ο",pi:"π",rho:"ρ",sigmaf:"ς",sigma:"σ",tau:"τ",upsilon:"υ",phi:"φ",chi:"χ",psi:"ψ",omega:"ω",thetasym:"ϑ",upsih:"ϒ",piv:"ϖ",ensp:" ",emsp:" ",thinsp:" ",zwnj:"‌",zwj:"‍",lrm:"‎",rlm:"‏",ndash:"–",mdash:"—",lsquo:"‘",rsquo:"’",sbquo:"‚",ldquo:"“",rdquo:"”",bdquo:"„",dagger:"†",Dagger:"‡",bull:"•",hellip:"…",permil:"‰",prime:"′",Prime:"″",lsaquo:"‹",rsaquo:"›",oline:"‾",frasl:"⁄",euro:"€",image:"ℑ",weierp:"℘",real:"ℜ",trade:"™",alefsym:"ℵ",larr:"←",uarr:"↑",rarr:"→",darr:"↓",harr:"↔",crarr:"↵",lArr:"⇐",uArr:"⇑",rArr:"⇒",dArr:"⇓",hArr:"⇔",forall:"∀",part:"∂",exist:"∃",empty:"∅",nabla:"∇",isin:"∈",notin:"∉",ni:"∋",prod:"∏",sum:"∑",minus:"−",lowast:"∗",radic:"√",prop:"∝",infin:"∞",ang:"∠",and:"∧",or:"∨",cap:"∩",cup:"∪",int:"∫",there4:"∴",sim:"∼",cong:"≅",asymp:"≈",ne:"≠",equiv:"≡",le:"≤",ge:"≥",sub:"⊂",sup:"⊃",nsub:"⊄",sube:"⊆",supe:"⊇",oplus:"⊕",otimes:"⊗",perp:"⊥",sdot:"⋅",lceil:"⌈",rceil:"⌉",lfloor:"⌊",rfloor:"⌋",loz:"◊",spades:"♠",clubs:"♣",hearts:"♥",diams:"♦",lang:"⟨",rang:"⟩"}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var s=i(10),r=i(12),n=i(13),o=function(){function e(){this.values=[],this.curly=this.paren=-1}return e.prototype.beforeFunctionExpression=function(e){return["(","{","[","in","typeof","instanceof","new","return","case","delete","throw","void","=","+=","-=","*=","**=","/=","%=","<<=",">>=",">>>=","&=","|=","^=",",","+","-","*","**","/","%","++","--","<<",">>",">>>","&","|","^","!","~","&&","||","?",":","===","==",">=","<=","<",">","!=","!=="].indexOf(e)>=0},e.prototype.isRegexStart=function(){var e=this.values[this.values.length-1],t=null!==e;switch(e){case"this":case"]":t=!1;break;case")":var i=this.values[this.paren-1];t="if"===i||"while"===i||"for"===i||"with"===i;break;case"}":if(t=!1,"function"===this.values[this.curly-3])t=!!(s=this.values[this.curly-4])&&!this.beforeFunctionExpression(s);else if("function"===this.values[this.curly-4]){var s;t=!(s=this.values[this.curly-5])||!this.beforeFunctionExpression(s)}}return t},e.prototype.push=function(e){7===e.type||4===e.type?("{"===e.value?this.curly=this.values.length:"("===e.value&&(this.paren=this.values.length),this.values.push(e.value)):this.values.push(null)},e}(),a=function(){function e(e,t){this.errorHandler=new s.ErrorHandler,this.errorHandler.tolerant=!!t&&"boolean"==typeof t.tolerant&&t.tolerant,this.scanner=new r.Scanner(e,this.errorHandler),this.scanner.trackComment=!!t&&"boolean"==typeof t.comment&&t.comment,this.trackRange=!!t&&"boolean"==typeof t.range&&t.range,this.trackLoc=!!t&&"boolean"==typeof t.loc&&t.loc,this.buffer=[],this.reader=new o}return e.prototype.errors=function(){return this.errorHandler.errors},e.prototype.getNextToken=function(){if(0===this.buffer.length){var e=this.scanner.scanComments();if(this.scanner.trackComment)for(var t=0;t{"use strict";var t=Object.prototype.hasOwnProperty,i="~";function s(){}function r(e,t,i){this.fn=e,this.context=t,this.once=i||!1}function n(e,t,s,n,o){if("function"!=typeof s)throw new TypeError("The listener must be a function");var a=new r(s,n||e,o),c=i?i+t:t;return e._events[c]?e._events[c].fn?e._events[c]=[e._events[c],a]:e._events[c].push(a):(e._events[c]=a,e._eventsCount++),e}function o(e,t){0==--e._eventsCount?e._events=new s:delete e._events[t]}function a(){this._events=new s,this._eventsCount=0}Object.create&&(s.prototype=Object.create(null),(new s).__proto__||(i=!1)),a.prototype.eventNames=function(){var e,s,r=[];if(0===this._eventsCount)return r;for(s in e=this._events)t.call(e,s)&&r.push(i?s.slice(1):s);return Object.getOwnPropertySymbols?r.concat(Object.getOwnPropertySymbols(e)):r},a.prototype.listeners=function(e){var t=i?i+e:e,s=this._events[t];if(!s)return[];if(s.fn)return[s.fn];for(var r=0,n=s.length,o=new Array(n);r{"use strict";var t=Object.prototype.hasOwnProperty,i="~";function s(){}function r(e,t,i){this.fn=e,this.context=t,this.once=i||!1}function n(e,t,s,n,o){if("function"!=typeof s)throw new TypeError("The listener must be a function");var a=new r(s,n||e,o),c=i?i+t:t;return e._events[c]?e._events[c].fn?e._events[c]=[e._events[c],a]:e._events[c].push(a):(e._events[c]=a,e._eventsCount++),e}function o(e,t){0==--e._eventsCount?e._events=new s:delete e._events[t]}function a(){this._events=new s,this._eventsCount=0}Object.create&&(s.prototype=Object.create(null),(new s).__proto__||(i=!1)),a.prototype.eventNames=function(){var e,s,r=[];if(0===this._eventsCount)return r;for(s in e=this._events)t.call(e,s)&&r.push(i?s.slice(1):s);return Object.getOwnPropertySymbols?r.concat(Object.getOwnPropertySymbols(e)):r},a.prototype.listeners=function(e){var t=i?i+e:e,s=this._events[t];if(!s)return[];if(s.fn)return[s.fn];for(var r=0,n=s.length,o=new Array(n);r{"use strict";var s=i(60195);function r(e,t){for(var i in t)n(t,i)&&(e[i]=t[i])}function n(e,t){return Object.prototype.hasOwnProperty.call(e,t)}e.exports=function(e){s(e)||(e={});for(var t=arguments.length,i=1;i{"use strict";const t=(()=>{const e=0,t=1,i=2,s={},r={font:"Standard",fontPath:"./fonts"};function n(e,t,i){return e===t&&e!==i&&e}function o(e,t){let i="|/\\[]{}()<>";if("_"===e){if(-1!==i.indexOf(t))return t}else if("_"===t&&-1!==i.indexOf(e))return e;return!1}function a(e,t){let i="| /\\ [] {} () <>",s=i.indexOf(e),r=i.indexOf(t);if(-1!==s&&-1!==r&&s!==r&&1!==Math.abs(s-r)){const e=Math.max(s,r);return i.substring(e,e+1)}return!1}function c(e,t){let i="[] {} ()",s=i.indexOf(e),r=i.indexOf(t);return-1!==s&&-1!==r&&Math.abs(s-r)<=1&&"|"}function l(e,t){let i="/\\ \\/ ><",s=i.indexOf(e),r=i.indexOf(t);return-1!==s&&-1!==r&&r-s==1&&{0:"|",3:"Y",6:"X"}[s]}function p(e,t,i){return e===i&&t===i&&i}function A(e,t){return e===t&&e}function u(e,t){let i="|/\\[]{}()<>";if("_"===e){if(-1!==i.indexOf(t))return t}else if("_"===t&&-1!==i.indexOf(e))return e;return!1}function d(e,t){let i="| /\\ [] {} () <>",s=i.indexOf(e),r=i.indexOf(t);if(-1!==s&&-1!==r&&s!==r&&1!==Math.abs(s-r)){const e=Math.max(s,r);return i.substring(e,e+1)}return!1}function h(e,t){return("-"===e&&"_"===t||"_"===e&&"-"===t)&&"="}function m(e,t){return"|"===e&&"|"===t&&"|"}function g(e,t,i){return" "===t||""===t||t===i&&" "!==e?e:t}function f(s,r,n){if(n.fittingRules.vLayout===e)return"invalid";let o,a,c,l,p=Math.min(s.length,r.length),g=!1;if(0===p)return"invalid";for(o=0;on?C(t,r-n):n>r&&C(e,n-r),s=function(e,t,i){let s,r,n,o,a,c,l=e.length,p=e.length,A=(t.length,1);for(;A<=l;){for(s=e.slice(Math.max(0,p-A),p),r=t.slice(0,Math.min(l,A)),n=r.length,c="",o=0;o=l?A[r]:E(A[r],u[r],s),d.push(o);return a=t.slice(Math.min(i,l),l),[].concat(p,d,a)}(e,t,s,i)}function v(s,r,A){if(A.fittingRules.hLayout===e)return 0;let u,d,h,m,g,f=s.length,E=r.length,C=f,y=1,v=!1,I=!1;if(0===f)return 0;e:for(;y<=C;){const e=f-y;for(d=s.substring(e,e+y),h=r.substring(0,Math.min(y,E)),u=0;u=y?"":I.substring(r,r+Math.max(0,y-r)),B[u]=m+f+E}return B}function B(e){let t,i=[];for(t=0;t0&&s.whitespaceBreak&&(p={chars:[],overlap:g}),1===s.printDirection&&(t=t.split("").reverse().join("")),c=t.length,r=0;r0&&(s.whitespaceBreak?(d=b(p.chars.concat([{fig:n,overlap:g}]),f,s),h=b(C.concat([{fig:d,overlap:p.overlap}]),f,s),l=w(h)):(h=I(a,n,g,s),l=w(h)),l>=s.width&&r>0&&(s.whitespaceBreak?(a=b(C.slice(0,-1),f,s),C.length>1&&(E.push(a),a=B(f)),C=[]):(E.push(a),a=B(f)))),s.width>0&&s.whitespaceBreak&&(u&&r!==c-1||p.chars.push({fig:n,overlap:g}),u||r===c-1)){for(m=null;h=b(p.chars,f,s),l=w(h),l>=s.width;)m=Q(p.chars,f,s),p={chars:m.chars},E.push(m.outputFigText);l>0&&(m?C.push({fig:h,overlap:1}):C.push({fig:h,overlap:p.overlap})),u&&(C.push({fig:n,overlap:g}),a=B(f)),r===c-1&&(a=b(C,f,s)),p={chars:[],overlap:g};continue}a=I(a,n,g,s)}return w(a)>0&&E.push(a),!0!==s.showHardBlanks&&E.forEach((function(e){for(c=e.length,o=0;o{S.loadFont(s,(function(o,a){if(o)return n(o),void(i&&i(o));const c=k(s,D(a,t),e);r(c),i&&i(null,c)}))}))},S.textSync=function(e,t){let i="";e+="","string"==typeof t?(i=t,t={}):i=(t=t||{}).font||r.font;var s=D(S.loadFontSync(i),t);return k(i,s,e)},S.metadata=function(e,t){e+="",S.loadFont(e,(function(i,r){i?t(i):t(null,r,s[e].comment)}))},S.defaults=function(e){if("object"==typeof e&&null!==e)for(var t in e)e.hasOwnProperty(t)&&(r[t]=e[t]);return JSON.parse(JSON.stringify(r))},S.parseFont=function(r,n){n=n.replace(/\r\n/g,"\n").replace(/\r/g,"\n"),s[r]={};var o=n.split("\n"),a=o.splice(0,1)[0].split(" "),c=s[r],l={};if(l.hardBlank=a[0].substr(5,1),l.height=parseInt(a[1],10),l.baseline=parseInt(a[2],10),l.maxLength=parseInt(a[3],10),l.oldLayout=parseInt(a[4],10),l.numCommentLines=parseInt(a[5],10),l.printDirection=a.length>=6?parseInt(a[6],10):0,l.fullLayout=a.length>=7?parseInt(a[7],10):null,l.codeTagCount=a.length>=8?parseInt(a[8],10):null,l.fittingRules=function(s,r){let n,o,a,c,l={},p=[[16384,"vLayout",i],[8192,"vLayout",t],[4096,"vRule5",!0],[2048,"vRule4",!0],[1024,"vRule3",!0],[512,"vRule2",!0],[256,"vRule1",!0],[128,"hLayout",i],[64,"hLayout",t],[32,"hRule6",!0],[16,"hRule5",!0],[8,"hRule4",!0],[4,"hRule3",!0],[2,"hRule2",!0],[1,"hRule1",!0]];for(n=null!==r?r:s,o=0,a=p.length;o=c[0]?(n-=c[0],l[c[1]]=void 0===l[c[1]]?c[2]:l[c[1]]):"vLayout"!==c[1]&&"hLayout"!==c[1]&&(l[c[1]]=!1),o++;return void 0===l.hLayout?0===s?l.hLayout=t:-1===s?l.hLayout=e:l.hRule1||l.hRule2||l.hRule3||l.hRule4||l.hRule5||l.hRule6?l.hLayout=3:l.hLayout=i:l.hLayout===i&&(l.hRule1||l.hRule2||l.hRule3||l.hRule4||l.hRule5||l.hRule6)&&(l.hLayout=3),void 0===l.vLayout?l.vRule1||l.vRule2||l.vRule3||l.vRule4||l.vRule5?l.vLayout=3:l.vLayout=e:l.vLayout===i&&(l.vRule1||l.vRule2||l.vRule3||l.vRule4||l.vRule5)&&(l.vLayout=3),l}(l.oldLayout,l.fullLayout),c.options=l,1!==l.hardBlank.length||isNaN(l.height)||isNaN(l.baseline)||isNaN(l.maxLength)||isNaN(l.oldLayout)||isNaN(l.numCommentLines))throw new Error("FIGlet header contains invalid values.");let p,A=[];for(p=32;p<=126;p++)A.push(p);if(A=A.concat(196,214,220,228,246,252,223),o.length0&&c.numChars0;){if(u=o.splice(0,1)[0].split(" ")[0],/^0[xX][0-9a-fA-F]+$/.test(u))u=parseInt(u,16);else if(/^0[0-7]+$/.test(u))u=parseInt(u,8);else if(/^[0-9]+$/.test(u))u=parseInt(u,10);else{if(!/^-0[xX][0-9a-fA-F]+$/.test(u)){if(""===u)break;console.log("Invalid data:"+u),h=!0;break}u=parseInt(u,16)}for(c[u]=o.splice(0,l.height),p=0;pe.text())).then((function(e){i.push(e)}))}))}),Promise.resolve()).then((function(s){for(var r in e)e.hasOwnProperty(r)&&S.parseFont(e[r],i[r]);t&&t()}))},S.figFonts=s,S})();void 0!==e.exports&&(e.exports=t)},47707:(e,t,i)=>{const s=i(88042),r=i(57147),n=i(71017),o=n.join(__dirname,"/../fonts/");s.loadFont=function(e,t){s.figFonts[e]?t(null,s.figFonts[e].options):r.readFile(n.join(o,e+".flf"),{encoding:"utf-8"},(function(i,r){if(i)return t(i);r+="";try{t(null,s.parseFont(e,r))}catch(e){t(e)}}))},s.loadFontSync=function(e){if(s.figFonts[e])return s.figFonts[e].options;var t=r.readFileSync(n.join(o,e+".flf"),{encoding:"utf-8"});return t+="",s.parseFont(e,t)},s.fonts=function(e){var t=[];r.readdir(o,(function(i,s){if(i)return e(i);s.forEach((function(e){/\.flf$/.test(e)&&t.push(e.replace(/\.flf$/,""))})),e(null,t)}))},s.fontsSync=function(){var e=[];return r.readdirSync(o).forEach((function(t){/\.flf$/.test(t)&&e.push(t.replace(/\.flf$/,""))})),e},e.exports=s},12937:(e,t,i)=>{var s;e.exports=function(){if(!s){try{s=i(8856)("follow-redirects")}catch(e){}"function"!=typeof s&&(s=function(){})}s.apply(null,arguments)}},12564:(e,t,i)=>{var s=i(57310),r=s.URL,n=i(13685),o=i(95687),a=i(12781).Writable,c=i(39491),l=i(12937),p=["abort","aborted","connect","error","socket","timeout"],A=Object.create(null);p.forEach((function(e){A[e]=function(t,i,s){this._redirectable.emit(e,t,i,s)}}));var u=v("ERR_FR_REDIRECTION_FAILURE","Redirected request failed"),d=v("ERR_FR_TOO_MANY_REDIRECTS","Maximum number of redirects exceeded"),h=v("ERR_FR_MAX_BODY_LENGTH_EXCEEDED","Request body larger than maxBodyLength limit"),m=v("ERR_STREAM_WRITE_AFTER_END","write after end");function g(e,t){a.call(this),this._sanitizeOptions(e),this._options=e,this._ended=!1,this._ending=!1,this._redirectCount=0,this._redirects=[],this._requestBodyLength=0,this._requestBodyBuffers=[],t&&this.on("response",t);var i=this;this._onNativeResponse=function(e){i._processResponse(e)},this._performRequest()}function f(e){var t={maxRedirects:21,maxBodyLength:10485760},i={};return Object.keys(e).forEach((function(n){var o=n+":",a=i[o]=e[n],p=t[n]=Object.create(a);Object.defineProperties(p,{request:{value:function(e,n,a){if("string"==typeof e){var p=e;try{e=C(new r(p))}catch(t){e=s.parse(p)}}else r&&e instanceof r?e=C(e):(a=n,n=e,e={protocol:o});return"function"==typeof n&&(a=n,n=null),(n=Object.assign({maxRedirects:t.maxRedirects,maxBodyLength:t.maxBodyLength},e,n)).nativeProtocols=i,c.equal(n.protocol,o,"protocol mismatch"),l("options",n),new g(n,a)},configurable:!0,enumerable:!0,writable:!0},get:{value:function(e,t,i){var s=p.request(e,t,i);return s.end(),s},configurable:!0,enumerable:!0,writable:!0}})})),t}function E(){}function C(e){var t={protocol:e.protocol,hostname:e.hostname.startsWith("[")?e.hostname.slice(1,-1):e.hostname,hash:e.hash,search:e.search,pathname:e.pathname,path:e.pathname+e.search,href:e.href};return""!==e.port&&(t.port=Number(e.port)),t}function y(e,t){var i;for(var s in t)e.test(s)&&(i=t[s],delete t[s]);return null==i?void 0:String(i).trim()}function v(e,t){function i(e){Error.captureStackTrace(this,this.constructor),e?(this.message=t+": "+e.message,this.cause=e):this.message=t}return i.prototype=new Error,i.prototype.constructor=i,i.prototype.name="Error ["+e+"]",i.prototype.code=e,i}function I(e){for(var t of p)e.removeListener(t,A[t]);e.on("error",E),e.abort()}g.prototype=Object.create(a.prototype),g.prototype.abort=function(){I(this._currentRequest),this.emit("abort")},g.prototype.write=function(e,t,i){if(this._ending)throw new m;if(!("string"==typeof e||"object"==typeof e&&"length"in e))throw new TypeError("data should be a string, Buffer or Uint8Array");"function"==typeof t&&(i=t,t=null),0!==e.length?this._requestBodyLength+e.length<=this._options.maxBodyLength?(this._requestBodyLength+=e.length,this._requestBodyBuffers.push({data:e,encoding:t}),this._currentRequest.write(e,t,i)):(this.emit("error",new h),this.abort()):i&&i()},g.prototype.end=function(e,t,i){if("function"==typeof e?(i=e,e=t=null):"function"==typeof t&&(i=t,t=null),e){var s=this,r=this._currentRequest;this.write(e,t,(function(){s._ended=!0,r.end(null,null,i)})),this._ending=!0}else this._ended=this._ending=!0,this._currentRequest.end(null,null,i)},g.prototype.setHeader=function(e,t){this._options.headers[e]=t,this._currentRequest.setHeader(e,t)},g.prototype.removeHeader=function(e){delete this._options.headers[e],this._currentRequest.removeHeader(e)},g.prototype.setTimeout=function(e,t){var i=this;function s(t){t.setTimeout(e),t.removeListener("timeout",t.destroy),t.addListener("timeout",t.destroy)}function r(t){i._timeout&&clearTimeout(i._timeout),i._timeout=setTimeout((function(){i.emit("timeout"),n()}),e),s(t)}function n(){i._timeout&&(clearTimeout(i._timeout),i._timeout=null),i.removeListener("abort",n),i.removeListener("error",n),i.removeListener("response",n),t&&i.removeListener("timeout",t),i.socket||i._currentRequest.removeListener("socket",r)}return t&&this.on("timeout",t),this.socket?r(this.socket):this._currentRequest.once("socket",r),this.on("socket",s),this.on("abort",n),this.on("error",n),this.on("response",n),this},["flushHeaders","getHeader","setNoDelay","setSocketKeepAlive"].forEach((function(e){g.prototype[e]=function(t,i){return this._currentRequest[e](t,i)}})),["aborted","connection","socket"].forEach((function(e){Object.defineProperty(g.prototype,e,{get:function(){return this._currentRequest[e]}})})),g.prototype._sanitizeOptions=function(e){if(e.headers||(e.headers={}),e.host&&(e.hostname||(e.hostname=e.host),delete e.host),!e.pathname&&e.path){var t=e.path.indexOf("?");t<0?e.pathname=e.path:(e.pathname=e.path.substring(0,t),e.search=e.path.substring(t))}},g.prototype._performRequest=function(){var e=this._options.protocol,t=this._options.nativeProtocols[e];if(t){if(this._options.agents){var i=e.slice(0,-1);this._options.agent=this._options.agents[i]}var r=this._currentRequest=t.request(this._options,this._onNativeResponse);for(var n of(r._redirectable=this,p))r.on(n,A[n]);if(this._currentUrl=/^\//.test(this._options.path)?s.format(this._options):this._currentUrl=this._options.path,this._isRedirect){var o=0,a=this,c=this._requestBodyBuffers;!function e(t){if(r===a._currentRequest)if(t)a.emit("error",t);else if(o=400)return e.responseUrl=this._currentUrl,e.redirects=this._redirects,this.emit("response",e),void(this._requestBodyBuffers=[]);if(I(this._currentRequest),e.destroy(),++this._redirectCount>this._options.maxRedirects)this.emit("error",new d);else{var r,n=this._options.beforeRedirect;n&&(r=Object.assign({Host:e.req.getHeader("host")},this._options.headers));var o=this._options.method;((301===t||302===t)&&"POST"===this._options.method||303===t&&!/^(?:GET|HEAD)$/.test(this._options.method))&&(this._options.method="GET",this._requestBodyBuffers=[],y(/^content-/i,this._options.headers));var a,c=y(/^host$/i,this._options.headers),p=s.parse(this._currentUrl),A=c||p.host,h=/^\w+:/.test(i)?this._currentUrl:s.format(Object.assign(p,{host:A}));try{a=s.resolve(h,i)}catch(e){return void this.emit("error",new u(e))}l("redirecting to",a),this._isRedirect=!0;var m=s.parse(a);if(Object.assign(this._options,m),(m.protocol!==p.protocol&&"https:"!==m.protocol||m.host!==A&&!function(e,t){const i=e.length-t.length-1;return i>0&&"."===e[i]&&e.endsWith(t)}(m.host,A))&&y(/^(?:authorization|cookie)$/i,this._options.headers),"function"==typeof n){var g={headers:e.headers,statusCode:t},f={url:h,method:o,headers:r};try{n(this._options,g,f)}catch(e){return void this.emit("error",e)}this._sanitizeOptions(this._options)}try{this._performRequest()}catch(e){this.emit("error",new u(e))}}},e.exports=f({http:n,https:o}),e.exports.wrap=f},90504:(e,t,i)=>{var s=i(14598),r=i(73837),n=i(71017),o=i(13685),a=i(95687),c=i(57310).parse,l=i(57147),p=i(69335),A=i(62720),u=i(91117);function d(e){if(!(this instanceof d))return new d;for(var t in this._overheadLength=0,this._valueLength=0,this._valuesToMeasure=[],s.call(this),e=e||{})this[t]=e[t]}e.exports=d,r.inherits(d,s),d.LINE_BREAK="\r\n",d.DEFAULT_CONTENT_TYPE="application/octet-stream",d.prototype.append=function(e,t,i){"string"==typeof(i=i||{})&&(i={filename:i});var n=s.prototype.append.bind(this);if("number"==typeof t&&(t=""+t),r.isArray(t))this._error(new Error("Arrays are not supported."));else{var o=this._multiPartHeader(e,t,i),a=this._multiPartFooter();n(o),n(t),n(a),this._trackLength(o,t,i)}},d.prototype._trackLength=function(e,t,i){var s=0;null!=i.knownLength?s+=+i.knownLength:Buffer.isBuffer(t)?s=t.length:"string"==typeof t&&(s=Buffer.byteLength(t)),this._valueLength+=s,this._overheadLength+=Buffer.byteLength(e)+d.LINE_BREAK.length,t&&(t.path||t.readable&&t.hasOwnProperty("httpVersion"))&&(i.knownLength||this._valuesToMeasure.push(t))},d.prototype._lengthRetriever=function(e,t){e.hasOwnProperty("fd")?null!=e.end&&e.end!=1/0&&null!=e.start?t(null,e.end+1-(e.start?e.start:0)):l.stat(e.path,(function(i,s){var r;i?t(i):(r=s.size-(e.start?e.start:0),t(null,r))})):e.hasOwnProperty("httpVersion")?t(null,+e.headers["content-length"]):e.hasOwnProperty("httpModule")?(e.on("response",(function(i){e.pause(),t(null,+i.headers["content-length"])})),e.resume()):t("Unknown stream")},d.prototype._multiPartHeader=function(e,t,i){if("string"==typeof i.header)return i.header;var s,r=this._getContentDisposition(t,i),n=this._getContentType(t,i),o="",a={"Content-Disposition":["form-data",'name="'+e+'"'].concat(r||[]),"Content-Type":[].concat(n||[])};for(var c in"object"==typeof i.header&&u(a,i.header),a)a.hasOwnProperty(c)&&null!=(s=a[c])&&(Array.isArray(s)||(s=[s]),s.length&&(o+=c+": "+s.join("; ")+d.LINE_BREAK));return"--"+this.getBoundary()+d.LINE_BREAK+o+d.LINE_BREAK},d.prototype._getContentDisposition=function(e,t){var i,s;return"string"==typeof t.filepath?i=n.normalize(t.filepath).replace(/\\/g,"/"):t.filename||e.name||e.path?i=n.basename(t.filename||e.name||e.path):e.readable&&e.hasOwnProperty("httpVersion")&&(i=n.basename(e.client._httpMessage.path||"")),i&&(s='filename="'+i+'"'),s},d.prototype._getContentType=function(e,t){var i=t.contentType;return!i&&e.name&&(i=p.lookup(e.name)),!i&&e.path&&(i=p.lookup(e.path)),!i&&e.readable&&e.hasOwnProperty("httpVersion")&&(i=e.headers["content-type"]),i||!t.filepath&&!t.filename||(i=p.lookup(t.filepath||t.filename)),i||"object"!=typeof e||(i=d.DEFAULT_CONTENT_TYPE),i},d.prototype._multiPartFooter=function(){return function(e){var t=d.LINE_BREAK;0===this._streams.length&&(t+=this._lastBoundary()),e(t)}.bind(this)},d.prototype._lastBoundary=function(){return"--"+this.getBoundary()+"--"+d.LINE_BREAK},d.prototype.getHeaders=function(e){var t,i={"content-type":"multipart/form-data; boundary="+this.getBoundary()};for(t in e)e.hasOwnProperty(t)&&(i[t.toLowerCase()]=e[t]);return i},d.prototype.getBoundary=function(){return this._boundary||this._generateBoundary(),this._boundary},d.prototype.getBuffer=function(){for(var e=new Buffer.alloc(0),t=this.getBoundary(),i=0,s=this._streams.length;i{e.exports=function(e,t){return Object.keys(t).forEach((function(i){e[i]=e[i]||t[i]})),e}},87534:(e,t,i)=>{var s=i(14598),r=i(73837),n=i(71017),o=i(13685),a=i(95687),c=i(57310).parse,l=i(57147),p=i(12781).Stream,A=i(69335),u=i(62720),d=i(39049);function h(e){if(!(this instanceof h))return new h(e);for(var t in this._overheadLength=0,this._valueLength=0,this._valuesToMeasure=[],s.call(this),e=e||{})this[t]=e[t]}e.exports=h,r.inherits(h,s),h.LINE_BREAK="\r\n",h.DEFAULT_CONTENT_TYPE="application/octet-stream",h.prototype.append=function(e,t,i){"string"==typeof(i=i||{})&&(i={filename:i});var n=s.prototype.append.bind(this);if("number"==typeof t&&(t=""+t),r.isArray(t))this._error(new Error("Arrays are not supported."));else{var o=this._multiPartHeader(e,t,i),a=this._multiPartFooter();n(o),n(t),n(a),this._trackLength(o,t,i)}},h.prototype._trackLength=function(e,t,i){var s=0;null!=i.knownLength?s+=+i.knownLength:Buffer.isBuffer(t)?s=t.length:"string"==typeof t&&(s=Buffer.byteLength(t)),this._valueLength+=s,this._overheadLength+=Buffer.byteLength(e)+h.LINE_BREAK.length,t&&(t.path||t.readable&&t.hasOwnProperty("httpVersion")||t instanceof p)&&(i.knownLength||this._valuesToMeasure.push(t))},h.prototype._lengthRetriever=function(e,t){e.hasOwnProperty("fd")?null!=e.end&&e.end!=1/0&&null!=e.start?t(null,e.end+1-(e.start?e.start:0)):l.stat(e.path,(function(i,s){var r;i?t(i):(r=s.size-(e.start?e.start:0),t(null,r))})):e.hasOwnProperty("httpVersion")?t(null,+e.headers["content-length"]):e.hasOwnProperty("httpModule")?(e.on("response",(function(i){e.pause(),t(null,+i.headers["content-length"])})),e.resume()):t("Unknown stream")},h.prototype._multiPartHeader=function(e,t,i){if("string"==typeof i.header)return i.header;var s,r=this._getContentDisposition(t,i),n=this._getContentType(t,i),o="",a={"Content-Disposition":["form-data",'name="'+e+'"'].concat(r||[]),"Content-Type":[].concat(n||[])};for(var c in"object"==typeof i.header&&d(a,i.header),a)a.hasOwnProperty(c)&&null!=(s=a[c])&&(Array.isArray(s)||(s=[s]),s.length&&(o+=c+": "+s.join("; ")+h.LINE_BREAK));return"--"+this.getBoundary()+h.LINE_BREAK+o+h.LINE_BREAK},h.prototype._getContentDisposition=function(e,t){var i,s;return"string"==typeof t.filepath?i=n.normalize(t.filepath).replace(/\\/g,"/"):t.filename||e.name||e.path?i=n.basename(t.filename||e.name||e.path):e.readable&&e.hasOwnProperty("httpVersion")&&(i=n.basename(e.client._httpMessage.path||"")),i&&(s='filename="'+i+'"'),s},h.prototype._getContentType=function(e,t){var i=t.contentType;return!i&&e.name&&(i=A.lookup(e.name)),!i&&e.path&&(i=A.lookup(e.path)),!i&&e.readable&&e.hasOwnProperty("httpVersion")&&(i=e.headers["content-type"]),i||!t.filepath&&!t.filename||(i=A.lookup(t.filepath||t.filename)),i||"object"!=typeof e||(i=h.DEFAULT_CONTENT_TYPE),i},h.prototype._multiPartFooter=function(){return function(e){var t=h.LINE_BREAK;0===this._streams.length&&(t+=this._lastBoundary()),e(t)}.bind(this)},h.prototype._lastBoundary=function(){return"--"+this.getBoundary()+"--"+h.LINE_BREAK},h.prototype.getHeaders=function(e){var t,i={"content-type":"multipart/form-data; boundary="+this.getBoundary()};for(t in e)e.hasOwnProperty(t)&&(i[t.toLowerCase()]=e[t]);return i},h.prototype.setBoundary=function(e){this._boundary=e},h.prototype.getBoundary=function(){return this._boundary||this._generateBoundary(),this._boundary},h.prototype.getBuffer=function(){for(var e=new Buffer.alloc(0),t=this.getBoundary(),i=0,s=this._streams.length;i{e.exports=function(e,t){return Object.keys(t).forEach((function(i){e[i]=e[i]||t[i]})),e}},38125:(e,t,i)=>{"use strict";const s=i(57147),r=i(66577),n=i(62683),o=i(97537),a=i(56398),c=i(2237),l=i(68042),p=i(28418),A=i(38164);function u(e,t){if(""===e)return{data:{},content:e,excerpt:"",orig:e};let i=l(e);const s=u.cache[i.content];if(!t){if(s)return i=Object.assign({},s),i.orig=s.orig,i;u.cache[i.content]=i}return function(e,t){const i=n(t),s=i.delimiters[0],o="\n"+i.delimiters[1];let c=e.content;i.language&&(e.language=i.language);const l=s.length;if(!A.startsWith(c,s,l))return a(e,i),e;if(c.charAt(l)===s.slice(-1))return e;c=c.slice(l);const d=c.length,h=u.language(c,i);h.name&&(e.language=h.name,c=c.slice(h.raw.length));let m=c.indexOf(o);-1===m&&(m=d),e.matter=c.slice(0,m);return""===e.matter.replace(/^\s*#[^\n]+/gm,"").trim()?(e.isEmpty=!0,e.empty=e.content,e.data={}):e.data=p(e.language,e.matter,i),m===d?e.content="":(e.content=c.slice(m+o.length),"\r"===e.content[0]&&(e.content=e.content.slice(1)),"\n"===e.content[0]&&(e.content=e.content.slice(1))),a(e,i),(!0===i.sections||"function"==typeof i.section)&&r(e,i.section),e}(i,t)}u.engines=c,u.stringify=function(e,t,i){return"string"==typeof e&&(e=u(e,i)),o(e,t,i)},u.read=function(e,t){const i=u(s.readFileSync(e,"utf8"),t);return i.path=e,i},u.test=function(e,t){return A.startsWith(e,n(t).delimiters[0])},u.language=function(e,t){const i=n(t).delimiters[0];u.test(e)&&(e=e.slice(i.length));const s=e.slice(0,e.search(/\r?\n/));return{raw:s,name:s?s.trim():""}},u.cache={},u.clearCache=function(){u.cache={}},e.exports=u},62683:(e,t,i)=>{"use strict";const s=i(2237),r=i(38164);e.exports=function(e){const t=Object.assign({},e);return t.delimiters=r.arrayify(t.delims||t.delimiters||"---"),1===t.delimiters.length&&t.delimiters.push(t.delimiters[0]),t.language=(t.language||t.lang||"yaml").toLowerCase(),t.engines=Object.assign({},s,t.parsers,t.engines),t}},21825:e=>{"use strict";e.exports=function(e,t){let i=t.engines[e]||t.engines[function(e){switch(e.toLowerCase()){case"js":case"javascript":return"javascript";case"coffee":case"coffeescript":case"cson":return"coffee";case"yaml":case"yml":return"yaml";default:return e}}(e)];if(void 0===i)throw new Error('gray-matter engine "'+e+'" is not registered');return"function"==typeof i&&(i={parse:i}),i}},2237:(module,exports,__webpack_require__)=>{"use strict";const yaml=__webpack_require__(43236),engines=exports=module.exports;engines.yaml={parse:yaml.safeLoad.bind(yaml),stringify:yaml.safeDump.bind(yaml)},engines.json={parse:JSON.parse.bind(JSON),stringify:function(e,t){const i=Object.assign({replacer:null,space:2},t);return JSON.stringify(e,i.replacer,i.space)}},engines.javascript={parse:function parse(str,options,wrap){try{return!1!==wrap&&(str="(function() {\nreturn "+str.trim()+";\n}());"),eval(str)||{}}catch(e){if(!1!==wrap&&/(unexpected|identifier)/i.test(e.message))return parse(str,options,!1);throw new SyntaxError(e)}},stringify:function(){throw new Error("stringifying JavaScript is not supported")}}},56398:(e,t,i)=>{"use strict";const s=i(62683);e.exports=function(e,t){const i=s(t);if(null==e.data&&(e.data={}),"function"==typeof i.excerpt)return i.excerpt(e,i);const r=e.data.excerpt_separator||i.excerpt_separator;if(null==r&&(!1===i.excerpt||null==i.excerpt))return e;const n="string"==typeof i.excerpt?i.excerpt:r||i.delimiters[0],o=e.content.indexOf(n);return-1!==o&&(e.excerpt=e.content.slice(0,o)),e}},28418:(e,t,i)=>{"use strict";const s=i(21825),r=i(62683);e.exports=function(e,t,i){const n=r(i),o=s(e,n);if("function"!=typeof o.parse)throw new TypeError('expected "'+e+'.parse" to be a function');return o.parse(t,n)}},97537:(e,t,i)=>{"use strict";const s=i(79049),r=i(21825),n=i(62683);function o(e){return"\n"!==e.slice(-1)?e+"\n":e}e.exports=function(e,t,i){if(null==t&&null==i)switch(s(e)){case"object":t=e.data,i={};break;case"string":return e;default:throw new TypeError("expected file to be a string or object")}const a=e.content,c=n(i);if(null==t){if(!c.data)return e;t=c.data}const l=e.language||c.language,p=r(l,c);if("function"!=typeof p.stringify)throw new TypeError('expected "'+l+'.stringify" to be a function');t=Object.assign({},e.data,t);const A=c.delimiters[0],u=c.delimiters[1],d=p.stringify(t,i).trim();let h="";return"{}"!==d&&(h=o(A)+o(d)+o(u)),"string"==typeof e.excerpt&&""!==e.excerpt&&-1===a.indexOf(e.excerpt.trim())&&(h+=o(e.excerpt)+o(u)),h+o(a)}},68042:(e,t,i)=>{"use strict";const s=i(79049),r=i(97537),n=i(38164);e.exports=function(e){return"object"!==s(e)&&(e={content:e}),"object"!==s(e.data)&&(e.data={}),e.contents&&null==e.content&&(e.content=e.contents),n.define(e,"orig",n.toBuffer(e.content)),n.define(e,"language",e.language||""),n.define(e,"matter",e.matter||""),n.define(e,"stringify",(function(t,i){return i&&i.language&&(e.language=i.language),r(e,t,i)})),e.content=n.toString(e.content),e.isEmpty=!1,e.excerpt="",e}},38164:(e,t,i)=>{"use strict";const s=i(47494),r=i(79049);t.define=function(e,t,i){Reflect.defineProperty(e,t,{enumerable:!1,configurable:!0,writable:!0,value:i})},t.isBuffer=function(e){return"buffer"===r(e)},t.isObject=function(e){return"object"===r(e)},t.toBuffer=function(e){return"string"==typeof e?Buffer.from(e):e},t.toString=function(e){if(t.isBuffer(e))return s(String(e));if("string"!=typeof e)throw new TypeError("expected input to be a string or buffer");return s(e)},t.arrayify=function(e){return e?Array.isArray(e)?e:[e]:[]},t.startsWith=function(e,t,i){return"number"!=typeof i&&(i=t.length),e.slice(0,i)===t}},5506:e=>{"use strict";e.exports=(e,t=process.argv)=>{const i=e.startsWith("-")?"":1===e.length?"-":"--",s=t.indexOf(i+e),r=t.indexOf("--");return-1!==s&&(-1===r||s{"use strict";e.exports=(e,t=1,i)=>{if(i={indent:" ",includeEmptyLines:!1,...i},"string"!=typeof e)throw new TypeError(`Expected \`input\` to be a \`string\`, got \`${typeof e}\``);if("number"!=typeof t)throw new TypeError(`Expected \`count\` to be a \`number\`, got \`${typeof t}\``);if("string"!=typeof i.indent)throw new TypeError(`Expected \`options.indent\` to be a \`string\`, got \`${typeof i.indent}\``);if(0===t)return e;const s=i.includeEmptyLines?/^/gm:/^(?!\s*$)/gm;return e.replace(s,i.indent.repeat(t))}},44236:(e,t,i)=>{try{var s=i(73837);if("function"!=typeof s.inherits)throw"";e.exports=s.inherits}catch(t){e.exports=i(67483)}},67483:e=>{"function"==typeof Object.create?e.exports=function(e,t){t&&(e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}))}:e.exports=function(e,t){if(t){e.super_=t;var i=function(){};i.prototype=t.prototype,e.prototype=new i,e.prototype.constructor=e}}},30547:e=>{e.exports=function(){return"undefined"!=typeof window&&"object"==typeof window.process&&"renderer"===window.process.type||!("undefined"==typeof process||"object"!=typeof process.versions||!process.versions.electron)||"object"==typeof navigator&&"string"==typeof navigator.userAgent&&navigator.userAgent.indexOf("Electron")>=0}},60195:e=>{"use strict";e.exports=function(e){return null!=e&&("object"==typeof e||"function"==typeof e)}},27759:e=>{"use strict";e.exports=({stream:e=process.stdout}={})=>Boolean(e&&e.isTTY&&"dumb"!==process.env.TERM&&!("CI"in process.env))},42412:e=>{"use strict";var t=e.exports=function(e){return null!==e&&"object"==typeof e&&"function"==typeof e.pipe};t.writable=function(e){return t(e)&&!1!==e.writable&&"function"==typeof e._write&&"object"==typeof e._writableState},t.readable=function(e){return t(e)&&!1!==e.readable&&"function"==typeof e._read&&"object"==typeof e._readableState},t.duplex=function(e){return t.writable(e)&&t.readable(e)},t.transform=function(e){return t.duplex(e)&&"function"==typeof e._transform&&"object"==typeof e._transformState}},5881:e=>{"use strict";e.exports=()=>"win32"!==process.platform||Boolean(process.env.CI)||Boolean(process.env.WT_SESSION)||"vscode"===process.env.TERM_PROGRAM||"xterm-256color"===process.env.TERM||"alacritty"===process.env.TERM},43236:(e,t,i)=>{"use strict";var s=i(5033);e.exports=s},5033:(e,t,i)=>{"use strict";var s=i(84780),r=i(44418);function n(e){return function(){throw new Error("Function "+e+" is deprecated and cannot be used.")}}e.exports.Type=i(42257),e.exports.Schema=i(38107),e.exports.FAILSAFE_SCHEMA=i(19777),e.exports.JSON_SCHEMA=i(93886),e.exports.CORE_SCHEMA=i(71514),e.exports.DEFAULT_SAFE_SCHEMA=i(79251),e.exports.DEFAULT_FULL_SCHEMA=i(13536),e.exports.load=s.load,e.exports.loadAll=s.loadAll,e.exports.safeLoad=s.safeLoad,e.exports.safeLoadAll=s.safeLoadAll,e.exports.dump=r.dump,e.exports.safeDump=r.safeDump,e.exports.YAMLException=i(86822),e.exports.MINIMAL_SCHEMA=i(19777),e.exports.SAFE_SCHEMA=i(79251),e.exports.DEFAULT_SCHEMA=i(13536),e.exports.scan=n("scan"),e.exports.parse=n("parse"),e.exports.compose=n("compose"),e.exports.addConstructor=n("addConstructor")},90560:e=>{"use strict";function t(e){return null==e}e.exports.isNothing=t,e.exports.isObject=function(e){return"object"==typeof e&&null!==e},e.exports.toArray=function(e){return Array.isArray(e)?e:t(e)?[]:[e]},e.exports.repeat=function(e,t){var i,s="";for(i=0;i{"use strict";var s=i(90560),r=i(86822),n=i(13536),o=i(79251),a=Object.prototype.toString,c=Object.prototype.hasOwnProperty,l=9,p=10,A=13,u=32,d=33,h=34,m=35,g=37,f=38,E=39,C=42,y=44,v=45,I=58,B=61,w=62,b=63,Q=64,x=91,k=93,D=96,S=123,_=124,R=125,T={0:"\\0",7:"\\a",8:"\\b",9:"\\t",10:"\\n",11:"\\v",12:"\\f",13:"\\r",27:"\\e",34:'\\"',92:"\\\\",133:"\\N",160:"\\_",8232:"\\L",8233:"\\P"},F=["y","Y","yes","Yes","YES","on","On","ON","n","N","no","No","NO","off","Off","OFF"];function N(e){var t,i,n;if(t=e.toString(16).toUpperCase(),e<=255)i="x",n=2;else if(e<=65535)i="u",n=4;else{if(!(e<=4294967295))throw new r("code point within a string may not be greater than 0xFFFFFFFF");i="U",n=8}return"\\"+i+s.repeat("0",n-t.length)+t}function L(e){this.schema=e.schema||n,this.indent=Math.max(1,e.indent||2),this.noArrayIndent=e.noArrayIndent||!1,this.skipInvalid=e.skipInvalid||!1,this.flowLevel=s.isNothing(e.flowLevel)?-1:e.flowLevel,this.styleMap=function(e,t){var i,s,r,n,o,a,l;if(null===t)return{};for(i={},r=0,n=(s=Object.keys(t)).length;r-1&&i>=e.flowLevel;switch(function(e,t,i,s,r){var n,o,a,c,l=!1,A=!1,u=-1!==s,T=-1,F=P(c=e.charCodeAt(0))&&65279!==c&&!U(c)&&c!==v&&c!==b&&c!==I&&c!==y&&c!==x&&c!==k&&c!==S&&c!==R&&c!==m&&c!==f&&c!==C&&c!==d&&c!==_&&c!==B&&c!==w&&c!==E&&c!==h&&c!==g&&c!==Q&&c!==D&&!U(e.charCodeAt(e.length-1));if(t)for(n=0;n0?e.charCodeAt(n-1):null,F=F&&G(o,a)}else{for(n=0;ns&&" "!==e[T+1],T=n);else if(!P(o))return Y;a=n>0?e.charCodeAt(n-1):null,F=F&&G(o,a)}A=A||u&&n-T-1>s&&" "!==e[T+1]}return l||A?i>9&&V(e)?Y:A?q:J:F&&!r(e)?j:H}(t,a,e.indent,o,(function(t){return function(e,t){var i,s;for(i=0,s=e.implicitTypes.length;i"+z(t,e.indent)+$(O(function(e,t){for(var i,s,r,n=/(\n+)([^\n]*)/g,o=(r=-1!==(r=e.indexOf("\n"))?r:e.length,n.lastIndex=r,X(e.slice(0,r),t)),a="\n"===e[0]||" "===e[0];s=n.exec(e);){var c=s[1],l=s[2];i=" "===l[0],o+=c+(a||i||""===l?"":"\n")+X(l,t),a=i}return o}(t,o),n));case Y:return'"'+function(e){for(var t,i,s,r="",n=0;n=55296&&t<=56319&&(i=e.charCodeAt(n+1))>=56320&&i<=57343?(r+=N(1024*(t-55296)+i-56320+65536),n++):r+=!(s=T[t])&&P(t)?e[n]:s||N(t);return r}(t)+'"';default:throw new r("impossible error: invalid scalar style")}}()}function z(e,t){var i=V(e)?String(t):"",s="\n"===e[e.length-1];return i+(!s||"\n"!==e[e.length-2]&&"\n"!==e?s?"":"-":"+")+"\n"}function $(e){return"\n"===e[e.length-1]?e.slice(0,-1):e}function X(e,t){if(""===e||" "===e[0])return e;for(var i,s,r=/ [^ ]/g,n=0,o=0,a=0,c="";i=r.exec(e);)(a=i.index)-n>t&&(s=o>n?o:a,c+="\n"+e.slice(n,s),n=s+1),o=a;return c+="\n",e.length-n>t&&o>n?c+=e.slice(n,o)+"\n"+e.slice(o+1):c+=e.slice(n),c.slice(1)}function K(e,t,i){var s,n,o,l,p,A;for(o=0,l=(n=i?e.explicitTypes:e.implicitTypes).length;o tag resolver accepts not "'+A+'" style');s=p.represent[A](t,A)}e.dump=s}return!0}return!1}function Z(e,t,i,s,n,o){e.tag=null,e.dump=i,K(e,i,!1)||K(e,i,!0);var c=a.call(e.dump);s&&(s=e.flowLevel<0||e.flowLevel>t);var l,A,u="[object Object]"===c||"[object Array]"===c;if(u&&(A=-1!==(l=e.duplicates.indexOf(i))),(null!==e.tag&&"?"!==e.tag||A||2!==e.indent&&t>0)&&(n=!1),A&&e.usedDuplicates[l])e.dump="*ref_"+l;else{if(u&&A&&!e.usedDuplicates[l]&&(e.usedDuplicates[l]=!0),"[object Object]"===c)s&&0!==Object.keys(e.dump).length?(function(e,t,i,s){var n,o,a,c,l,A,u="",d=e.tag,h=Object.keys(i);if(!0===e.sortKeys)h.sort();else if("function"==typeof e.sortKeys)h.sort(e.sortKeys);else if(e.sortKeys)throw new r("sortKeys must be a boolean or a function");for(n=0,o=h.length;n1024)&&(e.dump&&p===e.dump.charCodeAt(0)?A+="?":A+="? "),A+=e.dump,l&&(A+=M(e,t)),Z(e,t+1,c,!0,l)&&(e.dump&&p===e.dump.charCodeAt(0)?A+=":":A+=": ",u+=A+=e.dump));e.tag=d,e.dump=u||"{}"}(e,t,e.dump,n),A&&(e.dump="&ref_"+l+e.dump)):(function(e,t,i){var s,r,n,o,a,c="",l=e.tag,p=Object.keys(i);for(s=0,r=p.length;s1024&&(a+="? "),a+=e.dump+(e.condenseFlow?'"':"")+":"+(e.condenseFlow?"":" "),Z(e,t,o,!1,!1)&&(c+=a+=e.dump));e.tag=l,e.dump="{"+c+"}"}(e,t,e.dump),A&&(e.dump="&ref_"+l+" "+e.dump));else if("[object Array]"===c){var d=e.noArrayIndent&&t>0?t-1:t;s&&0!==e.dump.length?(function(e,t,i,s){var r,n,o="",a=e.tag;for(r=0,n=i.length;r "+e.dump)}return!0}function ee(e,t){var i,s,r=[],n=[];for(te(e,r,n),i=0,s=n.length;i{"use strict";function t(e,t){Error.call(this),this.name="YAMLException",this.reason=e,this.mark=t,this.message=(this.reason||"(unknown reason)")+(this.mark?" "+this.mark.toString():""),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack||""}t.prototype=Object.create(Error.prototype),t.prototype.constructor=t,t.prototype.toString=function(e){var t=this.name+": ";return t+=this.reason||"(unknown reason)",!e&&this.mark&&(t+=" "+this.mark.toString()),t},e.exports=t},84780:(e,t,i)=>{"use strict";var s=i(90560),r=i(86822),n=i(20917),o=i(79251),a=i(13536),c=Object.prototype.hasOwnProperty,l=1,p=2,A=3,u=4,d=1,h=2,m=3,g=/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/,f=/[\x85\u2028\u2029]/,E=/[,\[\]\{\}]/,C=/^(?:!|!!|![a-z\-]+!)$/i,y=/^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i;function v(e){return Object.prototype.toString.call(e)}function I(e){return 10===e||13===e}function B(e){return 9===e||32===e}function w(e){return 9===e||32===e||10===e||13===e}function b(e){return 44===e||91===e||93===e||123===e||125===e}function Q(e){var t;return 48<=e&&e<=57?e-48:97<=(t=32|e)&&t<=102?t-97+10:-1}function x(e){return 48===e?"\0":97===e?"":98===e?"\b":116===e||9===e?"\t":110===e?"\n":118===e?"\v":102===e?"\f":114===e?"\r":101===e?"":32===e?" ":34===e?'"':47===e?"/":92===e?"\\":78===e?"…":95===e?" ":76===e?"\u2028":80===e?"\u2029":""}function k(e){return e<=65535?String.fromCharCode(e):String.fromCharCode(55296+(e-65536>>10),56320+(e-65536&1023))}for(var D=new Array(256),S=new Array(256),_=0;_<256;_++)D[_]=x(_)?1:0,S[_]=x(_);function R(e,t){this.input=e,this.filename=t.filename||null,this.schema=t.schema||a,this.onWarning=t.onWarning||null,this.legacy=t.legacy||!1,this.json=t.json||!1,this.listener=t.listener||null,this.implicitTypes=this.schema.compiledImplicit,this.typeMap=this.schema.compiledTypeMap,this.length=e.length,this.position=0,this.line=0,this.lineStart=0,this.lineIndent=0,this.documents=[]}function T(e,t){return new r(t,new n(e.filename,e.input,e.position,e.line,e.position-e.lineStart))}function F(e,t){throw T(e,t)}function N(e,t){e.onWarning&&e.onWarning.call(null,T(e,t))}var L={YAML:function(e,t,i){var s,r,n;null!==e.version&&F(e,"duplication of %YAML directive"),1!==i.length&&F(e,"YAML directive accepts exactly one argument"),null===(s=/^([0-9]+)\.([0-9]+)$/.exec(i[0]))&&F(e,"ill-formed argument of the YAML directive"),r=parseInt(s[1],10),n=parseInt(s[2],10),1!==r&&F(e,"unacceptable YAML version of the document"),e.version=i[0],e.checkLineBreaks=n<2,1!==n&&2!==n&&N(e,"unsupported YAML version of the document")},TAG:function(e,t,i){var s,r;2!==i.length&&F(e,"TAG directive accepts exactly two arguments"),s=i[0],r=i[1],C.test(s)||F(e,"ill-formed tag handle (first argument) of the TAG directive"),c.call(e.tagMap,s)&&F(e,'there is a previously declared suffix for "'+s+'" tag handle'),y.test(r)||F(e,"ill-formed tag prefix (second argument) of the TAG directive"),e.tagMap[s]=r}};function O(e,t,i,s){var r,n,o,a;if(t1&&(e.result+=s.repeat("\n",t-1))}function H(e,t){var i,s,r=e.tag,n=e.anchor,o=[],a=!1;for(null!==e.anchor&&(e.anchorMap[e.anchor]=o),s=e.input.charCodeAt(e.position);0!==s&&45===s&&w(e.input.charCodeAt(e.position+1));)if(a=!0,e.position++,G(e,!0,-1)&&e.lineIndent<=t)o.push(null),s=e.input.charCodeAt(e.position);else if(i=e.line,Y(e,t,A,!1,!0),o.push(e.result),G(e,!0,-1),s=e.input.charCodeAt(e.position),(e.line===i||e.lineIndent>t)&&0!==s)F(e,"bad indentation of a sequence entry");else if(e.lineIndentt?x=1:e.lineIndent===t?x=0:e.lineIndentt?x=1:e.lineIndent===t?x=0:e.lineIndentt)&&(Y(e,t,u,!0,r)&&(f?m=e.result:g=e.result),f||(U(e,A,d,h,m,g,n,o),h=m=g=null),G(e,!0,-1),a=e.input.charCodeAt(e.position)),e.lineIndent>t&&0!==a)F(e,"bad indentation of a mapping entry");else if(e.lineIndent=0))break;0===n?F(e,"bad explicit indentation width of a block scalar; it cannot be less than one"):p?F(e,"repeat of an indentation width identifier"):(A=t+n-1,p=!0)}if(B(o)){do{o=e.input.charCodeAt(++e.position)}while(B(o));if(35===o)do{o=e.input.charCodeAt(++e.position)}while(!I(o)&&0!==o)}for(;0!==o;){for(P(e),e.lineIndent=0,o=e.input.charCodeAt(e.position);(!p||e.lineIndentA&&(A=e.lineIndent),I(o))u++;else{if(e.lineIndent0){for(r=o,n=0;r>0;r--)(o=Q(a=e.input.charCodeAt(++e.position)))>=0?n=(n<<4)+o:F(e,"expected hexadecimal character");e.result+=k(n),e.position++}else F(e,"unknown escape sequence");i=s=e.position}else I(a)?(O(e,i,s,!0),j(e,G(e,!1,t)),i=s=e.position):e.position===e.lineStart&&V(e)?F(e,"unexpected end of the document within a double quoted scalar"):(e.position++,s=e.position)}F(e,"unexpected end of the stream within a double quoted scalar")}(e,y)?R=!0:function(e){var t,i,s;if(42!==(s=e.input.charCodeAt(e.position)))return!1;for(s=e.input.charCodeAt(++e.position),t=e.position;0!==s&&!w(s)&&!b(s);)s=e.input.charCodeAt(++e.position);return e.position===t&&F(e,"name of an alias node must contain at least one character"),i=e.input.slice(t,e.position),c.call(e.anchorMap,i)||F(e,'unidentified alias "'+i+'"'),e.result=e.anchorMap[i],G(e,!0,-1),!0}(e)?(R=!0,null===e.tag&&null===e.anchor||F(e,"alias node should not have any properties")):function(e,t,i){var s,r,n,o,a,c,l,p,A=e.kind,u=e.result;if(w(p=e.input.charCodeAt(e.position))||b(p)||35===p||38===p||42===p||33===p||124===p||62===p||39===p||34===p||37===p||64===p||96===p)return!1;if((63===p||45===p)&&(w(s=e.input.charCodeAt(e.position+1))||i&&b(s)))return!1;for(e.kind="scalar",e.result="",r=n=e.position,o=!1;0!==p;){if(58===p){if(w(s=e.input.charCodeAt(e.position+1))||i&&b(s))break}else if(35===p){if(w(e.input.charCodeAt(e.position-1)))break}else{if(e.position===e.lineStart&&V(e)||i&&b(p))break;if(I(p)){if(a=e.line,c=e.lineStart,l=e.lineIndent,G(e,!1,-1),e.lineIndent>=t){o=!0,p=e.input.charCodeAt(e.position);continue}e.position=n,e.line=a,e.lineStart=c,e.lineIndent=l;break}}o&&(O(e,r,n,!1),j(e,e.line-a),r=n=e.position,o=!1),B(p)||(n=e.position+1),p=e.input.charCodeAt(++e.position)}return O(e,r,n,!1),!!e.result||(e.kind=A,e.result=u,!1)}(e,y,l===i)&&(R=!0,null===e.tag&&(e.tag="?")),null!==e.anchor&&(e.anchorMap[e.anchor]=e.result)):0===x&&(R=g&&H(e,v))),null!==e.tag&&"!"!==e.tag)if("?"===e.tag){for(null!==e.result&&"scalar"!==e.kind&&F(e,'unacceptable node kind for ! tag; it should be "scalar", not "'+e.kind+'"'),f=0,E=e.implicitTypes.length;f tag; it should be "'+C.kind+'", not "'+e.kind+'"'),C.resolve(e.result)?(e.result=C.construct(e.result),null!==e.anchor&&(e.anchorMap[e.anchor]=e.result)):F(e,"cannot resolve a node with !<"+e.tag+"> explicit tag")):F(e,"unknown tag !<"+e.tag+">");return null!==e.listener&&e.listener("close",e),null!==e.tag||null!==e.anchor||R}function W(e){var t,i,s,r,n=e.position,o=!1;for(e.version=null,e.checkLineBreaks=e.legacy,e.tagMap={},e.anchorMap={};0!==(r=e.input.charCodeAt(e.position))&&(G(e,!0,-1),r=e.input.charCodeAt(e.position),!(e.lineIndent>0||37!==r));){for(o=!0,r=e.input.charCodeAt(++e.position),t=e.position;0!==r&&!w(r);)r=e.input.charCodeAt(++e.position);for(s=[],(i=e.input.slice(t,e.position)).length<1&&F(e,"directive name must not be less than one character in length");0!==r;){for(;B(r);)r=e.input.charCodeAt(++e.position);if(35===r){do{r=e.input.charCodeAt(++e.position)}while(0!==r&&!I(r));break}if(I(r))break;for(t=e.position;0!==r&&!w(r);)r=e.input.charCodeAt(++e.position);s.push(e.input.slice(t,e.position))}0!==r&&P(e),c.call(L,i)?L[i](e,i,s):N(e,'unknown document directive "'+i+'"')}G(e,!0,-1),0===e.lineIndent&&45===e.input.charCodeAt(e.position)&&45===e.input.charCodeAt(e.position+1)&&45===e.input.charCodeAt(e.position+2)?(e.position+=3,G(e,!0,-1)):o&&F(e,"directives end mark is expected"),Y(e,e.lineIndent-1,u,!1,!0),G(e,!0,-1),e.checkLineBreaks&&f.test(e.input.slice(n,e.position))&&N(e,"non-ASCII line breaks are interpreted as content"),e.documents.push(e.result),e.position===e.lineStart&&V(e)?46===e.input.charCodeAt(e.position)&&(e.position+=3,G(e,!0,-1)):e.position{"use strict";var s=i(90560);function r(e,t,i,s,r){this.name=e,this.buffer=t,this.position=i,this.line=s,this.column=r}r.prototype.getSnippet=function(e,t){var i,r,n,o,a;if(!this.buffer)return null;for(e=e||4,t=t||75,i="",r=this.position;r>0&&-1==="\0\r\n…\u2028\u2029".indexOf(this.buffer.charAt(r-1));)if(r-=1,this.position-r>t/2-1){i=" ... ",r+=5;break}for(n="",o=this.position;ot/2-1){n=" ... ",o-=5;break}return a=this.buffer.slice(r,o),s.repeat(" ",e)+i+a+n+"\n"+s.repeat(" ",e+this.position-r+i.length)+"^"},r.prototype.toString=function(e){var t,i="";return this.name&&(i+='in "'+this.name+'" '),i+="at line "+(this.line+1)+", column "+(this.column+1),e||(t=this.getSnippet())&&(i+=":\n"+t),i},e.exports=r},38107:(e,t,i)=>{"use strict";var s=i(90560),r=i(86822),n=i(42257);function o(e,t,i){var s=[];return e.include.forEach((function(e){i=o(e,t,i)})),e[t].forEach((function(e){i.forEach((function(t,i){t.tag===e.tag&&t.kind===e.kind&&s.push(i)})),i.push(e)})),i.filter((function(e,t){return-1===s.indexOf(t)}))}function a(e){this.include=e.include||[],this.implicit=e.implicit||[],this.explicit=e.explicit||[],this.implicit.forEach((function(e){if(e.loadKind&&"scalar"!==e.loadKind)throw new r("There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.")})),this.compiledImplicit=o(this,"implicit",[]),this.compiledExplicit=o(this,"explicit",[]),this.compiledTypeMap=function(){var e,t,i={scalar:{},sequence:{},mapping:{},fallback:{}};function s(e){i[e.kind][e.tag]=i.fallback[e.tag]=e}for(e=0,t=arguments.length;e{"use strict";var s=i(38107);e.exports=new s({include:[i(93886)]})},13536:(e,t,i)=>{"use strict";var s=i(38107);e.exports=s.DEFAULT=new s({include:[i(79251)],explicit:[i(7021),i(55844),i(1327)]})},79251:(e,t,i)=>{"use strict";var s=i(38107);e.exports=new s({include:[i(71514)],implicit:[i(1363),i(3174)],explicit:[i(81676),i(53900),i(21908),i(30768)]})},19777:(e,t,i)=>{"use strict";var s=i(38107);e.exports=new s({explicit:[i(99678),i(23371),i(17261)]})},93886:(e,t,i)=>{"use strict";var s=i(38107);e.exports=new s({include:[i(19777)],implicit:[i(40707),i(47847),i(84302),i(28249)]})},42257:(e,t,i)=>{"use strict";var s=i(86822),r=["kind","resolve","construct","instanceOf","predicate","represent","defaultStyle","styleAliases"],n=["scalar","sequence","mapping"];e.exports=function(e,t){var i,o;if(t=t||{},Object.keys(t).forEach((function(t){if(-1===r.indexOf(t))throw new s('Unknown option "'+t+'" is met in definition of "'+e+'" YAML type.')})),this.tag=e,this.kind=t.kind||null,this.resolve=t.resolve||function(){return!0},this.construct=t.construct||function(e){return e},this.instanceOf=t.instanceOf||null,this.predicate=t.predicate||null,this.represent=t.represent||null,this.defaultStyle=t.defaultStyle||null,this.styleAliases=(i=t.styleAliases||null,o={},null!==i&&Object.keys(i).forEach((function(e){i[e].forEach((function(t){o[String(t)]=e}))})),o),-1===n.indexOf(this.kind))throw new s('Unknown kind "'+this.kind+'" is specified for "'+e+'" YAML type.')}},81676:(e,t,i)=>{"use strict";var s;try{s=i(14300).Buffer}catch(e){}var r=i(42257),n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\r";e.exports=new r("tag:yaml.org,2002:binary",{kind:"scalar",resolve:function(e){if(null===e)return!1;var t,i,s=0,r=e.length,o=n;for(i=0;i64)){if(t<0)return!1;s+=6}return s%8==0},construct:function(e){var t,i,r=e.replace(/[\r\n=]/g,""),o=r.length,a=n,c=0,l=[];for(t=0;t>16&255),l.push(c>>8&255),l.push(255&c)),c=c<<6|a.indexOf(r.charAt(t));return 0==(i=o%4*6)?(l.push(c>>16&255),l.push(c>>8&255),l.push(255&c)):18===i?(l.push(c>>10&255),l.push(c>>2&255)):12===i&&l.push(c>>4&255),s?s.from?s.from(l):new s(l):l},predicate:function(e){return s&&s.isBuffer(e)},represent:function(e){var t,i,s="",r=0,o=e.length,a=n;for(t=0;t>18&63],s+=a[r>>12&63],s+=a[r>>6&63],s+=a[63&r]),r=(r<<8)+e[t];return 0==(i=o%3)?(s+=a[r>>18&63],s+=a[r>>12&63],s+=a[r>>6&63],s+=a[63&r]):2===i?(s+=a[r>>10&63],s+=a[r>>4&63],s+=a[r<<2&63],s+=a[64]):1===i&&(s+=a[r>>2&63],s+=a[r<<4&63],s+=a[64],s+=a[64]),s}})},47847:(e,t,i)=>{"use strict";var s=i(42257);e.exports=new s("tag:yaml.org,2002:bool",{kind:"scalar",resolve:function(e){if(null===e)return!1;var t=e.length;return 4===t&&("true"===e||"True"===e||"TRUE"===e)||5===t&&("false"===e||"False"===e||"FALSE"===e)},construct:function(e){return"true"===e||"True"===e||"TRUE"===e},predicate:function(e){return"[object Boolean]"===Object.prototype.toString.call(e)},represent:{lowercase:function(e){return e?"true":"false"},uppercase:function(e){return e?"TRUE":"FALSE"},camelcase:function(e){return e?"True":"False"}},defaultStyle:"lowercase"})},28249:(e,t,i)=>{"use strict";var s=i(90560),r=i(42257),n=new RegExp("^(?:[-+]?(?:0|[1-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?|\\.[0-9_]+(?:[eE][-+]?[0-9]+)?|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$"),o=/^[-+]?[0-9]+e/;e.exports=new r("tag:yaml.org,2002:float",{kind:"scalar",resolve:function(e){return null!==e&&!(!n.test(e)||"_"===e[e.length-1])},construct:function(e){var t,i,s,r;return i="-"===(t=e.replace(/_/g,"").toLowerCase())[0]?-1:1,r=[],"+-".indexOf(t[0])>=0&&(t=t.slice(1)),".inf"===t?1===i?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY:".nan"===t?NaN:t.indexOf(":")>=0?(t.split(":").forEach((function(e){r.unshift(parseFloat(e,10))})),t=0,s=1,r.forEach((function(e){t+=e*s,s*=60})),i*t):i*parseFloat(t,10)},predicate:function(e){return"[object Number]"===Object.prototype.toString.call(e)&&(e%1!=0||s.isNegativeZero(e))},represent:function(e,t){var i;if(isNaN(e))switch(t){case"lowercase":return".nan";case"uppercase":return".NAN";case"camelcase":return".NaN"}else if(Number.POSITIVE_INFINITY===e)switch(t){case"lowercase":return".inf";case"uppercase":return".INF";case"camelcase":return".Inf"}else if(Number.NEGATIVE_INFINITY===e)switch(t){case"lowercase":return"-.inf";case"uppercase":return"-.INF";case"camelcase":return"-.Inf"}else if(s.isNegativeZero(e))return"-0.0";return i=e.toString(10),o.test(i)?i.replace("e",".e"):i},defaultStyle:"lowercase"})},84302:(e,t,i)=>{"use strict";var s=i(90560),r=i(42257);function n(e){return 48<=e&&e<=55}function o(e){return 48<=e&&e<=57}e.exports=new r("tag:yaml.org,2002:int",{kind:"scalar",resolve:function(e){if(null===e)return!1;var t,i,s=e.length,r=0,a=!1;if(!s)return!1;if("-"!==(t=e[r])&&"+"!==t||(t=e[++r]),"0"===t){if(r+1===s)return!0;if("b"===(t=e[++r])){for(r++;r=0?"0b"+e.toString(2):"-0b"+e.toString(2).slice(1)},octal:function(e){return e>=0?"0"+e.toString(8):"-0"+e.toString(8).slice(1)},decimal:function(e){return e.toString(10)},hexadecimal:function(e){return e>=0?"0x"+e.toString(16).toUpperCase():"-0x"+e.toString(16).toUpperCase().slice(1)}},defaultStyle:"decimal",styleAliases:{binary:[2,"bin"],octal:[8,"oct"],decimal:[10,"dec"],hexadecimal:[16,"hex"]}})},1327:(e,t,i)=>{"use strict";var s;try{s=i(87491)}catch(e){"undefined"!=typeof window&&(s=window.esprima)}var r=i(42257);e.exports=new r("tag:yaml.org,2002:js/function",{kind:"scalar",resolve:function(e){if(null===e)return!1;try{var t="("+e+")",i=s.parse(t,{range:!0});return"Program"===i.type&&1===i.body.length&&"ExpressionStatement"===i.body[0].type&&("ArrowFunctionExpression"===i.body[0].expression.type||"FunctionExpression"===i.body[0].expression.type)}catch(e){return!1}},construct:function(e){var t,i="("+e+")",r=s.parse(i,{range:!0}),n=[];if("Program"!==r.type||1!==r.body.length||"ExpressionStatement"!==r.body[0].type||"ArrowFunctionExpression"!==r.body[0].expression.type&&"FunctionExpression"!==r.body[0].expression.type)throw new Error("Failed to resolve function");return r.body[0].expression.params.forEach((function(e){n.push(e.name)})),t=r.body[0].expression.body.range,"BlockStatement"===r.body[0].expression.body.type?new Function(n,i.slice(t[0]+1,t[1]-1)):new Function(n,"return "+i.slice(t[0],t[1]))},predicate:function(e){return"[object Function]"===Object.prototype.toString.call(e)},represent:function(e){return e.toString()}})},55844:(e,t,i)=>{"use strict";var s=i(42257);e.exports=new s("tag:yaml.org,2002:js/regexp",{kind:"scalar",resolve:function(e){if(null===e)return!1;if(0===e.length)return!1;var t=e,i=/\/([gim]*)$/.exec(e),s="";if("/"===t[0]){if(i&&(s=i[1]),s.length>3)return!1;if("/"!==t[t.length-s.length-1])return!1}return!0},construct:function(e){var t=e,i=/\/([gim]*)$/.exec(e),s="";return"/"===t[0]&&(i&&(s=i[1]),t=t.slice(1,t.length-s.length-1)),new RegExp(t,s)},predicate:function(e){return"[object RegExp]"===Object.prototype.toString.call(e)},represent:function(e){var t="/"+e.source+"/";return e.global&&(t+="g"),e.multiline&&(t+="m"),e.ignoreCase&&(t+="i"),t}})},7021:(e,t,i)=>{"use strict";var s=i(42257);e.exports=new s("tag:yaml.org,2002:js/undefined",{kind:"scalar",resolve:function(){return!0},construct:function(){},predicate:function(e){return void 0===e},represent:function(){return""}})},17261:(e,t,i)=>{"use strict";var s=i(42257);e.exports=new s("tag:yaml.org,2002:map",{kind:"mapping",construct:function(e){return null!==e?e:{}}})},3174:(e,t,i)=>{"use strict";var s=i(42257);e.exports=new s("tag:yaml.org,2002:merge",{kind:"scalar",resolve:function(e){return"<<"===e||null===e}})},40707:(e,t,i)=>{"use strict";var s=i(42257);e.exports=new s("tag:yaml.org,2002:null",{kind:"scalar",resolve:function(e){if(null===e)return!0;var t=e.length;return 1===t&&"~"===e||4===t&&("null"===e||"Null"===e||"NULL"===e)},construct:function(){return null},predicate:function(e){return null===e},represent:{canonical:function(){return"~"},lowercase:function(){return"null"},uppercase:function(){return"NULL"},camelcase:function(){return"Null"}},defaultStyle:"lowercase"})},53900:(e,t,i)=>{"use strict";var s=i(42257),r=Object.prototype.hasOwnProperty,n=Object.prototype.toString;e.exports=new s("tag:yaml.org,2002:omap",{kind:"sequence",resolve:function(e){if(null===e)return!0;var t,i,s,o,a,c=[],l=e;for(t=0,i=l.length;t{"use strict";var s=i(42257),r=Object.prototype.toString;e.exports=new s("tag:yaml.org,2002:pairs",{kind:"sequence",resolve:function(e){if(null===e)return!0;var t,i,s,n,o,a=e;for(o=new Array(a.length),t=0,i=a.length;t{"use strict";var s=i(42257);e.exports=new s("tag:yaml.org,2002:seq",{kind:"sequence",construct:function(e){return null!==e?e:[]}})},30768:(e,t,i)=>{"use strict";var s=i(42257),r=Object.prototype.hasOwnProperty;e.exports=new s("tag:yaml.org,2002:set",{kind:"mapping",resolve:function(e){if(null===e)return!0;var t,i=e;for(t in i)if(r.call(i,t)&&null!==i[t])return!1;return!0},construct:function(e){return null!==e?e:{}}})},99678:(e,t,i)=>{"use strict";var s=i(42257);e.exports=new s("tag:yaml.org,2002:str",{kind:"scalar",construct:function(e){return null!==e?e:""}})},1363:(e,t,i)=>{"use strict";var s=i(42257),r=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$"),n=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:[Tt]|[ \\t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \\t]*(Z|([-+])([0-9][0-9]?)(?::([0-9][0-9]))?))?$");e.exports=new s("tag:yaml.org,2002:timestamp",{kind:"scalar",resolve:function(e){return null!==e&&(null!==r.exec(e)||null!==n.exec(e))},construct:function(e){var t,i,s,o,a,c,l,p,A=0,u=null;if(null===(t=r.exec(e))&&(t=n.exec(e)),null===t)throw new Error("Date resolve error");if(i=+t[1],s=+t[2]-1,o=+t[3],!t[4])return new Date(Date.UTC(i,s,o));if(a=+t[4],c=+t[5],l=+t[6],t[7]){for(A=t[7].slice(0,3);A.length<3;)A+="0";A=+A}return t[9]&&(u=6e4*(60*+t[10]+ +(t[11]||0)),"-"===t[9]&&(u=-u)),p=new Date(Date.UTC(i,s,o,a,c,l,A)),u&&p.setTime(p.getTime()-u),p},instanceOf:Date,represent:function(e){return e.toISOString()}})},20097:(e,t,i)=>{"use strict";var s=i(37980),r=i(99719);function n(e,t){return function(){throw new Error("Function yaml."+e+" is removed in js-yaml 4. Use yaml."+t+" instead, which is now safe by default.")}}e.exports.Type=i(95344),e.exports.Schema=i(42459),e.exports.FAILSAFE_SCHEMA=i(69366),e.exports.JSON_SCHEMA=i(74068),e.exports.CORE_SCHEMA=i(75243),e.exports.DEFAULT_SCHEMA=i(66392),e.exports.load=s.load,e.exports.loadAll=s.loadAll,e.exports.dump=r.dump,e.exports.YAMLException=i(34699),e.exports.types={binary:i(3389),float:i(25948),map:i(69274),null:i(91237),pairs:i(97988),set:i(576),timestamp:i(26405),bool:i(93691),int:i(30492),merge:i(30945),omap:i(4868),seq:i(56821),str:i(43432)},e.exports.safeLoad=n("safeLoad","load"),e.exports.safeLoadAll=n("safeLoadAll","loadAll"),e.exports.safeDump=n("safeDump","dump")},11392:e=>{"use strict";function t(e){return null==e}e.exports.isNothing=t,e.exports.isObject=function(e){return"object"==typeof e&&null!==e},e.exports.toArray=function(e){return Array.isArray(e)?e:t(e)?[]:[e]},e.exports.repeat=function(e,t){var i,s="";for(i=0;i{"use strict";var s=i(11392),r=i(34699),n=i(66392),o=Object.prototype.toString,a=Object.prototype.hasOwnProperty,c=65279,l=9,p=10,A=13,u=32,d=33,h=34,m=35,g=37,f=38,E=39,C=42,y=44,v=45,I=58,B=61,w=62,b=63,Q=64,x=91,k=93,D=96,S=123,_=124,R=125,T={0:"\\0",7:"\\a",8:"\\b",9:"\\t",10:"\\n",11:"\\v",12:"\\f",13:"\\r",27:"\\e",34:'\\"',92:"\\\\",133:"\\N",160:"\\_",8232:"\\L",8233:"\\P"},F=["y","Y","yes","Yes","YES","on","On","ON","n","N","no","No","NO","off","Off","OFF"],N=/^[-+]?[0-9_]+(?::[0-9_]+)+(?:\.[0-9_]*)?$/;function L(e){var t,i,n;if(t=e.toString(16).toUpperCase(),e<=255)i="x",n=2;else if(e<=65535)i="u",n=4;else{if(!(e<=4294967295))throw new r("code point within a string may not be greater than 0xFFFFFFFF");i="U",n=8}return"\\"+i+s.repeat("0",n-t.length)+t}var O=2;function M(e){this.schema=e.schema||n,this.indent=Math.max(1,e.indent||2),this.noArrayIndent=e.noArrayIndent||!1,this.skipInvalid=e.skipInvalid||!1,this.flowLevel=s.isNothing(e.flowLevel)?-1:e.flowLevel,this.styleMap=function(e,t){var i,s,r,n,o,c,l;if(null===t)return{};for(i={},r=0,n=(s=Object.keys(t)).length;r=55296&&s<=56319&&t+1=56320&&i<=57343?1024*(s-55296)+i-56320+65536:s}function q(e){return/^\n* /.test(e)}var Y=1,W=2,z=3,$=4,X=5;function K(e,t,i,s,n){e.dump=function(){if(0===t.length)return e.quotingType===O?'""':"''";if(!e.noCompatMode&&(-1!==F.indexOf(t)||N.test(t)))return e.quotingType===O?'"'+t+'"':"'"+t+"'";var o=e.indent*Math.max(1,i),a=-1===e.lineWidth?-1:Math.max(Math.min(e.lineWidth,40),e.lineWidth-o),l=s||e.flowLevel>-1&&i>=e.flowLevel;switch(function(e,t,i,s,r,n,o,a){var l,A,u=0,T=null,F=!1,N=!1,L=-1!==s,M=-1,U=V(A=J(e,0))&&A!==c&&!G(A)&&A!==v&&A!==b&&A!==I&&A!==y&&A!==x&&A!==k&&A!==S&&A!==R&&A!==m&&A!==f&&A!==C&&A!==d&&A!==_&&A!==B&&A!==w&&A!==E&&A!==h&&A!==g&&A!==Q&&A!==D&&function(e){return!G(e)&&e!==I}(J(e,e.length-1));if(t||o)for(l=0;l=65536?l+=2:l++){if(!V(u=J(e,l)))return X;U=U&&H(u,T,a),T=u}else{for(l=0;l=65536?l+=2:l++){if((u=J(e,l))===p)F=!0,L&&(N=N||l-M-1>s&&" "!==e[M+1],M=l);else if(!V(u))return X;U=U&&H(u,T,a),T=u}N=N||L&&l-M-1>s&&" "!==e[M+1]}return F||N?i>9&&q(e)?X:o?n===O?X:W:N?$:z:!U||o||r(e)?n===O?X:W:Y}(t,l,e.indent,a,(function(t){return function(e,t){var i,s;for(i=0,s=e.implicitTypes.length;i"+Z(t,e.indent)+ee(U(function(e,t){for(var i,s,r,n=/(\n+)([^\n]*)/g,o=(r=-1!==(r=e.indexOf("\n"))?r:e.length,n.lastIndex=r,te(e.slice(0,r),t)),a="\n"===e[0]||" "===e[0];s=n.exec(e);){var c=s[1],l=s[2];i=" "===l[0],o+=c+(a||i||""===l?"":"\n")+te(l,t),a=i}return o}(t,a),o));case X:return'"'+function(e){for(var t,i="",s=0,r=0;r=65536?r+=2:r++)s=J(e,r),!(t=T[s])&&V(s)?(i+=e[r],s>=65536&&(i+=e[r+1])):i+=t||L(s);return i}(t)+'"';default:throw new r("impossible error: invalid scalar style")}}()}function Z(e,t){var i=q(e)?String(t):"",s="\n"===e[e.length-1];return i+(!s||"\n"!==e[e.length-2]&&"\n"!==e?s?"":"-":"+")+"\n"}function ee(e){return"\n"===e[e.length-1]?e.slice(0,-1):e}function te(e,t){if(""===e||" "===e[0])return e;for(var i,s,r=/ [^ ]/g,n=0,o=0,a=0,c="";i=r.exec(e);)(a=i.index)-n>t&&(s=o>n?o:a,c+="\n"+e.slice(n,s),n=s+1),o=a;return c+="\n",e.length-n>t&&o>n?c+=e.slice(n,o)+"\n"+e.slice(o+1):c+=e.slice(n),c.slice(1)}function ie(e,t,i,s){var r,n,o,a="",c=e.tag;for(r=0,n=i.length;r tag resolver accepts not "'+A+'" style');s=p.represent[A](t,A)}e.dump=s}return!0}return!1}function re(e,t,i,s,n,a,c){e.tag=null,e.dump=i,se(e,i,!1)||se(e,i,!0);var l,A=o.call(e.dump),u=s;s&&(s=e.flowLevel<0||e.flowLevel>t);var d,h,m="[object Object]"===A||"[object Array]"===A;if(m&&(h=-1!==(d=e.duplicates.indexOf(i))),(null!==e.tag&&"?"!==e.tag||h||2!==e.indent&&t>0)&&(n=!1),h&&e.usedDuplicates[d])e.dump="*ref_"+d;else{if(m&&h&&!e.usedDuplicates[d]&&(e.usedDuplicates[d]=!0),"[object Object]"===A)s&&0!==Object.keys(e.dump).length?(function(e,t,i,s){var n,o,a,c,l,A,u="",d=e.tag,h=Object.keys(i);if(!0===e.sortKeys)h.sort();else if("function"==typeof e.sortKeys)h.sort(e.sortKeys);else if(e.sortKeys)throw new r("sortKeys must be a boolean or a function");for(n=0,o=h.length;n1024)&&(e.dump&&p===e.dump.charCodeAt(0)?A+="?":A+="? "),A+=e.dump,l&&(A+=P(e,t)),re(e,t+1,c,!0,l)&&(e.dump&&p===e.dump.charCodeAt(0)?A+=":":A+=": ",u+=A+=e.dump));e.tag=d,e.dump=u||"{}"}(e,t,e.dump,n),h&&(e.dump="&ref_"+d+e.dump)):(function(e,t,i){var s,r,n,o,a,c="",l=e.tag,p=Object.keys(i);for(s=0,r=p.length;s1024&&(a+="? "),a+=e.dump+(e.condenseFlow?'"':"")+":"+(e.condenseFlow?"":" "),re(e,t,o,!1,!1)&&(c+=a+=e.dump));e.tag=l,e.dump="{"+c+"}"}(e,t,e.dump),h&&(e.dump="&ref_"+d+" "+e.dump));else if("[object Array]"===A)s&&0!==e.dump.length?(e.noArrayIndent&&!c&&t>0?ie(e,t-1,e.dump,n):ie(e,t,e.dump,n),h&&(e.dump="&ref_"+d+e.dump)):(function(e,t,i){var s,r,n,o="",a=e.tag;for(s=0,r=i.length;s",e.dump=l+" "+e.dump)}return!0}function ne(e,t){var i,s,r=[],n=[];for(oe(e,r,n),i=0,s=n.length;i{"use strict";function t(e,t){var i="",s=e.reason||"(unknown reason)";return e.mark?(e.mark.name&&(i+='in "'+e.mark.name+'" '),i+="("+(e.mark.line+1)+":"+(e.mark.column+1)+")",!t&&e.mark.snippet&&(i+="\n\n"+e.mark.snippet),s+" "+i):s}function i(e,i){Error.call(this),this.name="YAMLException",this.reason=e,this.mark=i,this.message=t(this,!1),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack||""}i.prototype=Object.create(Error.prototype),i.prototype.constructor=i,i.prototype.toString=function(e){return this.name+": "+t(this,e)},e.exports=i},37980:(e,t,i)=>{"use strict";var s=i(11392),r=i(34699),n=i(25638),o=i(66392),a=Object.prototype.hasOwnProperty,c=1,l=2,p=3,A=4,u=1,d=2,h=3,m=/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/,g=/[\x85\u2028\u2029]/,f=/[,\[\]\{\}]/,E=/^(?:!|!!|![a-z\-]+!)$/i,C=/^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i;function y(e){return Object.prototype.toString.call(e)}function v(e){return 10===e||13===e}function I(e){return 9===e||32===e}function B(e){return 9===e||32===e||10===e||13===e}function w(e){return 44===e||91===e||93===e||123===e||125===e}function b(e){var t;return 48<=e&&e<=57?e-48:97<=(t=32|e)&&t<=102?t-97+10:-1}function Q(e){return 48===e?"\0":97===e?"":98===e?"\b":116===e||9===e?"\t":110===e?"\n":118===e?"\v":102===e?"\f":114===e?"\r":101===e?"":32===e?" ":34===e?'"':47===e?"/":92===e?"\\":78===e?"…":95===e?" ":76===e?"\u2028":80===e?"\u2029":""}function x(e){return e<=65535?String.fromCharCode(e):String.fromCharCode(55296+(e-65536>>10),56320+(e-65536&1023))}for(var k=new Array(256),D=new Array(256),S=0;S<256;S++)k[S]=Q(S)?1:0,D[S]=Q(S);function _(e,t){this.input=e,this.filename=t.filename||null,this.schema=t.schema||o,this.onWarning=t.onWarning||null,this.legacy=t.legacy||!1,this.json=t.json||!1,this.listener=t.listener||null,this.implicitTypes=this.schema.compiledImplicit,this.typeMap=this.schema.compiledTypeMap,this.length=e.length,this.position=0,this.line=0,this.lineStart=0,this.lineIndent=0,this.firstTabInLine=-1,this.documents=[]}function R(e,t){var i={name:e.filename,buffer:e.input.slice(0,-1),position:e.position,line:e.line,column:e.position-e.lineStart};return i.snippet=n(i),new r(t,i)}function T(e,t){throw R(e,t)}function F(e,t){e.onWarning&&e.onWarning.call(null,R(e,t))}var N={YAML:function(e,t,i){var s,r,n;null!==e.version&&T(e,"duplication of %YAML directive"),1!==i.length&&T(e,"YAML directive accepts exactly one argument"),null===(s=/^([0-9]+)\.([0-9]+)$/.exec(i[0]))&&T(e,"ill-formed argument of the YAML directive"),r=parseInt(s[1],10),n=parseInt(s[2],10),1!==r&&T(e,"unacceptable YAML version of the document"),e.version=i[0],e.checkLineBreaks=n<2,1!==n&&2!==n&&F(e,"unsupported YAML version of the document")},TAG:function(e,t,i){var s,r;2!==i.length&&T(e,"TAG directive accepts exactly two arguments"),s=i[0],r=i[1],E.test(s)||T(e,"ill-formed tag handle (first argument) of the TAG directive"),a.call(e.tagMap,s)&&T(e,'there is a previously declared suffix for "'+s+'" tag handle'),C.test(r)||T(e,"ill-formed tag prefix (second argument) of the TAG directive");try{r=decodeURIComponent(r)}catch(t){T(e,"tag prefix is malformed: "+r)}e.tagMap[s]=r}};function L(e,t,i,s){var r,n,o,a;if(t1&&(e.result+=s.repeat("\n",t-1))}function j(e,t){var i,s,r=e.tag,n=e.anchor,o=[],a=!1;if(-1!==e.firstTabInLine)return!1;for(null!==e.anchor&&(e.anchorMap[e.anchor]=o),s=e.input.charCodeAt(e.position);0!==s&&(-1!==e.firstTabInLine&&(e.position=e.firstTabInLine,T(e,"tab characters must not be used in indentation")),45===s)&&B(e.input.charCodeAt(e.position+1));)if(a=!0,e.position++,P(e,!0,-1)&&e.lineIndent<=t)o.push(null),s=e.input.charCodeAt(e.position);else if(i=e.line,q(e,t,p,!1,!0),o.push(e.result),P(e,!0,-1),s=e.input.charCodeAt(e.position),(e.line===i||e.lineIndent>t)&&0!==s)T(e,"bad indentation of a sequence entry");else if(e.lineIndentt?_=1:e.lineIndent===t?_=0:e.lineIndentt?_=1:e.lineIndent===t?_=0:e.lineIndentt)&&(C&&(o=e.line,a=e.lineStart,c=e.position),q(e,t,A,!0,r)&&(C?f=e.result:E=e.result),C||(M(e,h,m,g,f,E,o,a,c),g=f=E=null),P(e,!0,-1),p=e.input.charCodeAt(e.position)),(e.line===n||e.lineIndent>t)&&0!==p)T(e,"bad indentation of a mapping entry");else if(e.lineIndent=0))break;0===n?T(e,"bad explicit indentation width of a block scalar; it cannot be less than one"):p?T(e,"repeat of an indentation width identifier"):(A=t+n-1,p=!0)}if(I(o)){do{o=e.input.charCodeAt(++e.position)}while(I(o));if(35===o)do{o=e.input.charCodeAt(++e.position)}while(!v(o)&&0!==o)}for(;0!==o;){for(U(e),e.lineIndent=0,o=e.input.charCodeAt(e.position);(!p||e.lineIndentA&&(A=e.lineIndent),v(o))m++;else{if(e.lineIndent0){for(r=o,n=0;r>0;r--)(o=b(a=e.input.charCodeAt(++e.position)))>=0?n=(n<<4)+o:T(e,"expected hexadecimal character");e.result+=x(n),e.position++}else T(e,"unknown escape sequence");i=s=e.position}else v(a)?(L(e,i,s,!0),V(e,P(e,!1,t)),i=s=e.position):e.position===e.lineStart&&G(e)?T(e,"unexpected end of the document within a double quoted scalar"):(e.position++,s=e.position)}T(e,"unexpected end of the stream within a double quoted scalar")}(e,Q)?F=!0:function(e){var t,i,s;if(42!==(s=e.input.charCodeAt(e.position)))return!1;for(s=e.input.charCodeAt(++e.position),t=e.position;0!==s&&!B(s)&&!w(s);)s=e.input.charCodeAt(++e.position);return e.position===t&&T(e,"name of an alias node must contain at least one character"),i=e.input.slice(t,e.position),a.call(e.anchorMap,i)||T(e,'unidentified alias "'+i+'"'),e.result=e.anchorMap[i],P(e,!0,-1),!0}(e)?(F=!0,null===e.tag&&null===e.anchor||T(e,"alias node should not have any properties")):function(e,t,i){var s,r,n,o,a,c,l,p,A=e.kind,u=e.result;if(B(p=e.input.charCodeAt(e.position))||w(p)||35===p||38===p||42===p||33===p||124===p||62===p||39===p||34===p||37===p||64===p||96===p)return!1;if((63===p||45===p)&&(B(s=e.input.charCodeAt(e.position+1))||i&&w(s)))return!1;for(e.kind="scalar",e.result="",r=n=e.position,o=!1;0!==p;){if(58===p){if(B(s=e.input.charCodeAt(e.position+1))||i&&w(s))break}else if(35===p){if(B(e.input.charCodeAt(e.position-1)))break}else{if(e.position===e.lineStart&&G(e)||i&&w(p))break;if(v(p)){if(a=e.line,c=e.lineStart,l=e.lineIndent,P(e,!1,-1),e.lineIndent>=t){o=!0,p=e.input.charCodeAt(e.position);continue}e.position=n,e.line=a,e.lineStart=c,e.lineIndent=l;break}}o&&(L(e,r,n,!1),V(e,e.line-a),r=n=e.position,o=!1),I(p)||(n=e.position+1),p=e.input.charCodeAt(++e.position)}return L(e,r,n,!1),!!e.result||(e.kind=A,e.result=u,!1)}(e,Q,c===i)&&(F=!0,null===e.tag&&(e.tag="?")),null!==e.anchor&&(e.anchorMap[e.anchor]=e.result)):0===_&&(F=g&&j(e,S))),null===e.tag)null!==e.anchor&&(e.anchorMap[e.anchor]=e.result);else if("?"===e.tag){for(null!==e.result&&"scalar"!==e.kind&&T(e,'unacceptable node kind for ! tag; it should be "scalar", not "'+e.kind+'"'),f=0,E=e.implicitTypes.length;f"),null!==e.result&&y.kind!==e.kind&&T(e,"unacceptable node kind for !<"+e.tag+'> tag; it should be "'+y.kind+'", not "'+e.kind+'"'),y.resolve(e.result,e.tag)?(e.result=y.construct(e.result,e.tag),null!==e.anchor&&(e.anchorMap[e.anchor]=e.result)):T(e,"cannot resolve a node with !<"+e.tag+"> explicit tag")}return null!==e.listener&&e.listener("close",e),null!==e.tag||null!==e.anchor||F}function Y(e){var t,i,s,r,n=e.position,o=!1;for(e.version=null,e.checkLineBreaks=e.legacy,e.tagMap=Object.create(null),e.anchorMap=Object.create(null);0!==(r=e.input.charCodeAt(e.position))&&(P(e,!0,-1),r=e.input.charCodeAt(e.position),!(e.lineIndent>0||37!==r));){for(o=!0,r=e.input.charCodeAt(++e.position),t=e.position;0!==r&&!B(r);)r=e.input.charCodeAt(++e.position);for(s=[],(i=e.input.slice(t,e.position)).length<1&&T(e,"directive name must not be less than one character in length");0!==r;){for(;I(r);)r=e.input.charCodeAt(++e.position);if(35===r){do{r=e.input.charCodeAt(++e.position)}while(0!==r&&!v(r));break}if(v(r))break;for(t=e.position;0!==r&&!B(r);)r=e.input.charCodeAt(++e.position);s.push(e.input.slice(t,e.position))}0!==r&&U(e),a.call(N,i)?N[i](e,i,s):F(e,'unknown document directive "'+i+'"')}P(e,!0,-1),0===e.lineIndent&&45===e.input.charCodeAt(e.position)&&45===e.input.charCodeAt(e.position+1)&&45===e.input.charCodeAt(e.position+2)?(e.position+=3,P(e,!0,-1)):o&&T(e,"directives end mark is expected"),q(e,e.lineIndent-1,A,!1,!0),P(e,!0,-1),e.checkLineBreaks&&g.test(e.input.slice(n,e.position))&&F(e,"non-ASCII line breaks are interpreted as content"),e.documents.push(e.result),e.position===e.lineStart&&G(e)?46===e.input.charCodeAt(e.position)&&(e.position+=3,P(e,!0,-1)):e.position{"use strict";var s=i(34699),r=i(95344);function n(e,t){var i=[];return e[t].forEach((function(e){var t=i.length;i.forEach((function(i,s){i.tag===e.tag&&i.kind===e.kind&&i.multi===e.multi&&(t=s)})),i[t]=e})),i}function o(e){return this.extend(e)}o.prototype.extend=function(e){var t=[],i=[];if(e instanceof r)i.push(e);else if(Array.isArray(e))i=i.concat(e);else{if(!e||!Array.isArray(e.implicit)&&!Array.isArray(e.explicit))throw new s("Schema.extend argument should be a Type, [ Type ], or a schema definition ({ implicit: [...], explicit: [...] })");e.implicit&&(t=t.concat(e.implicit)),e.explicit&&(i=i.concat(e.explicit))}t.forEach((function(e){if(!(e instanceof r))throw new s("Specified list of YAML types (or a single Type object) contains a non-Type object.");if(e.loadKind&&"scalar"!==e.loadKind)throw new s("There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.");if(e.multi)throw new s("There is a multi type in the implicit list of a schema. Multi tags can only be listed as explicit.")})),i.forEach((function(e){if(!(e instanceof r))throw new s("Specified list of YAML types (or a single Type object) contains a non-Type object.")}));var a=Object.create(o.prototype);return a.implicit=(this.implicit||[]).concat(t),a.explicit=(this.explicit||[]).concat(i),a.compiledImplicit=n(a,"implicit"),a.compiledExplicit=n(a,"explicit"),a.compiledTypeMap=function(){var e,t,i={scalar:{},sequence:{},mapping:{},fallback:{},multi:{scalar:[],sequence:[],mapping:[],fallback:[]}};function s(e){e.multi?(i.multi[e.kind].push(e),i.multi.fallback.push(e)):i[e.kind][e.tag]=i.fallback[e.tag]=e}for(e=0,t=arguments.length;e{"use strict";e.exports=i(74068)},66392:(e,t,i)=>{"use strict";e.exports=i(75243).extend({implicit:[i(26405),i(30945)],explicit:[i(3389),i(4868),i(97988),i(576)]})},69366:(e,t,i)=>{"use strict";var s=i(42459);e.exports=new s({explicit:[i(43432),i(56821),i(69274)]})},74068:(e,t,i)=>{"use strict";e.exports=i(69366).extend({implicit:[i(91237),i(93691),i(30492),i(25948)]})},25638:(e,t,i)=>{"use strict";var s=i(11392);function r(e,t,i,s,r){var n="",o="",a=Math.floor(r/2)-1;return s-t>a&&(t=s-a+(n=" ... ").length),i-s>a&&(i=s+a-(o=" ...").length),{str:n+e.slice(t,i).replace(/\t/g,"→")+o,pos:s-t+n.length}}function n(e,t){return s.repeat(" ",t-e.length)+e}e.exports=function(e,t){if(t=Object.create(t||null),!e.buffer)return null;t.maxLength||(t.maxLength=79),"number"!=typeof t.indent&&(t.indent=1),"number"!=typeof t.linesBefore&&(t.linesBefore=3),"number"!=typeof t.linesAfter&&(t.linesAfter=2);for(var i,o=/\r?\n|\r|\0/g,a=[0],c=[],l=-1;i=o.exec(e.buffer);)c.push(i.index),a.push(i.index+i[0].length),e.position<=i.index&&l<0&&(l=a.length-2);l<0&&(l=a.length-1);var p,A,u="",d=Math.min(e.line+t.linesAfter,c.length).toString().length,h=t.maxLength-(t.indent+d+3);for(p=1;p<=t.linesBefore&&!(l-p<0);p++)A=r(e.buffer,a[l-p],c[l-p],e.position-(a[l]-a[l-p]),h),u=s.repeat(" ",t.indent)+n((e.line-p+1).toString(),d)+" | "+A.str+"\n"+u;for(A=r(e.buffer,a[l],c[l],e.position,h),u+=s.repeat(" ",t.indent)+n((e.line+1).toString(),d)+" | "+A.str+"\n",u+=s.repeat("-",t.indent+d+3+A.pos)+"^\n",p=1;p<=t.linesAfter&&!(l+p>=c.length);p++)A=r(e.buffer,a[l+p],c[l+p],e.position-(a[l]-a[l+p]),h),u+=s.repeat(" ",t.indent)+n((e.line+p+1).toString(),d)+" | "+A.str+"\n";return u.replace(/\n$/,"")}},95344:(e,t,i)=>{"use strict";var s=i(34699),r=["kind","multi","resolve","construct","instanceOf","predicate","represent","representName","defaultStyle","styleAliases"],n=["scalar","sequence","mapping"];e.exports=function(e,t){var i,o;if(t=t||{},Object.keys(t).forEach((function(t){if(-1===r.indexOf(t))throw new s('Unknown option "'+t+'" is met in definition of "'+e+'" YAML type.')})),this.options=t,this.tag=e,this.kind=t.kind||null,this.resolve=t.resolve||function(){return!0},this.construct=t.construct||function(e){return e},this.instanceOf=t.instanceOf||null,this.predicate=t.predicate||null,this.represent=t.represent||null,this.representName=t.representName||null,this.defaultStyle=t.defaultStyle||null,this.multi=t.multi||!1,this.styleAliases=(i=t.styleAliases||null,o={},null!==i&&Object.keys(i).forEach((function(e){i[e].forEach((function(t){o[String(t)]=e}))})),o),-1===n.indexOf(this.kind))throw new s('Unknown kind "'+this.kind+'" is specified for "'+e+'" YAML type.')}},3389:(e,t,i)=>{"use strict";var s=i(95344),r="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\r";e.exports=new s("tag:yaml.org,2002:binary",{kind:"scalar",resolve:function(e){if(null===e)return!1;var t,i,s=0,n=e.length,o=r;for(i=0;i64)){if(t<0)return!1;s+=6}return s%8==0},construct:function(e){var t,i,s=e.replace(/[\r\n=]/g,""),n=s.length,o=r,a=0,c=[];for(t=0;t>16&255),c.push(a>>8&255),c.push(255&a)),a=a<<6|o.indexOf(s.charAt(t));return 0==(i=n%4*6)?(c.push(a>>16&255),c.push(a>>8&255),c.push(255&a)):18===i?(c.push(a>>10&255),c.push(a>>2&255)):12===i&&c.push(a>>4&255),new Uint8Array(c)},predicate:function(e){return"[object Uint8Array]"===Object.prototype.toString.call(e)},represent:function(e){var t,i,s="",n=0,o=e.length,a=r;for(t=0;t>18&63],s+=a[n>>12&63],s+=a[n>>6&63],s+=a[63&n]),n=(n<<8)+e[t];return 0==(i=o%3)?(s+=a[n>>18&63],s+=a[n>>12&63],s+=a[n>>6&63],s+=a[63&n]):2===i?(s+=a[n>>10&63],s+=a[n>>4&63],s+=a[n<<2&63],s+=a[64]):1===i&&(s+=a[n>>2&63],s+=a[n<<4&63],s+=a[64],s+=a[64]),s}})},93691:(e,t,i)=>{"use strict";var s=i(95344);e.exports=new s("tag:yaml.org,2002:bool",{kind:"scalar",resolve:function(e){if(null===e)return!1;var t=e.length;return 4===t&&("true"===e||"True"===e||"TRUE"===e)||5===t&&("false"===e||"False"===e||"FALSE"===e)},construct:function(e){return"true"===e||"True"===e||"TRUE"===e},predicate:function(e){return"[object Boolean]"===Object.prototype.toString.call(e)},represent:{lowercase:function(e){return e?"true":"false"},uppercase:function(e){return e?"TRUE":"FALSE"},camelcase:function(e){return e?"True":"False"}},defaultStyle:"lowercase"})},25948:(e,t,i)=>{"use strict";var s=i(11392),r=i(95344),n=new RegExp("^(?:[-+]?(?:[0-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?|\\.[0-9_]+(?:[eE][-+]?[0-9]+)?|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$"),o=/^[-+]?[0-9]+e/;e.exports=new r("tag:yaml.org,2002:float",{kind:"scalar",resolve:function(e){return null!==e&&!(!n.test(e)||"_"===e[e.length-1])},construct:function(e){var t,i;return i="-"===(t=e.replace(/_/g,"").toLowerCase())[0]?-1:1,"+-".indexOf(t[0])>=0&&(t=t.slice(1)),".inf"===t?1===i?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY:".nan"===t?NaN:i*parseFloat(t,10)},predicate:function(e){return"[object Number]"===Object.prototype.toString.call(e)&&(e%1!=0||s.isNegativeZero(e))},represent:function(e,t){var i;if(isNaN(e))switch(t){case"lowercase":return".nan";case"uppercase":return".NAN";case"camelcase":return".NaN"}else if(Number.POSITIVE_INFINITY===e)switch(t){case"lowercase":return".inf";case"uppercase":return".INF";case"camelcase":return".Inf"}else if(Number.NEGATIVE_INFINITY===e)switch(t){case"lowercase":return"-.inf";case"uppercase":return"-.INF";case"camelcase":return"-.Inf"}else if(s.isNegativeZero(e))return"-0.0";return i=e.toString(10),o.test(i)?i.replace("e",".e"):i},defaultStyle:"lowercase"})},30492:(e,t,i)=>{"use strict";var s=i(11392),r=i(95344);function n(e){return 48<=e&&e<=55}function o(e){return 48<=e&&e<=57}e.exports=new r("tag:yaml.org,2002:int",{kind:"scalar",resolve:function(e){if(null===e)return!1;var t,i,s=e.length,r=0,a=!1;if(!s)return!1;if("-"!==(t=e[r])&&"+"!==t||(t=e[++r]),"0"===t){if(r+1===s)return!0;if("b"===(t=e[++r])){for(r++;r=0?"0b"+e.toString(2):"-0b"+e.toString(2).slice(1)},octal:function(e){return e>=0?"0o"+e.toString(8):"-0o"+e.toString(8).slice(1)},decimal:function(e){return e.toString(10)},hexadecimal:function(e){return e>=0?"0x"+e.toString(16).toUpperCase():"-0x"+e.toString(16).toUpperCase().slice(1)}},defaultStyle:"decimal",styleAliases:{binary:[2,"bin"],octal:[8,"oct"],decimal:[10,"dec"],hexadecimal:[16,"hex"]}})},69274:(e,t,i)=>{"use strict";var s=i(95344);e.exports=new s("tag:yaml.org,2002:map",{kind:"mapping",construct:function(e){return null!==e?e:{}}})},30945:(e,t,i)=>{"use strict";var s=i(95344);e.exports=new s("tag:yaml.org,2002:merge",{kind:"scalar",resolve:function(e){return"<<"===e||null===e}})},91237:(e,t,i)=>{"use strict";var s=i(95344);e.exports=new s("tag:yaml.org,2002:null",{kind:"scalar",resolve:function(e){if(null===e)return!0;var t=e.length;return 1===t&&"~"===e||4===t&&("null"===e||"Null"===e||"NULL"===e)},construct:function(){return null},predicate:function(e){return null===e},represent:{canonical:function(){return"~"},lowercase:function(){return"null"},uppercase:function(){return"NULL"},camelcase:function(){return"Null"},empty:function(){return""}},defaultStyle:"lowercase"})},4868:(e,t,i)=>{"use strict";var s=i(95344),r=Object.prototype.hasOwnProperty,n=Object.prototype.toString;e.exports=new s("tag:yaml.org,2002:omap",{kind:"sequence",resolve:function(e){if(null===e)return!0;var t,i,s,o,a,c=[],l=e;for(t=0,i=l.length;t{"use strict";var s=i(95344),r=Object.prototype.toString;e.exports=new s("tag:yaml.org,2002:pairs",{kind:"sequence",resolve:function(e){if(null===e)return!0;var t,i,s,n,o,a=e;for(o=new Array(a.length),t=0,i=a.length;t{"use strict";var s=i(95344);e.exports=new s("tag:yaml.org,2002:seq",{kind:"sequence",construct:function(e){return null!==e?e:[]}})},576:(e,t,i)=>{"use strict";var s=i(95344),r=Object.prototype.hasOwnProperty;e.exports=new s("tag:yaml.org,2002:set",{kind:"mapping",resolve:function(e){if(null===e)return!0;var t,i=e;for(t in i)if(r.call(i,t)&&null!==i[t])return!1;return!0},construct:function(e){return null!==e?e:{}}})},43432:(e,t,i)=>{"use strict";var s=i(95344);e.exports=new s("tag:yaml.org,2002:str",{kind:"scalar",construct:function(e){return null!==e?e:""}})},26405:(e,t,i)=>{"use strict";var s=i(95344),r=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$"),n=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:[Tt]|[ \\t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \\t]*(Z|([-+])([0-9][0-9]?)(?::([0-9][0-9]))?))?$");e.exports=new s("tag:yaml.org,2002:timestamp",{kind:"scalar",resolve:function(e){return null!==e&&(null!==r.exec(e)||null!==n.exec(e))},construct:function(e){var t,i,s,o,a,c,l,p,A=0,u=null;if(null===(t=r.exec(e))&&(t=n.exec(e)),null===t)throw new Error("Date resolve error");if(i=+t[1],s=+t[2]-1,o=+t[3],!t[4])return new Date(Date.UTC(i,s,o));if(a=+t[4],c=+t[5],l=+t[6],t[7]){for(A=t[7].slice(0,3);A.length<3;)A+="0";A=+A}return t[9]&&(u=6e4*(60*+t[10]+ +(t[11]||0)),"-"===t[9]&&(u=-u)),p=new Date(Date.UTC(i,s,o,a,c,l,A)),u&&p.setTime(p.getTime()-u),p},instanceOf:Date,represent:function(e){return e.toISOString()}})},79049:e=>{var t=Object.prototype.toString;function i(e){return"function"==typeof e.constructor?e.constructor.name:null}e.exports=function(e){if(void 0===e)return"undefined";if(null===e)return"null";var s=typeof e;if("boolean"===s)return"boolean";if("string"===s)return"string";if("number"===s)return"number";if("symbol"===s)return"symbol";if("function"===s)return"GeneratorFunction"===i(e)?"generatorfunction":"function";if(function(e){return Array.isArray?Array.isArray(e):e instanceof Array}(e))return"array";if(function(e){return!(!e.constructor||"function"!=typeof e.constructor.isBuffer)&&e.constructor.isBuffer(e)}(e))return"buffer";if(function(e){try{if("number"==typeof e.length&&"function"==typeof e.callee)return!0}catch(e){if(-1!==e.message.indexOf("callee"))return!0}return!1}(e))return"arguments";if(function(e){return e instanceof Date||"function"==typeof e.toDateString&&"function"==typeof e.getDate&&"function"==typeof e.setDate}(e))return"date";if(function(e){return e instanceof Error||"string"==typeof e.message&&e.constructor&&"number"==typeof e.constructor.stackTraceLimit}(e))return"error";if(function(e){return e instanceof RegExp||"string"==typeof e.flags&&"boolean"==typeof e.ignoreCase&&"boolean"==typeof e.multiline&&"boolean"==typeof e.global}(e))return"regexp";switch(i(e)){case"Symbol":return"symbol";case"Promise":return"promise";case"WeakMap":return"weakmap";case"WeakSet":return"weakset";case"Map":return"map";case"Set":return"set";case"Int8Array":return"int8array";case"Uint8Array":return"uint8array";case"Uint8ClampedArray":return"uint8clampedarray";case"Int16Array":return"int16array";case"Uint16Array":return"uint16array";case"Int32Array":return"int32array";case"Uint32Array":return"uint32array";case"Float32Array":return"float32array";case"Float64Array":return"float64array"}if(function(e){return"function"==typeof e.throw&&"function"==typeof e.return&&"function"==typeof e.next}(e))return"generator";switch(s=t.call(e)){case"[object Object]":return"object";case"[object Map Iterator]":return"mapiterator";case"[object Set Iterator]":return"setiterator";case"[object String Iterator]":return"stringiterator";case"[object Array Iterator]":return"arrayiterator"}return s.slice(8,-1).toLowerCase().replace(/\s/g,"")}},6853:(e,t,i)=>{"use strict";const s=i(47309),r=i(5881),n={info:s.blue("ℹ"),success:s.green("✔"),warning:s.yellow("⚠"),error:s.red("✖")},o={info:s.blue("i"),success:s.green("√"),warning:s.yellow("‼"),error:s.red("×")};e.exports=r()?n:o},29416:(e,t,i)=>{"use strict";const s=i(87406),r=Symbol("max"),n=Symbol("length"),o=Symbol("lengthCalculator"),a=Symbol("allowStale"),c=Symbol("maxAge"),l=Symbol("dispose"),p=Symbol("noDisposeOnSet"),A=Symbol("lruList"),u=Symbol("cache"),d=Symbol("updateAgeOnGet"),h=()=>1,m=(e,t,i)=>{const s=e[u].get(t);if(s){const t=s.value;if(g(e,t)){if(E(e,s),!e[a])return}else i&&(e[d]&&(s.value.now=Date.now()),e[A].unshiftNode(s));return t.value}},g=(e,t)=>{if(!t||!t.maxAge&&!e[c])return!1;const i=Date.now()-t.now;return t.maxAge?i>t.maxAge:e[c]&&i>e[c]},f=e=>{if(e[n]>e[r])for(let t=e[A].tail;e[n]>e[r]&&null!==t;){const i=t.prev;E(e,t),t=i}},E=(e,t)=>{if(t){const i=t.value;e[l]&&e[l](i.key,i.value),e[n]-=i.length,e[u].delete(i.key),e[A].removeNode(t)}};class C{constructor(e,t,i,s,r){this.key=e,this.value=t,this.length=i,this.now=s,this.maxAge=r||0}}const y=(e,t,i,s)=>{let r=i.value;g(e,r)&&(E(e,i),e[a]||(r=void 0)),r&&t.call(s,r.value,r.key,e)};e.exports=class{constructor(e){if("number"==typeof e&&(e={max:e}),e||(e={}),e.max&&("number"!=typeof e.max||e.max<0))throw new TypeError("max must be a non-negative number");this[r]=e.max||1/0;const t=e.length||h;if(this[o]="function"!=typeof t?h:t,this[a]=e.stale||!1,e.maxAge&&"number"!=typeof e.maxAge)throw new TypeError("maxAge must be a number");this[c]=e.maxAge||0,this[l]=e.dispose,this[p]=e.noDisposeOnSet||!1,this[d]=e.updateAgeOnGet||!1,this.reset()}set max(e){if("number"!=typeof e||e<0)throw new TypeError("max must be a non-negative number");this[r]=e||1/0,f(this)}get max(){return this[r]}set allowStale(e){this[a]=!!e}get allowStale(){return this[a]}set maxAge(e){if("number"!=typeof e)throw new TypeError("maxAge must be a non-negative number");this[c]=e,f(this)}get maxAge(){return this[c]}set lengthCalculator(e){"function"!=typeof e&&(e=h),e!==this[o]&&(this[o]=e,this[n]=0,this[A].forEach((e=>{e.length=this[o](e.value,e.key),this[n]+=e.length}))),f(this)}get lengthCalculator(){return this[o]}get length(){return this[n]}get itemCount(){return this[A].length}rforEach(e,t){t=t||this;for(let i=this[A].tail;null!==i;){const s=i.prev;y(this,e,i,t),i=s}}forEach(e,t){t=t||this;for(let i=this[A].head;null!==i;){const s=i.next;y(this,e,i,t),i=s}}keys(){return this[A].toArray().map((e=>e.key))}values(){return this[A].toArray().map((e=>e.value))}reset(){this[l]&&this[A]&&this[A].length&&this[A].forEach((e=>this[l](e.key,e.value))),this[u]=new Map,this[A]=new s,this[n]=0}dump(){return this[A].map((e=>!g(this,e)&&{k:e.key,v:e.value,e:e.now+(e.maxAge||0)})).toArray().filter((e=>e))}dumpLru(){return this[A]}set(e,t,i){if((i=i||this[c])&&"number"!=typeof i)throw new TypeError("maxAge must be a number");const s=i?Date.now():0,a=this[o](t,e);if(this[u].has(e)){if(a>this[r])return E(this,this[u].get(e)),!1;const o=this[u].get(e).value;return this[l]&&(this[p]||this[l](e,o.value)),o.now=s,o.maxAge=i,o.value=t,this[n]+=a-o.length,o.length=a,this.get(e),f(this),!0}const d=new C(e,t,a,s,i);return d.length>this[r]?(this[l]&&this[l](e,t),!1):(this[n]+=d.length,this[A].unshift(d),this[u].set(e,this[A].head),f(this),!0)}has(e){if(!this[u].has(e))return!1;const t=this[u].get(e).value;return!g(this,t)}get(e){return m(this,e,!0)}peek(e){return m(this,e,!1)}pop(){const e=this[A].tail;return e?(E(this,e),e.value):null}del(e){E(this,this[u].get(e))}load(e){this.reset();const t=Date.now();for(let i=e.length-1;i>=0;i--){const s=e[i],r=s.e||0;if(0===r)this.set(s.k,s.v);else{const e=r-t;e>0&&this.set(s.k,s.v,e)}}}prune(){this[u].forEach(((e,t)=>m(this,t,!1)))}}},69759:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});class i extends Error{}class s extends i{constructor(e){super(`Invalid DateTime: ${e.toMessage()}`)}}class r extends i{constructor(e){super(`Invalid Interval: ${e.toMessage()}`)}}class n extends i{constructor(e){super(`Invalid Duration: ${e.toMessage()}`)}}class o extends i{}class a extends i{constructor(e){super(`Invalid unit ${e}`)}}class c extends i{}class l extends i{constructor(){super("Zone is an abstract class")}}const p="numeric",A="short",u="long",d={year:p,month:p,day:p},h={year:p,month:A,day:p},m={year:p,month:A,day:p,weekday:A},g={year:p,month:u,day:p},f={year:p,month:u,day:p,weekday:u},E={hour:p,minute:p},C={hour:p,minute:p,second:p},y={hour:p,minute:p,second:p,timeZoneName:A},v={hour:p,minute:p,second:p,timeZoneName:u},I={hour:p,minute:p,hourCycle:"h23"},B={hour:p,minute:p,second:p,hourCycle:"h23"},w={hour:p,minute:p,second:p,hourCycle:"h23",timeZoneName:A},b={hour:p,minute:p,second:p,hourCycle:"h23",timeZoneName:u},Q={year:p,month:p,day:p,hour:p,minute:p},x={year:p,month:p,day:p,hour:p,minute:p,second:p},k={year:p,month:A,day:p,hour:p,minute:p},D={year:p,month:A,day:p,hour:p,minute:p,second:p},S={year:p,month:A,day:p,weekday:A,hour:p,minute:p},_={year:p,month:u,day:p,hour:p,minute:p,timeZoneName:A},R={year:p,month:u,day:p,hour:p,minute:p,second:p,timeZoneName:A},T={year:p,month:u,day:p,weekday:u,hour:p,minute:p,timeZoneName:u},F={year:p,month:u,day:p,weekday:u,hour:p,minute:p,second:p,timeZoneName:u};class N{get type(){throw new l}get name(){throw new l}get ianaName(){return this.name}get isUniversal(){throw new l}offsetName(e,t){throw new l}formatOffset(e,t){throw new l}offset(e){throw new l}equals(e){throw new l}get isValid(){throw new l}}let L=null;class O extends N{static get instance(){return null===L&&(L=new O),L}get type(){return"system"}get name(){return(new Intl.DateTimeFormat).resolvedOptions().timeZone}get isUniversal(){return!1}offsetName(e,{format:t,locale:i}){return $e(e,t,i)}formatOffset(e,t){return et(this.offset(e),t)}offset(e){return-new Date(e).getTimezoneOffset()}equals(e){return"system"===e.type}get isValid(){return!0}}let M={};const U={year:0,month:1,day:2,era:3,hour:4,minute:5,second:6};let P={};class G extends N{static create(e){return P[e]||(P[e]=new G(e)),P[e]}static resetCache(){P={},M={}}static isValidSpecifier(e){return this.isValidZone(e)}static isValidZone(e){if(!e)return!1;try{return new Intl.DateTimeFormat("en-US",{timeZone:e}).format(),!0}catch(e){return!1}}constructor(e){super(),this.zoneName=e,this.valid=G.isValidZone(e)}get type(){return"iana"}get name(){return this.zoneName}get isUniversal(){return!1}offsetName(e,{format:t,locale:i}){return $e(e,t,i,this.name)}formatOffset(e,t){return et(this.offset(e),t)}offset(e){const t=new Date(e);if(isNaN(t))return NaN;const i=(s=this.name,M[s]||(M[s]=new Intl.DateTimeFormat("en-US",{hour12:!1,timeZone:s,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",era:"short"})),M[s]);var s;let[r,n,o,a,c,l,p]=i.formatToParts?function(e,t){const i=e.formatToParts(t),s=[];for(let e=0;e=0?u:1e3+u,(qe({year:r,month:n,day:o,hour:24===c?0:c,minute:l,second:p,millisecond:0})-A)/6e4}equals(e){return"iana"===e.type&&e.name===this.name}get isValid(){return this.valid}}let V={},j={};function H(e,t={}){const i=JSON.stringify([e,t]);let s=j[i];return s||(s=new Intl.DateTimeFormat(e,t),j[i]=s),s}let J={},q={},Y=null,W={};function z(e,t,i,s){const r=e.listingMode();return"error"===r?null:"en"===r?i(t):s(t)}class ${constructor(e,t,i){this.padTo=i.padTo||0,this.floor=i.floor||!1;const{padTo:s,floor:r,...n}=i;if(!t||Object.keys(n).length>0){const t={useGrouping:!1,...i};i.padTo>0&&(t.minimumIntegerDigits=i.padTo),this.inf=function(e,t={}){const i=JSON.stringify([e,t]);let s=J[i];return s||(s=new Intl.NumberFormat(e,t),J[i]=s),s}(e,t)}}format(e){if(this.inf){const t=this.floor?Math.floor(e):e;return this.inf.format(t)}return Me(this.floor?Math.floor(e):Ve(e,3),this.padTo)}}class X{constructor(e,t,i){let s;if(this.opts=i,this.originalZone=void 0,this.opts.timeZone)this.dt=e;else if("fixed"===e.zone.type){const t=e.offset/60*-1,i=t>=0?`Etc/GMT+${t}`:`Etc/GMT${t}`;0!==e.offset&&G.create(i).valid?(s=i,this.dt=e):(s="UTC",this.dt=0===e.offset?e:e.setZone("UTC").plus({minutes:e.offset}),this.originalZone=e.zone)}else"system"===e.zone.type?this.dt=e:"iana"===e.zone.type?(this.dt=e,s=e.zone.name):(s="UTC",this.dt=e.setZone("UTC").plus({minutes:e.offset}),this.originalZone=e.zone);const r={...this.opts};r.timeZone=r.timeZone||s,this.dtf=H(t,r)}format(){return this.originalZone?this.formatToParts().map((({value:e})=>e)).join(""):this.dtf.format(this.dt.toJSDate())}formatToParts(){const e=this.dtf.formatToParts(this.dt.toJSDate());return this.originalZone?e.map((e=>{if("timeZoneName"===e.type){const t=this.originalZone.offsetName(this.dt.ts,{locale:this.dt.locale,format:this.opts.timeZoneName});return{...e,value:t}}return e})):e}resolvedOptions(){return this.dtf.resolvedOptions()}}class K{constructor(e,t,i){this.opts={style:"long",...i},!t&&Re()&&(this.rtf=function(e,t={}){const{base:i,...s}=t,r=JSON.stringify([e,s]);let n=q[r];return n||(n=new Intl.RelativeTimeFormat(e,t),q[r]=n),n}(e,i))}format(e,t){return this.rtf?this.rtf.format(e,t):function(e,t,i="always",s=!1){const r={years:["year","yr."],quarters:["quarter","qtr."],months:["month","mo."],weeks:["week","wk."],days:["day","day","days"],hours:["hour","hr."],minutes:["minute","min."],seconds:["second","sec."]},n=-1===["hours","minutes","seconds"].indexOf(e);if("auto"===i&&n){const i="days"===e;switch(t){case 1:return i?"tomorrow":`next ${r[e][0]}`;case-1:return i?"yesterday":`last ${r[e][0]}`;case 0:return i?"today":`this ${r[e][0]}`}}const o=Object.is(t,-0)||t<0,a=Math.abs(t),c=1===a,l=r[e],p=s?c?l[1]:l[2]||l[1]:c?r[e][0]:e;return o?`${a} ${p} ago`:`in ${a} ${p}`}(t,e,this.opts.numeric,"long"!==this.opts.style)}formatToParts(e,t){return this.rtf?this.rtf.formatToParts(e,t):[]}}const Z={firstDay:1,minimalDays:4,weekend:[6,7]};class ee{static fromOpts(e){return ee.create(e.locale,e.numberingSystem,e.outputCalendar,e.weekSettings,e.defaultToEN)}static create(e,t,i,s,r=!1){const n=e||de.defaultLocale,o=n||(r?"en-US":Y||(Y=(new Intl.DateTimeFormat).resolvedOptions().locale,Y)),a=t||de.defaultNumberingSystem,c=i||de.defaultOutputCalendar,l=Le(s)||de.defaultWeekSettings;return new ee(o,a,c,l,n)}static resetCache(){Y=null,j={},J={},q={}}static fromObject({locale:e,numberingSystem:t,outputCalendar:i,weekSettings:s}={}){return ee.create(e,t,i,s)}constructor(e,t,i,s,r){const[n,o,a]=function(e){const t=e.indexOf("-x-");-1!==t&&(e=e.substring(0,t));const i=e.indexOf("-u-");if(-1===i)return[e];{let t,s;try{t=H(e).resolvedOptions(),s=e}catch(r){const n=e.substring(0,i);t=H(n).resolvedOptions(),s=n}const{numberingSystem:r,calendar:n}=t;return[s,r,n]}}(e);this.locale=n,this.numberingSystem=t||o||null,this.outputCalendar=i||a||null,this.weekSettings=s,this.intl=function(e,t,i){return i||t?(e.includes("-u-")||(e+="-u"),i&&(e+=`-ca-${i}`),t&&(e+=`-nu-${t}`),e):e}(this.locale,this.numberingSystem,this.outputCalendar),this.weekdaysCache={format:{},standalone:{}},this.monthsCache={format:{},standalone:{}},this.meridiemCache=null,this.eraCache={},this.specifiedLocale=r,this.fastNumbersCached=null}get fastNumbers(){var e;return null==this.fastNumbersCached&&(this.fastNumbersCached=(!(e=this).numberingSystem||"latn"===e.numberingSystem)&&("latn"===e.numberingSystem||!e.locale||e.locale.startsWith("en")||"latn"===new Intl.DateTimeFormat(e.intl).resolvedOptions().numberingSystem)),this.fastNumbersCached}listingMode(){const e=this.isEnglish(),t=!(null!==this.numberingSystem&&"latn"!==this.numberingSystem||null!==this.outputCalendar&&"gregory"!==this.outputCalendar);return e&&t?"en":"intl"}clone(e){return e&&0!==Object.getOwnPropertyNames(e).length?ee.create(e.locale||this.specifiedLocale,e.numberingSystem||this.numberingSystem,e.outputCalendar||this.outputCalendar,Le(e.weekSettings)||this.weekSettings,e.defaultToEN||!1):this}redefaultToEN(e={}){return this.clone({...e,defaultToEN:!0})}redefaultToSystem(e={}){return this.clone({...e,defaultToEN:!1})}months(e,t=!1){return z(this,e,nt,(()=>{const i=t?{month:e,day:"numeric"}:{month:e},s=t?"format":"standalone";return this.monthsCache[s][e]||(this.monthsCache[s][e]=function(e){const t=[];for(let i=1;i<=12;i++){const s=os.utc(2009,i,1);t.push(e(s))}return t}((e=>this.extract(e,i,"month")))),this.monthsCache[s][e]}))}weekdays(e,t=!1){return z(this,e,lt,(()=>{const i=t?{weekday:e,year:"numeric",month:"long",day:"numeric"}:{weekday:e},s=t?"format":"standalone";return this.weekdaysCache[s][e]||(this.weekdaysCache[s][e]=function(e){const t=[];for(let i=1;i<=7;i++){const s=os.utc(2016,11,13+i);t.push(e(s))}return t}((e=>this.extract(e,i,"weekday")))),this.weekdaysCache[s][e]}))}meridiems(){return z(this,void 0,(()=>pt),(()=>{if(!this.meridiemCache){const e={hour:"numeric",hourCycle:"h12"};this.meridiemCache=[os.utc(2016,11,13,9),os.utc(2016,11,13,19)].map((t=>this.extract(t,e,"dayperiod")))}return this.meridiemCache}))}eras(e){return z(this,e,ht,(()=>{const t={era:e};return this.eraCache[e]||(this.eraCache[e]=[os.utc(-40,1,1),os.utc(2017,1,1)].map((e=>this.extract(e,t,"era")))),this.eraCache[e]}))}extract(e,t,i){const s=this.dtFormatter(e,t).formatToParts().find((e=>e.type.toLowerCase()===i));return s?s.value:null}numberFormatter(e={}){return new $(this.intl,e.forceSimple||this.fastNumbers,e)}dtFormatter(e,t={}){return new X(e,this.intl,t)}relFormatter(e={}){return new K(this.intl,this.isEnglish(),e)}listFormatter(e={}){return function(e,t={}){const i=JSON.stringify([e,t]);let s=V[i];return s||(s=new Intl.ListFormat(e,t),V[i]=s),s}(this.intl,e)}isEnglish(){return"en"===this.locale||"en-us"===this.locale.toLowerCase()||new Intl.DateTimeFormat(this.intl).resolvedOptions().locale.startsWith("en-us")}getWeekSettings(){return this.weekSettings?this.weekSettings:Te()?function(e){let t=W[e];if(!t){const i=new Intl.Locale(e);t="getWeekInfo"in i?i.getWeekInfo():i.weekInfo,W[e]=t}return t}(this.locale):Z}getStartOfWeek(){return this.getWeekSettings().firstDay}getMinDaysInFirstWeek(){return this.getWeekSettings().minimalDays}getWeekendDays(){return this.getWeekSettings().weekend}equals(e){return this.locale===e.locale&&this.numberingSystem===e.numberingSystem&&this.outputCalendar===e.outputCalendar}}let te=null;class ie extends N{static get utcInstance(){return null===te&&(te=new ie(0)),te}static instance(e){return 0===e?ie.utcInstance:new ie(e)}static parseSpecifier(e){if(e){const t=e.match(/^utc(?:([+-]\d{1,2})(?::(\d{2}))?)?$/i);if(t)return new ie(Xe(t[1],t[2]))}return null}constructor(e){super(),this.fixed=e}get type(){return"fixed"}get name(){return 0===this.fixed?"UTC":`UTC${et(this.fixed,"narrow")}`}get ianaName(){return 0===this.fixed?"Etc/UTC":`Etc/GMT${et(-this.fixed,"narrow")}`}offsetName(){return this.name}formatOffset(e,t){return et(this.fixed,t)}get isUniversal(){return!0}offset(){return this.fixed}equals(e){return"fixed"===e.type&&e.fixed===this.fixed}get isValid(){return!0}}class se extends N{constructor(e){super(),this.zoneName=e}get type(){return"invalid"}get name(){return this.zoneName}get isUniversal(){return!1}offsetName(){return null}formatOffset(){return""}offset(){return NaN}equals(){return!1}get isValid(){return!1}}function re(e,t){if(De(e)||null===e)return t;if(e instanceof N)return e;if("string"==typeof e){const i=e.toLowerCase();return"default"===i?t:"local"===i||"system"===i?O.instance:"utc"===i||"gmt"===i?ie.utcInstance:ie.parseSpecifier(i)||G.create(e)}return Se(e)?ie.instance(e):"object"==typeof e&&"offset"in e&&"function"==typeof e.offset?e:new se(e)}let ne,oe=()=>Date.now(),ae="system",ce=null,le=null,pe=null,Ae=60,ue=null;class de{static get now(){return oe}static set now(e){oe=e}static set defaultZone(e){ae=e}static get defaultZone(){return re(ae,O.instance)}static get defaultLocale(){return ce}static set defaultLocale(e){ce=e}static get defaultNumberingSystem(){return le}static set defaultNumberingSystem(e){le=e}static get defaultOutputCalendar(){return pe}static set defaultOutputCalendar(e){pe=e}static get defaultWeekSettings(){return ue}static set defaultWeekSettings(e){ue=Le(e)}static get twoDigitCutoffYear(){return Ae}static set twoDigitCutoffYear(e){Ae=e%100}static get throwOnInvalid(){return ne}static set throwOnInvalid(e){ne=e}static resetCaches(){ee.resetCache(),G.resetCache()}}class he{constructor(e,t){this.reason=e,this.explanation=t}toMessage(){return this.explanation?`${this.reason}: ${this.explanation}`:this.reason}}const me=[0,31,59,90,120,151,181,212,243,273,304,334],ge=[0,31,60,91,121,152,182,213,244,274,305,335];function fe(e,t){return new he("unit out of range",`you specified ${t} (of type ${typeof t}) as a ${e}, which is invalid`)}function Ee(e,t,i){const s=new Date(Date.UTC(e,t-1,i));e<100&&e>=0&&s.setUTCFullYear(s.getUTCFullYear()-1900);const r=s.getUTCDay();return 0===r?7:r}function Ce(e,t,i){return i+(je(e)?ge:me)[t-1]}function ye(e,t){const i=je(e)?ge:me,s=i.findIndex((e=>eWe(s,t,i)?(c=s+1,l=1):c=s,{weekYear:c,weekNumber:l,weekday:a,...tt(e)}}function Be(e,t=4,i=1){const{weekYear:s,weekNumber:r,weekday:n}=e,o=ve(Ee(s,1,t),i),a=He(s);let c,l=7*r+n-o-7+t;l<1?(c=s-1,l+=He(c)):l>a?(c=s+1,l-=He(s)):c=s;const{month:p,day:A}=ye(c,l);return{year:c,month:p,day:A,...tt(e)}}function we(e){const{year:t,month:i,day:s}=e;return{year:t,ordinal:Ce(t,i,s),...tt(e)}}function be(e){const{year:t,ordinal:i}=e,{month:s,day:r}=ye(t,i);return{year:t,month:s,day:r,...tt(e)}}function Qe(e,t){if(!De(e.localWeekday)||!De(e.localWeekNumber)||!De(e.localWeekYear)){if(!De(e.weekday)||!De(e.weekNumber)||!De(e.weekYear))throw new o("Cannot mix locale-based week fields with ISO-based week fields");return De(e.localWeekday)||(e.weekday=e.localWeekday),De(e.localWeekNumber)||(e.weekNumber=e.localWeekNumber),De(e.localWeekYear)||(e.weekYear=e.localWeekYear),delete e.localWeekday,delete e.localWeekNumber,delete e.localWeekYear,{minDaysInFirstWeek:t.getMinDaysInFirstWeek(),startOfWeek:t.getStartOfWeek()}}return{minDaysInFirstWeek:4,startOfWeek:1}}function xe(e){const t=_e(e.year),i=Oe(e.month,1,12),s=Oe(e.day,1,Je(e.year,e.month));return t?i?!s&&fe("day",e.day):fe("month",e.month):fe("year",e.year)}function ke(e){const{hour:t,minute:i,second:s,millisecond:r}=e,n=Oe(t,0,23)||24===t&&0===i&&0===s&&0===r,o=Oe(i,0,59),a=Oe(s,0,59),c=Oe(r,0,999);return n?o?a?!c&&fe("millisecond",r):fe("second",s):fe("minute",i):fe("hour",t)}function De(e){return void 0===e}function Se(e){return"number"==typeof e}function _e(e){return"number"==typeof e&&e%1==0}function Re(){try{return"undefined"!=typeof Intl&&!!Intl.RelativeTimeFormat}catch(e){return!1}}function Te(){try{return"undefined"!=typeof Intl&&!!Intl.Locale&&("weekInfo"in Intl.Locale.prototype||"getWeekInfo"in Intl.Locale.prototype)}catch(e){return!1}}function Fe(e,t,i){if(0!==e.length)return e.reduce(((e,s)=>{const r=[t(s),s];return e&&i(e[0],r[0])===e[0]?e:r}),null)[1]}function Ne(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function Le(e){if(null==e)return null;if("object"!=typeof e)throw new c("Week settings must be an object");if(!Oe(e.firstDay,1,7)||!Oe(e.minimalDays,1,7)||!Array.isArray(e.weekend)||e.weekend.some((e=>!Oe(e,1,7))))throw new c("Invalid week settings");return{firstDay:e.firstDay,minimalDays:e.minimalDays,weekend:Array.from(e.weekend)}}function Oe(e,t,i){return _e(e)&&e>=t&&e<=i}function Me(e,t=2){let i;return i=e<0?"-"+(""+-e).padStart(t,"0"):(""+e).padStart(t,"0"),i}function Ue(e){return De(e)||null===e||""===e?void 0:parseInt(e,10)}function Pe(e){return De(e)||null===e||""===e?void 0:parseFloat(e)}function Ge(e){if(!De(e)&&null!==e&&""!==e){const t=1e3*parseFloat("0."+e);return Math.floor(t)}}function Ve(e,t,i=!1){const s=10**t;return(i?Math.trunc:Math.round)(e*s)/s}function je(e){return e%4==0&&(e%100!=0||e%400==0)}function He(e){return je(e)?366:365}function Je(e,t){const i=(s=t-1)-12*Math.floor(s/12)+1;var s;return 2===i?je(e+(t-i)/12)?29:28:[31,null,31,30,31,30,31,31,30,31,30,31][i-1]}function qe(e){let t=Date.UTC(e.year,e.month-1,e.day,e.hour,e.minute,e.second,e.millisecond);return e.year<100&&e.year>=0&&(t=new Date(t),t.setUTCFullYear(e.year,e.month-1,e.day)),+t}function Ye(e,t,i){return-ve(Ee(e,1,t),i)+t-1}function We(e,t=4,i=1){const s=Ye(e,t,i),r=Ye(e+1,t,i);return(He(e)-s+r)/7}function ze(e){return e>99?e:e>de.twoDigitCutoffYear?1900+e:2e3+e}function $e(e,t,i,s=null){const r=new Date(e),n={hourCycle:"h23",year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"};s&&(n.timeZone=s);const o={timeZoneName:t,...n},a=new Intl.DateTimeFormat(i,o).formatToParts(r).find((e=>"timezonename"===e.type.toLowerCase()));return a?a.value:null}function Xe(e,t){let i=parseInt(e,10);Number.isNaN(i)&&(i=0);const s=parseInt(t,10)||0;return 60*i+(i<0||Object.is(i,-0)?-s:s)}function Ke(e){const t=Number(e);if("boolean"==typeof e||""===e||Number.isNaN(t))throw new c(`Invalid unit value ${e}`);return t}function Ze(e,t){const i={};for(const s in e)if(Ne(e,s)){const r=e[s];if(null==r)continue;i[t(s)]=Ke(r)}return i}function et(e,t){const i=Math.trunc(Math.abs(e/60)),s=Math.trunc(Math.abs(e%60)),r=e>=0?"+":"-";switch(t){case"short":return`${r}${Me(i,2)}:${Me(s,2)}`;case"narrow":return`${r}${i}${s>0?`:${s}`:""}`;case"techie":return`${r}${Me(i,2)}${Me(s,2)}`;default:throw new RangeError(`Value format ${t} is out of range for property format`)}}function tt(e){return function(e,t){return["hour","minute","second","millisecond"].reduce(((t,i)=>(t[i]=e[i],t)),{})}(e)}const it=["January","February","March","April","May","June","July","August","September","October","November","December"],st=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],rt=["J","F","M","A","M","J","J","A","S","O","N","D"];function nt(e){switch(e){case"narrow":return[...rt];case"short":return[...st];case"long":return[...it];case"numeric":return["1","2","3","4","5","6","7","8","9","10","11","12"];case"2-digit":return["01","02","03","04","05","06","07","08","09","10","11","12"];default:return null}}const ot=["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"],at=["Mon","Tue","Wed","Thu","Fri","Sat","Sun"],ct=["M","T","W","T","F","S","S"];function lt(e){switch(e){case"narrow":return[...ct];case"short":return[...at];case"long":return[...ot];case"numeric":return["1","2","3","4","5","6","7"];default:return null}}const pt=["AM","PM"],At=["Before Christ","Anno Domini"],ut=["BC","AD"],dt=["B","A"];function ht(e){switch(e){case"narrow":return[...dt];case"short":return[...ut];case"long":return[...At];default:return null}}function mt(e,t){let i="";for(const s of e)s.literal?i+=s.val:i+=t(s.val);return i}const gt={D:d,DD:h,DDD:g,DDDD:f,t:E,tt:C,ttt:y,tttt:v,T:I,TT:B,TTT:w,TTTT:b,f:Q,ff:k,fff:_,ffff:T,F:x,FF:D,FFF:R,FFFF:F};class ft{static create(e,t={}){return new ft(e,t)}static parseFormat(e){let t=null,i="",s=!1;const r=[];for(let n=0;n0&&r.push({literal:s||/^\s+$/.test(i),val:i}),t=null,i="",s=!s):s||o===t?i+=o:(i.length>0&&r.push({literal:/^\s+$/.test(i),val:i}),i=o,t=o)}return i.length>0&&r.push({literal:s||/^\s+$/.test(i),val:i}),r}static macroTokenToFormatOpts(e){return gt[e]}constructor(e,t){this.opts=t,this.loc=e,this.systemLoc=null}formatWithSystemDefault(e,t){return null===this.systemLoc&&(this.systemLoc=this.loc.redefaultToSystem()),this.systemLoc.dtFormatter(e,{...this.opts,...t}).format()}dtFormatter(e,t={}){return this.loc.dtFormatter(e,{...this.opts,...t})}formatDateTime(e,t){return this.dtFormatter(e,t).format()}formatDateTimeParts(e,t){return this.dtFormatter(e,t).formatToParts()}formatInterval(e,t){return this.dtFormatter(e.start,t).dtf.formatRange(e.start.toJSDate(),e.end.toJSDate())}resolvedOptions(e,t){return this.dtFormatter(e,t).resolvedOptions()}num(e,t=0){if(this.opts.forceSimple)return Me(e,t);const i={...this.opts};return t>0&&(i.padTo=t),this.loc.numberFormatter(i).format(e)}formatDateTimeFromString(e,t){const i="en"===this.loc.listingMode(),s=this.loc.outputCalendar&&"gregory"!==this.loc.outputCalendar,r=(t,i)=>this.loc.extract(e,t,i),n=t=>e.isOffsetFixed&&0===e.offset&&t.allowZ?"Z":e.isValid?e.zone.formatOffset(e.ts,t.format):"",o=(t,s)=>i?function(e,t){return nt(t)[e.month-1]}(e,t):r(s?{month:t}:{month:t,day:"numeric"},"month"),a=(t,s)=>i?function(e,t){return lt(t)[e.weekday-1]}(e,t):r(s?{weekday:t}:{weekday:t,month:"long",day:"numeric"},"weekday"),c=t=>{const i=ft.macroTokenToFormatOpts(t);return i?this.formatWithSystemDefault(e,i):t},l=t=>i?function(e,t){return ht(t)[e.year<0?0:1]}(e,t):r({era:t},"era");return mt(ft.parseFormat(t),(t=>{switch(t){case"S":return this.num(e.millisecond);case"u":case"SSS":return this.num(e.millisecond,3);case"s":return this.num(e.second);case"ss":return this.num(e.second,2);case"uu":return this.num(Math.floor(e.millisecond/10),2);case"uuu":return this.num(Math.floor(e.millisecond/100));case"m":return this.num(e.minute);case"mm":return this.num(e.minute,2);case"h":return this.num(e.hour%12==0?12:e.hour%12);case"hh":return this.num(e.hour%12==0?12:e.hour%12,2);case"H":return this.num(e.hour);case"HH":return this.num(e.hour,2);case"Z":return n({format:"narrow",allowZ:this.opts.allowZ});case"ZZ":return n({format:"short",allowZ:this.opts.allowZ});case"ZZZ":return n({format:"techie",allowZ:this.opts.allowZ});case"ZZZZ":return e.zone.offsetName(e.ts,{format:"short",locale:this.loc.locale});case"ZZZZZ":return e.zone.offsetName(e.ts,{format:"long",locale:this.loc.locale});case"z":return e.zoneName;case"a":return i?function(e){return pt[e.hour<12?0:1]}(e):r({hour:"numeric",hourCycle:"h12"},"dayperiod");case"d":return s?r({day:"numeric"},"day"):this.num(e.day);case"dd":return s?r({day:"2-digit"},"day"):this.num(e.day,2);case"c":case"E":return this.num(e.weekday);case"ccc":return a("short",!0);case"cccc":return a("long",!0);case"ccccc":return a("narrow",!0);case"EEE":return a("short",!1);case"EEEE":return a("long",!1);case"EEEEE":return a("narrow",!1);case"L":return s?r({month:"numeric",day:"numeric"},"month"):this.num(e.month);case"LL":return s?r({month:"2-digit",day:"numeric"},"month"):this.num(e.month,2);case"LLL":return o("short",!0);case"LLLL":return o("long",!0);case"LLLLL":return o("narrow",!0);case"M":return s?r({month:"numeric"},"month"):this.num(e.month);case"MM":return s?r({month:"2-digit"},"month"):this.num(e.month,2);case"MMM":return o("short",!1);case"MMMM":return o("long",!1);case"MMMMM":return o("narrow",!1);case"y":return s?r({year:"numeric"},"year"):this.num(e.year);case"yy":return s?r({year:"2-digit"},"year"):this.num(e.year.toString().slice(-2),2);case"yyyy":return s?r({year:"numeric"},"year"):this.num(e.year,4);case"yyyyyy":return s?r({year:"numeric"},"year"):this.num(e.year,6);case"G":return l("short");case"GG":return l("long");case"GGGGG":return l("narrow");case"kk":return this.num(e.weekYear.toString().slice(-2),2);case"kkkk":return this.num(e.weekYear,4);case"W":return this.num(e.weekNumber);case"WW":return this.num(e.weekNumber,2);case"n":return this.num(e.localWeekNumber);case"nn":return this.num(e.localWeekNumber,2);case"ii":return this.num(e.localWeekYear.toString().slice(-2),2);case"iiii":return this.num(e.localWeekYear,4);case"o":return this.num(e.ordinal);case"ooo":return this.num(e.ordinal,3);case"q":return this.num(e.quarter);case"qq":return this.num(e.quarter,2);case"X":return this.num(Math.floor(e.ts/1e3));case"x":return this.num(e.ts);default:return c(t)}}))}formatDurationFromString(e,t){const i=e=>{switch(e[0]){case"S":return"millisecond";case"s":return"second";case"m":return"minute";case"h":return"hour";case"d":return"day";case"w":return"week";case"M":return"month";case"y":return"year";default:return null}},s=ft.parseFormat(t),r=s.reduce(((e,{literal:t,val:i})=>t?e:e.concat(i)),[]);return mt(s,(e=>t=>{const s=i(t);return s?this.num(e.get(s),t.length):t})(e.shiftTo(...r.map(i).filter((e=>e)))))}}const Et=/[A-Za-z_+-]{1,256}(?::?\/[A-Za-z0-9_+-]{1,256}(?:\/[A-Za-z0-9_+-]{1,256})?)?/;function Ct(...e){const t=e.reduce(((e,t)=>e+t.source),"");return RegExp(`^${t}$`)}function yt(...e){return t=>e.reduce((([e,i,s],r)=>{const[n,o,a]=r(t,s);return[{...e,...n},o||i,a]}),[{},null,1]).slice(0,2)}function vt(e,...t){if(null==e)return[null,null];for(const[i,s]of t){const t=i.exec(e);if(t)return s(t)}return[null,null]}function It(...e){return(t,i)=>{const s={};let r;for(r=0;rvoid 0!==e&&(t||e&&p)?-e:e;return[{years:u(Pe(i)),months:u(Pe(s)),weeks:u(Pe(r)),days:u(Pe(n)),hours:u(Pe(o)),minutes:u(Pe(a)),seconds:u(Pe(c),"-0"===c),milliseconds:u(Ge(l),A)}]}const Mt={GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};function Ut(e,t,i,s,r,n,o){const a={year:2===t.length?ze(Ue(t)):Ue(t),month:st.indexOf(i)+1,day:Ue(s),hour:Ue(r),minute:Ue(n)};return o&&(a.second=Ue(o)),e&&(a.weekday=e.length>3?ot.indexOf(e)+1:at.indexOf(e)+1),a}const Pt=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|(?:([+-]\d\d)(\d\d)))$/;function Gt(e){const[,t,i,s,r,n,o,a,c,l,p,A]=e,u=Ut(t,r,s,i,n,o,a);let d;return d=c?Mt[c]:l?0:Xe(p,A),[u,new ie(d)]}const Vt=/^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), (\d\d) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d{4}) (\d\d):(\d\d):(\d\d) GMT$/,jt=/^(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (\d\d)-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(\d\d) (\d\d):(\d\d):(\d\d) GMT$/,Ht=/^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ( \d|\d\d) (\d\d):(\d\d):(\d\d) (\d{4})$/;function Jt(e){const[,t,i,s,r,n,o,a]=e;return[Ut(t,r,s,i,n,o,a),ie.utcInstance]}function qt(e){const[,t,i,s,r,n,o,a]=e;return[Ut(t,a,i,s,r,n,o),ie.utcInstance]}const Yt=Ct(/([+-]\d{6}|\d{4})(?:-?(\d\d)(?:-?(\d\d))?)?/,Qt),Wt=Ct(/(\d{4})-?W(\d\d)(?:-?(\d))?/,Qt),zt=Ct(/(\d{4})-?(\d{3})/,Qt),$t=Ct(bt),Xt=yt((function(e,t){return[{year:_t(e,t),month:_t(e,t+1,1),day:_t(e,t+2,1)},null,t+3]}),Rt,Tt,Ft),Kt=yt(xt,Rt,Tt,Ft),Zt=yt(kt,Rt,Tt,Ft),ei=yt(Rt,Tt,Ft),ti=yt(Rt),ii=Ct(/(\d{4})-(\d\d)-(\d\d)/,St),si=Ct(Dt),ri=yt(Rt,Tt,Ft),ni="Invalid Duration",oi={weeks:{days:7,hours:168,minutes:10080,seconds:604800,milliseconds:6048e5},days:{hours:24,minutes:1440,seconds:86400,milliseconds:864e5},hours:{minutes:60,seconds:3600,milliseconds:36e5},minutes:{seconds:60,milliseconds:6e4},seconds:{milliseconds:1e3}},ai={years:{quarters:4,months:12,weeks:52,days:365,hours:8760,minutes:525600,seconds:31536e3,milliseconds:31536e6},quarters:{months:3,weeks:13,days:91,hours:2184,minutes:131040,seconds:7862400,milliseconds:78624e5},months:{weeks:4,days:30,hours:720,minutes:43200,seconds:2592e3,milliseconds:2592e6},...oi},ci={years:{quarters:4,months:12,weeks:52.1775,days:365.2425,hours:8765.82,minutes:525949.2,seconds:525949.2*60,milliseconds:525949.2*60*1e3},quarters:{months:3,weeks:13.044375,days:91.310625,hours:2191.455,minutes:131487.3,seconds:525949.2*60/4,milliseconds:7889237999.999999},months:{weeks:4.3481250000000005,days:30.436875,hours:730.485,minutes:43829.1,seconds:2629746,milliseconds:2629746e3},...oi},li=["years","quarters","months","weeks","days","hours","minutes","seconds","milliseconds"],pi=li.slice(0).reverse();function Ai(e,t,i=!1){const s={values:i?t.values:{...e.values,...t.values||{}},loc:e.loc.clone(t.loc),conversionAccuracy:t.conversionAccuracy||e.conversionAccuracy,matrix:t.matrix||e.matrix};return new hi(s)}function ui(e,t){var i;let s=null!=(i=t.milliseconds)?i:0;for(const i of pi.slice(1))t[i]&&(s+=t[i]*e[i].milliseconds);return s}function di(e,t){const i=ui(e,t)<0?-1:1;li.reduceRight(((s,r)=>{if(De(t[r]))return s;if(s){const n=t[s]*i,o=e[r][s],a=Math.floor(n/o);t[r]+=a*i,t[s]-=a*o*i}return r}),null),li.reduce(((i,s)=>{if(De(t[s]))return i;if(i){const r=t[i]%1;t[i]-=r,t[s]+=r*e[i][s]}return s}),null)}class hi{constructor(e){const t="longterm"===e.conversionAccuracy||!1;let i=t?ci:ai;e.matrix&&(i=e.matrix),this.values=e.values,this.loc=e.loc||ee.create(),this.conversionAccuracy=t?"longterm":"casual",this.invalid=e.invalid||null,this.matrix=i,this.isLuxonDuration=!0}static fromMillis(e,t){return hi.fromObject({milliseconds:e},t)}static fromObject(e,t={}){if(null==e||"object"!=typeof e)throw new c("Duration.fromObject: argument expected to be an object, got "+(null===e?"null":typeof e));return new hi({values:Ze(e,hi.normalizeUnit),loc:ee.fromObject(t),conversionAccuracy:t.conversionAccuracy,matrix:t.matrix})}static fromDurationLike(e){if(Se(e))return hi.fromMillis(e);if(hi.isDuration(e))return e;if("object"==typeof e)return hi.fromObject(e);throw new c(`Unknown duration argument ${e} of type ${typeof e}`)}static fromISO(e,t){const[i]=function(e){return vt(e,[Lt,Ot])}(e);return i?hi.fromObject(i,t):hi.invalid("unparsable",`the input "${e}" can't be parsed as ISO 8601`)}static fromISOTime(e,t){const[i]=function(e){return vt(e,[Nt,ti])}(e);return i?hi.fromObject(i,t):hi.invalid("unparsable",`the input "${e}" can't be parsed as ISO 8601`)}static invalid(e,t=null){if(!e)throw new c("need to specify a reason the Duration is invalid");const i=e instanceof he?e:new he(e,t);if(de.throwOnInvalid)throw new n(i);return new hi({invalid:i})}static normalizeUnit(e){const t={year:"years",years:"years",quarter:"quarters",quarters:"quarters",month:"months",months:"months",week:"weeks",weeks:"weeks",day:"days",days:"days",hour:"hours",hours:"hours",minute:"minutes",minutes:"minutes",second:"seconds",seconds:"seconds",millisecond:"milliseconds",milliseconds:"milliseconds"}[e?e.toLowerCase():e];if(!t)throw new a(e);return t}static isDuration(e){return e&&e.isLuxonDuration||!1}get locale(){return this.isValid?this.loc.locale:null}get numberingSystem(){return this.isValid?this.loc.numberingSystem:null}toFormat(e,t={}){const i={...t,floor:!1!==t.round&&!1!==t.floor};return this.isValid?ft.create(this.loc,i).formatDurationFromString(this,e):ni}toHuman(e={}){if(!this.isValid)return ni;const t=li.map((t=>{const i=this.values[t];return De(i)?null:this.loc.numberFormatter({style:"unit",unitDisplay:"long",...e,unit:t.slice(0,-1)}).format(i)})).filter((e=>e));return this.loc.listFormatter({type:"conjunction",style:e.listStyle||"narrow",...e}).format(t)}toObject(){return this.isValid?{...this.values}:{}}toISO(){if(!this.isValid)return null;let e="P";return 0!==this.years&&(e+=this.years+"Y"),0===this.months&&0===this.quarters||(e+=this.months+3*this.quarters+"M"),0!==this.weeks&&(e+=this.weeks+"W"),0!==this.days&&(e+=this.days+"D"),0===this.hours&&0===this.minutes&&0===this.seconds&&0===this.milliseconds||(e+="T"),0!==this.hours&&(e+=this.hours+"H"),0!==this.minutes&&(e+=this.minutes+"M"),0===this.seconds&&0===this.milliseconds||(e+=Ve(this.seconds+this.milliseconds/1e3,3)+"S"),"P"===e&&(e+="T0S"),e}toISOTime(e={}){if(!this.isValid)return null;const t=this.toMillis();return t<0||t>=864e5?null:(e={suppressMilliseconds:!1,suppressSeconds:!1,includePrefix:!1,format:"extended",...e,includeOffset:!1},os.fromMillis(t,{zone:"UTC"}).toISOTime(e))}toJSON(){return this.toISO()}toString(){return this.toISO()}[Symbol.for("nodejs.util.inspect.custom")](){return this.isValid?`Duration { values: ${JSON.stringify(this.values)} }`:`Duration { Invalid, reason: ${this.invalidReason} }`}toMillis(){return this.isValid?ui(this.matrix,this.values):NaN}valueOf(){return this.toMillis()}plus(e){if(!this.isValid)return this;const t=hi.fromDurationLike(e),i={};for(const e of li)(Ne(t.values,e)||Ne(this.values,e))&&(i[e]=t.get(e)+this.get(e));return Ai(this,{values:i},!0)}minus(e){if(!this.isValid)return this;const t=hi.fromDurationLike(e);return this.plus(t.negate())}mapUnits(e){if(!this.isValid)return this;const t={};for(const i of Object.keys(this.values))t[i]=Ke(e(this.values[i],i));return Ai(this,{values:t},!0)}get(e){return this[hi.normalizeUnit(e)]}set(e){return this.isValid?Ai(this,{values:{...this.values,...Ze(e,hi.normalizeUnit)}}):this}reconfigure({locale:e,numberingSystem:t,conversionAccuracy:i,matrix:s}={}){return Ai(this,{loc:this.loc.clone({locale:e,numberingSystem:t}),matrix:s,conversionAccuracy:i})}as(e){return this.isValid?this.shiftTo(e).get(e):NaN}normalize(){if(!this.isValid)return this;const e=this.toObject();return di(this.matrix,e),Ai(this,{values:e},!0)}rescale(){return this.isValid?Ai(this,{values:function(e){const t={};for(const[i,s]of Object.entries(e))0!==s&&(t[i]=s);return t}(this.normalize().shiftToAll().toObject())},!0):this}shiftTo(...e){if(!this.isValid)return this;if(0===e.length)return this;e=e.map((e=>hi.normalizeUnit(e)));const t={},i={},s=this.toObject();let r;for(const n of li)if(e.indexOf(n)>=0){r=n;let e=0;for(const t in i)e+=this.matrix[t][n]*i[t],i[t]=0;Se(s[n])&&(e+=s[n]);const o=Math.trunc(e);t[n]=o,i[n]=(1e3*e-1e3*o)/1e3}else Se(s[n])&&(i[n]=s[n]);for(const e in i)0!==i[e]&&(t[r]+=e===r?i[e]:i[e]/this.matrix[r][e]);return di(this.matrix,t),Ai(this,{values:t},!0)}shiftToAll(){return this.isValid?this.shiftTo("years","months","weeks","days","hours","minutes","seconds","milliseconds"):this}negate(){if(!this.isValid)return this;const e={};for(const t of Object.keys(this.values))e[t]=0===this.values[t]?0:-this.values[t];return Ai(this,{values:e},!0)}get years(){return this.isValid?this.values.years||0:NaN}get quarters(){return this.isValid?this.values.quarters||0:NaN}get months(){return this.isValid?this.values.months||0:NaN}get weeks(){return this.isValid?this.values.weeks||0:NaN}get days(){return this.isValid?this.values.days||0:NaN}get hours(){return this.isValid?this.values.hours||0:NaN}get minutes(){return this.isValid?this.values.minutes||0:NaN}get seconds(){return this.isValid?this.values.seconds||0:NaN}get milliseconds(){return this.isValid?this.values.milliseconds||0:NaN}get isValid(){return null===this.invalid}get invalidReason(){return this.invalid?this.invalid.reason:null}get invalidExplanation(){return this.invalid?this.invalid.explanation:null}equals(e){if(!this.isValid||!e.isValid)return!1;if(!this.loc.equals(e.loc))return!1;for(const s of li)if(t=this.values[s],i=e.values[s],!(void 0===t||0===t?void 0===i||0===i:t===i))return!1;var t,i;return!0}}const mi="Invalid Interval";class gi{constructor(e){this.s=e.start,this.e=e.end,this.invalid=e.invalid||null,this.isLuxonInterval=!0}static invalid(e,t=null){if(!e)throw new c("need to specify a reason the Interval is invalid");const i=e instanceof he?e:new he(e,t);if(de.throwOnInvalid)throw new r(i);return new gi({invalid:i})}static fromDateTimes(e,t){const i=as(e),s=as(t),r=function(e,t){return e&&e.isValid?t&&t.isValid?te}isBefore(e){return!!this.isValid&&this.e<=e}contains(e){return!!this.isValid&&this.s<=e&&this.e>e}set({start:e,end:t}={}){return this.isValid?gi.fromDateTimes(e||this.s,t||this.e):this}splitAt(...e){if(!this.isValid)return[];const t=e.map(as).filter((e=>this.contains(e))).sort(((e,t)=>e.toMillis()-t.toMillis())),i=[];let{s}=this,r=0;for(;s+this.e?this.e:e;i.push(gi.fromDateTimes(s,n)),s=n,r+=1}return i}splitBy(e){const t=hi.fromDurationLike(e);if(!this.isValid||!t.isValid||0===t.as("milliseconds"))return[];let i,{s}=this,r=1;const n=[];for(;se*r)));i=+e>+this.e?this.e:e,n.push(gi.fromDateTimes(s,i)),s=i,r+=1}return n}divideEqually(e){return this.isValid?this.splitBy(this.length()/e).slice(0,e):[]}overlaps(e){return this.e>e.s&&this.s=e.e}equals(e){return!(!this.isValid||!e.isValid)&&this.s.equals(e.s)&&this.e.equals(e.e)}intersection(e){if(!this.isValid)return this;const t=this.s>e.s?this.s:e.s,i=this.e=i?null:gi.fromDateTimes(t,i)}union(e){if(!this.isValid)return this;const t=this.se.e?this.e:e.e;return gi.fromDateTimes(t,i)}static merge(e){const[t,i]=e.sort(((e,t)=>e.s-t.s)).reduce((([e,t],i)=>t?t.overlaps(i)||t.abutsStart(i)?[e,t.union(i)]:[e.concat([t]),i]:[e,i]),[[],null]);return i&&t.push(i),t}static xor(e){let t=null,i=0;const s=[],r=e.map((e=>[{time:e.s,type:"s"},{time:e.e,type:"e"}])),n=Array.prototype.concat(...r).sort(((e,t)=>e.time-t.time));for(const e of n)i+="s"===e.type?1:-1,1===i?t=e.time:(t&&+t!=+e.time&&s.push(gi.fromDateTimes(t,e.time)),t=null);return gi.merge(s)}difference(...e){return gi.xor([this].concat(e)).map((e=>this.intersection(e))).filter((e=>e&&!e.isEmpty()))}toString(){return this.isValid?`[${this.s.toISO()} – ${this.e.toISO()})`:mi}[Symbol.for("nodejs.util.inspect.custom")](){return this.isValid?`Interval { start: ${this.s.toISO()}, end: ${this.e.toISO()} }`:`Interval { Invalid, reason: ${this.invalidReason} }`}toLocaleString(e=d,t={}){return this.isValid?ft.create(this.s.loc.clone(t),e).formatInterval(this):mi}toISO(e){return this.isValid?`${this.s.toISO(e)}/${this.e.toISO(e)}`:mi}toISODate(){return this.isValid?`${this.s.toISODate()}/${this.e.toISODate()}`:mi}toISOTime(e){return this.isValid?`${this.s.toISOTime(e)}/${this.e.toISOTime(e)}`:mi}toFormat(e,{separator:t=" – "}={}){return this.isValid?`${this.s.toFormat(e)}${t}${this.e.toFormat(e)}`:mi}toDuration(e,t){return this.isValid?this.e.diff(this.s,e,t):hi.invalid(this.invalidReason)}mapEndpoints(e){return gi.fromDateTimes(e(this.s),e(this.e))}}class fi{static hasDST(e=de.defaultZone){const t=os.now().setZone(e).set({month:12});return!e.isUniversal&&t.offset!==t.set({month:6}).offset}static isValidIANAZone(e){return G.isValidZone(e)}static normalizeZone(e){return re(e,de.defaultZone)}static getStartOfWeek({locale:e=null,locObj:t=null}={}){return(t||ee.create(e)).getStartOfWeek()}static getMinimumDaysInFirstWeek({locale:e=null,locObj:t=null}={}){return(t||ee.create(e)).getMinDaysInFirstWeek()}static getWeekendWeekdays({locale:e=null,locObj:t=null}={}){return(t||ee.create(e)).getWeekendDays().slice()}static months(e="long",{locale:t=null,numberingSystem:i=null,locObj:s=null,outputCalendar:r="gregory"}={}){return(s||ee.create(t,i,r)).months(e)}static monthsFormat(e="long",{locale:t=null,numberingSystem:i=null,locObj:s=null,outputCalendar:r="gregory"}={}){return(s||ee.create(t,i,r)).months(e,!0)}static weekdays(e="long",{locale:t=null,numberingSystem:i=null,locObj:s=null}={}){return(s||ee.create(t,i,null)).weekdays(e)}static weekdaysFormat(e="long",{locale:t=null,numberingSystem:i=null,locObj:s=null}={}){return(s||ee.create(t,i,null)).weekdays(e,!0)}static meridiems({locale:e=null}={}){return ee.create(e).meridiems()}static eras(e="short",{locale:t=null}={}){return ee.create(t,null,"gregory").eras(e)}static features(){return{relative:Re(),localeWeek:Te()}}}function Ei(e,t){const i=e=>e.toUTC(0,{keepLocalTime:!0}).startOf("day").valueOf(),s=i(t)-i(e);return Math.floor(hi.fromMillis(s).as("days"))}const Ci={arab:"[٠-٩]",arabext:"[۰-۹]",bali:"[᭐-᭙]",beng:"[০-৯]",deva:"[०-९]",fullwide:"[0-9]",gujr:"[૦-૯]",hanidec:"[〇|一|二|三|四|五|六|七|八|九]",khmr:"[០-៩]",knda:"[೦-೯]",laoo:"[໐-໙]",limb:"[᥆-᥏]",mlym:"[൦-൯]",mong:"[᠐-᠙]",mymr:"[၀-၉]",orya:"[୦-୯]",tamldec:"[௦-௯]",telu:"[౦-౯]",thai:"[๐-๙]",tibt:"[༠-༩]",latn:"\\d"},yi={arab:[1632,1641],arabext:[1776,1785],bali:[6992,7001],beng:[2534,2543],deva:[2406,2415],fullwide:[65296,65303],gujr:[2790,2799],khmr:[6112,6121],knda:[3302,3311],laoo:[3792,3801],limb:[6470,6479],mlym:[3430,3439],mong:[6160,6169],mymr:[4160,4169],orya:[2918,2927],tamldec:[3046,3055],telu:[3174,3183],thai:[3664,3673],tibt:[3872,3881]},vi=Ci.hanidec.replace(/[\[|\]]/g,"").split("");function Ii({numberingSystem:e},t=""){return new RegExp(`${Ci[e||"latn"]}${t}`)}function Bi(e,t=(e=>e)){return{regex:e,deser:([e])=>t(function(e){let t=parseInt(e,10);if(isNaN(t)){t="";for(let i=0;i=i&&s<=r&&(t+=s-i)}}return parseInt(t,10)}return t}(e))}}const wi=`[ ${String.fromCharCode(160)}]`,bi=new RegExp(wi,"g");function Qi(e){return e.replace(/\./g,"\\.?").replace(bi,wi)}function xi(e){return e.replace(/\./g,"").replace(bi," ").toLowerCase()}function ki(e,t){return null===e?null:{regex:RegExp(e.map(Qi).join("|")),deser:([i])=>e.findIndex((e=>xi(i)===xi(e)))+t}}function Di(e,t){return{regex:e,deser:([,e,t])=>Xe(e,t),groups:t}}function Si(e){return{regex:e,deser:([e])=>e}}const _i={year:{"2-digit":"yy",numeric:"yyyyy"},month:{numeric:"M","2-digit":"MM",short:"MMM",long:"MMMM"},day:{numeric:"d","2-digit":"dd"},weekday:{short:"EEE",long:"EEEE"},dayperiod:"a",dayPeriod:"a",hour12:{numeric:"h","2-digit":"hh"},hour24:{numeric:"H","2-digit":"HH"},minute:{numeric:"m","2-digit":"mm"},second:{numeric:"s","2-digit":"ss"},timeZoneName:{long:"ZZZZZ",short:"ZZZ"}};let Ri=null;function Ti(e,t){return Array.prototype.concat(...e.map((e=>function(e,t){if(e.literal)return e;const i=Ni(ft.macroTokenToFormatOpts(e.val),t);return null==i||i.includes(void 0)?e:i}(e,t))))}function Fi(e,t,i){const s=Ti(ft.parseFormat(i),e),r=s.map((t=>function(e,t){const i=Ii(t),s=Ii(t,"{2}"),r=Ii(t,"{3}"),n=Ii(t,"{4}"),o=Ii(t,"{6}"),a=Ii(t,"{1,2}"),c=Ii(t,"{1,3}"),l=Ii(t,"{1,6}"),p=Ii(t,"{1,9}"),A=Ii(t,"{2,4}"),u=Ii(t,"{4,6}"),d=e=>{return{regex:RegExp((t=e.val,t.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&"))),deser:([e])=>e,literal:!0};var t},h=(h=>{if(e.literal)return d(h);switch(h.val){case"G":return ki(t.eras("short"),0);case"GG":return ki(t.eras("long"),0);case"y":return Bi(l);case"yy":case"kk":return Bi(A,ze);case"yyyy":case"kkkk":return Bi(n);case"yyyyy":return Bi(u);case"yyyyyy":return Bi(o);case"M":case"L":case"d":case"H":case"h":case"m":case"q":case"s":case"W":return Bi(a);case"MM":case"LL":case"dd":case"HH":case"hh":case"mm":case"qq":case"ss":case"WW":return Bi(s);case"MMM":return ki(t.months("short",!0),1);case"MMMM":return ki(t.months("long",!0),1);case"LLL":return ki(t.months("short",!1),1);case"LLLL":return ki(t.months("long",!1),1);case"o":case"S":return Bi(c);case"ooo":case"SSS":return Bi(r);case"u":return Si(p);case"uu":return Si(a);case"uuu":case"E":case"c":return Bi(i);case"a":return ki(t.meridiems(),0);case"EEE":return ki(t.weekdays("short",!1),1);case"EEEE":return ki(t.weekdays("long",!1),1);case"ccc":return ki(t.weekdays("short",!0),1);case"cccc":return ki(t.weekdays("long",!0),1);case"Z":case"ZZ":return Di(new RegExp(`([+-]${a.source})(?::(${s.source}))?`),2);case"ZZZ":return Di(new RegExp(`([+-]${a.source})(${s.source})?`),2);case"z":return Si(/[a-z_+-/]{1,256}?/i);case" ":return Si(/[^\S\n\r]/);default:return d(h)}})(e)||{invalidReason:"missing Intl.DateTimeFormat.formatToParts support"};return h.token=e,h}(t,e))),n=r.find((e=>e.invalidReason));if(n)return{input:t,tokens:s,invalidReason:n.invalidReason};{const[e,i]=function(e){return[`^${e.map((e=>e.regex)).reduce(((e,t)=>`${e}(${t.source})`),"")}$`,e]}(r),n=RegExp(e,"i"),[a,c]=function(e,t,i){const s=e.match(t);if(s){const e={};let t=1;for(const r in i)if(Ne(i,r)){const n=i[r],o=n.groups?n.groups+1:1;!n.literal&&n.token&&(e[n.token.val[0]]=n.deser(s.slice(t,t+o))),t+=o}return[s,e]}return[s,{}]}(t,n,i),[l,p,A]=c?function(e){let t,i=null;return De(e.z)||(i=G.create(e.z)),De(e.Z)||(i||(i=new ie(e.Z)),t=e.Z),De(e.q)||(e.M=3*(e.q-1)+1),De(e.h)||(e.h<12&&1===e.a?e.h+=12:12===e.h&&0===e.a&&(e.h=0)),0===e.G&&e.y&&(e.y=-e.y),De(e.u)||(e.S=Ge(e.u)),[Object.keys(e).reduce(((t,i)=>{const s=(e=>{switch(e){case"S":return"millisecond";case"s":return"second";case"m":return"minute";case"h":case"H":return"hour";case"d":return"day";case"o":return"ordinal";case"L":case"M":return"month";case"y":return"year";case"E":case"c":return"weekday";case"W":return"weekNumber";case"k":return"weekYear";case"q":return"quarter";default:return null}})(i);return s&&(t[s]=e[i]),t}),{}),i,t]}(c):[null,null,void 0];if(Ne(c,"a")&&Ne(c,"H"))throw new o("Can't include meridiem when specifying 24-hour format");return{input:t,tokens:s,regex:n,rawMatches:a,matches:c,result:l,zone:p,specificOffset:A}}}function Ni(e,t){if(!e)return null;const i=ft.create(t,e).dtFormatter((Ri||(Ri=os.fromMillis(1555555555555)),Ri)),s=i.formatToParts(),r=i.resolvedOptions();return s.map((t=>function(e,t,i){const{type:s,value:r}=e;if("literal"===s){const e=/^\s+$/.test(r);return{literal:!e,val:e?" ":r}}const n=t[s];let o=s;"hour"===s&&(o=null!=t.hour12?t.hour12?"hour12":"hour24":null!=t.hourCycle?"h11"===t.hourCycle||"h12"===t.hourCycle?"hour12":"hour24":i.hour12?"hour12":"hour24");let a=_i[o];if("object"==typeof a&&(a=a[n]),a)return{literal:!1,val:a}}(t,e,r)))}const Li="Invalid DateTime",Oi=864e13;function Mi(e){return new he("unsupported zone",`the zone "${e.name}" is not supported`)}function Ui(e){return null===e.weekData&&(e.weekData=Ie(e.c)),e.weekData}function Pi(e){return null===e.localWeekData&&(e.localWeekData=Ie(e.c,e.loc.getMinDaysInFirstWeek(),e.loc.getStartOfWeek())),e.localWeekData}function Gi(e,t){const i={ts:e.ts,zone:e.zone,c:e.c,o:e.o,loc:e.loc,invalid:e.invalid};return new os({...i,...t,old:i})}function Vi(e,t,i){let s=e-60*t*1e3;const r=i.offset(s);if(t===r)return[s,t];s-=60*(r-t)*1e3;const n=i.offset(s);return r===n?[s,r]:[e-60*Math.min(r,n)*1e3,Math.max(r,n)]}function ji(e,t){const i=new Date(e+=60*t*1e3);return{year:i.getUTCFullYear(),month:i.getUTCMonth()+1,day:i.getUTCDate(),hour:i.getUTCHours(),minute:i.getUTCMinutes(),second:i.getUTCSeconds(),millisecond:i.getUTCMilliseconds()}}function Hi(e,t,i){return Vi(qe(e),t,i)}function Ji(e,t){const i=e.o,s=e.c.year+Math.trunc(t.years),r=e.c.month+Math.trunc(t.months)+3*Math.trunc(t.quarters),n={...e.c,year:s,month:r,day:Math.min(e.c.day,Je(s,r))+Math.trunc(t.days)+7*Math.trunc(t.weeks)},o=hi.fromObject({years:t.years-Math.trunc(t.years),quarters:t.quarters-Math.trunc(t.quarters),months:t.months-Math.trunc(t.months),weeks:t.weeks-Math.trunc(t.weeks),days:t.days-Math.trunc(t.days),hours:t.hours,minutes:t.minutes,seconds:t.seconds,milliseconds:t.milliseconds}).as("milliseconds"),a=qe(n);let[c,l]=Vi(a,i,e.zone);return 0!==o&&(c+=o,l=e.zone.offset(c)),{ts:c,o:l}}function qi(e,t,i,s,r,n){const{setZone:o,zone:a}=i;if(e&&0!==Object.keys(e).length||t){const s=t||a,r=os.fromObject(e,{...i,zone:s,specificOffset:n});return o?r:r.setZone(a)}return os.invalid(new he("unparsable",`the input "${r}" can't be parsed as ${s}`))}function Yi(e,t,i=!0){return e.isValid?ft.create(ee.create("en-US"),{allowZ:i,forceSimple:!0}).formatDateTimeFromString(e,t):null}function Wi(e,t){const i=e.c.year>9999||e.c.year<0;let s="";return i&&e.c.year>=0&&(s+="+"),s+=Me(e.c.year,i?6:4),t?(s+="-",s+=Me(e.c.month),s+="-",s+=Me(e.c.day)):(s+=Me(e.c.month),s+=Me(e.c.day)),s}function zi(e,t,i,s,r,n){let o=Me(e.c.hour);return t?(o+=":",o+=Me(e.c.minute),0===e.c.millisecond&&0===e.c.second&&i||(o+=":")):o+=Me(e.c.minute),0===e.c.millisecond&&0===e.c.second&&i||(o+=Me(e.c.second),0===e.c.millisecond&&s||(o+=".",o+=Me(e.c.millisecond,3))),r&&(e.isOffsetFixed&&0===e.offset&&!n?o+="Z":e.o<0?(o+="-",o+=Me(Math.trunc(-e.o/60)),o+=":",o+=Me(Math.trunc(-e.o%60))):(o+="+",o+=Me(Math.trunc(e.o/60)),o+=":",o+=Me(Math.trunc(e.o%60)))),n&&(o+="["+e.zone.ianaName+"]"),o}const $i={month:1,day:1,hour:0,minute:0,second:0,millisecond:0},Xi={weekNumber:1,weekday:1,hour:0,minute:0,second:0,millisecond:0},Ki={ordinal:1,hour:0,minute:0,second:0,millisecond:0},Zi=["year","month","day","hour","minute","second","millisecond"],es=["weekYear","weekNumber","weekday","hour","minute","second","millisecond"],ts=["year","ordinal","hour","minute","second","millisecond"];function is(e){switch(e.toLowerCase()){case"localweekday":case"localweekdays":return"localWeekday";case"localweeknumber":case"localweeknumbers":return"localWeekNumber";case"localweekyear":case"localweekyears":return"localWeekYear";default:return function(e){const t={year:"year",years:"year",month:"month",months:"month",day:"day",days:"day",hour:"hour",hours:"hour",minute:"minute",minutes:"minute",quarter:"quarter",quarters:"quarter",second:"second",seconds:"second",millisecond:"millisecond",milliseconds:"millisecond",weekday:"weekday",weekdays:"weekday",weeknumber:"weekNumber",weeksnumber:"weekNumber",weeknumbers:"weekNumber",weekyear:"weekYear",weekyears:"weekYear",ordinal:"ordinal"}[e.toLowerCase()];if(!t)throw new a(e);return t}(e)}}function ss(e,t){const i=re(t.zone,de.defaultZone),s=ee.fromObject(t),r=de.now();let n,o;if(De(e.year))n=r;else{for(const t of Zi)De(e[t])&&(e[t]=$i[t]);const t=xe(e)||ke(e);if(t)return os.invalid(t);const s=i.offset(r);[n,o]=Hi(e,s,i)}return new os({ts:n,zone:i,loc:s,o})}function rs(e,t,i){const s=!!De(i.round)||i.round,r=(e,r)=>(e=Ve(e,s||i.calendary?0:2,!0),t.loc.clone(i).relFormatter(i).format(e,r)),n=s=>i.calendary?t.hasSame(e,s)?0:t.startOf(s).diff(e.startOf(s),s).get(s):t.diff(e,s).get(s);if(i.unit)return r(n(i.unit),i.unit);for(const e of i.units){const t=n(e);if(Math.abs(t)>=1)return r(t,e)}return r(e>t?-0:0,i.units[i.units.length-1])}function ns(e){let t,i={};return e.length>0&&"object"==typeof e[e.length-1]?(i=e[e.length-1],t=Array.from(e).slice(0,e.length-1)):t=Array.from(e),[i,t]}class os{constructor(e){const t=e.zone||de.defaultZone;let i=e.invalid||(Number.isNaN(e.ts)?new he("invalid input"):null)||(t.isValid?null:Mi(t));this.ts=De(e.ts)?de.now():e.ts;let s=null,r=null;if(!i)if(e.old&&e.old.ts===this.ts&&e.old.zone.equals(t))[s,r]=[e.old.c,e.old.o];else{const e=t.offset(this.ts);s=ji(this.ts,e),i=Number.isNaN(s.year)?new he("invalid input"):null,s=i?null:s,r=i?null:e}this._zone=t,this.loc=e.loc||ee.create(),this.invalid=i,this.weekData=null,this.localWeekData=null,this.c=s,this.o=r,this.isLuxonDateTime=!0}static now(){return new os({})}static local(){const[e,t]=ns(arguments),[i,s,r,n,o,a,c]=t;return ss({year:i,month:s,day:r,hour:n,minute:o,second:a,millisecond:c},e)}static utc(){const[e,t]=ns(arguments),[i,s,r,n,o,a,c]=t;return e.zone=ie.utcInstance,ss({year:i,month:s,day:r,hour:n,minute:o,second:a,millisecond:c},e)}static fromJSDate(e,t={}){const i=(s=e,"[object Date]"===Object.prototype.toString.call(s)?e.valueOf():NaN);var s;if(Number.isNaN(i))return os.invalid("invalid input");const r=re(t.zone,de.defaultZone);return r.isValid?new os({ts:i,zone:r,loc:ee.fromObject(t)}):os.invalid(Mi(r))}static fromMillis(e,t={}){if(Se(e))return e<-Oi||e>Oi?os.invalid("Timestamp out of range"):new os({ts:e,zone:re(t.zone,de.defaultZone),loc:ee.fromObject(t)});throw new c(`fromMillis requires a numerical input, but received a ${typeof e} with value ${e}`)}static fromSeconds(e,t={}){if(Se(e))return new os({ts:1e3*e,zone:re(t.zone,de.defaultZone),loc:ee.fromObject(t)});throw new c("fromSeconds requires a numerical input")}static fromObject(e,t={}){e=e||{};const i=re(t.zone,de.defaultZone);if(!i.isValid)return os.invalid(Mi(i));const s=ee.fromObject(t),r=Ze(e,is),{minDaysInFirstWeek:n,startOfWeek:a}=Qe(r,s),c=de.now(),l=De(t.specificOffset)?i.offset(c):t.specificOffset,p=!De(r.ordinal),A=!De(r.year),u=!De(r.month)||!De(r.day),d=A||u,h=r.weekYear||r.weekNumber;if((d||p)&&h)throw new o("Can't mix weekYear/weekNumber units with year/month/day or ordinals");if(u&&p)throw new o("Can't mix ordinal dates with month/day");const m=h||r.weekday&&!d;let g,f,E=ji(c,l);m?(g=es,f=Xi,E=Ie(E,n,a)):p?(g=ts,f=Ki,E=we(E)):(g=Zi,f=$i);let C=!1;for(const e of g)De(r[e])?r[e]=C?f[e]:E[e]:C=!0;const y=m?function(e,t=4,i=1){const s=_e(e.weekYear),r=Oe(e.weekNumber,1,We(e.weekYear,t,i)),n=Oe(e.weekday,1,7);return s?r?!n&&fe("weekday",e.weekday):fe("week",e.weekNumber):fe("weekYear",e.weekYear)}(r,n,a):p?function(e){const t=_e(e.year),i=Oe(e.ordinal,1,He(e.year));return t?!i&&fe("ordinal",e.ordinal):fe("year",e.year)}(r):xe(r),v=y||ke(r);if(v)return os.invalid(v);const I=m?Be(r,n,a):p?be(r):r,[B,w]=Hi(I,l,i),b=new os({ts:B,zone:i,o:w,loc:s});return r.weekday&&d&&e.weekday!==b.weekday?os.invalid("mismatched weekday",`you can't specify both a weekday of ${r.weekday} and a date of ${b.toISO()}`):b}static fromISO(e,t={}){const[i,s]=function(e){return vt(e,[Yt,Xt],[Wt,Kt],[zt,Zt],[$t,ei])}(e);return qi(i,s,t,"ISO 8601",e)}static fromRFC2822(e,t={}){const[i,s]=function(e){return vt(function(e){return e.replace(/\([^()]*\)|[\n\t]/g," ").replace(/(\s\s+)/g," ").trim()}(e),[Pt,Gt])}(e);return qi(i,s,t,"RFC 2822",e)}static fromHTTP(e,t={}){const[i,s]=function(e){return vt(e,[Vt,Jt],[jt,Jt],[Ht,qt])}(e);return qi(i,s,t,"HTTP",t)}static fromFormat(e,t,i={}){if(De(e)||De(t))throw new c("fromFormat requires an input string and a format");const{locale:s=null,numberingSystem:r=null}=i,n=ee.fromOpts({locale:s,numberingSystem:r,defaultToEN:!0}),[o,a,l,p]=function(e,t,i){const{result:s,zone:r,specificOffset:n,invalidReason:o}=Fi(e,t,i);return[s,r,n,o]}(n,e,t);return p?os.invalid(p):qi(o,a,i,`format ${t}`,e,l)}static fromString(e,t,i={}){return os.fromFormat(e,t,i)}static fromSQL(e,t={}){const[i,s]=function(e){return vt(e,[ii,Xt],[si,ri])}(e);return qi(i,s,t,"SQL",e)}static invalid(e,t=null){if(!e)throw new c("need to specify a reason the DateTime is invalid");const i=e instanceof he?e:new he(e,t);if(de.throwOnInvalid)throw new s(i);return new os({invalid:i})}static isDateTime(e){return e&&e.isLuxonDateTime||!1}static parseFormatForOpts(e,t={}){const i=Ni(e,ee.fromObject(t));return i?i.map((e=>e?e.val:null)).join(""):null}static expandFormat(e,t={}){return Ti(ft.parseFormat(e),ee.fromObject(t)).map((e=>e.val)).join("")}get(e){return this[e]}get isValid(){return null===this.invalid}get invalidReason(){return this.invalid?this.invalid.reason:null}get invalidExplanation(){return this.invalid?this.invalid.explanation:null}get locale(){return this.isValid?this.loc.locale:null}get numberingSystem(){return this.isValid?this.loc.numberingSystem:null}get outputCalendar(){return this.isValid?this.loc.outputCalendar:null}get zone(){return this._zone}get zoneName(){return this.isValid?this.zone.name:null}get year(){return this.isValid?this.c.year:NaN}get quarter(){return this.isValid?Math.ceil(this.c.month/3):NaN}get month(){return this.isValid?this.c.month:NaN}get day(){return this.isValid?this.c.day:NaN}get hour(){return this.isValid?this.c.hour:NaN}get minute(){return this.isValid?this.c.minute:NaN}get second(){return this.isValid?this.c.second:NaN}get millisecond(){return this.isValid?this.c.millisecond:NaN}get weekYear(){return this.isValid?Ui(this).weekYear:NaN}get weekNumber(){return this.isValid?Ui(this).weekNumber:NaN}get weekday(){return this.isValid?Ui(this).weekday:NaN}get isWeekend(){return this.isValid&&this.loc.getWeekendDays().includes(this.weekday)}get localWeekday(){return this.isValid?Pi(this).weekday:NaN}get localWeekNumber(){return this.isValid?Pi(this).weekNumber:NaN}get localWeekYear(){return this.isValid?Pi(this).weekYear:NaN}get ordinal(){return this.isValid?we(this.c).ordinal:NaN}get monthShort(){return this.isValid?fi.months("short",{locObj:this.loc})[this.month-1]:null}get monthLong(){return this.isValid?fi.months("long",{locObj:this.loc})[this.month-1]:null}get weekdayShort(){return this.isValid?fi.weekdays("short",{locObj:this.loc})[this.weekday-1]:null}get weekdayLong(){return this.isValid?fi.weekdays("long",{locObj:this.loc})[this.weekday-1]:null}get offset(){return this.isValid?+this.o:NaN}get offsetNameShort(){return this.isValid?this.zone.offsetName(this.ts,{format:"short",locale:this.locale}):null}get offsetNameLong(){return this.isValid?this.zone.offsetName(this.ts,{format:"long",locale:this.locale}):null}get isOffsetFixed(){return this.isValid?this.zone.isUniversal:null}get isInDST(){return!this.isOffsetFixed&&(this.offset>this.set({month:1,day:1}).offset||this.offset>this.set({month:5}).offset)}getPossibleOffsets(){if(!this.isValid||this.isOffsetFixed)return[this];const e=864e5,t=6e4,i=qe(this.c),s=this.zone.offset(i-e),r=this.zone.offset(i+e),n=this.zone.offset(i-s*t),o=this.zone.offset(i-r*t);if(n===o)return[this];const a=i-n*t,c=i-o*t,l=ji(a,n),p=ji(c,o);return l.hour===p.hour&&l.minute===p.minute&&l.second===p.second&&l.millisecond===p.millisecond?[Gi(this,{ts:a}),Gi(this,{ts:c})]:[this]}get isInLeapYear(){return je(this.year)}get daysInMonth(){return Je(this.year,this.month)}get daysInYear(){return this.isValid?He(this.year):NaN}get weeksInWeekYear(){return this.isValid?We(this.weekYear):NaN}get weeksInLocalWeekYear(){return this.isValid?We(this.localWeekYear,this.loc.getMinDaysInFirstWeek(),this.loc.getStartOfWeek()):NaN}resolvedLocaleOptions(e={}){const{locale:t,numberingSystem:i,calendar:s}=ft.create(this.loc.clone(e),e).resolvedOptions(this);return{locale:t,numberingSystem:i,outputCalendar:s}}toUTC(e=0,t={}){return this.setZone(ie.instance(e),t)}toLocal(){return this.setZone(de.defaultZone)}setZone(e,{keepLocalTime:t=!1,keepCalendarTime:i=!1}={}){if((e=re(e,de.defaultZone)).equals(this.zone))return this;if(e.isValid){let s=this.ts;if(t||i){const t=e.offset(this.ts),i=this.toObject();[s]=Hi(i,t,e)}return Gi(this,{ts:s,zone:e})}return os.invalid(Mi(e))}reconfigure({locale:e,numberingSystem:t,outputCalendar:i}={}){return Gi(this,{loc:this.loc.clone({locale:e,numberingSystem:t,outputCalendar:i})})}setLocale(e){return this.reconfigure({locale:e})}set(e){if(!this.isValid)return this;const t=Ze(e,is),{minDaysInFirstWeek:i,startOfWeek:s}=Qe(t,this.loc),r=!De(t.weekYear)||!De(t.weekNumber)||!De(t.weekday),n=!De(t.ordinal),a=!De(t.year),c=!De(t.month)||!De(t.day),l=a||c,p=t.weekYear||t.weekNumber;if((l||n)&&p)throw new o("Can't mix weekYear/weekNumber units with year/month/day or ordinals");if(c&&n)throw new o("Can't mix ordinal dates with month/day");let A;r?A=Be({...Ie(this.c,i,s),...t},i,s):De(t.ordinal)?(A={...this.toObject(),...t},De(t.day)&&(A.day=Math.min(Je(A.year,A.month),A.day))):A=be({...we(this.c),...t});const[u,d]=Hi(A,this.o,this.zone);return Gi(this,{ts:u,o:d})}plus(e){return this.isValid?Gi(this,Ji(this,hi.fromDurationLike(e))):this}minus(e){return this.isValid?Gi(this,Ji(this,hi.fromDurationLike(e).negate())):this}startOf(e,{useLocaleWeeks:t=!1}={}){if(!this.isValid)return this;const i={},s=hi.normalizeUnit(e);switch(s){case"years":i.month=1;case"quarters":case"months":i.day=1;case"weeks":case"days":i.hour=0;case"hours":i.minute=0;case"minutes":i.second=0;case"seconds":i.millisecond=0}if("weeks"===s)if(t){const e=this.loc.getStartOfWeek(),{weekday:t}=this;tthis.valueOf(),o=function(e,t,i,s){let[r,n,o,a]=function(e,t,i){const s=[["years",(e,t)=>t.year-e.year],["quarters",(e,t)=>t.quarter-e.quarter+4*(t.year-e.year)],["months",(e,t)=>t.month-e.month+12*(t.year-e.year)],["weeks",(e,t)=>{const i=Ei(e,t);return(i-i%7)/7}],["days",Ei]],r={},n=e;let o,a;for(const[c,l]of s)i.indexOf(c)>=0&&(o=c,r[c]=l(e,t),a=n.plus(r),a>t?(r[c]--,(e=n.plus(r))>t&&(a=e,r[c]--,e=n.plus(r))):e=a);return[e,r,a,o]}(e,t,i);const c=t-r,l=i.filter((e=>["hours","minutes","seconds","milliseconds"].indexOf(e)>=0));0===l.length&&(o0?hi.fromMillis(c,s).shiftTo(...l).plus(p):p}(n?this:e,n?e:this,r,s);var a;return n?o.negate():o}diffNow(e="milliseconds",t={}){return this.diff(os.now(),e,t)}until(e){return this.isValid?gi.fromDateTimes(this,e):this}hasSame(e,t,i){if(!this.isValid)return!1;const s=e.valueOf(),r=this.setZone(e.zone,{keepLocalTime:!0});return r.startOf(t,i)<=s&&s<=r.endOf(t,i)}equals(e){return this.isValid&&e.isValid&&this.valueOf()===e.valueOf()&&this.zone.equals(e.zone)&&this.loc.equals(e.loc)}toRelative(e={}){if(!this.isValid)return null;const t=e.base||os.fromObject({},{zone:this.zone}),i=e.padding?thise.valueOf()),Math.min)}static max(...e){if(!e.every(os.isDateTime))throw new c("max requires all arguments be DateTimes");return Fe(e,(e=>e.valueOf()),Math.max)}static fromFormatExplain(e,t,i={}){const{locale:s=null,numberingSystem:r=null}=i;return Fi(ee.fromOpts({locale:s,numberingSystem:r,defaultToEN:!0}),e,t)}static fromStringExplain(e,t,i={}){return os.fromFormatExplain(e,t,i)}static get DATE_SHORT(){return d}static get DATE_MED(){return h}static get DATE_MED_WITH_WEEKDAY(){return m}static get DATE_FULL(){return g}static get DATE_HUGE(){return f}static get TIME_SIMPLE(){return E}static get TIME_WITH_SECONDS(){return C}static get TIME_WITH_SHORT_OFFSET(){return y}static get TIME_WITH_LONG_OFFSET(){return v}static get TIME_24_SIMPLE(){return I}static get TIME_24_WITH_SECONDS(){return B}static get TIME_24_WITH_SHORT_OFFSET(){return w}static get TIME_24_WITH_LONG_OFFSET(){return b}static get DATETIME_SHORT(){return Q}static get DATETIME_SHORT_WITH_SECONDS(){return x}static get DATETIME_MED(){return k}static get DATETIME_MED_WITH_SECONDS(){return D}static get DATETIME_MED_WITH_WEEKDAY(){return S}static get DATETIME_FULL(){return _}static get DATETIME_FULL_WITH_SECONDS(){return R}static get DATETIME_HUGE(){return T}static get DATETIME_HUGE_WITH_SECONDS(){return F}}function as(e){if(os.isDateTime(e))return e;if(e&&e.valueOf&&Se(e.valueOf()))return os.fromJSDate(e);if(e&&"object"==typeof e)return os.fromObject(e);throw new c(`Unknown datetime argument: ${e}, of type ${typeof e}`)}t.DateTime=os,t.Duration=hi,t.FixedOffsetZone=ie,t.IANAZone=G,t.Info=fi,t.Interval=gi,t.InvalidZone=se,t.Settings=de,t.SystemZone=O,t.VERSION="3.4.4",t.Zone=N},10257:(e,t,i)=>{e.exports=i(66450)},69335:(e,t,i)=>{"use strict";var s,r,n,o=i(10257),a=i(71017).extname,c=/^\s*([^;\s]*)(?:;|\s|$)/,l=/^text\//i;function p(e){if(!e||"string"!=typeof e)return!1;var t=c.exec(e),i=t&&o[t[1].toLowerCase()];return i&&i.charset?i.charset:!(!t||!l.test(t[1]))&&"UTF-8"}t.charset=p,t.charsets={lookup:p},t.contentType=function(e){if(!e||"string"!=typeof e)return!1;var i=-1===e.indexOf("/")?t.lookup(e):e;if(!i)return!1;if(-1===i.indexOf("charset")){var s=t.charset(i);s&&(i+="; charset="+s.toLowerCase())}return i},t.extension=function(e){if(!e||"string"!=typeof e)return!1;var i=c.exec(e),s=i&&t.extensions[i[1].toLowerCase()];return!(!s||!s.length)&&s[0]},t.extensions=Object.create(null),t.lookup=function(e){if(!e||"string"!=typeof e)return!1;var i=a("x."+e).toLowerCase().substr(1);return i&&t.types[i]||!1},t.types=Object.create(null),s=t.extensions,r=t.types,n=["nginx","apache",void 0,"iana"],Object.keys(o).forEach((function(e){var t=o[e],i=t.extensions;if(i&&i.length){s[e]=i;for(var a=0;ap||l===p&&"application/"===r[c].substr(0,12)))continue}r[c]=e}}}))},70474:e=>{"use strict";const t=(e,t)=>{for(const i of Reflect.ownKeys(t))Object.defineProperty(e,i,Object.getOwnPropertyDescriptor(t,i));return e};e.exports=t,e.exports.default=t},44247:e=>{var t=1e3,i=60*t,s=60*i,r=24*s;function n(e,t,i,s){var r=t>=1.5*i;return Math.round(e/i)+" "+s+(r?"s":"")}e.exports=function(e,o){o=o||{};var a,c,l=typeof e;if("string"===l&&e.length>0)return function(e){if(!((e=String(e)).length>100)){var n=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(n){var o=parseFloat(n[1]);switch((n[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return 315576e5*o;case"weeks":case"week":case"w":return 6048e5*o;case"days":case"day":case"d":return o*r;case"hours":case"hour":case"hrs":case"hr":case"h":return o*s;case"minutes":case"minute":case"mins":case"min":case"m":return o*i;case"seconds":case"second":case"secs":case"sec":case"s":return o*t;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return o;default:return}}}}(e);if("number"===l&&isFinite(e))return o.long?(a=e,(c=Math.abs(a))>=r?n(a,c,r,"day"):c>=s?n(a,c,s,"hour"):c>=i?n(a,c,i,"minute"):c>=t?n(a,c,t,"second"):a+" ms"):function(e){var n=Math.abs(e);return n>=r?Math.round(e/r)+"d":n>=s?Math.round(e/s)+"h":n>=i?Math.round(e/i)+"m":n>=t?Math.round(e/t)+"s":e+"ms"}(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))}},83733:(e,t,i)=>{var s=i(12781);function r(e){s.apply(this),e=e||{},this.writable=this.readable=!0,this.muted=!1,this.on("pipe",this._onpipe),this.replace=e.replace,this._prompt=e.prompt||null,this._hadControl=!1}function n(e){return function(){var t=this._dest,i=this._src;t&&t[e]&&t[e].apply(t,arguments),i&&i[e]&&i[e].apply(i,arguments)}}e.exports=r,r.prototype=Object.create(s.prototype),Object.defineProperty(r.prototype,"constructor",{value:r,enumerable:!1}),r.prototype.mute=function(){this.muted=!0},r.prototype.unmute=function(){this.muted=!1},Object.defineProperty(r.prototype,"_onpipe",{value:function(e){this._src=e},enumerable:!1,writable:!0,configurable:!0}),Object.defineProperty(r.prototype,"isTTY",{get:function(){return this._dest?this._dest.isTTY:!!this._src&&this._src.isTTY},set:function(e){Object.defineProperty(this,"isTTY",{value:e,enumerable:!0,writable:!0,configurable:!0})},enumerable:!0,configurable:!0}),Object.defineProperty(r.prototype,"rows",{get:function(){return this._dest?this._dest.rows:this._src?this._src.rows:void 0},enumerable:!0,configurable:!0}),Object.defineProperty(r.prototype,"columns",{get:function(){return this._dest?this._dest.columns:this._src?this._src.columns:void 0},enumerable:!0,configurable:!0}),r.prototype.pipe=function(e,t){return this._dest=e,s.prototype.pipe.call(this,e,t)},r.prototype.pause=function(){if(this._src)return this._src.pause()},r.prototype.resume=function(){if(this._src)return this._src.resume()},r.prototype.write=function(e){if(this.muted){if(!this.replace)return!0;if(e.match(/^\u001b/))return 0===e.indexOf(this._prompt)&&(e=(e=e.substr(this._prompt.length)).replace(/./g,this.replace),e=this._prompt+e),this._hadControl=!0,this.emit("data",e);this._prompt&&this._hadControl&&0===e.indexOf(this._prompt)&&(this._hadControl=!1,this.emit("data",this._prompt),e=e.substr(this._prompt.length)),e=e.toString().replace(/./g,this.replace)}this.emit("data",e)},r.prototype.end=function(e){this.muted&&(e=e&&this.replace?e.toString().replace(/./g,this.replace):null),e&&this.emit("data",e),this.emit("end")},r.prototype.destroy=n("destroy"),r.prototype.destroySoon=n("destroySoon"),r.prototype.close=n("close")},39470:(e,t,i)=>{"use strict";i.r(t),i.d(t,{App:()=>ve,OAuthApp:()=>Ie,Octokit:()=>ye,RequestError:()=>Q.RequestError,createNodeMiddleware:()=>fe});var s=i(96049);function r(e,t,i){const s="function"==typeof t?t.endpoint(i):e.request.endpoint(t,i),r="function"==typeof t?t:e.request,n=s.method,o=s.headers;let a=s.url;return{[Symbol.asyncIterator]:()=>({async next(){if(!a)return{done:!0};try{const e=function(e){if(!e.data)return{...e,data:[]};if(!("total_count"in e.data)||"url"in e.data)return e;const t=e.data.incomplete_results,i=e.data.repository_selection,s=e.data.total_count;delete e.data.incomplete_results,delete e.data.repository_selection,delete e.data.total_count;const r=Object.keys(e.data)[0],n=e.data[r];return e.data=n,void 0!==t&&(e.data.incomplete_results=t),void 0!==i&&(e.data.repository_selection=i),e.data.total_count=s,e}(await r({method:n,url:a,headers:o}));return a=((e.headers.link||"").match(/<([^>]+)>;\s*rel="next"/)||[])[1],{value:e}}catch(e){if(409!==e.status)throw e;return a="",{value:{status:200,headers:{},data:[]}}}}})}}function n(e,t,i,s){return"function"==typeof i&&(s=i,i=void 0),o(e,[],r(e,t,i)[Symbol.asyncIterator](),s)}function o(e,t,i,s){return i.next().then((r=>{if(r.done)return t;let n=!1;return t=t.concat(s?s(r.value,(function(){n=!0})):r.value.data),n?t:o(e,t,i,s)}))}var a=Object.assign(n,{iterator:r});function c(e){return{paginate:Object.assign(n.bind(null,e),{iterator:r.bind(null,e)})}}c.VERSION="9.2.1";class l extends Error{constructor(e,t){super(((e,t)=>`The cursor at "${e.join(",")}" did not change its value "${t}" after a page transition. Please make sure your that your query is set up correctly.`)(e.pathInQuery,t)),this.pageInfo=e,this.cursorValue=t,this.name="MissingCursorChangeError",Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor)}}class p extends Error{constructor(e){super(`No pageInfo property found in response. Please make sure to specify the pageInfo in your query. Response-Data: ${JSON.stringify(e,null,2)}`),this.response=e,this.name="MissingPageInfo",Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor)}}function A(e){const t=u(e,"pageInfo");if(0===t.length)throw new p(e);return t}const u=(e,t,i=[])=>{for(const r of Object.keys(e)){const n=[...i,r],o=e[r];if(o.hasOwnProperty(t))return n;if(s=o,"[object Object]"===Object.prototype.toString.call(s)){const e=u(o,t,n);if(e.length>0)return e}}var s;return[]},d=(e,t)=>t.reduce(((e,t)=>e[t]),e),h=(e,t,i)=>{const s=t[t.length-1],r=[...t].slice(0,-1),n=d(e,r);n[s]="function"==typeof i?i(n[s]):i},m=e=>e.hasOwnProperty("hasNextPage"),g=e=>(t,i={})=>{let s=!0,r={...i};return{[Symbol.asyncIterator]:()=>({async next(){if(!s)return{done:!0,value:{}};const i=await e.graphql(t,r),n=(e=>{const t=A(e);return{pathInQuery:t,pageInfo:d(e,[...t,"pageInfo"])}})(i),o=(a=n.pageInfo,m(a)?a.endCursor:a.startCursor);var a;if(s=(e=>m(e)?e.hasNextPage:e.hasPreviousPage)(n.pageInfo),s&&o===r.cursor)throw new l(n,o);return r={...r,cursor:o},{done:!1,value:i}}})}},f=(e,t)=>{if(0===Object.keys(e).length)return Object.assign(e,t);const i=A(e),s=[...i,"nodes"],r=d(t,s);r&&h(e,s,(e=>[...e,...r]));const n=[...i,"edges"],o=d(t,n);o&&h(e,n,(e=>[...e,...o]));const a=[...i,"pageInfo"];return h(e,a,d(t,a)),e},E=e=>{const t=g(e);return async(e,i={})=>{let s={};for await(const r of t(e,i))s=f(s,r);return s}};const C=new Map;for(const[e,t]of Object.entries({actions:{addCustomLabelsToSelfHostedRunnerForOrg:["POST /orgs/{org}/actions/runners/{runner_id}/labels"],addCustomLabelsToSelfHostedRunnerForRepo:["POST /repos/{owner}/{repo}/actions/runners/{runner_id}/labels"],addSelectedRepoToOrgSecret:["PUT /orgs/{org}/actions/secrets/{secret_name}/repositories/{repository_id}"],addSelectedRepoToOrgVariable:["PUT /orgs/{org}/actions/variables/{name}/repositories/{repository_id}"],approveWorkflowRun:["POST /repos/{owner}/{repo}/actions/runs/{run_id}/approve"],cancelWorkflowRun:["POST /repos/{owner}/{repo}/actions/runs/{run_id}/cancel"],createEnvironmentVariable:["POST /repositories/{repository_id}/environments/{environment_name}/variables"],createOrUpdateEnvironmentSecret:["PUT /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}"],createOrUpdateOrgSecret:["PUT /orgs/{org}/actions/secrets/{secret_name}"],createOrUpdateRepoSecret:["PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}"],createOrgVariable:["POST /orgs/{org}/actions/variables"],createRegistrationTokenForOrg:["POST /orgs/{org}/actions/runners/registration-token"],createRegistrationTokenForRepo:["POST /repos/{owner}/{repo}/actions/runners/registration-token"],createRemoveTokenForOrg:["POST /orgs/{org}/actions/runners/remove-token"],createRemoveTokenForRepo:["POST /repos/{owner}/{repo}/actions/runners/remove-token"],createRepoVariable:["POST /repos/{owner}/{repo}/actions/variables"],createWorkflowDispatch:["POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches"],deleteActionsCacheById:["DELETE /repos/{owner}/{repo}/actions/caches/{cache_id}"],deleteActionsCacheByKey:["DELETE /repos/{owner}/{repo}/actions/caches{?key,ref}"],deleteArtifact:["DELETE /repos/{owner}/{repo}/actions/artifacts/{artifact_id}"],deleteEnvironmentSecret:["DELETE /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}"],deleteEnvironmentVariable:["DELETE /repositories/{repository_id}/environments/{environment_name}/variables/{name}"],deleteOrgSecret:["DELETE /orgs/{org}/actions/secrets/{secret_name}"],deleteOrgVariable:["DELETE /orgs/{org}/actions/variables/{name}"],deleteRepoSecret:["DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}"],deleteRepoVariable:["DELETE /repos/{owner}/{repo}/actions/variables/{name}"],deleteSelfHostedRunnerFromOrg:["DELETE /orgs/{org}/actions/runners/{runner_id}"],deleteSelfHostedRunnerFromRepo:["DELETE /repos/{owner}/{repo}/actions/runners/{runner_id}"],deleteWorkflowRun:["DELETE /repos/{owner}/{repo}/actions/runs/{run_id}"],deleteWorkflowRunLogs:["DELETE /repos/{owner}/{repo}/actions/runs/{run_id}/logs"],disableSelectedRepositoryGithubActionsOrganization:["DELETE /orgs/{org}/actions/permissions/repositories/{repository_id}"],disableWorkflow:["PUT /repos/{owner}/{repo}/actions/workflows/{workflow_id}/disable"],downloadArtifact:["GET /repos/{owner}/{repo}/actions/artifacts/{artifact_id}/{archive_format}"],downloadJobLogsForWorkflowRun:["GET /repos/{owner}/{repo}/actions/jobs/{job_id}/logs"],downloadWorkflowRunAttemptLogs:["GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}/logs"],downloadWorkflowRunLogs:["GET /repos/{owner}/{repo}/actions/runs/{run_id}/logs"],enableSelectedRepositoryGithubActionsOrganization:["PUT /orgs/{org}/actions/permissions/repositories/{repository_id}"],enableWorkflow:["PUT /repos/{owner}/{repo}/actions/workflows/{workflow_id}/enable"],forceCancelWorkflowRun:["POST /repos/{owner}/{repo}/actions/runs/{run_id}/force-cancel"],generateRunnerJitconfigForOrg:["POST /orgs/{org}/actions/runners/generate-jitconfig"],generateRunnerJitconfigForRepo:["POST /repos/{owner}/{repo}/actions/runners/generate-jitconfig"],getActionsCacheList:["GET /repos/{owner}/{repo}/actions/caches"],getActionsCacheUsage:["GET /repos/{owner}/{repo}/actions/cache/usage"],getActionsCacheUsageByRepoForOrg:["GET /orgs/{org}/actions/cache/usage-by-repository"],getActionsCacheUsageForOrg:["GET /orgs/{org}/actions/cache/usage"],getAllowedActionsOrganization:["GET /orgs/{org}/actions/permissions/selected-actions"],getAllowedActionsRepository:["GET /repos/{owner}/{repo}/actions/permissions/selected-actions"],getArtifact:["GET /repos/{owner}/{repo}/actions/artifacts/{artifact_id}"],getCustomOidcSubClaimForRepo:["GET /repos/{owner}/{repo}/actions/oidc/customization/sub"],getEnvironmentPublicKey:["GET /repositories/{repository_id}/environments/{environment_name}/secrets/public-key"],getEnvironmentSecret:["GET /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}"],getEnvironmentVariable:["GET /repositories/{repository_id}/environments/{environment_name}/variables/{name}"],getGithubActionsDefaultWorkflowPermissionsOrganization:["GET /orgs/{org}/actions/permissions/workflow"],getGithubActionsDefaultWorkflowPermissionsRepository:["GET /repos/{owner}/{repo}/actions/permissions/workflow"],getGithubActionsPermissionsOrganization:["GET /orgs/{org}/actions/permissions"],getGithubActionsPermissionsRepository:["GET /repos/{owner}/{repo}/actions/permissions"],getJobForWorkflowRun:["GET /repos/{owner}/{repo}/actions/jobs/{job_id}"],getOrgPublicKey:["GET /orgs/{org}/actions/secrets/public-key"],getOrgSecret:["GET /orgs/{org}/actions/secrets/{secret_name}"],getOrgVariable:["GET /orgs/{org}/actions/variables/{name}"],getPendingDeploymentsForRun:["GET /repos/{owner}/{repo}/actions/runs/{run_id}/pending_deployments"],getRepoPermissions:["GET /repos/{owner}/{repo}/actions/permissions",{},{renamed:["actions","getGithubActionsPermissionsRepository"]}],getRepoPublicKey:["GET /repos/{owner}/{repo}/actions/secrets/public-key"],getRepoSecret:["GET /repos/{owner}/{repo}/actions/secrets/{secret_name}"],getRepoVariable:["GET /repos/{owner}/{repo}/actions/variables/{name}"],getReviewsForRun:["GET /repos/{owner}/{repo}/actions/runs/{run_id}/approvals"],getSelfHostedRunnerForOrg:["GET /orgs/{org}/actions/runners/{runner_id}"],getSelfHostedRunnerForRepo:["GET /repos/{owner}/{repo}/actions/runners/{runner_id}"],getWorkflow:["GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}"],getWorkflowAccessToRepository:["GET /repos/{owner}/{repo}/actions/permissions/access"],getWorkflowRun:["GET /repos/{owner}/{repo}/actions/runs/{run_id}"],getWorkflowRunAttempt:["GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}"],getWorkflowRunUsage:["GET /repos/{owner}/{repo}/actions/runs/{run_id}/timing"],getWorkflowUsage:["GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/timing"],listArtifactsForRepo:["GET /repos/{owner}/{repo}/actions/artifacts"],listEnvironmentSecrets:["GET /repositories/{repository_id}/environments/{environment_name}/secrets"],listEnvironmentVariables:["GET /repositories/{repository_id}/environments/{environment_name}/variables"],listJobsForWorkflowRun:["GET /repos/{owner}/{repo}/actions/runs/{run_id}/jobs"],listJobsForWorkflowRunAttempt:["GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}/jobs"],listLabelsForSelfHostedRunnerForOrg:["GET /orgs/{org}/actions/runners/{runner_id}/labels"],listLabelsForSelfHostedRunnerForRepo:["GET /repos/{owner}/{repo}/actions/runners/{runner_id}/labels"],listOrgSecrets:["GET /orgs/{org}/actions/secrets"],listOrgVariables:["GET /orgs/{org}/actions/variables"],listRepoOrganizationSecrets:["GET /repos/{owner}/{repo}/actions/organization-secrets"],listRepoOrganizationVariables:["GET /repos/{owner}/{repo}/actions/organization-variables"],listRepoSecrets:["GET /repos/{owner}/{repo}/actions/secrets"],listRepoVariables:["GET /repos/{owner}/{repo}/actions/variables"],listRepoWorkflows:["GET /repos/{owner}/{repo}/actions/workflows"],listRunnerApplicationsForOrg:["GET /orgs/{org}/actions/runners/downloads"],listRunnerApplicationsForRepo:["GET /repos/{owner}/{repo}/actions/runners/downloads"],listSelectedReposForOrgSecret:["GET /orgs/{org}/actions/secrets/{secret_name}/repositories"],listSelectedReposForOrgVariable:["GET /orgs/{org}/actions/variables/{name}/repositories"],listSelectedRepositoriesEnabledGithubActionsOrganization:["GET /orgs/{org}/actions/permissions/repositories"],listSelfHostedRunnersForOrg:["GET /orgs/{org}/actions/runners"],listSelfHostedRunnersForRepo:["GET /repos/{owner}/{repo}/actions/runners"],listWorkflowRunArtifacts:["GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts"],listWorkflowRuns:["GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs"],listWorkflowRunsForRepo:["GET /repos/{owner}/{repo}/actions/runs"],reRunJobForWorkflowRun:["POST /repos/{owner}/{repo}/actions/jobs/{job_id}/rerun"],reRunWorkflow:["POST /repos/{owner}/{repo}/actions/runs/{run_id}/rerun"],reRunWorkflowFailedJobs:["POST /repos/{owner}/{repo}/actions/runs/{run_id}/rerun-failed-jobs"],removeAllCustomLabelsFromSelfHostedRunnerForOrg:["DELETE /orgs/{org}/actions/runners/{runner_id}/labels"],removeAllCustomLabelsFromSelfHostedRunnerForRepo:["DELETE /repos/{owner}/{repo}/actions/runners/{runner_id}/labels"],removeCustomLabelFromSelfHostedRunnerForOrg:["DELETE /orgs/{org}/actions/runners/{runner_id}/labels/{name}"],removeCustomLabelFromSelfHostedRunnerForRepo:["DELETE /repos/{owner}/{repo}/actions/runners/{runner_id}/labels/{name}"],removeSelectedRepoFromOrgSecret:["DELETE /orgs/{org}/actions/secrets/{secret_name}/repositories/{repository_id}"],removeSelectedRepoFromOrgVariable:["DELETE /orgs/{org}/actions/variables/{name}/repositories/{repository_id}"],reviewCustomGatesForRun:["POST /repos/{owner}/{repo}/actions/runs/{run_id}/deployment_protection_rule"],reviewPendingDeploymentsForRun:["POST /repos/{owner}/{repo}/actions/runs/{run_id}/pending_deployments"],setAllowedActionsOrganization:["PUT /orgs/{org}/actions/permissions/selected-actions"],setAllowedActionsRepository:["PUT /repos/{owner}/{repo}/actions/permissions/selected-actions"],setCustomLabelsForSelfHostedRunnerForOrg:["PUT /orgs/{org}/actions/runners/{runner_id}/labels"],setCustomLabelsForSelfHostedRunnerForRepo:["PUT /repos/{owner}/{repo}/actions/runners/{runner_id}/labels"],setCustomOidcSubClaimForRepo:["PUT /repos/{owner}/{repo}/actions/oidc/customization/sub"],setGithubActionsDefaultWorkflowPermissionsOrganization:["PUT /orgs/{org}/actions/permissions/workflow"],setGithubActionsDefaultWorkflowPermissionsRepository:["PUT /repos/{owner}/{repo}/actions/permissions/workflow"],setGithubActionsPermissionsOrganization:["PUT /orgs/{org}/actions/permissions"],setGithubActionsPermissionsRepository:["PUT /repos/{owner}/{repo}/actions/permissions"],setSelectedReposForOrgSecret:["PUT /orgs/{org}/actions/secrets/{secret_name}/repositories"],setSelectedReposForOrgVariable:["PUT /orgs/{org}/actions/variables/{name}/repositories"],setSelectedRepositoriesEnabledGithubActionsOrganization:["PUT /orgs/{org}/actions/permissions/repositories"],setWorkflowAccessToRepository:["PUT /repos/{owner}/{repo}/actions/permissions/access"],updateEnvironmentVariable:["PATCH /repositories/{repository_id}/environments/{environment_name}/variables/{name}"],updateOrgVariable:["PATCH /orgs/{org}/actions/variables/{name}"],updateRepoVariable:["PATCH /repos/{owner}/{repo}/actions/variables/{name}"]},activity:{checkRepoIsStarredByAuthenticatedUser:["GET /user/starred/{owner}/{repo}"],deleteRepoSubscription:["DELETE /repos/{owner}/{repo}/subscription"],deleteThreadSubscription:["DELETE /notifications/threads/{thread_id}/subscription"],getFeeds:["GET /feeds"],getRepoSubscription:["GET /repos/{owner}/{repo}/subscription"],getThread:["GET /notifications/threads/{thread_id}"],getThreadSubscriptionForAuthenticatedUser:["GET /notifications/threads/{thread_id}/subscription"],listEventsForAuthenticatedUser:["GET /users/{username}/events"],listNotificationsForAuthenticatedUser:["GET /notifications"],listOrgEventsForAuthenticatedUser:["GET /users/{username}/events/orgs/{org}"],listPublicEvents:["GET /events"],listPublicEventsForRepoNetwork:["GET /networks/{owner}/{repo}/events"],listPublicEventsForUser:["GET /users/{username}/events/public"],listPublicOrgEvents:["GET /orgs/{org}/events"],listReceivedEventsForUser:["GET /users/{username}/received_events"],listReceivedPublicEventsForUser:["GET /users/{username}/received_events/public"],listRepoEvents:["GET /repos/{owner}/{repo}/events"],listRepoNotificationsForAuthenticatedUser:["GET /repos/{owner}/{repo}/notifications"],listReposStarredByAuthenticatedUser:["GET /user/starred"],listReposStarredByUser:["GET /users/{username}/starred"],listReposWatchedByUser:["GET /users/{username}/subscriptions"],listStargazersForRepo:["GET /repos/{owner}/{repo}/stargazers"],listWatchedReposForAuthenticatedUser:["GET /user/subscriptions"],listWatchersForRepo:["GET /repos/{owner}/{repo}/subscribers"],markNotificationsAsRead:["PUT /notifications"],markRepoNotificationsAsRead:["PUT /repos/{owner}/{repo}/notifications"],markThreadAsDone:["DELETE /notifications/threads/{thread_id}"],markThreadAsRead:["PATCH /notifications/threads/{thread_id}"],setRepoSubscription:["PUT /repos/{owner}/{repo}/subscription"],setThreadSubscription:["PUT /notifications/threads/{thread_id}/subscription"],starRepoForAuthenticatedUser:["PUT /user/starred/{owner}/{repo}"],unstarRepoForAuthenticatedUser:["DELETE /user/starred/{owner}/{repo}"]},apps:{addRepoToInstallation:["PUT /user/installations/{installation_id}/repositories/{repository_id}",{},{renamed:["apps","addRepoToInstallationForAuthenticatedUser"]}],addRepoToInstallationForAuthenticatedUser:["PUT /user/installations/{installation_id}/repositories/{repository_id}"],checkToken:["POST /applications/{client_id}/token"],createFromManifest:["POST /app-manifests/{code}/conversions"],createInstallationAccessToken:["POST /app/installations/{installation_id}/access_tokens"],deleteAuthorization:["DELETE /applications/{client_id}/grant"],deleteInstallation:["DELETE /app/installations/{installation_id}"],deleteToken:["DELETE /applications/{client_id}/token"],getAuthenticated:["GET /app"],getBySlug:["GET /apps/{app_slug}"],getInstallation:["GET /app/installations/{installation_id}"],getOrgInstallation:["GET /orgs/{org}/installation"],getRepoInstallation:["GET /repos/{owner}/{repo}/installation"],getSubscriptionPlanForAccount:["GET /marketplace_listing/accounts/{account_id}"],getSubscriptionPlanForAccountStubbed:["GET /marketplace_listing/stubbed/accounts/{account_id}"],getUserInstallation:["GET /users/{username}/installation"],getWebhookConfigForApp:["GET /app/hook/config"],getWebhookDelivery:["GET /app/hook/deliveries/{delivery_id}"],listAccountsForPlan:["GET /marketplace_listing/plans/{plan_id}/accounts"],listAccountsForPlanStubbed:["GET /marketplace_listing/stubbed/plans/{plan_id}/accounts"],listInstallationReposForAuthenticatedUser:["GET /user/installations/{installation_id}/repositories"],listInstallationRequestsForAuthenticatedApp:["GET /app/installation-requests"],listInstallations:["GET /app/installations"],listInstallationsForAuthenticatedUser:["GET /user/installations"],listPlans:["GET /marketplace_listing/plans"],listPlansStubbed:["GET /marketplace_listing/stubbed/plans"],listReposAccessibleToInstallation:["GET /installation/repositories"],listSubscriptionsForAuthenticatedUser:["GET /user/marketplace_purchases"],listSubscriptionsForAuthenticatedUserStubbed:["GET /user/marketplace_purchases/stubbed"],listWebhookDeliveries:["GET /app/hook/deliveries"],redeliverWebhookDelivery:["POST /app/hook/deliveries/{delivery_id}/attempts"],removeRepoFromInstallation:["DELETE /user/installations/{installation_id}/repositories/{repository_id}",{},{renamed:["apps","removeRepoFromInstallationForAuthenticatedUser"]}],removeRepoFromInstallationForAuthenticatedUser:["DELETE /user/installations/{installation_id}/repositories/{repository_id}"],resetToken:["PATCH /applications/{client_id}/token"],revokeInstallationAccessToken:["DELETE /installation/token"],scopeToken:["POST /applications/{client_id}/token/scoped"],suspendInstallation:["PUT /app/installations/{installation_id}/suspended"],unsuspendInstallation:["DELETE /app/installations/{installation_id}/suspended"],updateWebhookConfigForApp:["PATCH /app/hook/config"]},billing:{getGithubActionsBillingOrg:["GET /orgs/{org}/settings/billing/actions"],getGithubActionsBillingUser:["GET /users/{username}/settings/billing/actions"],getGithubPackagesBillingOrg:["GET /orgs/{org}/settings/billing/packages"],getGithubPackagesBillingUser:["GET /users/{username}/settings/billing/packages"],getSharedStorageBillingOrg:["GET /orgs/{org}/settings/billing/shared-storage"],getSharedStorageBillingUser:["GET /users/{username}/settings/billing/shared-storage"]},checks:{create:["POST /repos/{owner}/{repo}/check-runs"],createSuite:["POST /repos/{owner}/{repo}/check-suites"],get:["GET /repos/{owner}/{repo}/check-runs/{check_run_id}"],getSuite:["GET /repos/{owner}/{repo}/check-suites/{check_suite_id}"],listAnnotations:["GET /repos/{owner}/{repo}/check-runs/{check_run_id}/annotations"],listForRef:["GET /repos/{owner}/{repo}/commits/{ref}/check-runs"],listForSuite:["GET /repos/{owner}/{repo}/check-suites/{check_suite_id}/check-runs"],listSuitesForRef:["GET /repos/{owner}/{repo}/commits/{ref}/check-suites"],rerequestRun:["POST /repos/{owner}/{repo}/check-runs/{check_run_id}/rerequest"],rerequestSuite:["POST /repos/{owner}/{repo}/check-suites/{check_suite_id}/rerequest"],setSuitesPreferences:["PATCH /repos/{owner}/{repo}/check-suites/preferences"],update:["PATCH /repos/{owner}/{repo}/check-runs/{check_run_id}"]},codeScanning:{deleteAnalysis:["DELETE /repos/{owner}/{repo}/code-scanning/analyses/{analysis_id}{?confirm_delete}"],getAlert:["GET /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}",{},{renamedParameters:{alert_id:"alert_number"}}],getAnalysis:["GET /repos/{owner}/{repo}/code-scanning/analyses/{analysis_id}"],getCodeqlDatabase:["GET /repos/{owner}/{repo}/code-scanning/codeql/databases/{language}"],getDefaultSetup:["GET /repos/{owner}/{repo}/code-scanning/default-setup"],getSarif:["GET /repos/{owner}/{repo}/code-scanning/sarifs/{sarif_id}"],listAlertInstances:["GET /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}/instances"],listAlertsForOrg:["GET /orgs/{org}/code-scanning/alerts"],listAlertsForRepo:["GET /repos/{owner}/{repo}/code-scanning/alerts"],listAlertsInstances:["GET /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}/instances",{},{renamed:["codeScanning","listAlertInstances"]}],listCodeqlDatabases:["GET /repos/{owner}/{repo}/code-scanning/codeql/databases"],listRecentAnalyses:["GET /repos/{owner}/{repo}/code-scanning/analyses"],updateAlert:["PATCH /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}"],updateDefaultSetup:["PATCH /repos/{owner}/{repo}/code-scanning/default-setup"],uploadSarif:["POST /repos/{owner}/{repo}/code-scanning/sarifs"]},codesOfConduct:{getAllCodesOfConduct:["GET /codes_of_conduct"],getConductCode:["GET /codes_of_conduct/{key}"]},codespaces:{addRepositoryForSecretForAuthenticatedUser:["PUT /user/codespaces/secrets/{secret_name}/repositories/{repository_id}"],addSelectedRepoToOrgSecret:["PUT /orgs/{org}/codespaces/secrets/{secret_name}/repositories/{repository_id}"],checkPermissionsForDevcontainer:["GET /repos/{owner}/{repo}/codespaces/permissions_check"],codespaceMachinesForAuthenticatedUser:["GET /user/codespaces/{codespace_name}/machines"],createForAuthenticatedUser:["POST /user/codespaces"],createOrUpdateOrgSecret:["PUT /orgs/{org}/codespaces/secrets/{secret_name}"],createOrUpdateRepoSecret:["PUT /repos/{owner}/{repo}/codespaces/secrets/{secret_name}"],createOrUpdateSecretForAuthenticatedUser:["PUT /user/codespaces/secrets/{secret_name}"],createWithPrForAuthenticatedUser:["POST /repos/{owner}/{repo}/pulls/{pull_number}/codespaces"],createWithRepoForAuthenticatedUser:["POST /repos/{owner}/{repo}/codespaces"],deleteForAuthenticatedUser:["DELETE /user/codespaces/{codespace_name}"],deleteFromOrganization:["DELETE /orgs/{org}/members/{username}/codespaces/{codespace_name}"],deleteOrgSecret:["DELETE /orgs/{org}/codespaces/secrets/{secret_name}"],deleteRepoSecret:["DELETE /repos/{owner}/{repo}/codespaces/secrets/{secret_name}"],deleteSecretForAuthenticatedUser:["DELETE /user/codespaces/secrets/{secret_name}"],exportForAuthenticatedUser:["POST /user/codespaces/{codespace_name}/exports"],getCodespacesForUserInOrg:["GET /orgs/{org}/members/{username}/codespaces"],getExportDetailsForAuthenticatedUser:["GET /user/codespaces/{codespace_name}/exports/{export_id}"],getForAuthenticatedUser:["GET /user/codespaces/{codespace_name}"],getOrgPublicKey:["GET /orgs/{org}/codespaces/secrets/public-key"],getOrgSecret:["GET /orgs/{org}/codespaces/secrets/{secret_name}"],getPublicKeyForAuthenticatedUser:["GET /user/codespaces/secrets/public-key"],getRepoPublicKey:["GET /repos/{owner}/{repo}/codespaces/secrets/public-key"],getRepoSecret:["GET /repos/{owner}/{repo}/codespaces/secrets/{secret_name}"],getSecretForAuthenticatedUser:["GET /user/codespaces/secrets/{secret_name}"],listDevcontainersInRepositoryForAuthenticatedUser:["GET /repos/{owner}/{repo}/codespaces/devcontainers"],listForAuthenticatedUser:["GET /user/codespaces"],listInOrganization:["GET /orgs/{org}/codespaces",{},{renamedParameters:{org_id:"org"}}],listInRepositoryForAuthenticatedUser:["GET /repos/{owner}/{repo}/codespaces"],listOrgSecrets:["GET /orgs/{org}/codespaces/secrets"],listRepoSecrets:["GET /repos/{owner}/{repo}/codespaces/secrets"],listRepositoriesForSecretForAuthenticatedUser:["GET /user/codespaces/secrets/{secret_name}/repositories"],listSecretsForAuthenticatedUser:["GET /user/codespaces/secrets"],listSelectedReposForOrgSecret:["GET /orgs/{org}/codespaces/secrets/{secret_name}/repositories"],preFlightWithRepoForAuthenticatedUser:["GET /repos/{owner}/{repo}/codespaces/new"],publishForAuthenticatedUser:["POST /user/codespaces/{codespace_name}/publish"],removeRepositoryForSecretForAuthenticatedUser:["DELETE /user/codespaces/secrets/{secret_name}/repositories/{repository_id}"],removeSelectedRepoFromOrgSecret:["DELETE /orgs/{org}/codespaces/secrets/{secret_name}/repositories/{repository_id}"],repoMachinesForAuthenticatedUser:["GET /repos/{owner}/{repo}/codespaces/machines"],setRepositoriesForSecretForAuthenticatedUser:["PUT /user/codespaces/secrets/{secret_name}/repositories"],setSelectedReposForOrgSecret:["PUT /orgs/{org}/codespaces/secrets/{secret_name}/repositories"],startForAuthenticatedUser:["POST /user/codespaces/{codespace_name}/start"],stopForAuthenticatedUser:["POST /user/codespaces/{codespace_name}/stop"],stopInOrganization:["POST /orgs/{org}/members/{username}/codespaces/{codespace_name}/stop"],updateForAuthenticatedUser:["PATCH /user/codespaces/{codespace_name}"]},copilot:{addCopilotSeatsForTeams:["POST /orgs/{org}/copilot/billing/selected_teams"],addCopilotSeatsForUsers:["POST /orgs/{org}/copilot/billing/selected_users"],cancelCopilotSeatAssignmentForTeams:["DELETE /orgs/{org}/copilot/billing/selected_teams"],cancelCopilotSeatAssignmentForUsers:["DELETE /orgs/{org}/copilot/billing/selected_users"],getCopilotOrganizationDetails:["GET /orgs/{org}/copilot/billing"],getCopilotSeatDetailsForUser:["GET /orgs/{org}/members/{username}/copilot"],listCopilotSeats:["GET /orgs/{org}/copilot/billing/seats"]},dependabot:{addSelectedRepoToOrgSecret:["PUT /orgs/{org}/dependabot/secrets/{secret_name}/repositories/{repository_id}"],createOrUpdateOrgSecret:["PUT /orgs/{org}/dependabot/secrets/{secret_name}"],createOrUpdateRepoSecret:["PUT /repos/{owner}/{repo}/dependabot/secrets/{secret_name}"],deleteOrgSecret:["DELETE /orgs/{org}/dependabot/secrets/{secret_name}"],deleteRepoSecret:["DELETE /repos/{owner}/{repo}/dependabot/secrets/{secret_name}"],getAlert:["GET /repos/{owner}/{repo}/dependabot/alerts/{alert_number}"],getOrgPublicKey:["GET /orgs/{org}/dependabot/secrets/public-key"],getOrgSecret:["GET /orgs/{org}/dependabot/secrets/{secret_name}"],getRepoPublicKey:["GET /repos/{owner}/{repo}/dependabot/secrets/public-key"],getRepoSecret:["GET /repos/{owner}/{repo}/dependabot/secrets/{secret_name}"],listAlertsForEnterprise:["GET /enterprises/{enterprise}/dependabot/alerts"],listAlertsForOrg:["GET /orgs/{org}/dependabot/alerts"],listAlertsForRepo:["GET /repos/{owner}/{repo}/dependabot/alerts"],listOrgSecrets:["GET /orgs/{org}/dependabot/secrets"],listRepoSecrets:["GET /repos/{owner}/{repo}/dependabot/secrets"],listSelectedReposForOrgSecret:["GET /orgs/{org}/dependabot/secrets/{secret_name}/repositories"],removeSelectedRepoFromOrgSecret:["DELETE /orgs/{org}/dependabot/secrets/{secret_name}/repositories/{repository_id}"],setSelectedReposForOrgSecret:["PUT /orgs/{org}/dependabot/secrets/{secret_name}/repositories"],updateAlert:["PATCH /repos/{owner}/{repo}/dependabot/alerts/{alert_number}"]},dependencyGraph:{createRepositorySnapshot:["POST /repos/{owner}/{repo}/dependency-graph/snapshots"],diffRange:["GET /repos/{owner}/{repo}/dependency-graph/compare/{basehead}"],exportSbom:["GET /repos/{owner}/{repo}/dependency-graph/sbom"]},emojis:{get:["GET /emojis"]},gists:{checkIsStarred:["GET /gists/{gist_id}/star"],create:["POST /gists"],createComment:["POST /gists/{gist_id}/comments"],delete:["DELETE /gists/{gist_id}"],deleteComment:["DELETE /gists/{gist_id}/comments/{comment_id}"],fork:["POST /gists/{gist_id}/forks"],get:["GET /gists/{gist_id}"],getComment:["GET /gists/{gist_id}/comments/{comment_id}"],getRevision:["GET /gists/{gist_id}/{sha}"],list:["GET /gists"],listComments:["GET /gists/{gist_id}/comments"],listCommits:["GET /gists/{gist_id}/commits"],listForUser:["GET /users/{username}/gists"],listForks:["GET /gists/{gist_id}/forks"],listPublic:["GET /gists/public"],listStarred:["GET /gists/starred"],star:["PUT /gists/{gist_id}/star"],unstar:["DELETE /gists/{gist_id}/star"],update:["PATCH /gists/{gist_id}"],updateComment:["PATCH /gists/{gist_id}/comments/{comment_id}"]},git:{createBlob:["POST /repos/{owner}/{repo}/git/blobs"],createCommit:["POST /repos/{owner}/{repo}/git/commits"],createRef:["POST /repos/{owner}/{repo}/git/refs"],createTag:["POST /repos/{owner}/{repo}/git/tags"],createTree:["POST /repos/{owner}/{repo}/git/trees"],deleteRef:["DELETE /repos/{owner}/{repo}/git/refs/{ref}"],getBlob:["GET /repos/{owner}/{repo}/git/blobs/{file_sha}"],getCommit:["GET /repos/{owner}/{repo}/git/commits/{commit_sha}"],getRef:["GET /repos/{owner}/{repo}/git/ref/{ref}"],getTag:["GET /repos/{owner}/{repo}/git/tags/{tag_sha}"],getTree:["GET /repos/{owner}/{repo}/git/trees/{tree_sha}"],listMatchingRefs:["GET /repos/{owner}/{repo}/git/matching-refs/{ref}"],updateRef:["PATCH /repos/{owner}/{repo}/git/refs/{ref}"]},gitignore:{getAllTemplates:["GET /gitignore/templates"],getTemplate:["GET /gitignore/templates/{name}"]},interactions:{getRestrictionsForAuthenticatedUser:["GET /user/interaction-limits"],getRestrictionsForOrg:["GET /orgs/{org}/interaction-limits"],getRestrictionsForRepo:["GET /repos/{owner}/{repo}/interaction-limits"],getRestrictionsForYourPublicRepos:["GET /user/interaction-limits",{},{renamed:["interactions","getRestrictionsForAuthenticatedUser"]}],removeRestrictionsForAuthenticatedUser:["DELETE /user/interaction-limits"],removeRestrictionsForOrg:["DELETE /orgs/{org}/interaction-limits"],removeRestrictionsForRepo:["DELETE /repos/{owner}/{repo}/interaction-limits"],removeRestrictionsForYourPublicRepos:["DELETE /user/interaction-limits",{},{renamed:["interactions","removeRestrictionsForAuthenticatedUser"]}],setRestrictionsForAuthenticatedUser:["PUT /user/interaction-limits"],setRestrictionsForOrg:["PUT /orgs/{org}/interaction-limits"],setRestrictionsForRepo:["PUT /repos/{owner}/{repo}/interaction-limits"],setRestrictionsForYourPublicRepos:["PUT /user/interaction-limits",{},{renamed:["interactions","setRestrictionsForAuthenticatedUser"]}]},issues:{addAssignees:["POST /repos/{owner}/{repo}/issues/{issue_number}/assignees"],addLabels:["POST /repos/{owner}/{repo}/issues/{issue_number}/labels"],checkUserCanBeAssigned:["GET /repos/{owner}/{repo}/assignees/{assignee}"],checkUserCanBeAssignedToIssue:["GET /repos/{owner}/{repo}/issues/{issue_number}/assignees/{assignee}"],create:["POST /repos/{owner}/{repo}/issues"],createComment:["POST /repos/{owner}/{repo}/issues/{issue_number}/comments"],createLabel:["POST /repos/{owner}/{repo}/labels"],createMilestone:["POST /repos/{owner}/{repo}/milestones"],deleteComment:["DELETE /repos/{owner}/{repo}/issues/comments/{comment_id}"],deleteLabel:["DELETE /repos/{owner}/{repo}/labels/{name}"],deleteMilestone:["DELETE /repos/{owner}/{repo}/milestones/{milestone_number}"],get:["GET /repos/{owner}/{repo}/issues/{issue_number}"],getComment:["GET /repos/{owner}/{repo}/issues/comments/{comment_id}"],getEvent:["GET /repos/{owner}/{repo}/issues/events/{event_id}"],getLabel:["GET /repos/{owner}/{repo}/labels/{name}"],getMilestone:["GET /repos/{owner}/{repo}/milestones/{milestone_number}"],list:["GET /issues"],listAssignees:["GET /repos/{owner}/{repo}/assignees"],listComments:["GET /repos/{owner}/{repo}/issues/{issue_number}/comments"],listCommentsForRepo:["GET /repos/{owner}/{repo}/issues/comments"],listEvents:["GET /repos/{owner}/{repo}/issues/{issue_number}/events"],listEventsForRepo:["GET /repos/{owner}/{repo}/issues/events"],listEventsForTimeline:["GET /repos/{owner}/{repo}/issues/{issue_number}/timeline"],listForAuthenticatedUser:["GET /user/issues"],listForOrg:["GET /orgs/{org}/issues"],listForRepo:["GET /repos/{owner}/{repo}/issues"],listLabelsForMilestone:["GET /repos/{owner}/{repo}/milestones/{milestone_number}/labels"],listLabelsForRepo:["GET /repos/{owner}/{repo}/labels"],listLabelsOnIssue:["GET /repos/{owner}/{repo}/issues/{issue_number}/labels"],listMilestones:["GET /repos/{owner}/{repo}/milestones"],lock:["PUT /repos/{owner}/{repo}/issues/{issue_number}/lock"],removeAllLabels:["DELETE /repos/{owner}/{repo}/issues/{issue_number}/labels"],removeAssignees:["DELETE /repos/{owner}/{repo}/issues/{issue_number}/assignees"],removeLabel:["DELETE /repos/{owner}/{repo}/issues/{issue_number}/labels/{name}"],setLabels:["PUT /repos/{owner}/{repo}/issues/{issue_number}/labels"],unlock:["DELETE /repos/{owner}/{repo}/issues/{issue_number}/lock"],update:["PATCH /repos/{owner}/{repo}/issues/{issue_number}"],updateComment:["PATCH /repos/{owner}/{repo}/issues/comments/{comment_id}"],updateLabel:["PATCH /repos/{owner}/{repo}/labels/{name}"],updateMilestone:["PATCH /repos/{owner}/{repo}/milestones/{milestone_number}"]},licenses:{get:["GET /licenses/{license}"],getAllCommonlyUsed:["GET /licenses"],getForRepo:["GET /repos/{owner}/{repo}/license"]},markdown:{render:["POST /markdown"],renderRaw:["POST /markdown/raw",{headers:{"content-type":"text/plain; charset=utf-8"}}]},meta:{get:["GET /meta"],getAllVersions:["GET /versions"],getOctocat:["GET /octocat"],getZen:["GET /zen"],root:["GET /"]},migrations:{cancelImport:["DELETE /repos/{owner}/{repo}/import",{},{deprecated:"octokit.rest.migrations.cancelImport() is deprecated, see https://docs.github.com/rest/migrations/source-imports#cancel-an-import"}],deleteArchiveForAuthenticatedUser:["DELETE /user/migrations/{migration_id}/archive"],deleteArchiveForOrg:["DELETE /orgs/{org}/migrations/{migration_id}/archive"],downloadArchiveForOrg:["GET /orgs/{org}/migrations/{migration_id}/archive"],getArchiveForAuthenticatedUser:["GET /user/migrations/{migration_id}/archive"],getCommitAuthors:["GET /repos/{owner}/{repo}/import/authors",{},{deprecated:"octokit.rest.migrations.getCommitAuthors() is deprecated, see https://docs.github.com/rest/migrations/source-imports#get-commit-authors"}],getImportStatus:["GET /repos/{owner}/{repo}/import",{},{deprecated:"octokit.rest.migrations.getImportStatus() is deprecated, see https://docs.github.com/rest/migrations/source-imports#get-an-import-status"}],getLargeFiles:["GET /repos/{owner}/{repo}/import/large_files",{},{deprecated:"octokit.rest.migrations.getLargeFiles() is deprecated, see https://docs.github.com/rest/migrations/source-imports#get-large-files"}],getStatusForAuthenticatedUser:["GET /user/migrations/{migration_id}"],getStatusForOrg:["GET /orgs/{org}/migrations/{migration_id}"],listForAuthenticatedUser:["GET /user/migrations"],listForOrg:["GET /orgs/{org}/migrations"],listReposForAuthenticatedUser:["GET /user/migrations/{migration_id}/repositories"],listReposForOrg:["GET /orgs/{org}/migrations/{migration_id}/repositories"],listReposForUser:["GET /user/migrations/{migration_id}/repositories",{},{renamed:["migrations","listReposForAuthenticatedUser"]}],mapCommitAuthor:["PATCH /repos/{owner}/{repo}/import/authors/{author_id}",{},{deprecated:"octokit.rest.migrations.mapCommitAuthor() is deprecated, see https://docs.github.com/rest/migrations/source-imports#map-a-commit-author"}],setLfsPreference:["PATCH /repos/{owner}/{repo}/import/lfs",{},{deprecated:"octokit.rest.migrations.setLfsPreference() is deprecated, see https://docs.github.com/rest/migrations/source-imports#update-git-lfs-preference"}],startForAuthenticatedUser:["POST /user/migrations"],startForOrg:["POST /orgs/{org}/migrations"],startImport:["PUT /repos/{owner}/{repo}/import",{},{deprecated:"octokit.rest.migrations.startImport() is deprecated, see https://docs.github.com/rest/migrations/source-imports#start-an-import"}],unlockRepoForAuthenticatedUser:["DELETE /user/migrations/{migration_id}/repos/{repo_name}/lock"],unlockRepoForOrg:["DELETE /orgs/{org}/migrations/{migration_id}/repos/{repo_name}/lock"],updateImport:["PATCH /repos/{owner}/{repo}/import",{},{deprecated:"octokit.rest.migrations.updateImport() is deprecated, see https://docs.github.com/rest/migrations/source-imports#update-an-import"}]},oidc:{getOidcCustomSubTemplateForOrg:["GET /orgs/{org}/actions/oidc/customization/sub"],updateOidcCustomSubTemplateForOrg:["PUT /orgs/{org}/actions/oidc/customization/sub"]},orgs:{addSecurityManagerTeam:["PUT /orgs/{org}/security-managers/teams/{team_slug}"],assignTeamToOrgRole:["PUT /orgs/{org}/organization-roles/teams/{team_slug}/{role_id}"],assignUserToOrgRole:["PUT /orgs/{org}/organization-roles/users/{username}/{role_id}"],blockUser:["PUT /orgs/{org}/blocks/{username}"],cancelInvitation:["DELETE /orgs/{org}/invitations/{invitation_id}"],checkBlockedUser:["GET /orgs/{org}/blocks/{username}"],checkMembershipForUser:["GET /orgs/{org}/members/{username}"],checkPublicMembershipForUser:["GET /orgs/{org}/public_members/{username}"],convertMemberToOutsideCollaborator:["PUT /orgs/{org}/outside_collaborators/{username}"],createCustomOrganizationRole:["POST /orgs/{org}/organization-roles"],createInvitation:["POST /orgs/{org}/invitations"],createOrUpdateCustomProperties:["PATCH /orgs/{org}/properties/schema"],createOrUpdateCustomPropertiesValuesForRepos:["PATCH /orgs/{org}/properties/values"],createOrUpdateCustomProperty:["PUT /orgs/{org}/properties/schema/{custom_property_name}"],createWebhook:["POST /orgs/{org}/hooks"],delete:["DELETE /orgs/{org}"],deleteCustomOrganizationRole:["DELETE /orgs/{org}/organization-roles/{role_id}"],deleteWebhook:["DELETE /orgs/{org}/hooks/{hook_id}"],enableOrDisableSecurityProductOnAllOrgRepos:["POST /orgs/{org}/{security_product}/{enablement}"],get:["GET /orgs/{org}"],getAllCustomProperties:["GET /orgs/{org}/properties/schema"],getCustomProperty:["GET /orgs/{org}/properties/schema/{custom_property_name}"],getMembershipForAuthenticatedUser:["GET /user/memberships/orgs/{org}"],getMembershipForUser:["GET /orgs/{org}/memberships/{username}"],getOrgRole:["GET /orgs/{org}/organization-roles/{role_id}"],getWebhook:["GET /orgs/{org}/hooks/{hook_id}"],getWebhookConfigForOrg:["GET /orgs/{org}/hooks/{hook_id}/config"],getWebhookDelivery:["GET /orgs/{org}/hooks/{hook_id}/deliveries/{delivery_id}"],list:["GET /organizations"],listAppInstallations:["GET /orgs/{org}/installations"],listBlockedUsers:["GET /orgs/{org}/blocks"],listCustomPropertiesValuesForRepos:["GET /orgs/{org}/properties/values"],listFailedInvitations:["GET /orgs/{org}/failed_invitations"],listForAuthenticatedUser:["GET /user/orgs"],listForUser:["GET /users/{username}/orgs"],listInvitationTeams:["GET /orgs/{org}/invitations/{invitation_id}/teams"],listMembers:["GET /orgs/{org}/members"],listMembershipsForAuthenticatedUser:["GET /user/memberships/orgs"],listOrgRoleTeams:["GET /orgs/{org}/organization-roles/{role_id}/teams"],listOrgRoleUsers:["GET /orgs/{org}/organization-roles/{role_id}/users"],listOrgRoles:["GET /orgs/{org}/organization-roles"],listOrganizationFineGrainedPermissions:["GET /orgs/{org}/organization-fine-grained-permissions"],listOutsideCollaborators:["GET /orgs/{org}/outside_collaborators"],listPatGrantRepositories:["GET /orgs/{org}/personal-access-tokens/{pat_id}/repositories"],listPatGrantRequestRepositories:["GET /orgs/{org}/personal-access-token-requests/{pat_request_id}/repositories"],listPatGrantRequests:["GET /orgs/{org}/personal-access-token-requests"],listPatGrants:["GET /orgs/{org}/personal-access-tokens"],listPendingInvitations:["GET /orgs/{org}/invitations"],listPublicMembers:["GET /orgs/{org}/public_members"],listSecurityManagerTeams:["GET /orgs/{org}/security-managers"],listWebhookDeliveries:["GET /orgs/{org}/hooks/{hook_id}/deliveries"],listWebhooks:["GET /orgs/{org}/hooks"],patchCustomOrganizationRole:["PATCH /orgs/{org}/organization-roles/{role_id}"],pingWebhook:["POST /orgs/{org}/hooks/{hook_id}/pings"],redeliverWebhookDelivery:["POST /orgs/{org}/hooks/{hook_id}/deliveries/{delivery_id}/attempts"],removeCustomProperty:["DELETE /orgs/{org}/properties/schema/{custom_property_name}"],removeMember:["DELETE /orgs/{org}/members/{username}"],removeMembershipForUser:["DELETE /orgs/{org}/memberships/{username}"],removeOutsideCollaborator:["DELETE /orgs/{org}/outside_collaborators/{username}"],removePublicMembershipForAuthenticatedUser:["DELETE /orgs/{org}/public_members/{username}"],removeSecurityManagerTeam:["DELETE /orgs/{org}/security-managers/teams/{team_slug}"],reviewPatGrantRequest:["POST /orgs/{org}/personal-access-token-requests/{pat_request_id}"],reviewPatGrantRequestsInBulk:["POST /orgs/{org}/personal-access-token-requests"],revokeAllOrgRolesTeam:["DELETE /orgs/{org}/organization-roles/teams/{team_slug}"],revokeAllOrgRolesUser:["DELETE /orgs/{org}/organization-roles/users/{username}"],revokeOrgRoleTeam:["DELETE /orgs/{org}/organization-roles/teams/{team_slug}/{role_id}"],revokeOrgRoleUser:["DELETE /orgs/{org}/organization-roles/users/{username}/{role_id}"],setMembershipForUser:["PUT /orgs/{org}/memberships/{username}"],setPublicMembershipForAuthenticatedUser:["PUT /orgs/{org}/public_members/{username}"],unblockUser:["DELETE /orgs/{org}/blocks/{username}"],update:["PATCH /orgs/{org}"],updateMembershipForAuthenticatedUser:["PATCH /user/memberships/orgs/{org}"],updatePatAccess:["POST /orgs/{org}/personal-access-tokens/{pat_id}"],updatePatAccesses:["POST /orgs/{org}/personal-access-tokens"],updateWebhook:["PATCH /orgs/{org}/hooks/{hook_id}"],updateWebhookConfigForOrg:["PATCH /orgs/{org}/hooks/{hook_id}/config"]},packages:{deletePackageForAuthenticatedUser:["DELETE /user/packages/{package_type}/{package_name}"],deletePackageForOrg:["DELETE /orgs/{org}/packages/{package_type}/{package_name}"],deletePackageForUser:["DELETE /users/{username}/packages/{package_type}/{package_name}"],deletePackageVersionForAuthenticatedUser:["DELETE /user/packages/{package_type}/{package_name}/versions/{package_version_id}"],deletePackageVersionForOrg:["DELETE /orgs/{org}/packages/{package_type}/{package_name}/versions/{package_version_id}"],deletePackageVersionForUser:["DELETE /users/{username}/packages/{package_type}/{package_name}/versions/{package_version_id}"],getAllPackageVersionsForAPackageOwnedByAnOrg:["GET /orgs/{org}/packages/{package_type}/{package_name}/versions",{},{renamed:["packages","getAllPackageVersionsForPackageOwnedByOrg"]}],getAllPackageVersionsForAPackageOwnedByTheAuthenticatedUser:["GET /user/packages/{package_type}/{package_name}/versions",{},{renamed:["packages","getAllPackageVersionsForPackageOwnedByAuthenticatedUser"]}],getAllPackageVersionsForPackageOwnedByAuthenticatedUser:["GET /user/packages/{package_type}/{package_name}/versions"],getAllPackageVersionsForPackageOwnedByOrg:["GET /orgs/{org}/packages/{package_type}/{package_name}/versions"],getAllPackageVersionsForPackageOwnedByUser:["GET /users/{username}/packages/{package_type}/{package_name}/versions"],getPackageForAuthenticatedUser:["GET /user/packages/{package_type}/{package_name}"],getPackageForOrganization:["GET /orgs/{org}/packages/{package_type}/{package_name}"],getPackageForUser:["GET /users/{username}/packages/{package_type}/{package_name}"],getPackageVersionForAuthenticatedUser:["GET /user/packages/{package_type}/{package_name}/versions/{package_version_id}"],getPackageVersionForOrganization:["GET /orgs/{org}/packages/{package_type}/{package_name}/versions/{package_version_id}"],getPackageVersionForUser:["GET /users/{username}/packages/{package_type}/{package_name}/versions/{package_version_id}"],listDockerMigrationConflictingPackagesForAuthenticatedUser:["GET /user/docker/conflicts"],listDockerMigrationConflictingPackagesForOrganization:["GET /orgs/{org}/docker/conflicts"],listDockerMigrationConflictingPackagesForUser:["GET /users/{username}/docker/conflicts"],listPackagesForAuthenticatedUser:["GET /user/packages"],listPackagesForOrganization:["GET /orgs/{org}/packages"],listPackagesForUser:["GET /users/{username}/packages"],restorePackageForAuthenticatedUser:["POST /user/packages/{package_type}/{package_name}/restore{?token}"],restorePackageForOrg:["POST /orgs/{org}/packages/{package_type}/{package_name}/restore{?token}"],restorePackageForUser:["POST /users/{username}/packages/{package_type}/{package_name}/restore{?token}"],restorePackageVersionForAuthenticatedUser:["POST /user/packages/{package_type}/{package_name}/versions/{package_version_id}/restore"],restorePackageVersionForOrg:["POST /orgs/{org}/packages/{package_type}/{package_name}/versions/{package_version_id}/restore"],restorePackageVersionForUser:["POST /users/{username}/packages/{package_type}/{package_name}/versions/{package_version_id}/restore"]},projects:{addCollaborator:["PUT /projects/{project_id}/collaborators/{username}"],createCard:["POST /projects/columns/{column_id}/cards"],createColumn:["POST /projects/{project_id}/columns"],createForAuthenticatedUser:["POST /user/projects"],createForOrg:["POST /orgs/{org}/projects"],createForRepo:["POST /repos/{owner}/{repo}/projects"],delete:["DELETE /projects/{project_id}"],deleteCard:["DELETE /projects/columns/cards/{card_id}"],deleteColumn:["DELETE /projects/columns/{column_id}"],get:["GET /projects/{project_id}"],getCard:["GET /projects/columns/cards/{card_id}"],getColumn:["GET /projects/columns/{column_id}"],getPermissionForUser:["GET /projects/{project_id}/collaborators/{username}/permission"],listCards:["GET /projects/columns/{column_id}/cards"],listCollaborators:["GET /projects/{project_id}/collaborators"],listColumns:["GET /projects/{project_id}/columns"],listForOrg:["GET /orgs/{org}/projects"],listForRepo:["GET /repos/{owner}/{repo}/projects"],listForUser:["GET /users/{username}/projects"],moveCard:["POST /projects/columns/cards/{card_id}/moves"],moveColumn:["POST /projects/columns/{column_id}/moves"],removeCollaborator:["DELETE /projects/{project_id}/collaborators/{username}"],update:["PATCH /projects/{project_id}"],updateCard:["PATCH /projects/columns/cards/{card_id}"],updateColumn:["PATCH /projects/columns/{column_id}"]},pulls:{checkIfMerged:["GET /repos/{owner}/{repo}/pulls/{pull_number}/merge"],create:["POST /repos/{owner}/{repo}/pulls"],createReplyForReviewComment:["POST /repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies"],createReview:["POST /repos/{owner}/{repo}/pulls/{pull_number}/reviews"],createReviewComment:["POST /repos/{owner}/{repo}/pulls/{pull_number}/comments"],deletePendingReview:["DELETE /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}"],deleteReviewComment:["DELETE /repos/{owner}/{repo}/pulls/comments/{comment_id}"],dismissReview:["PUT /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/dismissals"],get:["GET /repos/{owner}/{repo}/pulls/{pull_number}"],getReview:["GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}"],getReviewComment:["GET /repos/{owner}/{repo}/pulls/comments/{comment_id}"],list:["GET /repos/{owner}/{repo}/pulls"],listCommentsForReview:["GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/comments"],listCommits:["GET /repos/{owner}/{repo}/pulls/{pull_number}/commits"],listFiles:["GET /repos/{owner}/{repo}/pulls/{pull_number}/files"],listRequestedReviewers:["GET /repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers"],listReviewComments:["GET /repos/{owner}/{repo}/pulls/{pull_number}/comments"],listReviewCommentsForRepo:["GET /repos/{owner}/{repo}/pulls/comments"],listReviews:["GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews"],merge:["PUT /repos/{owner}/{repo}/pulls/{pull_number}/merge"],removeRequestedReviewers:["DELETE /repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers"],requestReviewers:["POST /repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers"],submitReview:["POST /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/events"],update:["PATCH /repos/{owner}/{repo}/pulls/{pull_number}"],updateBranch:["PUT /repos/{owner}/{repo}/pulls/{pull_number}/update-branch"],updateReview:["PUT /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}"],updateReviewComment:["PATCH /repos/{owner}/{repo}/pulls/comments/{comment_id}"]},rateLimit:{get:["GET /rate_limit"]},reactions:{createForCommitComment:["POST /repos/{owner}/{repo}/comments/{comment_id}/reactions"],createForIssue:["POST /repos/{owner}/{repo}/issues/{issue_number}/reactions"],createForIssueComment:["POST /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions"],createForPullRequestReviewComment:["POST /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions"],createForRelease:["POST /repos/{owner}/{repo}/releases/{release_id}/reactions"],createForTeamDiscussionCommentInOrg:["POST /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}/reactions"],createForTeamDiscussionInOrg:["POST /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions"],deleteForCommitComment:["DELETE /repos/{owner}/{repo}/comments/{comment_id}/reactions/{reaction_id}"],deleteForIssue:["DELETE /repos/{owner}/{repo}/issues/{issue_number}/reactions/{reaction_id}"],deleteForIssueComment:["DELETE /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions/{reaction_id}"],deleteForPullRequestComment:["DELETE /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions/{reaction_id}"],deleteForRelease:["DELETE /repos/{owner}/{repo}/releases/{release_id}/reactions/{reaction_id}"],deleteForTeamDiscussion:["DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions/{reaction_id}"],deleteForTeamDiscussionComment:["DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}/reactions/{reaction_id}"],listForCommitComment:["GET /repos/{owner}/{repo}/comments/{comment_id}/reactions"],listForIssue:["GET /repos/{owner}/{repo}/issues/{issue_number}/reactions"],listForIssueComment:["GET /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions"],listForPullRequestReviewComment:["GET /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions"],listForRelease:["GET /repos/{owner}/{repo}/releases/{release_id}/reactions"],listForTeamDiscussionCommentInOrg:["GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}/reactions"],listForTeamDiscussionInOrg:["GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions"]},repos:{acceptInvitation:["PATCH /user/repository_invitations/{invitation_id}",{},{renamed:["repos","acceptInvitationForAuthenticatedUser"]}],acceptInvitationForAuthenticatedUser:["PATCH /user/repository_invitations/{invitation_id}"],addAppAccessRestrictions:["POST /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps",{},{mapToData:"apps"}],addCollaborator:["PUT /repos/{owner}/{repo}/collaborators/{username}"],addStatusCheckContexts:["POST /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts",{},{mapToData:"contexts"}],addTeamAccessRestrictions:["POST /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams",{},{mapToData:"teams"}],addUserAccessRestrictions:["POST /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users",{},{mapToData:"users"}],cancelPagesDeployment:["POST /repos/{owner}/{repo}/pages/deployments/{pages_deployment_id}/cancel"],checkAutomatedSecurityFixes:["GET /repos/{owner}/{repo}/automated-security-fixes"],checkCollaborator:["GET /repos/{owner}/{repo}/collaborators/{username}"],checkVulnerabilityAlerts:["GET /repos/{owner}/{repo}/vulnerability-alerts"],codeownersErrors:["GET /repos/{owner}/{repo}/codeowners/errors"],compareCommits:["GET /repos/{owner}/{repo}/compare/{base}...{head}"],compareCommitsWithBasehead:["GET /repos/{owner}/{repo}/compare/{basehead}"],createAutolink:["POST /repos/{owner}/{repo}/autolinks"],createCommitComment:["POST /repos/{owner}/{repo}/commits/{commit_sha}/comments"],createCommitSignatureProtection:["POST /repos/{owner}/{repo}/branches/{branch}/protection/required_signatures"],createCommitStatus:["POST /repos/{owner}/{repo}/statuses/{sha}"],createDeployKey:["POST /repos/{owner}/{repo}/keys"],createDeployment:["POST /repos/{owner}/{repo}/deployments"],createDeploymentBranchPolicy:["POST /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies"],createDeploymentProtectionRule:["POST /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules"],createDeploymentStatus:["POST /repos/{owner}/{repo}/deployments/{deployment_id}/statuses"],createDispatchEvent:["POST /repos/{owner}/{repo}/dispatches"],createForAuthenticatedUser:["POST /user/repos"],createFork:["POST /repos/{owner}/{repo}/forks"],createInOrg:["POST /orgs/{org}/repos"],createOrUpdateCustomPropertiesValues:["PATCH /repos/{owner}/{repo}/properties/values"],createOrUpdateEnvironment:["PUT /repos/{owner}/{repo}/environments/{environment_name}"],createOrUpdateFileContents:["PUT /repos/{owner}/{repo}/contents/{path}"],createOrgRuleset:["POST /orgs/{org}/rulesets"],createPagesDeployment:["POST /repos/{owner}/{repo}/pages/deployments"],createPagesSite:["POST /repos/{owner}/{repo}/pages"],createRelease:["POST /repos/{owner}/{repo}/releases"],createRepoRuleset:["POST /repos/{owner}/{repo}/rulesets"],createTagProtection:["POST /repos/{owner}/{repo}/tags/protection"],createUsingTemplate:["POST /repos/{template_owner}/{template_repo}/generate"],createWebhook:["POST /repos/{owner}/{repo}/hooks"],declineInvitation:["DELETE /user/repository_invitations/{invitation_id}",{},{renamed:["repos","declineInvitationForAuthenticatedUser"]}],declineInvitationForAuthenticatedUser:["DELETE /user/repository_invitations/{invitation_id}"],delete:["DELETE /repos/{owner}/{repo}"],deleteAccessRestrictions:["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions"],deleteAdminBranchProtection:["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins"],deleteAnEnvironment:["DELETE /repos/{owner}/{repo}/environments/{environment_name}"],deleteAutolink:["DELETE /repos/{owner}/{repo}/autolinks/{autolink_id}"],deleteBranchProtection:["DELETE /repos/{owner}/{repo}/branches/{branch}/protection"],deleteCommitComment:["DELETE /repos/{owner}/{repo}/comments/{comment_id}"],deleteCommitSignatureProtection:["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_signatures"],deleteDeployKey:["DELETE /repos/{owner}/{repo}/keys/{key_id}"],deleteDeployment:["DELETE /repos/{owner}/{repo}/deployments/{deployment_id}"],deleteDeploymentBranchPolicy:["DELETE /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies/{branch_policy_id}"],deleteFile:["DELETE /repos/{owner}/{repo}/contents/{path}"],deleteInvitation:["DELETE /repos/{owner}/{repo}/invitations/{invitation_id}"],deleteOrgRuleset:["DELETE /orgs/{org}/rulesets/{ruleset_id}"],deletePagesSite:["DELETE /repos/{owner}/{repo}/pages"],deletePullRequestReviewProtection:["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews"],deleteRelease:["DELETE /repos/{owner}/{repo}/releases/{release_id}"],deleteReleaseAsset:["DELETE /repos/{owner}/{repo}/releases/assets/{asset_id}"],deleteRepoRuleset:["DELETE /repos/{owner}/{repo}/rulesets/{ruleset_id}"],deleteTagProtection:["DELETE /repos/{owner}/{repo}/tags/protection/{tag_protection_id}"],deleteWebhook:["DELETE /repos/{owner}/{repo}/hooks/{hook_id}"],disableAutomatedSecurityFixes:["DELETE /repos/{owner}/{repo}/automated-security-fixes"],disableDeploymentProtectionRule:["DELETE /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules/{protection_rule_id}"],disablePrivateVulnerabilityReporting:["DELETE /repos/{owner}/{repo}/private-vulnerability-reporting"],disableVulnerabilityAlerts:["DELETE /repos/{owner}/{repo}/vulnerability-alerts"],downloadArchive:["GET /repos/{owner}/{repo}/zipball/{ref}",{},{renamed:["repos","downloadZipballArchive"]}],downloadTarballArchive:["GET /repos/{owner}/{repo}/tarball/{ref}"],downloadZipballArchive:["GET /repos/{owner}/{repo}/zipball/{ref}"],enableAutomatedSecurityFixes:["PUT /repos/{owner}/{repo}/automated-security-fixes"],enablePrivateVulnerabilityReporting:["PUT /repos/{owner}/{repo}/private-vulnerability-reporting"],enableVulnerabilityAlerts:["PUT /repos/{owner}/{repo}/vulnerability-alerts"],generateReleaseNotes:["POST /repos/{owner}/{repo}/releases/generate-notes"],get:["GET /repos/{owner}/{repo}"],getAccessRestrictions:["GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions"],getAdminBranchProtection:["GET /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins"],getAllDeploymentProtectionRules:["GET /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules"],getAllEnvironments:["GET /repos/{owner}/{repo}/environments"],getAllStatusCheckContexts:["GET /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts"],getAllTopics:["GET /repos/{owner}/{repo}/topics"],getAppsWithAccessToProtectedBranch:["GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps"],getAutolink:["GET /repos/{owner}/{repo}/autolinks/{autolink_id}"],getBranch:["GET /repos/{owner}/{repo}/branches/{branch}"],getBranchProtection:["GET /repos/{owner}/{repo}/branches/{branch}/protection"],getBranchRules:["GET /repos/{owner}/{repo}/rules/branches/{branch}"],getClones:["GET /repos/{owner}/{repo}/traffic/clones"],getCodeFrequencyStats:["GET /repos/{owner}/{repo}/stats/code_frequency"],getCollaboratorPermissionLevel:["GET /repos/{owner}/{repo}/collaborators/{username}/permission"],getCombinedStatusForRef:["GET /repos/{owner}/{repo}/commits/{ref}/status"],getCommit:["GET /repos/{owner}/{repo}/commits/{ref}"],getCommitActivityStats:["GET /repos/{owner}/{repo}/stats/commit_activity"],getCommitComment:["GET /repos/{owner}/{repo}/comments/{comment_id}"],getCommitSignatureProtection:["GET /repos/{owner}/{repo}/branches/{branch}/protection/required_signatures"],getCommunityProfileMetrics:["GET /repos/{owner}/{repo}/community/profile"],getContent:["GET /repos/{owner}/{repo}/contents/{path}"],getContributorsStats:["GET /repos/{owner}/{repo}/stats/contributors"],getCustomDeploymentProtectionRule:["GET /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules/{protection_rule_id}"],getCustomPropertiesValues:["GET /repos/{owner}/{repo}/properties/values"],getDeployKey:["GET /repos/{owner}/{repo}/keys/{key_id}"],getDeployment:["GET /repos/{owner}/{repo}/deployments/{deployment_id}"],getDeploymentBranchPolicy:["GET /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies/{branch_policy_id}"],getDeploymentStatus:["GET /repos/{owner}/{repo}/deployments/{deployment_id}/statuses/{status_id}"],getEnvironment:["GET /repos/{owner}/{repo}/environments/{environment_name}"],getLatestPagesBuild:["GET /repos/{owner}/{repo}/pages/builds/latest"],getLatestRelease:["GET /repos/{owner}/{repo}/releases/latest"],getOrgRuleSuite:["GET /orgs/{org}/rulesets/rule-suites/{rule_suite_id}"],getOrgRuleSuites:["GET /orgs/{org}/rulesets/rule-suites"],getOrgRuleset:["GET /orgs/{org}/rulesets/{ruleset_id}"],getOrgRulesets:["GET /orgs/{org}/rulesets"],getPages:["GET /repos/{owner}/{repo}/pages"],getPagesBuild:["GET /repos/{owner}/{repo}/pages/builds/{build_id}"],getPagesDeployment:["GET /repos/{owner}/{repo}/pages/deployments/{pages_deployment_id}"],getPagesHealthCheck:["GET /repos/{owner}/{repo}/pages/health"],getParticipationStats:["GET /repos/{owner}/{repo}/stats/participation"],getPullRequestReviewProtection:["GET /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews"],getPunchCardStats:["GET /repos/{owner}/{repo}/stats/punch_card"],getReadme:["GET /repos/{owner}/{repo}/readme"],getReadmeInDirectory:["GET /repos/{owner}/{repo}/readme/{dir}"],getRelease:["GET /repos/{owner}/{repo}/releases/{release_id}"],getReleaseAsset:["GET /repos/{owner}/{repo}/releases/assets/{asset_id}"],getReleaseByTag:["GET /repos/{owner}/{repo}/releases/tags/{tag}"],getRepoRuleSuite:["GET /repos/{owner}/{repo}/rulesets/rule-suites/{rule_suite_id}"],getRepoRuleSuites:["GET /repos/{owner}/{repo}/rulesets/rule-suites"],getRepoRuleset:["GET /repos/{owner}/{repo}/rulesets/{ruleset_id}"],getRepoRulesets:["GET /repos/{owner}/{repo}/rulesets"],getStatusChecksProtection:["GET /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks"],getTeamsWithAccessToProtectedBranch:["GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams"],getTopPaths:["GET /repos/{owner}/{repo}/traffic/popular/paths"],getTopReferrers:["GET /repos/{owner}/{repo}/traffic/popular/referrers"],getUsersWithAccessToProtectedBranch:["GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users"],getViews:["GET /repos/{owner}/{repo}/traffic/views"],getWebhook:["GET /repos/{owner}/{repo}/hooks/{hook_id}"],getWebhookConfigForRepo:["GET /repos/{owner}/{repo}/hooks/{hook_id}/config"],getWebhookDelivery:["GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries/{delivery_id}"],listActivities:["GET /repos/{owner}/{repo}/activity"],listAutolinks:["GET /repos/{owner}/{repo}/autolinks"],listBranches:["GET /repos/{owner}/{repo}/branches"],listBranchesForHeadCommit:["GET /repos/{owner}/{repo}/commits/{commit_sha}/branches-where-head"],listCollaborators:["GET /repos/{owner}/{repo}/collaborators"],listCommentsForCommit:["GET /repos/{owner}/{repo}/commits/{commit_sha}/comments"],listCommitCommentsForRepo:["GET /repos/{owner}/{repo}/comments"],listCommitStatusesForRef:["GET /repos/{owner}/{repo}/commits/{ref}/statuses"],listCommits:["GET /repos/{owner}/{repo}/commits"],listContributors:["GET /repos/{owner}/{repo}/contributors"],listCustomDeploymentRuleIntegrations:["GET /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules/apps"],listDeployKeys:["GET /repos/{owner}/{repo}/keys"],listDeploymentBranchPolicies:["GET /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies"],listDeploymentStatuses:["GET /repos/{owner}/{repo}/deployments/{deployment_id}/statuses"],listDeployments:["GET /repos/{owner}/{repo}/deployments"],listForAuthenticatedUser:["GET /user/repos"],listForOrg:["GET /orgs/{org}/repos"],listForUser:["GET /users/{username}/repos"],listForks:["GET /repos/{owner}/{repo}/forks"],listInvitations:["GET /repos/{owner}/{repo}/invitations"],listInvitationsForAuthenticatedUser:["GET /user/repository_invitations"],listLanguages:["GET /repos/{owner}/{repo}/languages"],listPagesBuilds:["GET /repos/{owner}/{repo}/pages/builds"],listPublic:["GET /repositories"],listPullRequestsAssociatedWithCommit:["GET /repos/{owner}/{repo}/commits/{commit_sha}/pulls"],listReleaseAssets:["GET /repos/{owner}/{repo}/releases/{release_id}/assets"],listReleases:["GET /repos/{owner}/{repo}/releases"],listTagProtection:["GET /repos/{owner}/{repo}/tags/protection"],listTags:["GET /repos/{owner}/{repo}/tags"],listTeams:["GET /repos/{owner}/{repo}/teams"],listWebhookDeliveries:["GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries"],listWebhooks:["GET /repos/{owner}/{repo}/hooks"],merge:["POST /repos/{owner}/{repo}/merges"],mergeUpstream:["POST /repos/{owner}/{repo}/merge-upstream"],pingWebhook:["POST /repos/{owner}/{repo}/hooks/{hook_id}/pings"],redeliverWebhookDelivery:["POST /repos/{owner}/{repo}/hooks/{hook_id}/deliveries/{delivery_id}/attempts"],removeAppAccessRestrictions:["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps",{},{mapToData:"apps"}],removeCollaborator:["DELETE /repos/{owner}/{repo}/collaborators/{username}"],removeStatusCheckContexts:["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts",{},{mapToData:"contexts"}],removeStatusCheckProtection:["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks"],removeTeamAccessRestrictions:["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams",{},{mapToData:"teams"}],removeUserAccessRestrictions:["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users",{},{mapToData:"users"}],renameBranch:["POST /repos/{owner}/{repo}/branches/{branch}/rename"],replaceAllTopics:["PUT /repos/{owner}/{repo}/topics"],requestPagesBuild:["POST /repos/{owner}/{repo}/pages/builds"],setAdminBranchProtection:["POST /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins"],setAppAccessRestrictions:["PUT /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps",{},{mapToData:"apps"}],setStatusCheckContexts:["PUT /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts",{},{mapToData:"contexts"}],setTeamAccessRestrictions:["PUT /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams",{},{mapToData:"teams"}],setUserAccessRestrictions:["PUT /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users",{},{mapToData:"users"}],testPushWebhook:["POST /repos/{owner}/{repo}/hooks/{hook_id}/tests"],transfer:["POST /repos/{owner}/{repo}/transfer"],update:["PATCH /repos/{owner}/{repo}"],updateBranchProtection:["PUT /repos/{owner}/{repo}/branches/{branch}/protection"],updateCommitComment:["PATCH /repos/{owner}/{repo}/comments/{comment_id}"],updateDeploymentBranchPolicy:["PUT /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies/{branch_policy_id}"],updateInformationAboutPagesSite:["PUT /repos/{owner}/{repo}/pages"],updateInvitation:["PATCH /repos/{owner}/{repo}/invitations/{invitation_id}"],updateOrgRuleset:["PUT /orgs/{org}/rulesets/{ruleset_id}"],updatePullRequestReviewProtection:["PATCH /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews"],updateRelease:["PATCH /repos/{owner}/{repo}/releases/{release_id}"],updateReleaseAsset:["PATCH /repos/{owner}/{repo}/releases/assets/{asset_id}"],updateRepoRuleset:["PUT /repos/{owner}/{repo}/rulesets/{ruleset_id}"],updateStatusCheckPotection:["PATCH /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks",{},{renamed:["repos","updateStatusCheckProtection"]}],updateStatusCheckProtection:["PATCH /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks"],updateWebhook:["PATCH /repos/{owner}/{repo}/hooks/{hook_id}"],updateWebhookConfigForRepo:["PATCH /repos/{owner}/{repo}/hooks/{hook_id}/config"],uploadReleaseAsset:["POST /repos/{owner}/{repo}/releases/{release_id}/assets{?name,label}",{baseUrl:"https://uploads.github.com"}]},search:{code:["GET /search/code"],commits:["GET /search/commits"],issuesAndPullRequests:["GET /search/issues"],labels:["GET /search/labels"],repos:["GET /search/repositories"],topics:["GET /search/topics"],users:["GET /search/users"]},secretScanning:{getAlert:["GET /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number}"],listAlertsForEnterprise:["GET /enterprises/{enterprise}/secret-scanning/alerts"],listAlertsForOrg:["GET /orgs/{org}/secret-scanning/alerts"],listAlertsForRepo:["GET /repos/{owner}/{repo}/secret-scanning/alerts"],listLocationsForAlert:["GET /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number}/locations"],updateAlert:["PATCH /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number}"]},securityAdvisories:{createFork:["POST /repos/{owner}/{repo}/security-advisories/{ghsa_id}/forks"],createPrivateVulnerabilityReport:["POST /repos/{owner}/{repo}/security-advisories/reports"],createRepositoryAdvisory:["POST /repos/{owner}/{repo}/security-advisories"],createRepositoryAdvisoryCveRequest:["POST /repos/{owner}/{repo}/security-advisories/{ghsa_id}/cve"],getGlobalAdvisory:["GET /advisories/{ghsa_id}"],getRepositoryAdvisory:["GET /repos/{owner}/{repo}/security-advisories/{ghsa_id}"],listGlobalAdvisories:["GET /advisories"],listOrgRepositoryAdvisories:["GET /orgs/{org}/security-advisories"],listRepositoryAdvisories:["GET /repos/{owner}/{repo}/security-advisories"],updateRepositoryAdvisory:["PATCH /repos/{owner}/{repo}/security-advisories/{ghsa_id}"]},teams:{addOrUpdateMembershipForUserInOrg:["PUT /orgs/{org}/teams/{team_slug}/memberships/{username}"],addOrUpdateProjectPermissionsInOrg:["PUT /orgs/{org}/teams/{team_slug}/projects/{project_id}"],addOrUpdateRepoPermissionsInOrg:["PUT /orgs/{org}/teams/{team_slug}/repos/{owner}/{repo}"],checkPermissionsForProjectInOrg:["GET /orgs/{org}/teams/{team_slug}/projects/{project_id}"],checkPermissionsForRepoInOrg:["GET /orgs/{org}/teams/{team_slug}/repos/{owner}/{repo}"],create:["POST /orgs/{org}/teams"],createDiscussionCommentInOrg:["POST /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments"],createDiscussionInOrg:["POST /orgs/{org}/teams/{team_slug}/discussions"],deleteDiscussionCommentInOrg:["DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}"],deleteDiscussionInOrg:["DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}"],deleteInOrg:["DELETE /orgs/{org}/teams/{team_slug}"],getByName:["GET /orgs/{org}/teams/{team_slug}"],getDiscussionCommentInOrg:["GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}"],getDiscussionInOrg:["GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}"],getMembershipForUserInOrg:["GET /orgs/{org}/teams/{team_slug}/memberships/{username}"],list:["GET /orgs/{org}/teams"],listChildInOrg:["GET /orgs/{org}/teams/{team_slug}/teams"],listDiscussionCommentsInOrg:["GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments"],listDiscussionsInOrg:["GET /orgs/{org}/teams/{team_slug}/discussions"],listForAuthenticatedUser:["GET /user/teams"],listMembersInOrg:["GET /orgs/{org}/teams/{team_slug}/members"],listPendingInvitationsInOrg:["GET /orgs/{org}/teams/{team_slug}/invitations"],listProjectsInOrg:["GET /orgs/{org}/teams/{team_slug}/projects"],listReposInOrg:["GET /orgs/{org}/teams/{team_slug}/repos"],removeMembershipForUserInOrg:["DELETE /orgs/{org}/teams/{team_slug}/memberships/{username}"],removeProjectInOrg:["DELETE /orgs/{org}/teams/{team_slug}/projects/{project_id}"],removeRepoInOrg:["DELETE /orgs/{org}/teams/{team_slug}/repos/{owner}/{repo}"],updateDiscussionCommentInOrg:["PATCH /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}"],updateDiscussionInOrg:["PATCH /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}"],updateInOrg:["PATCH /orgs/{org}/teams/{team_slug}"]},users:{addEmailForAuthenticated:["POST /user/emails",{},{renamed:["users","addEmailForAuthenticatedUser"]}],addEmailForAuthenticatedUser:["POST /user/emails"],addSocialAccountForAuthenticatedUser:["POST /user/social_accounts"],block:["PUT /user/blocks/{username}"],checkBlocked:["GET /user/blocks/{username}"],checkFollowingForUser:["GET /users/{username}/following/{target_user}"],checkPersonIsFollowedByAuthenticated:["GET /user/following/{username}"],createGpgKeyForAuthenticated:["POST /user/gpg_keys",{},{renamed:["users","createGpgKeyForAuthenticatedUser"]}],createGpgKeyForAuthenticatedUser:["POST /user/gpg_keys"],createPublicSshKeyForAuthenticated:["POST /user/keys",{},{renamed:["users","createPublicSshKeyForAuthenticatedUser"]}],createPublicSshKeyForAuthenticatedUser:["POST /user/keys"],createSshSigningKeyForAuthenticatedUser:["POST /user/ssh_signing_keys"],deleteEmailForAuthenticated:["DELETE /user/emails",{},{renamed:["users","deleteEmailForAuthenticatedUser"]}],deleteEmailForAuthenticatedUser:["DELETE /user/emails"],deleteGpgKeyForAuthenticated:["DELETE /user/gpg_keys/{gpg_key_id}",{},{renamed:["users","deleteGpgKeyForAuthenticatedUser"]}],deleteGpgKeyForAuthenticatedUser:["DELETE /user/gpg_keys/{gpg_key_id}"],deletePublicSshKeyForAuthenticated:["DELETE /user/keys/{key_id}",{},{renamed:["users","deletePublicSshKeyForAuthenticatedUser"]}],deletePublicSshKeyForAuthenticatedUser:["DELETE /user/keys/{key_id}"],deleteSocialAccountForAuthenticatedUser:["DELETE /user/social_accounts"],deleteSshSigningKeyForAuthenticatedUser:["DELETE /user/ssh_signing_keys/{ssh_signing_key_id}"],follow:["PUT /user/following/{username}"],getAuthenticated:["GET /user"],getByUsername:["GET /users/{username}"],getContextForUser:["GET /users/{username}/hovercard"],getGpgKeyForAuthenticated:["GET /user/gpg_keys/{gpg_key_id}",{},{renamed:["users","getGpgKeyForAuthenticatedUser"]}],getGpgKeyForAuthenticatedUser:["GET /user/gpg_keys/{gpg_key_id}"],getPublicSshKeyForAuthenticated:["GET /user/keys/{key_id}",{},{renamed:["users","getPublicSshKeyForAuthenticatedUser"]}],getPublicSshKeyForAuthenticatedUser:["GET /user/keys/{key_id}"],getSshSigningKeyForAuthenticatedUser:["GET /user/ssh_signing_keys/{ssh_signing_key_id}"],list:["GET /users"],listBlockedByAuthenticated:["GET /user/blocks",{},{renamed:["users","listBlockedByAuthenticatedUser"]}],listBlockedByAuthenticatedUser:["GET /user/blocks"],listEmailsForAuthenticated:["GET /user/emails",{},{renamed:["users","listEmailsForAuthenticatedUser"]}],listEmailsForAuthenticatedUser:["GET /user/emails"],listFollowedByAuthenticated:["GET /user/following",{},{renamed:["users","listFollowedByAuthenticatedUser"]}],listFollowedByAuthenticatedUser:["GET /user/following"],listFollowersForAuthenticatedUser:["GET /user/followers"],listFollowersForUser:["GET /users/{username}/followers"],listFollowingForUser:["GET /users/{username}/following"],listGpgKeysForAuthenticated:["GET /user/gpg_keys",{},{renamed:["users","listGpgKeysForAuthenticatedUser"]}],listGpgKeysForAuthenticatedUser:["GET /user/gpg_keys"],listGpgKeysForUser:["GET /users/{username}/gpg_keys"],listPublicEmailsForAuthenticated:["GET /user/public_emails",{},{renamed:["users","listPublicEmailsForAuthenticatedUser"]}],listPublicEmailsForAuthenticatedUser:["GET /user/public_emails"],listPublicKeysForUser:["GET /users/{username}/keys"],listPublicSshKeysForAuthenticated:["GET /user/keys",{},{renamed:["users","listPublicSshKeysForAuthenticatedUser"]}],listPublicSshKeysForAuthenticatedUser:["GET /user/keys"],listSocialAccountsForAuthenticatedUser:["GET /user/social_accounts"],listSocialAccountsForUser:["GET /users/{username}/social_accounts"],listSshSigningKeysForAuthenticatedUser:["GET /user/ssh_signing_keys"],listSshSigningKeysForUser:["GET /users/{username}/ssh_signing_keys"],setPrimaryEmailVisibilityForAuthenticated:["PATCH /user/email/visibility",{},{renamed:["users","setPrimaryEmailVisibilityForAuthenticatedUser"]}],setPrimaryEmailVisibilityForAuthenticatedUser:["PATCH /user/email/visibility"],unblock:["DELETE /user/blocks/{username}"],unfollow:["DELETE /user/following/{username}"],updateAuthenticated:["PATCH /user"]}}))for(const[i,s]of Object.entries(t)){const[t,r,n]=s,[o,a]=t.split(/ /),c=Object.assign({method:o,url:a},r);C.has(e)||C.set(e,new Map),C.get(e).set(i,{scope:e,methodName:i,endpointDefaults:c,decorations:n})}const y={has:({scope:e},t)=>C.get(e).has(t),getOwnPropertyDescriptor(e,t){return{value:this.get(e,t),configurable:!0,writable:!0,enumerable:!0}},defineProperty:(e,t,i)=>(Object.defineProperty(e.cache,t,i),!0),deleteProperty:(e,t)=>(delete e.cache[t],!0),ownKeys:({scope:e})=>[...C.get(e).keys()],set:(e,t,i)=>e.cache[t]=i,get({octokit:e,scope:t,cache:i},s){if(i[s])return i[s];const r=C.get(t).get(s);if(!r)return;const{endpointDefaults:n,decorations:o}=r;return i[s]=o?function(e,t,i,s,r){const n=e.request.defaults(s);return Object.assign((function(...s){let o=n.endpoint.merge(...s);if(r.mapToData)return o=Object.assign({},o,{data:o[r.mapToData],[r.mapToData]:void 0}),n(o);if(r.renamed){const[s,n]=r.renamed;e.log.warn(`octokit.${t}.${i}() has been renamed to octokit.${s}.${n}()`)}if(r.deprecated&&e.log.warn(r.deprecated),r.renamedParameters){const o=n.endpoint.merge(...s);for(const[s,n]of Object.entries(r.renamedParameters))s in o&&(e.log.warn(`"${s}" parameter is deprecated for "octokit.${t}.${i}()". Use "${n}" instead`),n in o||(o[n]=o[s]),delete o[s]);return n(o)}return n(...s)}),n)}(e,t,s,n,o):e.request.defaults(n),i[s]}};function v(e){const t={};for(const i of C.keys())t[i]=new Proxy({octokit:e,scope:i,cache:{}},y);return t}function I(e){return{rest:v(e)}}async function B(e,t,i,s){if(!i.request||!i.request.request)throw i;if(i.status>=400&&!e.doNotRetry.includes(i.status)){const r=null!=s.request.retries?s.request.retries:e.retries,n=Math.pow((s.request.retryCount||0)+1,2);throw t.retry.retryRequest(i,r,n)}throw i}I.VERSION="10.4.1";var w=i(32722),b=i.n(w),Q=i(98838);async function x(e,t,i,s){const r=new(b());return r.on("failed",(function(t,i){const r=~~t.request.request.retries,n=~~t.request.request.retryAfter;if(s.request.retryCount=i.retryCount+1,r>i.retryCount)return n*e.retryAfterBaseValue})),r.schedule(k.bind(null,e,t,i),s)}async function k(e,t,i,s){const r=await i(i,s);return r.data&&r.data.errors&&/Something went wrong while executing your query/.test(r.data.errors[0].message)?B(e,t,new Q.RequestError(r.data.errors[0].message,500,{request:s,response:r}),s):r}function D(e,t){const i=Object.assign({enabled:!0,retryAfterBaseValue:1e3,doNotRetry:[400,401,403,404,422,451],retries:3},t.retry);return i.enabled&&(e.hook.error("request",B.bind(null,i,e)),e.hook.wrap("request",x.bind(null,i,e))),{retry:{retryRequest:(e,t,i)=>(e.request.request=Object.assign({},e.request.request,{retries:t,retryAfter:i}),e)}}}D.VERSION="0.0.0-development";const S=()=>Promise.resolve();function _(e,t,i){return e.retryLimiter.schedule(R,e,t,i)}async function R(e,t,i){const s="GET"!==i.method&&"HEAD"!==i.method,{pathname:r}=new URL(i.url,"http://github.test"),n="GET"===i.method&&r.startsWith("/search/"),o=r.startsWith("/graphql"),a=~~t.retryCount>0?{priority:0,weight:0}:{};e.clustering&&(a.expiration=6e4),(s||o)&&await e.write.key(e.id).schedule(a,S),s&&e.triggersNotification(r)&&await e.notifications.key(e.id).schedule(a,S),n&&await e.search.key(e.id).schedule(a,S);const c=e.global.key(e.id).schedule(a,t,i);if(o){const e=await c;if(null!=e.data.errors&&e.data.errors.some((e=>"RATE_LIMITED"===e.type)))throw Object.assign(new Error("GraphQL Rate Limit Exceeded"),{response:e,data:e.data})}return c}const T=function(e){const t=`^(?:${["/orgs/{org}/invitations","/orgs/{org}/invitations/{invitation_id}","/orgs/{org}/teams/{team_slug}/discussions","/orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments","/repos/{owner}/{repo}/collaborators/{username}","/repos/{owner}/{repo}/commits/{commit_sha}/comments","/repos/{owner}/{repo}/issues","/repos/{owner}/{repo}/issues/{issue_number}/comments","/repos/{owner}/{repo}/pulls","/repos/{owner}/{repo}/pulls/{pull_number}/comments","/repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies","/repos/{owner}/{repo}/pulls/{pull_number}/merge","/repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers","/repos/{owner}/{repo}/pulls/{pull_number}/reviews","/repos/{owner}/{repo}/releases","/teams/{team_id}/discussions","/teams/{team_id}/discussions/{discussion_number}/comments"].map((e=>e.split("/").map((e=>e.startsWith("{")?"(?:.+?)":e)).join("/"))).map((e=>`(?:${e})`)).join("|")})[^/]*$`;return new RegExp(t,"i")}(),F=T.test.bind(T),N={};function L(e,t){const{enabled:i=!0,Bottleneck:s=b(),id:r="no-id",timeout:n=12e4,connection:o}=t.throttle||{};if(!i)return{};const a={connection:o,timeout:n};null==N.global&&function(e,t){N.global=new e.Group({id:"octokit-global",maxConcurrent:10,...t}),N.search=new e.Group({id:"octokit-search",maxConcurrent:1,minTime:2e3,...t}),N.write=new e.Group({id:"octokit-write",maxConcurrent:1,minTime:1e3,...t}),N.notifications=new e.Group({id:"octokit-notifications",maxConcurrent:1,minTime:3e3,...t})}(s,a);const c=Object.assign({clustering:null!=o,triggersNotification:F,fallbackSecondaryRateRetryAfter:60,retryAfterBaseValue:1e3,retryLimiter:new s,id:r,...N},t.throttle);if("function"!=typeof c.onSecondaryRateLimit||"function"!=typeof c.onRateLimit)throw new Error("octokit/plugin-throttling error:\n You must pass the onSecondaryRateLimit and onRateLimit error handlers.\n See https://octokit.github.io/rest.js/#throttling\n\n const octokit = new Octokit({\n throttle: {\n onSecondaryRateLimit: (retryAfter, options) => {/* ... */},\n onRateLimit: (retryAfter, options) => {/* ... */}\n }\n })\n ");const l={},p=new s.Events(l);return l.on("secondary-limit",c.onSecondaryRateLimit),l.on("rate-limit",c.onRateLimit),l.on("error",(t=>e.log.warn("Error in throttling-plugin limit handler",t))),c.retryLimiter.on("failed",(async function(t,i){const[s,r,n]=i.args,{pathname:o}=new URL(n.url,"http://github.test");if((!o.startsWith("/graphql")||401===t.status)&&403!==t.status)return;const a=~~r.retryCount;r.retryCount=a,n.request.retryCount=a;const{wantRetry:c,retryAfter:l=0}=await async function(){if(/\bsecondary rate\b/i.test(t.message)){const i=Number(t.response.headers["retry-after"])||s.fallbackSecondaryRateRetryAfter;return{wantRetry:await p.trigger("secondary-limit",i,n,e,a),retryAfter:i}}if(null!=t.response.headers&&"0"===t.response.headers["x-ratelimit-remaining"]||(t.response.data?.errors??[]).some((e=>"RATE_LIMITED"===e.type))){const i=new Date(1e3*~~t.response.headers["x-ratelimit-reset"]).getTime(),s=Math.max(Math.ceil((i-Date.now())/1e3)+1,0);return{wantRetry:await p.trigger("rate-limit",s,n,e,a),retryAfter:s}}return{}}();return c?(r.retryCount++,l*s.retryAfterBaseValue):void 0})),e.hook.wrap("request",_.bind(null,c)),{}}L.VERSION="8.2.0",L.triggersNotification=F;var O=i(61786),M=i(32546),U=i(37573);const P=e=>({debug:()=>{},info:()=>{},warn:console.warn.bind(console),error:console.error.bind(console),...e}),G=["branch_protection_configuration","branch_protection_rule.disabled","branch_protection_rule.enabled","branch_protection_rule","branch_protection_rule.created","branch_protection_rule.deleted","branch_protection_rule.edited","check_run","check_run.completed","check_run.created","check_run.requested_action","check_run.rerequested","check_suite","check_suite.completed","check_suite.requested","check_suite.rerequested","code_scanning_alert","code_scanning_alert.appeared_in_branch","code_scanning_alert.closed_by_user","code_scanning_alert.created","code_scanning_alert.fixed","code_scanning_alert.reopened","code_scanning_alert.reopened_by_user","commit_comment","commit_comment.created","create","custom_property","custom_property.created","custom_property.deleted","custom_property_values","custom_property_values.updated","delete","dependabot_alert","dependabot_alert.created","dependabot_alert.dismissed","dependabot_alert.fixed","dependabot_alert.reintroduced","dependabot_alert.reopened","deploy_key","deploy_key.created","deploy_key.deleted","deployment","deployment.created","deployment_protection_rule","deployment_protection_rule.requested","deployment_review","deployment_review.approved","deployment_review.rejected","deployment_review.requested","deployment_status","deployment_status.created","discussion","discussion.answered","discussion.category_changed","discussion.created","discussion.deleted","discussion.edited","discussion.labeled","discussion.locked","discussion.pinned","discussion.transferred","discussion.unanswered","discussion.unlabeled","discussion.unlocked","discussion.unpinned","discussion_comment","discussion_comment.created","discussion_comment.deleted","discussion_comment.edited","fork","github_app_authorization","github_app_authorization.revoked","gollum","installation","installation.created","installation.deleted","installation.new_permissions_accepted","installation.suspend","installation.unsuspend","installation_repositories","installation_repositories.added","installation_repositories.removed","installation_target","installation_target.renamed","issue_comment","issue_comment.created","issue_comment.deleted","issue_comment.edited","issues","issues.assigned","issues.closed","issues.deleted","issues.demilestoned","issues.edited","issues.labeled","issues.locked","issues.milestoned","issues.opened","issues.pinned","issues.reopened","issues.transferred","issues.unassigned","issues.unlabeled","issues.unlocked","issues.unpinned","label","label.created","label.deleted","label.edited","marketplace_purchase","marketplace_purchase.cancelled","marketplace_purchase.changed","marketplace_purchase.pending_change","marketplace_purchase.pending_change_cancelled","marketplace_purchase.purchased","member","member.added","member.edited","member.removed","membership","membership.added","membership.removed","merge_group","merge_group.checks_requested","meta","meta.deleted","milestone","milestone.closed","milestone.created","milestone.deleted","milestone.edited","milestone.opened","org_block","org_block.blocked","org_block.unblocked","organization","organization.deleted","organization.member_added","organization.member_invited","organization.member_removed","organization.renamed","package","package.published","package.updated","page_build","ping","project","project.closed","project.created","project.deleted","project.edited","project.reopened","project_card","project_card.converted","project_card.created","project_card.deleted","project_card.edited","project_card.moved","project_column","project_column.created","project_column.deleted","project_column.edited","project_column.moved","projects_v2_item","projects_v2_item.archived","projects_v2_item.converted","projects_v2_item.created","projects_v2_item.deleted","projects_v2_item.edited","projects_v2_item.reordered","projects_v2_item.restored","public","pull_request","pull_request.assigned","pull_request.auto_merge_disabled","pull_request.auto_merge_enabled","pull_request.closed","pull_request.converted_to_draft","pull_request.demilestoned","pull_request.dequeued","pull_request.edited","pull_request.enqueued","pull_request.labeled","pull_request.locked","pull_request.milestoned","pull_request.opened","pull_request.ready_for_review","pull_request.reopened","pull_request.review_request_removed","pull_request.review_requested","pull_request.synchronize","pull_request.unassigned","pull_request.unlabeled","pull_request.unlocked","pull_request_review","pull_request_review.dismissed","pull_request_review.edited","pull_request_review.submitted","pull_request_review_comment","pull_request_review_comment.created","pull_request_review_comment.deleted","pull_request_review_comment.edited","pull_request_review_thread","pull_request_review_thread.resolved","pull_request_review_thread.unresolved","push","registry_package","registry_package.published","registry_package.updated","release","release.created","release.deleted","release.edited","release.prereleased","release.published","release.released","release.unpublished","repository","repository.archived","repository.created","repository.deleted","repository.edited","repository.privatized","repository.publicized","repository.renamed","repository.transferred","repository.unarchived","repository_dispatch","repository_import","repository_vulnerability_alert","repository_vulnerability_alert.create","repository_vulnerability_alert.dismiss","repository_vulnerability_alert.reopen","repository_vulnerability_alert.resolve","secret_scanning_alert","secret_scanning_alert.created","secret_scanning_alert.reopened","secret_scanning_alert.resolved","secret_scanning_alert.revoked","secret_scanning_alert_location","secret_scanning_alert_location.created","security_advisory","security_advisory.performed","security_advisory.published","security_advisory.updated","security_advisory.withdrawn","sponsorship","sponsorship.cancelled","sponsorship.created","sponsorship.edited","sponsorship.pending_cancellation","sponsorship.pending_tier_change","sponsorship.tier_changed","star","star.created","star.deleted","status","team","team.added_to_repository","team.created","team.deleted","team.edited","team.removed_from_repository","team_add","watch","watch.started","workflow_dispatch","workflow_job","workflow_job.completed","workflow_job.in_progress","workflow_job.queued","workflow_job.waiting","workflow_run","workflow_run.completed","workflow_run.in_progress","workflow_run.requested"];function V(e,t,i){e.hooks[t]||(e.hooks[t]=[]),e.hooks[t].push(i)}function j(e,t,i){if(Array.isArray(t))t.forEach((t=>j(e,t,i)));else{if(["*","error"].includes(t)){const e="*"===t?"any":t,i=`Using the "${t}" event with the regular Webhooks.on() function is not supported. Please use the Webhooks.on${e.charAt(0).toUpperCase()+e.slice(1)}() method instead`;throw new Error(i)}G.includes(t)||e.log.warn(`"${t}" is not a known webhook name (https://developer.github.com/v3/activity/events/types/)`),V(e,t,i)}}function H(e,t){V(e,"*",t)}function J(e,t){V(e,"error",t)}var q=i(14686),Y=i.n(q);function W(e,t){let i;try{i=e(t)}catch(e){console.log('FATAL: Error occurred in "error" event handler'),console.log(e)}i&&i.catch&&i.catch((e=>{console.log('FATAL: Error occurred in "error" event handler'),console.log(e)}))}function z(e,t){const i=e.hooks.error||[];if(t instanceof Error){const e=Object.assign(new(Y())([t]),{event:t,errors:[t]});return i.forEach((t=>W(t,e))),Promise.reject(e)}if(!t||!t.name)throw new(Y())(["Event name not passed"]);if(!t.payload)throw new(Y())(["Event payload not passed"]);const s=function(e,t,i){const s=[e.hooks[i],e.hooks["*"]];return t&&s.unshift(e.hooks[`${i}.${t}`]),[].concat(...s.filter(Boolean))}(e,"action"in t.payload?t.payload.action:null,t.name);if(0===s.length)return Promise.resolve();const r=[],n=s.map((i=>{let s=Promise.resolve(t);return e.transform&&(s=s.then(e.transform)),s.then((e=>i(e))).catch((e=>r.push(Object.assign(e,{event:t}))))}));return Promise.all(n).then((()=>{if(0===r.length)return;const e=new(Y())(r);throw Object.assign(e,{event:t,errors:r}),i.forEach((t=>W(t,e))),e}))}function $(e,t,i){if(Array.isArray(t))t.forEach((t=>$(e,t,i)));else if(e.hooks[t])for(let s=e.hooks[t].length-1;s>=0;s--)if(e.hooks[t][s]===i)return void e.hooks[t].splice(s,1)}function X(e){const t={hooks:{},log:P(e&&e.log)};return e&&e.transform&&(t.transform=e.transform),{on:j.bind(null,t),onAny:H.bind(null,t),onError:J.bind(null,t),removeListener:$.bind(null,t),receive:z.bind(null,t)}}const K=require("node:crypto");var Z=(e=>(e.SHA1="sha1",e.SHA256="sha256",e))(Z||{});const ee="4.1.0";async function te(e,t){const{secret:i,algorithm:s}="object"==typeof e?{secret:e.secret,algorithm:e.algorithm||Z.SHA256}:{secret:e,algorithm:Z.SHA256};if(!i||!t)throw new TypeError("[@octokit/webhooks-methods] secret & payload required for sign()");if("string"!=typeof t)throw new TypeError("[@octokit/webhooks-methods] payload must be a string");if(!Object.values(Z).includes(s))throw new TypeError(`[@octokit/webhooks] Algorithm ${s} is not supported. Must be 'sha1' or 'sha256'`);return`${s}=${(0,K.createHmac)(s,i).update(t).digest("hex")}`}te.VERSION=ee;const ie=require("node:buffer"),se=e=>e.startsWith("sha256=")?"sha256":"sha1";async function re(e,t,i){if(!e||!t||!i)throw new TypeError("[@octokit/webhooks-methods] secret, eventPayload & signature required");if("string"!=typeof t)throw new TypeError("[@octokit/webhooks-methods] eventPayload must be a string");const s=ie.Buffer.from(i),r=se(i),n=ie.Buffer.from(await te({secret:e,algorithm:r},t));return s.length===n.length&&(0,K.timingSafeEqual)(s,n)}async function ne(e,t){if(!await re(e.secret,t.payload,t.signature).catch((()=>!1))){const i=new Error("[@octokit/webhooks] signature does not match event payload and secret");return e.eventHandler.receive(Object.assign(i,{event:t,status:400}))}let i;try{i=JSON.parse(t.payload)}catch(e){throw e.message="Invalid JSON",e.status=400,new(Y())([e])}return e.eventHandler.receive({id:t.id,name:t.name,payload:i})}re.VERSION=ee;class oe{constructor(e){if(!e||!e.secret)throw new Error("[@octokit/webhooks] options.secret required");const t={eventHandler:X(e),secret:e.secret,hooks:{},log:P(e.log)};this.sign=te.bind(null,e.secret),this.verify=re.bind(null,e.secret),this.on=t.eventHandler.on,this.onAny=t.eventHandler.onAny,this.onError=t.eventHandler.onError,this.removeListener=t.eventHandler.removeListener,this.receive=t.eventHandler.receive,this.verifyAndReceive=ne.bind(null,t)}}const ae=["x-github-event","x-hub-signature-256","x-github-delivery"];async function ce(e,t,i,s,r){let n;try{n=new URL(i.url,"http://localhost").pathname}catch(e){return s.writeHead(422,{"content-type":"application/json"}),s.end(JSON.stringify({error:`Request URL could not be parsed: ${i.url}`})),!0}if(n!==t.path)return r?.(),!1;if("POST"!==i.method)return function(e,t){t.writeHead(404,{"content-type":"application/json"}),t.end(JSON.stringify({error:`Unknown route: ${e.method} ${e.url}`}))}(i,s),!0;if(!i.headers["content-type"]||!i.headers["content-type"].startsWith("application/json"))return s.writeHead(415,{"content-type":"application/json",accept:"application/json"}),s.end(JSON.stringify({error:'Unsupported "Content-Type" header value. Must be "application/json"'})),!0;const o=function(e){return ae.filter((t=>!(t in e.headers)))}(i).join(", ");if(o)return s.writeHead(400,{"content-type":"application/json"}),s.end(JSON.stringify({error:`Required headers missing: ${o}`})),!0;const a=i.headers["x-github-event"],c=i.headers["x-hub-signature-256"],l=i.headers["x-github-delivery"];t.log.debug(`${a} event received (id: ${l})`);let p=!1;const A=setTimeout((()=>{p=!0,s.statusCode=202,s.end("still processing\n")}),9e3).unref();try{const t=await function(e){return"body"in e?"object"==typeof e.body&&"rawBody"in e&&e.rawBody instanceof Buffer?Promise.resolve(e.rawBody.toString("utf8")):Promise.resolve(e.body):new Promise(((t,i)=>{let s=[];e.on("error",(e=>i(new(Y())([e])))),e.on("data",(e=>s.push(e))),e.on("end",(()=>setImmediate(t,1===s.length?s[0].toString("utf8"):Buffer.concat(s).toString("utf8"))))}))}(i);return await e.verifyAndReceive({id:l,name:a,payload:t,signature:c}),clearTimeout(A),p||s.end("ok\n"),!0}catch(e){if(clearTimeout(A),p)return!0;const i=Array.from(e)[0],r=i.message?`${i.name}: ${i.message}`:"Error: An Unspecified error occurred";return s.statusCode=void 0!==i.status?i.status:500,t.log.error(e),s.end(JSON.stringify({error:r})),!0}}async function le(e,t){return e.octokit.auth({type:"installation",installationId:t,factory(e){const i={...e.octokitOptions,authStrategy:O.createAppAuth,auth:{...e,installationId:t}};return new e.octokit.constructor(i)}})}function pe(e){return Object.assign(Ae.bind(null,e),{iterator:ue.bind(null,e)})}async function Ae(e,t){const i=ue(e)[Symbol.asyncIterator]();let s=await i.next();for(;!s.done;)await t(s.value),s=await i.next()}function ue(e){return{async*[Symbol.asyncIterator](){const t=a.iterator(e.octokit,"GET /app/installations");for await(const{data:i}of t)for(const t of i){const i=await le(e,t.id);yield{octokit:i,installation:t}}}}}function de(e){return Object.assign(he.bind(null,e),{iterator:me.bind(null,e)})}async function he(e,t,i){const s=me(e,i?t:void 0)[Symbol.asyncIterator]();let r=await s.next();for(;!r.done;)i?await i(r.value):await t(r.value),r=await s.next()}function me(e,t){return{async*[Symbol.asyncIterator](){const i=t?function(e,t){return{async*[Symbol.asyncIterator](){yield{octokit:await e.getInstallationOctokit(t)}}}}(e,t.installationId):e.eachInstallation.iterator();for await(const{octokit:e}of i){const t=a.iterator(e,"GET /installation/repositories");for await(const{data:i}of t)for(const t of i)yield{octokit:e,repository:t}}}}}function ge(){}function fe(e,t={}){const i=Object.assign({debug:ge,info:ge,warn:console.warn.bind(console),error:console.error.bind(console)},t.log),s={pathPrefix:"/api/github",...t,log:i},r=function(e,{path:t="/api/github/webhooks",log:i=P()}={}){return ce.bind(null,e,{path:t,log:i})}(e.webhooks,{path:s.pathPrefix+"/webhooks",log:i}),n=(0,M.createNodeMiddleware)(e.oauth,{pathPrefix:s.pathPrefix+"/oauth"});return Ee.bind(null,s.pathPrefix,r,n)}async function Ee(e,t,i,s,r,n){const{pathname:o}=new URL(s.url,"http://localhost");return o.startsWith(`${e}/`)?(o===`${e}/webhooks`?t(s,r):o.startsWith(`${e}/oauth/`)?i(s,r):(0,M.sendNodeResponse)((0,M.unknownRouteResponse)(s),r),!0):(n?.(),!1)}var Ce=class{static{this.VERSION="14.1.0"}static defaults(e){return class extends(this){constructor(...t){super({...e,...t[0]})}}}constructor(e){const t=e.Octokit||s.Octokit,i=Object.assign({appId:e.appId,privateKey:e.privateKey},e.oauth?{clientId:e.oauth.clientId,clientSecret:e.oauth.clientSecret}:{});this.octokit=new t({authStrategy:O.createAppAuth,auth:i,log:e.log}),this.log=Object.assign({debug:()=>{},info:()=>{},warn:console.warn.bind(console),error:console.error.bind(console)},e.log),e.webhooks?this.webhooks=function(e,t){return new oe({secret:t.secret,transform:async t=>{if(!("installation"in t.payload)||"object"!=typeof t.payload.installation){const i=new e.constructor({authStrategy:U.createUnauthenticatedAuth,auth:{reason:'"installation" key missing in webhook event payload'}});return{...t,octokit:i}}const i=t.payload.installation.id,s=await e.auth({type:"installation",installationId:i,factory:e=>new e.octokit.constructor({...e.octokitOptions,authStrategy:O.createAppAuth,auth:{...e,installationId:i}})});return s.hook.before("request",(e=>{e.headers["x-github-delivery"]=t.id})),{...t,octokit:s}}})}(this.octokit,e.webhooks):Object.defineProperty(this,"webhooks",{get(){throw new Error("[@octokit/app] webhooks option not set")}}),e.oauth?this.oauth=new M.OAuthApp({...e.oauth,clientType:"github-app",Octokit:t}):Object.defineProperty(this,"oauth",{get(){throw new Error("[@octokit/app] oauth.clientId / oauth.clientSecret options are not set")}}),this.getInstallationOctokit=le.bind(null,this),this.eachInstallation=pe(this),this.eachRepository=de(this)}},ye=s.Octokit.plugin(I,c,(function(e){return e.graphql,{graphql:Object.assign(e.graphql,{paginate:Object.assign(E(e),{iterator:g(e)})})}}),D,L).defaults({userAgent:"octokit.js/3.1.2",throttle:{onRateLimit:function(e,t,i){if(i.log.warn(`Request quota exhausted for request ${t.method} ${t.url}`),0===t.request.retryCount)return i.log.info(`Retrying after ${e} seconds!`),!0},onSecondaryRateLimit:function(e,t,i){if(i.log.warn(`SecondaryRateLimit detected for request ${t.method} ${t.url}`),0===t.request.retryCount)return i.log.info(`Retrying after ${e} seconds!`),!0}}}),ve=Ce.defaults({Octokit:ye}),Ie=M.OAuthApp.defaults({Octokit:ye})},36219:(e,t,i)=>{var s=i(42065);function r(e){var t=function(){return t.called?t.value:(t.called=!0,t.value=e.apply(this,arguments))};return t.called=!1,t}function n(e){var t=function(){if(t.called)throw new Error(t.onceError);return t.called=!0,t.value=e.apply(this,arguments)},i=e.name||"Function wrapped with `once`";return t.onceError=i+" shouldn't be called more than once",t.called=!1,t}e.exports=s(r),e.exports.strict=s(n),r.proto=r((function(){Object.defineProperty(Function.prototype,"once",{value:function(){return r(this)},configurable:!0}),Object.defineProperty(Function.prototype,"onceStrict",{value:function(){return n(this)},configurable:!0})}))},86015:(e,t,i)=>{"use strict";const s=i(70474),r=new WeakMap,n=(e,t={})=>{if("function"!=typeof e)throw new TypeError("Expected a function");let i,n=0;const o=e.displayName||e.name||"",a=function(...s){if(r.set(a,++n),1===n)i=e.apply(this,s),e=null;else if(!0===t.throw)throw new Error(`Function \`${o}\` can only be called once`);return i};return s(a,e),r.set(a,n),a};e.exports=n,e.exports.default=n,e.exports.callCount=e=>{if(!r.has(e))throw new Error(`The given function \`${e.name}\` is not wrapped by the \`onetime\` package`);return r.get(e)}},7540:(e,t,i)=>{"use strict";const s=i(14521),r=i(47309),n=i(55693),o=i(60881),a=i(6853),c=i(20281),l=i(80762),p=i(27759),A=i(5881),{BufferListStream:u}=i(26221),d=Symbol("text"),h=Symbol("prefixText");class m{constructor(){this.requests=0,this.mutedStream=new u,this.mutedStream.pipe(process.stdout);const e=this;this.ourEmit=function(t,i,...s){const{stdin:r}=process;if(e.requests>0||r.emit===e.ourEmit){if("keypress"===t)return;"data"===t&&i.includes(3)&&process.emit("SIGINT"),Reflect.apply(e.oldEmit,this,[t,i,...s])}else Reflect.apply(process.stdin.emit,this,[t,i,...s])}}start(){this.requests++,1===this.requests&&this.realStart()}stop(){if(this.requests<=0)throw new Error("`stop` called more times than `start`");this.requests--,0===this.requests&&this.realStop()}realStart(){"win32"!==process.platform&&(this.rl=s.createInterface({input:process.stdin,output:this.mutedStream}),this.rl.on("SIGINT",(()=>{0===process.listenerCount("SIGINT")?process.emit("SIGINT"):(this.rl.close(),process.kill(process.pid,"SIGINT"))})))}realStop(){"win32"!==process.platform&&(this.rl.close(),this.rl=void 0)}}let g;class f{constructor(e){g||(g=new m),"string"==typeof e&&(e={text:e}),this.options={text:"",color:"cyan",stream:process.stderr,discardStdin:!0,...e},this.spinner=this.options.spinner,this.color=this.options.color,this.hideCursor=!1!==this.options.hideCursor,this.interval=this.options.interval||this.spinner.interval||100,this.stream=this.options.stream,this.id=void 0,this.isEnabled="boolean"==typeof this.options.isEnabled?this.options.isEnabled:p({stream:this.stream}),this.isSilent="boolean"==typeof this.options.isSilent&&this.options.isSilent,this.text=this.options.text,this.prefixText=this.options.prefixText,this.linesToClear=0,this.indent=this.options.indent,this.discardStdin=this.options.discardStdin,this.isDiscardingStdin=!1}get indent(){return this._indent}set indent(e=0){if(!(e>=0&&Number.isInteger(e)))throw new Error("The `indent` option must be an integer from 0 and up");this._indent=e}_updateInterval(e){void 0!==e&&(this.interval=e)}get spinner(){return this._spinner}set spinner(e){if(this.frameIndex=0,"object"==typeof e){if(void 0===e.frames)throw new Error("The given spinner must have a `frames` property");this._spinner=e}else if(A())if(void 0===e)this._spinner=o.dots;else{if("default"===e||!o[e])throw new Error(`There is no built-in spinner named '${e}'. See https://github.com/sindresorhus/cli-spinners/blob/main/spinners.json for a full list.`);this._spinner=o[e]}else this._spinner=o.line;this._updateInterval(this._spinner.interval)}get text(){return this[d]}set text(e){this[d]=e,this.updateLineCount()}get prefixText(){return this[h]}set prefixText(e){this[h]=e,this.updateLineCount()}get isSpinning(){return void 0!==this.id}getFullPrefixText(e=this[h],t=" "){return"string"==typeof e?e+t:"function"==typeof e?e()+t:""}updateLineCount(){const e=this.stream.columns||80,t=this.getFullPrefixText(this.prefixText,"-");this.lineCount=0;for(const i of c(t+"--"+this[d]).split("\n"))this.lineCount+=Math.max(1,Math.ceil(l(i)/e))}get isEnabled(){return this._isEnabled&&!this.isSilent}set isEnabled(e){if("boolean"!=typeof e)throw new TypeError("The `isEnabled` option must be a boolean");this._isEnabled=e}get isSilent(){return this._isSilent}set isSilent(e){if("boolean"!=typeof e)throw new TypeError("The `isSilent` option must be a boolean");this._isSilent=e}frame(){const{frames:e}=this.spinner;let t=e[this.frameIndex];return this.color&&(t=r[this.color](t)),this.frameIndex=++this.frameIndex%e.length,("string"==typeof this.prefixText&&""!==this.prefixText?this.prefixText+" ":"")+t+("string"==typeof this.text?" "+this.text:"")}clear(){if(!this.isEnabled||!this.stream.isTTY)return this;for(let e=0;e0&&this.stream.moveCursor(0,-1),this.stream.clearLine(),this.stream.cursorTo(this.indent);return this.linesToClear=0,this}render(){return this.isSilent||(this.clear(),this.stream.write(this.frame()),this.linesToClear=this.lineCount),this}start(e){return e&&(this.text=e),this.isSilent?this:this.isEnabled?(this.isSpinning||(this.hideCursor&&n.hide(this.stream),this.discardStdin&&process.stdin.isTTY&&(this.isDiscardingStdin=!0,g.start()),this.render(),this.id=setInterval(this.render.bind(this),this.interval)),this):(this.text&&this.stream.write(`- ${this.text}\n`),this)}stop(){return this.isEnabled?(clearInterval(this.id),this.id=void 0,this.frameIndex=0,this.clear(),this.hideCursor&&n.show(this.stream),this.discardStdin&&process.stdin.isTTY&&this.isDiscardingStdin&&(g.stop(),this.isDiscardingStdin=!1),this):this}succeed(e){return this.stopAndPersist({symbol:a.success,text:e})}fail(e){return this.stopAndPersist({symbol:a.error,text:e})}warn(e){return this.stopAndPersist({symbol:a.warning,text:e})}info(e){return this.stopAndPersist({symbol:a.info,text:e})}stopAndPersist(e={}){if(this.isSilent)return this;const t=e.prefixText||this.prefixText,i=e.text||this.text,s="string"==typeof i?" "+i:"";return this.stop(),this.stream.write(`${this.getFullPrefixText(t," ")}${e.symbol||" "}${s}\n`),this}}e.exports=function(e){return new f(e)},e.exports.promise=(e,t)=>{if("function"!=typeof e.then)throw new TypeError("Parameter `action` must be a Promise");const i=new f(t);return i.start(),(async()=>{try{await e,i.succeed()}catch{i.fail()}})(),i}},23209:e=>{"use strict";e.exports=(e,t)=>(t=t||(()=>{}),e.then((e=>new Promise((e=>{e(t())})).then((()=>e))),(e=>new Promise((e=>{e(t())})).then((()=>{throw e})))))},88464:(e,t,i)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});const s=i(21883),r=i(26123),n=i(83991),o=()=>{},a=new r.TimeoutError;t.default=class extends s{constructor(e){var t,i,s,r;if(super(),this._intervalCount=0,this._intervalEnd=0,this._pendingCount=0,this._resolveEmpty=o,this._resolveIdle=o,!("number"==typeof(e=Object.assign({carryoverConcurrencyCount:!1,intervalCap:1/0,interval:0,concurrency:1/0,autoStart:!0,queueClass:n.default},e)).intervalCap&&e.intervalCap>=1))throw new TypeError(`Expected \`intervalCap\` to be a number from 1 and up, got \`${null!==(i=null===(t=e.intervalCap)||void 0===t?void 0:t.toString())&&void 0!==i?i:""}\` (${typeof e.intervalCap})`);if(void 0===e.interval||!(Number.isFinite(e.interval)&&e.interval>=0))throw new TypeError(`Expected \`interval\` to be a finite number >= 0, got \`${null!==(r=null===(s=e.interval)||void 0===s?void 0:s.toString())&&void 0!==r?r:""}\` (${typeof e.interval})`);this._carryoverConcurrencyCount=e.carryoverConcurrencyCount,this._isIntervalIgnored=e.intervalCap===1/0||0===e.interval,this._intervalCap=e.intervalCap,this._interval=e.interval,this._queue=new e.queueClass,this._queueClass=e.queueClass,this.concurrency=e.concurrency,this._timeout=e.timeout,this._throwOnTimeout=!0===e.throwOnTimeout,this._isPaused=!1===e.autoStart}get _doesIntervalAllowAnother(){return this._isIntervalIgnored||this._intervalCount{this._onResumeInterval()}),t)),!0;this._intervalCount=this._carryoverConcurrencyCount?this._pendingCount:0}return!1}_tryToStartAnother(){if(0===this._queue.size)return this._intervalId&&clearInterval(this._intervalId),this._intervalId=void 0,this._resolvePromises(),!1;if(!this._isPaused){const e=!this._isIntervalPaused();if(this._doesIntervalAllowAnother&&this._doesConcurrentAllowAnother){const t=this._queue.dequeue();return!!t&&(this.emit("active"),t(),e&&this._initializeIntervalIfNeeded(),!0)}}return!1}_initializeIntervalIfNeeded(){this._isIntervalIgnored||void 0!==this._intervalId||(this._intervalId=setInterval((()=>{this._onInterval()}),this._interval),this._intervalEnd=Date.now()+this._interval)}_onInterval(){0===this._intervalCount&&0===this._pendingCount&&this._intervalId&&(clearInterval(this._intervalId),this._intervalId=void 0),this._intervalCount=this._carryoverConcurrencyCount?this._pendingCount:0,this._processQueue()}_processQueue(){for(;this._tryToStartAnother(););}get concurrency(){return this._concurrency}set concurrency(e){if(!("number"==typeof e&&e>=1))throw new TypeError(`Expected \`concurrency\` to be a number from 1 and up, got \`${e}\` (${typeof e})`);this._concurrency=e,this._processQueue()}async add(e,t={}){return new Promise(((i,s)=>{this._queue.enqueue((async()=>{this._pendingCount++,this._intervalCount++;try{const n=void 0===this._timeout&&void 0===t.timeout?e():r.default(Promise.resolve(e()),void 0===t.timeout?this._timeout:t.timeout,(()=>{(void 0===t.throwOnTimeout?this._throwOnTimeout:t.throwOnTimeout)&&s(a)}));i(await n)}catch(e){s(e)}this._next()}),t),this._tryToStartAnother(),this.emit("add")}))}async addAll(e,t){return Promise.all(e.map((async e=>this.add(e,t))))}start(){return this._isPaused?(this._isPaused=!1,this._processQueue(),this):this}pause(){this._isPaused=!0}clear(){this._queue=new this._queueClass}async onEmpty(){if(0!==this._queue.size)return new Promise((e=>{const t=this._resolveEmpty;this._resolveEmpty=()=>{t(),e()}}))}async onIdle(){if(0!==this._pendingCount||0!==this._queue.size)return new Promise((e=>{const t=this._resolveIdle;this._resolveIdle=()=>{t(),e()}}))}get size(){return this._queue.size}sizeBy(e){return this._queue.filter(e).length}get pending(){return this._pendingCount}get isPaused(){return this._isPaused}get timeout(){return this._timeout}set timeout(e){this._timeout=e}}},10392:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t,i){let s=0,r=e.length;for(;r>0;){const n=r/2|0;let o=s+n;i(e[o],t)<=0?(s=++o,r-=n+1):r=n}return s}},83991:(e,t,i)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});const s=i(10392);t.default=class{constructor(){this._queue=[]}enqueue(e,t){const i={priority:(t=Object.assign({priority:0},t)).priority,run:e};if(this.size&&this._queue[this.size-1].priority>=t.priority)return void this._queue.push(i);const r=s.default(this._queue,i,((e,t)=>t.priority-e.priority));this._queue.splice(r,0,i)}dequeue(){const e=this._queue.shift();return null==e?void 0:e.run}filter(e){return this._queue.filter((t=>t.priority===e.priority)).map((e=>e.run))}get size(){return this._queue.length}}},32656:(e,t,i)=>{"use strict";const s=i(83490),r=["Failed to fetch","NetworkError when attempting to fetch resource.","The Internet connection appears to be offline.","Network request failed"];class n extends Error{constructor(e){super(),e instanceof Error?(this.originalError=e,({message:e}=e)):(this.originalError=new Error(e),this.originalError.stack=this.stack),this.name="AbortError",this.message=e}}const o=(e,t)=>new Promise(((i,o)=>{t={onFailedAttempt:()=>{},retries:10,...t};const a=s.operation(t);a.attempt((async s=>{try{i(await e(s))}catch(e){if(!(e instanceof Error))return void o(new TypeError(`Non-error was thrown: "${e}". You should only throw errors.`));if(e instanceof n)a.stop(),o(e.originalError);else if(e instanceof TypeError&&(c=e.message,!r.includes(c)))a.stop(),o(e);else{((e,t,i)=>{const s=i.retries-(t-1);e.attemptNumber=t,e.retriesLeft=s})(e,s,t);try{await t.onFailedAttempt(e)}catch(e){return void o(e)}a.retry(e)||o(a.mainError())}}var c}))}));e.exports=o,e.exports.default=o,e.exports.AbortError=n},26123:(e,t,i)=>{"use strict";const s=i(23209);class r extends Error{constructor(e){super(e),this.name="TimeoutError"}}const n=(e,t,i)=>new Promise(((n,o)=>{if("number"!=typeof t||t<0)throw new TypeError("Expected `milliseconds` to be a positive number");if(t===1/0)return void n(e);const a=setTimeout((()=>{if("function"==typeof i){try{n(i())}catch(e){o(e)}return}const s=i instanceof Error?i:new r("string"==typeof i?i:`Promise timed out after ${t} milliseconds`);"function"==typeof e.cancel&&e.cancel(),o(s)}),t);s(e.then(n,o),(()=>{clearTimeout(a)}))}));e.exports=n,e.exports.default=n,e.exports.TimeoutError=r},30486:(e,t,i)=>{"use strict";const s=i(30746),r=i(94057),n=e.exports;n.prompt=(e,t)=>(t=r(t),s(e,t)),n.password=(e,t)=>(t=r({silent:!0,trim:!1,default:"",...t}),s(e,t)),n.confirm=(e,t)=>((t=r({trim:!1,...t})).validator.unshift((e=>{switch(e=e.toLowerCase()){case"y":case"yes":case"1":return!0;case"n":case"no":case"0":return!1;default:throw new Error(`Invalid choice: ${e}`)}})),s(e,t)),n.choose=(e,t,i)=>((i=r({trim:!1,...i})).validator.unshift((e=>{const i=t.findIndex((t=>e==t));if(-1===i)throw new Error(`Invalid choice: ${e}`);return t[i]})),s(e,i))},94057:e=>{"use strict";e.exports=function(e){if(void 0!==(e={validator:void 0,retry:!0,trim:!0,default:void 0,useDefaultOnTimeout:!1,silent:!1,replace:"",input:process.stdin,output:process.stdout,timeout:0,...e}).default&&"string"!=typeof e.default)throw new Error("The default option value must be a string");return Array.isArray(e.validator)||(e.validator=e.validator?[e.validator]:[]),e}},30746:(e,t,i)=>{"use strict";const{EOL:s}=i(22037),{promisify:r}=i(73837),n=r(i(91460));e.exports=async function e(t,i){let r;try{r=await n({prompt:t,silent:i.silent,replace:i.replace,input:i.input,output:i.output,timeout:i.timeout})}catch(e){if("timed out"!==e.message||void 0===i.default||!i.useDefaultOnTimeout)throw Object.assign(new Error(e.message),{code:"TIMEDOUT"});r=i.default}if(i.trim&&(r=r.trim()),!r){if(void 0===i.default)return e(t,i);r=i.default}try{for(const e in i.validator)r=await i.validator[e](r)}catch(r){if(i.retry)return r.message&&i.output.write(r.message+s),e(t,i);throw r}return r}},67841:(e,t,i)=>{"use strict";var s=i(57310).parse,r={ftp:21,gopher:70,http:80,https:443,ws:80,wss:443},n=String.prototype.endsWith||function(e){return e.length<=this.length&&-1!==this.indexOf(e,this.length-e.length)};function o(e){return process.env[e.toLowerCase()]||process.env[e.toUpperCase()]||""}t.getProxyForUrl=function(e){var t="string"==typeof e?s(e):e||{},i=t.protocol,a=t.host,c=t.port;if("string"!=typeof a||!a||"string"!=typeof i)return"";if(i=i.split(":",1)[0],!function(e,t){var i=(o("npm_config_no_proxy")||o("no_proxy")).toLowerCase();return!i||"*"!==i&&i.split(/[,\s]/).every((function(i){if(!i)return!0;var s=i.match(/^(.+):(\d+)$/),r=s?s[1]:i,o=s?parseInt(s[2]):0;return!(!o||o===t)||(/^[.*]/.test(r)?("*"===r.charAt(0)&&(r=r.slice(1)),!n.call(e,r)):e!==r)}))}(a=a.replace(/:\d*$/,""),c=parseInt(c)||r[i]||0))return"";var l=o("npm_config_"+i+"_proxy")||o(i+"_proxy")||o("npm_config_proxy")||o("all_proxy");return l&&-1===l.indexOf("://")&&(l=i+"://"+l),l}},91460:(e,t,i)=>{e.exports=function(e,t){if(e.num)throw new Error("read() no longer accepts a char number limit");if(void 0!==e.default&&"string"!=typeof e.default&&"number"!=typeof e.default)throw new Error("default value must be string or number");var i=e.input||process.stdin,n=e.output||process.stdout,o=(e.prompt||"").trim()+" ",a=e.silent,c=!1,l=e.timeout,p=e.default||"";p&&(a?o+="(