From 422d4281d1c508a5a9b6ff73e25a6662d128aa6a Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sat, 31 Oct 2026 22:56:29 +0100 Subject: [PATCH 01/50] Update packages Refresh the root's, Linked Orders' and Internal Comments' Composer locks, which move only roave/security-advisories, and their npm locks, which take Babel 7.29.11, ignore 7.0.14, http-proxy-middleware 4.2.1 and their patch-level neighbours, every change upward. No code change was needed. Assisted-by: Claude Code:claude-opus-5-5 --- composer.lock | 121 +++++--- package-lock.json | 90 +++--- plugins/internal-comments/composer.lock | 255 +++++++++------ plugins/internal-comments/package-lock.json | 118 +++---- .../composer.lock | 291 ++++++++++++------ .../package-lock.json | 66 ++-- 6 files changed, 581 insertions(+), 360 deletions(-) diff --git a/composer.lock b/composer.lock index a353343..0d6bf14 100644 --- a/composer.lock +++ b/composer.lock @@ -206,7 +206,8 @@ "support": { "source": "https://github.com/Automattic/jetpack-changelogger/tree/v7.0.5" }, - "time": "2026-10-20T16:01:37+00:00" + "time": "2026-10-20T16:01:37+00:00", + "published-time": "2026-10-20T16:01:50+00:00" }, { "name": "composer/class-map-generator", @@ -275,7 +276,8 @@ "type": "github" } ], - "time": "2026-09-25T15:24:19+00:00" + "time": "2026-09-25T15:24:19+00:00", + "published-time": "2026-09-25T15:27:30+00:00" }, { "name": "composer/installers", @@ -421,7 +423,8 @@ "type": "tidelift" } ], - "time": "2024-06-24T20:46:46+00:00" + "time": "2024-06-24T20:46:46+00:00", + "published-time": "2024-06-24T21:01:36+00:00" }, { "name": "composer/pcre", @@ -497,7 +500,8 @@ "type": "github" } ], - "time": "2026-06-07T11:47:49+00:00" + "time": "2026-06-07T11:47:49+00:00", + "published-time": "2026-06-11T12:12:37+00:00" }, { "name": "dealerdirect/phpcodesniffer-composer-installer", @@ -593,7 +597,8 @@ "type": "thanks_dev" } ], - "time": "2026-05-06T08:26:05+00:00" + "time": "2026-05-06T08:26:05+00:00", + "published-time": "2026-05-06T12:56:03+00:00" }, { "name": "johnbillion/wp-compat", @@ -668,7 +673,8 @@ "type": "github" } ], - "time": "2026-10-21T20:54:48+00:00" + "time": "2026-10-21T20:54:48+00:00", + "published-time": "2026-10-21T20:54:55+00:00" }, { "name": "nikic/php-parser", @@ -725,7 +731,8 @@ "issues": "https://github.com/nikic/PHP-Parser/issues", "source": "https://github.com/nikic/PHP-Parser/tree/v5.9.0" }, - "time": "2026-09-13T18:51:52+00:00" + "time": "2026-09-13T18:51:52+00:00", + "published-time": "2026-09-13T18:53:16+00:00" }, { "name": "php-stubs/wordpress-stubs", @@ -777,7 +784,8 @@ "issues": "https://github.com/php-stubs/wordpress-stubs/issues", "source": "https://github.com/php-stubs/wordpress-stubs/tree/v7.1.2" }, - "time": "2026-10-06T06:59:21+00:00" + "time": "2026-10-06T06:59:21+00:00", + "published-time": "2026-10-06T07:32:21+00:00" }, { "name": "phpcompatibility/php-compatibility", @@ -867,7 +875,8 @@ "type": "thanks_dev" } ], - "time": "2025-11-28T11:36:33+00:00" + "time": "2025-11-28T11:36:33+00:00", + "published-time": "2025-11-28T11:41:33+00:00" }, { "name": "phpcompatibility/phpcompatibility-paragonie", @@ -938,7 +947,8 @@ "type": "thanks_dev" } ], - "time": "2025-11-29T13:09:49+00:00" + "time": "2025-11-29T13:09:49+00:00", + "published-time": "2025-11-29T13:22:17+00:00" }, { "name": "phpcompatibility/phpcompatibility-wp", @@ -1005,7 +1015,8 @@ "type": "thanks_dev" } ], - "time": "2025-12-16T13:35:20+00:00" + "time": "2025-12-16T13:35:20+00:00", + "published-time": "2025-12-16T13:47:29+00:00" }, { "name": "phpcsstandards/phpcsextra", @@ -1087,7 +1098,8 @@ "type": "thanks_dev" } ], - "time": "2026-07-27T11:13:17+00:00" + "time": "2026-07-27T11:13:17+00:00", + "published-time": "2026-07-27T11:19:59+00:00" }, { "name": "phpcsstandards/phpcsutils", @@ -1180,7 +1192,8 @@ "type": "thanks_dev" } ], - "time": "2026-07-27T10:28:41+00:00" + "time": "2026-07-27T10:28:41+00:00", + "published-time": "2026-07-27T10:36:31+00:00" }, { "name": "phpstan/extension-installer", @@ -1228,7 +1241,8 @@ "issues": "https://github.com/phpstan/extension-installer/issues", "source": "https://github.com/phpstan/extension-installer/tree/1.4.3" }, - "time": "2024-09-04T20:21:43+00:00" + "time": "2024-09-04T20:21:43+00:00", + "published-time": "2024-09-04T20:22:27+00:00" }, { "name": "phpstan/phpstan", @@ -1292,7 +1306,8 @@ "type": "github" } ], - "time": "2026-10-28T22:38:53+00:00" + "time": "2026-10-28T22:38:53+00:00", + "published-time": "2026-10-28T22:39:06+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", @@ -1343,7 +1358,8 @@ "issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues", "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/2.0.5" }, - "time": "2026-07-22T06:50:43+00:00" + "time": "2026-07-22T06:50:43+00:00", + "published-time": "2026-07-27T15:18:00+00:00" }, { "name": "phpstan/phpstan-strict-rules", @@ -1395,7 +1411,8 @@ "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.1.0" }, - "time": "2026-10-01T21:30:37+00:00" + "time": "2026-10-01T21:30:37+00:00", + "published-time": "2026-10-06T07:32:29+00:00" }, { "name": "psr/container", @@ -1448,7 +1465,8 @@ "issues": "https://github.com/php-fig/container/issues", "source": "https://github.com/php-fig/container/tree/2.0.2" }, - "time": "2021-11-05T16:47:00+00:00" + "time": "2021-11-05T16:47:00+00:00", + "published-time": "2021-11-05T16:53:27+00:00" }, { "name": "roave/security-advisories", @@ -1456,12 +1474,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "8e26a62e09d971af73b5455efcc52f6d142741c9" + "reference": "7c964bc969c1b16050a4b85b7a55614777a3f597" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/8e26a62e09d971af73b5455efcc52f6d142741c9", - "reference": "8e26a62e09d971af73b5455efcc52f6d142741c9", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/7c964bc969c1b16050a4b85b7a55614777a3f597", + "reference": "7c964bc969c1b16050a4b85b7a55614777a3f597", "shasum": "" }, "conflict": { @@ -1469,7 +1487,7 @@ "adaptcms/adaptcms": "<=1.3", "adawolfa/isdoc": "<1.4.3|>=1.5,<1.5.1|>=1.6,<1.6.1", "admidio/admidio": "<=5.0.11", - "adminneo-org/adminneo": ">=4.6.2,<5.5", + "adminneo-org/adminneo": "<5.5.1", "adodb/adodb-php": "<=5.22.9", "aheinze/cockpit": "<2.2", "aimeos/ai-admin-graphql": ">=2022.04.1,<2022.10.10|>=2023.04.1,<2023.10.6|>=2024.04.1,<2024.07.2", @@ -2618,7 +2636,8 @@ "type": "tidelift" } ], - "time": "2026-10-30T20:26:18+00:00" + "time": "2026-10-31T00:24:47+00:00", + "published-time": "2026-10-31T00:24:51+00:00" }, { "name": "sniccowp/php-scoper-wordpress-excludes", @@ -2665,7 +2684,8 @@ "issues": "https://github.com/snicco/php-scoper-wordpress-excludes/issues", "source": "https://github.com/snicco/php-scoper-wordpress-excludes/tree/7.1.0" }, - "time": "2026-08-28T02:50:05+00:00" + "time": "2026-08-28T02:50:05+00:00", + "published-time": "2026-09-23T19:33:26+00:00" }, { "name": "squizlabs/php_codesniffer", @@ -2744,7 +2764,8 @@ "type": "thanks_dev" } ], - "time": "2026-08-06T00:17:32+00:00" + "time": "2026-08-06T00:17:32+00:00", + "published-time": "2026-08-06T01:05:02+00:00" }, { "name": "swissspidy/phpstan-no-private", @@ -2796,7 +2817,8 @@ "issues": "https://github.com/swissspidy/phpstan-no-private/issues", "source": "https://github.com/swissspidy/phpstan-no-private/tree/v1.0.0" }, - "time": "2024-11-11T11:04:45+00:00" + "time": "2024-11-11T11:04:45+00:00", + "published-time": "2024-11-11T11:08:03+00:00" }, { "name": "symfony/console", @@ -2894,7 +2916,8 @@ "type": "tidelift" } ], - "time": "2026-10-28T11:08:27+00:00" + "time": "2026-10-28T11:08:27+00:00", + "published-time": "2026-10-28T18:21:33+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2965,7 +2988,8 @@ "type": "tidelift" } ], - "time": "2026-06-05T06:23:12+00:00" + "time": "2026-06-05T06:23:12+00:00", + "published-time": "2026-06-27T21:46:34+00:00" }, { "name": "symfony/filesystem", @@ -3035,7 +3059,8 @@ "type": "tidelift" } ], - "time": "2026-10-06T20:05:19+00:00" + "time": "2026-10-06T20:05:19+00:00", + "published-time": "2026-10-14T06:45:33+00:00" }, { "name": "symfony/finder", @@ -3103,7 +3128,8 @@ "type": "tidelift" } ], - "time": "2026-10-19T16:56:54+00:00" + "time": "2026-10-19T16:56:54+00:00", + "published-time": "2026-10-28T18:21:14+00:00" }, { "name": "symfony/polyfill-ctype", @@ -3186,7 +3212,8 @@ "type": "tidelift" } ], - "time": "2026-04-10T16:19:22+00:00" + "time": "2026-04-10T16:19:22+00:00", + "published-time": "2026-04-26T14:44:50+00:00" }, { "name": "symfony/polyfill-intl-grapheme", @@ -3268,7 +3295,8 @@ "type": "tidelift" } ], - "time": "2026-10-15T09:18:53+00:00" + "time": "2026-10-15T09:18:53+00:00", + "published-time": "2026-10-28T10:07:44+00:00" }, { "name": "symfony/polyfill-intl-normalizer", @@ -3353,7 +3381,8 @@ "type": "tidelift" } ], - "time": "2026-09-23T22:02:09+00:00" + "time": "2026-09-23T22:02:09+00:00", + "published-time": "2026-09-30T10:23:47+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -3438,7 +3467,8 @@ "type": "tidelift" } ], - "time": "2026-10-28T09:35:49+00:00" + "time": "2026-10-28T09:35:49+00:00", + "published-time": "2026-10-28T10:07:45+00:00" }, { "name": "symfony/process", @@ -3503,7 +3533,8 @@ "type": "tidelift" } ], - "time": "2026-10-25T14:40:16+00:00" + "time": "2026-10-25T14:40:16+00:00", + "published-time": "2026-10-28T18:21:18+00:00" }, { "name": "symfony/service-contracts", @@ -3590,7 +3621,8 @@ "type": "tidelift" } ], - "time": "2026-07-27T15:39:01+00:00" + "time": "2026-07-27T15:39:01+00:00", + "published-time": "2026-08-30T19:56:37+00:00" }, { "name": "symfony/string", @@ -3680,7 +3712,8 @@ "type": "tidelift" } ], - "time": "2026-10-23T17:06:53+00:00" + "time": "2026-10-23T17:06:53+00:00", + "published-time": "2026-10-28T18:24:27+00:00" }, { "name": "szepeviktor/phpstan-wordpress", @@ -3743,7 +3776,8 @@ "issues": "https://github.com/szepeviktor/phpstan-wordpress/issues", "source": "https://github.com/szepeviktor/phpstan-wordpress/tree/v2.0.4" }, - "time": "2026-05-22T16:22:09+00:00" + "time": "2026-05-22T16:22:09+00:00", + "published-time": "2026-08-28T06:48:49+00:00" }, { "name": "wp-coding-standards/wpcs", @@ -3809,7 +3843,8 @@ "type": "custom" } ], - "time": "2026-07-27T11:53:23+00:00" + "time": "2026-07-27T11:53:23+00:00", + "published-time": "2026-07-27T11:55:52+00:00" }, { "name": "wp-hooks/wordpress-core", @@ -3892,7 +3927,8 @@ "type": "github" } ], - "time": "2026-08-20T19:54:29+00:00" + "time": "2026-08-20T19:54:29+00:00", + "published-time": "2026-08-20T19:54:42+00:00" }, { "name": "wp-plugin/woocommerce", @@ -3923,7 +3959,8 @@ "issues": "https://wordpress.org/support/plugin/woocommerce", "source": "https://plugins.svn.wordpress.org/woocommerce" }, - "time": "2026-10-09T15:35:21+00:00" + "time": "2026-10-09T15:35:21+00:00", + "published-time": "2026-10-09T15:35:21+00:00" } ], "aliases": [], @@ -3937,5 +3974,5 @@ "php": ">=8.5" }, "platform-dev": {}, - "plugin-api-version": "2.9.0" + "plugin-api-version": "2.11.0" } diff --git a/package-lock.json b/package-lock.json index 72de75f..8f43839 100644 --- a/package-lock.json +++ b/package-lock.json @@ -370,9 +370,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.11.tgz", + "integrity": "sha512-snZbO7MEDOof+hvNa97EjEY7qD7h2kwvQr5AJC3ZhQudKW5dnej6k3kP9NQV88hJwaS8vDqRLFo9rivLEhle2g==", "dev": true, "license": "MIT", "engines": { @@ -429,13 +429,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.29.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz", - "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.11.tgz", + "integrity": "sha512-OZ6+oaK7GMyg3IfCpj8JOmn8n+w2rJbDT0LLfVDTLb1ZFeKDh3AiNELgCMUw175IJHSkLl74hG0M0VLsvx+gag==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.29.8" + "@babel/types": "^7.29.11" }, "bin": { "parser": "bin/babel-parser.js" @@ -708,9 +708,9 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.29.7.tgz", - "integrity": "sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.29.11.tgz", + "integrity": "sha512-iARF4jjHSM0v8DImCACPXrwEltBP3DgqDpXTvjAMBAVpyEY0LiGss78wvI+KtmW+IJNEvZ5sOhPDCsOsXsIybA==", "dev": true, "license": "MIT", "dependencies": { @@ -758,9 +758,9 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.7.tgz", - "integrity": "sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.11.tgz", + "integrity": "sha512-f8kTDM/nTvpLeafb+TJ4jboFr/Y3BbC+QzgPpPGzSKJqWLnj9LW0WLqv4XKA11LX5sr+EgvLE6Ix3YsCEusodw==", "dev": true, "license": "MIT", "dependencies": { @@ -769,7 +769,7 @@ "@babel/helper-globals": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-replace-supers": "^7.29.7", - "@babel/traverse": "^7.29.7" + "@babel/traverse": "^7.29.10" }, "engines": { "node": ">=6.9.0" @@ -1494,9 +1494,9 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.8.tgz", - "integrity": "sha512-4S9ksMGVWUshvgK0mKfvZky7leuG5/uoFVwMpAomJ8bMoDJiNHRVmc1EglwW/CmGVSqqWpEbXm9FmbRit22qoA==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.11.tgz", + "integrity": "sha512-5xWGhfuMgbOfIjsk/I2c4uMnoqRwQdKYi4QjvybQrcEVE0G9/p3qmIkp2Sxe8YF8DGNNVs5whfwNqIM5684XoQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1832,13 +1832,13 @@ } }, "node_modules/@babel/types": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", - "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.11.tgz", + "integrity": "sha512-WCsemW40Cpc6gouC1SrY6PBaZ+oLRlEVY9XBvZBI7il/HyUrb2nJ505wE1MHEFyDZNnrWDEuy4jL50WVHfTTSQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-string-parser": "^7.29.11", "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { @@ -6435,9 +6435,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.13.tgz", - "integrity": "sha512-2E8Q6rh+1RRSq/YdaGsNfXsEZy9FVqv6BSSrkmZJP7BnaoPnRfKL+YaofPvP+5rzdk144qSm6i4+hvPgUcbV4Q==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.14.tgz", + "integrity": "sha512-wCBiZ77u1tuWde6G/nfOtLaCTYcFDU5yCzARzK10y+rLhzYoL7a7yEemcopcc2Mzow7gZOIjnV2ym1NYmdiJOQ==", "dev": true, "license": "MIT", "engines": { @@ -8866,9 +8866,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.11.31", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.31.tgz", - "integrity": "sha512-b0k7wXlWjpuL/Uxb/pYlpjY0ej5BjAmurtPUk3eRGuloI4yjK32m8TiKUQ81l1ptcLjKS/KH2+uqIolZti1x4Q==", + "version": "2.11.32", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.32.tgz", + "integrity": "sha512-UvuxFU9P3hM29LrQdwu0cD6w6/+SFr+UlB9s/knI7+O2rK9xFxKUwSDWxGdbbqQ9/V7BJ5s0NI5nQAE4uJp3gQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -10883,9 +10883,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.463", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.463.tgz", - "integrity": "sha512-nVWNU6ltFjopvOdD44J4zoBkgO0M+WZiFKKFV7EjebOH9z0KNphk6bCEjplXtJofL21ARIuuwWOCsFkBVklm6w==", + "version": "1.5.464", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.464.tgz", + "integrity": "sha512-nRa65aFge40viPAKxMKULIY8tcXKwKboJWNn4RoKwyWXJ6SP7BnrDGKUTzc64818jIciH0PK7LSF8QzZes19GA==", "dev": true, "license": "ISC" }, @@ -13296,9 +13296,9 @@ } }, "node_modules/http-proxy-middleware": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-4.2.0.tgz", - "integrity": "sha512-ZA+oNOoM+GLoFTIzhkJptVQov73Srep2LBqhF8hG8CIPKO3nam1jonXVQ/QUH8RbwsmaaVz2SOJdzBNBHNtKbw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-4.2.1.tgz", + "integrity": "sha512-60SmYZpPD4dPbC2GZVkVV4uP/V/cbsXSwzGQXeHWbbE3Q1XdccJpqHTg6NJ6iMpFGVA1OEry8c8kHhx4ahiU1w==", "dev": true, "license": "MIT", "dependencies": { @@ -14781,9 +14781,9 @@ } }, "node_modules/legacy-javascript": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/legacy-javascript/-/legacy-javascript-0.0.3.tgz", - "integrity": "sha512-1r6MT4dVaJyXt4G07Cm2cQ0AUaLJdefiEp4SmIAylA++gnEQ82YvmzE58RkM5EP0fk5hROLCslUPGnwIYdZQVA==", + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/legacy-javascript/-/legacy-javascript-0.0.4.tgz", + "integrity": "sha512-UTmeKBVxnfJcpmpOvzzNnLsQT99BXHpPlc/ZKddIRmWmrR0fw6335I3lq4+sJoP60UTbBNaYsGYVPc65YB1x3A==", "dev": true, "license": "Apache-2.0" }, @@ -15257,9 +15257,9 @@ "license": "Python-2.0" }, "node_modules/markdownlint-cli/node_modules/ignore": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.13.tgz", - "integrity": "sha512-2E8Q6rh+1RRSq/YdaGsNfXsEZy9FVqv6BSSrkmZJP7BnaoPnRfKL+YaofPvP+5rzdk144qSm6i4+hvPgUcbV4Q==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.14.tgz", + "integrity": "sha512-wCBiZ77u1tuWde6G/nfOtLaCTYcFDU5yCzARzK10y+rLhzYoL7a7yEemcopcc2Mzow7gZOIjnV2ym1NYmdiJOQ==", "dev": true, "license": "MIT", "engines": { @@ -20863,9 +20863,9 @@ } }, "node_modules/stylelint/node_modules/ignore": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.13.tgz", - "integrity": "sha512-2E8Q6rh+1RRSq/YdaGsNfXsEZy9FVqv6BSSrkmZJP7BnaoPnRfKL+YaofPvP+5rzdk144qSm6i4+hvPgUcbV4Q==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.14.tgz", + "integrity": "sha512-wCBiZ77u1tuWde6G/nfOtLaCTYcFDU5yCzARzK10y+rLhzYoL7a7yEemcopcc2Mzow7gZOIjnV2ym1NYmdiJOQ==", "dev": true, "license": "MIT", "engines": { @@ -22119,9 +22119,9 @@ "license": "Apache-2.0" }, "node_modules/webpack": { - "version": "5.112.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.112.1.tgz", - "integrity": "sha512-DsEgOfaY5XrvTTEU7QvktlOpYEipgNUyXQY4rQO4KtFmr71IwjMoRWyv9rJNo6qwqctRzh9sO9VIwMgnWmiBVg==", + "version": "5.112.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.112.2.tgz", + "integrity": "sha512-B2tp1LI9D8GVpPDqprdt12ukTkXXahj9MzqSWJq4faSPgOUM7CdZaGAB6HrL/8E6UwbBpdj4vzWDbKoFnVoU6g==", "dev": true, "license": "MIT", "dependencies": { diff --git a/plugins/internal-comments/composer.lock b/plugins/internal-comments/composer.lock index 549309e..cd939fa 100644 --- a/plugins/internal-comments/composer.lock +++ b/plugins/internal-comments/composer.lock @@ -370,7 +370,8 @@ "support": { "source": "https://github.com/Automattic/jetpack-changelogger/tree/v7.0.5" }, - "time": "2026-10-20T16:01:37+00:00" + "time": "2026-10-20T16:01:37+00:00", + "published-time": "2026-10-20T16:01:50+00:00" }, { "name": "composer/class-map-generator", @@ -439,7 +440,8 @@ "type": "github" } ], - "time": "2026-09-25T15:24:19+00:00" + "time": "2026-09-25T15:24:19+00:00", + "published-time": "2026-09-25T15:27:30+00:00" }, { "name": "composer/installers", @@ -585,7 +587,8 @@ "type": "tidelift" } ], - "time": "2024-06-24T20:46:46+00:00" + "time": "2024-06-24T20:46:46+00:00", + "published-time": "2024-06-24T21:01:36+00:00" }, { "name": "composer/pcre", @@ -661,7 +664,8 @@ "type": "github" } ], - "time": "2026-06-07T11:47:49+00:00" + "time": "2026-06-07T11:47:49+00:00", + "published-time": "2026-06-11T12:12:37+00:00" }, { "name": "dealerdirect/phpcodesniffer-composer-installer", @@ -757,7 +761,8 @@ "type": "thanks_dev" } ], - "time": "2026-05-06T08:26:05+00:00" + "time": "2026-05-06T08:26:05+00:00", + "published-time": "2026-05-06T12:56:03+00:00" }, { "name": "fidry/console", @@ -843,7 +848,8 @@ "type": "github" } ], - "time": "2025-02-14T11:06:15+00:00" + "time": "2025-02-14T11:06:15+00:00", + "published-time": "2025-02-14T11:09:06+00:00" }, { "name": "fidry/filesystem", @@ -909,7 +915,8 @@ "type": "github" } ], - "time": "2025-02-13T23:05:19+00:00" + "time": "2025-02-13T23:05:19+00:00", + "published-time": "2025-02-13T23:09:29+00:00" }, { "name": "humbug/php-scoper", @@ -995,7 +1002,8 @@ "issues": "https://github.com/humbug/php-scoper/issues", "source": "https://github.com/humbug/php-scoper/tree/0.18.20" }, - "time": "2026-10-08T08:21:05+00:00" + "time": "2026-10-08T08:21:05+00:00", + "published-time": "2026-10-08T08:29:48+00:00" }, { "name": "jetbrains/phpstorm-stubs", @@ -1039,7 +1047,8 @@ "stubs", "type" ], - "time": "2026-06-12T13:19:10+00:00" + "time": "2026-06-12T13:19:10+00:00", + "published-time": "2026-07-22T11:41:13+00:00" }, { "name": "johnbillion/wp-compat", @@ -1114,7 +1123,8 @@ "type": "github" } ], - "time": "2026-10-21T20:54:48+00:00" + "time": "2026-10-21T20:54:48+00:00", + "published-time": "2026-10-21T20:54:55+00:00" }, { "name": "johnpbloch/wordpress-core", @@ -1162,7 +1172,8 @@ "source": "https://core.trac.wordpress.org/browser", "wiki": "https://codex.wordpress.org/" }, - "time": "2026-10-28T19:49:26+00:00" + "time": "2026-10-28T19:49:26+00:00", + "published-time": "2026-10-28T19:49:51+00:00" }, { "name": "laravel/serializable-closure", @@ -1223,7 +1234,8 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2026-09-22T14:32:34+00:00" + "time": "2026-09-22T14:32:34+00:00", + "published-time": "2026-09-22T14:33:13+00:00" }, { "name": "myclabs/deep-copy", @@ -1283,7 +1295,8 @@ "type": "github" } ], - "time": "2026-08-11T10:17:44+00:00" + "time": "2026-08-11T10:17:44+00:00", + "published-time": "2026-08-11T10:29:20+00:00" }, { "name": "nikic/php-parser", @@ -1340,7 +1353,8 @@ "issues": "https://github.com/nikic/PHP-Parser/issues", "source": "https://github.com/nikic/PHP-Parser/tree/v5.9.0" }, - "time": "2026-09-13T18:51:52+00:00" + "time": "2026-09-13T18:51:52+00:00", + "published-time": "2026-09-13T18:53:16+00:00" }, { "name": "phar-io/manifest", @@ -1407,7 +1421,8 @@ "type": "github" } ], - "time": "2024-03-03T12:33:53+00:00" + "time": "2024-03-03T12:33:53+00:00", + "published-time": "2024-03-03T12:39:44+00:00" }, { "name": "phar-io/version", @@ -1458,7 +1473,8 @@ "issues": "https://github.com/phar-io/version/issues", "source": "https://github.com/phar-io/version/tree/3.2.1" }, - "time": "2022-02-21T01:04:05+00:00" + "time": "2022-02-21T01:04:05+00:00", + "published-time": "2022-02-21T01:04:30+00:00" }, { "name": "php-di/invoker", @@ -1513,7 +1529,8 @@ "type": "github" } ], - "time": "2025-08-30T10:22:22+00:00" + "time": "2025-08-30T10:22:22+00:00", + "published-time": "2025-08-30T10:22:43+00:00" }, { "name": "php-di/php-di", @@ -1586,7 +1603,8 @@ "type": "tidelift" } ], - "time": "2025-08-16T11:10:48+00:00" + "time": "2025-08-16T11:10:48+00:00", + "published-time": "2025-08-16T11:11:08+00:00" }, { "name": "php-stubs/woocommerce-stubs", @@ -1630,7 +1648,8 @@ "issues": "https://github.com/php-stubs/woocommerce-stubs/issues", "source": "https://github.com/php-stubs/woocommerce-stubs/tree/v11.2.0" }, - "time": "2026-10-07T19:02:59+00:00" + "time": "2026-10-07T19:02:59+00:00", + "published-time": "2026-10-07T19:04:59+00:00" }, { "name": "php-stubs/wordpress-stubs", @@ -1682,7 +1701,8 @@ "issues": "https://github.com/php-stubs/wordpress-stubs/issues", "source": "https://github.com/php-stubs/wordpress-stubs/tree/v7.1.2" }, - "time": "2026-10-06T06:59:21+00:00" + "time": "2026-10-06T06:59:21+00:00", + "published-time": "2026-10-06T07:32:21+00:00" }, { "name": "phpcompatibility/php-compatibility", @@ -1772,7 +1792,8 @@ "type": "thanks_dev" } ], - "time": "2025-11-28T11:36:33+00:00" + "time": "2025-11-28T11:36:33+00:00", + "published-time": "2025-11-28T11:41:33+00:00" }, { "name": "phpcompatibility/phpcompatibility-paragonie", @@ -1843,7 +1864,8 @@ "type": "thanks_dev" } ], - "time": "2025-11-29T13:09:49+00:00" + "time": "2025-11-29T13:09:49+00:00", + "published-time": "2025-11-29T13:22:17+00:00" }, { "name": "phpcompatibility/phpcompatibility-wp", @@ -1910,7 +1932,8 @@ "type": "thanks_dev" } ], - "time": "2025-12-16T13:35:20+00:00" + "time": "2025-12-16T13:35:20+00:00", + "published-time": "2025-12-16T13:47:29+00:00" }, { "name": "phpcsstandards/phpcsextra", @@ -1992,7 +2015,8 @@ "type": "thanks_dev" } ], - "time": "2026-07-27T11:13:17+00:00" + "time": "2026-07-27T11:13:17+00:00", + "published-time": "2026-07-27T11:19:59+00:00" }, { "name": "phpcsstandards/phpcsutils", @@ -2085,7 +2109,8 @@ "type": "thanks_dev" } ], - "time": "2026-07-27T10:28:41+00:00" + "time": "2026-07-27T10:28:41+00:00", + "published-time": "2026-07-27T10:36:31+00:00" }, { "name": "phpstan/extension-installer", @@ -2133,7 +2158,8 @@ "issues": "https://github.com/phpstan/extension-installer/issues", "source": "https://github.com/phpstan/extension-installer/tree/1.4.3" }, - "time": "2024-09-04T20:21:43+00:00" + "time": "2024-09-04T20:21:43+00:00", + "published-time": "2024-09-04T20:22:27+00:00" }, { "name": "phpstan/phpstan", @@ -2197,7 +2223,8 @@ "type": "github" } ], - "time": "2026-10-28T22:38:53+00:00" + "time": "2026-10-28T22:38:53+00:00", + "published-time": "2026-10-28T22:39:06+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", @@ -2248,7 +2275,8 @@ "issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues", "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/2.0.5" }, - "time": "2026-07-22T06:50:43+00:00" + "time": "2026-07-22T06:50:43+00:00", + "published-time": "2026-07-27T15:18:00+00:00" }, { "name": "phpstan/phpstan-strict-rules", @@ -2300,7 +2328,8 @@ "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.1.0" }, - "time": "2026-10-01T21:30:37+00:00" + "time": "2026-10-01T21:30:37+00:00", + "published-time": "2026-10-06T07:32:29+00:00" }, { "name": "phpunit/php-code-coverage", @@ -2389,7 +2418,8 @@ "type": "tidelift" } ], - "time": "2026-10-30T08:11:15+00:00" + "time": "2026-10-30T08:11:15+00:00", + "published-time": "2026-10-30T08:11:34+00:00" }, { "name": "phpunit/php-file-iterator", @@ -2462,7 +2492,8 @@ "type": "tidelift" } ], - "time": "2026-08-25T14:47:43+00:00" + "time": "2026-08-25T14:47:43+00:00", + "published-time": "2026-08-25T14:48:49+00:00" }, { "name": "phpunit/php-invoker", @@ -2538,7 +2569,8 @@ "type": "tidelift" } ], - "time": "2026-02-06T04:34:47+00:00" + "time": "2026-02-06T04:34:47+00:00", + "published-time": "2026-02-06T04:35:29+00:00" }, { "name": "phpunit/php-text-template", @@ -2610,7 +2642,8 @@ "type": "tidelift" } ], - "time": "2026-10-03T04:54:24+00:00" + "time": "2026-10-03T04:54:24+00:00", + "published-time": "2026-10-03T04:54:39+00:00" }, { "name": "phpunit/php-timer", @@ -2682,7 +2715,8 @@ "type": "tidelift" } ], - "time": "2026-02-06T04:37:53+00:00" + "time": "2026-02-06T04:37:53+00:00", + "published-time": "2026-02-06T04:38:39+00:00" }, { "name": "phpunit/phpunit", @@ -2774,7 +2808,8 @@ "type": "other" } ], - "time": "2026-10-27T04:16:22+00:00" + "time": "2026-10-27T04:16:22+00:00", + "published-time": "2026-10-27T04:18:40+00:00" }, { "name": "pronamic/changelog-md-to-wordpress-plugin-readme-txt", @@ -2814,7 +2849,8 @@ "issues": "https://github.com/pronamic/pronamic-changelog-md-to-wordpress-plugin-readme-txt/issues", "source": "https://github.com/pronamic/pronamic-changelog-md-to-wordpress-plugin-readme-txt/tree/v1.0.0" }, - "time": "2024-10-01T13:41:34+00:00" + "time": "2024-10-01T13:41:34+00:00", + "published-time": "2024-10-02T09:31:51+00:00" }, { "name": "psr/clock", @@ -2862,7 +2898,8 @@ "issues": "https://github.com/php-fig/clock/issues", "source": "https://github.com/php-fig/clock/tree/1.0.0" }, - "time": "2022-11-25T14:36:26+00:00" + "time": "2022-11-25T14:36:26+00:00", + "published-time": "2022-11-25T14:59:22+00:00" }, { "name": "psr/container", @@ -2915,7 +2952,8 @@ "issues": "https://github.com/php-fig/container/issues", "source": "https://github.com/php-fig/container/tree/2.0.2" }, - "time": "2021-11-05T16:47:00+00:00" + "time": "2021-11-05T16:47:00+00:00", + "published-time": "2021-11-05T16:53:27+00:00" }, { "name": "psr/event-dispatcher", @@ -2965,7 +3003,8 @@ "issues": "https://github.com/php-fig/event-dispatcher/issues", "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" }, - "time": "2019-01-08T18:20:26+00:00" + "time": "2019-01-08T18:20:26+00:00", + "published-time": "2019-03-25T15:31:44+00:00" }, { "name": "psr/log", @@ -3015,7 +3054,8 @@ "support": { "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "time": "2024-09-11T13:17:53+00:00" + "time": "2024-09-11T13:17:53+00:00", + "published-time": "2024-09-11T13:21:14+00:00" }, { "name": "psr/simple-cache", @@ -3066,7 +3106,8 @@ "support": { "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" }, - "time": "2021-10-29T13:26:27+00:00" + "time": "2021-10-29T13:26:27+00:00", + "published-time": "2021-10-29T13:27:19+00:00" }, { "name": "roave/security-advisories", @@ -3074,12 +3115,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "7fd2aa4f9a2f0db3e04724273d5121c1d16a6c66" + "reference": "7c964bc969c1b16050a4b85b7a55614777a3f597" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/7fd2aa4f9a2f0db3e04724273d5121c1d16a6c66", - "reference": "7fd2aa4f9a2f0db3e04724273d5121c1d16a6c66", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/7c964bc969c1b16050a4b85b7a55614777a3f597", + "reference": "7c964bc969c1b16050a4b85b7a55614777a3f597", "shasum": "" }, "conflict": { @@ -3087,6 +3128,7 @@ "adaptcms/adaptcms": "<=1.3", "adawolfa/isdoc": "<1.4.3|>=1.5,<1.5.1|>=1.6,<1.6.1", "admidio/admidio": "<=5.0.11", + "adminneo-org/adminneo": "<5.5.1", "adodb/adodb-php": "<=5.22.9", "aheinze/cockpit": "<2.2", "aimeos/ai-admin-graphql": ">=2022.04.1,<2022.10.10|>=2023.04.1,<2023.10.6|>=2024.04.1,<2024.07.2", @@ -4235,7 +4277,8 @@ "type": "tidelift" } ], - "time": "2026-10-29T16:37:57+00:00" + "time": "2026-10-31T00:24:47+00:00", + "published-time": "2026-10-31T00:24:51+00:00" }, { "name": "sebastian/cli-parser", @@ -4304,7 +4347,8 @@ "type": "tidelift" } ], - "time": "2026-08-01T04:27:14+00:00" + "time": "2026-08-01T04:27:14+00:00", + "published-time": "2026-08-01T04:29:01+00:00" }, { "name": "sebastian/comparator", @@ -4396,7 +4440,8 @@ "type": "tidelift" } ], - "time": "2026-08-07T07:23:13+00:00" + "time": "2026-08-07T07:23:13+00:00", + "published-time": "2026-08-07T07:23:29+00:00" }, { "name": "sebastian/complexity", @@ -4466,7 +4511,8 @@ "type": "tidelift" } ], - "time": "2026-02-06T04:41:32+00:00" + "time": "2026-02-06T04:41:32+00:00", + "published-time": "2026-02-06T04:41:49+00:00" }, { "name": "sebastian/diff", @@ -4545,7 +4591,8 @@ "type": "tidelift" } ], - "time": "2026-08-25T15:38:55+00:00" + "time": "2026-08-25T15:38:55+00:00", + "published-time": "2026-08-25T15:39:44+00:00" }, { "name": "sebastian/environment", @@ -4621,7 +4668,8 @@ "type": "tidelift" } ], - "time": "2026-10-01T12:37:56+00:00" + "time": "2026-10-01T12:37:56+00:00", + "published-time": "2026-10-01T12:38:48+00:00" }, { "name": "sebastian/exporter", @@ -4711,7 +4759,8 @@ "type": "tidelift" } ], - "time": "2026-08-07T07:22:06+00:00" + "time": "2026-08-07T07:22:06+00:00", + "published-time": "2026-08-07T07:23:47+00:00" }, { "name": "sebastian/file-filter", @@ -4780,7 +4829,8 @@ "type": "tidelift" } ], - "time": "2026-04-22T07:20:04+00:00" + "time": "2026-04-22T07:20:04+00:00", + "published-time": "2026-04-22T07:20:25+00:00" }, { "name": "sebastian/git-state", @@ -4849,7 +4899,8 @@ "type": "tidelift" } ], - "time": "2026-10-21T05:59:52+00:00" + "time": "2026-10-21T05:59:52+00:00", + "published-time": "2026-10-21T06:01:28+00:00" }, { "name": "sebastian/global-state", @@ -4923,7 +4974,8 @@ "type": "tidelift" } ], - "time": "2026-06-01T15:11:33+00:00" + "time": "2026-06-01T15:11:33+00:00", + "published-time": "2026-06-01T15:20:45+00:00" }, { "name": "sebastian/lines-of-code", @@ -4993,7 +5045,8 @@ "type": "tidelift" } ], - "time": "2026-07-09T08:42:34+00:00" + "time": "2026-07-09T08:42:34+00:00", + "published-time": "2026-07-09T08:44:06+00:00" }, { "name": "sebastian/object-enumerator", @@ -5062,7 +5115,8 @@ "type": "tidelift" } ], - "time": "2026-08-13T07:05:05+00:00" + "time": "2026-08-13T07:05:05+00:00", + "published-time": "2026-08-13T07:07:58+00:00" }, { "name": "sebastian/object-reflector", @@ -5130,7 +5184,8 @@ "type": "tidelift" } ], - "time": "2026-08-13T06:34:36+00:00" + "time": "2026-08-13T06:34:36+00:00", + "published-time": "2026-08-13T06:39:22+00:00" }, { "name": "sebastian/recursion-context", @@ -5206,7 +5261,8 @@ "type": "tidelift" } ], - "time": "2026-08-03T05:58:12+00:00" + "time": "2026-08-03T05:58:12+00:00", + "published-time": "2026-08-03T05:59:59+00:00" }, { "name": "sebastian/type", @@ -5275,7 +5331,8 @@ "type": "tidelift" } ], - "time": "2026-08-10T08:00:57+00:00" + "time": "2026-08-10T08:00:57+00:00", + "published-time": "2026-08-10T08:01:43+00:00" }, { "name": "sebastian/version", @@ -5341,7 +5398,8 @@ "type": "tidelift" } ], - "time": "2026-10-04T05:51:14+00:00" + "time": "2026-10-04T05:51:14+00:00", + "published-time": "2026-10-04T05:52:22+00:00" }, { "name": "sniccowp/php-scoper-wordpress-excludes", @@ -5388,7 +5446,8 @@ "issues": "https://github.com/snicco/php-scoper-wordpress-excludes/issues", "source": "https://github.com/snicco/php-scoper-wordpress-excludes/tree/7.1.0" }, - "time": "2026-08-28T02:50:05+00:00" + "time": "2026-08-28T02:50:05+00:00", + "published-time": "2026-09-23T19:33:26+00:00" }, { "name": "squizlabs/php_codesniffer", @@ -5467,7 +5526,8 @@ "type": "thanks_dev" } ], - "time": "2026-08-06T00:17:32+00:00" + "time": "2026-08-06T00:17:32+00:00", + "published-time": "2026-08-06T01:05:02+00:00" }, { "name": "staabm/side-effects-detector", @@ -5519,7 +5579,8 @@ "type": "github" } ], - "time": "2024-10-20T05:08:20+00:00" + "time": "2024-10-20T05:08:20+00:00", + "published-time": "2024-10-20T05:09:39+00:00" }, { "name": "swissspidy/phpstan-no-private", @@ -5571,7 +5632,8 @@ "issues": "https://github.com/swissspidy/phpstan-no-private/issues", "source": "https://github.com/swissspidy/phpstan-no-private/tree/v1.0.0" }, - "time": "2024-11-11T11:04:45+00:00" + "time": "2024-11-11T11:04:45+00:00", + "published-time": "2024-11-11T11:08:03+00:00" }, { "name": "symfony/console", @@ -5669,7 +5731,8 @@ "type": "tidelift" } ], - "time": "2026-10-28T11:08:27+00:00" + "time": "2026-10-28T11:08:27+00:00", + "published-time": "2026-10-28T18:21:33+00:00" }, { "name": "symfony/deprecation-contracts", @@ -5740,7 +5803,8 @@ "type": "tidelift" } ], - "time": "2026-06-05T06:23:12+00:00" + "time": "2026-06-05T06:23:12+00:00", + "published-time": "2026-06-27T21:46:34+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -5820,7 +5884,8 @@ "type": "tidelift" } ], - "time": "2026-06-05T06:23:12+00:00" + "time": "2026-06-05T06:23:12+00:00", + "published-time": "2026-06-27T21:46:35+00:00" }, { "name": "symfony/filesystem", @@ -5890,7 +5955,8 @@ "type": "tidelift" } ], - "time": "2026-10-06T20:05:19+00:00" + "time": "2026-10-06T20:05:19+00:00", + "published-time": "2026-10-14T06:45:33+00:00" }, { "name": "symfony/finder", @@ -5958,7 +6024,8 @@ "type": "tidelift" } ], - "time": "2026-10-19T16:56:54+00:00" + "time": "2026-10-19T16:56:54+00:00", + "published-time": "2026-10-28T18:21:14+00:00" }, { "name": "symfony/polyfill-ctype", @@ -6041,7 +6108,8 @@ "type": "tidelift" } ], - "time": "2026-04-10T16:19:22+00:00" + "time": "2026-04-10T16:19:22+00:00", + "published-time": "2026-04-26T14:44:50+00:00" }, { "name": "symfony/polyfill-iconv", @@ -6125,7 +6193,8 @@ "type": "tidelift" } ], - "time": "2026-09-29T06:25:14+00:00" + "time": "2026-09-29T06:25:14+00:00", + "published-time": "2026-09-30T10:23:39+00:00" }, { "name": "symfony/polyfill-intl-grapheme", @@ -6207,7 +6276,8 @@ "type": "tidelift" } ], - "time": "2026-10-15T09:18:53+00:00" + "time": "2026-10-15T09:18:53+00:00", + "published-time": "2026-10-28T10:07:44+00:00" }, { "name": "symfony/polyfill-intl-normalizer", @@ -6292,7 +6362,8 @@ "type": "tidelift" } ], - "time": "2026-09-23T22:02:09+00:00" + "time": "2026-09-23T22:02:09+00:00", + "published-time": "2026-09-30T10:23:47+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -6377,7 +6448,8 @@ "type": "tidelift" } ], - "time": "2026-10-28T09:35:49+00:00" + "time": "2026-10-28T09:35:49+00:00", + "published-time": "2026-10-28T10:07:45+00:00" }, { "name": "symfony/polyfill-php84", @@ -6457,7 +6529,8 @@ "type": "tidelift" } ], - "time": "2026-09-23T22:01:09+00:00" + "time": "2026-09-23T22:01:09+00:00", + "published-time": "2026-09-30T10:23:33+00:00" }, { "name": "symfony/process", @@ -6522,7 +6595,8 @@ "type": "tidelift" } ], - "time": "2026-10-25T14:40:16+00:00" + "time": "2026-10-25T14:40:16+00:00", + "published-time": "2026-10-28T18:21:18+00:00" }, { "name": "symfony/service-contracts", @@ -6609,7 +6683,8 @@ "type": "tidelift" } ], - "time": "2026-07-27T15:39:01+00:00" + "time": "2026-07-27T15:39:01+00:00", + "published-time": "2026-08-30T19:56:37+00:00" }, { "name": "symfony/string", @@ -6699,7 +6774,8 @@ "type": "tidelift" } ], - "time": "2026-10-23T17:06:53+00:00" + "time": "2026-10-23T17:06:53+00:00", + "published-time": "2026-10-28T18:24:27+00:00" }, { "name": "symfony/var-dumper", @@ -6786,7 +6862,8 @@ "type": "tidelift" } ], - "time": "2026-10-25T21:04:04+00:00" + "time": "2026-10-25T21:04:04+00:00", + "published-time": "2026-10-28T18:20:00+00:00" }, { "name": "szepeviktor/phpstan-wordpress", @@ -6849,7 +6926,8 @@ "issues": "https://github.com/szepeviktor/phpstan-wordpress/issues", "source": "https://github.com/szepeviktor/phpstan-wordpress/tree/v2.0.4" }, - "time": "2026-05-22T16:22:09+00:00" + "time": "2026-05-22T16:22:09+00:00", + "published-time": "2026-08-28T06:48:49+00:00" }, { "name": "thecodingmachine/safe", @@ -6992,7 +7070,8 @@ "type": "github" } ], - "time": "2026-02-04T18:08:13+00:00" + "time": "2026-02-04T18:08:13+00:00", + "published-time": "2026-02-15T15:48:29+00:00" }, { "name": "webmozart/assert", @@ -7050,7 +7129,8 @@ "issues": "https://github.com/webmozarts/assert/issues", "source": "https://github.com/webmozarts/assert/tree/1.12.1" }, - "time": "2025-10-29T15:56:20+00:00" + "time": "2025-10-29T15:56:20+00:00", + "published-time": "2025-10-29T15:56:42+00:00" }, { "name": "wp-coding-standards/wpcs", @@ -7116,7 +7196,8 @@ "type": "custom" } ], - "time": "2026-07-27T11:53:23+00:00" + "time": "2026-07-27T11:53:23+00:00", + "published-time": "2026-07-27T11:55:52+00:00" }, { "name": "wp-hooks/wordpress-core", @@ -7199,7 +7280,8 @@ "type": "github" } ], - "time": "2026-08-20T19:54:29+00:00" + "time": "2026-08-20T19:54:29+00:00", + "published-time": "2026-08-20T19:54:42+00:00" }, { "name": "wp-plugin/woocommerce", @@ -7230,7 +7312,8 @@ "issues": "https://wordpress.org/support/plugin/woocommerce", "source": "https://plugins.svn.wordpress.org/woocommerce" }, - "time": "2026-10-09T15:35:21+00:00" + "time": "2026-10-09T15:35:21+00:00", + "published-time": "2026-10-09T15:35:21+00:00" } ], "aliases": [], @@ -7244,5 +7327,5 @@ "php": ">=8.5" }, "platform-dev": {}, - "plugin-api-version": "2.9.0" + "plugin-api-version": "2.11.0" } diff --git a/plugins/internal-comments/package-lock.json b/plugins/internal-comments/package-lock.json index 5ce1977..99a817c 100644 --- a/plugins/internal-comments/package-lock.json +++ b/plugins/internal-comments/package-lock.json @@ -391,9 +391,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.11.tgz", + "integrity": "sha512-snZbO7MEDOof+hvNa97EjEY7qD7h2kwvQr5AJC3ZhQudKW5dnej6k3kP9NQV88hJwaS8vDqRLFo9rivLEhle2g==", "dev": true, "license": "MIT", "engines": { @@ -450,13 +450,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.29.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz", - "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.11.tgz", + "integrity": "sha512-OZ6+oaK7GMyg3IfCpj8JOmn8n+w2rJbDT0LLfVDTLb1ZFeKDh3AiNELgCMUw175IJHSkLl74hG0M0VLsvx+gag==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.29.8" + "@babel/types": "^7.29.11" }, "bin": { "parser": "bin/babel-parser.js" @@ -729,9 +729,9 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.29.7.tgz", - "integrity": "sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.29.11.tgz", + "integrity": "sha512-iARF4jjHSM0v8DImCACPXrwEltBP3DgqDpXTvjAMBAVpyEY0LiGss78wvI+KtmW+IJNEvZ5sOhPDCsOsXsIybA==", "dev": true, "license": "MIT", "dependencies": { @@ -779,9 +779,9 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.7.tgz", - "integrity": "sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.11.tgz", + "integrity": "sha512-f8kTDM/nTvpLeafb+TJ4jboFr/Y3BbC+QzgPpPGzSKJqWLnj9LW0WLqv4XKA11LX5sr+EgvLE6Ix3YsCEusodw==", "dev": true, "license": "MIT", "dependencies": { @@ -790,7 +790,7 @@ "@babel/helper-globals": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-replace-supers": "^7.29.7", - "@babel/traverse": "^7.29.7" + "@babel/traverse": "^7.29.10" }, "engines": { "node": ">=6.9.0" @@ -1515,9 +1515,9 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.8.tgz", - "integrity": "sha512-4S9ksMGVWUshvgK0mKfvZky7leuG5/uoFVwMpAomJ8bMoDJiNHRVmc1EglwW/CmGVSqqWpEbXm9FmbRit22qoA==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.11.tgz", + "integrity": "sha512-5xWGhfuMgbOfIjsk/I2c4uMnoqRwQdKYi4QjvybQrcEVE0G9/p3qmIkp2Sxe8YF8DGNNVs5whfwNqIM5684XoQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1853,13 +1853,13 @@ } }, "node_modules/@babel/types": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", - "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.11.tgz", + "integrity": "sha512-WCsemW40Cpc6gouC1SrY6PBaZ+oLRlEVY9XBvZBI7il/HyUrb2nJ505wE1MHEFyDZNnrWDEuy4jL50WVHfTTSQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-string-parser": "^7.29.11", "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { @@ -6456,9 +6456,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.13.tgz", - "integrity": "sha512-2E8Q6rh+1RRSq/YdaGsNfXsEZy9FVqv6BSSrkmZJP7BnaoPnRfKL+YaofPvP+5rzdk144qSm6i4+hvPgUcbV4Q==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.14.tgz", + "integrity": "sha512-wCBiZ77u1tuWde6G/nfOtLaCTYcFDU5yCzARzK10y+rLhzYoL7a7yEemcopcc2Mzow7gZOIjnV2ym1NYmdiJOQ==", "dev": true, "license": "MIT", "engines": { @@ -8912,9 +8912,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.11.31", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.31.tgz", - "integrity": "sha512-b0k7wXlWjpuL/Uxb/pYlpjY0ej5BjAmurtPUk3eRGuloI4yjK32m8TiKUQ81l1ptcLjKS/KH2+uqIolZti1x4Q==", + "version": "2.11.32", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.32.tgz", + "integrity": "sha512-UvuxFU9P3hM29LrQdwu0cD6w6/+SFr+UlB9s/knI7+O2rK9xFxKUwSDWxGdbbqQ9/V7BJ5s0NI5nQAE4uJp3gQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -10929,9 +10929,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.463", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.463.tgz", - "integrity": "sha512-nVWNU6ltFjopvOdD44J4zoBkgO0M+WZiFKKFV7EjebOH9z0KNphk6bCEjplXtJofL21ARIuuwWOCsFkBVklm6w==", + "version": "1.5.464", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.464.tgz", + "integrity": "sha512-nRa65aFge40viPAKxMKULIY8tcXKwKboJWNn4RoKwyWXJ6SP7BnrDGKUTzc64818jIciH0PK7LSF8QzZes19GA==", "dev": true, "license": "ISC" }, @@ -12257,13 +12257,6 @@ "dev": true, "license": "MIT" }, - "node_modules/express/node_modules/path-to-regexp": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", - "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", - "dev": true, - "license": "MIT" - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -13349,9 +13342,9 @@ } }, "node_modules/http-proxy-middleware": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-4.2.0.tgz", - "integrity": "sha512-ZA+oNOoM+GLoFTIzhkJptVQov73Srep2LBqhF8hG8CIPKO3nam1jonXVQ/QUH8RbwsmaaVz2SOJdzBNBHNtKbw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-4.2.1.tgz", + "integrity": "sha512-60SmYZpPD4dPbC2GZVkVV4uP/V/cbsXSwzGQXeHWbbE3Q1XdccJpqHTg6NJ6iMpFGVA1OEry8c8kHhx4ahiU1w==", "dev": true, "license": "MIT", "dependencies": { @@ -14834,9 +14827,9 @@ } }, "node_modules/legacy-javascript": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/legacy-javascript/-/legacy-javascript-0.0.3.tgz", - "integrity": "sha512-1r6MT4dVaJyXt4G07Cm2cQ0AUaLJdefiEp4SmIAylA++gnEQ82YvmzE58RkM5EP0fk5hROLCslUPGnwIYdZQVA==", + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/legacy-javascript/-/legacy-javascript-0.0.4.tgz", + "integrity": "sha512-UTmeKBVxnfJcpmpOvzzNnLsQT99BXHpPlc/ZKddIRmWmrR0fw6335I3lq4+sJoP60UTbBNaYsGYVPc65YB1x3A==", "dev": true, "license": "Apache-2.0" }, @@ -15310,9 +15303,9 @@ "license": "Python-2.0" }, "node_modules/markdownlint-cli/node_modules/ignore": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.13.tgz", - "integrity": "sha512-2E8Q6rh+1RRSq/YdaGsNfXsEZy9FVqv6BSSrkmZJP7BnaoPnRfKL+YaofPvP+5rzdk144qSm6i4+hvPgUcbV4Q==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.14.tgz", + "integrity": "sha512-wCBiZ77u1tuWde6G/nfOtLaCTYcFDU5yCzARzK10y+rLhzYoL7a7yEemcopcc2Mzow7gZOIjnV2ym1NYmdiJOQ==", "dev": true, "license": "MIT", "engines": { @@ -17433,6 +17426,13 @@ "dev": true, "license": "ISC" }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "dev": true, + "license": "MIT" + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -18440,13 +18440,6 @@ "react-is": "^16.13.1" } }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, - "license": "MIT" - }, "node_modules/proxy-addr": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", @@ -18707,6 +18700,13 @@ "react": "^19.3.1" } }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, + "license": "MIT" + }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", @@ -20909,9 +20909,9 @@ } }, "node_modules/stylelint/node_modules/ignore": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.13.tgz", - "integrity": "sha512-2E8Q6rh+1RRSq/YdaGsNfXsEZy9FVqv6BSSrkmZJP7BnaoPnRfKL+YaofPvP+5rzdk144qSm6i4+hvPgUcbV4Q==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.14.tgz", + "integrity": "sha512-wCBiZ77u1tuWde6G/nfOtLaCTYcFDU5yCzARzK10y+rLhzYoL7a7yEemcopcc2Mzow7gZOIjnV2ym1NYmdiJOQ==", "dev": true, "license": "MIT", "engines": { @@ -22164,9 +22164,9 @@ "license": "Apache-2.0" }, "node_modules/webpack": { - "version": "5.112.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.112.1.tgz", - "integrity": "sha512-DsEgOfaY5XrvTTEU7QvktlOpYEipgNUyXQY4rQO4KtFmr71IwjMoRWyv9rJNo6qwqctRzh9sO9VIwMgnWmiBVg==", + "version": "5.112.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.112.2.tgz", + "integrity": "sha512-B2tp1LI9D8GVpPDqprdt12ukTkXXahj9MzqSWJq4faSPgOUM7CdZaGAB6HrL/8E6UwbBpdj4vzWDbKoFnVoU6g==", "dev": true, "license": "MIT", "dependencies": { diff --git a/plugins/linked-orders-for-woocommerce/composer.lock b/plugins/linked-orders-for-woocommerce/composer.lock index f6057e6..01c7822 100644 --- a/plugins/linked-orders-for-woocommerce/composer.lock +++ b/plugins/linked-orders-for-woocommerce/composer.lock @@ -12,7 +12,7 @@ "version": "dev-trunk", "source": { "type": "git", - "url": "git@github.com:ahegyes/wordpress-configs.git", + "url": "https://github.com/ahegyes/wordpress-configs.git", "reference": "8d53efd292e4abee4a9c57b0e379434f54bedbe9" }, "dist": { @@ -135,6 +135,10 @@ } ], "description": "Shared PHPCS, PHPStan and php-scoper configurations and reusable GitHub Actions workflows for WordPress projects.", + "support": { + "source": "https://github.com/ahegyes/wordpress-configs/tree/trunk", + "issues": "https://github.com/ahegyes/wordpress-configs/issues" + }, "time": "2026-10-30T11:34:20+00:00" }, { @@ -142,7 +146,7 @@ "version": "dev-trunk", "source": { "type": "git", - "url": "git@github.com:ahegyes/wp-framework-bootstrap.git", + "url": "https://github.com/ahegyes/wp-framework-bootstrap.git", "reference": "6f48ed4ed852ba35a06ef46dcfc104170732a189" }, "dist": { @@ -167,14 +171,17 @@ ], "description": "The code a WordPress plugin runs before its autoloader. It parses on PHP 7.4, so a site below the plugin's floors gets a notice instead of a fatal error and still receives the release that fixes it.", "homepage": "https://github.com/ahegyes/wordpress-framework", - "time": "2026-10-30T10:27:50+00:00" + "support": { + "source": "https://github.com/ahegyes/wp-framework-bootstrap/tree/trunk" + }, + "time": "2026-10-30T10:32:53+00:00" }, { "name": "ahegyes/wp-framework-core", "version": "dev-trunk", "source": { "type": "git", - "url": "git@github.com:ahegyes/wp-framework-core.git", + "url": "https://github.com/ahegyes/wp-framework-core.git", "reference": "6d087554f21ccc65ac52583d8d9e7de542c84cfd" }, "dist": { @@ -212,14 +219,17 @@ ], "description": "The plugin kernel. A plugin is composed of small components that load only where they apply, on WordPress's own APIs, with the infrastructure WordPress leaves every plugin to rebuild.", "homepage": "https://github.com/ahegyes/wordpress-framework", - "time": "2026-10-30T10:27:50+00:00" + "support": { + "source": "https://github.com/ahegyes/wp-framework-core/tree/trunk" + }, + "time": "2026-10-30T10:32:53+00:00" }, { "name": "ahegyes/wp-framework-settings", "version": "dev-trunk", "source": { "type": "git", - "url": "git@github.com:ahegyes/wp-framework-settings.git", + "url": "https://github.com/ahegyes/wp-framework-settings.git", "reference": "5942e878af9f8536698c724116ceef5b50948e24" }, "dist": { @@ -251,6 +261,9 @@ ], "description": "Settings screens and edit-screen fields declared as data, so WordPress's own APIs render, validate and save them and a plugin never hand-writes form handling.", "homepage": "https://github.com/ahegyes/wordpress-framework", + "support": { + "source": "https://github.com/ahegyes/wp-framework-settings/tree/trunk" + }, "time": "2026-10-30T10:12:55+00:00" }, { @@ -258,7 +271,7 @@ "version": "dev-trunk", "source": { "type": "git", - "url": "git@github.com:ahegyes/wp-framework-shared.git", + "url": "https://github.com/ahegyes/wp-framework-shared.git", "reference": "72568a6fd790b49926f6f74e4dd2478e31d91b8a" }, "dist": { @@ -287,6 +300,9 @@ ], "description": "The WordPress-free building blocks every framework package shares, kept portable enough to work unchanged in any PHP project.", "homepage": "https://github.com/ahegyes/wordpress-framework", + "support": { + "source": "https://github.com/ahegyes/wp-framework-shared/tree/trunk" + }, "time": "2026-10-30T10:12:55+00:00" }, { @@ -294,7 +310,7 @@ "version": "dev-trunk", "source": { "type": "git", - "url": "git@github.com:ahegyes/wp-framework-woocommerce.git", + "url": "https://github.com/ahegyes/wp-framework-woocommerce.git", "reference": "9dd04e7c2c0e99160a231ce3a0f17a443a492631" }, "dist": { @@ -332,6 +348,9 @@ ], "description": "The framework's settings, edit-screen and logging helpers carried onto WooCommerce's own screens and APIs, for plugins that extend WooCommerce.", "homepage": "https://github.com/ahegyes/wordpress-framework", + "support": { + "source": "https://github.com/ahegyes/wp-framework-woocommerce/tree/trunk" + }, "time": "2026-10-30T10:12:55+00:00" }, { @@ -399,7 +418,8 @@ "support": { "source": "https://github.com/Automattic/jetpack-changelogger/tree/v7.0.5" }, - "time": "2026-10-20T16:01:37+00:00" + "time": "2026-10-20T16:01:37+00:00", + "published-time": "2026-10-20T16:01:50+00:00" }, { "name": "composer/class-map-generator", @@ -468,7 +488,8 @@ "type": "github" } ], - "time": "2026-09-25T15:24:19+00:00" + "time": "2026-09-25T15:24:19+00:00", + "published-time": "2026-09-25T15:27:30+00:00" }, { "name": "composer/installers", @@ -614,7 +635,8 @@ "type": "tidelift" } ], - "time": "2024-06-24T20:46:46+00:00" + "time": "2024-06-24T20:46:46+00:00", + "published-time": "2024-06-24T21:01:36+00:00" }, { "name": "composer/pcre", @@ -690,7 +712,8 @@ "type": "github" } ], - "time": "2026-06-07T11:47:49+00:00" + "time": "2026-06-07T11:47:49+00:00", + "published-time": "2026-06-11T12:12:37+00:00" }, { "name": "dealerdirect/phpcodesniffer-composer-installer", @@ -786,7 +809,8 @@ "type": "thanks_dev" } ], - "time": "2026-05-06T08:26:05+00:00" + "time": "2026-05-06T08:26:05+00:00", + "published-time": "2026-05-06T12:56:03+00:00" }, { "name": "fidry/console", @@ -872,7 +896,8 @@ "type": "github" } ], - "time": "2025-02-14T11:06:15+00:00" + "time": "2025-02-14T11:06:15+00:00", + "published-time": "2025-02-14T11:09:06+00:00" }, { "name": "fidry/filesystem", @@ -938,7 +963,8 @@ "type": "github" } ], - "time": "2025-02-13T23:05:19+00:00" + "time": "2025-02-13T23:05:19+00:00", + "published-time": "2025-02-13T23:09:29+00:00" }, { "name": "humbug/php-scoper", @@ -1024,7 +1050,8 @@ "issues": "https://github.com/humbug/php-scoper/issues", "source": "https://github.com/humbug/php-scoper/tree/0.18.20" }, - "time": "2026-10-08T08:21:05+00:00" + "time": "2026-10-08T08:21:05+00:00", + "published-time": "2026-10-08T08:29:48+00:00" }, { "name": "jetbrains/phpstorm-stubs", @@ -1068,7 +1095,8 @@ "stubs", "type" ], - "time": "2026-06-12T13:19:10+00:00" + "time": "2026-06-12T13:19:10+00:00", + "published-time": "2026-07-22T11:41:13+00:00" }, { "name": "johnbillion/wp-compat", @@ -1143,7 +1171,8 @@ "type": "github" } ], - "time": "2026-10-21T20:54:48+00:00" + "time": "2026-10-21T20:54:48+00:00", + "published-time": "2026-10-21T20:54:55+00:00" }, { "name": "johnpbloch/wordpress-core", @@ -1191,7 +1220,8 @@ "source": "https://core.trac.wordpress.org/browser", "wiki": "https://codex.wordpress.org/" }, - "time": "2026-10-28T19:49:26+00:00" + "time": "2026-10-28T19:49:26+00:00", + "published-time": "2026-10-28T19:49:51+00:00" }, { "name": "laravel/serializable-closure", @@ -1252,7 +1282,8 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2026-09-22T14:32:34+00:00" + "time": "2026-09-22T14:32:34+00:00", + "published-time": "2026-09-22T14:33:13+00:00" }, { "name": "myclabs/deep-copy", @@ -1312,7 +1343,8 @@ "type": "github" } ], - "time": "2026-08-11T10:17:44+00:00" + "time": "2026-08-11T10:17:44+00:00", + "published-time": "2026-08-11T10:29:20+00:00" }, { "name": "nikic/php-parser", @@ -1369,7 +1401,8 @@ "issues": "https://github.com/nikic/PHP-Parser/issues", "source": "https://github.com/nikic/PHP-Parser/tree/v5.9.0" }, - "time": "2026-09-13T18:51:52+00:00" + "time": "2026-09-13T18:51:52+00:00", + "published-time": "2026-09-13T18:53:16+00:00" }, { "name": "phar-io/manifest", @@ -1436,7 +1469,8 @@ "type": "github" } ], - "time": "2024-03-03T12:33:53+00:00" + "time": "2024-03-03T12:33:53+00:00", + "published-time": "2024-03-03T12:39:44+00:00" }, { "name": "phar-io/version", @@ -1487,7 +1521,8 @@ "issues": "https://github.com/phar-io/version/issues", "source": "https://github.com/phar-io/version/tree/3.2.1" }, - "time": "2022-02-21T01:04:05+00:00" + "time": "2022-02-21T01:04:05+00:00", + "published-time": "2022-02-21T01:04:30+00:00" }, { "name": "php-di/invoker", @@ -1542,7 +1577,8 @@ "type": "github" } ], - "time": "2025-08-30T10:22:22+00:00" + "time": "2025-08-30T10:22:22+00:00", + "published-time": "2025-08-30T10:22:43+00:00" }, { "name": "php-di/php-di", @@ -1615,7 +1651,8 @@ "type": "tidelift" } ], - "time": "2025-08-16T11:10:48+00:00" + "time": "2025-08-16T11:10:48+00:00", + "published-time": "2025-08-16T11:11:08+00:00" }, { "name": "php-stubs/woocommerce-stubs", @@ -1659,7 +1696,8 @@ "issues": "https://github.com/php-stubs/woocommerce-stubs/issues", "source": "https://github.com/php-stubs/woocommerce-stubs/tree/v11.2.0" }, - "time": "2026-10-07T19:02:59+00:00" + "time": "2026-10-07T19:02:59+00:00", + "published-time": "2026-10-07T19:04:59+00:00" }, { "name": "php-stubs/wordpress-stubs", @@ -1711,7 +1749,8 @@ "issues": "https://github.com/php-stubs/wordpress-stubs/issues", "source": "https://github.com/php-stubs/wordpress-stubs/tree/v7.1.2" }, - "time": "2026-10-06T06:59:21+00:00" + "time": "2026-10-06T06:59:21+00:00", + "published-time": "2026-10-06T07:32:21+00:00" }, { "name": "phpcompatibility/php-compatibility", @@ -1801,7 +1840,8 @@ "type": "thanks_dev" } ], - "time": "2025-11-28T11:36:33+00:00" + "time": "2025-11-28T11:36:33+00:00", + "published-time": "2025-11-28T11:41:33+00:00" }, { "name": "phpcompatibility/phpcompatibility-paragonie", @@ -1872,7 +1912,8 @@ "type": "thanks_dev" } ], - "time": "2025-11-29T13:09:49+00:00" + "time": "2025-11-29T13:09:49+00:00", + "published-time": "2025-11-29T13:22:17+00:00" }, { "name": "phpcompatibility/phpcompatibility-wp", @@ -1939,7 +1980,8 @@ "type": "thanks_dev" } ], - "time": "2025-12-16T13:35:20+00:00" + "time": "2025-12-16T13:35:20+00:00", + "published-time": "2025-12-16T13:47:29+00:00" }, { "name": "phpcsstandards/phpcsextra", @@ -2021,7 +2063,8 @@ "type": "thanks_dev" } ], - "time": "2026-07-27T11:13:17+00:00" + "time": "2026-07-27T11:13:17+00:00", + "published-time": "2026-07-27T11:19:59+00:00" }, { "name": "phpcsstandards/phpcsutils", @@ -2114,7 +2157,8 @@ "type": "thanks_dev" } ], - "time": "2026-07-27T10:28:41+00:00" + "time": "2026-07-27T10:28:41+00:00", + "published-time": "2026-07-27T10:36:31+00:00" }, { "name": "phpstan/extension-installer", @@ -2162,7 +2206,8 @@ "issues": "https://github.com/phpstan/extension-installer/issues", "source": "https://github.com/phpstan/extension-installer/tree/1.4.3" }, - "time": "2024-09-04T20:21:43+00:00" + "time": "2024-09-04T20:21:43+00:00", + "published-time": "2024-09-04T20:22:27+00:00" }, { "name": "phpstan/phpstan", @@ -2226,7 +2271,8 @@ "type": "github" } ], - "time": "2026-10-28T22:38:53+00:00" + "time": "2026-10-28T22:38:53+00:00", + "published-time": "2026-10-28T22:39:06+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", @@ -2277,7 +2323,8 @@ "issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues", "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/2.0.5" }, - "time": "2026-07-22T06:50:43+00:00" + "time": "2026-07-22T06:50:43+00:00", + "published-time": "2026-07-27T15:18:00+00:00" }, { "name": "phpstan/phpstan-strict-rules", @@ -2329,7 +2376,8 @@ "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.1.0" }, - "time": "2026-10-01T21:30:37+00:00" + "time": "2026-10-01T21:30:37+00:00", + "published-time": "2026-10-06T07:32:29+00:00" }, { "name": "phpunit/php-code-coverage", @@ -2418,7 +2466,8 @@ "type": "tidelift" } ], - "time": "2026-10-30T08:11:15+00:00" + "time": "2026-10-30T08:11:15+00:00", + "published-time": "2026-10-30T08:11:34+00:00" }, { "name": "phpunit/php-file-iterator", @@ -2491,7 +2540,8 @@ "type": "tidelift" } ], - "time": "2026-08-25T14:47:43+00:00" + "time": "2026-08-25T14:47:43+00:00", + "published-time": "2026-08-25T14:48:49+00:00" }, { "name": "phpunit/php-invoker", @@ -2567,7 +2617,8 @@ "type": "tidelift" } ], - "time": "2026-02-06T04:34:47+00:00" + "time": "2026-02-06T04:34:47+00:00", + "published-time": "2026-02-06T04:35:29+00:00" }, { "name": "phpunit/php-text-template", @@ -2639,7 +2690,8 @@ "type": "tidelift" } ], - "time": "2026-10-03T04:54:24+00:00" + "time": "2026-10-03T04:54:24+00:00", + "published-time": "2026-10-03T04:54:39+00:00" }, { "name": "phpunit/php-timer", @@ -2711,7 +2763,8 @@ "type": "tidelift" } ], - "time": "2026-02-06T04:37:53+00:00" + "time": "2026-02-06T04:37:53+00:00", + "published-time": "2026-02-06T04:38:39+00:00" }, { "name": "phpunit/phpunit", @@ -2803,7 +2856,8 @@ "type": "other" } ], - "time": "2026-10-27T04:16:22+00:00" + "time": "2026-10-27T04:16:22+00:00", + "published-time": "2026-10-27T04:18:40+00:00" }, { "name": "pronamic/changelog-md-to-wordpress-plugin-readme-txt", @@ -2843,7 +2897,8 @@ "issues": "https://github.com/pronamic/pronamic-changelog-md-to-wordpress-plugin-readme-txt/issues", "source": "https://github.com/pronamic/pronamic-changelog-md-to-wordpress-plugin-readme-txt/tree/v1.0.0" }, - "time": "2024-10-01T13:41:34+00:00" + "time": "2024-10-01T13:41:34+00:00", + "published-time": "2024-10-02T09:31:51+00:00" }, { "name": "psr/clock", @@ -2891,7 +2946,8 @@ "issues": "https://github.com/php-fig/clock/issues", "source": "https://github.com/php-fig/clock/tree/1.0.0" }, - "time": "2022-11-25T14:36:26+00:00" + "time": "2022-11-25T14:36:26+00:00", + "published-time": "2022-11-25T14:59:22+00:00" }, { "name": "psr/container", @@ -2944,7 +3000,8 @@ "issues": "https://github.com/php-fig/container/issues", "source": "https://github.com/php-fig/container/tree/2.0.2" }, - "time": "2021-11-05T16:47:00+00:00" + "time": "2021-11-05T16:47:00+00:00", + "published-time": "2021-11-05T16:53:27+00:00" }, { "name": "psr/event-dispatcher", @@ -2994,7 +3051,8 @@ "issues": "https://github.com/php-fig/event-dispatcher/issues", "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" }, - "time": "2019-01-08T18:20:26+00:00" + "time": "2019-01-08T18:20:26+00:00", + "published-time": "2019-03-25T15:31:44+00:00" }, { "name": "psr/log", @@ -3044,7 +3102,8 @@ "support": { "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "time": "2024-09-11T13:17:53+00:00" + "time": "2024-09-11T13:17:53+00:00", + "published-time": "2024-09-11T13:21:14+00:00" }, { "name": "psr/simple-cache", @@ -3095,7 +3154,8 @@ "support": { "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" }, - "time": "2021-10-29T13:26:27+00:00" + "time": "2021-10-29T13:26:27+00:00", + "published-time": "2021-10-29T13:27:19+00:00" }, { "name": "roave/security-advisories", @@ -3103,12 +3163,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "8e26a62e09d971af73b5455efcc52f6d142741c9" + "reference": "7c964bc969c1b16050a4b85b7a55614777a3f597" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/8e26a62e09d971af73b5455efcc52f6d142741c9", - "reference": "8e26a62e09d971af73b5455efcc52f6d142741c9", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/7c964bc969c1b16050a4b85b7a55614777a3f597", + "reference": "7c964bc969c1b16050a4b85b7a55614777a3f597", "shasum": "" }, "conflict": { @@ -3116,7 +3176,7 @@ "adaptcms/adaptcms": "<=1.3", "adawolfa/isdoc": "<1.4.3|>=1.5,<1.5.1|>=1.6,<1.6.1", "admidio/admidio": "<=5.0.11", - "adminneo-org/adminneo": ">=4.6.2,<5.5", + "adminneo-org/adminneo": "<5.5.1", "adodb/adodb-php": "<=5.22.9", "aheinze/cockpit": "<2.2", "aimeos/ai-admin-graphql": ">=2022.04.1,<2022.10.10|>=2023.04.1,<2023.10.6|>=2024.04.1,<2024.07.2", @@ -4265,7 +4325,8 @@ "type": "tidelift" } ], - "time": "2026-10-30T20:26:18+00:00" + "time": "2026-10-31T00:24:47+00:00", + "published-time": "2026-10-31T00:24:51+00:00" }, { "name": "sebastian/cli-parser", @@ -4334,7 +4395,8 @@ "type": "tidelift" } ], - "time": "2026-08-01T04:27:14+00:00" + "time": "2026-08-01T04:27:14+00:00", + "published-time": "2026-08-01T04:29:01+00:00" }, { "name": "sebastian/comparator", @@ -4426,7 +4488,8 @@ "type": "tidelift" } ], - "time": "2026-08-07T07:23:13+00:00" + "time": "2026-08-07T07:23:13+00:00", + "published-time": "2026-08-07T07:23:29+00:00" }, { "name": "sebastian/complexity", @@ -4496,7 +4559,8 @@ "type": "tidelift" } ], - "time": "2026-02-06T04:41:32+00:00" + "time": "2026-02-06T04:41:32+00:00", + "published-time": "2026-02-06T04:41:49+00:00" }, { "name": "sebastian/diff", @@ -4575,7 +4639,8 @@ "type": "tidelift" } ], - "time": "2026-08-25T15:38:55+00:00" + "time": "2026-08-25T15:38:55+00:00", + "published-time": "2026-08-25T15:39:44+00:00" }, { "name": "sebastian/environment", @@ -4651,7 +4716,8 @@ "type": "tidelift" } ], - "time": "2026-10-01T12:37:56+00:00" + "time": "2026-10-01T12:37:56+00:00", + "published-time": "2026-10-01T12:38:48+00:00" }, { "name": "sebastian/exporter", @@ -4741,7 +4807,8 @@ "type": "tidelift" } ], - "time": "2026-08-07T07:22:06+00:00" + "time": "2026-08-07T07:22:06+00:00", + "published-time": "2026-08-07T07:23:47+00:00" }, { "name": "sebastian/file-filter", @@ -4810,7 +4877,8 @@ "type": "tidelift" } ], - "time": "2026-04-22T07:20:04+00:00" + "time": "2026-04-22T07:20:04+00:00", + "published-time": "2026-04-22T07:20:25+00:00" }, { "name": "sebastian/git-state", @@ -4879,7 +4947,8 @@ "type": "tidelift" } ], - "time": "2026-10-21T05:59:52+00:00" + "time": "2026-10-21T05:59:52+00:00", + "published-time": "2026-10-21T06:01:28+00:00" }, { "name": "sebastian/global-state", @@ -4953,7 +5022,8 @@ "type": "tidelift" } ], - "time": "2026-06-01T15:11:33+00:00" + "time": "2026-06-01T15:11:33+00:00", + "published-time": "2026-06-01T15:20:45+00:00" }, { "name": "sebastian/lines-of-code", @@ -5023,7 +5093,8 @@ "type": "tidelift" } ], - "time": "2026-07-09T08:42:34+00:00" + "time": "2026-07-09T08:42:34+00:00", + "published-time": "2026-07-09T08:44:06+00:00" }, { "name": "sebastian/object-enumerator", @@ -5092,7 +5163,8 @@ "type": "tidelift" } ], - "time": "2026-08-13T07:05:05+00:00" + "time": "2026-08-13T07:05:05+00:00", + "published-time": "2026-08-13T07:07:58+00:00" }, { "name": "sebastian/object-reflector", @@ -5160,7 +5232,8 @@ "type": "tidelift" } ], - "time": "2026-08-13T06:34:36+00:00" + "time": "2026-08-13T06:34:36+00:00", + "published-time": "2026-08-13T06:39:22+00:00" }, { "name": "sebastian/recursion-context", @@ -5236,7 +5309,8 @@ "type": "tidelift" } ], - "time": "2026-08-03T05:58:12+00:00" + "time": "2026-08-03T05:58:12+00:00", + "published-time": "2026-08-03T05:59:59+00:00" }, { "name": "sebastian/type", @@ -5305,7 +5379,8 @@ "type": "tidelift" } ], - "time": "2026-08-10T08:00:57+00:00" + "time": "2026-08-10T08:00:57+00:00", + "published-time": "2026-08-10T08:01:43+00:00" }, { "name": "sebastian/version", @@ -5371,7 +5446,8 @@ "type": "tidelift" } ], - "time": "2026-10-04T05:51:14+00:00" + "time": "2026-10-04T05:51:14+00:00", + "published-time": "2026-10-04T05:52:22+00:00" }, { "name": "sniccowp/php-scoper-wordpress-excludes", @@ -5418,7 +5494,8 @@ "issues": "https://github.com/snicco/php-scoper-wordpress-excludes/issues", "source": "https://github.com/snicco/php-scoper-wordpress-excludes/tree/7.1.0" }, - "time": "2026-08-28T02:50:05+00:00" + "time": "2026-08-28T02:50:05+00:00", + "published-time": "2026-09-23T19:33:26+00:00" }, { "name": "squizlabs/php_codesniffer", @@ -5497,7 +5574,8 @@ "type": "thanks_dev" } ], - "time": "2026-08-06T00:17:32+00:00" + "time": "2026-08-06T00:17:32+00:00", + "published-time": "2026-08-06T01:05:02+00:00" }, { "name": "staabm/side-effects-detector", @@ -5549,7 +5627,8 @@ "type": "github" } ], - "time": "2024-10-20T05:08:20+00:00" + "time": "2024-10-20T05:08:20+00:00", + "published-time": "2024-10-20T05:09:39+00:00" }, { "name": "swissspidy/phpstan-no-private", @@ -5601,7 +5680,8 @@ "issues": "https://github.com/swissspidy/phpstan-no-private/issues", "source": "https://github.com/swissspidy/phpstan-no-private/tree/v1.0.0" }, - "time": "2024-11-11T11:04:45+00:00" + "time": "2024-11-11T11:04:45+00:00", + "published-time": "2024-11-11T11:08:03+00:00" }, { "name": "symfony/console", @@ -5699,7 +5779,8 @@ "type": "tidelift" } ], - "time": "2026-10-28T11:08:27+00:00" + "time": "2026-10-28T11:08:27+00:00", + "published-time": "2026-10-28T18:21:33+00:00" }, { "name": "symfony/deprecation-contracts", @@ -5770,7 +5851,8 @@ "type": "tidelift" } ], - "time": "2026-06-05T06:23:12+00:00" + "time": "2026-06-05T06:23:12+00:00", + "published-time": "2026-06-27T21:46:34+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -5850,7 +5932,8 @@ "type": "tidelift" } ], - "time": "2026-06-05T06:23:12+00:00" + "time": "2026-06-05T06:23:12+00:00", + "published-time": "2026-06-27T21:46:35+00:00" }, { "name": "symfony/filesystem", @@ -5920,7 +6003,8 @@ "type": "tidelift" } ], - "time": "2026-10-06T20:05:19+00:00" + "time": "2026-10-06T20:05:19+00:00", + "published-time": "2026-10-14T06:45:33+00:00" }, { "name": "symfony/finder", @@ -5988,7 +6072,8 @@ "type": "tidelift" } ], - "time": "2026-10-19T16:56:54+00:00" + "time": "2026-10-19T16:56:54+00:00", + "published-time": "2026-10-28T18:21:14+00:00" }, { "name": "symfony/polyfill-ctype", @@ -6071,7 +6156,8 @@ "type": "tidelift" } ], - "time": "2026-04-10T16:19:22+00:00" + "time": "2026-04-10T16:19:22+00:00", + "published-time": "2026-04-26T14:44:50+00:00" }, { "name": "symfony/polyfill-iconv", @@ -6155,7 +6241,8 @@ "type": "tidelift" } ], - "time": "2026-09-29T06:25:14+00:00" + "time": "2026-09-29T06:25:14+00:00", + "published-time": "2026-09-30T10:23:39+00:00" }, { "name": "symfony/polyfill-intl-grapheme", @@ -6237,7 +6324,8 @@ "type": "tidelift" } ], - "time": "2026-10-15T09:18:53+00:00" + "time": "2026-10-15T09:18:53+00:00", + "published-time": "2026-10-28T10:07:44+00:00" }, { "name": "symfony/polyfill-intl-normalizer", @@ -6322,7 +6410,8 @@ "type": "tidelift" } ], - "time": "2026-09-23T22:02:09+00:00" + "time": "2026-09-23T22:02:09+00:00", + "published-time": "2026-09-30T10:23:47+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -6407,7 +6496,8 @@ "type": "tidelift" } ], - "time": "2026-10-28T09:35:49+00:00" + "time": "2026-10-28T09:35:49+00:00", + "published-time": "2026-10-28T10:07:45+00:00" }, { "name": "symfony/polyfill-php84", @@ -6487,7 +6577,8 @@ "type": "tidelift" } ], - "time": "2026-09-23T22:01:09+00:00" + "time": "2026-09-23T22:01:09+00:00", + "published-time": "2026-09-30T10:23:33+00:00" }, { "name": "symfony/process", @@ -6552,7 +6643,8 @@ "type": "tidelift" } ], - "time": "2026-10-25T14:40:16+00:00" + "time": "2026-10-25T14:40:16+00:00", + "published-time": "2026-10-28T18:21:18+00:00" }, { "name": "symfony/service-contracts", @@ -6639,7 +6731,8 @@ "type": "tidelift" } ], - "time": "2026-07-27T15:39:01+00:00" + "time": "2026-07-27T15:39:01+00:00", + "published-time": "2026-08-30T19:56:37+00:00" }, { "name": "symfony/string", @@ -6729,7 +6822,8 @@ "type": "tidelift" } ], - "time": "2026-10-23T17:06:53+00:00" + "time": "2026-10-23T17:06:53+00:00", + "published-time": "2026-10-28T18:24:27+00:00" }, { "name": "symfony/var-dumper", @@ -6816,7 +6910,8 @@ "type": "tidelift" } ], - "time": "2026-10-25T21:04:04+00:00" + "time": "2026-10-25T21:04:04+00:00", + "published-time": "2026-10-28T18:20:00+00:00" }, { "name": "szepeviktor/phpstan-wordpress", @@ -6879,7 +6974,8 @@ "issues": "https://github.com/szepeviktor/phpstan-wordpress/issues", "source": "https://github.com/szepeviktor/phpstan-wordpress/tree/v2.0.4" }, - "time": "2026-05-22T16:22:09+00:00" + "time": "2026-05-22T16:22:09+00:00", + "published-time": "2026-08-28T06:48:49+00:00" }, { "name": "thecodingmachine/safe", @@ -7022,7 +7118,8 @@ "type": "github" } ], - "time": "2026-02-04T18:08:13+00:00" + "time": "2026-02-04T18:08:13+00:00", + "published-time": "2026-02-15T15:48:29+00:00" }, { "name": "webmozart/assert", @@ -7080,7 +7177,8 @@ "issues": "https://github.com/webmozarts/assert/issues", "source": "https://github.com/webmozarts/assert/tree/1.12.1" }, - "time": "2025-10-29T15:56:20+00:00" + "time": "2025-10-29T15:56:20+00:00", + "published-time": "2025-10-29T15:56:42+00:00" }, { "name": "wp-coding-standards/wpcs", @@ -7146,7 +7244,8 @@ "type": "custom" } ], - "time": "2026-07-27T11:53:23+00:00" + "time": "2026-07-27T11:53:23+00:00", + "published-time": "2026-07-27T11:55:52+00:00" }, { "name": "wp-hooks/wordpress-core", @@ -7229,7 +7328,8 @@ "type": "github" } ], - "time": "2026-08-20T19:54:29+00:00" + "time": "2026-08-20T19:54:29+00:00", + "published-time": "2026-08-20T19:54:42+00:00" }, { "name": "wp-plugin/woocommerce", @@ -7260,7 +7360,8 @@ "issues": "https://wordpress.org/support/plugin/woocommerce", "source": "https://plugins.svn.wordpress.org/woocommerce" }, - "time": "2026-10-09T15:35:21+00:00" + "time": "2026-10-09T15:35:21+00:00", + "published-time": "2026-10-09T15:35:21+00:00" } ], "aliases": [], @@ -7274,5 +7375,5 @@ "php": ">=8.5" }, "platform-dev": {}, - "plugin-api-version": "2.9.0" + "plugin-api-version": "2.11.0" } diff --git a/plugins/linked-orders-for-woocommerce/package-lock.json b/plugins/linked-orders-for-woocommerce/package-lock.json index 40e0b98..ce304a7 100644 --- a/plugins/linked-orders-for-woocommerce/package-lock.json +++ b/plugins/linked-orders-for-woocommerce/package-lock.json @@ -389,9 +389,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.11.tgz", + "integrity": "sha512-snZbO7MEDOof+hvNa97EjEY7qD7h2kwvQr5AJC3ZhQudKW5dnej6k3kP9NQV88hJwaS8vDqRLFo9rivLEhle2g==", "dev": true, "license": "MIT", "engines": { @@ -448,13 +448,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.29.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz", - "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.11.tgz", + "integrity": "sha512-OZ6+oaK7GMyg3IfCpj8JOmn8n+w2rJbDT0LLfVDTLb1ZFeKDh3AiNELgCMUw175IJHSkLl74hG0M0VLsvx+gag==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.29.8" + "@babel/types": "^7.29.11" }, "bin": { "parser": "bin/babel-parser.js" @@ -727,9 +727,9 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.29.7.tgz", - "integrity": "sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.29.11.tgz", + "integrity": "sha512-iARF4jjHSM0v8DImCACPXrwEltBP3DgqDpXTvjAMBAVpyEY0LiGss78wvI+KtmW+IJNEvZ5sOhPDCsOsXsIybA==", "dev": true, "license": "MIT", "dependencies": { @@ -777,9 +777,9 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.7.tgz", - "integrity": "sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.11.tgz", + "integrity": "sha512-f8kTDM/nTvpLeafb+TJ4jboFr/Y3BbC+QzgPpPGzSKJqWLnj9LW0WLqv4XKA11LX5sr+EgvLE6Ix3YsCEusodw==", "dev": true, "license": "MIT", "dependencies": { @@ -788,7 +788,7 @@ "@babel/helper-globals": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-replace-supers": "^7.29.7", - "@babel/traverse": "^7.29.7" + "@babel/traverse": "^7.29.10" }, "engines": { "node": ">=6.9.0" @@ -1513,9 +1513,9 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.8.tgz", - "integrity": "sha512-4S9ksMGVWUshvgK0mKfvZky7leuG5/uoFVwMpAomJ8bMoDJiNHRVmc1EglwW/CmGVSqqWpEbXm9FmbRit22qoA==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.11.tgz", + "integrity": "sha512-5xWGhfuMgbOfIjsk/I2c4uMnoqRwQdKYi4QjvybQrcEVE0G9/p3qmIkp2Sxe8YF8DGNNVs5whfwNqIM5684XoQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1851,13 +1851,13 @@ } }, "node_modules/@babel/types": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", - "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.11.tgz", + "integrity": "sha512-WCsemW40Cpc6gouC1SrY6PBaZ+oLRlEVY9XBvZBI7il/HyUrb2nJ505wE1MHEFyDZNnrWDEuy4jL50WVHfTTSQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-string-parser": "^7.29.11", "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { @@ -6454,9 +6454,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.13.tgz", - "integrity": "sha512-2E8Q6rh+1RRSq/YdaGsNfXsEZy9FVqv6BSSrkmZJP7BnaoPnRfKL+YaofPvP+5rzdk144qSm6i4+hvPgUcbV4Q==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.14.tgz", + "integrity": "sha512-wCBiZ77u1tuWde6G/nfOtLaCTYcFDU5yCzARzK10y+rLhzYoL7a7yEemcopcc2Mzow7gZOIjnV2ym1NYmdiJOQ==", "dev": true, "license": "MIT", "engines": { @@ -13315,9 +13315,9 @@ } }, "node_modules/http-proxy-middleware": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-4.2.0.tgz", - "integrity": "sha512-ZA+oNOoM+GLoFTIzhkJptVQov73Srep2LBqhF8hG8CIPKO3nam1jonXVQ/QUH8RbwsmaaVz2SOJdzBNBHNtKbw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-4.2.1.tgz", + "integrity": "sha512-60SmYZpPD4dPbC2GZVkVV4uP/V/cbsXSwzGQXeHWbbE3Q1XdccJpqHTg6NJ6iMpFGVA1OEry8c8kHhx4ahiU1w==", "dev": true, "license": "MIT", "dependencies": { @@ -15276,9 +15276,9 @@ "license": "Python-2.0" }, "node_modules/markdownlint-cli/node_modules/ignore": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.13.tgz", - "integrity": "sha512-2E8Q6rh+1RRSq/YdaGsNfXsEZy9FVqv6BSSrkmZJP7BnaoPnRfKL+YaofPvP+5rzdk144qSm6i4+hvPgUcbV4Q==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.14.tgz", + "integrity": "sha512-wCBiZ77u1tuWde6G/nfOtLaCTYcFDU5yCzARzK10y+rLhzYoL7a7yEemcopcc2Mzow7gZOIjnV2ym1NYmdiJOQ==", "dev": true, "license": "MIT", "engines": { @@ -20882,9 +20882,9 @@ } }, "node_modules/stylelint/node_modules/ignore": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.13.tgz", - "integrity": "sha512-2E8Q6rh+1RRSq/YdaGsNfXsEZy9FVqv6BSSrkmZJP7BnaoPnRfKL+YaofPvP+5rzdk144qSm6i4+hvPgUcbV4Q==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.14.tgz", + "integrity": "sha512-wCBiZ77u1tuWde6G/nfOtLaCTYcFDU5yCzARzK10y+rLhzYoL7a7yEemcopcc2Mzow7gZOIjnV2ym1NYmdiJOQ==", "dev": true, "license": "MIT", "engines": { From ae66d2946edac0649cae8a86cd431d424faa6c42 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sat, 31 Oct 2026 22:57:05 +0100 Subject: [PATCH 02/50] Test that LO and IC load their safeguards before the floor gate Linked Orders and Internal Comments load includes/safeguards.php after the bootstrap package's floor gate, as the template did, so on a runtime below a floor their restrictions would never load while a failed boot keeps them. The template now loads the safeguards first. Each plugin's PluginBootTest gains the template's row, which expects includes/safeguards.php among the included files before the bootstrap package's load.php, and the include-loader row drops its safeguards.php assertion, which the order row covers. Both fail until the entries move. Assisted-by: Claude Code:claude-opus-5-5 --- .../tests/Integration/PluginBootTest.php | 11 ++++++++++- .../tests/Integration/PluginBootTest.php | 11 ++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/plugins/internal-comments/tests/Integration/PluginBootTest.php b/plugins/internal-comments/tests/Integration/PluginBootTest.php index 624c5ee..d84da1a 100644 --- a/plugins/internal-comments/tests/Integration/PluginBootTest.php +++ b/plugins/internal-comments/tests/Integration/PluginBootTest.php @@ -32,7 +32,6 @@ public function test_the_plugin_boots_every_component(): void { public function test_the_entry_loads_every_include_but_those_whose_names_start_with_an_underscore(): void { $includes = \dirname( DWS_IC_FILE ) . '/includes/'; - self::assertContains( $includes . 'safeguards.php', \get_included_files() ); self::assertContains( $includes . 'index.php', \get_included_files() ); self::assertIsInt( \file_put_contents( $includes . '_ic-skipped.php', "migrations ); self::assertSame( \count( dws_ic_get_migration_steps() ), MigrationRunner::get_revision( Plugin::PREFIX . '_revision' ) ); diff --git a/plugins/linked-orders-for-woocommerce/tests/Integration/PluginBootTest.php b/plugins/linked-orders-for-woocommerce/tests/Integration/PluginBootTest.php index 4171d66..30d9086 100644 --- a/plugins/linked-orders-for-woocommerce/tests/Integration/PluginBootTest.php +++ b/plugins/linked-orders-for-woocommerce/tests/Integration/PluginBootTest.php @@ -32,7 +32,6 @@ public function test_the_plugin_boots_every_component_but_the_integration_with_a public function test_the_entry_loads_every_include_but_those_whose_names_start_with_an_underscore(): void { $includes = \dirname( DWS_LOWC_FILE ) . '/includes/'; - self::assertContains( $includes . 'safeguards.php', \get_included_files() ); self::assertContains( $includes . 'index.php', \get_included_files() ); self::assertIsInt( \file_put_contents( $includes . '_lowc-skipped.php', "migrations ); self::assertSame( \count( dws_lowc_get_migration_steps() ), MigrationRunner::get_revision( Plugin::PREFIX . '_revision' ) ); From c16dcf21c40f07c097e99bd429b03e746d56f51b Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sat, 31 Oct 2026 23:01:07 +0100 Subject: [PATCH 03/50] Load LO's and IC's safeguards before the floor gate Linked Orders and Internal Comments loaded includes/safeguards.php after Requirements::check_floors(), as the template did, so a site below a floor would lose a safeguard's restriction while a failed boot keeps it. Both safeguards files are empty today, but a restriction either plugin adds must hold whenever the plugin cannot boot. Each entry now requires the safeguards first, as the template's does. The template's accompanying changes come along, since each plugin carries its own copy: the AGENTS.md layout lines and safeguards convention, docs/ARCHITECTURE.md's load order, the safeguards file's docblock, and phpstan.safeguards.neon, which lint:php:phpstan runs at PHP 7.4 and WordPress 5.2 and the release zip leaves out. Linked Orders' convention names the template's recipes, as Internal Comments' does, since it ships no docs/recipes.md. X ruling 89. Assisted-by: Claude Code:claude-opus-5-5 --- plugins/internal-comments/.distignore | 1 + plugins/internal-comments/AGENTS.md | 8 ++++---- plugins/internal-comments/composer.json | 5 ++++- plugins/internal-comments/docs/ARCHITECTURE.md | 6 +++--- plugins/internal-comments/includes/safeguards.php | 2 +- plugins/internal-comments/internal-comments.php | 4 ++-- plugins/internal-comments/phpstan.safeguards.neon | 13 +++++++++++++ plugins/linked-orders-for-woocommerce/.distignore | 1 + plugins/linked-orders-for-woocommerce/AGENTS.md | 8 ++++---- plugins/linked-orders-for-woocommerce/composer.json | 5 ++++- .../docs/ARCHITECTURE.md | 6 +++--- .../includes/safeguards.php | 2 +- .../linked-orders-for-woocommerce.php | 4 ++-- .../phpstan.safeguards.neon | 13 +++++++++++++ 14 files changed, 56 insertions(+), 22 deletions(-) create mode 100644 plugins/internal-comments/phpstan.safeguards.neon create mode 100644 plugins/linked-orders-for-woocommerce/phpstan.safeguards.neon diff --git a/plugins/internal-comments/.distignore b/plugins/internal-comments/.distignore index ee1bef4..5faf091 100644 --- a/plugins/internal-comments/.distignore +++ b/plugins/internal-comments/.distignore @@ -34,6 +34,7 @@ stylelint.config.js phpcs.dist.xml phpcs.tests.dist.xml phpstan.dist.neon +phpstan.safeguards.neon phpunit.dist.xml playwright.config.js scoper.inc.php diff --git a/plugins/internal-comments/AGENTS.md b/plugins/internal-comments/AGENTS.md index 96df771..c14ff40 100644 --- a/plugins/internal-comments/AGENTS.md +++ b/plugins/internal-comments/AGENTS.md @@ -7,7 +7,7 @@ v1 sites upgrade in place, so the port keeps v1's comment type, settings rows, c ## Layout ``` -internal-comments.php the entry: bootstrap load.php, the inert updater, the floor check, safeguards, the build gate, the autoloader +internal-comments.php the entry: safeguards, bootstrap load.php, the inert updater, the floor check, the build gate, the autoloader functions.php dws_ic_plugin(), the boot on plugins_loaded 15, deactivation, the order-tables declaration, v1's boot hooks, the includes/ loader uninstall.php the framework's uninstall engine over the footprint, with v1's remove-data-uninstall consent src/ the composition root and its components only @@ -20,7 +20,7 @@ src/ the composition root and its components only └── AdminScreens.php the posts and orders list column with its quick view, the Comments screen view, the comment edit screen models/DWS_Internal_Comment.php v1's global model class on the classmap, wrapping an internal comment with the user's permissions on it includes/ hook-free code as dws_ic_ functions and DWS_IC_ constants, loaded in name order, _-prefixed files skipped -├── safeguards.php restrictions that hold when the build fails; loaded by the entry before the autoloader +├── safeguards.php restrictions that hold whenever the plugin cannot boot; loaded by the entry before the floor check ├── capabilities.php v1's twelve capabilities and the roles CapabilityGrants gives them ├── options.php v1's two settings rows, their fields and their defaults ├── settings.php v1's settings API: the raw and validated reads, by v1 field id and group @@ -41,7 +41,7 @@ changelog/ the pending entries changelog:write turns into CHAN composer packages-install # dependencies, then php-scoper into vendor-prefixed/ composer test:unit composer test:integration # starts .wp-env.tests.json, then runs test:integration:run, which builds the scripts first -composer quality-check # manifest and changelog validation, PHPCS (both profiles), PHPStan, Unit and Integration +composer quality-check # manifest and changelog validation, PHPCS (both profiles), PHPStan (both profiles), Unit and Integration composer changelog:add # stages an entry in changelog/; composer changelog:write, at release, also syncs readme.txt npm run setup # both installs, the scripts' build, then the development site npm run build # the meta box's and quick view's assets through the local wp-scripts; npm run start watches them in a POSIX shell @@ -69,7 +69,7 @@ Both run PHP 8.5 with WooCommerce from `vendor/` and its order tables on; CI run 7. **Internal status.** An internal comment is stored under the `dws_internal` status, which keeps it out of WordPress's queries, counts, feeds and REST API without the plugin's help, so no code stores one under another status, and a query that lists them names the status. `Comments` puts back any that WordPress or another plugin moves, announcing the move as a status change after WordPress announces its own, so caches that follow status changes, such as WooCommerce's comment counts, end where the comment is; the migration's bulk move resets WooCommerce's counts instead. Until the migration has moved the comments v1 stored approved, it leaves the type out of every comment query and feed that names neither it nor the status. Every check of what a user may do with one goes through the `see_`, `edit_` and `trash_dws_internal_comment` meta capabilities. 8. **Order storage.** Order code takes ids WooCommerce resolves in either storage, through `dws_ic_get_post_type()` and `edit_post`, which WooCommerce maps for orders, and WooCommerce's screen ids and list hooks, never the posts table; every Integration row whose outcome the order storage can change runs on all four storages through `OrderStorage`. 9. **v1 hooks.** A hook v1 fired keeps its v1 literal, fired first through `apply_filters_deprecated()` or `do_action_deprecated()` with `'2.0.0'` and the v2 name, then the v2 name. -10. **Safeguards** live in `includes/safeguards.php`, call only WordPress and that file, and relax only through their own filter, through `dws_ic_is_relaxed()` (the template's recipes). +10. **Safeguards** live in `includes/safeguards.php`, call only WordPress and that file, and relax only through their own filter, through `dws_ic_is_relaxed()` (the template's recipes). The entry loads them before the floor check, so they parse on PHP 7.4 and call only WordPress APIs present at the oldest WordPress the bootstrap package runs on, or guard them, which `phpstan.safeguards.neon` checks. 11. **Migrations** are append-only: a step converges from any partial state, verifies its writes and throws when one fails; bulk work is a batched step, which runs only from the runner's scheduled hook. ## Style diff --git a/plugins/internal-comments/composer.json b/plugins/internal-comments/composer.json index bdb915e..3a38d76 100644 --- a/plugins/internal-comments/composer.json +++ b/plugins/internal-comments/composer.json @@ -100,7 +100,10 @@ ], "lint:php:phpcs": "phpcs --standard=./phpcs.dist.xml --basepath=. ./ -v", "lint:php:phpcs:tests": "phpcs --standard=./phpcs.tests.dist.xml --basepath=. ./tests -v", - "lint:php:phpstan": "phpstan analyse -v --memory-limit=1G", + "lint:php:phpstan": [ + "phpstan analyse -v --memory-limit=1G", + "phpstan analyse -c phpstan.safeguards.neon -v --memory-limit=1G" + ], "test": [ "@test:unit", diff --git a/plugins/internal-comments/docs/ARCHITECTURE.md b/plugins/internal-comments/docs/ARCHITECTURE.md index cbda4d3..d857f7c 100644 --- a/plugins/internal-comments/docs/ARCHITECTURE.md +++ b/plugins/internal-comments/docs/ARCHITECTURE.md @@ -7,8 +7,8 @@ This document describes how the plugin loads, boots, stores its data and leaves `internal-comments.php` is the only file WordPress includes, and it parses on PHP 7.4 so that a site below the floors gets a notice instead of a fatal error: 1. It defines `DWS_IC_FILE`; translations come from wordpress.org's language packs, which WordPress loads just in time. -2. It requires `vendor-prefixed/ahegyes/wp-framework-bootstrap/load.php`, registers the GitHub updater, which stays inert because the entry file has no `Update URI` header, then checks the `Requires PHP` and `Requires at least` headers. On a site below a floor, WordPress's own notice shows and nothing else loads. -3. It loads `includes/safeguards.php`, which holds even when the rest of the build fails. +2. It loads `includes/safeguards.php`, whose restrictions hold on any runtime that parses the plugin: below the floors, on a partial deploy and when the boot fails. +3. It requires `vendor-prefixed/ahegyes/wp-framework-bootstrap/load.php`, registers the GitHub updater, which stays inert because the entry file has no `Update URI` header, then checks the `Requires PHP` and `Requires at least` headers. On a site below a floor, WordPress's own notice shows and nothing past the safeguards loads. 4. It stops with a notice when the prefixed framework or the meta box's built script is missing. It then requires the autoloader and `functions.php`; a missing file throws, which local and development environments rethrow and every other environment logs, showing the same notice. `functions.php` defines the `dws_ic_plugin()` accessor, boots the plugin on `plugins_loaded` at priority 15, after WooCommerce and Action Scheduler have loaded where they are active, registers the deactivation callback, declares the plugin compatible with WooCommerce's order tables, relays the framework's boot hooks to v1's `dws_ic_initialized` and `dws_ic_initialization_failure`, and requires every `includes/*.php` in name order except those whose names start with `_`. @@ -29,7 +29,7 @@ WooCommerce is optional: without it, every feature but the order screens and the - `src/` holds the composition root and its components: final classes that implement the framework's `ComponentInterface` and add their hooks in `register_hooks()`. `Settings` declares v1's settings page to the framework's `OptionsPage`; `Permissions` maps the meta capabilities and holds core's REST API and XML-RPC to them, which otherwise let moderators and a comment's author past; `Comments` keeps internal comments under their status and records their revisions; `MetaBox` adds the meta box to posts and orders of the selected types; `RestRoutes` registers the routes the meta box and quick view call; `AdminScreens` adds the list column and quick view, the Comments screen view and the edit screen's revisions and privacy. Each checks the user's capabilities in its callbacks. - `models/DWS_Internal_Comment.php` keeps v1's global class on Composer's classmap: it wraps an internal comment, passes reads and calls to it, and answers whether a user may see, edit or trash it. -- `includes/` holds code that registers no hooks and is not part of the tree, as prefixed functions and constants: v1's capabilities (`capabilities.php`), the settings rows, fields and defaults (`options.php`), v1's settings and comment APIs (`settings.php`, `comments.php`), the markup the meta box prints and the routes answer with (`markup.php`), v1's deprecated root functions, constants and boot-hook relays (`deprecated.php`), the footprint (`footprint.php`), the migration steps (`migrations.php`), the asset registrars (`assets.php`) and the slug, name and version getters a peer plugin reads (`metadata.php`). Quality's syntax job parses the folder on PHP 7.4, because `safeguards.php` and what it requires run before the floor check passes on a site without the built files. +- `includes/` holds code that registers no hooks and is not part of the tree, as prefixed functions and constants: v1's capabilities (`capabilities.php`), the settings rows, fields and defaults (`options.php`), v1's settings and comment APIs (`settings.php`, `comments.php`), the markup the meta box prints and the routes answer with (`markup.php`), v1's deprecated root functions, constants and boot-hook relays (`deprecated.php`), the footprint (`footprint.php`), the migration steps (`migrations.php`), the asset registrars (`assets.php`) and the slug, name and version getters a peer plugin reads (`metadata.php`). Quality's syntax job parses the folder on PHP 7.4, because `safeguards.php` runs before the floor check, and `phpstan.safeguards.neon` analyzes it at PHP 7.4 and the oldest WordPress the bootstrap package runs on. - `assets/src/` holds the meta box's and quick view's TypeScript and SCSS, which `npm run build` compiles into `assets/build/`; `includes/assets.php` reads the `*.asset.php` file wp-scripts writes beside each build. ## Data diff --git a/plugins/internal-comments/includes/safeguards.php b/plugins/internal-comments/includes/safeguards.php index 280a797..c60efc1 100644 --- a/plugins/internal-comments/includes/safeguards.php +++ b/plugins/internal-comments/includes/safeguards.php @@ -1,6 +1,6 @@ Date: Sat, 31 Oct 2026 23:02:46 +0100 Subject: [PATCH 04/50] Add Locked Payment Methods from the plugin template Locked Payment Methods for WooCommerce is the third plugin ported to v2. It starts from wordpress-plugin-template's trunk (321d243) through the template's own fill-in, run in a clone of the template: name Locked Payment Methods for WooCommerce, slug locked-payment-methods-for-woocommerce, namespace DeepWebSolutions\LockedPaymentMethods, prefix dws_lpmwc, ports 8826 and 8827, repository ahegyes/wc-locked-payment-methods. As the v1 successor it stays at 2.0.0, which the fill-in sets to 0.1.0 for a new plugin, and its Composer name is its mirror, ahegyes/wc-locked-payment-methods. The directory keeps the template's files but for CONTRIBUTING.md, the pull-request and issue templates, Dependabot's configuration and the fill-in's workflow, script and Quality job, which belong to the monorepo. The template's demo stays until the port replaces it, and every suite passes as filled in. X rulings 87 and 88. Assisted-by: Claude Code:claude-opus-5-5 --- .../.distignore | 40 + .../.editorconfig | 24 + .../.github/workflows/audit.yml | 21 + .../.github/workflows/codeql.yml | 23 + .../.github/workflows/quality.yml | 71 + .../.github/workflows/release.yml | 53 + .../.github/workflows/tests-nightly.yml | 29 + .../.github/workflows/tests.yml | 74 + .../.github/workflows/workflow-checks.yml | 27 + .../.gitignore | 354 + .../.wp-env.json | 19 + .../.wp-env.tests.json | 19 + .../AGENTS.md | 74 + .../CHANGELOG.md | 10 + .../CLAUDE.md | 1 + .../LICENSE | 674 + .../README.md | 73 + .../SECURITY.md | 14 + .../assets/index.php | 1 + .../assets/src/editor.ts | 7 + .../assets/src/greeting.scss | 3 + .../assets/src/index.php | 1 + .../blocks/index.php | 1 + .../blocks/src/declarations.d.ts | 17 + .../blocks/src/example-notice/block.json | 18 + .../blocks/src/example-notice/edit.tsx | 15 + .../blocks/src/example-notice/editor.scss | 3 + .../blocks/src/example-notice/index.php | 1 + .../blocks/src/example-notice/index.ts | 11 + .../blocks/src/example-notice/save.tsx | 10 + .../blocks/src/example-notice/style.scss | 8 + .../blocks/src/index.php | 1 + .../changelog/.gitkeep | 0 .../composer.json | 183 + .../composer.lock | 7338 +++++ .../docs/ARCHITECTURE.md | 45 + .../docs/recipes.md | 1293 + .../eslint.config.mjs | 3 + .../functions.php | 92 + .../includes/assets.php | 77 + .../includes/footprint.php | 32 + .../includes/index.php | 1 + .../includes/metadata.php | 51 + .../includes/migrations.php | 49 + .../includes/options.php | 187 + .../includes/safeguards.php | 11 + .../index.php | 1 + .../languages/index.php | 1 + ...locked-payment-methods-for-woocommerce.php | 90 + .../models/index.php | 1 + .../package-lock.json | 25485 ++++++++++++++++ .../package.json | 60 + .../phpcs.dist.xml | 18 + .../phpcs.tests.dist.xml | 4 + .../phpstan.dist.neon | 20 + .../phpstan.safeguards.neon | 13 + .../phpunit.dist.xml | 32 + .../playwright.config.js | 11 + .../scoper.inc.php | 8 + .../src/Blocks.php | 60 + .../src/Feature/Feature.php | 51 + .../src/Feature/FeatureDetail.php | 70 + .../src/Feature/index.php | 1 + .../src/Plugin.php | 219 + .../src/Settings.php | 123 + .../src/index.php | 1 + .../stylelint.config.js | 3 + .../templates/index.php | 1 + .../tests/Fixtures/built.asset.php | 12 + .../tests/Integration/AssetsTest.php | 45 + .../tests/Integration/BlocksTest.php | 42 + .../tests/Integration/DeactivationTest.php | 32 + .../Integration/Feature/FeatureDetailTest.php | 130 + .../tests/Integration/Feature/FeatureTest.php | 63 + .../tests/Integration/MetadataTest.php | 57 + .../tests/Integration/MigrationsTest.php | 80 + .../tests/Integration/OptionsTest.php | 149 + .../tests/Integration/PartialDeployTest.php | 100 + .../tests/Integration/PluginBootTest.php | 46 + .../tests/Integration/SettingsTest.php | 127 + .../tests/Integration/UninstallTest.php | 75 + .../tests/Integration/bootstrap.php | 19 + .../tests/README.md | 58 + .../tests/Unit/ComponentTreeTest.php | 25 + .../tests/Unit/FootprintTest.php | 68 + .../tests/Unit/bootstrap.php | 8 + .../tests/bootstrap.php | 3 + .../tests/e2e/settings.spec.ts | 74 + .../tests/e2e/without-woocommerce.spec.ts | 51 + .../tsconfig.json | 4 + .../uninstall.php | 55 + 91 files changed, 38655 insertions(+) create mode 100644 plugins/locked-payment-methods-for-woocommerce/.distignore create mode 100644 plugins/locked-payment-methods-for-woocommerce/.editorconfig create mode 100644 plugins/locked-payment-methods-for-woocommerce/.github/workflows/audit.yml create mode 100644 plugins/locked-payment-methods-for-woocommerce/.github/workflows/codeql.yml create mode 100644 plugins/locked-payment-methods-for-woocommerce/.github/workflows/quality.yml create mode 100644 plugins/locked-payment-methods-for-woocommerce/.github/workflows/release.yml create mode 100644 plugins/locked-payment-methods-for-woocommerce/.github/workflows/tests-nightly.yml create mode 100644 plugins/locked-payment-methods-for-woocommerce/.github/workflows/tests.yml create mode 100644 plugins/locked-payment-methods-for-woocommerce/.github/workflows/workflow-checks.yml create mode 100644 plugins/locked-payment-methods-for-woocommerce/.gitignore create mode 100644 plugins/locked-payment-methods-for-woocommerce/.wp-env.json create mode 100644 plugins/locked-payment-methods-for-woocommerce/.wp-env.tests.json create mode 100644 plugins/locked-payment-methods-for-woocommerce/AGENTS.md create mode 100644 plugins/locked-payment-methods-for-woocommerce/CHANGELOG.md create mode 120000 plugins/locked-payment-methods-for-woocommerce/CLAUDE.md create mode 100644 plugins/locked-payment-methods-for-woocommerce/LICENSE create mode 100644 plugins/locked-payment-methods-for-woocommerce/README.md create mode 100644 plugins/locked-payment-methods-for-woocommerce/SECURITY.md create mode 100644 plugins/locked-payment-methods-for-woocommerce/assets/index.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/assets/src/editor.ts create mode 100644 plugins/locked-payment-methods-for-woocommerce/assets/src/greeting.scss create mode 100644 plugins/locked-payment-methods-for-woocommerce/assets/src/index.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/index.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/src/declarations.d.ts create mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/block.json create mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/edit.tsx create mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/editor.scss create mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/index.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/index.ts create mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/save.tsx create mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/style.scss create mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/src/index.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/changelog/.gitkeep create mode 100644 plugins/locked-payment-methods-for-woocommerce/composer.json create mode 100644 plugins/locked-payment-methods-for-woocommerce/composer.lock create mode 100644 plugins/locked-payment-methods-for-woocommerce/docs/ARCHITECTURE.md create mode 100644 plugins/locked-payment-methods-for-woocommerce/docs/recipes.md create mode 100644 plugins/locked-payment-methods-for-woocommerce/eslint.config.mjs create mode 100644 plugins/locked-payment-methods-for-woocommerce/functions.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/includes/assets.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/includes/footprint.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/includes/index.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/includes/metadata.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/includes/migrations.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/includes/options.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/index.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/languages/index.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/locked-payment-methods-for-woocommerce.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/models/index.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/package-lock.json create mode 100644 plugins/locked-payment-methods-for-woocommerce/package.json create mode 100644 plugins/locked-payment-methods-for-woocommerce/phpcs.dist.xml create mode 100644 plugins/locked-payment-methods-for-woocommerce/phpcs.tests.dist.xml create mode 100644 plugins/locked-payment-methods-for-woocommerce/phpstan.dist.neon create mode 100644 plugins/locked-payment-methods-for-woocommerce/phpstan.safeguards.neon create mode 100644 plugins/locked-payment-methods-for-woocommerce/phpunit.dist.xml create mode 100644 plugins/locked-payment-methods-for-woocommerce/playwright.config.js create mode 100644 plugins/locked-payment-methods-for-woocommerce/scoper.inc.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/src/Blocks.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/src/Feature/Feature.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/src/Feature/FeatureDetail.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/src/Feature/index.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/src/Plugin.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/src/Settings.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/src/index.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/stylelint.config.js create mode 100644 plugins/locked-payment-methods-for-woocommerce/templates/index.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Fixtures/built.asset.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/AssetsTest.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/BlocksTest.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/DeactivationTest.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/Feature/FeatureDetailTest.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/Feature/FeatureTest.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/MetadataTest.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/MigrationsTest.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/OptionsTest.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/PartialDeployTest.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/SettingsTest.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/UninstallTest.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/bootstrap.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/README.md create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Unit/ComponentTreeTest.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Unit/FootprintTest.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Unit/bootstrap.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/bootstrap.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/e2e/settings.spec.ts create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/e2e/without-woocommerce.spec.ts create mode 100644 plugins/locked-payment-methods-for-woocommerce/tsconfig.json create mode 100644 plugins/locked-payment-methods-for-woocommerce/uninstall.php diff --git a/plugins/locked-payment-methods-for-woocommerce/.distignore b/plugins/locked-payment-methods-for-woocommerce/.distignore new file mode 100644 index 0000000..5faf091 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/.distignore @@ -0,0 +1,40 @@ +.cache +.distignore +.editorconfig +.git +.github +.gitignore +.idea +.vscode +.wp-env.json +.wp-env.override.json +.wp-env.tests.json + +AGENTS.md +CHANGELOG.md +CLAUDE.md +CONTRIBUTING.md +README.md +SECURITY.md + +changelog/ +docs/ + +node_modules/ +tests/ +vendor/bin/ + +composer.json +composer.lock +package.json +package-lock.json +tsconfig.json +eslint.config.mjs +stylelint.config.js +phpcs.dist.xml +phpcs.tests.dist.xml +phpstan.dist.neon +phpstan.safeguards.neon +phpunit.dist.xml +playwright.config.js +scoper.inc.php diff --git a/plugins/locked-payment-methods-for-woocommerce/.editorconfig b/plugins/locked-payment-methods-for-woocommerce/.editorconfig new file mode 100644 index 0000000..05c9aa0 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/.editorconfig @@ -0,0 +1,24 @@ +# This file is for unifying the coding style for different editors and IDEs +# editorconfig.org + +# WordPress Coding Standards +# https://make.wordpress.org/core/handbook/coding-standards/ + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = tab + +[*.{yml,yaml,json}] +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[*.txt] +end_of_line = crlf diff --git a/plugins/locked-payment-methods-for-woocommerce/.github/workflows/audit.yml b/plugins/locked-payment-methods-for-woocommerce/.github/workflows/audit.yml new file mode 100644 index 0000000..36537c6 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/.github/workflows/audit.yml @@ -0,0 +1,21 @@ +name: Supply-Chain Audit + +on: + push: + branches: [trunk] + pull_request: + schedule: + # Weekly Monday at 07:00 UTC — staggered after dependabot (06:00) so PRs from + # dependency bumps surface audit results in the same morning batch. + - cron: '0 7 * * 1' + +permissions: + contents: read + +jobs: + audit: + name: Audit + # npm gate audits production deps only — the dev-tooling graph (wp-scripts/playwright trees) is advisory via dependabot alerts. + uses: ahegyes/wordpress-configs/.github/workflows/reusable-supply-chain-audit.yml@8d53efd292e4abee4a9c57b0e379434f54bedbe9 + with: + npm-audit-flags: '--omit=dev --audit-level=high' diff --git a/plugins/locked-payment-methods-for-woocommerce/.github/workflows/codeql.yml b/plugins/locked-payment-methods-for-woocommerce/.github/workflows/codeql.yml new file mode 100644 index 0000000..faef0d2 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/.github/workflows/codeql.yml @@ -0,0 +1,23 @@ +name: CodeQL + +on: + push: + branches: [trunk] + pull_request: + branches: [trunk] + schedule: + - cron: '0 6 * * 1' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: {} + +jobs: + analyze: + permissions: + actions: read + contents: read + security-events: write + uses: ahegyes/wordpress-configs/.github/workflows/reusable-codeql.yml@8d53efd292e4abee4a9c57b0e379434f54bedbe9 diff --git a/plugins/locked-payment-methods-for-woocommerce/.github/workflows/quality.yml b/plugins/locked-payment-methods-for-woocommerce/.github/workflows/quality.yml new file mode 100644 index 0000000..27586d6 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/.github/workflows/quality.yml @@ -0,0 +1,71 @@ +name: Quality + +on: + push: + branches: [trunk] + pull_request: + +permissions: + contents: read + +jobs: + lint-php: + name: PHP Lint + uses: ahegyes/wordpress-configs/.github/workflows/reusable-php-lint.yml@8d53efd292e4abee4a9c57b0e379434f54bedbe9 + with: + php-version: '8.5' + scripts: '["lint:php:phpcs", "lint:php:phpcs:tests", "lint:php:phpstan"]' + + lint-scripts-styles: + name: Scripts and styles lint + uses: ahegyes/wordpress-configs/.github/workflows/reusable-scripts-styles-lint.yml@8d53efd292e4abee4a9c57b0e379434f54bedbe9 + with: + scripts: '["lint:scripts", "lint:styles", "lint:types"]' + + block-json: + name: block.json schema + uses: ahegyes/wordpress-configs/.github/workflows/reusable-block-json-check.yml@8d53efd292e4abee4a9c57b0e379434f54bedbe9 + + slug: + name: Plugin slug + runs-on: ubuntu-latest + timeout-minutes: 5 + outputs: + slug: ${{ steps.slug.outputs.slug }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + # GitHub refuses a GITHUB_TOKEN push that changes a workflow, so the fill-in leaves workflows alone and they read the slug the plugin declares. + - name: Read the slug from composer.json + id: slug + run: | + slug=$(jq -r '.extra["text-domain"] // empty' composer.json) + if ! [[ "$slug" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]]; then + echo "::error::composer.json's extra.text-domain must be the plugin's kebab-case slug — got '$slug'." + exit 1 + fi + echo "slug=$slug" >> "$GITHUB_OUTPUT" + + php-syntax: + name: PHP syntax + needs: slug + uses: ahegyes/wordpress-configs/.github/workflows/reusable-php-syntax-check.yml@8d53efd292e4abee4a9c57b0e379434f54bedbe9 + with: + php-versions: '["7.4"]' + paths: ${{ format('["{0}.php", "uninstall.php", "includes"]', needs.slug.outputs.slug) }} + + changelog: + name: Changelog fragments + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962 # 2.40.0 + with: + php-version: '8.5' + coverage: none + tools: composer:v2 + - uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0 + - run: composer changelog:validate diff --git a/plugins/locked-payment-methods-for-woocommerce/.github/workflows/release.yml b/plugins/locked-payment-methods-for-woocommerce/.github/workflows/release.yml new file mode 100644 index 0000000..da95cb5 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/.github/workflows/release.yml @@ -0,0 +1,53 @@ +name: Release + +on: + push: + tags: ['v*'] + pull_request: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +permissions: + contents: read + +jobs: + slug: + name: Plugin slug + runs-on: ubuntu-latest + timeout-minutes: 5 + outputs: + slug: ${{ steps.slug.outputs.slug }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + # GitHub refuses a GITHUB_TOKEN push that changes a workflow, so the fill-in leaves workflows alone and they read the slug the plugin declares. + - name: Read the slug from composer.json + id: slug + run: | + slug=$(jq -r '.extra["text-domain"] // empty' composer.json) + if ! [[ "$slug" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]]; then + echo "::error::composer.json's extra.text-domain must be the plugin's kebab-case slug — got '$slug'." + exit 1 + fi + echo "slug=$slug" >> "$GITHUB_OUTPUT" + + release: + name: Release + needs: slug + # A reusable workflow can't elevate above the caller's token; grant the provenance check's and the GitHub Release's scopes here. + permissions: + actions: read + contents: write + uses: ahegyes/wordpress-configs/.github/workflows/reusable-release.yml@8d53efd292e4abee4a9c57b0e379434f54bedbe9 + with: + project-path: '.' + plugin-slug: ${{ needs.slug.outputs.slug }} + wp-env-config-file: '.wp-env.tests.json' + # Both parse every PHP file with php-parser 4, which stops at PHP 8.4 syntax such as protected(set). + exclude-checks: 'prefixing,direct_file_access' + # A pull request or a dispatch on a branch rehearses the release without publishing it. + publish: ${{ github.ref_type == 'tag' }} diff --git a/plugins/locked-payment-methods-for-woocommerce/.github/workflows/tests-nightly.yml b/plugins/locked-payment-methods-for-woocommerce/.github/workflows/tests-nightly.yml new file mode 100644 index 0000000..ed0d800 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/.github/workflows/tests-nightly.yml @@ -0,0 +1,29 @@ +name: Tests (nightly) + +# WordPress trunk changes daily without a commit here, so this runs on a schedule instead of blocking pull requests. +on: + schedule: + - cron: '0 5 * * *' + workflow_dispatch: + +permissions: + contents: read + +jobs: + integration: + name: Integration + uses: ahegyes/wordpress-configs/.github/workflows/reusable-phpunit.yml@8d53efd292e4abee4a9c57b0e379434f54bedbe9 + with: + php-version: '8.5' + wp-env-core: 'https://wordpress.org/nightly-builds/wordpress-latest.zip' + composer-script: 'test:integration:run' + wp-env-config-file: '.wp-env.tests.json' + + e2e: + name: E2E + uses: ahegyes/wordpress-configs/.github/workflows/reusable-playwright-e2e.yml@8d53efd292e4abee4a9c57b0e379434f54bedbe9 + with: + artifact-slug: nightly + php-version: '8.5' + wp-env-core: 'https://wordpress.org/nightly-builds/wordpress-latest.zip' + wp-env-config-file: '.wp-env.tests.json' diff --git a/plugins/locked-payment-methods-for-woocommerce/.github/workflows/tests.yml b/plugins/locked-payment-methods-for-woocommerce/.github/workflows/tests.yml new file mode 100644 index 0000000..eff35af --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/.github/workflows/tests.yml @@ -0,0 +1,74 @@ +name: Tests + +on: + push: + branches: [trunk] + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + unit: + name: Unit + uses: ahegyes/wordpress-configs/.github/workflows/reusable-phpunit.yml@8d53efd292e4abee4a9c57b0e379434f54bedbe9 + with: + php-version: '8.5' + composer-script: 'test:unit' + needs-wp-env: false + + wordpress: + name: WordPress versions + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + outputs: + versions: ${{ steps.versions.outputs.versions }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + # The floor is the Requires at least header of the entry file composer.json's text domain names, and WordPress's version API offers the newest release of every branch, so new releases, floors and a filled-in slug join without an edit here. + - name: List the newest release of every branch from the floor up + id: versions + run: | + entry="$(jq -r '.extra["text-domain"] // empty' composer.json).php" + floor=$(sed -nE 's|^[[:space:]/*#@]*Requires at least:[[:space:]]*([0-9]+(\.[0-9]+)*).*$|\1|p' "$entry" | head -n 1) + if [ -z "$floor" ]; then + echo "::error::$entry declares no Requires at least header." + exit 1 + fi + offers=$(curl -fsS https://api.wordpress.org/core/version-check/1.7/) + versions=$(printf '%s' "$offers" | jq -c --arg floor "$floor" '[.offers[].version | select((split(".")[0:2] | map(tonumber)) >= ($floor | split(".")[0:2] | map(tonumber)))] | unique | if length == 0 then error("No WordPress release at or above \($floor).") else . end') + echo "versions=$versions" >> "$GITHUB_OUTPUT" + + integration: + name: Integration + needs: wordpress + strategy: + fail-fast: false + matrix: + wp: ${{ fromJSON(needs.wordpress.outputs.versions) }} + uses: ahegyes/wordpress-configs/.github/workflows/reusable-phpunit.yml@8d53efd292e4abee4a9c57b0e379434f54bedbe9 + with: + php-version: '8.5' + wp-version: ${{ matrix.wp }} + composer-script: 'test:integration:run' + wp-env-config-file: '.wp-env.tests.json' + + e2e: + name: E2E + needs: wordpress + strategy: + fail-fast: false + matrix: + wp: ${{ fromJSON(needs.wordpress.outputs.versions) }} + uses: ahegyes/wordpress-configs/.github/workflows/reusable-playwright-e2e.yml@8d53efd292e4abee4a9c57b0e379434f54bedbe9 + with: + artifact-slug: wp-${{ matrix.wp }} + php-version: '8.5' + wp-version: ${{ matrix.wp }} + wp-env-config-file: '.wp-env.tests.json' diff --git a/plugins/locked-payment-methods-for-woocommerce/.github/workflows/workflow-checks.yml b/plugins/locked-payment-methods-for-woocommerce/.github/workflows/workflow-checks.yml new file mode 100644 index 0000000..8ea457c --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/.github/workflows/workflow-checks.yml @@ -0,0 +1,27 @@ +name: Workflow Checks + +on: + push: + branches: [trunk] + paths: ['.github/workflows/**'] + pull_request: + paths: ['.github/workflows/**'] + schedule: + # Weekly Monday 06:00 UTC. Keeps zizmor's online audits (e.g. + # known-vulnerable-actions) fresh between workflow changes. + - cron: '0 6 * * 1' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: {} + +jobs: + checks: + permissions: + contents: read + security-events: write + actions: read + uses: ahegyes/wordpress-configs/.github/workflows/reusable-workflow-checks.yml@8d53efd292e4abee4a9c57b0e379434f54bedbe9 diff --git a/plugins/locked-payment-methods-for-woocommerce/.gitignore b/plugins/locked-payment-methods-for-woocommerce/.gitignore new file mode 100644 index 0000000..9a44d1b --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/.gitignore @@ -0,0 +1,354 @@ +vendor-prefixed +blocks/build +assets/build +tests/.cache +.wp-env.override.json + +# Created by https://www.toptal.com/developers/gitignore/api/node,linux,macos,phpunit,windows,composer,phpstorm+all,visualstudiocode +# Edit at https://www.toptal.com/developers/gitignore?templates=node,linux,macos,phpunit,windows,composer,phpstorm+all,visualstudiocode + +### Composer ### +composer.phar +/vendor/ + +# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control +# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file +# composer.lock + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +### Node Patch ### +# Serverless Webpack directories +.webpack/ + +# Optional stylelint cache + +# SvelteKit build / generate output +.svelte-kit + +### PhpStorm+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### PhpStorm+all Patch ### +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. + +.idea/* + +!.idea/codeStyles +!.idea/runConfigurations + +### PHPUnit ### +# Covers PHPUnit +# Reference: https://phpunit.de/ + +# Generated files +.phpunit.result.cache +.phpunit.cache + +# PHPUnit +/app/phpunit.xml +/phpunit.xml + +# Build data +/build/ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/node,linux,macos,phpunit,windows,composer,phpstorm+all,visualstudiocode diff --git a/plugins/locked-payment-methods-for-woocommerce/.wp-env.json b/plugins/locked-payment-methods-for-woocommerce/.wp-env.json new file mode 100644 index 0000000..129d6be --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/.wp-env.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://schemas.wp.org/trunk/wp-env.schema.json", + "core": "WordPress/WordPress#tags/7.1.3", + "phpVersion": "8.5", + "port": 8826, + "testsEnvironment": false, + "mappings": { + "wp-content/plugins/locked-payment-methods-for-woocommerce": ".", + "wp-content/plugins/woocommerce": "./vendor/wp-plugin/woocommerce" + }, + "config": { + "WP_DEBUG": true, + "SCRIPT_DEBUG": true, + "WP_ENVIRONMENT_TYPE": "local" + }, + "lifecycleScripts": { + "afterStart": "node_modules/.bin/wp-env run cli wp plugin activate woocommerce && node_modules/.bin/wp-env run cli wp wc hpos enable --ignore-plugin-compatibility && node_modules/.bin/wp-env run cli wp plugin activate locked-payment-methods-for-woocommerce" + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/.wp-env.tests.json b/plugins/locked-payment-methods-for-woocommerce/.wp-env.tests.json new file mode 100644 index 0000000..b3f5b0b --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/.wp-env.tests.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://schemas.wp.org/trunk/wp-env.schema.json", + "core": "WordPress/WordPress#tags/7.1.3", + "phpVersion": "8.5", + "port": 8827, + "testsEnvironment": false, + "mappings": { + "wp-content/plugins/locked-payment-methods-for-woocommerce": ".", + "wp-content/plugins/woocommerce": "./vendor/wp-plugin/woocommerce" + }, + "config": { + "WP_DEBUG": true, + "SCRIPT_DEBUG": true, + "WP_ENVIRONMENT_TYPE": false + }, + "lifecycleScripts": { + "afterStart": "node_modules/.bin/wp-env --config .wp-env.tests.json run cli wp plugin activate woocommerce && node_modules/.bin/wp-env --config .wp-env.tests.json run cli wp wc hpos enable --ignore-plugin-compatibility && node_modules/.bin/wp-env --config .wp-env.tests.json run cli wp plugin activate locked-payment-methods-for-woocommerce" + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/AGENTS.md b/plugins/locked-payment-methods-for-woocommerce/AGENTS.md new file mode 100644 index 0000000..c701853 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/AGENTS.md @@ -0,0 +1,74 @@ +# wc-locked-payment-methods + +A WordPress plugin built on the five wp-framework packages (`ahegyes/wp-framework-{bootstrap,shared,core,settings,woocommerce}`), from the template that starts every such plugin. It ships its own copy of the framework, PHP-DI and their dependencies, prefixed under `DeepWebSolutions\LockedPaymentMethods\Scoped` by php-scoper through wordpress-configs into `vendor-prefixed/` on every development install. Its architecture follows the framework's conventions 1–10 in [wordpress-framework's AGENTS.md](https://github.com/ahegyes/wordpress-framework/blob/trunk/AGENTS.md); this file covers what is particular to the plugin. `docs/ARCHITECTURE.md` describes the boot, `docs/recipes.md` the common changes, `tests/README.md` the suites. + +## Layout + +``` +locked-payment-methods-for-woocommerce.php the entry: safeguards, bootstrap load.php, the updater, the floor check, the build gate, the autoloader +functions.php dws_lpmwc_plugin(), the boot on plugins_loaded 15, deactivation, the HPOS declaration (false), the includes/ loader +uninstall.php the framework's uninstall engine over the footprint, with DWS_LPMWC_OPTION_DELETE_DATA's consent +src/ the composition root and its components only +├── Plugin.php SLUG, PREFIX (dws_lpmwc), COMPONENTS, the logger, the scheduler, the migration runner and the kernel +├── Settings.php the WooCommerce settings tab dws_lpmwc: the greeting's fields and the uninstall consent +├── Blocks.php the blocks from blocks/build/blocks-manifest.php and the block-editor script +└── Feature/ the greeting: a composite gated on DWS_LPMWC_OPTION_FEATURE and its account-dashboard child +includes/ hook-free code as dws_lpmwc_ functions and DWS_LPMWC_ constants, loaded in name order, _-prefixed files skipped +├── safeguards.php restrictions that hold whenever the plugin cannot boot; loaded by the entry before the floor check +├── options.php the option keys, their defaults, the typed readers, the translated text and the validator +├── footprint.php dws_lpmwc_get_footprint(), every kind of data the plugin stores +├── migrations.php dws_lpmwc_get_migration_steps() and one function per step, append-only +├── assets.php the *.asset.php reader and the script and style registrars +└── metadata.php the slug, name and version getters, the plugin's surface for a peer plugin +blocks/src/, assets/src/ TypeScript and SCSS, built into blocks/build/ and assets/build/ (untracked) +models/, templates/ empty extension points +.github/scripts/fill-in-scaffold.sh the fill-in's sweep, which fill-in-scaffold.yml runs once in a new plugin and Quality rehearses here +``` + +## Commands + +```sh +composer packages-install # dependencies, then php-scoper into vendor-prefixed/ +composer test:unit +composer test:integration # starts .wp-env.tests.json, then runs test:integration:run, which builds the scripts first +composer quality-check # manifest and changelog validation, PHPCS (both profiles), PHPStan (both profiles), Unit and Integration +npm run setup # both installs, the scripts' build, then the development site +npm run build # blocks and assets through the local wp-scripts; npm run start watches them in a POSIX shell +npm run lint:scripts # ESLint, beside lint:styles (Stylelint) and lint:types (tsc) +npm run test:e2e # Playwright on .wp-env.tests.json, starting it if needed; the scripts must be built +``` + +Run wp-scripts only through the npm scripts or `node_modules/.bin/wp-scripts`: `npx wp-scripts` can install an unrelated package of that name. + +## Environments + +- `.wp-env.json`: development, as `local`, on port 8826, behind `npm run setup` and `wp-env:*`. The framework rethrows boot and migration failures there. +- `.wp-env.tests.json`: Integration and end to end, as `production`, on port 8827, behind `wp-env:tests:*`, `composer test:integration` and `npm run test:e2e`. The end-to-end suite deactivates and reactivates WooCommerce there. + +Both run PHP 8.5 with WooCommerce from `vendor/` and its order tables on; CI runs Integration and end to end on every WordPress branch from the entry's `Requires at least` up, and nightly on WordPress trunk. + +## Conventions + +1. **Placement.** `src/` holds only the composition root and its components. Code that registers no hooks, such as keys, readers, the footprint, migration steps and asset registrars, is `dws_lpmwc_` functions and `DWS_LPMWC_` constants in `includes/`, which parses on PHP 7.4: no `mixed` or union types, typed constants, `match` or first-class callables there. Container definitions are composition and stay in `Plugin::get_definitions()`. +2. **Variants.** A component that exists only in one build variant is a child in a branch of its parent's `get_child_component_classes()`, never a second entry in `COMPONENTS`. +3. **Request context.** `is_admin()` is false on REST requests and true in admin-ajax ones, so an admin-only feature decides the request context in its callbacks, never in `should_load()`. +4. **Add-ons.** An add-on extends a base plugin only through the base plugin's documented hooks. It reads a template-built base plugin's slug, name and version only through that plugin's `{prefix}_get_plugin_*()` getters, from `plugins_loaded` on, since the add-on may load first, and behind `function_exists()`, since a base plugin below its floors or with a partial build may stay active without defining them. +5. **Request reads.** An integer or boolean is read as `filter_var( wp_unslash( $_POST['key'] ?? '' ), FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE ) ?? $default` (`FILTER_VALIDATE_BOOL` for a boolean, where an absent key reads false, as an unchecked checkbox posts nothing), and a string through an `is_string()` guard and the field's sanitizer; never `filter_input()`, the `default` filter option or a cast on request input. +6. **Options and data.** Options are read only through `includes/options.php`'s typed readers, safeguards excepted. Everything stored lives under `dws_lpmwc_`, or `_dws_lpmwc_` for hidden meta. The footprint covers the prefix's options, transients and user meta; object meta needs a `meta_prefixes` entry per type, and anything else its own entry. +7. **Safeguards** live in `includes/safeguards.php`, call only WordPress and that file, and relax only through their own filter, through `dws_lpmwc_is_relaxed()` (`docs/recipes.md`). The entry loads them before the floor check, so they parse on PHP 7.4 and call only WordPress APIs present at the oldest WordPress the bootstrap package runs on, or guard them, which `phpstan.safeguards.neon` checks. +8. **Migrations** are append-only: a step converges from any partial state, verifies its writes and throws when one fails. + +## Style + +- Every PHP file opens with `:`, never `Co-Authored-By`. + +## Limitations + +- The fill-in pushes with the workflow's own token, which GitHub refuses for workflow changes, so it never edits `.github/workflows/`; the workflows read the slug from `composer.json`'s `extra.text-domain`. +- Integration and end to end share the tests site, so a row that changes shared data in a separate process flushes the parent's object cache. +- The release zip carries the TypeScript and SCSS sources beside the built files, since a plugin's repository may be private and the zip is then the only place its users find them. diff --git a/plugins/locked-payment-methods-for-woocommerce/CHANGELOG.md b/plugins/locked-payment-methods-for-woocommerce/CHANGELOG.md new file mode 100644 index 0000000..cc205c3 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +All notable changes to this plugin are documented in this file. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +Pending entries live in [`changelog/`](./changelog) — add via `composer changelog:add`. Aggregate into a release with `composer changelog:write`. + +## 2.0.0 - unreleased + +### Added +- Initial release. diff --git a/plugins/locked-payment-methods-for-woocommerce/CLAUDE.md b/plugins/locked-payment-methods-for-woocommerce/CLAUDE.md new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/plugins/locked-payment-methods-for-woocommerce/LICENSE b/plugins/locked-payment-methods-for-woocommerce/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/plugins/locked-payment-methods-for-woocommerce/README.md b/plugins/locked-payment-methods-for-woocommerce/README.md new file mode 100644 index 0000000..1574192 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/README.md @@ -0,0 +1,73 @@ +# Locked Payment Methods for WooCommerce + +TODO: one sentence saying why this plugin exists, its philosophy rather than a list of what it holds. The same sentence is `composer.json`'s and `package.json`'s `description`, the entry file's `Description` header and the repository's GitHub description. + +## Requirements + +The entry file's headers state the floors: `Requires PHP`, `Requires at least` for WordPress, and `Requires Plugins` with `WC requires at least` for WooCommerce. On a site below them, WordPress shows its own notice and none of the plugin's features load. + +## Installation + +Download `locked-payment-methods-for-woocommerce.zip` from the repository's latest release, then upload it under Plugins → Add New Plugin → Upload Plugin. Later releases arrive through WordPress's own update check, from the repository the entry file's `Update URI` names. + +## Development + +Development needs PHP and Composer at the `Requires PHP` floor or later, Node.js and npm at `package.json`'s `engines`, Docker for `@wordpress/env`, and a POSIX shell for `npm run start`. + +Install the dependencies, prefix the framework into `vendor-prefixed/`, build the scripts and start the development site on : + +```sh +npm run setup +``` + +Rebuild the blocks and assets on every change: + +```sh +npm run start +``` + +Run the manifest and changelog checks, PHPCS, PHPStan, and the Unit and Integration suites, starting the tests site on as needed: + +```sh +composer quality-check +``` + +Install the browser the end-to-end suite runs in, once per machine, then run the suite against the tests site: + +```sh +npx playwright install chromium +npm run test:e2e +``` + +Lint the scripts, styles and types: + +```sh +npm run lint:scripts && npm run lint:styles && npm run lint:types +``` + +`docs/ARCHITECTURE.md` describes how the plugin loads and boots, `docs/recipes.md` holds the step-by-step changes most plugins need, `tests/README.md` describes the test suites, and `AGENTS.md` holds the conventions for people and agents working on the code. + +## After the fill-in + +The fill-in renamed the plugin and kept the template's demo, which boots and passes every suite. Replace or delete each part as the plugin grows its own: + +| Part | Where it lives | Removing it | +| --- | --- | --- | +| The greeting | `src/Feature/`, the Greeting section of `src/Settings.php`, and the `DWS_LPMWC_OPTION_FEATURE`, `DWS_LPMWC_OPTION_GREETING` and `DWS_LPMWC_OPTION_GREETING_ORDERS` keys in `includes/options.php` | Delete `src/Feature/` and its row in `Plugin::COMPONENTS`, the Greeting section of `src/Settings.php`, `assets/src/greeting.scss` and its `greeting=` entry in `package.json`'s `build:assets` and `start:assets`, the three keys with their defaults, text and validator, their lines in `dws_lpmwc_add_settings()`, and `tests/Integration/Feature/`, then the rows naming them in `OptionsTest`, `MigrationsTest` and `SettingsTest`, and `PluginBootTest`'s expectation that the greeting is skipped. `tests/e2e/settings.spec.ts` drives the greeting's validated field, so point it at a validated field of the plugin's own. | +| The example block | `blocks/src/example-notice/`, registered by `src/Blocks.php` from `blocks/build/blocks-manifest.php` | Delete `blocks/`, `build:blocks` and `start:blocks` with their calls in `build` and `start`, `register_blocks()` and its `init` hook in `src/Blocks.php`, and `BlocksTest`'s block rows. | +| The editor script | `assets/src/editor.ts`, enqueued in the block editor by `src/Blocks.php` | Delete it, its `editor=` entry in `build:assets` and `start:assets`, `enqueue_editor_script()` and its hook in `src/Blocks.php`, and `BlocksTest`'s editor row. Once `assets/src/` holds nothing, delete `build:assets` and `start:assets` with their calls in `build` and `start`. Once the plugin registers no blocks either, delete `src/Blocks.php`, its row in `Plugin::COMPONENTS`, `BlocksTest`, and `Blocks` in `PluginBootTest`'s resolved components. | +| `models/` and `templates/` | Empty extension points, for classes on Composer's classmap and for template partials | Delete the folders the plugin never fills, and `models/` from `composer.json`'s `autoload.classmap`, since Composer fails to dump the autoloader for a missing folder. | +| The fill-in | `.github/workflows/fill-in-scaffold.yml`, `.github/scripts/fill-in-scaffold.sh` and Quality's `scaffold` job | Delete all three: GitHub refuses the fill-in's own token for workflow changes, so the fill-in cannot remove them, and the job skips itself outside the template. | + +Whichever parts go, four settings follow what is left, since a plugin removes them in any order: + +- The entry's build gate checks `blocks/build/blocks-manifest.php`, and the plugin stays off until that file exists, so point it at a file the remaining build writes, such as `assets/build/editor.asset.php`, or drop its built-file check once nothing is built. +- `lint:scripts` names only folders that still hold a script, since ESLint fails on a path that holds none. +- `lint:styles` names only folders that still hold SCSS. Once none does, it keeps one pattern, such as `'assets/src/**/*.scss'`, and takes `--allow-empty-input`, since Stylelint fails when nothing matches, Quality runs it, and without a pattern wp-scripts lints every stylesheet in the project. +- `build` stays, even once it runs nothing, since `npm run setup` and the Integration run call it. + +Still to decide by hand: + +- The Composer package name: the fill-in set `ahegyes/locked-payment-methods-for-woocommerce`, while plugins published to Composer use `ahegyes/wc-` for WooCommerce and `ahegyes/wp-` otherwise. +- WooCommerce's order tables: `dws_lpmwc_declare_compatibility()` in `functions.php` declares the plugin incompatible until its order code is verified on both storages. +- `SECURITY.md`'s scope and the issue forms in `.github/ISSUE_TEMPLATE/`, whose wording describes this plugin's demo. diff --git a/plugins/locked-payment-methods-for-woocommerce/SECURITY.md b/plugins/locked-payment-methods-for-woocommerce/SECURITY.md new file mode 100644 index 0000000..bd45895 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/SECURITY.md @@ -0,0 +1,14 @@ +# Security policy + +## Reporting a vulnerability + +Report it privately through [GitHub's security advisory form](https://github.com/ahegyes/wc-locked-payment-methods/security/advisories/new), never in a public issue. The first response comes within 7 days. + +## Scope + +In scope: the plugin's PHP, JavaScript and CSS, its prefixing configuration (`scoper.inc.php`) and its CI workflows. + +Out of scope, reported upstream instead: +- plugins generated from this repository, to their maintainers; +- the wp-framework packages, to `ahegyes/wordpress-framework`; +- WordPress, WooCommerce and the other Composer and npm dependencies, to their projects. Composer refuses to resolve a dependency version with a known advisory through `roave/security-advisories`. The Supply-Chain Audit workflow fails on an advisory anywhere in the Composer lock, and on a high-severity one among the npm runtime dependencies, of which the template has none; advisories in the npm development tools reach Dependabot alerts instead. diff --git a/plugins/locked-payment-methods-for-woocommerce/assets/index.php b/plugins/locked-payment-methods-for-woocommerce/assets/index.php new file mode 100644 index 0000000..f767346 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/assets/index.php @@ -0,0 +1 @@ + + ): HTMLAttributes< HTMLElement >; + + export namespace useBlockProps { + function save( + props?: HTMLAttributes< HTMLElement > + ): HTMLAttributes< HTMLElement >; + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/block.json b/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/block.json new file mode 100644 index 0000000..0b4f6df --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/block.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 3, + "name": "locked-payment-methods-for-woocommerce/example-notice", + "title": "Example Notice", + "category": "widgets", + "icon": "info", + "description": "Displays a static example notice.", + "attributes": {}, + "example": {}, + "supports": { + "html": false + }, + "textdomain": "locked-payment-methods-for-woocommerce", + "editorScript": "file:./index.js", + "editorStyle": "file:./index.css", + "style": "file:./style-index.css" +} diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/edit.tsx b/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/edit.tsx new file mode 100644 index 0000000..90b0382 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/edit.tsx @@ -0,0 +1,15 @@ +import { useBlockProps } from '@wordpress/block-editor'; +import { __ } from '@wordpress/i18n'; + +import './editor.scss'; + +export default function Edit() { + return ( +

+ { __( + 'An example notice from Locked Payment Methods for WooCommerce.', + 'locked-payment-methods-for-woocommerce' + ) } +

+ ); +} diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/editor.scss b/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/editor.scss new file mode 100644 index 0000000..dd5ac10 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/editor.scss @@ -0,0 +1,3 @@ +.wp-block-locked-payment-methods-for-woocommerce-example-notice { + outline: 1px dashed currentcolor; +} diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/index.php b/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/index.php new file mode 100644 index 0000000..f767346 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/index.php @@ -0,0 +1 @@ + + An example notice from Locked Payment Methods for WooCommerce. +

+ ); +} diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/style.scss b/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/style.scss new file mode 100644 index 0000000..dd43537 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/style.scss @@ -0,0 +1,8 @@ +.wp-block-locked-payment-methods-for-woocommerce-example-notice { + padding: 0.5em 1em; + border-left: 4px solid currentcolor; + + &.is-style-outlined { + border: 1px solid currentcolor; + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/src/index.php b/plugins/locked-payment-methods-for-woocommerce/blocks/src/index.php new file mode 100644 index 0000000..f767346 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/blocks/src/index.php @@ -0,0 +1 @@ +=8.5" + }, + "require-dev": { + "ahegyes/wordpress-configs": "dev-trunk", + "ahegyes/wp-framework-bootstrap": "^2.0", + "ahegyes/wp-framework-core": "^2.0", + "ahegyes/wp-framework-settings": "^2.0", + "ahegyes/wp-framework-shared": "^2.0", + "ahegyes/wp-framework-woocommerce": "^2.0", + "automattic/jetpack-changelogger": "^7", + "humbug/php-scoper": "^0.18", + "johnpbloch/wordpress-core": "^7.1", + "laravel/serializable-closure": "^2.1", + "php-di/php-di": "^7.1.1", + "php-stubs/woocommerce-stubs": "^11.2", + "phpunit/phpunit": "^13", + "wp-plugin/woocommerce": "^11.2" + }, + + "minimum-stability": "dev", + "prefer-stable": true, + + "autoload": { + "psr-4": { + "DeepWebSolutions\\LockedPaymentMethods\\": "src/" + }, + "classmap": [ + "models/" + ], + "files": [ + "vendor-prefixed/scoper-autoload.php" + ] + }, + "autoload-dev": { + "psr-4": { + "DeepWebSolutions\\LockedPaymentMethods\\Tests\\": "tests/" + } + }, + + "scripts": { + "post-autoload-dump": [ + "DeepWebSolutions\\Config\\Composer\\ScopePhpDependencies::postAutoloadDump" + ], + + "generate-autoloader": "@composer dump-autoload -o", + "packages-install": "@composer install --ignore-platform-req=php+ --no-interaction", + "packages-update": [ + "@composer clear-cache", + "@composer update --prefer-stable --ignore-platform-req=php+ --no-interaction" + ], + + "format:php": "phpcbf --standard=./phpcs.dist.xml --basepath=. ./ -v", + "format:php:tests": "phpcbf --standard=./phpcs.tests.dist.xml --basepath=. ./tests -v", + "lint:php": [ + "@lint:php:phpcs", + "@lint:php:phpcs:tests", + "@lint:php:phpstan" + ], + "lint:php:phpcs": "phpcs --standard=./phpcs.dist.xml --basepath=. ./ -v", + "lint:php:phpcs:tests": "phpcs --standard=./phpcs.tests.dist.xml --basepath=. ./tests -v", + "lint:php:phpstan": [ + "phpstan analyse -v --memory-limit=1G", + "phpstan analyse -c phpstan.safeguards.neon -v --memory-limit=1G" + ], + + "test": [ + "@test:unit", + "@test:integration" + ], + "test:unit": "@php vendor/bin/phpunit --testsuite=Unit", + "test:integration": [ + "Composer\\Config::disableProcessTimeout", + "@putenv CI=1", + "npm --prefix=. run wp-env:tests:start", + "@test:integration:run" + ], + "test:integration:run": [ + "Composer\\Config::disableProcessTimeout", + "@putenv CI=1", + "npm --prefix=. run build", + "node_modules/.bin/wp-env --config .wp-env.tests.json run cli --env-cwd=wp-content/plugins/locked-payment-methods-for-woocommerce vendor/bin/phpunit --testsuite=Integration" + ], + + "quality-check": [ + "@composer validate --strict", + "@changelog:validate", + "@lint:php", + "@test" + ], + + "changelog:add": "@php vendor/bin/changelogger add", + "changelog:validate": "@php vendor/bin/changelogger validate", + "changelog:write": "@php vendor/bin/changelogger write" + }, + + "extra": { + "text-domain": "locked-payment-methods-for-woocommerce", + "scoping-prefix": "DeepWebSolutions\\LockedPaymentMethods\\Scoped", + "installer-paths": { + "vendor/{$vendor}/{$name}/": [ + "type:wordpress-plugin", + "type:wordpress-theme" + ] + }, + "phpstan/extension-installer": { + "ignore": [ + "johnbillion/wp-compat", + "swissspidy/phpstan-no-private", + "szepeviktor/phpstan-wordpress" + ] + }, + "changelogger": { + "versioning": "semver", + "changelog": "CHANGELOG.md", + "changes-dir": "changelog", + "default-type": "changed", + "types": { + "added": "Added", + "changed": "Changed", + "deprecated": "Deprecated", + "removed": "Removed", + "fixed": "Fixed", + "security": "Security" + } + } + }, + + "config": { + "allow-plugins": { + "composer/installers": true, + "dealerdirect/phpcodesniffer-composer-installer": true, + "phpstan/extension-installer": true + }, + "platform-check": false, + "sort-packages": true + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/composer.lock b/plugins/locked-payment-methods-for-woocommerce/composer.lock new file mode 100644 index 0000000..fa7b31b --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/composer.lock @@ -0,0 +1,7338 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "76e70b93657e7e7295ec4f69e7784112", + "packages": [], + "packages-dev": [ + { + "name": "ahegyes/wordpress-configs", + "version": "dev-trunk", + "source": { + "type": "git", + "url": "https://github.com/ahegyes/wordpress-configs.git", + "reference": "8d53efd292e4abee4a9c57b0e379434f54bedbe9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ahegyes/wordpress-configs/zipball/8d53efd292e4abee4a9c57b0e379434f54bedbe9", + "reference": "8d53efd292e4abee4a9c57b0e379434f54bedbe9", + "shasum": "" + }, + "require": { + "composer/class-map-generator": "^1", + "ext-json": "*", + "johnbillion/wp-compat": "^3", + "nikic/php-parser": "^5", + "php": ">=8.5", + "php-stubs/wordpress-stubs": "^7.1", + "phpcompatibility/phpcompatibility-wp": "^3@alpha", + "phpstan/extension-installer": "^1", + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "roave/security-advisories": "dev-latest", + "sniccowp/php-scoper-wordpress-excludes": "^7.1", + "swissspidy/phpstan-no-private": "^1", + "symfony/filesystem": "^7.4", + "symfony/finder": "^7.4", + "szepeviktor/phpstan-wordpress": "^2", + "wp-coding-standards/wpcs": "^3.4.1" + }, + "require-dev": { + "composer/composer": "^2", + "dompdf/dompdf": "^3.1", + "humbug/php-scoper": "^0.18", + "infection/infection": "^0.35", + "php-di/php-di": "^7", + "php-stubs/woocommerce-stubs": "^11.1", + "phpunit/phpunit": "^13", + "psr/container": "^2", + "psr/log": "^3", + "symfony/yaml": "^8.1" + }, + "suggest": { + "humbug/php-scoper": "Required for the dependency-scoping pipeline (ScopePhpDependencies + the scoper-base config)." + }, + "default-branch": true, + "type": "library", + "autoload": { + "classmap": [ + "php/composer", + "php/phpstan" + ] + }, + "autoload-dev": { + "psr-4": { + "DeepWebSolutions\\Config\\Tests\\": "tests/" + }, + "exclude-from-classmap": [ + "/tests/fixtures/" + ] + }, + "scripts": { + "generate-autoloader": [ + "@composer dump-autoload --ignore-platform-req=php+ -o" + ], + "packages-install": [ + "@composer install --ignore-platform-req=php+ --no-interaction" + ], + "packages-update": [ + "@composer clear-cache", + "@composer update --prefer-stable --ignore-platform-req=php+ --no-interaction" + ], + "format:php": [ + "phpcbf --standard=./phpcs.dist.xml --basepath=. ./ -v" + ], + "format:php:tests": [ + "phpcbf --standard=./phpcs.tests.dist.xml --basepath=. ./tests -v" + ], + "lint:php": [ + "@lint:php:phpcs", + "@lint:php:phpcs:tests", + "@lint:php:phpstan" + ], + "lint:php:phpcs": [ + "phpcs --standard=./phpcs.dist.xml --basepath=. ./ -v" + ], + "lint:php:phpcs:tests": [ + "phpcs --standard=./phpcs.tests.dist.xml --basepath=. ./tests -v" + ], + "lint:php:phpstan": [ + "phpstan analyse -v --memory-limit=1G" + ], + "test": [ + "@test:unit" + ], + "test:unit": [ + "@php vendor/bin/phpunit --testsuite=Unit" + ], + "test:unit:mutation": [ + "@php vendor/bin/infection --threads=max --show-mutations=0" + ], + "test:all": [ + "@test:unit", + "@test:unit:mutation" + ], + "quality-check": [ + "@lint:php", + "@test" + ], + "quality-check:all": [ + "@lint:php", + "@test:all" + ] + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Contributors", + "homepage": "https://github.com/ahegyes/wordpress-configs/graphs/contributors" + } + ], + "description": "Shared PHPCS, PHPStan and php-scoper configurations and reusable GitHub Actions workflows for WordPress projects.", + "support": { + "source": "https://github.com/ahegyes/wordpress-configs/tree/trunk", + "issues": "https://github.com/ahegyes/wordpress-configs/issues" + }, + "time": "2026-10-30T11:34:20+00:00" + }, + { + "name": "ahegyes/wp-framework-bootstrap", + "version": "dev-trunk", + "source": { + "type": "git", + "url": "https://github.com/ahegyes/wp-framework-bootstrap.git", + "reference": "6f48ed4ed852ba35a06ef46dcfc104170732a189" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ahegyes/wp-framework-bootstrap/zipball/6f48ed4ed852ba35a06ef46dcfc104170732a189", + "reference": "6f48ed4ed852ba35a06ef46dcfc104170732a189", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "default-branch": true, + "type": "library", + "extra": { + "branch-alias": { + "dev-trunk": "2.x-dev" + }, + "requires-wp": "5.8" + }, + "license": [ + "GPL-3.0-or-later" + ], + "description": "The code a WordPress plugin runs before its autoloader. It parses on PHP 7.4, so a site below the plugin's floors gets a notice instead of a fatal error and still receives the release that fixes it.", + "homepage": "https://github.com/ahegyes/wordpress-framework", + "support": { + "source": "https://github.com/ahegyes/wp-framework-bootstrap/tree/trunk" + }, + "time": "2026-10-30T10:32:53+00:00" + }, + { + "name": "ahegyes/wp-framework-core", + "version": "dev-trunk", + "source": { + "type": "git", + "url": "https://github.com/ahegyes/wp-framework-core.git", + "reference": "6d087554f21ccc65ac52583d8d9e7de542c84cfd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ahegyes/wp-framework-core/zipball/6d087554f21ccc65ac52583d8d9e7de542c84cfd", + "reference": "6d087554f21ccc65ac52583d8d9e7de542c84cfd", + "shasum": "" + }, + "require": { + "ahegyes/wp-framework-bootstrap": "^2.0", + "ahegyes/wp-framework-shared": "^2.0", + "ext-filter": "*", + "php": ">=8.5", + "psr/clock": "^1", + "psr/container": "^2.0.2", + "psr/log": "^3.0.2", + "psr/simple-cache": "^3" + }, + "default-branch": true, + "type": "library", + "extra": { + "branch-alias": { + "dev-trunk": "2.x-dev" + }, + "requires-wp": "7.1", + "scoping-text-domain": "wp-framework" + }, + "autoload": { + "psr-4": { + "DeepWebSolutions\\Framework\\Core\\": "src/" + } + }, + "license": [ + "GPL-3.0-or-later" + ], + "description": "The plugin kernel. A plugin is composed of small components that load only where they apply, on WordPress's own APIs, with the infrastructure WordPress leaves every plugin to rebuild.", + "homepage": "https://github.com/ahegyes/wordpress-framework", + "support": { + "source": "https://github.com/ahegyes/wp-framework-core/tree/trunk" + }, + "time": "2026-10-30T10:32:53+00:00" + }, + { + "name": "ahegyes/wp-framework-settings", + "version": "dev-trunk", + "source": { + "type": "git", + "url": "https://github.com/ahegyes/wp-framework-settings.git", + "reference": "5942e878af9f8536698c724116ceef5b50948e24" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ahegyes/wp-framework-settings/zipball/5942e878af9f8536698c724116ceef5b50948e24", + "reference": "5942e878af9f8536698c724116ceef5b50948e24", + "shasum": "" + }, + "require": { + "ahegyes/wp-framework-core": "^2.0", + "php": ">=8.5" + }, + "default-branch": true, + "type": "library", + "extra": { + "branch-alias": { + "dev-trunk": "2.x-dev" + }, + "requires-wp": "7.1", + "scoping-text-domain": "wp-framework" + }, + "autoload": { + "psr-4": { + "DeepWebSolutions\\Framework\\Settings\\": "src/" + } + }, + "license": [ + "GPL-3.0-or-later" + ], + "description": "Settings screens and edit-screen fields declared as data, so WordPress's own APIs render, validate and save them and a plugin never hand-writes form handling.", + "homepage": "https://github.com/ahegyes/wordpress-framework", + "support": { + "source": "https://github.com/ahegyes/wp-framework-settings/tree/trunk" + }, + "time": "2026-10-30T10:12:55+00:00" + }, + { + "name": "ahegyes/wp-framework-shared", + "version": "dev-trunk", + "source": { + "type": "git", + "url": "https://github.com/ahegyes/wp-framework-shared.git", + "reference": "72568a6fd790b49926f6f74e4dd2478e31d91b8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ahegyes/wp-framework-shared/zipball/72568a6fd790b49926f6f74e4dd2478e31d91b8a", + "reference": "72568a6fd790b49926f6f74e4dd2478e31d91b8a", + "shasum": "" + }, + "require": { + "php": ">=8.5" + }, + "default-branch": true, + "type": "library", + "extra": { + "branch-alias": { + "dev-trunk": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "DeepWebSolutions\\Framework\\Shared\\": "src/" + } + }, + "license": [ + "GPL-3.0-or-later" + ], + "description": "The WordPress-free building blocks every framework package shares, kept portable enough to work unchanged in any PHP project.", + "homepage": "https://github.com/ahegyes/wordpress-framework", + "support": { + "source": "https://github.com/ahegyes/wp-framework-shared/tree/trunk" + }, + "time": "2026-10-30T10:12:55+00:00" + }, + { + "name": "ahegyes/wp-framework-woocommerce", + "version": "dev-trunk", + "source": { + "type": "git", + "url": "https://github.com/ahegyes/wp-framework-woocommerce.git", + "reference": "9dd04e7c2c0e99160a231ce3a0f17a443a492631" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ahegyes/wp-framework-woocommerce/zipball/9dd04e7c2c0e99160a231ce3a0f17a443a492631", + "reference": "9dd04e7c2c0e99160a231ce3a0f17a443a492631", + "shasum": "" + }, + "require": { + "ahegyes/wp-framework-settings": "^2.0", + "php": ">=8.5" + }, + "default-branch": true, + "type": "library", + "extra": { + "branch-alias": { + "dev-trunk": "2.x-dev" + }, + "scoping-stubs": [ + "php-stubs/woocommerce-stubs" + ], + "scoping-text-domain": "wp-framework", + "requires-wp": "7.1", + "requires-plugins": [ + "woocommerce" + ] + }, + "autoload": { + "psr-4": { + "DeepWebSolutions\\Framework\\WooCommerce\\": "src/" + } + }, + "license": [ + "GPL-3.0-or-later" + ], + "description": "The framework's settings, edit-screen and logging helpers carried onto WooCommerce's own screens and APIs, for plugins that extend WooCommerce.", + "homepage": "https://github.com/ahegyes/wordpress-framework", + "support": { + "source": "https://github.com/ahegyes/wp-framework-woocommerce/tree/trunk" + }, + "time": "2026-10-30T10:12:55+00:00" + }, + { + "name": "automattic/jetpack-changelogger", + "version": "v7.0.5", + "source": { + "type": "git", + "url": "https://github.com/Automattic/jetpack-changelogger.git", + "reference": "9a71296eeab91596a032a3bad09898827011b218" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Automattic/jetpack-changelogger/zipball/9a71296eeab91596a032a3bad09898827011b218", + "reference": "9a71296eeab91596a032a3bad09898827011b218", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.2.0", + "php": ">=7.4", + "symfony/console": "^5.4 || ^6.4 || ^7.1", + "symfony/process": "^5.4 || ^6.4 || ^7.1" + }, + "require-dev": { + "wikimedia/testing-access-wrapper": "^3.0 || ^4.0", + "yoast/phpunit-polyfills": "^4.0.0" + }, + "bin": [ + "bin/changelogger" + ], + "type": "project", + "extra": { + "mirror-repo": "Automattic/jetpack-changelogger", + "branch-alias": { + "dev-trunk": "7.0.x-dev" + }, + "changelogger": { + "link-template": "https://github.com/Automattic/jetpack-changelogger/compare/${old}...${new}" + }, + "dependencies": { + "test-only": [ + "packages/phpunit-select-config" + ] + }, + "version-constants": { + "::VERSION": "src/Application.php" + } + }, + "autoload": { + "psr-4": { + "Automattic\\Jetpack\\Changelog\\": "lib", + "Automattic\\Jetpack\\Changelogger\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "description": "Jetpack Changelogger tool. Allows for managing changelogs by dropping change files into a changelog directory with each PR.", + "keywords": [ + "changelog", + "cli", + "dev", + "keepachangelog" + ], + "support": { + "source": "https://github.com/Automattic/jetpack-changelogger/tree/v7.0.5" + }, + "time": "2026-10-20T16:01:37+00:00", + "published-time": "2026-10-20T16:01:50+00:00" + }, + { + "name": "composer/class-map-generator", + "version": "1.8.0", + "source": { + "type": "git", + "url": "https://github.com/composer/class-map-generator.git", + "reference": "c0efbeb2f3fd7e23c7f180a59765c0b48a31fbc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/class-map-generator/zipball/c0efbeb2f3fd7e23c7f180a59765c0b48a31fbc4", + "reference": "c0efbeb2f3fd7e23c7f180a59765c0b48a31fbc4", + "shasum": "" + }, + "require": { + "composer/pcre": "^2.1 || ^3.1", + "php": "^7.2 || ^8.0", + "symfony/finder": "^4.4 || ^5.3 || ^6 || ^7 || ^8" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-deprecation-rules": "^1 || ^2", + "phpstan/phpstan-phpunit": "^1 || ^2", + "phpstan/phpstan-strict-rules": "^1.1 || ^2", + "phpunit/phpunit": "^8", + "symfony/filesystem": "^5.4 || ^6 || ^7 || ^8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\ClassMapGenerator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Utilities to scan PHP code and generate class maps.", + "keywords": [ + "classmap" + ], + "support": { + "issues": "https://github.com/composer/class-map-generator/issues", + "source": "https://github.com/composer/class-map-generator/tree/1.8.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } + ], + "time": "2026-09-25T15:24:19+00:00", + "published-time": "2026-09-25T15:27:30+00:00" + }, + { + "name": "composer/installers", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/composer/installers.git", + "reference": "12fb2dfe5e16183de69e784a7b84046c43d97e8e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/installers/zipball/12fb2dfe5e16183de69e784a7b84046c43d97e8e", + "reference": "12fb2dfe5e16183de69e784a7b84046c43d97e8e", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "composer/composer": "^1.10.27 || ^2.7", + "composer/semver": "^1.7.2 || ^3.4.0", + "phpstan/phpstan": "^1.11", + "phpstan/phpstan-phpunit": "^1", + "symfony/phpunit-bridge": "^7.1.1", + "symfony/process": "^5 || ^6 || ^7" + }, + "type": "composer-plugin", + "extra": { + "class": "Composer\\Installers\\Plugin", + "branch-alias": { + "dev-main": "2.x-dev" + }, + "plugin-modifies-install-path": true + }, + "autoload": { + "psr-4": { + "Composer\\Installers\\": "src/Composer/Installers" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kyle Robinson Young", + "email": "kyle@dontkry.com", + "homepage": "https://github.com/shama" + } + ], + "description": "A multi-framework Composer library installer", + "homepage": "https://composer.github.io/installers/", + "keywords": [ + "Dolibarr", + "Eliasis", + "Hurad", + "ImageCMS", + "Kanboard", + "Lan Management System", + "MODX Evo", + "MantisBT", + "Mautic", + "Maya", + "OXID", + "Plentymarkets", + "Porto", + "RadPHP", + "SMF", + "Starbug", + "Thelia", + "Whmcs", + "WolfCMS", + "agl", + "annotatecms", + "attogram", + "bitrix", + "cakephp", + "chef", + "cockpit", + "codeigniter", + "concrete5", + "concreteCMS", + "croogo", + "dokuwiki", + "drupal", + "eZ Platform", + "elgg", + "expressionengine", + "fuelphp", + "grav", + "installer", + "itop", + "known", + "kohana", + "laravel", + "lavalite", + "lithium", + "magento", + "majima", + "mako", + "matomo", + "mediawiki", + "miaoxing", + "modulework", + "modx", + "moodle", + "osclass", + "pantheon", + "phpbb", + "piwik", + "ppi", + "processwire", + "puppet", + "pxcms", + "reindex", + "roundcube", + "shopware", + "silverstripe", + "sydes", + "sylius", + "tastyigniter", + "wordpress", + "yawik", + "zend", + "zikula" + ], + "support": { + "issues": "https://github.com/composer/installers/issues", + "source": "https://github.com/composer/installers/tree/v2.3.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-06-24T20:46:46+00:00", + "published-time": "2024-06-24T21:01:36+00:00" + }, + { + "name": "composer/pcre", + "version": "3.4.0", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "d5a341b3fb61f3001970940afb1d332968a183ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/d5a341b3fb61f3001970940afb1d332968a183ed", + "reference": "d5a341b3fb61f3001970940afb1d332968a183ed", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<2.2.2" + }, + "require-dev": { + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^9" + }, + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.4.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } + ], + "time": "2026-06-07T11:47:49+00:00", + "published-time": "2026-06-11T12:12:37+00:00" + }, + { + "name": "dealerdirect/phpcodesniffer-composer-installer", + "version": "v1.2.1", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/composer-installer.git", + "reference": "963f0c67bffde0eac41b56be71ac0e8ba132f0bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/963f0c67bffde0eac41b56be71ac0e8ba132f0bd", + "reference": "963f0c67bffde0eac41b56be71ac0e8ba132f0bd", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.2", + "php": ">=5.4", + "squizlabs/php_codesniffer": "^3.1.0 || ^4.0" + }, + "require-dev": { + "composer/composer": "^2.2", + "ext-json": "*", + "ext-zip": "*", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpcompatibility/php-compatibility": "^9.0 || ^10.0.0@dev", + "yoast/phpunit-polyfills": "^1.0" + }, + "type": "composer-plugin", + "extra": { + "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + }, + "autoload": { + "psr-4": { + "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Franck Nijhof", + "email": "opensource@frenck.dev", + "homepage": "https://frenck.dev", + "role": "Open source developer" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer Standards Composer Installer Plugin", + "keywords": [ + "PHPCodeSniffer", + "PHP_CodeSniffer", + "code quality", + "codesniffer", + "composer", + "installer", + "phpcbf", + "phpcs", + "plugin", + "qa", + "quality", + "standard", + "standards", + "style guide", + "stylecheck", + "tests" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/composer-installer/issues", + "security": "https://github.com/PHPCSStandards/composer-installer/security/policy", + "source": "https://github.com/PHPCSStandards/composer-installer" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2026-05-06T08:26:05+00:00", + "published-time": "2026-05-06T12:56:03+00:00" + }, + { + "name": "fidry/console", + "version": "0.6.11", + "source": { + "type": "git", + "url": "https://github.com/theofidry/console.git", + "reference": "bea8316beae874fc5b8be679d67dd3169c7e205f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/console/zipball/bea8316beae874fc5b8be679d67dd3169c7e205f", + "reference": "bea8316beae874fc5b8be679d67dd3169c7e205f", + "shasum": "" + }, + "require": { + "php": "^8.2", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "symfony/console": "^6.4 || ^7.2", + "symfony/deprecation-contracts": "^3.4", + "symfony/event-dispatcher-contracts": "^2.5 || ^3.0", + "symfony/polyfill-php84": "^1.31", + "symfony/service-contracts": "^2.5 || ^3.0", + "thecodingmachine/safe": "^2.0 || ^3.0", + "webmozart/assert": "^1.11" + }, + "conflict": { + "symfony/dependency-injection": "<6.4.0 || >=7.0.0 <7.2.0", + "symfony/framework-bundle": "<6.4.0 || >=7.0.0 <7.2.0", + "symfony/http-kernel": "<6.4.0 || >=7.0.0 <7.2.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "composer/semver": "^3.3.2", + "ergebnis/composer-normalize": "^2.33", + "fidry/makefile": "^0.2.1 || ^1.0.0", + "infection/infection": "^0.28", + "phpunit/phpunit": "^10.2", + "symfony/dependency-injection": "^6.4 || ^7.2", + "symfony/flex": "^2.4.0", + "symfony/framework-bundle": "^6.4 || ^7.2", + "symfony/http-kernel": "^6.4 || ^7.2", + "symfony/yaml": "^6.4 || ^7.2" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": false, + "forward-command": false + }, + "branch-alias": { + "dev-main": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Fidry\\Console\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo Fidry", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Library to create CLI applications", + "keywords": [ + "cli", + "console", + "symfony" + ], + "support": { + "issues": "https://github.com/theofidry/console/issues", + "source": "https://github.com/theofidry/console/tree/0.6.11" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2025-02-14T11:06:15+00:00", + "published-time": "2025-02-14T11:09:06+00:00" + }, + { + "name": "fidry/filesystem", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/filesystem.git", + "reference": "d0d9e8dfa43f7663da153c306b0d5bc24846ad8e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/filesystem/zipball/d0d9e8dfa43f7663da153c306b0d5bc24846ad8e", + "reference": "d0d9e8dfa43f7663da153c306b0d5bc24846ad8e", + "shasum": "" + }, + "require": { + "php": "^8.3", + "symfony/filesystem": "^6.4 || ^7.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4", + "ergebnis/composer-normalize": "^2.28", + "infection/infection": ">=0.26", + "phpunit/phpunit": "^12", + "symfony/finder": "^6.4 || ^7.0" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": false, + "forward-command": false + }, + "branch-alias": { + "dev-main": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Fidry\\FileSystem\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Théo Fidry", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Symfony Filesystem with a few more utilities.", + "keywords": [ + "filesystem" + ], + "support": { + "issues": "https://github.com/theofidry/filesystem/issues", + "source": "https://github.com/theofidry/filesystem/tree/1.3.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2025-02-13T23:05:19+00:00", + "published-time": "2025-02-13T23:09:29+00:00" + }, + { + "name": "humbug/php-scoper", + "version": "0.18.20", + "source": { + "type": "git", + "url": "https://github.com/humbug/php-scoper.git", + "reference": "f1991b803eb84b534b8b2aa1b477c170d1b611e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/humbug/php-scoper/zipball/f1991b803eb84b534b8b2aa1b477c170d1b611e1", + "reference": "f1991b803eb84b534b8b2aa1b477c170d1b611e1", + "shasum": "" + }, + "require": { + "fidry/console": "^0.6.10", + "fidry/filesystem": "^1.1", + "jetbrains/phpstorm-stubs": "^2026.2", + "nikic/php-parser": "^5.0", + "php": "^8.2", + "symfony/console": "^6.4 || ^7.4", + "symfony/filesystem": "^6.4 || ^7.4", + "symfony/finder": "^6.4 || ^7.4", + "symfony/polyfill-iconv": "^1.33", + "symfony/polyfill-mbstring": "^1.33", + "symfony/var-dumper": "^7.1", + "thecodingmachine/safe": "^3.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.1", + "ergebnis/composer-normalize": "^2.28", + "fidry/makefile": "^1.0", + "humbug/box": "^4.6.2", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^10.0 || ^11.0", + "symfony/yaml": "^6.4 || ^7.4" + }, + "bin": [ + "bin/php-scoper" + ], + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": false, + "forward-command": false + }, + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Humbug\\PhpScoper\\": "src/" + }, + "classmap": [ + "vendor-hotfix/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Théo Fidry", + "email": "theo.fidry@gmail.com" + }, + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com" + } + ], + "description": "Prefixes all PHP namespaces in a file or directory.", + "support": { + "issues": "https://github.com/humbug/php-scoper/issues", + "source": "https://github.com/humbug/php-scoper/tree/0.18.20" + }, + "time": "2026-10-08T08:21:05+00:00", + "published-time": "2026-10-08T08:29:48+00:00" + }, + { + "name": "jetbrains/phpstorm-stubs", + "version": "v2026.2", + "source": { + "type": "git", + "url": "https://github.com/JetBrains/phpstorm-stubs", + "reference": "709e512210784a7c0a677b3a89d35def844a59b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/JetBrains/phpstorm-stubs/zipball/709e512210784a7c0a677b3a89d35def844a59b9", + "reference": "709e512210784a7c0a677b3a89d35def844a59b9", + "shasum": "" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^v3.86", + "nikic/php-parser": "^v5.6", + "phpdocumentor/reflection-docblock": "^5.6", + "phpunit/phpunit": "^12.3" + }, + "type": "library", + "autoload": { + "files": [ + "PhpStormStubsMap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "PHP runtime & extensions header files for PhpStorm", + "homepage": "https://www.jetbrains.com/phpstorm", + "keywords": [ + "autocomplete", + "code", + "inference", + "inspection", + "jetbrains", + "phpstorm", + "stubs", + "type" + ], + "time": "2026-06-12T13:19:10+00:00", + "published-time": "2026-07-22T11:41:13+00:00" + }, + { + "name": "johnbillion/wp-compat", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/johnbillion/wp-compat.git", + "reference": "5bc1ad5c9c953f89271de607c7e3a8de8589371c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/johnbillion/wp-compat/zipball/5bc1ad5c9c953f89271de607c7e3a8de8589371c", + "reference": "5bc1ad5c9c953f89271de607c7e3a8de8589371c", + "shasum": "" + }, + "require": { + "php": ">= 7.2.24", + "phpstan/phpstan": "^1.12 || ^2.0", + "wp-hooks/wordpress-core": "^1.13" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "nikic/php-parser": "^4.18 || ^5.1", + "phpstan/phpstan-deprecation-rules": "^1.0 || ^2.0", + "phpstan/phpstan-phpunit": "^1.0 || ^2.0", + "phpstan/phpstan-strict-rules": "^1.0 || ^2.0", + "phpunit/phpunit": "^8.5 || ^9.0", + "roots/wordpress": "*", + "wp-coding-standards/wpcs": "3.4.1" + }, + "suggest": { + "phpstan/phpstan-deprecation-rules": "PHPStan rules for detecting usage of deprecated symbols", + "swissspidy/phpstan-no-private": "PHPStan rules for detecting usage of pseudo-private functions, classes, and methods", + "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "wordpress-install-dir": "vendor/wordpress/wordpress" + }, + "autoload": { + "psr-4": { + "WPCompat\\PHPStan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Blackbourn", + "homepage": "https://johnblackbourn.com/" + } + ], + "description": "PHPStan extension to help verify that your PHP code is compatible with a given version of WordPress", + "keywords": [ + "PHPStan", + "wordpress" + ], + "support": { + "issues": "https://github.com/johnbillion/wp-compat/issues", + "source": "https://github.com/johnbillion/wp-compat" + }, + "funding": [ + { + "url": "https://github.com/sponsors/johnbillion", + "type": "github" + } + ], + "time": "2026-10-21T20:54:48+00:00", + "published-time": "2026-10-21T20:54:55+00:00" + }, + { + "name": "johnpbloch/wordpress-core", + "version": "7.1.4", + "source": { + "type": "git", + "url": "https://github.com/johnpbloch/wordpress-core.git", + "reference": "36ac9875ff3f09afe1d91f996bfac0ac261922ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/johnpbloch/wordpress-core/zipball/36ac9875ff3f09afe1d91f996bfac0ac261922ed", + "reference": "36ac9875ff3f09afe1d91f996bfac0ac261922ed", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=7.4" + }, + "provide": { + "wordpress/core-implementation": "7.1.4" + }, + "type": "wordpress-core", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "WordPress Community", + "homepage": "https://wordpress.org/about/" + } + ], + "description": "WordPress is open source software you can use to create a beautiful website, blog, or app.", + "homepage": "https://wordpress.org/", + "keywords": [ + "blog", + "cms", + "wordpress" + ], + "support": { + "forum": "https://wordpress.org/support/", + "irc": "irc://irc.freenode.net/wordpress", + "issues": "https://core.trac.wordpress.org/", + "source": "https://core.trac.wordpress.org/browser", + "wiki": "https://codex.wordpress.org/" + }, + "time": "2026-10-28T19:49:26+00:00", + "published-time": "2026-10-28T19:49:51+00:00" + }, + { + "name": "laravel/serializable-closure", + "version": "v2.1.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/serializable-closure.git", + "reference": "2d5869a838bbcf37e0d8b0568fc41914e81374b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/2d5869a838bbcf37e0d8b0568fc41914e81374b5", + "reference": "2d5869a838bbcf37e0d8b0568fc41914e81374b5", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", + "nesbot/carbon": "^2.67|^3.0", + "pestphp/pest": "^2.36|^3.0|^4.0", + "phpstan/phpstan": "^2.0", + "symfony/var-dumper": "^6.2.0|^7.0.0|^8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\SerializableClosure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "nuno@laravel.com" + } + ], + "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", + "keywords": [ + "closure", + "laravel", + "serializable" + ], + "support": { + "issues": "https://github.com/laravel/serializable-closure/issues", + "source": "https://github.com/laravel/serializable-closure" + }, + "time": "2026-09-22T14:32:34+00:00", + "published-time": "2026-09-22T14:33:13+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.14.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "8680aa248f8e07bc8fb43f56f0f5fc77a0c96aae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/8680aa248f8e07bc8fb43f56f0f5fc77a0c96aae", + "reference": "8680aa248f8e07bc8fb43f56f0f5fc77a0c96aae", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.14.0" + }, + "funding": [ + { + "url": "https://github.com/mnapoli", + "type": "github" + } + ], + "time": "2026-08-11T10:17:44+00:00", + "published-time": "2026-08-11T10:29:20+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.9.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "9e33da9553fe7786f0962b35f4e4ecf01be89def" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/9e33da9553fe7786f0962b35f4e4ecf01be89def", + "reference": "9e33da9553fe7786f0962b35f4e4ecf01be89def", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.9.0" + }, + "time": "2026-09-13T18:51:52+00:00", + "published-time": "2026-09-13T18:53:16+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00", + "published-time": "2024-03-03T12:39:44+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00", + "published-time": "2022-02-21T01:04:30+00:00" + }, + { + "name": "php-di/invoker", + "version": "2.3.7", + "source": { + "type": "git", + "url": "https://github.com/PHP-DI/Invoker.git", + "reference": "3c1ddfdef181431fbc4be83378f6d036d59e81e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/3c1ddfdef181431fbc4be83378f6d036d59e81e1", + "reference": "3c1ddfdef181431fbc4be83378f6d036d59e81e1", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "psr/container": "^1.0|^2.0" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "mnapoli/hard-mode": "~0.3.0", + "phpunit/phpunit": "^9.0 || ^10 || ^11 || ^12" + }, + "type": "library", + "autoload": { + "psr-4": { + "Invoker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Generic and extensible callable invoker", + "homepage": "https://github.com/PHP-DI/Invoker", + "keywords": [ + "callable", + "dependency", + "dependency-injection", + "injection", + "invoke", + "invoker" + ], + "support": { + "issues": "https://github.com/PHP-DI/Invoker/issues", + "source": "https://github.com/PHP-DI/Invoker/tree/2.3.7" + }, + "funding": [ + { + "url": "https://github.com/mnapoli", + "type": "github" + } + ], + "time": "2025-08-30T10:22:22+00:00", + "published-time": "2025-08-30T10:22:43+00:00" + }, + { + "name": "php-di/php-di", + "version": "7.1.1", + "source": { + "type": "git", + "url": "https://github.com/PHP-DI/PHP-DI.git", + "reference": "f88054cc052e40dbe7b383c8817c19442d480352" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/f88054cc052e40dbe7b383c8817c19442d480352", + "reference": "f88054cc052e40dbe7b383c8817c19442d480352", + "shasum": "" + }, + "require": { + "laravel/serializable-closure": "^1.0 || ^2.0", + "php": ">=8.0", + "php-di/invoker": "^2.0", + "psr/container": "^1.1 || ^2.0" + }, + "provide": { + "psr/container-implementation": "^1.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3", + "friendsofphp/proxy-manager-lts": "^1", + "mnapoli/phpunit-easymock": "^1.3", + "phpunit/phpunit": "^9.6 || ^10 || ^11", + "vimeo/psalm": "^5|^6" + }, + "suggest": { + "friendsofphp/proxy-manager-lts": "Install it if you want to use lazy injection (version ^1)" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "DI\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The dependency injection container for humans", + "homepage": "https://php-di.org/", + "keywords": [ + "PSR-11", + "container", + "container-interop", + "dependency injection", + "di", + "ioc", + "psr11" + ], + "support": { + "issues": "https://github.com/PHP-DI/PHP-DI/issues", + "source": "https://github.com/PHP-DI/PHP-DI/tree/7.1.1" + }, + "funding": [ + { + "url": "https://github.com/mnapoli", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/php-di/php-di", + "type": "tidelift" + } + ], + "time": "2025-08-16T11:10:48+00:00", + "published-time": "2025-08-16T11:11:08+00:00" + }, + { + "name": "php-stubs/woocommerce-stubs", + "version": "v11.2.0", + "source": { + "type": "git", + "url": "https://github.com/php-stubs/woocommerce-stubs.git", + "reference": "fb590c1c2a26c47aa5da2c6c51a707130e8a389c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-stubs/woocommerce-stubs/zipball/fb590c1c2a26c47aa5da2c6c51a707130e8a389c", + "reference": "fb590c1c2a26c47aa5da2c6c51a707130e8a389c", + "shasum": "" + }, + "require": { + "php-stubs/wordpress-stubs": "^5.3 || ^6.0 || ^7.0" + }, + "require-dev": { + "php": "~7.1 || ~8.0", + "php-stubs/generator": "^0.9.0" + }, + "suggest": { + "symfony/polyfill-php73": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "WooCommerce function and class declaration stubs for static analysis.", + "homepage": "https://github.com/php-stubs/woocommerce-stubs", + "keywords": [ + "PHPStan", + "static analysis", + "woocommerce", + "wordpress" + ], + "support": { + "issues": "https://github.com/php-stubs/woocommerce-stubs/issues", + "source": "https://github.com/php-stubs/woocommerce-stubs/tree/v11.2.0" + }, + "time": "2026-10-07T19:02:59+00:00", + "published-time": "2026-10-07T19:04:59+00:00" + }, + { + "name": "php-stubs/wordpress-stubs", + "version": "v7.1.2", + "source": { + "type": "git", + "url": "https://github.com/php-stubs/wordpress-stubs.git", + "reference": "6926c5156431a3ff909d43faa16a92a0bc0c2428" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/6926c5156431a3ff909d43faa16a92a0bc0c2428", + "reference": "6926c5156431a3ff909d43faa16a92a0bc0c2428", + "shasum": "" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "5.6.1" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "nikic/php-parser": "^5.5", + "php": "^7.4 || ^8.0", + "php-stubs/generator": "^0.9", + "phpdocumentor/reflection-docblock": "^6.0", + "phpstan/phpstan": "^2.2", + "phpunit/phpunit": "^9.5", + "symfony/polyfill-php80": "*", + "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^1.2", + "wp-coding-standards/wpcs": "3.4.1 as 2.3.0" + }, + "suggest": { + "paragonie/sodium_compat": "Pure PHP implementation of libsodium", + "symfony/polyfill-php80": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "WordPress function and class declaration stubs for static analysis.", + "homepage": "https://github.com/php-stubs/wordpress-stubs", + "keywords": [ + "PHPStan", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/php-stubs/wordpress-stubs/issues", + "source": "https://github.com/php-stubs/wordpress-stubs/tree/v7.1.2" + }, + "time": "2026-10-06T06:59:21+00:00", + "published-time": "2026-10-06T07:32:21+00:00" + }, + { + "name": "phpcompatibility/php-compatibility", + "version": "10.0.0-alpha2", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibility.git", + "reference": "e0f0e5a3dc819a4a0f8d679a0f2453d941976e18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/e0f0e5a3dc819a4a0f8d679a0f2453d941976e18", + "reference": "e0f0e5a3dc819a4a0f8d679a0f2453d941976e18", + "shasum": "" + }, + "require": { + "php": ">=5.4", + "phpcsstandards/phpcsutils": "^1.1.2", + "squizlabs/php_codesniffer": "^3.13.3 || ^4.0" + }, + "replace": { + "wimg/php-compatibility": "*" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpcsstandards/phpcsdevcs": "^1.2.0", + "phpcsstandards/phpcsdevtools": "^1.2.3", + "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4 || ^10.5.32 || ^11.3.3", + "yoast/phpunit-polyfills": "^1.1.5 || ^2.0.5 || ^3.1.0" + }, + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev", + "dev-develop": "10.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "homepage": "https://github.com/wimg", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors" + } + ], + "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.", + "homepage": "https://techblog.wimgodden.be/tag/codesniffer/", + "keywords": [ + "compatibility", + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues", + "security": "https://github.com/PHPCompatibility/PHPCompatibility/security/policy", + "source": "https://github.com/PHPCompatibility/PHPCompatibility" + }, + "funding": [ + { + "url": "https://github.com/PHPCompatibility", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcompatibility", + "type": "thanks_dev" + } + ], + "time": "2025-11-28T11:36:33+00:00", + "published-time": "2025-11-28T11:41:33+00:00" + }, + { + "name": "phpcompatibility/phpcompatibility-paragonie", + "version": "2.0.0-alpha2", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git", + "reference": "7a979711c87d8202b52f56c56bd719d09d8ed7f5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/7a979711c87d8202b52f56c56bd719d09d8ed7f5", + "reference": "7a979711c87d8202b52f56c56bd719d09d8ed7f5", + "shasum": "" + }, + "require": { + "phpcompatibility/php-compatibility": "^10.0@dev" + }, + "require-dev": { + "paragonie/random_compat": "dev-master", + "paragonie/sodium_compat": "dev-master" + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "lead" + } + ], + "description": "A set of rulesets for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by the Paragonie polyfill libraries.", + "homepage": "http://phpcompatibility.com/", + "keywords": [ + "compatibility", + "paragonie", + "phpcs", + "polyfill", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/issues", + "security": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/security/policy", + "source": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie" + }, + "funding": [ + { + "url": "https://github.com/PHPCompatibility", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcompatibility", + "type": "thanks_dev" + } + ], + "time": "2025-11-29T13:09:49+00:00", + "published-time": "2025-11-29T13:22:17+00:00" + }, + { + "name": "phpcompatibility/phpcompatibility-wp", + "version": "3.0.0-alpha2", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git", + "reference": "bd53f24e7528422ac51d64dc8d53e8d4c4a877b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/bd53f24e7528422ac51d64dc8d53e8d4c4a877b3", + "reference": "bd53f24e7528422ac51d64dc8d53e8d4c4a877b3", + "shasum": "" + }, + "require": { + "phpcompatibility/php-compatibility": "^10.0@dev", + "phpcompatibility/phpcompatibility-paragonie": "^2.0@dev" + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "lead" + } + ], + "description": "A ruleset for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by WordPress.", + "homepage": "http://phpcompatibility.com/", + "keywords": [ + "compatibility", + "phpcs", + "standards", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibilityWP/issues", + "security": "https://github.com/PHPCompatibility/PHPCompatibilityWP/security/policy", + "source": "https://github.com/PHPCompatibility/PHPCompatibilityWP" + }, + "funding": [ + { + "url": "https://github.com/PHPCompatibility", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcompatibility", + "type": "thanks_dev" + } + ], + "time": "2025-12-16T13:35:20+00:00", + "published-time": "2025-12-16T13:47:29+00:00" + }, + { + "name": "phpcsstandards/phpcsextra", + "version": "1.5.1", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHPCSExtra.git", + "reference": "39467533fdb742446d68c1d10ac33d625ee0311c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/39467533fdb742446d68c1d10ac33d625ee0311c", + "reference": "39467533fdb742446d68c1d10ac33d625ee0311c", + "shasum": "" + }, + "require": { + "php": ">=5.4", + "phpcsstandards/phpcsutils": "^1.2.3", + "squizlabs/php_codesniffer": "^3.13.5 || ^4.0.1" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpcsstandards/phpcsdevcs": "^1.2.0", + "phpcsstandards/phpcsdevtools": "^1.2.1", + "phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" + }, + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-stable": "1.x-dev", + "dev-develop": "1.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHPCSExtra/graphs/contributors" + } + ], + "description": "A collection of sniffs and standards for use with PHP_CodeSniffer.", + "keywords": [ + "PHP_CodeSniffer", + "phpcbf", + "phpcodesniffer-standard", + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/PHPCSExtra/issues", + "security": "https://github.com/PHPCSStandards/PHPCSExtra/security/policy", + "source": "https://github.com/PHPCSStandards/PHPCSExtra" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2026-07-27T11:13:17+00:00", + "published-time": "2026-07-27T11:19:59+00:00" + }, + { + "name": "phpcsstandards/phpcsutils", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHPCSUtils.git", + "reference": "5f35d9408c54d7b529501f3c688b6eae562aea1f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/5f35d9408c54d7b529501f3c688b6eae562aea1f", + "reference": "5f35d9408c54d7b529501f3c688b6eae562aea1f", + "shasum": "" + }, + "require": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0", + "php": ">=5.4", + "squizlabs/php_codesniffer": "^3.13.5 || ^4.0.1" + }, + "require-dev": { + "ext-filter": "*", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpcsstandards/phpcsdevcs": "^1.2.0", + "yoast/phpunit-polyfills": "^1.1.0 || ^2.0.0 || ^3.0.0" + }, + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-stable": "1.x-dev", + "dev-develop": "1.x-dev" + } + }, + "autoload": { + "classmap": [ + "PHPCSUtils/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHPCSUtils/graphs/contributors" + } + ], + "description": "A suite of utility functions for use with PHP_CodeSniffer", + "homepage": "https://phpcsutils.com/", + "keywords": [ + "PHP_CodeSniffer", + "phpcbf", + "phpcodesniffer-standard", + "phpcs", + "phpcs3", + "phpcs4", + "standards", + "static analysis", + "tokens", + "utility" + ], + "support": { + "docs": "https://phpcsutils.com/", + "issues": "https://github.com/PHPCSStandards/PHPCSUtils/issues", + "security": "https://github.com/PHPCSStandards/PHPCSUtils/security/policy", + "source": "https://github.com/PHPCSStandards/PHPCSUtils" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2026-07-27T10:28:41+00:00", + "published-time": "2026-07-27T10:36:31+00:00" + }, + { + "name": "phpstan/extension-installer", + "version": "1.4.3", + "source": { + "type": "git", + "url": "https://github.com/phpstan/extension-installer.git", + "reference": "85e90b3942d06b2326fba0403ec24fe912372936" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/85e90b3942d06b2326fba0403ec24fe912372936", + "reference": "85e90b3942d06b2326fba0403ec24fe912372936", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0", + "php": "^7.2 || ^8.0", + "phpstan/phpstan": "^1.9.0 || ^2.0" + }, + "require-dev": { + "composer/composer": "^2.0", + "php-parallel-lint/php-parallel-lint": "^1.2.0", + "phpstan/phpstan-strict-rules": "^0.11 || ^0.12 || ^1.0" + }, + "type": "composer-plugin", + "extra": { + "class": "PHPStan\\ExtensionInstaller\\Plugin" + }, + "autoload": { + "psr-4": { + "PHPStan\\ExtensionInstaller\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Composer plugin for automatic installation of PHPStan extensions", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpstan/extension-installer/issues", + "source": "https://github.com/phpstan/extension-installer/tree/1.4.3" + }, + "time": "2024-09-04T20:21:43+00:00", + "published-time": "2024-09-04T20:22:27+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "2.3.4", + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/f20ce6f3c82556cfcfc086b152445a12d1d64fff", + "reference": "f20ce6f3c82556cfcfc086b152445a12d1d64fff", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "OndÅ™ej Mirtes" + }, + { + "name": "Markus Staab" + }, + { + "name": "Vincent Langlet" + } + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2026-10-28T22:38:53+00:00", + "published-time": "2026-10-28T22:39:06+00:00" + }, + { + "name": "phpstan/phpstan-deprecation-rules", + "version": "2.0.5", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan-deprecation-rules.git", + "reference": "67bedd65c24bc72840afc45aed48b1059dd44bec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/67bedd65c24bc72840afc45aed48b1059dd44bec", + "reference": "67bedd65c24bc72840afc45aed48b1059dd44bec", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.1.39" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6", + "shipmonk/name-collision-detector": "^2.1" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "PHPStan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan rules for detecting usage of deprecated classes, methods, properties, constants and traits.", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues", + "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/2.0.5" + }, + "time": "2026-07-22T06:50:43+00:00", + "published-time": "2026-07-27T15:18:00+00:00" + }, + { + "name": "phpstan/phpstan-strict-rules", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan-strict-rules.git", + "reference": "24ec51589f896f048d0d36de373d993b5cf71786" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/24ec51589f896f048d0d36de373d993b5cf71786", + "reference": "24ec51589f896f048d0d36de373d993b5cf71786", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.3" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6", + "shipmonk/name-collision-detector": "^2.1" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "PHPStan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Extra strict and opinionated rules for PHPStan", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", + "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.1.0" + }, + "time": "2026-10-01T21:30:37+00:00", + "published-time": "2026-10-06T07:32:29+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "14.5.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "1db2f40af7a6538742e4a634b71ee556d6219be7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/1db2f40af7a6538742e4a634b71ee556d6219be7", + "reference": "1db2f40af7a6538742e4a634b71ee556d6219be7", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.9.0", + "php": ">=8.4", + "phpunit/php-text-template": "^6.0.1", + "sebastian/complexity": "^6.0", + "sebastian/environment": "^9.3.3", + "sebastian/git-state": "^1.1.0", + "sebastian/lines-of-code": "^5.0.2", + "sebastian/version": "^7.0.1" + }, + "require-dev": { + "phpunit/phpunit": "^13.4.5" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "14.5.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/14.5.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" + } + ], + "time": "2026-10-30T08:11:15+00:00", + "published-time": "2026-10-30T08:11:34+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "7.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "9bb4e6c58b62c1e043be995c66abec7c97307aae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/9bb4e6c58b62c1e043be995c66abec7c97307aae", + "reference": "9bb4e6c58b62c1e043be995c66abec7c97307aae", + "shasum": "" + }, + "require": { + "php": ">=8.4" + }, + "require-dev": { + "phpunit/phpunit": "^13.3.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/7.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-file-iterator", + "type": "tidelift" + } + ], + "time": "2026-08-25T14:47:43+00:00", + "published-time": "2026-08-25T14:48:49+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "42e5c5cae0c65df12d1b1a3ab52bf3f50f244d88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/42e5c5cae0c65df12d1b1a3ab52bf3f50f244d88", + "reference": "42e5c5cae0c65df12d1b1a3ab52bf3f50f244d88", + "shasum": "" + }, + "require": { + "php": ">=8.4" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^13.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-invoker", + "type": "tidelift" + } + ], + "time": "2026-02-06T04:34:47+00:00", + "published-time": "2026-02-06T04:35:29+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "6.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "97107691cb2b6c743b5e0c44be19e983cb337a02" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/97107691cb2b6c743b5e0c44be19e983cb337a02", + "reference": "97107691cb2b6c743b5e0c44be19e983cb337a02", + "shasum": "" + }, + "require": { + "php": ">=8.4" + }, + "require-dev": { + "phpunit/phpunit": "^13.3.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/6.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-text-template", + "type": "tidelift" + } + ], + "time": "2026-10-03T04:54:24+00:00", + "published-time": "2026-10-03T04:54:39+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "9.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "a0e12065831f6ab0d83120dc61513eb8d9a966f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/a0e12065831f6ab0d83120dc61513eb8d9a966f6", + "reference": "a0e12065831f6ab0d83120dc61513eb8d9a966f6", + "shasum": "" + }, + "require": { + "php": ">=8.4" + }, + "require-dev": { + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "9.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/9.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-timer", + "type": "tidelift" + } + ], + "time": "2026-02-06T04:37:53+00:00", + "published-time": "2026-02-06T04:38:39+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "13.4.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "941457a4f147a60fe2074d40e1fce0d08ea8d930" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/941457a4f147a60fe2074d40e1fce0d08ea8d930", + "reference": "941457a4f147a60fe2074d40e1fce0d08ea8d930", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-filter": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.14.0", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.4.1", + "phpunit/php-code-coverage": "^14.4.1", + "phpunit/php-file-iterator": "^7.0.2", + "phpunit/php-invoker": "^7.0.0", + "phpunit/php-text-template": "^6.0.1", + "phpunit/php-timer": "^9.0.0", + "sebastian/cli-parser": "^5.0.1", + "sebastian/comparator": "^8.4", + "sebastian/diff": "^9.0.1", + "sebastian/environment": "^9.3.3", + "sebastian/exporter": "^8.2.1", + "sebastian/file-filter": "^1.0", + "sebastian/git-state": "^1.1", + "sebastian/global-state": "^9.0.1", + "sebastian/object-enumerator": "^8.1.0", + "sebastian/recursion-context": "^8.0.1", + "sebastian/type": "^7.0.2", + "sebastian/version": "^7.0.1", + "staabm/side-effects-detector": "^1.0.5" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "13.4-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/13.4.5" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsoring.html", + "type": "other" + } + ], + "time": "2026-10-27T04:16:22+00:00", + "published-time": "2026-10-27T04:18:40+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00", + "published-time": "2022-11-25T14:59:22+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00", + "published-time": "2021-11-05T16:53:27+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00", + "published-time": "2019-03-25T15:31:44+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00", + "published-time": "2024-09-11T13:21:14+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00", + "published-time": "2021-10-29T13:27:19+00:00" + }, + { + "name": "roave/security-advisories", + "version": "dev-latest", + "source": { + "type": "git", + "url": "https://github.com/Roave/SecurityAdvisories.git", + "reference": "7c964bc969c1b16050a4b85b7a55614777a3f597" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/7c964bc969c1b16050a4b85b7a55614777a3f597", + "reference": "7c964bc969c1b16050a4b85b7a55614777a3f597", + "shasum": "" + }, + "conflict": { + "3f/pygmentize": "<1.2", + "adaptcms/adaptcms": "<=1.3", + "adawolfa/isdoc": "<1.4.3|>=1.5,<1.5.1|>=1.6,<1.6.1", + "admidio/admidio": "<=5.0.11", + "adminneo-org/adminneo": "<5.5.1", + "adodb/adodb-php": "<=5.22.9", + "aheinze/cockpit": "<2.2", + "aimeos/ai-admin-graphql": ">=2022.04.1,<2022.10.10|>=2023.04.1,<2023.10.6|>=2024.04.1,<2024.07.2", + "aimeos/ai-admin-jsonadm": "<2020.10.13|>=2021.04.1,<2021.10.6|>=2022.04.1,<2022.10.3|>=2023.04.1,<2023.10.4|==2024.04.1", + "aimeos/ai-client-html": ">=2020.04.1,<2020.10.27|>=2021.04.1,<2021.10.22|>=2022.04.1,<2022.10.13|>=2023.04.1,<2023.10.15|>=2024.04.1,<2024.04.7", + "aimeos/ai-cms-grapesjs": ">=2021.04.1,<2021.10.8|>=2022.04.1,<2022.10.9|>=2023.04.1,<2023.10.15|>=2024.04.1,<2024.10.8|>=2025.04.1,<2025.10.2", + "aimeos/ai-controller-frontend": "<2020.10.15|>=2021.04.1,<2021.10.8|>=2022.04.1,<2022.10.8|>=2023.04.1,<2023.10.9|==2024.04.1", + "aimeos/aimeos-core": ">=2022.04.1,<2022.10.17|>=2023.04.1,<2023.10.17|>=2024.04.1,<2024.04.7", + "aimeos/aimeos-laravel": "==2021.10", + "aimeos/aimeos-typo3": "<19.10.12|>=20,<20.10.5", + "aimeos/pagible": "<0.10.4", + "airesvsg/acf-to-rest-api": "<=3.1", + "akaunting/akaunting": "<2.1.13", + "akeneo/pim-community-dev": "<5.0.119|>=6,<6.0.53", + "alextselegidis/easyappointments": "<=1.5.2", + "alexusmai/laravel-file-manager": "<=3.3.1", + "algolia/algoliasearch-magento-2": "<=3.16.1|>=3.17.0.0-beta1,<=3.17.1", + "almirhodzic/nova-toggle-5": "<1.3", + "alt-design/alt-redirect": "<1.6.4", + "altcha-org/altcha": "<1.3.1", + "alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1", + "amazing/media2click": ">=1,<1.3.3", + "ameos/ameos_filemanager": "<3.1.3", + "ameos/ameos_tarteaucitron": "<1.2.23", + "amphp/artax": "<1.0.6|>=2,<2.0.6", + "amphp/http": "<=1.7.2|>=2,<=2.1", + "amphp/http-client": ">=4,<4.4", + "amphp/http-server": ">=2.0.0.0-RC1,<2.1.10|>=3.0.0.0-beta1,<3.4.4", + "anchorcms/anchor-cms": "<=0.12.7", + "andreapollastri/cipi": "<=3.1.15", + "andrewhaine/silverstripe-form-capture": ">=0.2,<=0.2.3|>=1,<1.0.2|>=2,<2.2.5", + "aoe/restler": "<1.7.1", + "apache-solr-for-typo3/solr": "<11.2.8|>=11.5,<11.6.6|>=12,<12.1.4|>=13,<13.1.4", + "apache/thrift": "<0.24", + "apereo/phpcas": "<1.6", + "api-platform/core": "<4.1.30|>=4.2,<4.2.26|>=4.3,<4.3.12", + "api-platform/graphql": "<3.4.17|>=4,<4.0.22|>=4.1,<4.1.5", + "api-platform/hal": ">=4,<4.1.29|>=4.2,<4.2.25|>=4.3,<4.3.8", + "api-platform/json-api": ">=4,<4.1.29|>=4.2,<4.2.25|>=4.3,<4.3.8", + "appwrite/server-ce": "<=1.2.1", + "aptoma/twig-markdown": "<=3.4.1", + "arc/web": "<3", + "area17/twill": "<1.2.5|>=2,<2.5.3", + "artesaos/seotools": "<0.17.2", + "asymmetricrypt/asymmetricrypt": "<9.9.99", + "athlon1600/php-proxy": "<=5.1", + "athlon1600/php-proxy-app": "<=3", + "athlon1600/youtube-downloader": "<=4", + "aureuserp/aureuserp": "<1.3.0.0-beta1", + "austintoddj/canvas": "<=3.4.2", + "auth0/auth0-php": ">=3.3,<=8.18", + "auth0/login": "<=7.20", + "auth0/symfony": "<=5.8", + "auth0/wordpress": "<=5.5", + "automad/automad": "<=2.0.0.0-beta27", + "automattic/jetpack": "<9.8", + "awesome-support/awesome-support": "<=6.0.7", + "aws/aws-sdk-php": "<=3.371.3", + "ayacoo/redirect-tab": "<2.1.2|>=3,<3.1.7|>=4,<4.0.5", + "azuracast/azuracast": "<=0.23.5", + "b13/seo_basics": "<0.8.2", + "backdrop/backdrop": "<=1.32", + "backpack/crud": "<6.8.15|>=7,<7.0.47", + "backpack/filemanager": "<2.0.2|>=3,<3.0.9", + "bacula-web/bacula-web": "<9.7.1", + "badaso/core": "<=2.9.11", + "bagisto/bagisto": "<=2.3.15", + "barrelstrength/sprout-base-email": "<1.2.7", + "barrelstrength/sprout-forms": "<3.9", + "barryvdh/laravel-translation-manager": "<0.6.8", + "barzahlen/barzahlen-php": "<2.0.1", + "baserproject/basercms": "<=5.2.2", + "bassjobsen/bootstrap-3-typeahead": ">4.0.2", + "bbpress/bbpress": "<2.6.5", + "bcit-ci/codeigniter": "<3.1.3", + "bcosca/fatfree": "<3.7.2", + "bedita/bedita": "<4", + "bednee/cooluri": "<1.0.30", + "bigfork/silverstripe-form-capture": ">=3,<3.1.1", + "billabear/billabear": "<=2025.01.03", + "billz/raspap-webgui": "<3.3.6", + "binarytorch/larecipe": "<2.8.1", + "bk2k/bootstrap-package": ">=7.1,<7.1.2|>=8,<8.0.8|>=9,<9.0.4|>=9.1,<9.1.3|>=10,<10.0.10|>=11,<11.0.3", + "blueimp/jquery-file-upload": "==6.4.4", + "blueways/bw-captcha": ">=4,<5.4.2", + "bmarshall511/wordpress_zero_spam": "<5.2.13", + "bolt/bolt": "<3.7.2", + "bolt/core": "<=4.2", + "born05/craft-twofactorauthentication": "<3.3.4", + "bottelet/flarepoint": "<2.2.1", + "bref/bref": "<2.1.17", + "brightlocal/phpwhois": "<=4.2.5", + "brotkrueml/codehighlight": "<2.7", + "brotkrueml/schema": "<1.13.1|>=2,<2.5.1", + "brotkrueml/typo3-matomo-integration": "<1.3.2", + "buddypress/buddypress": "<14.5", + "bugsnag/bugsnag-laravel": ">=2,<2.0.2", + "bvbmedia/multishop": "<2.0.39", + "bytefury/crater": "<6.0.2", + "cachethq/cachet": "<2.5.1", + "cadmium-org/cadmium-cms": "<=0.4.9", + "cakephp/authentication": "<2.11.1|>=3,<3.3.6|>=4,<4.1.1", + "cakephp/cakephp": "<4.5.12|>=4.6,<4.6.5|>=5,<5.1.10|>=5.2,<5.2.15|>=5.3,<5.3.7", + "cakephp/database": ">=3,<4.5.12|>=4.6,<4.6.5|>=5,<5.1.10|>=5.2,<5.2.15|>=5.3,<5.3.7", + "cakephp/debug_kit": "<4.10.3|>=5,<5.2.4", + "cakephp/queue": ">=0.1.10,<2.3.1", + "cardgate/magento2": "<2.0.33", + "cardgate/woocommerce": "<=3.1.15", + "cart2quote/module-quotation": ">=4.1.6,<4.4.6|>=5,<5.4.4", + "cartalyst/sentry": "<2.1.7", + "catfan/medoo": "<1.7.5", + "causal/fal-protect": ">=1.7.1,<1.8.2", + "causal/oidc": "<4", + "cecil/cecil": "<7.47.1", + "centreon/centreon": "<22.10.15", + "cesargb/laravel-magiclink": ">=2,<2.25.1", + "cesnet/simplesamlphp-module-proxystatistics": "<3.1", + "chamilo/chamilo-lms": "<=2", + "chriskacerguis/codeigniter-restserver": "<=2.7.1", + "chrome-php/chrome": "<1.14", + "ci4-cms-erp/ci4ms": "<=0.31.8", + "civicrm/civicrm-core": ">=4.2,<4.2.9|>=4.3,<4.3.3", + "ckeditor/ckeditor": "<4.25", + "clickstorm/cs-seo": ">=6,<6.8|>=7,<7.5|>=8,<8.4|>=9,<9.3", + "co-stack/fal_sftp": "<0.2.6", + "cockpit-hq/cockpit": "<=2.14", + "code16/sharp": "<9.22.5", + "codeception/codeception": "<3.1.3|>=4,<4.1.22", + "codeigniter/framework": "<3.1.10", + "codeigniter4/framework": "<4.7.4", + "codeigniter4/shield": "<1.0.0.0-beta8", + "codiad/codiad": "<=2.8.4", + "codingms/additional-tca": ">=1.7,<1.15.17|>=1.16,<1.16.9", + "codingms/fluid-form": "<5.0.10|>=6,<6.0.6", + "codingms/modules": "<7.10.4|>=8,<8.1.4", + "codingms/shop": "<4.9.4|>=5,<5.21.1|>=6,<6.3", + "commerceteam/commerce": ">=0.9.6,<0.9.9", + "components/jquery": ">=1.0.3,<3.5", + "composer/composer": "<2.2.30|>=2.3,<2.10.3", + "concrete5/concrete5": "<9.5.2", + "concrete5/core": "<8.5.8|>=9,<9.1", + "contao-components/colorbox": ">=1,<1.6.4.3-dev", + "contao-components/mediaelement": ">=2.14.2,<2.21.1", + "contao/comments-bundle": ">=2,<5.3.50|>=5.4,<5.7.12", + "contao/contao": ">=3,<3.5.37|>=4,<5.3.50|>=5.4,<5.7.12", + "contao/core": "<3.5.39", + "contao/core-bundle": "<5.3.50|>=5.4,<5.7.12", + "contao/listing-bundle": ">=3,<=3.5.30|>=4,<4.4.8", + "contao/managed-edition": "<=1.5", + "contao/newsletter-bundle": ">=5,<5.3.50|>=5.4,<5.7.12", + "coreshop/core-shop": "<4.1.9|==5", + "corveda/phpsandbox": "<1.3.5", + "cosenary/instagram": "<=2.3", + "cotonti/cotonti": "<=1", + "couleurcitron/tarteaucitron-wp": "<0.3", + "cpsit/typo3-mailqueue": "<0.4.5|>=0.5,<0.5.2", + "craftcms/aws-s3": ">=2.0.2,<=2.2.4", + "craftcms/azure-blob": ">=2.0.0.0-beta1,<=2.1", + "craftcms/cms": "<4.18.3|>=5,<5.10.8", + "craftcms/commerce": ">=4,<=4.11.1|>=5,<=5.6.4", + "craftcms/composer": ">=4.0.0.0-RC1,<=4.10|>=5.0.0.0-RC1,<=5.5.1", + "craftcms/craft": ">=3.5,<=4.16.17|>=5.0.0.0-RC1,<=5.8.21", + "craftcms/google-cloud": ">=2.0.0.0-beta1,<=2.2", + "craftcms/webhooks": ">=3,<3.2", + "croogo/croogo": "<=4.0.7", + "cuyz/valinor": "<0.12", + "czim/file-handling": "<1.5|>=2,<2.3", + "czproject/git-php": "<4.0.3", + "damienharper/auditor-bundle": "<5.2.6", + "dapphp/securimage": "<3.6.6", + "darylldoyle/safe-svg": "<1.9.10", + "datadog/dd-trace": ">=0.30,<0.30.2", + "datahihi1/tiny-env": "<1.0.3|>=1.0.9,<1.0.11", + "datatables/datatables": "<1.10.10", + "david-garcia/phpwhois": "<=4.3.1", + "dbrisinajumi/d2files": "<1", + "dcat/laravel-admin": "<=2.1.3|==2.2.0.0-beta|==2.2.2.0-beta", + "dedoc/scramble": ">=0.13.2,<0.13.22", + "derhansen/fe_change_pwd": "<2.0.5|>=3,<3.0.3", + "derhansen/sf_event_mgt": "<5.9.3|>=6,<6.7.2|>=7,<7.9.3|>=8,<8.6.2|>=9,<9.0.3", + "desperado/xml-bundle": "<=0.1.7", + "dev-lancer/minecraft-motd-parser": "<=1.0.5", + "devcode-it/openstamanager": "<=2.10.1", + "devgroup/dotplant": "<2020.09.14-dev", + "digimix/wp-svg-upload": "<=1", + "directmailteam/direct-mail": "<6.0.3|>=7,<7.0.3|>=8,<9.5.2", + "directorytree/imapengine": "<1.22.3", + "dl/yag": "<3.0.1", + "dmk/webkitpdf": "<1.1.4", + "dnadesign/silverstripe-elemental": "<5.3.12", + "doctrine/annotations": "<1.2.7", + "doctrine/cache": ">=1,<1.3.2|>=1.4,<1.4.2", + "doctrine/common": "<2.4.3|>=2.5,<2.5.1", + "doctrine/dbal": ">=2,<2.0.8|>=2.1,<2.1.2|>=3,<3.1.4", + "doctrine/doctrine-bundle": "<1.5.2", + "doctrine/doctrine-module": "<0.7.2", + "doctrine/mongodb-odm": "<1.0.2", + "doctrine/mongodb-odm-bundle": "<3.0.1", + "doctrine/orm": ">=1,<1.2.4|>=2,<2.4.8|>=2.5,<2.5.1|>=2.8.3,<2.8.4", + "dolibarr/dolibarr": "<=23.0.2", + "dompdf/dompdf": "<3.1.6", + "doublethreedigital/guest-entries": "<3.1.2", + "dreamfactory/df-core": "<1.0.4", + "drupal-pattern-lab/add-attributes-twig-extension": "<=1.0.1", + "drupal-pattern-lab/bem-twig-extension": "<=1.0.1", + "drupal-pattern-lab/unified-twig-extensions": "<=0.1", + "drupal/access_code": "<2.0.5", + "drupal/acquia_dam": "<1.1.5", + "drupal/admin_audit_trail": "<1.0.5", + "drupal/ai": "<1.0.5", + "drupal/alogin": "<2.0.6", + "drupal/cache_utility": "<1.2.1", + "drupal/civictheme": "<1.12", + "drupal/commerce_alphabank_redirect": "<1.0.3", + "drupal/commerce_eurobank_redirect": "<2.1.1", + "drupal/config_split": "<1.10|>=2,<2.0.2", + "drupal/core": ">=6,<6.38|>=7,<7.103|>=8,<10.5.10|>=10.6,<10.6.9|>=11,<11.2.12|>=11.3,<11.3.10", + "drupal/core-recommended": ">=7,<7.102|>=8,<10.2.11|>=10.3,<10.3.9|>=11,<11.0.8", + "drupal/currency": "<3.5", + "drupal/drupal": ">=5,<5.11|>=6,<6.38|>=7,<7.102|>=8,<10.2.11|>=10.3,<10.3.9|>=11,<11.0.8", + "drupal/email_tfa": "<2.0.6", + "drupal/formatter_suite": "<2.1", + "drupal/gdpr": "<3.0.1|>=3.1,<3.1.2", + "drupal/google_tag": "<1.8|>=2,<2.0.8", + "drupal/ignition": "<1.0.4", + "drupal/json_field": "<1.5", + "drupal/lightgallery": "<1.6", + "drupal/link_field_display_mode_formatter": "<1.6", + "drupal/matomo": "<1.24", + "drupal/oauth2_client": "<4.1.3", + "drupal/oauth2_server": "<2.1", + "drupal/obfuscate": "<2.0.1", + "drupal/plausible_tracking": "<1.0.2", + "drupal/quick_node_block": "<2", + "drupal/rapidoc_elements_field_formatter": "<1.0.1", + "drupal/reverse_proxy_header": "<1.1.2", + "drupal/simple_multistep": "<2", + "drupal/simple_oauth": ">=6,<6.0.7", + "drupal/spamspan": "<3.2.1", + "drupal/tfa": "<1.10", + "drupal/umami_analytics": "<1.0.1", + "duncanmcclean/guest-entries": "<3.1.2", + "dweeves/magmi": "<=0.7.24", + "easycorp/easyadmin-bundle": ">=4,<4.29.16|>=5,<5.5.1", + "ec-cube/ec-cube": "<2.4.4|>=2.11,<=2.17.1|>=3,<=3.0.18.0-patch4|>=4,<=4.3.1", + "ecodev/newsletter": "<=4", + "ectouch/ectouch": "<=2.7.2", + "egroupware/egroupware": "<23.1.20260601|>=26.0.20251208,<26.5.20260507", + "elefant/cms": "<2.0.7", + "elgg/elgg": "<3.3.24|>=4,<4.0.5", + "elijaa/phpmemcacheadmin": "<=1.3", + "elmsln/haxcms": "<11.0.14", + "encore/laravel-admin": "<=1.8.19", + "endroid/qr-code-bundle": "<3.4.2", + "enhavo/enhavo-app": "<=0.13.1", + "enshrined/svg-sanitize": "<=0.22", + "erusev/parsedown": "<1.7.2", + "ether/logs": "<3.0.4", + "evolutioncms/evolution": "<=3.2.3", + "evoweb/sf-register": "<13.2.4|>=14,<14.0.2", + "exceedone/exment": "<4.4.3|>=5,<5.0.3", + "exceedone/laravel-admin": "<2.2.3|==3", + "ezsystems/demobundle": ">=5.4,<5.4.6.1-dev", + "ezsystems/ez-support-tools": ">=2.2,<2.2.3", + "ezsystems/ezdemo-ls-extension": ">=5.4,<5.4.2.1-dev", + "ezsystems/ezfind-ls": ">=5.3,<5.3.6.1-dev|>=5.4,<5.4.11.1-dev|>=2017.12,<2017.12.0.1-dev", + "ezsystems/ezplatform": "<=1.13.6|>=2,<=2.5.24", + "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6|>=1.5,<1.5.29|>=2.3,<2.3.39|>=3.3,<3.3.39", + "ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1|>=5.3.0.0-beta1,<5.3.5", + "ezsystems/ezplatform-graphql": ">=1.0.0.0-RC1,<1.0.13|>=2.0.0.0-beta1,<2.3.12", + "ezsystems/ezplatform-http-cache": "<2.3.16", + "ezsystems/ezplatform-kernel": "<=1.2.5|>=1.3,<1.3.35", + "ezsystems/ezplatform-rest": ">=1.2,<=1.2.2|>=1.3,<1.3.8", + "ezsystems/ezplatform-richtext": ">=2.3,<2.3.26|>=3.3,<3.3.40", + "ezsystems/ezplatform-solr-search-engine": ">=1.7,<1.7.12|>=2,<2.0.2|>=3.3,<3.3.15", + "ezsystems/ezplatform-user": ">=1,<1.0.1", + "ezsystems/ezpublish-kernel": "<=6.13.8.1|>=7,<7.5.31", + "ezsystems/ezpublish-legacy": "<=2017.12.7.3|>=2018.6,<=2019.03.5.1", + "ezsystems/platform-ui-assets-bundle": ">=4.2,<4.2.3", + "ezsystems/repository-forms": ">=2.3,<2.3.2.1-dev|>=2.5,<2.5.15", + "ezyang/htmlpurifier": "<=4.2", + "facade/ignition": "<1.16.15|>=2,<2.4.2|>=2.5,<2.5.2", + "facturascripts/facturascripts": "<=2026.2", + "fastly/magento2": "<1.2.26", + "feehi/cms": "<=2.1.1", + "feehi/feehicms": "<=2.1.1", + "fenom/fenom": "<=2.12.1", + "filament/actions": ">=3.2,<3.2.123|>=4,<=4.11.3|>=5,<=5.6.3", + "filament/filament": ">=3,<=3.3.51|>=4,<=4.13.2|>=5,<=5.8.2", + "filament/forms": ">=3,<=3.3.52", + "filament/infolists": ">=3,<3.2.115|>=4,<=4.11.4|>=5,<=5.6.4", + "filament/tables": ">=3,<=3.3.50|>=4,<=4.11.4|>=5,<=5.6.4", + "filegator/filegator": "<7.8", + "filp/whoops": "<2.1.13", + "fineuploader/php-traditional-server": "<=1.2.2", + "firebase/php-jwt": "<7", + "fisharebest/webtrees": "<=2.1.18", + "fixpunkt/fp-masterquiz": "<2.2.1|>=3,<3.5.2", + "fixpunkt/fp-newsletter": "<1.1.1|>=1.2,<2.1.2|>=2.2,<3.2.6", + "flarum/core": "<=1.8.15|>=2.0.0.0-beta1,<=2.0.0.0-beta8", + "flarum/flarum": "<0.1.0.0-beta8", + "flarum/framework": "<1.8.10", + "flarum/mentions": "<1.6.3", + "flarum/nicknames": "<1.8.3", + "flarum/sticky": ">=0.1.0.0-beta14,<=0.1.0.0-beta15", + "flarum/tags": "<=0.1.0.0-beta13", + "flightphp/core": "<3.18.1", + "floriangaerber/magnesium": "<0.3.1", + "fluidtypo3/vhs": "<5.1.1", + "fof/byobu": ">=0.3.0.0-beta2,<1.1.7", + "fof/oauth": "<1.7.4|>=2.0.0.0-beta1,<2.0.0.0-beta4", + "fof/pretty-mail": "<=1.1.2", + "fof/upload": "<1.2.3", + "foodcoopshop/foodcoopshop": ">=3.2,<3.6.1", + "fooman/tcpdf": "<6.2.22", + "forkcms/forkcms": "<5.11.1", + "fossar/tcpdf-parser": "<6.2.22", + "francoisjacquet/rosariosis": "<=11.5.1", + "frappant/frp-form-answers": "<5.0.5|>=6,<6.1.3|>=7,<7.1.1", + "friendsofsymfony/oauth2-php": "<1.3", + "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2", + "friendsofsymfony/user-bundle": ">=1,<1.3.5", + "friendsofsymfony1/swiftmailer": ">=4,<5.4.13|>=6,<6.2.5", + "friendsofsymfony1/symfony1": ">=1.1,<1.5.19", + "friendsoftypo3/mediace": ">=7.6.2,<7.6.5", + "friendsoftypo3/openid": ">=4.5,<4.5.31|>=4.7,<4.7.16|>=6,<6.0.11|>=6.1,<6.1.6", + "friendsoftypo3/tt-address": "<8.1.2|>=9,<9.1.1|>=10,<10.0.1", + "froala/wysiwyg-editor": "<=4.3", + "frosh/adminer-platform": "<2.2.1", + "froxlor/froxlor": "<2.3.8", + "frozennode/administrator": "<=5.0.12", + "fuel/core": "<1.8.1", + "funadmin/funadmin": "<=7.1.0.0-RC6", + "gaoming13/wechat-php-sdk": "<=1.10.2", + "genix/cms": "<=1.1.11", + "georgringer/news": "<10.0.4|>=11,<11.4.4|>=12,<12.3.2|>=13,<13.0.2|>=14,<14.0.3", + "geshi/geshi": "<=1.0.9.1", + "getformwork/formwork": "<=2.3.3", + "getgrav/grav": "<=2.0.19", + "getgrav/grav-plugin-api": "<1.0.0.0-beta15", + "getgrav/grav-plugin-form": "<9.1", + "getkirby/cms": "<4.9.5|>=5,<5.5.2", + "getkirby/kirby": "<3.9.8.3-dev|>=3.10,<3.10.1.2-dev|>=4,<4.7.1", + "getkirby/panel": "<2.5.14", + "getkirby/starterkit": "<=3.7.0.2", + "gilacms/gila": "<=1.15.4", + "gleez/cms": "<=1.3|==2", + "globalpayments/php-sdk": "<2", + "goalgorilla/open_social": "<12.3.11|>=12.4,<12.4.10|>=13.0.0.0-alpha1,<13.0.0.0-alpha11", + "gogentooss/samlbase": "<1.2.7", + "goodoneuz/pay-uz": "<=2.2.24", + "google/protobuf": "<4.33.6", + "gos/web-socket-bundle": "<1.10.4|>=2,<2.6.1|>=3,<3.3", + "gp247/core": "<1.1.24", + "gree/jose": "<2.2.1", + "gregwar/rst": "<1.0.3", + "grumpydictator/firefly-iii": "<=6.6.2", + "gugoan/economizzer": "<=0.9.0.0-beta1", + "guzzlehttp/guzzle": "<7.15.2|>=8,<8.0.1", + "guzzlehttp/guzzle-services": "<1.5.4", + "guzzlehttp/oauth-subscriber": "<0.8.1", + "guzzlehttp/psr7": "<2.12.3", + "haffner/jh_captcha": "<=2.1.3|>=3,<=3.0.2", + "handcraftedinthealps/goodby-csv": "<1.4.3", + "harvesthq/chosen": "<1.8.7", + "helloxz/imgurl": "<=2.31", + "hhxsv5/laravel-s": "<3.7.36", + "hillelcoren/invoice-ninja": "<5.3.35", + "himiklab/yii2-jqgrid-widget": "<1.0.8", + "hjue/justwriting": "<=1", + "hov/jobfair": "<1.0.13|>=2,<2.0.2", + "httpsoft/http-message": "<1.0.12", + "hybridauth/hybridauth": "<=3.12.2", + "hyn/multi-tenant": ">=5.6,<5.7.2", + "ibexa/admin-ui": ">=4.2,<4.2.3|>=4.6,<4.6.25|>=5,<5.0.3", + "ibexa/admin-ui-assets": ">=4.6.0.0-alpha1,<4.6.21", + "ibexa/core": ">=4,<4.0.7|>=4.1,<4.1.4|>=4.2,<4.2.3|>=4.5,<4.5.6|>=4.6,<4.6.2", + "ibexa/fieldtype-richtext": ">=4.6,<4.6.25|>=5,<5.0.3", + "ibexa/graphql": ">=2.5,<2.5.31|>=3.3,<3.3.28|>=4.2,<4.2.3", + "ibexa/http-cache": ">=4.6,<4.6.14", + "ibexa/post-install": "<1.0.16|>=4.6,<4.6.14", + "ibexa/solr": ">=4.5,<4.5.4", + "ibexa/user": ">=4,<4.4.3|>=5,<5.0.4", + "icecoder/icecoder": "<=8.1", + "idno/known": "<1.6.4", + "ilicmiljan/secure-props": ">=1.2,<1.2.2", + "illuminate/auth": "<5.5.10", + "illuminate/cookie": ">=4,<=4.0.11|>=4.1,<6.18.31|>=7,<7.22.4", + "illuminate/database": "<6.20.26|>=7,<7.30.5|>=8,<8.40", + "illuminate/encryption": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15", + "illuminate/mail": ">=9,<12.60|>=13,<13.10", + "illuminate/view": "<6.20.42|>=7,<7.30.6|>=8,<8.75", + "imdbphp/imdbphp": "<=5.1.1", + "impresscms/impresscms": "<=1.4.5", + "impresspages/impresspages": "<1.0.13", + "in2code/femanager": "<6.4.5|>=7,<7.5.5|>=8,<8.4.2|>=13,<13.3.5", + "in2code/ipandlanguageredirect": "<5.1.2", + "in2code/lux": "<17.6.1|>=18,<24.0.2", + "in2code/powermail": "<10.9.3|>=11,<12.6.1|>=13,<13.2.1", + "innologi/typo3-appointments": "<2.0.6", + "intelliants/subrion": "<4.2.2", + "inter-mediator/inter-mediator": "==5.5", + "intercom/intercom-php": "==5.0.2", + "invoiceninja/invoiceninja": "<5.13.4", + "ipl/web": "<=0.10.2|>=0.11,<=0.13", + "islandora/crayfish": "<4.1", + "islandora/islandora": ">=2,<2.4.1", + "ivankristianto/phpwhois": "<=4.3", + "j0k3r/graby": "<=2.5", + "j0k3r/httplug-ssrf-plugin": "<=3.0.1", + "jackalope/jackalope-doctrine-dbal": "<1.7.4", + "jambagecom/div2007": "<0.10.2", + "james-heinrich/getid3": "<1.9.21", + "james-heinrich/phpthumb": "<=1.7.23", + "jasig/phpcas": "<1.3.3", + "jayanta/laravel-threat-detection": "<1.9", + "jbartels/wec-map": "<3.0.3", + "jcbrand/converse.js": "<3.3.3", + "jleehr/canto-saas-api": "<=2", + "joedolson/my-calendar": "<3.7.7", + "joelbutcher/socialstream": "<5.6|>=6,<6.2", + "johnbillion/query-monitor": "<3.20.4", + "johnbillion/wp-crontrol": "<1.16.2|>=1.17,<1.19.2", + "joomla/application": "<1.0.13", + "joomla/archive": "<1.1.12|>=2,<2.0.1", + "joomla/database": ">=1,<2.2|>=3,<3.4", + "joomla/filesystem": "<1.6.2|>=2,<2.0.1", + "joomla/filter": "<2.0.6|>=3,<3.0.5|==4", + "joomla/framework": "<1.5.7|>=2.5.4,<=3.8.12", + "joomla/input": ">=2,<2.0.2", + "joomla/joomla-cms": "<3.9.12|>=4,<4.4.13|>=5,<5.2.6", + "joomla/joomla-platform": "<1.5.4", + "joomla/session": "<1.3.1", + "joyqi/hyper-down": "<=2.4.27", + "jsdecena/laracom": "<2.0.9", + "jsmitty12/phpwhois": "<5.1", + "juzaweb/cms": "<=3.4.2", + "jweiland/clubdirectory": "<6.0.2|>=7,<7.0.2|>=8,<8.1.3", + "jweiland/events2": "<8.6.3|>=9,<9.4.2|>=10,<10.2.12", + "jweiland/kk-downloader": "<1.2.2", + "jweiland/pforum": "<4.0.4|>=5,<5.0.1|>=6,<6.2.4", + "jweiland/telephonedirectory": "<4.1.1|>=5,<5.0.1|>=6,<6.2", + "jweiland/yellowpages2": "<6.1.6|>=7,<7.0.3|>=8,<8.1.2", + "kantorge/yaffa": "<=2", + "kazist/phpwhois": "<=4.2.6", + "kelvinmo/simplejwt": "<=1.1", + "kelvinmo/simplexrd": "<3.1.1", + "kevinpapst/kimai2": "<1.16.7", + "khodakhah/nodcms": "<=3.4.1", + "kimai/kimai": "<2.59", + "kitodo/presentation": "<6.0.1|>=7,<7.0.2", + "klaviyo/magento2-extension": ">=1,<3", + "knplabs/knp-snappy": "<=1.7", + "kohana/core": "<3.3.3", + "koillection/koillection": "<1.8.4", + "krayin/laravel-crm": "<=2.2", + "kreait/firebase-php": ">=3.2,<3.8.1", + "kumbiaphp/kumbiapp": "<=1.1.1", + "kunstmaan/bundles-cms": "<7.3.2", + "kunstmaan/media-bundle": "<7.3.2", + "la-haute-societe/tcpdf": "<6.2.22", + "laktak/hjson": "<2.3", + "laminas/laminas-diactoros": "<2.18.1|==2.19|==2.20|==2.21|==2.22|==2.23|>=2.24,<2.24.2|>=2.25,<2.25.2", + "laminas/laminas-form": "<2.17.1|>=3,<3.0.2|>=3.1,<3.1.1", + "laminas/laminas-http": "<2.14.2", + "lara-zeus/artemis": ">=1,<=1.0.6", + "lara-zeus/dynamic-dashboard": ">=3,<=3.0.1", + "laravel/fortify": "<1.11.1", + "laravel/framework": "<12.69|>=13,<13.30", + "laravel/laravel": ">=5.4,<5.4.22", + "laravel/passport": ">=13,<13.7.1", + "laravel/pulse": "<1.3.1", + "laravel/reverb": "<1.7", + "laravel/socialite": ">=1,<2.0.10", + "latte/latte": "<2.10.8", + "lavalite/cms": "<=10.1", + "lavitto/typo3-form-to-database": "<2.2.5|>=3,<3.2.2|>=4,<4.2.3|>=5,<5.0.2", + "lcobucci/jwt": ">=3.4,<3.4.6|>=4,<4.0.4|>=4.1,<4.1.5", + "league/commonmark": "<=2.10.1", + "league/flysystem": "<=3.35.2", + "league/oauth2-server": ">=8.3.2,<8.4.2|>=8.5,<8.5.3", + "leantime/leantime": "<3.10", + "lexik/jwt-authentication-bundle": "<2.10.7|>=2.11,<2.11.3", + "libreform/libreform": ">=2,<=2.0.8", + "librenms/librenms": "<26.7", + "liftkit/database": "<2.13.2", + "lightsaml/lightsaml": "<1.3.5", + "limesurvey/limesurvey": "<=7.0.0.0-beta1", + "litesaml/lightsaml": ">=6,<6.0.1", + "livehelperchat/livehelperchat": "<=3.91", + "livewire-filemanager/filemanager": "<=1.0.4", + "livewire/livewire": "<2.12.7|>=3.0.0.0-beta1,<=3.8.2|>=4.0.0.0-beta1,<4.4.8", + "livewire/volt": "<1.7", + "lms/routes": "<2.1.1", + "localizationteam/l10nmgr": "<7.4|>=8,<8.7|>=9,<9.2", + "lochmueller/html5videoplayer-powermail": "<=0.2.1", + "lomkit/laravel-rest-api": "<2.13", + "luracast/restler": "<3.1", + "luyadev/yii-helpers": "<1.2.1", + "maatwebsite/excel": ">=3.1.8,<3.1.70", + "macropay-solutions/laravel-crud-wizard-free": "<3.4.17", + "maestroerror/php-heic-to-jpg": "<1.0.5", + "magento/community-edition": "<2.4.6.0-patch13|>=2.4.7.0-beta1,<2.4.7.0-patch8|>=2.4.8.0-beta1,<2.4.8.0-patch3|>=2.4.9.0-alpha1,<2.4.9.0-alpha3|==2.4.9", + "magento/core": "<=1.9.4.5", + "magento/magento1ce": "<1.9.4.3-dev", + "magento/magento1ee": ">=1,<1.14.4.3-dev", + "magento/product-community-edition": "<2.4.4.0-patch9|>=2.4.5,<2.4.5.0-patch8|>=2.4.6,<2.4.6.0-patch6|>=2.4.7,<2.4.7.0-patch1", + "magento/project-community-edition": "<=2.0.2", + "magneto/core": "<1.9.4.4-dev", + "mahocommerce/maho": "<25.9", + "maikuolan/phpmussel": ">=1,<1.6", + "mainwp/mainwp": "<=4.4.3.3", + "manogi/nova-tiptap": "<=3.2.6", + "mantisbt/mantisbt": "<=2.28.3", + "marcwillmann/turn": "<0.3.3", + "markhuot/craftql": "<=1.3.7", + "marshmallow/nova-tiptap": "<5.7", + "mask/mask": "<8.3.12|>=9,<9.0.11", + "matomo/matomo": "<1.11", + "matyhtf/framework": "<3.0.6", + "mautic/core": "<5.2.11|>=6,<6.0.9|>=7,<7.1.2", + "mautic/core-lib": ">=1.0.0.0-beta,<4.4.13|>=5.0.0.0-alpha,<5.1.1", + "mautic/grapes-js-builder-bundle": ">=4,<4.4.18|>=5,<5.2.9|>=6,<6.0.7", + "maximebf/debugbar": "<1.19", + "mckenziearts/livewire-markdown-editor": "<1.3", + "mcp/sdk": ">=0.5,<0.7.1", + "mdanter/ecc": "<2", + "mediawiki/abuse-filter": "<1.39.9|>=1.40,<1.41.3|>=1.42,<1.42.2", + "mediawiki/cargo": "<3.8.3", + "mediawiki/core": "<1.39.5|==1.40", + "mediawiki/data-transfer": ">=1.39,<1.39.11|>=1.41,<1.41.3|>=1.42,<1.42.2", + "mediawiki/maps": "<12.1.3", + "mediawiki/matomo": "<2.4.3", + "mediawiki/semantic-media-wiki": "<=7.2.1", + "mehrwert/phpmyadmin": "<3.2", + "melisplatform/melis-asset-manager": "<5.0.1", + "melisplatform/melis-cms": "<5.3.4", + "melisplatform/melis-cms-slider": "<5.3.1", + "melisplatform/melis-core": "<5.3.11", + "melisplatform/melis-front": "<5.0.1", + "mezzio/mezzio-swoole": "<3.7|>=4,<4.3", + "mgallegos/laravel-jqgrid": "<=1.3", + "microsoft/microsoft-graph": ">=1.16,<1.109.1|>=2,<2.0.1", + "microsoft/microsoft-graph-beta": "<2.0.1", + "microsoft/microsoft-graph-core": "<2.0.2", + "microweber/microweber": "<=2.0.20", + "mikehaertl/php-shellcommand": "<1.6.1", + "mineadmin/mineadmin": "<3.2.0.0-alpha2", + "miniorange/miniorange-saml": "<1.4.3", + "miraheze/ts-portal": "<=33", + "mittwald/typo3_forum": "<1.2.1", + "mix/mix": ">=2,<=2.2.17", + "mmc/ceselector": "<3.0.3|>=4,<4.0.2|>=5,<5.0.1|>=6,<6.0.1", + "mobiledetect/mobiledetectlib": "<2.8.32", + "modx/revolution": "<=3.1", + "mojo42/jirafeau": "<4.4", + "mongodb/mongodb": "<1.21.5|>=2,<2.4.2", + "mongodb/mongodb-extension": "<1.21.2", + "monolog/monolog": ">=1.8,<1.12", + "moodle/moodle": "<4.5.9|>=5.0.0.0-beta,<5.0.5|>=5.1.0.0-beta,<5.1.2", + "moonshine/moonshine": "<=3.12.5", + "mos/cimage": "<0.7.19", + "movim/moxl": ">=0.8,<=0.10", + "movingbytes/social-network": "<=1.2.1", + "mpdf/mpdf": "<=7.1.7", + "mtdowling/jmespath.php": "<2.9.1", + "munkireport/comment": "<4", + "munkireport/managedinstalls": "<2.6", + "munkireport/munki_facts": "<1.5", + "munkireport/reportdata": "<3.5", + "munkireport/softwareupdate": "<1.6", + "mustache/mustache": ">=2,<2.14.1", + "mwdelaney/wp-enable-svg": "<=0.2", + "nabeel/phpvms": "<7.0.6", + "namshi/jose": "<2.2", + "nasirkhan/laravel-starter": "<11.11", + "nategood/httpful": "<1", + "neoan3-apps/template": "<1.1.1", + "neorazorx/facturascripts": "<2022.04", + "neos/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6", + "neos/form": ">=1.2,<4.3.3|>=5,<5.0.9|>=5.1,<5.1.3", + "neos/media-browser": "<7.3.19|>=8,<8.0.16|>=8.1,<8.1.11|>=8.2,<8.2.11|>=8.3,<8.3.9", + "neos/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<3.0.20|>=3.1,<3.1.18|>=3.2,<3.2.14|>=3.3,<5.3.10|>=7,<7.0.9|>=7.1,<7.1.7|>=7.2,<7.2.6|>=7.3,<7.3.4|>=8,<8.0.2", + "neos/swiftmailer": "<5.4.5", + "nesbot/carbon": "<2.72.6|>=3,<3.8.4", + "netcarver/textile": "<=4.1.2", + "netgen/tagsbundle": ">=3.4,<3.4.11|>=4,<4.0.15", + "nette/application": ">=2,<2.0.19|>=2.1,<2.1.13|>=2.2,<2.2.10|>=2.3,<2.3.14|>=2.4,<2.4.16|>=3,<3.0.6", + "nette/nette": ">=2,<2.0.19|>=2.1,<2.1.13", + "neuron-core/neuron-ai": "<=2.8.11", + "nilsteampassnet/teampass": "<3.1.3.1-dev", + "nitsan/ns-backup": "<13.0.1", + "nitsan/ns-t3ai": "<14.0.2", + "nonfiction/nterchange": "<4.1.1", + "notrinos/notrinos-erp": "<=1", + "noumo/easyii": "<=0.9", + "novaksolutions/infusionsoft-php-sdk": "<1", + "novosga/novosga": "<=2.2.12", + "nukeviet/nukeviet": "<4.6.00", + "nyholm/psr7": "<1.6.1", + "nystudio107/craft-seomatic": "<3.4.12", + "nzedb/nzedb": "<0.8", + "nzo/url-encryptor-bundle": ">=4,<4.3.2|>=5,<5.0.1", + "october/backend": "<1.1.2", + "october/cms": "<1.0.469|==1.0.469|==1.0.471|==1.1.1", + "october/october": "<3.7.14|>=4,<4.1.10|>=4.3,<4.3.4", + "october/rain": "<=3.7.13|>=4,<=4.1.9", + "october/system": "<3.7.17|>=4,<4.2.23", + "oliverklee/phpunit": "<3.5.15", + "omeka/omeka-s": "<4.0.3", + "onelogin/php-saml": "<2.21.1|>=3,<3.8.1|>=4,<4.3.1", + "oneup/uploader-bundle": ">=1,<1.9.3|>=2,<2.1.5", + "open-web-analytics/open-web-analytics": "<1.8.1", + "opencart/opencart": ">=0", + "openid/php-openid": "<2.3", + "openmage/magento-lts": "<=20.17", + "opensolutions/vimbadmin": "<=3.0.15", + "opensource-workshop/connect-cms": "<1.41.1|>=2,<2.41.1", + "orchid/platform": ">=8,<14.43", + "oro/calendar-bundle": ">=4.2,<=4.2.6|>=5,<=5.0.6|>=5.1,<5.1.1", + "oro/commerce": ">=4.1,<5.0.11|>=5.1,<5.1.1", + "oro/crm": ">=1.7,<1.7.4|>=3.1,<4.1.17|>=4.2,<4.2.7", + "oro/crm-call-bundle": ">=4.2,<=4.2.5|>=5,<5.0.4|>=5.1,<5.1.1", + "oro/customer-portal": ">=4.1,<=4.1.13|>=4.2,<=4.2.10|>=5,<=5.0.11|>=5.1,<=5.1.3", + "oro/platform": ">=1.7,<1.7.4|>=3.1,<3.1.29|>=4.1,<4.1.17|>=4.2,<=4.2.10|>=5,<=5.0.12|>=5.1,<=5.1.3", + "oveleon/contao-cookiebar": "<1.16.3|>=2,<2.1.3", + "oxid-esales/oxideshop-ce": "<4.5|>=6,<6.14.4", + "oxid-esales/oxideshop-metapackage-ce": ">=6,<6.5.5", + "oxid-esales/paymorrow-module": ">=1,<1.0.2|>=2,<2.0.1", + "oxid-esales/smarty-component": "<1.0.1", + "packbackbooks/lti-1-3-php-library": "<5", + "padraic/humbug_get_contents": "<1.1.2", + "pagarme/pagarme-php": "<3", + "pagekit/pagekit": "<=1.0.18", + "paragonie/ecc": "<2.0.1", + "paragonie/random_compat": "<2", + "paragonie/sodium_compat": "<1.24.1|>=2,<2.5.1", + "passbolt/passbolt_api": "<4.6.2", + "pattern-lab/drupal-twig-components": "<=2.2", + "paymenter/paymenter": "<=1.5.6", + "paypal/adaptivepayments-sdk-php": "<=3.9.2", + "paypal/invoice-sdk-php": "<=3.9", + "paypal/merchant-sdk-php": "<3.12", + "paypal/permissions-sdk-php": "<=3.9.1", + "pear/archive_tar": "<1.4.14", + "pear/auth": "<1.2.4", + "pear/crypt_gpg": "<1.6.7", + "pear/http_request2": "<2.7", + "pear/pear": "<=1.10.1", + "pegasus/google-for-jobs": "<1.5.1|>=2,<2.1.1", + "personnummer/personnummer": "<3.0.2", + "ph7software/ph7builder": "<=17.9.1", + "phalcon/cphalcon": "<=5.15", + "phanan/koel": "<=9.7", + "pheditor/pheditor": "<2.0.8", + "phenx/php-svg-lib": "<0.5.2", + "php": "<8.2.35|>=8.3,<8.3.36|>=8.4,<8.4.27|>=8.5,<8.5.12", + "php-censor/php-censor": "<2.0.13|>=2.1,<2.1.5", + "php-mod/curl": "<2.3.2", + "php-standard-library/h2": ">=6.1,<6.1.2|>=6.2,<6.2.1", + "php-standard-library/php-standard-library": ">=6.1,<6.1.2|>=6.2,<6.2.1", + "phpbb/phpbb": "<3.3.16|==4.0.0.0-alpha1", + "phpcsstandards/phpcsutils": ">=1.0.0.0-alpha1,<1.2.3", + "phpems/phpems": ">=6,<=6.1.3", + "phpfastcache/phpfastcache": "<6.1.5|>=7,<7.1.2|>=8,<8.0.7", + "phpmailer/phpmailer": "<6.5", + "phpmussel/phpmussel": ">=1,<1.6", + "phpmyadmin/phpmyadmin": "<5.2.2", + "phpmyfaq/phpmyfaq": "<4.2.0.0-alpha", + "phpoffice/common": "<0.2.9", + "phpoffice/math": "<=0.2", + "phpoffice/phpexcel": "<=1.8.2", + "phpoffice/phpspreadsheet": "<=1.30.6|>=2,<=2.1.18|>=2.2,<=2.4.7|>=3,<=3.10.7|>=4,<=5.9", + "phppgadmin/phppgadmin": "<=7.13", + "phpseclib/phpseclib": "<3.0.57|>=4,<4.0.1", + "phpservermon/phpservermon": "<3.6", + "phpsysinfo/phpsysinfo": "<=3.4.5", + "phpunit/phpunit": "<8.5.52|>=9,<9.6.33|>=10,<10.5.62|>=11,<11.5.50|>=12,<12.5.8|>=12.5.21,<12.5.22|>=13.1.5,<13.1.6|>=13.4,<13.4.4", + "phpwhois/phpwhois": "<=4.2.5", + "phpxmlrpc/extras": "<0.6.1", + "phpxmlrpc/phpxmlrpc": "<4.9.2", + "phraseanet/phraseanet": "==4.0.3", + "pi/pi": "<=2.5", + "pimcore/admin-ui-classic-bundle": "<1.7.18|>=2.0.0.0-RC1,<=2.3.5", + "pimcore/customer-management-framework-bundle": "<4.2.1", + "pimcore/data-hub": "<1.2.4", + "pimcore/data-importer": "<1.8.9|>=1.9,<1.9.3", + "pimcore/demo": "<10.3", + "pimcore/ecommerce-framework-bundle": "<1.0.10", + "pimcore/perspective-editor": "<1.5.1", + "pimcore/pimcore": "<=12.3.9|>=2026.1,<=2026.1.5", + "pimcore/studio-backend-bundle": "<2025.4.6|>=2026.1,<2026.1.6", + "pimcore/web2print-tools-bundle": "<=5.2.1|>=6.0.0.0-RC1,<=6.1", + "piwik/piwik": "<1.11", + "pixelfed/pixelfed": "<0.12.5", + "plank/laravel-mediable": "<7", + "plotly/plotly.js": "<2.25.2", + "pocketmine/bedrock-protocol": "<8.0.2", + "pocketmine/pocketmine-mp": "<5.44.2", + "pocketmine/raklib": ">=0.14,<0.14.6|>=0.15,<0.15.1", + "pontedilana/php-weasyprint": "<=2.5.1", + "poweradmin/poweradmin": "<4.2.5|>=4.3,<4.3.4", + "predis/predis": ">=3.0.0.0-RC1,<3.3", + "pressbooks/pressbooks": "<5.18", + "prestashop/autoupgrade": ">=4,<4.10.1", + "prestashop/blockreassurance": "<=5.1.3", + "prestashop/blockwishlist": ">=2,<2.1.1", + "prestashop/contactform": ">=1.0.1,<4.3", + "prestashop/gamification": "<2.3.2", + "prestashop/prestashop": "<8.2.6|>=9,<9.1.1", + "prestashop/productcomments": "<5.0.2", + "prestashop/ps_checkout": "<5.3", + "prestashop/ps_contactinfo": "<=3.3.2", + "prestashop/ps_emailsubscription": "<2.6.1", + "prestashop/ps_facetedsearch": "<4.0.4", + "prestashop/ps_linklist": "<3.1", + "privatebin/privatebin": "<=2.0.4", + "processwire/processwire": "<=3.0.255", + "propel/propel": ">=2.0.0.0-alpha1,<2.0.0.0-alpha8", + "propel/propel1": ">=1,<1.7.2", + "psy/psysh": "<=0.11.22|>=0.12,<=0.12.18", + "pterodactyl/panel": "<=1.12.4", + "ptheofan/yii2-statemachine": ">=2.0.0.0-RC1,<=2", + "ptrofimov/beanstalk_console": "<1.7.14", + "pubnub/pubnub": "<6.1", + "punktde/pt_extbase": "<1.5.1", + "pusher/pusher-php-server": "<2.2.1", + "putyourlightson/craft-sprig": ">=2,<2.15.2|>=3,<3.7.2", + "pwweb/laravel-core": "<=0.3.6.0-beta", + "pxlrbt/filament-excel": "<1.1.14|>=2.0.0.0-alpha,<2.3.3", + "pyrocms/pyrocms": "<=3.9.1", + "qcubed/qcubed": "<=3.1.1", + "quickapps/cms": "<=2.0.0.0-beta2", + "rainlab/blog-plugin": "<1.4.1", + "rainlab/debugbar-plugin": "<3.1", + "rainlab/user-plugin": "<=1.4.5", + "ralffreit/mfa-email": "<1.0.7|==2", + "rankmath/seo-by-rank-math": "<=1.0.95", + "rap2hpoutre/laravel-log-viewer": "<0.13", + "react/http": ">=0.6,<1.11.1", + "really-simple-plugins/complianz-gdpr": "<6.4.2", + "redaxo/source": "<=5.21.1", + "remdex/livehelperchat": "<4.29", + "renolit/reint-downloadmanager": "<4.0.2|>=5,<5.0.1", + "reportico-web/reportico": "<=8.1", + "rhukster/dom-sanitizer": "<1.0.10", + "rmccue/requests": ">=1.6,<1.8", + "roadiz/documents": "<2.3.42|>=2.4,<2.5.44|>=2.6,<2.6.28|>=2.7,<2.7.9", + "roadiz/openid": "<2.3.43|>=2.5,<2.5.45|>=2.6,<2.6.31|>=2.7,<2.7.18", + "robrichards/xmlseclibs": "<3.1.5", + "roots/soil": "<4.1", + "roundcube/roundcubemail": "<1.5.10|>=1.6,<1.6.11|>=1.7.0.0-beta,<1.7.0.0-RC5", + "rudloff/alltube": "<3.0.3", + "rudloff/rtmpdump-bin": "<=2.3.1", + "s-cart/core": "<=9.0.5", + "s-cart/s-cart": "<6.9", + "s9y/serendipity": "<2.6", + "sabberworm/php-css-parser": ">=1,<1.0.1|>=2,<2.0.1|>=3,<3.0.1|>=4,<4.0.1|>=5,<5.0.9|>=5.1,<5.1.3|>=5.2,<5.2.1|>=6,<6.0.2|>=7,<7.0.4|>=8,<8.0.1|>=8.1,<8.1.1|>=8.2,<8.2.1|>=8.3,<8.3.1", + "sabre/dav": ">=1.6,<1.7.11|>=1.8,<1.8.9", + "saloonphp/saloon": "<4", + "samwilson/unlinked-wikibase": "<1.42", + "scheb/two-factor-bundle": "<3.26|>=4,<4.11", + "sensiolabs/connect": "<4.2.3", + "serluck/phpwhois": "<=4.2.6", + "setasign/fpdi": "<2.6.7", + "sfroemken/url_redirect": "<=1.2.1", + "sgalinski/sg-cookie-optin": "<7.4.6|>=8,<8.1.12", + "sheng/yiicms": "<1.2.1", + "shlinkio/shlink": "<=5.0.1", + "shopper/cart": "<2.8", + "shopper/framework": "<2.9.2", + "shopware/core": "<6.6.10.18-dev|>=6.7,<6.7.10.1-dev", + "shopware/platform": "<6.6.10.18-dev|>=6.7,<6.7.10.1-dev", + "shopware/production": "<=6.3.5.2", + "shopware/shopware": "<=6.3.5.2|>=6.4.6,<6.6.10.10-dev|>=6.7,<6.7.6.1-dev", + "shopware/storefront": "<6.6.10.10-dev|>=6.7,<6.7.5.1-dev", + "shopxo/shopxo": "<=6.4", + "showdoc/showdoc": "<3.8.1", + "shuchkin/simplexlsx": ">=1.0.12,<1.1.13", + "silverstripe-australia/advancedreports": ">=1,<=2", + "silverstripe/admin": "<1.13.19|>=2,<2.1.8", + "silverstripe/assets": "<2.4.5|>=3,<3.1.3", + "silverstripe/cms": "<6.2.1", + "silverstripe/comments": ">=1.3,<3.1.1", + "silverstripe/forum": "<0.6.2|>=0.7,<0.7.4", + "silverstripe/framework": "<6.2.2", + "silverstripe/graphql": ">=2,<2.0.5|>=3,<3.8.2|>=4,<4.3.7|>=5,<5.1.3", + "silverstripe/hybridsessions": ">=1,<2.4.1|>=2.5,<2.5.1", + "silverstripe/mfa": "<6", + "silverstripe/recipe-cms": ">=4.5,<4.5.3", + "silverstripe/registry": ">=2.1,<2.1.2|>=2.2,<2.2.1", + "silverstripe/reports": "<5.2.3", + "silverstripe/restfulserver": ">=1,<1.0.9|>=2,<2.0.4|>=2.1,<2.1.2", + "silverstripe/silverstripe-omnipay": "<2.5.2|>=3,<3.0.2|>=3.1,<3.1.4|>=3.2,<3.2.1", + "silverstripe/subsites": ">=2,<2.6.1", + "silverstripe/taxonomy": ">=1.3,<1.3.1|>=2,<2.0.1", + "silverstripe/userforms": "<6.4.9|>=7,<7.0.7|>=7.1,<7.1.1", + "silverstripe/versioned": "<3.2.1", + "silverstripe/versioned-admin": ">=1,<1.11.1", + "simogeo/filemanager": "<=2.5", + "simple-updates/phpwhois": "<=1", + "simplesamlphp/saml2": "<4.19.3|>=4.20,<=4.20.2|>=5,<5.0.6|>=6,<6.2.1", + "simplesamlphp/saml2-legacy": "<4.19.3|>=4.20,<=4.20.2", + "simplesamlphp/simplesamlphp": "<=2.4.6|>=2.5,<=2.5.1", + "simplesamlphp/simplesamlphp-module-casserver": "<=7.0.2", + "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1", + "simplesamlphp/simplesamlphp-module-openid": "<1", + "simplesamlphp/simplesamlphp-module-openidprovider": "<0.9", + "simplesamlphp/xml-common": "<1.20", + "simplesamlphp/xml-security": "<1.13.9|>=2,<2.3.1", + "simplito/elliptic-php": "<1.0.6", + "sitegeist/fluid-components": "<3.5", + "sjbr/sr-feuser-register": "<2.6.2|>=5.1,<12.5", + "sjbr/sr-freecap": "<2.4.6|>=2.5,<2.5.3", + "sjbr/static-info-tables": "<2.3.1", + "slim/psr7": "<1.4.1|>=1.5,<1.5.1|>=1.6,<1.6.1", + "slim/slim": "<2.6|>=4.4,<=4.15.1", + "slub/slub-events": "<3.0.3", + "smarty/smarty": "<4.5.7|>=5,<5.8.4", + "snipe/snipe-it": "<8.7", + "socalnick/scn-social-auth": "<1.15.2", + "socialiteproviders/steam": "<1.1", + "solidinvoice/solidinvoice": "<=2.3.15", + "solspace/craft-freeform": "<4.1.29|>=5,<=5.14.6", + "soosyze/soosyze": "<=2", + "spatie/browsershot": "<5.0.5", + "spatie/image-optimizer": "<1.7.3", + "spatie/laravel-medialibrary": "<11.23", + "spatie/schema-org": ">=3.23.1,<3.23.2|>=4,<4.0.2", + "spencer14420/sp-php-email-handler": "<1", + "spipu/html2pdf": "<5.2.8", + "spiral/roadrunner": "<2025.1", + "spomky-labs/otphp": "<11.4.3", + "spoon/library": "<1.4.1", + "spoonity/tcpdf": "<6.2.22", + "squizlabs/php_codesniffer": "<3.13.6|>=4,<4.0.2", + "ssddanbrown/bookstack": "<24.05.1", + "starcitizentools/citizen-skin": ">=1.9.4,<3.9", + "starcitizentools/short-description": ">=4,<4.0.1", + "starcitizentools/tabber-neue": ">=1.9.1,<2.7.2|>=3,<3.1.1", + "starcitizenwiki/embedvideo": "<=4", + "statamic/cms": "<5.74.3|>=6,<=6.30", + "stormpath/sdk": "<9.9.99", + "studio-42/elfinder": "<2.1.70", + "studiomitte/friendlycaptcha": "<0.1.4", + "subhh/libconnect": "<7.0.8|>=8,<8.1", + "sukohi/surpass": "<1", + "sulu/form-bundle": ">=2,<2.5.3", + "sulu/sulu": "<2.6.25|>=3,<3.0.8", + "sumocoders/framework-user-bundle": "<1.4", + "superbig/craft-audit": "<3.0.2", + "svewap/a21glossary": "<=0.4.10", + "swag/paypal": "<5.4.4", + "swiftmailer/swiftmailer": "<6.2.5", + "swiftyedit/swiftyedit": "<1.2", + "sylius/admin-bundle": ">=1,<1.0.17|>=1.1,<1.1.9|>=1.2,<1.2.2", + "sylius/grid": ">=1,<1.1.19|>=1.2,<1.2.18|>=1.3,<1.3.13|>=1.4,<1.4.5|>=1.5,<1.5.1", + "sylius/grid-bundle": "<1.10.1", + "sylius/mollie-plugin": "<2.2.8|>=3,<3.2.4|>=3.3,<3.3.1", + "sylius/paypal-plugin": "<1.6.2|>=1.7,<1.7.2|>=2,<2.0.2", + "sylius/resource-bundle": ">=1,<1.3.14|>=1.4,<1.4.7|>=1.5,<1.5.2|>=1.6,<1.6.4", + "sylius/sylius": "<1.14.21|>=2,<2.1.17|>=2.2,<2.2.12|>=2.3,<2.3.2", + "symbiote/silverstripe-advancedworkflow": "<6.4.5|>=7,<7.1.3|>=7.2,<7.2.1", + "symbiote/silverstripe-multivaluefield": ">=3,<3.1", + "symbiote/silverstripe-queuedjobs": ">=3,<3.0.2|>=3.1,<3.1.4|>=4,<4.0.7|>=4.1,<4.1.2|>=4.2,<4.2.4|>=4.3,<4.3.3|>=4.4,<4.4.3|>=4.5,<4.5.1|>=4.6,<4.6.4", + "symbiote/silverstripe-seed": "<6.0.3", + "symbiote/silverstripe-versionedfiles": "<=2.0.3", + "symfont/process": ">=0", + "symfony/cache": "<5.4.52|>=6,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", + "symfony/dependency-injection": ">=2,<2.0.17|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", + "symfony/dom-crawler": "<5.4.52|>=6,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", + "symfony/error-handler": ">=4.4,<4.4.4|>=5,<5.0.4", + "symfony/expression-language": ">=2.4,<5.4.54|>=6,<6.4.48|>=7,<7.4.21|>=8,<8.1.9", + "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1", + "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7|>=5.3.14,<5.3.15|>=5.4.3,<5.4.4|>=6.0.3,<6.0.4", + "symfony/html-sanitizer": ">=6.1,<6.4.41|>=7,<7.4.13|>=8,<8.0.13", + "symfony/http-client": ">=4.3,<5.4.53|>=6,<6.4.15|>=7,<7.1.8", + "symfony/http-foundation": "<5.4.54|>=6,<6.4.46|>=7,<7.4.19|>=8,<8.1.7", + "symfony/http-kernel": ">=2,<4.4.50|>=5,<5.4.20|>=6,<6.0.20|>=6.1,<6.1.12|>=6.2,<6.2.6|>=7.4,<7.4.12|>=8,<8.0.12", + "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", + "symfony/json-path": ">=7.3,<7.4.12|>=8,<8.0.12", + "symfony/lox24-notifier": ">=7.1,<7.4.12|>=8,<8.0.12", + "symfony/mailer": "<5.4.52|>=6,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", + "symfony/mailjet-mailer": ">=6.4,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", + "symfony/mailomat-mailer": ">=7.2,<7.4.13|>=8,<8.0.13", + "symfony/mailtrap-mailer": ">=7.2,<7.4.12|>=8,<8.0.12", + "symfony/maker-bundle": ">=1.27,<1.29.2|>=1.30,<1.31.1", + "symfony/mime": "<5.4.55|>=6,<6.4.49|>=7,<7.4.22|>=8,<8.1.10", + "symfony/monolog-bridge": "<5.4.52|>=6,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", + "symfony/phpunit-bridge": ">=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", + "symfony/polyfill": ">=1,<1.10|>=1.17.1,<1.38.1", + "symfony/polyfill-intl-idn": ">=1.17.1,<1.38.1", + "symfony/polyfill-php55": ">=1,<1.10", + "symfony/process": "<5.4.51|>=6,<6.4.33|>=7,<7.1.7|>=7.3,<7.3.11|>=7.4,<7.4.5|>=8,<8.0.5", + "symfony/proxy-manager-bridge": ">=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7", + "symfony/routing": "<5.4.53|>=6,<6.4.41|>=7,<7.4.13|>=8,<8.0.13", + "symfony/runtime": ">=5.3,<5.4.52|>=6,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", + "symfony/security": ">=2,<2.7.51|>=2.8,<3.4.49|>=4,<4.4.24|>=5,<5.2.8", + "symfony/security-bundle": ">=2,<4.4.50|>=5,<5.4.20|>=6,<6.0.20|>=6.1,<6.1.12|>=6.2,<6.4.10|>=7,<7.0.10|>=7.1,<7.1.3", + "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<3.4.49|>=4,<4.4.24|>=5,<5.2.9", + "symfony/security-csrf": ">=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", + "symfony/security-guard": ">=2.8,<3.4.48|>=4,<4.4.23|>=5,<5.2.8", + "symfony/security-http": "<5.4.53|>=6,<6.4.41|>=7,<7.4.13|>=8,<8.0.13", + "symfony/serializer": ">=2,<2.0.11|>=4.1,<4.4.35|>=5,<5.3.12", + "symfony/symfony": "<5.4.55|>=6,<6.4.49|>=7,<7.4.22|>=8,<8.1.10", + "symfony/translation": ">=2,<2.0.17", + "symfony/twig-bridge": ">=2,<4.4.51|>=5,<5.4.31|>=6,<6.3.8|>=6.4.24,<6.4.40", + "symfony/twilio-notifier": ">=6.4,<6.4.40|>=7,<7.4.12|>=8,<8.0.12", + "symfony/ux-autocomplete": "<2.36|>=3,<3.1", + "symfony/ux-icons": ">=2.17,<2.36.1|>=3,<3.2", + "symfony/ux-live-component": "<2.36|>=3,<3.1", + "symfony/ux-toolkit": ">=2.32,<2.36.1|>=3,<3.2", + "symfony/ux-twig-component": "<2.25.1", + "symfony/validator": "<5.4.43|>=6,<6.4.11|>=7,<7.1.4", + "symfony/var-exporter": ">=4.2,<4.2.12|>=4.3,<4.3.8", + "symfony/web-profiler-bundle": ">=2,<2.3.19|>=2.4,<2.4.9|>=2.5,<2.5.4|>=7.2.9,<7.4.12|>=8,<8.0.12", + "symfony/webhook": ">=6.3,<6.3.8", + "symfony/yaml": "<5.4.54|>=6,<6.4.45|>=7,<7.4.18|>=8,<8.1.6", + "symphonycms/symphony-2": "<2.6.4", + "syssy/syssy-typo3-extension": "<3.0.6", + "t3/dce": "<0.11.5|>=2.2,<2.6.2", + "t3g/analytics": "<1.1.1", + "t3g/svg-sanitizer": "<1.0.3", + "t3s/content-consent": "<1.0.3|>=2,<2.0.2", + "tastyigniter/tastyigniter": "<4", + "tcg/voyager": "<=1.8", + "tecnickcom/tc-lib-pdf-font": "<2.6.4", + "tecnickcom/tcpdf": "<6.8", + "terminal42/contao-tablelookupwizard": "<3.3.5", + "thelia/backoffice-default-template": ">=2.1,<2.1.2", + "thelia/thelia": ">=2.0.0.0-beta1,<2.1.3", + "theonedemon/phpwhois": "<=4.2.5", + "thinkcmf/thinkcmf": "<6.0.8", + "thorsten/phpmyfaq": "<4.2.0.0-alpha", + "tikiwiki/tiki-manager": "<=17.1", + "timber/timber": ">=0.16.6,<1.23.1|>=1.24,<1.24.1|>=2,<2.1", + "tinymce/tinymce": "<7.9.3|==7.9.3|>=8,<8.9.3", + "tinymighty/wiki-seo": "<1.2.2", + "titon/framework": "<9.9.99", + "tltneon/lgsl": "<7", + "tobiasbg/tablepress": "<=2.0.0.0-RC1", + "tomasnorre/crawler": "<11.0.13|>=12,<12.0.11", + "topthink/framework": "<6.0.17|>=6.1,<=8.0.4", + "topthink/think": "<=6.1.1", + "topthink/thinkphp": "<=3.2.3|>=6.1.3,<=8.0.4", + "torrentpier/torrentpier": "<=2.8.8", + "tpwd/ke_search": "<5.6.2|>=6,<6.6.1|>=7,<7.0.1", + "tribalsystems/zenario": "<=9.7.61188", + "truckersmp/phpwhois": "<=4.3.1", + "ttskch/pagination-service-provider": "<1", + "twbs/bootstrap": "<3.4.1|>=4,<4.3.1", + "twig/cssinliner-extra": "<3.26", + "twig/intl-extra": "<3.26", + "twig/markdown-extra": "<3.26", + "twig/twig": "<3.27", + "typicms/core": "<12.0.5|>=13,<13.0.9|>=14,<14.0.27|>=15,<15.0.29|>=16,<16.1.7", + "typo3/cms": "<9.5.29|>=10,<10.4.35|>=11,<11.5.23|>=12,<12.2", + "typo3/cms-backend": "<10.4.60|>=11,<11.5.54|>=12,<12.4.49|>=13,<13.4.35|>=14,<14.3.7", + "typo3/cms-belog": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", + "typo3/cms-beuser": ">=9,<9.5.55|>=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", + "typo3/cms-core": "<10.4.57|>=11,<11.5.51|>=12,<12.4.46|>=13,<13.4.34|>=14,<14.3.6", + "typo3/cms-dashboard": ">=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", + "typo3/cms-extbase": "<6.2.24|>=7,<7.6.8|==8.1.1", + "typo3/cms-extensionmanager": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<=13.4.2", + "typo3/cms-felogin": ">=4.2,<4.2.3", + "typo3/cms-filelist": ">=11,<11.5.51|>=12,<12.4.46|>=13,<13.4.31|>=14,<14.3.3", + "typo3/cms-fluid": "<4.3.4|>=4.4,<4.4.1|>=8,<8.7.23|>=9,<9.5.4", + "typo3/cms-form": "<10.4.57|>=11,<11.5.51|>=12,<12.4.46|>=13,<13.4.31|>=14,<14.3.9", + "typo3/cms-frontend": "<4.3.9|>=4.4,<4.4.5", + "typo3/cms-indexed-search": ">=10,<=10.4.47|>=11,<=11.5.41|>=12,<=12.4.24|>=13,<13.4.31|>=14,<14.3.3", + "typo3/cms-install": "<4.1.14|>=4.2,<4.2.16|>=4.3,<4.3.9|>=4.4,<4.4.5|>=12.2,<12.4.8|==13.4.2", + "typo3/cms-lowlevel": ">=11,<=11.5.41|>=14.2,<14.3.7", + "typo3/cms-recordlist": ">=11,<11.5.48", + "typo3/cms-recycler": "<10.4.57|>=11,<11.5.51|>=12,<12.4.46|>=13,<13.4.31|>=14,<14.3.3", + "typo3/cms-redirects": ">=10,<=10.4.54|>=11,<=11.5.48|>=12,<=12.4.40|>=13,<=13.4.22|>=14,<=14.0.1", + "typo3/cms-rte-ckeditor": ">=9.5,<9.5.42|>=10,<10.4.39|>=11,<11.5.30", + "typo3/cms-scheduler": ">=11,<=11.5.41", + "typo3/cms-setup": ">=9,<=9.5.50|>=10,<=10.4.49|>=11,<=11.5.43|>=12,<=12.4.30|>=13,<=13.4.11", + "typo3/cms-webhooks": ">=12,<=12.4.30|>=13,<=13.4.11", + "typo3/cms-workspaces": ">=9,<9.5.55|>=10,<10.4.54|>=11,<11.5.48|>=12,<12.4.37|>=13,<13.4.18", + "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.12|>=3.1,<3.1.10|>=3.2,<3.2.13|>=3.3,<3.3.13|>=4,<4.0.6", + "typo3/html-sanitizer": "<2.3.2", + "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4|>=2.3,<2.3.99|>=3,<3.0.20|>=3.1,<3.1.18|>=3.2,<3.2.14|>=3.3,<3.3.23|>=4,<4.0.17|>=4.1,<4.1.16|>=4.2,<4.2.12|>=4.3,<4.3.3", + "typo3/phar-stream-wrapper": ">=1,<2.1.1|>=3,<3.1.1", + "typo3/swiftmailer": ">=4.1,<4.1.99|>=5.4,<5.4.5", + "typo3fluid/fluid": ">=2,<2.0.8|>=2.1,<2.1.7|>=2.2,<2.2.4|>=2.3,<2.3.7|>=2.4,<2.4.4|>=2.5,<2.5.11|>=2.6,<2.6.10", + "ua-parser/uap-php": "<3.8", + "uasoft-indonesia/badaso": "<=2.9.7", + "ueberdosis/tiptap-php": "<2.1.1", + "unisharp/laravel-filemanager": "<2.9.1", + "universal-omega/dynamic-page-list3": "<3.6.4", + "unopim/unopim": "<=0.3", + "userfrosting/userfrosting": ">=0.3.1,<4.6.3", + "usmanhalalit/pixie": "<1.0.3|>=2,<2.0.2", + "uvdesk/community-skeleton": "<=1.1.1", + "uvdesk/core-framework": "<=1.1.1", + "vanilla/safecurl": "<0.9.2", + "verbb/comments": "<1.5.5", + "verbb/formie": "<3.1.31", + "verbb/image-resizer": "<2.0.9", + "verbb/knock-knock": "<1.2.8", + "verot/class.upload.php": "<=2.1.6", + "vertexvaar/falsftp": "<0.2.6", + "villagedefrance/opencart-overclocked": "<=1.11.1", + "vova07/yii2-fileapi-widget": "<0.1.9", + "vrana/adminer": "<5.4.2", + "vufind/vufind": ">=2,<9.1.1", + "waldhacker/hcaptcha": "<2.1.2", + "wallabag/tcpdf": "<6.2.22", + "wallabag/wallabag": "<2.6.11", + "wanglelecc/laracms": "<=1.0.3", + "wapplersystems/a21glossary": "<=0.4.10", + "web-auth/webauthn-framework": ">=3.3,<3.3.4|>=4.5,<4.9|>=5.2,<5.2.4|>=5.3,<5.3.1", + "web-auth/webauthn-lib": ">=4.5,<5.3.5", + "web-auth/webauthn-symfony-bundle": "<5.3.4", + "web-feet/coastercms": "==5.5", + "web-token/jwt-bundle": "<3.4.10|>=4,<4.0.7|>=4.1,<4.1.7", + "web-token/jwt-experimental": "<4.1.7", + "web-token/jwt-framework": "<3.4.10|>=4,<4.0.7|>=4.1,<4.1.7", + "web-token/jwt-library": "<3.4.10|>=4,<4.0.7|>=4.1,<4.1.7", + "web-tp3/wec_map": "<3.0.3", + "webbuilders-group/silverstripe-kapost-bridge": "<0.4", + "webcoast/deferred-image-processing": "<1.0.2", + "webklex/laravel-imap": "<5.3", + "webklex/php-imap": "<5.3", + "webonyx/graphql-php": "<=15.32.2", + "webpa/webpa": "<3.1.2", + "webreinvent/vaahcms": "<=2.3.1", + "wikibase/wikibase": "<=1.39.3", + "wikimedia/parsoid": "<0.12.2", + "willdurand/js-translation-bundle": "<2.1.1", + "winter/wn-backend-module": "<1.2.14", + "winter/wn-cms-module": "<=1.2.12", + "winter/wn-dusk-plugin": "<2.1", + "winter/wn-system-module": "<1.2.13", + "wintercms/winter": "<=1.2.3", + "wireui/wireui": "<1.19.3|>=2,<2.1.3", + "wnx/laravel-backup-restore": "<=1.9.3", + "woocommerce/woocommerce": "<6.6|>=8.8,<8.8.5|>=8.9,<8.9.3", + "wp-cli/wp-cli": ">=0.12,<2.5", + "wp-coding-standards/wpcs": ">=0.14.1,<3.4.1", + "wp-graphql/wp-graphql": "<2.22.2", + "wp-premium/gravityforms": "<2.4.21", + "wpanel/wpanel4-cms": "<=4.3.1", + "wpcloud/wp-stateless": "<3.2", + "wpglobus/wpglobus": "<=1.9.6", + "wpmetabox/meta-box": "<5.11.2", + "wwbn/avideo": "<29.1", + "xataface/xataface": "<3", + "xpressengine/xpressengine": "<3.0.15", + "yab/quarx": "<2.4.5", + "yansongda/pay": "<=3.7.19", + "yeswiki/yeswiki": "<4.6.6", + "yetiforce/yetiforce-crm": "<6.5", + "yidashi/yii2cmf": "<=2", + "yii2mod/yii2-cms": "<1.9.2", + "yiisoft/yii": "<1.1.31", + "yiisoft/yii2": "<2.0.55", + "yiisoft/yii2-authclient": "<2.2.15", + "yiisoft/yii2-bootstrap": "<2.0.4", + "yiisoft/yii2-dev": "<=2.0.45", + "yiisoft/yii2-elasticsearch": "<2.0.5", + "yiisoft/yii2-gii": "<=2.2.4", + "yiisoft/yii2-jui": "<2.0.4", + "yiisoft/yii2-redis": "<2.0.20", + "yikesinc/yikes-inc-easy-mailchimp-extender": "<6.8.6", + "yoast-seo-for-typo3/yoast_seo": "<7.2.3", + "yoast/duplicate-post": "<=4.5", + "yourls/yourls": "<=1.10.4", + "yuan1994/tpadmin": "<=1.3.12", + "yungifez/skuul": "<=2.6.5", + "z-push/z-push-dev": "<2.7.6", + "zbateson/mail-mime-parser": "<3.0.6|>=4,<4.0.2", + "zencart/zencart": "<=1.5.7.0-beta", + "zendesk/zendesk_api_client_php": "<2.2.11", + "zendframework/zend-cache": ">=2.4,<2.4.8|>=2.5,<2.5.3", + "zendframework/zend-captcha": ">=2,<2.4.9|>=2.5,<2.5.2", + "zendframework/zend-crypt": ">=2,<2.4.9|>=2.5,<2.5.2", + "zendframework/zend-db": "<2.2.10|>=2.3,<2.3.5", + "zendframework/zend-developer-tools": ">=1.2.2,<1.2.3", + "zendframework/zend-diactoros": "<1.8.4", + "zendframework/zend-feed": "<2.10.3", + "zendframework/zend-form": ">=2,<2.2.7|>=2.3,<2.3.1", + "zendframework/zend-http": "<2.8.1", + "zendframework/zend-json": ">=2.1,<2.1.6|>=2.2,<2.2.6", + "zendframework/zend-ldap": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.8|>=2.3,<2.3.3", + "zendframework/zend-mail": "<2.4.11|>=2.5,<2.7.2", + "zendframework/zend-navigation": ">=2,<2.2.7|>=2.3,<2.3.1", + "zendframework/zend-session": ">=2,<2.2.9|>=2.3,<2.3.4", + "zendframework/zend-validator": ">=2.3,<2.3.6", + "zendframework/zend-view": ">=2,<2.2.7|>=2.3,<2.3.1", + "zendframework/zend-xmlrpc": ">=2.1,<2.1.6|>=2.2,<2.2.6", + "zendframework/zendframework": "<=3", + "zendframework/zendframework1": "<1.12.20", + "zendframework/zendopenid": "<2.0.2", + "zendframework/zendrest": "<2.0.2", + "zendframework/zendservice-amazon": "<2.0.3", + "zendframework/zendservice-api": "<1", + "zendframework/zendservice-audioscrobbler": "<2.0.2", + "zendframework/zendservice-nirvanix": "<2.0.2", + "zendframework/zendservice-slideshare": "<2.0.2", + "zendframework/zendservice-technorati": "<2.0.2", + "zendframework/zendservice-windowsazure": "<2.0.2", + "zendframework/zendxml": ">=1,<1.0.1", + "zenstruck/collection": "<0.2.1", + "zetacomponents/mail": "<1.8.2", + "zf-commons/zfc-user": "<1.2.2", + "zfcampus/zf-apigility-doctrine": ">=1,<1.0.3", + "zfr/zfr-oauth2-server-module": "<0.1.2", + "zoujingli/thinkadmin": "<=6.1.53", + "zumba/json-serializer": "<3.2.3" + }, + "default-branch": true, + "type": "metapackage", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "role": "maintainer" + }, + { + "name": "Ilya Tribusean", + "email": "slash3b@gmail.com", + "role": "maintainer" + } + ], + "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", + "keywords": [ + "dev" + ], + "support": { + "issues": "https://github.com/Roave/SecurityAdvisories/issues", + "source": "https://github.com/Roave/SecurityAdvisories/tree/latest" + }, + "funding": [ + { + "url": "https://github.com/Ocramius", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/roave/security-advisories", + "type": "tidelift" + } + ], + "time": "2026-10-31T00:24:47+00:00", + "published-time": "2026-10-31T00:24:51+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "eeb759ad3146b7096fb59c3195d39e071cd409e3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/eeb759ad3146b7096fb59c3195d39e071cd409e3", + "reference": "eeb759ad3146b7096fb59c3195d39e071cd409e3", + "shasum": "" + }, + "require": { + "php": ">=8.4" + }, + "require-dev": { + "phpunit/phpunit": "^13.2.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/5.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/cli-parser", + "type": "tidelift" + } + ], + "time": "2026-08-01T04:27:14+00:00", + "published-time": "2026-08-01T04:29:01+00:00" + }, + { + "name": "sebastian/comparator", + "version": "8.4.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "3b070e608146cba00fd6fd1f0ffba89e5a8897fb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/3b070e608146cba00fd6fd1f0ffba89e5a8897fb", + "reference": "3b070e608146cba00fd6fd1f0ffba89e5a8897fb", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.4", + "sebastian/diff": "^9.0", + "sebastian/exporter": "^8.2" + }, + "require-dev": { + "phpunit/phpunit": "^13.3" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/8.4.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" + } + ], + "time": "2026-08-07T07:23:13+00:00", + "published-time": "2026-08-07T07:23:29+00:00" + }, + { + "name": "sebastian/complexity", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "c5651c795c98093480df79350cb050813fc7a2f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/c5651c795c98093480df79350cb050813fc7a2f3", + "reference": "c5651c795c98093480df79350cb050813fc7a2f3", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.4" + }, + "require-dev": { + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/complexity", + "type": "tidelift" + } + ], + "time": "2026-02-06T04:41:32+00:00", + "published-time": "2026-02-06T04:41:49+00:00" + }, + { + "name": "sebastian/diff", + "version": "9.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "a2df6626c1baf31d5a88674882a3072f151b5a26" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/a2df6626c1baf31d5a88674882a3072f151b5a26", + "reference": "a2df6626c1baf31d5a88674882a3072f151b5a26", + "shasum": "" + }, + "require": { + "php": ">=8.4" + }, + "require-dev": { + "phpunit/phpunit": "^13.3.1", + "symfony/process": "^7.4.17" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "9.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/9.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/diff", + "type": "tidelift" + } + ], + "time": "2026-08-25T15:38:55+00:00", + "published-time": "2026-08-25T15:39:44+00:00" + }, + { + "name": "sebastian/environment", + "version": "9.3.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "d4dd10267a11055066a2a78ae04583d8ddd4f595" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d4dd10267a11055066a2a78ae04583d8ddd4f595", + "reference": "d4dd10267a11055066a2a78ae04583d8ddd4f595", + "shasum": "" + }, + "require": { + "php": ">=8.4" + }, + "require-dev": { + "phpunit/phpunit": "^13.3.6" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "9.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/9.3.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" + } + ], + "time": "2026-10-01T12:37:56+00:00", + "published-time": "2026-10-01T12:38:48+00:00" + }, + { + "name": "sebastian/exporter", + "version": "8.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "24a3b69bba4a12ab615fca9d34680c5598d9ab7a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/24a3b69bba4a12ab615fca9d34680c5598d9ab7a", + "reference": "24a3b69bba4a12ab615fca9d34680c5598d9ab7a", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.4", + "sebastian/recursion-context": "^8.0.1" + }, + "require-dev": { + "phpunit/phpunit": "^13.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/8.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" + } + ], + "time": "2026-08-07T07:22:06+00:00", + "published-time": "2026-08-07T07:23:47+00:00" + }, + { + "name": "sebastian/file-filter", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/file-filter.git", + "reference": "33a26f394330f6faa7684bb9cc73afb7727aae93" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/file-filter/zipball/33a26f394330f6faa7684bb9cc73afb7727aae93", + "reference": "33a26f394330f6faa7684bb9cc73afb7727aae93", + "shasum": "" + }, + "require": { + "php": ">=8.4" + }, + "require-dev": { + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for filtering files", + "homepage": "https://github.com/sebastianbergmann/file-filter", + "support": { + "issues": "https://github.com/sebastianbergmann/file-filter/issues", + "security": "https://github.com/sebastianbergmann/file-filter/security/policy", + "source": "https://github.com/sebastianbergmann/file-filter/tree/1.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/file-filter", + "type": "tidelift" + } + ], + "time": "2026-04-22T07:20:04+00:00", + "published-time": "2026-04-22T07:20:25+00:00" + }, + { + "name": "sebastian/git-state", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/git-state.git", + "reference": "31a565f463686f0eefa85f899549256bf5805985" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/git-state/zipball/31a565f463686f0eefa85f899549256bf5805985", + "reference": "31a565f463686f0eefa85f899549256bf5805985", + "shasum": "" + }, + "require": { + "php": ">=8.4" + }, + "require-dev": { + "phpunit/phpunit": "^13.4.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for describing the state of a Git checkout", + "homepage": "https://github.com/sebastianbergmann/git-state", + "support": { + "issues": "https://github.com/sebastianbergmann/git-state/issues", + "security": "https://github.com/sebastianbergmann/git-state/security/policy", + "source": "https://github.com/sebastianbergmann/git-state/tree/1.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/git-state", + "type": "tidelift" + } + ], + "time": "2026-10-21T05:59:52+00:00", + "published-time": "2026-10-21T06:01:28+00:00" + }, + { + "name": "sebastian/global-state", + "version": "9.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "ba68ba79da690cf7eddefd3ce5b78b20b9ba9945" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/ba68ba79da690cf7eddefd3ce5b78b20b9ba9945", + "reference": "ba68ba79da690cf7eddefd3ce5b78b20b9ba9945", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "sebastian/object-reflector": "^6.0", + "sebastian/recursion-context": "^8.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^13.1.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "9.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/9.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" + } + ], + "time": "2026-06-01T15:11:33+00:00", + "published-time": "2026-06-01T15:20:45+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "5.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "d1b6f8fce682505dbd048977f1abedf1b8ad3ff8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d1b6f8fce682505dbd048977f1abedf1b8ad3ff8", + "reference": "d1b6f8fce682505dbd048977f1abedf1b8ad3ff8", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.8.0", + "php": ">=8.4" + }, + "require-dev": { + "phpunit/phpunit": "^13.2.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/5.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/lines-of-code", + "type": "tidelift" + } + ], + "time": "2026-07-09T08:42:34+00:00", + "published-time": "2026-07-09T08:44:06+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "8.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "511064ecde82bd747e2ba2fab3dda8d977b59576" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/511064ecde82bd747e2ba2fab3dda8d977b59576", + "reference": "511064ecde82bd747e2ba2fab3dda8d977b59576", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "sebastian/recursion-context": "^8.0.1" + }, + "require-dev": { + "phpunit/phpunit": "^13.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/8.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/object-enumerator", + "type": "tidelift" + } + ], + "time": "2026-08-13T07:05:05+00:00", + "published-time": "2026-08-13T07:07:58+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "6.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "f71bbcdc4f95456b4622810bec64eb06372e25b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/f71bbcdc4f95456b4622810bec64eb06372e25b2", + "reference": "f71bbcdc4f95456b4622810bec64eb06372e25b2", + "shasum": "" + }, + "require": { + "php": ">=8.4" + }, + "require-dev": { + "phpunit/phpunit": "^13.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/6.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/object-reflector", + "type": "tidelift" + } + ], + "time": "2026-08-13T06:34:36+00:00", + "published-time": "2026-08-13T06:39:22+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "8.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "32dba72f2b4642d6a93db22d6c0a9280ff2e3ca0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/32dba72f2b4642d6a93db22d6c0a9280ff2e3ca0", + "reference": "32dba72f2b4642d6a93db22d6c0a9280ff2e3ca0", + "shasum": "" + }, + "require": { + "php": ">=8.4" + }, + "require-dev": { + "phpunit/phpunit": "^13.2.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/8.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" + } + ], + "time": "2026-08-03T05:58:12+00:00", + "published-time": "2026-08-03T05:59:59+00:00" + }, + { + "name": "sebastian/type", + "version": "7.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "bd1df467864cb95140414059a535b2d906173fcf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/bd1df467864cb95140414059a535b2d906173fcf", + "reference": "bd1df467864cb95140414059a535b2d906173fcf", + "shasum": "" + }, + "require": { + "php": ">=8.4" + }, + "require-dev": { + "phpunit/phpunit": "^13.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/7.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" + } + ], + "time": "2026-08-10T08:00:57+00:00", + "published-time": "2026-08-10T08:01:43+00:00" + }, + { + "name": "sebastian/version", + "version": "7.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "f1598cf1d8d1ca4c0f194a52f2fe72a559a7b0b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/f1598cf1d8d1ca4c0f194a52f2fe72a559a7b0b1", + "reference": "f1598cf1d8d1ca4c0f194a52f2fe72a559a7b0b1", + "shasum": "" + }, + "require": { + "php": ">=8.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/7.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/version", + "type": "tidelift" + } + ], + "time": "2026-10-04T05:51:14+00:00", + "published-time": "2026-10-04T05:52:22+00:00" + }, + { + "name": "sniccowp/php-scoper-wordpress-excludes", + "version": "7.1.0", + "source": { + "type": "git", + "url": "https://github.com/snicco/php-scoper-wordpress-excludes.git", + "reference": "d8ad8aae8a051a4f332150f37a7bf96df1d11d0a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/snicco/php-scoper-wordpress-excludes/zipball/d8ad8aae8a051a4f332150f37a7bf96df1d11d0a", + "reference": "d8ad8aae8a051a4f332150f37a7bf96df1d11d0a", + "shasum": "" + }, + "require-dev": { + "php": "^7.4", + "php-stubs/wordpress-globals": "0.2.0", + "php-stubs/wordpress-stubs": "7.1.0", + "sniccowp/php-scoper-excludes": "dev-master" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Calvin Alkan", + "email": "calvin@snicco.de" + }, + { + "name": "Marlon Alkan", + "email": "marlon@snicco.de" + } + ], + "description": "A list of all WordPress core classes, functions and constants. Meant to be used with the PHP-Scoper exclusion functionality.", + "keywords": [ + "php-scoper", + "scoping WordPress", + "wordpress" + ], + "support": { + "issues": "https://github.com/snicco/php-scoper-wordpress-excludes/issues", + "source": "https://github.com/snicco/php-scoper-wordpress-excludes/tree/7.1.0" + }, + "time": "2026-08-28T02:50:05+00:00", + "published-time": "2026-09-23T19:33:26+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.13.6", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "4c378e1a528ea066890fc2397cbdd2f94eb2fc91" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/4c378e1a528ea066890fc2397cbdd2f94eb2fc91", + "reference": "4c378e1a528ea066890fc2397cbdd2f94eb2fc91", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" + }, + "bin": [ + "bin/phpcbf", + "bin/phpcs" + ], + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "Former lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "Current lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", + "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", + "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2026-08-06T00:17:32+00:00", + "published-time": "2026-08-06T01:05:02+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00", + "published-time": "2024-10-20T05:09:39+00:00" + }, + { + "name": "swissspidy/phpstan-no-private", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/swissspidy/phpstan-no-private.git", + "reference": "559cb0e8d092df7314ed4254db83db0427440af2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swissspidy/phpstan-no-private/zipball/559cb0e8d092df7314ed4254db83db0427440af2", + "reference": "559cb0e8d092df7314ed4254db83db0427440af2", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0.0", + "nikic/php-parser": "^v5.3.1", + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.5", + "slevomat/coding-standard": "^8.8.0", + "squizlabs/php_codesniffer": "^3.5.3" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "Swissspidy\\PHPStan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan rules for detecting usage of pseudo-private functions, classes, and methods.", + "support": { + "issues": "https://github.com/swissspidy/phpstan-no-private/issues", + "source": "https://github.com/swissspidy/phpstan-no-private/tree/v1.0.0" + }, + "time": "2024-11-11T11:04:45+00:00", + "published-time": "2024-11-11T11:08:03+00:00" + }, + { + "name": "symfony/console", + "version": "v7.4.22", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "8012f842110b414fd1cc641adb0ed8423353b0bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/8012f842110b414fd1cc641adb0ed8423353b0bb", + "reference": "8012f842110b414fd1cc641adb0ed8423353b0bb", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.2|^8.0" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.4.22" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-10-28T11:08:27+00:00", + "published-time": "2026-10-28T18:21:33+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.7.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d", + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-06-05T06:23:12+00:00", + "published-time": "2026-06-27T21:46:34+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.7.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "c7de7a00ffb67842132da02ea92988a39ccd9f4e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/c7de7a00ffb67842132da02ea92988a39ccd9f4e", + "reference": "c7de7a00ffb67842132da02ea92988a39ccd9f4e", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.7.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-06-05T06:23:12+00:00", + "published-time": "2026-06-27T21:46:35+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v7.4.21", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "fd0e3462be6d32a0d130e8423e4940a7688162d8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/fd0e3462be6d32a0d130e8423e4940a7688162d8", + "reference": "fd0e3462be6d32a0d130e8423e4940a7688162d8", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v7.4.21" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-10-06T20:05:19+00:00", + "published-time": "2026-10-14T06:45:33+00:00" + }, + { + "name": "symfony/finder", + "version": "v7.4.22", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "e13420a8e0a76bdde1e7be09f923e62ad13739f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/e13420a8e0a76bdde1e7be09f923e62ad13739f2", + "reference": "e13420a8e0a76bdde1e7be09f923e62ad13739f2", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v7.4.22" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-10-19T16:56:54+00:00", + "published-time": "2026-10-28T18:21:14+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-10T16:19:22+00:00", + "published-time": "2026-04-26T14:44:50+00:00" + }, + { + "name": "symfony/polyfill-iconv", + "version": "v1.43.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-iconv.git", + "reference": "254724d95fe01b679cd97d88786e9623ee00bfb7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/254724d95fe01b679cd97d88786e9623ee00bfb7", + "reference": "254724d95fe01b679cd97d88786e9623ee00bfb7", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-iconv": "*" + }, + "suggest": { + "ext-iconv": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Iconv\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Iconv extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "iconv", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-iconv/tree/v1.43.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-09-29T06:25:14+00:00", + "published-time": "2026-09-30T10:23:39+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.43.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "5a8f33c87aeecffce24e721b5064fab05f4becf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/5a8f33c87aeecffce24e721b5064fab05f4becf8", + "reference": "5a8f33c87aeecffce24e721b5064fab05f4becf8", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.43.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-10-15T09:18:53+00:00", + "published-time": "2026-10-28T10:07:44+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.43.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "ebd57ccc7edb93d632e184efbe7693d2517bab3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/ebd57ccc7edb93d632e184efbe7693d2517bab3b", + "reference": "ebd57ccc7edb93d632e184efbe7693d2517bab3b", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.43.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-09-23T22:02:09+00:00", + "published-time": "2026-09-30T10:23:47+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.43.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "122889ed24dc000165bcbfd77ec412f15cbdfc6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/122889ed24dc000165bcbfd77ec412f15cbdfc6a", + "reference": "122889ed24dc000165bcbfd77ec412f15cbdfc6a", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.43.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-10-28T09:35:49+00:00", + "published-time": "2026-10-28T10:07:45+00:00" + }, + { + "name": "symfony/polyfill-php84", + "version": "v1.43.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "22c6548499cbe5c5bb072fc0c42312dff062a015" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/22c6548499cbe5c5bb072fc0c42312dff062a015", + "reference": "22c6548499cbe5c5bb072fc0c42312dff062a015", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php84\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php84/tree/v1.43.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-09-23T22:01:09+00:00", + "published-time": "2026-09-30T10:23:33+00:00" + }, + { + "name": "symfony/process", + "version": "v7.4.22", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "a5a6912edbbfc4c98cd080bc4bbe37c8e7eeb20a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/a5a6912edbbfc4c98cd080bc4bbe37c8e7eeb20a", + "reference": "a5a6912edbbfc4c98cd080bc4bbe37c8e7eeb20a", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v7.4.22" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-10-25T14:40:16+00:00", + "published-time": "2026-10-28T18:21:18+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.7.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "15e6a07ec2a2c75ceb1b21dd98105ee8456d2257" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/15e6a07ec2a2c75ceb1b21dd98105ee8456d2257", + "reference": "15e6a07ec2a2c75ceb1b21dd98105ee8456d2257", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.7.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-07-27T15:39:01+00:00", + "published-time": "2026-08-30T19:56:37+00:00" + }, + { + "name": "symfony/string", + "version": "v8.1.10", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "6c6110979c0150393bbb336170e286d51ab4d76c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/6c6110979c0150393bbb336170e286d51ab4d76c", + "reference": "6c6110979c0150393bbb336170e286d51ab4d76c", + "shasum": "" + }, + "require": { + "php": ">=8.4.1", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-intl-grapheme": "^1.33", + "symfony/polyfill-intl-normalizer": "^1.0", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v8.1.10" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-10-23T17:06:53+00:00", + "published-time": "2026-10-28T18:24:27+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v7.4.22", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "6e6ff33ea8cf5df27f00a5561ebd5ee9bd7f1618" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/6e6ff33ea8cf5df27f00a5561ebd5ee9bd7f1618", + "reference": "6e6ff33ea8cf5df27f00a5561ebd5ee9bd7f1618", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/uid": "^6.4|^7.0|^8.0", + "twig/twig": "^3.12|^4.0" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v7.4.22" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-10-25T21:04:04+00:00", + "published-time": "2026-10-28T18:20:00+00:00" + }, + { + "name": "szepeviktor/phpstan-wordpress", + "version": "v2.0.4", + "source": { + "type": "git", + "url": "https://github.com/szepeviktor/phpstan-wordpress.git", + "reference": "84577e2ea1e4c2a95537fe5ea2ac8e18f4ceab1a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/szepeviktor/phpstan-wordpress/zipball/84577e2ea1e4c2a95537fe5ea2ac8e18f4ceab1a", + "reference": "84577e2ea1e4c2a95537fe5ea2ac8e18f4ceab1a", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "php-stubs/wordpress-stubs": ">=6.6.2", + "phpstan/phpstan": "^2.0" + }, + "require-dev": { + "composer/composer": "^2.1.14", + "composer/semver": "^3.4", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.1", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.0", + "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^1.0", + "wp-coding-standards/wpcs": "3.1.0 as 2.3.0" + }, + "suggest": { + "swissspidy/phpstan-no-private": "Detect usage of internal core functions, classes and methods" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "SzepeViktor\\PHPStan\\WordPress\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "WordPress extensions for PHPStan", + "keywords": [ + "PHPStan", + "code analyse", + "code analysis", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/szepeviktor/phpstan-wordpress/issues", + "source": "https://github.com/szepeviktor/phpstan-wordpress/tree/v2.0.4" + }, + "time": "2026-05-22T16:22:09+00:00", + "published-time": "2026-08-28T06:48:49+00:00" + }, + { + "name": "thecodingmachine/safe", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/thecodingmachine/safe.git", + "reference": "705683a25bacf0d4860c7dea4d7947bfd09eea19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/705683a25bacf0d4860c7dea4d7947bfd09eea19", + "reference": "705683a25bacf0d4860c7dea4d7947bfd09eea19", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpstan/phpstan": "^2", + "phpunit/phpunit": "^10", + "squizlabs/php_codesniffer": "^3.2" + }, + "type": "library", + "autoload": { + "files": [ + "lib/special_cases.php", + "generated/apache.php", + "generated/apcu.php", + "generated/array.php", + "generated/bzip2.php", + "generated/calendar.php", + "generated/classobj.php", + "generated/com.php", + "generated/cubrid.php", + "generated/curl.php", + "generated/datetime.php", + "generated/dir.php", + "generated/eio.php", + "generated/errorfunc.php", + "generated/exec.php", + "generated/fileinfo.php", + "generated/filesystem.php", + "generated/filter.php", + "generated/fpm.php", + "generated/ftp.php", + "generated/funchand.php", + "generated/gettext.php", + "generated/gmp.php", + "generated/gnupg.php", + "generated/hash.php", + "generated/ibase.php", + "generated/ibmDb2.php", + "generated/iconv.php", + "generated/image.php", + "generated/imap.php", + "generated/info.php", + "generated/inotify.php", + "generated/json.php", + "generated/ldap.php", + "generated/libxml.php", + "generated/lzf.php", + "generated/mailparse.php", + "generated/mbstring.php", + "generated/misc.php", + "generated/mysql.php", + "generated/mysqli.php", + "generated/network.php", + "generated/oci8.php", + "generated/opcache.php", + "generated/openssl.php", + "generated/outcontrol.php", + "generated/pcntl.php", + "generated/pcre.php", + "generated/pgsql.php", + "generated/posix.php", + "generated/ps.php", + "generated/pspell.php", + "generated/readline.php", + "generated/rnp.php", + "generated/rpminfo.php", + "generated/rrd.php", + "generated/sem.php", + "generated/session.php", + "generated/shmop.php", + "generated/sockets.php", + "generated/sodium.php", + "generated/solr.php", + "generated/spl.php", + "generated/sqlsrv.php", + "generated/ssdeep.php", + "generated/ssh2.php", + "generated/stream.php", + "generated/strings.php", + "generated/swoole.php", + "generated/uodbc.php", + "generated/uopz.php", + "generated/url.php", + "generated/var.php", + "generated/xdiff.php", + "generated/xml.php", + "generated/xmlrpc.php", + "generated/yaml.php", + "generated/yaz.php", + "generated/zip.php", + "generated/zlib.php" + ], + "classmap": [ + "lib/DateTime.php", + "lib/DateTimeImmutable.php", + "lib/Exceptions/", + "generated/Exceptions/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHP core functions that throw exceptions instead of returning FALSE on error", + "support": { + "issues": "https://github.com/thecodingmachine/safe/issues", + "source": "https://github.com/thecodingmachine/safe/tree/v3.4.0" + }, + "funding": [ + { + "url": "https://github.com/OskarStark", + "type": "github" + }, + { + "url": "https://github.com/shish", + "type": "github" + }, + { + "url": "https://github.com/silasjoisten", + "type": "github" + }, + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2026-02-04T18:08:13+00:00", + "published-time": "2026-02-15T15:48:29+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.12.1", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", + "php": "^7.2 || ^8.0" + }, + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.12.1" + }, + "time": "2025-10-29T15:56:20+00:00", + "published-time": "2025-10-29T15:56:42+00:00" + }, + { + "name": "wp-coding-standards/wpcs", + "version": "3.4.1", + "source": { + "type": "git", + "url": "https://github.com/WordPress/WordPress-Coding-Standards.git", + "reference": "ec2ff942335f33683a5957a85d138753876a05cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/ec2ff942335f33683a5957a85d138753876a05cf", + "reference": "ec2ff942335f33683a5957a85d138753876a05cf", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "ext-libxml": "*", + "ext-tokenizer": "*", + "ext-xmlreader": "*", + "php": ">=7.2", + "phpcsstandards/phpcsextra": "^1.5.1", + "phpcsstandards/phpcsutils": "^1.2.3", + "squizlabs/php_codesniffer": "^3.13.5" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpcompatibility/php-compatibility": "^10.0.0@dev", + "phpcsstandards/phpcsdevtools": "^1.2.0", + "phpunit/phpunit": "^8.0 || ^9.0" + }, + "suggest": { + "ext-iconv": "For improved results", + "ext-mbstring": "For improved results" + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Contributors", + "homepage": "https://github.com/WordPress/WordPress-Coding-Standards/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions", + "keywords": [ + "phpcs", + "standards", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/WordPress/WordPress-Coding-Standards/issues", + "source": "https://github.com/WordPress/WordPress-Coding-Standards", + "wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki" + }, + "funding": [ + { + "url": "https://opencollective.com/php_codesniffer", + "type": "custom" + } + ], + "time": "2026-07-27T11:53:23+00:00", + "published-time": "2026-07-27T11:55:52+00:00" + }, + { + "name": "wp-hooks/wordpress-core", + "version": "1.13.0", + "source": { + "type": "git", + "url": "https://github.com/wp-hooks/wordpress-core-hooks.git", + "reference": "6bb3f47ccf1b58415ad8fbcd73ef404cae9cb187" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wp-hooks/wordpress-core-hooks/zipball/6bb3f47ccf1b58415ad8fbcd73ef404cae9cb187", + "reference": "6bb3f47ccf1b58415ad8fbcd73ef404cae9cb187", + "shasum": "" + }, + "replace": { + "johnbillion/wp-hooks": "*" + }, + "require-dev": { + "erusev/parsedown": "1.8.0-beta-7", + "oomphinc/composer-installers-extender": "^2", + "roots/wordpress-core-installer": "^1.0.0", + "roots/wordpress-full": "7.1", + "wp-hooks/generator": "1.0.0" + }, + "type": "library", + "extra": { + "wp-hooks": { + "ignore-files": [ + "wp-admin/includes/deprecated.php", + "wp-admin/includes/ms-deprecated.php", + "wp-content/", + "wp-includes/build/pages/", + "wp-includes/deprecated.php", + "wp-includes/ID3/", + "wp-includes/ms-deprecated.php", + "wp-includes/pomo/", + "wp-includes/random_compat/", + "wp-includes/Requests/", + "wp-includes/SimplePie/", + "wp-includes/sodium_compat/", + "wp-includes/Text/" + ], + "ignore-hooks": [ + "load-categories.php", + "load-edit-link-categories.php", + "load-edit-tags.php", + "load-page-new.php", + "load-page.php", + "option_enable_xmlrpc", + "edit_post_{$field}", + "pre_post_{$field}", + "post_{$field}", + "pre_option_enable_xmlrpc", + "$page_hook", + "$hook", + "$hook_name" + ] + }, + "wordpress-install-dir": "vendor/wordpress/wordpress" + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-3.0-or-later" + ], + "authors": [ + { + "name": "John Blackbourn", + "homepage": "https://johnblackbourn.com/" + } + ], + "description": "All the actions and filters from WordPress core in machine-readable JSON format.", + "support": { + "issues": "https://github.com/wp-hooks/wordpress-core-hooks/issues", + "source": "https://github.com/wp-hooks/wordpress-core-hooks/tree/1.13.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/johnbillion", + "type": "github" + } + ], + "time": "2026-08-20T19:54:29+00:00", + "published-time": "2026-08-20T19:54:42+00:00" + }, + { + "name": "wp-plugin/woocommerce", + "version": "11.2.1", + "source": { + "type": "svn", + "url": "https://plugins.svn.wordpress.org/woocommerce/", + "reference": "tags/11.2.1" + }, + "dist": { + "type": "zip", + "url": "https://downloads.wordpress.org/plugin/woocommerce.11.2.1.zip" + }, + "require": { + "composer/installers": "~1.0|~2.0" + }, + "type": "wordpress-plugin", + "notification-url": "https://wp-packages.org/downloads", + "authors": [ + { + "name": "Automattic" + } + ], + "description": "Everything you need to launch an online store in days and keep it growing for years. From your first sale to millions in revenue, Woo is with you.", + "homepage": "https://woocommerce.com/", + "support": { + "changelog": "https://wordpress.org/plugins/woocommerce/#developers", + "issues": "https://wordpress.org/support/plugin/woocommerce", + "source": "https://plugins.svn.wordpress.org/woocommerce" + }, + "time": "2026-10-09T15:35:21+00:00", + "published-time": "2026-10-09T15:35:21+00:00" + } + ], + "aliases": [], + "minimum-stability": "dev", + "stability-flags": { + "ahegyes/wordpress-configs": 20 + }, + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": ">=8.5" + }, + "platform-dev": {}, + "plugin-api-version": "2.11.0" +} diff --git a/plugins/locked-payment-methods-for-woocommerce/docs/ARCHITECTURE.md b/plugins/locked-payment-methods-for-woocommerce/docs/ARCHITECTURE.md new file mode 100644 index 0000000..63274de --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/docs/ARCHITECTURE.md @@ -0,0 +1,45 @@ +# Architecture + +This document describes how the plugin loads, boots, stores its data and leaves a site, and where each kind of code lives. + +## Loading + +`locked-payment-methods-for-woocommerce.php` is the only file WordPress includes, and it parses on PHP 7.4 so that a site below the floors gets a notice instead of a fatal error: + +1. It defines `DWS_LPMWC_FILE` and registers `languages/`, which WordPress skips for a network-activated plugin. +2. It loads `includes/safeguards.php`, whose restrictions hold on any runtime that parses the plugin: below the floors, on a partial deploy and when the boot fails. +3. It requires `vendor-prefixed/ahegyes/wp-framework-bootstrap/load.php`, registers the GitHub updater, then checks the `Requires PHP` and `Requires at least` headers. The updater comes first, so a release that lowers the floors still reaches a site below them. On a site below a floor, WordPress's own notice shows and nothing past the safeguards loads. +4. It stops with a notice when the prefixed framework or the built blocks are missing. It then requires the autoloader and `functions.php`; a missing file throws, which local and development environments rethrow and every other environment logs, showing the same notice. + +`functions.php` defines the `dws_lpmwc_plugin()` accessor, boots the plugin on `plugins_loaded` at priority 15, after WooCommerce and Action Scheduler have loaded, registers the deactivation callback, declares the plugin's WooCommerce feature compatibility, and requires every `includes/*.php` in name order except those whose names start with `_`. + +## Booting + +`src/Plugin.php` is the composition root. Its constructor builds the logger, the scheduler and the framework's `PluginBoot`, which runs the composition and reports its outcome: + +- The scheduler registers its hooks first, so the plugin's recurring jobs keep their schedules while the plugin is blocked. +- `WooCommerceRequirement` checks WooCommerce against the `WC requires at least` header; without it, the boot is `blocked`, `dws_lpmwc/boot/blocked` fires, and users who can activate plugins see why the plugin stays off. +- The migration runner registers, and runs pending steps on `wp_loaded`. +- The kernel builds each class in `Plugin::COMPONENTS` through a PHP-DI container and calls its `register_hooks()`; a composite adds its children, and a conditional component loads only when its static `should_load()` returns true. +- Once every component has registered, the boot is `booted` and `dws_lpmwc/boot/succeeded` fires. A throw anywhere in the composition makes it `failed`: `dws_lpmwc/boot/failed` fires, the failure is logged, and administrators see a notice, while local and development environments rethrow it. + +The request that activates the plugin does not boot it, because WordPress includes the plugin's file after `plugins_loaded`; its post types, routes and migrations first run on the next request. + +## Where code lives + +- `src/` holds the composition root and its components: final classes that implement the framework's `ComponentInterface` and add their hooks in `register_hooks()`. A component takes ungated services in its constructor, such as the logger, the scheduler or `AdminNotices`, which `Plugin::get_definitions()` provides. +- `includes/` holds code that registers no hooks and is not part of the tree, as prefixed functions and constants: the option keys, defaults and typed readers (`options.php`), the footprint (`footprint.php`), the migration steps (`migrations.php`), the asset registrars (`assets.php`) and the slug, name and version getters a peer plugin reads (`metadata.php`). Quality's syntax job parses the folder on PHP 7.4, because `safeguards.php` runs before the floor check, and `phpstan.safeguards.neon` analyzes it at PHP 7.4 and the oldest WordPress the bootstrap package runs on. +- `blocks/src/` and `assets/src/` hold the TypeScript and SCSS sources, which `npm run build` compiles into `blocks/build/` and `assets/build/`; `includes/assets.php` reads the `*.asset.php` file wp-scripts writes beside each build, and `src/Blocks.php` registers the blocks from `blocks/build/blocks-manifest.php`. +- `models/` and `templates/` are empty extension points, for classes on Composer's classmap and for template partials. + +## Data + +- **Options** are prefixed constants in `includes/options.php`, read only through its typed readers, which guard each stored value and fall back to the key's default. Translated defaults come from `dws_lpmwc_get_option_text()` after `init`; a field shows one as its placeholder and stores an empty value, which its reader reads as the default, so no save stores the admin's language. +- **Migrations** are the append-only steps `dws_lpmwc_get_migration_steps()` returns. The framework's runner runs each pending step once per site on `wp_loaded`, behind a lock, and records the revision reached in `dws_lpmwc_revision`. A step converges from any partial state and throws when a write fails, so a failed step runs again on a later request. +- **The footprint**, `dws_lpmwc_get_footprint()`, names every kind of data the plugin stores: option and user meta prefixes, network options, scheduled hooks and Action Scheduler groups. Options, transients and user meta under the `dws_lpmwc_` prefix are covered; meta on posts, terms, comments or orders needs a `meta_prefixes` entry per object type, and data under another name its own entry, which no check enforces. +- **Deactivation** unschedules every hook in the footprint's `cron_hooks` and `as_hooks` on the current site. +- **Uninstall** runs `uninstall.php`, which calls the framework's uninstall engine over the footprint, with WooCommerce's order meta types, on every site whose `dws_lpmwc_delete_data` setting consents; a site that never consented keeps its data. Below the floors or with an incomplete build, a web request deletes the files, keeps the data and logs why, while WP-CLI fails and keeps both for a rerun. + +## Prefixing and distribution + +Every development install runs php-scoper through wordpress-configs, which copies the framework, PHP-DI and their dependencies into `vendor-prefixed/` under `DeepWebSolutions\LockedPaymentMethods\Scoped\`, so each plugin carries its own copy and never meets another plugin's. The release workflow builds `locked-payment-methods-for-woocommerce.zip` from a production install, checks it with Plugin Check, the archive and header checks, activates it on a site and runs the end-to-end suite against it. A pull request rehearses the same build without publishing. A tag publishes a GitHub release, which the bundled updater offers through WordPress's own update check while the `Update URI` header points at the repository. diff --git a/plugins/locked-payment-methods-for-woocommerce/docs/recipes.md b/plugins/locked-payment-methods-for-woocommerce/docs/recipes.md new file mode 100644 index 0000000..b7fcfd7 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/docs/recipes.md @@ -0,0 +1,1293 @@ +# Recipes + +These are the changes most plugins built from this template make, each as the files to add or change. A recipe that stores data lists its keys for `includes/footprint.php`, which no check enforces. Options, transients and user meta under the plugin's `dws_lpmwc_` prefix are in the footprint already; meta on posts, terms, comments or orders needs its own `meta_prefixes` entry per object type. + +- [A safeguard](#a-safeguard) +- [A one-time notice](#a-one-time-notice) +- [A WordPress settings page](#a-wordpress-settings-page) +- [A product data tab](#a-product-data-tab) +- [A form on an edit screen](#a-form-on-an-edit-screen) +- [A section on WooCommerce's Payments tab](#a-section-on-woocommerces-payments-tab) +- [Recurring jobs](#recurring-jobs) +- [Rewrite rules after activation](#rewrite-rules-after-activation) +- [A script module](#a-script-module) +- [Renaming a public hook](#renaming-a-public-hook) +- [Distribution through wordpress.org](#distribution-through-wordpressorg) +- [Without WooCommerce](#without-woocommerce) + +## A safeguard + +A safeguard is a restriction that holds even when the rest of the plugin fails to load, such as a capability that stays locked unless the plugin unlocks it. It lives in `includes/safeguards.php`, which the entry file loads before the floor gate and the autoloader, so it calls only WordPress and what that file defines. The plugin relaxes it only through the safeguard's own filter, with an explicit `false`, and only after `dws_lpmwc/boot/succeeded`, which fires once the plugin has booted, so a half-loaded build never relaxes it. + +Replace `includes/safeguards.php` with the helper and the safeguard. The file runs on any runtime that parses the plugin, so it must parse on PHP 7.4 and call only WordPress APIs present at the oldest WordPress the bootstrap package runs on, or guard them, which `phpstan.safeguards.neon` checks: + +```php +export(); // Checks current_user_can( 'dws_lpmwc_export_data' ). +} finally { + remove_filter( 'dws_lpmwc/data_export/should_restrict', '__return_false' ); +} +``` + +Add the safeguard's failure test as `tests/Integration/SafeguardsTest.php`. The plugin has booted when the Integration suite runs, so the last row takes `dws_lpmwc/boot/succeeded` back for its duration: + +```php + array( array(), false ), + 'null' => array( array( 'relaxation' => null ), false ), + 'zero' => array( array( 'relaxation' => 0 ), false ), + 'empty-string' => array( array( 'relaxation' => '' ), false ), + 'false' => array( array( 'relaxation' => false ), true ), + ); + } + + #[DataProvider( 'relaxations' )] + public function test_only_an_explicit_false_relaxes_the_export_lock( array $spec, bool $relaxed ): void { + $relaxation = static fn (): mixed => $spec['relaxation']; + if ( \array_key_exists( 'relaxation', $spec ) ) { + add_filter( self::FILTER, $relaxation ); + } + + try { + self::assertSame( $relaxed ? array( 'manage_options' ) : array( 'do_not_allow' ), map_meta_cap( 'dws_lpmwc_export_data', 0 ) ); + } finally { + remove_filter( self::FILTER, $relaxation ); + } + } + + public function test_an_explicit_false_does_not_relax_the_export_lock_before_the_plugin_booted(): void { + global $wp_actions; + self::assertGreaterThan( 0, did_action( 'dws_lpmwc/boot/succeeded' ) ); + $fired = $wp_actions['dws_lpmwc/boot/succeeded']; + unset( $wp_actions['dws_lpmwc/boot/succeeded'] ); + add_filter( self::FILTER, '__return_false' ); + + try { + self::assertSame( array( 'do_not_allow' ), map_meta_cap( 'dws_lpmwc_export_data', 0 ) ); + } finally { + remove_filter( self::FILTER, '__return_false' ); + $wp_actions['dws_lpmwc/boot/succeeded'] = $fired; + } + } +} +``` + +## A one-time notice + +A one-time notice shows once, on the user's next admin screen, such as after a form post redirects. The plugin's `AdminNotices` is in `Plugin::COMPONENTS` and in the container, so a component takes it in its constructor: + +```php +use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\AdminNotice\AdminNotices; +use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\AdminNotice\Notice; +use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\AdminNotice\NoticeType; + +// ... + + /** + * Constructor. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param AdminNotices $notices The plugin's admin notices. + */ + public function __construct( + protected AdminNotices $notices + ) {} + + // ... + + /** + * Tells the current user, on their next admin screen, how many rows the export wrote. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param int $rows The number of rows the export wrote. + * + * @return void + */ + protected function report_export( int $rows ): void { + $this->notices->add( + new Notice( + Plugin::PREFIX . '-export-' . time(), // A viewer's delivery outlives the notice, so each occasion takes a new id. + /* translators: %d: The number of rows. */ + esc_html( sprintf( _n( 'The export wrote %d row.', 'The export wrote %d rows.', $rows, 'locked-payment-methods-for-woocommerce' ), $rows ) ), + NoticeType::Success + ) + ); + } +``` + +The notice's scope defaults to the current user and its message is HTML, so escape it. A notice for every administrator of the site takes `NoticeScope::Site`, a capability and `per_viewer: true`, since without it the first administrator's viewing consumes it for everyone; one that stays until dismissed takes `persistent: true`. + +## A WordPress settings page + +An options page adds a screen under a WordPress admin menu and stores each tab's fields in one option through the Settings API. WooCommerce settings belong on a WooCommerce tab, as `src/Settings.php` does. + +Add the option, its field and its default, a reader that guards the field and the validator to `includes/options.php`, which stays PHP 7.4-parseable. The non-WooCommerce recipe below uses the same names, with the uninstall consent as a second field: + +```php +/** + * The option that holds the settings page's fields, keyed by field id. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @var string + */ +const DWS_LPMWC_OPTION_SETTINGS = Plugin::PREFIX . '_settings'; + +/** + * The settings field that holds the limit. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @var string + */ +const DWS_LPMWC_SETTING_LIMIT = 'limit'; + +/** + * The value each settings field reads as while nothing valid is stored. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @var array + */ +const DWS_LPMWC_SETTING_DEFAULTS = array( + DWS_LPMWC_SETTING_LIMIT => 1, +); + +/** + * Returns a settings field as an integer. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string $field The field's id. + * + * @return int + */ +function dws_lpmwc_get_int_setting( string $field ): int { + $default = DWS_LPMWC_SETTING_DEFAULTS[ $field ] ?? null; + $stored = get_option( DWS_LPMWC_OPTION_SETTINGS ); + $stored = is_array( $stored ) && array_key_exists( $field, $stored ) ? $stored[ $field ] : $default; + + return filter_var( $stored, FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE ) ?? ( is_int( $default ) ? $default : 0 ); +} + +/** + * Returns the limit as the settings page stores it, or an error when the submission is not a whole number of at least 1. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param mixed $submitted The submitted value. + * + * @return int|WP_Error + */ +function dws_lpmwc_validate_limit( $submitted ) { + $limit = is_string( $submitted ) ? filter_var( $submitted, FILTER_VALIDATE_INT, array( 'options' => array( 'min_range' => 1 ) ) ) : false; + + return false === $limit ? new WP_Error( 'invalid_number', __( 'Enter a whole number of 1 or more, such as 3.', 'locked-payment-methods-for-woocommerce' ) ) : $limit; +} +``` + +Add the page as a component, `src/SettingsPage.php`, and list it in `Plugin::COMPONENTS`: + +```php +register_hooks(); + } + + /** + * {@inheritDoc} + * + * @since 2.0.0 + * @version 2.0.0 + */ + #[\Override] + public function get_options_page( string $page_id ): array { + return array( + 'parent' => 'options-general.php', + 'capability' => 'manage_options', + 'page_title' => __( 'Locked Payment Methods for WooCommerce', 'locked-payment-methods-for-woocommerce' ), + 'menu_title' => __( 'Locked Payment Methods for WooCommerce', 'locked-payment-methods-for-woocommerce' ), + 'tabs' => array( + array( + 'slug' => 'general', + 'title' => __( 'General', 'locked-payment-methods-for-woocommerce' ), + 'option' => DWS_LPMWC_OPTION_SETTINGS, + 'fields' => array( + array( + 'type' => 'number', + 'id' => DWS_LPMWC_SETTING_LIMIT, + 'title' => __( 'Limit', 'locked-payment-methods-for-woocommerce' ), + 'default' => (string) DWS_LPMWC_SETTING_DEFAULTS[ DWS_LPMWC_SETTING_LIMIT ], + 'custom_attributes' => array( + 'min' => '1', + 'step' => '1', + ), + 'validate' => 'dws_lpmwc_validate_limit', + ), + ), + ), + ), + ); + } + + // endregion +} +``` + +Read the limit with `dws_lpmwc_get_int_setting( DWS_LPMWC_SETTING_LIMIT )`, which returns the default until a valid limit is stored. The option is under the plugin's prefix, so the footprint covers it; a migration step that stores `DWS_LPMWC_SETTING_DEFAULTS` autoloaded saves the first request's query, as the non-WooCommerce recipe's step does. + +## A product data tab + +A product data tab adds a panel to the product editor's Product data box, whose fields WooCommerce renders and saves as the product's meta. Add it as a component, `src/ProductData.php`, and list it in `Plugin::COMPONENTS`: + +```php +register_hooks(); + } + + /** + * {@inheritDoc} + * + * @since 2.0.0 + * @version 2.0.0 + */ + #[\Override] + public function get_product_data_tab( string $tab_id, \WC_Product $product ): array { + return array( + 'label' => __( 'Packing', 'locked-payment-methods-for-woocommerce' ), + 'groups' => array( + array( + 'fields' => array( + array( + 'type' => 'text', + 'id' => '_' . Plugin::PREFIX . '_box', + 'label' => __( 'Box', 'locked-payment-methods-for-woocommerce' ), + ), + ), + ), + ), + ); + } + + // endregion +} +``` + +Read the stored value with `ProductMeta::get_value( $product, '_dws_lpmwc_box' )`. The leading underscore hides the key from WordPress's Custom Fields box. Post meta is not in the footprint until its type is, so declare the prefix for uninstall in `includes/footprint.php`: + +```php +'meta_prefixes' => array( 'post' => array( '_' . Plugin::PREFIX . '_' ) ), +``` + +A field with its own `render` callable returns its control as inline markup, escapes, names and describes it, and prints a hidden empty input before it unless the field is disabled. Without that input, a cleared control posts nothing, and the save leaves the stored value untouched. It also takes a `validate` callable that returns the value to store or a `WP_Error`, since the framework refuses to save a custom control's value that nothing validates. + +## A form on an edit screen + +An object form adds fields to an edit screen, such as a post's meta box, a user's profile or a term's form, and stores each field on the edited object. It reports a rejected value through the plugin's `AdminNotices`. Add it as a component, `src/PostNote.php`, and list it in `Plugin::COMPONENTS`: + +```php +notices )->register_hooks(); + } + + /** + * {@inheritDoc} + * + * @since 2.0.0 + * @version 2.0.0 + */ + #[\Override] + public function get_object_form( string $form_id, int $object_id ): array { + return array( + 'title' => __( 'Editorial note', 'locked-payment-methods-for-woocommerce' ), + 'capability' => 'edit_posts', + 'fields' => array( + array( + 'type' => 'textarea', + 'id' => '_' . Plugin::PREFIX . '_note', + 'title' => __( 'Note', 'locked-payment-methods-for-woocommerce' ), + ), + ), + ); + } + + // endregion +} +``` + +The host checks that the user can edit the post itself, and the form's `capability` is one more capability the user needs, checked without an object, so it names a primitive capability such as `edit_posts` rather than `edit_post`. The form verifies its own nonce before it asks for the definition on a save. Declare the meta for uninstall in `includes/footprint.php`, as the product data tab does. A field with its own `render` callable prints a hidden empty input before its control unless the field is disabled, and takes a `validate` callable, for the reasons that recipe gives. + +## A section on WooCommerce's Payments tab + +A section on an existing WooCommerce tab, here the Payments tab (`checkout`), holds settings that belong beside WooCommerce's own. WooCommerce renders the Payments tab as its own page and lists no extension sections there, so the section is reached through its direct link, which the plugin's row on the Plugins screen carries. Add it as a component, `src/PaymentSettings.php`, and list it in `Plugin::COMPONENTS`: + +```php + 'title', + 'id' => Plugin::PREFIX . '_payment_options', + 'title' => __( 'Locked Payment Methods for WooCommerce', 'locked-payment-methods-for-woocommerce' ), + ), + array( + 'type' => 'checkbox', + 'id' => Plugin::PREFIX . '_payment_notes', + 'title' => __( 'Payment notes', 'locked-payment-methods-for-woocommerce' ), + 'desc' => __( 'Add a note to each order when its payment completes', 'locked-payment-methods-for-woocommerce' ), + ), + array( + 'type' => 'sectionend', + 'id' => Plugin::PREFIX . '_payment_options', + ), + ); + } + + // endregion + + // region HOOKS + + /** + * Registers the plugin's section on the Payments tab. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param SettingsSectionRegistry $registry WooCommerce's registry of settings sections. + * + * @return void + */ + public function register_section( SettingsSectionRegistry $registry ): void { + $registry->register( + new SettingsSection( + Plugin::PREFIX, + array( + 'tab' => 'checkout', + 'label' => __( 'Locked Payment Methods for WooCommerce', 'locked-payment-methods-for-woocommerce' ), + ), + $this + ) + ); + } + + /** + * Adds the link to the plugin's section to its row on the Plugins screen. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string[] $links The row's action links. + * + * @return string[] + */ + public function add_settings_link( array $links ): array { + array_unshift( $links, sprintf( '%s', esc_url( admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=' . Plugin::PREFIX ) ), esc_html__( 'Settings', 'locked-payment-methods-for-woocommerce' ) ) ); + + return $links; + } + + // endregion +} +``` + +WooCommerce stores each field in its own option under its id, flat rather than in a group, and a WooCommerce checkbox stores `yes` or `no`; read it with `dws_lpmwc_get_bool_option()` once the key and its default are in `includes/options.php`. + +## Recurring jobs + +A recurring job runs a hook at an interval through the plugin's scheduler, which uses Action Scheduler where it is loaded and WP-Cron otherwise. Either can run an invocation twice or skip one, so the job's callback leaves its data consistent after a duplicate or a missed run. + +Add the repair gate's option to `includes/options.php`: + +```php +/** + * The option that holds when the plugin last ensured its recurring jobs, which lets a request skip ensuring them for an hour. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @var string + */ +const DWS_LPMWC_OPTION_RECURRING_ENSURED_AT = Plugin::PREFIX . '_recurring_ensured_at'; +``` + +Add the job as a component, `src/Pruning.php`, and list it in `Plugin::COMPONENTS`. It ensures the schedule on `wp_loaded` at most once an hour, writing the time first so concurrent requests skip, and again when Action Scheduler asks every day: + +```php +ensure_schedule(); + } + + /** + * Schedules the daily prune unless it runs at that interval already, logging a scheduler that refuses. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @return void + */ + public function ensure_schedule(): void { + $ensured = $this->scheduler->ensure_recurring( new ScheduledHook( self::HOOK ), Recurrence::from_interval( DAY_IN_SECONDS ) ); + if ( true !== $ensured ) { + $this->logger->warning( + 'Could not schedule {hook}: {error}', + array( + 'hook' => self::HOOK, + 'error' => $ensured->get_error_message(), + 'exception' => $ensured->get_error_data(), + ) + ); + } + } + + /** + * Deletes the records that have expired. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @return void + */ + public function prune(): void { + // Delete by expiry, so a second run in a day finds nothing left and a missed day catches up. + } + + // endregion +} +``` + +Then: +- Add `Plugin::PREFIX . '/records/prune'` to `cron_hooks` in `includes/footprint.php`. Deactivation unschedules every hook there through the plugin's scheduler, and uninstall clears WP-Cron's copies, while Action Scheduler's are in the footprint's `as_groups` already. +- Delete the repair gate's option on activation, so the next request ensures the jobs: add `register_activation_hook( DWS_LPMWC_FILE, 'dws_lpmwc_activate' );` to `functions.php` beside the deactivation hook, and a `dws_lpmwc_activate()` that calls `delete_option( DWS_LPMWC_OPTION_RECURRING_ENSURED_AT )`. +- Change an interval in code only: `ensure_recurring()` replaces a schedule whose interval differs on the next pass of the repair gate. A "run now" schedules the same hook with an argument that sets it apart, or calls `prune()` directly. + +## Rewrite rules after activation + +WordPress builds its rewrite rules from the post types and taxonomies registered on `init`. The request that activates the plugin does not boot it, because WordPress includes the plugin's file after `plugins_loaded`, so flushing there would drop the plugin's rules. Delete the stored rules instead: the next request rebuilds them once the plugin has registered its types. + +In `functions.php`, beside the deactivation hook: + +```php +register_activation_hook( DWS_LPMWC_FILE, 'dws_lpmwc_activate' ); + +// ... + +/** + * Lets the next request rebuild the site's rewrite rules with the plugin's post types and taxonomies. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @return void + */ +function dws_lpmwc_activate(): void { + delete_option( 'rewrite_rules' ); +} +``` + +Add the same `delete_option( 'rewrite_rules' )` to `dws_lpmwc_deactivate()`, so the next request rebuilds the rules without them. Both act on the current site only, as WordPress's own activation does; a network activation leaves the other sites' rules until they next rebuild them. + +## A script module + +A script module is a JavaScript module WordPress loads through its import map, such as a block's view script for the Interactivity API. wp-scripts builds script modules only from a block's `block.json`, and WordPress registers each one from the block's metadata, with the imports wp-scripts recorded, and enqueues it when the block renders, so the plugin registers nothing itself. + +Name the module in the block's `block.json`, here `blocks/src/example-notice/block.json`: + +```json +"viewScriptModule": "file:./view.js" +``` + +Add the module beside it, as `blocks/src/example-notice/view.ts`: + +```ts +import { store } from '@wordpress/interactivity'; + +store( 'locked-payment-methods-for-woocommerce/example-notice', {} ); +``` + +Declare the module's import, which today arrives only through `@wordpress/block-editor`: + +```sh +npm install --save-dev @wordpress/interactivity +``` + +Build the blocks' modules with their scripts, by adding `--experimental-modules` to `build:blocks` and `start:blocks` in `package.json`: + +```json +"build:blocks": "wp-scripts build --webpack-src-dir=blocks/src --output-path=blocks/build --blocks-manifest --experimental-modules", +"start:blocks": "wp-scripts start --webpack-src-dir=blocks/src --output-path=blocks/build --blocks-manifest --experimental-modules" +``` + +The build writes `view.js` with a `view.asset.php` whose `type` is `module`, and `src/Blocks.php`'s registration from the manifest picks it up. + +## Renaming a public hook + +A public hook's name is part of the plugin's contract, as its arguments, its options and its REST routes are. To rename one in a released plugin: + +1. Rename the literal at every `do_action()` or `apply_filters()` and in its docblock, which keeps its `@since` and moves its `@version` to the release. +2. Add a changelog fragment with `composer changelog:add` that names the old and the new hook, with a major significance. +3. Migrate stored data that holds the name, such as a scheduled job's hook, in a migration step that converges from any partial state: unschedule the old name, then ensure the new one. + +Ship no deprecated alias: the major release is the migration path. + +## Distribution through wordpress.org + +A plugin listed on wordpress.org gets its updates and its language packs from there, so it drops the GitHub updater's header and adds the directory's `readme.txt`. + +1. In the entry file, delete the `Update URI` header, which turns the bundled updater inert, and the `load_plugin_textdomain()` line, since language packs load just in time. +2. Add `readme.txt`, whose `Stable tag` the release checks against the `Version` header, whose floors repeat the entry file's headers, and whose changelog block `composer changelog:write` fills: + +```text +=== Locked Payment Methods for WooCommerce === +Contributors: TODO +Tags: TODO +Tested up to: TODO, the newest WordPress branch the plugin is tested on +Stable tag: TODO, the Version header +Requires at least: TODO, the Requires at least header +Requires PHP: TODO, the Requires PHP header +License: GPLv3 or later +License URI: https://www.gnu.org/licenses/gpl-3.0.html + +TODO: the plugin's one sentence, at most 150 characters. + +== Description == + +TODO: what the plugin does, for the directory's listing. + +== Installation == + +1. Install the plugin from Plugins → Add New Plugin. +2. Activate it. + +== Changelog == + + + + + +[See the full changelog on GitHub.](https://github.com/ahegyes/wc-locked-payment-methods/blob/trunk/CHANGELOG.md) +``` + +3. Wrap `CHANGELOG.md`'s version blocks in the same `` and `` markers, require `pronamic/changelog-md-to-wordpress-plugin-readme-txt` in `require-dev`, and make `changelog:write` sync the file: + +```json +"changelog:write": [ + "@php vendor/bin/changelogger write", + "@php vendor/bin/pronamic-changelog-md-to-wordpress-plugin-readme-txt" +] +``` + +4. Lint the file in `.github/workflows/quality.yml`: + +```yaml + readme-lint: + name: readme.txt lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: thetwopct/wporg-plugin-readme-linter@1f869aba959265ecc90935b2380c6569d56646ac # v1.0.2 + with: + path: readme.txt + fail-on: error +``` + +5. In `.github/workflows/release.yml`, pass `wp-org: true` to the reusable release. A tag then also deploys the zip and the `.wordpress-org/` assets to the directory's SVN from the `wp-org-release` environment: create it, store `SVN_USERNAME` and `SVN_PASSWORD` there and only there, and protect it with required reviewers. + +The wordpress.org build runs Plugin Check's full set but for the checks `exclude-checks` names; Plugin Check's `prefixing` and `direct_file_access` stop at PHP 8.4 syntax such as `protected(set)`, which wordpress.org's own scan may meet too. The bundled updater ships inert under `vendor-prefixed/`, which wordpress.org's reviewers may still ask to remove. + +## Without WooCommerce + +A plugin that does not need WooCommerce drops the WooCommerce package, the WooCommerce demo and its tooling, and keeps its settings on a WordPress options page. + +1. In `composer.json`, delete the `vcs` repository entry whose `url` is `https://github.com/ahegyes/wp-framework-woocommerce`. Then remove the WooCommerce packages, which leaves every other locked version as it is: + +```sh +composer remove --dev --ignore-platform-req=php+ --no-interaction ahegyes/wp-framework-woocommerce php-stubs/woocommerce-stubs wp-plugin/woocommerce +``` + +2. In `phpstan.dist.neon`, delete the `scanFiles` block that names the WooCommerce stubs: + +```neon + scanFiles: + - vendor/php-stubs/woocommerce-stubs/woocommerce-stubs.php + - vendor/php-stubs/woocommerce-stubs/woocommerce-packages-stubs.php +``` + +3. In `locked-payment-methods-for-woocommerce.php`, delete the `Requires Plugins`, `WC requires at least` and `WC tested up to` headers. + +4. In `functions.php`, delete the `FeaturesUtil` import, the `before_woocommerce_init` hook and `dws_lpmwc_declare_compatibility()`, and drop "declares its WooCommerce feature compatibility" from the file's summary: + +```php +use DeepWebSolutions\LockedPaymentMethods\Plugin; + +defined( 'ABSPATH' ) || exit; + +add_action( 'plugins_loaded', 'dws_lpmwc_boot', 15 ); +register_deactivation_hook( DWS_LPMWC_FILE, 'dws_lpmwc_deactivate' ); +``` + +5. In `src/Plugin.php`, log through core's `ErrorLogLogger`, which drops records below warning unless `WP_DEBUG` is on, so `DWS_LPMWC_DEBUG` goes. Compose without the WooCommerce check, and leave the greeting out of `COMPONENTS`: + +```php +use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\AdminNotice\AdminNotices; +use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\Logging\ErrorLogLogger; +use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\Migration\MigrationRunner; +// ... + + public const array COMPONENTS = array( + Scheduler::class, + AdminNotices::class, + Settings::class, + Blocks::class, + ); + + /** + * The scheduler, which the components and the deactivation callback share. + // ... + */ + public readonly Scheduler $scheduler; + + // ... + + public function __construct() { + $this->logger = new ErrorLogLogger( self::SLUG ); + $this->scheduler = new Scheduler( self::PREFIX ); + $this->boot = new PluginBoot( DWS_LPMWC_FILE, self::PREFIX, $this->logger ); + } + + // ... + + /** + * Registers the scheduler and the migrations, then builds the kernel and boots the plugin's components. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @return bool + */ + protected function compose(): bool { + $this->scheduler->register_hooks(); + $this->migrations = new MigrationRunner( DWS_LPMWC_FILE, self::PREFIX . '_revision', dws_lpmwc_get_migration_steps(), $this->logger, $this->scheduler ); + $this->migrations->register_hooks(); + + $this->kernel = new PluginKernel( new ContainerBuilder()->addDefinitions( $this->get_definitions( $this->logger, $this->scheduler ) )->build() ); + $this->kernel->boot( self::COMPONENTS ); + + return true; + } +``` + +6. Delete the greeting: `src/Feature/`, `tests/Integration/Feature/` and `assets/src/greeting.scss`, then its entries in `package.json`: + +```json +"build:assets": "wp-scripts build editor=./assets/src/editor.ts --output-path=assets/build", +"start:assets": "wp-scripts start editor=./assets/src/editor.ts --output-path=assets/build" +``` + +7. Replace `src/Settings.php` with an options page under Settings, which stores both fields in one option through the Settings API: + +```php +register_hooks(); + } + + /** + * {@inheritDoc} + * + * @since 2.0.0 + * @version 2.0.0 + */ + #[\Override] + public function get_options_page( string $page_id ): array { + return array( + 'parent' => 'options-general.php', + 'capability' => 'manage_options', + 'page_title' => __( 'Locked Payment Methods for WooCommerce', 'locked-payment-methods-for-woocommerce' ), + 'menu_title' => __( 'Locked Payment Methods for WooCommerce', 'locked-payment-methods-for-woocommerce' ), + 'tabs' => array( + array( + 'slug' => 'general', + 'title' => __( 'General', 'locked-payment-methods-for-woocommerce' ), + 'option' => DWS_LPMWC_OPTION_SETTINGS, + 'fields' => array( + array( + 'type' => 'number', + 'id' => DWS_LPMWC_SETTING_LIMIT, + 'title' => __( 'Limit', 'locked-payment-methods-for-woocommerce' ), + 'default' => (string) DWS_LPMWC_SETTING_DEFAULTS[ DWS_LPMWC_SETTING_LIMIT ], + 'custom_attributes' => array( + 'min' => '1', + 'step' => '1', + ), + 'validate' => 'dws_lpmwc_validate_limit', + ), + array( + 'type' => 'checkbox', + 'id' => DWS_LPMWC_SETTING_DELETE_DATA, + 'title' => __( 'Data on uninstall', 'locked-payment-methods-for-woocommerce' ), + 'desc' => __( 'Delete the plugin\'s data when it is uninstalled', 'locked-payment-methods-for-woocommerce' ), + ), + ), + ), + ), + ); + } + + // endregion +} +``` + +8. Replace `includes/options.php` with the one settings option, its fields, their defaults, and readers that guard each field. The file stays PHP 7.4-parseable, because Quality's syntax job lints `includes/`: + +```php + + */ +const DWS_LPMWC_SETTING_DEFAULTS = array( + DWS_LPMWC_SETTING_DELETE_DATA => false, + DWS_LPMWC_SETTING_LIMIT => 1, +); + +/** + * Returns a settings field as a boolean, which reads checkbox values such as 'yes' and 'no'. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string $field The field's id. + * + * @return bool + */ +function dws_lpmwc_get_bool_setting( string $field ): bool { + $default = dws_lpmwc_get_setting_default( $field ); + + return filter_var( dws_lpmwc_get_setting( $field, $default ), FILTER_VALIDATE_BOOL, FILTER_NULL_ON_FAILURE ) ?? ( true === $default ); +} + +/** + * Returns a settings field as an integer. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string $field The field's id. + * + * @return int + */ +function dws_lpmwc_get_int_setting( string $field ): int { + $default = dws_lpmwc_get_setting_default( $field ); + + return filter_var( dws_lpmwc_get_setting( $field, $default ), FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE ) ?? ( is_int( $default ) ? $default : 0 ); +} + +/** + * Returns the limit as the settings page stores it, or an error when the submission is not a whole number of at least 1. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param mixed $submitted The submitted value. + * + * @return int|WP_Error + */ +function dws_lpmwc_validate_limit( $submitted ) { + $limit = is_string( $submitted ) ? filter_var( $submitted, FILTER_VALIDATE_INT, array( 'options' => array( 'min_range' => 1 ) ) ) : false; + + return false === $limit ? new WP_Error( 'invalid_number', __( 'Enter a whole number of 1 or more, such as 3.', 'locked-payment-methods-for-woocommerce' ) ) : $limit; +} + +/** + * Returns a settings field's stored value, or the fallback while the settings hold none. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string $field The field's id. + * @param mixed $fallback The value to return while nothing is stored. + * + * @return mixed + */ +function dws_lpmwc_get_setting( string $field, $fallback ) { + $settings = get_option( DWS_LPMWC_OPTION_SETTINGS ); + + return is_array( $settings ) && array_key_exists( $field, $settings ) ? $settings[ $field ] : $fallback; +} + +/** + * Returns a settings field's default, or null for a field without one. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string $field The field's id. + * + * @return mixed + */ +function dws_lpmwc_get_setting_default( string $field ) { + return DWS_LPMWC_SETTING_DEFAULTS[ $field ] ?? null; +} +``` + +9. In `includes/migrations.php`, the first step stores the defaults as they are. A stored `false` renders as an unchecked checkbox, the readers guard it, and uninstall consents only on `'yes'`: + +```php +/** + * Stores the settings' defaults, autoloaded, so a site that never saved the settings page reads them without a query. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @throws RuntimeException Thrown when the defaults could not be stored. + * + * @return void + */ +function dws_lpmwc_add_settings(): void { + if ( false === get_option( DWS_LPMWC_OPTION_SETTINGS ) && ! add_option( DWS_LPMWC_OPTION_SETTINGS, DWS_LPMWC_SETTING_DEFAULTS, '', true ) ) { + throw new RuntimeException( 'The option ' . DWS_LPMWC_OPTION_SETTINGS . ' could not be stored.' ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing, never rendered. + } +} +``` + +10. In `uninstall.php`, delete the `MetaTypes` import, and take consent from the settings row's `delete_data` key: + +```php + $dws_lpmwc_footprint = dws_lpmwc_get_footprint(); + $dws_lpmwc_consent = array( + 'option' => DWS_LPMWC_OPTION_SETTINGS, + 'key' => DWS_LPMWC_SETTING_DELETE_DATA, + ); +// ... + +Uninstaller::run( + $dws_lpmwc_file, + $dws_lpmwc_footprint, + array( 'consent' => $dws_lpmwc_consent ) +); +``` + +11. In `.wp-env.json` and `.wp-env.tests.json`, delete the WooCommerce mapping, and let `afterStart` only activate the plugin; `.wp-env.json`'s command takes no `--config`: + +```json +"mappings": { + "wp-content/plugins/locked-payment-methods-for-woocommerce": "." +}, +"lifecycleScripts": { + "afterStart": "node_modules/.bin/wp-env --config .wp-env.tests.json run cli wp plugin activate locked-payment-methods-for-woocommerce" +} +``` + +12. Adapt the tests: +- Delete `tests/e2e/without-woocommerce.spec.ts`. +- `OptionsTest` reads through `dws_lpmwc_get_bool_setting()` and `dws_lpmwc_get_int_setting()` from a `DWS_LPMWC_OPTION_SETTINGS` row; a malformed limit reads its default 1; the validator rows call `dws_lpmwc_validate_limit()`, which returns integers. +- `MigrationsTest` expects `DWS_LPMWC_SETTING_DEFAULTS` stored autoloaded, a stored row kept as it is, and the throw when that option's `INSERT` fails. +- `UninstallTest` consents through `array( DWS_LPMWC_SETTING_DELETE_DATA => 'yes' )`, seeds and asserts no Action Scheduler action, and expects the defaults stored again afterwards. +- `PluginBootTest` expects nothing in `$plugin->kernel->skipped`. +- `SettingsTest` builds `new OptionsPage( Plugin::PREFIX, new Settings() )` and calls its `register_settings()` in `setUpBeforeClass()`, which first requires `wp-admin/includes/template.php`. `tearDownAfterClass()` removes the page's `pre_update_option_` and `option_page_capability_` filters and calls `unregister_setting()`. Each test resets `$GLOBALS['wp_settings_errors']`, renders through `render_page()` and saves as `options.php` does: + +```php + // options.php saves a submission through update_option(), whose filter the page hooks for its own option page only. + protected function save( array $posted ): void { + $GLOBALS['pagenow'] = 'options.php'; + $_REQUEST['option_page'] = DWS_LPMWC_OPTION_SETTINGS; + + try { + update_option( DWS_LPMWC_OPTION_SETTINGS, $posted ); + } finally { + unset( $GLOBALS['pagenow'], $_REQUEST['option_page'] ); + } + } +``` + +- `tests/e2e/settings.spec.ts` drives the page's limit field with the same checks: it focuses `page.getByLabel( 'Limit' )`, tabs to the `Save Changes` button and scopes axe to the form: + +```ts +const SAVED = 'Settings saved.'; +const REJECTED = + 'Limit was not saved, so it keeps its previous value. Enter a whole number of 1 or more, such as 3.'; +const FORM = 'form[action="options.php"]'; + +// ... + + test.beforeEach( async ( { admin } ) => { + await admin.visitAdminPage( 'options-general.php', 'page=dws_lpmwc' ); + } ); +``` + +13. Update `AGENTS.md`, `README.md` and `docs/ARCHITECTURE.md` wherever they name WooCommerce, the greeting, the settings tab or the order-tables declaration. diff --git a/plugins/locked-payment-methods-for-woocommerce/eslint.config.mjs b/plugins/locked-payment-methods-for-woocommerce/eslint.config.mjs new file mode 100644 index 0000000..edf6c09 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/eslint.config.mjs @@ -0,0 +1,3 @@ +import baseConfig from '@ahegyes/wordpress-configs/node/eslint.config.base.mjs'; + +export default [ ...baseConfig ]; diff --git a/plugins/locked-payment-methods-for-woocommerce/functions.php b/plugins/locked-payment-methods-for-woocommerce/functions.php new file mode 100644 index 0000000..53e92c5 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/functions.php @@ -0,0 +1,92 @@ +boot(); + } catch ( Throwable $error ) { + dws_lpmwc_handle_build_error( $error ); + } +} + +/** + * Unschedules the plugin's jobs on the site it is deactivated on, reporting a composition root that cannot be built as an incomplete build. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @return void + */ +function dws_lpmwc_deactivate(): void { + try { + dws_lpmwc_plugin()->deactivate(); + } catch ( Throwable $error ) { + dws_lpmwc_handle_build_error( $error ); + } +} + +/** + * Declares whether the plugin works with WooCommerce's order tables, which each plugin built from the template sets from its own evidence. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @return void + */ +function dws_lpmwc_declare_compatibility(): void { + FeaturesUtil::declare_compatibility( 'custom_order_tables', DWS_LPMWC_FILE, false ); +} diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/assets.php b/plugins/locked-payment-methods-for-woocommerce/includes/assets.php new file mode 100644 index 0000000..55381e3 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/includes/assets.php @@ -0,0 +1,77 @@ +, version: string, type?: string, handle?: string} + */ +function dws_lpmwc_get_asset( string $path ): array { + /** + * The payload wp-scripts generates. + * + * @var array{dependencies: list, version: string, type?: string, handle?: string} $asset + */ + $asset = require plugin_dir_path( DWS_LPMWC_FILE ) . dirname( $path ) . '/' . basename( $path, '.' . pathinfo( $path, PATHINFO_EXTENSION ) ) . '.asset.php'; + + return $asset; +} + +/** + * Registers a built script with its generated dependencies and version, and loads its translations from languages/. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param non-empty-string $handle The script's handle. + * @param non-empty-string $path The built script's path, relative to the plugin's directory. + * @param array{strategy?: 'async'|'defer', in_footer?: bool, fetchpriority?: 'auto'|'low'|'high'} $args Optional. How the script loads. Default none, which loads it in the head. + * + * @return bool + */ +function dws_lpmwc_register_script( string $handle, string $path, array $args = array() ): bool { + $asset = dws_lpmwc_get_asset( $path ); + $registered = wp_register_script( $handle, plugin_dir_url( DWS_LPMWC_FILE ) . $path, array_values( array_filter( $asset['dependencies'], 'is_string' ) ), $asset['version'], $args ); + if ( $registered ) { + wp_set_script_translations( $handle, Plugin::SLUG, plugin_dir_path( DWS_LPMWC_FILE ) . 'languages' ); + } + + return $registered; +} + +/** + * Registers a built stylesheet with its generated version, and its right-to-left variant for right-to-left languages. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param non-empty-string $handle The stylesheet's handle. + * @param non-empty-string $path The built stylesheet's path, relative to the plugin's directory. + * @param string $media Optional. The media the stylesheet applies to. Default 'all'. + * + * @return bool + */ +function dws_lpmwc_register_style( string $handle, string $path, string $media = 'all' ): bool { + $registered = wp_register_style( $handle, plugin_dir_url( DWS_LPMWC_FILE ) . $path, array(), dws_lpmwc_get_asset( $path )['version'], $media ); + if ( $registered ) { + wp_style_add_data( $handle, 'rtl', 'replace' ); + } + + return $registered; +} diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/footprint.php b/plugins/locked-payment-methods-for-woocommerce/includes/footprint.php new file mode 100644 index 0000000..fdb32bc --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/includes/footprint.php @@ -0,0 +1,32 @@ +, user_meta_site_prefixes: list, user_meta_global_prefixes: list, network_options: list, cron_hooks: list, as_groups: list} + */ +function dws_lpmwc_get_footprint(): array { + return array( + 'option_prefixes' => array( Plugin::PREFIX . '_', '_transient_' . Plugin::PREFIX . '_', '_transient_timeout_' . Plugin::PREFIX . '_' ), + 'user_meta_site_prefixes' => array( Plugin::PREFIX . '_' ), + 'user_meta_global_prefixes' => array( Plugin::PREFIX . '_' ), + 'network_options' => array( Plugin::PREFIX . '_network_notices' ), + 'cron_hooks' => array( Plugin::PREFIX . '_revision/migration/run' ), + 'as_groups' => array( Plugin::PREFIX ), + ); +} diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/index.php b/plugins/locked-payment-methods-for-woocommerce/includes/index.php new file mode 100644 index 0000000..f767346 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/includes/index.php @@ -0,0 +1 @@ + 'Text Domain' ) )['TextDomain']; +} + +/** + * Returns the plugin's name from its header, translated only once init has fired, because WordPress flags translations loaded earlier. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @return string + */ +function dws_lpmwc_get_plugin_name(): string { + $name = get_file_data( DWS_LPMWC_FILE, array( 'Name' => 'Plugin Name' ) )['Name']; + + return 0 < did_action( 'init' ) ? __( $name, 'locked-payment-methods-for-woocommerce' ) : $name; // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText -- The header's name reaches the POT through make-pot's header extraction. +} + +/** + * Returns the plugin's version from its header. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @return string + */ +function dws_lpmwc_get_plugin_version(): string { + return get_file_data( DWS_LPMWC_FILE, array( 'Version' => 'Version' ) )['Version']; +} diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php b/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php new file mode 100644 index 0000000..c9b33b2 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php @@ -0,0 +1,49 @@ + + */ +function dws_lpmwc_get_migration_steps(): array { + return array( + 'dws_lpmwc_add_settings', + ); +} + +/** + * Stores the defaults of the settings that need no translation, autoloaded, so a site that never saved them reads them without a query; a translated default is never stored, because it would keep the language the site had when the step ran. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @throws RuntimeException Thrown when a default could not be stored. + * + * @return void + */ +function dws_lpmwc_add_settings(): void { + $defaults = array( + DWS_LPMWC_OPTION_FEATURE => wc_bool_to_string( DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_FEATURE ] ), + DWS_LPMWC_OPTION_GREETING_ORDERS => (string) DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_GREETING_ORDERS ], + DWS_LPMWC_OPTION_DELETE_DATA => wc_bool_to_string( DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_DELETE_DATA ] ), + ); + + foreach ( $defaults as $key => $value ) { + if ( false === get_option( $key ) && ! add_option( $key, $value, '', true ) ) { + throw new RuntimeException( "The option $key could not be stored." ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing, never rendered. + } + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/options.php b/plugins/locked-payment-methods-for-woocommerce/includes/options.php new file mode 100644 index 0000000..5335149 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/includes/options.php @@ -0,0 +1,187 @@ +> + */ +const DWS_LPMWC_OPTION_DEFAULTS = array( + DWS_LPMWC_OPTION_DELETE_DATA => false, + DWS_LPMWC_OPTION_FEATURE => false, + DWS_LPMWC_OPTION_GREETING_ORDERS => 1, +); + +/** + * Returns an option as a boolean, which reads checkbox values such as 'yes' and 'no'. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string $key The option's key. + * + * @return bool + */ +function dws_lpmwc_get_bool_option( string $key ): bool { + $default = dws_lpmwc_get_option_default( $key ); + + return filter_var( get_option( $key, $default ), FILTER_VALIDATE_BOOL, FILTER_NULL_ON_FAILURE ) ?? ( true === $default ); +} + +/** + * Returns an option as an integer. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string $key The option's key. + * + * @return int + */ +function dws_lpmwc_get_int_option( string $key ): int { + $default = dws_lpmwc_get_option_default( $key ); + + return filter_var( get_option( $key, $default ), FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE ) ?? ( is_int( $default ) ? $default : 0 ); +} + +/** + * Returns an option as a string, reading a missing or empty value of an option with a translated default as that default, only after init. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string $key The option's key. + * + * @return string + */ +function dws_lpmwc_get_string_option( string $key ): string { + $text = dws_lpmwc_get_option_text( $key ); + $default = dws_lpmwc_get_option_default( $key ) ?? $text; + $stored = get_option( $key, $default ); + if ( '' === $stored ) { + return $text; // The field shows a translated default as its placeholder, so an untouched field stores an empty value. + } + + return is_string( $stored ) ? $stored : ( is_string( $default ) ? $default : '' ); +} + +/** + * Returns an option as a list of strings, keeping the stored strings in order. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string $key The option's key. + * + * @return list + */ +function dws_lpmwc_get_list_option( string $key ): array { + $default = dws_lpmwc_get_option_default( $key ); + $stored = get_option( $key, $default ); + $stored = is_array( $stored ) ? $stored : $default; + + return is_array( $stored ) ? array_values( array_filter( $stored, 'is_string' ) ) : array(); +} + +/** + * Returns an option's translated default, which exists only after init, or an empty string for an option without one. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string $key The option's key. + * + * @return string + */ +function dws_lpmwc_get_option_text( string $key ): string { + switch ( $key ) { + case DWS_LPMWC_OPTION_GREETING: + return __( 'Welcome back, and thank you for shopping with us again.', 'locked-payment-methods-for-woocommerce' ); + default: + return ''; + } +} + +/** + * Returns the number of orders before the greeting shows in the form WooCommerce stores, or an error when the submission is not a whole number of at least 1. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param mixed $submitted The submitted value. + * + * @return string|WP_Error + */ +function dws_lpmwc_validate_greeting_orders( $submitted ) { + $orders = is_string( $submitted ) ? filter_var( $submitted, FILTER_VALIDATE_INT, array( 'options' => array( 'min_range' => 1 ) ) ) : false; + + return false === $orders ? new WP_Error( 'invalid_number', __( 'Enter a whole number of 1 or more, such as 3.', 'locked-payment-methods-for-woocommerce' ) ) : (string) $orders; +} + +/** + * Returns the option's default, or null for an option without one. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string $key The option's key. + * + * @return mixed + */ +function dws_lpmwc_get_option_default( string $key ) { + return DWS_LPMWC_OPTION_DEFAULTS[ $key ] ?? null; +} diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php b/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php new file mode 100644 index 0000000..189cd76 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php @@ -0,0 +1,11 @@ +getMessage() ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- The plugin's logger is part of the build that failed. + add_action( 'all_admin_notices', 'dws_lpmwc_render_build_notice' ); +} + +/** + * Prints the notice that the plugin's build is incomplete to users who can activate plugins. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @return void + */ +function dws_lpmwc_render_build_notice(): void { + if ( ! current_user_can( 'activate_plugins' ) ) { + return; + } + + printf( '

%s

', esc_html__( 'Locked Payment Methods for WooCommerce is missing some of its files, so its features are off. Reinstall the plugin to restore them.', 'locked-payment-methods-for-woocommerce' ) ); +} diff --git a/plugins/locked-payment-methods-for-woocommerce/models/index.php b/plugins/locked-payment-methods-for-woocommerce/models/index.php new file mode 100644 index 0000000..f767346 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/models/index.php @@ -0,0 +1 @@ +=26", + "npm": ">=11" + } + }, + "node_modules/@ahegyes/wordpress-configs": { + "version": "0.0.0", + "resolved": "git+ssh://git@github.com/ahegyes/wordpress-configs.git#8d53efd292e4abee4a9c57b0e379434f54bedbe9", + "integrity": "sha512-zTqkUCRwgWiUbcr6mm6QcOgObxrz+m1lYuybslhMRm5DHg2ZQC0zwi/cxNZAB/+AZduTfTbzHM+nu1Dpw523/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier": "npm:wp-prettier@^3.0.3" + }, + "engines": { + "node": ">=26", + "npm": ">=11" + }, + "peerDependencies": { + "@playwright/test": ">=1.64.1", + "@wordpress/eslint-plugin": ">=28.0.0", + "@wordpress/postcss-plugins-preset": ">=6.0.0", + "@wordpress/scripts": ">=37.0.0", + "@wordpress/stylelint-config": ">=27.0.0", + "typescript": ">=6.0.0" + } + }, + "node_modules/@ariakit/components": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/@ariakit/components/-/components-0.1.15.tgz", + "integrity": "sha512-Rq8cU/DXURIHbvRPZHXmUd7RHkTG0cXDjkjSWSeQEOQHp8w1bNenYUygoe0MzM0gwWakHrctA78XWGFgHmzUWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ariakit/store": "0.1.11", + "@ariakit/utils": "0.2.2" + } + }, + "node_modules/@ariakit/react": { + "version": "0.4.42", + "resolved": "https://registry.npmjs.org/@ariakit/react/-/react-0.4.42.tgz", + "integrity": "sha512-D+nElmWhT2w0TV0BKUh84ox21AaflvMv1dTt6Fn9TEOQIdZmOAqF1B6VAv69L5R1oyLzNV7neLD8cMTku+vbEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ariakit/react-components": "0.7.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ariakit" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@ariakit/react-components": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@ariakit/react-components/-/react-components-0.7.1.tgz", + "integrity": "sha512-HOqqJqwtXYCrdwYRZeIg/hvXCPceGO+Ts+UIu9FIIIeaVqAxgyFYUb8Umbh40Wscek3PrYDdx/r5B5lpByUX8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ariakit/components": "0.1.15", + "@ariakit/react-store": "0.1.13", + "@ariakit/react-utils": "0.2.8", + "@ariakit/store": "0.1.11", + "@ariakit/utils": "0.2.2", + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@ariakit/react-store": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/@ariakit/react-store/-/react-store-0.1.13.tgz", + "integrity": "sha512-/4R0HVcx14RlYCNqffytY9v3E+HLn+kL/Iaef3iOGSSvzr+Po75ZFJzMrNv3jVZsuhXXTilI16XmRT8nSiSUsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ariakit/react-utils": "0.2.8", + "@ariakit/store": "0.1.11", + "@ariakit/utils": "0.2.2", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@ariakit/react-utils": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@ariakit/react-utils/-/react-utils-0.2.8.tgz", + "integrity": "sha512-F8bQ4tbPxK9oniw/XS2HB0bCvZA0djpkFlFsqx4rGH1RPrhwg17OCf6rOMCo1nMfAqrUT6rpdRkxlocmgaznvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ariakit/store": "0.1.11", + "@ariakit/utils": "0.2.2" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@ariakit/store": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/@ariakit/store/-/store-0.1.11.tgz", + "integrity": "sha512-rffsPgyf6MTVK3pZSS86oKIj8370F9229c/7QqCwvbEIl0zM0bfEEdE6K+djnsabwajhWlJ/2syURdD4x30rXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ariakit/utils": "0.2.2" + } + }, + "node_modules/@ariakit/utils": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@ariakit/utils/-/utils-0.2.2.tgz", + "integrity": "sha512-OjdYlnJ8dW6XkBHirX0jx8bzLm577IL9Fe8T9dPrNKgCVvvJJiUL18iA5Oqqq9+FQ5ahhx5t4WgdE+2AaqG3IA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@axe-core/playwright": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.14.0.tgz", + "integrity": "sha512-SGUhG5PIHd2eMdpgBF2PyQLOhX+qyDR2PT4cmoS4Qk2ak+jX/rrSvst9l0PGv5WTHolKpUMvVmoHpr4cZgf1RQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "axe-core": "~4.14.0" + }, + "engines": { + "node": ">=22.12.0" + }, + "peerDependencies": { + "playwright-core": ">= 1.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", + "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/traverse": "^7.29.7", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.29.10.tgz", + "integrity": "sha512-/YY9soQuWa8TURtHXHT+GbklpbAA+dZErxFGHIr6qgDg96xuGWrTAs1+MfjgVJPF0YLQsOVsOYs8lhgsPbOinQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "regexpu-core": "^6.5.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz", + "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "debug": "^4.4.3", + "lodash.debounce": "^4.0.8", + "resolve": "^1.22.11" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", + "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", + "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.29.7.tgz", + "integrity": "sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-wrap-function": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", + "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", + "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.11.tgz", + "integrity": "sha512-snZbO7MEDOof+hvNa97EjEY7qD7h2kwvQr5AJC3ZhQudKW5dnej6k3kP9NQV88hJwaS8vDqRLFo9rivLEhle2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.29.7.tgz", + "integrity": "sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.10.tgz", + "integrity": "sha512-EMCx3AGZ3WfAl6ht7AyQoPm2Lh+BGYEMdc4Q6Hf7uSCDOZFuLq44qNwL+iaim8VlyYhRAo4X43+weadK8ZB4Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.11.tgz", + "integrity": "sha512-OZ6+oaK7GMyg3IfCpj8JOmn8n+w2rJbDT0LLfVDTLb1ZFeKDh3AiNELgCMUw175IJHSkLl74hG0M0VLsvx+gag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.11" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.29.7.tgz", + "integrity": "sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.29.7.tgz", + "integrity": "sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.29.7.tgz", + "integrity": "sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.7.tgz", + "integrity": "sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.29.7.tgz", + "integrity": "sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/plugin-transform-optional-chaining": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.29.7.tgz", + "integrity": "sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.29.7.tgz", + "integrity": "sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", + "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.29.7.tgz", + "integrity": "sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.7.tgz", + "integrity": "sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-remap-async-to-generator": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.29.7.tgz", + "integrity": "sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-remap-async-to-generator": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.29.7.tgz", + "integrity": "sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.29.11.tgz", + "integrity": "sha512-iARF4jjHSM0v8DImCACPXrwEltBP3DgqDpXTvjAMBAVpyEY0LiGss78wvI+KtmW+IJNEvZ5sOhPDCsOsXsIybA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.29.7.tgz", + "integrity": "sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.29.7.tgz", + "integrity": "sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.11.tgz", + "integrity": "sha512-f8kTDM/nTvpLeafb+TJ4jboFr/Y3BbC+QzgPpPGzSKJqWLnj9LW0WLqv4XKA11LX5sr+EgvLE6Ix3YsCEusodw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/traverse": "^7.29.10" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.29.7.tgz", + "integrity": "sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/template": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.29.7.tgz", + "integrity": "sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.29.7.tgz", + "integrity": "sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.29.7.tgz", + "integrity": "sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.7.tgz", + "integrity": "sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.29.7.tgz", + "integrity": "sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-explicit-resource-management": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.29.7.tgz", + "integrity": "sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.29.7.tgz", + "integrity": "sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.29.7.tgz", + "integrity": "sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.29.7.tgz", + "integrity": "sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.29.7.tgz", + "integrity": "sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.29.7.tgz", + "integrity": "sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.29.7.tgz", + "integrity": "sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.29.7.tgz", + "integrity": "sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.29.7.tgz", + "integrity": "sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.29.7.tgz", + "integrity": "sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz", + "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.8.tgz", + "integrity": "sha512-6iSnEK0zlkLKU4heofK/AdmRD4e2SHVpJMtrwnTCzhnaM98ria4rTrOXBBi45BTTYnJtO8txnPsX4fChYXkmeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.29.7.tgz", + "integrity": "sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.7.tgz", + "integrity": "sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.29.7.tgz", + "integrity": "sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.29.7.tgz", + "integrity": "sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.29.7.tgz", + "integrity": "sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.29.7.tgz", + "integrity": "sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7", + "@babel/plugin-transform-parameters": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.29.7.tgz", + "integrity": "sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.29.7.tgz", + "integrity": "sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.29.7.tgz", + "integrity": "sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.29.7.tgz", + "integrity": "sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.29.7.tgz", + "integrity": "sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.29.7.tgz", + "integrity": "sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.29.7.tgz", + "integrity": "sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-constant-elements": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.29.7.tgz", + "integrity": "sha512-J0wGhKan+rIiE2OhfhRptySLrJ6SjQYM6b6N1FMlhyhCcw1Mig8vQjWchyB+bgHGDvaWo6Diu6CLRMra2uMtmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.29.7.tgz", + "integrity": "sha512-+1wdDMGNb4UPeY3Q4L5yLiYe6TXPXubs4NjrgRFw13hPRLJfEMw2Q5OXkee6/IfdqePIeW4Jjwe3aBh7SdKz4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.29.7.tgz", + "integrity": "sha512-WsZulLVBUHXVj2cUcPVx6UE21TpalB6bHbSFErKT0Ib++ax24jjXe73FqlWvdylFOjiuPHYi6VCcgRad1ItN+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-syntax-jsx": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.29.7.tgz", + "integrity": "sha512-Xfy3UVMF04+ypnFbkhvfqtmvwfe92qwQdbGZVonhE+6v35GzlofmOnA1szaZqzb9xYWr0nl1e5EMmzi0DNON1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.29.7.tgz", + "integrity": "sha512-H5E+HBgDpr6Q5t+Aj11tL7XkIui1jhbIoArVQnqjgXo5/3YxkN7ZEBcWF4RQlB0T4rrxJQbXS6kiFV6B7XTqUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.8.tgz", + "integrity": "sha512-0UpIXPtdDtMXfnV2OJAVMLpj3H/92vmkA6lpSRakmycJvj3VUy6Xs1dM8tXRugupykr5WB+LpiVl0J8LMVg2mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.29.7.tgz", + "integrity": "sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.29.7.tgz", + "integrity": "sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.7.tgz", + "integrity": "sha512-xmAscdE/AsqRW7vutbPNoUmu/nF5SrLKPs7aoJgEjo35lLKA/Bc0i2rMv/hr1+Y0o1bQCiVtith3u2vdgRL39Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "babel-plugin-polyfill-corejs2": "^0.4.14", + "babel-plugin-polyfill-corejs3": "^0.13.0", + "babel-plugin-polyfill-regenerator": "^0.6.5", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", + "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.5", + "core-js-compat": "^3.43.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.29.7.tgz", + "integrity": "sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.11.tgz", + "integrity": "sha512-5xWGhfuMgbOfIjsk/I2c4uMnoqRwQdKYi4QjvybQrcEVE0G9/p3qmIkp2Sxe8YF8DGNNVs5whfwNqIM5684XoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.29.7.tgz", + "integrity": "sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.29.7.tgz", + "integrity": "sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.29.7.tgz", + "integrity": "sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.29.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.9.tgz", + "integrity": "sha512-FFwIwzU+7SCOuxxV4YtJql6T9981ZVTm+FHO5GhVsRqCTdy0WwrEZh3l42ARpXruJUDGOptdduHW6Zr7pNPLNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/plugin-syntax-typescript": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.29.7.tgz", + "integrity": "sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.29.7.tgz", + "integrity": "sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.29.7.tgz", + "integrity": "sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.29.7.tgz", + "integrity": "sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.7.tgz", + "integrity": "sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.29.7", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.29.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.29.7", + "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^7.29.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.29.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.29.7", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.29.7", + "@babel/plugin-syntax-import-attributes": "^7.29.7", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.29.7", + "@babel/plugin-transform-async-generator-functions": "^7.29.7", + "@babel/plugin-transform-async-to-generator": "^7.29.7", + "@babel/plugin-transform-block-scoped-functions": "^7.29.7", + "@babel/plugin-transform-block-scoping": "^7.29.7", + "@babel/plugin-transform-class-properties": "^7.29.7", + "@babel/plugin-transform-class-static-block": "^7.29.7", + "@babel/plugin-transform-classes": "^7.29.7", + "@babel/plugin-transform-computed-properties": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7", + "@babel/plugin-transform-dotall-regex": "^7.29.7", + "@babel/plugin-transform-duplicate-keys": "^7.29.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.7", + "@babel/plugin-transform-dynamic-import": "^7.29.7", + "@babel/plugin-transform-explicit-resource-management": "^7.29.7", + "@babel/plugin-transform-exponentiation-operator": "^7.29.7", + "@babel/plugin-transform-export-namespace-from": "^7.29.7", + "@babel/plugin-transform-for-of": "^7.29.7", + "@babel/plugin-transform-function-name": "^7.29.7", + "@babel/plugin-transform-json-strings": "^7.29.7", + "@babel/plugin-transform-literals": "^7.29.7", + "@babel/plugin-transform-logical-assignment-operators": "^7.29.7", + "@babel/plugin-transform-member-expression-literals": "^7.29.7", + "@babel/plugin-transform-modules-amd": "^7.29.7", + "@babel/plugin-transform-modules-commonjs": "^7.29.7", + "@babel/plugin-transform-modules-systemjs": "^7.29.7", + "@babel/plugin-transform-modules-umd": "^7.29.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.7", + "@babel/plugin-transform-new-target": "^7.29.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.29.7", + "@babel/plugin-transform-numeric-separator": "^7.29.7", + "@babel/plugin-transform-object-rest-spread": "^7.29.7", + "@babel/plugin-transform-object-super": "^7.29.7", + "@babel/plugin-transform-optional-catch-binding": "^7.29.7", + "@babel/plugin-transform-optional-chaining": "^7.29.7", + "@babel/plugin-transform-parameters": "^7.29.7", + "@babel/plugin-transform-private-methods": "^7.29.7", + "@babel/plugin-transform-private-property-in-object": "^7.29.7", + "@babel/plugin-transform-property-literals": "^7.29.7", + "@babel/plugin-transform-regenerator": "^7.29.7", + "@babel/plugin-transform-regexp-modifiers": "^7.29.7", + "@babel/plugin-transform-reserved-words": "^7.29.7", + "@babel/plugin-transform-shorthand-properties": "^7.29.7", + "@babel/plugin-transform-spread": "^7.29.7", + "@babel/plugin-transform-sticky-regex": "^7.29.7", + "@babel/plugin-transform-template-literals": "^7.29.7", + "@babel/plugin-transform-typeof-symbol": "^7.29.7", + "@babel/plugin-transform-unicode-escapes": "^7.29.7", + "@babel/plugin-transform-unicode-property-regex": "^7.29.7", + "@babel/plugin-transform-unicode-regex": "^7.29.7", + "@babel/plugin-transform-unicode-sets-regex": "^7.29.7", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.15", + "babel-plugin-polyfill-corejs3": "^0.14.0", + "babel-plugin-polyfill-regenerator": "^0.6.6", + "core-js-compat": "^3.48.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-react": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.29.7.tgz", + "integrity": "sha512-C+PV1TFUPTmBQGoPBL8j2QmLpZ117YTCwxIZeJOM96GbYMFSc7/pOXU5lVykwnZxyTqQxRsvoRk6f2FktZgGHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "@babel/plugin-transform-react-display-name": "^7.29.7", + "@babel/plugin-transform-react-jsx": "^7.29.7", + "@babel/plugin-transform-react-jsx-development": "^7.29.7", + "@babel/plugin-transform-react-pure-annotations": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.29.7.tgz", + "integrity": "sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "@babel/plugin-syntax-jsx": "^7.29.7", + "@babel/plugin-transform-modules-commonjs": "^7.29.7", + "@babel/plugin-transform-typescript": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-8.0.8.tgz", + "integrity": "sha512-dEu8nGlRqjsJsvviUUoSMfcRHpwomCiRlXsc2mPfJ79+ffTpDm+JtfUtTMzUrqCApHQnCsVPD3gT1ZT8RgBRCQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.10.tgz", + "integrity": "sha512-B3lYAs2nkKskbgQ1nfy8Ma9ao9Q1rntTAQHrzzyDuj+TSO+X3le6PfWwqDvodsb+OumE10eMFfuMCTQSQ5iuIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.9", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.11.tgz", + "integrity": "sha512-WCsemW40Cpc6gouC1SrY6PBaZ+oLRlEVY9XBvZBI7il/HyUrb2nJ505wE1MHEFyDZNnrWDEuy4jL50WVHfTTSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.11", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@base-ui/react": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@base-ui/react/-/react-1.9.0.tgz", + "integrity": "sha512-S8CSYosASBKtpL8SWUfS78rFElfLijiCgA7oHOK22jCJ95FULsYyKwBNFOgudt0RA+/7yNc7F81bOvQnRgbBiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.7 || ^8.0.0", + "@base-ui/utils": "0.5.0", + "@floating-ui/react-dom": "^2.1.9", + "@floating-ui/utils": "^0.2.12", + "use-sync-external-store": "^1.7.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@date-fns/tz": "^1.2.0", + "@types/react": "^17 || ^18 || ^19", + "date-fns": "^4.0.0", + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@date-fns/tz": { + "optional": true + }, + "@types/react": { + "optional": true + }, + "date-fns": { + "optional": true + } + } + }, + "node_modules/@base-ui/utils": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@base-ui/utils/-/utils-0.5.0.tgz", + "integrity": "sha512-dYGGcEttjkYiMoa5nHm8vLaJ2UcuTWxo3BFAwOYXeRg/q7UVVvA6cacEIXEs3FgBfK1CGtW1ptElqs7wsx+t9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.7 || ^8.0.0", + "@floating-ui/utils": "^0.2.12", + "reselect": "^5.3.0", + "use-sync-external-store": "^1.7.0" + }, + "peerDependencies": { + "@types/react": "^17 || ^18 || ^19", + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@cacheable/memory": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.2.0.tgz", + "integrity": "sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cacheable/utils": "^2.5.0", + "@keyv/bigmap": "^1.3.1", + "hookified": "^1.15.1", + "keyv": "^5.6.0" + } + }, + "node_modules/@cacheable/memory/node_modules/@keyv/bigmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@keyv/bigmap/-/bigmap-1.3.1.tgz", + "integrity": "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hashery": "^1.4.0", + "hookified": "^1.15.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "keyv": "^5.6.0" + } + }, + "node_modules/@cacheable/memory/node_modules/keyv": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@keyv/serialize": "^1.1.1" + } + }, + "node_modules/@cacheable/utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.5.0.tgz", + "integrity": "sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hashery": "^1.5.1", + "keyv": "^5.6.0" + } + }, + "node_modules/@cacheable/utils/node_modules/keyv": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@keyv/serialize": "^1.1.1" + } + }, + "node_modules/@csstools/css-calc": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.4.3.tgz", + "integrity": "sha512-iex20d8CHVkyvg6B7UKV7uHnI2Bqo9g+EFfT9E0y+GvTvhZ/DwONJ+9aKb1dlqm0ZiGsL5RXjp0fCoJYnkeDjA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.2", + "@csstools/css-tokenizer": "^4.0.2" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.2.tgz", + "integrity": "sha512-40cSKyMvK+tq4qz6Awrlye2WGuOKt3FwPgtGg6KTfbHOWNw+Rk1rzbAtZnZ6IBhsY491HLRnDXwoyBAijmmILA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.2" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.15.tgz", + "integrity": "sha512-J0u7HkVl2nzSlhsiTOp4AmwcUQ3D+mGEEKfBy/7To5/y7F2OHwyLrXfrhR0SMgr4p5Lo+eaMVSeai24zUcBIxA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.2.tgz", + "integrity": "sha512-OoKoR0f76dCY666JlcbhmVTs2drYj1GUXZTYTcbUgJjh9Nv41aFfZ21bPQTERm5+L5cBDo466NltB2lplS5GBw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/media-query-list-parser": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-5.0.2.tgz", + "integrity": "sha512-qx19O8AbbLLbDSAcI6YOby/Vq8rV51psgIvlF5OZT/L8fQJDCyQifL6k+kYU4a8ySpzJ4RBzZkIs530bAn8y4Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.2", + "@csstools/css-tokenizer": "^4.0.2" + } + }, + "node_modules/@csstools/selector-resolve-nested": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-4.0.2.tgz", + "integrity": "sha512-JpjNBJFA+4OnAHKRK4AMp8xrYyLhI9XapOeBlP2PhRU/IylSw+1ep3BFNTksBJnrTS3KT6kqKNVfpzLLwZUW5Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.1.1" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-6.0.1.tgz", + "integrity": "sha512-MBuqFIIvqk4S593MGAskTkPpEUUzZNvGzh8mMxEwl3oHwjQlXmqHEDxNbN3Y0Tsh+xjmem4GZjeg7I0qkwBivg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.1.1" + } + }, + "node_modules/@date-fns/tz": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.5.0.tgz", + "integrity": "sha512-lwYN/vDPeNRULcepoE/LO2Pgx+7/RV+S9ARfbc9lr2DtGkOD7pAiruHvbR1RX3Qyf6ja47EWJDMsNK5vK08DJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@date-fns/utc": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@date-fns/utc/-/utc-2.1.1.tgz", + "integrity": "sha512-SlJDfG6RPeEX8wEVv6ZB3kak4MmbtyiI2qX/5zuKdordbrhB/iaJ58GVMZgJ6P1sJaM1gMgENFYYeg1JWrCFrA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@daypicker/react": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@daypicker/react/-/react-10.0.2.tgz", + "integrity": "sha512-Z/gvGXnc/xjRr5Z6l9OhIey00C7kwbIyJ85Pff9B9EWDkjTlkTQztB3qeylmGJDQ9xmxRJUV1W+02rskqarUBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "react-day-picker": "10.0.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/gpbl" + }, + "peerDependencies": { + "@types/react": ">=16.8.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@dual-bundle/import-meta-resolve": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.2.1.tgz", + "integrity": "sha512-id+7YRUgoUX6CgV0DtuhirQWodeeA7Lf4i2x71JS/vtA5pRb/hIGWlw+G6MeXvsM+MXrz0VAydTGElX1rAfgPg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/JounQin" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.98.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.98.0.tgz", + "integrity": "sha512-QS+b7I2aaSM+gZfDz1b9E4B367JJ2qTjFXRKF289iKkh9sYdZ21Q5MiF/Duzouy3J0C4VhM3I/8f2F4+zT8gYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.9", + "@typescript-eslint/types": "^8.70.0", + "comment-parser": "1.4.9", + "esquery": "^1.7.0", + "jsdoc-type-pratt-parser": "~9.2.2" + }, + "engines": { + "node": "^22.22.2 || >=24.15.0" + } + }, + "node_modules/@es-joy/resolve.exports": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@es-joy/resolve.exports/-/resolve.exports-1.2.0.tgz", + "integrity": "sha512-Q9hjxWI5xBM+qW2enxfe8wDKdFWMfd0Z29k5ZJnuBqD/CasY5Zryj09aCA6owbGATWz+39p5uIdaHXpopOcG8g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/compat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-2.1.1.tgz", + "integrity": "sha512-rMcy8GSrwNzcISX/BlTDY/GLB4eCopEuy9woIls3To+15OLxykZrxxq+WUcylCPCQ6F4MujjBM1DX5V1aqI3Vw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "peerDependencies": { + "eslint": "^8.40 || 9 || 10" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers/node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.7.tgz", + "integrity": "sha512-F42g89Qd5oAWtp0k0nnSrjziAKza7w8SVT4mStc18LZMaRb4J1HQAHLCalEtDCxrTuksx7NU9qsmeLwpOfPqWw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.3.2", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0", + "peer": true + }, + "node_modules/@eslint/eslintrc/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", + "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz", + "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.8.0.tgz", + "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.8.0", + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.9.tgz", + "integrity": "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.8.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.12.tgz", + "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@formatjs/ecma402-abstract": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.6.tgz", + "integrity": "sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/intl-localematcher": "0.6.2", + "decimal.js": "^10.4.3", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/fast-memoize": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz", + "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.4.tgz", + "integrity": "sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.6", + "@formatjs/icu-skeleton-parser": "1.8.16", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.8.16", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.16.tgz", + "integrity": "sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.6", + "tslib": "^2.8.0" + } + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.2.tgz", + "integrity": "sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@inquirer/ansi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", + "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", + "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.21", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", + "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", + "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/editor": { + "version": "4.2.23", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", + "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/external-editor": "^1.0.3", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", + "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", + "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", + "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", + "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz", + "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.3.2", + "@inquirer/confirm": "^5.1.21", + "@inquirer/editor": "^4.2.23", + "@inquirer/expand": "^4.0.23", + "@inquirer/input": "^4.3.1", + "@inquirer/number": "^3.0.23", + "@inquirer/password": "^4.0.23", + "@inquirer/rawlist": "^4.1.11", + "@inquirer/search": "^3.2.2", + "@inquirer/select": "^4.4.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", + "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", + "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", + "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", + "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jsonjoy.com/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/buffers": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-17.67.0.tgz", + "integrity": "sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/codegen": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-1.0.0.tgz", + "integrity": "sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-core": { + "version": "4.80.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.80.0.tgz", + "integrity": "sha512-qMKWshnyjbyhm+NbzqGE3w5y1mckFFEMj0aDPU2/JBNh1BzIjVy9esq9lBmRnNVmhMEkxwDzLZ8Gqh5G1P5Dag==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-node-builtins": "4.80.0", + "@jsonjoy.com/fs-node-utils": "4.80.0", + "thingies": "^2.5.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-fsa": { + "version": "4.80.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.80.0.tgz", + "integrity": "sha512-ZBEKl7J6dbkRCfmrFV48Re211A/FHqniTUhsWCsaRteNSOqMgkX3qF1UreVgwB9oSPm81JLAwhIi6Lb/NH/PoA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-core": "4.80.0", + "@jsonjoy.com/fs-node-builtins": "4.80.0", + "@jsonjoy.com/fs-node-utils": "4.80.0", + "thingies": "^2.5.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-node": { + "version": "4.80.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.80.0.tgz", + "integrity": "sha512-yiBlUfkMMRFFGy8CD/h2zd5rPnrM6bjlF+nx2LRAxNV6hOSiuObQIUEE7m7y2VTMisjxCRtzINH94bFGySWL7g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-core": "4.80.0", + "@jsonjoy.com/fs-node-builtins": "4.80.0", + "@jsonjoy.com/fs-node-utils": "4.80.0", + "@jsonjoy.com/fs-print": "4.80.0", + "@jsonjoy.com/fs-snapshot": "4.80.0", + "glob-to-regex.js": "^1.3.1", + "thingies": "^2.5.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-node-builtins": { + "version": "4.80.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.80.0.tgz", + "integrity": "sha512-OIOIhqaWiwUySFMny4epIAoviXsyMaQQ63PTc3zH+5HEOvarl5hpUmZIhp2m5+OfyKAh5ylxXnQIgew1OgJDVw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-node-to-fsa": { + "version": "4.80.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.80.0.tgz", + "integrity": "sha512-ljeaR5xwtX1EyRmB0wvXANUMQZ3mjKcm7z8s2N8D8SLQ04CcAGtUhHFRZQXFb9mhgFyEVMydQGQtSOFS7pcrFQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-fsa": "4.80.0", + "@jsonjoy.com/fs-node-builtins": "4.80.0", + "@jsonjoy.com/fs-node-utils": "4.80.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-node-utils": { + "version": "4.80.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.80.0.tgz", + "integrity": "sha512-xwHeVVi3f+Khg/Fmb8i5qoYVmQ08QznE5TWhQokUmIZjz4KBPzU+zt/+bahBI6G1ghtqajt27lWuAciIxApuzA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-node-builtins": "4.80.0", + "glob-to-regex.js": "^1.3.1" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-print": { + "version": "4.80.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.80.0.tgz", + "integrity": "sha512-1ocZLJw/m/xlq0moaIHO7HV9nHRVBUvdc2zMzhsJGkFOrqfwWvexfCv5cYCPPLRhP+9p3yunoi4Ep5YhRORC9Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-node-utils": "4.80.0", + "tree-dump": "^1.1.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot": { + "version": "4.80.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.80.0.tgz", + "integrity": "sha512-3AV23Q1H228KTtJKTUE0nNq8FCtxSvtENOLrEat80wkAJCKHuqQEv8yjhpS8Bei2OEFJAnluGXp9Ri2SWBlROw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/buffers": "^17.65.0", + "@jsonjoy.com/fs-node-utils": "4.80.0", + "@jsonjoy.com/json-pack": "^17.65.0", + "@jsonjoy.com/util": "^17.65.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/base64": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-17.67.0.tgz", + "integrity": "sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/codegen": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/codegen/-/codegen-17.67.0.tgz", + "integrity": "sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/json-pack": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-17.67.0.tgz", + "integrity": "sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/base64": "17.67.0", + "@jsonjoy.com/buffers": "17.67.0", + "@jsonjoy.com/codegen": "17.67.0", + "@jsonjoy.com/json-pointer": "17.67.0", + "@jsonjoy.com/util": "17.67.0", + "hyperdyperid": "^1.2.0", + "thingies": "^2.5.0", + "tree-dump": "^1.1.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/json-pointer": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-17.67.0.tgz", + "integrity": "sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/util": "17.67.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/fs-snapshot/node_modules/@jsonjoy.com/util": { + "version": "17.67.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-17.67.0.tgz", + "integrity": "sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/buffers": "17.67.0", + "@jsonjoy.com/codegen": "17.67.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pack": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.21.0.tgz", + "integrity": "sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/base64": "^1.1.2", + "@jsonjoy.com/buffers": "^1.2.0", + "@jsonjoy.com/codegen": "^1.0.0", + "@jsonjoy.com/json-pointer": "^1.0.2", + "@jsonjoy.com/util": "^1.9.0", + "hyperdyperid": "^1.2.0", + "thingies": "^2.5.0", + "tree-dump": "^1.1.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pack/node_modules/@jsonjoy.com/buffers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz", + "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pointer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pointer/-/json-pointer-1.0.2.tgz", + "integrity": "sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/codegen": "^1.0.0", + "@jsonjoy.com/util": "^1.9.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/util": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.9.0.tgz", + "integrity": "sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/buffers": "^1.0.0", + "@jsonjoy.com/codegen": "^1.0.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/util/node_modules/@jsonjoy.com/buffers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz", + "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@keyv/serialize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", + "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.5.tgz", + "integrity": "sha512-HshSvXzmBxNzqejd3k/KemgQ3hdREUYFRRrgxOZ+gPljDIsO2SDZrVZMd4Z/HsxudQbE62OVyVFE8z9BCurfjA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodable/entities": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-3.1.0.tgz", + "integrity": "sha512-LsS/DjHr+uDM647Gru/cA8+J3a3HfhttwCKLyuoyN7yXTFCxKBtSgMQBvrW9yNPa2/zDRUNuGPaH5QUFoa4arQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@octokit/app": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/app/-/app-14.1.0.tgz", + "integrity": "sha512-g3uEsGOQCBl1+W1rgfwoRFUIR6PtvB2T1E4RpygeUU5LrLvlOqcxrt5lfykIeRpUPpupreGJUYl70fqMDXdTpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-app": "^6.0.0", + "@octokit/auth-unauthenticated": "^5.0.0", + "@octokit/core": "^5.0.0", + "@octokit/oauth-app": "^6.0.0", + "@octokit/plugin-paginate-rest": "^9.0.0", + "@octokit/types": "^12.0.0", + "@octokit/webhooks": "^12.0.4" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/auth-app": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-6.1.4.tgz", + "integrity": "sha512-QkXkSOHZK4dA5oUqY5Dk3S+5pN2s1igPjEASNQV8/vgJgW034fQWR16u7VsNOK/EljA00eyjYF5mWNxWKWhHRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-oauth-app": "^7.1.0", + "@octokit/auth-oauth-user": "^4.1.0", + "@octokit/request": "^8.3.1", + "@octokit/request-error": "^5.1.0", + "@octokit/types": "^13.1.0", + "deprecation": "^2.3.1", + "lru-cache": "npm:@wolfy1339/lru-cache@^11.0.2-patch.1", + "universal-github-app-jwt": "^1.1.2", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/auth-app/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/auth-app/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/auth-app/node_modules/lru-cache": { + "name": "@wolfy1339/lru-cache", + "version": "11.0.2-patch.1", + "resolved": "https://registry.npmjs.org/@wolfy1339/lru-cache/-/lru-cache-11.0.2-patch.1.tgz", + "integrity": "sha512-BgYZfL2ADCXKOw2wJtkM3slhHotawWkgIRRxq4wEybnZQPjvAp71SPX35xepMykTw8gXlzWcWPTY31hlbnRsDA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "18 >=18.20 || 20 || >=22" + } + }, + "node_modules/@octokit/auth-oauth-app": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-7.1.0.tgz", + "integrity": "sha512-w+SyJN/b0l/HEb4EOPRudo7uUOSW51jcK1jwLa+4r7PA8FPFpoxEnHBHMITqCsc/3Vo2qqFjgQfz/xUUvsSQnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-oauth-device": "^6.1.0", + "@octokit/auth-oauth-user": "^4.1.0", + "@octokit/request": "^8.3.1", + "@octokit/types": "^13.0.0", + "@types/btoa-lite": "^1.0.0", + "btoa-lite": "^1.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/auth-oauth-app/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/auth-oauth-app/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/auth-oauth-device": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-6.1.0.tgz", + "integrity": "sha512-FNQ7cb8kASufd6Ej4gnJ3f1QB5vJitkoV1O0/g6e6lUsQ7+VsSNRHRmFScN2tV4IgKA12frrr/cegUs0t+0/Lw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/oauth-methods": "^4.1.0", + "@octokit/request": "^8.3.1", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/auth-oauth-device/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/auth-oauth-device/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/auth-oauth-user": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-4.1.0.tgz", + "integrity": "sha512-FrEp8mtFuS/BrJyjpur+4GARteUCrPeR/tZJzD8YourzoVhRics7u7we/aDcKv+yywRNwNi/P4fRi631rG/OyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-oauth-device": "^6.1.0", + "@octokit/oauth-methods": "^4.1.0", + "@octokit/request": "^8.3.1", + "@octokit/types": "^13.0.0", + "btoa-lite": "^1.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/auth-oauth-user/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/auth-oauth-user/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/auth-token": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", + "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/auth-unauthenticated": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-unauthenticated/-/auth-unauthenticated-5.0.1.tgz", + "integrity": "sha512-oxeWzmBFxWd+XolxKTc4zr+h3mt+yofn4r7OfoIkR/Cj/o70eEGmPsFbueyJE2iBAGpjgTnEOKM3pnuEGVmiqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.2.tgz", + "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^4.0.0", + "@octokit/graphql": "^7.1.0", + "@octokit/request": "^8.4.1", + "@octokit/request-error": "^5.1.1", + "@octokit/types": "^13.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/core/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/endpoint": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz", + "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/endpoint/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/graphql": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz", + "integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request": "^8.4.1", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/graphql/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/oauth-app": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@octokit/oauth-app/-/oauth-app-6.1.0.tgz", + "integrity": "sha512-nIn/8eUJ/BKUVzxUXd5vpzl1rwaVxMyYbQkNZjHrF7Vk/yu98/YDF/N2KeWO7uZ0g3b5EyiFXFkZI8rJ+DH1/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-oauth-app": "^7.0.0", + "@octokit/auth-oauth-user": "^4.0.0", + "@octokit/auth-unauthenticated": "^5.0.0", + "@octokit/core": "^5.0.0", + "@octokit/oauth-authorization-url": "^6.0.2", + "@octokit/oauth-methods": "^4.0.0", + "@types/aws-lambda": "^8.10.83", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/oauth-authorization-url": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-6.0.2.tgz", + "integrity": "sha512-CdoJukjXXxqLNK4y/VOiVzQVjibqoj/xHgInekviUJV73y/BSIcwvJ/4aNHPBPKcPWFnd4/lO9uqRV65jXhcLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/oauth-methods": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-4.1.0.tgz", + "integrity": "sha512-4tuKnCRecJ6CG6gr0XcEXdZtkTDbfbnD5oaHBmLERTjTMZNi2CbfEHZxPU41xXLDG4DfKf+sonu00zvKI9NSbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/oauth-authorization-url": "^6.0.2", + "@octokit/request": "^8.3.1", + "@octokit/request-error": "^5.1.0", + "@octokit/types": "^13.0.0", + "btoa-lite": "^1.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/oauth-methods/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/oauth-methods/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-graphql": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-4.0.1.tgz", + "integrity": "sha512-R8ZQNmrIKKpHWC6V2gum4x9LG2qF1RxRjo27gjQcG3j+vf2tLsEfE7I/wRWEPzYMaenr1M+qDAtNcwZve1ce1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=5" + } + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.2.tgz", + "integrity": "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^12.6.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.4.1.tgz", + "integrity": "sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^12.6.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-retry": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-6.1.0.tgz", + "integrity": "sha512-WrO3bvq4E1Xh1r2mT9w6SDFg01gFmP81nIG77+p/MqW1JeXXgL++6umim3t6x0Zj5pZm3rXAN+0HEjmmdhIRig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^13.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-retry/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-retry/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/plugin-throttling": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-8.2.0.tgz", + "integrity": "sha512-nOpWtLayKFpgqmgD0y3GqXafMFuKcA4tRPZIfu7BArd2lEZeb1988nhWhwx4aZWmjDmUfdgVf7W+Tt4AmvRmMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^12.2.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "^5.0.0" + } + }, + "node_modules/@octokit/request": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz", + "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^9.0.6", + "@octokit/request-error": "^5.1.1", + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz", + "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/request-error/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/request/node_modules/@octokit/openapi-types": { + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/request/node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/types": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^20.0.0" + } + }, + "node_modules/@octokit/webhooks": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-12.3.2.tgz", + "integrity": "sha512-exj1MzVXoP7xnAcAB3jZ97pTvVPkQF9y6GA/dvYC47HV7vLv+24XRS6b/v/XnyikpEuvMhugEXdGtAlU086WkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request-error": "^5.0.0", + "@octokit/webhooks-methods": "^4.1.0", + "@octokit/webhooks-types": "7.6.1", + "aggregate-error": "^3.1.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/webhooks-methods": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-4.1.0.tgz", + "integrity": "sha512-zoQyKw8h9STNPqtm28UGOYFE7O6D4Il8VJwhAtMHFt2C4L0VQT1qGKLeefUOqHNs1mNRYSadVv7x0z8U2yyeWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/webhooks-types": { + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-7.6.1.tgz", + "integrity": "sha512-S8u2cJzklBC0FgTwWVLaM8tMrDuDMVE4xiTK4EYXM9GntyvrdbSoxqDQa+Fh57CCNApyIpyeqPhhFEmHPfrXgw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT" + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", + "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.220.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.220.0.tgz", + "integrity": "sha512-CmVa4ImJ+ynfrPMNaAXHET6Bhb44SwzmfyVJFq9ni2jgXJR/l7C6gfVFddNmHP+ZOkP9cf4f9DBe68qVLTHc9w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.12.0.tgz", + "integrity": "sha512-1HsSAuvT4/my0QrXWsyXFdWjKaPeCHsismKZgEEIb8NK13LP6DrMRlsUbvD/0F9mDWK3xDom9AMv3IscxgOnOQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.220.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.220.0.tgz", + "integrity": "sha512-xQx3E2WxP1mDvKzxLxX+CTCtNLa560YJZ3087qYHerl2YmiKpv7AH+dAy7vmx+eVrZ5BwhfWUAVoKOoxCNHcpw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.220.0", + "import-in-the-middle": "^3.0.0", + "require-in-the-middle": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.12.0.tgz", + "integrity": "sha512-+rSRoOdln6NhXTVcBdHSlZxJDNzwxadeweK2ltZO3tVtfp6aklABc06xclDk6ovRR+EiGgIcSbh/6NgjFhnb3A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.12.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace/-/sdk-trace-2.12.0.tgz", + "integrity": "sha512-ImVuRQ6faOsTi1Wna88s2avgrrf/iDgXPigIeynkUhjstMuoQUDOJXgkez2j1mlU4btjXcXBGa+q7qKFYR3GsA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.12.0", + "@opentelemetry/resources": "2.12.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.12.0.tgz", + "integrity": "sha512-JOERgZCxRM06FEJ5kEWxlSRChah6MmXDx3KwCruqEv/iEyLkTokCSemf2y1w8oIHtfrr8EVTpzebq/31vn2Cpg==", + "deprecated": "Use @opentelemetry/sdk-trace package", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.12.0", + "@opentelemetry/resources": "2.12.0", + "@opentelemetry/sdk-trace": "2.12.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.43.0.tgz", + "integrity": "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.6.0.tgz", + "integrity": "sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.6.0", + "@parcel/watcher-darwin-arm64": "2.6.0", + "@parcel/watcher-darwin-x64": "2.6.0", + "@parcel/watcher-freebsd-x64": "2.6.0", + "@parcel/watcher-linux-arm-glibc": "2.6.0", + "@parcel/watcher-linux-arm-musl": "2.6.0", + "@parcel/watcher-linux-arm64-glibc": "2.6.0", + "@parcel/watcher-linux-arm64-musl": "2.6.0", + "@parcel/watcher-linux-x64-glibc": "2.6.0", + "@parcel/watcher-linux-x64-musl": "2.6.0", + "@parcel/watcher-win32-arm64": "2.6.0", + "@parcel/watcher-win32-x64": "2.6.0" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.6.0.tgz", + "integrity": "sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.6.0.tgz", + "integrity": "sha512-Y3QV0gl7Q1zbfueunkWIERICbEojQFCgpyG7YqOGNFLsckXyI1xu9mAIUpKY9QBYzBtSkN8dBPwd3yiAO9ovMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.6.0.tgz", + "integrity": "sha512-Ohv6OpzhUfKYD7Beb8kDvG0jbIxORCYY1JRdZnaBtnjjkJxgD7ZVL0nw2sCYd0yTMKTvz3nnTnOF3cDifK+kvw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.6.0.tgz", + "integrity": "sha512-5HmXvDgs8VK+74jF9y9/2FE3/OnlcKmc56tjmSrEuZjpSZOGL+fvAu+HKJBdPs9uwoP2hE6TlSUpXZ/C5jUFmQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.6.0.tgz", + "integrity": "sha512-Ps/hui3A+vMbjdqlqAowK2ZL8+BO8dBjxeWXj6npTBs3jx4wWmbPpaLuqwrQrSqIVMCnpWo238bJ1U37GhQOYg==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.6.0.tgz", + "integrity": "sha512-9c6AUHgHoG+IY88MRIHupztQiQnrbqHYQjkM2btA+Bf/wQnQMuiD0Wfk1EVv3TlNT3x41uU71rn6E4xh/+zvkw==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.6.0.tgz", + "integrity": "sha512-yHRqS2owEXe6Hic9z6Mh1ECsCd+ODVOGvZDyciqRd21+v+o+DnXMOrw50DSpIG2sb8GPEaPPmfeCAWKPJdq46g==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.6.0.tgz", + "integrity": "sha512-WhB2e/V7rqdHHWZusBSPuy5Ei8S6lSz6FE5TKKQz5h3a0O+C+mhY7vxU9b/stqvMb8beLnPY82ZrFTLKs+SrKA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.6.0.tgz", + "integrity": "sha512-ulGE6x6Oz6iAwg75T8YQSoguBWasniIbX+QWpaYPcCnDOpdWX3k+4xbEYPZVLxOuoJI+svJJPD3sEj8G7lrQ3A==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.6.0.tgz", + "integrity": "sha512-tkBYKt7YQrjIJWYDnto2YgO8MRkjlMTSNoRHzsXinBqbLdeOM3L32wPZJvIZxqaLMfSlS/4sUjH/6STVP/XDLw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.6.0.tgz", + "integrity": "sha512-gIZAP23jaHjGWasY/TY6yL7NHFClf0Ga7FN+iINvk+KN94rhm94lYZhFsbYFNcA04/onvGD9kKmiJLJB2HbNwQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.6.0.tgz", + "integrity": "sha512-cA+/pXV2YkfxlIcXOQ5fSWqAzzPyD78/x5qbK/I0vUkrlYHA8TIz+MXjAbGouguKVSI4bOmkTSJ1/poVSsgt+A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@paulirish/trace_engine": { + "version": "0.0.65", + "resolved": "https://registry.npmjs.org/@paulirish/trace_engine/-/trace_engine-0.0.65.tgz", + "integrity": "sha512-Qsm6F5C8xf6ZzQXbQc2+wcpe6sggfs/gvc/ytqSurdvYg3kyW0ECHCqE0CWBKZpqgjVfPNX9c7SCS3r2nEIRGg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "legacy-javascript": "latest", + "third-party-web": "latest" + } + }, + "node_modules/@peculiar/asn1-cms": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.11.0.tgz", + "integrity": "sha512-SlAiDoT/Cd8qaNmbTt78gqyaEvu2fQd6ML6KWJkdjXGrgDKnHHrusVkfQUelkfinL2p665IIADb0JBixtT8W1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.11.0", + "@peculiar/asn1-x509": "^2.11.0", + "@peculiar/asn1-x509-attr": "^2.11.0", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@peculiar/asn1-csr": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.11.0.tgz", + "integrity": "sha512-M/ED0yJ05qMK+UADPKnIa8vhAeewOJALSxTAZB7RQ/batRHhIJZkwznCcWAbFO5saHJ1yEQ+m4lncEcG9RnQSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.11.0", + "@peculiar/asn1-x509": "^2.11.0", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@peculiar/asn1-ecc": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.11.0.tgz", + "integrity": "sha512-jj1alGEKev7/un+cI+exyVK9m31lmP7cCBMCZuCb0wkFQH2zmKmJzNiG4wqXFNzlcP8timHLnzf3lV3OxBpb/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.11.0", + "@peculiar/asn1-x509": "^2.11.0", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@peculiar/asn1-pfx": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.11.0.tgz", + "integrity": "sha512-ykSAWRu8KkmzU8LRpk9ZQC3zuLmBEtoSMjIv9XHVamD5W7amqreV5wttKqnvtjWOzzSJQxA/HVT8/tHwpEEGSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.11.0", + "@peculiar/asn1-pkcs8": "^2.11.0", + "@peculiar/asn1-rsa": "^2.11.0", + "@peculiar/asn1-schema": "^2.11.0", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@peculiar/asn1-pkcs8": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.11.0.tgz", + "integrity": "sha512-lragjkjQGnQ5luxC+sxoAfAO/jBiQ7hOEGlDKJUk7MC5zY3AzjB2DU0GyPUH1Sswdv10t7DurY0zylOYMLb88w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.11.0", + "@peculiar/asn1-x509": "^2.11.0", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@peculiar/asn1-pkcs9": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.11.0.tgz", + "integrity": "sha512-gDdFjpqV0weJjgNUvueUVf4D8uVbpq+VbG0TsxLAY2QGljAkJyL9qwQ7avol9j0jaYSJ7Ey7v7RZp0M3GTcQSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.11.0", + "@peculiar/asn1-pfx": "^2.11.0", + "@peculiar/asn1-pkcs8": "^2.11.0", + "@peculiar/asn1-schema": "^2.11.0", + "@peculiar/asn1-x509": "^2.11.0", + "@peculiar/asn1-x509-attr": "^2.11.0", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@peculiar/asn1-rsa": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.11.0.tgz", + "integrity": "sha512-S2sBJmbfI09Kzdt9cEhE6pDNjHpVTkURs+811+PxS8AvsTLxC4woMNRhxHYaIbClkkNfAVt0tZxJdINVfUmzuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.11.0", + "@peculiar/asn1-x509": "^2.11.0", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@peculiar/asn1-schema": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.11.0.tgz", + "integrity": "sha512-8xh4avo53/vXO6UEv9yaoJ83hY581geaLTygf61NPIXP5AeIZCPjJhpTaYU23IZkG+NpLN/C9FYzWp7hphIT9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/utils": "^2.0.2", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@peculiar/asn1-x509": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.11.0.tgz", + "integrity": "sha512-J1sHAwGNlk+8J+FeR1eLVc6Gq0T9f9ITc3HWyPYgJraoW6U8AJuQiWVJ3SCEpltfTdpOY7fHdHT5oKAsbK/jww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.11.0", + "@peculiar/utils": "^2.0.2", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@peculiar/asn1-x509-attr": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.11.0.tgz", + "integrity": "sha512-Fgvnd9REmLxEj/4cbDP54dNUNUXkzUksXLqEHqs1mHBuSRjSTCUSPM/WQIZYSvrdG7ND9CX1AQtPoIBekhgQSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.11.0", + "@peculiar/asn1-x509": "^2.11.0", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@peculiar/utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@peculiar/utils/-/utils-2.0.3.tgz", + "integrity": "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/x509": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.3.tgz", + "integrity": "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.6.0", + "@peculiar/asn1-csr": "^2.6.0", + "@peculiar/asn1-ecc": "^2.6.0", + "@peculiar/asn1-pkcs9": "^2.6.0", + "@peculiar/asn1-rsa": "^2.6.0", + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.0", + "pvtsutils": "^1.3.6", + "reflect-metadata": "^0.2.2", + "tslib": "^2.8.1", + "tsyringe": "^4.10.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@php-wasm/cli-util": { + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@php-wasm/cli-util/-/cli-util-3.1.60.tgz", + "integrity": "sha512-3+fc2rLw4BBMJ7Z+S2rbCkHCyaFiDA7mvvDsqlRWkd4Wwv74Savt2LJ19g9vgiPz9H6ag0ZK7C1qY8rylAWcWA==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/util": "3.1.60", + "fast-xml-parser": "^5.8.0", + "jsonc-parser": "3.3.1" + }, + "engines": { + "node": ">=24.18.0", + "npm": ">=11.16.0" + } + }, + "node_modules/@php-wasm/logger": { + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@php-wasm/logger/-/logger-3.1.60.tgz", + "integrity": "sha512-8tRxcpzdjU5CE/UnmWKfXNi+LDIpMVioETbXKsWQ3W4dv4p3W/84fGK5nFP6xz/iFfBwDU20ioEKaAGqahHSoQ==", + "dev": true, + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=24.18.0", + "npm": ">=11.16.0" + } + }, + "node_modules/@php-wasm/node": { + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@php-wasm/node/-/node-3.1.60.tgz", + "integrity": "sha512-w6INvilykOhP7QKWhOhNTIun3OmNpXk30KruVPsAfcvGHfa0BPflnscu/gmunDhuwdmyQ9cHPTCtTrCDD7Zr0A==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/cli-util": "3.1.60", + "@php-wasm/logger": "3.1.60", + "@php-wasm/node-5-2": "3.1.60", + "@php-wasm/node-7-4": "3.1.60", + "@php-wasm/node-8-0": "3.1.60", + "@php-wasm/node-8-1": "3.1.60", + "@php-wasm/node-8-2": "3.1.60", + "@php-wasm/node-8-3": "3.1.60", + "@php-wasm/node-8-4": "3.1.60", + "@php-wasm/node-8-5": "3.1.60", + "@php-wasm/universal": "3.1.60", + "@php-wasm/util": "3.1.60", + "wasm-feature-detect": "1.8.0", + "ws": "8.21.0" + }, + "engines": { + "node": ">=24.18.0", + "npm": ">=11.16.0" + }, + "optionalDependencies": { + "fs-ext-extra-prebuilt": "~2.2.15" + } + }, + "node_modules/@php-wasm/node-5-2": { + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@php-wasm/node-5-2/-/node-5-2-3.1.60.tgz", + "integrity": "sha512-UiwPf0kMTVDHI3I0B+1eGoIO0u++lQgnGvYHWeeEEUrb+eK4RWInIjYBl5Zg8MyeGcisCpxdZZlAEhV5Rim77Q==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/universal": "3.1.60", + "wasm-feature-detect": "1.8.0" + }, + "engines": { + "node": ">=24.18.0", + "npm": ">=11.16.0" + } + }, + "node_modules/@php-wasm/node-7-4": { + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@php-wasm/node-7-4/-/node-7-4-3.1.60.tgz", + "integrity": "sha512-iKTZiMNAGrl8uGpQ1T/33tvINiYfCbc1ZOkjEDT2LqGeAH7YiNL40eQME9PPTs2A7zTRUUBSs0z7wVjXK4MVMA==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/universal": "3.1.60", + "wasm-feature-detect": "1.8.0" + }, + "engines": { + "node": ">=24.18.0", + "npm": ">=11.16.0" + } + }, + "node_modules/@php-wasm/node-8-0": { + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@php-wasm/node-8-0/-/node-8-0-3.1.60.tgz", + "integrity": "sha512-eD8RSwrfmiytAmhb9ziSsqvlwBakIsiYS18JzDV1ekmzFenkYLptH/2cERzLtOJ60k7fePIkIUgDLOS/SZW6gg==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/universal": "3.1.60", + "wasm-feature-detect": "1.8.0" + }, + "engines": { + "node": ">=24.18.0", + "npm": ">=11.16.0" + } + }, + "node_modules/@php-wasm/node-8-1": { + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@php-wasm/node-8-1/-/node-8-1-3.1.60.tgz", + "integrity": "sha512-NRwHB8lWG7o71cOawrWQJaR4yhSfmKs1FOHoe60wYi6mGu8k3o4AcQ3rLfGphAQfriVI2cDMBYrIPDHcj2UMEA==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/universal": "3.1.60", + "wasm-feature-detect": "1.8.0" + }, + "engines": { + "node": ">=24.18.0", + "npm": ">=11.16.0" + } + }, + "node_modules/@php-wasm/node-8-2": { + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@php-wasm/node-8-2/-/node-8-2-3.1.60.tgz", + "integrity": "sha512-RTnfuVIPikfNHdtOuQ4DEs72xt21zStlW1qSxMYq8qlRvOOtD/aDe9cdlq5pbPvheyWCFH/yVBkt7kSwdI3Ylw==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/universal": "3.1.60", + "wasm-feature-detect": "1.8.0" + }, + "engines": { + "node": ">=24.18.0", + "npm": ">=11.16.0" + } + }, + "node_modules/@php-wasm/node-8-3": { + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@php-wasm/node-8-3/-/node-8-3-3.1.60.tgz", + "integrity": "sha512-GdSiJDM+XuZNy25uiHDLOQDtfHR6T390mPien3HtsPMSSihf+Zf96FTgLSFmPK6AzfPiQePTmlw30rVNdbKefg==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/universal": "3.1.60", + "wasm-feature-detect": "1.8.0" + }, + "engines": { + "node": ">=24.18.0", + "npm": ">=11.16.0" + } + }, + "node_modules/@php-wasm/node-8-4": { + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@php-wasm/node-8-4/-/node-8-4-3.1.60.tgz", + "integrity": "sha512-Vmzb27+FqVQgtG/axuslmHd9gAyVD9dBywx+azz0U8oLmsD+WiFpYf1vFoJLvwMARYJZc0ERMxKdiIz3z7ZOCA==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/universal": "3.1.60", + "wasm-feature-detect": "1.8.0" + }, + "engines": { + "node": ">=24.18.0", + "npm": ">=11.16.0" + } + }, + "node_modules/@php-wasm/node-8-5": { + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@php-wasm/node-8-5/-/node-8-5-3.1.60.tgz", + "integrity": "sha512-NruRb/HKIQBXkTgdGwKqu9zgfvh/fSgILONmkWknamE60ZRIzrk2X59FS0NBnTmldFkS5SOj7BvahG7VRKhwzw==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/universal": "3.1.60", + "wasm-feature-detect": "1.8.0" + }, + "engines": { + "node": ">=24.18.0", + "npm": ">=11.16.0" + } + }, + "node_modules/@php-wasm/progress": { + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@php-wasm/progress/-/progress-3.1.60.tgz", + "integrity": "sha512-r9tUvCkNbLPQWNmORPgp2JLHShkwzAufeZNXy/SQ/X0rZAkPmODghDwgxmQQh0VzhmNXuipBtGD7BdZzPde9Ag==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/logger": "3.1.60" + }, + "engines": { + "node": ">=24.18.0", + "npm": ">=11.16.0" + } + }, + "node_modules/@php-wasm/scopes": { + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@php-wasm/scopes/-/scopes-3.1.60.tgz", + "integrity": "sha512-Z/79G8hnw3MBCkSE2qvHdyS1nNQKkeaYBIALV689LL4uqHRSi9ww8SB6/Ze6wl/Woc+Rm8UkABakToUF5Di8QA==", + "dev": true, + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=24.18.0", + "npm": ">=11.16.0" + } + }, + "node_modules/@php-wasm/stream-compression": { + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@php-wasm/stream-compression/-/stream-compression-3.1.60.tgz", + "integrity": "sha512-GgB1J08XW3tzIzVnAqGVxg6P7NBDGwnt7P+V7L/M4Fz4ZpMdjuY2euUDQQRE1W3yA4hOdI8qtUjc2l3WznkjpA==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/util": "3.1.60" + } + }, + "node_modules/@php-wasm/universal": { + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@php-wasm/universal/-/universal-3.1.60.tgz", + "integrity": "sha512-iF3Hg/JO1gRl9jXaFsbpqCj79O8L2FO+H/OoWTrYwH/ot4lTCuskVBJ9d6o/XMwYR9ncovP70K9dxwuu7I2d9w==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/logger": "3.1.60", + "@php-wasm/progress": "3.1.60", + "@php-wasm/stream-compression": "3.1.60", + "@php-wasm/util": "3.1.60", + "ini": "4.1.2" + }, + "engines": { + "node": ">=24.18.0", + "npm": ">=11.16.0" + } + }, + "node_modules/@php-wasm/util": { + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@php-wasm/util/-/util-3.1.60.tgz", + "integrity": "sha512-SSrUJFd9+g45d/jvvC+NysSeOyTFQDNYlHQCtfMkzi8yq6+zRj9YFx6LnR2wpuPzcyZ7rDlh7yoOvYnu6xbhbg==", + "dev": true, + "engines": { + "node": ">=24.18.0", + "npm": ">=11.16.0" + } + }, + "node_modules/@php-wasm/web-service-worker": { + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@php-wasm/web-service-worker/-/web-service-worker-3.1.60.tgz", + "integrity": "sha512-CMWB7tQxjxnHWTPdZX8wVz/0whEr/wIXFihjlakTRVIXN/G+fD9nE3/dsntH1fTb5e33ENDYHjyeyO8KuA1U0A==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/scopes": "3.1.60", + "@php-wasm/universal": "3.1.60" + }, + "engines": { + "node": ">=24.18.0", + "npm": ">=11.16.0" + } + }, + "node_modules/@php-wasm/xdebug-bridge": { + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@php-wasm/xdebug-bridge/-/xdebug-bridge-3.1.60.tgz", + "integrity": "sha512-ZGxfD+/jbe5tikiPRZHSUSCaMQoD0/Na88avaPKMCY1d4EegqPmLoJCP4W+7BNZiWiE02i2u8vrjDBcRPEwXCQ==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/logger": "3.1.60", + "@php-wasm/universal": "3.1.60", + "ws": "8.21.0", + "xml2js": "0.6.2", + "yargs": "17.7.2" + }, + "bin": { + "xdebug-bridge": "xdebug-bridge.js" + }, + "engines": { + "node": ">=24.18.0", + "npm": ">=11.16.0" + } + }, + "node_modules/@php-wasm/xdebug-bridge/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@php-wasm/xdebug-bridge/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@php-wasm/xdebug-bridge/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.3.6.tgz", + "integrity": "sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@playwright/test": { + "version": "1.64.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.64.1.tgz", + "integrity": "sha512-IaYGml/nSOiZWROfPtuIL7neuu9qDOZ3TNvNljUIrhDMxiGTgPjzj/g9p9RxpS2tSepPDRY+UwAaEeiKtUvIAg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.64.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@pmmmwh/react-refresh-webpack-plugin": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.6.3.tgz", + "integrity": "sha512-k1EEmFS9K7685tqJM55DCA3sYlTNZb7aFj1M3HNFwMqH+fGgMEvemIQ5kurIzKc9wYRSmfKpSZN3WUyY/GHlvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "anser": "^2.1.1", + "core-js-pure": "^3.23.3", + "error-stack-parser": "^2.0.6", + "html-entities": "^2.1.0", + "schema-utils": "^4.2.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "@types/webpack": "5.x", + "react-refresh": ">=0.10.0 <1.0.0", + "sockjs-client": "^1.4.0", + "type-fest": ">=0.17.0 <6.0.0", + "webpack": "^5.0.0", + "webpack-dev-server": "^4.8.0 || 5.x || 6.x", + "webpack-hot-middleware": "2.x", + "webpack-plugin-serve": "1.x" + }, + "peerDependenciesMeta": { + "@types/webpack": { + "optional": true + }, + "sockjs-client": { + "optional": true + }, + "type-fest": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + }, + "webpack-hot-middleware": { + "optional": true + }, + "webpack-plugin-serve": { + "optional": true + } + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@preact/signals": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@preact/signals/-/signals-1.3.4.tgz", + "integrity": "sha512-TPMkStdT0QpSc8FpB63aOwXoSiZyIrPsP9Uj347KopdS6olZdAYeeird/5FZv/M1Yc1ge5qstub2o8VDbvkT4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@preact/signals-core": "^1.7.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + }, + "peerDependencies": { + "preact": "10.x" + } + }, + "node_modules/@preact/signals-core": { + "version": "1.14.4", + "resolved": "https://registry.npmjs.org/@preact/signals-core/-/signals-core-1.14.4.tgz", + "integrity": "sha512-HNB6HYeYKhQbJ1aKl+YRjrS4+QWHLKX6qKoUsfS/m0vqzsVaEBiZiaKbG/e+NKk2ch5ALQr/ihWaMHxiCuuWHA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/@puppeteer/browsers": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-3.2.6.tgz", + "integrity": "sha512-MJP29gVoHznmqmwhH1tlZqzfeQ3aylTpSSa6WKZXtkwYUH02i9imBmWGTyZrbnAou5paX78SE4jMAUqnEPf8ug==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "modern-tar": "^0.8.6", + "yargs": "^18.0.0" + }, + "bin": { + "browsers": "lib/main-cli.js" + }, + "engines": { + "node": ">=22.12.0" + }, + "peerDependencies": { + "proxy-agent": ">=8.0.1", + "yauzl": "^2.10.0 || ^3.4.0" + }, + "peerDependenciesMeta": { + "proxy-agent": { + "optional": true + }, + "yauzl": { + "optional": true + } + } + }, + "node_modules/@puppeteer/browsers/node_modules/ansi-regex": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@puppeteer/browsers/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@puppeteer/browsers/node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@puppeteer/browsers/node_modules/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@puppeteer/browsers/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@puppeteer/browsers/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@puppeteer/browsers/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@puppeteer/browsers/node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@puppeteer/browsers/node_modules/yargs": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.2.0.tgz", + "integrity": "sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^8.2.1", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/@puppeteer/browsers/node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/@radix-ui/primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.2.0.tgz", + "integrity": "sha512-KABY+VMIRPJL+Vyzv/335FEhXXD/xDv8BMb1kl48/aXJZMH0LtHCTx5ga5RnxaEP3nZHtmTLf1P9vanmFX46bw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.5.tgz", + "integrity": "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.3.0.tgz", + "integrity": "sha512-v5AYEocMISkbJ+1fTXzZpiFv7NRr3D2WiYbLIKf+FkmJ8VkP6d503eV2n34MFussHXY1pcoq3yfJiQH77dc4Mg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.3.1.tgz", + "integrity": "sha512-+0E4qvVTwjAWra9sEhJ+9BTsvHE00zkUuNw1VGh7S+Gc13dfYk+S1PVQdchWDj+XYHhAai7Z7IpzXVDCGsDplg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-dismissable-layer": "1.2.1", + "@radix-ui/react-focus-guards": "1.2.0", + "@radix-ui/react-focus-scope": "1.3.1", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-portal": "1.2.0", + "@radix-ui/react-presence": "1.1.11", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-slot": "1.5.0", + "@radix-ui/react-use-controllable-state": "1.3.0", + "@radix-ui/react-use-layout-effect": "1.1.4", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.7.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.2.1.tgz", + "integrity": "sha512-KIfDcAsx+oVylFZ4xryVKusJUrLLhTQmfnJW4kwk4Km5myvhHCrj04hnWWUC166+xD4DHdH8tQGylwYX1h6+Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-use-callback-ref": "1.1.5", + "@radix-ui/react-use-effect-event": "0.0.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.2.0.tgz", + "integrity": "sha512-MPm5R0iRGUDrDnPNxmLBkv+X7pN4XKzt+MbWJEuJQsH3NFH4omrbpXpucyYCCKTSt+na/ZxGhEWQrWTZ0285cA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.3.1.tgz", + "integrity": "sha512-ugeMhbXt4y5S2T1GnIqbWDKEN962PDwnX1fN0eKOOgf5f63Dh2mn8F1FUySlgW0iF6GiVsRF32DzuDIWTUyQJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-use-callback-ref": "1.1.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.4.tgz", + "integrity": "sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.2.0.tgz", + "integrity": "sha512-StX7xXnaRU41VvFtMm9zlhVGz8XF6GmWWTXftkQaY+tqVkbs/R9UmAp5mLzHHapk5hM9RMZQvO/3usyIP/g42g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.11.tgz", + "integrity": "sha512-Z7/atlkab36Z8hm+V8zYc79r0W+tiOB1o0tun1By94JyadkekLQmtEAnBSP9XKXBNQsStoStS4+gX10JpS5akA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.2.0.tgz", + "integrity": "sha512-HCTSPMNqv55Qk0IX4geJbpyXazmqulcimwuyjxTkKi29v+QZbG2Zho9BnbBq4YS2PF+KlNb2CXoLz66uUmblkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.5.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.5.0.tgz", + "integrity": "sha512-tyYd9ddPXAysAbdbD9eYxgwpCSv6f3PncqVSNKQFSR5lcJVLz3cb/pANDoYrLiHBpXv7skortyUgGUWsYuPPFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.5" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.5.tgz", + "integrity": "sha512-Y5GBf4NKeADoXI1geiMlMuMoURT3k9MizzzKhGb/m55H94aGigZ01PIpkvsO6lML13BA6XYAXSJ6gza7vTn5nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.3.0.tgz", + "integrity": "sha512-TAcBI7bw2ljYCbv4+zuTSd6zICu8pRt6SDN2vJ/Z06K2MXh6I9WYFe/A9ovlFDtR5HVHGm5iDxDoABm/++dbKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-use-effect-event": "0.0.5", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.5.tgz", + "integrity": "sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.4.tgz", + "integrity": "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@react-spring/animated": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-10.1.2.tgz", + "integrity": "sha512-yAsQ/bbp6+vko7WNCI1M00c6KLE9XKTGCrgRhQqS4JcK3oF5qBV4rHYrQEprvEvYXxt+1H5FsLS2eVValEPfFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@react-spring/shared": "~10.1.2", + "@react-spring/types": "~10.1.2" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@react-spring/core": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-10.1.2.tgz", + "integrity": "sha512-lPGOAg0V+PV3ucopOajD+YCxoe8twALqAeG4c/+sqVjBsyUNNdx8qfz/DcNSPKA8PV3+AyQELlCxlDfap9cmBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@react-spring/animated": "~10.1.2", + "@react-spring/shared": "~10.1.2", + "@react-spring/types": "~10.1.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-spring/donate" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@react-spring/rafz": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-10.1.2.tgz", + "integrity": "sha512-KC6vSFZyPnRJ2rXqipV9QqR4SaYbYXjvKfdYKWipNK2mWuV79gr20WmpVUOsTiEHGRY3WSOdWCHN+P9Pdaqb7Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@react-spring/shared": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-10.1.2.tgz", + "integrity": "sha512-47/8bNQ/o0uEmxEnPBuERlC29VSqiTn5P9ln9tUMSVkYKYxBtouYE686F5kAGj+eHRPoNCw7drxWE9nv2d1LMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@react-spring/rafz": "~10.1.2", + "@react-spring/types": "~10.1.2" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@react-spring/types": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-10.1.2.tgz", + "integrity": "sha512-G4CWowmVPz+rDG1y9QRVq/prZNxiNwQaHC0kgT/MgY6jAGgdRgTV4VThpvJDwWvUitk3xZB4soP4d36fjeQ09g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@react-spring/web": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-10.1.2.tgz", + "integrity": "sha512-KxDB3zaDqy9qFsu7fdxjyraAxweHH4k5TW5WGT/OuMK6hKaxSDfhrQfRBzfFaSVvMBf+NghbJFanllV4ia6i7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@react-spring/animated": "~10.1.2", + "@react-spring/core": "~10.1.2", + "@react-spring/shared": "~10.1.2", + "@react-spring/types": "~10.1.2", + "csstype": "^3.2.3" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sentry/conventions": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@sentry/conventions/-/conventions-0.16.0.tgz", + "integrity": "sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@sentry/core": { + "version": "10.76.2", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.76.2.tgz", + "integrity": "sha512-vndrQalRiwYatTkjlLWvyf69KocXIb9TKJ4A9oiyWguL8PJYtqIRTlRe4OpkpjLrxS18fvRdV0jEm29c/9p3hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sentry/conventions": "^0.16.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry/node": { + "version": "10.76.2", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.76.2.tgz", + "integrity": "sha512-LSwGGE/GceTbkDHRoQQr3lq/dpxxA6RdooBbuGTJ/S6BdqKX2kw2FdV7Se9eJzZJJJ5AxCZum37+ejYS7N2pJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@opentelemetry/api": "^1.9.1", + "@opentelemetry/instrumentation": "^0.220.0", + "@opentelemetry/sdk-trace-base": "^2.9.0", + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.76.2", + "@sentry/node-core": "10.76.2", + "@sentry/opentelemetry": "10.76.2", + "@sentry/server-utils": "10.76.2", + "import-in-the-middle": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry/node-core": { + "version": "10.76.2", + "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.76.2.tgz", + "integrity": "sha512-TrgAc/SvU5kQfjw8pz6I8LLIUiblOSf9BPPNkTbS0Dzcqa4yQLVbmsbS3rvGT7ot64m9Vjzn327ZRy63MlI6/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.76.2", + "@sentry/opentelemetry": "10.76.2", + "import-in-the-middle": "^3.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/core": "^1.30.1 || ^2.1.0", + "@opentelemetry/exporter-trace-otlp-http": ">=0.57.0 <1", + "@opentelemetry/instrumentation": ">=0.57.1 <1", + "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@opentelemetry/core": { + "optional": true + }, + "@opentelemetry/exporter-trace-otlp-http": { + "optional": true + }, + "@opentelemetry/instrumentation": { + "optional": true + }, + "@opentelemetry/sdk-trace-base": { + "optional": true + } + } + }, + "node_modules/@sentry/opentelemetry": { + "version": "10.76.2", + "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.76.2.tgz", + "integrity": "sha512-deqWrKJGaku3nhiZchg34z8W9v6kUwtzoQ4cmEq6CeT8/4X93X4YfeJgoh1dhbswFRpwor4pQ6kj0KttagKtfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.76.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/core": "^1.30.1 || ^2.1.0", + "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0" + } + }, + "node_modules/@sentry/server-utils": { + "version": "10.76.2", + "resolved": "https://registry.npmjs.org/@sentry/server-utils/-/server-utils-10.76.2.tgz", + "integrity": "sha512-wogj6Trj8fAu+aD7HbpvHEBlyANMOvvO9MP5NFJaiQbprLKugtCdE6rFTxDhofXvRo4nswL9m4Cvysg3k5KcvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.76.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@simple-git/args-pathspec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@simple-git/args-pathspec/-/args-pathspec-1.0.4.tgz", + "integrity": "sha512-EtMX6XjRWSastG2SdkmSQByPtJ9dx/NIjnHbpQPCYt62j4RmSx5rgLTGpw0YCjF5h191gZOmBfheOT23cRSFdw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@simple-git/argv-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@simple-git/argv-parser/-/argv-parser-2.0.1.tgz", + "integrity": "sha512-M++IaVWrN+vYalilOHfwvT2IL3wYZtVEBQpmX6DLSOJLtxbZ+SUeIDE3IiPogzhPACLajwtxfa5rp+ZX1FczzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@simple-git/args-pathspec": "^1.0.4" + } + }, + "node_modules/@sindresorhus/base62": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/base62/-/base62-1.0.0.tgz", + "integrity": "sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@stylistic/stylelint-plugin": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-5.3.1.tgz", + "integrity": "sha512-w6WFLYzQS6nhbcoq8pwYP+OJYYhsdGEiCxY+eINi2+ouDwRLXTucXZj6IrDzkY4di+vsGOPVvJ4/gXChSeOtlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0", + "@csstools/media-query-list-parser": "^5.0.0", + "postcss": "^8.5.26", + "postcss-selector-parser": "^7.1.5", + "postcss-value-parser": "^4.2.0", + "style-search": "^0.1.0" + }, + "peerDependencies": { + "stylelint": "^17.6.0" + } + }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/core": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/core/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/@svgr/core/node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@svgr/core/node_modules/js-yaml": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.21.3", + "entities": "^4.4.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", + "svg-parser": "^2.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, + "node_modules/@svgr/plugin-svgo": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", + "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cosmiconfig": "^8.1.3", + "deepmerge": "^4.3.1", + "svgo": "^3.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, + "node_modules/@svgr/plugin-svgo/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/@svgr/plugin-svgo/node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@svgr/plugin-svgo/node_modules/js-yaml": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@svgr/webpack": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz", + "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.21.3", + "@babel/plugin-transform-react-constant-elements": "^7.21.3", + "@babel/preset-env": "^7.20.2", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.21.0", + "@svgr/core": "8.1.0", + "@svgr/plugin-jsx": "8.1.0", + "@svgr/plugin-svgo": "8.1.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tannin/compile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@tannin/compile/-/compile-1.1.0.tgz", + "integrity": "sha512-n8m9eNDfoNZoxdvWiTfW/hSPhehzLJ3zW7f8E7oT6mCROoMNWCB4TYtv041+2FMAxweiE0j7i1jubQU4MEC/Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tannin/evaluate": "^1.2.0", + "@tannin/postfix": "^1.1.0" + } + }, + "node_modules/@tannin/evaluate": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@tannin/evaluate/-/evaluate-1.2.0.tgz", + "integrity": "sha512-3ioXvNowbO/wSrxsDG5DKIMxC81P0QrQTYai8zFNY+umuoHWRPbQ/TuuDEOju9E+jQDXmj6yI5GyejNuh8I+eg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tannin/plural-forms": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@tannin/plural-forms/-/plural-forms-1.1.0.tgz", + "integrity": "sha512-xl9R2mDZO/qiHam1AgMnAES6IKIg7OBhcXqy6eDsRCdXuxAFPcjrej9HMjyCLE0DJ/8cHf0i5OQTstuBRhpbHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tannin/compile": "^1.1.0" + } + }, + "node_modules/@tannin/postfix": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@tannin/postfix/-/postfix-1.1.0.tgz", + "integrity": "sha512-oocsqY7g0cR+Gur5jRQLSrX2OtpMLMse1I10JQBm8CdGMrDkh1Mg2gjsiquMHRtBs4Qwu5wgEp5GgIYHk4SNPw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tannin/sprintf": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@tannin/sprintf/-/sprintf-1.3.4.tgz", + "integrity": "sha512-Kypdet5L0Hhkfbdkn2QREczNTjCXhiAnouRINyAePy1lZKHD/zcwiqN93HC7i/vVCAuzNAlp+wuT4yOZ5icOSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.4.tgz", + "integrity": "sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/aws-lambda": { + "version": "8.10.164", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.164.tgz", + "integrity": "sha512-XOnrazWcOd6yWPnR7DxqCBPPYciDjHq+NN8LPwwqBCJNXLyPJgKo47fZpJ113+oyAn9Zti0Cf6H4k6yPcE3WFg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/btoa-lite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/btoa-lite/-/btoa-lite-1.0.2.tgz", + "integrity": "sha512-ZYbcE2x7yrvNFJiU7xJGrpF/ihpkM7zKgw8bha3LNJSesvTtUNxbpzaT7WXBIryf6jovisrxTBvymxMeLLj1Mg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/dom-mediacapture-transform": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@types/dom-mediacapture-transform/-/dom-mediacapture-transform-0.1.12.tgz", + "integrity": "sha512-d7/QsLRwF864A5mgIM/YrfiglHoYn7zgCcAoJgW404r+2DwnNr7EBbLnCWpmOMgH8y0te73L1AV6H1bmauaWFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/dom-webcodecs": "*" + } + }, + "node_modules/@types/dom-webcodecs": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/@types/dom-webcodecs/-/dom-webcodecs-0.1.13.tgz", + "integrity": "sha512-O5hkiFIcjjszPIYyUSyvScyvrBoV3NOEEZx/pMlsu44TKzWNkLVBBxnxJz42in5n3QIolYOcBYFCPZZ0h8SkwQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.3.tgz", + "integrity": "sha512-dPfW8NFiOF4wOHc7+N/QSxlY9cfSsenewGbAz8C8U/MULPd/YZ27LvJUIlzaXie7e6Ove9YunJGgC9tbHD2cKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/highlight-words-core": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/highlight-words-core/-/highlight-words-core-1.2.3.tgz", + "integrity": "sha512-PWNU/NR0CaYEsK38mcCTyDzeS2TlEGK9kRhRMz1i86jVAe836ZlA3gl6QYpu+CG6IpfNKTgWpEnJuRededvC0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/katex": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.8.tgz", + "integrity": "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mousetrap": { + "version": "1.6.15", + "resolved": "https://registry.npmjs.org/@types/mousetrap/-/mousetrap-1.6.15.tgz", + "integrity": "sha512-qL0hyIMNPow317QWW/63RvL1x5MVMV+Ru3NaY9f/CuEpCqrmb7WeuK2071ZY5hczOnm38qExWM2i2WtkXLSqFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "26.6.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.5.tgz", + "integrity": "sha512-uMzsv9stDdofIZBx4UGh4Z9ZgXnGC9seRDscilBcTHAeOFk67MZg/k9bWVfO+bk4qZmG+l52hRnLwUiH2LnJTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.9.0" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/qs": { + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.3.0.tgz", + "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-index": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, + "node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.2.tgz", + "integrity": "sha512-67MQl+fpWKVTT1NYdnmo3U4sc/xPo/zQBncVnI74qmQa0z/b+1g6iYqNmGCPbxO+zz2aklb08a0oHfegiVd0/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.72.0.tgz", + "integrity": "sha512-aysKgysPX9XOv/dk95UkvUDe++Q3n3dJPG9CEWT3RUccxfkPJ+Qn6nhKPDhZXU6Ofo9kgwGWwKyQCnZIXTgngg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.72.0", + "@typescript-eslint/type-utils": "8.72.0", + "@typescript-eslint/utils": "8.72.0", + "@typescript-eslint/visitor-keys": "8.72.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.72.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.14.tgz", + "integrity": "sha512-wCBiZ77u1tuWde6G/nfOtLaCTYcFDU5yCzARzK10y+rLhzYoL7a7yEemcopcc2Mzow7gZOIjnV2ym1NYmdiJOQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.72.0.tgz", + "integrity": "sha512-0O+x3ynciD6zuXb1CKSu165haQV6ey53DKhHHUnujP7U+XlltJG+/keT0Xhd1HXCze+IHf7Tvt39ovyepX/LNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.72.0", + "@typescript-eslint/types": "8.72.0", + "@typescript-eslint/typescript-estree": "8.72.0", + "@typescript-eslint/visitor-keys": "8.72.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.72.0.tgz", + "integrity": "sha512-bzHngBbmIgzj6TsTjVUCntGx6FFLgE7mPjpO0PzHycOaZHXqS43XnrkFIHRS16LLr/xO6FHSXG3GJFo/F4bSBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.72.0", + "@typescript-eslint/types": "^8.72.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.72.0.tgz", + "integrity": "sha512-JewPyJOYcXBSAS7PIgufscHOwZqhTGyBRiQJhhuDln66tMchxvvt7nJ90JKKIoOelmdHA3kAQbm9cEUHc4JSEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.72.0", + "@typescript-eslint/visitor-keys": "8.72.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.72.0.tgz", + "integrity": "sha512-0vbxCf7xLVPSjcOdm6eQbgViH8EDJ8xnl1laLT9yoqSz2YmcSfSg5Vdl0JUvZf6rcuJ0ZivJIuMD4edTg8obaA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.72.0.tgz", + "integrity": "sha512-gEXxmmbSTSOVTC37IYYvremOM31v+UN2W772bJxmVYPhAOPSUxtacyb7wkm1EhkYKU6KzFJsdU44DCV4to0LWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.72.0", + "@typescript-eslint/typescript-estree": "8.72.0", + "@typescript-eslint/utils": "8.72.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.72.0.tgz", + "integrity": "sha512-Adfoy59kxNEjT5E5fnyrp7GpHYuWs8ONbdohu4ez6IU46qDq8LltG90tLmkucZB2uhYL8mqBjtXqGd8yhfjXqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.72.0.tgz", + "integrity": "sha512-gwLfFgWcw15tN2KXDCJvxEOQjPn+P3j/n3eSqkZ5sX4VpW9Cosodr+tWwBZRvJT3TcKx6HnPRGZMCNAiP1Xhug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.72.0", + "@typescript-eslint/tsconfig-utils": "8.72.0", + "@typescript-eslint/types": "8.72.0", + "@typescript-eslint/visitor-keys": "8.72.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.72.0.tgz", + "integrity": "sha512-0up3oArJZBYnzOaEXKo8DmI43QvMZyPoFEt9LWzRJcyl/RcJjfKKPebknN1VbTgXBf3M/KggUh5uUMdURLnseg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.72.0", + "@typescript-eslint/types": "8.72.0", + "@typescript-eslint/typescript-estree": "8.72.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.72.0.tgz", + "integrity": "sha512-E2mroviasaWecDzTGiGMtuEguuLVOeRggnrIn93CJuxYJzQBHbyymlT08aVQydbBal5538o+JkBKXh2sRuCyIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.72.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz", + "integrity": "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.12.2.tgz", + "integrity": "sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.12.2.tgz", + "integrity": "sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.12.2.tgz", + "integrity": "sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.12.2.tgz", + "integrity": "sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.12.2.tgz", + "integrity": "sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.12.2.tgz", + "integrity": "sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.12.2.tgz", + "integrity": "sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.12.2.tgz", + "integrity": "sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-gnu/-/resolver-binding-linux-loong64-gnu-1.12.2.tgz", + "integrity": "sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-musl/-/resolver-binding-linux-loong64-musl-1.12.2.tgz", + "integrity": "sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.12.2.tgz", + "integrity": "sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.12.2.tgz", + "integrity": "sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.12.2.tgz", + "integrity": "sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.12.2.tgz", + "integrity": "sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.12.2.tgz", + "integrity": "sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.12.2.tgz", + "integrity": "sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-openharmony-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-openharmony-arm64/-/resolver-binding-openharmony-arm64-1.12.2.tgz", + "integrity": "sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.12.2.tgz", + "integrity": "sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz", + "integrity": "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz", + "integrity": "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz", + "integrity": "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@use-gesture/core": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz", + "integrity": "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@use-gesture/react": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.1.tgz", + "integrity": "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@use-gesture/core": "10.3.1" + }, + "peerDependencies": { + "react": ">= 16.8.0" + } + }, + "node_modules/@vitest/eslint-plugin": { + "version": "1.6.28", + "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.6.28.tgz", + "integrity": "sha512-2QorUqhTIR7gaDkANEBTjuG2SE5MJgFaWElINyq9bIaOEJXycZUliu5xYSzG6QRLunWopYshgcRs2vW2CTRqGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "^8.71.2", + "@typescript-eslint/utils": "^8.71.2" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "*", + "eslint": ">=8.57.0", + "typescript": ">=6.0.0", + "vitest": "*" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "typescript": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webpack-cli/configtest": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", + "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + } + }, + "node_modules/@webpack-cli/info": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", + "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", + "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/@wordpress/a11y": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/a11y/-/a11y-5.0.0.tgz", + "integrity": "sha512-ocYyNn7Z85gDzvto3ppOPeuaj4VoDk03ymsO3ubuAoEDdxXgsTiJIZIBTYrwxvQZ3KznBe5i/HzPKrpvdpJQ+Q==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/dom-ready": "^5.0.0", + "@wordpress/i18n": "^7.0.0" + }, + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/autop": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/autop/-/autop-5.0.0.tgz", + "integrity": "sha512-aHfC5Z7TmwvuRT0iV9e6l/N17C3VVcXcOlzouEC0x2/SNEc0dR71b3WPc0Qx4P44TAvpp7YQPJcaaM/Fz7Bfbg==", + "dev": true, + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/babel-preset-default": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/babel-preset-default/-/babel-preset-default-10.0.0.tgz", + "integrity": "sha512-mXxwcIfEuj10SnofavV13/0NshtkrI+d1S/vSWrtgU5ibj+ejcvAXBiqIPBsb3RZDZwipTbRJV+VbyBjaf4IrQ==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@babel/core": "^7.25.7", + "@babel/plugin-syntax-import-attributes": "^7.26.0", + "@babel/plugin-transform-react-jsx": "^7.25.7", + "@babel/plugin-transform-runtime": "^7.25.7", + "@babel/preset-env": "^7.25.7", + "@babel/preset-typescript": "^7.25.7", + "@wordpress/browserslist-config": "^7.0.0", + "@wordpress/warning": "^4.0.0", + "browserslist": "^4.28.7", + "core-js": "^3.31.0", + "react": "^19.3.0" + }, + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/base-styles": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/base-styles/-/base-styles-14.0.0.tgz", + "integrity": "sha512-BkxwZfnaazn6qziF1yBcYPyGziFArrcsyKgm09fDd0SdegRsh2ZmlENwAcvfuQ23ENS/m3vuFMMmBUbqg+Efmg==", + "dev": true, + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/blob": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/blob/-/blob-5.0.0.tgz", + "integrity": "sha512-BB2FpHEOq1my0Qe6BD/K5SAWt5I40vDZaFxH5PeeUyZ1KCEszaidkwYsY02sXxRK8rK/WOHGii2fOcpIqrmajA==", + "dev": true, + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/block-editor": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/block-editor/-/block-editor-19.0.0.tgz", + "integrity": "sha512-HZqOQC10gR7NOU1cUWRrIKx/6XuZBrlbezw8fLgbMpZD91E0oglSqrkvejhE+Ezw8zpDHvD9L/SacfZv8BP/Hw==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@react-spring/web": "^10.1.2", + "@wordpress/a11y": "^5.0.0", + "@wordpress/base-styles": "^14.0.0", + "@wordpress/blob": "^5.0.0", + "@wordpress/block-serialization-default-parser": "^6.0.0", + "@wordpress/blocks": "^17.0.0", + "@wordpress/commands": "^2.0.0", + "@wordpress/components": "^43.0.0", + "@wordpress/compose": "^9.0.0", + "@wordpress/data": "^11.0.0", + "@wordpress/dataviews": "^21.0.0", + "@wordpress/date": "^6.0.0", + "@wordpress/deprecated": "^5.0.0", + "@wordpress/dom": "^5.0.0", + "@wordpress/element": "^10.0.0", + "@wordpress/global-styles-engine": "^2.0.0", + "@wordpress/hooks": "^5.0.0", + "@wordpress/html-entities": "^5.0.0", + "@wordpress/i18n": "^7.0.0", + "@wordpress/icons": "^19.0.0", + "@wordpress/image-cropper": "^2.0.0", + "@wordpress/interactivity": "^8.0.0", + "@wordpress/is-shallow-equal": "^6.0.0", + "@wordpress/kebab-case": "^2.0.0", + "@wordpress/keyboard-shortcuts": "^6.0.0", + "@wordpress/keycodes": "^5.0.0", + "@wordpress/notices": "^6.0.0", + "@wordpress/preferences": "^5.0.0", + "@wordpress/priority-queue": "^4.0.0", + "@wordpress/private-apis": "^3.0.0", + "@wordpress/react-inert-value": "^2.0.0", + "@wordpress/rich-text": "^8.0.0", + "@wordpress/style-engine": "^3.0.0", + "@wordpress/token-list": "^4.0.0", + "@wordpress/ui": "^0.25.0", + "@wordpress/upload-media": "^0.43.0", + "@wordpress/url": "^5.0.0", + "@wordpress/warning": "^4.0.0", + "change-case": "^4.1.2", + "clsx": "^2.1.1", + "colord": "^2.9.4", + "diff": "^8.0.3", + "fast-deep-equal": "^3.1.3", + "parsel-js": "^1.1.2", + "postcss": "^8.4.38", + "postcss-prefix-selector": "^1.16.0", + "postcss-urlrebase": "^1.4.0", + "react-easy-crop": "^5.4.2", + "remove-accents": "^0.5.0" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "@types/react": "^18 || ^19", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@wordpress/block-serialization-default-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/block-serialization-default-parser/-/block-serialization-default-parser-6.0.0.tgz", + "integrity": "sha512-C9HgZeToeeNouMGjTnz5WV8z4vNZ+NBrX4af4Npy6F/VrikOpMAYYJ5hi1K07g/o9hzalZMYN+fHXvuQcYK9MQ==", + "dev": true, + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/blocks": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/blocks/-/blocks-17.0.0.tgz", + "integrity": "sha512-6lZpvRhaFvg/GxYa6RXauU67g7ocjR2UloY1i53wFrCPoNgZAIIfn2q94IX5KeE35nSr0u8XdzDjyRwFz0QGYw==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/autop": "^5.0.0", + "@wordpress/blob": "^5.0.0", + "@wordpress/block-serialization-default-parser": "^6.0.0", + "@wordpress/data": "^11.0.0", + "@wordpress/deprecated": "^5.0.0", + "@wordpress/dom": "^5.0.0", + "@wordpress/element": "^10.0.0", + "@wordpress/hooks": "^5.0.0", + "@wordpress/html-entities": "^5.0.0", + "@wordpress/i18n": "^7.0.0", + "@wordpress/is-shallow-equal": "^6.0.0", + "@wordpress/keycodes": "^5.0.0", + "@wordpress/private-apis": "^3.0.0", + "@wordpress/rich-text": "^8.0.0", + "@wordpress/shortcode": "^5.0.0", + "@wordpress/warning": "^4.0.0", + "change-case": "^4.1.2", + "colord": "^2.9.4", + "fast-deep-equal": "^3.1.3", + "hpq": "^1.4.0", + "is-plain-object": "^5.1.0", + "marked": "^18.0.3", + "memize": "^2.1.1", + "react-is": "^19.3.0", + "remove-accents": "^0.5.0", + "simple-html-tokenizer": "^0.5.7", + "uuid": "^14.0.0" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "@types/react": "^18 || ^19", + "@types/react-dom": "^18 || ^19", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@wordpress/browserslist-config": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/browserslist-config/-/browserslist-config-7.0.0.tgz", + "integrity": "sha512-eZ2yaOBprRNppFNN/qubJ7C2nOX6llR+IeQ5jQC1665sFcOkZes+hLwpbKsZNcmL26ge+77vdQ0f/Ur67rgkgw==", + "dev": true, + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/commands": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/commands/-/commands-2.0.0.tgz", + "integrity": "sha512-pfkGCvuEOJ6v67++VKdn387pM3AFbAQKVLZJ2lq9Ei75xvd7pkmsnz3iqLA326E88IiXmSY49KSXuU/4bGsCYA==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/base-styles": "^14.0.0", + "@wordpress/components": "^43.0.0", + "@wordpress/data": "^11.0.0", + "@wordpress/element": "^10.0.0", + "@wordpress/i18n": "^7.0.0", + "@wordpress/icons": "^19.0.0", + "@wordpress/keyboard-shortcuts": "^6.0.0", + "@wordpress/keycodes": "^5.0.0", + "@wordpress/preferences": "^5.0.0", + "@wordpress/private-apis": "^3.0.0", + "clsx": "^2.1.1", + "cmdk": "^1.0.0" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "@types/react": "^18 || ^19", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@wordpress/components": { + "version": "43.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/components/-/components-43.0.0.tgz", + "integrity": "sha512-8IfVyT0QXTlhcw9id9aSygYISKR+xjyyVWuJuwMGgb/SqHGBFLnBBKBRq/ypamF/eB0vE8Gb+AmdjP6G8P1LBw==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@ariakit/react": "^0.4.42", + "@date-fns/utc": "^2.1.1", + "@floating-ui/react-dom": "^2.1.9", + "@types/highlight-words-core": "^1.2.1", + "@use-gesture/react": "^10.3.1", + "@wordpress/a11y": "^5.0.0", + "@wordpress/base-styles": "^14.0.0", + "@wordpress/compose": "^9.0.0", + "@wordpress/date": "^6.0.0", + "@wordpress/deprecated": "^5.0.0", + "@wordpress/dom": "^5.0.0", + "@wordpress/element": "^10.0.0", + "@wordpress/escape-html": "^4.0.0", + "@wordpress/hooks": "^5.0.0", + "@wordpress/html-entities": "^5.0.0", + "@wordpress/i18n": "^7.0.0", + "@wordpress/icons": "^19.0.0", + "@wordpress/is-shallow-equal": "^6.0.0", + "@wordpress/keycodes": "^5.0.0", + "@wordpress/primitives": "^5.0.0", + "@wordpress/private-apis": "^3.0.0", + "@wordpress/react-inert-value": "^2.0.0", + "@wordpress/rich-text": "^8.0.0", + "@wordpress/style-runtime": "^0.14.0", + "@wordpress/ui": "^0.25.0", + "@wordpress/warning": "^4.0.0", + "clsx": "^2.1.1", + "colord": "^2.9.4", + "csstype": "^3.2.3", + "date-fns": "^4.4.0", + "deepmerge": "^4.3.1", + "fast-deep-equal": "^3.1.3", + "framer-motion": "^11.15.0", + "gradient-parser": "^1.2.0", + "highlight-words-core": "^1.2.2", + "is-plain-object": "^5.1.0", + "memize": "^2.1.1", + "path-to-regexp": "^6.2.1", + "re-resizable": "^6.11.2", + "react-colorful": "^5.6.1", + "remove-accents": "^0.5.0", + "uuid": "^14.0.0" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "@types/react": "^18 || ^19", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@wordpress/compose": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/compose/-/compose-9.0.0.tgz", + "integrity": "sha512-xP7EDOTyJrc5fC48Zh/u9Hvu+eHvKlU+qn5OyDQi1ahmg5GqNq3UAaODnsgeLyggGgWtmJhbzgN0qIJvhn52Sw==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@types/mousetrap": "^1.6.8", + "@wordpress/deprecated": "^5.0.0", + "@wordpress/dom": "^5.0.0", + "@wordpress/element": "^10.0.0", + "@wordpress/is-shallow-equal": "^6.0.0", + "@wordpress/keycodes": "^5.0.0", + "@wordpress/priority-queue": "^4.0.0", + "@wordpress/private-apis": "^3.0.0", + "@wordpress/undo-manager": "^2.0.0", + "change-case": "^4.1.2", + "mousetrap": "^1.6.5" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "@types/react": "^18 || ^19", + "@types/react-dom": "^18 || ^19", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@wordpress/data": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/data/-/data-11.0.0.tgz", + "integrity": "sha512-z74Y4/cfyw+Kel5uhVYsoZTytFtUw2xRrfFpa7uWIdfrdQxSVgxajd74pRjxGQ6W+g0VWzV6ZBptHDxYf58Nmg==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/compose": "^9.0.0", + "@wordpress/deprecated": "^5.0.0", + "@wordpress/element": "^10.0.0", + "@wordpress/is-shallow-equal": "^6.0.0", + "@wordpress/priority-queue": "^4.0.0", + "@wordpress/private-apis": "^3.0.0", + "@wordpress/redux-routine": "^6.0.0", + "deepmerge": "^4.3.1", + "equivalent-key-map": "^0.2.2", + "is-plain-object": "^5.1.0", + "is-promise": "^4.0.0", + "redux": "^5.0.1", + "rememo": "^4.0.2" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "@types/react": "^18 || ^19", + "@types/react-dom": "^18 || ^19", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@wordpress/dataviews": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/dataviews/-/dataviews-21.0.0.tgz", + "integrity": "sha512-/aojI+0aZEpMYkteZ404V2Bhyxu0Kr5zcf7WhEgmIyHEcfBmuoKIxiPjnVQgAC990gmwW522LYBLnepOymdIOg==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@ariakit/react": "^0.4.42", + "@base-ui/react": "^1.9.0", + "@date-fns/tz": "^1.5.0", + "@daypicker/react": "^10.0.1", + "@floating-ui/react-dom": "^2.1.9", + "@use-gesture/react": "^10.3.1", + "@wordpress/a11y": "^5.0.0", + "@wordpress/base-styles": "^14.0.0", + "@wordpress/components": "^43.0.0", + "@wordpress/compose": "^9.0.0", + "@wordpress/data": "^11.0.0", + "@wordpress/date": "^6.0.0", + "@wordpress/deprecated": "^5.0.0", + "@wordpress/element": "^10.0.0", + "@wordpress/hooks": "^5.0.0", + "@wordpress/i18n": "^7.0.0", + "@wordpress/icons": "^19.0.0", + "@wordpress/kebab-case": "^2.0.0", + "@wordpress/keycodes": "^5.0.0", + "@wordpress/react-inert-value": "^2.0.0", + "@wordpress/ui": "^0.25.0", + "@wordpress/warning": "^4.0.0", + "change-case": "^4.1.2", + "clsx": "^2.1.1", + "colord": "^2.9.4", + "colorjs.io": "^0.7.1", + "date-fns": "^4.4.0", + "deepmerge": "^4.3.1", + "fast-deep-equal": "^3.1.3", + "framer-motion": "^11.15.0", + "highlight-words-core": "^1.2.2", + "is-plain-object": "^5.1.0", + "memize": "^2.1.1", + "react-colorful": "^5.6.1", + "remove-accents": "^0.5.0", + "uuid": "^14.0.0" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "@types/react": "^18 || ^19", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@wordpress/date": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/date/-/date-6.0.0.tgz", + "integrity": "sha512-knjw0jMLk2RzLq8wUzLuS0lvNI1UqwpbqxYVuSyXrUb+8GKXaT/uq0ql82Dha0RiagNCfrneowZFo3mJc7EyTw==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/deprecated": "^5.0.0", + "moment": "^2.31.0", + "moment-timezone": "^0.5.40" + }, + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/dependency-extraction-webpack-plugin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/dependency-extraction-webpack-plugin/-/dependency-extraction-webpack-plugin-7.0.0.tgz", + "integrity": "sha512-SYzb/CWt8VfGGMwGb7UVjBV8qGBkY6PqybUWFtn581lx/KMJANwswLPTWA/QOhog0NSDynscpQuOPibQ5ULceQ==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "json2php": "^0.0.9" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/@wordpress/deprecated": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/deprecated/-/deprecated-5.0.0.tgz", + "integrity": "sha512-PZjrzeX15ZNIDvcBzOXnXF/FpKNwZgz23hQ+xpcmRBjZKCyT6YoxC45O3jFIGyZZtBwxoxCYQPBkCjRuKbVcpg==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/hooks": "^5.0.0" + }, + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/dom": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/dom/-/dom-5.0.0.tgz", + "integrity": "sha512-jKRFuQgNuAbFhiIU/b1xQl00rEphMdBQal6pmiN/pDVfIR1idZFaaI8K+eH/Gqn+yb/q20YIqPLM1+SXR4xy8w==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/deprecated": "^5.0.0" + }, + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/dom-ready": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/dom-ready/-/dom-ready-5.0.0.tgz", + "integrity": "sha512-a2Y+loEwENAM+kyTnXU7LTe8GgriXWfoxbco4486e2FuNpfELPLjDLAeJVbaFuw8pbtkk6xARlLh32y7yGG7Rg==", + "dev": true, + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/e2e-test-utils-playwright": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@wordpress/e2e-test-utils-playwright/-/e2e-test-utils-playwright-3.1.0.tgz", + "integrity": "sha512-ybc60l3g1syfpVJ7L/zOculIYBsfVfwsWOAn/bm4vN4+irRT6jJ/11bbPhclb9yjDyGatn+aHw89OhhHLjz8mQ==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "change-case": "^4.1.2", + "get-port": "^5.1.1", + "lighthouse": "^13.4.1", + "mime": "^3.0.0", + "web-vitals": "^4.2.4" + }, + "engines": { + "node": ">=22.19.0" + }, + "peerDependencies": { + "@playwright/test": ">=1", + "@types/node": ">=20" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@wordpress/element": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-10.0.0.tgz", + "integrity": "sha512-gv3cZ3O2j4y7qUUBo/h1Ni9OxG6ak78+D0YhggMsGyIPavT4CpJtRyMbarhENJ7K1MM9JzpuecdlMID8Cfd51Q==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/escape-html": "^4.0.0", + "change-case": "^4.1.2", + "is-plain-object": "^5.1.0" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "@types/react": "^18 || ^19", + "@types/react-dom": "^18 || ^19", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@wordpress/env": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/env/-/env-12.0.0.tgz", + "integrity": "sha512-RoUQV3sl1qF4h/kg+vrec9PCfr2bwXSOmWoLd9NmV8rD/sCBMt7tSBfeTWZEbClMhWHce8tu4hJ+pQeHXNnu2A==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@inquirer/prompts": "^7.2.0", + "@wp-playground/cli": "^3.1.56", + "adm-zip": "^0.6.1", + "chalk": "^4.1.1", + "copy-dir": "^1.3.0", + "cross-spawn": "^7.0.6", + "docker-compose": "^0.24.3", + "got": "^11.8.5", + "js-yaml": "^3.15.2", + "ora": "^4.0.2", + "rimraf": "^5.0.10", + "simple-git": "^4.0.2", + "yargs": "^17.3.0" + }, + "bin": { + "wp-env": "bin/wp-env" + }, + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/escape-html": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-4.0.0.tgz", + "integrity": "sha512-fWEvTJvNyPUSAXz3pMETrt1DHKaQ9CJVirhuBqb1xuZmIdRY4AlsjLgayd+lUSTkTU+0gRIPu1TDdU5/6Ue2Nw==", + "dev": true, + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/eslint-plugin": { + "version": "28.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/eslint-plugin/-/eslint-plugin-28.0.0.tgz", + "integrity": "sha512-VRFrez67J9HWGR4s149A3ZG1ynY9uEczmuR7SFXzlcUn3itlo5ood42pv1dgTZK3eqJo3l5tdK3hD94W+ZEq8w==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@eslint/compat": "^2.0.0", + "@vitest/eslint-plugin": "^1.6.27", + "@wordpress/prettier-config": "^5.0.0", + "@wordpress/theme": "^3.0.0", + "cosmiconfig": "^7.0.0", + "eslint-config-prettier": "^10.0.0", + "eslint-import-resolver-typescript": "^4.4.4", + "eslint-plugin-import": "^2.32.0", + "eslint-plugin-jest": "^29.16.5", + "eslint-plugin-jsdoc": "^64.0.0", + "eslint-plugin-jsx-a11y": "^6.10.0", + "eslint-plugin-playwright": "^2.1.0", + "eslint-plugin-prettier": "^5.5.5", + "eslint-plugin-react": "^7.37.0", + "eslint-plugin-react-hooks": "^7.1.1", + "globals": "^16.0.0", + "requireindex": "^1.2.0", + "typescript-eslint": "^8.70.1" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "eslint": "^9.0.0 || ^10.0.0", + "prettier": ">=3", + "typescript": ">=5" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@wordpress/global-styles-engine": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/global-styles-engine/-/global-styles-engine-2.0.0.tgz", + "integrity": "sha512-lhMen98kG/nLv3QPD8HWXPdhuXDbOje7/gYpljv4zKBjAf3tfbRQ11MblKW8OvYUux3Iu9vaZE+DB2LOgNee5g==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/blocks": "^17.0.0", + "@wordpress/data": "^11.0.0", + "@wordpress/i18n": "^7.0.0", + "@wordpress/private-apis": "^3.0.0", + "@wordpress/style-engine": "^3.0.0", + "colord": "^2.9.4", + "deepmerge": "^4.3.1", + "fast-deep-equal": "^3.1.3", + "is-plain-object": "^5.1.0", + "memize": "^2.1.1" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "@types/react": "^18 || ^19", + "@types/react-dom": "^18 || ^19", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@wordpress/hooks": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-5.0.0.tgz", + "integrity": "sha512-jA2hDMNr2Ky/D14s8K+TQ/ZI125icjZfTPZYcsgEHM6mTtSJY2dhcmp9+tfSrcnY+M17svzplIxXma+bpBzcFg==", + "dev": true, + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/html-entities": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/html-entities/-/html-entities-5.0.0.tgz", + "integrity": "sha512-rj95Op3y+4+QsdOzlguOT8lpAHegJwdMsDx9Eq/9/fL/CGskgugUm4aqqeFQDNd7B+gOzXM7NZMX2ohaYYXYdA==", + "dev": true, + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/i18n": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-7.0.0.tgz", + "integrity": "sha512-xk7qKLDB08c3LDUtThfmuumq//354bXLGRrPWfC1gmwPMNJPRC7ggwtSFxzH+ImdssxIKk8NaG4H3yF5WJoS5w==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@tannin/sprintf": "^1.3.2", + "@wordpress/hooks": "^5.0.0", + "gettext-parser": "^1.3.1", + "tannin": "^1.2.0" + }, + "bin": { + "pot-to-php": "tools/pot-to-php.js" + }, + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/icons": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/icons/-/icons-19.0.0.tgz", + "integrity": "sha512-LPx0Tppj5Y/54fuqIRMUqzplP0ZQL1nNvWthUv+ctpylDbGe4qdlWJpLCLZq1BpI1IXS2s3g5NaN2dXFi5H45w==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/element": "^10.0.0", + "@wordpress/primitives": "^5.0.0", + "change-case": "^4.1.2" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "@types/react": "^18 || ^19", + "@types/react-dom": "^18 || ^19", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@wordpress/image-cropper": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/image-cropper/-/image-cropper-2.0.0.tgz", + "integrity": "sha512-Ach0WfHZbwM8oomQtgHsTMgG0+wuhlZYD7ZHsygREkPVD9f0Kd+513rtRFfMdQ2NJzdjqJXSD2nEK0hT/gGjOQ==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/components": "^43.0.0", + "@wordpress/element": "^10.0.0", + "@wordpress/i18n": "^7.0.0", + "dequal": "^2.0.3", + "react-easy-crop": "^5.4.2" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "@types/react": "^18 || ^19", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@wordpress/interactivity": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/interactivity/-/interactivity-8.0.0.tgz", + "integrity": "sha512-VI60fYRVsSErIs/K9xnUNoAXUycgQwMGP7cq7YHvWdKROPE285+lnv9GarRGFR7W5/9i5fk3a8ifV+2o6sMCOg==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@preact/signals": "^1.3.0", + "@preact/signals-core": "^1.7.0", + "preact": "^10.29.1" + }, + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/is-shallow-equal": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/is-shallow-equal/-/is-shallow-equal-6.0.0.tgz", + "integrity": "sha512-KMEZTCaXWp5udPNdPc8lEky3AEhKnUej0aKfjV38Ggox2e87SDXALt3DdMhydO//2xlXZXfGtYtBJ0qk9LpnLQ==", + "dev": true, + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/kebab-case": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/kebab-case/-/kebab-case-2.0.0.tgz", + "integrity": "sha512-JjPgCJ5SRbbiKboK1U+k37NY8Hb42hHzMphczPtYPGGRNiasMV7T4RkbKlvUo+Qcm0hE6G8ct9VnEsfSsEbjoQ==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "change-case": "^4.1.2" + }, + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/keyboard-shortcuts": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/keyboard-shortcuts/-/keyboard-shortcuts-6.0.0.tgz", + "integrity": "sha512-6OkdL+ZFJ+0llQtiDkP5OifzKvX7NMJpwsbBssm9vfNa93okyLOWY3j8HzUyKQLnuSrO8z+SwBzSJjImUHorTg==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/data": "^11.0.0", + "@wordpress/element": "^10.0.0", + "@wordpress/keycodes": "^5.0.0" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "@types/react": "^18 || ^19", + "@types/react-dom": "^18 || ^19", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@wordpress/keycodes": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/keycodes/-/keycodes-5.0.0.tgz", + "integrity": "sha512-15T931kJjj90KlDcsXKaGucK+iyqY9CftR7tXO60m8T8gDC2GVuUWMIX3Sq9+4fsahABuqoUd+QTiW4xSDlpCg==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/i18n": "^7.0.0" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "@types/react": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@wordpress/notices": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/notices/-/notices-6.0.0.tgz", + "integrity": "sha512-IDiPfOgUvswr1aXVm3SwOziTtZY+R8t0CfO0jHzK1M50ddzRRTReZPM1bPftU23PAW5k1UqxPc/aYRwLK4Xopg==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/a11y": "^5.0.0", + "@wordpress/components": "^43.0.0", + "@wordpress/compose": "^9.0.0", + "@wordpress/data": "^11.0.0", + "@wordpress/deprecated": "^5.0.0", + "@wordpress/element": "^10.0.0", + "@wordpress/i18n": "^7.0.0", + "@wordpress/private-apis": "^3.0.0", + "@wordpress/ui": "^0.25.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "@types/react": "^18 || ^19", + "@types/react-dom": "^18 || ^19", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@wordpress/npm-package-json-lint-config": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/npm-package-json-lint-config/-/npm-package-json-lint-config-6.0.0.tgz", + "integrity": "sha512-h0VN1RW3OqsreiFrJ13FD/jFX9b+VHerfWNED/OUUSi17pmVgik47niTfth1/xpotL+kz2RuBopb9oV+HpKcQQ==", + "dev": true, + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "npm-package-json-lint": ">=6.0.0" + } + }, + "node_modules/@wordpress/postcss-plugins-preset": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/postcss-plugins-preset/-/postcss-plugins-preset-6.0.0.tgz", + "integrity": "sha512-Z8PLYiY32e0k7xQz0Fc7K30PcOIFvaoc9JduPMQMCfmukJKFMaRYxqyJZNdva7fPu18vUl9dFpB6ZP/+C5aZYQ==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/browserslist-config": "^7.0.0", + "autoprefixer": "^10.4.21", + "postcss-import": "^16.1.1" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/@wordpress/preferences": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/preferences/-/preferences-5.0.0.tgz", + "integrity": "sha512-rQaSz2di9k3tzm9BkO26ikydgevBDf+QU9ET97rFjlSaoDqQ1YqOLcFH61o8vtthr3NjQFBq/XAz38VMCVVsXw==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/a11y": "^5.0.0", + "@wordpress/base-styles": "^14.0.0", + "@wordpress/components": "^43.0.0", + "@wordpress/compose": "^9.0.0", + "@wordpress/data": "^11.0.0", + "@wordpress/deprecated": "^5.0.0", + "@wordpress/element": "^10.0.0", + "@wordpress/i18n": "^7.0.0", + "@wordpress/icons": "^19.0.0", + "@wordpress/private-apis": "^3.0.0" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "@types/react": "^18 || ^19", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@wordpress/prettier-config": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/prettier-config/-/prettier-config-5.0.0.tgz", + "integrity": "sha512-uQ3rtrqt8yvUHFN9Xcxuw7EfSw4ArMkwFxtz2RHZM2Sis9jzB7xk0FcK/iWE55q0WWSjmFGWoNVpOEQoSE7ocQ==", + "dev": true, + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "prettier": ">=3" + } + }, + "node_modules/@wordpress/primitives": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/primitives/-/primitives-5.0.0.tgz", + "integrity": "sha512-XrRJIbNXai/jlL9FLEANUzaAigTdN8XgWzAHTfAjDskvy3X6CjUbMyjXLWE5OhyCKhq+JAd3mWb9zq4sgl+V2w==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/element": "^10.0.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "@types/react": "^18 || ^19", + "@types/react-dom": "^18 || ^19", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@wordpress/priority-queue": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/priority-queue/-/priority-queue-4.0.0.tgz", + "integrity": "sha512-YXEwn7xtr4bUts2IOPREjlWYa3n/ba7ndLCqFN3ofeRvvr2ZVCI1WP20v7a+CQZi+n9FS7fy+lYqx3wnTYtGzg==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "requestidlecallback": "^0.3.0" + }, + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/private-apis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/private-apis/-/private-apis-3.0.0.tgz", + "integrity": "sha512-IDXGi2VWfjUFwBTDtWj7/FMY240JpqraxFEIaXIeD3KFumX8lQuAYvE9gU8GcNBt/NlXZCDEmNioxCbAZN+jiA==", + "dev": true, + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/react-inert-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/react-inert-value/-/react-inert-value-2.0.0.tgz", + "integrity": "sha512-inwoViBZ2K3ubTvfjkvpzwpXovZVF05CMWZ08scQt+ttYHD1Sf3pL3gn11iAxcR1Wf/0TzZoPeLc+PhnAUzuSg==", + "dev": true, + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "@types/react": "^18 || ^19", + "react": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@wordpress/redux-routine": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/redux-routine/-/redux-routine-6.0.0.tgz", + "integrity": "sha512-mJm9DeHXFIPeaCA6E+VYupWXBW7g1QuKqEgBclm3HCDN+Th6OO+HMLfa2eS+dAPyoTu2NhSF0QlY1ArijOzuiw==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "is-plain-object": "^5.1.0", + "is-promise": "^4.0.0", + "rungen": "^0.3.2" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "redux": ">=4" + } + }, + "node_modules/@wordpress/rich-text": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/rich-text/-/rich-text-8.0.0.tgz", + "integrity": "sha512-2Jg50SJwE82+qO8tnQbIhSqCIb9I+rW6bi0hRVcTMOtRxC8wBisw5n0zngLfGRSHtAA9irnEzrOfPk2m6LtyuQ==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/a11y": "^5.0.0", + "@wordpress/compose": "^9.0.0", + "@wordpress/data": "^11.0.0", + "@wordpress/deprecated": "^5.0.0", + "@wordpress/dom": "^5.0.0", + "@wordpress/element": "^10.0.0", + "@wordpress/escape-html": "^4.0.0", + "@wordpress/i18n": "^7.0.0", + "@wordpress/keycodes": "^5.0.0", + "@wordpress/private-apis": "^3.0.0", + "colord": "^2.9.4" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "@types/react": "^18 || ^19", + "@types/react-dom": "^18 || ^19", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@wordpress/scripts": { + "version": "37.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/scripts/-/scripts-37.0.0.tgz", + "integrity": "sha512-/a01kTH8lqAPzO6AhZRBiFpWcDorZs32U07YdkNhV7UOQAhWrBAWBOXPsLBcCh7NRt603C/hYOea8YcIqPpbZA==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@babel/core": "^7.25.7", + "@pmmmwh/react-refresh-webpack-plugin": "^0.6.3", + "@svgr/webpack": "^8.0.1", + "@wordpress/babel-preset-default": "^10.0.0", + "@wordpress/browserslist-config": "^7.0.0", + "@wordpress/dependency-extraction-webpack-plugin": "^7.0.0", + "@wordpress/e2e-test-utils-playwright": "^3.1.0", + "@wordpress/eslint-plugin": "^28.0.0", + "@wordpress/npm-package-json-lint-config": "^6.0.0", + "@wordpress/postcss-plugins-preset": "^6.0.0", + "@wordpress/prettier-config": "^5.0.0", + "@wordpress/stylelint-config": "^27.0.0", + "adm-zip": "^0.6.1", + "babel-loader": "^9.2.1", + "browserslist": "^4.28.7", + "chalk": "^4.1.1", + "check-node-version": "^4.1.0", + "copy-webpack-plugin": "^10.2.0", + "cross-spawn": "^7.0.6", + "css-loader": "^6.2.0", + "cssnano": "^6.0.1", + "dir-glob": "^3.0.1", + "eslint": "^10.0.0", + "fast-glob": "^3.2.7", + "json2php": "^0.0.9", + "markdownlint-cli": "^0.49.1", + "mini-css-extract-plugin": "^2.9.2", + "minimist": "^1.2.0", + "npm-package-json-lint": "^6.4.0", + "npm-packlist": "^3.0.0", + "postcss": "^8.4.38", + "postcss-loader": "^6.2.1", + "prettier": "npm:wp-prettier@^3.9.6", + "react-refresh": "^0.14.0", + "read-pkg-up": "^7.0.1", + "resolve-bin": "^1.0.1", + "rtlcss": "^4.3.0", + "sass": "^1.54.0", + "sass-loader": "^16.0.3", + "schema-utils": "^4.2.0", + "source-map-loader": "^3.0.0", + "stylelint": "^17.14.1", + "stylelint-scss": "^7.2.0", + "terser-webpack-plugin": "^5.3.10", + "url-loader": "^4.1.1", + "webpack": "^5.111.0", + "webpack-bundle-analyzer": "^4.9.1", + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^6.0.0" + }, + "bin": { + "wp-scripts": "bin/wp-scripts.js" + }, + "engines": { + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" + }, + "peerDependencies": { + "@playwright/test": ">=1", + "@wordpress/env": ">=10.0.0", + "jest": "^30", + "react": "^18 || ^19", + "react-dom": "^18 || ^19", + "vite": "^7 || ^8", + "vitest": "^5" + }, + "peerDependenciesMeta": { + "@playwright/test": { + "optional": true + }, + "@wordpress/env": { + "optional": true + }, + "jest": { + "optional": true + }, + "vite": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/@wordpress/scripts/node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@wordpress/scripts/node_modules/@eslint/config-helpers": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", + "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@wordpress/scripts/node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@wordpress/scripts/node_modules/@eslint/plugin-kit": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.3.tgz", + "integrity": "sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@wordpress/scripts/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@wordpress/scripts/node_modules/eslint": { + "version": "10.13.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.13.0.tgz", + "integrity": "sha512-UohDjD4zSzEwhf58M3O7vc/ycc8O1I7dNlhtD0WxE8Px8EHNJKtUwzBqrzeGhisRuv2ag4sAYswszCJRgKXYUg==", + "dev": true, + "license": "MIT", + "workspaces": [ + "packages/*" + ], + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.7.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.3", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "11.1.5 || >11.1.6 <12", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/@wordpress/scripts/node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@wordpress/scripts/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@wordpress/scripts/node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@wordpress/scripts/node_modules/file-entry-cache": { + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.5.tgz", + "integrity": "sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^6.1.23" + } + }, + "node_modules/@wordpress/scripts/node_modules/flat-cache": { + "version": "6.1.23", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.23.tgz", + "integrity": "sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cacheable": "^2.5.0", + "flatted": "^3.4.2", + "hookified": "^1.15.0" + } + }, + "node_modules/@wordpress/scripts/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@wordpress/shortcode": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/shortcode/-/shortcode-5.0.0.tgz", + "integrity": "sha512-anwstb/SfweKgw8BNEouvzwCII9AAY7I/asPZJf084rEtG/5V2II9ls0PdfamYuDedNvFdToCV3uiK9NFD06jw==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "memize": "^2.1.1" + }, + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/style-engine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/style-engine/-/style-engine-3.0.0.tgz", + "integrity": "sha512-obU8yuXphfCZVdcv7bVf/dn6AlzBqYfglyyc/ymqEyjmh4cTY0pF/Do1sraT/XP5n+NZrFGV5D9wcJILTNC3Lg==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "change-case": "^4.1.2" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "@types/react": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@wordpress/style-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@wordpress/style-runtime/-/style-runtime-0.14.0.tgz", + "integrity": "sha512-cWk3G7Pe6Wo9uMpaPAbrhsPJAmkShPn3LLc8vkte1ZQ7ls1M+e6m78KQxd0HYy4ty2nbGGlhnHE7ISxQzGhrsQ==", + "dev": true, + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/stylelint-config": { + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/stylelint-config/-/stylelint-config-27.0.0.tgz", + "integrity": "sha512-NvLTijt+fZR2/0T3EwH5cswLdpuo/On1B+BQlkkuQ3HsNrwWLTEvwboaOqzJUwFs/owB9+AO11Ocaayexesalw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@stylistic/stylelint-plugin": "^5.2.1", + "@wordpress/theme": "^3.0.0", + "stylelint-config-recommended": "^18.0.0", + "stylelint-config-recommended-scss": "^17.0.1" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "stylelint": "^17.14.1", + "stylelint-scss": "^7.2.0" + } + }, + "node_modules/@wordpress/theme": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/theme/-/theme-3.0.0.tgz", + "integrity": "sha512-m25ORtLD77WL40vG3Vt/0WKzg/liuiM2PGMNaDv4SDO1hZ7H0t+Lp8rfTlsYnBdI+iO8+juICyK4OiVRde1I0Q==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@babel/parser": "^7.28.6", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "@jridgewell/trace-mapping": "^0.3.31", + "@wordpress/compose": "^9.0.0", + "@wordpress/deprecated": "^5.0.0", + "@wordpress/element": "^10.0.0", + "@wordpress/private-apis": "^3.0.0", + "@wordpress/style-runtime": "^0.14.0", + "colorjs.io": "^0.7.1", + "magic-string": "^0.30.21", + "memize": "^2.1.1" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "@types/react": "^18 || ^19", + "esbuild": ">=0.27.2 <1.0.0", + "lightningcss": ">=1.33.0 <2.0.0", + "postcss": "^8.0.0", + "react": "^18 || ^19", + "react-dom": "^18 || ^19", + "stylelint": "^16 || ^17", + "vite": "^7 || ^8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "postcss": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "stylelint": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@wordpress/token-list": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/token-list/-/token-list-4.0.0.tgz", + "integrity": "sha512-W8WY1B4POIXLQcWM/N3Zz9TGMMqtfJvS2VcIql889eEFKk1trx0ynsbzVFO/UblGiSjAdxpANES9yHNKa43VWw==", + "dev": true, + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/ui": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@wordpress/ui/-/ui-0.25.0.tgz", + "integrity": "sha512-G/aUYgQpL18q3DfyV/1ebB8tEjXBubWPPXitcs4IvXYbAAFNBLsoUUGv5K3sfqJuxhi51lbyKT0Eap6Wj0kkCQ==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@base-ui/react": "^1.9.0", + "@daypicker/react": "^10.0.1", + "@wordpress/a11y": "^5.0.0", + "@wordpress/compose": "^9.0.0", + "@wordpress/element": "^10.0.0", + "@wordpress/i18n": "^7.0.0", + "@wordpress/icons": "^19.0.0", + "@wordpress/keycodes": "^5.0.0", + "@wordpress/primitives": "^5.0.0", + "@wordpress/private-apis": "^3.0.0", + "@wordpress/react-inert-value": "^2.0.0", + "@wordpress/style-runtime": "^0.14.0", + "@wordpress/theme": "^3.0.0", + "@wordpress/warning": "^4.0.0", + "clsx": "^2.1.1", + "date-fns": "^4.4.0", + "tabbable": "^6.4.0" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "@types/react": "^18 || ^19", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@wordpress/undo-manager": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/undo-manager/-/undo-manager-2.0.0.tgz", + "integrity": "sha512-OQfGPUBz79/NKkkBvF5L/k1DmxuCQGYvczA/BOef+ECCCnjpIXF6HS1txpULozI5owejQDDjhZp25bq25jF5Vg==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/is-shallow-equal": "^6.0.0" + }, + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/upload-media": { + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/@wordpress/upload-media/-/upload-media-0.43.0.tgz", + "integrity": "sha512-Cgi0XNoWurFFbCJPljPaii2cy7hHwMml3Dbf9zhlUsyihT3H9LfkSR/Oox+NHFPOqAPv1q2YPWCGuZe3mXDZEQ==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/blob": "^5.0.0", + "@wordpress/compose": "^9.0.0", + "@wordpress/data": "^11.0.0", + "@wordpress/element": "^10.0.0", + "@wordpress/i18n": "^7.0.0", + "@wordpress/private-apis": "^3.0.0", + "@wordpress/video-conversion": "^0.9.0", + "@wordpress/vips": "^5.0.0", + "uuid": "^14.0.0" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "@types/react": "^18 || ^19", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@wordpress/url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/url/-/url-5.0.0.tgz", + "integrity": "sha512-oPDn3vjO7unimaiFSnwoP8Ys6TVGp3UXwF0CwRDMoYLqRIjDuPpZLoUbkPAnWnLOrNP+GjIzCm7qQNCrze/UAA==", + "dev": true, + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/video-conversion": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@wordpress/video-conversion/-/video-conversion-0.9.0.tgz", + "integrity": "sha512-fxU5BDB809r/AbxM70LUst3CjWpn85ugT6QebwugOlX+s7aUQ7/HZ5ON0XZE4V2nPtOeUsT1HyVDcikcbaMZbQ==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/worker-threads": "^2.0.0", + "mediabunny": "^1.45.2" + }, + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/vips": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/vips/-/vips-5.0.0.tgz", + "integrity": "sha512-7he8zRvtoaI+uGCoQ6Tf5+PbuhB0OowB0h8H4HLB7yv8hl4YOxnGG1tB1ldB6gypNyukcawk+aK/CcJ2+cnI2A==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/worker-threads": "^2.0.0", + "wasm-vips": "^0.0.18" + }, + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/warning": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/warning/-/warning-4.0.0.tgz", + "integrity": "sha512-qhAwfjMUYT1TfyMlrkO4i1IbcqfjfjLBFepXSRvMUhNbL8CRRu3cgjdZNCf82FkvUwmuCEuMsrmGisxu4CGr1A==", + "dev": true, + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wordpress/worker-threads": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/worker-threads/-/worker-threads-2.0.0.tgz", + "integrity": "sha512-ytIahXlVLdMMDfdDvOl1X4GrzVRcxc4JB+41zWJFdkNnzxDe6xbuyKujNe6kwrWFvr2YfC41wXfPViHEzB96pw==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "comctx": "^1.4.3" + }, + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@wp-playground/blueprints": { + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@wp-playground/blueprints/-/blueprints-3.1.60.tgz", + "integrity": "sha512-z35HVBZTdN8mV9XDzbu9tnGHp7q93/sB+QAmNA8KRB5zISXqRxtqRJJTWcoonXNJTdv/mXWNLfqKj07Lj12Hvw==", + "dev": true, + "dependencies": { + "@php-wasm/logger": "3.1.60", + "@php-wasm/progress": "3.1.60", + "@php-wasm/stream-compression": "3.1.60", + "@php-wasm/universal": "3.1.60", + "@php-wasm/util": "3.1.60", + "@php-wasm/web-service-worker": "3.1.60", + "@wp-playground/common": "3.1.60", + "@wp-playground/storage": "3.1.60", + "@wp-playground/wordpress": "3.1.60", + "ajv": "8.18.0" + }, + "engines": { + "node": ">=24.18.0", + "npm": ">=11.16.0" + } + }, + "node_modules/@wp-playground/cli": { + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@wp-playground/cli/-/cli-3.1.60.tgz", + "integrity": "sha512-2xv0yf84c6j9dypY22nlQSn2p1YRiJLsChopAiisfZWeZttZ0ZcjO7SSq45jD/GuqG14fxwbuz/LuCAfXtbgiA==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/cli-util": "3.1.60", + "@php-wasm/logger": "3.1.60", + "@php-wasm/node": "3.1.60", + "@php-wasm/progress": "3.1.60", + "@php-wasm/universal": "3.1.60", + "@php-wasm/util": "3.1.60", + "@php-wasm/xdebug-bridge": "3.1.60", + "@wp-playground/blueprints": "3.1.60", + "@wp-playground/common": "3.1.60", + "@wp-playground/storage": "3.1.60", + "@wp-playground/tools": "3.1.60", + "@wp-playground/wordpress": "3.1.60", + "express": "4.22.2", + "fs-extra": "11.1.1", + "tmp-promise": "3.0.3", + "wasm-feature-detect": "1.8.0", + "yargs": "17.7.2" + }, + "bin": { + "wp-playground-cli": "wp-playground.js" + } + }, + "node_modules/@wp-playground/cli/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@wp-playground/cli/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wp-playground/cli/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@wp-playground/common": { + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@wp-playground/common/-/common-3.1.60.tgz", + "integrity": "sha512-zLxeAHs4CdmyYD7IqhqEjOwOM4Ubv5psvG8mirkOTVNTcOvIHeW1K9eo3gU4OEfk9lxIUDvORATF8oeiHt1dMA==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/universal": "3.1.60", + "@php-wasm/util": "3.1.60" + }, + "engines": { + "node": ">=24.18.0", + "npm": ">=11.16.0" + } + }, + "node_modules/@wp-playground/storage": { + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@wp-playground/storage/-/storage-3.1.60.tgz", + "integrity": "sha512-wYFMXxfkExFkfYSO6AszGxJL/7rKnZLSAj7xJA0FF3dbpy4PSCR8KcBKnpIz8kq+ihaN6CXC1dyse9+Go4JCZw==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/stream-compression": "3.1.60", + "@php-wasm/universal": "3.1.60", + "@php-wasm/util": "3.1.60", + "@zip.js/zip.js": "2.7.57", + "isomorphic-git": "1.37.6", + "octokit": "3.1.2", + "pako": "^1.0.10", + "sha.js": "2.4.12" + } + }, + "node_modules/@wp-playground/tools": { + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@wp-playground/tools/-/tools-3.1.60.tgz", + "integrity": "sha512-2cr48TGs0gWb1ErzEaLNXfoknH3pZ+L1USSWUroDwvL1ySwsyjM9Dgfj9ibPSNBJU2PrnUy3Dak7MsRz4FPLbQ==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/node": "3.1.60", + "@php-wasm/universal": "3.1.60", + "@php-wasm/util": "3.1.60", + "@wp-playground/blueprints": "3.1.60", + "@wp-playground/common": "3.1.60", + "@wp-playground/wordpress": "3.1.60" + }, + "engines": { + "node": ">=24.18.0", + "npm": ">=11.16.0" + } + }, + "node_modules/@wp-playground/wordpress": { + "version": "3.1.60", + "resolved": "https://registry.npmjs.org/@wp-playground/wordpress/-/wordpress-3.1.60.tgz", + "integrity": "sha512-KlOIUcRsPaw+xQ3KSXIfZRzouvs0Fd9zxVfbTpMsRvcw9stUoTQCn7P1QhDYEKRB9l2UzDjbXptz8GLB1IWG5A==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/logger": "3.1.60", + "@php-wasm/universal": "3.1.60", + "@php-wasm/util": "3.1.60", + "@wp-playground/common": "3.1.60", + "zstddec": "^0.2.0" + }, + "engines": { + "node": ">=24.18.0", + "npm": ">=11.16.0" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@zip.js/zip.js": { + "version": "2.7.57", + "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.57.tgz", + "integrity": "sha512-BtonQ1/jDnGiMed6OkV6rZYW78gLmLswkHOzyMrMb+CAR7CZO8phOHO6c2qw6qb1g1betN7kwEHhhZk30dv+NA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "bun": ">=0.7.0", + "deno": ">=1.0.0", + "node": ">=16.5.0" + } + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.19.0.tgz", + "integrity": "sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/adm-zip": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.6.1.tgz", + "integrity": "sha512-Xwrja8nx9e5o2N1my4DsKCeKpdrnACyr1wtbPxBDgGzKzKyE9kRtBFA8mWldI+RVlD7CBZNWY/wQ2+ydwOR6kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": ">=5.0.0" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/anser": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/anser/-/anser-2.3.5.tgz", + "integrity": "sha512-vcZjxvvVoxTeR5XBNJB38oTu/7eDCZlwdz32N1eNgpyPF7j/Z7Idf+CUwQOkKKpJ7RJyjxgLHCM7vdIK0iCNMQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anynum": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz", + "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/are-docs-informative": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.1.1.tgz", + "integrity": "sha512-sqRsNQBwbKLRX0jV5Cu5uzmtflf892n4Vukz7T659ebL4pz3mpOqCMU7lxMoBTFwnp10E3YB5ZcyHM41W5bcDA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/array-includes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.2.0.tgz", + "integrity": "sha512-VXY5eFRarnXcYxwBjJzPmEhH55+rmP79/+ueDhi0F+TuqfHCItagIHqxeUZrmgrOPa31QTh9H85DjX3FfJ0FTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-object-atoms": "^1.1.2", + "es-shim-unscopables": "^1.1.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", + "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asn1js": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.1.0.tgz", + "integrity": "sha512-HWYi1THkOqguCiO+vwj09ZkQFD2kKEsejYHAYCtX0D80WVdoDnvRc9IukXX/jeb0T9NRWJnRtqwdbHltnFjwXA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "pvtsutils": "^1.3.6", + "pvutils": "^1.2.0", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/async-lock": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/atomically": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/atomically/-/atomically-2.1.1.tgz", + "integrity": "sha512-P4w9o2dqARji6P7MHprklbfiArZAWvo07yW7qs3pdljb3BWr12FIB7W+p0zJiuiVsUpRO0iZn1kFFcpPegg0tQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "stubborn-fs": "^2.0.0", + "when-exit": "^2.1.4" + } + }, + "node_modules/autoprefixer": { + "version": "10.6.1", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.6.1.tgz", + "integrity": "sha512-cL1Qz6ADZhcEbny/8HPfe99J6HhNoYtpX2LFLIbhgGE7Q1hlQVkYFdetDN7Id3KiQxhDrHwzlHr/YQCnZ8+xSA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.9", + "caniuse-lite": "^1.0.30001810", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.14.0.tgz", + "integrity": "sha512-9WTZxEjsZ7b13TH8JPmbV2z8CHbl80/2hm3XPEG4JgNdQLK81IBRXmSxHfMAOkSqQeRxT/0dwNDz2GOm3zzpcQ==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/babel-loader": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz", + "integrity": "sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-cache-dir": "^4.0.0", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0", + "webpack": ">=5" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", + "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-define-polyfill-provider": "^0.6.8", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz", + "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.8", + "core-js-compat": "^3.48.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", + "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.8" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.32", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.32.tgz", + "integrity": "sha512-UvuxFU9P3hM29LrQdwu0cD6w6/+SFr+UlB9s/knI7+O2rK9xFxKUwSDWxGdbbqQ9/V7BJ5s0NI5nQAE4uJp3gQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true, + "license": "MIT" + }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/body-parser": { + "version": "1.20.9", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.9.tgz", + "integrity": "sha512-FISIQxV5u91Y9gvuFq3q/tA/qTqLJn3AhRcEvyozyhJTh5OBFFtv827AdHU7Rbyct4r+Ba1/99oFa1Y3yuFsdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.16.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.1.tgz", + "integrity": "sha512-5Px6J7rfHBAGUhNQ3es4YzcOdBBw0SDcVtThJml4RdnKw4tKs2epA0kPZ+K8X+WiHktT93RHVcQ/3ym5aTCgtg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/bonjour-service": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.4.4.tgz", + "integrity": "sha512-jCZcVv7eoc4QesRscwEZtSROBen+6LpKAmBIsQYQrsAeVHLyMXWX/t6eIV5KiRZYNUBl8eVqImEEMQ8L5+c/Kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/bottleneck": { + "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.29.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.4.tgz", + "integrity": "sha512-2TcbbiwXEq+N0kD36tykpCfyyQigUXmuwNQbnPp4Xk4PX9JOqVIKjJlIDjpH7uY6tmRGoAd+apeT1p60T8wsdA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.11.29", + "caniuse-lite": "^1.0.30001816", + "electron-to-chromium": "^1.5.459", + "node-releases": "^2.0.59", + "update-browserslist-db": "^1.3.4" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/btoa-lite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", + "integrity": "sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA==", + "dev": true, + "license": "MIT" + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bundle-name": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.1.tgz", + "integrity": "sha512-DdH81/zPLVS11EUgWq3tEu/xn+EzljlMYooDNdzWEnFha3R3NBMpMV1UqYIpjYHV/SgpFKMIX1Oh7o06SjM/oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/bytestreamjs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.1.tgz", + "integrity": "sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/cacheable": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.5.0.tgz", + "integrity": "sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cacheable/memory": "^2.2.0", + "@cacheable/utils": "^2.5.0", + "hookified": "^1.15.0", + "keyv": "^5.6.0", + "qified": "^0.10.1" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable/node_modules/keyv": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@keyv/serialize": "^1.1.1" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-keys/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys/node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001819", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001819.tgz", + "integrity": "sha512-LFUqIIAwpzsIuevnKnsR9qnStBcgzPShTX61lBwN9uFkkf8o8zLiebEjbW8AYtZiF+1YKDWfdt/pWDtMuG8Xwg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/capital-case": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", + "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/change-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", + "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "camel-case": "^4.1.2", + "capital-case": "^1.0.4", + "constant-case": "^3.0.4", + "dot-case": "^3.0.4", + "header-case": "^2.0.4", + "no-case": "^3.0.4", + "param-case": "^3.0.4", + "pascal-case": "^3.1.2", + "path-case": "^3.0.4", + "sentence-case": "^3.0.4", + "snake-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chardet": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.2.0.tgz", + "integrity": "sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==", + "dev": true, + "license": "MIT" + }, + "node_modules/check-node-version": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/check-node-version/-/check-node-version-4.2.1.tgz", + "integrity": "sha512-YYmFYHV/X7kSJhuN/QYHUu998n/TRuDe8UenM3+m5NrkiH670lb9ILqHIvBencvJc4SDh+XcbXMR4b+TtubJiw==", + "dev": true, + "license": "Unlicense", + "dependencies": { + "chalk": "^3.0.0", + "map-values": "^1.0.1", + "minimist": "^1.2.0", + "object-filter": "^1.0.2", + "run-parallel": "^1.1.4", + "semver": "^6.3.0" + }, + "bin": { + "check-node-version": "bin.js" + }, + "engines": { + "node": ">=8.3.0" + } + }, + "node_modules/check-node-version/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/check-node-version/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chrome-launcher": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-1.2.2.tgz", + "integrity": "sha512-sI90u5qf3854bTrWsQMMdsQfSjjMbeSn7QtxVb9zrikf+QgMfHQ71KepHW7ZfixQKrDlAFKb+qnVlSR+xepBzw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^2.0.1" + }, + "bin": { + "print-chrome-path": "bin/print-chrome-path.cjs" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/chromium-bidi": { + "version": "157.0.8090-0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-157.0.8090-0.tgz", + "integrity": "sha512-KJw7IQgqXwxpJgJlEIPg5mr2ZoqA+jkTRtc5EABI7sUJ3bu3cpI7NuEBoj4Gn1AsF78CaT9So5WQWspK3/wYag==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "mitt": "^3.0.1", + "zod": "^4.6.5" + }, + "engines": { + "node": ">=20.19.0 <22.0.0 || >=22.12.0" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, + "node_modules/cjs-module-lexer": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.3.0.tgz", + "integrity": "sha512-lmNyBzi6iYiqyrExTctfmjd8tInx+dHu9PBomTfH5RXS53c27JMFJgzNv5TyAnm3PgGf+MLy0+Ws41/HlVxdKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/clean-git-ref": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/clean-git-ref/-/clean-git-ref-2.0.1.tgz", + "integrity": "sha512-bLSptAy2P0s6hU4PzuIMKmMJJSE6gLXGH1cntDu7bWJUksvuM+7ReOK61mozULErYvP6a15rnYl0zFDef+pyPw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-deep/node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cmdk": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz", + "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "^1.1.1", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-id": "^1.1.0", + "@radix-ui/react-primitive": "^2.0.2" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colord": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.10.0.tgz", + "integrity": "sha512-AidJptpBJmjTclAp9BkLwJi0T93fo5epJnbaZslpg6QVzpHjAiveF55mE9AcUJiGMqRHgMDY8soMsQtuNYMHfw==", + "dev": true, + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/colorjs.io": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.7.1.tgz", + "integrity": "sha512-LY7OHnJZxHwT5UlzNa9bbhHHDbzB6yE5+3MIPwJEQKRvSCt/T4G7epsj+9j2BExUIIfXFIJGsIXUKdfrK9Q5tA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/color" + } + }, + "node_modules/comctx": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/comctx/-/comctx-1.7.6.tgz", + "integrity": "sha512-rDhDqh6qfmivZfVOGi4qs8RB3PnVtIAB5zLsWu4JqCoQ1p4IEJSB5pW2JvOhFwCMmCLiei9qpaIMxmVVQRhzyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/comment-parser": { + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.9.tgz", + "integrity": "sha512-+2AvZKjJaNq9GQljm3tr0utwrig5BL+jgJGvz5rDpGKNIp+ojcRXWUwBbh/sGIi1QCprR6PsKFakub+w1v+4hQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", + "dev": true, + "license": "ISC" + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.2.tgz", + "integrity": "sha512-o8vI5RE5A6EVVOd9o41jKp41aJom+QTEO/Bx8MYNjexMo/Bv2WOjUfZr+aL0WnYSgymUy6zeguqLTsIhV0gMvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "destroy": "1.2.0", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/compression/node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/configstore": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-7.1.0.tgz", + "integrity": "sha512-N4oog6YJWbR9kGyXvS7jEykLDXIE2C0ILYqNBZBp9iwiJpoCBWYsuAdW6PPFn6w06jjnC+3JstVvWHO4cZqvRg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "atomically": "^2.0.3", + "dot-prop": "^9.0.0", + "graceful-fs": "^4.2.11", + "xdg-basedir": "^5.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/constant-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", + "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case": "^2.0.2" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/copy-dir/-/copy-dir-1.3.0.tgz", + "integrity": "sha512-Q4+qBFnN4bwGwvtXXzbp4P/4iNk0MaiGAzvQ8OiMtlLjkIKjmNN689uVzShSM0908q7GoFHXIPx4zi75ocoaHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-webpack-plugin": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-10.2.4.tgz", + "integrity": "sha512-xFVltahqlsRcyyJqQbDY6EYTtyQZF9rf+JPjwHObLdPFMEISqkFkr7mFoVOC6BfYS/dNThyoQKvziugm+OnwBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "^3.2.7", + "glob-parent": "^6.0.1", + "globby": "^12.0.2", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 12.20.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/core-js": { + "version": "3.51.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.51.0.tgz", + "integrity": "sha512-7bDKx/5jm5MxzsXxc93g6Z2+4ZsuT/MddrfRmTDJNAkWsYWHU8EVNEsBiIEWnMAMCHMmRfUEjkuo3jeNBOIHRw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.51.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.51.0.tgz", + "integrity": "sha512-52r6fkXgxv4GLMjmFcMWRx7X9bw071xTTdrDcdyPGRqU2oE+9pCG0bAOMifSBdLw0JRBZKEF0q3B55qPlLuE8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.29.3" + }, + "engines": { + "node": ">=6.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-pure": { + "version": "3.51.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.51.0.tgz", + "integrity": "sha512-NE4SFDJ+qzQeqHG2vVSNwopzS6zZNPYJ5kinIqs+OofTqGzOmICgT89E/yvS9XY7rwb3eU1utWXTJ9nVFqv74A==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csp_evaluator": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/csp_evaluator/-/csp_evaluator-1.1.8.tgz", + "integrity": "sha512-EwOnfYuNbTytvbMKsLixTrRgnjOa0WZCxGy8A9nnSYAicrdwn+T/epU/yjgymmOxlgKnvH+8wXt+7p/8ak5Feg==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/css-declaration-sorter": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.4.0.tgz", + "integrity": "sha512-LTuzjPoyA2vMGKKcaOqKSp7Ub2eGrNfKiZH4LpezxpNrsICGCSFvsQOI29psISxNZtaXibkC2CXzrQ5enMeGGw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-functions-list": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.3.3.tgz", + "integrity": "sha512-8HFEBPKhOpJPEPu70wJJetjKta86Gw9+CCyCnB3sui2qQfOvRyqBy4IKLKKAwdMpWb2lHXWk9Wb4Z6AmaUT1Pg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/css-loader": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", + "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz", + "integrity": "sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssnano-preset-default": "^6.1.2", + "lilconfig": "^3.1.1" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/cssnano-preset-default": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz", + "integrity": "sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.0", + "css-declaration-sorter": "^7.2.0", + "cssnano-utils": "^4.0.2", + "postcss-calc": "^9.0.1", + "postcss-colormin": "^6.1.0", + "postcss-convert-values": "^6.1.0", + "postcss-discard-comments": "^6.0.2", + "postcss-discard-duplicates": "^6.0.3", + "postcss-discard-empty": "^6.0.3", + "postcss-discard-overridden": "^6.0.2", + "postcss-merge-longhand": "^6.0.5", + "postcss-merge-rules": "^6.1.1", + "postcss-minify-font-values": "^6.1.0", + "postcss-minify-gradients": "^6.0.3", + "postcss-minify-params": "^6.1.0", + "postcss-minify-selectors": "^6.0.4", + "postcss-normalize-charset": "^6.0.2", + "postcss-normalize-display-values": "^6.0.2", + "postcss-normalize-positions": "^6.0.2", + "postcss-normalize-repeat-style": "^6.0.2", + "postcss-normalize-string": "^6.0.2", + "postcss-normalize-timing-functions": "^6.0.2", + "postcss-normalize-unicode": "^6.1.0", + "postcss-normalize-url": "^6.0.2", + "postcss-normalize-whitespace": "^6.0.2", + "postcss-ordered-values": "^6.0.2", + "postcss-reduce-initial": "^6.1.0", + "postcss-reduce-transforms": "^6.0.2", + "postcss-svgo": "^6.0.3", + "postcss-unique-selectors": "^6.0.4" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/cssnano-utils": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz", + "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/date-fns": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.4.0.tgz", + "integrity": "sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-browser": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz", + "integrity": "sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/devtools-protocol": { + "version": "0.0.1687809", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1687809.tgz", + "integrity": "sha512-t7kSb+UKYdyxcmqy98U2P86ne6rCve3DIJ4+lp54i/hhClgFuG35JrLj23rO14Jw3sWDsp7i+22QhNU0+/qk8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/diff": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff3": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/diff3/-/diff3-0.0.3.tgz", + "integrity": "sha512-iSq8ngPOt0K53A6eVr4d5Kn6GNrM2nQZtC740pzIriHtn4pOQ2lyzEXQMBeVcWERN0ye7fhBsk9PbLLQOnUx/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/docker-compose": { + "version": "0.24.8", + "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.24.8.tgz", + "integrity": "sha512-plizRs/Vf15H+GCVxq2EUvyPK7ei9b/cVesHvjnX4xaXjM9spHe2Ytq0BitndFgvTJ3E3NljPNUEl7BAN43iZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "yaml": "^2.2.2" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/docker-compose/node_modules/yaml": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", + "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dot-prop": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz", + "integrity": "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^4.18.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true, + "license": "MIT" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.464", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.464.tgz", + "integrity": "sha512-nRa65aFge40viPAKxMKULIY8tcXKwKboJWNn4RoKwyWXJ6SP7BnrDGKUTzc64818jIciH0PK7LSF8QzZes19GA==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.14.tgz", + "integrity": "sha512-pqH+jIxIhwX5U4w+Tnu3jjowTo5QTACbqef2C5Cw2RhApr7COQqgnai53u8VbtqjWRcSoAcTf7uZ62dO5kC8Iw==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.7.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.26.0.tgz", + "integrity": "sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/envinfo": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.21.0.tgz", + "integrity": "sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==", + "dev": true, + "license": "MIT", + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/equivalent-key-map": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/equivalent-key-map/-/equivalent-key-map-0.2.2.tgz", + "integrity": "sha512-xvHeyCDbZzkpN4VHQj/n+j2lOwL0VWszG30X4cOrc9Y7Tuo2qCdZK/0AMod23Z5dCtNUbaju6p0rwOhHUk05ew==", + "dev": true, + "license": "MIT" + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/es-abstract": { + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-abstract-get": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-abstract-get/-/es-abstract-get-1.0.0.tgz", + "integrity": "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.2", + "is-callable": "^1.2.7", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.4.0.tgz", + "integrity": "sha512-c/A0P0oxkACDc+cKWw8evLXK83oBKgn0qPOqCYT4x9uolpCIJAcYvJC9QYKNDRPsTeGyCrQ326jrvgZWdCdK5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.1.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.3.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.5", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-3.0.4.tgz", + "integrity": "sha512-BldNY5mwkVu+cQ5Ymn3e6hB9Sx8XHx4ZevWbiUyZbfKxJxYO27Hf0wfzkxIaD5CAJMaGl3YspKELj0ff/7nCMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.4.tgz", + "integrity": "sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-abstract-get": "^1.0.0", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "is-callable": "^1.2.7", + "is-date-object": "^1.1.0", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", + "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.6", + "@eslint/js": "9.39.5", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-import-context": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/eslint-import-context/-/eslint-import-context-0.1.9.tgz", + "integrity": "sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-tsconfig": "^4.10.1", + "stable-hash-x": "^0.2.0" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-import-context" + }, + "peerDependencies": { + "unrs-resolver": "^1.0.0" + }, + "peerDependenciesMeta": { + "unrs-resolver": { + "optional": true + } + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz", + "integrity": "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.16.1", + "resolve": "^2.0.0-next.6" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/resolve": { + "version": "2.0.0-next.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz", + "integrity": "sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.2", + "node-exports-info": "^1.6.0", + "object-keys": "^1.1.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.4.5.tgz", + "integrity": "sha512-nbE5XLph6TLtGYcu/U6e6ZVXyKBhbDWK5cLGk76eJ7NdZpwf1P9EFkpt1Z01mNZNrrilsAYWKH6zUkL4reoXbw==", + "dev": true, + "license": "ISC", + "dependencies": { + "debug": "^4.4.1", + "eslint-import-context": "^0.1.8", + "get-tsconfig": "^4.10.1", + "is-bun-module": "^2.0.0", + "stable-hash-x": "^0.2.0", + "tinyglobby": "^0.2.14", + "unrs-resolver": "^1.7.11" + }, + "engines": { + "node": "^16.17.0 || >=18.6.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-import-resolver-typescript" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.14.0.tgz", + "integrity": "sha512-W2WCRZ9Dqntd+2u8jJcVMV2PKulc6RdLgUUoh/yQr3uB6lo/ZOeGx11sv60/8S4QFFKNslAlWhr9u0Ef7ZW6Ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint-plugin-import/node_modules/brace-expansion": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-jest": { + "version": "29.17.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-29.17.0.tgz", + "integrity": "sha512-6jtpU6MhYKbagkES02TupWAUUooJpZtKg94Ax0b8HhUorv3InSTBU8RRtlSBe0LocOcShXOpyec3PHzPDkJCSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.0.0" + }, + "engines": { + "node": "^20.12.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^8.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "jest": "*", + "typescript": ">=4.8.4 <8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-jsdoc": { + "version": "64.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-64.5.4.tgz", + "integrity": "sha512-xfRhXPSSWT612muJ6XTvxuVJ8zYHv99qNgBqPVqF28aSqoy4s4XbSkrWXnYA2hl5ejC02yEpk6fqkj3BmJ6Xbg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@es-joy/jsdoccomment": "~0.98.0", + "@es-joy/resolve.exports": "1.2.0", + "@typescript-eslint/utils": "^8.70.0", + "are-docs-informative": "^0.1.1", + "comment-parser": "1.4.9", + "debug": "^4.4.3", + "escape-string-regexp": "^5.0.0", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "html-entities": "^2.6.0", + "object-deep-merge": "^2.0.1", + "parse-imports-exports": "^0.2.4", + "semver": "^7.8.5", + "spdx-expression-parse": "^5.0.0", + "to-valid-identifier": "^1.0.0" + }, + "engines": { + "node": "^22.22.2 || >=24.15.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0", + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-playwright": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-2.12.1.tgz", + "integrity": "sha512-JhavNuYBIq7SE/Zg9Esf9RGHkXoDbzrEKS383cowzMyvZ4JMbmdNCN6CoW+RU1Cjzwcoea1tou0je58uXmT/9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "globals": "^17.3.0" + }, + "engines": { + "node": ">=16.9.0" + }, + "peerDependencies": { + "eslint": ">=8.40.0" + } + }, + "node_modules/eslint-plugin-playwright/node_modules/globals": { + "version": "17.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.13.0.tgz", + "integrity": "sha512-RwMTC61u7hrG3ZJMkZQOK4JLJrKS8QtoTii63EmWvFXHqycY9FObBJQCbsLxSO8kjKhWE5kV1GC+Vb1g3RI0Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.5.7", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.7.tgz", + "integrity": "sha512-WjHyn3GrFd+ZR0rzsLiM4zJZ35PZMOE0SUIxzGmnWNG0126yNfJKJzObmmkYDwHJfGlkz3PSlzyG9ez/LJntwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.1", + "synckit": "^0.12.3" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint-plugin-react/node_modules/brace-expansion": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-react/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz", + "integrity": "sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.2", + "node-exports-info": "^1.6.0", + "object-keys": "^1.1.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/express": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.15.1", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/express/node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.9.tgz", + "integrity": "sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-xml-builder": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.3.1.tgz", + "integrity": "sha512-pIM/1n3ntFXKYrUZwW7QCK0gAW7XY+wzj1YMIV3tLDvPj/V+zTGJK5e3/4WJfwj0qWw2ElNXiTixda/R+3YSug==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.6.2", + "xml-naming": "^0.3.0" + } + }, + "node_modules/fast-xml-parser": { + "version": "5.11.2", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.11.2.tgz", + "integrity": "sha512-R9iDuNrQYeQut46cn2r2wHKn4HYzVDvDm5J1wW+koZewykv0yuO2HChTYeZtrULyHIDM9cj9TUXloCsueCQUog==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "@nodable/entities": "^3.0.1", + "fast-xml-builder": "^1.2.0", + "is-unsafe": "^2.0.0", + "path-expression-matcher": "^1.6.2", + "strnum": "^2.4.2", + "xml-naming": "^0.3.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastq": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", + "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/find-cache-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", + "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "common-path-prefix": "^3.0.0", + "pkg-dir": "^7.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-parent-dir": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/find-parent-dir/-/find-parent-dir-0.3.1.tgz", + "integrity": "sha512-o4UcykWV/XN9wm+jMEtWLPlV8RXCZnMhQI6F6OdHeSez7iiJWePw8ijOlskJZMsaQoGR/b7dH6lO02HhaTN7+A==", + "dev": true, + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/framer-motion": { + "version": "11.18.2", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz", + "integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "motion-dom": "^11.18.1", + "motion-utils": "^11.18.1", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-ext-extra-prebuilt": { + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/fs-ext-extra-prebuilt/-/fs-ext-extra-prebuilt-2.2.16.tgz", + "integrity": "sha512-mVKH62x8olJe64hwGt0sl7DAsnGZajnjwaNRzkzMdwfWxafUGU7/AqYgSEj1D+yHkwhTuys8r8BYyhYT1c2nuw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "nan": "^2.27.0" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/fs-extra": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.2.0.tgz", + "integrity": "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2", + "hasown": "^2.0.4", + "is-callable": "^1.2.7", + "is-document.all": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz", + "integrity": "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.3.tgz", + "integrity": "sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/gettext-parser": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/gettext-parser/-/gettext-parser-1.4.0.tgz", + "integrity": "sha512-sedZYLHlHeBop/gZ1jdg59hlUEcpcZJofLq2JFwJT1zTqAU3l2wFv6IsuwFHGqbiT9DWzMUW4/em2+hspnmMMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "encoding": "^0.1.12", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regex.js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/glob-to-regex.js/-/glob-to-regex.js-1.3.1.tgz", + "integrity": "sha512-zhWhMRsgnOym+1txhUmYJGD6eUsbUdrZv55gCYKuJX8t2p3+M8My23PD09bEIsreGUs5pnSa/idmcfp2/IlqGg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globals": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-12.2.0.tgz", + "integrity": "sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^3.0.1", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.7", + "ignore": "^5.1.9", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", + "dev": true, + "license": "MIT" + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/gradient-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gradient-parser/-/gradient-parser-1.2.0.tgz", + "integrity": "sha512-6ABGa9CR7WR/0pAJicBy5SJkiikbFM6kf/JjykwX7x+t+s8ORWVnlbi6FkHeFFb36yWsjUpHqSYrygd7ofEUqA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer": "^0.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hashery": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.5.1.tgz", + "integrity": "sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookified": "^1.15.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/header-case": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", + "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "capital-case": "^1.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/highlight-words-core": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/highlight-words-core/-/highlight-words-core-1.2.3.tgz", + "integrity": "sha512-m1O9HW3/GNHxzSIXWw1wCNXXsgLlxrP0OI6+ycGUhiUHkikqW3OrwVHz+lxeNBe5yqLESdIcj8PowHQ2zLvUvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/hookified": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz", + "integrity": "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/hpq": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/hpq/-/hpq-1.4.0.tgz", + "integrity": "sha512-ycJQMRaRPBcfnoT1gS5I1XCvbbw9KO94Y0vkwksuOjcJMqNZtb03MF2tCItLI2mQbkZWSSeFinoRDPmjzv4tKg==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-entities": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", + "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-tags": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-5.1.0.tgz", + "integrity": "sha512-n6l5uca7/y5joxZ3LUePhzmBFUJ+U2YWzhMa8XUTecSeSlQiZdF5XAd/Q3/WUl0VsXgUwWi8I7CNIwdI5WN1SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.3.0.tgz", + "integrity": "sha512-M5t5LlJpS1UHMjvwRQVdFHvPISGeLAxNcrWuJkeGh0KxsqCHZ1O3NXZU/8x7cD0BDcGW8kapxMKTvwlqrNkHkA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-link-header": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/http-link-header/-/http-link-header-1.1.4.tgz", + "integrity": "sha512-xT3GPW6/ZbGuw4UvwHqErSCEjNUlwbQJuZn9/q5U4WEKfp2kENVCAlousG1zLxHeaQ/ffOHUNpWamvkbBW0eNw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-4.2.1.tgz", + "integrity": "sha512-60SmYZpPD4dPbC2GZVkVV4uP/V/cbsXSwzGQXeHWbbE3Q1XdccJpqHTg6NJ6iMpFGVA1OEry8c8kHhx4ahiU1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "httpxy": "^0.5.4", + "is-glob": "^4.0.3", + "is-plain-obj": "^4.1.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": "^22.15.0 || ^24.0.0 || >=26.0.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/httpxy": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/httpxy/-/httpxy-0.5.6.tgz", + "integrity": "sha512-iqc3nOxvNb5FxbhJozt8wNmHgjlmk4zzba512jMEXVwHCEBCbGAj49eGfy5PI8ts3/LFSFq8siiSNqwfY1AlfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/hyperdyperid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", + "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.18" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-walk": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-4.0.1.tgz", + "integrity": "sha512-rzDQLaW4jQbh2YrOFlJdCtX8qgJTehFRYiUB2r1osqTeDzV/3+Jh8fz1oAPzUThf3iku8Ds4IDqawI5d8mUiQw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ignore-walk/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ignore-walk/node_modules/brace-expansion": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/ignore-walk/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/image-ssim": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/image-ssim/-/image-ssim-0.2.0.tgz", + "integrity": "sha512-W7+sO6/yhxy83L0G7xR8YAc5Z5QFtYEXXRV6EaE8tuYBZJnA3gVgp3q7X7muhLZVodeb9UfvjSbwt9VJwjIYAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/immutable": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", + "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", + "dev": true, + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-in-the-middle": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.5.2.tgz", + "integrity": "sha512-WseIA/4o56+GYO3RmlOPvejYi771OFeJEeK/ejROsr9bdjW+E5NXTOD1Di8TL40Qgg3JJX9aa97Hf8iiSVIKZw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "cjs-module-lexer": "^2.2.0", + "es-module-lexer": "^3.0.2", + "module-details-from-path": "^1.0.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.2.tgz", + "integrity": "sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/intl-messageformat": { + "version": "10.7.18", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.18.tgz", + "integrity": "sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.6", + "@formatjs/fast-memoize": "2.2.7", + "@formatjs/icu-messageformat-parser": "2.11.4", + "tslib": "^2.8.0" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/irregular-plurals": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.5.0.tgz", + "integrity": "sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bun-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.7.1" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.17.0.tgz", + "integrity": "sha512-J/vG0zBCbIKOQFfufSwyXdMrsohyJIUNkrnmo6WZGzoM7tr/lsbfW5b2BvisL6zsyMzK9UxV9L6c7AoFbyXHOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-document.all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-document.all/-/is-document.all-1.0.0.tgz", + "integrity": "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-in-ssh": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz", + "integrity": "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container/node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-network-error": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.2.tgz", + "integrity": "sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", + "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.1.0.tgz", + "integrity": "sha512-bUi/yjmtKYcRVUtWRGr0UA6xEFh2I6zWUwMrUXB3s7bmYCaZ8a+0ZsTRkrawh/mzlSD1Y0Ph8bp/U+TvBpWDNw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unsafe": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-unsafe/-/is-unsafe-2.0.2.tgz", + "integrity": "sha512-HgbIHPBH0KHHCcjLfGsCvhtPTVxjaAZlXjwdz7/GQC40SjSe4sfQsar8J5VFo8JOSbarkpV0OLG95bbaNd9aAQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isomorphic-git": { + "version": "1.37.6", + "resolved": "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.37.6.tgz", + "integrity": "sha512-qr1NFCPsVTZ6YGqTXw0CzamnsHyH9QQ1OTEfeXIweSljRUMzuHFCJdUn0wc6OcjtTDns6knxjPb7N6LmJeftOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-lock": "^1.4.1", + "clean-git-ref": "^2.0.1", + "crc-32": "^1.2.0", + "diff3": "0.0.3", + "ignore": "^5.1.4", + "minimisted": "^2.0.0", + "pako": "^1.0.10", + "pify": "^4.0.1", + "readable-stream": "^4.0.0", + "sha.js": "^2.4.12", + "simple-get": "^4.0.1" + }, + "bin": { + "isogit": "cli.cjs" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jpeg-js": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", + "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/js-library-detector": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/js-library-detector/-/js-library-detector-6.7.0.tgz", + "integrity": "sha512-c80Qupofp43y4cJ7+8TTDN/AsDwLi5oOm/plBrWI+iQt485vKXCco+yVmOwEgdo9VOdsYTuV0UlTeetVPTriXA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.2.tgz", + "integrity": "sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "9.2.3", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-9.2.3.tgz", + "integrity": "sha512-xsCb5EbmLWNlQ8v5zQIgSYSyf1rSyVPjh8p9G2xviOl1weHm8a611fHWUOGn7ISquCL6LoBKZvh0tcKmpVGgzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.9" + }, + "engines": { + "node": "^22.22.2 || >=24.15.0" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json2php": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/json2php/-/json2php-0.0.9.tgz", + "integrity": "sha512-fQMYwvPsQt8hxRnCGyg1r2JVi6yL8Um0DIIawiKiMK9yhAAkcRNj5UsBWoaFvFzPpcWbgw9L6wzj+UMYA702Mw==", + "dev": true, + "license": "BSD" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "dev": true, + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/katex": { + "version": "0.16.47", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz", + "integrity": "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==", + "dev": true, + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/known-css-properties": { + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.37.0.tgz", + "integrity": "sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/launch-editor": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.14.2.tgz", + "integrity": "sha512-NHJEb30MGBCkmZXLZwz25t37QR4aY03ZzPYUJXOJaOa92uWwExI9SBQUuIznBYc1xkD7cJNGX7oc3Nl3sq2kQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.1.1", + "shell-quote": "^1.10.0" + } + }, + "node_modules/legacy-javascript": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/legacy-javascript/-/legacy-javascript-0.0.4.tgz", + "integrity": "sha512-UTmeKBVxnfJcpmpOvzzNnLsQT99BXHpPlc/ZKddIRmWmrR0fw6335I3lq4+sJoP60UTbBNaYsGYVPc65YB1x3A==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lighthouse": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/lighthouse/-/lighthouse-13.5.0.tgz", + "integrity": "sha512-w/yYIwg7mP9pCwkcbAXRztnvKHua2dJnZaNptPLa1cEw+akr6lbsIDiUOLsQwvaiAUr0xvxdmwvAzl3pj3Np0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@paulirish/trace_engine": "0.0.65", + "@sentry/node": "^10.0.0", + "ajv": "^8.20.0", + "ajv-formats": "^3.0.1", + "axe-core": "^4.13.0", + "chrome-launcher": "^1.2.1", + "configstore": "^7.0.0", + "csp_evaluator": "1.1.8", + "devtools-protocol": "0.0.1687809", + "enquirer": "^2.3.6", + "http-link-header": "^1.1.1", + "intl-messageformat": "^10.5.3", + "jpeg-js": "^0.4.4", + "js-library-detector": "^6.7.0", + "lighthouse-logger": "^2.0.2", + "lighthouse-stack-packs": "1.12.3", + "lodash-es": "^4.17.21", + "lookup-closest-locale": "6.2.0", + "open": "^8.4.0", + "puppeteer-core": "^25.9.0", + "robots-parser": "^3.0.1", + "speedline-core": "^1.4.3", + "third-party-web": "^0.30.0", + "tldts-icann": "^7.4.13", + "web-features": "^3.39.0", + "ws": "^7.0.0", + "yargs": "^17.3.1", + "yargs-parser": "^21.0.0" + }, + "bin": { + "chrome-debug": "core/scripts/manual-chrome-launcher.js", + "lighthouse": "cli/index.js", + "smokehouse": "cli/test/smokehouse/frontends/smokehouse-bin.js" + }, + "engines": { + "node": ">=22.19" + } + }, + "node_modules/lighthouse-logger": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-2.0.2.tgz", + "integrity": "sha512-vWl2+u5jgOQuZR55Z1WM0XDdrJT6mzMP8zHUct7xTlWhuQs+eV0g+QL0RQdFjT54zVmbhLCP8vIVpy1wGn/gCg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.4.1", + "marky": "^1.2.2" + } + }, + "node_modules/lighthouse-stack-packs": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/lighthouse-stack-packs/-/lighthouse-stack-packs-1.12.3.tgz", + "integrity": "sha512-d8IsOpE83kbANgnM+Tp8+x6HcMpX9o2ITBiUERssgzAIFdZCQzs/f4k6D0DLQTE59enml9mbAOU52Wu35exWtg==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/lighthouse/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/lighthouse/node_modules/ws": { + "version": "7.5.13", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.13.tgz", + "integrity": "sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==", + "dev": true, + "license": "MIT", + "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 + } + } + }, + "node_modules/lilconfig": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.4.tgz", + "integrity": "sha512-zJZyBmQhArDRefuWTypZxwDpFDIYmrj8ypSBvwGD5P13Y95s6DjzKgu4DVdGbeQa5PDEsAOCAPNk4X+QOR006w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/linkify-it": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz", + "integrity": "sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash-es": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lookup-closest-locale": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/lookup-closest-locale/-/lookup-closest-locale-6.2.0.tgz", + "integrity": "sha512-/c2kL+Vnp1jnV6K6RpDTHK3dgg0Tu2VVp+elEiJpjfS1UyY7AjOYHohRug6wT0OpoX2qFgNORndE9RqesfVxWQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/map-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-values/-/map-values-1.0.1.tgz", + "integrity": "sha512-BbShUnr5OartXJe1GeccAWtfro11hhgNJg6G9/UtWKjVGvV5U4C09cg5nk8JUevhXODaXY+hQ3xxMUKSs62ONQ==", + "dev": true, + "license": "Public Domain" + }, + "node_modules/markdown-it": { + "version": "14.3.3", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.3.tgz", + "integrity": "sha512-L//tDAkjTjJCU6pBUSBTgs3Kg4InVaZuUZog/b6V12UKhcDQ7S78877ibbCanRExwPuYP1CZD7Gd2dxSy1GXRg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.5.0", + "linkify-it": "^5.0.2", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-it/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/markdownlint": { + "version": "0.41.1", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.41.1.tgz", + "integrity": "sha512-qHKeU2E1bdyNAT077go2FVTNXvYcktN5IHtF6XyeD1l0PClxzSp2tUApAV14ORI8DGX4H9bNKZEzelZp4qn8IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromark": "4.0.2", + "micromark-core-commonmark": "2.0.3", + "micromark-extension-directive": "4.0.0", + "micromark-extension-gfm-autolink-literal": "2.1.0", + "micromark-extension-gfm-footnote": "2.1.0", + "micromark-extension-gfm-table": "2.1.1", + "micromark-extension-math": "3.1.0", + "micromark-util-types": "2.0.2", + "string-width": "8.2.1" + }, + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://github.com/sponsors/DavidAnson" + } + }, + "node_modules/markdownlint-cli": { + "version": "0.49.1", + "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.49.1.tgz", + "integrity": "sha512-qpYqJbSYf3jv57bdnFmCaZ/Wlu6IYHp2b6SOKrKBJ7OnPrDHIKmx4NERWH49QH9viTI6yO6raVDDn5nrf60VQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "~15.0.0", + "deep-extend": "~0.6.0", + "ignore": "~7.0.6", + "js-yaml": "~5.2.1", + "jsonc-parser": "~3.3.1", + "jsonpointer": "~5.0.1", + "markdown-it": "~14.3.0", + "markdownlint": "~0.41.1", + "minimatch": "~10.2.5", + "run-con": "~1.3.3", + "smol-toml": "~1.7.0", + "tinyglobby": "~0.2.17" + }, + "bin": { + "markdownlint": "markdownlint.js" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/markdownlint-cli/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/markdownlint-cli/node_modules/ignore": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.14.tgz", + "integrity": "sha512-wCBiZ77u1tuWde6G/nfOtLaCTYcFDU5yCzARzK10y+rLhzYoL7a7yEemcopcc2Mzow7gZOIjnV2ym1NYmdiJOQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/markdownlint-cli/node_modules/js-yaml": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.2.3.tgz", + "integrity": "sha512-n+mUVyUX5bVv7G/G2zyIHOhdxfuU1dY2NOFzTQUWiMUbFss8b57NFlgCCaggU78wSw5KVS9cllzeLyzyR+n5nw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.mjs" + } + }, + "node_modules/marked": { + "version": "18.1.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-18.1.1.tgz", + "integrity": "sha512-zEGgrtUG8crk+TrMacJSVABU4DYDDkS8uqgU3ohrn/2fW9C2AEfUrO3m6IhpIpQpl7YcVpiXXC9cSMWDANT3dg==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/marky": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz", + "integrity": "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mathml-tag-names": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-4.0.0.tgz", + "integrity": "sha512-aa6AU2Pcx0VP/XWnh8IGL0SYSgQHDT6Ucror2j2mXeFAlN3ahaNs8EZtG1YiticMkSLj3Gt6VPFfZogt7G5iFQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/mdurl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.1.0.tgz", + "integrity": "sha512-1+HBaOx0zi/dQWht8rNv9MYf9qqpqL/kxI0hXImU6Y547zM6Sni8BQibt7ifgMcYtQg41ao3Ivd6cnSM86inpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mediabunny": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.62.1.tgz", + "integrity": "sha512-lYzeerDwo9QLhBCipqQDXOSV7InvaIFplllZQviPO5rPwH+IUr8M1uvumk1olIUgMRGkPiInRriv25WFErUgMw==", + "dev": true, + "license": "MPL-2.0", + "workspaces": [ + ".", + "packages/*" + ], + "dependencies": { + "@types/dom-mediacapture-transform": "^0.1.11", + "@types/dom-webcodecs": "0.1.13" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/Vanilagy" + } + }, + "node_modules/memfs": { + "version": "4.80.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.80.0.tgz", + "integrity": "sha512-N70W2isi44XdGn7uPo9NJ4zRukcdkwSFHlnYx42SWOH/+hzqNc8P7+tVoA2up4z7faHvNi1RSNCFjxoYQ71VPQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/fs-core": "4.80.0", + "@jsonjoy.com/fs-fsa": "4.80.0", + "@jsonjoy.com/fs-node": "4.80.0", + "@jsonjoy.com/fs-node-builtins": "4.80.0", + "@jsonjoy.com/fs-node-to-fsa": "4.80.0", + "@jsonjoy.com/fs-node-utils": "4.80.0", + "@jsonjoy.com/fs-print": "4.80.0", + "@jsonjoy.com/fs-snapshot": "4.80.0", + "@jsonjoy.com/json-pack": "^1.11.0", + "@jsonjoy.com/util": "^1.9.0", + "glob-to-regex.js": "^1.3.1", + "thingies": "^2.5.0", + "tree-dump": "^1.0.3", + "tslib": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + } + }, + "node_modules/memize": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/memize/-/memize-2.1.1.tgz", + "integrity": "sha512-8Nl+i9S5D6KXnruM03Jgjb+LwSupvR13WBr4hJegaaEyobvowCVupi79y2WSiWvO1mzBWxPwEYE5feCe8vyA5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-directive": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-4.0.0.tgz", + "integrity": "sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-math": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", + "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/katex": "^0.16.0", + "devlop": "^1.0.0", + "katex": "^0.16.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.1.0.tgz", + "integrity": "sha512-fS8hnLIjnjvdQIj39Geug8wWsR0HrYZ43KShKxNfwT7t2LOHo/LbWZEzEaSOjwtjdCsjoE6syHhonEzW0zv0+Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.10.2.tgz", + "integrity": "sha512-AOSS0IdEB95ayVkxn5oGzNQwqAi2J0Jb/kKm43t7H73s8+f5873g0yuj0PNvK4dO75mu5DHg4nlgp4k6Kga8eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/minimist-options/node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/minimisted": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minimisted/-/minimisted-2.0.1.tgz", + "integrity": "sha512-1oPjfuLQa2caorJUM8HV8lGgWCc0qqAO1MNv/k05G4qslmsndV/5WdNZrqCiyqiz3wohia2Ij2B7w2Dr7/IyrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5" + } + }, + "node_modules/minimizer-webpack-plugin": { + "version": "5.13.1", + "resolved": "https://registry.npmjs.org/minimizer-webpack-plugin/-/minimizer-webpack-plugin-5.13.1.tgz", + "integrity": "sha512-1mhAfvachWYYOm8tpmany5NLuvP+F2wxZWBSX8DC0uFS2wJpM9q3rWgN9wBooAH9d5V12wX0BjyJe4fMjHMarQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "jest-worker": "^27.4.5", + "schema-utils": "^4.6.0", + "terser": "^5.51.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@minify-html/node": { + "optional": true + }, + "@napi-rs/image": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "@swc/html": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "cssnano": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "html-minifier-terser": { + "optional": true + }, + "imagemin": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "postcss": { + "optional": true + }, + "sharp": { + "optional": true + }, + "svgo": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, + "license": "MIT" + }, + "node_modules/modern-tar": { + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/modern-tar/-/modern-tar-0.8.6.tgz", + "integrity": "sha512-LYSks0tNVqbtez/9OB0YFP6qLRSqkdAxJWOQpVaR67lNTbj2zNLyYpowkRMia5lXLCZKzxwoxkxH2MbbBR2aEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/module-details-from-path": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", + "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", + "dev": true, + "license": "MIT" + }, + "node_modules/moment": { + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.31.0.tgz", + "integrity": "sha512-0acOTfMiWOheYS4eoWb80yYMb/JLvVv9SHbs2PehaDzfUG0Bw855SKyk0IKTnPGa5+U2bmi3W68l1+sGLX/pvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.48", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz", + "integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/motion-dom": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz", + "integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "motion-utils": "^11.18.1" + } + }, + "node_modules/motion-utils": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz", + "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mousetrap": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.5.tgz", + "integrity": "sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA==", + "dev": true, + "license": "Apache-2.0 WITH LLVM-exception" + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dev": true, + "license": "MIT", + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/nan": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.30.0.tgz", + "integrity": "sha512-P5mokDkSvCGBz6mZDD/2l+w9QoICRXdCyZ9ONGmTePr40oihJg3Kpn0YU3twKd6PJnwSlLBWNQeOT6yWEV2c4A==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/nanoid": { + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/napi-postinstall": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.7.tgz", + "integrity": "sha512-jyU4cU4kVAmy/3qjP+DcoQTNXfINHc+dFw54gGqWC8nu0mpV0kOnk7kff+J+Q18SbX0k3Np8Rm7yFjd3phatNQ==", + "dev": true, + "license": "MIT", + "bin": { + "napi-patch": "lib/patch.js", + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-exports-info": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.2.tgz", + "integrity": "sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==", + "dev": true, + "license": "MIT", + "dependencies": { + "array.prototype.flatmap": "^1.3.3", + "es-errors": "^1.3.0", + "object.entries": "^1.1.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/node-exports-info/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/node-releases": { + "version": "2.0.60", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.60.tgz", + "integrity": "sha512-OmO8+GM1JUZqXET720VrJCwkjUCXmCMQm0Z3UDDRbfNLHS4NEAvY60j5jiFv0tkKw2PNNlqOLhahVmwJIOLDIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/normalize-wheel": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/normalize-wheel/-/normalize-wheel-1.0.1.tgz", + "integrity": "sha512-1OnlAPZ3zgrk8B91HyRj+eVv+kS5u+Z0SCsak6Xil/kmgEia50ga7zfkumayonZrImffAxPU/5WcyGhzetHNPA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/npm-bundled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true, + "license": "ISC" + }, + "node_modules/npm-package-json-lint": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/npm-package-json-lint/-/npm-package-json-lint-6.4.0.tgz", + "integrity": "sha512-cuXAJJB1Rdqz0UO6w524matlBqDBjcNt7Ru+RDIu4y6RI1gVqiWBnylrK8sPRk81gGBA0X8hJbDXolVOoTc+sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.6", + "ajv-errors": "^1.0.1", + "chalk": "^4.1.2", + "cosmiconfig": "^8.0.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "ignore": "^5.2.0", + "is-plain-obj": "^3.0.0", + "jsonc-parser": "^3.2.0", + "log-symbols": "^4.1.0", + "meow": "^9.0.0", + "plur": "^4.0.0", + "semver": "^7.3.8", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1", + "type-fest": "^3.2.0", + "validate-npm-package-name": "^5.0.0" + }, + "bin": { + "npmPkgJsonLint": "dist/cli.js" + }, + "engines": { + "node": ">=14.0.0", + "npm": ">=6.0.0" + } + }, + "node_modules/npm-package-json-lint/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/npm-package-json-lint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/npm-package-json-lint/node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm-package-json-lint/node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/npm-package-json-lint/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-package-json-lint/node_modules/js-yaml": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/npm-package-json-lint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/npm-package-json-lint/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm-package-json-lint/node_modules/type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-packlist": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-3.0.0.tgz", + "integrity": "sha512-L/cbzmutAwII5glUcf2DBRNY/d0TFd4e/FnaZigJV6JD85RHZXJFGwCndjMWiiViiWSsWt3tiOLpI3ByTnIdFQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.6", + "ignore-walk": "^4.0.1", + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "npm-packlist": "bin/index.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-deep-merge": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object-deep-merge/-/object-deep-merge-2.0.1.tgz", + "integrity": "sha512-aKttDKcU3pyZqKcCkDhsMn70WmZFG2JGDQLP9EcLyTSIFQRCPWLAmBZRLJnrVUrhPG1jETEEbfdgbNtJf1LyMg==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-filter": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/object-filter/-/object-filter-1.0.2.tgz", + "integrity": "sha512-NahvP2vZcy1ZiiYah30CEPw0FpDcSkSePJBMpzl5EQgCmISijiGuJm3SPYp7U+Lf2TljyaIw3E5EgkEx/TNEVA==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/octokit": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/octokit/-/octokit-3.1.2.tgz", + "integrity": "sha512-MG5qmrTL5y8KYwFgE1A4JWmgfQBaIETE/lOlfwNYx1QOtCQHGVxkRJmdUJltFc1HVn73d61TlMhMyNTOtMl+ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/app": "^14.0.2", + "@octokit/core": "^5.0.0", + "@octokit/oauth-app": "^6.0.0", + "@octokit/plugin-paginate-graphql": "^4.0.0", + "@octokit/plugin-paginate-rest": "^9.0.0", + "@octokit/plugin-rest-endpoint-methods": "^10.0.0", + "@octokit/plugin-retry": "^6.0.0", + "@octokit/plugin-throttling": "^8.0.0", + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/on-finished": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.2.tgz", + "integrity": "sha512-ATWUv387i+nLD49EiDoOft5scf32Vsdz3ktrQJG9bi7jRFmaXTssvyvj1EmeKVVgvesbqUtvQQvaS2TpTukAZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true, + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-4.1.1.tgz", + "integrity": "sha512-sjYP8QyVWBpBZWD6Vr1M/KwknSw6kJOz41tvGMlwWeClHBtYKTbHMki1PsLZnxKpXMPbTKv9b3pjQu3REib96A==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^3.0.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.2.0", + "is-interactive": "^1.0.0", + "log-symbols": "^3.0.0", + "mute-stream": "0.0.8", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/ora/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ora/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/ora/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ora/node_modules/log-symbols": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", + "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/log-symbols/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ora/node_modules/log-symbols/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ora/node_modules/log-symbols/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ora/node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true, + "license": "ISC" + }, + "node_modules/own-keys": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.2.tgz", + "integrity": "sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-8.0.1.tgz", + "integrity": "sha512-NAigyu8hfvJe7MsS18mnc4is0MZtau3QMdBklaJKK23oBQb6occO3UPM3vHmTckW8KhfyjMZaUOqdTFEYliHgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-network-error": "^1.3.0" + }, + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-imports-exports": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz", + "integrity": "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-statements": "1.0.11" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-statements": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/parse-statements/-/parse-statements-1.0.11.tgz", + "integrity": "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==", + "dev": true, + "license": "MIT" + }, + "node_modules/parsel-js": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/parsel-js/-/parsel-js-1.2.3.tgz", + "integrity": "sha512-kJHFmSNnujpusFfVEXjNaoJ09VYHF8Ih0aVUn7Clu+Ug8AFoA2wx7Ezh17JfW4+vhm/lauAd9A+//uFkmydZtQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/LeaVerou" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/leaverou" + } + ], + "license": "MIT" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/path-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", + "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-expression-matcher": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.2.tgz", + "integrity": "sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-dir": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", + "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^6.3.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/pkg-dir/node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkijs": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.4.3.tgz", + "integrity": "sha512-V9kOzaItwDgqlryhXysQsAXUEKi5O7kqWUaP3dXI7MLvkPqWWMb4wVb1QAtmEDCKI+XIAINsZoNKMFF7sZb1eg==", + "dev": true, + "license": "BSD-3-Clause", + "workspaces": [ + "website" + ], + "dependencies": { + "@noble/hashes": "1.8.0", + "asn1js": "^3.0.6", + "bytestreamjs": "^2.0.1", + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/playwright": { + "version": "1.64.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.64.1.tgz", + "integrity": "sha512-HCrVhHr5QQuwYoswEUGTb7gGp522yflbv/ST8nF4sFWGuLjQCt17Q/EubpGGSNKYn1tsTCPZxqHhHCfVURu7UA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.64.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/playwright-core": { + "version": "1.64.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.64.1.tgz", + "integrity": "sha512-BC4s8y6SEVA6Q1rSxZTto8hI1KUI3xXlHGZgvGqP4IqPdI/a6FX9H29IDNz7vKo9l21b6LfYosScPL6M3xxyDQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/plur": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/plur/-/plur-4.0.0.tgz", + "integrity": "sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "irregular-plurals": "^3.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.31.tgz", + "integrity": "sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.20", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-calc": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", + "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.0.11", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.2.2" + } + }, + "node_modules/postcss-calc/node_modules/postcss-selector-parser": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.5.tgz", + "integrity": "sha512-TEHFzb5KRDKaOgJ4TYE+0jyJtFbjLvkdVOu0fZ2b6MTWzoqZd4Le+61aEppWkvTlYePgOX4ATL1wsAIPMLTw6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-colormin": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz", + "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.0", + "caniuse-api": "^3.0.0", + "colord": "^2.9.3", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-convert-values": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz", + "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-discard-comments": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz", + "integrity": "sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz", + "integrity": "sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-discard-empty": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz", + "integrity": "sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz", + "integrity": "sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-import": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-16.2.0.tgz", + "integrity": "sha512-0mQUGlSp87Zl70K58RNwQAN1WS9plFE6KWGui9nyK6ninduMyjW/Khaoy/BpBoFTBh7ndAvAKrSKVbwy6vd/BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-merge-longhand": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz", + "integrity": "sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^6.1.1" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-merge-rules": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz", + "integrity": "sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.0", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^4.0.2", + "postcss-selector-parser": "^6.0.16" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-merge-rules/node_modules/postcss-selector-parser": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.5.tgz", + "integrity": "sha512-TEHFzb5KRDKaOgJ4TYE+0jyJtFbjLvkdVOu0fZ2b6MTWzoqZd4Le+61aEppWkvTlYePgOX4ATL1wsAIPMLTw6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz", + "integrity": "sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz", + "integrity": "sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "colord": "^2.9.3", + "cssnano-utils": "^4.0.2", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-minify-params": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz", + "integrity": "sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.0", + "cssnano-utils": "^4.0.2", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz", + "integrity": "sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.0.16" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-minify-selectors/node_modules/postcss-selector-parser": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.5.tgz", + "integrity": "sha512-TEHFzb5KRDKaOgJ4TYE+0jyJtFbjLvkdVOu0fZ2b6MTWzoqZd4Le+61aEppWkvTlYePgOX4ATL1wsAIPMLTw6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", + "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", + "dev": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz", + "integrity": "sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz", + "integrity": "sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz", + "integrity": "sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz", + "integrity": "sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-string": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz", + "integrity": "sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz", + "integrity": "sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz", + "integrity": "sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-url": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz", + "integrity": "sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz", + "integrity": "sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-ordered-values": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz", + "integrity": "sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssnano-utils": "^4.0.2", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-prefix-selector": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/postcss-prefix-selector/-/postcss-prefix-selector-1.16.1.tgz", + "integrity": "sha512-Umxu+FvKMwlY6TyDzGFoSUnzW+NOfMBLyC1tAkIjgX+Z/qGspJeRjVC903D7mx7TuBpJlwti2ibXtWuA7fKMeQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "postcss": ">4 <9" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz", + "integrity": "sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.0", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz", + "integrity": "sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-resolve-nested-selector": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.7.tgz", + "integrity": "sha512-lFcugRCQXWManq7nyZ0dDtir52wc7gKYJfWpSuz4GxvnGc7dtlpXcZYX4A0asatzTuqpQ3ngDdyD+QG/szeXWw==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-safe-parser": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.1.0.tgz", + "integrity": "sha512-1WzZxRLaAFwEh6Do+zyGpjWV3nGJNxxhuh7Ubu/q1ICImMgZJnLwhoaEpRbG4pJppp2Y1ncL9ffA2f+LhrefQg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-scss": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.9.tgz", + "integrity": "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-scss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.4.29" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.7.tgz", + "integrity": "sha512-wWBro/7hJ40ye6TPiXkUQoXKCw23xSsjriUvqd7Uls9Sso3oLiON1flc18cCdnjgCRXw4fBVqUJJeOdDTh3P3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz", + "integrity": "sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^3.2.0" + }, + "engines": { + "node": "^14 || ^16 || >= 18" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz", + "integrity": "sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.0.16" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-unique-selectors/node_modules/postcss-selector-parser": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.5.tgz", + "integrity": "sha512-TEHFzb5KRDKaOgJ4TYE+0jyJtFbjLvkdVOu0fZ2b6MTWzoqZd4Le+61aEppWkvTlYePgOX4ATL1wsAIPMLTw6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-urlrebase": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/postcss-urlrebase/-/postcss-urlrebase-1.4.0.tgz", + "integrity": "sha512-rRaxMmWvXrn8Rk1PqsxmaJwldRHsr0WbbASKKCZYxXwotHkM/5X/6IrwaEe8pdzpbNGCEY86yhYMN0MhgOkADA==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "peerDependencies": { + "postcss": "^8.3.0" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/powershell-utils": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.2.1.tgz", + "integrity": "sha512-C+y9x90UElAddDZmV4qOx9W53B61PO7cIqWz2dQsWlwswuq4mr8NEwytdGKboYbQlGZ3awrkTeNvcZiZNHnQ8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/preact": { + "version": "10.29.8", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.8.tgz", + "integrity": "sha512-ej2aVZ+vZ8WO7tvlQWRM9N63A0KzF9q4mWJfDUHgYaIofWY9hu74QdnQrjoPMmZi2/nZ5gN0bJCQF49xQqx09Q==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + }, + "peerDependencies": { + "preact-render-to-string": ">=5" + }, + "peerDependenciesMeta": { + "preact-render-to-string": { + "optional": true + } + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "name": "wp-prettier", + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/wp-prettier/-/wp-prettier-3.9.6.tgz", + "integrity": "sha512-rKjrWazF+8QexwTG4Y1Xmnnnp9SDNvaakDtXTRDHxhyudrelGQ5tqoviFtGUuE4fvMs4ck3pmgrZ2Ap202GTIQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz", + "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", + "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/puppeteer-core": { + "version": "25.16.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-25.16.0.tgz", + "integrity": "sha512-VA5Pf54N8shfopNkZZXCdiiwfnnY8ZJYAMqFQRhlH5mGUhfwf53Ar770GPtdpTDLUqvQeYpIBtC9/G2Jij2IJw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "3.2.6", + "chromium-bidi": "157.0.8090-0", + "devtools-protocol": "0.0.1704951", + "typed-query-selector": "^2.12.3", + "webdriver-bidi-protocol": "1.0.1", + "ws": "^8.22.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/puppeteer-core/node_modules/devtools-protocol": { + "version": "0.0.1704951", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1704951.tgz", + "integrity": "sha512-GQhQOs9u/Mvm1zMtJIH872JS4AskuZdUSsPNPk4ChahtWAUqgz6atEtIsMt+nO7MqF1Ic4HxII/k8Cu36JivNg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/puppeteer-core/node_modules/ws": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.22.0.tgz", + "integrity": "sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/pvtsutils": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", + "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + } + }, + "node_modules/pvutils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.2.0.tgz", + "integrity": "sha512-BbubeCEyTuQjVMakvJQ/Sxbc93F2pwmbsxONT/ZRrwU7Ua38d8unYTwXpTVLAKJ4BDuH9IGztCjQcd/N/39Dvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/qified": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/qified/-/qified-0.10.1.tgz", + "integrity": "sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookified": "^2.1.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/qified/node_modules/hookified": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-2.2.0.tgz", + "integrity": "sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==", + "dev": true, + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/re-resizable": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-6.11.2.tgz", + "integrity": "sha512-2xI2P3OHs5qw7K0Ud1aLILK6MQxW50TcO+DetD9eIV58j84TqYeHoZcL9H4GXFXXIh7afhH8mv5iUCXII7OW7A==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react": { + "version": "19.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.3.1.tgz", + "integrity": "sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-colorful": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.8.1.tgz", + "integrity": "sha512-oz68bhsnFWnpDf1ZR8daiQbYpXUnM2h2J6hl9Zg2rTpM/DU6vCqe1E+CpqmqLnJucMZetHZeifSAfJ+geN9lcA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/react-day-picker": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-10.0.2.tgz", + "integrity": "sha512-QetiKtZWE9Uaw/pM/SYNv6WPYOTRnSJ2puL0GgwkmUFMIwiY0dtjH1W4B7DOVXVNQM0ZOClGpH/heOfBSzWeiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@date-fns/tz": "^1.4.1", + "date-fns": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/gpbl" + }, + "peerDependencies": { + "@types/react": ">=16.8.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-dom": { + "version": "19.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.1.tgz", + "integrity": "sha512-Y0bARz53lhEC1NMCASs0/j+faB/zqt3R8hpvg1e8+61ZVJ4UL0UdC4cBbXfZMcIuhrfgeILTLe+ZeBCavQPtKQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "scheduler": "^0.28.0" + }, + "peerDependencies": { + "react": "^19.3.1" + } + }, + "node_modules/react-easy-crop": { + "version": "5.5.7", + "resolved": "https://registry.npmjs.org/react-easy-crop/-/react-easy-crop-5.5.7.tgz", + "integrity": "sha512-kYo4NtMeXFQB7h1U+h5yhUkE46WQbQdq7if54uDlbMdZHdRgNehfvaFrXnFw5NR1PNoUOJIfTwLnWmEx/MaZnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "normalize-wheel": "^1.0.1", + "tslib": "^2.0.1" + }, + "peerDependencies": { + "react": ">=16.4.0", + "react-dom": ">=16.4.0" + } + }, + "node_modules/react-is": { + "version": "19.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.3.1.tgz", + "integrity": "sha512-RRieF6IyxQBkcK6pP8xOHprLTPW6QjRkbGdImtqNme+/hYVwCZkLQLdlZGOhUqXwkS9IEynaGvPJtv6XB/cz0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-remove-scroll": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/read-cache": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.2.tgz", + "integrity": "sha512-/peqiBB/n07gQGLsWaHho3WfvUyRscw0gYTsEFMhrIe/nWLkYaf5SbKYjGYqtRV3aPwykJgF2VEMo1ac4bnsGA==", + "dev": true, + "license": "MIT" + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true, + "license": "ISC" + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/readable-stream": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.8.0.tgz", + "integrity": "sha512-2jhTTU3Eilp/2DMT71gbcoqC35eQHasMJH0RHkYxwlxIv3+FR6Y5xAFZIwEGLxKkjZYEFlsiMNIdwUqjK2lLHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readdirp": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", + "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "dev": true, + "license": "MIT" + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regenerate": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.5.0.tgz", + "integrity": "sha512-1s+CxXjJ71i7LNVy8nYwg47XV4y6lAnOEhsMtSKq3WpmPfhl9LuKlTqKIn/3yUpE8lBz+W+TT88Bxyhnr0U4yw==", + "dev": true, + "license": "MIT" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.3.0.tgz", + "integrity": "sha512-9ns8odR8e9q3otKeHj3DE80jZYKg6zVeZKE+zQmRKXFoVUcuiTGeO6iusDhPRoIQQPVPRvH+SfBhrTud4eczuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpu-core": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.5.3.tgz", + "integrity": "sha512-E8VgUoSK+LMQrLboDxavm76MTBGrQhx9RiNuomRmYnZ10Uw12HrI8fkxOOncktb2hBpzIZ9SV/347euQHG5z+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.5.0", + "regenerate-unicode-properties": "^10.3.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.13.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.2.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.4.tgz", + "integrity": "sha512-EWV80YgFaVgFrjGCAsV7oybp4GOxbls5RzBg8OB0nKEVXsBJR1JlV8Di1JhCROg3MzTfe1RI8yitIm1eOs68iA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~3.1.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/rememo": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/rememo/-/rememo-4.0.2.tgz", + "integrity": "sha512-NVfSP9NstE3QPNs/TnegQY0vnJnstKQSpcrsI2kBTB3dB2PkdfKdTa+abbjMIDqpc63fE5LfjLgfMst0ULMFxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/remove-accents": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz", + "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==", + "dev": true, + "license": "MIT" + }, + "node_modules/requestidlecallback": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/requestidlecallback/-/requestidlecallback-0.3.0.tgz", + "integrity": "sha512-TWHFkT7S9p7IxLC5A1hYmAYQx2Eb9w1skrXmQ+dS1URyvR8tenMLl4lHbqEOUnpEYxNKpkVMXUgknVpBZWXXfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-in-the-middle": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", + "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3" + }, + "engines": { + "node": ">=9.3.0 || >=8.10.0 <9.0.0" + } + }, + "node_modules/requireindex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", + "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.5" + } + }, + "node_modules/reselect": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.3.0.tgz", + "integrity": "sha512-XGoLeRAVzUTcJ1qkxPQhDJyIZ5d6zzZD9nT7AEZOaaU9UbWclhycElmhO+VD5bFeLuzhPBaOV2oXC8uG35ZSpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/reserved-identifiers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/reserved-identifiers/-/reserved-identifiers-1.2.0.tgz", + "integrity": "sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/resolve": { + "version": "1.22.13", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.13.tgz", + "integrity": "sha512-Dj/cW9zBV2aRj6XzCxuGIs3ESZKv02jixne3q6jFZYZ3/Tg09gHHviSMDL4kObh9pqSahdRHEnDoaP2gn8YLhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.17.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-bin/-/resolve-bin-1.0.1.tgz", + "integrity": "sha512-4G9C3udcDB1c9qaopB+9dygm2bMyF2LeJ2JHBIc24N7ob+UuSSwX3ID1hQwpDEQep9ZRNdhT//rgEd6xbWA/SA==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-parent-dir": "~0.3.0" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", + "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/robots-parser": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/robots-parser/-/robots-parser-3.0.1.tgz", + "integrity": "sha512-s+pyvQeIKIZ0dx5iJiQk1tPLJAWln39+MI5jtM8wnyws+G5azk+dMnMX0qfbqNetKKNgcWWOdi0sfm+FbQbgdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/router/node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/rtlcss": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.3.0.tgz", + "integrity": "sha512-FI+pHEn7Wc4NqKXMXFM+VAYKEj/mRIcW4h24YVwVtyjI+EqGrLc2Hx/Ny0lrZ21cBWU2goLy36eqMcNj3AQJig==", + "dev": true, + "license": "MIT", + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0", + "postcss": "^8.4.21", + "strip-json-comments": "^3.1.1" + }, + "bin": { + "rtlcss": "bin/rtlcss.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-con": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/run-con/-/run-con-1.3.3.tgz", + "integrity": "sha512-Lb7OKM9aaykzyoNiHGhSVCjZsvbyy6qDMp2vDXL+MoCfz3GfNJtHYH7uYsU3QNMyInBk++xx+EZ8xZ8Sxs5fNQ==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~7.0.0", + "minimist": "^1.2.8", + "strip-json-comments": "~3.1.1" + }, + "bin": { + "run-con": "cli.js" + } + }, + "node_modules/run-con/node_modules/ini": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-7.0.0.tgz", + "integrity": "sha512-ifK0CgjALofS5bkrcTy4RaQ9Vx2Knf/eLeIO+NaswQEpH1UblrtTSCIvN71qQDMq0PeQ/SSPojvEJp9vvvfr+w==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rungen": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/rungen/-/rungen-0.3.2.tgz", + "integrity": "sha512-zWl10xu2D7zoR8zSC2U6bg5bYF6T/Wk7rxwp8IPaJH7f0Ge21G03kNHVgHR7tyVkSSfAOG0Rqf/Cl38JftSmtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/safe-array-concat": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", + "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sass": { + "version": "1.106.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.106.1.tgz", + "integrity": "sha512-kD+Gi66vycZcbJt+42NC4z4PXNMcCajVrJPhqoKKXy5cOBMmJQt052CxIBcRjwYXeImHYOpu/ydWWGZQIenl7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^5.0.0", + "immutable": "^5.1.5", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=20.19.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/sass-loader": { + "version": "16.0.8", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.8.tgz", + "integrity": "sha512-hcov4ZwZJIGbEuyNr9EmiTmZueyrxSToE6GOzoZnq5JM7ecRO7ttyvilPn+VmRsqiP16+VYZzVnGZj/hzZgKBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || ^1.0.0 || ^2.0.0-0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/sax": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", + "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/scheduler": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz", + "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/schema-utils": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.8.1.tgz", + "integrity": "sha512-x39pYlJPyH7Uge5QO64B9XGP9zLDc9WNmiQZc+CE3IzZPvMu3lHOuAMGpYNoPixEL7MSWlGhYTg5UlLSL8G6iw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.15", + "ajv": "^8.20.0", + "ajv-formats": "^3.0.1", + "ajv-keywords": "^5.1.0" + }, + "bin": { + "schema-utils": "bin/schema-utils.js" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.112.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + } + } + }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/selfsigned": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-5.5.0.tgz", + "integrity": "sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/x509": "^1.14.2", + "pkijs": "^3.3.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/sentence-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", + "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.2.tgz", + "integrity": "sha512-KDj11HScOaLmrPxl70KYNW1PksP4Nb/CLL2yvC+Qd2kHMPEEpfc4Re2e4FOay+bC/+XQl/7zAcWON3JVo5v3KQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.8.0", + "mime-types": "~2.1.35", + "parseurl": "~1.3.3" + }, + "engines": { + "node": ">= 0.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/sha.js": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", + "dev": true, + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" + }, + "bin": { + "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.12.0.tgz", + "integrity": "sha512-PcByqNyT/38F2kDNi006HAMRJaULuBzq/FOsw3qdZvX/GA9W/jamDaRskgHjubHiftXK5sIFxLNkvrXUwcof6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-git": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-4.0.2.tgz", + "integrity": "sha512-l0sIsv9VrPwovKELFDBOj/lhmIcbMvxBk2B+hRhsMM4/VL3gKtuZvYnBNixVo+N/0HnqQx2cBdLvxnAbygHpqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "@simple-git/args-pathspec": "1.0.4", + "@simple-git/argv-parser": "2.0.1", + "debug": "^4.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/git-js?sponsor=1" + } + }, + "node_modules/simple-html-tokenizer": { + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/simple-html-tokenizer/-/simple-html-tokenizer-0.5.11.tgz", + "integrity": "sha512-C2WEK/Z3HoSFbYq8tI7ni3eOo/NneSPRoPpcM7WdLjFOArFuyXEjAoCdOC3DgMfRyziZQ1hCNR4mrNdWEvD0og==", + "dev": true, + "license": "MIT" + }, + "node_modules/sirv": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/smol-toml": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.7.2.tgz", + "integrity": "sha512-pXFZ9B2WinEPzxWkMmlYE/oYx2BP+qLrE95wP8tCuK901uLSMGdCb6QSr82z+wnhXkG4+cO+OMLbZB2Cn+97zw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.2.tgz", + "integrity": "sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/source-map-loader/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-5.0.0.tgz", + "integrity": "sha512-vngmw3Rgn+o2arXNbnZaj5UtOEBuWBfvaI+Wc8GFfykIhA5/vdK9/Sp/XkLv63dykz2rxKDvKEHupF5P0FORcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.24", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.24.tgz", + "integrity": "sha512-cLS9TtWkIQFyLkJ3/5aFQAOHOSKTlOs/7WDut/XPSdjom1fJhUdkepeJAKXa9Y05+FabHd0sti+Et559vDtkpQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/speedline-core": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/speedline-core/-/speedline-core-1.4.3.tgz", + "integrity": "sha512-DI7/OuAUD+GMpR6dmu8lliO2Wg5zfeh+/xsdyJZCzd8o5JgFUjCeLsBDuZjIQJdwXS3J0L/uZYrELKYqx+PXog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "image-ssim": "^0.2.0", + "jpeg-js": "^0.4.1" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stable-hash-x": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/stable-hash-x/-/stable-hash-x-0.2.0.tgz", + "integrity": "sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "dev": true, + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz", + "integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.1.0.tgz", + "integrity": "sha512-tHNHTxInrYLCga9O9YGxWA3G9/nnzQw8UGAyqGx3Ar1pSTTzIuM4woFSq4SowkXCjJIwq5sIiQvEfRI9tCH1qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.2", + "get-intrinsic": "^1.3.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.4", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz", + "integrity": "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-object-atoms": "^1.1.2", + "has-property-descriptors": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz", + "integrity": "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strnum": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.5.1.tgz", + "integrity": "sha512-UM4nTLWzQiDBseVxUGxN0wJzIk0UU89Qi7pKXz9qISo+lhviJikTuikBsK/oFRlpvYHvSdz0TVoLyQ+sEpYwxA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "anynum": "^1.0.1" + } + }, + "node_modules/stubborn-fs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stubborn-fs/-/stubborn-fs-2.0.0.tgz", + "integrity": "sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "stubborn-utils": "^1.0.1" + } + }, + "node_modules/stubborn-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stubborn-utils/-/stubborn-utils-1.0.2.tgz", + "integrity": "sha512-zOh9jPYI+xrNOyisSelgym4tolKTJCQd5GBhK0+0xJvcYDcwlOoxF/rnFKQ2KRZknXSG9jWAp66fwP6AxN9STg==", + "dev": true, + "license": "MIT" + }, + "node_modules/style-search": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", + "integrity": "sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==", + "dev": true, + "license": "ISC" + }, + "node_modules/stylehacks": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.1.tgz", + "integrity": "sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.0", + "postcss-selector-parser": "^6.0.16" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/stylehacks/node_modules/postcss-selector-parser": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.5.tgz", + "integrity": "sha512-TEHFzb5KRDKaOgJ4TYE+0jyJtFbjLvkdVOu0fZ2b6MTWzoqZd4Le+61aEppWkvTlYePgOX4ATL1wsAIPMLTw6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/stylelint": { + "version": "17.16.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.16.0.tgz", + "integrity": "sha512-ejlWnbJ13x1GU32+zZXa6sYQJsKKkwSdv2B4rAdO3OWdEmvhSJKcXi5pOphQYSzyaj1kXpuGJr7pcMBNfDIuPw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^3.3.0", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-syntax-patches-for-csstree": "^1.1.9", + "@csstools/css-tokenizer": "^4.0.0", + "@csstools/media-query-list-parser": "^5.0.0", + "@csstools/selector-resolve-nested": "^4.0.1", + "@csstools/selector-specificity": "^6.0.0", + "colord": "^2.10.0", + "cosmiconfig": "^9.0.2", + "css-functions-list": "^3.3.3", + "css-tree": "^3.2.1", + "debug": "^4.4.3", + "fast-glob": "^3.3.3", + "fastest-levenshtein": "^1.0.16", + "file-entry-cache": "^11.1.5", + "global-modules": "^2.0.0", + "globby": "^16.2.4", + "globjoin": "^0.1.4", + "html-tags": "^5.1.0", + "ignore": "^7.0.6", + "import-meta-resolve": "^4.2.0", + "mathml-tag-names": "^4.0.0", + "meow": "^14.1.0", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.5.26", + "postcss-safe-parser": "^7.0.1", + "postcss-selector-parser": "^7.1.5", + "postcss-value-parser": "^4.2.0", + "string-width": "^8.2.2", + "supports-hyperlinks": "^4.5.0", + "svg-tags": "^1.0.0", + "table": "^6.9.0", + "write-file-atomic": "^7.0.1" + }, + "bin": { + "stylelint": "bin/stylelint.mjs" + }, + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/stylelint-config-recommended": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-18.0.0.tgz", + "integrity": "sha512-mxgT2XY6YZ3HWWe3Di8umG6aBmWmHTblTgu/f10rqFXnyWxjKWwNdjSWkgkwCtxIKnqjSJzvFmPT5yabVIRxZg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "stylelint": "^17.0.0" + } + }, + "node_modules/stylelint-config-recommended-scss": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-17.0.1.tgz", + "integrity": "sha512-x5DVehzJudcwF0od3sGpgkln2PLLranFE7twwbp7dqDINCyZvwzFkMc6TLhNOvazRiVBJYATQLouJY0xPGB8WA==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-scss": "^4.0.9", + "stylelint-config-recommended": "^18.0.0", + "stylelint-scss": "^7.0.0" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "postcss": "^8.3.3", + "stylelint": "^17.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + } + } + }, + "node_modules/stylelint-scss": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-7.3.0.tgz", + "integrity": "sha512-FBz075AUFoEJ1WQKsgsRr6EJKcjy7jxQjV8xsAqbPcnhNiXnJ7C489vqUyDZccBy1GorewlshERkaMuCyOKXaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^3.3.0", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-syntax-patches-for-csstree": "^1.1.8", + "@csstools/css-tokenizer": "^4.0.0", + "css-tree": "^3.2.1", + "is-plain-object": "^5.1.0", + "known-css-properties": "^0.37.0", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.6", + "postcss-selector-parser": "^7.1.6", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "stylelint": "^16.8.2 || ^17.0.0" + } + }, + "node_modules/stylelint/node_modules/ansi-regex": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/stylelint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/stylelint/node_modules/cosmiconfig": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz", + "integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/stylelint/node_modules/file-entry-cache": { + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.5.tgz", + "integrity": "sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^6.1.23" + } + }, + "node_modules/stylelint/node_modules/flat-cache": { + "version": "6.1.23", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.23.tgz", + "integrity": "sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cacheable": "^2.5.0", + "flatted": "^3.4.2", + "hookified": "^1.15.0" + } + }, + "node_modules/stylelint/node_modules/globby": { + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-16.2.4.tgz", + "integrity": "sha512-c8B/VNLmxRcmqqenRA9t+9IyOjf9+V6lTxPaUJLqOCONdQkWZ0ETYgX0qbtJqPsgCNusT9MZ5Jeidw8Eb9tn2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.5", + "is-path-inside": "^4.0.0", + "micromatch": "^4.0.8", + "slash": "^5.1.0", + "unicorn-magic": "^0.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylelint/node_modules/ignore": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.14.tgz", + "integrity": "sha512-wCBiZ77u1tuWde6G/nfOtLaCTYcFDU5yCzARzK10y+rLhzYoL7a7yEemcopcc2Mzow7gZOIjnV2ym1NYmdiJOQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/stylelint/node_modules/js-yaml": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/stylelint/node_modules/meow": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-14.1.0.tgz", + "integrity": "sha512-EDYo6VlmtnumlcBCbh1gLJ//9jvM/ndXHfVXIFrZVr6fGcwTUyCTFNTLCKuY3ffbK8L/+3Mzqnd58RojiZqHVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylelint/node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylelint/node_modules/string-width": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.3.0.tgz", + "integrity": "sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylelint/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-4.6.0.tgz", + "integrity": "sha512-FL/q/YePS4tFFTNiBOR+zPk2ZrKheqNtmTJ/9OUZ4e1wKHJofDbGQSmSsxS0XNnabSkqexryCaXGvEKz1vf9UQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^5.0.1", + "supports-color": "^10.2.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/supports-hyperlinks/node_modules/has-flag": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-5.0.1.tgz", + "integrity": "sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-parser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.1.0.tgz", + "integrity": "sha512-bwLf38YmY+TDYHJw1Ex0Co8c4yeXuJAo8YnXGZrscxrvYoVVIvLeniEkV1Ks/54VteMnL4FtyN1+P+TZJdUPmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", + "dev": true + }, + "node_modules/svgo": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.5.tgz", + "integrity": "sha512-8SQMzdrvWaD8deUmrnYB+ASyxBVgWUOilg+A75nE/76WdLpj6LopCwiAVvkzkcqy/9b7t2Mg7faFLjg0ZRcZ3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^7.2.0", + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0", + "sax": "^1.5.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/svgo/node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/svgo/node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/synckit": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.12.3.tgz", + "integrity": "sha512-U+/AXT6LWsxnj2vz9HeUuPWBNgdWxjyIEjEPdbJARC9txmgPzo2VEdqnFo4hHaNyMPtycyVJrSaQYeQv/PoyyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@dual-bundle/import-meta-resolve": "^4.2.1", + "@pkgr/core": "^0.3.6" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/tabbable": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.5.0.tgz", + "integrity": "sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/table/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tannin": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tannin/-/tannin-1.2.0.tgz", + "integrity": "sha512-U7GgX/RcSeUETbV7gYgoz8PD7Ni4y95pgIP/Z6ayI3CfhSujwKEBlGFTCRN+Aqnuyf4AN2yHL+L8x+TCGjb9uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tannin/plural-forms": "^1.1.0" + } + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser": { + "version": "5.51.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.51.2.tgz", + "integrity": "sha512-bWnjSNscmuI+GJze6ZupnHP8G/cTcsJF+bXCeQknk2SHQsgbNJnLrqiH9jZ2W4STPVXH2mDKKRX3iwPhc9Cn/Q==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.6.1.tgz", + "integrity": "sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@minify-html/node": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "@swc/html": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "cssnano": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "html-minifier-terser": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "postcss": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/thingies": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-2.6.1.tgz", + "integrity": "sha512-cV/CMGTK3M4MlnJ/0At6ismOw/A0EEniDNScajjz/Br3c1sqE72YD01rGpPTKwd27wAxI5Pr+6+0w8yofzFRYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "^2" + } + }, + "node_modules/third-party-web": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/third-party-web/-/third-party-web-0.30.0.tgz", + "integrity": "sha512-p+PfyL5U0ediGzvwPzMSdD9MnC8rHW6nWJJVLIQvqN7RsSqfaZ+7n4c9GILqH36EYR0eXB9CzAeCRnKFr/9uJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tldts-core": { + "version": "7.4.19", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.19.tgz", + "integrity": "sha512-goNXV6Ho9TIOfpJZanTd6CKG5DyYgYAL+k+22T0SLh8l6+KHIfBUPFuePuSZv05UttIaaLmI9tb7TmGIdK5AkQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tldts-icann": { + "version": "7.4.19", + "resolved": "https://registry.npmjs.org/tldts-icann/-/tldts-icann-7.4.19.tgz", + "integrity": "sha512-lnGUR5BZm5UpNDYMBFGAr9pxS2RTUr/euXuJ9o1irR+zKhsBAFUn7quHoZt8UP/eIwykp4ye5f56bCjMBytbTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.4.19" + } + }, + "node_modules/tmp": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/tmp-promise": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tmp": "^0.2.0" + } + }, + "node_modules/to-buffer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/to-valid-identifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-valid-identifier/-/to-valid-identifier-1.0.0.tgz", + "integrity": "sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/base62": "^1.0.0", + "reserved-identifiers": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tree-dump": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.1.0.tgz", + "integrity": "sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tsyringe": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz", + "integrity": "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^1.9.3" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/tsyringe/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.5.tgz", + "integrity": "sha512-0FHJvLPqZ7KJzp17O13jfsAjsqazgrxBu2zEK95PmUz8lv2+GjRuxUInCr2Rk9Dms3ihN21zJ929ZO43yJ95QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.9", + "for-each": "^0.3.5", + "gopd": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.8.tgz", + "integrity": "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "for-each": "^0.3.5", + "gopd": "^1.2.0", + "is-typed-array": "^1.1.15", + "possible-typed-array-names": "^1.1.0", + "reflect.getprototypeof": "^1.0.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-query-selector": { + "version": "2.12.3", + "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.3.tgz", + "integrity": "sha512-/dtuMZyutHLlrxWhhC1uib00ELUr2dZ8C9B8YzZYsJmeE3V56OxRblrSM2UaF142pcMtak7ehZ6+poWGeehNIg==", + "dev": true, + "license": "MIT" + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.72.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.72.0.tgz", + "integrity": "sha512-r26Fkc8As+mZvqp7SLsMmNFyYUup/6CjK7/eHBHmhOw6GH6n6YFxHVFrG0jN1c5CMpDzelLPxvgeCOzx7BjN+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.72.0", + "@typescript-eslint/parser": "8.72.0", + "@typescript-eslint/typescript-estree": "8.72.0", + "@typescript-eslint/utils": "8.72.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz", + "integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", + "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", + "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicorn-magic": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.4.1.tgz", + "integrity": "sha512-lzlXPoVB0Uy/FvTaUgX39RbixYiSKoc3JSoSf01+0c2B6FR3qdYIPN7Qjo/AV7n6sqLCk0509cT3LRj1oSZ1+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universal-github-app-jwt": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-1.2.0.tgz", + "integrity": "sha512-dncpMpnsKBk0eetwfN8D8OUHGfiDhhJ+mtsbMl+7PfW7mYjiH8LIcqRmYMtzYLgSh47HjfdBtrBwIQ/gizKR3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/jsonwebtoken": "^9.0.0", + "jsonwebtoken": "^9.0.2" + } + }, + "node_modules/universal-user-agent": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unrs-resolver": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz", + "integrity": "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.4" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.12.2", + "@unrs/resolver-binding-android-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-x64": "1.12.2", + "@unrs/resolver-binding-freebsd-x64": "1.12.2", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-arm64-musl": "1.12.2", + "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-loong64-musl": "1.12.2", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2", + "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-musl": "1.12.2", + "@unrs/resolver-binding-openharmony-arm64": "1.12.2", + "@unrs/resolver-binding-wasm32-wasi": "1.12.2", + "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2", + "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2", + "@unrs/resolver-binding-win32-x64-msvc": "1.12.2" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.4.tgz", + "integrity": "sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/upper-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", + "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/upper-case-first": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", + "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "file-loader": "*", + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "file-loader": { + "optional": true + } + } + }, + "node_modules/url-loader/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/url-loader/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/url-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/url-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sync-external-store": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.7.0.tgz", + "integrity": "sha512-6L+EeigHMQhdaIPNIFUKwfWJSwWFQ8gJbJ2DLOs5sDIegTwR9fRxvnM3uciHKjIZhFz+KAv2emhWMRvDmMcY8A==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.3.tgz", + "integrity": "sha512-Sc5u1VVln93y1Xnz4R+CunOZc7R9LUcTmhj7Rc7Eg3HVcf7rVn2FOoa812fe0W8p3jD7xeoGSYbfqCGGH2B6xw==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", + "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wasm-feature-detect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz", + "integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wasm-vips": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/wasm-vips/-/wasm-vips-0.0.18.tgz", + "integrity": "sha512-AJyCvxZj/3qceKNnh+YyEobu/IaJFoPN7x7SxyyHmYBS3kASMqJqxQEuN0ZHKQDWsCJ8armfx4Tq3uKrNc+nMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=17.0.0" + } + }, + "node_modules/watchpack": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.6.3.tgz", + "integrity": "sha512-s3NZy0QpTY9qGqzc0wtz4qI28z3+5qKbbAec4MV4G9N3CfgZ9wgvQRr0cl2jh/tqkWG1NWHkmVMHZpCamev5Wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/web-features": { + "version": "3.44.0", + "resolved": "https://registry.npmjs.org/web-features/-/web-features-3.44.0.tgz", + "integrity": "sha512-K/+YJI7uVE/b4G8SU67/F/4j81/ajJ6MfBCZyuQaqyuPlVXF8cd6dLqBznXIWJI9nwnvuY2dapKK08OHzTjYww==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/web-vitals": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", + "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/webdriver-bidi-protocol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-1.0.1.tgz", + "integrity": "sha512-WXWs/aXF2a6Al1Z2/V/7Gp2oPZOAAmHjJOTmv4KCXRJ122IMj3tSSMc6a4q3K4QOmhQbse/Ahr+oTVJOU2dUiw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/webpack": { + "version": "5.112.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.112.2.tgz", + "integrity": "sha512-B2tp1LI9D8GVpPDqprdt12ukTkXXahj9MzqSWJq4faSPgOUM7CdZaGAB6HrL/8E6UwbBpdj4vzWDbKoFnVoU6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "browserslist": "^4.28.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.26.0", + "es-module-lexer": "^2.1.0", + "events": "^3.2.0", + "graceful-fs": "^4.2.11", + "mime-db": "^1.54.0", + "minimizer-webpack-plugin": "^5.13.0", + "schema-utils": "^4.8.1", + "tapable": "^2.3.0", + "watchpack": "^2.6.3", + "webpack-sources": "^3.6.0" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-bundle-analyzer": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz", + "integrity": "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@discoveryjs/json-ext": "0.5.7", + "acorn": "^8.0.4", + "acorn-walk": "^8.0.0", + "commander": "^7.2.0", + "debounce": "^1.2.1", + "escape-string-regexp": "^4.0.0", + "gzip-size": "^6.0.0", + "html-escaper": "^2.0.2", + "opener": "^1.5.2", + "picocolors": "^1.0.0", + "sirv": "^2.0.3", + "ws": "^7.3.1" + }, + "bin": { + "webpack-bundle-analyzer": "lib/bin/analyzer.js" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/ws": { + "version": "7.5.13", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.13.tgz", + "integrity": "sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==", + "dev": true, + "license": "MIT", + "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 + } + } + }, + "node_modules/webpack-cli": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", + "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^2.1.1", + "@webpack-cli/info": "^2.0.2", + "@webpack-cli/serve": "^2.0.5", + "colorette": "^2.0.14", + "commander": "^10.0.1", + "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/webpack-dev-middleware": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-8.5.0.tgz", + "integrity": "sha512-+rXF28E5GKr7VnLH5qJaGIKLNPkthlA0VhqBFp/K6mjjCzOd6q34vYc2/3/ysRTjxmN01S98r1LwTTpWXDzWWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "memfs": "^4.68.2", + "mime-db": "^1.54.0", + "range-parser": "^1.3.0", + "schema-utils": "^4.3.3" + }, + "engines": { + "node": ">= 20.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "optionalDependencies": { + "ws": "^8.18.3" + }, + "peerDependencies": { + "webpack": "^5.101.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-middleware/node_modules/range-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/webpack-dev-server": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-6.1.0.tgz", + "integrity": "sha512-T52SEoefJWZDgiYRMeQn4q/w9EVIZjBNWfy/WNRR2+EosPwpUmdUY1Wj0b1Mk7+svgKXojdQTP425fJ+DN58yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/bonjour": "^3.5.13", + "@types/connect-history-api-fallback": "^1.5.4", + "@types/express": "^5.0.6", + "@types/express-serve-static-core": "^5.1.1", + "@types/serve-index": "^1.9.4", + "@types/serve-static": "^2.2.0", + "@types/ws": "^8.18.1", + "bonjour-service": "^1.3.0", + "chokidar": "^5.0.0", + "compression": "^1.8.1", + "connect-history-api-fallback": "^2.0.0", + "express": "^5.2.1", + "graceful-fs": "^4.2.11", + "http-proxy-middleware": "^4.1.1", + "ipaddr.js": "^2.3.0", + "launch-editor": "^2.14.1", + "open": "^11.0.0", + "p-retry": "^8.0.0", + "schema-utils": "^4.3.3", + "selfsigned": "^5.5.0", + "serve-index": "^1.9.2", + "tinyglobby": "^0.2.15", + "webpack-dev-middleware": "^8.4.0", + "ws": "^8.20.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 22.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.101.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-server/node_modules/body-parser": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.4.0.tgz", + "integrity": "sha512-Ifk1KcLrxaGt1occQagQNFNNOqRt1LFuehEnHHzB8Z6cRgIxRU1TBaiIsUiCdA+gy5PL7Y1IX31DNKkRmxtXjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^2.1.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.3", + "on-finished": "^2.4.1", + "qs": "^6.16.1", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/webpack-dev-server/node_modules/content-disposition": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-2.0.1.tgz", + "integrity": "sha512-e+H0ZXHSWYrENhQzw1LPuP4oF5MzVKmDU6d3hxlvaPEYLLg62MxtQNPRx4SYSuYJSBUgnQIG4HIN2tEtNv7Dog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/webpack-dev-server/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/webpack-dev-server/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/webpack-dev-server/node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-dev-server/node_modules/express": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.3.0.tgz", + "integrity": "sha512-qCqy1xSoaugxOD4IVHb1ZWdkvCF2W3aE2okKouWj8VwJ+ztskSiXoBnCSJqfKT+cVPJhuJqe9YLbY425+Zh00g==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.3.0", + "content-disposition": "^2.0.1", + "content-type": "^2.0.0", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.8", + "qs": "^6.16.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.1.0", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/webpack-dev-server/node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/webpack-dev-server/node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/webpack-dev-server/node_modules/ipaddr.js": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.5.0.tgz", + "integrity": "sha512-aq+t5NAc+cS6rZQQVWC2x98CPqGtKKTMDd4Gaodv0wShnItdKg/51djkGJ1hqH+Oy0ivDftCbSLCQob8zso01w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/webpack-dev-server/node_modules/media-typer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", + "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/webpack-dev-server/node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-dev-server/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-server/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/webpack-dev-server/node_modules/negotiator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz", + "integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "content-type": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/webpack-dev-server/node_modules/open": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/open/-/open-11.0.4.tgz", + "integrity": "sha512-++Zlftm0kVLPmzC06t6epuWmcRMDbI4z5P3NNX979WA/k23+NtSOynEGzsVfZwguKw2mi5umVgnBlJQMwRz4Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.5.1", + "define-lazy-prop": "^3.0.0", + "is-in-ssh": "^1.0.0", + "is-inside-container": "^1.0.0", + "powershell-utils": "^0.2.1", + "wsl-utils": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack-dev-server/node_modules/qs": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.1.tgz", + "integrity": "sha512-5Px6J7rfHBAGUhNQ3es4YzcOdBBw0SDcVtThJml4RdnKw4tKs2epA0kPZ+K8X+WiHktT93RHVcQ/3ym5aTCgtg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/webpack-dev-server/node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/webpack-dev-server/node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/webpack-dev-server/node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/webpack-dev-server/node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "dev": true, + "license": "MIT", + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/webpack-merge": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.6.0.tgz", + "integrity": "sha512-EIMmPVvNI0CYXyRPp34F2Qk7W37/BZVZIofS6LAUnrNjCVahS+hO4mxwFJDjQjODTGZDtVzLYoN2+1dYkhk9qA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/es-module-lexer": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/when-exit": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/when-exit/-/when-exit-2.1.5.tgz", + "integrity": "sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg==", + "dev": true, + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.24", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.24.tgz", + "integrity": "sha512-wk4Mf4pR5mRP7eYuuTBCIQ9d0ud2Fv2jRLQpfgnRjbOxAFHmjKFValgTpitVKzJJS8ajnYQV2Du1SZ8j6b/EUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-7.0.1.tgz", + "integrity": "sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg==", + "dev": true, + "license": "ISC", + "dependencies": { + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/wsl-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-1.0.1.tgz", + "integrity": "sha512-c16PKLIRxBYpR3jSWsdS7SFp0COm5SOQdiFjY818dYFHJbMGuLBszpF07LBWmOS1uzrHa9VGdynsEgoeKy0oOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0", + "powershell-utils": "^0.2.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wsl-utils/node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xdg-basedir": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", + "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xml-naming": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.3.0.tgz", + "integrity": "sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.7.tgz", + "integrity": "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + }, + "node_modules/zstddec": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.2.0.tgz", + "integrity": "sha512-oyPnDa1X5c13+Y7mA/FDMNJrn4S8UNBe0KCqtDmor40Re7ALrPN6npFwyYVRRh+PqozZQdeg23QtbcamZnG5rA==", + "dev": true, + "license": "MIT AND BSD-3-Clause" + } + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/package.json b/plugins/locked-payment-methods-for-woocommerce/package.json new file mode 100644 index 0000000..04c2391 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/package.json @@ -0,0 +1,60 @@ +{ + "name": "@ahegyes/locked-payment-methods-for-woocommerce", + "private": true, + "description": "DWS WordPress plugin template — wp-env, script and style tooling.", + "license": "GPL-3.0-or-later", + "repository": { + "type": "git", + "url": "git+https://github.com/ahegyes/wc-locked-payment-methods.git" + }, + "bugs": "https://github.com/ahegyes/wc-locked-payment-methods/issues", + "engines": { + "node": ">=26", + "npm": ">=11" + }, + "devDependencies": { + "@ahegyes/wordpress-configs": "git+https://github.com/ahegyes/wordpress-configs.git#8d53efd292e4abee4a9c57b0e379434f54bedbe9", + "@axe-core/playwright": "^4.14.0", + "@playwright/test": "^1.64.1", + "@types/node": "^26.6.5", + "@types/react": "^19.3.0", + "@wordpress/block-editor": "^19.0.0", + "@wordpress/blocks": "^17.0.0", + "@wordpress/e2e-test-utils-playwright": "^3.1.0", + "@wordpress/env": "^12.0.0", + "@wordpress/i18n": "^7.0.0", + "@wordpress/scripts": "^37.0.0", + "typescript": "^6.0.3" + }, + "scripts": { + "setup": "npm run packages-install:all && npm run build && npm run wp-env:start", + "packages-install:all": "composer packages-install && npm run packages-install", + "packages-update:all": "composer packages-update && npm run packages-update && npm run packages-update:wp", + "packages-install": "npm install", + "packages-update": "npm update", + "packages-update:wp": "wp-scripts packages-update", + "wp-env": "wp-env", + "wp-env:start": "wp-env start", + "wp-env:stop": "wp-env stop", + "wp-env:update": "wp-env start --update", + "wp-env:destroy": "wp-env destroy", + "wp-env:tests:start": "wp-env --config .wp-env.tests.json start", + "wp-env:tests:stop": "wp-env --config .wp-env.tests.json stop", + "wp-env:tests:update": "wp-env --config .wp-env.tests.json start --update", + "wp-env:tests:destroy": "wp-env --config .wp-env.tests.json destroy", + "build": "npm run build:blocks && npm run build:assets", + "build:blocks": "wp-scripts build --webpack-src-dir=blocks/src --output-path=blocks/build --blocks-manifest", + "build:assets": "wp-scripts build editor=./assets/src/editor.ts greeting=./assets/src/greeting.scss --output-path=assets/build", + "start": "npm run start:blocks & npm run start:assets & wait", + "start:blocks": "wp-scripts start --webpack-src-dir=blocks/src --output-path=blocks/build --blocks-manifest", + "start:assets": "wp-scripts start editor=./assets/src/editor.ts greeting=./assets/src/greeting.scss --output-path=assets/build", + "lint:scripts": "wp-scripts lint-js blocks/src assets/src tests/e2e", + "lint:styles": "wp-scripts lint-style 'blocks/src/**/*.scss' 'assets/src/**/*.scss'", + "lint:types": "tsc", + "audit": "composer audit && npm audit", + "audit:fix": "npm audit fix", + "check:engines": "wp-scripts check-engines", + "check:licenses": "wp-scripts check-licenses", + "test:e2e": "playwright test --config=playwright.config.js" + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/phpcs.dist.xml b/plugins/locked-payment-methods-for-woocommerce/phpcs.dist.xml new file mode 100644 index 0000000..de8a13d --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/phpcs.dist.xml @@ -0,0 +1,18 @@ + + + + + + + */blocks/build/* + */assets/build/* + + + + + + + + + + diff --git a/plugins/locked-payment-methods-for-woocommerce/phpcs.tests.dist.xml b/plugins/locked-payment-methods-for-woocommerce/phpcs.tests.dist.xml new file mode 100644 index 0000000..1b2ac21 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/phpcs.tests.dist.xml @@ -0,0 +1,4 @@ + + + + diff --git a/plugins/locked-payment-methods-for-woocommerce/phpstan.dist.neon b/plugins/locked-payment-methods-for-woocommerce/phpstan.dist.neon new file mode 100644 index 0000000..4bb3bb7 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/phpstan.dist.neon @@ -0,0 +1,20 @@ +# The WordPress extensions are listed here because composer.json keeps them from installing themselves, and before configs' profile because wp-compat's neon resets WPCompat.requiresAtLeast to null. +includes: + - vendor/szepeviktor/phpstan-wordpress/extension.neon + - vendor/johnbillion/wp-compat/extension.neon + - vendor/swissspidy/phpstan-no-private/rules.neon + - vendor/ahegyes/wordpress-configs/php/quality-assurance/phpstan.dist.neon + +parameters: + paths: + - locked-payment-methods-for-woocommerce.php + - functions.php + - uninstall.php + - includes + - src + scanFiles: + - vendor/php-stubs/woocommerce-stubs/woocommerce-stubs.php + - vendor/php-stubs/woocommerce-stubs/woocommerce-packages-stubs.php + # The scoped packages load through vendor-prefixed/scoper-autoload.php, which PHPStan does not read. + scanDirectories: + - vendor-prefixed diff --git a/plugins/locked-payment-methods-for-woocommerce/phpstan.safeguards.neon b/plugins/locked-payment-methods-for-woocommerce/phpstan.safeguards.neon new file mode 100644 index 0000000..2d217fd --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/phpstan.safeguards.neon @@ -0,0 +1,13 @@ +# The safeguards load before the floor gate, so they are analyzed at the oldest PHP and WordPress the bootstrap package runs on, which the framework's phpstan.bootstrap.neon sets; the WordPress extensions are listed because composer.json keeps them from installing themselves. +includes: + - vendor/szepeviktor/phpstan-wordpress/extension.neon + - vendor/johnbillion/wp-compat/extension.neon + - vendor/swissspidy/phpstan-no-private/rules.neon + - vendor/ahegyes/wordpress-configs/php/quality-assurance/phpstan.dist.neon + +parameters: + paths: + - includes/safeguards.php + phpVersion: 70400 + WPCompat: + requiresAtLeast: '5.2' diff --git a/plugins/locked-payment-methods-for-woocommerce/phpunit.dist.xml b/plugins/locked-payment-methods-for-woocommerce/phpunit.dist.xml new file mode 100644 index 0000000..c04a1e5 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/phpunit.dist.xml @@ -0,0 +1,32 @@ + + + + + tests/Unit + + + tests/Integration + + + + + + + + + + src + + + diff --git a/plugins/locked-payment-methods-for-woocommerce/playwright.config.js b/plugins/locked-payment-methods-for-woocommerce/playwright.config.js new file mode 100644 index 0000000..59effb6 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/playwright.config.js @@ -0,0 +1,11 @@ +const { defineConfig } = require( '@playwright/test' ); +// The port of .wp-env.tests.json, so the suite runs on the production tests environment and leaves the development one's data alone. +const baseConfig = require( '@ahegyes/wordpress-configs/node/playwright.config.base.js' )( { port: 8827 } ); + +module.exports = defineConfig( { + ...baseConfig, + webServer: { + ...baseConfig.webServer, + command: 'npm run wp-env:tests:start', + }, +} ); diff --git a/plugins/locked-payment-methods-for-woocommerce/scoper.inc.php b/plugins/locked-payment-methods-for-woocommerce/scoper.inc.php new file mode 100644 index 0000000..11b1f9b --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/scoper.inc.php @@ -0,0 +1,8 @@ + 'greeting' ) ); + } + + /** + * Enqueues the greeting's stylesheet on the account dashboard. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @return void + */ + public function enqueue_style(): void { + if ( ! is_account_page() || is_wc_endpoint_url() ) { + return; + } + + dws_lpmwc_register_style( Plugin::SLUG . '-greeting', 'assets/build/greeting.css' ); + wp_enqueue_style( Plugin::SLUG . '-greeting' ); + } + + // endregion +} diff --git a/plugins/locked-payment-methods-for-woocommerce/src/Feature/index.php b/plugins/locked-payment-methods-for-woocommerce/src/Feature/index.php new file mode 100644 index 0000000..f767346 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/src/Feature/index.php @@ -0,0 +1 @@ + + */ + public const array COMPONENTS = array( + Scheduler::class, + AdminNotices::class, + Settings::class, + Blocks::class, + Feature\Feature::class, + ); + + /** + * The boot boundary, whose status tells whether the plugin booted. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @var PluginBoot + */ + public readonly PluginBoot $boot; + + /** + * The scheduler, which registers its WP-Cron schedules even while the plugin is blocked. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @var Scheduler + */ + public readonly Scheduler $scheduler; + + /** + * The migration runner, once the composition built it. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @var MigrationRunner|null + */ + protected(set) ?MigrationRunner $migrations = null; + + /** + * The kernel, once the composition built it, which tells which components loaded and which were gated off. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @var PluginKernel|null + */ + protected(set) ?PluginKernel $kernel = null; + + /** + * The logger every part of the plugin writes to. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @var PsrLoggerInterface + */ + protected readonly PsrLoggerInterface $logger; + + // endregion + + // region MAGIC METHODS + + /** + * Constructor. + * + * @since 2.0.0 + * @version 2.0.0 + */ + public function __construct() { + $this->logger = new WooCommerceLogger( self::SLUG, \defined( 'DWS_LPMWC_DEBUG' ) && true === \constant( 'DWS_LPMWC_DEBUG' ) ? 'debug' : null ); + $this->scheduler = new Scheduler( self::PREFIX ); + $this->boot = new PluginBoot( DWS_LPMWC_FILE, self::PREFIX, $this->logger ); + } + + // endregion + + // region METHODS + + /** + * Runs the composition inside the framework's boot boundary. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @return void + */ + public function boot(): void { + $this->boot->run( $this->compose( ... ) ); + } + + /** + * Unschedules the footprint's jobs on the current site, logging each one that could not be unscheduled. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @return void + */ + public function deactivate(): void { + foreach ( array( 'cron_hooks', 'as_hooks' ) as $kind ) { + foreach ( dws_lpmwc_get_footprint()[ $kind ] ?? array() as $hook ) { + $unscheduled = $this->scheduler->unschedule_hook( $hook ); + if ( true !== $unscheduled ) { + $this->logger->warning( + 'Could not unschedule {hook} on deactivation: {error}', + array( + 'hook' => $hook, + 'error' => $unscheduled->get_error_message(), + 'exception' => $unscheduled->get_error_data(), + ) + ); + } + } + } + } + + // endregion + + // region HELPERS + + /** + * Builds the plugin and boots its components, returning false when WooCommerce is missing or too old. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @return bool + */ + protected function compose(): bool { + $this->scheduler->register_hooks(); // Before the WooCommerce check, so the plugin's recurring WP-Cron events keep their schedules while it is blocked. + if ( ! ( new WooCommerceRequirement( DWS_LPMWC_FILE ) )->check() ) { + return false; + } + + $this->migrations = new MigrationRunner( DWS_LPMWC_FILE, self::PREFIX . '_revision', dws_lpmwc_get_migration_steps(), $this->logger, $this->scheduler ); + $this->migrations->register_hooks(); + + $this->kernel = new PluginKernel( new ContainerBuilder()->addDefinitions( $this->get_definitions( $this->logger, $this->scheduler ) )->build() ); + $this->kernel->boot( self::COMPONENTS ); + + return true; + } + + /** + * Returns the container's definitions of the services the root builds, which the kernel's components share. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param PsrLoggerInterface $logger The plugin's logger. + * @param Scheduler $scheduler The plugin's scheduler. + * + * @return array + */ + protected function get_definitions( PsrLoggerInterface $logger, Scheduler $scheduler ): array { + return array( + PsrLoggerInterface::class => static fn (): PsrLoggerInterface => $logger, + Scheduler::class => static fn (): Scheduler => $scheduler, + SchedulerInterface::class => static fn (): Scheduler => $scheduler, + AdminNotices::class => static fn (): AdminNotices => new AdminNotices( self::PREFIX, $logger ), + ); + } + + // endregion +} diff --git a/plugins/locked-payment-methods-for-woocommerce/src/Settings.php b/plugins/locked-payment-methods-for-woocommerce/src/Settings.php new file mode 100644 index 0000000..4a9cfc4 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/src/Settings.php @@ -0,0 +1,123 @@ + 'title', + 'id' => Plugin::PREFIX . '_greeting_options', + 'title' => __( 'Greeting', 'locked-payment-methods-for-woocommerce' ), + 'desc' => __( 'Greets returning customers on their account dashboard.', 'locked-payment-methods-for-woocommerce' ), + ), + array( + 'type' => 'checkbox', + 'id' => DWS_LPMWC_OPTION_FEATURE, + 'title' => __( 'Greeting', 'locked-payment-methods-for-woocommerce' ), + 'desc' => __( 'Greet returning customers', 'locked-payment-methods-for-woocommerce' ), + 'default' => wc_bool_to_string( DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_FEATURE ] ), + ), + array( + 'type' => 'text', + 'id' => DWS_LPMWC_OPTION_GREETING, + 'title' => __( 'Message', 'locked-payment-methods-for-woocommerce' ), + 'default' => '', + 'placeholder' => dws_lpmwc_get_option_text( DWS_LPMWC_OPTION_GREETING ), // A default would be saved in the admin's language with the first save of the tab. + ), + array( + 'type' => 'number', + 'id' => DWS_LPMWC_OPTION_GREETING_ORDERS, + 'title' => __( 'Orders before the greeting', 'locked-payment-methods-for-woocommerce' ), + 'desc' => __( 'Customers see the greeting once they have placed this many orders.', 'locked-payment-methods-for-woocommerce' ), + 'default' => (string) DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_GREETING_ORDERS ], + 'custom_attributes' => array( + 'min' => '1', + 'step' => '1', + ), + 'validate' => 'dws_lpmwc_validate_greeting_orders', + ), + array( + 'type' => 'sectionend', + 'id' => Plugin::PREFIX . '_greeting_options', + ), + array( + 'type' => 'title', + 'id' => Plugin::PREFIX . '_data_options', + 'title' => __( 'Data', 'locked-payment-methods-for-woocommerce' ), + ), + array( + 'type' => 'checkbox', + 'id' => DWS_LPMWC_OPTION_DELETE_DATA, + 'title' => __( 'Data on uninstall', 'locked-payment-methods-for-woocommerce' ), + 'desc' => __( 'Delete the plugin\'s data when it is uninstalled', 'locked-payment-methods-for-woocommerce' ), + 'default' => wc_bool_to_string( DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_DELETE_DATA ] ), + ), + array( + 'type' => 'sectionend', + 'id' => Plugin::PREFIX . '_data_options', + ), + ); + } + + // endregion + + // region HOOKS + + /** + * Adds the plugin's settings tab, which WooCommerce's settings page class must exist to construct. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param array $pages The settings tabs. + * + * @return array + */ + public function add_settings_tab( array $pages ): array { + $pages[] = new SettingsTab( + Plugin::PREFIX, + array( + 'label' => __( 'Locked Payment Methods for WooCommerce', 'locked-payment-methods-for-woocommerce' ), + 'sections' => array( '' => __( 'General', 'locked-payment-methods-for-woocommerce' ) ), + ), + $this + ); + + return $pages; + } + + // endregion +} diff --git a/plugins/locked-payment-methods-for-woocommerce/src/index.php b/plugins/locked-payment-methods-for-woocommerce/src/index.php new file mode 100644 index 0000000..f767346 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/src/index.php @@ -0,0 +1 @@ + array( + 'wp-i18n', + array( + 'id' => '@wordpress/interactivity', + 'import' => 'dynamic', + ), + ), + 'version' => 'fixture', +); diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/AssetsTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/AssetsTest.php new file mode 100644 index 0000000..f841d90 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/AssetsTest.php @@ -0,0 +1,45 @@ + 'defer' ) ) ); + + $script = wp_scripts()->registered[ self::HANDLE ]; + self::assertSame( plugins_url( 'tests/Fixtures/built.js', DWS_LPMWC_FILE ), $script->src ); + self::assertSame( array( 'wp-i18n' ), $script->deps ); + self::assertSame( 'fixture', $script->ver ); + self::assertSame( 'defer', $script->extra['strategy'] ?? null ); + self::assertSame( Plugin::SLUG, $script->textdomain ); + self::assertSame( plugin_dir_path( DWS_LPMWC_FILE ) . 'languages', $script->translations_path ); + } + + public function test_a_stylesheet_registers_with_its_generated_version_and_its_right_to_left_variant(): void { + self::assertTrue( dws_lpmwc_register_style( self::HANDLE, 'tests/Fixtures/built.css', 'print' ) ); + + $style = wp_styles()->registered[ self::HANDLE ]; + self::assertSame( plugins_url( 'tests/Fixtures/built.css', DWS_LPMWC_FILE ), $style->src ); + self::assertSame( 'fixture', $style->ver ); + self::assertSame( 'print', $style->args ); + self::assertSame( 'replace', $style->extra['rtl'] ?? null ); + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/BlocksTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/BlocksTest.php new file mode 100644 index 0000000..db98a41 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/BlocksTest.php @@ -0,0 +1,42 @@ +get_registered( 'locked-payment-methods-for-woocommerce/example-notice' ); + + self::assertInstanceOf( \WP_Block_Type::class, $block ); + self::assertSame( array( 'locked-payment-methods-for-woocommerce-example-notice-editor-script' ), $block->editor_script_handles ); + self::assertSame( array( 'locked-payment-methods-for-woocommerce-example-notice-editor-style' ), $block->editor_style_handles ); + self::assertSame( array( 'locked-payment-methods-for-woocommerce-example-notice-style' ), $block->style_handles ); + } + + public function test_the_block_editor_gets_the_editor_script_with_its_generated_dependencies(): void { + $blocks = new Blocks(); + $blocks->register_hooks(); + self::assertSame( 10, has_action( 'enqueue_block_editor_assets', array( $blocks, 'enqueue_editor_script' ) ) ); + remove_action( 'init', array( $blocks, 'register_blocks' ) ); + remove_action( 'enqueue_block_editor_assets', array( $blocks, 'enqueue_editor_script' ) ); + + $blocks->enqueue_editor_script(); + + $asset = require plugin_dir_path( DWS_LPMWC_FILE ) . 'assets/build/editor.asset.php'; + self::assertTrue( wp_script_is( Plugin::SLUG . '-editor', 'enqueued' ) ); + self::assertSame( $asset['dependencies'], wp_scripts()->registered[ Plugin::SLUG . '-editor' ]->deps ); + self::assertSame( $asset['version'], wp_scripts()->registered[ Plugin::SLUG . '-editor' ]->ver ); + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/DeactivationTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/DeactivationTest.php new file mode 100644 index 0000000..fe28012 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/DeactivationTest.php @@ -0,0 +1,32 @@ +scheduler->unschedule_hook( $hook ); + } + } + + public function test_deactivation_unschedules_every_hook_of_the_footprint(): void { + $scheduler = dws_lpmwc_plugin()->scheduler; + $hooks = \array_map( static fn ( string $hook ): ScheduledHook => new ScheduledHook( $hook ), dws_lpmwc_get_footprint()['cron_hooks'] ); + foreach ( $hooks as $hook ) { + self::assertTrue( $scheduler->enqueue( $hook ) ); + } + + do_action( 'deactivate_' . plugin_basename( DWS_LPMWC_FILE ), false ); + + foreach ( $hooks as $hook ) { + self::assertFalse( $scheduler->is_scheduled( $hook ), "The hook $hook->hook is still scheduled." ); + } + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/Feature/FeatureDetailTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/Feature/FeatureDetailTest.php new file mode 100644 index 0000000..046a3cc --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/Feature/FeatureDetailTest.php @@ -0,0 +1,130 @@ +stored[ $key ] = get_option( $key ); + } + + $customer = wp_insert_user( + array( + 'user_login' => 'dws_lpmwc_customer_' . \wp_generate_password( 8, false ), + 'user_pass' => wp_generate_password(), + 'role' => 'customer', + ) + ); + self::assertIsInt( $customer ); + $this->customer = $customer; + wp_set_current_user( $customer ); + } + + #[After] + protected function clean_up(): void { + foreach ( $this->stored as $key => $value ) { + false === $value ? delete_option( $key ) : update_option( $key, $value ); + } + + foreach ( $this->orders as $order ) { + $order->delete( true ); + } + + wp_set_current_user( 0 ); + wp_delete_user( $this->customer ); + } + + public function test_the_greeting_prints_on_the_account_dashboard(): void { + $detail = new FeatureDetail(); + $detail->register_hooks(); + + self::assertSame( 10, has_action( 'woocommerce_account_dashboard', array( $detail, 'print_greeting' ) ) ); + self::assertSame( 10, has_action( 'wp_enqueue_scripts', array( $detail, 'enqueue_style' ) ) ); + remove_action( 'woocommerce_account_dashboard', array( $detail, 'print_greeting' ) ); + remove_action( 'wp_enqueue_scripts', array( $detail, 'enqueue_style' ) ); + } + + public function test_the_greeting_style_loads_on_the_account_dashboard_only(): void { + ( new FeatureDetail() )->enqueue_style(); + self::assertFalse( wp_style_is( 'locked-payment-methods-for-woocommerce-greeting' ) ); + + add_filter( 'woocommerce_is_account_page', '__return_true' ); + try { + ( new FeatureDetail() )->enqueue_style(); + } finally { + remove_filter( 'woocommerce_is_account_page', '__return_true' ); + } + + self::assertTrue( wp_style_is( 'locked-payment-methods-for-woocommerce-greeting' ) ); + self::assertSame( plugins_url( 'assets/build/greeting.css', DWS_LPMWC_FILE ), wp_styles()->registered['locked-payment-methods-for-woocommerce-greeting']->src ); + wp_dequeue_style( 'locked-payment-methods-for-woocommerce-greeting' ); + wp_deregister_style( 'locked-payment-methods-for-woocommerce-greeting' ); + } + + public function test_a_customer_whose_orders_reach_the_set_number_is_greeted_as_text(): void { + update_option( DWS_LPMWC_OPTION_GREETING, 'Hi & welcome' ); + update_option( DWS_LPMWC_OPTION_GREETING_ORDERS, '2' ); + $this->place_orders( 2 ); + + $notice = \Dom\HTMLDocument::createFromString( '' . $this->print() . '', \LIBXML_NOERROR )->querySelector( '[role="status"]' ); + + self::assertNotNull( $notice ); + self::assertSame( 'greeting', $notice->getAttribute( 'data-locked-payment-methods-for-woocommerce' ) ); + self::assertSame( 'Hi & welcome', \trim( (string) $notice->textContent ) ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- A DOM property. + } + + public function test_a_customer_below_the_set_number_is_not_greeted(): void { + update_option( DWS_LPMWC_OPTION_GREETING_ORDERS, '2' ); + $this->place_orders( 1 ); + + self::assertSame( '', $this->print() ); + } + + public function test_an_unsaved_greeting_prints_its_translated_default(): void { + delete_option( DWS_LPMWC_OPTION_GREETING ); + delete_option( DWS_LPMWC_OPTION_GREETING_ORDERS ); + $this->place_orders( 1 ); + + self::assertStringContainsString( esc_html( dws_lpmwc_get_option_text( DWS_LPMWC_OPTION_GREETING ) ), $this->print() ); + } + + public function test_an_empty_greeting_prints_its_translated_default(): void { + update_option( DWS_LPMWC_OPTION_GREETING, '' ); + $this->place_orders( 1 ); + + self::assertStringContainsString( esc_html( dws_lpmwc_get_option_text( DWS_LPMWC_OPTION_GREETING ) ), $this->print() ); + } + + protected function place_orders( int $count ): void { + for ( $placed = 0; $placed < $count; $placed++ ) { + $this->orders[] = wc_create_order( array( 'customer_id' => $this->customer ) ); + } + } + + protected function print(): string { + \ob_start(); + ( new FeatureDetail() )->print_greeting(); + + return (string) \ob_get_clean(); + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/Feature/FeatureTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/Feature/FeatureTest.php new file mode 100644 index 0000000..1f6d327 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/Feature/FeatureTest.php @@ -0,0 +1,63 @@ + array( 'yes', true ), + 'off' => array( 'no', false ), + 'unset' => array( null, false ), + ); + } + + #[\Override] + protected function setUp(): void { + $this->stored = get_option( DWS_LPMWC_OPTION_FEATURE ); + } + + #[After] + protected function restore_row(): void { + false === $this->stored ? delete_option( DWS_LPMWC_OPTION_FEATURE ) : update_option( DWS_LPMWC_OPTION_FEATURE, $this->stored ); + } + + #[DataProvider( 'stored_toggles' )] + public function test_the_feature_loads_only_where_the_greeting_is_switched_on( ?string $stored, bool $expected ): void { + \is_null( $stored ) ? delete_option( DWS_LPMWC_OPTION_FEATURE ) : update_option( DWS_LPMWC_OPTION_FEATURE, $stored ); + + self::assertSame( $expected, Feature::should_load() ); + } + + #[RunInSeparateProcess] + public function test_with_the_greeting_switched_on_every_declared_component_boots(): void { + $declared = array(); + $pending = Plugin::COMPONENTS; + while ( array() !== $pending ) { + $class = \array_shift( $pending ); + $declared[] = $class; + if ( \is_a( $class, CompositeComponentInterface::class, true ) ) { + \array_unshift( $pending, ...$class::get_child_component_classes() ); + } + } + + update_option( DWS_LPMWC_OPTION_FEATURE, 'yes' ); + $plugin = new Plugin(); + $plugin->boot(); + + self::assertSame( 'booted', $plugin->boot->status ); + self::assertSame( $declared, $plugin->kernel?->resolved ); + self::assertSame( array(), $plugin->kernel->skipped ); + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/MetadataTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/MetadataTest.php new file mode 100644 index 0000000..d542c6e --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/MetadataTest.php @@ -0,0 +1,57 @@ + $name === $text ? self::TRANSLATION : $translation; + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/MigrationsTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/MigrationsTest.php new file mode 100644 index 0000000..ceb5ddc --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/MigrationsTest.php @@ -0,0 +1,80 @@ +stored[ $key ] = get_option( $key ); + } + } + + #[After] + protected function restore_rows(): void { + foreach ( $this->stored as $key => $value ) { + false === $value ? delete_option( $key ) : update_option( $key, $value ); + } + + update_option( Plugin::PREFIX . '_revision', \count( dws_lpmwc_get_migration_steps() ) ); + } + + // The runner's time budget lasts for its request, and this process's bootstrap spends it when it migrates a new site, so the row runs as a request of its own. + #[RunInSeparateProcess] + public function test_the_runner_brings_a_new_site_to_revision_one_with_the_untranslated_defaults_autoloaded(): void { + global $wpdb; + foreach ( self::KEYS as $key ) { + delete_option( $key ); + } + update_option( Plugin::PREFIX . '_revision', 0 ); + + dws_lpmwc_plugin()->migrations?->run(); + + self::assertSame( 1, MigrationRunner::get_revision( Plugin::PREFIX . '_revision' ) ); + self::assertSame( 'no', get_option( DWS_LPMWC_OPTION_FEATURE ) ); + self::assertSame( '1', get_option( DWS_LPMWC_OPTION_GREETING_ORDERS ) ); + self::assertSame( 'no', get_option( DWS_LPMWC_OPTION_DELETE_DATA ) ); + self::assertFalse( get_option( DWS_LPMWC_OPTION_GREETING ) ); + foreach ( array( DWS_LPMWC_OPTION_FEATURE, DWS_LPMWC_OPTION_GREETING_ORDERS, DWS_LPMWC_OPTION_DELETE_DATA ) as $key ) { + self::assertSame( 'on', $wpdb->get_var( $wpdb->prepare( "SELECT autoload FROM {$wpdb->options} WHERE option_name = %s", $key ) ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- Reads the stored autoload flag. + } + } + + public function test_the_first_step_keeps_the_values_a_site_stored(): void { + update_option( DWS_LPMWC_OPTION_FEATURE, 'yes' ); + update_option( DWS_LPMWC_OPTION_GREETING_ORDERS, '5' ); + + dws_lpmwc_add_settings(); + + self::assertSame( 'yes', get_option( DWS_LPMWC_OPTION_FEATURE ) ); + self::assertSame( '5', get_option( DWS_LPMWC_OPTION_GREETING_ORDERS ) ); + } + + public function test_the_first_step_throws_when_a_default_cannot_be_stored(): void { + global $wpdb; + delete_option( DWS_LPMWC_OPTION_DELETE_DATA ); + $fail = static fn ( string $query ): string => \str_contains( $query, "'" . DWS_LPMWC_OPTION_DELETE_DATA . "'" ) && \str_starts_with( \ltrim( $query ), 'INSERT' ) ? "SELECT * FROM {$wpdb->prefix}dws_lpmwc_missing" : $query; + $suppressed = $wpdb->suppress_errors(); + add_filter( 'query', $fail ); + + try { + $this->expectExceptionObject( new \RuntimeException( 'The option ' . DWS_LPMWC_OPTION_DELETE_DATA . ' could not be stored.' ) ); + dws_lpmwc_add_settings(); + } finally { + remove_filter( 'query', $fail ); + $wpdb->suppress_errors( $suppressed ); + } + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/OptionsTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/OptionsTest.php new file mode 100644 index 0000000..557b016 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/OptionsTest.php @@ -0,0 +1,149 @@ + array( 'yes', true ), + 'no' => array( 'no', false ), + 'one' => array( '1', true ), + 'zero' => array( '0', false ), + 'true' => array( true, true ), + 'empty' => array( '', false ), + 'malformed' => array( 'maybe', false ), + 'array' => array( array( 'yes' ), false ), + ); + } + + public static function stored_ints(): array { + return array( + 'int' => array( 7, 7 ), + 'numeric' => array( '7', 7 ), + 'padded' => array( ' 7 ', 7 ), + 'negative' => array( '-7', -7 ), + 'float' => array( '7.5', 0 ), + 'malformed' => array( 'seven', 0 ), + 'array' => array( array( 7 ), 0 ), + ); + } + + public static function stored_strings(): array { + return array( + 'string' => array( 'kept', 'kept' ), + 'empty' => array( '', '' ), + 'array' => array( array( 'kept' ), '' ), + ); + } + + public static function stored_lists(): array { + return array( + 'list' => array( array( 'a', 'b' ), array( 'a', 'b' ) ), + 'mixed' => array( array( 'a', 7, 'b' ), array( 'a', 'b' ) ), + 'keyed' => array( array( 'x' => 'a' ), array( 'a' ) ), + 'string' => array( 'a', array() ), + ); + } + + public static function submitted_order_counts(): array { + return array( + 'one' => array( '1', '1' ), + 'padded' => array( ' 3 ', '3' ), + 'zero' => array( '0', null ), + 'negative' => array( '-3', null ), + 'decimal' => array( '2.5', null ), + 'word' => array( 'three', null ), + 'empty' => array( '', null ), + 'array' => array( array( '3' ), null ), + ); + } + + #[Before] + protected function store_rows(): void { + foreach ( self::RESTORED as $key ) { + $this->stored[ $key ] = get_option( $key ); + } + } + + #[After] + protected function restore_rows(): void { + delete_option( self::KEY ); + foreach ( $this->stored as $key => $value ) { + false === $value ? delete_option( $key ) : update_option( $key, $value ); + } + } + + #[DataProvider( 'stored_bools' )] + public function test_a_bool_reads_the_stored_checkbox_value_or_false( mixed $stored, bool $expected ): void { + update_option( DWS_LPMWC_OPTION_DELETE_DATA, $stored ); + + self::assertSame( $expected, dws_lpmwc_get_bool_option( DWS_LPMWC_OPTION_DELETE_DATA ) ); + } + + public function test_a_missing_option_reads_its_default(): void { + delete_option( DWS_LPMWC_OPTION_DELETE_DATA ); + + self::assertSame( DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_DELETE_DATA ], dws_lpmwc_get_bool_option( DWS_LPMWC_OPTION_DELETE_DATA ) ); + } + + #[DataProvider( 'stored_ints' )] + public function test_an_int_reads_a_whole_number_or_zero( mixed $stored, int $expected ): void { + update_option( self::KEY, $stored ); + + self::assertSame( $expected, dws_lpmwc_get_int_option( self::KEY ) ); + } + + #[DataProvider( 'stored_strings' )] + public function test_a_string_reads_a_stored_string_or_the_empty_string( mixed $stored, string $expected ): void { + update_option( self::KEY, $stored ); + + self::assertSame( $expected, dws_lpmwc_get_string_option( self::KEY ) ); + } + + #[DataProvider( 'stored_lists' )] + public function test_a_list_reads_the_stored_strings_in_order( mixed $stored, array $expected ): void { + update_option( self::KEY, $stored ); + + self::assertSame( $expected, dws_lpmwc_get_list_option( self::KEY ) ); + } + + public function test_a_string_without_a_stored_value_reads_its_translated_default(): void { + delete_option( DWS_LPMWC_OPTION_GREETING ); + + self::assertNotSame( '', dws_lpmwc_get_option_text( DWS_LPMWC_OPTION_GREETING ) ); + self::assertSame( dws_lpmwc_get_option_text( DWS_LPMWC_OPTION_GREETING ), dws_lpmwc_get_string_option( DWS_LPMWC_OPTION_GREETING ) ); + + update_option( DWS_LPMWC_OPTION_GREETING, '' ); + + self::assertSame( dws_lpmwc_get_option_text( DWS_LPMWC_OPTION_GREETING ), dws_lpmwc_get_string_option( DWS_LPMWC_OPTION_GREETING ) ); + + update_option( DWS_LPMWC_OPTION_GREETING, 'Hello' ); + + self::assertSame( 'Hello', dws_lpmwc_get_string_option( DWS_LPMWC_OPTION_GREETING ) ); + } + + public function test_an_option_without_a_translated_default_has_no_text(): void { + self::assertSame( '', dws_lpmwc_get_option_text( DWS_LPMWC_OPTION_FEATURE ) ); + } + + #[DataProvider( 'submitted_order_counts' )] + public function test_an_order_count_validates_as_a_whole_number_of_at_least_one( mixed $submitted, ?string $expected ): void { + $validated = dws_lpmwc_validate_greeting_orders( $submitted ); + + \is_null( $expected ) ? self::assertInstanceOf( \WP_Error::class, $validated ) : self::assertSame( $expected, $validated ); + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PartialDeployTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PartialDeployTest.php new file mode 100644 index 0000000..5cb1264 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PartialDeployTest.php @@ -0,0 +1,100 @@ +&1', $output, $status ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.system_calls_exec -- Each row needs a fresh request that loads the plugin. + $output = \array_values( \array_filter( $output, static fn ( string $line ): bool => '' !== \trim( $line ) ) ); + + return array( + 'status' => $status, + 'output' => \implode( "\n", $output ), + 'last' => (string) \end( $output ), + ); + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php new file mode 100644 index 0000000..fa49a5f --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php @@ -0,0 +1,46 @@ +boot->status ); + self::assertSame( array( Scheduler::class, AdminNotices::class, Settings::class, Blocks::class ), $plugin->kernel?->resolved ); + self::assertSame( array( Feature::class ), $plugin->kernel->skipped ); + } + + public function test_the_entry_loads_every_include_but_those_whose_names_start_with_an_underscore(): void { + $includes = \dirname( DWS_LPMWC_FILE ) . '/includes/'; + + self::assertContains( $includes . 'index.php', \get_included_files() ); + self::assertNotContains( $includes . '_disabled-example.php', \get_included_files() ); + } + + public function test_the_entry_loads_its_safeguards_before_the_bootstrap_package_and_its_floor_gate(): void { + $root = \dirname( DWS_LPMWC_FILE ); + $safeguards = \array_search( $root . '/includes/safeguards.php', \get_included_files(), true ); + $bootstrap = \array_search( $root . '/vendor-prefixed/ahegyes/wp-framework-bootstrap/load.php', \get_included_files(), true ); + + self::assertIsInt( $safeguards ); + self::assertIsInt( $bootstrap ); + self::assertLessThan( $bootstrap, $safeguards ); + } + + public function test_the_stored_data_is_at_the_revision_of_the_code(): void { + self::assertInstanceOf( MigrationRunner::class, dws_lpmwc_plugin()->migrations ); + self::assertSame( \count( dws_lpmwc_get_migration_steps() ), MigrationRunner::get_revision( Plugin::PREFIX . '_revision' ) ); + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SettingsTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SettingsTest.php new file mode 100644 index 0000000..5fb4c3e --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SettingsTest.php @@ -0,0 +1,127 @@ +get_id() ) { + self::$tab = $page; + } + } + } + + public static function invalid_order_counts(): array { + return array( + 'zero' => array( '0' ), + 'negative' => array( '-2' ), + 'decimal' => array( '2.5' ), + 'word' => array( 'three' ), + 'empty' => array( '' ), + ); + } + + #[Before] + protected function store_rows(): void { + foreach ( self::KEYS as $key ) { + $this->stored[ $key ] = get_option( $key ); + } + } + + #[After] + protected function restore_rows(): void { + foreach ( $this->stored as $key => $value ) { + false === $value ? delete_option( $key ) : update_option( $key, $value ); + } + + ( new \ReflectionProperty( \WC_Admin_Settings::class, 'errors' ) )->setValue( null, array() ); + } + + public function test_the_tab_shows_the_greeting_and_data_fields_with_their_defaults(): void { + foreach ( self::KEYS as $key ) { + delete_option( $key ); + } + + $form = $this->render(); + + self::assertSame( 'Locked Payment Methods for WooCommerce', self::$tab->get_label() ); + self::assertFalse( $form->getElementById( DWS_LPMWC_OPTION_FEATURE )?->hasAttribute( 'checked' ) ); + self::assertSame( '', $form->getElementById( DWS_LPMWC_OPTION_GREETING )?->getAttribute( 'value' ) ); + self::assertSame( dws_lpmwc_get_option_text( DWS_LPMWC_OPTION_GREETING ), $form->getElementById( DWS_LPMWC_OPTION_GREETING )?->getAttribute( 'placeholder' ) ); + self::assertSame( '1', $form->getElementById( DWS_LPMWC_OPTION_GREETING_ORDERS )?->getAttribute( 'value' ) ); + self::assertSame( '1', $form->getElementById( DWS_LPMWC_OPTION_GREETING_ORDERS )?->getAttribute( 'min' ) ); + self::assertFalse( $form->getElementById( DWS_LPMWC_OPTION_DELETE_DATA )?->hasAttribute( 'checked' ) ); + } + + public function test_a_save_that_leaves_the_message_untouched_stores_no_translated_text(): void { + delete_option( DWS_LPMWC_OPTION_GREETING ); + + $this->save( array( DWS_LPMWC_OPTION_GREETING => $this->render()->getElementById( DWS_LPMWC_OPTION_GREETING )?->getAttribute( 'value' ) ) ); + + self::assertSame( '', get_option( DWS_LPMWC_OPTION_GREETING ) ); + self::assertSame( dws_lpmwc_get_option_text( DWS_LPMWC_OPTION_GREETING ), dws_lpmwc_get_string_option( DWS_LPMWC_OPTION_GREETING ) ); + } + + public function test_the_retention_opt_in_round_trips_through_woocommerces_save(): void { + $this->save( array( DWS_LPMWC_OPTION_DELETE_DATA => '1' ) ); + + self::assertSame( 'yes', get_option( DWS_LPMWC_OPTION_DELETE_DATA ) ); + self::assertTrue( dws_lpmwc_get_bool_option( DWS_LPMWC_OPTION_DELETE_DATA ) ); + self::assertTrue( $this->render()->getElementById( DWS_LPMWC_OPTION_DELETE_DATA )?->hasAttribute( 'checked' ) ); + + $this->save( array() ); + + self::assertSame( 'no', get_option( DWS_LPMWC_OPTION_DELETE_DATA ) ); + self::assertFalse( dws_lpmwc_get_bool_option( DWS_LPMWC_OPTION_DELETE_DATA ) ); + } + + #[DataProvider( 'invalid_order_counts' )] + public function test_an_order_count_that_is_not_a_whole_number_of_at_least_one_keeps_the_stored_count( string $posted ): void { + update_option( DWS_LPMWC_OPTION_GREETING_ORDERS, '4' ); + + $this->save( array( DWS_LPMWC_OPTION_GREETING_ORDERS => $posted ) ); + + self::assertSame( '4', get_option( DWS_LPMWC_OPTION_GREETING_ORDERS ) ); + self::assertSame( array( 'Orders before the greeting was not saved, so it keeps its previous value. Enter a whole number of 1 or more, such as 3.' ), ( new \ReflectionProperty( \WC_Admin_Settings::class, 'errors' ) )->getValue() ); + } + + public function test_a_whole_order_count_of_at_least_one_is_stored(): void { + $this->save( array( DWS_LPMWC_OPTION_GREETING_ORDERS => ' 3 ' ) ); + + self::assertSame( '3', get_option( DWS_LPMWC_OPTION_GREETING_ORDERS ) ); + self::assertSame( 3, dws_lpmwc_get_int_option( DWS_LPMWC_OPTION_GREETING_ORDERS ) ); + } + + protected function render(): \Dom\HTMLDocument { + \ob_start(); + \WC_Admin_Settings::output_fields( self::$tab->get_settings_for_section( '' ) ); + + return \Dom\HTMLDocument::createFromString( '' . \ob_get_clean() . '', \LIBXML_NOERROR ); + } + + // WooCommerce's form always posts its save button, so a save that changes nothing still runs. + protected function save( array $posted ): void { + \WC_Admin_Settings::save_fields( self::$tab->get_settings_for_section( '' ), $posted + array( 'save' => 'Save changes' ) ); + wp_cache_flush(); + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UninstallTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UninstallTest.php new file mode 100644 index 0000000..9b48f4e --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UninstallTest.php @@ -0,0 +1,75 @@ + Plugin::PREFIX, + 'status' => 'pending', + ), + 'ids' + ) + ); + + // The other tests share the database, so the runner stores again what a new site starts with. + dws_lpmwc_plugin()->migrations?->run(); + self::assertSame( 'no', get_option( DWS_LPMWC_OPTION_DELETE_DATA ) ); + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/bootstrap.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/bootstrap.php new file mode 100644 index 0000000..fe4c33b --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/bootstrap.php @@ -0,0 +1,19 @@ +, as `local`, where the framework rethrows boot and migration failures. +- `.wp-env.tests.json` is the tests site on , as `production`, which the Integration and end-to-end suites share. A row whose separate process changes the database flushes the parent's object cache, and the end-to-end specs set their own preconditions, because either suite may run first. + +Both start WordPress on PHP 8.5 with WooCommerce from `vendor/` and its order tables on. + +## Running + +Run the Unit suite: + +```sh +composer test:unit +``` + +Start the tests site, build the scripts and run the Integration suite: + +```sh +composer test:integration +``` + +Run the end-to-end suite, which starts the tests site when it is down and needs the built scripts and, once per machine, Playwright's Chromium: + +```sh +npx playwright install chromium +npm run test:e2e +``` + +Run every check CI runs on PHP, Integration included: + +```sh +composer quality-check +``` + +## Continuous integration + +- **Tests** runs Unit once, and Integration and the end-to-end suite once per WordPress branch, from the entry file's `Requires at least` up to the newest release, which WordPress's version API lists, so a new release joins without an edit. +- **Tests (nightly)** runs Integration and the end-to-end suite on WordPress trunk every day, outside pull requests. +- **Release** builds the zip on every pull request as a tag would, runs Plugin Check and the archive checks, then activates the zip on a site and runs the end-to-end suite against it. +- **Quality** runs PHPCS, PHPStan, the PHP 7.4 parse of the entry file, `uninstall.php` and `includes/`, the script, style and type lints, the `block.json` schema and the changelog fragments. + +## Conventions + +Test classes are `final`, named after the class or the behavior they cover with a `Test` suffix, with `test_snake_case` methods, kebab-case data provider keys and no docblocks. They use real instances and WordPress's own APIs, never mocks of the code under test. diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Unit/ComponentTreeTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Unit/ComponentTreeTest.php new file mode 100644 index 0000000..5bb8bad --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Unit/ComponentTreeTest.php @@ -0,0 +1,25 @@ +getDocComment(); + self::assertSame( 1, \preg_match( '/@phpstan-type Footprint (.+)$/m', $doc, $type ) ); + \preg_match_all( '/(\w+)\?:/', $type[1], $kinds ); + + self::assertSame( array(), \array_diff( \array_keys( dws_lpmwc_get_footprint() ), $kinds[1] ) ); + } + + public function test_every_entry_is_in_the_plugin_prefix_family(): void { + $family = array( + Plugin::PREFIX . '_', + Plugin::PREFIX . '/', // Hook names put a slash after the prefix. + '_' . Plugin::PREFIX . '_', // WordPress hides meta keys that start with an underscore. + '_transient_' . Plugin::PREFIX . '_', + '_transient_timeout_' . Plugin::PREFIX . '_', + ...self::LEGACY_FAMILIES, + ); + + foreach ( dws_lpmwc_get_footprint() as $kind => $entries ) { + $entries = \in_array( $kind, array( 'meta', 'meta_prefixes' ), true ) ? \array_merge( ...\array_values( $entries ) ) : $entries; + foreach ( $entries as $entry ) { + $covered = 'as_groups' === $kind ? Plugin::PREFIX === $entry : \array_any( $family, static fn ( string $start ): bool => \str_starts_with( $entry, $start ) ); + self::assertTrue( $covered, "The $kind entry $entry is outside the plugin's prefix family." ); + } + } + } + + public function test_it_covers_what_the_framework_roots_store(): void { + $prefix = Plugin::PREFIX . '_'; + + self::assertContains( $prefix, dws_lpmwc_get_footprint()['option_prefixes'] ); + self::assertContains( '_transient_' . $prefix, dws_lpmwc_get_footprint()['option_prefixes'] ); + self::assertContains( '_transient_timeout_' . $prefix, dws_lpmwc_get_footprint()['option_prefixes'] ); + self::assertContains( $prefix, dws_lpmwc_get_footprint()['user_meta_site_prefixes'] ); + self::assertContains( $prefix, dws_lpmwc_get_footprint()['user_meta_global_prefixes'] ); + self::assertContains( $prefix . 'network_notices', dws_lpmwc_get_footprint()['network_options'] ); + self::assertContains( $prefix . 'revision/migration/run', dws_lpmwc_get_footprint()['cron_hooks'] ); + self::assertContains( Plugin::PREFIX, dws_lpmwc_get_footprint()['as_groups'] ); + } + + public function test_it_covers_every_option_key(): void { + $keys = \array_filter( \get_defined_constants( true )['user'], static fn ( mixed $value, string $name ): bool => \str_starts_with( $name, 'DWS_LPMWC_OPTION_' ) && \is_string( $value ), \ARRAY_FILTER_USE_BOTH ); + + self::assertNotSame( array(), $keys ); + foreach ( $keys as $key ) { + $covered = \in_array( $key, dws_lpmwc_get_footprint()['options'] ?? array(), true ) || \array_any( dws_lpmwc_get_footprint()['option_prefixes'], static fn ( string $prefix ): bool => \str_starts_with( $key, $prefix ) ); + self::assertTrue( $covered, "The option $key is outside the footprint." ); + } + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Unit/bootstrap.php b/plugins/locked-payment-methods-for-woocommerce/tests/Unit/bootstrap.php new file mode 100644 index 0000000..af0ab55 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Unit/bootstrap.php @@ -0,0 +1,8 @@ + => { + for ( let presses = 0; presses < 10; presses++ ) { + await page.keyboard.press( 'Tab' ); + if ( + await target.evaluate( + ( element ) => element === element.ownerDocument.activeElement + ) + ) { + return; + } + } + throw new Error( 'Tab never reached the element.' ); +}; + +const typeAndSave = async ( page: Page, value: string ): Promise< void > => { + await page.keyboard.press( 'ControlOrMeta+a' ); + await page.keyboard.press( 'Backspace' ); + await page.keyboard.type( value ); + await tabTo( page, page.getByRole( 'button', { name: 'Save changes' } ) ); + await page.keyboard.press( 'Enter' ); + await page.waitForLoadState(); +}; + +test.describe( 'Settings tab', () => { + test.beforeEach( async ( { admin } ) => { + await admin.visitAdminPage( + 'admin.php', + 'page=wc-settings&tab=dws_lpmwc' + ); + } ); + + test( 'has no accessibility violations in its form', async ( { page } ) => { + const results = await new AxeBuilder( { page } ) + .include( '#mainform' ) + .analyze(); + + expect( results.violations ).toEqual( [] ); + } ); + + test( 'saves the order threshold by keyboard, rejects an empty one and keeps the saved value until a valid one replaces it', async ( { + page, + } ) => { + const threshold = page.getByLabel( 'Orders before the greeting' ); + + await page.getByLabel( 'Message' ).focus(); + await tabTo( page, threshold ); + await typeAndSave( page, '3' ); + await expect( page.getByText( SAVED ) ).toBeVisible(); + await expect( threshold ).toHaveValue( '3' ); + + await threshold.focus(); + await typeAndSave( page, '' ); + await expect( page.getByText( REJECTED ) ).toBeVisible(); + await expect( threshold ).toHaveValue( '3' ); + const rejected = await new AxeBuilder( { page } ) + .include( '#mainform' ) + .analyze(); + expect( rejected.violations ).toEqual( [] ); + + await threshold.focus(); + await typeAndSave( page, '1' ); + await expect( page.getByText( SAVED ) ).toBeVisible(); + await expect( page.getByText( REJECTED ) ).toBeHidden(); + await expect( threshold ).toHaveValue( '1' ); + } ); +} ); diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/e2e/without-woocommerce.spec.ts b/plugins/locked-payment-methods-for-woocommerce/tests/e2e/without-woocommerce.spec.ts new file mode 100644 index 0000000..cd51443 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/e2e/without-woocommerce.spec.ts @@ -0,0 +1,51 @@ +/// +import { execFileSync } from 'node:child_process'; +import { expect, test } from '@wordpress/e2e-test-utils-playwright'; + +// WordPress core's own message for a plugin whose Requires Plugins header lists an inactive plugin. +const DEPENDENCY = + 'Locked Payment Methods for WooCommerce requires 1 plugin to be installed and activated: WooCommerce.'; + +// No page shows the boot's outcome, so the site's WP-CLI reads it. +const bootStatus = (): string => + execFileSync( + 'node_modules/.bin/wp-env', + [ + '--config', + '.wp-env.tests.json', + 'run', + 'cli', + 'wp', + 'eval', + 'echo dws_lpmwc_plugin()->boot->status;', + ], + { encoding: 'utf8', stdio: [ 'ignore', 'pipe', 'ignore' ] } + ) + .trim() + .split( '\n' ) + .pop() ?? ''; + +test.describe( 'Without WooCommerce', () => { + test.beforeAll( async ( { requestUtils } ) => { + await requestUtils.deactivatePlugin( 'woocommerce' ); + } ); + + test.afterAll( async ( { requestUtils } ) => { + await requestUtils.activatePlugin( 'woocommerce' ); + } ); + + test( 'the plugin stays blocked, WordPress names the missing plugin and every screen still loads', async ( { + page, + } ) => { + expect( bootStatus() ).toBe( 'blocked' ); + + const front = await page.goto( '/' ); + expect( front?.status() ).toBe( 200 ); + + for ( const screen of [ '/wp-admin/', '/wp-admin/plugins.php' ] ) { + const response = await page.goto( screen ); + expect( response?.status() ).toBe( 200 ); + await expect( page.getByText( DEPENDENCY ) ).toBeVisible(); + } + } ); +} ); diff --git a/plugins/locked-payment-methods-for-woocommerce/tsconfig.json b/plugins/locked-payment-methods-for-woocommerce/tsconfig.json new file mode 100644 index 0000000..a68d869 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@ahegyes/wordpress-configs/node/tsconfig.base.json", + "include": ["blocks/src/**/*", "assets/src/**/*", "tests/e2e/**/*"] +} diff --git a/plugins/locked-payment-methods-for-woocommerce/uninstall.php b/plugins/locked-payment-methods-for-woocommerce/uninstall.php new file mode 100644 index 0000000..6794020 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/uninstall.php @@ -0,0 +1,55 @@ +getMessage() ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- The uninstall request has no other channel. + return; +} + +Uninstaller::run( + $dws_lpmwc_file, + $dws_lpmwc_footprint, + array( + 'consent' => array( 'option' => $dws_lpmwc_consent ), + 'meta_types' => $dws_lpmwc_meta_types, + ) +); From 5dfdd10d22ee597a91fe186ffd2d9cfedc5c28d2 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sat, 31 Oct 2026 23:04:48 +0100 Subject: [PATCH 05/50] Name Locked Payment Methods' Composer package after its mirror The fill-in names the Composer package after the plugin's slug, ahegyes/locked-payment-methods-for-woocommerce, while a plugin in this monorepo takes its mirror's name, which the split reads to find the mirror: ahegyes/wc-locked-payment-methods. The scaffold's commit said so but left the fill-in's name; the lock's content hash follows. Assisted-by: Claude Code:claude-opus-5-5 --- plugins/locked-payment-methods-for-woocommerce/composer.json | 2 +- plugins/locked-payment-methods-for-woocommerce/composer.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/locked-payment-methods-for-woocommerce/composer.json b/plugins/locked-payment-methods-for-woocommerce/composer.json index e5027c2..04ccc69 100644 --- a/plugins/locked-payment-methods-for-woocommerce/composer.json +++ b/plugins/locked-payment-methods-for-woocommerce/composer.json @@ -1,5 +1,5 @@ { - "name": "ahegyes/locked-payment-methods-for-woocommerce", + "name": "ahegyes/wc-locked-payment-methods", "description": "WordPress plugins on the wp-framework packages start here, from one tested anatomy, so all are built alike and a fix found in one applies to all.", "type": "wordpress-plugin", "license": "GPL-3.0-or-later", diff --git a/plugins/locked-payment-methods-for-woocommerce/composer.lock b/plugins/locked-payment-methods-for-woocommerce/composer.lock index fa7b31b..561137f 100644 --- a/plugins/locked-payment-methods-for-woocommerce/composer.lock +++ b/plugins/locked-payment-methods-for-woocommerce/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "76e70b93657e7e7295ec4f69e7784112", + "content-hash": "1616032afedf26ffa7ffc9d3e0fe1c56", "packages": [], "packages-dev": [ { From 392f70ab92b0e3f1747dec73acd81a8db2f6d0c3 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sat, 31 Oct 2026 23:09:05 +0100 Subject: [PATCH 06/50] Remove the template's demo from Locked Payment Methods Locked Payment Methods carried the template's demo, which a plugin replaces: the greeting feature, the example block and the editor script, with their options, settings fields, tests and build entries. They go as the template's README describes. v1 shipped no scripts or styles, and the port renders every screen through WordPress and WooCommerce, so nothing is built: blocks/, assets/, the asset registrars, the empty models/ and templates/ extension points, and the block and React packages go; build stays and runs nothing, as the README asks, and lint:styles keeps one pattern with --allow-empty-input. The entry's build gate checks only the bootstrap package, which the partial-deploy row now removes. The data consent and the template's generic readers stay until the port replaces them; the docs follow in the commit that describes the plugin. Assisted-by: Claude Code:claude-opus-5-5 --- .../.gitignore | 2 - .../assets/build/editor.asset.php | 7 + .../assets/build/editor.js | 1 + .../assets/build/greeting-rtl.css | 1 + .../assets/build/greeting.asset.php | 6 + .../assets/build/greeting.css | 1 + .../assets/build/greeting.js | 0 .../assets/index.php | 1 - .../assets/src/editor.ts | 7 - .../assets/src/greeting.scss | 3 - .../assets/src/index.php | 1 - .../blocks/build/blocks-manifest.php | 26 + .../{src => build}/example-notice/block.json | 2 +- .../blocks/build/example-notice/index-rtl.css | 1 + .../build/example-notice/index.asset.php | 9 + .../blocks/build/example-notice/index.css | 1 + .../blocks/build/example-notice/index.js | 1 + .../build/example-notice/style-index-rtl.css | 1 + .../build/example-notice/style-index.css | 1 + .../blocks/index.php | 1 - .../blocks/src/declarations.d.ts | 17 - .../blocks/src/example-notice/edit.tsx | 15 - .../blocks/src/example-notice/editor.scss | 3 - .../blocks/src/example-notice/index.php | 1 - .../blocks/src/example-notice/index.ts | 11 - .../blocks/src/example-notice/save.tsx | 10 - .../blocks/src/example-notice/style.scss | 8 - .../blocks/src/index.php | 1 - .../composer.json | 3 - .../includes/assets.php | 77 - .../includes/migrations.php | 4 +- .../includes/options.php | 52 +- ...locked-payment-methods-for-woocommerce.php | 2 +- .../models/index.php | 1 - .../package-lock.json | 2388 +---------------- .../package.json | 15 +- .../phpcs.dist.xml | 2 - .../src/Blocks.php | 60 - .../src/Feature/Feature.php | 51 - .../src/Feature/FeatureDetail.php | 70 - .../src/Feature/index.php | 1 - .../src/Plugin.php | 2 - .../src/Settings.php | 38 +- .../templates/index.php | 1 - .../tests/Fixtures/built.asset.php | 12 - .../tests/Integration/AssetsTest.php | 45 - .../tests/Integration/BlocksTest.php | 42 - .../Integration/Feature/FeatureDetailTest.php | 130 - .../tests/Integration/Feature/FeatureTest.php | 63 - .../tests/Integration/MigrationsTest.php | 13 +- .../tests/Integration/OptionsTest.php | 41 +- .../tests/Integration/PartialDeployTest.php | 4 +- .../tests/Integration/PluginBootTest.php | 8 +- .../tests/Integration/SettingsTest.php | 46 +- .../tests/e2e/settings.spec.ts | 74 - .../tsconfig.json | 2 +- 56 files changed, 170 insertions(+), 3216 deletions(-) create mode 100644 plugins/locked-payment-methods-for-woocommerce/assets/build/editor.asset.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/assets/build/editor.js create mode 100644 plugins/locked-payment-methods-for-woocommerce/assets/build/greeting-rtl.css create mode 100644 plugins/locked-payment-methods-for-woocommerce/assets/build/greeting.asset.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/assets/build/greeting.css create mode 100644 plugins/locked-payment-methods-for-woocommerce/assets/build/greeting.js delete mode 100644 plugins/locked-payment-methods-for-woocommerce/assets/index.php delete mode 100644 plugins/locked-payment-methods-for-woocommerce/assets/src/editor.ts delete mode 100644 plugins/locked-payment-methods-for-woocommerce/assets/src/greeting.scss delete mode 100644 plugins/locked-payment-methods-for-woocommerce/assets/src/index.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/build/blocks-manifest.php rename plugins/locked-payment-methods-for-woocommerce/blocks/{src => build}/example-notice/block.json (99%) create mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index-rtl.css create mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index.asset.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index.css create mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index.js create mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/style-index-rtl.css create mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/style-index.css delete mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/index.php delete mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/src/declarations.d.ts delete mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/edit.tsx delete mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/editor.scss delete mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/index.php delete mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/index.ts delete mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/save.tsx delete mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/style.scss delete mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/src/index.php delete mode 100644 plugins/locked-payment-methods-for-woocommerce/includes/assets.php delete mode 100644 plugins/locked-payment-methods-for-woocommerce/models/index.php delete mode 100644 plugins/locked-payment-methods-for-woocommerce/src/Blocks.php delete mode 100644 plugins/locked-payment-methods-for-woocommerce/src/Feature/Feature.php delete mode 100644 plugins/locked-payment-methods-for-woocommerce/src/Feature/FeatureDetail.php delete mode 100644 plugins/locked-payment-methods-for-woocommerce/src/Feature/index.php delete mode 100644 plugins/locked-payment-methods-for-woocommerce/templates/index.php delete mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Fixtures/built.asset.php delete mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/AssetsTest.php delete mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/BlocksTest.php delete mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/Feature/FeatureDetailTest.php delete mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/Feature/FeatureTest.php delete mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/e2e/settings.spec.ts diff --git a/plugins/locked-payment-methods-for-woocommerce/.gitignore b/plugins/locked-payment-methods-for-woocommerce/.gitignore index 9a44d1b..7e94aa5 100644 --- a/plugins/locked-payment-methods-for-woocommerce/.gitignore +++ b/plugins/locked-payment-methods-for-woocommerce/.gitignore @@ -1,6 +1,4 @@ vendor-prefixed -blocks/build -assets/build tests/.cache .wp-env.override.json diff --git a/plugins/locked-payment-methods-for-woocommerce/assets/build/editor.asset.php b/plugins/locked-payment-methods-for-woocommerce/assets/build/editor.asset.php new file mode 100644 index 0000000..23e6096 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/assets/build/editor.asset.php @@ -0,0 +1,7 @@ + array( + 'wp-blocks', + 'wp-i18n' + ), + 'version' => 'bf1425f40472313c45de' +); diff --git a/plugins/locked-payment-methods-for-woocommerce/assets/build/editor.js b/plugins/locked-payment-methods-for-woocommerce/assets/build/editor.js new file mode 100644 index 0000000..675511f --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/assets/build/editor.js @@ -0,0 +1 @@ +(()=>{"use strict";const e=window.wp.blocks,o=window.wp.i18n;(0,e.registerBlockStyle)("locked-payment-methods-for-woocommerce/example-notice",{name:"outlined",label:(0,o.__)("Outlined","locked-payment-methods-for-woocommerce")})})(); \ No newline at end of file diff --git a/plugins/locked-payment-methods-for-woocommerce/assets/build/greeting-rtl.css b/plugins/locked-payment-methods-for-woocommerce/assets/build/greeting-rtl.css new file mode 100644 index 0000000..4070942 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/assets/build/greeting-rtl.css @@ -0,0 +1 @@ +[data-locked-payment-methods-for-woocommerce=greeting]{font-weight:600} diff --git a/plugins/locked-payment-methods-for-woocommerce/assets/build/greeting.asset.php b/plugins/locked-payment-methods-for-woocommerce/assets/build/greeting.asset.php new file mode 100644 index 0000000..5b607f2 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/assets/build/greeting.asset.php @@ -0,0 +1,6 @@ + array( + + ), + 'version' => 'f7397bd60bff06cddc75' +); diff --git a/plugins/locked-payment-methods-for-woocommerce/assets/build/greeting.css b/plugins/locked-payment-methods-for-woocommerce/assets/build/greeting.css new file mode 100644 index 0000000..4070942 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/assets/build/greeting.css @@ -0,0 +1 @@ +[data-locked-payment-methods-for-woocommerce=greeting]{font-weight:600} diff --git a/plugins/locked-payment-methods-for-woocommerce/assets/build/greeting.js b/plugins/locked-payment-methods-for-woocommerce/assets/build/greeting.js new file mode 100644 index 0000000..e69de29 diff --git a/plugins/locked-payment-methods-for-woocommerce/assets/index.php b/plugins/locked-payment-methods-for-woocommerce/assets/index.php deleted file mode 100644 index f767346..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/assets/index.php +++ /dev/null @@ -1 +0,0 @@ - array( + '$schema' => 'https://schemas.wp.org/trunk/block.json', + 'apiVersion' => 3, + 'name' => 'locked-payment-methods-for-woocommerce/example-notice', + 'title' => 'Example Notice', + 'category' => 'widgets', + 'icon' => 'info', + 'description' => 'Displays a static example notice.', + 'attributes' => array( + + ), + 'example' => array( + + ), + 'supports' => array( + 'html' => false + ), + 'textdomain' => 'locked-payment-methods-for-woocommerce', + 'editorScript' => 'file:./index.js', + 'editorStyle' => 'file:./index.css', + 'style' => 'file:./style-index.css' + ) +); diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/block.json b/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/block.json similarity index 99% rename from plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/block.json rename to plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/block.json index 0b4f6df..4fe0aa7 100644 --- a/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/block.json +++ b/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/block.json @@ -15,4 +15,4 @@ "editorScript": "file:./index.js", "editorStyle": "file:./index.css", "style": "file:./style-index.css" -} +} \ No newline at end of file diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index-rtl.css b/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index-rtl.css new file mode 100644 index 0000000..0dde168 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index-rtl.css @@ -0,0 +1 @@ +.wp-block-locked-payment-methods-for-woocommerce-example-notice{outline:1px dashed currentcolor} diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index.asset.php b/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index.asset.php new file mode 100644 index 0000000..7f9c68b --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index.asset.php @@ -0,0 +1,9 @@ + array( + 'react-jsx-runtime', + 'wp-block-editor', + 'wp-blocks', + 'wp-i18n' + ), + 'version' => '8467c3ed7cc607ac19aa' +); diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index.css b/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index.css new file mode 100644 index 0000000..0dde168 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index.css @@ -0,0 +1 @@ +.wp-block-locked-payment-methods-for-woocommerce-example-notice{outline:1px dashed currentcolor} diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index.js b/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index.js new file mode 100644 index 0000000..09daa8a --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index.js @@ -0,0 +1 @@ +(()=>{"use strict";var e={857(){const e=window.wp.blocks,o=window.wp.blockEditor,t=window.wp.i18n,n=window.ReactJSXRuntime,s=JSON.parse('{"$schema":"https://schemas.wp.org/trunk/block.json","apiVersion":3,"name":"locked-payment-methods-for-woocommerce/example-notice","title":"Example Notice","category":"widgets","icon":"info","description":"Displays a static example notice.","attributes":{},"example":{},"supports":{"html":false},"textdomain":"locked-payment-methods-for-woocommerce","editorScript":"file:./index.js","editorStyle":"file:./index.css","style":"file:./style-index.css"}');(0,e.registerBlockType)(s,{edit:function(){return(0,n.jsx)("p",{...(0,o.useBlockProps)(),children:(0,t.__)("An example notice from Locked Payment Methods for WooCommerce.","locked-payment-methods-for-woocommerce")})},save:function(){return(0,n.jsx)("p",{...o.useBlockProps.save(),children:"An example notice from Locked Payment Methods for WooCommerce."})}})}};const o={};function t(n){const s=o[n];if(void 0!==s)return s.exports;const r=o[n]={exports:{}};return e[n](r,r.exports,t),r.exports}t.m=e,(()=>{const e=[];t.O=(o,n,s)=>{if(!n){for(var r=0;rObject.hasOwn(e,o),(()=>{const e={369:0,629:0};t.O.j=o=>0===e[o];const o=(o,n)=>{let[s,r,c]=n;var i,l,m=0;if(s.some(o=>0!==e[o])){for(i in r)t.o(r,i)&&(t.m[i]=r[i]);if(c)var a=c(t)}for(o&&o(n);mt(857));n=t.O(n)})(); \ No newline at end of file diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/style-index-rtl.css b/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/style-index-rtl.css new file mode 100644 index 0000000..9849cf9 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/style-index-rtl.css @@ -0,0 +1 @@ +.wp-block-locked-payment-methods-for-woocommerce-example-notice{border-right:4px solid;padding:.5em 1em}.wp-block-locked-payment-methods-for-woocommerce-example-notice.is-style-outlined{border:1px solid} diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/style-index.css b/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/style-index.css new file mode 100644 index 0000000..4950830 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/style-index.css @@ -0,0 +1 @@ +.wp-block-locked-payment-methods-for-woocommerce-example-notice{border-left:4px solid;padding:.5em 1em}.wp-block-locked-payment-methods-for-woocommerce-example-notice.is-style-outlined{border:1px solid} diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/index.php b/plugins/locked-payment-methods-for-woocommerce/blocks/index.php deleted file mode 100644 index f767346..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/blocks/index.php +++ /dev/null @@ -1 +0,0 @@ - - ): HTMLAttributes< HTMLElement >; - - export namespace useBlockProps { - function save( - props?: HTMLAttributes< HTMLElement > - ): HTMLAttributes< HTMLElement >; - } -} diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/edit.tsx b/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/edit.tsx deleted file mode 100644 index 90b0382..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/edit.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { useBlockProps } from '@wordpress/block-editor'; -import { __ } from '@wordpress/i18n'; - -import './editor.scss'; - -export default function Edit() { - return ( -

- { __( - 'An example notice from Locked Payment Methods for WooCommerce.', - 'locked-payment-methods-for-woocommerce' - ) } -

- ); -} diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/editor.scss b/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/editor.scss deleted file mode 100644 index dd5ac10..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/editor.scss +++ /dev/null @@ -1,3 +0,0 @@ -.wp-block-locked-payment-methods-for-woocommerce-example-notice { - outline: 1px dashed currentcolor; -} diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/index.php b/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/index.php deleted file mode 100644 index f767346..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/index.php +++ /dev/null @@ -1 +0,0 @@ - - An example notice from Locked Payment Methods for WooCommerce. -

- ); -} diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/style.scss b/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/style.scss deleted file mode 100644 index dd43537..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/blocks/src/example-notice/style.scss +++ /dev/null @@ -1,8 +0,0 @@ -.wp-block-locked-payment-methods-for-woocommerce-example-notice { - padding: 0.5em 1em; - border-left: 4px solid currentcolor; - - &.is-style-outlined { - border: 1px solid currentcolor; - } -} diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/src/index.php b/plugins/locked-payment-methods-for-woocommerce/blocks/src/index.php deleted file mode 100644 index f767346..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/blocks/src/index.php +++ /dev/null @@ -1 +0,0 @@ -, version: string, type?: string, handle?: string} - */ -function dws_lpmwc_get_asset( string $path ): array { - /** - * The payload wp-scripts generates. - * - * @var array{dependencies: list, version: string, type?: string, handle?: string} $asset - */ - $asset = require plugin_dir_path( DWS_LPMWC_FILE ) . dirname( $path ) . '/' . basename( $path, '.' . pathinfo( $path, PATHINFO_EXTENSION ) ) . '.asset.php'; - - return $asset; -} - -/** - * Registers a built script with its generated dependencies and version, and loads its translations from languages/. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @param non-empty-string $handle The script's handle. - * @param non-empty-string $path The built script's path, relative to the plugin's directory. - * @param array{strategy?: 'async'|'defer', in_footer?: bool, fetchpriority?: 'auto'|'low'|'high'} $args Optional. How the script loads. Default none, which loads it in the head. - * - * @return bool - */ -function dws_lpmwc_register_script( string $handle, string $path, array $args = array() ): bool { - $asset = dws_lpmwc_get_asset( $path ); - $registered = wp_register_script( $handle, plugin_dir_url( DWS_LPMWC_FILE ) . $path, array_values( array_filter( $asset['dependencies'], 'is_string' ) ), $asset['version'], $args ); - if ( $registered ) { - wp_set_script_translations( $handle, Plugin::SLUG, plugin_dir_path( DWS_LPMWC_FILE ) . 'languages' ); - } - - return $registered; -} - -/** - * Registers a built stylesheet with its generated version, and its right-to-left variant for right-to-left languages. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @param non-empty-string $handle The stylesheet's handle. - * @param non-empty-string $path The built stylesheet's path, relative to the plugin's directory. - * @param string $media Optional. The media the stylesheet applies to. Default 'all'. - * - * @return bool - */ -function dws_lpmwc_register_style( string $handle, string $path, string $media = 'all' ): bool { - $registered = wp_register_style( $handle, plugin_dir_url( DWS_LPMWC_FILE ) . $path, array(), dws_lpmwc_get_asset( $path )['version'], $media ); - if ( $registered ) { - wp_style_add_data( $handle, 'rtl', 'replace' ); - } - - return $registered; -} diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php b/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php index c9b33b2..b59c35c 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php @@ -36,9 +36,7 @@ function dws_lpmwc_get_migration_steps(): array { */ function dws_lpmwc_add_settings(): void { $defaults = array( - DWS_LPMWC_OPTION_FEATURE => wc_bool_to_string( DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_FEATURE ] ), - DWS_LPMWC_OPTION_GREETING_ORDERS => (string) DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_GREETING_ORDERS ], - DWS_LPMWC_OPTION_DELETE_DATA => wc_bool_to_string( DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_DELETE_DATA ] ), + DWS_LPMWC_OPTION_DELETE_DATA => wc_bool_to_string( DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_DELETE_DATA ] ), ); foreach ( $defaults as $key => $value ) { diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/options.php b/plugins/locked-payment-methods-for-woocommerce/includes/options.php index 5335149..ed22da5 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/options.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/options.php @@ -22,36 +22,6 @@ */ const DWS_LPMWC_OPTION_DELETE_DATA = Plugin::PREFIX . '_delete_data'; -/** - * The option that holds 'yes' where the site greets returning customers on their account dashboard. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @var string - */ -const DWS_LPMWC_OPTION_FEATURE = Plugin::PREFIX . '_feature'; - -/** - * The option that holds the greeting. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @var string - */ -const DWS_LPMWC_OPTION_GREETING = Plugin::PREFIX . '_greeting'; - -/** - * The option that holds the number of orders a customer places before the greeting shows. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @var string - */ -const DWS_LPMWC_OPTION_GREETING_ORDERS = Plugin::PREFIX . '_greeting_orders'; - /** * The value each option reads as while nothing valid is stored. * @@ -61,9 +31,7 @@ * @var array> */ const DWS_LPMWC_OPTION_DEFAULTS = array( - DWS_LPMWC_OPTION_DELETE_DATA => false, - DWS_LPMWC_OPTION_FEATURE => false, - DWS_LPMWC_OPTION_GREETING_ORDERS => 1, + DWS_LPMWC_OPTION_DELETE_DATA => false, ); /** @@ -149,29 +117,11 @@ function dws_lpmwc_get_list_option( string $key ): array { */ function dws_lpmwc_get_option_text( string $key ): string { switch ( $key ) { - case DWS_LPMWC_OPTION_GREETING: - return __( 'Welcome back, and thank you for shopping with us again.', 'locked-payment-methods-for-woocommerce' ); default: return ''; } } -/** - * Returns the number of orders before the greeting shows in the form WooCommerce stores, or an error when the submission is not a whole number of at least 1. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @param mixed $submitted The submitted value. - * - * @return string|WP_Error - */ -function dws_lpmwc_validate_greeting_orders( $submitted ) { - $orders = is_string( $submitted ) ? filter_var( $submitted, FILTER_VALIDATE_INT, array( 'options' => array( 'min_range' => 1 ) ) ) : false; - - return false === $orders ? new WP_Error( 'invalid_number', __( 'Enter a whole number of 1 or more, such as 3.', 'locked-payment-methods-for-woocommerce' ) ) : (string) $orders; -} - /** * Returns the option's default, or null for an option without one. * diff --git a/plugins/locked-payment-methods-for-woocommerce/locked-payment-methods-for-woocommerce.php b/plugins/locked-payment-methods-for-woocommerce/locked-payment-methods-for-woocommerce.php index 8d44eab..43a547f 100644 --- a/plugins/locked-payment-methods-for-woocommerce/locked-payment-methods-for-woocommerce.php +++ b/plugins/locked-payment-methods-for-woocommerce/locked-payment-methods-for-woocommerce.php @@ -39,7 +39,7 @@ } } -if ( ! is_readable( $dws_lpmwc_bootstrap ) || ! is_readable( __DIR__ . '/blocks/build/blocks-manifest.php' ) ) { // The floor gate never ran, or the scripts were never built, so nothing built may load. +if ( ! is_readable( $dws_lpmwc_bootstrap ) ) { // The floor gate never ran, so nothing built may load. add_action( 'all_admin_notices', 'dws_lpmwc_render_build_notice' ); return; } diff --git a/plugins/locked-payment-methods-for-woocommerce/models/index.php b/plugins/locked-payment-methods-for-woocommerce/models/index.php deleted file mode 100644 index f767346..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/models/index.php +++ /dev/null @@ -1 +0,0 @@ -=6.0.0" } }, - "node_modules/@ariakit/components": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/@ariakit/components/-/components-0.1.15.tgz", - "integrity": "sha512-Rq8cU/DXURIHbvRPZHXmUd7RHkTG0cXDjkjSWSeQEOQHp8w1bNenYUygoe0MzM0gwWakHrctA78XWGFgHmzUWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ariakit/store": "0.1.11", - "@ariakit/utils": "0.2.2" - } - }, - "node_modules/@ariakit/react": { - "version": "0.4.42", - "resolved": "https://registry.npmjs.org/@ariakit/react/-/react-0.4.42.tgz", - "integrity": "sha512-D+nElmWhT2w0TV0BKUh84ox21AaflvMv1dTt6Fn9TEOQIdZmOAqF1B6VAv69L5R1oyLzNV7neLD8cMTku+vbEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ariakit/react-components": "0.7.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ariakit" - }, - "peerDependencies": { - "react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/@ariakit/react-components": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@ariakit/react-components/-/react-components-0.7.1.tgz", - "integrity": "sha512-HOqqJqwtXYCrdwYRZeIg/hvXCPceGO+Ts+UIu9FIIIeaVqAxgyFYUb8Umbh40Wscek3PrYDdx/r5B5lpByUX8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ariakit/components": "0.1.15", - "@ariakit/react-store": "0.1.13", - "@ariakit/react-utils": "0.2.8", - "@ariakit/store": "0.1.11", - "@ariakit/utils": "0.2.2", - "@floating-ui/dom": "^1.0.0" - }, - "peerDependencies": { - "react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/@ariakit/react-store": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/@ariakit/react-store/-/react-store-0.1.13.tgz", - "integrity": "sha512-/4R0HVcx14RlYCNqffytY9v3E+HLn+kL/Iaef3iOGSSvzr+Po75ZFJzMrNv3jVZsuhXXTilI16XmRT8nSiSUsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ariakit/react-utils": "0.2.8", - "@ariakit/store": "0.1.11", - "@ariakit/utils": "0.2.2", - "use-sync-external-store": "^1.6.0" - }, - "peerDependencies": { - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/@ariakit/react-utils": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@ariakit/react-utils/-/react-utils-0.2.8.tgz", - "integrity": "sha512-F8bQ4tbPxK9oniw/XS2HB0bCvZA0djpkFlFsqx4rGH1RPrhwg17OCf6rOMCo1nMfAqrUT6rpdRkxlocmgaznvg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ariakit/store": "0.1.11", - "@ariakit/utils": "0.2.2" - }, - "peerDependencies": { - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/@ariakit/store": { - "version": "0.1.11", - "resolved": "https://registry.npmjs.org/@ariakit/store/-/store-0.1.11.tgz", - "integrity": "sha512-rffsPgyf6MTVK3pZSS86oKIj8370F9229c/7QqCwvbEIl0zM0bfEEdE6K+djnsabwajhWlJ/2syURdD4x30rXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ariakit/utils": "0.2.2" - } - }, - "node_modules/@ariakit/utils": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@ariakit/utils/-/utils-0.2.2.tgz", - "integrity": "sha512-OjdYlnJ8dW6XkBHirX0jx8bzLm577IL9Fe8T9dPrNKgCVvvJJiUL18iA5Oqqq9+FQ5ahhx5t4WgdE+2AaqG3IA==", - "dev": true, - "license": "MIT" - }, "node_modules/@axe-core/playwright": { "version": "4.14.0", "resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.14.0.tgz", @@ -1915,13 +1816,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/runtime": { - "version": "8.0.8", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-8.0.8.tgz", - "integrity": "sha512-dEu8nGlRqjsJsvviUUoSMfcRHpwomCiRlXsc2mPfJ79+ffTpDm+JtfUtTMzUrqCApHQnCsVPD3gT1ZT8RgBRCQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@babel/template": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", @@ -1970,68 +1864,6 @@ "node": ">=6.9.0" } }, - "node_modules/@base-ui/react": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@base-ui/react/-/react-1.9.0.tgz", - "integrity": "sha512-S8CSYosASBKtpL8SWUfS78rFElfLijiCgA7oHOK22jCJ95FULsYyKwBNFOgudt0RA+/7yNc7F81bOvQnRgbBiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.29.7 || ^8.0.0", - "@base-ui/utils": "0.5.0", - "@floating-ui/react-dom": "^2.1.9", - "@floating-ui/utils": "^0.2.12", - "use-sync-external-store": "^1.7.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@date-fns/tz": "^1.2.0", - "@types/react": "^17 || ^18 || ^19", - "date-fns": "^4.0.0", - "react": "^17 || ^18 || ^19", - "react-dom": "^17 || ^18 || ^19" - }, - "peerDependenciesMeta": { - "@date-fns/tz": { - "optional": true - }, - "@types/react": { - "optional": true - }, - "date-fns": { - "optional": true - } - } - }, - "node_modules/@base-ui/utils": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@base-ui/utils/-/utils-0.5.0.tgz", - "integrity": "sha512-dYGGcEttjkYiMoa5nHm8vLaJ2UcuTWxo3BFAwOYXeRg/q7UVVvA6cacEIXEs3FgBfK1CGtW1ptElqs7wsx+t9A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.29.7 || ^8.0.0", - "@floating-ui/utils": "^0.2.12", - "reselect": "^5.3.0", - "use-sync-external-store": "^1.7.0" - }, - "peerDependencies": { - "@types/react": "^17 || ^18 || ^19", - "react": "^17 || ^18 || ^19", - "react-dom": "^17 || ^18 || ^19" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@cacheable/memory": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.2.0.tgz", @@ -2255,46 +2087,6 @@ "postcss-selector-parser": "^7.1.1" } }, - "node_modules/@date-fns/tz": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.5.0.tgz", - "integrity": "sha512-lwYN/vDPeNRULcepoE/LO2Pgx+7/RV+S9ARfbc9lr2DtGkOD7pAiruHvbR1RX3Qyf6ja47EWJDMsNK5vK08DJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@date-fns/utc": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@date-fns/utc/-/utc-2.1.1.tgz", - "integrity": "sha512-SlJDfG6RPeEX8wEVv6ZB3kak4MmbtyiI2qX/5zuKdordbrhB/iaJ58GVMZgJ6P1sJaM1gMgENFYYeg1JWrCFrA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@daypicker/react": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@daypicker/react/-/react-10.0.2.tgz", - "integrity": "sha512-Z/gvGXnc/xjRr5Z6l9OhIey00C7kwbIyJ85Pff9B9EWDkjTlkTQztB3qeylmGJDQ9xmxRJUV1W+02rskqarUBw==", - "dev": true, - "license": "MIT", - "dependencies": { - "react-day-picker": "10.0.2" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "individual", - "url": "https://github.com/sponsors/gpbl" - }, - "peerDependencies": { - "@types/react": ">=16.8.0", - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", @@ -2703,48 +2495,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@floating-ui/core": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz", - "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@floating-ui/utils": "^0.2.12" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.8.0.tgz", - "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.8.0", - "@floating-ui/utils": "^0.2.12" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.9.tgz", - "integrity": "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@floating-ui/dom": "^1.8.0" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.12.tgz", - "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==", - "dev": true, - "license": "MIT" - }, "node_modules/@formatjs/ecma402-abstract": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.6.tgz", @@ -5558,34 +5308,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@preact/signals": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@preact/signals/-/signals-1.3.4.tgz", - "integrity": "sha512-TPMkStdT0QpSc8FpB63aOwXoSiZyIrPsP9Uj347KopdS6olZdAYeeird/5FZv/M1Yc1ge5qstub2o8VDbvkT4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@preact/signals-core": "^1.7.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/preact" - }, - "peerDependencies": { - "preact": "10.x" - } - }, - "node_modules/@preact/signals-core": { - "version": "1.14.4", - "resolved": "https://registry.npmjs.org/@preact/signals-core/-/signals-core-1.14.4.tgz", - "integrity": "sha512-HNB6HYeYKhQbJ1aKl+YRjrS4+QWHLKX6qKoUsfS/m0vqzsVaEBiZiaKbG/e+NKk2ch5ALQr/ihWaMHxiCuuWHA==", - "dev": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/preact" - } - }, "node_modules/@puppeteer/browsers": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-3.2.6.tgz", @@ -5761,521 +5483,109 @@ "node": "^20.19.0 || ^22.12.0 || >=23" } }, - "node_modules/@radix-ui/primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.2.0.tgz", - "integrity": "sha512-KABY+VMIRPJL+Vyzv/335FEhXXD/xDv8BMb1kl48/aXJZMH0LtHCTx5ga5RnxaEP3nZHtmTLf1P9vanmFX46bw==", + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", "dev": true, "license": "MIT" }, - "node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.5.tgz", - "integrity": "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==", + "node_modules/@sentry/conventions": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@sentry/conventions/-/conventions-0.16.0.tgz", + "integrity": "sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ==", "dev": true, "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": ">=14" } }, - "node_modules/@radix-ui/react-context": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.3.0.tgz", - "integrity": "sha512-v5AYEocMISkbJ+1fTXzZpiFv7NRr3D2WiYbLIKf+FkmJ8VkP6d503eV2n34MFussHXY1pcoq3yfJiQH77dc4Mg==", + "node_modules/@sentry/core": { + "version": "10.76.2", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.76.2.tgz", + "integrity": "sha512-vndrQalRiwYatTkjlLWvyf69KocXIb9TKJ4A9oiyWguL8PJYtqIRTlRe4OpkpjLrxS18fvRdV0jEm29c/9p3hQ==", "dev": true, "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "dependencies": { + "@sentry/conventions": "^0.16.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": ">=18" } }, - "node_modules/@radix-ui/react-dialog": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.3.1.tgz", - "integrity": "sha512-+0E4qvVTwjAWra9sEhJ+9BTsvHE00zkUuNw1VGh7S+Gc13dfYk+S1PVQdchWDj+XYHhAai7Z7IpzXVDCGsDplg==", + "node_modules/@sentry/node": { + "version": "10.76.2", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.76.2.tgz", + "integrity": "sha512-LSwGGE/GceTbkDHRoQQr3lq/dpxxA6RdooBbuGTJ/S6BdqKX2kw2FdV7Se9eJzZJJJ5AxCZum37+ejYS7N2pJg==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.2.0", - "@radix-ui/react-compose-refs": "1.1.5", - "@radix-ui/react-context": "1.3.0", - "@radix-ui/react-dismissable-layer": "1.2.1", - "@radix-ui/react-focus-guards": "1.2.0", - "@radix-ui/react-focus-scope": "1.3.1", - "@radix-ui/react-id": "1.1.4", - "@radix-ui/react-portal": "1.2.0", - "@radix-ui/react-presence": "1.1.11", - "@radix-ui/react-primitive": "2.2.0", - "@radix-ui/react-slot": "1.5.0", - "@radix-ui/react-use-controllable-state": "1.3.0", - "@radix-ui/react-use-layout-effect": "1.1.4", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.7.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@opentelemetry/api": "^1.9.1", + "@opentelemetry/instrumentation": "^0.220.0", + "@opentelemetry/sdk-trace-base": "^2.9.0", + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.76.2", + "@sentry/node-core": "10.76.2", + "@sentry/opentelemetry": "10.76.2", + "@sentry/server-utils": "10.76.2", + "import-in-the-middle": "^3.0.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "engines": { + "node": ">=18" } }, - "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.2.1.tgz", - "integrity": "sha512-KIfDcAsx+oVylFZ4xryVKusJUrLLhTQmfnJW4kwk4Km5myvhHCrj04hnWWUC166+xD4DHdH8tQGylwYX1h6+Pg==", + "node_modules/@sentry/node-core": { + "version": "10.76.2", + "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.76.2.tgz", + "integrity": "sha512-TrgAc/SvU5kQfjw8pz6I8LLIUiblOSf9BPPNkTbS0Dzcqa4yQLVbmsbS3rvGT7ot64m9Vjzn327ZRy63MlI6/w==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.2.0", - "@radix-ui/react-compose-refs": "1.1.5", - "@radix-ui/react-primitive": "2.2.0", - "@radix-ui/react-use-callback-ref": "1.1.5", - "@radix-ui/react-use-effect-event": "0.0.5" + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.76.2", + "@sentry/opentelemetry": "10.76.2", + "import-in-the-middle": "^3.0.0" + }, + "engines": { + "node": ">=18" }, "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/core": "^1.30.1 || ^2.1.0", + "@opentelemetry/exporter-trace-otlp-http": ">=0.57.0 <1", + "@opentelemetry/instrumentation": ">=0.57.1 <1", + "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0" }, "peerDependenciesMeta": { - "@types/react": { + "@opentelemetry/api": { "optional": true }, - "@types/react-dom": { + "@opentelemetry/core": { "optional": true - } - } - }, - "node_modules/@radix-ui/react-focus-guards": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.2.0.tgz", - "integrity": "sha512-MPm5R0iRGUDrDnPNxmLBkv+X7pN4XKzt+MbWJEuJQsH3NFH4omrbpXpucyYCCKTSt+na/ZxGhEWQrWTZ0285cA==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { + }, + "@opentelemetry/exporter-trace-otlp-http": { + "optional": true + }, + "@opentelemetry/instrumentation": { + "optional": true + }, + "@opentelemetry/sdk-trace-base": { "optional": true } } }, - "node_modules/@radix-ui/react-focus-scope": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.3.1.tgz", - "integrity": "sha512-ugeMhbXt4y5S2T1GnIqbWDKEN962PDwnX1fN0eKOOgf5f63Dh2mn8F1FUySlgW0iF6GiVsRF32DzuDIWTUyQJg==", + "node_modules/@sentry/opentelemetry": { + "version": "10.76.2", + "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.76.2.tgz", + "integrity": "sha512-deqWrKJGaku3nhiZchg34z8W9v6kUwtzoQ4cmEq6CeT8/4X93X4YfeJgoh1dhbswFRpwor4pQ6kj0KttagKtfA==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.5", - "@radix-ui/react-primitive": "2.2.0", - "@radix-ui/react-use-callback-ref": "1.1.5" + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.76.2" }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-id": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.4.tgz", - "integrity": "sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-portal": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.2.0.tgz", - "integrity": "sha512-StX7xXnaRU41VvFtMm9zlhVGz8XF6GmWWTXftkQaY+tqVkbs/R9UmAp5mLzHHapk5hM9RMZQvO/3usyIP/g42g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.2.0", - "@radix-ui/react-use-layout-effect": "1.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-presence": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.11.tgz", - "integrity": "sha512-Z7/atlkab36Z8hm+V8zYc79r0W+tiOB1o0tun1By94JyadkekLQmtEAnBSP9XKXBNQsStoStS4+gX10JpS5akA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-primitive": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.2.0.tgz", - "integrity": "sha512-HCTSPMNqv55Qk0IX4geJbpyXazmqulcimwuyjxTkKi29v+QZbG2Zho9BnbBq4YS2PF+KlNb2CXoLz66uUmblkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.5.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-slot": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.5.0.tgz", - "integrity": "sha512-tyYd9ddPXAysAbdbD9eYxgwpCSv6f3PncqVSNKQFSR5lcJVLz3cb/pANDoYrLiHBpXv7skortyUgGUWsYuPPFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.5" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.5.tgz", - "integrity": "sha512-Y5GBf4NKeADoXI1geiMlMuMoURT3k9MizzzKhGb/m55H94aGigZ01PIpkvsO6lML13BA6XYAXSJ6gza7vTn5nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.3.0.tgz", - "integrity": "sha512-TAcBI7bw2ljYCbv4+zuTSd6zICu8pRt6SDN2vJ/Z06K2MXh6I9WYFe/A9ovlFDtR5HVHGm5iDxDoABm/++dbKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.2.0", - "@radix-ui/react-use-effect-event": "0.0.5", - "@radix-ui/react-use-layout-effect": "1.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-effect-event": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.5.tgz", - "integrity": "sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.4.tgz", - "integrity": "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@react-spring/animated": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-10.1.2.tgz", - "integrity": "sha512-yAsQ/bbp6+vko7WNCI1M00c6KLE9XKTGCrgRhQqS4JcK3oF5qBV4rHYrQEprvEvYXxt+1H5FsLS2eVValEPfFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@react-spring/shared": "~10.1.2", - "@react-spring/types": "~10.1.2" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/@react-spring/core": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-10.1.2.tgz", - "integrity": "sha512-lPGOAg0V+PV3ucopOajD+YCxoe8twALqAeG4c/+sqVjBsyUNNdx8qfz/DcNSPKA8PV3+AyQELlCxlDfap9cmBQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@react-spring/animated": "~10.1.2", - "@react-spring/shared": "~10.1.2", - "@react-spring/types": "~10.1.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/react-spring/donate" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/@react-spring/rafz": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-10.1.2.tgz", - "integrity": "sha512-KC6vSFZyPnRJ2rXqipV9QqR4SaYbYXjvKfdYKWipNK2mWuV79gr20WmpVUOsTiEHGRY3WSOdWCHN+P9Pdaqb7Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@react-spring/shared": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-10.1.2.tgz", - "integrity": "sha512-47/8bNQ/o0uEmxEnPBuERlC29VSqiTn5P9ln9tUMSVkYKYxBtouYE686F5kAGj+eHRPoNCw7drxWE9nv2d1LMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@react-spring/rafz": "~10.1.2", - "@react-spring/types": "~10.1.2" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/@react-spring/types": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-10.1.2.tgz", - "integrity": "sha512-G4CWowmVPz+rDG1y9QRVq/prZNxiNwQaHC0kgT/MgY6jAGgdRgTV4VThpvJDwWvUitk3xZB4soP4d36fjeQ09g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@react-spring/web": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-10.1.2.tgz", - "integrity": "sha512-KxDB3zaDqy9qFsu7fdxjyraAxweHH4k5TW5WGT/OuMK6hKaxSDfhrQfRBzfFaSVvMBf+NghbJFanllV4ia6i7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@react-spring/animated": "~10.1.2", - "@react-spring/core": "~10.1.2", - "@react-spring/shared": "~10.1.2", - "@react-spring/types": "~10.1.2", - "csstype": "^3.2.3" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/@rtsao/scc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sentry/conventions": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@sentry/conventions/-/conventions-0.16.0.tgz", - "integrity": "sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/@sentry/core": { - "version": "10.76.2", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.76.2.tgz", - "integrity": "sha512-vndrQalRiwYatTkjlLWvyf69KocXIb9TKJ4A9oiyWguL8PJYtqIRTlRe4OpkpjLrxS18fvRdV0jEm29c/9p3hQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sentry/conventions": "^0.16.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@sentry/node": { - "version": "10.76.2", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.76.2.tgz", - "integrity": "sha512-LSwGGE/GceTbkDHRoQQr3lq/dpxxA6RdooBbuGTJ/S6BdqKX2kw2FdV7Se9eJzZJJJ5AxCZum37+ejYS7N2pJg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@opentelemetry/api": "^1.9.1", - "@opentelemetry/instrumentation": "^0.220.0", - "@opentelemetry/sdk-trace-base": "^2.9.0", - "@sentry/conventions": "^0.16.0", - "@sentry/core": "10.76.2", - "@sentry/node-core": "10.76.2", - "@sentry/opentelemetry": "10.76.2", - "@sentry/server-utils": "10.76.2", - "import-in-the-middle": "^3.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@sentry/node-core": { - "version": "10.76.2", - "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.76.2.tgz", - "integrity": "sha512-TrgAc/SvU5kQfjw8pz6I8LLIUiblOSf9BPPNkTbS0Dzcqa4yQLVbmsbS3rvGT7ot64m9Vjzn327ZRy63MlI6/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sentry/conventions": "^0.16.0", - "@sentry/core": "10.76.2", - "@sentry/opentelemetry": "10.76.2", - "import-in-the-middle": "^3.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/core": "^1.30.1 || ^2.1.0", - "@opentelemetry/exporter-trace-otlp-http": ">=0.57.0 <1", - "@opentelemetry/instrumentation": ">=0.57.1 <1", - "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "@opentelemetry/core": { - "optional": true - }, - "@opentelemetry/exporter-trace-otlp-http": { - "optional": true - }, - "@opentelemetry/instrumentation": { - "optional": true - }, - "@opentelemetry/sdk-trace-base": { - "optional": true - } - } - }, - "node_modules/@sentry/opentelemetry": { - "version": "10.76.2", - "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.76.2.tgz", - "integrity": "sha512-deqWrKJGaku3nhiZchg34z8W9v6kUwtzoQ4cmEq6CeT8/4X93X4YfeJgoh1dhbswFRpwor4pQ6kj0KttagKtfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sentry/conventions": "^0.16.0", - "@sentry/core": "10.76.2" - }, - "engines": { - "node": ">=18" + "engines": { + "node": ">=18" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0", @@ -6902,23 +6212,6 @@ "@types/ms": "*" } }, - "node_modules/@types/dom-mediacapture-transform": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/@types/dom-mediacapture-transform/-/dom-mediacapture-transform-0.1.12.tgz", - "integrity": "sha512-d7/QsLRwF864A5mgIM/YrfiglHoYn7zgCcAoJgW404r+2DwnNr7EBbLnCWpmOMgH8y0te73L1AV6H1bmauaWFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/dom-webcodecs": "*" - } - }, - "node_modules/@types/dom-webcodecs": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/@types/dom-webcodecs/-/dom-webcodecs-0.1.13.tgz", - "integrity": "sha512-O5hkiFIcjjszPIYyUSyvScyvrBoV3NOEEZx/pMlsu44TKzWNkLVBBxnxJz42in5n3QIolYOcBYFCPZZ0h8SkwQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/esrecurse": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", @@ -6958,13 +6251,6 @@ "@types/send": "*" } }, - "node_modules/@types/highlight-words-core": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@types/highlight-words-core/-/highlight-words-core-1.2.3.tgz", - "integrity": "sha512-PWNU/NR0CaYEsK38mcCTyDzeS2TlEGK9kRhRMz1i86jVAe836ZlA3gl6QYpu+CG6IpfNKTgWpEnJuRededvC0g==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/http-cache-semantics": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", @@ -7080,16 +6366,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/react": { - "version": "19.3.0", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.3.0.tgz", - "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==", - "dev": true, - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" - } - }, "node_modules/@types/responselike": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", @@ -7734,26 +7010,6 @@ "win32" ] }, - "node_modules/@use-gesture/core": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz", - "integrity": "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@use-gesture/react": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.1.tgz", - "integrity": "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@use-gesture/core": "10.3.1" - }, - "peerDependencies": { - "react": ">= 16.8.0" - } - }, "node_modules/@vitest/eslint-plugin": { "version": "1.6.28", "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.6.28.tgz", @@ -7993,30 +7249,6 @@ } } }, - "node_modules/@wordpress/a11y": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/a11y/-/a11y-5.0.0.tgz", - "integrity": "sha512-ocYyNn7Z85gDzvto3ppOPeuaj4VoDk03ymsO3ubuAoEDdxXgsTiJIZIBTYrwxvQZ3KznBe5i/HzPKrpvdpJQ+Q==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@wordpress/dom-ready": "^5.0.0", - "@wordpress/i18n": "^7.0.0" - }, - "engines": { - "node": ">=22.13.0" - } - }, - "node_modules/@wordpress/autop": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/autop/-/autop-5.0.0.tgz", - "integrity": "sha512-aHfC5Z7TmwvuRT0iV9e6l/N17C3VVcXcOlzouEC0x2/SNEc0dR71b3WPc0Qx4P44TAvpp7YQPJcaaM/Fz7Bfbg==", - "dev": true, - "license": "GPL-2.0-or-later", - "engines": { - "node": ">=22.13.0" - } - }, "node_modules/@wordpress/babel-preset-default": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/@wordpress/babel-preset-default/-/babel-preset-default-10.0.0.tgz", @@ -8040,160 +7272,6 @@ "node": ">=22.13.0" } }, - "node_modules/@wordpress/base-styles": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/base-styles/-/base-styles-14.0.0.tgz", - "integrity": "sha512-BkxwZfnaazn6qziF1yBcYPyGziFArrcsyKgm09fDd0SdegRsh2ZmlENwAcvfuQ23ENS/m3vuFMMmBUbqg+Efmg==", - "dev": true, - "license": "GPL-2.0-or-later", - "engines": { - "node": ">=22.13.0" - } - }, - "node_modules/@wordpress/blob": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/blob/-/blob-5.0.0.tgz", - "integrity": "sha512-BB2FpHEOq1my0Qe6BD/K5SAWt5I40vDZaFxH5PeeUyZ1KCEszaidkwYsY02sXxRK8rK/WOHGii2fOcpIqrmajA==", - "dev": true, - "license": "GPL-2.0-or-later", - "engines": { - "node": ">=22.13.0" - } - }, - "node_modules/@wordpress/block-editor": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/block-editor/-/block-editor-19.0.0.tgz", - "integrity": "sha512-HZqOQC10gR7NOU1cUWRrIKx/6XuZBrlbezw8fLgbMpZD91E0oglSqrkvejhE+Ezw8zpDHvD9L/SacfZv8BP/Hw==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@react-spring/web": "^10.1.2", - "@wordpress/a11y": "^5.0.0", - "@wordpress/base-styles": "^14.0.0", - "@wordpress/blob": "^5.0.0", - "@wordpress/block-serialization-default-parser": "^6.0.0", - "@wordpress/blocks": "^17.0.0", - "@wordpress/commands": "^2.0.0", - "@wordpress/components": "^43.0.0", - "@wordpress/compose": "^9.0.0", - "@wordpress/data": "^11.0.0", - "@wordpress/dataviews": "^21.0.0", - "@wordpress/date": "^6.0.0", - "@wordpress/deprecated": "^5.0.0", - "@wordpress/dom": "^5.0.0", - "@wordpress/element": "^10.0.0", - "@wordpress/global-styles-engine": "^2.0.0", - "@wordpress/hooks": "^5.0.0", - "@wordpress/html-entities": "^5.0.0", - "@wordpress/i18n": "^7.0.0", - "@wordpress/icons": "^19.0.0", - "@wordpress/image-cropper": "^2.0.0", - "@wordpress/interactivity": "^8.0.0", - "@wordpress/is-shallow-equal": "^6.0.0", - "@wordpress/kebab-case": "^2.0.0", - "@wordpress/keyboard-shortcuts": "^6.0.0", - "@wordpress/keycodes": "^5.0.0", - "@wordpress/notices": "^6.0.0", - "@wordpress/preferences": "^5.0.0", - "@wordpress/priority-queue": "^4.0.0", - "@wordpress/private-apis": "^3.0.0", - "@wordpress/react-inert-value": "^2.0.0", - "@wordpress/rich-text": "^8.0.0", - "@wordpress/style-engine": "^3.0.0", - "@wordpress/token-list": "^4.0.0", - "@wordpress/ui": "^0.25.0", - "@wordpress/upload-media": "^0.43.0", - "@wordpress/url": "^5.0.0", - "@wordpress/warning": "^4.0.0", - "change-case": "^4.1.2", - "clsx": "^2.1.1", - "colord": "^2.9.4", - "diff": "^8.0.3", - "fast-deep-equal": "^3.1.3", - "parsel-js": "^1.1.2", - "postcss": "^8.4.38", - "postcss-prefix-selector": "^1.16.0", - "postcss-urlrebase": "^1.4.0", - "react-easy-crop": "^5.4.2", - "remove-accents": "^0.5.0" - }, - "engines": { - "node": ">=22.13.0" - }, - "peerDependencies": { - "@types/react": "^18 || ^19", - "react": "^18 || ^19", - "react-dom": "^18 || ^19" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@wordpress/block-serialization-default-parser": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/block-serialization-default-parser/-/block-serialization-default-parser-6.0.0.tgz", - "integrity": "sha512-C9HgZeToeeNouMGjTnz5WV8z4vNZ+NBrX4af4Npy6F/VrikOpMAYYJ5hi1K07g/o9hzalZMYN+fHXvuQcYK9MQ==", - "dev": true, - "license": "GPL-2.0-or-later", - "engines": { - "node": ">=22.13.0" - } - }, - "node_modules/@wordpress/blocks": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/blocks/-/blocks-17.0.0.tgz", - "integrity": "sha512-6lZpvRhaFvg/GxYa6RXauU67g7ocjR2UloY1i53wFrCPoNgZAIIfn2q94IX5KeE35nSr0u8XdzDjyRwFz0QGYw==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@wordpress/autop": "^5.0.0", - "@wordpress/blob": "^5.0.0", - "@wordpress/block-serialization-default-parser": "^6.0.0", - "@wordpress/data": "^11.0.0", - "@wordpress/deprecated": "^5.0.0", - "@wordpress/dom": "^5.0.0", - "@wordpress/element": "^10.0.0", - "@wordpress/hooks": "^5.0.0", - "@wordpress/html-entities": "^5.0.0", - "@wordpress/i18n": "^7.0.0", - "@wordpress/is-shallow-equal": "^6.0.0", - "@wordpress/keycodes": "^5.0.0", - "@wordpress/private-apis": "^3.0.0", - "@wordpress/rich-text": "^8.0.0", - "@wordpress/shortcode": "^5.0.0", - "@wordpress/warning": "^4.0.0", - "change-case": "^4.1.2", - "colord": "^2.9.4", - "fast-deep-equal": "^3.1.3", - "hpq": "^1.4.0", - "is-plain-object": "^5.1.0", - "marked": "^18.0.3", - "memize": "^2.1.1", - "react-is": "^19.3.0", - "remove-accents": "^0.5.0", - "simple-html-tokenizer": "^0.5.7", - "uuid": "^14.0.0" - }, - "engines": { - "node": ">=22.13.0" - }, - "peerDependencies": { - "@types/react": "^18 || ^19", - "@types/react-dom": "^18 || ^19", - "react": "^18 || ^19", - "react-dom": "^18 || ^19" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@wordpress/browserslist-config": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@wordpress/browserslist-config/-/browserslist-config-7.0.0.tgz", @@ -8204,104 +7282,6 @@ "node": ">=22.13.0" } }, - "node_modules/@wordpress/commands": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/commands/-/commands-2.0.0.tgz", - "integrity": "sha512-pfkGCvuEOJ6v67++VKdn387pM3AFbAQKVLZJ2lq9Ei75xvd7pkmsnz3iqLA326E88IiXmSY49KSXuU/4bGsCYA==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@wordpress/base-styles": "^14.0.0", - "@wordpress/components": "^43.0.0", - "@wordpress/data": "^11.0.0", - "@wordpress/element": "^10.0.0", - "@wordpress/i18n": "^7.0.0", - "@wordpress/icons": "^19.0.0", - "@wordpress/keyboard-shortcuts": "^6.0.0", - "@wordpress/keycodes": "^5.0.0", - "@wordpress/preferences": "^5.0.0", - "@wordpress/private-apis": "^3.0.0", - "clsx": "^2.1.1", - "cmdk": "^1.0.0" - }, - "engines": { - "node": ">=22.13.0" - }, - "peerDependencies": { - "@types/react": "^18 || ^19", - "react": "^18 || ^19", - "react-dom": "^18 || ^19" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@wordpress/components": { - "version": "43.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/components/-/components-43.0.0.tgz", - "integrity": "sha512-8IfVyT0QXTlhcw9id9aSygYISKR+xjyyVWuJuwMGgb/SqHGBFLnBBKBRq/ypamF/eB0vE8Gb+AmdjP6G8P1LBw==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@ariakit/react": "^0.4.42", - "@date-fns/utc": "^2.1.1", - "@floating-ui/react-dom": "^2.1.9", - "@types/highlight-words-core": "^1.2.1", - "@use-gesture/react": "^10.3.1", - "@wordpress/a11y": "^5.0.0", - "@wordpress/base-styles": "^14.0.0", - "@wordpress/compose": "^9.0.0", - "@wordpress/date": "^6.0.0", - "@wordpress/deprecated": "^5.0.0", - "@wordpress/dom": "^5.0.0", - "@wordpress/element": "^10.0.0", - "@wordpress/escape-html": "^4.0.0", - "@wordpress/hooks": "^5.0.0", - "@wordpress/html-entities": "^5.0.0", - "@wordpress/i18n": "^7.0.0", - "@wordpress/icons": "^19.0.0", - "@wordpress/is-shallow-equal": "^6.0.0", - "@wordpress/keycodes": "^5.0.0", - "@wordpress/primitives": "^5.0.0", - "@wordpress/private-apis": "^3.0.0", - "@wordpress/react-inert-value": "^2.0.0", - "@wordpress/rich-text": "^8.0.0", - "@wordpress/style-runtime": "^0.14.0", - "@wordpress/ui": "^0.25.0", - "@wordpress/warning": "^4.0.0", - "clsx": "^2.1.1", - "colord": "^2.9.4", - "csstype": "^3.2.3", - "date-fns": "^4.4.0", - "deepmerge": "^4.3.1", - "fast-deep-equal": "^3.1.3", - "framer-motion": "^11.15.0", - "gradient-parser": "^1.2.0", - "highlight-words-core": "^1.2.2", - "is-plain-object": "^5.1.0", - "memize": "^2.1.1", - "path-to-regexp": "^6.2.1", - "re-resizable": "^6.11.2", - "react-colorful": "^5.6.1", - "remove-accents": "^0.5.0", - "uuid": "^14.0.0" - }, - "engines": { - "node": ">=22.13.0" - }, - "peerDependencies": { - "@types/react": "^18 || ^19", - "react": "^18 || ^19", - "react-dom": "^18 || ^19" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@wordpress/compose": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/@wordpress/compose/-/compose-9.0.0.tgz", @@ -8326,131 +7306,19 @@ }, "peerDependencies": { "@types/react": "^18 || ^19", - "@types/react-dom": "^18 || ^19", - "react": "^18 || ^19", - "react-dom": "^18 || ^19" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@wordpress/data": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/data/-/data-11.0.0.tgz", - "integrity": "sha512-z74Y4/cfyw+Kel5uhVYsoZTytFtUw2xRrfFpa7uWIdfrdQxSVgxajd74pRjxGQ6W+g0VWzV6ZBptHDxYf58Nmg==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@wordpress/compose": "^9.0.0", - "@wordpress/deprecated": "^5.0.0", - "@wordpress/element": "^10.0.0", - "@wordpress/is-shallow-equal": "^6.0.0", - "@wordpress/priority-queue": "^4.0.0", - "@wordpress/private-apis": "^3.0.0", - "@wordpress/redux-routine": "^6.0.0", - "deepmerge": "^4.3.1", - "equivalent-key-map": "^0.2.2", - "is-plain-object": "^5.1.0", - "is-promise": "^4.0.0", - "redux": "^5.0.1", - "rememo": "^4.0.2" - }, - "engines": { - "node": ">=22.13.0" - }, - "peerDependencies": { - "@types/react": "^18 || ^19", - "@types/react-dom": "^18 || ^19", - "react": "^18 || ^19", - "react-dom": "^18 || ^19" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@wordpress/dataviews": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/dataviews/-/dataviews-21.0.0.tgz", - "integrity": "sha512-/aojI+0aZEpMYkteZ404V2Bhyxu0Kr5zcf7WhEgmIyHEcfBmuoKIxiPjnVQgAC990gmwW522LYBLnepOymdIOg==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@ariakit/react": "^0.4.42", - "@base-ui/react": "^1.9.0", - "@date-fns/tz": "^1.5.0", - "@daypicker/react": "^10.0.1", - "@floating-ui/react-dom": "^2.1.9", - "@use-gesture/react": "^10.3.1", - "@wordpress/a11y": "^5.0.0", - "@wordpress/base-styles": "^14.0.0", - "@wordpress/components": "^43.0.0", - "@wordpress/compose": "^9.0.0", - "@wordpress/data": "^11.0.0", - "@wordpress/date": "^6.0.0", - "@wordpress/deprecated": "^5.0.0", - "@wordpress/element": "^10.0.0", - "@wordpress/hooks": "^5.0.0", - "@wordpress/i18n": "^7.0.0", - "@wordpress/icons": "^19.0.0", - "@wordpress/kebab-case": "^2.0.0", - "@wordpress/keycodes": "^5.0.0", - "@wordpress/react-inert-value": "^2.0.0", - "@wordpress/ui": "^0.25.0", - "@wordpress/warning": "^4.0.0", - "change-case": "^4.1.2", - "clsx": "^2.1.1", - "colord": "^2.9.4", - "colorjs.io": "^0.7.1", - "date-fns": "^4.4.0", - "deepmerge": "^4.3.1", - "fast-deep-equal": "^3.1.3", - "framer-motion": "^11.15.0", - "highlight-words-core": "^1.2.2", - "is-plain-object": "^5.1.0", - "memize": "^2.1.1", - "react-colorful": "^5.6.1", - "remove-accents": "^0.5.0", - "uuid": "^14.0.0" - }, - "engines": { - "node": ">=22.13.0" - }, - "peerDependencies": { - "@types/react": "^18 || ^19", + "@types/react-dom": "^18 || ^19", "react": "^18 || ^19", "react-dom": "^18 || ^19" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@wordpress/date": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/date/-/date-6.0.0.tgz", - "integrity": "sha512-knjw0jMLk2RzLq8wUzLuS0lvNI1UqwpbqxYVuSyXrUb+8GKXaT/uq0ql82Dha0RiagNCfrneowZFo3mJc7EyTw==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@wordpress/deprecated": "^5.0.0", - "moment": "^2.31.0", - "moment-timezone": "^0.5.40" - }, - "engines": { - "node": ">=22.13.0" - } - }, "node_modules/@wordpress/dependency-extraction-webpack-plugin": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@wordpress/dependency-extraction-webpack-plugin/-/dependency-extraction-webpack-plugin-7.0.0.tgz", @@ -8493,16 +7361,6 @@ "node": ">=22.13.0" } }, - "node_modules/@wordpress/dom-ready": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/dom-ready/-/dom-ready-5.0.0.tgz", - "integrity": "sha512-a2Y+loEwENAM+kyTnXU7LTe8GgriXWfoxbco4486e2FuNpfELPLjDLAeJVbaFuw8pbtkk6xARlLh32y7yGG7Rg==", - "dev": true, - "license": "GPL-2.0-or-later", - "engines": { - "node": ">=22.13.0" - } - }, "node_modules/@wordpress/e2e-test-utils-playwright": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@wordpress/e2e-test-utils-playwright/-/e2e-test-utils-playwright-3.1.0.tgz", @@ -8639,42 +7497,6 @@ } } }, - "node_modules/@wordpress/global-styles-engine": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/global-styles-engine/-/global-styles-engine-2.0.0.tgz", - "integrity": "sha512-lhMen98kG/nLv3QPD8HWXPdhuXDbOje7/gYpljv4zKBjAf3tfbRQ11MblKW8OvYUux3Iu9vaZE+DB2LOgNee5g==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@wordpress/blocks": "^17.0.0", - "@wordpress/data": "^11.0.0", - "@wordpress/i18n": "^7.0.0", - "@wordpress/private-apis": "^3.0.0", - "@wordpress/style-engine": "^3.0.0", - "colord": "^2.9.4", - "deepmerge": "^4.3.1", - "fast-deep-equal": "^3.1.3", - "is-plain-object": "^5.1.0", - "memize": "^2.1.1" - }, - "engines": { - "node": ">=22.13.0" - }, - "peerDependencies": { - "@types/react": "^18 || ^19", - "@types/react-dom": "^18 || ^19", - "react": "^18 || ^19", - "react-dom": "^18 || ^19" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@wordpress/hooks": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-5.0.0.tgz", @@ -8685,16 +7507,6 @@ "node": ">=22.13.0" } }, - "node_modules/@wordpress/html-entities": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/html-entities/-/html-entities-5.0.0.tgz", - "integrity": "sha512-rj95Op3y+4+QsdOzlguOT8lpAHegJwdMsDx9Eq/9/fL/CGskgugUm4aqqeFQDNd7B+gOzXM7NZMX2ohaYYXYdA==", - "dev": true, - "license": "GPL-2.0-or-later", - "engines": { - "node": ">=22.13.0" - } - }, "node_modules/@wordpress/i18n": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-7.0.0.tgz", @@ -8714,77 +7526,6 @@ "node": ">=22.13.0" } }, - "node_modules/@wordpress/icons": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/icons/-/icons-19.0.0.tgz", - "integrity": "sha512-LPx0Tppj5Y/54fuqIRMUqzplP0ZQL1nNvWthUv+ctpylDbGe4qdlWJpLCLZq1BpI1IXS2s3g5NaN2dXFi5H45w==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@wordpress/element": "^10.0.0", - "@wordpress/primitives": "^5.0.0", - "change-case": "^4.1.2" - }, - "engines": { - "node": ">=22.13.0" - }, - "peerDependencies": { - "@types/react": "^18 || ^19", - "@types/react-dom": "^18 || ^19", - "react": "^18 || ^19", - "react-dom": "^18 || ^19" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@wordpress/image-cropper": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/image-cropper/-/image-cropper-2.0.0.tgz", - "integrity": "sha512-Ach0WfHZbwM8oomQtgHsTMgG0+wuhlZYD7ZHsygREkPVD9f0Kd+513rtRFfMdQ2NJzdjqJXSD2nEK0hT/gGjOQ==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@wordpress/components": "^43.0.0", - "@wordpress/element": "^10.0.0", - "@wordpress/i18n": "^7.0.0", - "dequal": "^2.0.3", - "react-easy-crop": "^5.4.2" - }, - "engines": { - "node": ">=22.13.0" - }, - "peerDependencies": { - "@types/react": "^18 || ^19", - "react": "^18 || ^19", - "react-dom": "^18 || ^19" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@wordpress/interactivity": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/interactivity/-/interactivity-8.0.0.tgz", - "integrity": "sha512-VI60fYRVsSErIs/K9xnUNoAXUycgQwMGP7cq7YHvWdKROPE285+lnv9GarRGFR7W5/9i5fk3a8ifV+2o6sMCOg==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@preact/signals": "^1.3.0", - "@preact/signals-core": "^1.7.0", - "preact": "^10.29.1" - }, - "engines": { - "node": ">=22.13.0" - } - }, "node_modules/@wordpress/is-shallow-equal": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@wordpress/is-shallow-equal/-/is-shallow-equal-6.0.0.tgz", @@ -8795,48 +7536,6 @@ "node": ">=22.13.0" } }, - "node_modules/@wordpress/kebab-case": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/kebab-case/-/kebab-case-2.0.0.tgz", - "integrity": "sha512-JjPgCJ5SRbbiKboK1U+k37NY8Hb42hHzMphczPtYPGGRNiasMV7T4RkbKlvUo+Qcm0hE6G8ct9VnEsfSsEbjoQ==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "change-case": "^4.1.2" - }, - "engines": { - "node": ">=22.13.0" - } - }, - "node_modules/@wordpress/keyboard-shortcuts": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/keyboard-shortcuts/-/keyboard-shortcuts-6.0.0.tgz", - "integrity": "sha512-6OkdL+ZFJ+0llQtiDkP5OifzKvX7NMJpwsbBssm9vfNa93okyLOWY3j8HzUyKQLnuSrO8z+SwBzSJjImUHorTg==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@wordpress/data": "^11.0.0", - "@wordpress/element": "^10.0.0", - "@wordpress/keycodes": "^5.0.0" - }, - "engines": { - "node": ">=22.13.0" - }, - "peerDependencies": { - "@types/react": "^18 || ^19", - "@types/react-dom": "^18 || ^19", - "react": "^18 || ^19", - "react-dom": "^18 || ^19" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@wordpress/keycodes": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@wordpress/keycodes/-/keycodes-5.0.0.tgz", @@ -8858,42 +7557,6 @@ } } }, - "node_modules/@wordpress/notices": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/notices/-/notices-6.0.0.tgz", - "integrity": "sha512-IDiPfOgUvswr1aXVm3SwOziTtZY+R8t0CfO0jHzK1M50ddzRRTReZPM1bPftU23PAW5k1UqxPc/aYRwLK4Xopg==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@wordpress/a11y": "^5.0.0", - "@wordpress/components": "^43.0.0", - "@wordpress/compose": "^9.0.0", - "@wordpress/data": "^11.0.0", - "@wordpress/deprecated": "^5.0.0", - "@wordpress/element": "^10.0.0", - "@wordpress/i18n": "^7.0.0", - "@wordpress/private-apis": "^3.0.0", - "@wordpress/ui": "^0.25.0", - "clsx": "^2.1.1" - }, - "engines": { - "node": ">=22.13.0" - }, - "peerDependencies": { - "@types/react": "^18 || ^19", - "@types/react-dom": "^18 || ^19", - "react": "^18 || ^19", - "react-dom": "^18 || ^19" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@wordpress/npm-package-json-lint-config": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@wordpress/npm-package-json-lint-config/-/npm-package-json-lint-config-6.0.0.tgz", @@ -8915,184 +7578,50 @@ "license": "GPL-2.0-or-later", "dependencies": { "@wordpress/browserslist-config": "^7.0.0", - "autoprefixer": "^10.4.21", - "postcss-import": "^16.1.1" - }, - "engines": { - "node": ">=22.13.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/@wordpress/preferences": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/preferences/-/preferences-5.0.0.tgz", - "integrity": "sha512-rQaSz2di9k3tzm9BkO26ikydgevBDf+QU9ET97rFjlSaoDqQ1YqOLcFH61o8vtthr3NjQFBq/XAz38VMCVVsXw==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@wordpress/a11y": "^5.0.0", - "@wordpress/base-styles": "^14.0.0", - "@wordpress/components": "^43.0.0", - "@wordpress/compose": "^9.0.0", - "@wordpress/data": "^11.0.0", - "@wordpress/deprecated": "^5.0.0", - "@wordpress/element": "^10.0.0", - "@wordpress/i18n": "^7.0.0", - "@wordpress/icons": "^19.0.0", - "@wordpress/private-apis": "^3.0.0" - }, - "engines": { - "node": ">=22.13.0" - }, - "peerDependencies": { - "@types/react": "^18 || ^19", - "react": "^18 || ^19", - "react-dom": "^18 || ^19" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@wordpress/prettier-config": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/prettier-config/-/prettier-config-5.0.0.tgz", - "integrity": "sha512-uQ3rtrqt8yvUHFN9Xcxuw7EfSw4ArMkwFxtz2RHZM2Sis9jzB7xk0FcK/iWE55q0WWSjmFGWoNVpOEQoSE7ocQ==", - "dev": true, - "license": "GPL-2.0-or-later", - "engines": { - "node": ">=22.13.0" - }, - "peerDependencies": { - "prettier": ">=3" - } - }, - "node_modules/@wordpress/primitives": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/primitives/-/primitives-5.0.0.tgz", - "integrity": "sha512-XrRJIbNXai/jlL9FLEANUzaAigTdN8XgWzAHTfAjDskvy3X6CjUbMyjXLWE5OhyCKhq+JAd3mWb9zq4sgl+V2w==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@wordpress/element": "^10.0.0", - "clsx": "^2.1.1" - }, - "engines": { - "node": ">=22.13.0" - }, - "peerDependencies": { - "@types/react": "^18 || ^19", - "@types/react-dom": "^18 || ^19", - "react": "^18 || ^19", - "react-dom": "^18 || ^19" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@wordpress/priority-queue": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/priority-queue/-/priority-queue-4.0.0.tgz", - "integrity": "sha512-YXEwn7xtr4bUts2IOPREjlWYa3n/ba7ndLCqFN3ofeRvvr2ZVCI1WP20v7a+CQZi+n9FS7fy+lYqx3wnTYtGzg==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "requestidlecallback": "^0.3.0" - }, - "engines": { - "node": ">=22.13.0" - } - }, - "node_modules/@wordpress/private-apis": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/private-apis/-/private-apis-3.0.0.tgz", - "integrity": "sha512-IDXGi2VWfjUFwBTDtWj7/FMY240JpqraxFEIaXIeD3KFumX8lQuAYvE9gU8GcNBt/NlXZCDEmNioxCbAZN+jiA==", - "dev": true, - "license": "GPL-2.0-or-later", + "autoprefixer": "^10.4.21", + "postcss-import": "^16.1.1" + }, "engines": { "node": ">=22.13.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" } }, - "node_modules/@wordpress/react-inert-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/react-inert-value/-/react-inert-value-2.0.0.tgz", - "integrity": "sha512-inwoViBZ2K3ubTvfjkvpzwpXovZVF05CMWZ08scQt+ttYHD1Sf3pL3gn11iAxcR1Wf/0TzZoPeLc+PhnAUzuSg==", + "node_modules/@wordpress/prettier-config": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/prettier-config/-/prettier-config-5.0.0.tgz", + "integrity": "sha512-uQ3rtrqt8yvUHFN9Xcxuw7EfSw4ArMkwFxtz2RHZM2Sis9jzB7xk0FcK/iWE55q0WWSjmFGWoNVpOEQoSE7ocQ==", "dev": true, "license": "GPL-2.0-or-later", "engines": { "node": ">=22.13.0" }, "peerDependencies": { - "@types/react": "^18 || ^19", - "react": "^18 || ^19" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "prettier": ">=3" } }, - "node_modules/@wordpress/redux-routine": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/redux-routine/-/redux-routine-6.0.0.tgz", - "integrity": "sha512-mJm9DeHXFIPeaCA6E+VYupWXBW7g1QuKqEgBclm3HCDN+Th6OO+HMLfa2eS+dAPyoTu2NhSF0QlY1ArijOzuiw==", + "node_modules/@wordpress/priority-queue": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/priority-queue/-/priority-queue-4.0.0.tgz", + "integrity": "sha512-YXEwn7xtr4bUts2IOPREjlWYa3n/ba7ndLCqFN3ofeRvvr2ZVCI1WP20v7a+CQZi+n9FS7fy+lYqx3wnTYtGzg==", "dev": true, "license": "GPL-2.0-or-later", "dependencies": { - "is-plain-object": "^5.1.0", - "is-promise": "^4.0.0", - "rungen": "^0.3.2" + "requestidlecallback": "^0.3.0" }, "engines": { "node": ">=22.13.0" - }, - "peerDependencies": { - "redux": ">=4" } }, - "node_modules/@wordpress/rich-text": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/rich-text/-/rich-text-8.0.0.tgz", - "integrity": "sha512-2Jg50SJwE82+qO8tnQbIhSqCIb9I+rW6bi0hRVcTMOtRxC8wBisw5n0zngLfGRSHtAA9irnEzrOfPk2m6LtyuQ==", + "node_modules/@wordpress/private-apis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/private-apis/-/private-apis-3.0.0.tgz", + "integrity": "sha512-IDXGi2VWfjUFwBTDtWj7/FMY240JpqraxFEIaXIeD3KFumX8lQuAYvE9gU8GcNBt/NlXZCDEmNioxCbAZN+jiA==", "dev": true, "license": "GPL-2.0-or-later", - "dependencies": { - "@wordpress/a11y": "^5.0.0", - "@wordpress/compose": "^9.0.0", - "@wordpress/data": "^11.0.0", - "@wordpress/deprecated": "^5.0.0", - "@wordpress/dom": "^5.0.0", - "@wordpress/element": "^10.0.0", - "@wordpress/escape-html": "^4.0.0", - "@wordpress/i18n": "^7.0.0", - "@wordpress/keycodes": "^5.0.0", - "@wordpress/private-apis": "^3.0.0", - "colord": "^2.9.4" - }, "engines": { "node": ">=22.13.0" - }, - "peerDependencies": { - "@types/react": "^18 || ^19", - "@types/react-dom": "^18 || ^19", - "react": "^18 || ^19", - "react-dom": "^18 || ^19" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } } }, "node_modules/@wordpress/scripts": { @@ -9392,40 +7921,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@wordpress/shortcode": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/shortcode/-/shortcode-5.0.0.tgz", - "integrity": "sha512-anwstb/SfweKgw8BNEouvzwCII9AAY7I/asPZJf084rEtG/5V2II9ls0PdfamYuDedNvFdToCV3uiK9NFD06jw==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "memize": "^2.1.1" - }, - "engines": { - "node": ">=22.13.0" - } - }, - "node_modules/@wordpress/style-engine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/style-engine/-/style-engine-3.0.0.tgz", - "integrity": "sha512-obU8yuXphfCZVdcv7bVf/dn6AlzBqYfglyyc/ymqEyjmh4cTY0pF/Do1sraT/XP5n+NZrFGV5D9wcJILTNC3Lg==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "change-case": "^4.1.2" - }, - "engines": { - "node": ">=22.13.0" - }, - "peerDependencies": { - "@types/react": "^18 || ^19" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@wordpress/style-runtime": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/@wordpress/style-runtime/-/style-runtime-0.14.0.tgz", @@ -9516,55 +8011,6 @@ } } }, - "node_modules/@wordpress/token-list": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/token-list/-/token-list-4.0.0.tgz", - "integrity": "sha512-W8WY1B4POIXLQcWM/N3Zz9TGMMqtfJvS2VcIql889eEFKk1trx0ynsbzVFO/UblGiSjAdxpANES9yHNKa43VWw==", - "dev": true, - "license": "GPL-2.0-or-later", - "engines": { - "node": ">=22.13.0" - } - }, - "node_modules/@wordpress/ui": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@wordpress/ui/-/ui-0.25.0.tgz", - "integrity": "sha512-G/aUYgQpL18q3DfyV/1ebB8tEjXBubWPPXitcs4IvXYbAAFNBLsoUUGv5K3sfqJuxhi51lbyKT0Eap6Wj0kkCQ==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@base-ui/react": "^1.9.0", - "@daypicker/react": "^10.0.1", - "@wordpress/a11y": "^5.0.0", - "@wordpress/compose": "^9.0.0", - "@wordpress/element": "^10.0.0", - "@wordpress/i18n": "^7.0.0", - "@wordpress/icons": "^19.0.0", - "@wordpress/keycodes": "^5.0.0", - "@wordpress/primitives": "^5.0.0", - "@wordpress/private-apis": "^3.0.0", - "@wordpress/react-inert-value": "^2.0.0", - "@wordpress/style-runtime": "^0.14.0", - "@wordpress/theme": "^3.0.0", - "@wordpress/warning": "^4.0.0", - "clsx": "^2.1.1", - "date-fns": "^4.4.0", - "tabbable": "^6.4.0" - }, - "engines": { - "node": ">=22.13.0" - }, - "peerDependencies": { - "@types/react": "^18 || ^19", - "react": "^18 || ^19", - "react-dom": "^18 || ^19" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@wordpress/undo-manager": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@wordpress/undo-manager/-/undo-manager-2.0.0.tgz", @@ -9578,75 +8024,6 @@ "node": ">=22.13.0" } }, - "node_modules/@wordpress/upload-media": { - "version": "0.43.0", - "resolved": "https://registry.npmjs.org/@wordpress/upload-media/-/upload-media-0.43.0.tgz", - "integrity": "sha512-Cgi0XNoWurFFbCJPljPaii2cy7hHwMml3Dbf9zhlUsyihT3H9LfkSR/Oox+NHFPOqAPv1q2YPWCGuZe3mXDZEQ==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@wordpress/blob": "^5.0.0", - "@wordpress/compose": "^9.0.0", - "@wordpress/data": "^11.0.0", - "@wordpress/element": "^10.0.0", - "@wordpress/i18n": "^7.0.0", - "@wordpress/private-apis": "^3.0.0", - "@wordpress/video-conversion": "^0.9.0", - "@wordpress/vips": "^5.0.0", - "uuid": "^14.0.0" - }, - "engines": { - "node": ">=22.13.0" - }, - "peerDependencies": { - "@types/react": "^18 || ^19", - "react": "^18 || ^19", - "react-dom": "^18 || ^19" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@wordpress/url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/url/-/url-5.0.0.tgz", - "integrity": "sha512-oPDn3vjO7unimaiFSnwoP8Ys6TVGp3UXwF0CwRDMoYLqRIjDuPpZLoUbkPAnWnLOrNP+GjIzCm7qQNCrze/UAA==", - "dev": true, - "license": "GPL-2.0-or-later", - "engines": { - "node": ">=22.13.0" - } - }, - "node_modules/@wordpress/video-conversion": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@wordpress/video-conversion/-/video-conversion-0.9.0.tgz", - "integrity": "sha512-fxU5BDB809r/AbxM70LUst3CjWpn85ugT6QebwugOlX+s7aUQ7/HZ5ON0XZE4V2nPtOeUsT1HyVDcikcbaMZbQ==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@wordpress/worker-threads": "^2.0.0", - "mediabunny": "^1.45.2" - }, - "engines": { - "node": ">=22.13.0" - } - }, - "node_modules/@wordpress/vips": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/vips/-/vips-5.0.0.tgz", - "integrity": "sha512-7he8zRvtoaI+uGCoQ6Tf5+PbuhB0OowB0h8H4HLB7yv8hl4YOxnGG1tB1ldB6gypNyukcawk+aK/CcJ2+cnI2A==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "@wordpress/worker-threads": "^2.0.0", - "wasm-vips": "^0.0.18" - }, - "engines": { - "node": ">=22.13.0" - } - }, "node_modules/@wordpress/warning": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@wordpress/warning/-/warning-4.0.0.tgz", @@ -9657,19 +8034,6 @@ "node": ">=22.13.0" } }, - "node_modules/@wordpress/worker-threads": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@wordpress/worker-threads/-/worker-threads-2.0.0.tgz", - "integrity": "sha512-ytIahXlVLdMMDfdDvOl1X4GrzVRcxc4JB+41zWJFdkNnzxDe6xbuyKujNe6kwrWFvr2YfC41wXfPViHEzB96pw==", - "dev": true, - "license": "GPL-2.0-or-later", - "dependencies": { - "comctx": "^1.4.3" - }, - "engines": { - "node": ">=22.13.0" - } - }, "node_modules/@wp-playground/blueprints": { "version": "3.1.60", "resolved": "https://registry.npmjs.org/@wp-playground/blueprints/-/blueprints-3.1.60.tgz", @@ -10086,19 +8450,6 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/aria-hidden": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", - "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/aria-query": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", @@ -11381,33 +9732,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/cmdk": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz", - "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "^1.1.1", - "@radix-ui/react-dialog": "^1.1.6", - "@radix-ui/react-id": "^1.1.0", - "@radix-ui/react-primitive": "^2.0.2" - }, - "peerDependencies": { - "react": "^18 || ^19 || ^19.0.0-rc", - "react-dom": "^18 || ^19 || ^19.0.0-rc" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -11453,13 +9777,6 @@ "url": "https://opencollective.com/color" } }, - "node_modules/comctx": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/comctx/-/comctx-1.7.6.tgz", - "integrity": "sha512-rDhDqh6qfmivZfVOGi4qs8RB3PnVtIAB5zLsWu4JqCoQ1p4IEJSB5pW2JvOhFwCMmCLiei9qpaIMxmVVQRhzyw==", - "dev": true, - "license": "MIT" - }, "node_modules/commander": { "version": "15.0.0", "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", @@ -12008,13 +10325,6 @@ "dev": true, "license": "CC0-1.0" }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, - "license": "MIT" - }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -12076,17 +10386,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/date-fns": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.4.0.tgz", - "integrity": "sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/kossnocorp" - } - }, "node_modules/debounce": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", @@ -12374,13 +10673,6 @@ "node": ">=8" } }, - "node_modules/detect-node-es": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", - "dev": true, - "license": "MIT" - }, "node_modules/devlop": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", @@ -12402,16 +10694,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/diff": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", - "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/diff3": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/diff3/-/diff3-0.0.3.tgz", @@ -12740,13 +11022,6 @@ "node": ">=4" } }, - "node_modules/equivalent-key-map": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/equivalent-key-map/-/equivalent-key-map-0.2.2.tgz", - "integrity": "sha512-xvHeyCDbZzkpN4VHQj/n+j2lOwL0VWszG30X4cOrc9Y7Tuo2qCdZK/0AMod23Z5dCtNUbaju6p0rwOhHUk05ew==", - "dev": true, - "license": "MIT" - }, "node_modules/error-ex": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", @@ -14291,34 +12566,6 @@ "url": "https://github.com/sponsors/rawify" } }, - "node_modules/framer-motion": { - "version": "11.18.2", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz", - "integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "motion-dom": "^11.18.1", - "motion-utils": "^11.18.1", - "tslib": "^2.4.0" - }, - "peerDependencies": { - "@emotion/is-prop-valid": "*", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/is-prop-valid": { - "optional": true - }, - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -14478,16 +12725,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-nonce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", - "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/get-port": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", @@ -14808,15 +13045,6 @@ "dev": true, "license": "ISC" }, - "node_modules/gradient-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gradient-parser/-/gradient-parser-1.2.0.tgz", - "integrity": "sha512-6ABGa9CR7WR/0pAJicBy5SJkiikbFM6kf/JjykwX7x+t+s8ORWVnlbi6FkHeFFb36yWsjUpHqSYrygd7ofEUqA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/gzip-size": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", @@ -14975,15 +13203,8 @@ "dev": true, "license": "MIT", "dependencies": { - "hermes-estree": "0.25.1" - } - }, - "node_modules/highlight-words-core": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/highlight-words-core/-/highlight-words-core-1.2.3.tgz", - "integrity": "sha512-m1O9HW3/GNHxzSIXWw1wCNXXsgLlxrP0OI6+ycGUhiUHkikqW3OrwVHz+lxeNBe5yqLESdIcj8PowHQ2zLvUvQ==", - "dev": true, - "license": "MIT" + "hermes-estree": "0.25.1" + } }, "node_modules/hookified": { "version": "1.15.1", @@ -15025,13 +13246,6 @@ "dev": true, "license": "ISC" }, - "node_modules/hpq": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/hpq/-/hpq-1.4.0.tgz", - "integrity": "sha512-ycJQMRaRPBcfnoT1gS5I1XCvbbw9KO94Y0vkwksuOjcJMqNZtb03MF2tCItLI2mQbkZWSSeFinoRDPmjzv4tKg==", - "dev": true, - "license": "MIT" - }, "node_modules/html-entities": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", @@ -17101,19 +15315,6 @@ "js-yaml": "bin/js-yaml.mjs" } }, - "node_modules/marked": { - "version": "18.1.1", - "resolved": "https://registry.npmjs.org/marked/-/marked-18.1.1.tgz", - "integrity": "sha512-zEGgrtUG8crk+TrMacJSVABU4DYDDkS8uqgU3ohrn/2fW9C2AEfUrO3m6IhpIpQpl7YcVpiXXC9cSMWDANT3dg==", - "dev": true, - "license": "MIT", - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 20" - } - }, "node_modules/marky": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz", @@ -17166,25 +15367,6 @@ "node": ">= 0.6" } }, - "node_modules/mediabunny": { - "version": "1.62.1", - "resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.62.1.tgz", - "integrity": "sha512-lYzeerDwo9QLhBCipqQDXOSV7InvaIFplllZQviPO5rPwH+IUr8M1uvumk1olIUgMRGkPiInRriv25WFErUgMw==", - "dev": true, - "license": "MPL-2.0", - "workspaces": [ - ".", - "packages/*" - ], - "dependencies": { - "@types/dom-mediacapture-transform": "^0.1.11", - "@types/dom-webcodecs": "0.1.13" - }, - "funding": { - "type": "individual", - "url": "https://github.com/sponsors/Vanilagy" - } - }, "node_modules/memfs": { "version": "4.80.0", "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.80.0.tgz", @@ -18111,46 +16293,6 @@ "dev": true, "license": "MIT" }, - "node_modules/moment": { - "version": "2.31.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.31.0.tgz", - "integrity": "sha512-0acOTfMiWOheYS4eoWb80yYMb/JLvVv9SHbs2PehaDzfUG0Bw855SKyk0IKTnPGa5+U2bmi3W68l1+sGLX/pvw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/moment-timezone": { - "version": "0.5.48", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz", - "integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==", - "dev": true, - "license": "MIT", - "dependencies": { - "moment": "^2.29.4" - }, - "engines": { - "node": "*" - } - }, - "node_modules/motion-dom": { - "version": "11.18.1", - "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz", - "integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==", - "dev": true, - "license": "MIT", - "dependencies": { - "motion-utils": "^11.18.1" - } - }, - "node_modules/motion-utils": { - "version": "11.18.1", - "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz", - "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==", - "dev": true, - "license": "MIT" - }, "node_modules/mousetrap": { "version": "1.6.5", "resolved": "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.5.tgz", @@ -18364,13 +16506,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/normalize-wheel": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/normalize-wheel/-/normalize-wheel-1.0.1.tgz", - "integrity": "sha512-1OnlAPZ3zgrk8B91HyRj+eVv+kS5u+Z0SCsak6Xil/kmgEia50ga7zfkumayonZrImffAxPU/5WcyGhzetHNPA==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/npm-bundled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", @@ -19162,23 +17297,6 @@ "dev": true, "license": "MIT" }, - "node_modules/parsel-js": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/parsel-js/-/parsel-js-1.2.3.tgz", - "integrity": "sha512-kJHFmSNnujpusFfVEXjNaoJ09VYHF8Ih0aVUn7Clu+Ug8AFoA2wx7Ezh17JfW4+vhm/lauAd9A+//uFkmydZtQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/LeaVerou" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/leaverou" - } - ], - "license": "MIT" - }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -19288,13 +17406,6 @@ "dev": true, "license": "ISC" }, - "node_modules/path-to-regexp": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", - "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", - "dev": true, - "license": "MIT" - }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -20065,16 +18176,6 @@ "postcss": "^8.4.31" } }, - "node_modules/postcss-prefix-selector": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/postcss-prefix-selector/-/postcss-prefix-selector-1.16.1.tgz", - "integrity": "sha512-Umxu+FvKMwlY6TyDzGFoSUnzW+NOfMBLyC1tAkIjgX+Z/qGspJeRjVC903D7mx7TuBpJlwti2ibXtWuA7fKMeQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "postcss": ">4 <9" - } - }, "node_modules/postcss-reduce-initial": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz", @@ -20230,19 +18331,6 @@ "node": ">=4" } }, - "node_modules/postcss-urlrebase": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/postcss-urlrebase/-/postcss-urlrebase-1.4.0.tgz", - "integrity": "sha512-rRaxMmWvXrn8Rk1PqsxmaJwldRHsr0WbbASKKCZYxXwotHkM/5X/6IrwaEe8pdzpbNGCEY86yhYMN0MhgOkADA==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "peerDependencies": { - "postcss": "^8.3.0" - } - }, "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", @@ -20263,25 +18351,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/preact": { - "version": "10.29.8", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.8.tgz", - "integrity": "sha512-ej2aVZ+vZ8WO7tvlQWRM9N63A0KzF9q4mWJfDUHgYaIofWY9hu74QdnQrjoPMmZi2/nZ5gN0bJCQF49xQqx09Q==", - "dev": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/preact" - }, - "peerDependencies": { - "preact-render-to-string": ">=5" - }, - "peerDependenciesMeta": { - "preact-render-to-string": { - "optional": true - } - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -20587,17 +18656,6 @@ "node": ">=0.10.0" } }, - "node_modules/re-resizable": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-6.11.2.tgz", - "integrity": "sha512-2xI2P3OHs5qw7K0Ud1aLILK6MQxW50TcO+DetD9eIV58j84TqYeHoZcL9H4GXFXXIh7afhH8mv5iUCXII7OW7A==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "react": "^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/react": { "version": "19.3.1", "resolved": "https://registry.npmjs.org/react/-/react-19.3.1.tgz", @@ -20608,44 +18666,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-colorful": { - "version": "5.8.1", - "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.8.1.tgz", - "integrity": "sha512-oz68bhsnFWnpDf1ZR8daiQbYpXUnM2h2J6hl9Zg2rTpM/DU6vCqe1E+CpqmqLnJucMZetHZeifSAfJ+geN9lcA==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/react-day-picker": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-10.0.2.tgz", - "integrity": "sha512-QetiKtZWE9Uaw/pM/SYNv6WPYOTRnSJ2puL0GgwkmUFMIwiY0dtjH1W4B7DOVXVNQM0ZOClGpH/heOfBSzWeiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@date-fns/tz": "^1.4.1", - "date-fns": "^4.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "individual", - "url": "https://github.com/sponsors/gpbl" - }, - "peerDependencies": { - "@types/react": ">=16.8.0", - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/react-dom": { "version": "19.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.1.tgz", @@ -20660,28 +18680,6 @@ "react": "^19.3.1" } }, - "node_modules/react-easy-crop": { - "version": "5.5.7", - "resolved": "https://registry.npmjs.org/react-easy-crop/-/react-easy-crop-5.5.7.tgz", - "integrity": "sha512-kYo4NtMeXFQB7h1U+h5yhUkE46WQbQdq7if54uDlbMdZHdRgNehfvaFrXnFw5NR1PNoUOJIfTwLnWmEx/MaZnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "normalize-wheel": "^1.0.1", - "tslib": "^2.0.1" - }, - "peerDependencies": { - "react": ">=16.4.0", - "react-dom": ">=16.4.0" - } - }, - "node_modules/react-is": { - "version": "19.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.3.1.tgz", - "integrity": "sha512-RRieF6IyxQBkcK6pP8xOHprLTPW6QjRkbGdImtqNme+/hYVwCZkLQLdlZGOhUqXwkS9IEynaGvPJtv6XB/cz0Q==", - "dev": true, - "license": "MIT" - }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", @@ -20692,78 +18690,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-remove-scroll": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", - "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "react-remove-scroll-bar": "^2.3.7", - "react-style-singleton": "^2.2.3", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.3", - "use-sidecar": "^1.1.3" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-remove-scroll-bar": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", - "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "react-style-singleton": "^2.2.2", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-style-singleton": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", - "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-nonce": "^1.0.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/read-cache": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.2.tgz", @@ -20969,13 +18895,6 @@ "node": ">=8" } }, - "node_modules/redux": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", - "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "dev": true, - "license": "MIT" - }, "node_modules/reflect-metadata": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", @@ -21085,20 +19004,6 @@ "regjsparser": "bin/parser" } }, - "node_modules/rememo": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/rememo/-/rememo-4.0.2.tgz", - "integrity": "sha512-NVfSP9NstE3QPNs/TnegQY0vnJnstKQSpcrsI2kBTB3dB2PkdfKdTa+abbjMIDqpc63fE5LfjLgfMst0ULMFxQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/remove-accents": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz", - "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==", - "dev": true, - "license": "MIT" - }, "node_modules/requestidlecallback": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/requestidlecallback/-/requestidlecallback-0.3.0.tgz", @@ -21150,13 +19055,6 @@ "node": ">=0.10.5" } }, - "node_modules/reselect": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.3.0.tgz", - "integrity": "sha512-XGoLeRAVzUTcJ1qkxPQhDJyIZ5d6zzZD9nT7AEZOaaU9UbWclhycElmhO+VD5bFeLuzhPBaOV2oXC8uG35ZSpg==", - "dev": true, - "license": "MIT" - }, "node_modules/reserved-identifiers": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/reserved-identifiers/-/reserved-identifiers-1.2.0.tgz", @@ -21488,13 +19386,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/rungen": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/rungen/-/rungen-0.3.2.tgz", - "integrity": "sha512-zWl10xu2D7zoR8zSC2U6bg5bYF6T/Wk7rxwp8IPaJH7f0Ge21G03kNHVgHR7tyVkSSfAOG0Rqf/Cl38JftSmtw==", - "dev": true, - "license": "MIT" - }, "node_modules/safe-array-concat": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", @@ -22183,13 +20074,6 @@ "url": "https://github.com/steveukx/git-js?sponsor=1" } }, - "node_modules/simple-html-tokenizer": { - "version": "0.5.11", - "resolved": "https://registry.npmjs.org/simple-html-tokenizer/-/simple-html-tokenizer-0.5.11.tgz", - "integrity": "sha512-C2WEK/Z3HoSFbYq8tI7ni3eOo/NneSPRoPpcM7WdLjFOArFuyXEjAoCdOC3DgMfRyziZQ1hCNR4mrNdWEvD0og==", - "dev": true, - "license": "MIT" - }, "node_modules/sirv": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", @@ -23248,13 +21132,6 @@ "url": "https://opencollective.com/synckit" } }, - "node_modules/tabbable": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.5.0.tgz", - "integrity": "sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA==", - "dev": true, - "license": "MIT" - }, "node_modules/table": { "version": "6.9.0", "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", @@ -24149,61 +22026,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/use-callback-ref": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", - "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-sidecar": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", - "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-node-es": "^1.1.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-sync-external-store": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.7.0.tgz", - "integrity": "sha512-6L+EeigHMQhdaIPNIFUKwfWJSwWFQ8gJbJ2DLOs5sDIegTwR9fRxvnM3uciHKjIZhFz+KAv2emhWMRvDmMcY8A==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -24221,20 +22043,6 @@ "node": ">= 0.4.0" } }, - "node_modules/uuid": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.3.tgz", - "integrity": "sha512-Sc5u1VVln93y1Xnz4R+CunOZc7R9LUcTmhj7Rc7Eg3HVcf7rVn2FOoa812fe0W8p3jD7xeoGSYbfqCGGH2B6xw==", - "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist-node/bin/uuid" - } - }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -24284,16 +22092,6 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/wasm-vips": { - "version": "0.0.18", - "resolved": "https://registry.npmjs.org/wasm-vips/-/wasm-vips-0.0.18.tgz", - "integrity": "sha512-AJyCvxZj/3qceKNnh+YyEobu/IaJFoPN7x7SxyyHmYBS3kASMqJqxQEuN0ZHKQDWsCJ8armfx4Tq3uKrNc+nMA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=17.0.0" - } - }, "node_modules/watchpack": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.6.3.tgz", diff --git a/plugins/locked-payment-methods-for-woocommerce/package.json b/plugins/locked-payment-methods-for-woocommerce/package.json index 04c2391..a309608 100644 --- a/plugins/locked-payment-methods-for-woocommerce/package.json +++ b/plugins/locked-payment-methods-for-woocommerce/package.json @@ -17,12 +17,8 @@ "@axe-core/playwright": "^4.14.0", "@playwright/test": "^1.64.1", "@types/node": "^26.6.5", - "@types/react": "^19.3.0", - "@wordpress/block-editor": "^19.0.0", - "@wordpress/blocks": "^17.0.0", "@wordpress/e2e-test-utils-playwright": "^3.1.0", "@wordpress/env": "^12.0.0", - "@wordpress/i18n": "^7.0.0", "@wordpress/scripts": "^37.0.0", "typescript": "^6.0.3" }, @@ -42,14 +38,9 @@ "wp-env:tests:stop": "wp-env --config .wp-env.tests.json stop", "wp-env:tests:update": "wp-env --config .wp-env.tests.json start --update", "wp-env:tests:destroy": "wp-env --config .wp-env.tests.json destroy", - "build": "npm run build:blocks && npm run build:assets", - "build:blocks": "wp-scripts build --webpack-src-dir=blocks/src --output-path=blocks/build --blocks-manifest", - "build:assets": "wp-scripts build editor=./assets/src/editor.ts greeting=./assets/src/greeting.scss --output-path=assets/build", - "start": "npm run start:blocks & npm run start:assets & wait", - "start:blocks": "wp-scripts start --webpack-src-dir=blocks/src --output-path=blocks/build --blocks-manifest", - "start:assets": "wp-scripts start editor=./assets/src/editor.ts greeting=./assets/src/greeting.scss --output-path=assets/build", - "lint:scripts": "wp-scripts lint-js blocks/src assets/src tests/e2e", - "lint:styles": "wp-scripts lint-style 'blocks/src/**/*.scss' 'assets/src/**/*.scss'", + "build": "exit 0", + "lint:scripts": "wp-scripts lint-js tests/e2e", + "lint:styles": "wp-scripts lint-style 'assets/src/**/*.scss' --allow-empty-input", "lint:types": "tsc", "audit": "composer audit && npm audit", "audit:fix": "npm audit fix", diff --git a/plugins/locked-payment-methods-for-woocommerce/phpcs.dist.xml b/plugins/locked-payment-methods-for-woocommerce/phpcs.dist.xml index de8a13d..4416f8d 100644 --- a/plugins/locked-payment-methods-for-woocommerce/phpcs.dist.xml +++ b/plugins/locked-payment-methods-for-woocommerce/phpcs.dist.xml @@ -4,8 +4,6 @@ - */blocks/build/* - */assets/build/* diff --git a/plugins/locked-payment-methods-for-woocommerce/src/Blocks.php b/plugins/locked-payment-methods-for-woocommerce/src/Blocks.php deleted file mode 100644 index 5fa8099..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/src/Blocks.php +++ /dev/null @@ -1,60 +0,0 @@ - 'greeting' ) ); - } - - /** - * Enqueues the greeting's stylesheet on the account dashboard. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @return void - */ - public function enqueue_style(): void { - if ( ! is_account_page() || is_wc_endpoint_url() ) { - return; - } - - dws_lpmwc_register_style( Plugin::SLUG . '-greeting', 'assets/build/greeting.css' ); - wp_enqueue_style( Plugin::SLUG . '-greeting' ); - } - - // endregion -} diff --git a/plugins/locked-payment-methods-for-woocommerce/src/Feature/index.php b/plugins/locked-payment-methods-for-woocommerce/src/Feature/index.php deleted file mode 100644 index f767346..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/src/Feature/index.php +++ /dev/null @@ -1 +0,0 @@ - 'title', - 'id' => Plugin::PREFIX . '_greeting_options', - 'title' => __( 'Greeting', 'locked-payment-methods-for-woocommerce' ), - 'desc' => __( 'Greets returning customers on their account dashboard.', 'locked-payment-methods-for-woocommerce' ), - ), - array( - 'type' => 'checkbox', - 'id' => DWS_LPMWC_OPTION_FEATURE, - 'title' => __( 'Greeting', 'locked-payment-methods-for-woocommerce' ), - 'desc' => __( 'Greet returning customers', 'locked-payment-methods-for-woocommerce' ), - 'default' => wc_bool_to_string( DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_FEATURE ] ), - ), - array( - 'type' => 'text', - 'id' => DWS_LPMWC_OPTION_GREETING, - 'title' => __( 'Message', 'locked-payment-methods-for-woocommerce' ), - 'default' => '', - 'placeholder' => dws_lpmwc_get_option_text( DWS_LPMWC_OPTION_GREETING ), // A default would be saved in the admin's language with the first save of the tab. - ), - array( - 'type' => 'number', - 'id' => DWS_LPMWC_OPTION_GREETING_ORDERS, - 'title' => __( 'Orders before the greeting', 'locked-payment-methods-for-woocommerce' ), - 'desc' => __( 'Customers see the greeting once they have placed this many orders.', 'locked-payment-methods-for-woocommerce' ), - 'default' => (string) DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_GREETING_ORDERS ], - 'custom_attributes' => array( - 'min' => '1', - 'step' => '1', - ), - 'validate' => 'dws_lpmwc_validate_greeting_orders', - ), - array( - 'type' => 'sectionend', - 'id' => Plugin::PREFIX . '_greeting_options', - ), array( 'type' => 'title', 'id' => Plugin::PREFIX . '_data_options', diff --git a/plugins/locked-payment-methods-for-woocommerce/templates/index.php b/plugins/locked-payment-methods-for-woocommerce/templates/index.php deleted file mode 100644 index f767346..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/templates/index.php +++ /dev/null @@ -1 +0,0 @@ - array( - 'wp-i18n', - array( - 'id' => '@wordpress/interactivity', - 'import' => 'dynamic', - ), - ), - 'version' => 'fixture', -); diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/AssetsTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/AssetsTest.php deleted file mode 100644 index f841d90..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/AssetsTest.php +++ /dev/null @@ -1,45 +0,0 @@ - 'defer' ) ) ); - - $script = wp_scripts()->registered[ self::HANDLE ]; - self::assertSame( plugins_url( 'tests/Fixtures/built.js', DWS_LPMWC_FILE ), $script->src ); - self::assertSame( array( 'wp-i18n' ), $script->deps ); - self::assertSame( 'fixture', $script->ver ); - self::assertSame( 'defer', $script->extra['strategy'] ?? null ); - self::assertSame( Plugin::SLUG, $script->textdomain ); - self::assertSame( plugin_dir_path( DWS_LPMWC_FILE ) . 'languages', $script->translations_path ); - } - - public function test_a_stylesheet_registers_with_its_generated_version_and_its_right_to_left_variant(): void { - self::assertTrue( dws_lpmwc_register_style( self::HANDLE, 'tests/Fixtures/built.css', 'print' ) ); - - $style = wp_styles()->registered[ self::HANDLE ]; - self::assertSame( plugins_url( 'tests/Fixtures/built.css', DWS_LPMWC_FILE ), $style->src ); - self::assertSame( 'fixture', $style->ver ); - self::assertSame( 'print', $style->args ); - self::assertSame( 'replace', $style->extra['rtl'] ?? null ); - } -} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/BlocksTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/BlocksTest.php deleted file mode 100644 index db98a41..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/BlocksTest.php +++ /dev/null @@ -1,42 +0,0 @@ -get_registered( 'locked-payment-methods-for-woocommerce/example-notice' ); - - self::assertInstanceOf( \WP_Block_Type::class, $block ); - self::assertSame( array( 'locked-payment-methods-for-woocommerce-example-notice-editor-script' ), $block->editor_script_handles ); - self::assertSame( array( 'locked-payment-methods-for-woocommerce-example-notice-editor-style' ), $block->editor_style_handles ); - self::assertSame( array( 'locked-payment-methods-for-woocommerce-example-notice-style' ), $block->style_handles ); - } - - public function test_the_block_editor_gets_the_editor_script_with_its_generated_dependencies(): void { - $blocks = new Blocks(); - $blocks->register_hooks(); - self::assertSame( 10, has_action( 'enqueue_block_editor_assets', array( $blocks, 'enqueue_editor_script' ) ) ); - remove_action( 'init', array( $blocks, 'register_blocks' ) ); - remove_action( 'enqueue_block_editor_assets', array( $blocks, 'enqueue_editor_script' ) ); - - $blocks->enqueue_editor_script(); - - $asset = require plugin_dir_path( DWS_LPMWC_FILE ) . 'assets/build/editor.asset.php'; - self::assertTrue( wp_script_is( Plugin::SLUG . '-editor', 'enqueued' ) ); - self::assertSame( $asset['dependencies'], wp_scripts()->registered[ Plugin::SLUG . '-editor' ]->deps ); - self::assertSame( $asset['version'], wp_scripts()->registered[ Plugin::SLUG . '-editor' ]->ver ); - } -} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/Feature/FeatureDetailTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/Feature/FeatureDetailTest.php deleted file mode 100644 index 046a3cc..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/Feature/FeatureDetailTest.php +++ /dev/null @@ -1,130 +0,0 @@ -stored[ $key ] = get_option( $key ); - } - - $customer = wp_insert_user( - array( - 'user_login' => 'dws_lpmwc_customer_' . \wp_generate_password( 8, false ), - 'user_pass' => wp_generate_password(), - 'role' => 'customer', - ) - ); - self::assertIsInt( $customer ); - $this->customer = $customer; - wp_set_current_user( $customer ); - } - - #[After] - protected function clean_up(): void { - foreach ( $this->stored as $key => $value ) { - false === $value ? delete_option( $key ) : update_option( $key, $value ); - } - - foreach ( $this->orders as $order ) { - $order->delete( true ); - } - - wp_set_current_user( 0 ); - wp_delete_user( $this->customer ); - } - - public function test_the_greeting_prints_on_the_account_dashboard(): void { - $detail = new FeatureDetail(); - $detail->register_hooks(); - - self::assertSame( 10, has_action( 'woocommerce_account_dashboard', array( $detail, 'print_greeting' ) ) ); - self::assertSame( 10, has_action( 'wp_enqueue_scripts', array( $detail, 'enqueue_style' ) ) ); - remove_action( 'woocommerce_account_dashboard', array( $detail, 'print_greeting' ) ); - remove_action( 'wp_enqueue_scripts', array( $detail, 'enqueue_style' ) ); - } - - public function test_the_greeting_style_loads_on_the_account_dashboard_only(): void { - ( new FeatureDetail() )->enqueue_style(); - self::assertFalse( wp_style_is( 'locked-payment-methods-for-woocommerce-greeting' ) ); - - add_filter( 'woocommerce_is_account_page', '__return_true' ); - try { - ( new FeatureDetail() )->enqueue_style(); - } finally { - remove_filter( 'woocommerce_is_account_page', '__return_true' ); - } - - self::assertTrue( wp_style_is( 'locked-payment-methods-for-woocommerce-greeting' ) ); - self::assertSame( plugins_url( 'assets/build/greeting.css', DWS_LPMWC_FILE ), wp_styles()->registered['locked-payment-methods-for-woocommerce-greeting']->src ); - wp_dequeue_style( 'locked-payment-methods-for-woocommerce-greeting' ); - wp_deregister_style( 'locked-payment-methods-for-woocommerce-greeting' ); - } - - public function test_a_customer_whose_orders_reach_the_set_number_is_greeted_as_text(): void { - update_option( DWS_LPMWC_OPTION_GREETING, 'Hi & welcome' ); - update_option( DWS_LPMWC_OPTION_GREETING_ORDERS, '2' ); - $this->place_orders( 2 ); - - $notice = \Dom\HTMLDocument::createFromString( '' . $this->print() . '', \LIBXML_NOERROR )->querySelector( '[role="status"]' ); - - self::assertNotNull( $notice ); - self::assertSame( 'greeting', $notice->getAttribute( 'data-locked-payment-methods-for-woocommerce' ) ); - self::assertSame( 'Hi & welcome', \trim( (string) $notice->textContent ) ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- A DOM property. - } - - public function test_a_customer_below_the_set_number_is_not_greeted(): void { - update_option( DWS_LPMWC_OPTION_GREETING_ORDERS, '2' ); - $this->place_orders( 1 ); - - self::assertSame( '', $this->print() ); - } - - public function test_an_unsaved_greeting_prints_its_translated_default(): void { - delete_option( DWS_LPMWC_OPTION_GREETING ); - delete_option( DWS_LPMWC_OPTION_GREETING_ORDERS ); - $this->place_orders( 1 ); - - self::assertStringContainsString( esc_html( dws_lpmwc_get_option_text( DWS_LPMWC_OPTION_GREETING ) ), $this->print() ); - } - - public function test_an_empty_greeting_prints_its_translated_default(): void { - update_option( DWS_LPMWC_OPTION_GREETING, '' ); - $this->place_orders( 1 ); - - self::assertStringContainsString( esc_html( dws_lpmwc_get_option_text( DWS_LPMWC_OPTION_GREETING ) ), $this->print() ); - } - - protected function place_orders( int $count ): void { - for ( $placed = 0; $placed < $count; $placed++ ) { - $this->orders[] = wc_create_order( array( 'customer_id' => $this->customer ) ); - } - } - - protected function print(): string { - \ob_start(); - ( new FeatureDetail() )->print_greeting(); - - return (string) \ob_get_clean(); - } -} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/Feature/FeatureTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/Feature/FeatureTest.php deleted file mode 100644 index 1f6d327..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/Feature/FeatureTest.php +++ /dev/null @@ -1,63 +0,0 @@ - array( 'yes', true ), - 'off' => array( 'no', false ), - 'unset' => array( null, false ), - ); - } - - #[\Override] - protected function setUp(): void { - $this->stored = get_option( DWS_LPMWC_OPTION_FEATURE ); - } - - #[After] - protected function restore_row(): void { - false === $this->stored ? delete_option( DWS_LPMWC_OPTION_FEATURE ) : update_option( DWS_LPMWC_OPTION_FEATURE, $this->stored ); - } - - #[DataProvider( 'stored_toggles' )] - public function test_the_feature_loads_only_where_the_greeting_is_switched_on( ?string $stored, bool $expected ): void { - \is_null( $stored ) ? delete_option( DWS_LPMWC_OPTION_FEATURE ) : update_option( DWS_LPMWC_OPTION_FEATURE, $stored ); - - self::assertSame( $expected, Feature::should_load() ); - } - - #[RunInSeparateProcess] - public function test_with_the_greeting_switched_on_every_declared_component_boots(): void { - $declared = array(); - $pending = Plugin::COMPONENTS; - while ( array() !== $pending ) { - $class = \array_shift( $pending ); - $declared[] = $class; - if ( \is_a( $class, CompositeComponentInterface::class, true ) ) { - \array_unshift( $pending, ...$class::get_child_component_classes() ); - } - } - - update_option( DWS_LPMWC_OPTION_FEATURE, 'yes' ); - $plugin = new Plugin(); - $plugin->boot(); - - self::assertSame( 'booted', $plugin->boot->status ); - self::assertSame( $declared, $plugin->kernel?->resolved ); - self::assertSame( array(), $plugin->kernel->skipped ); - } -} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/MigrationsTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/MigrationsTest.php index ceb5ddc..c77389e 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/MigrationsTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/MigrationsTest.php @@ -11,7 +11,7 @@ #[CoversNothing] final class MigrationsTest extends TestCase { - protected const array KEYS = array( DWS_LPMWC_OPTION_FEATURE, DWS_LPMWC_OPTION_GREETING, DWS_LPMWC_OPTION_GREETING_ORDERS, DWS_LPMWC_OPTION_DELETE_DATA ); + protected const array KEYS = array( DWS_LPMWC_OPTION_DELETE_DATA ); protected array $stored = array(); @@ -43,23 +43,18 @@ public function test_the_runner_brings_a_new_site_to_revision_one_with_the_untra dws_lpmwc_plugin()->migrations?->run(); self::assertSame( 1, MigrationRunner::get_revision( Plugin::PREFIX . '_revision' ) ); - self::assertSame( 'no', get_option( DWS_LPMWC_OPTION_FEATURE ) ); - self::assertSame( '1', get_option( DWS_LPMWC_OPTION_GREETING_ORDERS ) ); self::assertSame( 'no', get_option( DWS_LPMWC_OPTION_DELETE_DATA ) ); - self::assertFalse( get_option( DWS_LPMWC_OPTION_GREETING ) ); - foreach ( array( DWS_LPMWC_OPTION_FEATURE, DWS_LPMWC_OPTION_GREETING_ORDERS, DWS_LPMWC_OPTION_DELETE_DATA ) as $key ) { + foreach ( array( DWS_LPMWC_OPTION_DELETE_DATA ) as $key ) { self::assertSame( 'on', $wpdb->get_var( $wpdb->prepare( "SELECT autoload FROM {$wpdb->options} WHERE option_name = %s", $key ) ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- Reads the stored autoload flag. } } public function test_the_first_step_keeps_the_values_a_site_stored(): void { - update_option( DWS_LPMWC_OPTION_FEATURE, 'yes' ); - update_option( DWS_LPMWC_OPTION_GREETING_ORDERS, '5' ); + update_option( DWS_LPMWC_OPTION_DELETE_DATA, 'yes' ); dws_lpmwc_add_settings(); - self::assertSame( 'yes', get_option( DWS_LPMWC_OPTION_FEATURE ) ); - self::assertSame( '5', get_option( DWS_LPMWC_OPTION_GREETING_ORDERS ) ); + self::assertSame( 'yes', get_option( DWS_LPMWC_OPTION_DELETE_DATA ) ); } public function test_the_first_step_throws_when_a_default_cannot_be_stored(): void { diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/OptionsTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/OptionsTest.php index 557b016..9155a90 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/OptionsTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/OptionsTest.php @@ -13,7 +13,7 @@ final class OptionsTest extends TestCase { protected const string KEY = Plugin::PREFIX . '_options_test'; - protected const array RESTORED = array( DWS_LPMWC_OPTION_DELETE_DATA, DWS_LPMWC_OPTION_GREETING ); + protected const array RESTORED = array( DWS_LPMWC_OPTION_DELETE_DATA ); protected array $stored = array(); @@ -59,19 +59,6 @@ public static function stored_lists(): array { ); } - public static function submitted_order_counts(): array { - return array( - 'one' => array( '1', '1' ), - 'padded' => array( ' 3 ', '3' ), - 'zero' => array( '0', null ), - 'negative' => array( '-3', null ), - 'decimal' => array( '2.5', null ), - 'word' => array( 'three', null ), - 'empty' => array( '', null ), - 'array' => array( array( '3' ), null ), - ); - } - #[Before] protected function store_rows(): void { foreach ( self::RESTORED as $key ) { @@ -120,30 +107,4 @@ public function test_a_list_reads_the_stored_strings_in_order( mixed $stored, ar self::assertSame( $expected, dws_lpmwc_get_list_option( self::KEY ) ); } - - public function test_a_string_without_a_stored_value_reads_its_translated_default(): void { - delete_option( DWS_LPMWC_OPTION_GREETING ); - - self::assertNotSame( '', dws_lpmwc_get_option_text( DWS_LPMWC_OPTION_GREETING ) ); - self::assertSame( dws_lpmwc_get_option_text( DWS_LPMWC_OPTION_GREETING ), dws_lpmwc_get_string_option( DWS_LPMWC_OPTION_GREETING ) ); - - update_option( DWS_LPMWC_OPTION_GREETING, '' ); - - self::assertSame( dws_lpmwc_get_option_text( DWS_LPMWC_OPTION_GREETING ), dws_lpmwc_get_string_option( DWS_LPMWC_OPTION_GREETING ) ); - - update_option( DWS_LPMWC_OPTION_GREETING, 'Hello' ); - - self::assertSame( 'Hello', dws_lpmwc_get_string_option( DWS_LPMWC_OPTION_GREETING ) ); - } - - public function test_an_option_without_a_translated_default_has_no_text(): void { - self::assertSame( '', dws_lpmwc_get_option_text( DWS_LPMWC_OPTION_FEATURE ) ); - } - - #[DataProvider( 'submitted_order_counts' )] - public function test_an_order_count_validates_as_a_whole_number_of_at_least_one( mixed $submitted, ?string $expected ): void { - $validated = dws_lpmwc_validate_greeting_orders( $submitted ); - - \is_null( $expected ) ? self::assertInstanceOf( \WP_Error::class, $validated ) : self::assertSame( $expected, $validated ); - } } diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PartialDeployTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PartialDeployTest.php index 5cb1264..884c048 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PartialDeployTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PartialDeployTest.php @@ -9,8 +9,8 @@ #[CoversNothing] final class PartialDeployTest extends TestCase { - public function test_a_missing_blocks_manifest_shows_the_build_notice_and_loads_nothing_built(): void { - $manifest = \dirname( DWS_LPMWC_FILE ) . '/blocks/build/blocks-manifest.php'; + public function test_a_missing_bootstrap_package_shows_the_build_notice_and_loads_nothing_built(): void { + $manifest = \dirname( DWS_LPMWC_FILE ) . '/vendor-prefixed/ahegyes/wp-framework-bootstrap/load.php'; \rename( $manifest, "$manifest.hidden" ); try { $request = self::request( 'echo wp_json_encode( array( function_exists( "dws_lpmwc_boot" ), false !== has_action( "all_admin_notices", "dws_lpmwc_render_build_notice" ) ) );' ); diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php index fa49a5f..e49a57e 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php @@ -2,8 +2,6 @@ namespace DeepWebSolutions\LockedPaymentMethods\Tests\Integration; -use DeepWebSolutions\LockedPaymentMethods\Blocks; -use DeepWebSolutions\LockedPaymentMethods\Feature\Feature; use DeepWebSolutions\LockedPaymentMethods\Plugin; use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\AdminNotice\AdminNotices; use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\Migration\MigrationRunner; @@ -14,12 +12,12 @@ #[CoversNothing] final class PluginBootTest extends TestCase { - public function test_the_plugin_boots_with_the_greeting_gated_off_until_a_site_switches_it_on(): void { + public function test_the_plugin_boots_every_component(): void { $plugin = dws_lpmwc_plugin(); self::assertSame( 'booted', $plugin->boot->status ); - self::assertSame( array( Scheduler::class, AdminNotices::class, Settings::class, Blocks::class ), $plugin->kernel?->resolved ); - self::assertSame( array( Feature::class ), $plugin->kernel->skipped ); + self::assertSame( array( Scheduler::class, AdminNotices::class, Settings::class ), $plugin->kernel?->resolved ); + self::assertSame( array(), $plugin->kernel->skipped ); } public function test_the_entry_loads_every_include_but_those_whose_names_start_with_an_underscore(): void { diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SettingsTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SettingsTest.php index 5fb4c3e..feb1357 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SettingsTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SettingsTest.php @@ -7,12 +7,11 @@ use PHPUnit\Framework\Attributes\After; use PHPUnit\Framework\Attributes\Before; use PHPUnit\Framework\Attributes\CoversNothing; -use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; #[CoversNothing] final class SettingsTest extends TestCase { - protected const array KEYS = array( DWS_LPMWC_OPTION_FEATURE, DWS_LPMWC_OPTION_GREETING, DWS_LPMWC_OPTION_GREETING_ORDERS, DWS_LPMWC_OPTION_DELETE_DATA ); + protected const array KEYS = array( DWS_LPMWC_OPTION_DELETE_DATA ); protected static SettingsTab $tab; @@ -31,16 +30,6 @@ public static function setUpBeforeClass(): void { } } - public static function invalid_order_counts(): array { - return array( - 'zero' => array( '0' ), - 'negative' => array( '-2' ), - 'decimal' => array( '2.5' ), - 'word' => array( 'three' ), - 'empty' => array( '' ), - ); - } - #[Before] protected function store_rows(): void { foreach ( self::KEYS as $key ) { @@ -57,7 +46,7 @@ protected function restore_rows(): void { ( new \ReflectionProperty( \WC_Admin_Settings::class, 'errors' ) )->setValue( null, array() ); } - public function test_the_tab_shows_the_greeting_and_data_fields_with_their_defaults(): void { + public function test_the_tab_shows_the_data_field_with_its_default(): void { foreach ( self::KEYS as $key ) { delete_option( $key ); } @@ -65,23 +54,9 @@ public function test_the_tab_shows_the_greeting_and_data_fields_with_their_defau $form = $this->render(); self::assertSame( 'Locked Payment Methods for WooCommerce', self::$tab->get_label() ); - self::assertFalse( $form->getElementById( DWS_LPMWC_OPTION_FEATURE )?->hasAttribute( 'checked' ) ); - self::assertSame( '', $form->getElementById( DWS_LPMWC_OPTION_GREETING )?->getAttribute( 'value' ) ); - self::assertSame( dws_lpmwc_get_option_text( DWS_LPMWC_OPTION_GREETING ), $form->getElementById( DWS_LPMWC_OPTION_GREETING )?->getAttribute( 'placeholder' ) ); - self::assertSame( '1', $form->getElementById( DWS_LPMWC_OPTION_GREETING_ORDERS )?->getAttribute( 'value' ) ); - self::assertSame( '1', $form->getElementById( DWS_LPMWC_OPTION_GREETING_ORDERS )?->getAttribute( 'min' ) ); self::assertFalse( $form->getElementById( DWS_LPMWC_OPTION_DELETE_DATA )?->hasAttribute( 'checked' ) ); } - public function test_a_save_that_leaves_the_message_untouched_stores_no_translated_text(): void { - delete_option( DWS_LPMWC_OPTION_GREETING ); - - $this->save( array( DWS_LPMWC_OPTION_GREETING => $this->render()->getElementById( DWS_LPMWC_OPTION_GREETING )?->getAttribute( 'value' ) ) ); - - self::assertSame( '', get_option( DWS_LPMWC_OPTION_GREETING ) ); - self::assertSame( dws_lpmwc_get_option_text( DWS_LPMWC_OPTION_GREETING ), dws_lpmwc_get_string_option( DWS_LPMWC_OPTION_GREETING ) ); - } - public function test_the_retention_opt_in_round_trips_through_woocommerces_save(): void { $this->save( array( DWS_LPMWC_OPTION_DELETE_DATA => '1' ) ); @@ -95,23 +70,6 @@ public function test_the_retention_opt_in_round_trips_through_woocommerces_save( self::assertFalse( dws_lpmwc_get_bool_option( DWS_LPMWC_OPTION_DELETE_DATA ) ); } - #[DataProvider( 'invalid_order_counts' )] - public function test_an_order_count_that_is_not_a_whole_number_of_at_least_one_keeps_the_stored_count( string $posted ): void { - update_option( DWS_LPMWC_OPTION_GREETING_ORDERS, '4' ); - - $this->save( array( DWS_LPMWC_OPTION_GREETING_ORDERS => $posted ) ); - - self::assertSame( '4', get_option( DWS_LPMWC_OPTION_GREETING_ORDERS ) ); - self::assertSame( array( 'Orders before the greeting was not saved, so it keeps its previous value. Enter a whole number of 1 or more, such as 3.' ), ( new \ReflectionProperty( \WC_Admin_Settings::class, 'errors' ) )->getValue() ); - } - - public function test_a_whole_order_count_of_at_least_one_is_stored(): void { - $this->save( array( DWS_LPMWC_OPTION_GREETING_ORDERS => ' 3 ' ) ); - - self::assertSame( '3', get_option( DWS_LPMWC_OPTION_GREETING_ORDERS ) ); - self::assertSame( 3, dws_lpmwc_get_int_option( DWS_LPMWC_OPTION_GREETING_ORDERS ) ); - } - protected function render(): \Dom\HTMLDocument { \ob_start(); \WC_Admin_Settings::output_fields( self::$tab->get_settings_for_section( '' ) ); diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/e2e/settings.spec.ts b/plugins/locked-payment-methods-for-woocommerce/tests/e2e/settings.spec.ts deleted file mode 100644 index d68824e..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/tests/e2e/settings.spec.ts +++ /dev/null @@ -1,74 +0,0 @@ -import AxeBuilder from '@axe-core/playwright'; -import { expect, test } from '@wordpress/e2e-test-utils-playwright'; -import type { Locator, Page } from '@playwright/test'; - -const SAVED = 'Your settings have been saved.'; -const REJECTED = - 'Orders before the greeting was not saved, so it keeps its previous value. Enter a whole number of 1 or more, such as 3.'; - -const tabTo = async ( page: Page, target: Locator ): Promise< void > => { - for ( let presses = 0; presses < 10; presses++ ) { - await page.keyboard.press( 'Tab' ); - if ( - await target.evaluate( - ( element ) => element === element.ownerDocument.activeElement - ) - ) { - return; - } - } - throw new Error( 'Tab never reached the element.' ); -}; - -const typeAndSave = async ( page: Page, value: string ): Promise< void > => { - await page.keyboard.press( 'ControlOrMeta+a' ); - await page.keyboard.press( 'Backspace' ); - await page.keyboard.type( value ); - await tabTo( page, page.getByRole( 'button', { name: 'Save changes' } ) ); - await page.keyboard.press( 'Enter' ); - await page.waitForLoadState(); -}; - -test.describe( 'Settings tab', () => { - test.beforeEach( async ( { admin } ) => { - await admin.visitAdminPage( - 'admin.php', - 'page=wc-settings&tab=dws_lpmwc' - ); - } ); - - test( 'has no accessibility violations in its form', async ( { page } ) => { - const results = await new AxeBuilder( { page } ) - .include( '#mainform' ) - .analyze(); - - expect( results.violations ).toEqual( [] ); - } ); - - test( 'saves the order threshold by keyboard, rejects an empty one and keeps the saved value until a valid one replaces it', async ( { - page, - } ) => { - const threshold = page.getByLabel( 'Orders before the greeting' ); - - await page.getByLabel( 'Message' ).focus(); - await tabTo( page, threshold ); - await typeAndSave( page, '3' ); - await expect( page.getByText( SAVED ) ).toBeVisible(); - await expect( threshold ).toHaveValue( '3' ); - - await threshold.focus(); - await typeAndSave( page, '' ); - await expect( page.getByText( REJECTED ) ).toBeVisible(); - await expect( threshold ).toHaveValue( '3' ); - const rejected = await new AxeBuilder( { page } ) - .include( '#mainform' ) - .analyze(); - expect( rejected.violations ).toEqual( [] ); - - await threshold.focus(); - await typeAndSave( page, '1' ); - await expect( page.getByText( SAVED ) ).toBeVisible(); - await expect( page.getByText( REJECTED ) ).toBeHidden(); - await expect( threshold ).toHaveValue( '1' ); - } ); -} ); diff --git a/plugins/locked-payment-methods-for-woocommerce/tsconfig.json b/plugins/locked-payment-methods-for-woocommerce/tsconfig.json index a68d869..3b2d37c 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tsconfig.json +++ b/plugins/locked-payment-methods-for-woocommerce/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "@ahegyes/wordpress-configs/node/tsconfig.base.json", - "include": ["blocks/src/**/*", "assets/src/**/*", "tests/e2e/**/*"] + "include": ["tests/e2e/**/*"] } From b344a4206db34878cc3cbee8e43538094194772c Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sat, 31 Oct 2026 23:20:03 +0100 Subject: [PATCH 07/50] Test Locked Payment Methods' settings and their migration from v1 v1 kept its settings in an unlisted section of WooCommerce's Payments tab, under three key spellings, and offered only enabled gateways, so saving the section while a locked gateway was disabled unlocked it once it was enabled again. The port moves them to a listed section on the Advanced tab under the plugin's prefix, and copies v1's values. SettingsTest expects the section on the Advanced tab under v1's id, v1's General and Plugin groups with their defaults, every registered gateway in the locked list with the disabled ones marked and kept through a save, every role in the roles field whether or not the role strategy is on, checkboxes storing 'yes' and 'no', v1's group filters firing as deprecated on v1's field ids, and the Settings and support links. MigrationsTest expects the first step to store every setting, autoloaded, from v1's row where it holds a value of the right kind and otherwise from the default, to leave v1's rows and the values a site stored, to map v1's eight settings one to one, and to throw when a write fails. X ruling 93. Assisted-by: Claude Code:claude-opus-5-5 --- .../tests/Integration/MigrationsTest.php | 123 +++++++++++-- .../tests/Integration/SettingsTest.php | 164 +++++++++++++++--- 2 files changed, 254 insertions(+), 33 deletions(-) diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/MigrationsTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/MigrationsTest.php index c77389e..f8fb303 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/MigrationsTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/MigrationsTest.php @@ -11,13 +11,34 @@ #[CoversNothing] final class MigrationsTest extends TestCase { - protected const array KEYS = array( DWS_LPMWC_OPTION_DELETE_DATA ); + // v1.3.5's settings, as its free and premium builds stored them. + protected const array V1_KEYS = array( + 'dws-wc-lpm_general_locked-payment-methods', + 'dws-wc-lpm_general_unlock-via-user-role', + 'dws-wc-lpm_general_full-access-user-roles', + 'dws-wc-lpm_general_unlock-via-user-meta', + 'dws-wc-lpm_general_unlock-via-order-meta', + 'dws-wc-lpm_integrations_unlock-via-groups', + 'dws-wc-lpm_integrations_unlock-via-wc-memberships', + 'dws-wc-lpm_plugin_remove-data-uninstall', + ); + + protected const array KEYS = array( + DWS_LPMWC_OPTION_LOCKED_METHODS, + DWS_LPMWC_OPTION_UNLOCK_VIA_USER_ROLES, + DWS_LPMWC_OPTION_FULL_ACCESS_ROLES, + DWS_LPMWC_OPTION_UNLOCK_VIA_USER_PROFILES, + DWS_LPMWC_OPTION_UNLOCK_VIA_ORDERS, + DWS_LPMWC_OPTION_UNLOCK_VIA_GROUPS, + DWS_LPMWC_OPTION_UNLOCK_VIA_WC_MEMBERSHIPS, + DWS_LPMWC_OPTION_DELETE_DATA, + ); protected array $stored = array(); #[\Override] protected function setUp(): void { - foreach ( self::KEYS as $key ) { + foreach ( array( ...self::KEYS, ...self::V1_KEYS ) as $key ) { $this->stored[ $key ] = get_option( $key ); } } @@ -27,15 +48,15 @@ protected function restore_rows(): void { foreach ( $this->stored as $key => $value ) { false === $value ? delete_option( $key ) : update_option( $key, $value ); } - update_option( Plugin::PREFIX . '_revision', \count( dws_lpmwc_get_migration_steps() ) ); } // The runner's time budget lasts for its request, and this process's bootstrap spends it when it migrates a new site, so the row runs as a request of its own. #[RunInSeparateProcess] - public function test_the_runner_brings_a_new_site_to_revision_one_with_the_untranslated_defaults_autoloaded(): void { + public function test_the_runner_brings_a_new_site_to_revision_one_with_every_default_autoloaded(): void { global $wpdb; - foreach ( self::KEYS as $key ) { + + foreach ( array( ...self::KEYS, ...self::V1_KEYS ) as $key ) { delete_option( $key ); } update_option( Plugin::PREFIX . '_revision', 0 ); @@ -43,27 +64,107 @@ public function test_the_runner_brings_a_new_site_to_revision_one_with_the_untra dws_lpmwc_plugin()->migrations?->run(); self::assertSame( 1, MigrationRunner::get_revision( Plugin::PREFIX . '_revision' ) ); - self::assertSame( 'no', get_option( DWS_LPMWC_OPTION_DELETE_DATA ) ); - foreach ( array( DWS_LPMWC_OPTION_DELETE_DATA ) as $key ) { - self::assertSame( 'on', $wpdb->get_var( $wpdb->prepare( "SELECT autoload FROM {$wpdb->options} WHERE option_name = %s", $key ) ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- Reads the stored autoload flag. + self::assertSame( + array( + DWS_LPMWC_OPTION_LOCKED_METHODS => array(), + DWS_LPMWC_OPTION_UNLOCK_VIA_USER_ROLES => 'yes', + DWS_LPMWC_OPTION_FULL_ACCESS_ROLES => array( 'administrator', 'shop_manager' ), + DWS_LPMWC_OPTION_UNLOCK_VIA_USER_PROFILES => 'yes', + DWS_LPMWC_OPTION_UNLOCK_VIA_ORDERS => 'no', + DWS_LPMWC_OPTION_UNLOCK_VIA_GROUPS => 'no', + DWS_LPMWC_OPTION_UNLOCK_VIA_WC_MEMBERSHIPS => 'no', + DWS_LPMWC_OPTION_DELETE_DATA => 'no', + ), + \array_combine( self::KEYS, \array_map( 'get_option', self::KEYS ) ) + ); + foreach ( self::KEYS as $key ) { + self::assertSame( 'on', $wpdb->get_var( $wpdb->prepare( "SELECT autoload FROM {$wpdb->options} WHERE option_name = %s", $key ) ), $key ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- Reads the stored autoload flag. } } - public function test_the_first_step_keeps_the_values_a_site_stored(): void { + public function test_the_step_copies_v1s_settings_and_leaves_v1s_rows_in_place(): void { + $v1 = array( + 'dws-wc-lpm_general_locked-payment-methods' => array( 'cod', 'bacs' ), + 'dws-wc-lpm_general_unlock-via-user-role' => 'no', + 'dws-wc-lpm_general_full-access-user-roles' => array( 'administrator' ), + 'dws-wc-lpm_general_unlock-via-user-meta' => 'no', + 'dws-wc-lpm_general_unlock-via-order-meta' => 'yes', + 'dws-wc-lpm_integrations_unlock-via-groups' => 'yes', + 'dws-wc-lpm_integrations_unlock-via-wc-memberships' => 'yes', + 'dws-wc-lpm_plugin_remove-data-uninstall' => 'yes', + ); + foreach ( self::KEYS as $key ) { + delete_option( $key ); + } + foreach ( $v1 as $key => $value ) { + update_option( $key, $value ); + } + + dws_lpmwc_add_settings(); + + self::assertSame( + array( + DWS_LPMWC_OPTION_LOCKED_METHODS => array( 'cod', 'bacs' ), + DWS_LPMWC_OPTION_UNLOCK_VIA_USER_ROLES => 'no', + DWS_LPMWC_OPTION_FULL_ACCESS_ROLES => array( 'administrator' ), + DWS_LPMWC_OPTION_UNLOCK_VIA_USER_PROFILES => 'no', + DWS_LPMWC_OPTION_UNLOCK_VIA_ORDERS => 'yes', + DWS_LPMWC_OPTION_UNLOCK_VIA_GROUPS => 'yes', + DWS_LPMWC_OPTION_UNLOCK_VIA_WC_MEMBERSHIPS => 'yes', + DWS_LPMWC_OPTION_DELETE_DATA => 'yes', + ), + \array_combine( self::KEYS, \array_map( 'get_option', self::KEYS ) ) + ); + self::assertSame( $v1, \array_combine( \array_keys( $v1 ), \array_map( 'get_option', \array_keys( $v1 ) ) ) ); + } + + public function test_the_step_keeps_the_values_a_site_stored(): void { update_option( DWS_LPMWC_OPTION_DELETE_DATA, 'yes' ); + update_option( DWS_LPMWC_OPTION_LOCKED_METHODS, array() ); + update_option( 'dws-wc-lpm_plugin_remove-data-uninstall', 'no' ); + update_option( 'dws-wc-lpm_general_locked-payment-methods', array( 'cod' ) ); dws_lpmwc_add_settings(); self::assertSame( 'yes', get_option( DWS_LPMWC_OPTION_DELETE_DATA ) ); + self::assertSame( array(), get_option( DWS_LPMWC_OPTION_LOCKED_METHODS ) ); } - public function test_the_first_step_throws_when_a_default_cannot_be_stored(): void { + public function test_the_step_stores_the_default_for_a_v1_value_of_the_wrong_kind(): void { + foreach ( self::KEYS as $key ) { + delete_option( $key ); + } + update_option( 'dws-wc-lpm_general_unlock-via-user-role', 'maybe' ); + update_option( 'dws-wc-lpm_general_locked-payment-methods', 'cod' ); + update_option( 'dws-wc-lpm_general_full-access-user-roles', array( 'editor', 7, array( 'nested' ) ) ); + + dws_lpmwc_add_settings(); + + self::assertSame( 'yes', get_option( DWS_LPMWC_OPTION_UNLOCK_VIA_USER_ROLES ) ); + self::assertSame( array(), get_option( DWS_LPMWC_OPTION_LOCKED_METHODS ) ); + self::assertSame( array( 'editor' ), get_option( DWS_LPMWC_OPTION_FULL_ACCESS_ROLES ) ); + } + + public function test_v1s_settings_map_onto_the_plugins_settings_one_to_one(): void { + $v1_keys = array(); + foreach ( DWS_LPMWC_V1_SETTINGS as $group => $fields ) { + foreach ( \array_keys( $fields ) as $id ) { + $v1_keys[] = "dws-wc-lpm_{$group}_{$id}"; + } + } + + self::assertEqualsCanonicalizing( self::V1_KEYS, $v1_keys ); + self::assertEqualsCanonicalizing( self::KEYS, \array_merge( ...\array_values( \array_map( 'array_values', DWS_LPMWC_V1_SETTINGS ) ) ) ); + self::assertEqualsCanonicalizing( self::KEYS, \array_keys( DWS_LPMWC_OPTION_DEFAULTS ) ); + } + + public function test_the_step_throws_when_a_setting_cannot_be_stored(): void { global $wpdb; + delete_option( DWS_LPMWC_OPTION_DELETE_DATA ); $fail = static fn ( string $query ): string => \str_contains( $query, "'" . DWS_LPMWC_OPTION_DELETE_DATA . "'" ) && \str_starts_with( \ltrim( $query ), 'INSERT' ) ? "SELECT * FROM {$wpdb->prefix}dws_lpmwc_missing" : $query; $suppressed = $wpdb->suppress_errors(); add_filter( 'query', $fail ); - try { $this->expectExceptionObject( new \RuntimeException( 'The option ' . DWS_LPMWC_OPTION_DELETE_DATA . ' could not be stored.' ) ); dws_lpmwc_add_settings(); diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SettingsTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SettingsTest.php index feb1357..60284c9 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SettingsTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SettingsTest.php @@ -2,39 +2,52 @@ namespace DeepWebSolutions\LockedPaymentMethods\Tests\Integration; -use DeepWebSolutions\LockedPaymentMethods\Plugin; -use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\WooCommerce\Setting\SettingsTab; +use DeepWebSolutions\LockedPaymentMethods\Settings; use PHPUnit\Framework\Attributes\After; use PHPUnit\Framework\Attributes\Before; use PHPUnit\Framework\Attributes\CoversNothing; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; #[CoversNothing] final class SettingsTest extends TestCase { - protected const array KEYS = array( DWS_LPMWC_OPTION_DELETE_DATA ); + protected const string SECTION = 'dws-locked-payment-methods'; - protected static SettingsTab $tab; + protected const array CHECKBOXES = array( DWS_LPMWC_OPTION_UNLOCK_VIA_USER_ROLES, DWS_LPMWC_OPTION_UNLOCK_VIA_USER_PROFILES, DWS_LPMWC_OPTION_UNLOCK_VIA_ORDERS, DWS_LPMWC_OPTION_DELETE_DATA ); + + protected const array KEYS = array( DWS_LPMWC_OPTION_LOCKED_METHODS, DWS_LPMWC_OPTION_FULL_ACCESS_ROLES, ...self::CHECKBOXES ); + + protected static \WC_Settings_Page $advanced; protected array $stored = array(); + protected array $deprecated = array(); + #[\Override] public static function setUpBeforeClass(): void { - require_once WC_ABSPATH . 'includes/admin/settings/class-wc-settings-page.php'; require_once WC_ABSPATH . 'includes/admin/class-wc-admin-settings.php'; - - // WooCommerce builds its settings pages once per request, so the class shares one build of the tab. - foreach ( apply_filters( 'woocommerce_get_settings_pages', array() ) as $page ) { - if ( $page instanceof SettingsTab && Plugin::PREFIX === $page->get_id() ) { - self::$tab = $page; + // WooCommerce builds its settings pages once per request, so the class shares one build of the Advanced tab. + foreach ( \WC_Admin_Settings::get_settings_pages() as $page ) { + if ( 'advanced' === $page->get_id() ) { + self::$advanced = $page; } } } + public static function setting_groups(): array { + return array( + 'general' => array( 'general', array( 'locked-payment-methods', 'unlock-via-user-role', 'full-access-user-roles', 'unlock-via-user-meta', 'unlock-via-order-meta' ) ), + 'plugin' => array( 'plugin', array( 'remove-data-uninstall' ) ), + ); + } + #[Before] protected function store_rows(): void { foreach ( self::KEYS as $key ) { $this->stored[ $key ] = get_option( $key ); } + add_filter( 'deprecated_hook_trigger_error', '__return_false' ); + add_action( 'deprecated_hook_run', array( $this, 'record_deprecated_hook' ), 10, 2 ); } #[After] @@ -42,44 +55,151 @@ protected function restore_rows(): void { foreach ( $this->stored as $key => $value ) { false === $value ? delete_option( $key ) : update_option( $key, $value ); } - ( new \ReflectionProperty( \WC_Admin_Settings::class, 'errors' ) )->setValue( null, array() ); + remove_filter( 'deprecated_hook_trigger_error', '__return_false' ); + remove_action( 'deprecated_hook_run', array( $this, 'record_deprecated_hook' ) ); + } + + public function record_deprecated_hook( string $hook, string $replacement ): void { + $this->deprecated[] = array( $hook, $replacement ); + } + + public function test_the_section_sits_on_the_advanced_tab_under_its_v1_id(): void { + self::assertSame( 'Locked Payment Methods', self::$advanced->get_sections()[ self::SECTION ] ?? null ); } - public function test_the_tab_shows_the_data_field_with_its_default(): void { + public function test_the_section_shows_v1s_groups_with_their_defaults(): void { foreach ( self::KEYS as $key ) { delete_option( $key ); } $form = $this->render(); - self::assertSame( 'Locked Payment Methods for WooCommerce', self::$tab->get_label() ); + self::assertSame( array( 'General Settings', 'Plugin Settings' ), \array_map( static fn ( \Dom\Element $heading ): string => \trim( $heading->textContent ), \iterator_to_array( $form->querySelectorAll( 'h2' ) ) ) ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- A DOM property. + self::assertSame( array(), self::selected( $form, DWS_LPMWC_OPTION_LOCKED_METHODS ) ); + self::assertSame( array( 'administrator', 'shop_manager' ), self::selected( $form, DWS_LPMWC_OPTION_FULL_ACCESS_ROLES ) ); + foreach ( self::CHECKBOXES as $key ) { + self::assertSame( 'checkbox', $form->getElementById( $key )?->getAttribute( 'type' ), $key ); + } + self::assertTrue( $form->getElementById( DWS_LPMWC_OPTION_UNLOCK_VIA_USER_ROLES )?->hasAttribute( 'checked' ) ); + self::assertTrue( $form->getElementById( DWS_LPMWC_OPTION_UNLOCK_VIA_USER_PROFILES )?->hasAttribute( 'checked' ) ); + self::assertFalse( $form->getElementById( DWS_LPMWC_OPTION_UNLOCK_VIA_ORDERS )?->hasAttribute( 'checked' ) ); self::assertFalse( $form->getElementById( DWS_LPMWC_OPTION_DELETE_DATA )?->hasAttribute( 'checked' ) ); } - public function test_the_retention_opt_in_round_trips_through_woocommerces_save(): void { - $this->save( array( DWS_LPMWC_OPTION_DELETE_DATA => '1' ) ); + public function test_the_locked_list_offers_every_registered_gateway_and_marks_the_disabled_ones(): void { + $expected = array(); + foreach ( WC()->payment_gateways()->payment_gateways() as $id => $gateway ) { + $expected[ $id ] = 'yes' === $gateway->enabled ? $gateway->get_title() : $gateway->get_title() . ' (disabled)'; + } + + $options = array(); + foreach ( $this->render()->querySelectorAll( '[id="' . DWS_LPMWC_OPTION_LOCKED_METHODS . '"] option' ) as $option ) { + $options[ (string) $option->getAttribute( 'value' ) ] = \trim( $option->textContent ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- A DOM property. + } + + self::assertNotSame( array(), $expected ); + self::assertContains( 'no', \array_column( WC()->payment_gateways()->payment_gateways(), 'enabled' ), 'The tests site has no disabled gateway to mark.' ); + self::assertSame( $expected, $options ); + } + + public function test_a_disabled_gateway_stays_locked_through_a_save(): void { + $disabled = \array_key_first( \array_filter( WC()->payment_gateways()->payment_gateways(), static fn ( \WC_Payment_Gateway $gateway ): bool => 'yes' !== $gateway->enabled ) ); + update_option( DWS_LPMWC_OPTION_LOCKED_METHODS, array( $disabled ) ); + + self::assertSame( array( $disabled ), self::selected( $this->render(), DWS_LPMWC_OPTION_LOCKED_METHODS ) ); + + $this->save( array( DWS_LPMWC_OPTION_LOCKED_METHODS => array( $disabled ) ) ); + + self::assertSame( array( $disabled ), get_option( DWS_LPMWC_OPTION_LOCKED_METHODS ) ); + } - self::assertSame( 'yes', get_option( DWS_LPMWC_OPTION_DELETE_DATA ) ); - self::assertTrue( dws_lpmwc_get_bool_option( DWS_LPMWC_OPTION_DELETE_DATA ) ); - self::assertTrue( $this->render()->getElementById( DWS_LPMWC_OPTION_DELETE_DATA )?->hasAttribute( 'checked' ) ); + public function test_the_roles_field_offers_every_role_and_shows_while_the_role_strategy_is_off(): void { + update_option( DWS_LPMWC_OPTION_UNLOCK_VIA_USER_ROLES, 'no' ); + + $options = array(); + foreach ( $this->render()->querySelectorAll( '[id="' . DWS_LPMWC_OPTION_FULL_ACCESS_ROLES . '"] option' ) as $option ) { + $options[ (string) $option->getAttribute( 'value' ) ] = \trim( $option->textContent ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- A DOM property. + } + + self::assertSame( \array_map( 'translate_user_role', \array_column( wp_roles()->roles, 'name' ) ), \array_values( $options ) ); + self::assertSame( \array_keys( wp_roles()->roles ), \array_map( 'strval', \array_keys( $options ) ) ); + } + + public function test_the_checkboxes_store_yes_and_no_as_v1s_selects_did(): void { + $this->save( \array_fill_keys( self::CHECKBOXES, '1' ) ); + + foreach ( self::CHECKBOXES as $key ) { + self::assertSame( 'yes', get_option( $key ), $key ); + } $this->save( array() ); - self::assertSame( 'no', get_option( DWS_LPMWC_OPTION_DELETE_DATA ) ); - self::assertFalse( dws_lpmwc_get_bool_option( DWS_LPMWC_OPTION_DELETE_DATA ) ); + foreach ( self::CHECKBOXES as $key ) { + self::assertSame( 'no', get_option( $key ), $key ); + } + } + + #[DataProvider( 'setting_groups' )] + public function test_a_groups_fields_pass_its_v1_filter_under_v1s_ids_and_keep_v1s_keys_for_fields_it_adds( string $group, array $v1_ids ): void { + $seen = array(); + $v1_hook = "locked_payment_methods_for_woocommerce/{$group}_settings/get_group_fields"; + $v1_extra = static function ( array $fields ) use ( &$seen ): array { + $seen[] = \array_keys( $fields ); + + return $fields + array( + 'extra' => array( + 'type' => 'text', + 'title' => 'Extra', + ), + ); + }; + add_filter( $v1_hook, $v1_extra ); + + try { + $ids = \array_column( ( new Settings() )->get_section_fields( 'advanced', self::SECTION ), 'id' ); + } finally { + remove_filter( $v1_hook, $v1_extra ); + } + + self::assertSame( array( $v1_ids ), $seen ); + foreach ( $v1_ids as $v1_id ) { + self::assertContains( DWS_LPMWC_V1_SETTINGS[ $group ][ $v1_id ], $ids ); + } + self::assertContains( "dws-wc-lpm_{$group}_extra", $ids ); + self::assertSame( array( array( $v1_hook, 'woocommerce_get_settings_advanced' ) ), $this->deprecated ); + } + + public function test_the_plugin_row_links_to_the_section_and_to_the_support_forum(): void { + $basename = plugin_basename( DWS_LPMWC_FILE ); + + $links = apply_filters( "plugin_action_links_{$basename}", array( 'deactivate' => 'Deactivate' ), $basename, array(), 'all' ); + $meta = apply_filters( 'plugin_row_meta', array(), $basename, array(), 'all' ); + + self::assertStringContainsString( \esc_url( admin_url( 'admin.php?page=wc-settings&tab=advanced§ion=' . self::SECTION ) ), (string) \reset( $links ) ); + self::assertStringContainsString( 'https://wordpress.org/support/plugin/locked-payment-methods-for-woocommerce/', \implode( '', $meta ) ); + self::assertSame( array(), apply_filters( 'plugin_row_meta', array(), 'another-plugin/another-plugin.php', array(), 'all' ) ); + } + + protected static function selected( \Dom\HTMLDocument $form, string $key ): array { + $values = array(); + foreach ( $form->querySelectorAll( '[id="' . $key . '"] option[selected]' ) as $option ) { + $values[] = (string) $option->getAttribute( 'value' ); + } + + return $values; } protected function render(): \Dom\HTMLDocument { \ob_start(); - \WC_Admin_Settings::output_fields( self::$tab->get_settings_for_section( '' ) ); + \WC_Admin_Settings::output_fields( self::$advanced->get_settings_for_section( self::SECTION ) ); return \Dom\HTMLDocument::createFromString( '' . \ob_get_clean() . '', \LIBXML_NOERROR ); } // WooCommerce's form always posts its save button, so a save that changes nothing still runs. protected function save( array $posted ): void { - \WC_Admin_Settings::save_fields( self::$tab->get_settings_for_section( '' ), $posted + array( 'save' => 'Save changes' ) ); + \WC_Admin_Settings::save_fields( self::$advanced->get_settings_for_section( self::SECTION ), $posted + array( 'save' => 'Save changes' ) ); wp_cache_flush(); } } From 9e6fa87054f5dd67cee7e18d7d52e95558ea5a5b Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sat, 31 Oct 2026 23:27:15 +0100 Subject: [PATCH 08/50] Move Locked Payment Methods' settings to the Advanced tab v1 kept its settings in an unlisted section of the Payments tab, which merchants could not find from WooCommerce's menus, and its locked list offered only enabled gateways, so a save while a locked gateway was disabled dropped it, and enabling the gateway again showed it to every customer. The section moves to the Advanced tab under v1's id, beside Linked Orders', with v1's General, Integrations and Plugin blocks; every setting is a flat option under dws_lpmwc_, and the on/off selects are checkboxes. The locked list offers every registered gateway, marking the disabled ones, so a disabled gateway stays locked, and the roles field always shows. v1's group-field filters fire as deprecated on v1's field ids, and a field one adds keeps the option v1 stored it under. The first migration step copies each of v1's eight settings where its row holds a value of the setting's kind, takes the default otherwise, and leaves v1's rows, so a downgrade still locks what v1 locked. The plugin row links to the section and the support forum. The integration switches arrive with their strategies. X ruling 93. Assisted-by: Claude Code:claude-opus-5-5 --- .../includes/migrations.php | 26 ++- .../includes/options.php | 104 ++++++++- .../src/Settings.php | 205 +++++++++++++++--- .../tests/Integration/SettingsTest.php | 5 +- 4 files changed, 298 insertions(+), 42 deletions(-) diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php b/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php index b59c35c..7cde3e0 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php @@ -25,23 +25,33 @@ function dws_lpmwc_get_migration_steps(): array { } /** - * Stores the defaults of the settings that need no translation, autoloaded, so a site that never saved them reads them without a query; a translated default is never stored, because it would keep the language the site had when the step ran. + * Stores each setting the site has not stored yet, autoloaded, from v1's row where it holds a value of the setting's kind and otherwise from the setting's default, and leaves v1's rows in place, so a site that goes back to v1 still finds its settings. * * @since 2.0.0 * @version 2.0.0 * - * @throws RuntimeException Thrown when a default could not be stored. + * @throws RuntimeException Thrown when a setting could not be stored. * * @return void */ function dws_lpmwc_add_settings(): void { - $defaults = array( - DWS_LPMWC_OPTION_DELETE_DATA => wc_bool_to_string( DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_DELETE_DATA ] ), - ); + foreach ( DWS_LPMWC_V1_SETTINGS as $group => $fields ) { + foreach ( $fields as $id => $key ) { + if ( false !== get_option( $key ) ) { + continue; + } + + $default = DWS_LPMWC_OPTION_DEFAULTS[ $key ]; + $v1 = get_option( "dws-wc-lpm_{$group}_{$id}", null ); + if ( is_array( $default ) ) { + $value = is_array( $v1 ) ? array_values( array_filter( $v1, 'is_string' ) ) : $default; + } else { + $value = wc_bool_to_string( null === $v1 ? $default : ( filter_var( $v1, FILTER_VALIDATE_BOOL, FILTER_NULL_ON_FAILURE ) ?? $default ) ); // filter_var() reads null as false, so an absent row takes the default before it. + } - foreach ( $defaults as $key => $value ) { - if ( false === get_option( $key ) && ! add_option( $key, $value, '', true ) ) { - throw new RuntimeException( "The option $key could not be stored." ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing, never rendered. + if ( ! add_option( $key, $value, '', true ) ) { + throw new RuntimeException( "The option $key could not be stored." ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing, never rendered. + } } } } diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/options.php b/plugins/locked-payment-methods-for-woocommerce/includes/options.php index ed22da5..1870b40 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/options.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/options.php @@ -22,6 +22,76 @@ */ const DWS_LPMWC_OPTION_DELETE_DATA = Plugin::PREFIX . '_delete_data'; +/** + * The option that holds the ids of the payment methods locked for every customer whom no unlock strategy unlocks. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @var string + */ +const DWS_LPMWC_OPTION_LOCKED_METHODS = Plugin::PREFIX . '_locked_payment_methods'; + +/** + * The option that holds 'yes' where users with the full-access roles may use every locked payment method. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @var string + */ +const DWS_LPMWC_OPTION_UNLOCK_VIA_USER_ROLES = Plugin::PREFIX . '_unlock_via_user_roles'; + +/** + * The option that holds the roles whose users may use every locked payment method while that strategy is on. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @var string + */ +const DWS_LPMWC_OPTION_FULL_ACCESS_ROLES = Plugin::PREFIX . '_full_access_user_roles'; + +/** + * The option that holds 'yes' where a user's profile can unlock locked payment methods for that user. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @var string + */ +const DWS_LPMWC_OPTION_UNLOCK_VIA_USER_PROFILES = Plugin::PREFIX . '_unlock_via_user_profiles'; + +/** + * The option that holds 'yes' where an order can unlock locked payment methods for paying that order. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @var string + */ +const DWS_LPMWC_OPTION_UNLOCK_VIA_ORDERS = Plugin::PREFIX . '_unlock_via_orders'; + +/** + * The option that holds 'yes' where a Groups group can unlock locked payment methods for its members. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @var string + */ +const DWS_LPMWC_OPTION_UNLOCK_VIA_GROUPS = Plugin::PREFIX . '_unlock_via_groups'; + +/** + * The option that holds 'yes' where a WooCommerce Memberships plan can unlock locked payment methods for its active members. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @var string + */ +const DWS_LPMWC_OPTION_UNLOCK_VIA_WC_MEMBERSHIPS = Plugin::PREFIX . '_unlock_via_wc_memberships'; + /** * The value each option reads as while nothing valid is stored. * @@ -31,7 +101,39 @@ * @var array> */ const DWS_LPMWC_OPTION_DEFAULTS = array( - DWS_LPMWC_OPTION_DELETE_DATA => false, + DWS_LPMWC_OPTION_LOCKED_METHODS => array(), + DWS_LPMWC_OPTION_UNLOCK_VIA_USER_ROLES => true, + DWS_LPMWC_OPTION_FULL_ACCESS_ROLES => array( 'administrator', 'shop_manager' ), + DWS_LPMWC_OPTION_UNLOCK_VIA_USER_PROFILES => true, + DWS_LPMWC_OPTION_UNLOCK_VIA_ORDERS => false, + DWS_LPMWC_OPTION_UNLOCK_VIA_GROUPS => false, + DWS_LPMWC_OPTION_UNLOCK_VIA_WC_MEMBERSHIPS => false, + DWS_LPMWC_OPTION_DELETE_DATA => false, +); + +/** + * The option each of v1's settings moved to, by v1's settings group and field id, from which v1 stored it as dws-wc-lpm_{group}_{id}. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @var array> + */ +const DWS_LPMWC_V1_SETTINGS = array( + 'general' => array( + 'locked-payment-methods' => DWS_LPMWC_OPTION_LOCKED_METHODS, + 'unlock-via-user-role' => DWS_LPMWC_OPTION_UNLOCK_VIA_USER_ROLES, + 'full-access-user-roles' => DWS_LPMWC_OPTION_FULL_ACCESS_ROLES, + 'unlock-via-user-meta' => DWS_LPMWC_OPTION_UNLOCK_VIA_USER_PROFILES, + 'unlock-via-order-meta' => DWS_LPMWC_OPTION_UNLOCK_VIA_ORDERS, + ), + 'integrations' => array( + 'unlock-via-groups' => DWS_LPMWC_OPTION_UNLOCK_VIA_GROUPS, + 'unlock-via-wc-memberships' => DWS_LPMWC_OPTION_UNLOCK_VIA_WC_MEMBERSHIPS, + ), + 'plugin' => array( + 'remove-data-uninstall' => DWS_LPMWC_OPTION_DELETE_DATA, + ), ); /** diff --git a/plugins/locked-payment-methods-for-woocommerce/src/Settings.php b/plugins/locked-payment-methods-for-woocommerce/src/Settings.php index c540b64..b41d951 100644 --- a/plugins/locked-payment-methods-for-woocommerce/src/Settings.php +++ b/plugins/locked-payment-methods-for-woocommerce/src/Settings.php @@ -2,19 +2,37 @@ namespace DeepWebSolutions\LockedPaymentMethods; +use Automattic\WooCommerce\Admin\Settings\SettingsSectionRegistry; use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\ComponentInterface; +use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Settings\Forms; use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\WooCommerce\Setting\SectionFieldsDefinitionInterface; -use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\WooCommerce\Setting\SettingsTab; +use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\WooCommerce\Setting\SettingsSection; \defined( 'ABSPATH' ) || exit; /** - * Adds the plugin's WooCommerce settings tab, which holds the consent to delete the plugin's data on uninstall. + * Adds the plugin's section to WooCommerce's Advanced tab under v1's id, and links to it and to the support forum from the plugin's row. * * @since 2.0.0 * @version 2.0.0 + * + * @phpstan-import-type Field from Forms */ final class Settings implements ComponentInterface, SectionFieldsDefinitionInterface { + // region FIELDS AND CONSTANTS + + /** + * The section's id, which v1 used on the Payments tab. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @var string + */ + public const string SECTION = 'dws-locked-payment-methods'; + + // endregion + // region INHERITED METHODS /** @@ -25,7 +43,9 @@ final class Settings implements ComponentInterface, SectionFieldsDefinitionInter */ #[\Override] public function register_hooks(): void { - add_filter( 'woocommerce_get_settings_pages', array( $this, 'add_settings_tab' ) ); + add_action( 'woocommerce_settings_sections_registration', array( $this, 'register_section' ) ); + add_filter( 'plugin_action_links_' . plugin_basename( DWS_LPMWC_FILE ), array( $this, 'add_settings_link' ) ); + add_filter( 'plugin_row_meta', array( $this, 'add_support_link' ), 10, 2 ); } /** @@ -36,24 +56,43 @@ public function register_hooks(): void { */ #[\Override] public function get_section_fields( string $tab_id, string $section_id ): array { - return array( - array( + $titles = array( + 'general' => __( 'General Settings', 'locked-payment-methods-for-woocommerce' ), + 'integrations' => __( 'Integrations Settings', 'locked-payment-methods-for-woocommerce' ), + 'plugin' => __( 'Plugin Settings', 'locked-payment-methods-for-woocommerce' ), + ); + + $fields = array(); + foreach ( DWS_LPMWC_V1_SETTINGS as $group => $keys ) { + /** + * Filters a group's fields, keyed by their v1 ids. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use woocommerce_get_settings_advanced. + * + * @param array $fields The group's fields. + */ + $group_fields = apply_filters_deprecated( "locked_payment_methods_for_woocommerce/{$group}_settings/get_group_fields", array( $this->get_group_fields( $group ) ), '2.0.0', 'woocommerce_get_settings_advanced' ); + if ( array() === $group_fields ) { + continue; + } + + $fields[] = array( 'type' => 'title', - 'id' => Plugin::PREFIX . '_data_options', - 'title' => __( 'Data', 'locked-payment-methods-for-woocommerce' ), - ), - array( - 'type' => 'checkbox', - 'id' => DWS_LPMWC_OPTION_DELETE_DATA, - 'title' => __( 'Data on uninstall', 'locked-payment-methods-for-woocommerce' ), - 'desc' => __( 'Delete the plugin\'s data when it is uninstalled', 'locked-payment-methods-for-woocommerce' ), - 'default' => wc_bool_to_string( DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_DELETE_DATA ] ), - ), - array( + 'id' => "dws-wc-lpm_{$group}", + 'title' => $titles[ $group ], + ); + foreach ( $group_fields as $id => $field ) { + $fields[] = array( 'id' => $keys[ $id ] ?? "dws-wc-lpm_{$group}_{$id}" ) + $field; // A field a v1 callback adds keeps the option v1 stored it under. + } + $fields[] = array( 'type' => 'sectionend', - 'id' => Plugin::PREFIX . '_data_options', - ), - ); + 'id' => "dws-wc-lpm_{$group}", + ); + } + + return $fields; } // endregion @@ -61,26 +100,130 @@ public function get_section_fields( string $tab_id, string $section_id ): array // region HOOKS /** - * Adds the plugin's settings tab, which WooCommerce's settings page class must exist to construct. + * Registers the plugin's section on the Advanced tab. * * @since 2.0.0 * @version 2.0.0 * - * @param array $pages The settings tabs. + * @param SettingsSectionRegistry $registry WooCommerce's registry of settings sections. * - * @return array + * @return void */ - public function add_settings_tab( array $pages ): array { - $pages[] = new SettingsTab( - Plugin::PREFIX, - array( - 'label' => __( 'Locked Payment Methods for WooCommerce', 'locked-payment-methods-for-woocommerce' ), - 'sections' => array( '' => __( 'General', 'locked-payment-methods-for-woocommerce' ) ), - ), - $this + public function register_section( SettingsSectionRegistry $registry ): void { + $registry->register( + new SettingsSection( + self::SECTION, + array( + 'tab' => 'advanced', + 'label' => _x( 'Locked Payment Methods', 'settings', 'locked-payment-methods-for-woocommerce' ), + ), + $this + ) ); + } - return $pages; + /** + * Adds the link to the plugin's section to its row on the Plugins screen. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string[] $links The row's action links. + * + * @return string[] + */ + public function add_settings_link( array $links ): array { + \array_unshift( $links, \sprintf( '%s', esc_url( admin_url( 'admin.php?page=wc-settings&tab=advanced§ion=' . self::SECTION ) ), esc_html__( 'Settings', 'locked-payment-methods-for-woocommerce' ) ) ); + + return $links; + } + + /** + * Adds the link to the plugin's support forum to its row on the Plugins screen. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string[] $meta The row's meta links. + * @param string $basename The basename of the row's plugin. + * + * @return string[] + */ + public function add_support_link( array $meta, string $basename ): array { + if ( plugin_basename( DWS_LPMWC_FILE ) === $basename ) { + $meta[] = \sprintf( '%s', esc_url( 'https://wordpress.org/support/plugin/locked-payment-methods-for-woocommerce/' ), esc_html__( 'Community support', 'locked-payment-methods-for-woocommerce' ) ); + } + + return $meta; + } + + // endregion + + // region HELPERS + + /** + * Returns a group's fields, keyed by their v1 ids. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string $group The group's v1 id. + * + * @return array + */ + protected function get_group_fields( string $group ): array { + return match ( $group ) { + 'general' => array( + 'locked-payment-methods' => array( + 'title' => __( 'Payment methods which are locked by default', 'locked-payment-methods-for-woocommerce' ), + 'type' => 'multiselect', + 'class' => 'wc-enhanced-select', + 'options' => \array_map( + static fn ( \WC_Payment_Gateway $gateway ): string => 'yes' === $gateway->enabled // @phpstan-ignore argument.type (WooCommerce's registry holds only WC_Payment_Gateway instances, which its stubs leave untyped.) + ? $gateway->get_title() + /* translators: %s: payment method title. */ + : \sprintf( __( '%s (disabled)', 'locked-payment-methods-for-woocommerce' ), $gateway->get_title() ), + WC()->payment_gateways()->payment_gateways() + ), + 'default' => DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_LOCKED_METHODS ], + 'desc_tip' => __( 'A disabled payment method stays locked, so it is still locked once it is enabled again.', 'locked-payment-methods-for-woocommerce' ), + ), + 'unlock-via-user-role' => array( + 'title' => __( 'Unlock via user roles', 'locked-payment-methods-for-woocommerce' ), + 'type' => 'checkbox', + 'desc' => __( 'Give users with the roles below every locked payment method', 'locked-payment-methods-for-woocommerce' ), + 'default' => wc_bool_to_string( DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_UNLOCK_VIA_USER_ROLES ] ), + ), + 'full-access-user-roles' => array( + 'title' => __( 'User roles with full access to every locked payment method', 'locked-payment-methods-for-woocommerce' ), + 'type' => 'multiselect', + 'class' => 'wc-enhanced-select', + 'options' => \array_map( 'translate_user_role', wp_roles()->get_names() ), + 'default' => DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_FULL_ACCESS_ROLES ], + ), + 'unlock-via-user-meta' => array( + 'title' => __( 'Unlock via user profiles', 'locked-payment-methods-for-woocommerce' ), + 'type' => 'checkbox', + 'desc' => __( 'Unlock payment methods for a user from their profile, for all their future orders and their unpaid orders', 'locked-payment-methods-for-woocommerce' ), + 'default' => wc_bool_to_string( DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_UNLOCK_VIA_USER_PROFILES ] ), + ), + 'unlock-via-order-meta' => array( + 'title' => __( 'Unlock via orders', 'locked-payment-methods-for-woocommerce' ), + 'type' => 'checkbox', + 'desc' => __( 'Unlock payment methods for an unpaid order from its edit screen, for that order only', 'locked-payment-methods-for-woocommerce' ), + 'default' => wc_bool_to_string( DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_UNLOCK_VIA_ORDERS ] ), + ), + ), + 'plugin' => array( + 'remove-data-uninstall' => array( + 'title' => __( 'Remove all data on uninstallation', 'locked-payment-methods-for-woocommerce' ), + 'type' => 'checkbox', + 'desc' => __( 'Delete the plugin\'s data when it is uninstalled, so it must be configured again if it is installed later', 'locked-payment-methods-for-woocommerce' ), + 'default' => wc_bool_to_string( DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_DELETE_DATA ] ), + ), + ), + default => array(), + }; } // endregion diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SettingsTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SettingsTest.php index 60284c9..340be19 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SettingsTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SettingsTest.php @@ -36,8 +36,9 @@ public static function setUpBeforeClass(): void { public static function setting_groups(): array { return array( - 'general' => array( 'general', array( 'locked-payment-methods', 'unlock-via-user-role', 'full-access-user-roles', 'unlock-via-user-meta', 'unlock-via-order-meta' ) ), - 'plugin' => array( 'plugin', array( 'remove-data-uninstall' ) ), + 'general' => array( 'general', array( 'locked-payment-methods', 'unlock-via-user-role', 'full-access-user-roles', 'unlock-via-user-meta', 'unlock-via-order-meta' ) ), + 'integrations' => array( 'integrations', array() ), + 'plugin' => array( 'plugin', array( 'remove-data-uninstall' ) ), ); } From 51c0c0a9e0d29e1aa7930720b73f5dd8c4b838f9 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sat, 31 Oct 2026 23:33:54 +0100 Subject: [PATCH 09/50] Test Locked Payment Methods' lock as its safeguard v1's lock existed only once v1 had booted, and it removed a gateway only on an exact true, so a site below v1's floors, a failed boot or a callback returning null showed every locked method to every customer. v1 also judged order grants by the order-pay query variable alone, so a checkout request naming another order's id took that order's grants. SafeguardsTest expects the lock to remove the methods the plugin's option lists, unlocking one only on an explicit false from should_lock once the plugin has booted; to read v1's list while the plugin's option is absent, and to stop once the plugin stores its own, an empty one included (X ruling 102); to lock only the strings of a stored list; to let the locked_ids filter and v1's list filters add methods but never unlock stored ones; to fire v1's list and lock filters as deprecated, in v1's order and under v1's safe id; to judge the current user, and an order-pay page's order only given that order's key; and, in fresh requests below the PHP floor and below WooCommerce's, to hold with a relaxation hooked. The request helper moves into a trait both PartialDeployTest and SafeguardsTest use. X rulings 89, 94 and 102. Assisted-by: Claude Code:claude-opus-5-5 --- .../tests/Integration/PartialDeployTest.php | 17 +- .../tests/Integration/SafeguardsTest.php | 317 ++++++++++++++++++ .../tests/Support/RunsRequestsTrait.php | 19 ++ 3 files changed, 339 insertions(+), 14 deletions(-) create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/SafeguardsTest.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Support/RunsRequestsTrait.php diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PartialDeployTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PartialDeployTest.php index 884c048..e7b4b35 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PartialDeployTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PartialDeployTest.php @@ -3,12 +3,15 @@ namespace DeepWebSolutions\LockedPaymentMethods\Tests\Integration; use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\Uninstall\Uninstaller; +use DeepWebSolutions\LockedPaymentMethods\Tests\Support\RunsRequestsTrait; use PHPUnit\Framework\Attributes\CoversNothing; use PHPUnit\Framework\Attributes\RunInSeparateProcess; use PHPUnit\Framework\TestCase; #[CoversNothing] final class PartialDeployTest extends TestCase { + use RunsRequestsTrait; + public function test_a_missing_bootstrap_package_shows_the_build_notice_and_loads_nothing_built(): void { $manifest = \dirname( DWS_LPMWC_FILE ) . '/vendor-prefixed/ahegyes/wp-framework-bootstrap/load.php'; \rename( $manifest, "$manifest.hidden" ); @@ -83,18 +86,4 @@ static function ( string $class_name ): void { \unlink( $log ); update_option( DWS_LPMWC_OPTION_DELETE_DATA, 'no' ); } - - /** - * @return array{status: int, output: string, last: string} - */ - protected static function request( string $php ): array { - \exec( 'wp --path=' . \escapeshellarg( ABSPATH ) . ' eval ' . \escapeshellarg( $php ) . ' 2>&1', $output, $status ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.system_calls_exec -- Each row needs a fresh request that loads the plugin. - $output = \array_values( \array_filter( $output, static fn ( string $line ): bool => '' !== \trim( $line ) ) ); - - return array( - 'status' => $status, - 'output' => \implode( "\n", $output ), - 'last' => (string) \end( $output ), - ); - } } diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SafeguardsTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SafeguardsTest.php new file mode 100644 index 0000000..a833b41 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SafeguardsTest.php @@ -0,0 +1,317 @@ + array( array(), false ), + 'null' => array( array( 'relaxation' => null ), false ), + 'zero' => array( array( 'relaxation' => 0 ), false ), + 'empty-string' => array( array( 'relaxation' => '' ), false ), + 'false' => array( array( 'relaxation' => false ), true ), + ); + } + + public static function order_keys(): array { + return array( + 'missing' => array( null ), + 'wrong' => array( 'wc_order_wrong' ), + ); + } + + public static function floors(): array { + return array( + 'at-the-floors' => array( null, '[1,true]' ), + 'below-php' => array( 'Requires PHP', '[0,false]' ), + 'below-woocommerce' => array( 'WC requires at least', '[0,false]' ), + ); + } + + #[Before] + protected function store_rows(): void { + foreach ( array( DWS_LPMWC_OPTION_LOCKED_METHODS, self::V1_KEY ) as $key ) { + $this->stored[ $key ] = get_option( $key ); + } + update_option( DWS_LPMWC_OPTION_LOCKED_METHODS, array( 'cod' ) ); + add_filter( 'deprecated_hook_trigger_error', '__return_false' ); + add_action( 'deprecated_hook_run', array( $this, 'record_deprecated_hook' ), 10, 2 ); + } + + #[After] + protected function restore_rows(): void { + foreach ( $this->stored as $key => $value ) { + false === $value ? delete_option( $key ) : update_option( $key, $value ); + } + remove_filter( 'deprecated_hook_trigger_error', '__return_false' ); + remove_action( 'deprecated_hook_run', array( $this, 'record_deprecated_hook' ) ); + remove_filter( 'woocommerce_is_checkout', '__return_true' ); + unset( $GLOBALS['wp']->query_vars['order-pay'], $_GET['key'] ); + set_query_var( 'order-pay', '' ); + wp_set_current_user( 0 ); + } + + public function record_deprecated_hook( string $hook, string $replacement ): void { + $this->deprecated[] = array( $hook, $replacement ); + } + + public function test_the_lock_removes_the_methods_the_plugins_option_lists(): void { + $available = self::available(); + + self::assertArrayNotHasKey( 'cod', $available ); + self::assertArrayHasKey( 'bacs', $available ); + } + + #[DataProvider( 'relaxations' )] + public function test_only_an_explicit_false_from_should_lock_unlocks_a_locked_method( array $spec, bool $relaxed ): void { + $relaxation = static fn (): mixed => $spec['relaxation']; + if ( \array_key_exists( 'relaxation', $spec ) ) { + add_filter( self::FILTER, $relaxation ); + } + + try { + self::assertSame( $relaxed, \array_key_exists( 'cod', self::available() ) ); + } finally { + remove_filter( self::FILTER, $relaxation ); + } + } + + public function test_an_explicit_false_does_not_unlock_before_the_plugin_booted(): void { + global $wp_actions; + self::assertGreaterThan( 0, did_action( 'dws_lpmwc/boot/succeeded' ) ); + $fired = $wp_actions['dws_lpmwc/boot/succeeded']; + unset( $wp_actions['dws_lpmwc/boot/succeeded'] ); + add_filter( self::FILTER, '__return_false' ); + + try { + self::assertArrayNotHasKey( 'cod', self::available() ); + } finally { + remove_filter( self::FILTER, '__return_false' ); + $wp_actions['dws_lpmwc/boot/succeeded'] = $fired; + } + } + + public function test_the_lock_reads_v1s_list_while_the_plugin_has_stored_none(): void { + delete_option( DWS_LPMWC_OPTION_LOCKED_METHODS ); + update_option( self::V1_KEY, array( 'bacs' ) ); + + $available = self::available(); + + self::assertArrayNotHasKey( 'bacs', $available ); + self::assertArrayHasKey( 'cod', $available ); + } + + public function test_v1s_list_stops_counting_once_the_plugin_stores_its_own_even_an_empty_one(): void { + update_option( DWS_LPMWC_OPTION_LOCKED_METHODS, array() ); + update_option( self::V1_KEY, array( 'bacs' ) ); + + $available = self::available(); + + self::assertArrayHasKey( 'bacs', $available ); + self::assertArrayHasKey( 'cod', $available ); + } + + public function test_only_the_strings_of_a_stored_list_are_locked(): void { + update_option( DWS_LPMWC_OPTION_LOCKED_METHODS, array( 'cod', 7, array( 'bacs' ) ) ); + $available = self::available(); + self::assertArrayNotHasKey( 'cod', $available ); + self::assertArrayHasKey( 'bacs', $available ); + + update_option( DWS_LPMWC_OPTION_LOCKED_METHODS, 'bacs' ); + $available = self::available(); + self::assertArrayHasKey( 'bacs', $available ); + self::assertArrayHasKey( 'cod', $available ); + } + + public function test_the_locked_ids_filter_locks_more_methods_but_cannot_unlock_the_stored_ones(): void { + $replace = static fn (): array => array( 'bacs' ); + add_filter( 'dws_lpmwc/payment_method/locked_ids', $replace ); + + try { + $available = self::available(); + } finally { + remove_filter( 'dws_lpmwc/payment_method/locked_ids', $replace ); + } + + self::assertArrayNotHasKey( 'cod', $available ); + self::assertArrayNotHasKey( 'bacs', $available ); + } + + public function test_v1s_list_filters_fire_as_deprecated_before_the_locked_ids_filter(): void { + $seen = array(); + $gateways = WC()->payment_gateways()->payment_gateways(); + $hooks = array( + 'locked_payment_methods_for_woocommerce/general_settings/get_validated_option_value' => 'first', + 'locked_payment_methods_for_woocommerce/lock_manager/locked_payment_methods' => 'second', + 'dws_lpmwc/payment_method/locked_ids' => 'third', + ); + $adders = array(); + foreach ( $hooks as $hook => $id ) { + $adders[ $hook ] = static function ( array $ids, mixed $context ) use ( &$seen, $hook, $id ): array { + $seen[] = array( $hook, $ids, \is_array( $context ) ? \array_keys( $context ) : $context ); + + return array( ...$ids, $id ); + }; + add_filter( $hook, $adders[ $hook ], 10, 2 ); + } + + try { + self::available(); + } finally { + foreach ( $adders as $hook => $adder ) { + remove_filter( $hook, $adder ); + } + } + + $hooks = \array_keys( $hooks ); + self::assertSame( + array( + array( $hooks[0], array( 'cod' ), 'locked-payment-methods' ), + array( $hooks[1], array( 'cod', 'first' ), \array_keys( $gateways ) ), + array( $hooks[2], array( 'cod', 'first', 'second' ), \array_keys( $gateways ) ), + ), + $seen + ); + self::assertSame( array( array( $hooks[0], $hooks[2] ), array( $hooks[1], $hooks[2] ) ), $this->deprecated ); + } + + public function test_v1s_lock_filters_fire_as_deprecated_before_should_lock_under_v1s_safe_id(): void { + update_option( DWS_LPMWC_OPTION_LOCKED_METHODS, array( "WooCommerce_Pay\x01ments" ) ); + $seen = array(); + $v1 = 'locked_payment_methods_for_woocommerce/lock_manager/is_locked_payment_method'; + $v1_safe = "$v1/woocommerce_payments"; + $record = static function ( mixed ...$args ) use ( &$seen ): mixed { + $seen[] = $args; + + return $args[0]; + }; + $returns_null = static function ( mixed $lock ) use ( &$seen ): mixed { + $seen[] = array( $lock ); + + return null; + }; + add_filter( $v1, $record, 10, 2 ); + add_filter( $v1_safe, $returns_null ); + add_filter( self::FILTER, $record, 0, 4 ); + + try { + $available = self::available( array( "WooCommerce_Pay\x01ments" => new \stdClass() ) ); + } finally { + remove_filter( $v1, $record ); + remove_filter( $v1_safe, $returns_null ); + remove_filter( self::FILTER, $record, 0 ); + } + + self::assertSame( array( array( true, "WooCommerce_Pay\x01ments" ), array( true ), array( true, "WooCommerce_Pay\x01ments", 0, 0 ) ), $seen ); + self::assertArrayNotHasKey( "WooCommerce_Pay\x01ments", $available ); + self::assertSame( array( array( $v1, self::FILTER ), array( $v1_safe, self::FILTER ) ), $this->deprecated ); + } + + public function test_a_v1_callback_that_returns_false_still_unlocks_once_the_plugin_booted(): void { + $v1 = 'locked_payment_methods_for_woocommerce/lock_manager/is_locked_payment_method'; + add_filter( $v1, '__return_false' ); + + try { + self::assertArrayHasKey( 'cod', self::available() ); + } finally { + remove_filter( $v1, '__return_false' ); + } + } + + public function test_should_lock_judges_the_current_user_and_no_order_off_the_order_pay_page(): void { + wp_set_current_user( 1 ); + + self::assertSame( array( true, 'cod', 1, 0 ), self::should_lock_arguments() ); + } + + public function test_should_lock_judges_the_order_its_order_pay_page_pays_given_the_orders_key(): void { + $order = wc_create_order(); + self::visit_order_pay( $order->get_id(), $order->get_order_key() ); + + try { + self::assertSame( array( true, 'cod', 0, $order->get_id() ), self::should_lock_arguments() ); + } finally { + $order->delete( true ); + } + } + + #[DataProvider( 'order_keys' )] + public function test_should_lock_judges_no_order_on_an_order_pay_page_without_the_orders_key( ?string $key ): void { + $order = wc_create_order(); + self::visit_order_pay( $order->get_id(), $key ); + + try { + self::assertSame( array( true, 'cod', 0, 0 ), self::should_lock_arguments() ); + } finally { + $order->delete( true ); + } + } + + #[DataProvider( 'floors' )] + public function test_the_lock_holds_and_nothing_relaxes_it_while_the_plugin_cannot_boot( ?string $header, string $expected ): void { + $source = (string) \file_get_contents( DWS_LPMWC_FILE ); + if ( null !== $header ) { + \file_put_contents( DWS_LPMWC_FILE, (string) \preg_replace( '/^(\s*\*\s*' . \preg_quote( $header, '/' ) . ':\s*).*$/m', '${1}99.0', $source ) ); + } + + try { + $request = self::request( 'add_filter( "dws_lpmwc/payment_method/should_lock", "__return_false" ); echo wp_json_encode( array( did_action( "dws_lpmwc/boot/succeeded" ), array_key_exists( "cod", apply_filters( "woocommerce_available_payment_gateways", WC()->payment_gateways()->payment_gateways() ) ) ) );' ); + } finally { + \file_put_contents( DWS_LPMWC_FILE, $source ); + } + + self::assertSame( 0, $request['status'], $request['output'] ); + self::assertSame( $expected, $request['last'] ); + } + + protected static function available( array $gateways = array() ): array { + return apply_filters( 'woocommerce_available_payment_gateways', $gateways + WC()->payment_gateways()->payment_gateways() ); + } + + protected static function should_lock_arguments(): array { + $seen = array(); + $record = static function ( mixed ...$args ) use ( &$seen ): mixed { + if ( 'cod' === $args[1] ) { + $seen = $args; + } + + return $args[0]; + }; + add_filter( self::FILTER, $record, 0, 4 ); + + try { + self::available(); + } finally { + remove_filter( self::FILTER, $record, 0 ); + } + + return $seen; + } + + protected static function visit_order_pay( int $order_id, ?string $key ): void { + $GLOBALS['wp']->query_vars['order-pay'] = (string) $order_id; + set_query_var( 'order-pay', (string) $order_id ); + add_filter( 'woocommerce_is_checkout', '__return_true' ); + if ( null !== $key ) { + $_GET['key'] = $key; + } + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Support/RunsRequestsTrait.php b/plugins/locked-payment-methods-for-woocommerce/tests/Support/RunsRequestsTrait.php new file mode 100644 index 0000000..ed52ba0 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Support/RunsRequestsTrait.php @@ -0,0 +1,19 @@ +&1', $output, $status ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.system_calls_exec -- Each row needs a fresh request that loads the plugin. + $output = \array_values( \array_filter( $output, static fn ( string $line ): bool => '' !== \trim( $line ) ) ); + + return array( + 'status' => $status, + 'output' => \implode( "\n", $output ), + 'last' => (string) \end( $output ), + ); + } +} From 98113ac7af36585b2d5286c1a5621c51b161d43b Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sat, 31 Oct 2026 23:37:47 +0100 Subject: [PATCH 10/50] Lock payment methods in Locked Payment Methods' safeguard v1's lock existed only once v1 had booted and removed a gateway only on an exact true, so a site below v1's floors, a failed boot or a callback returning null showed every locked method to every customer; it also took the order-pay query variable as the order being paid, so a checkout request naming another order's id took that order's grants. The lock is the plugin's safeguard. includes/safeguards.php filters woocommerce_available_payment_gateways at 999 and removes every offered gateway on the plugin's list, or on v1's while the plugin has stored none, since the migration that copies v1's list runs only once the plugin boots (X ruling 102). dws_lpmwc/payment_method/locked_ids and v1's list filters only add ids, so nothing but an explicit false from dws_lpmwc/payment_method/should_lock, once the plugin has booted, unlocks a method. should_lock judges the current user and the order the order-pay page pays, which counts only with that order's key, as WooCommerce requires before it takes the payment. v1's list, value and lock filters fire as deprecated where v1 fired them, the per-id one under v1's safe id, for the offered gateways. phpstan.safeguards.neon now scans WooCommerce's stubs and is the safeguards' only profile, since an ignore WPCompat needs at WordPress 5.2 matches nothing at the plugin's own floor. X rulings 89, 94 and 102. Assisted-by: Claude Code:claude-opus-5-5 --- .../includes/safeguards.php | 181 ++++++++++++++++++ .../phpstan.dist.neon | 3 + .../phpstan.safeguards.neon | 3 + 3 files changed, 187 insertions(+) diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php b/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php index 189cd76..8908145 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php @@ -9,3 +9,184 @@ */ defined( 'ABSPATH' ) || exit; + +add_filter( 'woocommerce_available_payment_gateways', 'dws_lpmwc_remove_locked_payment_methods', 999 ); + +/** + * Returns whether a safeguard's filtered value relaxes it, which takes an explicit false once the plugin has finished booting. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param mixed $filtered The value the safeguard's filter returned for true. + * + * @return bool + */ +function dws_lpmwc_is_relaxed( $filtered ): bool { + return 0 < did_action( 'dws_lpmwc/boot/succeeded' ) && false === $filtered; +} + +/** + * Returns the ids of the locked payment methods: the plugin's list, or v1's while the plugin has stored none, because the migration that copies v1's list runs only once the plugin boots. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @return string[] + */ +function dws_lpmwc_get_locked_payment_method_ids(): array { + $stored = get_option( 'dws_lpmwc_locked_payment_methods' ); + $ids = dws_lpmwc_add_locked_ids( array(), false === $stored ? get_option( 'dws-wc-lpm_general_locked-payment-methods' ) : $stored ); + + /** + * Filters the locked payment methods' ids as v1's settings read them. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use dws_lpmwc/payment_method/locked_ids. + * + * @param string[] $ids The locked payment methods' ids. + * @param string $field_id v1's id of the setting. + */ + return dws_lpmwc_add_locked_ids( $ids, apply_filters_deprecated( 'locked_payment_methods_for_woocommerce/general_settings/get_validated_option_value', array( $ids, 'locked-payment-methods' ), '2.0.0', 'dws_lpmwc/payment_method/locked_ids' ) ); +} + +/** + * Returns the ids with the strings of a filter's value added, so a filter locks more payment methods but never unlocks one. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string[] $ids The ids before the filter. + * @param mixed $filtered The value the filter returned. + * + * @return string[] + */ +function dws_lpmwc_add_locked_ids( array $ids, $filtered ): array { + return is_array( $filtered ) ? array_values( array_unique( array_merge( $ids, array_filter( $filtered, 'is_string' ) ) ) ) : $ids; +} + +/** + * Returns whether a payment method stays locked for a user and an order, which only an explicit false from the plugin's filter relaxes, once the plugin has booted. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param bool $lock Whether the payment method is locked before the plugin's filter runs. + * @param string $gateway_id The payment method's id. + * @param int $user_id The paying user, or 0 for a guest. + * @param int $order_id The order being paid, or 0 off the order-pay page. + * + * @return bool + */ +function dws_lpmwc_should_lock( bool $lock, string $gateway_id, int $user_id, int $order_id ): bool { + /** + * Filters whether a payment method stays locked for a user and an order; an explicit false unlocks it once the plugin has booted. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param bool $lock Whether the payment method stays locked. + * @param string $gateway_id The payment method's id. + * @param int $user_id The paying user, or 0 for a guest. + * @param int $order_id The order being paid, or 0 off the order-pay page. + */ + return ! dws_lpmwc_is_relaxed( apply_filters( 'dws_lpmwc/payment_method/should_lock', $lock, $gateway_id, $user_id, $order_id ) ); // @phpstan-ignore WPCompat.parameterNotAvailable.applyfilters.args (WordPress 6.0 only formalized the arguments that older versions already passed on.) +} + +/** + * Returns the order the order-pay page pays, or 0 elsewhere or without that order's key, which WooCommerce requires before it takes the payment, so a request naming another order's id gains none of its grants. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @global WP $wp WordPress's request. + * + * @return int + */ +function dws_lpmwc_get_paid_order_id(): int { + global $wp; + + if ( ! function_exists( 'is_checkout_pay_page' ) || ! is_checkout_pay_page() ) { + return 0; + } + + $order = wc_get_order( $wp->query_vars['order-pay'] ?? 0 ); + $key = isset( $_GET['key'] ) && is_string( $_GET['key'] ) ? sanitize_text_field( wp_unslash( $_GET['key'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The order key authorizes paying the order, as on WooCommerce's order-pay page. + + return $order instanceof WC_Order && '' !== $key && hash_equals( $order->get_order_key(), $key ) ? $order->get_id() : 0; +} + +/** + * Removes the locked payment methods from the payment gateways offered, keeping each one the plugin relaxes for the current user and the order being paid. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param array $gateways The payment gateways offered, by id. + * + * @return array + */ +function dws_lpmwc_remove_locked_payment_methods( array $gateways ): array { + $ids = dws_lpmwc_get_locked_payment_method_ids(); + + /** + * Filters the locked payment methods' ids. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use dws_lpmwc/payment_method/locked_ids. + * + * @param string[] $ids The locked payment methods' ids. + * @param array $gateways The payment gateways offered, by id. + */ + $ids = dws_lpmwc_add_locked_ids( $ids, apply_filters_deprecated( 'locked_payment_methods_for_woocommerce/lock_manager/locked_payment_methods', array( $ids, $gateways ), '2.0.0', 'dws_lpmwc/payment_method/locked_ids' ) ); + + /** + * Filters the locked payment methods' ids, to which a callback adds the ids it locks; an id it leaves out stays locked. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string[] $ids The locked payment methods' ids. + * @param array $gateways The payment gateways offered, by id. + */ + $ids = dws_lpmwc_add_locked_ids( $ids, apply_filters( 'dws_lpmwc/payment_method/locked_ids', $ids, $gateways ) ); // @phpstan-ignore WPCompat.parameterNotAvailable.applyfilters.args (WordPress 6.0 only formalized the arguments that older versions already passed on.) + + $user_id = get_current_user_id(); + $order_id = dws_lpmwc_get_paid_order_id(); + foreach ( $ids as $id ) { + if ( ! isset( $gateways[ $id ] ) ) { + continue; + } + + /** + * Filters whether a payment method stays locked. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use dws_lpmwc/payment_method/should_lock. + * + * @param bool $is_locked Whether the payment method stays locked. + * @param string $id The payment method's id. + */ + $lock = apply_filters_deprecated( 'locked_payment_methods_for_woocommerce/lock_manager/is_locked_payment_method', array( true, $id ), '2.0.0', 'dws_lpmwc/payment_method/should_lock' ); + + /** + * Filters whether a payment method stays locked, under its id lowercased and stripped of control and non-ASCII bytes, as v1 named it. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use dws_lpmwc/payment_method/should_lock. + * + * @param bool $is_locked Whether the payment method stays locked. + */ + $lock = apply_filters_deprecated( 'locked_payment_methods_for_woocommerce/lock_manager/is_locked_payment_method/' . strtolower( (string) filter_var( $id, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH ) ), array( $lock ), '2.0.0', 'dws_lpmwc/payment_method/should_lock' ); + + if ( dws_lpmwc_should_lock( false !== $lock, $id, $user_id, $order_id ) ) { + unset( $gateways[ $id ] ); + } + } + + return $gateways; +} diff --git a/plugins/locked-payment-methods-for-woocommerce/phpstan.dist.neon b/plugins/locked-payment-methods-for-woocommerce/phpstan.dist.neon index 4bb3bb7..4dc0f96 100644 --- a/plugins/locked-payment-methods-for-woocommerce/phpstan.dist.neon +++ b/plugins/locked-payment-methods-for-woocommerce/phpstan.dist.neon @@ -12,6 +12,9 @@ parameters: - uninstall.php - includes - src + # phpstan.safeguards.neon analyzes the safeguards at the oldest runtime they parse on. + excludePaths: + - includes/safeguards.php scanFiles: - vendor/php-stubs/woocommerce-stubs/woocommerce-stubs.php - vendor/php-stubs/woocommerce-stubs/woocommerce-packages-stubs.php diff --git a/plugins/locked-payment-methods-for-woocommerce/phpstan.safeguards.neon b/plugins/locked-payment-methods-for-woocommerce/phpstan.safeguards.neon index 2d217fd..a4f0449 100644 --- a/plugins/locked-payment-methods-for-woocommerce/phpstan.safeguards.neon +++ b/plugins/locked-payment-methods-for-woocommerce/phpstan.safeguards.neon @@ -8,6 +8,9 @@ includes: parameters: paths: - includes/safeguards.php + # The lock filters WooCommerce's gateways, so it calls WooCommerce too. + scanFiles: + - vendor/php-stubs/woocommerce-stubs/woocommerce-stubs.php phpVersion: 70400 WPCompat: requiresAtLeast: '5.2' From d3a80642942ab60bd8a3e1cfc9de9f31a2f5738b Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sat, 31 Oct 2026 23:39:10 +0100 Subject: [PATCH 11/50] Test Locked Payment Methods' capabilities under v1's names v1 granted its capabilities from a permissions component that checked users at boot, and its free build granted the order capability that only its premium build used. CapabilitiesTest expects v1's four names and v1's grants: every capability to administrators, and the user-profile and order ones to shop managers. X ruling 92. Assisted-by: Claude Code:claude-opus-5-5 --- .../tests/Integration/CapabilitiesTest.php | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/CapabilitiesTest.php diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/CapabilitiesTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/CapabilitiesTest.php new file mode 100644 index 0000000..f3cf5c8 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/CapabilitiesTest.php @@ -0,0 +1,37 @@ + self::EVERY, + 'shop_manager' => array( DWS_LPMWC_CAP_UNLOCK_VIA_USER_PROFILES, DWS_LPMWC_CAP_UNLOCK_VIA_ORDERS ), + ), + DWS_LPMWC_CAPABILITY_GRANTS + ); + foreach ( DWS_LPMWC_CAPABILITY_GRANTS as $role => $capabilities ) { + foreach ( $capabilities as $capability ) { + self::assertTrue( get_role( $role )?->has_cap( $capability ), "The $role role lacks $capability." ); + } + } + self::assertFalse( get_role( 'shop_manager' )?->has_cap( DWS_LPMWC_CAP_UNLOCK_VIA_GROUPS ) ); + self::assertFalse( get_role( 'customer' )?->has_cap( DWS_LPMWC_CAP_UNLOCK_VIA_USER_PROFILES ) ); + } +} From 8d612134c0ab8176df5e6860f5b13436460f74b3 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sat, 31 Oct 2026 23:40:07 +0100 Subject: [PATCH 12/50] Grant Locked Payment Methods' capabilities through the framework v1 granted its four capabilities from a permissions component that checked users at boot, and removed them only on uninstall. includes/capabilities.php names them as v1 did and declares v1's grants, which core's CapabilityGrants applies whenever the declaration changes or a declared role appears: every capability to administrators, and the user-profile and order ones to shop managers. Each form checks its capability in its callback, so nothing checks a user at boot. X ruling 92. Assisted-by: Claude Code:claude-opus-5-5 --- .../includes/capabilities.php | 64 +++++++++++++++++++ .../src/Plugin.php | 3 + .../tests/Integration/PluginBootTest.php | 3 +- 3 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 plugins/locked-payment-methods-for-woocommerce/includes/capabilities.php diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/capabilities.php b/plugins/locked-payment-methods-for-woocommerce/includes/capabilities.php new file mode 100644 index 0000000..429bcd8 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/includes/capabilities.php @@ -0,0 +1,64 @@ +> + */ +const DWS_LPMWC_CAPABILITY_GRANTS = array( + 'administrator' => array( DWS_LPMWC_CAP_UNLOCK_VIA_USER_PROFILES, DWS_LPMWC_CAP_UNLOCK_VIA_ORDERS, DWS_LPMWC_CAP_UNLOCK_VIA_GROUPS, DWS_LPMWC_CAP_UNLOCK_VIA_WC_MEMBERSHIPS ), + 'shop_manager' => array( DWS_LPMWC_CAP_UNLOCK_VIA_USER_PROFILES, DWS_LPMWC_CAP_UNLOCK_VIA_ORDERS ), +); diff --git a/plugins/locked-payment-methods-for-woocommerce/src/Plugin.php b/plugins/locked-payment-methods-for-woocommerce/src/Plugin.php index 1ca2fdb..07edc9e 100644 --- a/plugins/locked-payment-methods-for-woocommerce/src/Plugin.php +++ b/plugins/locked-payment-methods-for-woocommerce/src/Plugin.php @@ -4,6 +4,7 @@ use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\AdminNotice\AdminNotices; use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\Migration\MigrationRunner; +use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\Permission\CapabilityGrants; use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\PluginBoot; use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\PluginKernel; use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\Scheduling\Scheduler; @@ -55,6 +56,7 @@ final class Plugin { public const array COMPONENTS = array( Scheduler::class, AdminNotices::class, + CapabilityGrants::class, Settings::class, ); @@ -210,6 +212,7 @@ protected function get_definitions( PsrLoggerInterface $logger, Scheduler $sched Scheduler::class => static fn (): Scheduler => $scheduler, SchedulerInterface::class => static fn (): Scheduler => $scheduler, AdminNotices::class => static fn (): AdminNotices => new AdminNotices( self::PREFIX, $logger ), + CapabilityGrants::class => static fn (): CapabilityGrants => new CapabilityGrants( self::PREFIX . '_capabilities', DWS_LPMWC_CAPABILITY_GRANTS, $logger ), ); } diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php index e49a57e..d0c16e2 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php @@ -5,6 +5,7 @@ use DeepWebSolutions\LockedPaymentMethods\Plugin; use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\AdminNotice\AdminNotices; use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\Migration\MigrationRunner; +use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\Permission\CapabilityGrants; use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\Scheduling\Scheduler; use DeepWebSolutions\LockedPaymentMethods\Settings; use PHPUnit\Framework\Attributes\CoversNothing; @@ -16,7 +17,7 @@ public function test_the_plugin_boots_every_component(): void { $plugin = dws_lpmwc_plugin(); self::assertSame( 'booted', $plugin->boot->status ); - self::assertSame( array( Scheduler::class, AdminNotices::class, Settings::class ), $plugin->kernel?->resolved ); + self::assertSame( array( Scheduler::class, AdminNotices::class, CapabilityGrants::class, Settings::class ), $plugin->kernel?->resolved ); self::assertSame( array(), $plugin->kernel->skipped ); } From 253a6def65637b81ede8de80099503eb6a904090 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sat, 31 Oct 2026 23:41:38 +0100 Subject: [PATCH 13/50] Test Locked Payment Methods' role strategy v1's role strategy unlocked every locked method for every visitor, guests included, while its roles list was empty, because v1's role helper answers true for an empty list before it looks the user up. UserRoleStrategyTest expects the strategy to load while its switch is on, as v1's was by default; to unlock every locked method only for a user holding a full-access role, never for a guest and never through an empty list; to leave a method another callback unlocked unlocked; to open the lock at checkout for such a user only; and to read the roles through v1's General value filter, fired as deprecated. X rulings 90 and 94. Assisted-by: Claude Code:claude-opus-5-5 --- .../UnlockStrategies/UserRoleStrategyTest.php | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/UserRoleStrategyTest.php diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/UserRoleStrategyTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/UserRoleStrategyTest.php new file mode 100644 index 0000000..a70741f --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/UserRoleStrategyTest.php @@ -0,0 +1,128 @@ + "lpm-roles-$role", + 'user_pass' => 'password', + 'user_email' => "lpm-roles-$role@example.org", + 'role' => $role, + ) + ); + } + } + + #[\Override] + public static function tearDownAfterClass(): void { + foreach ( self::$users as $user ) { + wp_delete_user( $user ); + } + } + + #[Before] + protected function store_rows(): void { + foreach ( self::KEYS as $key ) { + $this->stored[ $key ] = get_option( $key ); + } + update_option( DWS_LPMWC_OPTION_FULL_ACCESS_ROLES, array( 'shop_manager' ) ); + add_filter( 'deprecated_hook_trigger_error', '__return_false' ); + add_action( 'deprecated_hook_run', array( $this, 'record_deprecated_hook' ), 10, 2 ); + } + + #[After] + protected function restore_rows(): void { + foreach ( $this->stored as $key => $value ) { + false === $value ? delete_option( $key ) : update_option( $key, $value ); + } + remove_filter( 'deprecated_hook_trigger_error', '__return_false' ); + remove_action( 'deprecated_hook_run', array( $this, 'record_deprecated_hook' ) ); + wp_set_current_user( 0 ); + } + + public function record_deprecated_hook( string $hook, string $replacement ): void { + $this->deprecated[] = array( $hook, $replacement ); + } + + public function test_the_strategy_loads_while_its_switch_is_on_as_v1s_did_by_default(): void { + delete_option( DWS_LPMWC_OPTION_UNLOCK_VIA_USER_ROLES ); + self::assertTrue( UserRoleStrategy::should_load() ); + + update_option( DWS_LPMWC_OPTION_UNLOCK_VIA_USER_ROLES, 'no' ); + self::assertFalse( UserRoleStrategy::should_load() ); + + update_option( DWS_LPMWC_OPTION_UNLOCK_VIA_USER_ROLES, 'yes' ); + self::assertTrue( UserRoleStrategy::should_load() ); + } + + public function test_the_booted_plugin_runs_the_strategy(): void { + self::assertContains( UserRoleStrategy::class, dws_lpmwc_plugin()->kernel?->resolved ?? array() ); + } + + public function test_only_a_user_with_a_full_access_role_is_unlocked(): void { + $strategy = new UserRoleStrategy(); + + self::assertFalse( $strategy->unlock_for_full_access_roles( true, 'cod', self::$users['shop_manager'] ) ); + self::assertTrue( $strategy->unlock_for_full_access_roles( true, 'cod', self::$users['customer'] ) ); + self::assertTrue( $strategy->unlock_for_full_access_roles( true, 'cod', 0 ) ); + self::assertFalse( $strategy->unlock_for_full_access_roles( false, 'cod', self::$users['customer'] ) ); + } + + public function test_an_empty_roles_list_unlocks_no_one(): void { + update_option( DWS_LPMWC_OPTION_FULL_ACCESS_ROLES, array() ); + + self::assertTrue( ( new UserRoleStrategy() )->unlock_for_full_access_roles( true, 'cod', self::$users['shop_manager'] ) ); + } + + public function test_at_checkout_the_lock_opens_for_a_full_access_user_only(): void { + update_option( DWS_LPMWC_OPTION_LOCKED_METHODS, array( 'cod' ) ); + + wp_set_current_user( self::$users['shop_manager'] ); + self::assertArrayHasKey( 'cod', apply_filters( 'woocommerce_available_payment_gateways', WC()->payment_gateways()->payment_gateways() ) ); + + wp_set_current_user( self::$users['customer'] ); + self::assertArrayNotHasKey( 'cod', apply_filters( 'woocommerce_available_payment_gateways', WC()->payment_gateways()->payment_gateways() ) ); + } + + public function test_v1s_value_filter_on_the_roles_fires_as_deprecated_and_counts(): void { + $seen = array(); + $replace = static function ( mixed $value, string $field_id ) use ( &$seen ): array { + $seen[] = array( $value, $field_id ); + + return array( 'customer' ); + }; + add_filter( self::V1_HOOK, $replace, 10, 2 ); + + try { + $lock = ( new UserRoleStrategy() )->unlock_for_full_access_roles( true, 'cod', self::$users['customer'] ); + } finally { + remove_filter( self::V1_HOOK, $replace ); + } + + self::assertFalse( $lock ); + self::assertSame( array( array( array( 'shop_manager' ), 'full-access-user-roles' ) ), $seen ); + self::assertSame( array( array( self::V1_HOOK, 'option_' . DWS_LPMWC_OPTION_FULL_ACCESS_ROLES ) ), $this->deprecated ); + } +} From 7afa17f1b0e6b74d6d7d33ef9938179c12c52dae Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sat, 31 Oct 2026 23:43:04 +0100 Subject: [PATCH 14/50] Unlock every locked method for Locked Payment Methods' full-access roles v1's role strategy unlocked every locked method for every visitor, guests included, while its roles list was empty, because v1's role helper answers true for an empty list before it looks the user up. UserRoleStrategy is a component gated on its switch, on by default as v1's was. On dws_lpmwc/payment_method/should_lock it returns false for a user holding one of the full-access roles and leaves every other answer alone, so a guest or an empty list unlocks nothing. It reads the roles through v1's General value filter, which fires as deprecated and now lives beside the safeguard's own read of the locked list in includes/safeguards.php, the one file both can call. The main PHPStan profile scans that file, which it no longer analyzes. X rulings 90 and 94. Assisted-by: Claude Code:claude-opus-5-5 --- .../includes/safeguards.php | 27 ++++++-- .../phpstan.dist.neon | 1 + .../src/Plugin.php | 2 + .../src/UnlockStrategies/UserRoleStrategy.php | 69 +++++++++++++++++++ .../tests/Integration/PluginBootTest.php | 3 +- 5 files changed, 96 insertions(+), 6 deletions(-) create mode 100644 plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserRoleStrategy.php diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php b/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php index 8908145..a6ad992 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php @@ -38,17 +38,34 @@ function dws_lpmwc_get_locked_payment_method_ids(): array { $stored = get_option( 'dws_lpmwc_locked_payment_methods' ); $ids = dws_lpmwc_add_locked_ids( array(), false === $stored ? get_option( 'dws-wc-lpm_general_locked-payment-methods' ) : $stored ); + return dws_lpmwc_add_locked_ids( $ids, dws_lpmwc_apply_v1_setting_filter( $ids, 'general', 'locked-payment-methods', 'dws_lpmwc/payment_method/locked_ids' ) ); +} + +/** + * Returns a setting's value after v1's value filter for its settings group, which v1 applied on every read of a setting through that group. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param mixed $value The setting's value. + * @param string $group v1's id of the setting's group. + * @param string $field_id v1's id of the setting. + * @param string $replacement The hook that replaces v1's filter. + * + * @return mixed + */ +function dws_lpmwc_apply_v1_setting_filter( $value, string $group, string $field_id, string $replacement ) { /** - * Filters the locked payment methods' ids as v1's settings read them. + * Filters a setting's value as v1's settings group read it. * * @since 1.0.0 * @version 2.0.0 - * @deprecated 2.0.0 Use dws_lpmwc/payment_method/locked_ids. + * @deprecated 2.0.0 Use the hook the deprecation notice names. * - * @param string[] $ids The locked payment methods' ids. - * @param string $field_id v1's id of the setting. + * @param mixed $value The setting's value. + * @param string $field_id v1's id of the setting. */ - return dws_lpmwc_add_locked_ids( $ids, apply_filters_deprecated( 'locked_payment_methods_for_woocommerce/general_settings/get_validated_option_value', array( $ids, 'locked-payment-methods' ), '2.0.0', 'dws_lpmwc/payment_method/locked_ids' ) ); + return apply_filters_deprecated( "locked_payment_methods_for_woocommerce/{$group}_settings/get_validated_option_value", array( $value, $field_id ), '2.0.0', $replacement ); } /** diff --git a/plugins/locked-payment-methods-for-woocommerce/phpstan.dist.neon b/plugins/locked-payment-methods-for-woocommerce/phpstan.dist.neon index 4dc0f96..c47ac8a 100644 --- a/plugins/locked-payment-methods-for-woocommerce/phpstan.dist.neon +++ b/plugins/locked-payment-methods-for-woocommerce/phpstan.dist.neon @@ -16,6 +16,7 @@ parameters: excludePaths: - includes/safeguards.php scanFiles: + - includes/safeguards.php - vendor/php-stubs/woocommerce-stubs/woocommerce-stubs.php - vendor/php-stubs/woocommerce-stubs/woocommerce-packages-stubs.php # The scoped packages load through vendor-prefixed/scoper-autoload.php, which PHPStan does not read. diff --git a/plugins/locked-payment-methods-for-woocommerce/src/Plugin.php b/plugins/locked-payment-methods-for-woocommerce/src/Plugin.php index 07edc9e..8757b54 100644 --- a/plugins/locked-payment-methods-for-woocommerce/src/Plugin.php +++ b/plugins/locked-payment-methods-for-woocommerce/src/Plugin.php @@ -13,6 +13,7 @@ use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\WooCommerce\WooCommerceRequirement; use DeepWebSolutions\LockedPaymentMethods\Scoped\DI\ContainerBuilder; use DeepWebSolutions\LockedPaymentMethods\Scoped\Psr\Log\LoggerInterface as PsrLoggerInterface; +use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\UserRoleStrategy; \defined( 'ABSPATH' ) || exit; @@ -58,6 +59,7 @@ final class Plugin { AdminNotices::class, CapabilityGrants::class, Settings::class, + UserRoleStrategy::class, ); /** diff --git a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserRoleStrategy.php b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserRoleStrategy.php new file mode 100644 index 0000000..91c4a32 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserRoleStrategy.php @@ -0,0 +1,69 @@ +roles, \array_filter( $roles, 'is_string' ) ) ? false : $lock; + } + + // endregion +} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php index d0c16e2..fe4b058 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php @@ -8,6 +8,7 @@ use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\Permission\CapabilityGrants; use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\Scheduling\Scheduler; use DeepWebSolutions\LockedPaymentMethods\Settings; +use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\UserRoleStrategy; use PHPUnit\Framework\Attributes\CoversNothing; use PHPUnit\Framework\TestCase; @@ -17,7 +18,7 @@ public function test_the_plugin_boots_every_component(): void { $plugin = dws_lpmwc_plugin(); self::assertSame( 'booted', $plugin->boot->status ); - self::assertSame( array( Scheduler::class, AdminNotices::class, CapabilityGrants::class, Settings::class ), $plugin->kernel?->resolved ); + self::assertSame( array( Scheduler::class, AdminNotices::class, CapabilityGrants::class, Settings::class, UserRoleStrategy::class ), $plugin->kernel?->resolved ); self::assertSame( array(), $plugin->kernel->skipped ); } From ba7a75b818c7cf4da485a0c0a569ba334c3622dc Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sat, 31 Oct 2026 23:47:00 +0100 Subject: [PATCH 15/50] Test Locked Payment Methods' user grants and their move from v1 v1 stored a user's grants as global user meta, one row per gateway, so a grant on one store of a network applied on every store; its Users column showed icons without text, its filter printed unescaped markup and read only the top dropdown, and its profile section saved nothing for a method outside the locked list. UserProfileStrategyTest expects the strategy to load while its switch is on, as v1's was by default; to unlock only the methods a user's per-site list holds, ignoring the global key; to offer on the profile the locked methods and the user's other grants, marking a grant outside the list and a method another rule already unlocks; to show nothing without the capability or anything to offer; to store the checked methods and keep the grants a save names wrongly; and to list and filter users by their grants in text, from either dropdown. MigrationsTest expects the grants step to move v1's 'yes' rows into the site's list beside its own grants, to delete every v1 grant row, to converge when run again, and to throw, keeping v1's rows, when a write fails. X ruling 90. Assisted-by: Claude Code:claude-opus-5-5 --- .../tests/Integration/MigrationsTest.php | 65 ++++- .../UserProfileStrategyTest.php | 257 ++++++++++++++++++ 2 files changed, 320 insertions(+), 2 deletions(-) create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/UserProfileStrategyTest.php diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/MigrationsTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/MigrationsTest.php index f8fb303..45475dc 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/MigrationsTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/MigrationsTest.php @@ -53,7 +53,7 @@ protected function restore_rows(): void { // The runner's time budget lasts for its request, and this process's bootstrap spends it when it migrates a new site, so the row runs as a request of its own. #[RunInSeparateProcess] - public function test_the_runner_brings_a_new_site_to_revision_one_with_every_default_autoloaded(): void { + public function test_the_runner_brings_a_new_site_to_the_latest_revision_with_every_default_autoloaded(): void { global $wpdb; foreach ( array( ...self::KEYS, ...self::V1_KEYS ) as $key ) { @@ -63,7 +63,7 @@ public function test_the_runner_brings_a_new_site_to_revision_one_with_every_def dws_lpmwc_plugin()->migrations?->run(); - self::assertSame( 1, MigrationRunner::get_revision( Plugin::PREFIX . '_revision' ) ); + self::assertSame( \count( dws_lpmwc_get_migration_steps() ), MigrationRunner::get_revision( Plugin::PREFIX . '_revision' ) ); self::assertSame( array( DWS_LPMWC_OPTION_LOCKED_METHODS => array(), @@ -158,6 +158,45 @@ public function test_v1s_settings_map_onto_the_plugins_settings_one_to_one(): vo self::assertEqualsCanonicalizing( self::KEYS, \array_keys( DWS_LPMWC_OPTION_DEFAULTS ) ); } + public function test_the_grants_step_moves_v1s_user_grants_into_the_sites_lists_and_deletes_every_v1_grant_row(): void { + global $wpdb; + + $key = $wpdb->get_blog_prefix() . DWS_LPMWC_USER_OPTION_UNLOCKED_METHODS; + $users = self::create_users( 2 ); + update_user_meta( $users[0], 'dws_wc_lpm_grant_access_cod', 'yes' ); + update_user_meta( $users[0], 'dws_wc_lpm_grant_access_bacs', 'no' ); + update_user_meta( $users[1], 'dws_wc_lpm_grant_access_cod', 'yes' ); + update_user_meta( $users[1], $key, array( 'cheque' ) ); + + try { + dws_lpmwc_move_user_grants(); + dws_lpmwc_move_user_grants(); + wp_cache_flush(); + + self::assertSame( array( 'cod' ), get_user_meta( $users[0], $key, true ) ); + self::assertSame( array( 'cheque', 'cod' ), get_user_meta( $users[1], $key, true ) ); + self::assertSame( '0', $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->usermeta} WHERE meta_key LIKE %s", 'dws\_wc\_lpm\_grant\_access\_%' ) ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- Counts the rows the step deletes. + } finally { + self::delete_users( $users ); + } + } + + public function test_the_grants_step_throws_when_a_users_grants_cannot_be_stored(): void { + $users = self::create_users( 1 ); + update_user_meta( $users[0], 'dws_wc_lpm_grant_access_cod', 'yes' ); + $fail = static fn ( mixed $check, int $object_id, string $meta_key ): mixed => \str_ends_with( $meta_key, DWS_LPMWC_USER_OPTION_UNLOCKED_METHODS ) ? false : $check; + add_filter( 'update_user_metadata', $fail, 10, 3 ); + + try { + $this->expectExceptionObject( new \RuntimeException( "The payment methods unlocked for user {$users[0]} could not be stored." ) ); + dws_lpmwc_move_user_grants(); + } finally { + remove_filter( 'update_user_metadata', $fail ); + self::assertSame( 'yes', get_user_meta( $users[0], 'dws_wc_lpm_grant_access_cod', true ) ); + self::delete_users( $users ); + } + } + public function test_the_step_throws_when_a_setting_cannot_be_stored(): void { global $wpdb; @@ -173,4 +212,26 @@ public function test_the_step_throws_when_a_setting_cannot_be_stored(): void { $wpdb->suppress_errors( $suppressed ); } } + + protected static function create_users( int $count ): array { + $users = array(); + for ( $i = 0; $i < $count; $i++ ) { + $users[] = (int) wp_insert_user( + array( + 'user_login' => "lpm-migrations-$i", + 'user_pass' => 'password', + 'user_email' => "lpm-migrations-$i@example.org", + ) + ); + } + + return $users; + } + + protected static function delete_users( array $users ): void { + require_once ABSPATH . 'wp-admin/includes/user.php'; + foreach ( $users as $user ) { + wp_delete_user( $user ); + } + } } diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/UserProfileStrategyTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/UserProfileStrategyTest.php new file mode 100644 index 0000000..19d9cd5 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/UserProfileStrategyTest.php @@ -0,0 +1,257 @@ + "lpm-profiles-$role", + 'user_pass' => 'password', + 'user_email' => "lpm-profiles-$role@example.org", + 'role' => $role, + ) + ); + } + get_userdata( self::$users['editor'] )?->add_cap( 'edit_users' ); + } + + #[\Override] + public static function tearDownAfterClass(): void { + foreach ( self::$users as $user ) { + wp_delete_user( $user ); + } + } + + #[Before] + protected function store_rows(): void { + foreach ( self::KEYS as $key ) { + $this->stored[ $key ] = get_option( $key ); + } + update_option( DWS_LPMWC_OPTION_LOCKED_METHODS, array( 'cod', 'bacs' ) ); + update_option( DWS_LPMWC_OPTION_FULL_ACCESS_ROLES, array( 'shop_manager' ) ); + } + + #[After] + protected function restore_rows(): void { + foreach ( $this->stored as $key => $value ) { + false === $value ? delete_option( $key ) : update_option( $key, $value ); + } + foreach ( self::$users as $user ) { + delete_user_meta( $user, self::key() ); + delete_user_meta( $user, DWS_LPMWC_USER_OPTION_UNLOCKED_METHODS ); + } + delete_option( 'dws_lpmwc_notices' ); + unset( $_POST[ self::FORM ], $_POST[ self::FORM . '_nonce' ], $_GET['dws_lpmwc_unlocked_for_top'], $_GET['dws_lpmwc_unlocked_for_bottom'], $_GET['bottom'] ); + $GLOBALS['pagenow'] = 'index.php'; + wp_set_current_user( 0 ); + } + + public function test_the_strategy_loads_while_its_switch_is_on_as_v1s_did_by_default(): void { + delete_option( DWS_LPMWC_OPTION_UNLOCK_VIA_USER_PROFILES ); + self::assertTrue( UserProfileStrategy::should_load() ); + + update_option( DWS_LPMWC_OPTION_UNLOCK_VIA_USER_PROFILES, 'no' ); + self::assertFalse( UserProfileStrategy::should_load() ); + } + + public function test_the_booted_plugin_runs_the_strategy(): void { + self::assertContains( UserProfileStrategy::class, dws_lpmwc_plugin()->kernel?->resolved ?? array() ); + } + + public function test_a_user_is_unlocked_only_for_the_methods_their_site_grants_list_holds(): void { + update_user_meta( self::$users['customer'], self::key(), array( 'cod' ) ); + update_user_meta( self::$users['editor'], DWS_LPMWC_USER_OPTION_UNLOCKED_METHODS, array( 'cod' ) ); + + self::assertSame( array( 'cod' ), dws_lpmwc_get_user_unlocked_payment_method_ids( self::$users['customer'] ) ); + self::assertFalse( self::should_lock( 'cod', self::$users['customer'] ) ); + self::assertTrue( self::should_lock( 'bacs', self::$users['customer'] ) ); + self::assertTrue( self::should_lock( 'cod', self::$users['editor'] ) ); + self::assertTrue( self::should_lock( 'cod', 0 ) ); + } + + public function test_a_stored_grants_value_that_is_not_a_list_unlocks_nothing(): void { + update_user_meta( self::$users['customer'], self::key(), 'cod' ); + + self::assertSame( array(), dws_lpmwc_get_user_unlocked_payment_method_ids( self::$users['customer'] ) ); + } + + public function test_at_checkout_a_customer_may_use_the_methods_granted_to_them(): void { + update_user_meta( self::$users['customer'], self::key(), array( 'cod' ) ); + wp_set_current_user( self::$users['customer'] ); + + $available = apply_filters( 'woocommerce_available_payment_gateways', WC()->payment_gateways()->payment_gateways() ); + + self::assertArrayHasKey( 'cod', $available ); + self::assertArrayNotHasKey( 'bacs', $available ); + } + + public function test_the_profile_offers_the_locked_methods_and_the_users_other_grants_and_marks_each_one(): void { + update_user_meta( self::$users['customer'], self::key(), array( 'cod', 'cheque' ) ); + wp_set_current_user( self::$users['administrator'] ); + + $form = self::render( self::$users['customer'] ); + + self::assertSame( + array( + 'cod' => array( 'Cash on delivery', true ), + 'bacs' => array( 'Direct bank transfer', false ), + 'cheque' => array( 'Check payments (not locked)', true ), + ), + self::choices( $form ) + ); + self::assertSame( 'Locked Payment Methods', \trim( (string) $form->querySelector( 'h2' )?->textContent ) ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- A DOM property. + } + + public function test_the_profile_marks_the_methods_another_rule_already_unlocks_for_the_user(): void { + wp_set_current_user( self::$users['administrator'] ); + + self::assertSame( + array( + 'cod' => array( 'Cash on delivery (unlocked by another rule)', false ), + 'bacs' => array( 'Direct bank transfer (unlocked by another rule)', false ), + ), + self::choices( self::render( self::$users['shop_manager'] ) ) + ); + } + + public function test_the_profile_shows_nothing_without_the_capability_or_with_nothing_to_offer(): void { + wp_set_current_user( self::$users['editor'] ); + self::assertNull( self::render( self::$users['customer'] )->querySelector( 'input' ) ); + + wp_set_current_user( self::$users['administrator'] ); + update_option( DWS_LPMWC_OPTION_LOCKED_METHODS, array() ); + self::assertNull( self::render( self::$users['customer'] )->querySelector( 'input' ) ); + } + + public function test_saving_the_profile_stores_the_checked_methods_in_the_sites_grants_list(): void { + wp_set_current_user( self::$users['administrator'] ); + + self::save( self::$users['customer'], array( 'bacs' ) ); + self::assertSame( array( 'bacs' ), get_user_meta( self::$users['customer'], self::key(), true ) ); + self::assertSame( '', get_user_meta( self::$users['customer'], DWS_LPMWC_USER_OPTION_UNLOCKED_METHODS, true ) ); + + self::save( self::$users['customer'], '' ); + self::assertSame( array(), get_user_meta( self::$users['customer'], self::key(), true ) ); + } + + public function test_a_save_naming_a_method_the_profile_does_not_offer_keeps_the_stored_grants(): void { + update_user_meta( self::$users['customer'], self::key(), array( 'cod' ) ); + wp_set_current_user( self::$users['administrator'] ); + + self::save( self::$users['customer'], array( 'bacs', 'paypal' ) ); + + self::assertSame( array( 'cod' ), get_user_meta( self::$users['customer'], self::key(), true ) ); + } + + public function test_a_save_without_the_capability_stores_nothing(): void { + wp_set_current_user( self::$users['editor'] ); + + self::save( self::$users['customer'], array( 'bacs' ) ); + + self::assertSame( '', get_user_meta( self::$users['customer'], self::key(), true ) ); + } + + public function test_the_users_screen_lists_each_users_locked_methods_with_their_state_in_text(): void { + update_user_meta( self::$users['customer'], self::key(), array( 'cod' ) ); + + $columns = apply_filters( + 'manage_users_columns', + array( + 'username' => 'Username', + 'role' => 'Role', + 'posts' => 'Posts', + ) + ); + self::assertSame( array( 'username', 'role', 'dws_lpmwc_unlocked_for', 'posts' ), \array_keys( $columns ) ); + self::assertSame( 'Unlocked for', $columns['dws_lpmwc_unlocked_for'] ); + + self::assertSame( 'Cash on delivery: unlocked on the profile
Direct bank transfer: locked', apply_filters( 'manage_users_custom_column', '', 'dws_lpmwc_unlocked_for', self::$users['customer'] ) ); + self::assertSame( 'Cash on delivery: unlocked by another rule
Direct bank transfer: unlocked by another rule', apply_filters( 'manage_users_custom_column', '', 'dws_lpmwc_unlocked_for', self::$users['shop_manager'] ) ); + self::assertSame( 'other', apply_filters( 'manage_users_custom_column', 'other', 'posts', self::$users['customer'] ) ); + } + + public function test_the_users_screen_filters_by_a_method_granted_on_the_profile_from_either_dropdown(): void { + update_user_meta( self::$users['customer'], self::key(), array( 'cod' ) ); + update_user_meta( self::$users['editor'], self::key(), array( 'bacs' ) ); + $GLOBALS['pagenow'] = 'users.php'; + set_current_screen( 'users' ); + + \ob_start(); + do_action( 'restrict_manage_users', 'top' ); + $select = \Dom\HTMLDocument::createFromString( '' . \ob_get_clean() . '', \LIBXML_NOERROR ); + self::assertSame( array( '', 'cod', 'bacs' ), \array_map( static fn ( \Dom\Element $option ): string => (string) $option->getAttribute( 'value' ), \iterator_to_array( $select->querySelectorAll( 'select[name="dws_lpmwc_unlocked_for_top"] option' ) ) ) ); + + $_GET['dws_lpmwc_unlocked_for_top'] = 'cod'; + self::assertSame( array( self::$users['customer'] ), self::listed_users() ); + + $_GET['dws_lpmwc_unlocked_for_bottom'] = 'bacs'; + $_GET['bottom'] = 'Filter'; + self::assertSame( array( self::$users['editor'] ), self::listed_users() ); + } + + protected static function key(): string { + global $wpdb; + + return $wpdb->get_blog_prefix() . DWS_LPMWC_USER_OPTION_UNLOCKED_METHODS; + } + + protected static function should_lock( string $gateway_id, int $user_id ): mixed { + return apply_filters( 'dws_lpmwc/payment_method/should_lock', true, $gateway_id, $user_id, 0 ); + } + + protected static function render( int $user_id ): \Dom\HTMLDocument { + \ob_start(); + do_action( 'edit_user_profile', get_userdata( $user_id ) ); + + return \Dom\HTMLDocument::createFromString( '' . \ob_get_clean() . '', \LIBXML_NOERROR ); + } + + protected static function choices( \Dom\HTMLDocument $form ): array { + $choices = array(); + foreach ( $form->querySelectorAll( 'input[type="checkbox"]' ) as $input ) { + $choices[ (string) $input->getAttribute( 'value' ) ] = array( \trim( (string) $input->parentElement?->textContent ), $input->hasAttribute( 'checked' ) ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- DOM properties. + } + + return $choices; + } + + protected static function save( int $user_id, array|string $methods ): void { + $_POST[ self::FORM ] = array( self::key() => $methods ); + $_POST[ self::FORM . '_nonce' ] = wp_create_nonce( self::FORM . "_nonce_$user_id" ); + do_action( 'edit_user_profile_update', $user_id ); + wp_cache_flush(); + } + + protected static function listed_users(): array { + $query = new \WP_User_Query( + array( + 'include' => \array_values( self::$users ), + 'fields' => 'ID', + 'orderby' => 'ID', + ) + ); + + return \array_map( 'intval', $query->get_results() ); + } +} From c563afba9b2e9bd1a651a3a76f22c995c9810df2 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sat, 31 Oct 2026 23:52:23 +0100 Subject: [PATCH 16/50] Unlock payment methods per user from Locked Payment Methods' profiles v1 stored a user's grants as global user meta, one row per gateway, so a grant on one store of a network applied on every store; its Users column showed icons without text, its filter printed unescaped markup and read only the top dropdown, and its profile section saved nothing for a method outside the locked list. A user's grants are one per-site user option, the list of unlocked payment method ids under the site's table prefix, read from that key alone. UserProfileStrategy, gated on its switch, returns false on should_lock for a granted method, and edits the list on the profile through the framework's UserProfile host and an ObjectForm with one checkboxes field, behind unlock_dws_lpm_via_user_profiles and edit_user. The field offers the locked methods and the user's other grants, since a save keeps only offered choices, and marks a grant outside the list and a method another rule already unlocks. The Users screen gains a column naming each locked method's state in text and a filter by grant, read from the dropdown whose button submitted the list. The second migration step moves v1's 'yes' rows into every site's list beside its own grants, keeping v1's reach, then deletes every v1 grant row; a partial run converges on the next. X ruling 90. Assisted-by: Claude Code:claude-opus-5-5 --- .../includes/grants.php | 53 ++++ .../includes/migrations.php | 64 ++++ .../src/Plugin.php | 2 + .../UnlockStrategies/UserProfileStrategy.php | 292 ++++++++++++++++++ .../tests/Integration/PluginBootTest.php | 3 +- .../UserProfileStrategyTest.php | 46 ++- 6 files changed, 447 insertions(+), 13 deletions(-) create mode 100644 plugins/locked-payment-methods-for-woocommerce/includes/grants.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserProfileStrategy.php diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/grants.php b/plugins/locked-payment-methods-for-woocommerce/includes/grants.php new file mode 100644 index 0000000..d4730ee --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/includes/grants.php @@ -0,0 +1,53 @@ +get_blog_prefix() . DWS_LPMWC_USER_OPTION_UNLOCKED_METHODS; +} + +/** + * Returns the ids of the payment methods unlocked for a user on the current site, read from the site's own key, since get_user_option() falls back to a key every site shares. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param int $user_id The user's id. + * + * @return string[] + */ +function dws_lpmwc_get_user_unlocked_payment_method_ids( int $user_id ): array { + $ids = get_user_meta( $user_id, dws_lpmwc_get_user_grants_key(), true ); + + return is_array( $ids ) ? array_values( array_filter( $ids, 'is_string' ) ) : array(); +} diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php b/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php index 7cde3e0..296f24c 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php @@ -21,6 +21,7 @@ function dws_lpmwc_get_migration_steps(): array { return array( 'dws_lpmwc_add_settings', + 'dws_lpmwc_move_user_grants', ); } @@ -55,3 +56,66 @@ function dws_lpmwc_add_settings(): void { } } } + +/** + * Moves v1's user grants, one global 'yes' row per payment method, into every site's list beside the grants it holds, so they keep v1's reach across a network, then deletes every v1 grant row; a run that stops part way converges on the next, since each list keeps what an earlier run merged. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @global wpdb $wpdb WordPress's database access. + * + * @throws RuntimeException Thrown when v1's grants could not be read, a user's grants could not be stored or a v1 grant could not be deleted. + * + * @return void + */ +function dws_lpmwc_move_user_grants(): void { + global $wpdb; + + $prefix = 'dws_wc_lpm_grant_access_'; + $wpdb->flush(); + $rows = $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT user_id, meta_key, meta_value FROM %i WHERE meta_key LIKE %s', $wpdb->usermeta, $wpdb->esc_like( $prefix ) . '%' ) ?? '', ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- v1's keys carry the payment method's id, so no metadata API lists them. + if ( '' !== $wpdb->last_error ) { + throw new RuntimeException( 'The v1 grants could not be read.' ); + } + + $grants = array(); + $keys = array(); + foreach ( (array) $rows as $row ) { + // The column's collation also matches keys that differ from v1's in letter case. + if ( ! is_array( $row ) || ! is_numeric( $row['user_id'] ?? null ) || ! is_string( $row['meta_key'] ?? null ) || ! str_starts_with( $row['meta_key'], $prefix ) ) { + continue; + } + + $user_id = (int) $row['user_id']; + $keys[ $user_id ][ $row['meta_key'] ] = true; + if ( 'yes' === ( $row['meta_value'] ?? null ) ) { + $grants[ $user_id ][] = substr( $row['meta_key'], strlen( $prefix ) ); + } + } + + $sites = is_multisite() ? get_sites( + array( + 'fields' => 'ids', + 'number' => 0, + ) + ) : array( get_current_blog_id() ); + foreach ( $grants as $user_id => $ids ) { + foreach ( $sites as $site_id ) { + $key = $wpdb->get_blog_prefix( $site_id ) . DWS_LPMWC_USER_OPTION_UNLOCKED_METHODS; + $current = get_user_meta( $user_id, $key, true ); + $merged = array_values( array_unique( array_merge( is_array( $current ) ? array_filter( $current, 'is_string' ) : array(), $ids ) ) ); + if ( $merged !== $current && false === update_user_meta( $user_id, $key, wp_slash( $merged ) ) ) { + throw new RuntimeException( "The payment methods unlocked for user $user_id could not be stored." ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing, never rendered. + } + } + } + + foreach ( $keys as $user_id => $user_keys ) { + foreach ( array_keys( $user_keys ) as $meta_key ) { + if ( ! delete_user_meta( $user_id, $meta_key ) ) { + throw new RuntimeException( "The v1 grant $meta_key of user $user_id could not be deleted." ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing, never rendered. + } + } + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/src/Plugin.php b/plugins/locked-payment-methods-for-woocommerce/src/Plugin.php index 8757b54..7f24492 100644 --- a/plugins/locked-payment-methods-for-woocommerce/src/Plugin.php +++ b/plugins/locked-payment-methods-for-woocommerce/src/Plugin.php @@ -13,6 +13,7 @@ use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\WooCommerce\WooCommerceRequirement; use DeepWebSolutions\LockedPaymentMethods\Scoped\DI\ContainerBuilder; use DeepWebSolutions\LockedPaymentMethods\Scoped\Psr\Log\LoggerInterface as PsrLoggerInterface; +use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\UserProfileStrategy; use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\UserRoleStrategy; \defined( 'ABSPATH' ) || exit; @@ -60,6 +61,7 @@ final class Plugin { CapabilityGrants::class, Settings::class, UserRoleStrategy::class, + UserProfileStrategy::class, ); /** diff --git a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserProfileStrategy.php b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserProfileStrategy.php new file mode 100644 index 0000000..3be8f31 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserProfileStrategy.php @@ -0,0 +1,292 @@ +notices )->register_hooks(); + add_filter( 'manage_users_columns', array( $this, 'add_users_column' ) ); + add_filter( 'manage_users_custom_column', array( $this, 'render_users_column' ), 10, 3 ); + add_action( 'restrict_manage_users', array( $this, 'render_users_filter' ) ); + add_action( 'pre_get_users', array( $this, 'filter_users' ) ); + } + + /** + * {@inheritDoc} + * + * @since 2.0.0 + * @version 2.0.0 + */ + #[\Override] + public function get_object_form( string $form_id, int $object_id ): array { + $granted = dws_lpmwc_get_user_unlocked_payment_method_ids( $object_id ); + $locked = dws_lpmwc_get_locked_payment_method_ids(); + + // A save keeps only the choices the field offers, so the user's grants outside the locked list are offered too. + $options = array(); + foreach ( \array_unique( \array_merge( $locked, $granted ) ) as $id ) { + $options[ $id ] = match ( true ) { + /* translators: %s: payment method title. */ + ! \in_array( $id, $locked, true ) => \sprintf( __( '%s (not locked)', 'locked-payment-methods-for-woocommerce' ), $this->get_title( $id ) ), + /* translators: %s: payment method title. */ + ! \in_array( $id, $granted, true ) && ! dws_lpmwc_should_lock( true, $id, $object_id, 0 ) => \sprintf( __( '%s (unlocked by another rule)', 'locked-payment-methods-for-woocommerce' ), $this->get_title( $id ) ), + default => $this->get_title( $id ), + }; + } + + return array( + 'title' => __( 'Locked Payment Methods', 'locked-payment-methods-for-woocommerce' ), + 'capability' => DWS_LPMWC_CAP_UNLOCK_VIA_USER_PROFILES, + 'fields' => array() === $options ? array() : array( + array( + 'type' => 'checkboxes', + 'id' => dws_lpmwc_get_user_grants_key(), + 'title' => __( 'Unlocked payment methods', 'locked-payment-methods-for-woocommerce' ), + 'desc' => __( 'The user may pay with these methods for all their future orders and their unpaid orders.', 'locked-payment-methods-for-woocommerce' ), + 'options' => $options, + ), + ), + ); + } + + // endregion + + // region HOOKS + + /** + * Returns false for a payment method the user's grants list holds, and the lock as given otherwise. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param bool $lock Whether the payment method stays locked. + * @param string $gateway_id The payment method's id. + * @param int $user_id The paying user, or 0 for a guest. + * + * @return bool + */ + public function unlock_granted_methods( bool $lock, string $gateway_id, int $user_id ): bool { + return \in_array( $gateway_id, dws_lpmwc_get_user_unlocked_payment_method_ids( $user_id ), true ) ? false : $lock; + } + + /** + * Adds the column of the locked payment methods' states after the Users screen's role column. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param array $columns The screen's column titles, by column id. + * + * @return array + */ + public function add_users_column( array $columns ): array { + $position = \array_search( 'role', \array_keys( $columns ), true ); + $position = false === $position ? \count( $columns ) : $position + 1; + + return \array_slice( $columns, 0, $position, true ) + array( self::COLUMN => __( 'Unlocked for', 'locked-payment-methods-for-woocommerce' ) ) + \array_slice( $columns, $position, null, true ); + } + + /** + * Returns a user's cell of the column, one line per locked payment method naming its state for the user. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string $output The cell's markup so far. + * @param string $column The column's id. + * @param int $user_id The user's id. + * + * @return string + */ + public function render_users_column( string $output, string $column, int $user_id ): string { + if ( self::COLUMN !== $column ) { + return $output; + } + + $granted = dws_lpmwc_get_user_unlocked_payment_method_ids( $user_id ); + $lines = array(); + foreach ( dws_lpmwc_get_locked_payment_method_ids() as $id ) { + $state = match ( true ) { + \in_array( $id, $granted, true ) => __( 'unlocked on the profile', 'locked-payment-methods-for-woocommerce' ), + ! dws_lpmwc_should_lock( true, $id, $user_id, 0 ) => __( 'unlocked by another rule', 'locked-payment-methods-for-woocommerce' ), + default => __( 'locked', 'locked-payment-methods-for-woocommerce' ), + }; + /* translators: 1: payment method title, 2: the payment method's state for the user. */ + $lines[] = esc_html( \sprintf( __( '%1$s: %2$s', 'locked-payment-methods-for-woocommerce' ), $this->get_title( $id ), $state ) ); + } + + return \implode( '
', $lines ); + } + + /** + * Prints the dropdown that filters the Users screen by a payment method unlocked on the profile, above and below the list. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string $which Where the dropdown prints, 'top' or 'bottom'. + * + * @return void + */ + public function render_users_filter( string $which ): void { + $locked = dws_lpmwc_get_locked_payment_method_ids(); + if ( array() === $locked ) { + return; + } + + $name = self::COLUMN . "_$which"; + $active = $this->get_active_filter(); + \printf( ''; + submit_button( _x( 'Filter', 'users screen', 'locked-payment-methods-for-woocommerce' ), '', $which, false, array( 'id' => "{$name}_submit" ) ); + } + + /** + * Limits the Users screen's list to the users holding the filtered payment method on the profile. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @global string $pagenow The admin screen's file. + * + * @param \WP_User_Query $query The users query. + * + * @return void + */ + public function filter_users( \WP_User_Query $query ): void { + global $pagenow; + + $active = 'users.php' === $pagenow && is_admin() ? $this->get_active_filter() : ''; + if ( '' === $active ) { + return; + } + + $meta_query = $query->get( 'meta_query' ); + $meta_query = \is_array( $meta_query ) ? $meta_query : array(); + $meta_query[] = array( + 'key' => dws_lpmwc_get_user_grants_key(), + 'value' => '"' . $active . '"', // Matches the id as one element of the stored, serialized list. + 'compare' => 'LIKE', + ); + $query->set( 'meta_query', $meta_query ); + } + + // endregion + + // region HELPERS + + /** + * Returns a payment method's title, or its id for a payment method no longer registered. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string $gateway_id The payment method's id. + * + * @return string + */ + protected function get_title( string $gateway_id ): string { + $gateway = WC()->payment_gateways()->payment_gateways()[ $gateway_id ] ?? null; + + return $gateway instanceof \WC_Payment_Gateway ? $gateway->get_title() : $gateway_id; + } + + /** + * Returns the locked payment method the Users screen filters by, from the dropdown whose button submitted the list, or an empty string. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @return string + */ + protected function get_active_filter(): string { + // phpcs:disable WordPress.Security.NonceVerification.Recommended -- A list filter, which changes nothing. + $name = self::COLUMN . ( isset( $_GET['bottom'] ) ? '_bottom' : '_top' ); + $value = isset( $_GET[ $name ] ) && \is_string( $_GET[ $name ] ) ? sanitize_text_field( wp_unslash( $_GET[ $name ] ) ) : ''; + // phpcs:enable WordPress.Security.NonceVerification.Recommended + + return \in_array( $value, dws_lpmwc_get_locked_payment_method_ids(), true ) ? $value : ''; + } + + // endregion +} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php index fe4b058..123badb 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php @@ -8,6 +8,7 @@ use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\Permission\CapabilityGrants; use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\Scheduling\Scheduler; use DeepWebSolutions\LockedPaymentMethods\Settings; +use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\UserProfileStrategy; use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\UserRoleStrategy; use PHPUnit\Framework\Attributes\CoversNothing; use PHPUnit\Framework\TestCase; @@ -18,7 +19,7 @@ public function test_the_plugin_boots_every_component(): void { $plugin = dws_lpmwc_plugin(); self::assertSame( 'booted', $plugin->boot->status ); - self::assertSame( array( Scheduler::class, AdminNotices::class, CapabilityGrants::class, Settings::class, UserRoleStrategy::class ), $plugin->kernel?->resolved ); + self::assertSame( array( Scheduler::class, AdminNotices::class, CapabilityGrants::class, Settings::class, UserRoleStrategy::class, UserProfileStrategy::class ), $plugin->kernel?->resolved ); self::assertSame( array(), $plugin->kernel->skipped ); } diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/UserProfileStrategyTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/UserProfileStrategyTest.php index 19d9cd5..a64d86a 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/UserProfileStrategyTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/UserProfileStrategyTest.php @@ -21,17 +21,11 @@ final class UserProfileStrategyTest extends TestCase { #[\Override] public static function setUpBeforeClass(): void { require_once ABSPATH . 'wp-admin/includes/user.php'; - require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php'; - require_once ABSPATH . 'wp-admin/includes/class-wp-users-list-table.php'; - foreach ( array( 'administrator', 'shop_manager', 'customer', 'editor' ) as $role ) { - self::$users[ $role ] = (int) wp_insert_user( - array( - 'user_login' => "lpm-profiles-$role", - 'user_pass' => 'password', - 'user_email' => "lpm-profiles-$role@example.org", - 'role' => $role, - ) - ); + require_once ABSPATH . 'wp-admin/includes/template.php'; + require_once ABSPATH . 'wp-admin/includes/class-wp-screen.php'; + require_once ABSPATH . 'wp-admin/includes/screen.php'; + foreach ( array( 'administrator', 'shop_manager', 'editor' ) as $role ) { + self::$users[ $role ] = self::create_user( $role ); } get_userdata( self::$users['editor'] )?->add_cap( 'edit_users' ); } @@ -43,8 +37,10 @@ public static function tearDownAfterClass(): void { } } + // The form evaluates its definition and saves an object once per request, so every row edits a customer of its own. #[Before] protected function store_rows(): void { + self::$users['customer'] = self::create_user( 'customer' ); foreach ( self::KEYS as $key ) { $this->stored[ $key ] = get_option( $key ); } @@ -57,6 +53,7 @@ protected function restore_rows(): void { foreach ( $this->stored as $key => $value ) { false === $value ? delete_option( $key ) : update_option( $key, $value ); } + wp_delete_user( self::$users['customer'] ); foreach ( self::$users as $user ) { delete_user_meta( $user, self::key() ); delete_user_meta( $user, DWS_LPMWC_USER_OPTION_UNLOCKED_METHODS ); @@ -64,6 +61,7 @@ protected function restore_rows(): void { delete_option( 'dws_lpmwc_notices' ); unset( $_POST[ self::FORM ], $_POST[ self::FORM . '_nonce' ], $_GET['dws_lpmwc_unlocked_for_top'], $_GET['dws_lpmwc_unlocked_for_bottom'], $_GET['bottom'] ); $GLOBALS['pagenow'] = 'index.php'; + unset( $GLOBALS['current_screen'] ); wp_set_current_user( 0 ); } @@ -135,12 +133,16 @@ public function test_the_profile_marks_the_methods_another_rule_already_unlocks_ ); } - public function test_the_profile_shows_nothing_without_the_capability_or_with_nothing_to_offer(): void { + public function test_the_profile_shows_nothing_to_a_user_without_the_capability(): void { wp_set_current_user( self::$users['editor'] ); + self::assertNull( self::render( self::$users['customer'] )->querySelector( 'input' ) ); + } + public function test_the_profile_shows_nothing_while_there_is_nothing_to_offer(): void { wp_set_current_user( self::$users['administrator'] ); update_option( DWS_LPMWC_OPTION_LOCKED_METHODS, array() ); + self::assertNull( self::render( self::$users['customer'] )->querySelector( 'input' ) ); } @@ -148,10 +150,17 @@ public function test_saving_the_profile_stores_the_checked_methods_in_the_sites_ wp_set_current_user( self::$users['administrator'] ); self::save( self::$users['customer'], array( 'bacs' ) ); + self::assertSame( array( 'bacs' ), get_user_meta( self::$users['customer'], self::key(), true ) ); self::assertSame( '', get_user_meta( self::$users['customer'], DWS_LPMWC_USER_OPTION_UNLOCKED_METHODS, true ) ); + } + + public function test_saving_the_profile_with_nothing_checked_stores_an_empty_list(): void { + update_user_meta( self::$users['customer'], self::key(), array( 'cod' ) ); + wp_set_current_user( self::$users['administrator'] ); self::save( self::$users['customer'], '' ); + self::assertSame( array(), get_user_meta( self::$users['customer'], self::key(), true ) ); } @@ -210,6 +219,19 @@ public function test_the_users_screen_filters_by_a_method_granted_on_the_profile self::assertSame( array( self::$users['editor'] ), self::listed_users() ); } + protected static function create_user( string $role ): int { + $login = \uniqid( "lpm-profiles-$role-" ); + + return (int) wp_insert_user( + array( + 'user_login' => $login, + 'user_pass' => 'password', + 'user_email' => "$login@example.org", + 'role' => $role, + ) + ); + } + protected static function key(): string { global $wpdb; From 1ec3c54d495b31bca03fda6d9d771060f5e87670 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sat, 31 Oct 2026 23:55:13 +0100 Subject: [PATCH 17/50] Test Locked Payment Methods' order grants and their move from v1 v1's premium build stored order grants as one post meta row per gateway, edited them only on the posts screen, and deleted a disabled control's grant on every save, so saving a paid order or one whose customer was unlocked another way dropped its grants. OrderStrategyTest expects the strategy to load only while its switch is on, as v1's was off by default, and the booted plugin to run it then; on each order storage, an order to unlock only the methods its list holds when it is the order being paid, and a save of its box to store the checked methods; the box to offer the locked methods and the order's other grants, behind unlock_dws_lpm_via_orders, and to be disabled on a paid order, so a save leaves its grants; the order-pay page to unlock the order's methods given its key; and, on each storage, the grants step to move v1's 'yes' rows into the order's list beside its own grants and delete every v1 row. OrderStorage comes from Linked Orders' tests. X rulings 91 and 92. Assisted-by: Claude Code:claude-opus-5-5 --- .../UnlockStrategies/OrderStrategyTest.php | 193 ++++++++++++++++++ .../tests/Support/OrderStorage.php | 57 ++++++ 2 files changed, 250 insertions(+) create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/OrderStrategyTest.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Support/OrderStorage.php diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/OrderStrategyTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/OrderStrategyTest.php new file mode 100644 index 0000000..2af3b1b --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/OrderStrategyTest.php @@ -0,0 +1,193 @@ +register_hooks(); + self::$admin = (int) wp_insert_user( + array( + 'user_login' => 'lpm-orders-admin', + 'user_pass' => 'password', + 'user_email' => 'lpm-orders-admin@example.org', + 'role' => 'administrator', + ) + ); + } + + #[\Override] + public static function tearDownAfterClass(): void { + wp_delete_user( self::$admin ); + } + + #[Before] + protected function store_rows(): void { + foreach ( self::KEYS as $key ) { + $this->stored[ $key ] = get_option( $key ); + } + update_option( DWS_LPMWC_OPTION_LOCKED_METHODS, array( 'cod', 'bacs' ) ); + } + + #[After] + protected function restore_rows(): void { + foreach ( $this->stored as $key => $value ) { + false === $value ? delete_option( $key ) : update_option( $key, $value ); + } + unset( $_POST[ self::FORM ], $_POST[ self::FORM . '_nonce' ] ); + wp_set_current_user( 0 ); + } + + public function test_the_strategy_loads_only_while_its_switch_is_on_as_v1s_did(): void { + delete_option( DWS_LPMWC_OPTION_UNLOCK_VIA_ORDERS ); + self::assertFalse( OrderStrategy::should_load() ); + + update_option( DWS_LPMWC_OPTION_UNLOCK_VIA_ORDERS, 'yes' ); + self::assertTrue( OrderStrategy::should_load() ); + } + + public function test_the_plugin_runs_the_strategy_once_its_switch_is_on(): void { + update_option( DWS_LPMWC_OPTION_UNLOCK_VIA_ORDERS, 'yes' ); + + $request = self::request( 'echo wp_json_encode( in_array( DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\OrderStrategy::class, dws_lpmwc_plugin()->kernel->resolved, true ) );' ); + + self::assertSame( 0, $request['status'], $request['output'] ); + self::assertSame( 'true', $request['last'] ); + } + + #[DataProvider( 'storages' )] + public function test_an_order_unlocks_only_the_methods_its_list_holds_when_it_is_the_order_being_paid( bool $hpos, bool $sync ): void { + $this->use_storage( $hpos, $sync ); + $order = $this->create_order(); + $order->update_meta_data( DWS_LPMWC_ORDER_META_UNLOCKED_METHODS, array( 'cod' ) ); + $order->save(); + + self::assertSame( array( 'cod' ), dws_lpmwc_get_order_unlocked_payment_method_ids( $order->get_id() ) ); + self::assertFalse( self::should_lock( 'cod', $order->get_id() ) ); + self::assertTrue( self::should_lock( 'bacs', $order->get_id() ) ); + self::assertTrue( self::should_lock( 'cod', 0 ) ); + } + + public function test_the_box_offers_the_locked_methods_and_the_orders_other_grants_and_marks_each_one(): void { + $order = $this->create_order(); + $order->update_meta_data( DWS_LPMWC_ORDER_META_UNLOCKED_METHODS, array( 'cod', 'cheque' ) ); + $order->save(); + + $form = self::$strategy->get_object_form( self::FORM, $order->get_id() ); + + self::assertSame( DWS_LPMWC_CAP_UNLOCK_VIA_ORDERS, $form['capability'] ); + self::assertSame( DWS_LPMWC_ORDER_META_UNLOCKED_METHODS, $form['fields'][0]['id'] ); + self::assertSame( + array( + 'cod' => 'Cash on delivery', + 'bacs' => 'Direct bank transfer', + 'cheque' => 'Check payments (not locked)', + ), + $form['fields'][0]['options'] + ); + self::assertArrayNotHasKey( 'custom_attributes', $form['fields'][0] ); + } + + public function test_the_box_of_a_paid_order_is_disabled_so_a_save_leaves_its_grants(): void { + $order = $this->create_order( array( 'status' => 'processing' ) ); + $order->update_meta_data( DWS_LPMWC_ORDER_META_UNLOCKED_METHODS, array( 'cod' ) ); + $order->save(); + wp_set_current_user( self::$admin ); + + self::assertSame( array( 'disabled' => 'disabled' ), self::$strategy->get_object_form( self::FORM, $order->get_id() )['fields'][0]['custom_attributes'] ?? null ); + + self::save( $order->get_id(), array() ); + self::assertSame( array( 'cod' ), dws_lpmwc_get_order_unlocked_payment_method_ids( $order->get_id() ) ); + } + + #[DataProvider( 'storages' )] + public function test_saving_the_box_stores_the_checked_methods_on_the_order( bool $hpos, bool $sync ): void { + $this->use_storage( $hpos, $sync ); + $order = $this->create_order(); + wp_set_current_user( self::$admin ); + + self::save( $order->get_id(), array( 'bacs' ) ); + + self::assertSame( array( 'bacs' ), dws_lpmwc_get_order_unlocked_payment_method_ids( $order->get_id() ) ); + } + + public function test_on_its_order_pay_page_an_order_unlocks_its_methods_given_its_key(): void { + $order = $this->create_order(); + $order->update_meta_data( DWS_LPMWC_ORDER_META_UNLOCKED_METHODS, array( 'cod' ) ); + $order->save(); + $GLOBALS['wp']->query_vars['order-pay'] = (string) $order->get_id(); + add_filter( 'woocommerce_is_checkout', '__return_true' ); + $_GET['key'] = $order->get_order_key(); + + try { + $available = apply_filters( 'woocommerce_available_payment_gateways', WC()->payment_gateways()->payment_gateways() ); + } finally { + unset( $GLOBALS['wp']->query_vars['order-pay'], $_GET['key'] ); + remove_filter( 'woocommerce_is_checkout', '__return_true' ); + } + + self::assertArrayHasKey( 'cod', $available ); + self::assertArrayNotHasKey( 'bacs', $available ); + } + + #[DataProvider( 'storages' )] + public function test_the_grants_step_moves_v1s_order_grants_into_the_orders_list( bool $hpos, bool $sync ): void { + $this->use_storage( $hpos, $sync ); + $moved = $this->create_order(); + $moved->update_meta_data( '_dws_wc_lpm_grant_access_cod', 'yes' ); + $moved->update_meta_data( '_dws_wc_lpm_grant_access_bacs', 'no' ); + $moved->save(); + $merged = $this->create_order(); + $merged->update_meta_data( '_dws_wc_lpm_grant_access_cod', 'yes' ); + $merged->update_meta_data( DWS_LPMWC_ORDER_META_UNLOCKED_METHODS, array( 'cheque' ) ); + $merged->save(); + + dws_lpmwc_move_order_grants(); + dws_lpmwc_move_order_grants(); + + self::assertSame( array( 'cod' ), dws_lpmwc_get_order_unlocked_payment_method_ids( $moved->get_id() ) ); + self::assertSame( array( 'cheque', 'cod' ), dws_lpmwc_get_order_unlocked_payment_method_ids( $merged->get_id() ) ); + foreach ( array( $moved, $merged ) as $order ) { + $keys = \array_map( static fn ( \WC_Meta_Data $meta ): string => (string) $meta->get_data()['key'], wc_get_order( $order->get_id() )->get_meta_data() ); + self::assertSame( array(), \array_values( \array_filter( $keys, static fn ( string $key ): bool => \str_starts_with( $key, '_dws_wc_lpm_' ) ) ) ); + } + } + + protected static function should_lock( string $gateway_id, int $order_id ): mixed { + return apply_filters( 'dws_lpmwc/payment_method/should_lock', true, $gateway_id, 0, $order_id ); + } + + protected static function save( int $order_id, array|string $methods ): void { + $_POST[ self::FORM ] = array( DWS_LPMWC_ORDER_META_UNLOCKED_METHODS => $methods ); + $_POST[ self::FORM . '_nonce' ] = wp_create_nonce( self::FORM . "_nonce_$order_id" ); + do_action( 'woocommerce_process_shop_order_meta', $order_id, wc_get_order( $order_id ) ); + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Support/OrderStorage.php b/plugins/locked-payment-methods-for-woocommerce/tests/Support/OrderStorage.php new file mode 100644 index 0000000..13eea6b --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Support/OrderStorage.php @@ -0,0 +1,57 @@ + array( false, false ), + 'posts-with-sync' => array( false, true ), + 'hpos' => array( true, false ), + 'hpos-with-sync' => array( true, true ), + ); + } + + protected function use_storage( bool $hpos, bool $sync, bool $caching = false ): void { + $this->storage_filters = array( + 'pre_option_woocommerce_custom_orders_table_enabled' => static fn (): string => $hpos ? 'yes' : 'no', + 'pre_option_woocommerce_custom_orders_table_data_sync_enabled' => static fn (): string => $sync ? 'yes' : 'no', + 'pre_option_woocommerce_hpos_datastore_caching_enabled' => static fn (): string => $caching ? 'yes' : 'no', + ); + foreach ( $this->storage_filters as $hook => $filter ) { + add_filter( $hook, $filter ); + } + } + + protected function create_order( array $args = array() ): \WC_Order { + $order = wc_create_order( $args + array( 'status' => 'pending' ) ); + self::assertInstanceOf( \WC_Order::class, $order ); + $this->orders[] = $order->get_id(); + + return $order; + } + + // The orders go before the filters, because an order deletes from the storage it was loaded from. + #[After] + protected function delete_orders_and_restore_storage(): void { + foreach ( $this->orders as $id ) { + $order = wc_get_order( $id ); + if ( $order instanceof \WC_Order ) { + $order->delete( true ); + } + } + $this->orders = array(); + + foreach ( $this->storage_filters as $hook => $filter ) { + remove_filter( $hook, $filter ); + } + $this->storage_filters = array(); + } +} From 9f3072bc27507a310e0d91dc3921028315b66131 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sat, 31 Oct 2026 23:57:14 +0100 Subject: [PATCH 18/50] Unlock payment methods per order in Locked Payment Methods v1's premium build stored order grants as one post meta row per gateway, edited them only on the posts screen, and deleted a disabled control's grant on every save, so saving a paid order or one whose customer was unlocked another way dropped its grants. An order's grants are one order meta list, read through WooCommerce's order data on either storage. OrderStrategy, gated on its switch and off by default as v1's was, returns false on should_lock for a method on the list of the order being paid, which the safeguard names only on the order's own payment page given its key. It edits the list in a box on the order screen through the framework's OrderMetaBox, behind unlock_dws_lpm_via_orders and edit_post; a paid order's box is disabled, so a save leaves its grants. The third migration step moves v1's 'yes' rows, found in either storage's meta table, into each order's list beside its own grants, and deletes the order's v1 rows. The profile and order forms build their choices and titles through two shared functions in includes/grants.php. X rulings 91 and 92. Assisted-by: Claude Code:claude-opus-5-5 --- .../includes/grants.php | 72 ++++++++++ .../includes/migrations.php | 65 +++++++++ .../src/Plugin.php | 2 + .../src/UnlockStrategies/OrderStrategy.php | 127 ++++++++++++++++++ .../UnlockStrategies/UserProfileStrategy.php | 35 +---- .../tests/Integration/PluginBootTest.php | 5 +- 6 files changed, 272 insertions(+), 34 deletions(-) create mode 100644 plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/OrderStrategy.php diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/grants.php b/plugins/locked-payment-methods-for-woocommerce/includes/grants.php index d4730ee..ea80367 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/grants.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/grants.php @@ -51,3 +51,75 @@ function dws_lpmwc_get_user_unlocked_payment_method_ids( int $user_id ): array { return is_array( $ids ) ? array_values( array_filter( $ids, 'is_string' ) ) : array(); } + +/** + * The order meta that holds the ids of the payment methods unlocked for paying the order. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @var string + */ +const DWS_LPMWC_ORDER_META_UNLOCKED_METHODS = '_dws_lpmwc_unlocked_payment_methods'; + +/** + * Returns the ids of the payment methods unlocked for paying an order. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param int $order_id The order's id. + * + * @return string[] + */ +function dws_lpmwc_get_order_unlocked_payment_method_ids( int $order_id ): array { + $order = 0 === $order_id ? false : wc_get_order( $order_id ); + $ids = $order instanceof WC_Order ? $order->get_meta( DWS_LPMWC_ORDER_META_UNLOCKED_METHODS ) : array(); + + return is_array( $ids ) ? array_values( array_filter( $ids, 'is_string' ) ) : array(); +} + +/** + * Returns a payment method's title, or its id for a payment method no longer registered. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string $gateway_id The payment method's id. + * + * @return string + */ +function dws_lpmwc_get_payment_method_title( string $gateway_id ): string { + $gateway = WC()->payment_gateways()->payment_gateways()[ $gateway_id ] ?? null; + + return $gateway instanceof WC_Payment_Gateway ? $gateway->get_title() : $gateway_id; +} + +/** + * Returns the choices of a grants field by payment method id: the locked methods and the subject's other grants, since a save keeps only the choices a field offers, each marked when it is not locked or when another rule already unlocks it for the user. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string[] $granted The ids of the payment methods the subject unlocks. + * @param int $user_id The user other rules are judged for, or 0 for none. + * + * @return array + */ +function dws_lpmwc_get_grant_choices( array $granted, int $user_id ): array { + $locked = dws_lpmwc_get_locked_payment_method_ids(); + $choices = array(); + foreach ( array_unique( array_merge( $locked, $granted ) ) as $id ) { + $title = dws_lpmwc_get_payment_method_title( $id ); + if ( ! in_array( $id, $locked, true ) ) { + /* translators: %s: payment method title. */ + $title = sprintf( __( '%s (not locked)', 'locked-payment-methods-for-woocommerce' ), $title ); + } elseif ( 0 !== $user_id && ! in_array( $id, $granted, true ) && ! dws_lpmwc_should_lock( true, $id, $user_id, 0 ) ) { + /* translators: %s: payment method title. */ + $title = sprintf( __( '%s (unlocked by another rule)', 'locked-payment-methods-for-woocommerce' ), $title ); + } + $choices[ $id ] = $title; + } + + return $choices; +} diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php b/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php index 296f24c..ce5ac29 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php @@ -22,6 +22,7 @@ function dws_lpmwc_get_migration_steps(): array { return array( 'dws_lpmwc_add_settings', 'dws_lpmwc_move_user_grants', + 'dws_lpmwc_move_order_grants', ); } @@ -119,3 +120,67 @@ function dws_lpmwc_move_user_grants(): void { } } } + +/** + * Moves v1's order grants, one 'yes' row per payment method, into each order's list beside the grants it holds, through WooCommerce's order data, so either order storage keeps them, then deletes every v1 grant row of the order. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @global wpdb $wpdb WordPress's database access. + * + * @throws RuntimeException Thrown when v1's order grants could not be read or an order's grants could not be stored. + * + * @return void + */ +function dws_lpmwc_move_order_grants(): void { + global $wpdb; + + $prefix = '_dws_wc_lpm_grant_access_'; + $ids = array(); + $tables = array( + $wpdb->postmeta => 'post_id', + $wpdb->prefix . 'wc_orders_meta' => 'order_id', + ); + foreach ( $tables as $table => $column ) { + $wpdb->flush(); + if ( $table !== $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $table ) ) ) ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- The order tables exist only once WooCommerce created them. + continue; + } + + $found = $wpdb->get_col( $wpdb->prepare( 'SELECT DISTINCT %i FROM %i WHERE meta_key LIKE %s', $column, $table, $wpdb->esc_like( $prefix ) . '%' ) ?? '' ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- v1's keys carry the payment method's id, so no order query lists them. + if ( '' !== $wpdb->last_error ) { + throw new RuntimeException( 'The v1 order grants could not be read.' ); + } + $ids = array_merge( $ids, wp_parse_id_list( $found ) ); + } + + foreach ( array_unique( $ids ) as $order_id ) { + $order = wc_get_order( $order_id ); + if ( ! $order instanceof WC_Order ) { + continue; + } + + $granted = dws_lpmwc_get_order_unlocked_payment_method_ids( $order_id ); + $moved = false; + foreach ( $order->get_meta_data() as $meta ) { + $data = $meta instanceof WC_Meta_Data ? $meta->get_data() : array(); + if ( is_string( $data['key'] ?? null ) && str_starts_with( $data['key'], $prefix ) ) { + if ( 'yes' === ( $data['value'] ?? null ) ) { + $granted[] = substr( $data['key'], strlen( $prefix ) ); + } + $order->delete_meta_data( $data['key'] ); + $moved = true; + } + } + if ( ! $moved ) { + continue; + } + + $order->update_meta_data( DWS_LPMWC_ORDER_META_UNLOCKED_METHODS, array_values( array_unique( $granted ) ) ); + $order->save(); + if ( dws_lpmwc_get_order_unlocked_payment_method_ids( $order_id ) !== array_values( array_unique( $granted ) ) ) { + throw new RuntimeException( "The payment methods unlocked for order $order_id could not be stored." ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing, never rendered. + } + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/src/Plugin.php b/plugins/locked-payment-methods-for-woocommerce/src/Plugin.php index 7f24492..d2389c2 100644 --- a/plugins/locked-payment-methods-for-woocommerce/src/Plugin.php +++ b/plugins/locked-payment-methods-for-woocommerce/src/Plugin.php @@ -13,6 +13,7 @@ use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\WooCommerce\WooCommerceRequirement; use DeepWebSolutions\LockedPaymentMethods\Scoped\DI\ContainerBuilder; use DeepWebSolutions\LockedPaymentMethods\Scoped\Psr\Log\LoggerInterface as PsrLoggerInterface; +use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\OrderStrategy; use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\UserProfileStrategy; use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\UserRoleStrategy; @@ -62,6 +63,7 @@ final class Plugin { Settings::class, UserRoleStrategy::class, UserProfileStrategy::class, + OrderStrategy::class, ); /** diff --git a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/OrderStrategy.php b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/OrderStrategy.php new file mode 100644 index 0000000..284fda1 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/OrderStrategy.php @@ -0,0 +1,127 @@ +notices )->register_hooks(); + } + + /** + * {@inheritDoc} + * + * @since 2.0.0 + * @version 2.0.0 + */ + #[\Override] + public function get_object_form( string $form_id, int $object_id ): array { + $order = wc_get_order( $object_id ); + $field = array( + 'type' => 'checkboxes', + 'id' => DWS_LPMWC_ORDER_META_UNLOCKED_METHODS, + 'title' => __( 'Unlocked payment methods', 'locked-payment-methods-for-woocommerce' ), + 'desc' => __( 'The customer may pay for this order with these methods on its payment page.', 'locked-payment-methods-for-woocommerce' ), + 'options' => $order instanceof \WC_Order ? dws_lpmwc_get_grant_choices( dws_lpmwc_get_order_unlocked_payment_method_ids( $object_id ), $order->get_customer_id() ) : array(), + ); + if ( $order instanceof \WC_Order && $order->is_paid() ) { + $field['desc'] = __( 'This order is paid, so its unlocked payment methods no longer change.', 'locked-payment-methods-for-woocommerce' ); + $field['custom_attributes'] = array( 'disabled' => 'disabled' ); + } + + return array( + 'title' => __( 'Locked Payment Methods', 'locked-payment-methods-for-woocommerce' ), + 'capability' => DWS_LPMWC_CAP_UNLOCK_VIA_ORDERS, + 'fields' => array() === $field['options'] ? array() : array( $field ), + ); + } + + // endregion + + // region HOOKS + + /** + * Returns false for a payment method the list of the order being paid holds, and the lock as given otherwise. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param bool $lock Whether the payment method stays locked. + * @param string $gateway_id The payment method's id. + * @param int $user_id The paying user, or 0 for a guest. + * @param int $order_id The order being paid, or 0 off the order-pay page. + * + * @return bool + */ + public function unlock_granted_methods( bool $lock, string $gateway_id, int $user_id, int $order_id ): bool { + return \in_array( $gateway_id, dws_lpmwc_get_order_unlocked_payment_method_ids( $order_id ), true ) ? false : $lock; + } + + // endregion +} diff --git a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserProfileStrategy.php b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserProfileStrategy.php index 3be8f31..9fb0c18 100644 --- a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserProfileStrategy.php +++ b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserProfileStrategy.php @@ -96,20 +96,7 @@ public function register_hooks(): void { */ #[\Override] public function get_object_form( string $form_id, int $object_id ): array { - $granted = dws_lpmwc_get_user_unlocked_payment_method_ids( $object_id ); - $locked = dws_lpmwc_get_locked_payment_method_ids(); - - // A save keeps only the choices the field offers, so the user's grants outside the locked list are offered too. - $options = array(); - foreach ( \array_unique( \array_merge( $locked, $granted ) ) as $id ) { - $options[ $id ] = match ( true ) { - /* translators: %s: payment method title. */ - ! \in_array( $id, $locked, true ) => \sprintf( __( '%s (not locked)', 'locked-payment-methods-for-woocommerce' ), $this->get_title( $id ) ), - /* translators: %s: payment method title. */ - ! \in_array( $id, $granted, true ) && ! dws_lpmwc_should_lock( true, $id, $object_id, 0 ) => \sprintf( __( '%s (unlocked by another rule)', 'locked-payment-methods-for-woocommerce' ), $this->get_title( $id ) ), - default => $this->get_title( $id ), - }; - } + $options = dws_lpmwc_get_grant_choices( dws_lpmwc_get_user_unlocked_payment_method_ids( $object_id ), $object_id ); return array( 'title' => __( 'Locked Payment Methods', 'locked-payment-methods-for-woocommerce' ), @@ -189,7 +176,7 @@ public function render_users_column( string $output, string $column, int $user_i default => __( 'locked', 'locked-payment-methods-for-woocommerce' ), }; /* translators: 1: payment method title, 2: the payment method's state for the user. */ - $lines[] = esc_html( \sprintf( __( '%1$s: %2$s', 'locked-payment-methods-for-woocommerce' ), $this->get_title( $id ), $state ) ); + $lines[] = esc_html( \sprintf( __( '%1$s: %2$s', 'locked-payment-methods-for-woocommerce' ), dws_lpmwc_get_payment_method_title( $id ), $state ) ); } return \implode( '
', $lines ); @@ -215,7 +202,7 @@ public function render_users_filter( string $which ): void { $active = $this->get_active_filter(); \printf( ''; submit_button( _x( 'Filter', 'users screen', 'locked-payment-methods-for-woocommerce' ), '', $which, false, array( 'id' => "{$name}_submit" ) ); @@ -255,22 +242,6 @@ public function filter_users( \WP_User_Query $query ): void { // region HELPERS - /** - * Returns a payment method's title, or its id for a payment method no longer registered. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @param string $gateway_id The payment method's id. - * - * @return string - */ - protected function get_title( string $gateway_id ): string { - $gateway = WC()->payment_gateways()->payment_gateways()[ $gateway_id ] ?? null; - - return $gateway instanceof \WC_Payment_Gateway ? $gateway->get_title() : $gateway_id; - } - /** * Returns the locked payment method the Users screen filters by, from the dropdown whose button submitted the list, or an empty string. * diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php index 123badb..92be135 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php @@ -8,6 +8,7 @@ use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\Permission\CapabilityGrants; use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\Scheduling\Scheduler; use DeepWebSolutions\LockedPaymentMethods\Settings; +use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\OrderStrategy; use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\UserProfileStrategy; use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\UserRoleStrategy; use PHPUnit\Framework\Attributes\CoversNothing; @@ -15,12 +16,12 @@ #[CoversNothing] final class PluginBootTest extends TestCase { - public function test_the_plugin_boots_every_component(): void { + public function test_the_plugin_boots_every_component_whose_switch_is_on(): void { $plugin = dws_lpmwc_plugin(); self::assertSame( 'booted', $plugin->boot->status ); self::assertSame( array( Scheduler::class, AdminNotices::class, CapabilityGrants::class, Settings::class, UserRoleStrategy::class, UserProfileStrategy::class ), $plugin->kernel?->resolved ); - self::assertSame( array(), $plugin->kernel->skipped ); + self::assertSame( array( OrderStrategy::class ), $plugin->kernel->skipped ); // Orders are off by default, as v1's were. } public function test_the_entry_loads_every_include_but_those_whose_names_start_with_an_underscore(): void { From d05ec1c6aa9cb93fcaa7fa4c06aba24a0140e76b Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 00:01:09 +0100 Subject: [PATCH 19/50] Test Locked Payment Methods' Groups grants and their move from v1 v1's premium build kept each Groups group's grants in an option of its own, which deleting the group left behind. GroupsStrategyTest, against Groups 4.8.0 and in the groups group so environments without Groups leave it out, expects the strategy to load only while Groups is active and its switch is on; the settings to show the switch while Groups is active; a member of a group or of one of its subgroups to be unlocked for the group's methods; Groups' add and edit forms to offer the locked methods to a user holding both unlock_dws_lpm_via_groups and Groups' own capability, and their saves to store the checked methods under the group, keep them on a choice the field does not offer, and store nothing without the capability or the field; deleting a group to forget its methods; and the grants step to move v1's per-group options into one map and delete them. SettingsTest's integrations rows follow whether Groups is active. X rulings 91 and 92. Assisted-by: Claude Code:claude-opus-5-5 --- .../tests/Integration/SettingsTest.php | 5 +- .../UnlockStrategies/GroupsStrategyTest.php | 231 ++++++++++++++++++ 2 files changed, 234 insertions(+), 2 deletions(-) create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/GroupsStrategyTest.php diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SettingsTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SettingsTest.php index 340be19..4eefb9b 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SettingsTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SettingsTest.php @@ -3,6 +3,7 @@ namespace DeepWebSolutions\LockedPaymentMethods\Tests\Integration; use DeepWebSolutions\LockedPaymentMethods\Settings; +use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\GroupsStrategy; use PHPUnit\Framework\Attributes\After; use PHPUnit\Framework\Attributes\Before; use PHPUnit\Framework\Attributes\CoversNothing; @@ -37,7 +38,7 @@ public static function setUpBeforeClass(): void { public static function setting_groups(): array { return array( 'general' => array( 'general', array( 'locked-payment-methods', 'unlock-via-user-role', 'full-access-user-roles', 'unlock-via-user-meta', 'unlock-via-order-meta' ) ), - 'integrations' => array( 'integrations', array() ), + 'integrations' => array( 'integrations', GroupsStrategy::is_available() ? array( 'unlock-via-groups' ) : array() ), 'plugin' => array( 'plugin', array( 'remove-data-uninstall' ) ), ); } @@ -76,7 +77,7 @@ public function test_the_section_shows_v1s_groups_with_their_defaults(): void { $form = $this->render(); - self::assertSame( array( 'General Settings', 'Plugin Settings' ), \array_map( static fn ( \Dom\Element $heading ): string => \trim( $heading->textContent ), \iterator_to_array( $form->querySelectorAll( 'h2' ) ) ) ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- A DOM property. + self::assertSame( GroupsStrategy::is_available() ? array( 'General Settings', 'Integrations Settings', 'Plugin Settings' ) : array( 'General Settings', 'Plugin Settings' ), \array_map( static fn ( \Dom\Element $heading ): string => \trim( $heading->textContent ), \iterator_to_array( $form->querySelectorAll( 'h2' ) ) ) ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- A DOM property. self::assertSame( array(), self::selected( $form, DWS_LPMWC_OPTION_LOCKED_METHODS ) ); self::assertSame( array( 'administrator', 'shop_manager' ), self::selected( $form, DWS_LPMWC_OPTION_FULL_ACCESS_ROLES ) ); foreach ( self::CHECKBOXES as $key ) { diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/GroupsStrategyTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/GroupsStrategyTest.php new file mode 100644 index 0000000..d0fd577 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/GroupsStrategyTest.php @@ -0,0 +1,231 @@ +register_hooks(); + foreach ( array( 'administrator', 'shop_manager', 'customer' ) as $role ) { + self::$users[ $role ] = (int) wp_insert_user( + array( + 'user_login' => "lpm-groups-$role", + 'user_pass' => 'password', + 'user_email' => "lpm-groups-$role@example.org", + 'role' => $role, + ) + ); + } + } + + #[\Override] + public static function tearDownAfterClass(): void { + foreach ( self::$users as $user ) { + wp_delete_user( $user ); + } + } + + #[Before] + protected function store_rows(): void { + foreach ( self::KEYS as $key ) { + $this->stored[ $key ] = get_option( $key ); + } + update_option( DWS_LPMWC_OPTION_LOCKED_METHODS, array( 'cod', 'bacs' ) ); + delete_option( DWS_LPMWC_OPTION_GROUPS_UNLOCKED_METHODS ); + } + + #[After] + protected function restore_rows(): void { + foreach ( $this->groups as $group ) { + \Groups_Group::delete( $group ); + } + foreach ( $this->stored as $key => $value ) { + false === $value ? delete_option( $key ) : update_option( $key, $value ); + } + unset( $_POST['dws_lpmwc_group'] ); + wp_set_current_user( 0 ); + } + + public function test_the_strategy_loads_only_while_groups_is_active_and_its_switch_is_on(): void { + self::assertTrue( GroupsStrategy::is_available() ); + + delete_option( DWS_LPMWC_OPTION_UNLOCK_VIA_GROUPS ); + self::assertFalse( GroupsStrategy::should_load() ); + + update_option( DWS_LPMWC_OPTION_UNLOCK_VIA_GROUPS, 'yes' ); + self::assertTrue( GroupsStrategy::should_load() ); + } + + public function test_the_settings_show_the_groups_switch_while_groups_is_active(): void { + $fields = ( new \DeepWebSolutions\LockedPaymentMethods\Settings() )->get_section_fields( 'advanced', 'dws-locked-payment-methods' ); + $switch = \array_values( \array_filter( $fields, static fn ( array $field ): bool => DWS_LPMWC_OPTION_UNLOCK_VIA_GROUPS === ( $field['id'] ?? null ) ) ); + + self::assertSame( 'checkbox', $switch[0]['type'] ?? null ); + self::assertSame( 'no', $switch[0]['default'] ?? null ); + self::assertContains( 'dws-wc-lpm_integrations', \array_column( $fields, 'id' ) ); + } + + public function test_a_member_of_a_group_or_of_its_subgroup_is_unlocked_for_the_groups_methods(): void { + $parent = $this->create_group( 'LPM parent' ); + $child = $this->create_group( 'LPM child', $parent ); + \Groups_User_Group::create( + array( + 'user_id' => self::$users['customer'], + 'group_id' => $child, + ) + ); + update_option( DWS_LPMWC_OPTION_GROUPS_UNLOCKED_METHODS, array( $parent => array( 'cod' ) ) ); + + self::assertSame( array( $parent => array( 'cod' ) ), dws_lpmwc_get_groups_unlocked_payment_method_ids() ); + self::assertFalse( self::should_lock( 'cod', self::$users['customer'] ) ); + self::assertTrue( self::should_lock( 'bacs', self::$users['customer'] ) ); + self::assertTrue( self::should_lock( 'cod', self::$users['shop_manager'] ) ); + self::assertTrue( self::should_lock( 'cod', 0 ) ); + } + + public function test_the_group_forms_offer_the_locked_methods_to_a_user_with_both_capabilities(): void { + $group = $this->create_group( 'LPM shown' ); + update_option( DWS_LPMWC_OPTION_GROUPS_UNLOCKED_METHODS, array( $group => array( 'cod' ) ) ); + wp_set_current_user( self::$users['administrator'] ); + + $edit = self::parse( apply_filters( 'groups_admin_groups_edit_form_after_fields', '', $group ) ); + self::assertSame( + array( + 'cod' => true, + 'bacs' => false, + ), + self::choices( $edit ) + ); + self::assertSame( + array( + 'cod' => false, + 'bacs' => false, + ), + self::choices( self::parse( apply_filters( 'groups_admin_groups_add_form_after_fields', '' ) ) ) + ); + + wp_set_current_user( self::$users['shop_manager'] ); + self::assertSame( '', apply_filters( 'groups_admin_groups_edit_form_after_fields', '', $group ) ); + } + + public function test_a_group_form_save_stores_the_checked_methods_under_the_group(): void { + $group = $this->create_group( 'LPM saved' ); + wp_set_current_user( self::$users['administrator'] ); + + $_POST['dws_lpmwc_group'] = array( 'dws_lpmwc_unlocked_payment_methods' => array( 'bacs' ) ); + do_action( 'groups_admin_groups_add_submit_success', $group ); + self::assertSame( array( $group => array( 'bacs' ) ), dws_lpmwc_get_groups_unlocked_payment_method_ids() ); + + $_POST['dws_lpmwc_group'] = array( 'dws_lpmwc_unlocked_payment_methods' => array( 'paypal' ) ); + do_action( 'groups_admin_groups_edit_submit_success', $group ); + self::assertSame( array( $group => array( 'bacs' ) ), dws_lpmwc_get_groups_unlocked_payment_method_ids() ); + + $_POST['dws_lpmwc_group'] = array( 'dws_lpmwc_unlocked_payment_methods' => '' ); + do_action( 'groups_admin_groups_edit_submit_success', $group ); + self::assertSame( array(), dws_lpmwc_get_groups_unlocked_payment_method_ids() ); + } + + public function test_a_save_without_the_capability_or_without_the_field_stores_nothing(): void { + $group = $this->create_group( 'LPM refused' ); + + wp_set_current_user( self::$users['shop_manager'] ); + $_POST['dws_lpmwc_group'] = array( 'dws_lpmwc_unlocked_payment_methods' => array( 'bacs' ) ); + do_action( 'groups_admin_groups_edit_submit_success', $group ); + self::assertSame( array(), dws_lpmwc_get_groups_unlocked_payment_method_ids() ); + + wp_set_current_user( self::$users['administrator'] ); + unset( $_POST['dws_lpmwc_group'] ); + do_action( 'groups_admin_groups_edit_submit_success', $group ); + self::assertSame( array(), dws_lpmwc_get_groups_unlocked_payment_method_ids() ); + } + + public function test_deleting_a_group_forgets_its_methods(): void { + $kept = $this->create_group( 'LPM kept' ); + $deleted = $this->create_group( 'LPM deleted' ); + update_option( + DWS_LPMWC_OPTION_GROUPS_UNLOCKED_METHODS, + array( + $kept => array( 'cod' ), + $deleted => array( 'bacs' ), + ) + ); + + \Groups_Group::delete( $deleted ); + + self::assertSame( array( $kept => array( 'cod' ) ), dws_lpmwc_get_groups_unlocked_payment_method_ids() ); + } + + public function test_the_grants_step_moves_v1s_group_options_into_the_groups_map(): void { + $moved = $this->create_group( 'LPM moved' ); + $merged = $this->create_group( 'LPM merged' ); + update_option( "dws-wc-lpm_groups-settings_$moved", array( 'cod' ) ); + update_option( "dws-wc-lpm_groups-settings_$merged", array( 'cod', 7 ) ); + update_option( DWS_LPMWC_OPTION_GROUPS_UNLOCKED_METHODS, array( $merged => array( 'cheque' ) ) ); + + dws_lpmwc_move_group_grants(); + dws_lpmwc_move_group_grants(); + + self::assertSame( + array( + $merged => array( 'cheque', 'cod' ), + $moved => array( 'cod' ), + ), + dws_lpmwc_get_groups_unlocked_payment_method_ids() + ); + self::assertFalse( get_option( "dws-wc-lpm_groups-settings_$moved" ) ); + self::assertFalse( get_option( "dws-wc-lpm_groups-settings_$merged" ) ); + } + + protected function create_group( string $name, int $parent_id = 0 ): int { + $group = (int) \Groups_Group::create( + array( + 'name' => \uniqid( "$name " ), + 'parent_id' => 0 === $parent_id ? null : $parent_id, + ) + ); + $this->groups[] = $group; + + return $group; + } + + protected static function should_lock( string $gateway_id, int $user_id ): mixed { + return apply_filters( 'dws_lpmwc/payment_method/should_lock', true, $gateway_id, $user_id, 0 ); + } + + protected static function parse( string $markup ): \Dom\HTMLDocument { + return \Dom\HTMLDocument::createFromString( '' . $markup . '', \LIBXML_NOERROR ); + } + + protected static function choices( \Dom\HTMLDocument $form ): array { + $choices = array(); + foreach ( $form->querySelectorAll( 'input[type="checkbox"][name="dws_lpmwc_group[dws_lpmwc_unlocked_payment_methods][]"]' ) as $input ) { + $choices[ (string) $input->getAttribute( 'value' ) ] = $input->hasAttribute( 'checked' ); + } + + return $choices; + } +} From 39fecbf1d29bc1c9223c6466f8df673f15ea5187 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 00:04:27 +0100 Subject: [PATCH 20/50] Unlock payment methods for Groups members in Locked Payment Methods v1's premium build kept each Groups group's grants in an option of its own, which deleting the group left behind. The groups' grants are one option mapping each group's id to its list. GroupsStrategy, gated on Groups at v1's oldest version and on its switch, off by default, returns false on should_lock for a method a group of the user, or a group above one, unlocks. Groups verifies its forms' nonce and its own capability before it fires the actions that save them, and its add form has no group to bind a nonce to, so the field rides Groups' add and edit forms: it renders and sanitizes through the framework's Forms, for a user who also holds unlock_dws_lpm_via_groups, reports a rejection as an admin notice, and a deleted group's entry goes. The settings show the Groups switch while Groups is active. The fourth migration step moves v1's per-group options into the map and deletes them. Groups 4.8.0 comes from WP Packages as a development package, mapped and activated in Locked Payment Methods' own environments; PHPStan scans its classes. X rulings 91 and 92. Assisted-by: Claude Code:claude-opus-5-5 --- .../.wp-env.json | 5 +- .../.wp-env.tests.json | 5 +- .../composer.json | 1 + .../composer.lock | 34 ++- .../includes/grants.php | 30 +++ .../includes/migrations.php | 50 ++++ .../phpstan.dist.neon | 1 + .../src/Plugin.php | 2 + .../src/Settings.php | 11 + .../src/UnlockStrategies/GroupsStrategy.php | 250 ++++++++++++++++++ .../tests/Integration/PluginBootTest.php | 3 +- .../UnlockStrategies/GroupsStrategyTest.php | 4 +- 12 files changed, 388 insertions(+), 8 deletions(-) create mode 100644 plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/GroupsStrategy.php diff --git a/plugins/locked-payment-methods-for-woocommerce/.wp-env.json b/plugins/locked-payment-methods-for-woocommerce/.wp-env.json index 129d6be..7a65f5d 100644 --- a/plugins/locked-payment-methods-for-woocommerce/.wp-env.json +++ b/plugins/locked-payment-methods-for-woocommerce/.wp-env.json @@ -6,7 +6,8 @@ "testsEnvironment": false, "mappings": { "wp-content/plugins/locked-payment-methods-for-woocommerce": ".", - "wp-content/plugins/woocommerce": "./vendor/wp-plugin/woocommerce" + "wp-content/plugins/woocommerce": "./vendor/wp-plugin/woocommerce", + "wp-content/plugins/groups": "./vendor/wp-plugin/groups" }, "config": { "WP_DEBUG": true, @@ -14,6 +15,6 @@ "WP_ENVIRONMENT_TYPE": "local" }, "lifecycleScripts": { - "afterStart": "node_modules/.bin/wp-env run cli wp plugin activate woocommerce && node_modules/.bin/wp-env run cli wp wc hpos enable --ignore-plugin-compatibility && node_modules/.bin/wp-env run cli wp plugin activate locked-payment-methods-for-woocommerce" + "afterStart": "node_modules/.bin/wp-env run cli wp plugin activate woocommerce groups && node_modules/.bin/wp-env run cli wp wc hpos enable --ignore-plugin-compatibility && node_modules/.bin/wp-env run cli wp plugin activate locked-payment-methods-for-woocommerce" } } diff --git a/plugins/locked-payment-methods-for-woocommerce/.wp-env.tests.json b/plugins/locked-payment-methods-for-woocommerce/.wp-env.tests.json index b3f5b0b..33fa6ad 100644 --- a/plugins/locked-payment-methods-for-woocommerce/.wp-env.tests.json +++ b/plugins/locked-payment-methods-for-woocommerce/.wp-env.tests.json @@ -6,7 +6,8 @@ "testsEnvironment": false, "mappings": { "wp-content/plugins/locked-payment-methods-for-woocommerce": ".", - "wp-content/plugins/woocommerce": "./vendor/wp-plugin/woocommerce" + "wp-content/plugins/woocommerce": "./vendor/wp-plugin/woocommerce", + "wp-content/plugins/groups": "./vendor/wp-plugin/groups" }, "config": { "WP_DEBUG": true, @@ -14,6 +15,6 @@ "WP_ENVIRONMENT_TYPE": false }, "lifecycleScripts": { - "afterStart": "node_modules/.bin/wp-env --config .wp-env.tests.json run cli wp plugin activate woocommerce && node_modules/.bin/wp-env --config .wp-env.tests.json run cli wp wc hpos enable --ignore-plugin-compatibility && node_modules/.bin/wp-env --config .wp-env.tests.json run cli wp plugin activate locked-payment-methods-for-woocommerce" + "afterStart": "node_modules/.bin/wp-env --config .wp-env.tests.json run cli wp plugin activate woocommerce groups && node_modules/.bin/wp-env --config .wp-env.tests.json run cli wp wc hpos enable --ignore-plugin-compatibility && node_modules/.bin/wp-env --config .wp-env.tests.json run cli wp plugin activate locked-payment-methods-for-woocommerce" } } diff --git a/plugins/locked-payment-methods-for-woocommerce/composer.json b/plugins/locked-payment-methods-for-woocommerce/composer.json index a17c6ec..6c143c5 100644 --- a/plugins/locked-payment-methods-for-woocommerce/composer.json +++ b/plugins/locked-payment-methods-for-woocommerce/composer.json @@ -60,6 +60,7 @@ "php-di/php-di": "^7.1.1", "php-stubs/woocommerce-stubs": "^11.2", "phpunit/phpunit": "^13", + "wp-plugin/groups": "^4.8", "wp-plugin/woocommerce": "^11.2" }, diff --git a/plugins/locked-payment-methods-for-woocommerce/composer.lock b/plugins/locked-payment-methods-for-woocommerce/composer.lock index 561137f..caf788f 100644 --- a/plugins/locked-payment-methods-for-woocommerce/composer.lock +++ b/plugins/locked-payment-methods-for-woocommerce/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1616032afedf26ffa7ffc9d3e0fe1c56", + "content-hash": "daf3375aeffc7b0510f18f581f61d7df", "packages": [], "packages-dev": [ { @@ -7290,6 +7290,38 @@ "time": "2026-08-20T19:54:29+00:00", "published-time": "2026-08-20T19:54:42+00:00" }, + { + "name": "wp-plugin/groups", + "version": "4.8.0", + "source": { + "type": "svn", + "url": "https://plugins.svn.wordpress.org/groups/", + "reference": "tags/4.8.0" + }, + "dist": { + "type": "zip", + "url": "https://downloads.wordpress.org/plugin/groups.4.8.0.zip" + }, + "require": { + "composer/installers": "~1.0|~2.0" + }, + "type": "wordpress-plugin", + "notification-url": "https://wp-packages.org/downloads", + "authors": [ + { + "name": "itthinx" + } + ], + "description": "Turn your site into a powerful membership solution. Manage members, teams and access to content easily.", + "homepage": "https://www.itthinx.com/plugins/groups", + "support": { + "changelog": "https://wordpress.org/plugins/groups/#developers", + "issues": "https://wordpress.org/support/plugin/groups", + "source": "https://plugins.svn.wordpress.org/groups" + }, + "time": "2026-09-29T13:03:58+00:00", + "published-time": "2026-09-29T13:03:58+00:00" + }, { "name": "wp-plugin/woocommerce", "version": "11.2.1", diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/grants.php b/plugins/locked-payment-methods-for-woocommerce/includes/grants.php index ea80367..9997aed 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/grants.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/grants.php @@ -123,3 +123,33 @@ function dws_lpmwc_get_grant_choices( array $granted, int $user_id ): array { return $choices; } + +/** + * The option that maps each Groups group's id to the ids of the payment methods unlocked for its members. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @var string + */ +const DWS_LPMWC_OPTION_GROUPS_UNLOCKED_METHODS = 'dws_lpmwc_groups_unlocked_payment_methods'; + +/** + * Returns the ids of the payment methods unlocked for each Groups group's members, by group id. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @return array + */ +function dws_lpmwc_get_groups_unlocked_payment_method_ids(): array { + $stored = get_option( DWS_LPMWC_OPTION_GROUPS_UNLOCKED_METHODS, array() ); + $groups = array(); + foreach ( is_array( $stored ) ? $stored : array() as $group_id => $ids ) { + if ( is_int( $group_id ) && is_array( $ids ) ) { + $groups[ $group_id ] = array_values( array_filter( $ids, 'is_string' ) ); + } + } + + return $groups; +} diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php b/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php index ce5ac29..c82d9ed 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php @@ -23,6 +23,7 @@ function dws_lpmwc_get_migration_steps(): array { 'dws_lpmwc_add_settings', 'dws_lpmwc_move_user_grants', 'dws_lpmwc_move_order_grants', + 'dws_lpmwc_move_group_grants', ); } @@ -184,3 +185,52 @@ function dws_lpmwc_move_order_grants(): void { } } } + +/** + * Moves v1's Groups grants, one option per group, into the map of every group's grants beside the grants it holds, then deletes v1's options. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @global wpdb $wpdb WordPress's database access. + * + * @throws RuntimeException Thrown when v1's options could not be read, the map could not be stored or a v1 option could not be deleted. + * + * @return void + */ +function dws_lpmwc_move_group_grants(): void { + global $wpdb; + + $prefix = 'dws-wc-lpm_groups-settings_'; + $wpdb->flush(); + $names = $wpdb->get_col( $wpdb->prepare( 'SELECT option_name FROM %i WHERE option_name LIKE %s', $wpdb->options, $wpdb->esc_like( $prefix ) . '%' ) ?? '' ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- v1's option names carry the group's id, so no options API lists them. + if ( '' !== $wpdb->last_error ) { + throw new RuntimeException( 'The v1 Groups grants could not be read.' ); + } + + $groups = dws_lpmwc_get_groups_unlocked_payment_method_ids(); + $moved = array(); + foreach ( $names as $name ) { + if ( ! is_string( $name ) || ! str_starts_with( $name, $prefix ) || ! ctype_digit( substr( $name, strlen( $prefix ) ) ) ) { + continue; + } + + $id = (int) substr( $name, strlen( $prefix ) ); + $stored = get_option( $name ); + $groups[ $id ] = array_values( array_unique( array_merge( $groups[ $id ] ?? array(), is_array( $stored ) ? array_filter( $stored, 'is_string' ) : array() ) ) ); + $moved[] = $name; + } + if ( array() === $moved ) { + return; + } + + $groups = array_filter( $groups ); + if ( dws_lpmwc_get_groups_unlocked_payment_method_ids() !== $groups && ! update_option( DWS_LPMWC_OPTION_GROUPS_UNLOCKED_METHODS, $groups, true ) ) { + throw new RuntimeException( 'The payment methods unlocked for Groups groups could not be stored.' ); + } + foreach ( $moved as $name ) { + if ( ! delete_option( $name ) ) { + throw new RuntimeException( "The v1 option $name could not be deleted." ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing, never rendered. + } + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/phpstan.dist.neon b/plugins/locked-payment-methods-for-woocommerce/phpstan.dist.neon index c47ac8a..cbb80f9 100644 --- a/plugins/locked-payment-methods-for-woocommerce/phpstan.dist.neon +++ b/plugins/locked-payment-methods-for-woocommerce/phpstan.dist.neon @@ -22,3 +22,4 @@ parameters: # The scoped packages load through vendor-prefixed/scoper-autoload.php, which PHPStan does not read. scanDirectories: - vendor-prefixed + - vendor/wp-plugin/groups/lib diff --git a/plugins/locked-payment-methods-for-woocommerce/src/Plugin.php b/plugins/locked-payment-methods-for-woocommerce/src/Plugin.php index d2389c2..98cc01d 100644 --- a/plugins/locked-payment-methods-for-woocommerce/src/Plugin.php +++ b/plugins/locked-payment-methods-for-woocommerce/src/Plugin.php @@ -13,6 +13,7 @@ use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\WooCommerce\WooCommerceRequirement; use DeepWebSolutions\LockedPaymentMethods\Scoped\DI\ContainerBuilder; use DeepWebSolutions\LockedPaymentMethods\Scoped\Psr\Log\LoggerInterface as PsrLoggerInterface; +use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\GroupsStrategy; use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\OrderStrategy; use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\UserProfileStrategy; use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\UserRoleStrategy; @@ -64,6 +65,7 @@ final class Plugin { UserRoleStrategy::class, UserProfileStrategy::class, OrderStrategy::class, + GroupsStrategy::class, ); /** diff --git a/plugins/locked-payment-methods-for-woocommerce/src/Settings.php b/plugins/locked-payment-methods-for-woocommerce/src/Settings.php index b41d951..f1f3238 100644 --- a/plugins/locked-payment-methods-for-woocommerce/src/Settings.php +++ b/plugins/locked-payment-methods-for-woocommerce/src/Settings.php @@ -7,6 +7,7 @@ use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Settings\Forms; use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\WooCommerce\Setting\SectionFieldsDefinitionInterface; use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\WooCommerce\Setting\SettingsSection; +use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\GroupsStrategy; \defined( 'ABSPATH' ) || exit; @@ -214,6 +215,16 @@ protected function get_group_fields( string $group ): array { 'default' => wc_bool_to_string( DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_UNLOCK_VIA_ORDERS ] ), ), ), + 'integrations' => \array_filter( + array( + 'unlock-via-groups' => GroupsStrategy::is_available() ? array( + 'title' => __( 'Unlock via Groups', 'locked-payment-methods-for-woocommerce' ), + 'type' => 'checkbox', + 'desc' => __( 'Unlock payment methods for the members of a Groups group from the group\'s settings', 'locked-payment-methods-for-woocommerce' ), + 'default' => wc_bool_to_string( DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_UNLOCK_VIA_GROUPS ] ), + ) : array(), + ) + ), 'plugin' => array( 'remove-data-uninstall' => array( 'title' => __( 'Remove all data on uninstallation', 'locked-payment-methods-for-woocommerce' ), diff --git a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/GroupsStrategy.php b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/GroupsStrategy.php new file mode 100644 index 0000000..3e59c07 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/GroupsStrategy.php @@ -0,0 +1,250 @@ +=' ); + } + + // endregion + + // region HOOKS + + /** + * Returns false for a payment method a group of the user, or a group above one, unlocks, and the lock as given otherwise. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param bool $lock Whether the payment method stays locked. + * @param string $gateway_id The payment method's id. + * @param int $user_id The paying user, or 0 for a guest. + * + * @return bool + */ + public function unlock_group_methods( bool $lock, string $gateway_id, int $user_id ): bool { + if ( 0 === $user_id ) { + return $lock; + } + + $groups = dws_lpmwc_get_groups_unlocked_payment_method_ids(); + foreach ( (array) ( new \Groups_User( $user_id ) )->group_ids_deep as $group_id ) { // @phpstan-ignore property.notFound (Groups_User resolves its group ids through __get, as at v1's oldest Groups.) + if ( \is_numeric( $group_id ) && \in_array( $gateway_id, $groups[ (int) $group_id ] ?? array(), true ) ) { + return false; + } + } + + return $lock; + } + + /** + * Returns Groups' form markup with the field appended, for a user holding both the plugin's and Groups' capability. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string $output The markup Groups prints after its own fields. + * @param int $group_id Optional. The edited group's id. Default 0, on the add form. + * + * @return string + */ + public function render_group_field( string $output, int $group_id = 0 ): string { + $field = $this->get_field( $group_id ); + if ( ! $this->can_edit() || array() === $field['options'] ) { + return $output; + } + + $value = dws_lpmwc_get_groups_unlocked_payment_method_ids()[ $group_id ] ?? array(); + + return $output . '

' . esc_html( $field['title'] ) . '

' . Forms::render_control( $field, $value, self::ENTRY . '[' . self::FIELD . ']', self::FIELD ) . '
'; + } + + /** + * Stores the checked payment methods under the group Groups just saved, when its form carried the field. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param int $group_id The saved group's id. + * + * @return void + */ + public function save_group_field( int $group_id ): void { + if ( ! $this->can_edit() ) { + return; + } + + $submitted = isset( $_POST[ self::ENTRY ] ) && \is_array( $_POST[ self::ENTRY ] ) ? wp_unslash( $_POST[ self::ENTRY ] ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Groups verified its form's nonce, and Forms::sanitize_fields() sanitizes the field. + $field = $this->get_field( $group_id ); + $result = Forms::sanitize_fields( array( $field ), $submitted ); + if ( isset( $result['errors'][ self::FIELD ] ) ) { + $this->notices->add( new Notice( 'rejection-' . \md5( self::ENTRY . '|' . self::FIELD ), esc_html( Forms::get_rejection_message( $field['title'], $result['errors'][ self::FIELD ] ) ) ) ); + } + if ( ! isset( $result['values'][ self::FIELD ] ) ) { + return; + } + + $groups = dws_lpmwc_get_groups_unlocked_payment_method_ids(); + $groups[ $group_id ] = \is_array( $result['values'][ self::FIELD ] ) ? \array_values( \array_filter( $result['values'][ self::FIELD ], 'is_string' ) ) : array(); + update_option( DWS_LPMWC_OPTION_GROUPS_UNLOCKED_METHODS, \array_filter( $groups ), true ); + } + + /** + * Forgets the payment methods a deleted group unlocked. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param int $group_id The deleted group's id. + * + * @return void + */ + public function forget_group( int $group_id ): void { + $groups = dws_lpmwc_get_groups_unlocked_payment_method_ids(); + if ( isset( $groups[ $group_id ] ) ) { + unset( $groups[ $group_id ] ); + update_option( DWS_LPMWC_OPTION_GROUPS_UNLOCKED_METHODS, $groups, true ); + } + } + + // endregion + + // region HELPERS + + /** + * Returns whether the current user holds the plugin's capability and Groups' own, which its group screens require. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @return bool + */ + protected function can_edit(): bool { + return current_user_can( DWS_LPMWC_CAP_UNLOCK_VIA_GROUPS ) && \Groups_User::current_user_can( 'groups_admin_groups' ); + } + + /** + * Returns the group's field, which offers the locked payment methods and the group's other grants. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param int $group_id The group's id, or 0 on the add form. + * + * @return array{type: string, id: string, title: string, desc: string, options: array} + */ + protected function get_field( int $group_id ): array { + return array( + 'type' => 'checkboxes', + 'id' => self::FIELD, + 'title' => __( 'Unlocked payment methods', 'locked-payment-methods-for-woocommerce' ), + 'desc' => __( 'The group\'s members, and the members of its subgroups, may pay with these methods.', 'locked-payment-methods-for-woocommerce' ), + 'options' => dws_lpmwc_get_grant_choices( dws_lpmwc_get_groups_unlocked_payment_method_ids()[ $group_id ] ?? array(), 0 ), + ); + } + + // endregion +} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php index 92be135..e4f893a 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php @@ -8,6 +8,7 @@ use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\Permission\CapabilityGrants; use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\Scheduling\Scheduler; use DeepWebSolutions\LockedPaymentMethods\Settings; +use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\GroupsStrategy; use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\OrderStrategy; use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\UserProfileStrategy; use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\UserRoleStrategy; @@ -21,7 +22,7 @@ public function test_the_plugin_boots_every_component_whose_switch_is_on(): void self::assertSame( 'booted', $plugin->boot->status ); self::assertSame( array( Scheduler::class, AdminNotices::class, CapabilityGrants::class, Settings::class, UserRoleStrategy::class, UserProfileStrategy::class ), $plugin->kernel?->resolved ); - self::assertSame( array( OrderStrategy::class ), $plugin->kernel->skipped ); // Orders are off by default, as v1's were. + self::assertSame( array( OrderStrategy::class, GroupsStrategy::class ), $plugin->kernel->skipped ); // Orders and Groups are off by default, as v1's were. } public function test_the_entry_loads_every_include_but_those_whose_names_start_with_an_underscore(): void { diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/GroupsStrategyTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/GroupsStrategyTest.php index d0fd577..e10b264 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/GroupsStrategyTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/GroupsStrategyTest.php @@ -30,7 +30,7 @@ public static function setUpBeforeClass(): void { require_once ABSPATH . 'wp-admin/includes/user.php'; self::$strategy = new GroupsStrategy( new AdminNotices( 'dws_lpmwc', new PsrNullLogger() ) ); self::$strategy->register_hooks(); - foreach ( array( 'administrator', 'shop_manager', 'customer' ) as $role ) { + foreach ( array( 'administrator', 'shop_manager', 'customer', 'subscriber' ) as $role ) { self::$users[ $role ] = (int) wp_insert_user( array( 'user_login' => "lpm-groups-$role", @@ -103,7 +103,7 @@ public function test_a_member_of_a_group_or_of_its_subgroup_is_unlocked_for_the_ self::assertSame( array( $parent => array( 'cod' ) ), dws_lpmwc_get_groups_unlocked_payment_method_ids() ); self::assertFalse( self::should_lock( 'cod', self::$users['customer'] ) ); self::assertTrue( self::should_lock( 'bacs', self::$users['customer'] ) ); - self::assertTrue( self::should_lock( 'cod', self::$users['shop_manager'] ) ); + self::assertTrue( self::should_lock( 'cod', self::$users['subscriber'] ) ); self::assertTrue( self::should_lock( 'cod', 0 ) ); } From 6401a48ef30186675af034e118836aa3e4ad0449 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 00:05:48 +0100 Subject: [PATCH 21/50] Name Locked Payment Methods' grants meta for every object it unlocks for WooCommerce Memberships plans take the same meta key as orders, each on its own object, so the constant that named it for orders alone names it for both. Assisted-by: Claude Code:claude-opus-5-5 --- .../includes/grants.php | 6 +++--- .../includes/migrations.php | 2 +- .../src/UnlockStrategies/OrderStrategy.php | 2 +- .../UnlockStrategies/OrderStrategyTest.php | 14 +++++++------- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/grants.php b/plugins/locked-payment-methods-for-woocommerce/includes/grants.php index 9997aed..963255b 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/grants.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/grants.php @@ -53,14 +53,14 @@ function dws_lpmwc_get_user_unlocked_payment_method_ids( int $user_id ): array { } /** - * The order meta that holds the ids of the payment methods unlocked for paying the order. + * The meta that holds the ids of the payment methods unlocked for paying an order, or for a WooCommerce Memberships plan's active members. * * @since 2.0.0 * @version 2.0.0 * * @var string */ -const DWS_LPMWC_ORDER_META_UNLOCKED_METHODS = '_dws_lpmwc_unlocked_payment_methods'; +const DWS_LPMWC_META_UNLOCKED_METHODS = '_dws_lpmwc_unlocked_payment_methods'; /** * Returns the ids of the payment methods unlocked for paying an order. @@ -74,7 +74,7 @@ function dws_lpmwc_get_user_unlocked_payment_method_ids( int $user_id ): array { */ function dws_lpmwc_get_order_unlocked_payment_method_ids( int $order_id ): array { $order = 0 === $order_id ? false : wc_get_order( $order_id ); - $ids = $order instanceof WC_Order ? $order->get_meta( DWS_LPMWC_ORDER_META_UNLOCKED_METHODS ) : array(); + $ids = $order instanceof WC_Order ? $order->get_meta( DWS_LPMWC_META_UNLOCKED_METHODS ) : array(); return is_array( $ids ) ? array_values( array_filter( $ids, 'is_string' ) ) : array(); } diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php b/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php index c82d9ed..20c26d0 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php @@ -178,7 +178,7 @@ function dws_lpmwc_move_order_grants(): void { continue; } - $order->update_meta_data( DWS_LPMWC_ORDER_META_UNLOCKED_METHODS, array_values( array_unique( $granted ) ) ); + $order->update_meta_data( DWS_LPMWC_META_UNLOCKED_METHODS, array_values( array_unique( $granted ) ) ); $order->save(); if ( dws_lpmwc_get_order_unlocked_payment_method_ids( $order_id ) !== array_values( array_unique( $granted ) ) ) { throw new RuntimeException( "The payment methods unlocked for order $order_id could not be stored." ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing, never rendered. diff --git a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/OrderStrategy.php b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/OrderStrategy.php index 284fda1..df4abc8 100644 --- a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/OrderStrategy.php +++ b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/OrderStrategy.php @@ -85,7 +85,7 @@ public function get_object_form( string $form_id, int $object_id ): array { $order = wc_get_order( $object_id ); $field = array( 'type' => 'checkboxes', - 'id' => DWS_LPMWC_ORDER_META_UNLOCKED_METHODS, + 'id' => DWS_LPMWC_META_UNLOCKED_METHODS, 'title' => __( 'Unlocked payment methods', 'locked-payment-methods-for-woocommerce' ), 'desc' => __( 'The customer may pay for this order with these methods on its payment page.', 'locked-payment-methods-for-woocommerce' ), 'options' => $order instanceof \WC_Order ? dws_lpmwc_get_grant_choices( dws_lpmwc_get_order_unlocked_payment_method_ids( $object_id ), $order->get_customer_id() ) : array(), diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/OrderStrategyTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/OrderStrategyTest.php index 2af3b1b..a784e05 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/OrderStrategyTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/OrderStrategyTest.php @@ -87,7 +87,7 @@ public function test_the_plugin_runs_the_strategy_once_its_switch_is_on(): void public function test_an_order_unlocks_only_the_methods_its_list_holds_when_it_is_the_order_being_paid( bool $hpos, bool $sync ): void { $this->use_storage( $hpos, $sync ); $order = $this->create_order(); - $order->update_meta_data( DWS_LPMWC_ORDER_META_UNLOCKED_METHODS, array( 'cod' ) ); + $order->update_meta_data( DWS_LPMWC_META_UNLOCKED_METHODS, array( 'cod' ) ); $order->save(); self::assertSame( array( 'cod' ), dws_lpmwc_get_order_unlocked_payment_method_ids( $order->get_id() ) ); @@ -98,13 +98,13 @@ public function test_an_order_unlocks_only_the_methods_its_list_holds_when_it_is public function test_the_box_offers_the_locked_methods_and_the_orders_other_grants_and_marks_each_one(): void { $order = $this->create_order(); - $order->update_meta_data( DWS_LPMWC_ORDER_META_UNLOCKED_METHODS, array( 'cod', 'cheque' ) ); + $order->update_meta_data( DWS_LPMWC_META_UNLOCKED_METHODS, array( 'cod', 'cheque' ) ); $order->save(); $form = self::$strategy->get_object_form( self::FORM, $order->get_id() ); self::assertSame( DWS_LPMWC_CAP_UNLOCK_VIA_ORDERS, $form['capability'] ); - self::assertSame( DWS_LPMWC_ORDER_META_UNLOCKED_METHODS, $form['fields'][0]['id'] ); + self::assertSame( DWS_LPMWC_META_UNLOCKED_METHODS, $form['fields'][0]['id'] ); self::assertSame( array( 'cod' => 'Cash on delivery', @@ -118,7 +118,7 @@ public function test_the_box_offers_the_locked_methods_and_the_orders_other_gran public function test_the_box_of_a_paid_order_is_disabled_so_a_save_leaves_its_grants(): void { $order = $this->create_order( array( 'status' => 'processing' ) ); - $order->update_meta_data( DWS_LPMWC_ORDER_META_UNLOCKED_METHODS, array( 'cod' ) ); + $order->update_meta_data( DWS_LPMWC_META_UNLOCKED_METHODS, array( 'cod' ) ); $order->save(); wp_set_current_user( self::$admin ); @@ -141,7 +141,7 @@ public function test_saving_the_box_stores_the_checked_methods_on_the_order( boo public function test_on_its_order_pay_page_an_order_unlocks_its_methods_given_its_key(): void { $order = $this->create_order(); - $order->update_meta_data( DWS_LPMWC_ORDER_META_UNLOCKED_METHODS, array( 'cod' ) ); + $order->update_meta_data( DWS_LPMWC_META_UNLOCKED_METHODS, array( 'cod' ) ); $order->save(); $GLOBALS['wp']->query_vars['order-pay'] = (string) $order->get_id(); add_filter( 'woocommerce_is_checkout', '__return_true' ); @@ -167,7 +167,7 @@ public function test_the_grants_step_moves_v1s_order_grants_into_the_orders_list $moved->save(); $merged = $this->create_order(); $merged->update_meta_data( '_dws_wc_lpm_grant_access_cod', 'yes' ); - $merged->update_meta_data( DWS_LPMWC_ORDER_META_UNLOCKED_METHODS, array( 'cheque' ) ); + $merged->update_meta_data( DWS_LPMWC_META_UNLOCKED_METHODS, array( 'cheque' ) ); $merged->save(); dws_lpmwc_move_order_grants(); @@ -186,7 +186,7 @@ protected static function should_lock( string $gateway_id, int $order_id ): mixe } protected static function save( int $order_id, array|string $methods ): void { - $_POST[ self::FORM ] = array( DWS_LPMWC_ORDER_META_UNLOCKED_METHODS => $methods ); + $_POST[ self::FORM ] = array( DWS_LPMWC_META_UNLOCKED_METHODS => $methods ); $_POST[ self::FORM . '_nonce' ] = wp_create_nonce( self::FORM . "_nonce_$order_id" ); do_action( 'woocommerce_process_shop_order_meta', $order_id, wc_get_order( $order_id ) ); } From cf7e621304debb1e1744632d401c1957f1c84333 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 00:05:56 +0100 Subject: [PATCH 22/50] Test Locked Payment Methods' Memberships grants and their move from v1 v1's premium build kept a WooCommerce Memberships plan's grants in plan meta under v1's prefix and saved its plan tab without a nonce of its own. WooCommerceMembershipsStrategyTest, against a double of Memberships' one function and in the wc-memberships group, since Memberships is a paid plugin absent from WP Packages, expects the strategy to load only while Memberships is active at v1's oldest version and its switch is on; an active member of a plan to be unlocked for the plan's methods, and an inactive member, another user or a guest not to be; the plan data to offer a tab and its panel only to a user with the capability; a save to store the checked methods only under the form's own nonce for that plan; and the grants step to move v1's plan meta to the plugin's key and delete it. X rulings 91 and 92. Assisted-by: Claude Code:claude-opus-5-5 --- .../WooCommerceMembershipsStrategyTest.php | 167 ++++++++++++++++++ .../tests/Support/MembershipsDouble.php | 22 +++ .../tests/Support/memberships-functions.php | 14 ++ 3 files changed, 203 insertions(+) create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/WooCommerceMembershipsStrategyTest.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Support/MembershipsDouble.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Support/memberships-functions.php diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/WooCommerceMembershipsStrategyTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/WooCommerceMembershipsStrategyTest.php new file mode 100644 index 0000000..52b6fcb --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/WooCommerceMembershipsStrategyTest.php @@ -0,0 +1,167 @@ +register_hooks(); + foreach ( array( 'administrator', 'customer', 'subscriber' ) as $role ) { + self::$users[ $role ] = (int) wp_insert_user( + array( + 'user_login' => "lpm-memberships-$role", + 'user_pass' => 'password', + 'user_email' => "lpm-memberships-$role@example.org", + 'role' => $role, + ) + ); + } + } + + #[\Override] + public static function tearDownAfterClass(): void { + foreach ( self::$users as $user ) { + wp_delete_user( $user ); + } + unregister_post_type( 'wc_membership_plan' ); + } + + // The form evaluates its definition and saves an object once per request, so every row edits a plan of its own. + #[Before] + protected function store_rows(): void { + foreach ( self::KEYS as $key ) { + $this->stored[ $key ] = get_option( $key ); + } + update_option( DWS_LPMWC_OPTION_LOCKED_METHODS, array( 'cod', 'bacs' ) ); + update_option( 'wc_memberships_version', '1.27.0' ); + $this->plan = (int) wp_insert_post( + array( + 'post_type' => 'wc_membership_plan', + 'post_title' => 'LPM plan', + 'post_status' => 'publish', + ) + ); + } + + #[After] + protected function restore_rows(): void { + wp_delete_post( $this->plan, true ); + foreach ( $this->stored as $key => $value ) { + false === $value ? delete_option( $key ) : update_option( $key, $value ); + } + MembershipsDouble::$memberships = array(); + unset( $_POST[ self::FORM ], $_POST[ self::FORM . '_nonce' ], $GLOBALS['post'] ); + wp_set_current_user( 0 ); + } + + public function test_the_strategy_loads_only_while_memberships_is_active_at_v1s_oldest_version_and_its_switch_is_on(): void { + update_option( DWS_LPMWC_OPTION_UNLOCK_VIA_WC_MEMBERSHIPS, 'yes' ); + self::assertTrue( WooCommerceMembershipsStrategy::is_available() ); + self::assertTrue( WooCommerceMembershipsStrategy::should_load() ); + + update_option( 'wc_memberships_version', '1.17.9' ); + self::assertFalse( WooCommerceMembershipsStrategy::is_available() ); + + update_option( 'wc_memberships_version', '1.27.0' ); + delete_option( DWS_LPMWC_OPTION_UNLOCK_VIA_WC_MEMBERSHIPS ); + self::assertFalse( WooCommerceMembershipsStrategy::should_load() ); + } + + public function test_an_active_member_of_a_plan_is_unlocked_for_the_plans_methods(): void { + update_post_meta( $this->plan, DWS_LPMWC_META_UNLOCKED_METHODS, array( 'cod' ) ); + MembershipsDouble::add( self::$users['customer'], $this->plan ); + MembershipsDouble::add( self::$users['subscriber'], $this->plan, false ); + + self::assertSame( array( 'cod' ), dws_lpmwc_get_plan_unlocked_payment_method_ids( $this->plan ) ); + self::assertFalse( self::should_lock( 'cod', self::$users['customer'] ) ); + self::assertTrue( self::should_lock( 'bacs', self::$users['customer'] ) ); + self::assertTrue( self::should_lock( 'cod', self::$users['subscriber'] ) ); + self::assertTrue( self::should_lock( 'cod', 0 ) ); + } + + public function test_the_plan_data_offers_a_tab_and_its_panel_to_a_user_with_the_capability(): void { + $GLOBALS['post'] = get_post( $this->plan ); + update_post_meta( $this->plan, DWS_LPMWC_META_UNLOCKED_METHODS, array( 'cod' ) ); + + wp_set_current_user( self::$users['customer'] ); + self::assertSame( array(), apply_filters( 'wc_membership_plan_data_tabs', array() ) ); + + wp_set_current_user( self::$users['administrator'] ); + self::assertSame( 'dws-lpmwc-membership-plan-data', apply_filters( 'wc_membership_plan_data_tabs', array() )[ self::FORM ]['target'] ?? null ); + + \ob_start(); + do_action( 'wc_membership_plan_data_panels' ); + $panel = \Dom\HTMLDocument::createFromString( '' . \ob_get_clean() . '', \LIBXML_NOERROR ); + + $choices = array(); + foreach ( $panel->querySelectorAll( '#dws-lpmwc-membership-plan-data input[type="checkbox"]' ) as $input ) { + $choices[ (string) $input->getAttribute( 'value' ) ] = $input->hasAttribute( 'checked' ); + } + self::assertSame( + array( + 'cod' => true, + 'bacs' => false, + ), + $choices + ); + } + + public function test_saving_the_plan_stores_the_checked_methods_under_its_own_nonce(): void { + wp_set_current_user( self::$users['administrator'] ); + $_POST[ self::FORM ] = array( DWS_LPMWC_META_UNLOCKED_METHODS => array( 'bacs' ) ); + + $_POST[ self::FORM . '_nonce' ] = wp_create_nonce( self::FORM . '_nonce_' . ( $this->plan + 1 ) ); + do_action( 'wc_memberships_save_meta_box', array(), 'wc-memberships-membership-plan-data', $this->plan, get_post( $this->plan ) ); + self::assertSame( array(), dws_lpmwc_get_plan_unlocked_payment_method_ids( $this->plan ) ); + + $_POST[ self::FORM . '_nonce' ] = wp_create_nonce( self::FORM . "_nonce_$this->plan" ); + do_action( 'wc_memberships_save_meta_box', array(), 'wc-memberships-membership-plan-data', $this->plan, get_post( $this->plan ) ); + self::assertSame( array( 'bacs' ), dws_lpmwc_get_plan_unlocked_payment_method_ids( $this->plan ) ); + } + + public function test_the_grants_step_moves_v1s_plan_meta_to_the_plugins_key(): void { + update_post_meta( $this->plan, '_dws_wc_lpm_unlocked_payment_methods', array( 'cod', 7 ) ); + update_post_meta( $this->plan, DWS_LPMWC_META_UNLOCKED_METHODS, array( 'cheque' ) ); + + dws_lpmwc_move_plan_grants(); + dws_lpmwc_move_plan_grants(); + + self::assertSame( array( 'cheque', 'cod' ), dws_lpmwc_get_plan_unlocked_payment_method_ids( $this->plan ) ); + self::assertSame( '', get_post_meta( $this->plan, '_dws_wc_lpm_unlocked_payment_methods', true ) ); + } + + protected static function should_lock( string $gateway_id, int $user_id ): mixed { + return apply_filters( 'dws_lpmwc/payment_method/should_lock', true, $gateway_id, $user_id, 0 ); + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Support/MembershipsDouble.php b/plugins/locked-payment-methods-for-woocommerce/tests/Support/MembershipsDouble.php new file mode 100644 index 0000000..edfb399 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Support/MembershipsDouble.php @@ -0,0 +1,22 @@ +plan_id; + } + + public function is_active(): bool { + return $this->active; + } + }; + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Support/memberships-functions.php b/plugins/locked-payment-methods-for-woocommerce/tests/Support/memberships-functions.php new file mode 100644 index 0000000..806a477 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Support/memberships-functions.php @@ -0,0 +1,14 @@ + Date: Sun, 1 Nov 2026 00:08:25 +0100 Subject: [PATCH 23/50] Unlock payment methods for Memberships plans in Locked Payment Methods v1's premium build kept a WooCommerce Memberships plan's grants in plan meta under v1's prefix and saved its plan tab without a nonce of its own. A plan's grants are its post meta under the plugin's key. WooCommerceMembershipsStrategy, gated on Memberships at v1's oldest version and on its switch, off by default, returns false on should_lock for a method a plan the user is an active member of unlocks, skipping the cancelled memberships Memberships sometimes returns. Its form shows as a tab of the plan data box through MembershipPlanTab, a host on the framework's ObjectFormHostInterface, so the form prints and verifies its own nonce for the plan, behind unlock_dws_lpm_via_wc_memberships and edit_post. The settings show the Memberships switch while Memberships is active. The fifth migration step moves v1's plan meta to the plugin's key and deletes it. Memberships is a paid plugin absent from WP Packages, so the rows run against a double of its one function and its three hooks, unverified against a licensed copy; PHPStan reads the function from a declaration in tests/PHPStan. X rulings 91 and 92. Assisted-by: Claude Code:claude-opus-5-5 --- .../includes/grants.php | 16 ++ .../includes/migrations.php | 35 ++++ .../phpstan.dist.neon | 1 + .../EditScreen/Hosts/MembershipPlanTab.php | 157 ++++++++++++++++++ .../src/Plugin.php | 2 + .../src/Settings.php | 9 +- .../WooCommerceMembershipsStrategy.php | 153 +++++++++++++++++ .../tests/Integration/PluginBootTest.php | 3 +- .../tests/PHPStan/woocommerce-memberships.php | 16 ++ 9 files changed, 390 insertions(+), 2 deletions(-) create mode 100644 plugins/locked-payment-methods-for-woocommerce/src/EditScreen/Hosts/MembershipPlanTab.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/WooCommerceMembershipsStrategy.php create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/PHPStan/woocommerce-memberships.php diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/grants.php b/plugins/locked-payment-methods-for-woocommerce/includes/grants.php index 963255b..fa5c5ab 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/grants.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/grants.php @@ -153,3 +153,19 @@ function dws_lpmwc_get_groups_unlocked_payment_method_ids(): array { return $groups; } + +/** + * Returns the ids of the payment methods unlocked for a WooCommerce Memberships plan's active members. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param int $plan_id The plan's id. + * + * @return string[] + */ +function dws_lpmwc_get_plan_unlocked_payment_method_ids( int $plan_id ): array { + $ids = get_post_meta( $plan_id, DWS_LPMWC_META_UNLOCKED_METHODS, true ); + + return is_array( $ids ) ? array_values( array_filter( $ids, 'is_string' ) ) : array(); +} diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php b/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php index 20c26d0..03d8480 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php @@ -24,6 +24,7 @@ function dws_lpmwc_get_migration_steps(): array { 'dws_lpmwc_move_user_grants', 'dws_lpmwc_move_order_grants', 'dws_lpmwc_move_group_grants', + 'dws_lpmwc_move_plan_grants', ); } @@ -234,3 +235,37 @@ function dws_lpmwc_move_group_grants(): void { } } } + +/** + * Moves v1's WooCommerce Memberships grants from each plan's v1 meta to the plugin's key beside the grants it holds, then deletes the v1 meta; it reads the post meta table itself, since Memberships may be inactive and its plan type unregistered. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @global wpdb $wpdb WordPress's database access. + * + * @throws RuntimeException Thrown when v1's plan meta could not be read, a plan's grants could not be stored or v1's meta could not be deleted. + * + * @return void + */ +function dws_lpmwc_move_plan_grants(): void { + global $wpdb; + + $v1_key = '_dws_wc_lpm_unlocked_payment_methods'; + $wpdb->flush(); + $plans = wp_parse_id_list( $wpdb->get_col( $wpdb->prepare( 'SELECT DISTINCT post_id FROM %i WHERE meta_key = %s', $wpdb->postmeta, $v1_key ) ?? '' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- Memberships' plan type may be unregistered, so no post query lists the plans. + if ( '' !== $wpdb->last_error ) { + throw new RuntimeException( 'The v1 plan grants could not be read.' ); + } + + foreach ( $plans as $plan_id ) { + $v1 = get_post_meta( $plan_id, $v1_key, true ); + $merged = array_values( array_unique( array_merge( dws_lpmwc_get_plan_unlocked_payment_method_ids( $plan_id ), is_array( $v1 ) ? array_filter( $v1, 'is_string' ) : array() ) ) ); + if ( dws_lpmwc_get_plan_unlocked_payment_method_ids( $plan_id ) !== $merged && false === update_post_meta( $plan_id, DWS_LPMWC_META_UNLOCKED_METHODS, wp_slash( $merged ) ) ) { + throw new RuntimeException( "The payment methods unlocked for plan $plan_id could not be stored." ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing, never rendered. + } + if ( ! delete_post_meta( $plan_id, $v1_key ) ) { + throw new RuntimeException( "The v1 grants of plan $plan_id could not be deleted." ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing, never rendered. + } + } +} diff --git a/plugins/locked-payment-methods-for-woocommerce/phpstan.dist.neon b/plugins/locked-payment-methods-for-woocommerce/phpstan.dist.neon index cbb80f9..1d2e4a3 100644 --- a/plugins/locked-payment-methods-for-woocommerce/phpstan.dist.neon +++ b/plugins/locked-payment-methods-for-woocommerce/phpstan.dist.neon @@ -17,6 +17,7 @@ parameters: - includes/safeguards.php scanFiles: - includes/safeguards.php + - tests/PHPStan/woocommerce-memberships.php - vendor/php-stubs/woocommerce-stubs/woocommerce-stubs.php - vendor/php-stubs/woocommerce-stubs/woocommerce-packages-stubs.php # The scoped packages load through vendor-prefixed/scoper-autoload.php, which PHPStan does not read. diff --git a/plugins/locked-payment-methods-for-woocommerce/src/EditScreen/Hosts/MembershipPlanTab.php b/plugins/locked-payment-methods-for-woocommerce/src/EditScreen/Hosts/MembershipPlanTab.php new file mode 100644 index 0000000..bbd3a03 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/src/EditScreen/Hosts/MembershipPlanTab.php @@ -0,0 +1,157 @@ +form = $form; + add_filter( 'wc_membership_plan_data_tabs', array( $this, 'add_tab' ) ); + add_action( 'wc_membership_plan_data_panels', array( $this, 'render_panel' ) ); + add_action( 'wc_memberships_save_meta_box', array( $this, 'save_panel' ), 10, 3 ); + } + + /** + * {@inheritDoc} + * + * @since 2.0.0 + * @version 2.0.0 + */ + #[\Override] + public function get_object_capability(): string { + return 'edit_post'; + } + + /** + * {@inheritDoc} + * + * @since 2.0.0 + * @version 2.0.0 + */ + #[\Override] + public function get_default_store(): ObjectStoreInterface { + return new MetadataStore( 'post' ); + } + + /** + * {@inheritDoc} + * + * @since 2.0.0 + * @version 2.0.0 + */ + #[\Override] + public function render_rows( string $title, array $rows ): void { + foreach ( $rows as $row ) { + echo '

' . $row['title'] . '

' . $row['control'] . '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- The form escapes titles and controls. + } + } + + // endregion + + // region HOOKS + + /** + * Adds the form's tab to the plan data box, for a user who may edit the plan with it. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param array> $tabs The box's tabs, by id. + * + * @return array> + */ + public function add_tab( array $tabs ): array { + $plan_id = (int) get_the_ID(); + if ( 0 !== $plan_id && $this->form->can_render( $plan_id ) ) { + $tabs[ $this->form->get_id() ] = array( + 'label' => $this->form->get_title( $plan_id ), + 'target' => $this->get_panel_id(), + ); + } + + return $tabs; + } + + /** + * Prints the form's panel in the plan data box. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @return void + */ + public function render_panel(): void { + echo '
'; + $this->form->render( (int) get_the_ID() ); + echo '
'; + } + + /** + * Saves the form with the plan's data box. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param mixed $post_data The data Memberships saves. + * @param mixed $meta_box_id The id of the box Memberships saves. + * @param int $plan_id The plan's id. + * + * @return void + */ + public function save_panel( mixed $post_data, mixed $meta_box_id, int $plan_id ): void { + $this->form->save( $plan_id ); + } + + // endregion + + // region HELPERS + + /** + * Returns the id of the form's panel. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @return string + */ + protected function get_panel_id(): string { + return \str_replace( '_', '-', $this->form->get_id() ) . '-data'; + } + + // endregion +} diff --git a/plugins/locked-payment-methods-for-woocommerce/src/Plugin.php b/plugins/locked-payment-methods-for-woocommerce/src/Plugin.php index 98cc01d..9445a6a 100644 --- a/plugins/locked-payment-methods-for-woocommerce/src/Plugin.php +++ b/plugins/locked-payment-methods-for-woocommerce/src/Plugin.php @@ -17,6 +17,7 @@ use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\OrderStrategy; use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\UserProfileStrategy; use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\UserRoleStrategy; +use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\WooCommerceMembershipsStrategy; \defined( 'ABSPATH' ) || exit; @@ -66,6 +67,7 @@ final class Plugin { UserProfileStrategy::class, OrderStrategy::class, GroupsStrategy::class, + WooCommerceMembershipsStrategy::class, ); /** diff --git a/plugins/locked-payment-methods-for-woocommerce/src/Settings.php b/plugins/locked-payment-methods-for-woocommerce/src/Settings.php index f1f3238..4d2833b 100644 --- a/plugins/locked-payment-methods-for-woocommerce/src/Settings.php +++ b/plugins/locked-payment-methods-for-woocommerce/src/Settings.php @@ -8,6 +8,7 @@ use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\WooCommerce\Setting\SectionFieldsDefinitionInterface; use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\WooCommerce\Setting\SettingsSection; use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\GroupsStrategy; +use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\WooCommerceMembershipsStrategy; \defined( 'ABSPATH' ) || exit; @@ -217,12 +218,18 @@ protected function get_group_fields( string $group ): array { ), 'integrations' => \array_filter( array( - 'unlock-via-groups' => GroupsStrategy::is_available() ? array( + 'unlock-via-groups' => GroupsStrategy::is_available() ? array( 'title' => __( 'Unlock via Groups', 'locked-payment-methods-for-woocommerce' ), 'type' => 'checkbox', 'desc' => __( 'Unlock payment methods for the members of a Groups group from the group\'s settings', 'locked-payment-methods-for-woocommerce' ), 'default' => wc_bool_to_string( DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_UNLOCK_VIA_GROUPS ] ), ) : array(), + 'unlock-via-wc-memberships' => WooCommerceMembershipsStrategy::is_available() ? array( + 'title' => __( 'Unlock via WooCommerce Memberships', 'locked-payment-methods-for-woocommerce' ), + 'type' => 'checkbox', + 'desc' => __( 'Unlock payment methods for the active members of a membership plan from the plan\'s data', 'locked-payment-methods-for-woocommerce' ), + 'default' => wc_bool_to_string( DWS_LPMWC_OPTION_DEFAULTS[ DWS_LPMWC_OPTION_UNLOCK_VIA_WC_MEMBERSHIPS ] ), + ) : array(), ) ), 'plugin' => array( diff --git a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/WooCommerceMembershipsStrategy.php b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/WooCommerceMembershipsStrategy.php new file mode 100644 index 0000000..6c1c350 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/WooCommerceMembershipsStrategy.php @@ -0,0 +1,153 @@ +notices )->register_hooks(); + } + + /** + * {@inheritDoc} + * + * @since 2.0.0 + * @version 2.0.0 + */ + #[\Override] + public function get_object_form( string $form_id, int $object_id ): array { + $options = dws_lpmwc_get_grant_choices( dws_lpmwc_get_plan_unlocked_payment_method_ids( $object_id ), 0 ); + + return array( + 'title' => __( 'Unlocked Payment Methods', 'locked-payment-methods-for-woocommerce' ), + 'capability' => DWS_LPMWC_CAP_UNLOCK_VIA_WC_MEMBERSHIPS, + 'fields' => array() === $options ? array() : array( + array( + 'type' => 'checkboxes', + 'id' => DWS_LPMWC_META_UNLOCKED_METHODS, + 'title' => __( 'Unlocked payment methods', 'locked-payment-methods-for-woocommerce' ), + 'desc' => __( 'The plan\'s active members may pay with these methods.', 'locked-payment-methods-for-woocommerce' ), + 'options' => $options, + ), + ), + ); + } + + // endregion + + // region METHODS + + /** + * Returns whether WooCommerce Memberships is active at the oldest version v1 supported. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @return bool + */ + public static function is_available(): bool { + $version = get_option( 'wc_memberships_version', '0.0.0' ); + + return \function_exists( 'wc_memberships_get_user_active_memberships' ) && \is_string( $version ) && \version_compare( $version, '1.18.0', '>=' ); + } + + // endregion + + // region HOOKS + + /** + * Returns false for a payment method a plan the user is an active member of unlocks, and the lock as given otherwise. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param bool $lock Whether the payment method stays locked. + * @param string $gateway_id The payment method's id. + * @param int $user_id The paying user, or 0 for a guest. + * + * @return bool + */ + public function unlock_member_methods( bool $lock, string $gateway_id, int $user_id ): bool { + if ( 0 === $user_id ) { + return $lock; + } + + foreach ( (array) wc_memberships_get_user_active_memberships( $user_id ) as $membership ) { + // Memberships sometimes returns cancelled memberships among the active ones. + $plan_id = \is_object( $membership ) && \method_exists( $membership, 'is_active' ) && \method_exists( $membership, 'get_plan_id' ) && true === $membership->is_active() ? $membership->get_plan_id() : null; + if ( \is_numeric( $plan_id ) && \in_array( $gateway_id, dws_lpmwc_get_plan_unlocked_payment_method_ids( (int) $plan_id ), true ) ) { + return false; + } + } + + return $lock; + } + + // endregion +} diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php index e4f893a..f8694df 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php @@ -12,6 +12,7 @@ use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\OrderStrategy; use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\UserProfileStrategy; use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\UserRoleStrategy; +use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\WooCommerceMembershipsStrategy; use PHPUnit\Framework\Attributes\CoversNothing; use PHPUnit\Framework\TestCase; @@ -22,7 +23,7 @@ public function test_the_plugin_boots_every_component_whose_switch_is_on(): void self::assertSame( 'booted', $plugin->boot->status ); self::assertSame( array( Scheduler::class, AdminNotices::class, CapabilityGrants::class, Settings::class, UserRoleStrategy::class, UserProfileStrategy::class ), $plugin->kernel?->resolved ); - self::assertSame( array( OrderStrategy::class, GroupsStrategy::class ), $plugin->kernel->skipped ); // Orders and Groups are off by default, as v1's were. + self::assertSame( array( OrderStrategy::class, GroupsStrategy::class, WooCommerceMembershipsStrategy::class ), $plugin->kernel->skipped ); // Orders and the integrations are off by default, as v1's were. } public function test_the_entry_loads_every_include_but_those_whose_names_start_with_an_underscore(): void { diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/PHPStan/woocommerce-memberships.php b/plugins/locked-payment-methods-for-woocommerce/tests/PHPStan/woocommerce-memberships.php new file mode 100644 index 0000000..5cd8268 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/PHPStan/woocommerce-memberships.php @@ -0,0 +1,16 @@ + $args The query's arguments. + * + * @return array + */ +function wc_memberships_get_user_active_memberships( $user_id = null, $args = array() ) {} // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- A declaration only. From 794ca0075d1fdfdb10e7640e5d971b8ee198dd07 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 00:12:00 +0100 Subject: [PATCH 24/50] Test Locked Payment Methods' v1 API as deprecated shims v1 exposed root getters, hook-tag builders, constants, boot hooks, access checks and setting readers that returned true for "locked" under names that read the other way and took v1's field ids, so code written against v1 calls them. DeprecatedApiTest expects v1's getters and constants to answer from the plugin header, v1's hook-tag builders to build v1's names for the plugin and for v1's twelve components, v1's boot hooks to fire after the v2 ones, dws_lpmwc_is_payment_method_locked() to answer for a user, v1's access checks to fire v1's filters and then answer from the strategies, v1's per-strategy checks to answer from their strategy alone and true while it is off, and v1's setting readers to map v1's ids to the plugin's options through v1's value filters, each shim deprecated with its replacement. X rulings 94 and 95. Assisted-by: Claude Code:claude-opus-5-5 --- .../tests/Integration/DeprecatedApiTest.php | 257 ++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Integration/DeprecatedApiTest.php diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/DeprecatedApiTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/DeprecatedApiTest.php new file mode 100644 index 0000000..cff59ff --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/DeprecatedApiTest.php @@ -0,0 +1,257 @@ + array( 'dws_lpmwc_get_hook_tag', array( 'check_payment_method_access_for_user' ), 'locked_payment_methods_for_woocommerce/check_payment_method_access_for_user' ), + 'plugin-unsafe' => array( 'dws_lpmwc_get_hook_tag', array( 'Some Name', array( 'A\\B', '' ) ), 'locked_payment_methods_for_woocommerce/some_name/a_b' ), + 'component' => array( 'dws_lpmwc_get_component_hook_tag', array( 'lock-manager', 'is_locked_payment_method', array( 'cod' ) ), 'locked_payment_methods_for_woocommerce/lock_manager/is_locked_payment_method/cod' ), + 'component-group' => array( 'dws_lpmwc_get_component_hook_tag', array( 'general-settings', 'get_group_fields' ), 'locked_payment_methods_for_woocommerce/general_settings/get_group_fields' ), + 'component-strategy' => array( 'dws_lpmwc_get_component_hook_tag', array( 'user-meta-strategy', 'name' ), 'locked_payment_methods_for_woocommerce/user_meta_unlock_strategy/name' ), + 'component-premium' => array( 'dws_lpmwc_get_component_hook_tag', array( 'wc-memberships-strategy', 'name' ), 'locked_payment_methods_for_woocommerce/wc_memberships_unlock_strategy/name' ), + 'component-integrate' => array( 'dws_lpmwc_get_component_hook_tag', array( 'integrations-settings', 'get_group_fields' ), 'locked_payment_methods_for_woocommerce/integrations_settings/get_group_fields' ), + 'component-absent' => array( 'dws_lpmwc_get_component_hook_tag', array( 'freemius', 'name' ), null ), + ); + } + + #[\Override] + public static function setUpBeforeClass(): void { + require_once ABSPATH . 'wp-admin/includes/user.php'; + foreach ( array( 'shop_manager', 'customer' ) as $role ) { + self::$users[ $role ] = (int) wp_insert_user( + array( + 'user_login' => "lpm-deprecated-$role", + 'user_pass' => 'password', + 'user_email' => "lpm-deprecated-$role@example.org", + 'role' => $role, + ) + ); + } + } + + #[\Override] + public static function tearDownAfterClass(): void { + foreach ( self::$users as $user ) { + wp_delete_user( $user ); + } + } + + #[Before] + protected function record_deprecations(): void { + foreach ( self::KEYS as $key ) { + $this->stored[ $key ] = get_option( $key ); + } + update_option( DWS_LPMWC_OPTION_LOCKED_METHODS, array( 'cod' ) ); + update_option( DWS_LPMWC_OPTION_FULL_ACCESS_ROLES, array( 'shop_manager' ) ); + add_filter( 'deprecated_function_trigger_error', '__return_false' ); + add_filter( 'deprecated_hook_trigger_error', '__return_false' ); + add_action( 'deprecated_function_run', array( $this, 'record' ), 10, 2 ); + add_action( 'deprecated_hook_run', array( $this, 'record' ), 10, 2 ); + } + + #[After] + protected function stop_recording(): void { + foreach ( $this->stored as $key => $value ) { + false === $value ? delete_option( $key ) : update_option( $key, $value ); + } + remove_filter( 'deprecated_function_trigger_error', '__return_false' ); + remove_filter( 'deprecated_hook_trigger_error', '__return_false' ); + remove_action( 'deprecated_function_run', array( $this, 'record' ) ); + remove_action( 'deprecated_hook_run', array( $this, 'record' ) ); + } + + public function record( string $name, string $replacement ): void { + $this->deprecated[] = array( $name, $replacement ); + } + + public function test_the_v1_getters_answer_from_the_plugin_header(): void { + $header = get_file_data( + DWS_LPMWC_FILE, + array( + 'Version' => 'Version', + 'RequiresWP' => 'Requires at least', + 'RequiresPHP' => 'Requires PHP', + ) + ); + + self::assertSame( dws_lpmwc_get_plugin_name(), dws_lpmwc_name() ); + self::assertSame( $header['Version'], dws_lpmwc_version() ); + self::assertSame( DWS_LPMWC_FILE, dws_lpmwc_path() ); + self::assertSame( $header['RequiresPHP'], dws_lpmwc_min_php() ); + self::assertSame( $header['RequiresWP'], dws_lpmwc_min_wp() ); + self::assertSame( + array( + array( 'dws_lpmwc_name', 'dws_lpmwc_get_plugin_name()' ), + array( 'dws_lpmwc_version', 'dws_lpmwc_get_plugin_version()' ), + array( 'dws_lpmwc_path', '' ), + array( 'dws_lpmwc_min_php', '' ), + array( 'dws_lpmwc_min_wp', '' ), + ), + $this->deprecated + ); + } + + public function test_the_v1_constants_hold_the_plugin_header_without_v1s_whitelabel_prefix(): void { + $header = get_file_data( + DWS_LPMWC_FILE, + array( + 'Version' => 'Version', + 'RequiresWP' => 'Requires at least', + 'RequiresPHP' => 'Requires PHP', + ) + ); + + self::assertSame( 'Locked Payment Methods for WooCommerce', DWS_LPMWC_NAME ); + self::assertSame( $header['Version'], DWS_LPMWC_VERSION ); + self::assertSame( DWS_LPMWC_FILE, DWS_LPMWC_PATH ); + self::assertSame( $header['RequiresPHP'], DWS_LPMWC_MIN_PHP ); + self::assertSame( $header['RequiresWP'], DWS_LPMWC_MIN_WP ); + } + + #[DataProvider( 'hook_tags' )] + public function test_a_v1_hook_tag_builds_as_v1_built_it( string $shim, array $arguments, ?string $expected ): void { + self::assertSame( $expected, $shim( ...$arguments ) ); + self::assertSame( array( array( $shim, '' ) ), $this->deprecated ); + } + + public function test_the_v1_boot_hooks_fire_after_the_v2_ones_with_the_failure(): void { + $fired = array(); + $spy = static function ( mixed $error = null ) use ( &$fired ): void { + $fired[] = array( current_action(), $error ); + }; + add_action( 'dws_lpmwc_initialized', $spy ); + add_action( 'dws_lpmwc_initialization_failure', $spy ); + $error = new \RuntimeException( 'Failed.' ); + + try { + do_action( 'dws_lpmwc/boot/succeeded' ); + do_action( 'dws_lpmwc/boot/failed', $error ); + } finally { + remove_action( 'dws_lpmwc_initialized', $spy ); + remove_action( 'dws_lpmwc_initialization_failure', $spy ); + } + + self::assertSame( + array( + array( 'dws_lpmwc_initialized', null ), + array( 'dws_lpmwc_initialization_failure', $error ), + ), + $fired + ); + self::assertSame( + array( + array( 'dws_lpmwc_initialized', 'dws_lpmwc/boot/succeeded' ), + array( 'dws_lpmwc_initialization_failure', 'dws_lpmwc/boot/failed' ), + ), + $this->deprecated + ); + } + + public function test_a_payment_method_is_locked_for_a_user_when_it_is_listed_and_no_strategy_unlocks_it(): void { + self::assertTrue( dws_lpmwc_is_payment_method_locked( 'cod', self::$users['customer'] ) ); + self::assertFalse( dws_lpmwc_is_payment_method_locked( 'cod', self::$users['shop_manager'] ) ); + self::assertFalse( dws_lpmwc_is_payment_method_locked( 'bacs', self::$users['customer'] ) ); + self::assertSame( array(), $this->deprecated ); + } + + public function test_v1s_access_checks_answer_true_for_locked_from_the_strategies_alone_after_v1s_filter(): void { + $v1 = 'locked_payment_methods_for_woocommerce/check_payment_method_access_for_user'; + $seen = array(); + $spy = static function ( mixed ...$args ) use ( &$seen ): mixed { + $seen[] = $args; + + return $args[0]; + }; + add_filter( $v1, $spy, 10, 3 ); + + try { + self::assertTrue( dws_lpmwc_check_payment_method_access_for_user( 'bacs', self::$users['customer'] ) ); + self::assertFalse( dws_lpmwc_check_payment_method_access_for_user( 'bacs', self::$users['shop_manager'] ) ); + } finally { + remove_filter( $v1, $spy ); + } + + self::assertSame( array( array( true, 'bacs', self::$users['customer'] ), array( true, 'bacs', self::$users['shop_manager'] ) ), $seen ); + self::assertContains( array( 'dws_lpmwc_check_payment_method_access_for_user', 'dws_lpmwc_is_payment_method_locked()' ), $this->deprecated ); + self::assertContains( array( $v1, 'dws_lpmwc/payment_method/should_lock' ), $this->deprecated ); + } + + public function test_v1s_order_access_check_judges_the_order_and_its_customer_after_v1s_filter(): void { + $order = wc_create_order( array( 'customer_id' => self::$users['shop_manager'] ) ); + $v1 = 'locked_payment_methods_for_woocommerce/check_payment_method_access_for_wc_order'; + $seen = array(); + $spy = static function ( mixed ...$args ) use ( &$seen ): mixed { + $seen[] = $args; + + return $args[0]; + }; + add_filter( $v1, $spy, 10, 3 ); + + try { + self::assertFalse( dws_lpmwc_check_payment_method_access_for_wc_order( 'cod', $order->get_id() ) ); + } finally { + remove_filter( $v1, $spy ); + $order->delete( true ); + } + + self::assertSame( array( array( true, 'cod', $order->get_id() ) ), $seen ); + self::assertContains( array( $v1, 'dws_lpmwc/payment_method/should_lock' ), $this->deprecated ); + } + + public function test_v1s_per_strategy_checks_answer_from_their_strategy_alone_and_true_while_it_is_off(): void { + $order = wc_create_order(); + $order->update_meta_data( DWS_LPMWC_META_UNLOCKED_METHODS, array( 'cod' ) ); + $order->save(); + + try { + self::assertFalse( dws_lpmwc_check_payment_method_access_via_user_roles( 'cod', self::$users['shop_manager'] ) ); + self::assertTrue( dws_lpmwc_check_payment_method_access_via_user_roles( 'cod', self::$users['customer'] ) ); + self::assertTrue( dws_lpmwc_check_payment_method_access_via_user_meta( 'cod', self::$users['shop_manager'] ) ); + self::assertTrue( dws_lpmwc_check_payment_method_access_via_wc_order_meta( 'cod', $order->get_id() ) ); + + update_option( DWS_LPMWC_OPTION_UNLOCK_VIA_ORDERS, 'yes' ); + self::assertFalse( dws_lpmwc_check_payment_method_access_via_wc_order_meta( 'cod', $order->get_id() ) ); + + update_option( DWS_LPMWC_OPTION_UNLOCK_VIA_USER_ROLES, 'no' ); + self::assertTrue( dws_lpmwc_check_payment_method_access_via_user_roles( 'cod', self::$users['shop_manager'] ) ); + } finally { + $order->delete( true ); + } + + self::assertContains( array( 'dws_lpmwc_check_payment_method_access_via_user_roles', 'dws_lpmwc_is_payment_method_locked()' ), $this->deprecated ); + } + + public function test_v1s_setting_readers_map_v1s_ids_to_the_plugins_options_through_v1s_filters(): void { + update_option( DWS_LPMWC_OPTION_UNLOCK_VIA_USER_ROLES, 'yes' ); + $page = 'locked_payment_methods_for_woocommerce/settings/get_validated_option_value'; + + self::assertSame( array( 'cod' ), dws_lpmwc_get_validated_general_setting( 'locked-payment-methods' ) ); + self::assertTrue( dws_lpmwc_get_validated_setting( 'unlock-via-user-role', 'general' ) ); + self::assertSame( array( 'shop_manager' ), dws_lpmwc_get_validated_setting( 'general/full-access-user-roles' ) ); + self::assertSame( 'yes', dws_lpmwc_get_raw_setting( 'unlock-via-user-role', 'general' ) ); + self::assertFalse( dws_lpmwc_validate_setting( 'maybe-not', 'remove-data-uninstall', 'plugin' ) ); + self::assertSame( array( 'bacs' ), dws_lpmwc_validate_setting( array( 'bacs', 7 ), 'locked-payment-methods', 'general' ) ); + self::assertNull( dws_lpmwc_get_validated_setting( 'unknown', 'general' ) ); + + self::assertContains( array( 'locked_payment_methods_for_woocommerce/general_settings/get_validated_option_value', 'option_' . DWS_LPMWC_OPTION_FULL_ACCESS_ROLES ), $this->deprecated ); + self::assertNotContains( array( $page, 'option_' . DWS_LPMWC_OPTION_FULL_ACCESS_ROLES ), $this->deprecated ); // No callback hooks the page's filter, so it reports no deprecation. + self::assertContains( array( 'dws_lpmwc_get_validated_general_setting', 'get_option()' ), $this->deprecated ); + } +} From c50138281b38b7d2f3518c18b49248599541dce3 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 00:15:28 +0100 Subject: [PATCH 25/50] Keep Locked Payment Methods' v1 API as deprecated shims v1 exposed root getters, hook-tag builders, constants, boot hooks, access checks and setting readers that returned true for "locked" under names that read the other way, ignored the locked list and took v1's field ids, which no longer name options. includes/deprecated.php keeps them through 2.x, each deprecated with its replacement: the getters and constants answer from the plugin header, without v1's whitelabel prefix; the hook-tag builders build v1's names for the plugin and its twelve v1 components; v1's boot hooks fire after the v2 ones; the access checks fire v1's per-user and per-order filters, then answer from the strategies; the per-strategy checks answer from their strategy alone, through an internal static check each strategy's hook now wraps, and true while it is off; and the setting readers map v1's ids to the plugin's options through v1's page and group value filters. dws_lpmwc_is_payment_method_locked() answers whether a method is on the list and no strategy unlocks it for a user and an order, and the profile and Users screens ask it. X rulings 94 and 95. Assisted-by: Claude Code:claude-opus-5-5 --- .../functions.php | 2 + .../includes/deprecated.php | 584 ++++++++++++++++++ .../includes/grants.php | 20 +- .../src/UnlockStrategies/GroupsStrategy.php | 39 +- .../src/UnlockStrategies/OrderStrategy.php | 23 +- .../UnlockStrategies/UserProfileStrategy.php | 29 +- .../src/UnlockStrategies/UserRoleStrategy.php | 35 +- .../WooCommerceMembershipsStrategy.php | 39 +- .../tests/Integration/DeprecatedApiTest.php | 38 +- 9 files changed, 758 insertions(+), 51 deletions(-) create mode 100644 plugins/locked-payment-methods-for-woocommerce/includes/deprecated.php diff --git a/plugins/locked-payment-methods-for-woocommerce/functions.php b/plugins/locked-payment-methods-for-woocommerce/functions.php index 53e92c5..b320d21 100644 --- a/plugins/locked-payment-methods-for-woocommerce/functions.php +++ b/plugins/locked-payment-methods-for-woocommerce/functions.php @@ -28,6 +28,8 @@ // The hooks come last, so an include that throws leaves nothing of the plugin hooked. add_action( 'plugins_loaded', 'dws_lpmwc_boot', 15 ); add_action( 'before_woocommerce_init', 'dws_lpmwc_declare_compatibility' ); +add_action( 'dws_lpmwc/boot/succeeded', 'dws_lpmwc_fire_v1_initialized' ); +add_action( 'dws_lpmwc/boot/failed', 'dws_lpmwc_fire_v1_initialization_failure' ); register_deactivation_hook( DWS_LPMWC_FILE, 'dws_lpmwc_deactivate' ); /** diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/deprecated.php b/plugins/locked-payment-methods-for-woocommerce/includes/deprecated.php new file mode 100644 index 0000000..e0604d2 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/includes/deprecated.php @@ -0,0 +1,584 @@ + + */ +$dws_lpmwc_header = get_file_data( + DWS_LPMWC_FILE, + array( + 'Name' => 'Plugin Name', + 'Version' => 'Version', + 'RequiresWP' => 'Requires at least', + 'RequiresPHP' => 'Requires PHP', + ) +); + +/** + * The plugin's untranslated name. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use dws_lpmwc_get_plugin_name(). + */ +define( 'DWS_LPMWC_NAME', $dws_lpmwc_header['Name'] ); + +/** + * The plugin's version. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use dws_lpmwc_get_plugin_version(). + */ +define( 'DWS_LPMWC_VERSION', $dws_lpmwc_header['Version'] ); + +/** + * The path of the plugin's main file. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use DWS_LPMWC_FILE. + */ +define( 'DWS_LPMWC_PATH', DWS_LPMWC_FILE ); + +/** + * The oldest PHP version the plugin runs on. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 + */ +define( 'DWS_LPMWC_MIN_PHP', $dws_lpmwc_header['RequiresPHP'] ); + +/** + * The oldest WordPress version the plugin runs on. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 + */ +define( 'DWS_LPMWC_MIN_WP', $dws_lpmwc_header['RequiresWP'] ); + +unset( $dws_lpmwc_header ); + +/** + * The root of each v1 component's hook tags, keyed by the component's id, which v1 built from the component's name. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @var array + */ +const DWS_LPMWC_V1_COMPONENTS = array( + 'settings' => 'settings', + 'general-settings' => 'general_settings', + 'plugin-settings' => 'plugin_settings', + 'permissions' => 'permissions', + 'lock-manager' => 'lock_manager', + 'user-meta-strategy' => 'user_meta_unlock_strategy', + 'user-role-strategy' => 'user_role_unlock_strategy', + 'integrations-settings' => 'integrations_settings', + 'integrations-permissions' => 'integrations_permissions', + 'order-meta-strategy' => 'order_meta_unlock_strategy', + 'groups-strategy' => 'groups_unlock_strategy', + 'wc-memberships-strategy' => 'wc_memberships_unlock_strategy', +); + +/** + * Returns the plugin's name, translated once init has fired. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use dws_lpmwc_get_plugin_name(). + * + * @return string + */ +function dws_lpmwc_name(): string { + _deprecated_function( __FUNCTION__, '2.0.0', 'dws_lpmwc_get_plugin_name()' ); + + return dws_lpmwc_get_plugin_name(); +} + +/** + * Returns the plugin's version. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use dws_lpmwc_get_plugin_version(). + * + * @return string + */ +function dws_lpmwc_version(): string { + _deprecated_function( __FUNCTION__, '2.0.0', 'dws_lpmwc_get_plugin_version()' ); + + return dws_lpmwc_get_plugin_version(); +} + +/** + * Returns the path of the plugin's main file. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 + * + * @return string + */ +function dws_lpmwc_path(): string { + _deprecated_function( __FUNCTION__, '2.0.0' ); + + return DWS_LPMWC_PATH; +} + +/** + * Returns the oldest PHP version the plugin runs on. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 + * + * @return string + */ +function dws_lpmwc_min_php(): string { + _deprecated_function( __FUNCTION__, '2.0.0' ); + + return DWS_LPMWC_MIN_PHP; +} + +/** + * Returns the oldest WordPress version the plugin runs on. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 + * + * @return string + */ +function dws_lpmwc_min_wp(): string { + _deprecated_function( __FUNCTION__, '2.0.0' ); + + return DWS_LPMWC_MIN_WP; +} + +/** + * Returns a hook name as v1 built it for the plugin. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 + * + * @param string $name The hook's name. + * @param string|array $extra The name's further parts. + * + * @return string + */ +function dws_lpmwc_get_hook_tag( string $name, $extra = array() ): string { + _deprecated_function( __FUNCTION__, '2.0.0' ); + + return dws_lpmwc_join_v1_hook_tag( 'locked_payment_methods_for_woocommerce', $name, $extra ); +} + +/** + * Returns a hook name as v1 built it for one of its components, or null for an id v1 had no component for. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 + * + * @param string $component_id The v1 component's id. + * @param string $name The hook's name. + * @param string|array $extra The name's further parts. + * + * @return string|null + */ +function dws_lpmwc_get_component_hook_tag( string $component_id, string $name, $extra = array() ): ?string { + _deprecated_function( __FUNCTION__, '2.0.0' ); + + return isset( DWS_LPMWC_V1_COMPONENTS[ $component_id ] ) ? dws_lpmwc_join_v1_hook_tag( 'locked_payment_methods_for_woocommerce/' . DWS_LPMWC_V1_COMPONENTS[ $component_id ], $name, $extra ) : null; +} + +/** + * Returns a hook name joined and sanitized as v1 did: the non-empty parts joined with slashes, spaces and backslashes as underscores, lowercased. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string $root The name's root. + * @param string $name The hook's name. + * @param string|array $extra The name's further parts. + * + * @return string + */ +function dws_lpmwc_join_v1_hook_tag( string $root, string $name, $extra ): string { + return strtolower( str_replace( array( ' ', '\\' ), '_', implode( '/', array_filter( array_merge( array( $root, $name ), is_array( $extra ) ? $extra : array( $extra ) ), 'boolval' ) ) ) ); +} + +/** + * Fires v1's hook for a plugin that booted. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @return void + */ +function dws_lpmwc_fire_v1_initialized(): void { + /** + * Fires once the plugin has booted. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use dws_lpmwc/boot/succeeded. + */ + do_action_deprecated( 'dws_lpmwc_initialized', array(), '2.0.0', 'dws_lpmwc/boot/succeeded' ); +} + +/** + * Fires v1's hook for a plugin whose boot failed, with the failure. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param Throwable $error The failure. + * + * @return void + */ +function dws_lpmwc_fire_v1_initialization_failure( Throwable $error ): void { + /** + * Fires when the plugin's boot failed. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use dws_lpmwc/boot/failed. + * + * @param Throwable $error The failure, where v1 passed its own exception. + */ + do_action_deprecated( 'dws_lpmwc_initialization_failure', array( $error ), '2.0.0', 'dws_lpmwc/boot/failed' ); +} + +/** + * Returns whether a payment method stays locked for a user, as the plugin's strategies judge it, firing v1's filter first. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use dws_lpmwc_is_payment_method_locked(). + * + * @param string $locked_method_id The payment method's id. + * @param int $user_id The user's id. + * + * @return bool + */ +function dws_lpmwc_check_payment_method_access_for_user( string $locked_method_id, int $user_id ): bool { + _deprecated_function( __FUNCTION__, '2.0.0', 'dws_lpmwc_is_payment_method_locked()' ); + + /** + * Filters whether a payment method stays locked for a user. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use dws_lpmwc/payment_method/should_lock. + * + * @param bool $is_locked Whether the payment method stays locked. + * @param string $locked_method_id The payment method's id. + * @param int $user_id The user's id. + */ + $lock = apply_filters_deprecated( 'locked_payment_methods_for_woocommerce/check_payment_method_access_for_user', array( true, $locked_method_id, $user_id ), '2.0.0', 'dws_lpmwc/payment_method/should_lock' ); + + return dws_lpmwc_should_lock( false !== $lock, $locked_method_id, $user_id, 0 ); +} + +/** + * Returns whether a payment method stays locked for paying an order, as the plugin's strategies judge it for the order and its customer, firing v1's filter first. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use dws_lpmwc_is_payment_method_locked(). + * + * @param string $locked_method_id The payment method's id. + * @param int $order_id The order's id. + * + * @return bool + */ +function dws_lpmwc_check_payment_method_access_for_wc_order( string $locked_method_id, int $order_id ): bool { + _deprecated_function( __FUNCTION__, '2.0.0', 'dws_lpmwc_is_payment_method_locked()' ); + $order = wc_get_order( $order_id ); + + /** + * Filters whether a payment method stays locked for paying an order. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use dws_lpmwc/payment_method/should_lock. + * + * @param bool $is_locked Whether the payment method stays locked. + * @param string $locked_method_id The payment method's id. + * @param int $order_id The order's id. + */ + $lock = apply_filters_deprecated( 'locked_payment_methods_for_woocommerce/check_payment_method_access_for_wc_order', array( true, $locked_method_id, $order_id ), '2.0.0', 'dws_lpmwc/payment_method/should_lock' ); + + return dws_lpmwc_should_lock( false !== $lock, $locked_method_id, $order instanceof WC_Order ? $order->get_customer_id() : 0, $order_id ); +} + +/** + * Returns whether the user-profile strategy leaves a payment method locked for a user, true while the strategy is off. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use dws_lpmwc_is_payment_method_locked(). + * + * @param string $locked_method_id The payment method's id. + * @param int $user_id The user's id. + * + * @return bool + */ +function dws_lpmwc_check_payment_method_access_via_user_meta( string $locked_method_id, int $user_id ): bool { + _deprecated_function( __FUNCTION__, '2.0.0', 'dws_lpmwc_is_payment_method_locked()' ); + + return ! ( UserProfileStrategy::should_load() && UserProfileStrategy::unlocks( $locked_method_id, $user_id ) ); +} + +/** + * Returns whether the role strategy leaves a payment method locked for a user, true while the strategy is off. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use dws_lpmwc_is_payment_method_locked(). + * + * @param string $locked_method_id The payment method's id. + * @param int $user_id The user's id. + * + * @return bool + */ +function dws_lpmwc_check_payment_method_access_via_user_roles( string $locked_method_id, int $user_id ): bool { + _deprecated_function( __FUNCTION__, '2.0.0', 'dws_lpmwc_is_payment_method_locked()' ); + + return ! ( UserRoleStrategy::should_load() && UserRoleStrategy::unlocks( $locked_method_id, $user_id ) ); +} + +/** + * Returns whether the Groups strategy leaves a payment method locked for a user, true while the strategy is off. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use dws_lpmwc_is_payment_method_locked(). + * + * @param string $locked_method_id The payment method's id. + * @param int $user_id The user's id. + * + * @return bool + */ +function dws_lpmwc_check_payment_method_access_via_groups( string $locked_method_id, int $user_id ): bool { + _deprecated_function( __FUNCTION__, '2.0.0', 'dws_lpmwc_is_payment_method_locked()' ); + + return ! ( GroupsStrategy::should_load() && GroupsStrategy::unlocks( $locked_method_id, $user_id ) ); +} + +/** + * Returns whether the WooCommerce Memberships strategy leaves a payment method locked for a user, true while the strategy is off. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use dws_lpmwc_is_payment_method_locked(). + * + * @param string $locked_method_id The payment method's id. + * @param int $user_id The user's id. + * + * @return bool + */ +function dws_lpmwc_check_payment_method_access_via_wc_memberships( string $locked_method_id, int $user_id ): bool { + _deprecated_function( __FUNCTION__, '2.0.0', 'dws_lpmwc_is_payment_method_locked()' ); + + return ! ( WooCommerceMembershipsStrategy::should_load() && WooCommerceMembershipsStrategy::unlocks( $locked_method_id, $user_id ) ); +} + +/** + * Returns whether the order strategy leaves a payment method locked for paying an order, true while the strategy is off. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use dws_lpmwc_is_payment_method_locked(). + * + * @param string $locked_method_id The payment method's id. + * @param int $order_id The order's id. + * + * @return bool + */ +function dws_lpmwc_check_payment_method_access_via_wc_order_meta( string $locked_method_id, int $order_id ): bool { + _deprecated_function( __FUNCTION__, '2.0.0', 'dws_lpmwc_is_payment_method_locked()' ); + + return ! ( OrderStrategy::should_load() && OrderStrategy::unlocks( $locked_method_id, $order_id ) ); +} + +/** + * Returns a setting's stored value, by v1's field id and group, or null for an id v1 had no setting for. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use get_option(). + * + * @param string $field_id v1's id of the setting, prefixed with its group and a slash when the group is null. + * @param string|null $group Optional. v1's id of the setting's group. Default null. + * + * @return mixed + */ +function dws_lpmwc_get_raw_setting( string $field_id, ?string $group = null ) { + _deprecated_function( __FUNCTION__, '2.0.0', 'get_option()' ); + $setting = dws_lpmwc_find_v1_setting( $field_id, $group ); + + return null === $setting ? null : get_option( $setting['key'], null ); +} + +/** + * Returns a setting's value, of the setting's kind, by v1's field id and group, through v1's value filters, or null for an id v1 had no setting for. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use get_option(). + * + * @param string $field_id v1's id of the setting, prefixed with its group and a slash when the group is null. + * @param string|null $group Optional. v1's id of the setting's group. Default null. + * + * @return mixed + */ +function dws_lpmwc_get_validated_setting( string $field_id, ?string $group = null ) { + _deprecated_function( __FUNCTION__, '2.0.0', 'get_option()' ); + + return dws_lpmwc_read_v1_setting( $field_id, $group ); +} + +/** + * Returns a value as the setting's kind, by v1's field id and group: a boolean for a switch and a list of strings for a list, falling back to the setting's default. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 + * + * @param mixed $value The value. + * @param string $field_id v1's id of the setting, prefixed with its group and a slash when the group is null. + * @param string|null $group Optional. v1's id of the setting's group. Default null. + * + * @return mixed + */ +function dws_lpmwc_validate_setting( $value, string $field_id, ?string $group = null ) { + _deprecated_function( __FUNCTION__, '2.0.0' ); + $setting = dws_lpmwc_find_v1_setting( $field_id, $group ); + + return null === $setting ? null : dws_lpmwc_validate_v1_setting( $value, $setting['key'] ); +} + +/** + * Returns a General setting's value, of the setting's kind, by v1's field id, through v1's value filter. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use get_option(). + * + * @param string $field_id v1's id of the setting. + * + * @return mixed + */ +function dws_lpmwc_get_validated_general_setting( string $field_id ) { + _deprecated_function( __FUNCTION__, '2.0.0', 'get_option()' ); + + return dws_lpmwc_read_v1_setting( $field_id, 'general' ); +} + +/** + * Returns v1's group and field ids and the plugin's option for one of v1's settings, or null for an id v1 had no setting for. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string $field_id v1's id of the setting, prefixed with its group and a slash when the group is null. + * @param string|null $group v1's id of the setting's group. + * + * @return array{group: string, field: string, key: string}|null + */ +function dws_lpmwc_find_v1_setting( string $field_id, ?string $group ): ?array { + if ( null === $group ) { + $parts = explode( '/', $field_id, 2 ); + if ( 2 !== count( $parts ) ) { + return null; + } + list( $group, $field_id ) = $parts; + } + + $key = DWS_LPMWC_V1_SETTINGS[ $group ][ $field_id ] ?? null; + + return null === $key ? null : array( + 'group' => $group, + 'field' => $field_id, + 'key' => $key, + ); +} + +/** + * Returns a setting's value as v1's settings read it: through v1's page filter when the group is null, then through its group's filter. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string $field_id v1's id of the setting, prefixed with its group and a slash when the group is null. + * @param string|null $group v1's id of the setting's group. + * + * @return mixed + */ +function dws_lpmwc_read_v1_setting( string $field_id, ?string $group ) { + $setting = dws_lpmwc_find_v1_setting( $field_id, $group ); + if ( null === $group ) { + /** + * Filters a setting's value, by its group-prefixed v1 id, before its group reads it. + * + * @since 1.0.0 + * @version 2.0.0 + * @deprecated 2.0.0 Use the hook the deprecation notice names. + * + * @param mixed $value Null, or the value a callback answers. + * @param string $field_id v1's id of the setting, prefixed with its group and a slash. + */ + $value = apply_filters_deprecated( 'locked_payment_methods_for_woocommerce/settings/get_validated_option_value', array( null, $field_id ), '2.0.0', null === $setting ? 'get_option' : 'option_' . $setting['key'] ); + if ( null !== $value ) { + return $value; + } + } + + return null === $setting ? null : dws_lpmwc_apply_v1_setting_filter( dws_lpmwc_validate_v1_setting( get_option( $setting['key'], null ), $setting['key'] ), $setting['group'], $setting['field'], 'option_' . $setting['key'] ); +} + +/** + * Returns a value as a setting's kind, a boolean for a switch and a list of strings for a list, or the setting's default. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param mixed $value The value. + * @param string $key The setting's option. + * + * @return mixed + */ +function dws_lpmwc_validate_v1_setting( $value, string $key ) { + $default = DWS_LPMWC_OPTION_DEFAULTS[ $key ] ?? null; + if ( is_bool( $default ) ) { + return filter_var( $value, FILTER_VALIDATE_BOOL, FILTER_NULL_ON_FAILURE ) ?? $default; + } + + return is_array( $value ) ? array_values( array_filter( $value, 'is_string' ) ) : $default; +} diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/grants.php b/plugins/locked-payment-methods-for-woocommerce/includes/grants.php index fa5c5ab..97611b9 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/grants.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/grants.php @@ -1,6 +1,6 @@ =' ); } - // endregion - - // region HOOKS - /** - * Returns false for a payment method a group of the user, or a group above one, unlocks, and the lock as given otherwise. + * Returns whether a group of the user, or a group above one, unlocks the payment method. + * + * @internal * * @since 2.0.0 * @version 2.0.0 * - * @param bool $lock Whether the payment method stays locked. * @param string $gateway_id The payment method's id. - * @param int $user_id The paying user, or 0 for a guest. + * @param int $user_id The user's id, or 0 for a guest. * * @return bool */ - public function unlock_group_methods( bool $lock, string $gateway_id, int $user_id ): bool { + public static function unlocks( string $gateway_id, int $user_id ): bool { if ( 0 === $user_id ) { - return $lock; + return false; } $groups = dws_lpmwc_get_groups_unlocked_payment_method_ids(); foreach ( (array) ( new \Groups_User( $user_id ) )->group_ids_deep as $group_id ) { // @phpstan-ignore property.notFound (Groups_User resolves its group ids through __get, as at v1's oldest Groups.) if ( \is_numeric( $group_id ) && \in_array( $gateway_id, $groups[ (int) $group_id ] ?? array(), true ) ) { - return false; + return true; } } - return $lock; + return false; + } + + // endregion + + // region HOOKS + + /** + * Returns false for a payment method a group of the user, or a group above one, unlocks, and the lock as given otherwise. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param bool $lock Whether the payment method stays locked. + * @param string $gateway_id The payment method's id. + * @param int $user_id The paying user, or 0 for a guest. + * + * @return bool + */ + public function unlock_group_methods( bool $lock, string $gateway_id, int $user_id ): bool { + return self::unlocks( $gateway_id, $user_id ) ? false : $lock; } /** diff --git a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/OrderStrategy.php b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/OrderStrategy.php index df4abc8..f4ccf2c 100644 --- a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/OrderStrategy.php +++ b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/OrderStrategy.php @@ -104,6 +104,27 @@ public function get_object_form( string $form_id, int $object_id ): array { // endregion + // region METHODS + + /** + * Returns whether the order's grants list holds the payment method. + * + * @internal + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string $gateway_id The payment method's id. + * @param int $order_id The order's id, or 0 for none. + * + * @return bool + */ + public static function unlocks( string $gateway_id, int $order_id ): bool { + return \in_array( $gateway_id, dws_lpmwc_get_order_unlocked_payment_method_ids( $order_id ), true ); + } + + // endregion + // region HOOKS /** @@ -120,7 +141,7 @@ public function get_object_form( string $form_id, int $object_id ): array { * @return bool */ public function unlock_granted_methods( bool $lock, string $gateway_id, int $user_id, int $order_id ): bool { - return \in_array( $gateway_id, dws_lpmwc_get_order_unlocked_payment_method_ids( $order_id ), true ) ? false : $lock; + return self::unlocks( $gateway_id, $order_id ) ? false : $lock; } // endregion diff --git a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserProfileStrategy.php b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserProfileStrategy.php index 9fb0c18..b288fb6 100644 --- a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserProfileStrategy.php +++ b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserProfileStrategy.php @@ -115,6 +115,27 @@ public function get_object_form( string $form_id, int $object_id ): array { // endregion + // region METHODS + + /** + * Returns whether the user's grants list on the current site holds the payment method. + * + * @internal + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string $gateway_id The payment method's id. + * @param int $user_id The user's id, or 0 for a guest. + * + * @return bool + */ + public static function unlocks( string $gateway_id, int $user_id ): bool { + return \in_array( $gateway_id, dws_lpmwc_get_user_unlocked_payment_method_ids( $user_id ), true ); + } + + // endregion + // region HOOKS /** @@ -130,7 +151,7 @@ public function get_object_form( string $form_id, int $object_id ): array { * @return bool */ public function unlock_granted_methods( bool $lock, string $gateway_id, int $user_id ): bool { - return \in_array( $gateway_id, dws_lpmwc_get_user_unlocked_payment_method_ids( $user_id ), true ) ? false : $lock; + return self::unlocks( $gateway_id, $user_id ) ? false : $lock; } /** @@ -171,9 +192,9 @@ public function render_users_column( string $output, string $column, int $user_i $lines = array(); foreach ( dws_lpmwc_get_locked_payment_method_ids() as $id ) { $state = match ( true ) { - \in_array( $id, $granted, true ) => __( 'unlocked on the profile', 'locked-payment-methods-for-woocommerce' ), - ! dws_lpmwc_should_lock( true, $id, $user_id, 0 ) => __( 'unlocked by another rule', 'locked-payment-methods-for-woocommerce' ), - default => __( 'locked', 'locked-payment-methods-for-woocommerce' ), + \in_array( $id, $granted, true ) => __( 'unlocked on the profile', 'locked-payment-methods-for-woocommerce' ), + ! dws_lpmwc_is_payment_method_locked( $id, $user_id ) => __( 'unlocked by another rule', 'locked-payment-methods-for-woocommerce' ), + default => __( 'locked', 'locked-payment-methods-for-woocommerce' ), }; /* translators: 1: payment method title, 2: the payment method's state for the user. */ $lines[] = esc_html( \sprintf( __( '%1$s: %2$s', 'locked-payment-methods-for-woocommerce' ), dws_lpmwc_get_payment_method_title( $id ), $state ) ); diff --git a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserRoleStrategy.php b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserRoleStrategy.php index 91c4a32..b078b29 100644 --- a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserRoleStrategy.php +++ b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserRoleStrategy.php @@ -40,29 +40,50 @@ public function register_hooks(): void { // endregion - // region HOOKS + // region METHODS /** - * Returns false for a user holding one of the full-access roles, and the lock as given otherwise. + * Returns whether the user holds one of the full-access roles, which unlocks every locked payment method. + * + * @internal * * @since 2.0.0 * @version 2.0.0 * - * @param bool $lock Whether the payment method stays locked. * @param string $gateway_id The payment method's id. - * @param int $user_id The paying user, or 0 for a guest. + * @param int $user_id The user's id, or 0 for a guest. * * @return bool */ - public function unlock_for_full_access_roles( bool $lock, string $gateway_id, int $user_id ): bool { + public static function unlocks( string $gateway_id, int $user_id ): bool { $user = get_userdata( $user_id ); if ( ! $user instanceof \WP_User ) { - return $lock; + return false; } $roles = dws_lpmwc_apply_v1_setting_filter( dws_lpmwc_get_list_option( DWS_LPMWC_OPTION_FULL_ACCESS_ROLES ), 'general', 'full-access-user-roles', 'option_' . DWS_LPMWC_OPTION_FULL_ACCESS_ROLES ); - return \is_array( $roles ) && array() !== \array_intersect( $user->roles, \array_filter( $roles, 'is_string' ) ) ? false : $lock; + return \is_array( $roles ) && array() !== \array_intersect( $user->roles, \array_filter( $roles, 'is_string' ) ); + } + + // endregion + + // region HOOKS + + /** + * Returns false for a user holding one of the full-access roles, and the lock as given otherwise. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param bool $lock Whether the payment method stays locked. + * @param string $gateway_id The payment method's id. + * @param int $user_id The paying user, or 0 for a guest. + * + * @return bool + */ + public function unlock_for_full_access_roles( bool $lock, string $gateway_id, int $user_id ): bool { + return self::unlocks( $gateway_id, $user_id ) ? false : $lock; } // endregion diff --git a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/WooCommerceMembershipsStrategy.php b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/WooCommerceMembershipsStrategy.php index 6c1c350..51076c9 100644 --- a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/WooCommerceMembershipsStrategy.php +++ b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/WooCommerceMembershipsStrategy.php @@ -117,36 +117,53 @@ public static function is_available(): bool { return \function_exists( 'wc_memberships_get_user_active_memberships' ) && \is_string( $version ) && \version_compare( $version, '1.18.0', '>=' ); } - // endregion - - // region HOOKS - /** - * Returns false for a payment method a plan the user is an active member of unlocks, and the lock as given otherwise. + * Returns whether a plan the user is an active member of unlocks the payment method. + * + * @internal * * @since 2.0.0 * @version 2.0.0 * - * @param bool $lock Whether the payment method stays locked. * @param string $gateway_id The payment method's id. - * @param int $user_id The paying user, or 0 for a guest. + * @param int $user_id The user's id, or 0 for a guest. * * @return bool */ - public function unlock_member_methods( bool $lock, string $gateway_id, int $user_id ): bool { + public static function unlocks( string $gateway_id, int $user_id ): bool { if ( 0 === $user_id ) { - return $lock; + return false; } foreach ( (array) wc_memberships_get_user_active_memberships( $user_id ) as $membership ) { // Memberships sometimes returns cancelled memberships among the active ones. $plan_id = \is_object( $membership ) && \method_exists( $membership, 'is_active' ) && \method_exists( $membership, 'get_plan_id' ) && true === $membership->is_active() ? $membership->get_plan_id() : null; if ( \is_numeric( $plan_id ) && \in_array( $gateway_id, dws_lpmwc_get_plan_unlocked_payment_method_ids( (int) $plan_id ), true ) ) { - return false; + return true; } } - return $lock; + return false; + } + + // endregion + + // region HOOKS + + /** + * Returns false for a payment method a plan the user is an active member of unlocks, and the lock as given otherwise. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param bool $lock Whether the payment method stays locked. + * @param string $gateway_id The payment method's id. + * @param int $user_id The paying user, or 0 for a guest. + * + * @return bool + */ + public function unlock_member_methods( bool $lock, string $gateway_id, int $user_id ): bool { + return self::unlocks( $gateway_id, $user_id ) ? false : $lock; } // endregion diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/DeprecatedApiTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/DeprecatedApiTest.php index cff59ff..7cddae8 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/DeprecatedApiTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/DeprecatedApiTest.php @@ -194,10 +194,11 @@ public function test_v1s_access_checks_answer_true_for_locked_from_the_strategie } public function test_v1s_order_access_check_judges_the_order_and_its_customer_after_v1s_filter(): void { - $order = wc_create_order( array( 'customer_id' => self::$users['shop_manager'] ) ); - $v1 = 'locked_payment_methods_for_woocommerce/check_payment_method_access_for_wc_order'; - $seen = array(); - $spy = static function ( mixed ...$args ) use ( &$seen ): mixed { + $order = wc_create_order( array( 'customer_id' => self::$users['shop_manager'] ) ); + $order_id = $order->get_id(); + $v1 = 'locked_payment_methods_for_woocommerce/check_payment_method_access_for_wc_order'; + $seen = array(); + $spy = static function ( mixed ...$args ) use ( &$seen ): mixed { $seen[] = $args; return $args[0]; @@ -205,13 +206,13 @@ public function test_v1s_order_access_check_judges_the_order_and_its_customer_af add_filter( $v1, $spy, 10, 3 ); try { - self::assertFalse( dws_lpmwc_check_payment_method_access_for_wc_order( 'cod', $order->get_id() ) ); + self::assertFalse( dws_lpmwc_check_payment_method_access_for_wc_order( 'cod', $order_id ) ); } finally { remove_filter( $v1, $spy ); $order->delete( true ); } - self::assertSame( array( array( true, 'cod', $order->get_id() ) ), $seen ); + self::assertSame( array( array( true, 'cod', $order_id ) ), $seen ); self::assertContains( array( $v1, 'dws_lpmwc/payment_method/should_lock' ), $this->deprecated ); } @@ -240,17 +241,24 @@ public function test_v1s_per_strategy_checks_answer_from_their_strategy_alone_an public function test_v1s_setting_readers_map_v1s_ids_to_the_plugins_options_through_v1s_filters(): void { update_option( DWS_LPMWC_OPTION_UNLOCK_VIA_USER_ROLES, 'yes' ); - $page = 'locked_payment_methods_for_woocommerce/settings/get_validated_option_value'; + $page = 'locked_payment_methods_for_woocommerce/settings/get_validated_option_value'; + $group = 'locked_payment_methods_for_woocommerce/general_settings/get_validated_option_value'; + $through = static fn ( mixed $value ): mixed => $value; + add_filter( $group, $through ); - self::assertSame( array( 'cod' ), dws_lpmwc_get_validated_general_setting( 'locked-payment-methods' ) ); - self::assertTrue( dws_lpmwc_get_validated_setting( 'unlock-via-user-role', 'general' ) ); - self::assertSame( array( 'shop_manager' ), dws_lpmwc_get_validated_setting( 'general/full-access-user-roles' ) ); - self::assertSame( 'yes', dws_lpmwc_get_raw_setting( 'unlock-via-user-role', 'general' ) ); - self::assertFalse( dws_lpmwc_validate_setting( 'maybe-not', 'remove-data-uninstall', 'plugin' ) ); - self::assertSame( array( 'bacs' ), dws_lpmwc_validate_setting( array( 'bacs', 7 ), 'locked-payment-methods', 'general' ) ); - self::assertNull( dws_lpmwc_get_validated_setting( 'unknown', 'general' ) ); + try { + self::assertSame( array( 'cod' ), dws_lpmwc_get_validated_general_setting( 'locked-payment-methods' ) ); + self::assertTrue( dws_lpmwc_get_validated_setting( 'unlock-via-user-role', 'general' ) ); + self::assertSame( array( 'shop_manager' ), dws_lpmwc_get_validated_setting( 'general/full-access-user-roles' ) ); + self::assertSame( 'yes', dws_lpmwc_get_raw_setting( 'unlock-via-user-role', 'general' ) ); + self::assertFalse( dws_lpmwc_validate_setting( 'maybe-not', 'remove-data-uninstall', 'plugin' ) ); + self::assertSame( array( 'bacs' ), dws_lpmwc_validate_setting( array( 'bacs', 7 ), 'locked-payment-methods', 'general' ) ); + self::assertNull( dws_lpmwc_get_validated_setting( 'unknown', 'general' ) ); + } finally { + remove_filter( $group, $through ); + } - self::assertContains( array( 'locked_payment_methods_for_woocommerce/general_settings/get_validated_option_value', 'option_' . DWS_LPMWC_OPTION_FULL_ACCESS_ROLES ), $this->deprecated ); + self::assertContains( array( $group, 'option_' . DWS_LPMWC_OPTION_FULL_ACCESS_ROLES ), $this->deprecated ); self::assertNotContains( array( $page, 'option_' . DWS_LPMWC_OPTION_FULL_ACCESS_ROLES ), $this->deprecated ); // No callback hooks the page's filter, so it reports no deprecation. self::assertContains( array( 'dws_lpmwc_get_validated_general_setting', 'get_option()' ), $this->deprecated ); } From 5e6ab43987fcbda7ad8ad61aae4e2bb3576c9961 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 00:16:57 +0100 Subject: [PATCH 26/50] Test that Locked Payment Methods' uninstall removes v2's and v1's data v1 removed data only from a Freemius hook, deleting its options under an unescaped LIKE and its capabilities, and left user, order and plan grants behind. FootprintTest expects the footprint to name v1's option prefix, version rows and notices, order meta under both prefixes, the plan meta under both keys, v1's global user grants and the four capabilities. UninstallTest expects a consenting uninstall to remove the Groups map, v1's options, a user's site grants, v1's global grants, v1's notices, an order's and a plan's grants under both keys, and the capabilities. X ruling 96. Assisted-by: Claude Code:claude-opus-5-5 --- .../tests/Integration/UninstallTest.php | 53 +++++++++++++++++++ .../tests/Unit/FootprintTest.php | 14 ++++- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UninstallTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UninstallTest.php index 9b48f4e..e0ee6a2 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UninstallTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UninstallTest.php @@ -72,4 +72,57 @@ public function test_a_consenting_uninstall_removes_a_row_of_every_kind_the_fram dws_lpmwc_plugin()->migrations?->run(); self::assertSame( 'no', get_option( DWS_LPMWC_OPTION_DELETE_DATA ) ); } + + #[RunInSeparateProcess] + public function test_a_consenting_uninstall_removes_the_grants_v1s_rows_and_the_capabilities(): void { + global $wpdb; + + update_option( DWS_LPMWC_OPTION_DELETE_DATA, 'yes' ); + update_option( DWS_LPMWC_OPTION_GROUPS_UNLOCKED_METHODS, array( 7 => array( 'cod' ) ) ); + foreach ( array( 'dws-wc-lpm_general_locked-payment-methods', 'locked_payment_methods_for_woocommerce_version', 'locked_payment_methods_for_woocommerce_original_version', '_locked_payment_methods_for_woocommerce_admin_notices' ) as $v1_option ) { + update_option( $v1_option, 'seeded' ); + } + update_user_meta( self::USER, $wpdb->get_blog_prefix() . DWS_LPMWC_USER_OPTION_UNLOCKED_METHODS, array( 'cod' ) ); + update_user_meta( self::USER, 'dws_wc_lpm_grant_access_cod', 'yes' ); + update_user_meta( self::USER, '_locked_payment_methods_for_woocommerce_admin_notices', array( 'seeded' ) ); + $order = wc_create_order(); + $order->update_meta_data( DWS_LPMWC_META_UNLOCKED_METHODS, array( 'cod' ) ); + $order->update_meta_data( '_dws_wc_lpm_grant_access_cod', 'yes' ); + $order->save(); + $plan = (int) wp_insert_post( + array( + 'post_type' => 'post', + 'post_title' => 'LPM plan', + 'post_status' => 'draft', + ) + ); + update_post_meta( $plan, DWS_LPMWC_META_UNLOCKED_METHODS, array( 'cod' ) ); + update_post_meta( $plan, '_dws_wc_lpm_unlocked_payment_methods', array( 'cod' ) ); + self::assertTrue( get_role( 'administrator' )?->has_cap( DWS_LPMWC_CAP_UNLOCK_VIA_GROUPS ) ); + + try { + self::assertTrue( uninstall_plugin( plugin_basename( DWS_LPMWC_FILE ) ) ); + wp_cache_flush(); + wp_roles()->for_site(); + + self::assertFalse( get_option( DWS_LPMWC_OPTION_GROUPS_UNLOCKED_METHODS ) ); + foreach ( array( 'dws-wc-lpm_general_locked-payment-methods', 'locked_payment_methods_for_woocommerce_version', 'locked_payment_methods_for_woocommerce_original_version', '_locked_payment_methods_for_woocommerce_admin_notices' ) as $v1_option ) { + self::assertFalse( get_option( $v1_option ), $v1_option ); + } + self::assertSame( '', get_user_meta( self::USER, $wpdb->get_blog_prefix() . DWS_LPMWC_USER_OPTION_UNLOCKED_METHODS, true ) ); + self::assertSame( '', get_user_meta( self::USER, 'dws_wc_lpm_grant_access_cod', true ) ); + self::assertSame( '', get_user_meta( self::USER, '_locked_payment_methods_for_woocommerce_admin_notices', true ) ); + self::assertSame( '', wc_get_order( $order->get_id() )?->get_meta( DWS_LPMWC_META_UNLOCKED_METHODS ) ); + self::assertSame( '', wc_get_order( $order->get_id() )?->get_meta( '_dws_wc_lpm_grant_access_cod' ) ); + self::assertSame( '', get_post_meta( $plan, DWS_LPMWC_META_UNLOCKED_METHODS, true ) ); + self::assertSame( '', get_post_meta( $plan, '_dws_wc_lpm_unlocked_payment_methods', true ) ); + self::assertFalse( get_role( 'administrator' )?->has_cap( DWS_LPMWC_CAP_UNLOCK_VIA_GROUPS ) ); + } finally { + $order->delete( true ); + wp_delete_post( $plan, true ); + // The other tests share the database, so the runner stores again what a new site starts with, and the grants act again. + dws_lpmwc_plugin()->migrations?->run(); + do_action( 'wp_loaded' ); + } + } } diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Unit/FootprintTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Unit/FootprintTest.php index dc918c5..8514628 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Unit/FootprintTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Unit/FootprintTest.php @@ -10,7 +10,7 @@ #[CoversNothing] final class FootprintTest extends TestCase { // Prefix families a port keeps from its v1 names, which uninstall must still remove. - protected const array LEGACY_FAMILIES = array(); + protected const array LEGACY_FAMILIES = array( 'dws-wc-lpm_', '_dws_wc_lpm_', 'dws_wc_lpm_', 'locked_payment_methods_for_woocommerce_', '_locked_payment_methods_for_woocommerce_', 'unlock_dws_lpm_via_' ); public function test_the_prefix_has_one_segment_after_dws_so_no_prefix_in_its_family_starts_with_another(): void { self::assertMatchesRegularExpression( '/^dws_[a-z0-9]+$/', Plugin::PREFIX ); @@ -65,4 +65,16 @@ public function test_it_covers_every_option_key(): void { self::assertTrue( $covered, "The option $key is outside the footprint." ); } } + + public function test_it_covers_what_v1_stored_and_the_grants_and_capabilities(): void { + $footprint = dws_lpmwc_get_footprint(); + + self::assertContains( 'dws-wc-lpm_', $footprint['option_prefixes'] ); + self::assertSame( array( 'locked_payment_methods_for_woocommerce_version', 'locked_payment_methods_for_woocommerce_original_version', '_locked_payment_methods_for_woocommerce_admin_notices' ), $footprint['options'] ); + self::assertSame( array( '_' . Plugin::PREFIX . '_', '_dws_wc_lpm_' ), $footprint['meta_prefixes']['order'] ); + self::assertSame( array( DWS_LPMWC_META_UNLOCKED_METHODS, '_dws_wc_lpm_unlocked_payment_methods' ), $footprint['meta']['post'] ); + self::assertContains( 'dws_wc_lpm_', $footprint['user_meta_global_prefixes'] ); + self::assertSame( array( '_locked_payment_methods_for_woocommerce_admin_notices' ), $footprint['user_meta_global'] ); + self::assertSame( array( DWS_LPMWC_CAP_UNLOCK_VIA_USER_PROFILES, DWS_LPMWC_CAP_UNLOCK_VIA_ORDERS, DWS_LPMWC_CAP_UNLOCK_VIA_GROUPS, DWS_LPMWC_CAP_UNLOCK_VIA_WC_MEMBERSHIPS ), $footprint['capabilities'] ); + } } From a828378ab00143080fb268fa20146c53615304a7 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 00:18:14 +0100 Subject: [PATCH 27/50] Remove Locked Payment Methods' v2 and v1 data on a consenting uninstall v1 removed data only from a Freemius hook, deleting its options under an unescaped LIKE and its capabilities, and left user, order and plan grants behind. The footprint, which the framework's uninstall engine removes where the site consents through dws_lpmwc_delete_data, adds v1's option prefix, version rows and notices; order meta under the plugin's and v1's prefixes, on either storage; the plan meta under both keys; v1's global user grants that a migration has not moved yet; and the four capabilities. The Unit bootstrap loads the files whose constants the footprint reads. X ruling 96. Assisted-by: Claude Code:claude-opus-5-5 --- .../includes/footprint.php | 13 +++++++++---- .../tests/Unit/bootstrap.php | 2 ++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/footprint.php b/plugins/locked-payment-methods-for-woocommerce/includes/footprint.php index fdb32bc..ae53bad 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/footprint.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/footprint.php @@ -13,19 +13,24 @@ defined( 'ABSPATH' ) || exit; /** - * Returns the plugin's stored data by kind, which covers every option, transient, user meta key and scheduled job under the plugin's prefix. + * Returns the plugin's stored data by kind, which covers every option, transient, user meta key and scheduled job under the plugin's prefix, the grants on users, orders, Groups groups and WooCommerce Memberships plans, and what v1 stored under its own names: its settings, version rows, notices, grants and capabilities. * * @since 2.0.0 * @version 2.0.0 * - * @return array{option_prefixes: list, user_meta_site_prefixes: list, user_meta_global_prefixes: list, network_options: list, cron_hooks: list, as_groups: list} + * @return array{option_prefixes: list, options: list, meta: array>, meta_prefixes: array>, user_meta_site_prefixes: list, user_meta_global_prefixes: list, user_meta_global: list, network_options: list, capabilities: list, cron_hooks: list, as_groups: list} */ function dws_lpmwc_get_footprint(): array { return array( - 'option_prefixes' => array( Plugin::PREFIX . '_', '_transient_' . Plugin::PREFIX . '_', '_transient_timeout_' . Plugin::PREFIX . '_' ), + 'option_prefixes' => array( Plugin::PREFIX . '_', '_transient_' . Plugin::PREFIX . '_', '_transient_timeout_' . Plugin::PREFIX . '_', 'dws-wc-lpm_' ), + 'options' => array( 'locked_payment_methods_for_woocommerce_version', 'locked_payment_methods_for_woocommerce_original_version', '_locked_payment_methods_for_woocommerce_admin_notices' ), + 'meta' => array( 'post' => array( DWS_LPMWC_META_UNLOCKED_METHODS, '_dws_wc_lpm_unlocked_payment_methods' ) ), + 'meta_prefixes' => array( 'order' => array( '_' . Plugin::PREFIX . '_', '_dws_wc_lpm_' ) ), 'user_meta_site_prefixes' => array( Plugin::PREFIX . '_' ), - 'user_meta_global_prefixes' => array( Plugin::PREFIX . '_' ), + 'user_meta_global_prefixes' => array( Plugin::PREFIX . '_', 'dws_wc_lpm_' ), + 'user_meta_global' => array( '_locked_payment_methods_for_woocommerce_admin_notices' ), 'network_options' => array( Plugin::PREFIX . '_network_notices' ), + 'capabilities' => array( DWS_LPMWC_CAP_UNLOCK_VIA_USER_PROFILES, DWS_LPMWC_CAP_UNLOCK_VIA_ORDERS, DWS_LPMWC_CAP_UNLOCK_VIA_GROUPS, DWS_LPMWC_CAP_UNLOCK_VIA_WC_MEMBERSHIPS ), 'cron_hooks' => array( Plugin::PREFIX . '_revision/migration/run' ), 'as_groups' => array( Plugin::PREFIX ), ); diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Unit/bootstrap.php b/plugins/locked-payment-methods-for-woocommerce/tests/Unit/bootstrap.php index af0ab55..b371842 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Unit/bootstrap.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Unit/bootstrap.php @@ -4,5 +4,7 @@ define( 'ABSPATH', __DIR__ . '/' ); // WordPress's include loader is absent here, so the suite requires the declarations it reads. +require_once dirname( __DIR__, 2 ) . '/includes/capabilities.php'; +require_once dirname( __DIR__, 2 ) . '/includes/grants.php'; require_once dirname( __DIR__, 2 ) . '/includes/options.php'; require_once dirname( __DIR__, 2 ) . '/includes/footprint.php'; From f5e2a323317cacf66a8d353edfd689072396c95f Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 00:19:20 +0100 Subject: [PATCH 28/50] Remove the demo's build output from Locked Payment Methods Removing the template's demo also dropped the ignore rules for blocks/build and assets/build, so the same commit added the build output the scaffold had left in place: the example block's and the greeting's compiled scripts and styles, which nothing loads. Assisted-by: Claude Code:claude-opus-5-5 --- .../assets/build/editor.asset.php | 7 ----- .../assets/build/editor.js | 1 - .../assets/build/greeting-rtl.css | 1 - .../assets/build/greeting.asset.php | 6 ----- .../assets/build/greeting.css | 1 - .../assets/build/greeting.js | 0 .../blocks/build/blocks-manifest.php | 26 ------------------- .../blocks/build/example-notice/block.json | 18 ------------- .../blocks/build/example-notice/index-rtl.css | 1 - .../build/example-notice/index.asset.php | 9 ------- .../blocks/build/example-notice/index.css | 1 - .../blocks/build/example-notice/index.js | 1 - .../build/example-notice/style-index-rtl.css | 1 - .../build/example-notice/style-index.css | 1 - 14 files changed, 74 deletions(-) delete mode 100644 plugins/locked-payment-methods-for-woocommerce/assets/build/editor.asset.php delete mode 100644 plugins/locked-payment-methods-for-woocommerce/assets/build/editor.js delete mode 100644 plugins/locked-payment-methods-for-woocommerce/assets/build/greeting-rtl.css delete mode 100644 plugins/locked-payment-methods-for-woocommerce/assets/build/greeting.asset.php delete mode 100644 plugins/locked-payment-methods-for-woocommerce/assets/build/greeting.css delete mode 100644 plugins/locked-payment-methods-for-woocommerce/assets/build/greeting.js delete mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/build/blocks-manifest.php delete mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/block.json delete mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index-rtl.css delete mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index.asset.php delete mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index.css delete mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index.js delete mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/style-index-rtl.css delete mode 100644 plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/style-index.css diff --git a/plugins/locked-payment-methods-for-woocommerce/assets/build/editor.asset.php b/plugins/locked-payment-methods-for-woocommerce/assets/build/editor.asset.php deleted file mode 100644 index 23e6096..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/assets/build/editor.asset.php +++ /dev/null @@ -1,7 +0,0 @@ - array( - 'wp-blocks', - 'wp-i18n' - ), - 'version' => 'bf1425f40472313c45de' -); diff --git a/plugins/locked-payment-methods-for-woocommerce/assets/build/editor.js b/plugins/locked-payment-methods-for-woocommerce/assets/build/editor.js deleted file mode 100644 index 675511f..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/assets/build/editor.js +++ /dev/null @@ -1 +0,0 @@ -(()=>{"use strict";const e=window.wp.blocks,o=window.wp.i18n;(0,e.registerBlockStyle)("locked-payment-methods-for-woocommerce/example-notice",{name:"outlined",label:(0,o.__)("Outlined","locked-payment-methods-for-woocommerce")})})(); \ No newline at end of file diff --git a/plugins/locked-payment-methods-for-woocommerce/assets/build/greeting-rtl.css b/plugins/locked-payment-methods-for-woocommerce/assets/build/greeting-rtl.css deleted file mode 100644 index 4070942..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/assets/build/greeting-rtl.css +++ /dev/null @@ -1 +0,0 @@ -[data-locked-payment-methods-for-woocommerce=greeting]{font-weight:600} diff --git a/plugins/locked-payment-methods-for-woocommerce/assets/build/greeting.asset.php b/plugins/locked-payment-methods-for-woocommerce/assets/build/greeting.asset.php deleted file mode 100644 index 5b607f2..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/assets/build/greeting.asset.php +++ /dev/null @@ -1,6 +0,0 @@ - array( - - ), - 'version' => 'f7397bd60bff06cddc75' -); diff --git a/plugins/locked-payment-methods-for-woocommerce/assets/build/greeting.css b/plugins/locked-payment-methods-for-woocommerce/assets/build/greeting.css deleted file mode 100644 index 4070942..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/assets/build/greeting.css +++ /dev/null @@ -1 +0,0 @@ -[data-locked-payment-methods-for-woocommerce=greeting]{font-weight:600} diff --git a/plugins/locked-payment-methods-for-woocommerce/assets/build/greeting.js b/plugins/locked-payment-methods-for-woocommerce/assets/build/greeting.js deleted file mode 100644 index e69de29..0000000 diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/build/blocks-manifest.php b/plugins/locked-payment-methods-for-woocommerce/blocks/build/blocks-manifest.php deleted file mode 100644 index 99f4491..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/blocks/build/blocks-manifest.php +++ /dev/null @@ -1,26 +0,0 @@ - array( - '$schema' => 'https://schemas.wp.org/trunk/block.json', - 'apiVersion' => 3, - 'name' => 'locked-payment-methods-for-woocommerce/example-notice', - 'title' => 'Example Notice', - 'category' => 'widgets', - 'icon' => 'info', - 'description' => 'Displays a static example notice.', - 'attributes' => array( - - ), - 'example' => array( - - ), - 'supports' => array( - 'html' => false - ), - 'textdomain' => 'locked-payment-methods-for-woocommerce', - 'editorScript' => 'file:./index.js', - 'editorStyle' => 'file:./index.css', - 'style' => 'file:./style-index.css' - ) -); diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/block.json b/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/block.json deleted file mode 100644 index 4fe0aa7..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/block.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 3, - "name": "locked-payment-methods-for-woocommerce/example-notice", - "title": "Example Notice", - "category": "widgets", - "icon": "info", - "description": "Displays a static example notice.", - "attributes": {}, - "example": {}, - "supports": { - "html": false - }, - "textdomain": "locked-payment-methods-for-woocommerce", - "editorScript": "file:./index.js", - "editorStyle": "file:./index.css", - "style": "file:./style-index.css" -} \ No newline at end of file diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index-rtl.css b/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index-rtl.css deleted file mode 100644 index 0dde168..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index-rtl.css +++ /dev/null @@ -1 +0,0 @@ -.wp-block-locked-payment-methods-for-woocommerce-example-notice{outline:1px dashed currentcolor} diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index.asset.php b/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index.asset.php deleted file mode 100644 index 7f9c68b..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index.asset.php +++ /dev/null @@ -1,9 +0,0 @@ - array( - 'react-jsx-runtime', - 'wp-block-editor', - 'wp-blocks', - 'wp-i18n' - ), - 'version' => '8467c3ed7cc607ac19aa' -); diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index.css b/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index.css deleted file mode 100644 index 0dde168..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index.css +++ /dev/null @@ -1 +0,0 @@ -.wp-block-locked-payment-methods-for-woocommerce-example-notice{outline:1px dashed currentcolor} diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index.js b/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index.js deleted file mode 100644 index 09daa8a..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/index.js +++ /dev/null @@ -1 +0,0 @@ -(()=>{"use strict";var e={857(){const e=window.wp.blocks,o=window.wp.blockEditor,t=window.wp.i18n,n=window.ReactJSXRuntime,s=JSON.parse('{"$schema":"https://schemas.wp.org/trunk/block.json","apiVersion":3,"name":"locked-payment-methods-for-woocommerce/example-notice","title":"Example Notice","category":"widgets","icon":"info","description":"Displays a static example notice.","attributes":{},"example":{},"supports":{"html":false},"textdomain":"locked-payment-methods-for-woocommerce","editorScript":"file:./index.js","editorStyle":"file:./index.css","style":"file:./style-index.css"}');(0,e.registerBlockType)(s,{edit:function(){return(0,n.jsx)("p",{...(0,o.useBlockProps)(),children:(0,t.__)("An example notice from Locked Payment Methods for WooCommerce.","locked-payment-methods-for-woocommerce")})},save:function(){return(0,n.jsx)("p",{...o.useBlockProps.save(),children:"An example notice from Locked Payment Methods for WooCommerce."})}})}};const o={};function t(n){const s=o[n];if(void 0!==s)return s.exports;const r=o[n]={exports:{}};return e[n](r,r.exports,t),r.exports}t.m=e,(()=>{const e=[];t.O=(o,n,s)=>{if(!n){for(var r=0;rObject.hasOwn(e,o),(()=>{const e={369:0,629:0};t.O.j=o=>0===e[o];const o=(o,n)=>{let[s,r,c]=n;var i,l,m=0;if(s.some(o=>0!==e[o])){for(i in r)t.o(r,i)&&(t.m[i]=r[i]);if(c)var a=c(t)}for(o&&o(n);mt(857));n=t.O(n)})(); \ No newline at end of file diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/style-index-rtl.css b/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/style-index-rtl.css deleted file mode 100644 index 9849cf9..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/style-index-rtl.css +++ /dev/null @@ -1 +0,0 @@ -.wp-block-locked-payment-methods-for-woocommerce-example-notice{border-right:4px solid;padding:.5em 1em}.wp-block-locked-payment-methods-for-woocommerce-example-notice.is-style-outlined{border:1px solid} diff --git a/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/style-index.css b/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/style-index.css deleted file mode 100644 index 4950830..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/blocks/build/example-notice/style-index.css +++ /dev/null @@ -1 +0,0 @@ -.wp-block-locked-payment-methods-for-woocommerce-example-notice{border-left:4px solid;padding:.5em 1em}.wp-block-locked-payment-methods-for-woocommerce-example-notice.is-style-outlined{border:1px solid} From 3502dfa506f8973522a2a6eea78101acf73dbcb6 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 00:23:14 +0100 Subject: [PATCH 29/50] Distribute Locked Payment Methods through wordpress.org v1's readme advertised a premium build, linked a knowledge base and a contact form that no longer exist, said only enabled payment methods can be locked and placed the settings on the Payments tab; v1's version history lived only in that readme. Locked Payment Methods stays listed on wordpress.org, so the entry drops the Update URI header, which leaves the bundled updater inert, and the text-domain loader, since language packs load just in time, and states what the plugin does. readme.txt keeps v1's Description, Installation, FAQ and Screenshots, updated: the five ways of unlocking a payment method as plain features, WooCommerce 11.2 and the Advanced tab, any registered payment method lockable, grants per site on a network, support on the forum. Its Upgrade Notice names the floors, the settings' move and the explicit-false unlock, and points at the changelog. CHANGELOG.md carries v1's history between the markers that changelog:write copies into readme.txt, through the Pronamic tool now in require-dev, and 23 fragments record 2.0.0's changes. X rulings 88 and 97. Assisted-by: Claude Code:claude-opus-5-5 --- .../CHANGELOG.md | 81 ++++++++++++++++- .../changelog/deprecated-functions | 4 + .../changelog/deprecated-hooks | 4 + .../changelog/downgrade | 4 + .../changelog/empty-roles | 4 + .../changelog/freemius | 4 + .../changelog/grant-choices | 4 + .../changelog/group-grants | 4 + .../changelog/lock-hooks | 4 + .../changelog/lock-list-filters | 4 + .../changelog/lock-safeguard | 4 + .../changelog/lockable-methods | 4 + .../changelog/order-grants | 4 + .../changelog/order-pay-key | 4 + .../changelog/plan-grants | 4 + .../changelog/removed-functions | 4 + .../changelog/removed-hooks | 4 + .../changelog/requirements | 4 + .../changelog/settings-section | 4 + .../changelog/settings-switches | 4 + .../changelog/translations | 4 + .../changelog/uninstall | 4 + .../changelog/user-grants | 4 + .../changelog/users-screen | 4 + .../composer.json | 6 +- .../composer.lock | 43 ++++++++- ...locked-payment-methods-for-woocommerce.php | 4 +- .../readme.txt | 90 +++++++++++++++++++ 28 files changed, 308 insertions(+), 8 deletions(-) create mode 100644 plugins/locked-payment-methods-for-woocommerce/changelog/deprecated-functions create mode 100644 plugins/locked-payment-methods-for-woocommerce/changelog/deprecated-hooks create mode 100644 plugins/locked-payment-methods-for-woocommerce/changelog/downgrade create mode 100644 plugins/locked-payment-methods-for-woocommerce/changelog/empty-roles create mode 100644 plugins/locked-payment-methods-for-woocommerce/changelog/freemius create mode 100644 plugins/locked-payment-methods-for-woocommerce/changelog/grant-choices create mode 100644 plugins/locked-payment-methods-for-woocommerce/changelog/group-grants create mode 100644 plugins/locked-payment-methods-for-woocommerce/changelog/lock-hooks create mode 100644 plugins/locked-payment-methods-for-woocommerce/changelog/lock-list-filters create mode 100644 plugins/locked-payment-methods-for-woocommerce/changelog/lock-safeguard create mode 100644 plugins/locked-payment-methods-for-woocommerce/changelog/lockable-methods create mode 100644 plugins/locked-payment-methods-for-woocommerce/changelog/order-grants create mode 100644 plugins/locked-payment-methods-for-woocommerce/changelog/order-pay-key create mode 100644 plugins/locked-payment-methods-for-woocommerce/changelog/plan-grants create mode 100644 plugins/locked-payment-methods-for-woocommerce/changelog/removed-functions create mode 100644 plugins/locked-payment-methods-for-woocommerce/changelog/removed-hooks create mode 100644 plugins/locked-payment-methods-for-woocommerce/changelog/requirements create mode 100644 plugins/locked-payment-methods-for-woocommerce/changelog/settings-section create mode 100644 plugins/locked-payment-methods-for-woocommerce/changelog/settings-switches create mode 100644 plugins/locked-payment-methods-for-woocommerce/changelog/translations create mode 100644 plugins/locked-payment-methods-for-woocommerce/changelog/uninstall create mode 100644 plugins/locked-payment-methods-for-woocommerce/changelog/user-grants create mode 100644 plugins/locked-payment-methods-for-woocommerce/changelog/users-screen create mode 100644 plugins/locked-payment-methods-for-woocommerce/readme.txt diff --git a/plugins/locked-payment-methods-for-woocommerce/CHANGELOG.md b/plugins/locked-payment-methods-for-woocommerce/CHANGELOG.md index cc205c3..e46512e 100644 --- a/plugins/locked-payment-methods-for-woocommerce/CHANGELOG.md +++ b/plugins/locked-payment-methods-for-woocommerce/CHANGELOG.md @@ -2,9 +2,84 @@ All notable changes to this plugin are documented in this file. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -Pending entries live in [`changelog/`](./changelog) — add via `composer changelog:add`. Aggregate into a release with `composer changelog:write`. +Pending entries live in [`changelog/`](./changelog) — add via `composer changelog:add`. Aggregate into a release with `composer changelog:write`, which also copies the versions between the markers into `readme.txt`. -## 2.0.0 - unreleased + + +## 1.3.5 - 2022-07-13 + +### Changed +- Tested up to WordPress 6.0.1. +- Tested up to WooCommerce 6.7.0. +- Updated Freemius SDK. + +## 1.3.4 - 2022-03-20 + +### Changed +- Tested up to WooCommerce 6.3. + +### Security +- Updated Freemius SDK. + +## 1.3.3 - 2022-02-08 + +### Changed +- Tested up to WordPress 5.9. +- Updated DWS framework. + +## 1.3.2 - 2022-01-14 + +### Changed +- Tested up to WooCommerce 6.1. +- Tested up to PHP 8.1. +- Updated DWS framework. + +### Fixed +- Handling of a framework initialization failure. + +## 1.3.1 - 2021-12-02 + +### Changed +- Tested up to the latest version of WordPress. +- Optimized the PSR-4 autoloader. +- Hook tags can be generated before the plugin is initialized. +- Updated DWS framework. + +## 1.3.0 - 2021-11-15 + +### Changed +- Tested up to the latest versions of WordPress and WooCommerce. +- Additional licensing terms for redistribution. +- Updated the dependencies' namespace. +- Updated DWS framework. + +### Fixed +- Readme formatting. + +## 1.2.1 - 2021-09-26 + +### Fixed +- Language domain inside the dependencies folder. +- PHP 8 error on the settings page. + +## 1.2.0 - 2021-09-24 + +### Changed +- Tested up to the latest versions of WordPress and WooCommerce. +- Updated DWS framework. +- Added more automated tests. + +## 1.1.0 - 2021-07-26 + +### Changed +- Tested up to the latest versions of WordPress and WooCommerce. +- Renamed the capabilities after WordPress core's. +- Moved the bootstrap functions to the global namespace. +- Updated DWS framework. + +## 1.0.0 - 2021-05-29 ### Added -- Initial release. +- First official release. + + diff --git a/plugins/locked-payment-methods-for-woocommerce/changelog/deprecated-functions b/plugins/locked-payment-methods-for-woocommerce/changelog/deprecated-functions new file mode 100644 index 0000000..3c6072c --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/changelog/deprecated-functions @@ -0,0 +1,4 @@ +Significance: minor +Type: deprecated + +v1's root getters, hook-tag builders, access checks, per-strategy checks and setting readers, and DWS_LPMWC_NAME, DWS_LPMWC_VERSION, DWS_LPMWC_PATH, DWS_LPMWC_MIN_PHP and DWS_LPMWC_MIN_WP; DWS_LPMWC_NAME no longer carries v1's whitelabel prefix. diff --git a/plugins/locked-payment-methods-for-woocommerce/changelog/deprecated-hooks b/plugins/locked-payment-methods-for-woocommerce/changelog/deprecated-hooks new file mode 100644 index 0000000..3bbe753 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/changelog/deprecated-hooks @@ -0,0 +1,4 @@ +Significance: minor +Type: deprecated + +v1's lock, access, group-fields and value filters and dws_lpmwc_initialized and dws_lpmwc_initialization_failure fire deprecated, where v1 fired them and before their replacements. diff --git a/plugins/locked-payment-methods-for-woocommerce/changelog/downgrade b/plugins/locked-payment-methods-for-woocommerce/changelog/downgrade new file mode 100644 index 0000000..8764a35 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/changelog/downgrade @@ -0,0 +1,4 @@ +Significance: minor +Type: changed + +A downgrade to 1.3.5 finds no unlocked payment methods for customers, orders, groups or plans, and locks every payment method on v1's list for everyone without full access. diff --git a/plugins/locked-payment-methods-for-woocommerce/changelog/empty-roles b/plugins/locked-payment-methods-for-woocommerce/changelog/empty-roles new file mode 100644 index 0000000..c261419 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/changelog/empty-roles @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +An empty list of full-access roles unlocks nothing; v1 then unlocked every locked payment method for everyone, guests included. diff --git a/plugins/locked-payment-methods-for-woocommerce/changelog/freemius b/plugins/locked-payment-methods-for-woocommerce/changelog/freemius new file mode 100644 index 0000000..e5e4d9e --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/changelog/freemius @@ -0,0 +1,4 @@ +Significance: major +Type: removed + +Freemius and the premium build's licensing, with dws_lpmwc_fs(), dws_lpmwc_fs_init(), dws_lpmwc_fs_settings_url(), dws_lpmwc_fs_loaded and the Upgrade and Freemius links; every way of unlocking a payment method is part of the plugin. diff --git a/plugins/locked-payment-methods-for-woocommerce/changelog/grant-choices b/plugins/locked-payment-methods-for-woocommerce/changelog/grant-choices new file mode 100644 index 0000000..7b3dcd1 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/changelog/grant-choices @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Saving a profile, an order, a group or a plan keeps the payment methods it unlocks that are no longer locked, which it lists marked as not locked. diff --git a/plugins/locked-payment-methods-for-woocommerce/changelog/group-grants b/plugins/locked-payment-methods-for-woocommerce/changelog/group-grants new file mode 100644 index 0000000..85a895f --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/changelog/group-grants @@ -0,0 +1,4 @@ +Significance: major +Type: changed + +A Groups group's unlocked payment methods move from v1's option per group into one dws_lpmwc_groups_unlocked_payment_methods map, which deleting the group leaves no entry of. diff --git a/plugins/locked-payment-methods-for-woocommerce/changelog/lock-hooks b/plugins/locked-payment-methods-for-woocommerce/changelog/lock-hooks new file mode 100644 index 0000000..587fa85 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/changelog/lock-hooks @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +dws_lpmwc/payment_method/locked_ids, which locks more payment methods, dws_lpmwc/payment_method/should_lock, which judges a payment method for the paying user and the order being paid, and dws_lpmwc_is_payment_method_locked(). diff --git a/plugins/locked-payment-methods-for-woocommerce/changelog/lock-list-filters b/plugins/locked-payment-methods-for-woocommerce/changelog/lock-list-filters new file mode 100644 index 0000000..b01645a --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/changelog/lock-list-filters @@ -0,0 +1,4 @@ +Significance: minor +Type: changed + +A filter on the locked payment methods' ids only adds ids, so a callback that removes one from locked_payment_methods_for_woocommerce/lock_manager/locked_payment_methods or locked_payment_methods_for_woocommerce/general_settings/get_validated_option_value no longer unlocks it. diff --git a/plugins/locked-payment-methods-for-woocommerce/changelog/lock-safeguard b/plugins/locked-payment-methods-for-woocommerce/changelog/lock-safeguard new file mode 100644 index 0000000..28e1d88 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/changelog/lock-safeguard @@ -0,0 +1,4 @@ +Significance: major +Type: changed + +The lock holds whenever the plugin is active, on a runtime below its floors, with WooCommerce below 11.2 and when the plugin fails to boot, until the plugin has booted; only an explicit false from dws_lpmwc/payment_method/should_lock unlocks a payment method, so a callback that returns null, 0 or an empty string no longer does. diff --git a/plugins/locked-payment-methods-for-woocommerce/changelog/lockable-methods b/plugins/locked-payment-methods-for-woocommerce/changelog/lockable-methods new file mode 100644 index 0000000..23fbde0 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/changelog/lockable-methods @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Any payment method registered with WooCommerce can be locked, disabled ones marked, so a locked payment method that is disabled stays locked once it is enabled again; v1 offered only enabled ones, and saving while a locked one was disabled unlocked it. diff --git a/plugins/locked-payment-methods-for-woocommerce/changelog/order-grants b/plugins/locked-payment-methods-for-woocommerce/changelog/order-grants new file mode 100644 index 0000000..f781256 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/changelog/order-grants @@ -0,0 +1,4 @@ +Significance: major +Type: changed + +An order's unlocked payment methods are one list under the order meta _dws_lpmwc_unlocked_payment_methods, edited in a box on the order screen on either order storage; the box is read-only on a paid order, so saving it keeps them, and the first update moves v1's _dws_wc_lpm_grant_access_{gateway} order meta into the list and deletes it. diff --git a/plugins/locked-payment-methods-for-woocommerce/changelog/order-pay-key b/plugins/locked-payment-methods-for-woocommerce/changelog/order-pay-key new file mode 100644 index 0000000..4a8f32b --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/changelog/order-pay-key @@ -0,0 +1,4 @@ +Significance: patch +Type: security + +An order unlocks its payment methods only on its own payment page and with its order key, so a checkout request naming another order's id no longer takes that order's unlocked payment methods. diff --git a/plugins/locked-payment-methods-for-woocommerce/changelog/plan-grants b/plugins/locked-payment-methods-for-woocommerce/changelog/plan-grants new file mode 100644 index 0000000..fdb63eb --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/changelog/plan-grants @@ -0,0 +1,4 @@ +Significance: major +Type: changed + +A WooCommerce Memberships plan's unlocked payment methods move from the plan meta _dws_wc_lpm_unlocked_payment_methods to _dws_lpmwc_unlocked_payment_methods, and its tab saves under a nonce of its own. diff --git a/plugins/locked-payment-methods-for-woocommerce/changelog/removed-functions b/plugins/locked-payment-methods-for-woocommerce/changelog/removed-functions new file mode 100644 index 0000000..fe5ad98 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/changelog/removed-functions @@ -0,0 +1,4 @@ +Significance: major +Type: removed + +dws_lpmwc_di_container(), dws_lpmwc_instance(), dws_lpmwc_component(), dws_lpmwc_instance_initialize(), dws_lpmwc_plugin_activate() and dws_lpmwc_plugin_uninstall(), which exposed v1's framework internals. diff --git a/plugins/locked-payment-methods-for-woocommerce/changelog/removed-hooks b/plugins/locked-payment-methods-for-woocommerce/changelog/removed-hooks new file mode 100644 index 0000000..6875e64 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/changelog/removed-hooks @@ -0,0 +1,4 @@ +Significance: major +Type: removed + +v1's settings hooks locked_payment_methods_for_woocommerce/settings/{registered_options_page,registered_options_groups,get_option_value,update_option_value,delete_option_value,validate_option_value} and locked_payment_methods_for_woocommerce/{general,integrations,plugin}_settings/validate_option_value, v1's framework notice actions and the dws_wp_framework/*/init_status filters. diff --git a/plugins/locked-payment-methods-for-woocommerce/changelog/requirements b/plugins/locked-payment-methods-for-woocommerce/changelog/requirements new file mode 100644 index 0000000..d7346a9 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/changelog/requirements @@ -0,0 +1,4 @@ +Significance: major +Type: changed + +Requires PHP 8.5, WordPress 7.1 and WooCommerce 11.2, and declares compatibility with WooCommerce's order tables. diff --git a/plugins/locked-payment-methods-for-woocommerce/changelog/settings-section b/plugins/locked-payment-methods-for-woocommerce/changelog/settings-section new file mode 100644 index 0000000..24b8927 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/changelog/settings-section @@ -0,0 +1,4 @@ +Significance: major +Type: changed + +The settings move from an unlisted section of WooCommerce's Payments tab to Locked Payment Methods on its Advanced tab, under dws_lpmwc_ options that the first update copies from v1's; v1's options stay until uninstall, so a downgrade to 1.3.5 still locks what it locked, and v1's settings URL no longer opens them. diff --git a/plugins/locked-payment-methods-for-woocommerce/changelog/settings-switches b/plugins/locked-payment-methods-for-woocommerce/changelog/settings-switches new file mode 100644 index 0000000..de7eb90 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/changelog/settings-switches @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Each way of unlocking a payment method is a checkbox, and turning one off keeps what it unlocked for when it is turned on again; the Groups and WooCommerce Memberships checkboxes show only while their plugin is active. diff --git a/plugins/locked-payment-methods-for-woocommerce/changelog/translations b/plugins/locked-payment-methods-for-woocommerce/changelog/translations new file mode 100644 index 0000000..39952ea --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/changelog/translations @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Translations come from translate.wordpress.org, and the plugin ships no bundled translation files. diff --git a/plugins/locked-payment-methods-for-woocommerce/changelog/uninstall b/plugins/locked-payment-methods-for-woocommerce/changelog/uninstall new file mode 100644 index 0000000..da7c381 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/changelog/uninstall @@ -0,0 +1,4 @@ +Significance: minor +Type: changed + +Uninstalling with Remove all data on uninstallation checked also deletes the unlocked payment methods of customers, orders, groups and plans, v1's options and the capabilities, on every site of a network that consents. diff --git a/plugins/locked-payment-methods-for-woocommerce/changelog/user-grants b/plugins/locked-payment-methods-for-woocommerce/changelog/user-grants new file mode 100644 index 0000000..211e4f0 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/changelog/user-grants @@ -0,0 +1,4 @@ +Significance: major +Type: changed + +A customer's unlocked payment methods are one list per site, under the user option dws_lpmwc_unlocked_payment_methods, edited on the profile behind unlock_dws_lpm_via_user_profiles; the first update moves v1's dws_wc_lpm_grant_access_{gateway} user meta into every site's list, so those keep applying network-wide, and deletes it, so a downgrade to 1.3.5 finds no customer's unlocked payment methods. diff --git a/plugins/locked-payment-methods-for-woocommerce/changelog/users-screen b/plugins/locked-payment-methods-for-woocommerce/changelog/users-screen new file mode 100644 index 0000000..fa0e5de --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/changelog/users-screen @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +The Users screen names each locked payment method's state in text and escapes it, and its filter applies from either dropdown; v1's showed icons without text and read only the top dropdown. diff --git a/plugins/locked-payment-methods-for-woocommerce/composer.json b/plugins/locked-payment-methods-for-woocommerce/composer.json index 6c143c5..0de19c2 100644 --- a/plugins/locked-payment-methods-for-woocommerce/composer.json +++ b/plugins/locked-payment-methods-for-woocommerce/composer.json @@ -60,6 +60,7 @@ "php-di/php-di": "^7.1.1", "php-stubs/woocommerce-stubs": "^11.2", "phpunit/phpunit": "^13", + "pronamic/changelog-md-to-wordpress-plugin-readme-txt": "^1.0", "wp-plugin/groups": "^4.8", "wp-plugin/woocommerce": "^11.2" }, @@ -134,7 +135,10 @@ "changelog:add": "@php vendor/bin/changelogger add", "changelog:validate": "@php vendor/bin/changelogger validate", - "changelog:write": "@php vendor/bin/changelogger write" + "changelog:write": [ + "@php vendor/bin/changelogger write", + "@php vendor/bin/pronamic-changelog-md-to-wordpress-plugin-readme-txt" + ] }, "extra": { diff --git a/plugins/locked-payment-methods-for-woocommerce/composer.lock b/plugins/locked-payment-methods-for-woocommerce/composer.lock index caf788f..c0e6158 100644 --- a/plugins/locked-payment-methods-for-woocommerce/composer.lock +++ b/plugins/locked-payment-methods-for-woocommerce/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "daf3375aeffc7b0510f18f581f61d7df", + "content-hash": "8096846a869e42b7074b39c03e99073f", "packages": [], "packages-dev": [ { @@ -2859,6 +2859,47 @@ "time": "2026-10-27T04:16:22+00:00", "published-time": "2026-10-27T04:18:40+00:00" }, + { + "name": "pronamic/changelog-md-to-wordpress-plugin-readme-txt", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/pronamic/pronamic-changelog-md-to-wordpress-plugin-readme-txt.git", + "reference": "9419cc48c0a348caeae74040c1c35e07535f63ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pronamic/pronamic-changelog-md-to-wordpress-plugin-readme-txt/zipball/9419cc48c0a348caeae74040c1c35e07535f63ed", + "reference": "9419cc48c0a348caeae74040c1c35e07535f63ed", + "shasum": "" + }, + "bin": [ + "bin/pronamic-changelog-md-to-wordpress-plugin-readme-txt" + ], + "type": "library", + "autoload": { + "psr-4": { + "Pronamic\\ChangelogMdToWordPressPluginReadmeTxt\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "Pronamic", + "email": "info@pronamic.eu" + } + ], + "description": "Automates syncing of changelog entries from CHANGELOG.md to WordPress plugin readme.txt.", + "support": { + "issues": "https://github.com/pronamic/pronamic-changelog-md-to-wordpress-plugin-readme-txt/issues", + "source": "https://github.com/pronamic/pronamic-changelog-md-to-wordpress-plugin-readme-txt/tree/v1.0.0" + }, + "time": "2024-10-01T13:41:34+00:00", + "published-time": "2024-10-02T09:31:51+00:00" + }, { "name": "psr/clock", "version": "1.0.0", diff --git a/plugins/locked-payment-methods-for-woocommerce/locked-payment-methods-for-woocommerce.php b/plugins/locked-payment-methods-for-woocommerce/locked-payment-methods-for-woocommerce.php index 43a547f..ea15055 100644 --- a/plugins/locked-payment-methods-for-woocommerce/locked-payment-methods-for-woocommerce.php +++ b/plugins/locked-payment-methods-for-woocommerce/locked-payment-methods-for-woocommerce.php @@ -2,7 +2,7 @@ /** * Plugin Name: Locked Payment Methods for WooCommerce * Plugin URI: https://github.com/ahegyes/wc-locked-payment-methods - * Description: WordPress plugins on the wp-framework packages start here, from one tested anatomy, so all are built alike and a fix found in one applies to all. + * Description: Hides the payment methods you lock from customers at checkout until you unlock them for a user, a role, an order, a group or a membership plan. * Version: 2.0.0 * Requires at least: 7.1 * Requires PHP: 8.5 @@ -13,7 +13,6 @@ * Author URI: https://github.com/ahegyes * License: GPL-3.0-or-later * License URI: https://www.gnu.org/licenses/gpl-3.0.html - * Update URI: https://github.com/ahegyes/wc-locked-payment-methods * Text Domain: locked-payment-methods-for-woocommerce * Domain Path: /languages * @@ -26,7 +25,6 @@ defined( 'ABSPATH' ) || exit; define( 'DWS_LPMWC_FILE', __FILE__ ); -load_plugin_textdomain( 'locked-payment-methods-for-woocommerce', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); // phpcs:ignore PluginCheck.CodeAnalysis.DiscouragedFunctions.load_plugin_textdomainFound -- WordPress registers languages/ itself only for plugins it does not network-activate. require_once __DIR__ . '/includes/safeguards.php'; // Before the floor gate, so the restrictions hold on any runtime that parses the plugin. diff --git a/plugins/locked-payment-methods-for-woocommerce/readme.txt b/plugins/locked-payment-methods-for-woocommerce/readme.txt new file mode 100644 index 0000000..a682b09 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/readme.txt @@ -0,0 +1,90 @@ +=== Locked Payment Methods for WooCommerce === +Contributors: ahegyes, deepwebsolutions +Tags: payment methods, woocommerce, checkout, manual permission +Tested up to: 7.1 +Stable tag: 2.0.0 +Requires at least: 7.1 +Requires PHP: 8.5 +License: GPLv3 or later +License URI: https://www.gnu.org/licenses/gpl-3.0.html + +Hides the payment methods you lock from customers at checkout until you unlock them for a user, a role, an order, a group or a membership plan. + +== Description == + +**Locked Payment Methods for WooCommerce hides the payment methods you lock from every customer you have not unlocked them for.** + +A typical use-case involves locking high-risk payment methods like *Cash on delivery* or *Pay by invoice*, but you can lock any payment method registered with WooCommerce, for any reason. + +Locked payment methods are removed from the checkout and the order payment page for every customer they are not unlocked for. You can then unlock them case by case, or by criteria for logged-in users you trust, while everyone else sees only the payment methods you leave unlocked. + +### Unlocking a payment method for your customers + +* Unlock payment methods for a customer from their user profile. +* Unlock every payment method for all customers with given user roles. +* Unlock payment methods for one unpaid order from its edit screen. +* Unlock payment methods for every active member of a [WooCommerce Memberships](https://woocommerce.com/products/woocommerce-memberships/) plan. +* Unlock payment methods for every member of a [Groups](https://wordpress.org/plugins/groups/) group and of its subgroups. + +Developers can lock and unlock payment methods by their own rules through the plugin's filters. + +== Installation == + +This plugin requires WooCommerce 11.2 or newer. If you're running a lower version, please update it first. + +### INSTALL FROM WITHIN WORDPRESS + +1. Visit the plugins page within your dashboard and select `Add New Plugin`. +1. Search for `Locked Payment Methods for WooCommerce` and click the `Install Now` button. +1. Activate the plugin from within your `Plugins` page. + +### INSTALL MANUALLY + +1. Download the plugin from https://wordpress.org/plugins/locked-payment-methods-for-woocommerce/ and unzip the archive. +1. Copy the unzipped `locked-payment-methods-for-woocommerce` folder into your site's `wp-content/plugins` folder, through SFTP or your host's file manager. +1. Activate the plugin through the `Plugins` menu in WordPress. + +### AFTER ACTIVATION + +If WooCommerce 11.2 or newer is active, you will find a section called `Locked Payment Methods` under `WooCommerce` → `Settings` → `Advanced`, which the plugin's `Settings` link on the `Plugins` page also opens. There you will be able to: + +1. Choose which payment methods are locked by default. +1. Choose which ways of unlocking them you use, and which user roles have access to every payment method. +1. Choose whether to remove the plugin's data, the unlocked payment methods included, when it is uninstalled. + +== Frequently Asked Questions == + += What is a locked payment method? = + +That's what we call payment methods that you choose to hide from your customers by default. Granting a customer access is called *unlocking* the payment method. + += Which payment methods can I lock? = + +Any payment method registered with WooCommerce, disabled ones included. A locked payment method that you disable stays locked, so it is still locked once you enable it again. + += Are unlocked payment methods shared across a multisite network? = + +No. A customer's unlocked payment methods are kept per site, so unlocking a payment method for a customer on one store of a network leaves it locked on the others. Payment methods unlocked before version 2.0.0 keep applying on every site. + += How can I get help if I'm stuck? = + +You can open a community support ticket here at [wordpress.org](https://wordpress.org/support/plugin/locked-payment-methods-for-woocommerce/). + +== Screenshots == + +1. Plugin settings section within the WooCommerce settings. +2. Example of how to unlock payment methods for all users of given roles. +3. Example of how to unlock payment methods for a specific user. + +== Upgrade Notice == + += 2.0.0 = +Requires PHP 8.5, WordPress 7.1 and WooCommerce 11.2. Settings move to WooCommerce → Settings → Advanced, and a customer's unlocked payment methods become per site. Only an explicit false unlocks a payment method. Read the changelog before updating. + +== Changelog == + + + + + +[See the full changelog on GitHub.](https://github.com/ahegyes/wc-locked-payment-methods/blob/trunk/CHANGELOG.md) From f5c05553bfa4141b85901a33da89030e49c8dac3 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 00:40:10 +0100 Subject: [PATCH 30/50] Unhook each strategy's test class after its rows The Groups, Memberships and order rows hook their own strategy for the whole class and never unhooked it, so every later class in the process ran it too. In the root's shared environment, which has no Groups, the leftover Groups strategy threw on Groups_User from the user profile rows' checkout filter. Each class clones WordPress's hook table before it hooks its strategy and puts the copy back after its last row, as WordPress's own test case does around each test. Assisted-by: Claude Code:claude-opus-5-5 --- .../tests/Integration/UnlockStrategies/GroupsStrategyTest.php | 4 ++++ .../tests/Integration/UnlockStrategies/OrderStrategyTest.php | 4 ++++ .../UnlockStrategies/WooCommerceMembershipsStrategyTest.php | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/GroupsStrategyTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/GroupsStrategyTest.php index e10b264..025a00a 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/GroupsStrategyTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/GroupsStrategyTest.php @@ -18,6 +18,8 @@ final class GroupsStrategyTest extends TestCase { protected static GroupsStrategy $strategy; + protected static array $hooks = array(); + protected static array $users = array(); protected array $stored = array(); @@ -29,6 +31,7 @@ final class GroupsStrategyTest extends TestCase { public static function setUpBeforeClass(): void { require_once ABSPATH . 'wp-admin/includes/user.php'; self::$strategy = new GroupsStrategy( new AdminNotices( 'dws_lpmwc', new PsrNullLogger() ) ); + self::$hooks = \array_map( static fn ( \WP_Hook $hook ): \WP_Hook => clone $hook, $GLOBALS['wp_filter'] ); self::$strategy->register_hooks(); foreach ( array( 'administrator', 'shop_manager', 'customer', 'subscriber' ) as $role ) { self::$users[ $role ] = (int) wp_insert_user( @@ -47,6 +50,7 @@ public static function tearDownAfterClass(): void { foreach ( self::$users as $user ) { wp_delete_user( $user ); } + $GLOBALS['wp_filter'] = self::$hooks; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Unhooks the class's strategy and its form. } #[Before] diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/OrderStrategyTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/OrderStrategyTest.php index a784e05..17ddcfe 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/OrderStrategyTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/OrderStrategyTest.php @@ -24,6 +24,8 @@ final class OrderStrategyTest extends TestCase { protected static OrderStrategy $strategy; + protected static array $hooks = array(); + protected static int $admin; protected array $stored = array(); @@ -33,6 +35,7 @@ final class OrderStrategyTest extends TestCase { public static function setUpBeforeClass(): void { require_once ABSPATH . 'wp-admin/includes/user.php'; self::$strategy = new OrderStrategy( new AdminNotices( 'dws_lpmwc', new PsrNullLogger() ) ); + self::$hooks = \array_map( static fn ( \WP_Hook $hook ): \WP_Hook => clone $hook, $GLOBALS['wp_filter'] ); self::$strategy->register_hooks(); self::$admin = (int) wp_insert_user( array( @@ -47,6 +50,7 @@ public static function setUpBeforeClass(): void { #[\Override] public static function tearDownAfterClass(): void { wp_delete_user( self::$admin ); + $GLOBALS['wp_filter'] = self::$hooks; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Unhooks the class's strategy and its form. } #[Before] diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/WooCommerceMembershipsStrategyTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/WooCommerceMembershipsStrategyTest.php index 52b6fcb..a9c3ed1 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/WooCommerceMembershipsStrategyTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/WooCommerceMembershipsStrategyTest.php @@ -24,6 +24,8 @@ final class WooCommerceMembershipsStrategyTest extends TestCase { protected static WooCommerceMembershipsStrategy $strategy; + protected static array $hooks = array(); + protected static array $users = array(); protected array $stored = array(); @@ -36,6 +38,7 @@ public static function setUpBeforeClass(): void { require_once ABSPATH . 'wp-admin/includes/user.php'; register_post_type( 'wc_membership_plan' ); self::$strategy = new WooCommerceMembershipsStrategy( new AdminNotices( 'dws_lpmwc', new PsrNullLogger() ) ); + self::$hooks = \array_map( static fn ( \WP_Hook $hook ): \WP_Hook => clone $hook, $GLOBALS['wp_filter'] ); self::$strategy->register_hooks(); foreach ( array( 'administrator', 'customer', 'subscriber' ) as $role ) { self::$users[ $role ] = (int) wp_insert_user( @@ -55,6 +58,7 @@ public static function tearDownAfterClass(): void { wp_delete_user( $user ); } unregister_post_type( 'wc_membership_plan' ); + $GLOBALS['wp_filter'] = self::$hooks; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Unhooks the class's strategy and its form. } // The form evaluates its definition and saves an object once per request, so every row edits a plan of its own. From 0d4231c9e06dd53dee5ca49e9dc1df5c287f909d Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 00:40:10 +0100 Subject: [PATCH 31/50] Declare Locked Payment Methods compatible with the order tables The template's declaration marks a new plugin incompatible with WooCommerce's order tables until its own evidence says otherwise, so WooCommerce warned on every Locked Payment Methods site that stores orders there, though the plugin reads and writes orders only through WC CRUD and its order rows run on both storages. The plugin declares itself compatible, and a boot row checks the declaration. It declares nothing for the cart and checkout blocks, whose checkout the lock covers through the gateways filter they share. X ruling 88. Assisted-by: Claude Code:claude-opus-5-5 --- .../locked-payment-methods-for-woocommerce/functions.php | 4 ++-- .../tests/Integration/PluginBootTest.php | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/locked-payment-methods-for-woocommerce/functions.php b/plugins/locked-payment-methods-for-woocommerce/functions.php index b320d21..ddf3ffc 100644 --- a/plugins/locked-payment-methods-for-woocommerce/functions.php +++ b/plugins/locked-payment-methods-for-woocommerce/functions.php @@ -82,7 +82,7 @@ function dws_lpmwc_deactivate(): void { } /** - * Declares whether the plugin works with WooCommerce's order tables, which each plugin built from the template sets from its own evidence. + * Declares that the plugin works with WooCommerce's order tables, which its tests cover on both order storages. * * @since 2.0.0 * @version 2.0.0 @@ -90,5 +90,5 @@ function dws_lpmwc_deactivate(): void { * @return void */ function dws_lpmwc_declare_compatibility(): void { - FeaturesUtil::declare_compatibility( 'custom_order_tables', DWS_LPMWC_FILE, false ); + FeaturesUtil::declare_compatibility( 'custom_order_tables', DWS_LPMWC_FILE, true ); } diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php index f8694df..cacfda6 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php @@ -47,4 +47,10 @@ public function test_the_stored_data_is_at_the_revision_of_the_code(): void { self::assertInstanceOf( MigrationRunner::class, dws_lpmwc_plugin()->migrations ); self::assertSame( \count( dws_lpmwc_get_migration_steps() ), MigrationRunner::get_revision( Plugin::PREFIX . '_revision' ) ); } + + public function test_the_plugin_declares_itself_compatible_with_woocommerces_order_tables(): void { + $plugins = \Automattic\WooCommerce\Utilities\FeaturesUtil::get_compatible_plugins_for_feature( 'custom_order_tables' ); + + self::assertContains( plugin_basename( DWS_LPMWC_FILE ), $plugins['compatible'] ); + } } From 4e9934a7ddb494fc4b919be406ce95cc77a98319 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 00:40:10 +0100 Subject: [PATCH 32/50] Check that the readme repeats the entry's version and floors wordpress.org reads the stable tag and the floors from readme.txt, while WordPress and the release read them from the entry's headers, and nothing kept the two in step. A Unit row compares readme.txt's Stable tag, Requires at least and Requires PHP with the entry's headers, and another checks the changelog markers the readme sync fills, as Linked Orders' does. Assisted-by: Claude Code:claude-opus-5-5 --- .../tests/Unit/ReadmeTest.php | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/Unit/ReadmeTest.php diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Unit/ReadmeTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Unit/ReadmeTest.php new file mode 100644 index 0000000..a17ccb3 --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Unit/ReadmeTest.php @@ -0,0 +1,30 @@ +.+/s', $readme ); + } + + protected static function read_fields( string $file ): array { + \preg_match_all( '/^[\s*]*([A-Za-z ]+):\s*(\S.*?)\s*$/m', (string) \file_get_contents( \dirname( __DIR__, 2 ) . "/$file" ), $matches ); + + return \array_combine( $matches[1], $matches[2] ); + } +} From 8f0723e4d5a6b84a32a8bbcd0a710ec9b7e8e950 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 00:40:11 +0100 Subject: [PATCH 33/50] Test the lock on the block checkout and the Store API The Integration rows filter the gateways directly, so no row showed a granted customer the block checkout or sent the Store API a checkout naming a locked payment method. An end-to-end spec locks cash on delivery and opens the store, which a new WooCommerce install keeps as coming soon, then checks that a customer it is granted to sees it on the block checkout, and that the Store API refuses a guest's checkout naming it with woocommerce_rest_checkout_payment_method_disabled. Assisted-by: Claude Code:claude-opus-5-5 --- .../tests/e2e/checkout.spec.ts | 178 ++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 plugins/locked-payment-methods-for-woocommerce/tests/e2e/checkout.spec.ts diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/e2e/checkout.spec.ts b/plugins/locked-payment-methods-for-woocommerce/tests/e2e/checkout.spec.ts new file mode 100644 index 0000000..73c34cf --- /dev/null +++ b/plugins/locked-payment-methods-for-woocommerce/tests/e2e/checkout.spec.ts @@ -0,0 +1,178 @@ +/// +import { execFileSync } from 'node:child_process'; +import { expect, test } from '@wordpress/e2e-test-utils-playwright'; + +// No screen enables a payment method, locks it or grants it in one step, so the site's WP-CLI stores them. +const cli = ( ...args: string[] ): string => + execFileSync( + 'node_modules/.bin/wp-env', + [ '--config', '.wp-env.tests.json', 'run', 'cli', 'wp', ...args ], + { encoding: 'utf8', stdio: [ 'ignore', 'pipe', 'ignore' ] } + ) + .trim() + .split( '\n' ) + .pop() ?? ''; + +// A context takes the config's storage state, the administrator's session, unless it names its own. +const LOGGED_OUT = { cookies: [], origins: [] }; + +test.describe( 'Checkout', () => { + let product: number; + let customer: number; + let comingSoon: string; + + test.beforeAll( async ( { requestUtils } ) => { + // A new store shows its pages as coming soon to everyone but its managers, so the rows open it while they run. + comingSoon = cli( + 'eval', + 'echo get_option( "woocommerce_coming_soon", "no" );' + ); + cli( 'option', 'update', 'woocommerce_coming_soon', 'no' ); + for ( const gateway of [ 'cod', 'bacs' ] ) { + cli( + 'wc', + 'payment_gateway', + 'update', + gateway, + '--enabled=true', + '--user=1' + ); + } + cli( + 'option', + 'update', + 'dws_lpmwc_locked_payment_methods', + JSON.stringify( [ 'cod' ] ), + '--format=json' + ); + product = ( + await requestUtils.rest< { id: number } >( { + method: 'POST', + path: '/wc/v3/products', + data: { + name: 'Locked checkout', + type: 'simple', + regular_price: '10', + virtual: true, + }, + } ) + ).id; + customer = Number( + cli( + 'user', + 'create', + 'lpm-e2e-customer', + 'lpm-e2e-customer@example.org', + '--role=customer', + '--user_pass=password', + '--porcelain' + ) + ); + cli( + 'eval', + `update_user_option( ${ customer }, DWS_LPMWC_USER_OPTION_UNLOCKED_METHODS, array( 'cod' ) );` + ); + } ); + + test.afterAll( async ( { requestUtils } ) => { + await requestUtils.rest( { + method: 'DELETE', + path: `/wc/v3/products/${ product }`, + params: { force: true }, + } ); + cli( 'user', 'delete', String( customer ), '--yes' ); + cli( + 'option', + 'update', + 'dws_lpmwc_locked_payment_methods', + '[]', + '--format=json' + ); + for ( const gateway of [ 'cod', 'bacs' ] ) { + cli( + 'wc', + 'payment_gateway', + 'update', + gateway, + '--enabled=false', + '--user=1' + ); + } + cli( 'option', 'update', 'woocommerce_coming_soon', comingSoon ); + } ); + + test( 'a customer granted a locked payment method may choose it on the block checkout', async ( { + browser, + baseURL, + } ) => { + const context = await browser.newContext( { + baseURL, + storageState: LOGGED_OUT, + } ); + + try { + const page = await context.newPage(); + await page.goto( '/wp-login.php' ); + await page + .getByLabel( 'Username or Email Address' ) + .fill( 'lpm-e2e-customer' ); + await page + .getByLabel( 'Password', { exact: true } ) + .fill( 'password' ); + await page.getByRole( 'button', { name: 'Log In' } ).click(); + await page.waitForLoadState(); + await page.goto( `/?add-to-cart=${ product }` ); + + await page.goto( '/checkout/' ); + await expect( + page.getByLabel( 'Direct bank transfer' ) + ).toBeVisible(); + await expect( page.getByLabel( 'Cash on delivery' ) ).toBeVisible(); + } finally { + await context.close(); + } + } ); + + test( "the Store API refuses a guest's checkout with a locked payment method", async ( { + playwright, + baseURL, + } ) => { + const api = await playwright.request.newContext( { + baseURL, + storageState: LOGGED_OUT, + } ); + + try { + const nonce = ( + await api.get( '/wp-json/wc/store/v1/cart' ) + ).headers().nonce; + await api.post( '/wp-json/wc/store/v1/cart/add-item', { + headers: { Nonce: nonce }, + data: { id: product, quantity: 1 }, + } ); + const checkout = await api.post( '/wp-json/wc/store/v1/checkout', { + headers: { Nonce: nonce }, + data: { + billing_address: { + first_name: 'Guest', + last_name: 'Buyer', + address_1: '1 Main Street', + city: 'Springfield', + postcode: '12345', + country: 'US', + state: 'CA', + email: 'lpm-e2e-guest@example.org', + }, + payment_method: 'cod', + }, + } ); + + expect( checkout.status() ).toBe( 400 ); + expect( ( await checkout.json() ).code ).toBe( + 'woocommerce_rest_checkout_payment_method_disabled' + ); + } finally { + await api.dispose(); + } + } ); +} ); From 6346388bdf983cbe6304f6a334ee71bc1a762ce5 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 00:40:11 +0100 Subject: [PATCH 34/50] Describe Locked Payment Methods in its own docs The plugin's README, AGENTS.md, architecture and tests docs, SECURITY scope and manifest descriptions still described the template: its demo, its fill-in, its blocks and its GitHub distribution. docs/ recipes.md, the template's guide, came with the scaffold, and phpcs.dist.xml kept the comment of the build exclusions the demo's removal dropped. Each now describes Locked Payment Methods: what it does and how other plugins lock and unlock payment methods, its strategies, its lock and v1's API, its data under dws_lpmwc_ and what the migrations move from v1, its grants forms, order storage and v1-hook conventions, and its wordpress.org distribution. Release passes wp-org: true and Quality lints readme.txt, as Linked Orders' workflows do, which the plugin's now match. They point at the template's recipes, which it maintains. Assisted-by: Claude Code:claude-opus-5-5 --- .../.github/workflows/quality.yml | 14 +- .../.github/workflows/release.yml | 3 +- .../AGENTS.md | 68 +- .../README.md | 53 +- .../SECURITY.md | 3 +- .../composer.json | 2 +- .../docs/ARCHITECTURE.md | 40 +- .../docs/recipes.md | 1293 ----------------- .../package.json | 2 +- .../phpcs.dist.xml | 2 - .../tests/README.md | 14 +- 11 files changed, 104 insertions(+), 1390 deletions(-) delete mode 100644 plugins/locked-payment-methods-for-woocommerce/docs/recipes.md diff --git a/plugins/locked-payment-methods-for-woocommerce/.github/workflows/quality.yml b/plugins/locked-payment-methods-for-woocommerce/.github/workflows/quality.yml index 27586d6..27647ff 100644 --- a/plugins/locked-payment-methods-for-woocommerce/.github/workflows/quality.yml +++ b/plugins/locked-payment-methods-for-woocommerce/.github/workflows/quality.yml @@ -36,7 +36,7 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - # GitHub refuses a GITHUB_TOKEN push that changes a workflow, so the fill-in leaves workflows alone and they read the slug the plugin declares. + # The workflows read the slug the plugin declares, as the template's do, so a change to the template's applies here unedited. - name: Read the slug from composer.json id: slug run: | @@ -55,6 +55,18 @@ jobs: php-versions: '["7.4"]' paths: ${{ format('["{0}.php", "uninstall.php", "includes"]', needs.slug.outputs.slug) }} + readme-lint: + name: readme.txt lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: thetwopct/wporg-plugin-readme-linter@1f869aba959265ecc90935b2380c6569d56646ac # v1.0.2 + with: + path: readme.txt + fail-on: error + changelog: name: Changelog fragments runs-on: ubuntu-latest diff --git a/plugins/locked-payment-methods-for-woocommerce/.github/workflows/release.yml b/plugins/locked-payment-methods-for-woocommerce/.github/workflows/release.yml index da95cb5..9d43fc9 100644 --- a/plugins/locked-payment-methods-for-woocommerce/.github/workflows/release.yml +++ b/plugins/locked-payment-methods-for-woocommerce/.github/workflows/release.yml @@ -24,7 +24,7 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - # GitHub refuses a GITHUB_TOKEN push that changes a workflow, so the fill-in leaves workflows alone and they read the slug the plugin declares. + # The workflows read the slug the plugin declares, as the template's do, so a change to the template's applies here unedited. - name: Read the slug from composer.json id: slug run: | @@ -49,5 +49,6 @@ jobs: wp-env-config-file: '.wp-env.tests.json' # Both parse every PHP file with php-parser 4, which stops at PHP 8.4 syntax such as protected(set). exclude-checks: 'prefixing,direct_file_access' + wp-org: true # A pull request or a dispatch on a branch rehearses the release without publishing it. publish: ${{ github.ref_type == 'tag' }} diff --git a/plugins/locked-payment-methods-for-woocommerce/AGENTS.md b/plugins/locked-payment-methods-for-woocommerce/AGENTS.md index c701853..b1dffc6 100644 --- a/plugins/locked-payment-methods-for-woocommerce/AGENTS.md +++ b/plugins/locked-payment-methods-for-woocommerce/AGENTS.md @@ -1,28 +1,35 @@ # wc-locked-payment-methods -A WordPress plugin built on the five wp-framework packages (`ahegyes/wp-framework-{bootstrap,shared,core,settings,woocommerce}`), from the template that starts every such plugin. It ships its own copy of the framework, PHP-DI and their dependencies, prefixed under `DeepWebSolutions\LockedPaymentMethods\Scoped` by php-scoper through wordpress-configs into `vendor-prefixed/` on every development install. Its architecture follows the framework's conventions 1–10 in [wordpress-framework's AGENTS.md](https://github.com/ahegyes/wordpress-framework/blob/trunk/AGENTS.md); this file covers what is particular to the plugin. `docs/ARCHITECTURE.md` describes the boot, `docs/recipes.md` the common changes, `tests/README.md` the suites. +Locked Payment Methods for WooCommerce, the v2 port of the wordpress.org plugin of that name, built on the five wp-framework packages (`ahegyes/wp-framework-{bootstrap,shared,core,settings,woocommerce}`) from the plugin template. It ships its own copy of the framework, PHP-DI and their dependencies, prefixed under `DeepWebSolutions\LockedPaymentMethods\Scoped` by php-scoper through wordpress-configs into `vendor-prefixed/` on every development install. Its architecture follows the framework's conventions 1–10 in [wordpress-framework's AGENTS.md](https://github.com/ahegyes/wordpress-framework/blob/trunk/AGENTS.md); this file covers what is particular to the plugin. `docs/ARCHITECTURE.md` describes the boot and the data, `tests/README.md` the suites, and the template's [recipes](https://github.com/ahegyes/wordpress-plugin-template/blob/trunk/docs/recipes.md) the common changes, under the template's `dws_tpl` prefix. + +v1 sites upgrade in place: the port moves v1's settings and grants under `dws_lpmwc_` through migration steps, keeps v1's capabilities, settings section id and helper API, and fires each v1 hook, deprecated, where v1 fired it. v1's premium build's strategies are part of the plugin. ## Layout ``` -locked-payment-methods-for-woocommerce.php the entry: safeguards, bootstrap load.php, the updater, the floor check, the build gate, the autoloader -functions.php dws_lpmwc_plugin(), the boot on plugins_loaded 15, deactivation, the HPOS declaration (false), the includes/ loader +locked-payment-methods-for-woocommerce.php the entry: safeguards, bootstrap load.php, the inert updater, the floor check, the build gate, the autoloader +functions.php dws_lpmwc_plugin(), the boot on plugins_loaded 15, deactivation, the order-tables declaration, v1's boot hooks, the includes/ loader uninstall.php the framework's uninstall engine over the footprint, with DWS_LPMWC_OPTION_DELETE_DATA's consent -src/ the composition root and its components only +src/ the composition root, its components and the plan tab host ├── Plugin.php SLUG, PREFIX (dws_lpmwc), COMPONENTS, the logger, the scheduler, the migration runner and the kernel -├── Settings.php the WooCommerce settings tab dws_lpmwc: the greeting's fields and the uninstall consent -├── Blocks.php the blocks from blocks/build/blocks-manifest.php and the block-editor script -└── Feature/ the greeting: a composite gated on DWS_LPMWC_OPTION_FEATURE and its account-dashboard child +├── Settings.php the dws-locked-payment-methods section on WooCommerce's Advanced tab, v1's three groups, and the plugin row's links +├── UnlockStrategies/ one component per way of unlocking, each gated on its switch, answering should_lock and owning its screen +│ ├── UserRoleStrategy.php every locked method for the full-access roles +│ ├── UserProfileStrategy.php per-site user grants on the profile, the Users column and filter +│ ├── OrderStrategy.php order grants in a box on the order screen, on either storage +│ ├── GroupsStrategy.php group grants on Groups' own forms, gated on Groups too +│ └── WooCommerceMembershipsStrategy.php plan grants in a tab of the plan data, gated on Memberships too +└── EditScreen/Hosts/MembershipPlanTab.php an ObjectForm host for Memberships' plan data box includes/ hook-free code as dws_lpmwc_ functions and DWS_LPMWC_ constants, loaded in name order, _-prefixed files skipped -├── safeguards.php restrictions that hold whenever the plugin cannot boot; loaded by the entry before the floor check -├── options.php the option keys, their defaults, the typed readers, the translated text and the validator -├── footprint.php dws_lpmwc_get_footprint(), every kind of data the plugin stores +├── safeguards.php the lock, its v1 filters and dws_lpmwc_should_lock(); loaded by the entry before the floor check +├── capabilities.php v1's four capabilities and the roles CapabilityGrants gives them +├── grants.php the grants' keys and readers, the grants fields' choices and dws_lpmwc_is_payment_method_locked() +├── options.php the settings' keys, defaults and typed readers, and v1's settings map +├── deprecated.php v1's root getters, hook-tag builders, constants, boot-hook relays, access checks and setting readers, deprecated +├── footprint.php dws_lpmwc_get_footprint(), including what v1 stored under its own names ├── migrations.php dws_lpmwc_get_migration_steps() and one function per step, append-only -├── assets.php the *.asset.php reader and the script and style registrars └── metadata.php the slug, name and version getters, the plugin's surface for a peer plugin -blocks/src/, assets/src/ TypeScript and SCSS, built into blocks/build/ and assets/build/ (untracked) -models/, templates/ empty extension points -.github/scripts/fill-in-scaffold.sh the fill-in's sweep, which fill-in-scaffold.yml runs once in a new plugin and Quality rehearses here +changelog/ the pending entries changelog:write turns into CHANGELOG.md and readme.txt's changelog ``` ## Commands @@ -30,12 +37,12 @@ models/, templates/ empty extension points ```sh composer packages-install # dependencies, then php-scoper into vendor-prefixed/ composer test:unit -composer test:integration # starts .wp-env.tests.json, then runs test:integration:run, which builds the scripts first +composer test:integration # starts .wp-env.tests.json, then runs test:integration:run composer quality-check # manifest and changelog validation, PHPCS (both profiles), PHPStan (both profiles), Unit and Integration -npm run setup # both installs, the scripts' build, then the development site -npm run build # blocks and assets through the local wp-scripts; npm run start watches them in a POSIX shell -npm run lint:scripts # ESLint, beside lint:styles (Stylelint) and lint:types (tsc) -npm run test:e2e # Playwright on .wp-env.tests.json, starting it if needed; the scripts must be built +composer changelog:add # stages an entry in changelog/; composer changelog:write, at release, also syncs readme.txt +npm run setup # both installs, then the development site +npm run lint:scripts # ESLint over tests/e2e, beside lint:types (tsc); the plugin builds no scripts or styles +npm run test:e2e # Playwright on .wp-env.tests.json, starting it if needed ``` Run wp-scripts only through the npm scripts or `node_modules/.bin/wp-scripts`: `npx wp-scripts` can install an unrelated package of that name. @@ -45,30 +52,35 @@ Run wp-scripts only through the npm scripts or `node_modules/.bin/wp-scripts`: ` - `.wp-env.json`: development, as `local`, on port 8826, behind `npm run setup` and `wp-env:*`. The framework rethrows boot and migration failures there. - `.wp-env.tests.json`: Integration and end to end, as `production`, on port 8827, behind `wp-env:tests:*`, `composer test:integration` and `npm run test:e2e`. The end-to-end suite deactivates and reactivates WooCommerce there. -Both run PHP 8.5 with WooCommerce from `vendor/` and its order tables on; CI runs Integration and end to end on every WordPress branch from the entry's `Requires at least` up, and nightly on WordPress trunk. +Both run PHP 8.5 with WooCommerce and Groups from `vendor/` and WooCommerce's order tables on; CI runs Integration and end to end on every WordPress branch from the entry's `Requires at least` up, and nightly on WordPress trunk. The monorepo's shared environment runs the Integration suite beside Linked Orders and Internal Comments, without the `groups` and `wc-memberships` groups. ## Conventions -1. **Placement.** `src/` holds only the composition root and its components. Code that registers no hooks, such as keys, readers, the footprint, migration steps and asset registrars, is `dws_lpmwc_` functions and `DWS_LPMWC_` constants in `includes/`, which parses on PHP 7.4: no `mixed` or union types, typed constants, `match` or first-class callables there. Container definitions are composition and stay in `Plugin::get_definitions()`. -2. **Variants.** A component that exists only in one build variant is a child in a branch of its parent's `get_child_component_classes()`, never a second entry in `COMPONENTS`. +1. **Placement.** `src/` holds the composition root, its components and the plan tab host. Code that registers no hooks, such as keys, readers, v1's helper API, the footprint and migration steps, is `dws_lpmwc_` functions and `DWS_LPMWC_` constants in `includes/`, which parses on PHP 7.4: no `mixed` or union types, typed constants, `match`, nullsafe calls or first-class callables there. An include that reads `Plugin` while it loads must sort after `metadata.php`, so `capabilities.php` and `grants.php` name their keys as literals. Container definitions are composition and stay in `Plugin::get_definitions()`. +2. **Strategies.** Each way of unlocking is a component in `src/UnlockStrategies/`, gated on its switch, and an integration on its plugin too through a static `is_available()` that the settings read. It answers `dws_lpmwc/payment_method/should_lock` from the arguments alone, through an `@internal` static `unlocks()` that v1's per-strategy checks call, and returns `false` or the lock it was given. The safeguard passes the current user and, on an order's own payment page with its key, that order. 3. **Request context.** `is_admin()` is false on REST requests and true in admin-ajax ones, so an admin-only feature decides the request context in its callbacks, never in `should_load()`. 4. **Add-ons.** An add-on extends a base plugin only through the base plugin's documented hooks. It reads a template-built base plugin's slug, name and version only through that plugin's `{prefix}_get_plugin_*()` getters, from `plugins_loaded` on, since the add-on may load first, and behind `function_exists()`, since a base plugin below its floors or with a partial build may stay active without defining them. 5. **Request reads.** An integer or boolean is read as `filter_var( wp_unslash( $_POST['key'] ?? '' ), FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE ) ?? $default` (`FILTER_VALIDATE_BOOL` for a boolean, where an absent key reads false, as an unchecked checkbox posts nothing), and a string through an `is_string()` guard and the field's sanitizer; never `filter_input()`, the `default` filter option or a cast on request input. -6. **Options and data.** Options are read only through `includes/options.php`'s typed readers, safeguards excepted. Everything stored lives under `dws_lpmwc_`, or `_dws_lpmwc_` for hidden meta. The footprint covers the prefix's options, transients and user meta; object meta needs a `meta_prefixes` entry per type, and anything else its own entry. -7. **Safeguards** live in `includes/safeguards.php`, call only WordPress and that file, and relax only through their own filter, through `dws_lpmwc_is_relaxed()` (`docs/recipes.md`). The entry loads them before the floor check, so they parse on PHP 7.4 and call only WordPress APIs present at the oldest WordPress the bootstrap package runs on, or guard them, which `phpstan.safeguards.neon` checks. -8. **Migrations** are append-only: a step converges from any partial state, verifies its writes and throws when one fails. +6. **Options and data.** The settings are read only through `includes/options.php`'s typed readers, safeguards excepted, and each subject's grants only through `includes/grants.php`'s readers. Everything stored lives under `dws_lpmwc_`, or `_dws_lpmwc_` for hidden meta; a user's grants are a user option under the site's table prefix, read from that key alone. The plugin's settings fire no settings hooks of their own, since WordPress's `option_{key}` reaches every read; v1's value filters fire where v1 read through them. +7. **Grants forms** offer the locked payment methods and the subject's other grants, through `dws_lpmwc_get_grant_choices()`, since a `checkboxes` save keeps only the choices it offers. The profile, order and plan forms are the framework's `ObjectForm`s; the Groups field rides Groups' own forms, whose nonce and capability Groups checks before its save actions, and checks the plugin's capability in each callback. +8. **Order storage.** Order code goes through WC CRUD and WooCommerce's screen ids, never the posts table, and every Integration row whose outcome the order storage can change runs on all four storages through `OrderStorage`. +9. **v1 hooks.** A hook v1 fired keeps its v1 literal, fired through `apply_filters_deprecated()` or `do_action_deprecated()` with `'2.0.0'` and its replacement, before the v2 name; a filter on the locked list only adds ids. +10. **Safeguards** live in `includes/safeguards.php`, call only WordPress, WooCommerce and that file, and relax only through their own filter, through `dws_lpmwc_is_relaxed()` (the template's recipes). The entry loads them before the floor check, so they parse on PHP 7.4 and call only WordPress APIs present at the oldest WordPress the bootstrap package runs on, or guard them, which `phpstan.safeguards.neon`, the file's only PHPStan profile, checks with WooCommerce's stubs. +11. **Migrations** are append-only: a step converges from any partial state, verifies its writes and throws when one fails. ## Style - Every PHP file opens with `:`, never `Co-Authored-By`. ## Limitations -- The fill-in pushes with the workflow's own token, which GitHub refuses for workflow changes, so it never edits `.github/workflows/`; the workflows read the slug from `composer.json`'s `extra.text-domain`. - Integration and end to end share the tests site, so a row that changes shared data in a separate process flushes the parent's object cache. -- The release zip carries the TypeScript and SCSS sources beside the built files, since a plugin's repository may be private and the zip is then the only place its users find them. +- The Memberships rows run against a double of its one function and its three hooks, since Memberships is a paid plugin absent from WP Packages; they are unverified against a licensed copy. +- No environment runs a multisite network, so the migration that copies v1's user grants into every site's list runs only on a single site in the suites. +- The bundled GitHub updater ships inert, since the entry file has no `Update URI`, and wordpress.org's reviewers may ask to remove it. diff --git a/plugins/locked-payment-methods-for-woocommerce/README.md b/plugins/locked-payment-methods-for-woocommerce/README.md index 1574192..8dbe3ce 100644 --- a/plugins/locked-payment-methods-for-woocommerce/README.md +++ b/plugins/locked-payment-methods-for-woocommerce/README.md @@ -1,29 +1,29 @@ # Locked Payment Methods for WooCommerce -TODO: one sentence saying why this plugin exists, its philosophy rather than a list of what it holds. The same sentence is `composer.json`'s and `package.json`'s `description`, the entry file's `Description` header and the repository's GitHub description. +Hides the payment methods you lock from customers at checkout until you unlock them for a user, a role, an order, a group or a membership plan. + +A store locks a payment method it trusts only some customers with, such as cash on delivery or pay by invoice, and unlocks it per customer on their profile, for every customer with given roles, for one unpaid order, or for the members of a Groups group or a WooCommerce Memberships plan. The lock holds while the plugin cannot boot, so an outdated runtime or a broken deploy never shows a locked payment method to everyone. ## Requirements -The entry file's headers state the floors: `Requires PHP`, `Requires at least` for WordPress, and `Requires Plugins` with `WC requires at least` for WooCommerce. On a site below them, WordPress shows its own notice and none of the plugin's features load. +The entry file's headers state the floors: `Requires PHP`, `Requires at least` for WordPress, and `Requires Plugins` with `WC requires at least` for WooCommerce. On a site below them, WordPress shows its own notice and none of the plugin's features load, while the lock still holds. ## Installation -Download `locked-payment-methods-for-woocommerce.zip` from the repository's latest release, then upload it under Plugins → Add New Plugin → Upload Plugin. Later releases arrive through WordPress's own update check, from the repository the entry file's `Update URI` names. +Install the plugin from the WordPress plugin directory under Plugins → Add New Plugin, where its later releases arrive too, then open its section under WooCommerce → Settings → Advanced from the Settings link on its row of the Plugins screen. Each release also attaches `locked-payment-methods-for-woocommerce.zip` to the repository's GitHub release. -## Development +## Extending -Development needs PHP and Composer at the `Requires PHP` floor or later, Node.js and npm at `package.json`'s `engines`, Docker for `@wordpress/env`, and a POSIX shell for `npm run start`. +Other plugins lock more payment methods through `dws_lpmwc/payment_method/locked_ids`, unlock one for a user and an order by returning `false` from `dws_lpmwc/payment_method/should_lock`, and ask `dws_lpmwc_is_payment_method_locked()` whether one is locked. Every hook is named `dws_lpmwc/area/name`, and each one v1 fired under `locked_payment_methods_for_woocommerce/` still fires, deprecated, where v1 fired it; `CHANGELOG.md` lists the renames and the removals. -Install the dependencies, prefix the framework into `vendor-prefixed/`, build the scripts and start the development site on : +## Development -```sh -npm run setup -``` +Development needs PHP and Composer at the `Requires PHP` floor or later, Node.js and npm at `package.json`'s `engines`, and Docker for `@wordpress/env`. -Rebuild the blocks and assets on every change: +Install the dependencies, prefix the framework into `vendor-prefixed/` and start the development site, with WooCommerce and Groups, on : ```sh -npm run start +npm run setup ``` Run the manifest and changelog checks, PHPCS, PHPStan, and the Unit and Integration suites, starting the tests site on as needed: @@ -39,35 +39,10 @@ npx playwright install chromium npm run test:e2e ``` -Lint the scripts, styles and types: +Lint the end-to-end scripts and types: ```sh -npm run lint:scripts && npm run lint:styles && npm run lint:types +npm run lint:scripts && npm run lint:types ``` -`docs/ARCHITECTURE.md` describes how the plugin loads and boots, `docs/recipes.md` holds the step-by-step changes most plugins need, `tests/README.md` describes the test suites, and `AGENTS.md` holds the conventions for people and agents working on the code. - -## After the fill-in - -The fill-in renamed the plugin and kept the template's demo, which boots and passes every suite. Replace or delete each part as the plugin grows its own: - -| Part | Where it lives | Removing it | -| --- | --- | --- | -| The greeting | `src/Feature/`, the Greeting section of `src/Settings.php`, and the `DWS_LPMWC_OPTION_FEATURE`, `DWS_LPMWC_OPTION_GREETING` and `DWS_LPMWC_OPTION_GREETING_ORDERS` keys in `includes/options.php` | Delete `src/Feature/` and its row in `Plugin::COMPONENTS`, the Greeting section of `src/Settings.php`, `assets/src/greeting.scss` and its `greeting=` entry in `package.json`'s `build:assets` and `start:assets`, the three keys with their defaults, text and validator, their lines in `dws_lpmwc_add_settings()`, and `tests/Integration/Feature/`, then the rows naming them in `OptionsTest`, `MigrationsTest` and `SettingsTest`, and `PluginBootTest`'s expectation that the greeting is skipped. `tests/e2e/settings.spec.ts` drives the greeting's validated field, so point it at a validated field of the plugin's own. | -| The example block | `blocks/src/example-notice/`, registered by `src/Blocks.php` from `blocks/build/blocks-manifest.php` | Delete `blocks/`, `build:blocks` and `start:blocks` with their calls in `build` and `start`, `register_blocks()` and its `init` hook in `src/Blocks.php`, and `BlocksTest`'s block rows. | -| The editor script | `assets/src/editor.ts`, enqueued in the block editor by `src/Blocks.php` | Delete it, its `editor=` entry in `build:assets` and `start:assets`, `enqueue_editor_script()` and its hook in `src/Blocks.php`, and `BlocksTest`'s editor row. Once `assets/src/` holds nothing, delete `build:assets` and `start:assets` with their calls in `build` and `start`. Once the plugin registers no blocks either, delete `src/Blocks.php`, its row in `Plugin::COMPONENTS`, `BlocksTest`, and `Blocks` in `PluginBootTest`'s resolved components. | -| `models/` and `templates/` | Empty extension points, for classes on Composer's classmap and for template partials | Delete the folders the plugin never fills, and `models/` from `composer.json`'s `autoload.classmap`, since Composer fails to dump the autoloader for a missing folder. | -| The fill-in | `.github/workflows/fill-in-scaffold.yml`, `.github/scripts/fill-in-scaffold.sh` and Quality's `scaffold` job | Delete all three: GitHub refuses the fill-in's own token for workflow changes, so the fill-in cannot remove them, and the job skips itself outside the template. | - -Whichever parts go, four settings follow what is left, since a plugin removes them in any order: - -- The entry's build gate checks `blocks/build/blocks-manifest.php`, and the plugin stays off until that file exists, so point it at a file the remaining build writes, such as `assets/build/editor.asset.php`, or drop its built-file check once nothing is built. -- `lint:scripts` names only folders that still hold a script, since ESLint fails on a path that holds none. -- `lint:styles` names only folders that still hold SCSS. Once none does, it keeps one pattern, such as `'assets/src/**/*.scss'`, and takes `--allow-empty-input`, since Stylelint fails when nothing matches, Quality runs it, and without a pattern wp-scripts lints every stylesheet in the project. -- `build` stays, even once it runs nothing, since `npm run setup` and the Integration run call it. - -Still to decide by hand: - -- The Composer package name: the fill-in set `ahegyes/locked-payment-methods-for-woocommerce`, while plugins published to Composer use `ahegyes/wc-` for WooCommerce and `ahegyes/wp-` otherwise. -- WooCommerce's order tables: `dws_lpmwc_declare_compatibility()` in `functions.php` declares the plugin incompatible until its order code is verified on both storages. -- `SECURITY.md`'s scope and the issue forms in `.github/ISSUE_TEMPLATE/`, whose wording describes this plugin's demo. +`docs/ARCHITECTURE.md` describes how the plugin loads, boots and stores its data, `tests/README.md` describes the test suites, and `AGENTS.md` holds the conventions for people and agents working on the code. The plugin template's [recipes](https://github.com/ahegyes/wordpress-plugin-template/blob/trunk/docs/recipes.md) hold the step-by-step changes a plugin built from it makes, under the template's `dws_tpl` prefix. diff --git a/plugins/locked-payment-methods-for-woocommerce/SECURITY.md b/plugins/locked-payment-methods-for-woocommerce/SECURITY.md index bd45895..c0bb5bd 100644 --- a/plugins/locked-payment-methods-for-woocommerce/SECURITY.md +++ b/plugins/locked-payment-methods-for-woocommerce/SECURITY.md @@ -9,6 +9,5 @@ Report it privately through [GitHub's security advisory form](https://github.com In scope: the plugin's PHP, JavaScript and CSS, its prefixing configuration (`scoper.inc.php`) and its CI workflows. Out of scope, reported upstream instead: -- plugins generated from this repository, to their maintainers; - the wp-framework packages, to `ahegyes/wordpress-framework`; -- WordPress, WooCommerce and the other Composer and npm dependencies, to their projects. Composer refuses to resolve a dependency version with a known advisory through `roave/security-advisories`. The Supply-Chain Audit workflow fails on an advisory anywhere in the Composer lock, and on a high-severity one among the npm runtime dependencies, of which the template has none; advisories in the npm development tools reach Dependabot alerts instead. +- WordPress, WooCommerce and the other Composer and npm dependencies, to their projects. Composer refuses to resolve a dependency version with a known advisory through `roave/security-advisories`. The Supply-Chain Audit workflow fails on an advisory anywhere in the Composer lock, and on a high-severity one among the npm runtime dependencies, of which the plugin has none; advisories in the npm development tools reach Dependabot alerts instead. diff --git a/plugins/locked-payment-methods-for-woocommerce/composer.json b/plugins/locked-payment-methods-for-woocommerce/composer.json index 0de19c2..0ac8029 100644 --- a/plugins/locked-payment-methods-for-woocommerce/composer.json +++ b/plugins/locked-payment-methods-for-woocommerce/composer.json @@ -1,6 +1,6 @@ { "name": "ahegyes/wc-locked-payment-methods", - "description": "WordPress plugins on the wp-framework packages start here, from one tested anatomy, so all are built alike and a fix found in one applies to all.", + "description": "Hides the payment methods you lock from customers at checkout until you unlock them for a user, a role, an order, a group or a membership plan.", "type": "wordpress-plugin", "license": "GPL-3.0-or-later", "homepage": "https://github.com/ahegyes/wc-locked-payment-methods", diff --git a/plugins/locked-payment-methods-for-woocommerce/docs/ARCHITECTURE.md b/plugins/locked-payment-methods-for-woocommerce/docs/ARCHITECTURE.md index 63274de..23b8abf 100644 --- a/plugins/locked-payment-methods-for-woocommerce/docs/ARCHITECTURE.md +++ b/plugins/locked-payment-methods-for-woocommerce/docs/ARCHITECTURE.md @@ -6,12 +6,12 @@ This document describes how the plugin loads, boots, stores its data and leaves `locked-payment-methods-for-woocommerce.php` is the only file WordPress includes, and it parses on PHP 7.4 so that a site below the floors gets a notice instead of a fatal error: -1. It defines `DWS_LPMWC_FILE` and registers `languages/`, which WordPress skips for a network-activated plugin. -2. It loads `includes/safeguards.php`, whose restrictions hold on any runtime that parses the plugin: below the floors, on a partial deploy and when the boot fails. -3. It requires `vendor-prefixed/ahegyes/wp-framework-bootstrap/load.php`, registers the GitHub updater, then checks the `Requires PHP` and `Requires at least` headers. The updater comes first, so a release that lowers the floors still reaches a site below them. On a site below a floor, WordPress's own notice shows and nothing past the safeguards loads. -4. It stops with a notice when the prefixed framework or the built blocks are missing. It then requires the autoloader and `functions.php`; a missing file throws, which local and development environments rethrow and every other environment logs, showing the same notice. +1. It defines `DWS_LPMWC_FILE`; translations come from wordpress.org's language packs, which WordPress loads just in time. +2. It loads `includes/safeguards.php`, whose lock holds on any runtime that parses the plugin: below the floors, on a partial deploy and when the boot fails. +3. It requires `vendor-prefixed/ahegyes/wp-framework-bootstrap/load.php`, registers the GitHub updater, which stays inert because the entry file has no `Update URI` header, then checks the `Requires PHP` and `Requires at least` headers. On a site below a floor, WordPress's own notice shows and nothing past the safeguards loads. +4. It stops with a notice when the prefixed framework is missing. It then requires the autoloader and `functions.php`; a missing file throws, which local and development environments rethrow and every other environment logs, showing the same notice. -`functions.php` defines the `dws_lpmwc_plugin()` accessor, boots the plugin on `plugins_loaded` at priority 15, after WooCommerce and Action Scheduler have loaded, registers the deactivation callback, declares the plugin's WooCommerce feature compatibility, and requires every `includes/*.php` in name order except those whose names start with `_`. +`functions.php` defines the `dws_lpmwc_plugin()` accessor, boots the plugin on `plugins_loaded` at priority 15, after WooCommerce and Action Scheduler have loaded, registers the deactivation callback, declares the plugin compatible with WooCommerce's order tables, relays the framework's boot hooks to v1's `dws_lpmwc_initialized` and `dws_lpmwc_initialization_failure`, and requires every `includes/*.php` in name order except those whose names start with `_`. ## Booting @@ -20,26 +20,36 @@ This document describes how the plugin loads, boots, stores its data and leaves - The scheduler registers its hooks first, so the plugin's recurring jobs keep their schedules while the plugin is blocked. - `WooCommerceRequirement` checks WooCommerce against the `WC requires at least` header; without it, the boot is `blocked`, `dws_lpmwc/boot/blocked` fires, and users who can activate plugins see why the plugin stays off. - The migration runner registers, and runs pending steps on `wp_loaded`. -- The kernel builds each class in `Plugin::COMPONENTS` through a PHP-DI container and calls its `register_hooks()`; a composite adds its children, and a conditional component loads only when its static `should_load()` returns true. +- The kernel builds each class in `Plugin::COMPONENTS` through a PHP-DI container and calls its `register_hooks()`; a conditional component, such as each unlock strategy, loads only when its static `should_load()` returns true. +- `CapabilityGrants` gives administrators v1's four capabilities, and shop managers the user-profile and order ones, on `wp_loaded`, once per change of the declaration or the roles. - Once every component has registered, the boot is `booted` and `dws_lpmwc/boot/succeeded` fires. A throw anywhere in the composition makes it `failed`: `dws_lpmwc/boot/failed` fires, the failure is logged, and administrators see a notice, while local and development environments rethrow it. -The request that activates the plugin does not boot it, because WordPress includes the plugin's file after `plugins_loaded`; its post types, routes and migrations first run on the next request. +The request that activates the plugin does not boot it, because WordPress includes the plugin's file after `plugins_loaded`; its strategies and migrations first run on the next request. + +## The lock + +`includes/safeguards.php` filters `woocommerce_available_payment_gateways` at 999, which WooCommerce applies on the classic checkout, the order payment page and the Store API's checkout: + +1. It reads `dws_lpmwc_locked_payment_methods`, or v1's `dws-wc-lpm_general_locked-payment-methods` while the plugin has stored none, since the migration that copies v1's list runs only once the plugin boots, then lets v1's value and list filters and `dws_lpmwc/payment_method/locked_ids` add ids. +2. For each offered payment method on the list, it fires v1's two lock filters, then `dws_lpmwc/payment_method/should_lock` with the current user and, on an order's own payment page with that order's key, the order. +3. It removes the payment method unless `should_lock` returns an explicit `false` after `dws_lpmwc/boot/succeeded`, so the lock binds everyone while the plugin cannot boot. + +Each unlock strategy in `src/UnlockStrategies/` hooks `should_lock` while its switch is on, and returns `false` when its subject unlocks the payment method: the user's roles, the user's grants on the site, the order's grants, the user's Groups groups or the plans the user is an active member of. `dws_lpmwc_is_payment_method_locked()` runs the same filter for a user and an order outside checkout. ## Where code lives -- `src/` holds the composition root and its components: final classes that implement the framework's `ComponentInterface` and add their hooks in `register_hooks()`. A component takes ungated services in its constructor, such as the logger, the scheduler or `AdminNotices`, which `Plugin::get_definitions()` provides. -- `includes/` holds code that registers no hooks and is not part of the tree, as prefixed functions and constants: the option keys, defaults and typed readers (`options.php`), the footprint (`footprint.php`), the migration steps (`migrations.php`), the asset registrars (`assets.php`) and the slug, name and version getters a peer plugin reads (`metadata.php`). Quality's syntax job parses the folder on PHP 7.4, because `safeguards.php` runs before the floor check, and `phpstan.safeguards.neon` analyzes it at PHP 7.4 and the oldest WordPress the bootstrap package runs on. -- `blocks/src/` and `assets/src/` hold the TypeScript and SCSS sources, which `npm run build` compiles into `blocks/build/` and `assets/build/`; `includes/assets.php` reads the `*.asset.php` file wp-scripts writes beside each build, and `src/Blocks.php` registers the blocks from `blocks/build/blocks-manifest.php`. -- `models/` and `templates/` are empty extension points, for classes on Composer's classmap and for template partials. +- `src/` holds the composition root and its components: final classes that implement the framework's `ComponentInterface` and add their hooks in `register_hooks()`. `Settings` adds the `dws-locked-payment-methods` section to WooCommerce's Advanced tab; the strategies add their forms: `UserProfileStrategy` an `ObjectForm` on the profile and the Users screen's column and filter, `OrderStrategy` an `ObjectForm` in a box on the order screen on either storage, `GroupsStrategy` a field on Groups' own group forms, and `WooCommerceMembershipsStrategy` an `ObjectForm` in a tab of the plan data through `EditScreen\Hosts\MembershipPlanTab`. A component takes ungated services in its constructor, such as `AdminNotices`, which `Plugin::get_definitions()` provides. +- `includes/` holds code that registers no hooks and is not part of the tree, as prefixed functions and constants: the lock (`safeguards.php`), v1's capabilities (`capabilities.php`), the grants' keys and readers (`grants.php`), the settings' keys, defaults, readers and v1's settings map (`options.php`), v1's deprecated helper API (`deprecated.php`), the footprint (`footprint.php`), the migration steps (`migrations.php`) and the slug, name and version getters a peer plugin reads (`metadata.php`). Quality's syntax job parses the folder on PHP 7.4, because `safeguards.php` runs before the floor check, and `phpstan.safeguards.neon` analyzes that file at PHP 7.4 and the oldest WordPress the bootstrap package runs on. ## Data -- **Options** are prefixed constants in `includes/options.php`, read only through its typed readers, which guard each stored value and fall back to the key's default. Translated defaults come from `dws_lpmwc_get_option_text()` after `init`; a field shows one as its placeholder and stores an empty value, which its reader reads as the default, so no save stores the admin's language. +- **Settings** are flat options under `dws_lpmwc_`, as constants in `includes/options.php`: the locked list, a switch per strategy, the full-access roles and the uninstall consent. The first migration step copies each of v1's eight settings from its `dws-wc-lpm_{group}_{field}` row where it holds a value of the setting's kind, and leaves v1's rows until uninstall, so a downgrade to 1.3.5 still locks what it locked. +- **Grants** are one list of payment method ids per subject: the user option `dws_lpmwc_unlocked_payment_methods` under each site's table prefix, the order meta and the Memberships plan meta `_dws_lpmwc_unlocked_payment_methods`, and the `dws_lpmwc_groups_unlocked_payment_methods` map from Groups group id to list, from which deleting a group removes its entry. The second to fifth migration steps move v1's grants into these shapes and delete v1's rows: user meta into every site's list, so v1's grants keep their network reach, order meta through WC CRUD on either storage, the per-group options and the plan meta. - **Migrations** are the append-only steps `dws_lpmwc_get_migration_steps()` returns. The framework's runner runs each pending step once per site on `wp_loaded`, behind a lock, and records the revision reached in `dws_lpmwc_revision`. A step converges from any partial state and throws when a write fails, so a failed step runs again on a later request. -- **The footprint**, `dws_lpmwc_get_footprint()`, names every kind of data the plugin stores: option and user meta prefixes, network options, scheduled hooks and Action Scheduler groups. Options, transients and user meta under the `dws_lpmwc_` prefix are covered; meta on posts, terms, comments or orders needs a `meta_prefixes` entry per object type, and data under another name its own entry, which no check enforces. +- **The footprint**, `dws_lpmwc_get_footprint()`, names every kind of data the plugin stores: the `dws_lpmwc_` and `dws-wc-lpm_` option prefixes, v1's version and notice rows, the order meta under `_dws_lpmwc_` and `_dws_wc_lpm_` on both storages, the plan meta under both keys, the site user meta under `dws_lpmwc_`, v1's global user grants under `dws_wc_lpm_`, network options, the capabilities, scheduled hooks and Action Scheduler groups. Freemius's `fs_` rows from v1 stay, since they belong to Freemius. - **Deactivation** unschedules every hook in the footprint's `cron_hooks` and `as_hooks` on the current site. -- **Uninstall** runs `uninstall.php`, which calls the framework's uninstall engine over the footprint, with WooCommerce's order meta types, on every site whose `dws_lpmwc_delete_data` setting consents; a site that never consented keeps its data. Below the floors or with an incomplete build, a web request deletes the files, keeps the data and logs why, while WP-CLI fails and keeps both for a rerun. +- **Uninstall** runs `uninstall.php`, which calls the framework's uninstall engine over the footprint, with WooCommerce's order meta types, on every site whose `dws_lpmwc_delete_data` setting consents, which the first migration step copies from v1's; a site that never consented keeps its data. Below the floors or with an incomplete build, a web request deletes the files, keeps the data and logs why, while WP-CLI fails and keeps both for a rerun. ## Prefixing and distribution -Every development install runs php-scoper through wordpress-configs, which copies the framework, PHP-DI and their dependencies into `vendor-prefixed/` under `DeepWebSolutions\LockedPaymentMethods\Scoped\`, so each plugin carries its own copy and never meets another plugin's. The release workflow builds `locked-payment-methods-for-woocommerce.zip` from a production install, checks it with Plugin Check, the archive and header checks, activates it on a site and runs the end-to-end suite against it. A pull request rehearses the same build without publishing. A tag publishes a GitHub release, which the bundled updater offers through WordPress's own update check while the `Update URI` header points at the repository. +Every development install runs php-scoper through wordpress-configs, which copies the framework, PHP-DI and their dependencies into `vendor-prefixed/` under `DeepWebSolutions\LockedPaymentMethods\Scoped\`, so each plugin carries its own copy and never meets another plugin's. The release workflow builds `locked-payment-methods-for-woocommerce.zip` from a production install, checks it with Plugin Check, the archive and header checks, activates it on a site and runs the end-to-end suite against it. A pull request rehearses the same build without publishing. A tag publishes a GitHub release and deploys the zip to the wordpress.org directory, which offers it to sites; `composer changelog:write` turns `changelog/` into `CHANGELOG.md` and copies its versions into `readme.txt`. The listing's banners, icons and screenshots stay in the directory's SVN, since the plugin ships no `.wordpress-org/`. diff --git a/plugins/locked-payment-methods-for-woocommerce/docs/recipes.md b/plugins/locked-payment-methods-for-woocommerce/docs/recipes.md deleted file mode 100644 index b7fcfd7..0000000 --- a/plugins/locked-payment-methods-for-woocommerce/docs/recipes.md +++ /dev/null @@ -1,1293 +0,0 @@ -# Recipes - -These are the changes most plugins built from this template make, each as the files to add or change. A recipe that stores data lists its keys for `includes/footprint.php`, which no check enforces. Options, transients and user meta under the plugin's `dws_lpmwc_` prefix are in the footprint already; meta on posts, terms, comments or orders needs its own `meta_prefixes` entry per object type. - -- [A safeguard](#a-safeguard) -- [A one-time notice](#a-one-time-notice) -- [A WordPress settings page](#a-wordpress-settings-page) -- [A product data tab](#a-product-data-tab) -- [A form on an edit screen](#a-form-on-an-edit-screen) -- [A section on WooCommerce's Payments tab](#a-section-on-woocommerces-payments-tab) -- [Recurring jobs](#recurring-jobs) -- [Rewrite rules after activation](#rewrite-rules-after-activation) -- [A script module](#a-script-module) -- [Renaming a public hook](#renaming-a-public-hook) -- [Distribution through wordpress.org](#distribution-through-wordpressorg) -- [Without WooCommerce](#without-woocommerce) - -## A safeguard - -A safeguard is a restriction that holds even when the rest of the plugin fails to load, such as a capability that stays locked unless the plugin unlocks it. It lives in `includes/safeguards.php`, which the entry file loads before the floor gate and the autoloader, so it calls only WordPress and what that file defines. The plugin relaxes it only through the safeguard's own filter, with an explicit `false`, and only after `dws_lpmwc/boot/succeeded`, which fires once the plugin has booted, so a half-loaded build never relaxes it. - -Replace `includes/safeguards.php` with the helper and the safeguard. The file runs on any runtime that parses the plugin, so it must parse on PHP 7.4 and call only WordPress APIs present at the oldest WordPress the bootstrap package runs on, or guard them, which `phpstan.safeguards.neon` checks: - -```php -export(); // Checks current_user_can( 'dws_lpmwc_export_data' ). -} finally { - remove_filter( 'dws_lpmwc/data_export/should_restrict', '__return_false' ); -} -``` - -Add the safeguard's failure test as `tests/Integration/SafeguardsTest.php`. The plugin has booted when the Integration suite runs, so the last row takes `dws_lpmwc/boot/succeeded` back for its duration: - -```php - array( array(), false ), - 'null' => array( array( 'relaxation' => null ), false ), - 'zero' => array( array( 'relaxation' => 0 ), false ), - 'empty-string' => array( array( 'relaxation' => '' ), false ), - 'false' => array( array( 'relaxation' => false ), true ), - ); - } - - #[DataProvider( 'relaxations' )] - public function test_only_an_explicit_false_relaxes_the_export_lock( array $spec, bool $relaxed ): void { - $relaxation = static fn (): mixed => $spec['relaxation']; - if ( \array_key_exists( 'relaxation', $spec ) ) { - add_filter( self::FILTER, $relaxation ); - } - - try { - self::assertSame( $relaxed ? array( 'manage_options' ) : array( 'do_not_allow' ), map_meta_cap( 'dws_lpmwc_export_data', 0 ) ); - } finally { - remove_filter( self::FILTER, $relaxation ); - } - } - - public function test_an_explicit_false_does_not_relax_the_export_lock_before_the_plugin_booted(): void { - global $wp_actions; - self::assertGreaterThan( 0, did_action( 'dws_lpmwc/boot/succeeded' ) ); - $fired = $wp_actions['dws_lpmwc/boot/succeeded']; - unset( $wp_actions['dws_lpmwc/boot/succeeded'] ); - add_filter( self::FILTER, '__return_false' ); - - try { - self::assertSame( array( 'do_not_allow' ), map_meta_cap( 'dws_lpmwc_export_data', 0 ) ); - } finally { - remove_filter( self::FILTER, '__return_false' ); - $wp_actions['dws_lpmwc/boot/succeeded'] = $fired; - } - } -} -``` - -## A one-time notice - -A one-time notice shows once, on the user's next admin screen, such as after a form post redirects. The plugin's `AdminNotices` is in `Plugin::COMPONENTS` and in the container, so a component takes it in its constructor: - -```php -use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\AdminNotice\AdminNotices; -use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\AdminNotice\Notice; -use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\AdminNotice\NoticeType; - -// ... - - /** - * Constructor. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @param AdminNotices $notices The plugin's admin notices. - */ - public function __construct( - protected AdminNotices $notices - ) {} - - // ... - - /** - * Tells the current user, on their next admin screen, how many rows the export wrote. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @param int $rows The number of rows the export wrote. - * - * @return void - */ - protected function report_export( int $rows ): void { - $this->notices->add( - new Notice( - Plugin::PREFIX . '-export-' . time(), // A viewer's delivery outlives the notice, so each occasion takes a new id. - /* translators: %d: The number of rows. */ - esc_html( sprintf( _n( 'The export wrote %d row.', 'The export wrote %d rows.', $rows, 'locked-payment-methods-for-woocommerce' ), $rows ) ), - NoticeType::Success - ) - ); - } -``` - -The notice's scope defaults to the current user and its message is HTML, so escape it. A notice for every administrator of the site takes `NoticeScope::Site`, a capability and `per_viewer: true`, since without it the first administrator's viewing consumes it for everyone; one that stays until dismissed takes `persistent: true`. - -## A WordPress settings page - -An options page adds a screen under a WordPress admin menu and stores each tab's fields in one option through the Settings API. WooCommerce settings belong on a WooCommerce tab, as `src/Settings.php` does. - -Add the option, its field and its default, a reader that guards the field and the validator to `includes/options.php`, which stays PHP 7.4-parseable. The non-WooCommerce recipe below uses the same names, with the uninstall consent as a second field: - -```php -/** - * The option that holds the settings page's fields, keyed by field id. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @var string - */ -const DWS_LPMWC_OPTION_SETTINGS = Plugin::PREFIX . '_settings'; - -/** - * The settings field that holds the limit. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @var string - */ -const DWS_LPMWC_SETTING_LIMIT = 'limit'; - -/** - * The value each settings field reads as while nothing valid is stored. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @var array - */ -const DWS_LPMWC_SETTING_DEFAULTS = array( - DWS_LPMWC_SETTING_LIMIT => 1, -); - -/** - * Returns a settings field as an integer. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @param string $field The field's id. - * - * @return int - */ -function dws_lpmwc_get_int_setting( string $field ): int { - $default = DWS_LPMWC_SETTING_DEFAULTS[ $field ] ?? null; - $stored = get_option( DWS_LPMWC_OPTION_SETTINGS ); - $stored = is_array( $stored ) && array_key_exists( $field, $stored ) ? $stored[ $field ] : $default; - - return filter_var( $stored, FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE ) ?? ( is_int( $default ) ? $default : 0 ); -} - -/** - * Returns the limit as the settings page stores it, or an error when the submission is not a whole number of at least 1. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @param mixed $submitted The submitted value. - * - * @return int|WP_Error - */ -function dws_lpmwc_validate_limit( $submitted ) { - $limit = is_string( $submitted ) ? filter_var( $submitted, FILTER_VALIDATE_INT, array( 'options' => array( 'min_range' => 1 ) ) ) : false; - - return false === $limit ? new WP_Error( 'invalid_number', __( 'Enter a whole number of 1 or more, such as 3.', 'locked-payment-methods-for-woocommerce' ) ) : $limit; -} -``` - -Add the page as a component, `src/SettingsPage.php`, and list it in `Plugin::COMPONENTS`: - -```php -register_hooks(); - } - - /** - * {@inheritDoc} - * - * @since 2.0.0 - * @version 2.0.0 - */ - #[\Override] - public function get_options_page( string $page_id ): array { - return array( - 'parent' => 'options-general.php', - 'capability' => 'manage_options', - 'page_title' => __( 'Locked Payment Methods for WooCommerce', 'locked-payment-methods-for-woocommerce' ), - 'menu_title' => __( 'Locked Payment Methods for WooCommerce', 'locked-payment-methods-for-woocommerce' ), - 'tabs' => array( - array( - 'slug' => 'general', - 'title' => __( 'General', 'locked-payment-methods-for-woocommerce' ), - 'option' => DWS_LPMWC_OPTION_SETTINGS, - 'fields' => array( - array( - 'type' => 'number', - 'id' => DWS_LPMWC_SETTING_LIMIT, - 'title' => __( 'Limit', 'locked-payment-methods-for-woocommerce' ), - 'default' => (string) DWS_LPMWC_SETTING_DEFAULTS[ DWS_LPMWC_SETTING_LIMIT ], - 'custom_attributes' => array( - 'min' => '1', - 'step' => '1', - ), - 'validate' => 'dws_lpmwc_validate_limit', - ), - ), - ), - ), - ); - } - - // endregion -} -``` - -Read the limit with `dws_lpmwc_get_int_setting( DWS_LPMWC_SETTING_LIMIT )`, which returns the default until a valid limit is stored. The option is under the plugin's prefix, so the footprint covers it; a migration step that stores `DWS_LPMWC_SETTING_DEFAULTS` autoloaded saves the first request's query, as the non-WooCommerce recipe's step does. - -## A product data tab - -A product data tab adds a panel to the product editor's Product data box, whose fields WooCommerce renders and saves as the product's meta. Add it as a component, `src/ProductData.php`, and list it in `Plugin::COMPONENTS`: - -```php -register_hooks(); - } - - /** - * {@inheritDoc} - * - * @since 2.0.0 - * @version 2.0.0 - */ - #[\Override] - public function get_product_data_tab( string $tab_id, \WC_Product $product ): array { - return array( - 'label' => __( 'Packing', 'locked-payment-methods-for-woocommerce' ), - 'groups' => array( - array( - 'fields' => array( - array( - 'type' => 'text', - 'id' => '_' . Plugin::PREFIX . '_box', - 'label' => __( 'Box', 'locked-payment-methods-for-woocommerce' ), - ), - ), - ), - ), - ); - } - - // endregion -} -``` - -Read the stored value with `ProductMeta::get_value( $product, '_dws_lpmwc_box' )`. The leading underscore hides the key from WordPress's Custom Fields box. Post meta is not in the footprint until its type is, so declare the prefix for uninstall in `includes/footprint.php`: - -```php -'meta_prefixes' => array( 'post' => array( '_' . Plugin::PREFIX . '_' ) ), -``` - -A field with its own `render` callable returns its control as inline markup, escapes, names and describes it, and prints a hidden empty input before it unless the field is disabled. Without that input, a cleared control posts nothing, and the save leaves the stored value untouched. It also takes a `validate` callable that returns the value to store or a `WP_Error`, since the framework refuses to save a custom control's value that nothing validates. - -## A form on an edit screen - -An object form adds fields to an edit screen, such as a post's meta box, a user's profile or a term's form, and stores each field on the edited object. It reports a rejected value through the plugin's `AdminNotices`. Add it as a component, `src/PostNote.php`, and list it in `Plugin::COMPONENTS`: - -```php -notices )->register_hooks(); - } - - /** - * {@inheritDoc} - * - * @since 2.0.0 - * @version 2.0.0 - */ - #[\Override] - public function get_object_form( string $form_id, int $object_id ): array { - return array( - 'title' => __( 'Editorial note', 'locked-payment-methods-for-woocommerce' ), - 'capability' => 'edit_posts', - 'fields' => array( - array( - 'type' => 'textarea', - 'id' => '_' . Plugin::PREFIX . '_note', - 'title' => __( 'Note', 'locked-payment-methods-for-woocommerce' ), - ), - ), - ); - } - - // endregion -} -``` - -The host checks that the user can edit the post itself, and the form's `capability` is one more capability the user needs, checked without an object, so it names a primitive capability such as `edit_posts` rather than `edit_post`. The form verifies its own nonce before it asks for the definition on a save. Declare the meta for uninstall in `includes/footprint.php`, as the product data tab does. A field with its own `render` callable prints a hidden empty input before its control unless the field is disabled, and takes a `validate` callable, for the reasons that recipe gives. - -## A section on WooCommerce's Payments tab - -A section on an existing WooCommerce tab, here the Payments tab (`checkout`), holds settings that belong beside WooCommerce's own. WooCommerce renders the Payments tab as its own page and lists no extension sections there, so the section is reached through its direct link, which the plugin's row on the Plugins screen carries. Add it as a component, `src/PaymentSettings.php`, and list it in `Plugin::COMPONENTS`: - -```php - 'title', - 'id' => Plugin::PREFIX . '_payment_options', - 'title' => __( 'Locked Payment Methods for WooCommerce', 'locked-payment-methods-for-woocommerce' ), - ), - array( - 'type' => 'checkbox', - 'id' => Plugin::PREFIX . '_payment_notes', - 'title' => __( 'Payment notes', 'locked-payment-methods-for-woocommerce' ), - 'desc' => __( 'Add a note to each order when its payment completes', 'locked-payment-methods-for-woocommerce' ), - ), - array( - 'type' => 'sectionend', - 'id' => Plugin::PREFIX . '_payment_options', - ), - ); - } - - // endregion - - // region HOOKS - - /** - * Registers the plugin's section on the Payments tab. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @param SettingsSectionRegistry $registry WooCommerce's registry of settings sections. - * - * @return void - */ - public function register_section( SettingsSectionRegistry $registry ): void { - $registry->register( - new SettingsSection( - Plugin::PREFIX, - array( - 'tab' => 'checkout', - 'label' => __( 'Locked Payment Methods for WooCommerce', 'locked-payment-methods-for-woocommerce' ), - ), - $this - ) - ); - } - - /** - * Adds the link to the plugin's section to its row on the Plugins screen. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @param string[] $links The row's action links. - * - * @return string[] - */ - public function add_settings_link( array $links ): array { - array_unshift( $links, sprintf( '%s', esc_url( admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=' . Plugin::PREFIX ) ), esc_html__( 'Settings', 'locked-payment-methods-for-woocommerce' ) ) ); - - return $links; - } - - // endregion -} -``` - -WooCommerce stores each field in its own option under its id, flat rather than in a group, and a WooCommerce checkbox stores `yes` or `no`; read it with `dws_lpmwc_get_bool_option()` once the key and its default are in `includes/options.php`. - -## Recurring jobs - -A recurring job runs a hook at an interval through the plugin's scheduler, which uses Action Scheduler where it is loaded and WP-Cron otherwise. Either can run an invocation twice or skip one, so the job's callback leaves its data consistent after a duplicate or a missed run. - -Add the repair gate's option to `includes/options.php`: - -```php -/** - * The option that holds when the plugin last ensured its recurring jobs, which lets a request skip ensuring them for an hour. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @var string - */ -const DWS_LPMWC_OPTION_RECURRING_ENSURED_AT = Plugin::PREFIX . '_recurring_ensured_at'; -``` - -Add the job as a component, `src/Pruning.php`, and list it in `Plugin::COMPONENTS`. It ensures the schedule on `wp_loaded` at most once an hour, writing the time first so concurrent requests skip, and again when Action Scheduler asks every day: - -```php -ensure_schedule(); - } - - /** - * Schedules the daily prune unless it runs at that interval already, logging a scheduler that refuses. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @return void - */ - public function ensure_schedule(): void { - $ensured = $this->scheduler->ensure_recurring( new ScheduledHook( self::HOOK ), Recurrence::from_interval( DAY_IN_SECONDS ) ); - if ( true !== $ensured ) { - $this->logger->warning( - 'Could not schedule {hook}: {error}', - array( - 'hook' => self::HOOK, - 'error' => $ensured->get_error_message(), - 'exception' => $ensured->get_error_data(), - ) - ); - } - } - - /** - * Deletes the records that have expired. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @return void - */ - public function prune(): void { - // Delete by expiry, so a second run in a day finds nothing left and a missed day catches up. - } - - // endregion -} -``` - -Then: -- Add `Plugin::PREFIX . '/records/prune'` to `cron_hooks` in `includes/footprint.php`. Deactivation unschedules every hook there through the plugin's scheduler, and uninstall clears WP-Cron's copies, while Action Scheduler's are in the footprint's `as_groups` already. -- Delete the repair gate's option on activation, so the next request ensures the jobs: add `register_activation_hook( DWS_LPMWC_FILE, 'dws_lpmwc_activate' );` to `functions.php` beside the deactivation hook, and a `dws_lpmwc_activate()` that calls `delete_option( DWS_LPMWC_OPTION_RECURRING_ENSURED_AT )`. -- Change an interval in code only: `ensure_recurring()` replaces a schedule whose interval differs on the next pass of the repair gate. A "run now" schedules the same hook with an argument that sets it apart, or calls `prune()` directly. - -## Rewrite rules after activation - -WordPress builds its rewrite rules from the post types and taxonomies registered on `init`. The request that activates the plugin does not boot it, because WordPress includes the plugin's file after `plugins_loaded`, so flushing there would drop the plugin's rules. Delete the stored rules instead: the next request rebuilds them once the plugin has registered its types. - -In `functions.php`, beside the deactivation hook: - -```php -register_activation_hook( DWS_LPMWC_FILE, 'dws_lpmwc_activate' ); - -// ... - -/** - * Lets the next request rebuild the site's rewrite rules with the plugin's post types and taxonomies. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @return void - */ -function dws_lpmwc_activate(): void { - delete_option( 'rewrite_rules' ); -} -``` - -Add the same `delete_option( 'rewrite_rules' )` to `dws_lpmwc_deactivate()`, so the next request rebuilds the rules without them. Both act on the current site only, as WordPress's own activation does; a network activation leaves the other sites' rules until they next rebuild them. - -## A script module - -A script module is a JavaScript module WordPress loads through its import map, such as a block's view script for the Interactivity API. wp-scripts builds script modules only from a block's `block.json`, and WordPress registers each one from the block's metadata, with the imports wp-scripts recorded, and enqueues it when the block renders, so the plugin registers nothing itself. - -Name the module in the block's `block.json`, here `blocks/src/example-notice/block.json`: - -```json -"viewScriptModule": "file:./view.js" -``` - -Add the module beside it, as `blocks/src/example-notice/view.ts`: - -```ts -import { store } from '@wordpress/interactivity'; - -store( 'locked-payment-methods-for-woocommerce/example-notice', {} ); -``` - -Declare the module's import, which today arrives only through `@wordpress/block-editor`: - -```sh -npm install --save-dev @wordpress/interactivity -``` - -Build the blocks' modules with their scripts, by adding `--experimental-modules` to `build:blocks` and `start:blocks` in `package.json`: - -```json -"build:blocks": "wp-scripts build --webpack-src-dir=blocks/src --output-path=blocks/build --blocks-manifest --experimental-modules", -"start:blocks": "wp-scripts start --webpack-src-dir=blocks/src --output-path=blocks/build --blocks-manifest --experimental-modules" -``` - -The build writes `view.js` with a `view.asset.php` whose `type` is `module`, and `src/Blocks.php`'s registration from the manifest picks it up. - -## Renaming a public hook - -A public hook's name is part of the plugin's contract, as its arguments, its options and its REST routes are. To rename one in a released plugin: - -1. Rename the literal at every `do_action()` or `apply_filters()` and in its docblock, which keeps its `@since` and moves its `@version` to the release. -2. Add a changelog fragment with `composer changelog:add` that names the old and the new hook, with a major significance. -3. Migrate stored data that holds the name, such as a scheduled job's hook, in a migration step that converges from any partial state: unschedule the old name, then ensure the new one. - -Ship no deprecated alias: the major release is the migration path. - -## Distribution through wordpress.org - -A plugin listed on wordpress.org gets its updates and its language packs from there, so it drops the GitHub updater's header and adds the directory's `readme.txt`. - -1. In the entry file, delete the `Update URI` header, which turns the bundled updater inert, and the `load_plugin_textdomain()` line, since language packs load just in time. -2. Add `readme.txt`, whose `Stable tag` the release checks against the `Version` header, whose floors repeat the entry file's headers, and whose changelog block `composer changelog:write` fills: - -```text -=== Locked Payment Methods for WooCommerce === -Contributors: TODO -Tags: TODO -Tested up to: TODO, the newest WordPress branch the plugin is tested on -Stable tag: TODO, the Version header -Requires at least: TODO, the Requires at least header -Requires PHP: TODO, the Requires PHP header -License: GPLv3 or later -License URI: https://www.gnu.org/licenses/gpl-3.0.html - -TODO: the plugin's one sentence, at most 150 characters. - -== Description == - -TODO: what the plugin does, for the directory's listing. - -== Installation == - -1. Install the plugin from Plugins → Add New Plugin. -2. Activate it. - -== Changelog == - - - - - -[See the full changelog on GitHub.](https://github.com/ahegyes/wc-locked-payment-methods/blob/trunk/CHANGELOG.md) -``` - -3. Wrap `CHANGELOG.md`'s version blocks in the same `` and `` markers, require `pronamic/changelog-md-to-wordpress-plugin-readme-txt` in `require-dev`, and make `changelog:write` sync the file: - -```json -"changelog:write": [ - "@php vendor/bin/changelogger write", - "@php vendor/bin/pronamic-changelog-md-to-wordpress-plugin-readme-txt" -] -``` - -4. Lint the file in `.github/workflows/quality.yml`: - -```yaml - readme-lint: - name: readme.txt lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - uses: thetwopct/wporg-plugin-readme-linter@1f869aba959265ecc90935b2380c6569d56646ac # v1.0.2 - with: - path: readme.txt - fail-on: error -``` - -5. In `.github/workflows/release.yml`, pass `wp-org: true` to the reusable release. A tag then also deploys the zip and the `.wordpress-org/` assets to the directory's SVN from the `wp-org-release` environment: create it, store `SVN_USERNAME` and `SVN_PASSWORD` there and only there, and protect it with required reviewers. - -The wordpress.org build runs Plugin Check's full set but for the checks `exclude-checks` names; Plugin Check's `prefixing` and `direct_file_access` stop at PHP 8.4 syntax such as `protected(set)`, which wordpress.org's own scan may meet too. The bundled updater ships inert under `vendor-prefixed/`, which wordpress.org's reviewers may still ask to remove. - -## Without WooCommerce - -A plugin that does not need WooCommerce drops the WooCommerce package, the WooCommerce demo and its tooling, and keeps its settings on a WordPress options page. - -1. In `composer.json`, delete the `vcs` repository entry whose `url` is `https://github.com/ahegyes/wp-framework-woocommerce`. Then remove the WooCommerce packages, which leaves every other locked version as it is: - -```sh -composer remove --dev --ignore-platform-req=php+ --no-interaction ahegyes/wp-framework-woocommerce php-stubs/woocommerce-stubs wp-plugin/woocommerce -``` - -2. In `phpstan.dist.neon`, delete the `scanFiles` block that names the WooCommerce stubs: - -```neon - scanFiles: - - vendor/php-stubs/woocommerce-stubs/woocommerce-stubs.php - - vendor/php-stubs/woocommerce-stubs/woocommerce-packages-stubs.php -``` - -3. In `locked-payment-methods-for-woocommerce.php`, delete the `Requires Plugins`, `WC requires at least` and `WC tested up to` headers. - -4. In `functions.php`, delete the `FeaturesUtil` import, the `before_woocommerce_init` hook and `dws_lpmwc_declare_compatibility()`, and drop "declares its WooCommerce feature compatibility" from the file's summary: - -```php -use DeepWebSolutions\LockedPaymentMethods\Plugin; - -defined( 'ABSPATH' ) || exit; - -add_action( 'plugins_loaded', 'dws_lpmwc_boot', 15 ); -register_deactivation_hook( DWS_LPMWC_FILE, 'dws_lpmwc_deactivate' ); -``` - -5. In `src/Plugin.php`, log through core's `ErrorLogLogger`, which drops records below warning unless `WP_DEBUG` is on, so `DWS_LPMWC_DEBUG` goes. Compose without the WooCommerce check, and leave the greeting out of `COMPONENTS`: - -```php -use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\AdminNotice\AdminNotices; -use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\Logging\ErrorLogLogger; -use DeepWebSolutions\LockedPaymentMethods\Scoped\DeepWebSolutions\Framework\Core\Migration\MigrationRunner; -// ... - - public const array COMPONENTS = array( - Scheduler::class, - AdminNotices::class, - Settings::class, - Blocks::class, - ); - - /** - * The scheduler, which the components and the deactivation callback share. - // ... - */ - public readonly Scheduler $scheduler; - - // ... - - public function __construct() { - $this->logger = new ErrorLogLogger( self::SLUG ); - $this->scheduler = new Scheduler( self::PREFIX ); - $this->boot = new PluginBoot( DWS_LPMWC_FILE, self::PREFIX, $this->logger ); - } - - // ... - - /** - * Registers the scheduler and the migrations, then builds the kernel and boots the plugin's components. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @return bool - */ - protected function compose(): bool { - $this->scheduler->register_hooks(); - $this->migrations = new MigrationRunner( DWS_LPMWC_FILE, self::PREFIX . '_revision', dws_lpmwc_get_migration_steps(), $this->logger, $this->scheduler ); - $this->migrations->register_hooks(); - - $this->kernel = new PluginKernel( new ContainerBuilder()->addDefinitions( $this->get_definitions( $this->logger, $this->scheduler ) )->build() ); - $this->kernel->boot( self::COMPONENTS ); - - return true; - } -``` - -6. Delete the greeting: `src/Feature/`, `tests/Integration/Feature/` and `assets/src/greeting.scss`, then its entries in `package.json`: - -```json -"build:assets": "wp-scripts build editor=./assets/src/editor.ts --output-path=assets/build", -"start:assets": "wp-scripts start editor=./assets/src/editor.ts --output-path=assets/build" -``` - -7. Replace `src/Settings.php` with an options page under Settings, which stores both fields in one option through the Settings API: - -```php -register_hooks(); - } - - /** - * {@inheritDoc} - * - * @since 2.0.0 - * @version 2.0.0 - */ - #[\Override] - public function get_options_page( string $page_id ): array { - return array( - 'parent' => 'options-general.php', - 'capability' => 'manage_options', - 'page_title' => __( 'Locked Payment Methods for WooCommerce', 'locked-payment-methods-for-woocommerce' ), - 'menu_title' => __( 'Locked Payment Methods for WooCommerce', 'locked-payment-methods-for-woocommerce' ), - 'tabs' => array( - array( - 'slug' => 'general', - 'title' => __( 'General', 'locked-payment-methods-for-woocommerce' ), - 'option' => DWS_LPMWC_OPTION_SETTINGS, - 'fields' => array( - array( - 'type' => 'number', - 'id' => DWS_LPMWC_SETTING_LIMIT, - 'title' => __( 'Limit', 'locked-payment-methods-for-woocommerce' ), - 'default' => (string) DWS_LPMWC_SETTING_DEFAULTS[ DWS_LPMWC_SETTING_LIMIT ], - 'custom_attributes' => array( - 'min' => '1', - 'step' => '1', - ), - 'validate' => 'dws_lpmwc_validate_limit', - ), - array( - 'type' => 'checkbox', - 'id' => DWS_LPMWC_SETTING_DELETE_DATA, - 'title' => __( 'Data on uninstall', 'locked-payment-methods-for-woocommerce' ), - 'desc' => __( 'Delete the plugin\'s data when it is uninstalled', 'locked-payment-methods-for-woocommerce' ), - ), - ), - ), - ), - ); - } - - // endregion -} -``` - -8. Replace `includes/options.php` with the one settings option, its fields, their defaults, and readers that guard each field. The file stays PHP 7.4-parseable, because Quality's syntax job lints `includes/`: - -```php - - */ -const DWS_LPMWC_SETTING_DEFAULTS = array( - DWS_LPMWC_SETTING_DELETE_DATA => false, - DWS_LPMWC_SETTING_LIMIT => 1, -); - -/** - * Returns a settings field as a boolean, which reads checkbox values such as 'yes' and 'no'. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @param string $field The field's id. - * - * @return bool - */ -function dws_lpmwc_get_bool_setting( string $field ): bool { - $default = dws_lpmwc_get_setting_default( $field ); - - return filter_var( dws_lpmwc_get_setting( $field, $default ), FILTER_VALIDATE_BOOL, FILTER_NULL_ON_FAILURE ) ?? ( true === $default ); -} - -/** - * Returns a settings field as an integer. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @param string $field The field's id. - * - * @return int - */ -function dws_lpmwc_get_int_setting( string $field ): int { - $default = dws_lpmwc_get_setting_default( $field ); - - return filter_var( dws_lpmwc_get_setting( $field, $default ), FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE ) ?? ( is_int( $default ) ? $default : 0 ); -} - -/** - * Returns the limit as the settings page stores it, or an error when the submission is not a whole number of at least 1. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @param mixed $submitted The submitted value. - * - * @return int|WP_Error - */ -function dws_lpmwc_validate_limit( $submitted ) { - $limit = is_string( $submitted ) ? filter_var( $submitted, FILTER_VALIDATE_INT, array( 'options' => array( 'min_range' => 1 ) ) ) : false; - - return false === $limit ? new WP_Error( 'invalid_number', __( 'Enter a whole number of 1 or more, such as 3.', 'locked-payment-methods-for-woocommerce' ) ) : $limit; -} - -/** - * Returns a settings field's stored value, or the fallback while the settings hold none. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @param string $field The field's id. - * @param mixed $fallback The value to return while nothing is stored. - * - * @return mixed - */ -function dws_lpmwc_get_setting( string $field, $fallback ) { - $settings = get_option( DWS_LPMWC_OPTION_SETTINGS ); - - return is_array( $settings ) && array_key_exists( $field, $settings ) ? $settings[ $field ] : $fallback; -} - -/** - * Returns a settings field's default, or null for a field without one. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @param string $field The field's id. - * - * @return mixed - */ -function dws_lpmwc_get_setting_default( string $field ) { - return DWS_LPMWC_SETTING_DEFAULTS[ $field ] ?? null; -} -``` - -9. In `includes/migrations.php`, the first step stores the defaults as they are. A stored `false` renders as an unchecked checkbox, the readers guard it, and uninstall consents only on `'yes'`: - -```php -/** - * Stores the settings' defaults, autoloaded, so a site that never saved the settings page reads them without a query. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @throws RuntimeException Thrown when the defaults could not be stored. - * - * @return void - */ -function dws_lpmwc_add_settings(): void { - if ( false === get_option( DWS_LPMWC_OPTION_SETTINGS ) && ! add_option( DWS_LPMWC_OPTION_SETTINGS, DWS_LPMWC_SETTING_DEFAULTS, '', true ) ) { - throw new RuntimeException( 'The option ' . DWS_LPMWC_OPTION_SETTINGS . ' could not be stored.' ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing, never rendered. - } -} -``` - -10. In `uninstall.php`, delete the `MetaTypes` import, and take consent from the settings row's `delete_data` key: - -```php - $dws_lpmwc_footprint = dws_lpmwc_get_footprint(); - $dws_lpmwc_consent = array( - 'option' => DWS_LPMWC_OPTION_SETTINGS, - 'key' => DWS_LPMWC_SETTING_DELETE_DATA, - ); -// ... - -Uninstaller::run( - $dws_lpmwc_file, - $dws_lpmwc_footprint, - array( 'consent' => $dws_lpmwc_consent ) -); -``` - -11. In `.wp-env.json` and `.wp-env.tests.json`, delete the WooCommerce mapping, and let `afterStart` only activate the plugin; `.wp-env.json`'s command takes no `--config`: - -```json -"mappings": { - "wp-content/plugins/locked-payment-methods-for-woocommerce": "." -}, -"lifecycleScripts": { - "afterStart": "node_modules/.bin/wp-env --config .wp-env.tests.json run cli wp plugin activate locked-payment-methods-for-woocommerce" -} -``` - -12. Adapt the tests: -- Delete `tests/e2e/without-woocommerce.spec.ts`. -- `OptionsTest` reads through `dws_lpmwc_get_bool_setting()` and `dws_lpmwc_get_int_setting()` from a `DWS_LPMWC_OPTION_SETTINGS` row; a malformed limit reads its default 1; the validator rows call `dws_lpmwc_validate_limit()`, which returns integers. -- `MigrationsTest` expects `DWS_LPMWC_SETTING_DEFAULTS` stored autoloaded, a stored row kept as it is, and the throw when that option's `INSERT` fails. -- `UninstallTest` consents through `array( DWS_LPMWC_SETTING_DELETE_DATA => 'yes' )`, seeds and asserts no Action Scheduler action, and expects the defaults stored again afterwards. -- `PluginBootTest` expects nothing in `$plugin->kernel->skipped`. -- `SettingsTest` builds `new OptionsPage( Plugin::PREFIX, new Settings() )` and calls its `register_settings()` in `setUpBeforeClass()`, which first requires `wp-admin/includes/template.php`. `tearDownAfterClass()` removes the page's `pre_update_option_` and `option_page_capability_` filters and calls `unregister_setting()`. Each test resets `$GLOBALS['wp_settings_errors']`, renders through `render_page()` and saves as `options.php` does: - -```php - // options.php saves a submission through update_option(), whose filter the page hooks for its own option page only. - protected function save( array $posted ): void { - $GLOBALS['pagenow'] = 'options.php'; - $_REQUEST['option_page'] = DWS_LPMWC_OPTION_SETTINGS; - - try { - update_option( DWS_LPMWC_OPTION_SETTINGS, $posted ); - } finally { - unset( $GLOBALS['pagenow'], $_REQUEST['option_page'] ); - } - } -``` - -- `tests/e2e/settings.spec.ts` drives the page's limit field with the same checks: it focuses `page.getByLabel( 'Limit' )`, tabs to the `Save Changes` button and scopes axe to the form: - -```ts -const SAVED = 'Settings saved.'; -const REJECTED = - 'Limit was not saved, so it keeps its previous value. Enter a whole number of 1 or more, such as 3.'; -const FORM = 'form[action="options.php"]'; - -// ... - - test.beforeEach( async ( { admin } ) => { - await admin.visitAdminPage( 'options-general.php', 'page=dws_lpmwc' ); - } ); -``` - -13. Update `AGENTS.md`, `README.md` and `docs/ARCHITECTURE.md` wherever they name WooCommerce, the greeting, the settings tab or the order-tables declaration. diff --git a/plugins/locked-payment-methods-for-woocommerce/package.json b/plugins/locked-payment-methods-for-woocommerce/package.json index a309608..6a8b9c2 100644 --- a/plugins/locked-payment-methods-for-woocommerce/package.json +++ b/plugins/locked-payment-methods-for-woocommerce/package.json @@ -1,7 +1,7 @@ { "name": "@ahegyes/locked-payment-methods-for-woocommerce", "private": true, - "description": "DWS WordPress plugin template — wp-env, script and style tooling.", + "description": "Hides the payment methods you lock from customers at checkout until you unlock them for a user, a role, an order, a group or a membership plan.", "license": "GPL-3.0-or-later", "repository": { "type": "git", diff --git a/plugins/locked-payment-methods-for-woocommerce/phpcs.dist.xml b/plugins/locked-payment-methods-for-woocommerce/phpcs.dist.xml index 4416f8d..cc94b8b 100644 --- a/plugins/locked-payment-methods-for-woocommerce/phpcs.dist.xml +++ b/plugins/locked-payment-methods-for-woocommerce/phpcs.dist.xml @@ -3,8 +3,6 @@ - - diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/README.md b/plugins/locked-payment-methods-for-woocommerce/tests/README.md index f9e51a2..dc89259 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/README.md +++ b/plugins/locked-payment-methods-for-woocommerce/tests/README.md @@ -6,9 +6,9 @@ This document describes the plugin's three test suites, the environments they ru | Suite | Where | Runs on | Covers | | --- | --- | --- | --- | -| Unit | `tests/Unit/` | PHP alone, without WordPress | the component tree and the footprint's shape and coverage | -| Integration | `tests/Integration/` | the tests site, with WordPress, WooCommerce and order tables loaded | the boot, every component, the options and their readers, the migrations, deactivation, uninstall, the assets and blocks, the peer getters, and a partial deploy: a missing build file or engine, or an include that throws | -| End to end | `tests/e2e/` | the tests site, in a browser through Playwright | the settings tab by keyboard, a rejected and a corrected value, accessibility through axe, and the plugin with WooCommerce deactivated | +| Unit | `tests/Unit/` | PHP alone, without WordPress | the component tree, the footprint's shape and coverage of v1's data, and the readme's version and floors | +| Integration | `tests/Integration/` | the tests site, with WordPress, WooCommerce, Groups and order tables loaded | the boot, the lock and its relaxation, fresh requests below the PHP and WooCommerce floors, every strategy and its form, the settings, the capabilities, v1's deprecated functions and hooks, the migrations from v1, deactivation, uninstall, the peer getters, and a partial deploy: a missing build file or engine, or an include that throws; every row whose outcome the order storage can change runs on all four storages through `OrderStorage`, the Groups rows run in the `groups` group against Groups itself, and the Memberships rows in the `wc-memberships` group against a double of its one function | +| End to end | `tests/e2e/` | the tests site, in a browser through Playwright | a locked payment method a customer is granted on the block checkout, the Store API's refusal of a guest's checkout with a locked payment method, and the plugin with WooCommerce deactivated; the monorepo's shared environment checks a locked payment method's absence from a guest's classic and block checkout | PHPUnit runs both PHP suites in random order, so each row sets up what it needs and restores what it changes; its cache lives in `tests/.cache/phpunit/`, and Playwright's output in `tests/.cache/artifacts/`. @@ -17,7 +17,7 @@ PHPUnit runs both PHP suites in random order, so each row sets up what it needs - `.wp-env.json` is the development site on , as `local`, where the framework rethrows boot and migration failures. - `.wp-env.tests.json` is the tests site on , as `production`, which the Integration and end-to-end suites share. A row whose separate process changes the database flushes the parent's object cache, and the end-to-end specs set their own preconditions, because either suite may run first. -Both start WordPress on PHP 8.5 with WooCommerce from `vendor/` and its order tables on. +Both start WordPress on PHP 8.5 with WooCommerce and Groups from `vendor/` and WooCommerce's order tables on. ## Running @@ -27,13 +27,13 @@ Run the Unit suite: composer test:unit ``` -Start the tests site, build the scripts and run the Integration suite: +Start the tests site and run the Integration suite: ```sh composer test:integration ``` -Run the end-to-end suite, which starts the tests site when it is down and needs the built scripts and, once per machine, Playwright's Chromium: +Run the end-to-end suite, which starts the tests site when it is down and needs, once per machine, Playwright's Chromium: ```sh npx playwright install chromium @@ -51,7 +51,7 @@ composer quality-check - **Tests** runs Unit once, and Integration and the end-to-end suite once per WordPress branch, from the entry file's `Requires at least` up to the newest release, which WordPress's version API lists, so a new release joins without an edit. - **Tests (nightly)** runs Integration and the end-to-end suite on WordPress trunk every day, outside pull requests. - **Release** builds the zip on every pull request as a tag would, runs Plugin Check and the archive checks, then activates the zip on a site and runs the end-to-end suite against it. -- **Quality** runs PHPCS, PHPStan, the PHP 7.4 parse of the entry file, `uninstall.php` and `includes/`, the script, style and type lints, the `block.json` schema and the changelog fragments. +- **Quality** runs PHPCS, PHPStan, the PHP 7.4 parse of the entry file, `uninstall.php` and `includes/`, the script and type lints, the `readme.txt` lint and the changelog fragments. ## Conventions From c0767f92bdc24c273c9d2274acc95f0b4e906828 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 00:40:11 +0100 Subject: [PATCH 35/50] Add Locked Payment Methods to the root's shared environment The root's shared environment, its Integration run and its plugins:* fan-outs named Linked Orders and Internal Comments alone, so Locked Payment Methods would have joined the CI lanes but not the shared site where the plugins are tested side by side. The shared smoke check only checked that the plugins were active, and the README's table listed Linked Orders alone. The shared environment maps and activates Locked Payment Methods, and test:integration:run runs its Integration suite there without the groups and wc-memberships groups, whose plugins the site lacks; every Composer and npm fan-out lists it. The shared checks add that every plugin boots from its own prefixed framework, and that a guest sees a locked payment method on neither the classic nor the block checkout. AGENTS.md and the README name the plugin, and the README Internal Comments too. X ruling 99. Assisted-by: Claude Code:claude-opus-5-5 --- .wp-env.tests.json | 3 +- AGENTS.md | 9 ++- README.md | 4 +- composer.json | 27 ++++--- package.json | 10 +-- tests/e2e/shared-environment.spec.ts | 114 +++++++++++++++++++++++++-- 6 files changed, 140 insertions(+), 27 deletions(-) diff --git a/.wp-env.tests.json b/.wp-env.tests.json index c2bfbbb..2578648 100644 --- a/.wp-env.tests.json +++ b/.wp-env.tests.json @@ -7,6 +7,7 @@ "mappings": { "wp-content/plugins/internal-comments": "./plugins/internal-comments", "wp-content/plugins/linked-orders-for-woocommerce": "./plugins/linked-orders-for-woocommerce", + "wp-content/plugins/locked-payment-methods-for-woocommerce": "./plugins/locked-payment-methods-for-woocommerce", "wp-content/plugins/woocommerce": "./vendor/wp-plugin/woocommerce" }, "config": { @@ -15,6 +16,6 @@ "WP_ENVIRONMENT_TYPE": false }, "lifecycleScripts": { - "afterStart": "node_modules/.bin/wp-env --config .wp-env.tests.json run cli wp plugin activate woocommerce && node_modules/.bin/wp-env --config .wp-env.tests.json run cli wp wc hpos enable --ignore-plugin-compatibility && node_modules/.bin/wp-env --config .wp-env.tests.json run cli wp plugin activate linked-orders-for-woocommerce internal-comments" + "afterStart": "node_modules/.bin/wp-env --config .wp-env.tests.json run cli wp plugin activate woocommerce && node_modules/.bin/wp-env --config .wp-env.tests.json run cli wp wc hpos enable --ignore-plugin-compatibility && node_modules/.bin/wp-env --config .wp-env.tests.json run cli wp plugin activate linked-orders-for-woocommerce internal-comments locked-payment-methods-for-woocommerce" } } diff --git a/AGENTS.md b/AGENTS.md index 9fe9590..ddf223b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,9 +8,10 @@ The monorepo of the DWS v2 WordPress plugins, each built on the wp-framework pac wordpress-plugins/ ├── plugins/ │ ├── linked-orders-for-woocommerce/ Linked Orders for WooCommerce → wc-linked-orders, ports 8822 and 8823, on wordpress.org -│ └── internal-comments/ Internal Comments → wp-internal-comments, ports 8824 and 8825, on wordpress.org +│ ├── internal-comments/ Internal Comments → wp-internal-comments, ports 8824 and 8825, on wordpress.org +│ └── locked-payment-methods-for-woocommerce/ Locked Payment Methods for WooCommerce → wc-locked-payment-methods, ports 8826 and 8827, on wordpress.org ├── tests/ -│ ├── e2e/ the shared environment's smoke check, over the plugins .wp-env.tests.json maps +│ ├── e2e/ the shared environment's checks: every plugin .wp-env.tests.json maps is active and boots, and the plugins work side by side │ └── Fixtures/multi-plugin-smoke/ reserved for a fixture plugin the shared environment mounts ├── .github/workflows/ │ ├── plugins.yml lists the plugins, their mirrors and readmes and each one's WordPress versions for the other workflows @@ -28,7 +29,7 @@ wordpress-plugins/ npm run setup # every install, then every plugin's build composer plugins:quality-check # each plugin's quality-check, in its own environments composer test:integration # starts the shared environment and runs each plugin's Integration suite in it -npm run test:e2e # the shared environment's smoke check +npm run test:e2e # the shared environment's checks npm run plugins:test:e2e # each plugin's end-to-end suite, in its own environment actionlint .github/workflows/*.yml plugins/*/.github/workflows/*.yml && zizmor .github/ plugins/*/.github/ ``` @@ -49,4 +50,4 @@ Each plugin's `AGENTS.md` states its code style, the template's. Markdown keeps - CI runs twice per change: the root's lanes on the pull request, and the mirror's own after the split. - The root's workflow checks cover the root's workflows; a plugin's own are checked on its mirror after the split, and locally by the command above. -- Each plugin's Integration suite runs twice: in the plugin's own environments and in the shared one. +- Each plugin's Integration suite runs twice: in the plugin's own environments and in the shared one, which leaves out the rows of a plugin the shared environment does not install, such as Locked Payment Methods' Groups and WooCommerce Memberships groups. diff --git a/README.md b/README.md index 4d96be7..d96e595 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ Plugins built on the wp-framework packages are developed side by side here, so a | Plugin | Mirror | Distribution | | --- | --- | --- | | [Linked Orders for WooCommerce](./plugins/linked-orders-for-woocommerce) | [`wc-linked-orders`](https://github.com/ahegyes/wc-linked-orders) | wordpress.org | +| [Internal Comments](./plugins/internal-comments) | [`wp-internal-comments`](https://github.com/ahegyes/wp-internal-comments) | wordpress.org | +| [Locked Payment Methods for WooCommerce](./plugins/locked-payment-methods-for-woocommerce) | [`wc-locked-payment-methods`](https://github.com/ahegyes/wc-locked-payment-methods) | wordpress.org | Each plugin under `plugins//` is a complete plugin repository, started from [wordpress-plugin-template](https://github.com/ahegyes/wordpress-plugin-template): its own Composer and npm manifests, prefixed framework, test environments, CI and release workflows. Every push to `trunk` splits it to its mirror, which runs the plugin's own CI and releases it. @@ -29,7 +31,7 @@ composer test:integration npm run test:e2e ``` -`composer test:integration` runs each plugin's Integration suite in the shared environment, and `npm run test:e2e` its smoke check. +`composer test:integration` runs each plugin's Integration suite in the shared environment, and `npm run test:e2e` its checks of the plugins side by side. ## Releases diff --git a/composer.json b/composer.json index 2634a67..7799df3 100644 --- a/composer.json +++ b/composer.json @@ -59,7 +59,8 @@ "@putenv CI=1", "npm --prefix=. run plugins:build", "node_modules/.bin/wp-env --config .wp-env.tests.json run cli --env-cwd=wp-content/plugins/linked-orders-for-woocommerce vendor/bin/phpunit --testsuite=Integration", - "node_modules/.bin/wp-env --config .wp-env.tests.json run cli --env-cwd=wp-content/plugins/internal-comments vendor/bin/phpunit --testsuite=Integration" + "node_modules/.bin/wp-env --config .wp-env.tests.json run cli --env-cwd=wp-content/plugins/internal-comments vendor/bin/phpunit --testsuite=Integration", + "node_modules/.bin/wp-env --config .wp-env.tests.json run cli --env-cwd=wp-content/plugins/locked-payment-methods-for-woocommerce vendor/bin/phpunit --testsuite=Integration --exclude-group groups --exclude-group wc-memberships" ], "quality-check": [ @@ -70,38 +71,46 @@ "plugins:packages-install": [ "@composer --working-dir=plugins/linked-orders-for-woocommerce packages-install", - "@composer --working-dir=plugins/internal-comments packages-install" + "@composer --working-dir=plugins/internal-comments packages-install", + "@composer --working-dir=plugins/locked-payment-methods-for-woocommerce packages-install" ], "plugins:packages-update": [ "@composer --working-dir=plugins/linked-orders-for-woocommerce packages-update", - "@composer --working-dir=plugins/internal-comments packages-update" + "@composer --working-dir=plugins/internal-comments packages-update", + "@composer --working-dir=plugins/locked-payment-methods-for-woocommerce packages-update" ], "plugins:format:php": [ "@composer --working-dir=plugins/linked-orders-for-woocommerce format:php", - "@composer --working-dir=plugins/internal-comments format:php" + "@composer --working-dir=plugins/internal-comments format:php", + "@composer --working-dir=plugins/locked-payment-methods-for-woocommerce format:php" ], "plugins:lint:php": [ "@composer --working-dir=plugins/linked-orders-for-woocommerce lint:php", - "@composer --working-dir=plugins/internal-comments lint:php" + "@composer --working-dir=plugins/internal-comments lint:php", + "@composer --working-dir=plugins/locked-payment-methods-for-woocommerce lint:php" ], "plugins:test": [ "@composer --working-dir=plugins/linked-orders-for-woocommerce test", - "@composer --working-dir=plugins/internal-comments test" + "@composer --working-dir=plugins/internal-comments test", + "@composer --working-dir=plugins/locked-payment-methods-for-woocommerce test" ], "plugins:test:unit": [ "@composer --working-dir=plugins/linked-orders-for-woocommerce test:unit", - "@composer --working-dir=plugins/internal-comments test:unit" + "@composer --working-dir=plugins/internal-comments test:unit", + "@composer --working-dir=plugins/locked-payment-methods-for-woocommerce test:unit" ], "plugins:test:integration": [ "@composer --working-dir=plugins/linked-orders-for-woocommerce test:integration", - "@composer --working-dir=plugins/internal-comments test:integration" + "@composer --working-dir=plugins/internal-comments test:integration", + "@composer --working-dir=plugins/locked-payment-methods-for-woocommerce test:integration" ], "plugins:quality-check": [ "@composer --working-dir=plugins/linked-orders-for-woocommerce quality-check", - "@composer --working-dir=plugins/internal-comments quality-check" + "@composer --working-dir=plugins/internal-comments quality-check", + "@composer --working-dir=plugins/locked-payment-methods-for-woocommerce quality-check" ], "changelog:add": "@php vendor/bin/changelogger add", diff --git a/package.json b/package.json index ae6ea95..1f23ba0 100644 --- a/package.json +++ b/package.json @@ -28,11 +28,11 @@ "packages-update": "npm update", "packages-update:wp": "wp-scripts packages-update", - "plugins:packages-install": "npm --prefix=plugins/linked-orders-for-woocommerce run packages-install && npm --prefix=plugins/internal-comments run packages-install", - "plugins:packages-update": "npm --prefix=plugins/linked-orders-for-woocommerce run packages-update && npm --prefix=plugins/internal-comments run packages-update", - "plugins:packages-update:wp": "npm --prefix=plugins/linked-orders-for-woocommerce run packages-update:wp && npm --prefix=plugins/internal-comments run packages-update:wp", + "plugins:packages-install": "npm --prefix=plugins/linked-orders-for-woocommerce run packages-install && npm --prefix=plugins/internal-comments run packages-install && npm --prefix=plugins/locked-payment-methods-for-woocommerce run packages-install", + "plugins:packages-update": "npm --prefix=plugins/linked-orders-for-woocommerce run packages-update && npm --prefix=plugins/internal-comments run packages-update && npm --prefix=plugins/locked-payment-methods-for-woocommerce run packages-update", + "plugins:packages-update:wp": "npm --prefix=plugins/linked-orders-for-woocommerce run packages-update:wp && npm --prefix=plugins/internal-comments run packages-update:wp && npm --prefix=plugins/locked-payment-methods-for-woocommerce run packages-update:wp", - "plugins:build": "npm --prefix=plugins/linked-orders-for-woocommerce run build && npm --prefix=plugins/internal-comments run build", + "plugins:build": "npm --prefix=plugins/linked-orders-for-woocommerce run build && npm --prefix=plugins/internal-comments run build && npm --prefix=plugins/locked-payment-methods-for-woocommerce run build", "wp-env:tests:start": "wp-env --config .wp-env.tests.json start", "wp-env:tests:stop": "wp-env --config .wp-env.tests.json stop", @@ -45,7 +45,7 @@ "check:licenses": "wp-scripts check-licenses", "test:e2e": "playwright test --config=playwright.config.js", - "plugins:test:e2e": "npm --prefix=plugins/linked-orders-for-woocommerce run test:e2e && npm --prefix=plugins/internal-comments run test:e2e", + "plugins:test:e2e": "npm --prefix=plugins/linked-orders-for-woocommerce run test:e2e && npm --prefix=plugins/internal-comments run test:e2e && npm --prefix=plugins/locked-payment-methods-for-woocommerce run test:e2e", "test:all": "composer test:integration && npm run test:e2e", "quality-check:all": "composer quality-check && npm run test:e2e" diff --git a/tests/e2e/shared-environment.spec.ts b/tests/e2e/shared-environment.spec.ts index 0613cdf..db77c82 100644 --- a/tests/e2e/shared-environment.spec.ts +++ b/tests/e2e/shared-environment.spec.ts @@ -32,7 +32,7 @@ test( 'every mapped plugin is active beside the others and the dashboard loads', ).toBeVisible(); } ); -// No page shows a boot's outcome or the Internal Comments settings, so the site's WP-CLI reads and stores them. +// No page shows a boot's outcome or the plugins' settings, so the site's WP-CLI reads and stores them. const cli = ( ...args: string[] ): string => execFileSync( 'node_modules/.bin/wp-env', @@ -43,17 +43,19 @@ const cli = ( ...args: string[] ): string => .split( '\n' ) .pop() ?? ''; -test( 'Linked Orders and Internal Comments each boot and share an order screen', async ( { - page, - requestUtils, -} ) => { +test( 'every plugin boots from its own scoped framework', () => { expect( cli( 'eval', - 'echo wp_json_encode( array( did_action( "dws_lowc/boot/succeeded" ), did_action( "dws_ic/boot/succeeded" ) ) );' + 'echo wp_json_encode( array( did_action( "dws_lowc/boot/succeeded" ), did_action( "dws_ic/boot/succeeded" ), did_action( "dws_lpmwc/boot/succeeded" ), class_exists( "DeepWebSolutions\\LinkedOrders\\Scoped\\DeepWebSolutions\\Framework\\Core\\PluginKernel", false ), class_exists( "DeepWebSolutions\\InternalComments\\Scoped\\DeepWebSolutions\\Framework\\Core\\PluginKernel", false ), class_exists( "DeepWebSolutions\\LockedPaymentMethods\\Scoped\\DeepWebSolutions\\Framework\\Core\\PluginKernel", false ) ) );' ) - ).toBe( '[1,1]' ); + ).toBe( '[1,1,1,true,true,true]' ); +} ); +test( 'Linked Orders and Internal Comments share an order screen', async ( { + page, + requestUtils, +} ) => { cli( 'option', 'update', @@ -88,3 +90,101 @@ test( 'Linked Orders and Internal Comments each boot and share an order screen', ); } } ); + +test( 'a locked payment method stays hidden from a guest on the classic and the block checkout', async ( { + browser, + baseURL, + requestUtils, +} ) => { + // A new store shows its pages to visitors as coming soon, so the row opens it while it runs. + const comingSoon = cli( + 'eval', + 'echo get_option( "woocommerce_coming_soon", "no" );' + ); + cli( 'option', 'update', 'woocommerce_coming_soon', 'no' ); + for ( const gateway of [ 'cod', 'bacs' ] ) { + cli( + 'wc', + 'payment_gateway', + 'update', + gateway, + '--enabled=true', + '--user=1' + ); + } + cli( + 'option', + 'update', + 'dws_lpmwc_locked_payment_methods', + JSON.stringify( [ 'cod' ] ), + '--format=json' + ); + const product = await requestUtils.rest< { id: number } >( { + method: 'POST', + path: '/wc/v3/products', + data: { + name: 'Locked checkout', + type: 'simple', + regular_price: '10', + virtual: true, + }, + } ); + const classic = await requestUtils.rest< { id: number; link: string } >( { + method: 'POST', + path: '/wp/v2/pages', + data: { + title: 'Classic checkout', + status: 'publish', + content: + '[woocommerce_checkout]', + }, + } ); + // A context takes the config's storage state, the administrator's session, unless it names its own. + const guest = await browser.newContext( { + baseURL, + storageState: { cookies: [], origins: [] }, + } ); + + try { + const page = await guest.newPage(); + await page.goto( `/?add-to-cart=${ product.id }` ); + + await page.goto( classic.link ); + await expect( page.locator( '#payment_method_bacs' ) ).toBeAttached(); + await expect( page.locator( '#payment_method_cod' ) ).toHaveCount( 0 ); + + await page.goto( '/checkout/' ); + await expect( page.getByLabel( 'Direct bank transfer' ) ).toBeVisible(); + await expect( page.getByLabel( 'Cash on delivery' ) ).toHaveCount( 0 ); + } finally { + await guest.close(); + await requestUtils.rest( { + method: 'DELETE', + path: `/wp/v2/pages/${ classic.id }`, + params: { force: true }, + } ); + await requestUtils.rest( { + method: 'DELETE', + path: `/wc/v3/products/${ product.id }`, + params: { force: true }, + } ); + cli( + 'option', + 'update', + 'dws_lpmwc_locked_payment_methods', + '[]', + '--format=json' + ); + for ( const gateway of [ 'cod', 'bacs' ] ) { + cli( + 'wc', + 'payment_gateway', + 'update', + gateway, + '--enabled=false', + '--user=1' + ); + } + cli( 'option', 'update', 'woocommerce_coming_soon', comingSoon ); + } +} ); From d90f4ee74f6a5cafb9fea017169b9a88bda3a4e6 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 00:50:43 +0100 Subject: [PATCH 36/50] Keep a payment method locked unless a callback returns false Only an explicit false from dws_lpmwc/payment_method/should_lock is meant to unlock a payment method, but the strategies typed the lock as bool. WordPress calls them in weak mode, so a 0 or an empty string an earlier callback returned became false and unlocked the method for everyone, guests included, and a null threw a TypeError that broke the checkout. The lock itself took the gateways as an array, so another plugin's gateways filter returning anything else broke it too. Each strategy takes the lock untyped and returns it unchanged unless its subject unlocks the method, and the lock passes on a gateways value that is not an array, which WooCommerce casts to an empty list. Rows hook a null, a 0 and an empty string before the strategies, and a null in place of the gateways. Assisted-by: Claude Code:claude-opus-5-5 --- .../includes/safeguards.php | 18 +++++++---- .../src/UnlockStrategies/GroupsStrategy.php | 6 ++-- .../src/UnlockStrategies/OrderStrategy.php | 6 ++-- .../UnlockStrategies/UserProfileStrategy.php | 6 ++-- .../src/UnlockStrategies/UserRoleStrategy.php | 6 ++-- .../WooCommerceMembershipsStrategy.php | 6 ++-- .../tests/Integration/SafeguardsTest.php | 32 ++++++++++++++++++- 7 files changed, 57 insertions(+), 23 deletions(-) diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php b/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php index a6ad992..81aee81 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php @@ -140,11 +140,15 @@ function dws_lpmwc_get_paid_order_id(): int { * @since 2.0.0 * @version 2.0.0 * - * @param array $gateways The payment gateways offered, by id. + * @param mixed $gateways The payment gateways offered, by id, or what an earlier callback returned in their place, which WooCommerce casts to a list. * - * @return array + * @return mixed */ -function dws_lpmwc_remove_locked_payment_methods( array $gateways ): array { +function dws_lpmwc_remove_locked_payment_methods( $gateways ) { + if ( ! is_array( $gateways ) ) { + return $gateways; + } + $ids = dws_lpmwc_get_locked_payment_method_ids(); /** @@ -154,8 +158,8 @@ function dws_lpmwc_remove_locked_payment_methods( array $gateways ): array { * @version 2.0.0 * @deprecated 2.0.0 Use dws_lpmwc/payment_method/locked_ids. * - * @param string[] $ids The locked payment methods' ids. - * @param array $gateways The payment gateways offered, by id. + * @param string[] $ids The locked payment methods' ids. + * @param array $gateways The payment gateways offered, by id. */ $ids = dws_lpmwc_add_locked_ids( $ids, apply_filters_deprecated( 'locked_payment_methods_for_woocommerce/lock_manager/locked_payment_methods', array( $ids, $gateways ), '2.0.0', 'dws_lpmwc/payment_method/locked_ids' ) ); @@ -165,8 +169,8 @@ function dws_lpmwc_remove_locked_payment_methods( array $gateways ): array { * @since 2.0.0 * @version 2.0.0 * - * @param string[] $ids The locked payment methods' ids. - * @param array $gateways The payment gateways offered, by id. + * @param string[] $ids The locked payment methods' ids. + * @param array $gateways The payment gateways offered, by id. */ $ids = dws_lpmwc_add_locked_ids( $ids, apply_filters( 'dws_lpmwc/payment_method/locked_ids', $ids, $gateways ) ); // @phpstan-ignore WPCompat.parameterNotAvailable.applyfilters.args (WordPress 6.0 only formalized the arguments that older versions already passed on.) diff --git a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/GroupsStrategy.php b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/GroupsStrategy.php index a29b200..69b5ee5 100644 --- a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/GroupsStrategy.php +++ b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/GroupsStrategy.php @@ -147,13 +147,13 @@ public static function unlocks( string $gateway_id, int $user_id ): bool { * @since 2.0.0 * @version 2.0.0 * - * @param bool $lock Whether the payment method stays locked. + * @param mixed $lock Whether the payment method stays locked, as earlier callbacks left it, passed on unchanged since only an explicit false unlocks. * @param string $gateway_id The payment method's id. * @param int $user_id The paying user, or 0 for a guest. * - * @return bool + * @return mixed */ - public function unlock_group_methods( bool $lock, string $gateway_id, int $user_id ): bool { + public function unlock_group_methods( mixed $lock, string $gateway_id, int $user_id ): mixed { return self::unlocks( $gateway_id, $user_id ) ? false : $lock; } diff --git a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/OrderStrategy.php b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/OrderStrategy.php index f4ccf2c..2dce681 100644 --- a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/OrderStrategy.php +++ b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/OrderStrategy.php @@ -133,14 +133,14 @@ public static function unlocks( string $gateway_id, int $order_id ): bool { * @since 2.0.0 * @version 2.0.0 * - * @param bool $lock Whether the payment method stays locked. + * @param mixed $lock Whether the payment method stays locked, as earlier callbacks left it, passed on unchanged since only an explicit false unlocks. * @param string $gateway_id The payment method's id. * @param int $user_id The paying user, or 0 for a guest. * @param int $order_id The order being paid, or 0 off the order-pay page. * - * @return bool + * @return mixed */ - public function unlock_granted_methods( bool $lock, string $gateway_id, int $user_id, int $order_id ): bool { + public function unlock_granted_methods( mixed $lock, string $gateway_id, int $user_id, int $order_id ): mixed { return self::unlocks( $gateway_id, $order_id ) ? false : $lock; } diff --git a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserProfileStrategy.php b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserProfileStrategy.php index b288fb6..cf590e4 100644 --- a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserProfileStrategy.php +++ b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserProfileStrategy.php @@ -144,13 +144,13 @@ public static function unlocks( string $gateway_id, int $user_id ): bool { * @since 2.0.0 * @version 2.0.0 * - * @param bool $lock Whether the payment method stays locked. + * @param mixed $lock Whether the payment method stays locked, as earlier callbacks left it, passed on unchanged since only an explicit false unlocks. * @param string $gateway_id The payment method's id. * @param int $user_id The paying user, or 0 for a guest. * - * @return bool + * @return mixed */ - public function unlock_granted_methods( bool $lock, string $gateway_id, int $user_id ): bool { + public function unlock_granted_methods( mixed $lock, string $gateway_id, int $user_id ): mixed { return self::unlocks( $gateway_id, $user_id ) ? false : $lock; } diff --git a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserRoleStrategy.php b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserRoleStrategy.php index b078b29..ae71926 100644 --- a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserRoleStrategy.php +++ b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserRoleStrategy.php @@ -76,13 +76,13 @@ public static function unlocks( string $gateway_id, int $user_id ): bool { * @since 2.0.0 * @version 2.0.0 * - * @param bool $lock Whether the payment method stays locked. + * @param mixed $lock Whether the payment method stays locked, as earlier callbacks left it, passed on unchanged since only an explicit false unlocks. * @param string $gateway_id The payment method's id. * @param int $user_id The paying user, or 0 for a guest. * - * @return bool + * @return mixed */ - public function unlock_for_full_access_roles( bool $lock, string $gateway_id, int $user_id ): bool { + public function unlock_for_full_access_roles( mixed $lock, string $gateway_id, int $user_id ): mixed { return self::unlocks( $gateway_id, $user_id ) ? false : $lock; } diff --git a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/WooCommerceMembershipsStrategy.php b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/WooCommerceMembershipsStrategy.php index 51076c9..72cdf5d 100644 --- a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/WooCommerceMembershipsStrategy.php +++ b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/WooCommerceMembershipsStrategy.php @@ -156,13 +156,13 @@ public static function unlocks( string $gateway_id, int $user_id ): bool { * @since 2.0.0 * @version 2.0.0 * - * @param bool $lock Whether the payment method stays locked. + * @param mixed $lock Whether the payment method stays locked, as earlier callbacks left it, passed on unchanged since only an explicit false unlocks. * @param string $gateway_id The payment method's id. * @param int $user_id The paying user, or 0 for a guest. * - * @return bool + * @return mixed */ - public function unlock_member_methods( bool $lock, string $gateway_id, int $user_id ): bool { + public function unlock_member_methods( mixed $lock, string $gateway_id, int $user_id ): mixed { return self::unlocks( $gateway_id, $user_id ) ? false : $lock; } diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SafeguardsTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SafeguardsTest.php index a833b41..e9fd4a2 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SafeguardsTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SafeguardsTest.php @@ -31,6 +31,14 @@ public static function relaxations(): array { ); } + public static function early_relaxations(): array { + return array( + 'null' => array( null ), + 'zero' => array( 0 ), + 'empty-string' => array( '' ), + ); + } + public static function order_keys(): array { return array( 'missing' => array( null ), @@ -94,6 +102,28 @@ public function test_only_an_explicit_false_from_should_lock_unlocks_a_locked_me } } + #[DataProvider( 'early_relaxations' )] + public function test_a_callback_before_the_strategies_unlocks_nothing_without_an_explicit_false( mixed $relaxation ): void { + $early = static fn (): mixed => $relaxation; + add_filter( self::FILTER, $early, 5 ); + + try { + self::assertArrayNotHasKey( 'cod', self::available() ); + } finally { + remove_filter( self::FILTER, $early, 5 ); + } + } + + public function test_the_lock_passes_on_a_gateways_value_that_is_not_a_list(): void { + add_filter( 'woocommerce_available_payment_gateways', '__return_null' ); + + try { + self::assertNull( self::available() ); + } finally { + remove_filter( 'woocommerce_available_payment_gateways', '__return_null' ); + } + } + public function test_an_explicit_false_does_not_unlock_before_the_plugin_booted(): void { global $wp_actions; self::assertGreaterThan( 0, did_action( 'dws_lpmwc/boot/succeeded' ) ); @@ -282,7 +312,7 @@ public function test_the_lock_holds_and_nothing_relaxes_it_while_the_plugin_cann self::assertSame( $expected, $request['last'] ); } - protected static function available( array $gateways = array() ): array { + protected static function available( array $gateways = array() ): ?array { return apply_filters( 'woocommerce_available_payment_gateways', $gateways + WC()->payment_gateways()->payment_gateways() ); } From eb8171a1f8d83beef6e7b89134b06cbe37b735f5 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 00:51:37 +0100 Subject: [PATCH 37/50] Say which list the lock check and the grants forms read dws_lpmwc_is_payment_method_locked() and the grants forms read the settings' locked list, while checkout also locks the ids the list filters add, and their docblocks called it "the locked list", which reads as the list checkout uses. The docblocks and the architecture name the settings' list and say why the list filters stay out: they judge the payment methods a checkout offers, as v1's did, so a callback written for a cart never runs on a profile or the Users screen. Assisted-by: Claude Code:claude-opus-5-5 --- .../docs/ARCHITECTURE.md | 2 +- .../includes/grants.php | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/locked-payment-methods-for-woocommerce/docs/ARCHITECTURE.md b/plugins/locked-payment-methods-for-woocommerce/docs/ARCHITECTURE.md index 23b8abf..921f98f 100644 --- a/plugins/locked-payment-methods-for-woocommerce/docs/ARCHITECTURE.md +++ b/plugins/locked-payment-methods-for-woocommerce/docs/ARCHITECTURE.md @@ -34,7 +34,7 @@ The request that activates the plugin does not boot it, because WordPress includ 2. For each offered payment method on the list, it fires v1's two lock filters, then `dws_lpmwc/payment_method/should_lock` with the current user and, on an order's own payment page with that order's key, the order. 3. It removes the payment method unless `should_lock` returns an explicit `false` after `dws_lpmwc/boot/succeeded`, so the lock binds everyone while the plugin cannot boot. -Each unlock strategy in `src/UnlockStrategies/` hooks `should_lock` while its switch is on, and returns `false` when its subject unlocks the payment method: the user's roles, the user's grants on the site, the order's grants, the user's Groups groups or the plans the user is an active member of. `dws_lpmwc_is_payment_method_locked()` runs the same filter for a user and an order outside checkout. +Each unlock strategy in `src/UnlockStrategies/` hooks `should_lock` while its switch is on, and returns `false` when its subject unlocks the payment method: the user's roles, the user's grants on the site, the order's grants, the user's Groups groups or the plans the user is an active member of. `dws_lpmwc_is_payment_method_locked()` runs the same filter for a user and an order outside checkout, over the settings' list alone, because the list filters judge the payment methods a checkout offers; the grants forms offer the same list. ## Where code lives diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/grants.php b/plugins/locked-payment-methods-for-woocommerce/includes/grants.php index 97611b9..cfadfc7 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/grants.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/grants.php @@ -96,7 +96,7 @@ function dws_lpmwc_get_payment_method_title( string $gateway_id ): string { } /** - * Returns the choices of a grants field by payment method id: the locked methods and the subject's other grants, since a save keeps only the choices a field offers, each marked when it is not locked or when another rule already unlocks it for the user. + * Returns the choices of a grants field by payment method id: the methods the settings lock and the subject's other grants, since a save keeps only the choices a field offers, each marked when it is not locked or when another rule already unlocks it for the user. * * @since 2.0.0 * @version 2.0.0 @@ -171,7 +171,9 @@ function dws_lpmwc_get_plan_unlocked_payment_method_ids( int $plan_id ): array { } /** - * Returns whether a payment method is locked for a user and an order: whether it is on the locked list and the plugin's strategies do not unlock it. + * Returns whether a payment method is locked for a user and an order: whether the settings lock it and dws_lpmwc/payment_method/should_lock does not relax it. + * + * The ids the list filters add are left out, because those filters judge the payment methods a checkout offers. * * @since 2.0.0 * @version 2.0.0 From 7d174cd535809d6ca93e4cf58812a031113ea7cb Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 00:52:53 +0100 Subject: [PATCH 38/50] Filter only the Users list by a granted payment method The Users screen's filter narrowed every user query run on users.php through pre_get_users, as v1's did, so another plugin's user query on that screen, such as a dropdown or a count, saw only the users granted the filtered payment method. The filter adds its meta query on users_list_table_query_args, which only the list tables apply to their own query, and a row checks that a query of its own on the Users screen keeps every user. Assisted-by: Claude Code:claude-opus-5-5 --- .../UnlockStrategies/UserProfileStrategy.php | 28 +++++++++--------- .../UserProfileStrategyTest.php | 29 ++++++++++++++----- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserProfileStrategy.php b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserProfileStrategy.php index cf590e4..afa89d9 100644 --- a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserProfileStrategy.php +++ b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserProfileStrategy.php @@ -85,7 +85,7 @@ public function register_hooks(): void { add_filter( 'manage_users_columns', array( $this, 'add_users_column' ) ); add_filter( 'manage_users_custom_column', array( $this, 'render_users_column' ), 10, 3 ); add_action( 'restrict_manage_users', array( $this, 'render_users_filter' ) ); - add_action( 'pre_get_users', array( $this, 'filter_users' ) ); + add_filter( 'users_list_table_query_args', array( $this, 'filter_users' ) ); } /** @@ -230,33 +230,31 @@ public function render_users_filter( string $which ): void { } /** - * Limits the Users screen's list to the users holding the filtered payment method on the profile. + * Returns the Users screen's query arguments limited to the users holding the filtered payment method on the profile. * * @since 2.0.0 * @version 2.0.0 * - * @global string $pagenow The admin screen's file. + * @param array $args The list table's query arguments. * - * @param \WP_User_Query $query The users query. - * - * @return void + * @return array */ - public function filter_users( \WP_User_Query $query ): void { - global $pagenow; - - $active = 'users.php' === $pagenow && is_admin() ? $this->get_active_filter() : ''; + public function filter_users( array $args ): array { + $active = $this->get_active_filter(); if ( '' === $active ) { - return; + return $args; } - $meta_query = $query->get( 'meta_query' ); - $meta_query = \is_array( $meta_query ) ? $meta_query : array(); - $meta_query[] = array( + $meta_query = $args['meta_query'] ?? array(); + $meta_query = \is_array( $meta_query ) ? $meta_query : array(); + $meta_query[] = array( 'key' => dws_lpmwc_get_user_grants_key(), 'value' => '"' . $active . '"', // Matches the id as one element of the stored, serialized list. 'compare' => 'LIKE', ); - $query->set( 'meta_query', $meta_query ); + $args['meta_query'] = $meta_query; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- The filter matches a user meta value, which only a meta query reaches. + + return $args; } // endregion diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/UserProfileStrategyTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/UserProfileStrategyTest.php index a64d86a..4c93f22 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/UserProfileStrategyTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/UserProfileStrategyTest.php @@ -219,6 +219,15 @@ public function test_the_users_screen_filters_by_a_method_granted_on_the_profile self::assertSame( array( self::$users['editor'] ), self::listed_users() ); } + public function test_the_users_screen_filter_leaves_other_user_queries_alone(): void { + update_user_meta( self::$users['customer'], self::key(), array( 'cod' ) ); + $GLOBALS['pagenow'] = 'users.php'; + $_GET['dws_lpmwc_unlocked_for_top'] = 'cod'; + set_current_screen( 'users' ); + + self::assertSame( \array_values( self::$users ), self::queried_users() ); + } + protected static function create_user( string $role ): int { $login = \uniqid( "lpm-profiles-$role-" ); @@ -266,14 +275,18 @@ protected static function save( int $user_id, array|string $methods ): void { } protected static function listed_users(): array { - $query = new \WP_User_Query( - array( - 'include' => \array_values( self::$users ), - 'fields' => 'ID', - 'orderby' => 'ID', - ) - ); + return self::queried_users( apply_filters( 'users_list_table_query_args', self::query_args() ) ); + } - return \array_map( 'intval', $query->get_results() ); + protected static function queried_users( ?array $args = null ): array { + return \array_map( 'intval', ( new \WP_User_Query( $args ?? self::query_args() ) )->get_results() ); + } + + protected static function query_args(): array { + return array( + 'include' => \array_values( self::$users ), + 'fields' => 'ID', + 'orderby' => 'ID', + ); } } From 55e4e3e91fefd477ae0d0774adb86f2444022646 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 00:59:27 +0100 Subject: [PATCH 39/50] Load the footprint's constants when uninstalling WordPress uninstalls a plugin in a request that never included it, and uninstall.php loaded footprint.php and options.php but not the capabilities and grants files whose constants the footprint names. The footprint threw on an undefined constant, so a web uninstall kept every row despite the site's consent, and WP-CLI failed. The uninstall rows ran in processes that had loaded the plugin, which defined them. uninstall.php requires capabilities.php and grants.php too, and a row uninstalls through a WP-CLI request that skips the plugin, as WordPress's own uninstall request does. Assisted-by: Claude Code:claude-opus-5-5 --- .../tests/Integration/UninstallTest.php | 22 +++++++++++++++++++ .../tests/Support/RunsRequestsTrait.php | 4 ++-- .../uninstall.php | 2 ++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UninstallTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UninstallTest.php index e0ee6a2..f31de77 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UninstallTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UninstallTest.php @@ -3,6 +3,7 @@ namespace DeepWebSolutions\LockedPaymentMethods\Tests\Integration; use DeepWebSolutions\LockedPaymentMethods\Plugin; +use DeepWebSolutions\LockedPaymentMethods\Tests\Support\RunsRequestsTrait; use PHPUnit\Framework\Attributes\AfterClass; use PHPUnit\Framework\Attributes\CoversNothing; use PHPUnit\Framework\Attributes\RunInSeparateProcess; @@ -10,6 +11,8 @@ #[CoversNothing] final class UninstallTest extends TestCase { + use RunsRequestsTrait; + protected const int USER = 1; #[AfterClass] @@ -73,6 +76,25 @@ public function test_a_consenting_uninstall_removes_a_row_of_every_kind_the_fram self::assertSame( 'no', get_option( DWS_LPMWC_OPTION_DELETE_DATA ) ); } + #[RunInSeparateProcess] + public function test_an_uninstall_in_a_request_that_never_loaded_the_plugin_removes_its_data(): void { + update_option( DWS_LPMWC_OPTION_DELETE_DATA, 'yes' ); + update_option( DWS_LPMWC_OPTION_GROUPS_UNLOCKED_METHODS, array( 7 => array( 'cod' ) ) ); + + try { + // WordPress uninstalls a plugin in a request that never included it, as WP-CLI's --skip-plugins does here. + $request = self::request( 'require_once ABSPATH . "wp-admin/includes/plugin.php"; echo wp_json_encode( array( uninstall_plugin( "' . plugin_basename( DWS_LPMWC_FILE ) . '" ), get_option( "' . DWS_LPMWC_OPTION_GROUPS_UNLOCKED_METHODS . '" ) ) );', '--skip-plugins=' . Plugin::SLUG ); + + self::assertSame( '[true,false]', $request['last'], $request['output'] ); + } finally { + wp_cache_flush(); + wp_roles()->for_site(); + // The other tests share the database, so the runner stores again what a new site starts with, and the grants act again. + dws_lpmwc_plugin()->migrations?->run(); + do_action( 'wp_loaded' ); + } + } + #[RunInSeparateProcess] public function test_a_consenting_uninstall_removes_the_grants_v1s_rows_and_the_capabilities(): void { global $wpdb; diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Support/RunsRequestsTrait.php b/plugins/locked-payment-methods-for-woocommerce/tests/Support/RunsRequestsTrait.php index ed52ba0..2042376 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Support/RunsRequestsTrait.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Support/RunsRequestsTrait.php @@ -6,8 +6,8 @@ trait RunsRequestsTrait { /** * @return array{status: int, output: string, last: string} */ - protected static function request( string $php ): array { - \exec( 'wp --path=' . \escapeshellarg( ABSPATH ) . ' eval ' . \escapeshellarg( $php ) . ' 2>&1', $output, $status ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.system_calls_exec -- Each row needs a fresh request that loads the plugin. + protected static function request( string $php, string $flags = '' ): array { + \exec( 'wp --path=' . \escapeshellarg( ABSPATH ) . " $flags eval " . \escapeshellarg( $php ) . ' 2>&1', $output, $status ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.system_calls_exec -- Each row needs a fresh request that loads the plugin. $output = \array_values( \array_filter( $output, static fn ( string $line ): bool => '' !== \trim( $line ) ) ); return array( diff --git a/plugins/locked-payment-methods-for-woocommerce/uninstall.php b/plugins/locked-payment-methods-for-woocommerce/uninstall.php index 6794020..52ede59 100644 --- a/plugins/locked-payment-methods-for-woocommerce/uninstall.php +++ b/plugins/locked-payment-methods-for-woocommerce/uninstall.php @@ -27,7 +27,9 @@ } require_once __DIR__ . '/vendor/autoload.php'; + require_once __DIR__ . '/includes/capabilities.php'; require_once __DIR__ . '/includes/footprint.php'; + require_once __DIR__ . '/includes/grants.php'; require_once __DIR__ . '/includes/options.php'; $dws_lpmwc_footprint = dws_lpmwc_get_footprint(); $dws_lpmwc_consent = DWS_LPMWC_OPTION_DELETE_DATA; From 3d98a539d8c65dad1f6762679638f1b80008cc1e Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 01:01:36 +0100 Subject: [PATCH 40/50] Store an order's grants list before deleting v1's rows The order grants step queued the deletion of v1's grant rows and the new list in one save, and WooCommerce deletes an order's meta before it adds any. When the list's insert failed, the step threw after v1's rows were gone, so its retry found nothing to move and the order lost its grants. The step saves the merged list and verifies it, then deletes v1's rows in a second save and verifies that, so a failure at either save leaves what a retry needs. A row refuses the list's insert on the posts storage, then checks that v1's grant survives and the retry moves it. Assisted-by: Claude Code:claude-opus-5-5 --- .../includes/migrations.php | 57 +++++++++++++------ .../UnlockStrategies/OrderStrategyTest.php | 23 ++++++++ 2 files changed, 64 insertions(+), 16 deletions(-) diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php b/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php index 03d8480..ed1fa1b 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php @@ -126,12 +126,14 @@ function dws_lpmwc_move_user_grants(): void { /** * Moves v1's order grants, one 'yes' row per payment method, into each order's list beside the grants it holds, through WooCommerce's order data, so either order storage keeps them, then deletes every v1 grant row of the order. * + * The list is saved before v1's rows are deleted, because one save deletes an order's meta before it adds any, so a failed write would lose the grants a retry needs. + * * @since 2.0.0 * @version 2.0.0 * * @global wpdb $wpdb WordPress's database access. * - * @throws RuntimeException Thrown when v1's order grants could not be read or an order's grants could not be stored. + * @throws RuntimeException Thrown when v1's order grants could not be read, or an order's grants could not be stored or its v1 rows deleted. * * @return void */ @@ -159,34 +161,57 @@ function dws_lpmwc_move_order_grants(): void { foreach ( array_unique( $ids ) as $order_id ) { $order = wc_get_order( $order_id ); - if ( ! $order instanceof WC_Order ) { + $rows = $order instanceof WC_Order ? dws_lpmwc_get_v1_order_grant_rows( $order ) : array(); + if ( array() === $rows ) { continue; } $granted = dws_lpmwc_get_order_unlocked_payment_method_ids( $order_id ); - $moved = false; - foreach ( $order->get_meta_data() as $meta ) { - $data = $meta instanceof WC_Meta_Data ? $meta->get_data() : array(); - if ( is_string( $data['key'] ?? null ) && str_starts_with( $data['key'], $prefix ) ) { - if ( 'yes' === ( $data['value'] ?? null ) ) { - $granted[] = substr( $data['key'], strlen( $prefix ) ); - } - $order->delete_meta_data( $data['key'] ); - $moved = true; + foreach ( $rows as $key => $value ) { + if ( 'yes' === $value ) { + $granted[] = substr( $key, strlen( $prefix ) ); } } - if ( ! $moved ) { - continue; + $granted = array_values( array_unique( $granted ) ); + $order->update_meta_data( DWS_LPMWC_META_UNLOCKED_METHODS, $granted ); + $order->save(); + if ( dws_lpmwc_get_order_unlocked_payment_method_ids( $order_id ) !== $granted ) { + throw new RuntimeException( "The payment methods unlocked for order $order_id could not be stored." ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing, never rendered. } - $order->update_meta_data( DWS_LPMWC_META_UNLOCKED_METHODS, array_values( array_unique( $granted ) ) ); + foreach ( array_keys( $rows ) as $key ) { + $order->delete_meta_data( $key ); + } $order->save(); - if ( dws_lpmwc_get_order_unlocked_payment_method_ids( $order_id ) !== array_values( array_unique( $granted ) ) ) { - throw new RuntimeException( "The payment methods unlocked for order $order_id could not be stored." ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing, never rendered. + $order = wc_get_order( $order_id ); + if ( $order instanceof WC_Order && array() !== dws_lpmwc_get_v1_order_grant_rows( $order ) ) { + throw new RuntimeException( "The v1 grants of order $order_id could not be deleted." ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing, never rendered. } } } +/** + * Returns an order's v1 grant rows, by meta key. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param WC_Order $order The order. + * + * @return array + */ +function dws_lpmwc_get_v1_order_grant_rows( WC_Order $order ): array { + $rows = array(); + foreach ( $order->get_meta_data() as $meta ) { + $data = $meta instanceof WC_Meta_Data ? $meta->get_data() : array(); + if ( is_string( $data['key'] ?? null ) && str_starts_with( $data['key'], '_dws_wc_lpm_grant_access_' ) ) { + $rows[ $data['key'] ] = $data['value'] ?? null; + } + } + + return $rows; +} + /** * Moves v1's Groups grants, one option per group, into the map of every group's grants beside the grants it holds, then deletes v1's options. * diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/OrderStrategyTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/OrderStrategyTest.php index 17ddcfe..6bc35df 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/OrderStrategyTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/OrderStrategyTest.php @@ -185,6 +185,29 @@ public function test_the_grants_step_moves_v1s_order_grants_into_the_orders_list } } + public function test_a_grants_step_whose_write_fails_keeps_v1s_grants_for_its_retry(): void { + $this->use_storage( false, false ); + $order = $this->create_order(); + $order->update_meta_data( '_dws_wc_lpm_grant_access_cod', 'yes' ); + $order->save(); + $refuse = static fn ( mixed $check, int $object_id, string $key ): mixed => DWS_LPMWC_META_UNLOCKED_METHODS === $key ? false : $check; + add_filter( 'add_post_metadata', $refuse, 10, 3 ); + + $failed = false; + try { + dws_lpmwc_move_order_grants(); + } catch ( \RuntimeException ) { + $failed = true; + } finally { + remove_filter( 'add_post_metadata', $refuse ); + } + + self::assertTrue( $failed ); + self::assertSame( 'yes', wc_get_order( $order->get_id() )->get_meta( '_dws_wc_lpm_grant_access_cod' ) ); + dws_lpmwc_move_order_grants(); + self::assertSame( array( 'cod' ), dws_lpmwc_get_order_unlocked_payment_method_ids( $order->get_id() ) ); + } + protected static function should_lock( string $gateway_id, int $order_id ): mixed { return apply_filters( 'dws_lpmwc/payment_method/should_lock', true, $gateway_id, 0, $order_id ); } From 75148bd8bd55b7dd012783e99ce11d61df3fb894 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 01:02:40 +0100 Subject: [PATCH 41/50] Leave no orders behind the checkout and uninstall rows The block checkout and the Store API's checkout leave a draft order each time the checkout spec runs, and an uninstall row created an order before checking a capability outside its try, so a failed check left the order behind. The tests site's start enables WooCommerce's order tables, whose check refuses while any order is unsynced, so the next quality-check failed to start the site. The checkout spec deletes every draft order after its rows, and the uninstall row checks the capability inside the try whose finally deletes the order. Assisted-by: Claude Code:claude-opus-5-5 --- .../tests/Integration/UninstallTest.php | 2 +- .../tests/e2e/checkout.spec.ts | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UninstallTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UninstallTest.php index f31de77..8815cf8 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UninstallTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UninstallTest.php @@ -120,9 +120,9 @@ public function test_a_consenting_uninstall_removes_the_grants_v1s_rows_and_the_ ); update_post_meta( $plan, DWS_LPMWC_META_UNLOCKED_METHODS, array( 'cod' ) ); update_post_meta( $plan, '_dws_wc_lpm_unlocked_payment_methods', array( 'cod' ) ); - self::assertTrue( get_role( 'administrator' )?->has_cap( DWS_LPMWC_CAP_UNLOCK_VIA_GROUPS ) ); try { + self::assertTrue( get_role( 'administrator' )?->has_cap( DWS_LPMWC_CAP_UNLOCK_VIA_GROUPS ) ); self::assertTrue( uninstall_plugin( plugin_basename( DWS_LPMWC_FILE ) ) ); wp_cache_flush(); wp_roles()->for_site(); diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/e2e/checkout.spec.ts b/plugins/locked-payment-methods-for-woocommerce/tests/e2e/checkout.spec.ts index 73c34cf..94a07ec 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/e2e/checkout.spec.ts +++ b/plugins/locked-payment-methods-for-woocommerce/tests/e2e/checkout.spec.ts @@ -81,6 +81,11 @@ test.describe( 'Checkout', () => { params: { force: true }, } ); cli( 'user', 'delete', String( customer ), '--yes' ); + // The block checkout and the Store API's checkout leave draft orders, which the next start's order tables check counts as unsynced. + cli( + 'eval', + 'foreach ( wc_get_orders( array( "status" => "checkout-draft", "limit" => -1 ) ) as $order ) { $order->delete( true ); }' + ); cli( 'option', 'update', From 9e85659b10e55cf380b7e7a3a196fcc63525b9f3 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 01:08:47 +0100 Subject: [PATCH 42/50] Keep order grants off checkouts that pay a new order The lock passed should_lock the order an order-pay URL names once the request carried that order's key. WordPress parses the order-pay query var on AJAX and REST requests too, and WooCommerce's AJAX checkout counts as the checkout, so a customer posting the cart checkout to ?wc-ajax=checkout&order-pay=&key= of an order they hold had that order's unlocked payment methods offered for a new order from the cart. The Store API's checkout took the same path. The order-pay order counts only outside AJAX and REST requests, where the order-pay page and its pay form run, and rows give an AJAX and a REST request a valid key. The changelog's security entry says so. Assisted-by: Claude Code:claude-opus-5-5 --- .../changelog/order-pay-key | 2 +- .../includes/safeguards.php | 4 ++- .../tests/Integration/SafeguardsTest.php | 27 +++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/plugins/locked-payment-methods-for-woocommerce/changelog/order-pay-key b/plugins/locked-payment-methods-for-woocommerce/changelog/order-pay-key index 4a8f32b..1eec1d3 100644 --- a/plugins/locked-payment-methods-for-woocommerce/changelog/order-pay-key +++ b/plugins/locked-payment-methods-for-woocommerce/changelog/order-pay-key @@ -1,4 +1,4 @@ Significance: patch Type: security -An order unlocks its payment methods only on its own payment page and with its order key, so a checkout request naming another order's id no longer takes that order's unlocked payment methods. +An order unlocks its payment methods only on its own payment page with its order key, never in an AJAX or REST request, so a checkout of a new order that names an existing order's id, or its key too, no longer takes that order's unlocked payment methods. diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php b/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php index 81aee81..a5df1f2 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php @@ -114,6 +114,8 @@ function dws_lpmwc_should_lock( bool $lock, string $gateway_id, int $user_id, in /** * Returns the order the order-pay page pays, or 0 elsewhere or without that order's key, which WooCommerce requires before it takes the payment, so a request naming another order's id gains none of its grants. * + * AJAX and REST requests count as elsewhere, because the checkouts they run pay a new order from the cart whatever order the URL names. + * * @since 2.0.0 * @version 2.0.0 * @@ -124,7 +126,7 @@ function dws_lpmwc_should_lock( bool $lock, string $gateway_id, int $user_id, in function dws_lpmwc_get_paid_order_id(): int { global $wp; - if ( ! function_exists( 'is_checkout_pay_page' ) || ! is_checkout_pay_page() ) { + if ( wp_doing_ajax() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) || ! function_exists( 'is_checkout_pay_page' ) || ! is_checkout_pay_page() ) { return 0; } diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SafeguardsTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SafeguardsTest.php index e9fd4a2..82c03f5 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SafeguardsTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SafeguardsTest.php @@ -7,6 +7,7 @@ use PHPUnit\Framework\Attributes\Before; use PHPUnit\Framework\Attributes\CoversNothing; use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\RunInSeparateProcess; use PHPUnit\Framework\TestCase; #[CoversNothing] @@ -283,6 +284,32 @@ public function test_should_lock_judges_the_order_its_order_pay_page_pays_given_ } } + public function test_should_lock_judges_no_order_in_an_ajax_checkout_that_names_an_order_and_its_key(): void { + $order = wc_create_order(); + self::visit_order_pay( $order->get_id(), $order->get_order_key() ); + add_filter( 'wp_doing_ajax', '__return_true' ); + + try { + self::assertSame( array( true, 'cod', 0, 0 ), self::should_lock_arguments() ); + } finally { + remove_filter( 'wp_doing_ajax', '__return_true' ); + $order->delete( true ); + } + } + + #[RunInSeparateProcess] + public function test_should_lock_judges_no_order_in_a_rest_checkout_that_names_an_order_and_its_key(): void { + \define( 'REST_REQUEST', true ); + $order = wc_create_order(); + self::visit_order_pay( $order->get_id(), $order->get_order_key() ); + + try { + self::assertSame( array( true, 'cod', 0, 0 ), self::should_lock_arguments() ); + } finally { + $order->delete( true ); + } + } + #[DataProvider( 'order_keys' )] public function test_should_lock_judges_no_order_on_an_order_pay_page_without_the_orders_key( ?string $key ): void { $order = wc_create_order(); From ba10de848c751f2ed7ffd976a7b93bf3f58efce5 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 01:09:56 +0100 Subject: [PATCH 43/50] Test the underscore include skip with an include that exists The boot row asserted that includes/_disabled-example.php never loads, but no such file exists, so the row passed even with the loader's underscore check removed. The row writes an underscore-prefixed include defining a constant and checks in a fresh request that the constant stays undefined, as Linked Orders' row does; it fails once the loader's check is removed. Assisted-by: Claude Code:claude-opus-5-5 --- .../tests/Integration/PluginBootTest.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php index cacfda6..077adae 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php @@ -13,11 +13,14 @@ use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\UserProfileStrategy; use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\UserRoleStrategy; use DeepWebSolutions\LockedPaymentMethods\UnlockStrategies\WooCommerceMembershipsStrategy; +use DeepWebSolutions\LockedPaymentMethods\Tests\Support\RunsRequestsTrait; use PHPUnit\Framework\Attributes\CoversNothing; use PHPUnit\Framework\TestCase; #[CoversNothing] final class PluginBootTest extends TestCase { + use RunsRequestsTrait; + public function test_the_plugin_boots_every_component_whose_switch_is_on(): void { $plugin = dws_lpmwc_plugin(); @@ -30,7 +33,14 @@ public function test_the_entry_loads_every_include_but_those_whose_names_start_w $includes = \dirname( DWS_LPMWC_FILE ) . '/includes/'; self::assertContains( $includes . 'index.php', \get_included_files() ); - self::assertNotContains( $includes . '_disabled-example.php', \get_included_files() ); + \file_put_contents( $includes . '_lpmwc-skipped.php', " Date: Sun, 1 Nov 2026 01:14:18 +0100 Subject: [PATCH 44/50] Fail the order grants step when its table probe fails The order grants step probed for each meta table with SHOW TABLES and skipped a table the probe did not name, so a failed probe read as an absent table. The step then finished, the runner recorded it, and the orders in that table kept v1's grants, which v2 never reads. A probe that fails throws, so the runner runs the step again on a later request, and a row breaks the probe and expects the throw. Assisted-by: Claude Code:claude-opus-5-5 --- .../includes/migrations.php | 6 +++++- .../UnlockStrategies/OrderStrategyTest.php | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php b/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php index ed1fa1b..9308ff5 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/migrations.php @@ -148,7 +148,11 @@ function dws_lpmwc_move_order_grants(): void { ); foreach ( $tables as $table => $column ) { $wpdb->flush(); - if ( $table !== $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $table ) ) ) ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- The order tables exist only once WooCommerce created them. + $listed = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $table ) ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- The order tables exist only once WooCommerce created them. + if ( null === $listed && '' !== $wpdb->last_error ) { + throw new RuntimeException( 'The v1 order grants could not be read.' ); + } + if ( $table !== $listed ) { continue; } diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/OrderStrategyTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/OrderStrategyTest.php index 6bc35df..3b9dc4e 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/OrderStrategyTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/OrderStrategyTest.php @@ -185,6 +185,21 @@ public function test_the_grants_step_moves_v1s_order_grants_into_the_orders_list } } + public function test_the_grants_step_throws_when_it_cannot_tell_whether_an_order_table_exists(): void { + global $wpdb; + + $fail = static fn ( string $query ): string => \str_starts_with( \ltrim( $query ), 'SHOW TABLES' ) ? "SELECT * FROM {$wpdb->prefix}dws_lpmwc_missing" : $query; + $suppressed = $wpdb->suppress_errors(); + add_filter( 'query', $fail ); + try { + $this->expectExceptionObject( new \RuntimeException( 'The v1 order grants could not be read.' ) ); + dws_lpmwc_move_order_grants(); + } finally { + remove_filter( 'query', $fail ); + $wpdb->suppress_errors( $suppressed ); + } + } + public function test_a_grants_step_whose_write_fails_keeps_v1s_grants_for_its_retry(): void { $this->use_storage( false, false ); $order = $this->create_order(); From 41021c7920288ba28917c7772d2a2f997e2269c3 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 01:19:29 +0100 Subject: [PATCH 45/50] Narrow the uninstall's v1 user meta prefix to the grants The footprint swept every user meta key starting with dws_wc_lpm_, though v1 stored only its grants, dws_wc_lpm_grant_access_{gateway}, under it. A user meta prefix sweep also matches WordPress's own per-site keys, such as {table prefix}capabilities, when the site's table prefix starts with it, so the wider prefix put every user's roles at risk on a site whose table prefix begins dws_wc_lpm_. The footprint names v1's grants prefix alone, and the Unit row and the architecture say so. Assisted-by: Claude Code:claude-opus-5-5 --- .../locked-payment-methods-for-woocommerce/docs/ARCHITECTURE.md | 2 +- .../includes/footprint.php | 2 +- .../tests/Unit/FootprintTest.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/locked-payment-methods-for-woocommerce/docs/ARCHITECTURE.md b/plugins/locked-payment-methods-for-woocommerce/docs/ARCHITECTURE.md index 921f98f..4564b32 100644 --- a/plugins/locked-payment-methods-for-woocommerce/docs/ARCHITECTURE.md +++ b/plugins/locked-payment-methods-for-woocommerce/docs/ARCHITECTURE.md @@ -46,7 +46,7 @@ Each unlock strategy in `src/UnlockStrategies/` hooks `should_lock` while its sw - **Settings** are flat options under `dws_lpmwc_`, as constants in `includes/options.php`: the locked list, a switch per strategy, the full-access roles and the uninstall consent. The first migration step copies each of v1's eight settings from its `dws-wc-lpm_{group}_{field}` row where it holds a value of the setting's kind, and leaves v1's rows until uninstall, so a downgrade to 1.3.5 still locks what it locked. - **Grants** are one list of payment method ids per subject: the user option `dws_lpmwc_unlocked_payment_methods` under each site's table prefix, the order meta and the Memberships plan meta `_dws_lpmwc_unlocked_payment_methods`, and the `dws_lpmwc_groups_unlocked_payment_methods` map from Groups group id to list, from which deleting a group removes its entry. The second to fifth migration steps move v1's grants into these shapes and delete v1's rows: user meta into every site's list, so v1's grants keep their network reach, order meta through WC CRUD on either storage, the per-group options and the plan meta. - **Migrations** are the append-only steps `dws_lpmwc_get_migration_steps()` returns. The framework's runner runs each pending step once per site on `wp_loaded`, behind a lock, and records the revision reached in `dws_lpmwc_revision`. A step converges from any partial state and throws when a write fails, so a failed step runs again on a later request. -- **The footprint**, `dws_lpmwc_get_footprint()`, names every kind of data the plugin stores: the `dws_lpmwc_` and `dws-wc-lpm_` option prefixes, v1's version and notice rows, the order meta under `_dws_lpmwc_` and `_dws_wc_lpm_` on both storages, the plan meta under both keys, the site user meta under `dws_lpmwc_`, v1's global user grants under `dws_wc_lpm_`, network options, the capabilities, scheduled hooks and Action Scheduler groups. Freemius's `fs_` rows from v1 stay, since they belong to Freemius. +- **The footprint**, `dws_lpmwc_get_footprint()`, names every kind of data the plugin stores: the `dws_lpmwc_` and `dws-wc-lpm_` option prefixes, v1's version and notice rows, the order meta under `_dws_lpmwc_` and `_dws_wc_lpm_` on both storages, the plan meta under both keys, the site user meta under `dws_lpmwc_`, v1's global user grants under `dws_wc_lpm_grant_access_`, network options, the capabilities, scheduled hooks and Action Scheduler groups. Freemius's `fs_` rows from v1 stay, since they belong to Freemius. - **Deactivation** unschedules every hook in the footprint's `cron_hooks` and `as_hooks` on the current site. - **Uninstall** runs `uninstall.php`, which calls the framework's uninstall engine over the footprint, with WooCommerce's order meta types, on every site whose `dws_lpmwc_delete_data` setting consents, which the first migration step copies from v1's; a site that never consented keeps its data. Below the floors or with an incomplete build, a web request deletes the files, keeps the data and logs why, while WP-CLI fails and keeps both for a rerun. diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/footprint.php b/plugins/locked-payment-methods-for-woocommerce/includes/footprint.php index ae53bad..64b4d47 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/footprint.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/footprint.php @@ -27,7 +27,7 @@ function dws_lpmwc_get_footprint(): array { 'meta' => array( 'post' => array( DWS_LPMWC_META_UNLOCKED_METHODS, '_dws_wc_lpm_unlocked_payment_methods' ) ), 'meta_prefixes' => array( 'order' => array( '_' . Plugin::PREFIX . '_', '_dws_wc_lpm_' ) ), 'user_meta_site_prefixes' => array( Plugin::PREFIX . '_' ), - 'user_meta_global_prefixes' => array( Plugin::PREFIX . '_', 'dws_wc_lpm_' ), + 'user_meta_global_prefixes' => array( Plugin::PREFIX . '_', 'dws_wc_lpm_grant_access_' ), 'user_meta_global' => array( '_locked_payment_methods_for_woocommerce_admin_notices' ), 'network_options' => array( Plugin::PREFIX . '_network_notices' ), 'capabilities' => array( DWS_LPMWC_CAP_UNLOCK_VIA_USER_PROFILES, DWS_LPMWC_CAP_UNLOCK_VIA_ORDERS, DWS_LPMWC_CAP_UNLOCK_VIA_GROUPS, DWS_LPMWC_CAP_UNLOCK_VIA_WC_MEMBERSHIPS ), diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Unit/FootprintTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Unit/FootprintTest.php index 8514628..e9e4d0a 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Unit/FootprintTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Unit/FootprintTest.php @@ -73,7 +73,7 @@ public function test_it_covers_what_v1_stored_and_the_grants_and_capabilities(): self::assertSame( array( 'locked_payment_methods_for_woocommerce_version', 'locked_payment_methods_for_woocommerce_original_version', '_locked_payment_methods_for_woocommerce_admin_notices' ), $footprint['options'] ); self::assertSame( array( '_' . Plugin::PREFIX . '_', '_dws_wc_lpm_' ), $footprint['meta_prefixes']['order'] ); self::assertSame( array( DWS_LPMWC_META_UNLOCKED_METHODS, '_dws_wc_lpm_unlocked_payment_methods' ), $footprint['meta']['post'] ); - self::assertContains( 'dws_wc_lpm_', $footprint['user_meta_global_prefixes'] ); + self::assertContains( 'dws_wc_lpm_grant_access_', $footprint['user_meta_global_prefixes'] ); self::assertSame( array( '_locked_payment_methods_for_woocommerce_admin_notices' ), $footprint['user_meta_global'] ); self::assertSame( array( DWS_LPMWC_CAP_UNLOCK_VIA_USER_PROFILES, DWS_LPMWC_CAP_UNLOCK_VIA_ORDERS, DWS_LPMWC_CAP_UNLOCK_VIA_GROUPS, DWS_LPMWC_CAP_UNLOCK_VIA_WC_MEMBERSHIPS ), $footprint['capabilities'] ); } From d9239cadf01e94b3937aec955cd9250fa6655428 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 01:24:14 +0100 Subject: [PATCH 46/50] Judge the gateways as WooCommerce casts them The lock passed on a gateways value that was not an array, so an earlier callback that returned the gateways as an object skipped the lock, and WooCommerce, which casts the filtered value to an array, then offered every locked payment method. The lock casts the value as WooCommerce does before it judges it, and rows replace the gateways with null and with an object. Assisted-by: Claude Code:claude-opus-5-5 --- .../includes/safeguards.php | 10 ++++----- .../tests/Integration/SafeguardsTest.php | 21 ++++++++++++++----- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php b/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php index a5df1f2..824345f 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php @@ -142,14 +142,12 @@ function dws_lpmwc_get_paid_order_id(): int { * @since 2.0.0 * @version 2.0.0 * - * @param mixed $gateways The payment gateways offered, by id, or what an earlier callback returned in their place, which WooCommerce casts to a list. + * @param mixed $gateways The payment gateways offered, by id, or what an earlier callback returned in their place. * - * @return mixed + * @return array */ -function dws_lpmwc_remove_locked_payment_methods( $gateways ) { - if ( ! is_array( $gateways ) ) { - return $gateways; - } +function dws_lpmwc_remove_locked_payment_methods( $gateways ): array { + $gateways = (array) $gateways; // WooCommerce casts the filtered value to a list, so the lock judges what it would offer. $ids = dws_lpmwc_get_locked_payment_method_ids(); diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SafeguardsTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SafeguardsTest.php index 82c03f5..62caed5 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SafeguardsTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SafeguardsTest.php @@ -32,6 +32,13 @@ public static function relaxations(): array { ); } + public static function gateway_values(): array { + return array( + 'null' => array( 'null' ), + 'object' => array( 'object' ), + ); + } + public static function early_relaxations(): array { return array( 'null' => array( null ), @@ -115,13 +122,17 @@ public function test_a_callback_before_the_strategies_unlocks_nothing_without_an } } - public function test_the_lock_passes_on_a_gateways_value_that_is_not_a_list(): void { - add_filter( 'woocommerce_available_payment_gateways', '__return_null' ); + #[DataProvider( 'gateway_values' )] + public function test_the_lock_judges_a_gateways_value_that_is_not_a_list_as_woocommerce_casts_it( string $kind ): void { + $replace = static fn ( array $gateways ): ?object => 'object' === $kind ? (object) $gateways : null; + add_filter( 'woocommerce_available_payment_gateways', $replace ); try { - self::assertNull( self::available() ); + $offered = (array) self::available(); + self::assertArrayNotHasKey( 'cod', $offered ); + self::assertSame( 'object' === $kind, \array_key_exists( 'bacs', $offered ) ); } finally { - remove_filter( 'woocommerce_available_payment_gateways', '__return_null' ); + remove_filter( 'woocommerce_available_payment_gateways', $replace ); } } @@ -339,7 +350,7 @@ public function test_the_lock_holds_and_nothing_relaxes_it_while_the_plugin_cann self::assertSame( $expected, $request['last'] ); } - protected static function available( array $gateways = array() ): ?array { + protected static function available( array $gateways = array() ): mixed { return apply_filters( 'woocommerce_available_payment_gateways', $gateways + WC()->payment_gateways()->payment_gateways() ); } From 58ac0a3ed9f5ca51fc4df038482db37244b49731 Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 10:51:45 +0100 Subject: [PATCH 47/50] Test that every reader of the locked list counts the locked_ids filter dws_lpmwc/payment_method/locked_ids fired only in the checkout's gateways filter, so a payment method it locked was hidden at checkout but reported unlocked by dws_lpmwc_is_payment_method_locked() and offered on no grants form, leaving it to full-access roles and should_lock callbacks. The rows check that the lock check and the profile form count an id the filter adds, and that the filter runs right after v1's value filter with the ids alone, before v1's list filter at checkout. Assisted-by: Claude Code:claude-opus-5-5 --- .../tests/Integration/SafeguardsTest.php | 25 ++++++++++++++----- .../UserProfileStrategyTest.php | 21 ++++++++++++++++ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SafeguardsTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SafeguardsTest.php index 62caed5..ba418ad 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SafeguardsTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/SafeguardsTest.php @@ -197,17 +197,30 @@ public function test_the_locked_ids_filter_locks_more_methods_but_cannot_unlock_ self::assertArrayNotHasKey( 'bacs', $available ); } - public function test_v1s_list_filters_fire_as_deprecated_before_the_locked_ids_filter(): void { + public function test_the_lock_check_counts_the_methods_the_locked_ids_filter_adds(): void { + $add = static fn ( array $ids ): array => array( ...$ids, 'bacs' ); + add_filter( 'dws_lpmwc/payment_method/locked_ids', $add ); + + try { + self::assertTrue( dws_lpmwc_is_payment_method_locked( 'bacs', 0 ) ); + } finally { + remove_filter( 'dws_lpmwc/payment_method/locked_ids', $add ); + } + + self::assertFalse( dws_lpmwc_is_payment_method_locked( 'bacs', 0 ) ); + } + + public function test_the_locked_ids_filter_follows_v1s_value_filter_and_v1s_list_filter_follows_it_at_checkout(): void { $seen = array(); $gateways = WC()->payment_gateways()->payment_gateways(); $hooks = array( 'locked_payment_methods_for_woocommerce/general_settings/get_validated_option_value' => 'first', - 'locked_payment_methods_for_woocommerce/lock_manager/locked_payment_methods' => 'second', - 'dws_lpmwc/payment_method/locked_ids' => 'third', + 'dws_lpmwc/payment_method/locked_ids' => 'second', + 'locked_payment_methods_for_woocommerce/lock_manager/locked_payment_methods' => 'third', ); $adders = array(); foreach ( $hooks as $hook => $id ) { - $adders[ $hook ] = static function ( array $ids, mixed $context ) use ( &$seen, $hook, $id ): array { + $adders[ $hook ] = static function ( array $ids, mixed $context = null ) use ( &$seen, $hook, $id ): array { $seen[] = array( $hook, $ids, \is_array( $context ) ? \array_keys( $context ) : $context ); return array( ...$ids, $id ); @@ -227,12 +240,12 @@ public function test_v1s_list_filters_fire_as_deprecated_before_the_locked_ids_f self::assertSame( array( array( $hooks[0], array( 'cod' ), 'locked-payment-methods' ), - array( $hooks[1], array( 'cod', 'first' ), \array_keys( $gateways ) ), + array( $hooks[1], array( 'cod', 'first' ), null ), array( $hooks[2], array( 'cod', 'first', 'second' ), \array_keys( $gateways ) ), ), $seen ); - self::assertSame( array( array( $hooks[0], $hooks[2] ), array( $hooks[1], $hooks[2] ) ), $this->deprecated ); + self::assertSame( array( array( $hooks[0], $hooks[1] ), array( $hooks[2], $hooks[1] ) ), $this->deprecated ); } public function test_v1s_lock_filters_fire_as_deprecated_before_should_lock_under_v1s_safe_id(): void { diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/UserProfileStrategyTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/UserProfileStrategyTest.php index 4c93f22..dfc4749 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/UserProfileStrategyTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/UnlockStrategies/UserProfileStrategyTest.php @@ -121,6 +121,27 @@ public function test_the_profile_offers_the_locked_methods_and_the_users_other_g self::assertSame( 'Locked Payment Methods', \trim( (string) $form->querySelector( 'h2' )?->textContent ) ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- A DOM property. } + public function test_the_profile_offers_the_methods_the_locked_ids_filter_adds_as_locked(): void { + $add = static fn ( array $ids ): array => array( ...$ids, 'cheque' ); + add_filter( 'dws_lpmwc/payment_method/locked_ids', $add ); + wp_set_current_user( self::$users['administrator'] ); + + try { + $form = self::render( self::$users['customer'] ); + } finally { + remove_filter( 'dws_lpmwc/payment_method/locked_ids', $add ); + } + + self::assertSame( + array( + 'cod' => array( 'Cash on delivery', false ), + 'bacs' => array( 'Direct bank transfer', false ), + 'cheque' => array( 'Check payments', false ), + ), + self::choices( $form ) + ); + } + public function test_the_profile_marks_the_methods_another_rule_already_unlocks_for_the_user(): void { wp_set_current_user( self::$users['administrator'] ); From 2f5885eb64bb924be11dd3220bce17193d55554a Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 10:52:59 +0100 Subject: [PATCH 48/50] Filter the locked list wherever Locked Payment Methods reads it dws_lpmwc/payment_method/locked_ids fired only in the checkout's gateways filter with the offered gateways, so the lock check, the grants forms and the Users filter read a shorter list than checkout did, and a payment method locked from code could be unlocked only by full-access roles or should_lock callbacks. The filter fires inside dws_lpmwc_get_locked_payment_method_ids() with the ids alone, after v1's value filter, so every reader sees the ids it adds; v1's deprecated list filter still fires only at checkout, with the offered gateways, as v1 fired it. Assisted-by: Claude Code:claude-opus-5-5 --- .../docs/ARCHITECTURE.md | 4 ++-- .../includes/grants.php | 6 ++--- .../includes/safeguards.php | 24 +++++++++---------- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/plugins/locked-payment-methods-for-woocommerce/docs/ARCHITECTURE.md b/plugins/locked-payment-methods-for-woocommerce/docs/ARCHITECTURE.md index 4564b32..4855f51 100644 --- a/plugins/locked-payment-methods-for-woocommerce/docs/ARCHITECTURE.md +++ b/plugins/locked-payment-methods-for-woocommerce/docs/ARCHITECTURE.md @@ -30,11 +30,11 @@ The request that activates the plugin does not boot it, because WordPress includ `includes/safeguards.php` filters `woocommerce_available_payment_gateways` at 999, which WooCommerce applies on the classic checkout, the order payment page and the Store API's checkout: -1. It reads `dws_lpmwc_locked_payment_methods`, or v1's `dws-wc-lpm_general_locked-payment-methods` while the plugin has stored none, since the migration that copies v1's list runs only once the plugin boots, then lets v1's value and list filters and `dws_lpmwc/payment_method/locked_ids` add ids. +1. It reads `dws_lpmwc_locked_payment_methods`, or v1's `dws-wc-lpm_general_locked-payment-methods` while the plugin has stored none, since the migration that copies v1's list runs only once the plugin boots, then lets v1's value filter and `dws_lpmwc/payment_method/locked_ids` add ids, as every read of the list does, and v1's list filter add more. 2. For each offered payment method on the list, it fires v1's two lock filters, then `dws_lpmwc/payment_method/should_lock` with the current user and, on an order's own payment page with that order's key, the order. 3. It removes the payment method unless `should_lock` returns an explicit `false` after `dws_lpmwc/boot/succeeded`, so the lock binds everyone while the plugin cannot boot. -Each unlock strategy in `src/UnlockStrategies/` hooks `should_lock` while its switch is on, and returns `false` when its subject unlocks the payment method: the user's roles, the user's grants on the site, the order's grants, the user's Groups groups or the plans the user is an active member of. `dws_lpmwc_is_payment_method_locked()` runs the same filter for a user and an order outside checkout, over the settings' list alone, because the list filters judge the payment methods a checkout offers; the grants forms offer the same list. +Each unlock strategy in `src/UnlockStrategies/` hooks `should_lock` while its switch is on, and returns `false` when its subject unlocks the payment method: the user's roles, the user's grants on the site, the order's grants, the user's Groups groups or the plans the user is an active member of. `dws_lpmwc_is_payment_method_locked()` runs the same filter for a user and an order outside checkout, over the same list without v1's list filter, which v1 fired only at checkout; the grants forms offer that list. ## Where code lives diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/grants.php b/plugins/locked-payment-methods-for-woocommerce/includes/grants.php index cfadfc7..d56499c 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/grants.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/grants.php @@ -96,7 +96,7 @@ function dws_lpmwc_get_payment_method_title( string $gateway_id ): string { } /** - * Returns the choices of a grants field by payment method id: the methods the settings lock and the subject's other grants, since a save keeps only the choices a field offers, each marked when it is not locked or when another rule already unlocks it for the user. + * Returns the choices of a grants field by payment method id: the locked methods and the subject's other grants, since a save keeps only the choices a field offers, each marked when it is not locked or when another rule already unlocks it for the user. * * @since 2.0.0 * @version 2.0.0 @@ -171,9 +171,7 @@ function dws_lpmwc_get_plan_unlocked_payment_method_ids( int $plan_id ): array { } /** - * Returns whether a payment method is locked for a user and an order: whether the settings lock it and dws_lpmwc/payment_method/should_lock does not relax it. - * - * The ids the list filters add are left out, because those filters judge the payment methods a checkout offers. + * Returns whether a payment method is locked for a user and an order: whether the locked list holds it and dws_lpmwc/payment_method/should_lock does not relax it. * * @since 2.0.0 * @version 2.0.0 diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php b/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php index 824345f..ecb9d19 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/safeguards.php @@ -27,7 +27,7 @@ function dws_lpmwc_is_relaxed( $filtered ): bool { } /** - * Returns the ids of the locked payment methods: the plugin's list, or v1's while the plugin has stored none, because the migration that copies v1's list runs only once the plugin boots. + * Returns the ids of the locked payment methods: the plugin's list, or v1's while the plugin has stored none, because the migration that copies v1's list runs only once the plugin boots, with the ids the plugin's list filters add. * * @since 2.0.0 * @version 2.0.0 @@ -37,8 +37,17 @@ function dws_lpmwc_is_relaxed( $filtered ): bool { function dws_lpmwc_get_locked_payment_method_ids(): array { $stored = get_option( 'dws_lpmwc_locked_payment_methods' ); $ids = dws_lpmwc_add_locked_ids( array(), false === $stored ? get_option( 'dws-wc-lpm_general_locked-payment-methods' ) : $stored ); + $ids = dws_lpmwc_add_locked_ids( $ids, dws_lpmwc_apply_v1_setting_filter( $ids, 'general', 'locked-payment-methods', 'dws_lpmwc/payment_method/locked_ids' ) ); - return dws_lpmwc_add_locked_ids( $ids, dws_lpmwc_apply_v1_setting_filter( $ids, 'general', 'locked-payment-methods', 'dws_lpmwc/payment_method/locked_ids' ) ); + /** + * Filters the locked payment methods' ids wherever the plugin reads them, at checkout, in the lock check and on the grants forms; a callback adds the ids it locks, and an id it leaves out stays locked. + * + * @since 2.0.0 + * @version 2.0.0 + * + * @param string[] $ids The locked payment methods' ids. + */ + return dws_lpmwc_add_locked_ids( $ids, apply_filters( 'dws_lpmwc/payment_method/locked_ids', $ids ) ); } /** @@ -163,17 +172,6 @@ function dws_lpmwc_remove_locked_payment_methods( $gateways ): array { */ $ids = dws_lpmwc_add_locked_ids( $ids, apply_filters_deprecated( 'locked_payment_methods_for_woocommerce/lock_manager/locked_payment_methods', array( $ids, $gateways ), '2.0.0', 'dws_lpmwc/payment_method/locked_ids' ) ); - /** - * Filters the locked payment methods' ids, to which a callback adds the ids it locks; an id it leaves out stays locked. - * - * @since 2.0.0 - * @version 2.0.0 - * - * @param string[] $ids The locked payment methods' ids. - * @param array $gateways The payment gateways offered, by id. - */ - $ids = dws_lpmwc_add_locked_ids( $ids, apply_filters( 'dws_lpmwc/payment_method/locked_ids', $ids, $gateways ) ); // @phpstan-ignore WPCompat.parameterNotAvailable.applyfilters.args (WordPress 6.0 only formalized the arguments that older versions already passed on.) - $user_id = get_current_user_id(); $order_id = dws_lpmwc_get_paid_order_id(); foreach ( $ids as $id ) { From ae2e3f312e89ba2196d24d9379714849fc735b7f Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 11:05:26 +0100 Subject: [PATCH 49/50] Judge a locked method for a user without reading the list again The Users column and the grants choices asked dws_lpmwc_is_payment_method_locked() about ids they had just read from the locked list, so every row and payment method read the options and ran the list filters again, locked_ids callbacks included. Both call dws_lpmwc_should_lock() for the id they already know is locked, which judges it the same way. Assisted-by: Claude Code:claude-opus-5-5 --- .../includes/grants.php | 2 +- .../src/UnlockStrategies/UserProfileStrategy.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/locked-payment-methods-for-woocommerce/includes/grants.php b/plugins/locked-payment-methods-for-woocommerce/includes/grants.php index d56499c..93eebb0 100644 --- a/plugins/locked-payment-methods-for-woocommerce/includes/grants.php +++ b/plugins/locked-payment-methods-for-woocommerce/includes/grants.php @@ -114,7 +114,7 @@ function dws_lpmwc_get_grant_choices( array $granted, int $user_id ): array { if ( ! in_array( $id, $locked, true ) ) { /* translators: %s: payment method title. */ $title = sprintf( __( '%s (not locked)', 'locked-payment-methods-for-woocommerce' ), $title ); - } elseif ( 0 !== $user_id && ! in_array( $id, $granted, true ) && ! dws_lpmwc_is_payment_method_locked( $id, $user_id ) ) { + } elseif ( 0 !== $user_id && ! in_array( $id, $granted, true ) && ! dws_lpmwc_should_lock( true, $id, $user_id, 0 ) ) { /* translators: %s: payment method title. */ $title = sprintf( __( '%s (unlocked by another rule)', 'locked-payment-methods-for-woocommerce' ), $title ); } diff --git a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserProfileStrategy.php b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserProfileStrategy.php index afa89d9..e258854 100644 --- a/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserProfileStrategy.php +++ b/plugins/locked-payment-methods-for-woocommerce/src/UnlockStrategies/UserProfileStrategy.php @@ -192,9 +192,9 @@ public function render_users_column( string $output, string $column, int $user_i $lines = array(); foreach ( dws_lpmwc_get_locked_payment_method_ids() as $id ) { $state = match ( true ) { - \in_array( $id, $granted, true ) => __( 'unlocked on the profile', 'locked-payment-methods-for-woocommerce' ), - ! dws_lpmwc_is_payment_method_locked( $id, $user_id ) => __( 'unlocked by another rule', 'locked-payment-methods-for-woocommerce' ), - default => __( 'locked', 'locked-payment-methods-for-woocommerce' ), + \in_array( $id, $granted, true ) => __( 'unlocked on the profile', 'locked-payment-methods-for-woocommerce' ), + ! dws_lpmwc_should_lock( true, $id, $user_id, 0 ) => __( 'unlocked by another rule', 'locked-payment-methods-for-woocommerce' ), + default => __( 'locked', 'locked-payment-methods-for-woocommerce' ), }; /* translators: 1: payment method title, 2: the payment method's state for the user. */ $lines[] = esc_html( \sprintf( __( '%1$s: %2$s', 'locked-payment-methods-for-woocommerce' ), dws_lpmwc_get_payment_method_title( $id ), $state ) ); From aa509c6cb7dbba0224c934b95ff8ec4927122cce Mon Sep 17 00:00:00 2001 From: Tony Hegyes Date: Sun, 1 Nov 2026 11:05:26 +0100 Subject: [PATCH 50/50] Check that the plugin booted in the underscore include row The row read "skipped" from a fresh request whenever the constant the underscore include defines stayed undefined, which also happens when the plugin never loads or boots in that request or the include was never written, so the row could pass without testing the loader. The row checks the write and the request's exit status, and prints "not booted" unless the plugin's boot succeeded. Assisted-by: Claude Code:claude-opus-5-5 --- .../tests/Integration/PluginBootTest.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php index 077adae..f580892 100644 --- a/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php +++ b/plugins/locked-payment-methods-for-woocommerce/tests/Integration/PluginBootTest.php @@ -33,13 +33,14 @@ public function test_the_entry_loads_every_include_but_those_whose_names_start_w $includes = \dirname( DWS_LPMWC_FILE ) . '/includes/'; self::assertContains( $includes . 'index.php', \get_included_files() ); - \file_put_contents( $includes . '_lpmwc-skipped.php', "