2026-04-10

- corvée: met à jour les deps
- corvée: formate
This commit is contained in:
gcch 2026-04-10 17:21:57 +02:00
commit b2a0012590
85 changed files with 132083 additions and 31328 deletions

View file

@ -73,7 +73,11 @@ return new Config()
'multiline_comment_opening_closing' => true,
'native_constant_invocation' => true,
'native_function_casing' => true,
'native_function_invocation' => ['include' => ['@compiler_optimized'], 'scope' => 'namespaced', 'strict' => true],
'native_function_invocation' => [
'include' => ['@compiler_optimized'],
'scope' => 'namespaced',
'strict' => true,
],
'native_type_declaration_casing' => true,
'new_expression_parentheses' => true,
'no_alias_functions' => ['sets' => ['@all']],

View file

@ -1,9 +1,9 @@
{
"$schema": "./phpactor.schema.json",
"indexer.exclude_patterns": [
"/vendor/**/Tests/**/*",
"/vendor/**/tests/**/*",
"/var/cache/**/*",
"/vendor/**/tests/**/*",
"/vendor/**/Tests/**/*",
"/vendor/composer/**/*"
],
"language_server.diagnostic_outsource_timeout": 5,

View file

@ -15,9 +15,9 @@
"initialization_options": {
"settings": {
"configPath": "cfg/oxlint.config.ts",
"run": "onType",
"disableNestedConfig": false,
"fixKind": "safe_fix",
"run": "onType",
"unusedDisableDirectives": "deny"
}
}

198
bun.lock
View file

@ -15,7 +15,7 @@
"valibot": "1.1.0",
},
"devDependencies": {
"@effect/language-service": "^0.84.3",
"@effect/language-service": "^0.85.0",
"@gcch/configuration-eslint": "git+https://git.gcch.fr/gcch/configuration-eslint#62ee424274",
"@gcch/configuration-oxlint": "git+https://git.gcch.fr/gcch/configuration-oxlint#0968f683",
"@gcch/configuration-prettier": "git+https://git.gcch.fr/gcch/configuration-prettier#8de937e801",
@ -54,7 +54,7 @@
"stylelint-plugin-logical-css": "^2.1.0",
"typescript": "6.0.2",
"typescript-eslint": "^8.58.1",
"vite": "^8.0.7",
"vite": "^8.0.8",
"vite-tsconfig-paths": "^6.1.1",
},
},
@ -266,13 +266,13 @@
"@csstools/selector-specificity": ["@csstools/selector-specificity@6.0.0", "", { "peerDependencies": { "postcss-selector-parser": "^7.1.1" } }, "sha512-4sSgl78OtOXEX/2d++8A83zHNTgwCJMaR24FvsYL7Uf/VS8HZk9PTwR51elTbGqMuwH3szLvvOXEaVnqn0Z3zA=="],
"@effect/language-service": ["@effect/language-service@0.84.3", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-zpxi6rLCwst/cBQd7ElwDvt36Y6Jvz8v6bCLnNiOL6OXvdLmqjOFWyzWZdMh92vvBQA/aVKhfIAAOP3o4wKt0A=="],
"@effect/language-service": ["@effect/language-service@0.85.1", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-EXnJjIy6zQ3nUO/MZ+ynWUb8B895KZPotd1++oTs9JjDkplwM7cb6zo8Zq2zU6piwq+KflO7amXbEfj1UMpHkw=="],
"@emnapi/core": ["@emnapi/core@1.9.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" } }, "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA=="],
"@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="],
"@emnapi/runtime": ["@emnapi/runtime@1.9.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA=="],
"@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="],
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg=="],
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="],
"@es-joy/jsdoccomment": ["@es-joy/jsdoccomment@0.86.0", "", { "dependencies": { "@types/estree": "^1.0.8", "@typescript-eslint/types": "^8.58.0", "comment-parser": "1.4.6", "esquery": "^1.7.0", "jsdoc-type-pratt-parser": "~7.2.0" } }, "sha512-ukZmRQ81WiTpDWO6D/cTBM7XbrNtutHKvAVnZN/8pldAwLoJArGOvkNyxPTBGsPjsoaQBJxlH+tE2TNA/92Qgw=="],
@ -282,17 +282,17 @@
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="],
"@eslint/config-array": ["@eslint/config-array@0.23.4", "", { "dependencies": { "@eslint/object-schema": "^3.0.4", "debug": "^4.3.1", "minimatch": "^10.2.4" } }, "sha512-lf19F24LSMfF8weXvW5QEtnLqW70u7kgit5e9PSx0MsHAFclGd1T9ynvWEMDT1w5J4Qt54tomGeAhdoAku1Xow=="],
"@eslint/config-array": ["@eslint/config-array@0.23.5", "", { "dependencies": { "@eslint/object-schema": "^3.0.5", "debug": "^4.3.1", "minimatch": "^10.2.4" } }, "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA=="],
"@eslint/config-helpers": ["@eslint/config-helpers@0.5.4", "", { "dependencies": { "@eslint/core": "^1.2.0" } }, "sha512-jJhqiY3wPMlWWO3370M86CPJ7pt8GmEwSLglMfQhjXal07RCvhmU0as4IuUEW5SJeunfItiEetHmSxCCe9lDBg=="],
"@eslint/config-helpers": ["@eslint/config-helpers@0.5.5", "", { "dependencies": { "@eslint/core": "^1.2.1" } }, "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w=="],
"@eslint/core": ["@eslint/core@1.2.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-8FTGbNzTvmSlc4cZBaShkC6YvFMG0riksYWRFKXztqVdXaQbcZLXlFbSpC05s70sGEsXAw0qwhx69JiW7hQS7A=="],
"@eslint/core": ["@eslint/core@1.2.1", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ=="],
"@eslint/js": ["@eslint/js@10.0.1", "", { "peerDependencies": { "eslint": "^10.0.0" }, "optionalPeers": ["eslint"] }, "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA=="],
"@eslint/object-schema": ["@eslint/object-schema@3.0.4", "", {}, "sha512-55lO/7+Yp0ISKRP0PsPtNTeNGapXaO085aELZmWCVc5SH3jfrqpuU6YgOdIxMS99ZHkQN1cXKE+cdIqwww9ptw=="],
"@eslint/object-schema": ["@eslint/object-schema@3.0.5", "", {}, "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw=="],
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.7.0", "", { "dependencies": { "@eslint/core": "^1.2.0", "levn": "^0.4.1" } }, "sha512-ejvBr8MQCbVsWNZnCwDXjUKq40MDmHalq7cJ6e9s/qzTUFIIo/afzt1Vui9T97FM/V/pN4YsFVoed5NIa96RDg=="],
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.7.1", "", { "dependencies": { "@eslint/core": "^1.2.1", "levn": "^0.4.1" } }, "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ=="],
"@gcch/configuration-eslint": ["@gcch/configuration-eslint@git+https://git.gcch.fr/gcch/configuration-eslint#62ee424274f0bfebd5135a728960644f4b1cdcb8", { "dependencies": { "@eslint/js": "^10.0.1", "astro-eslint-parser": "^1.3.0", "eslint": "^10.0.3", "eslint-plugin-astro": "^1.6.0", "eslint-plugin-functional": "^9.0.4", "eslint-plugin-jsdoc": "^62.8.0", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-perfectionist": "^5.6.0", "eslint-plugin-sonarjs": "^4.0.2", "eslint-plugin-unicorn": "^63.0.0", "globals": "^17.4.0", "typescript-eslint": "^8.57.0" }, "peerDependencies": { "eslint": "^10.0.3", "typescript": "^6.0.1-rc" } }, "62ee424274f0bfebd5135a728960644f4b1cdcb8"],
@ -338,7 +338,7 @@
"@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ=="],
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.2", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw=="],
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.3", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ=="],
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
@ -512,49 +512,49 @@
"@reteps/dockerfmt": ["@reteps/dockerfmt@0.5.2", "", {}, "sha512-Hbr7yen4fP5TxGM54ucXa4o5NwWXatJ6Bd9I8gp0PValYbI4Rug2Gu+rVv7K7o/efQc3F5ctqWJz47rYaa8zBw=="],
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.13", "", { "os": "android", "cpu": "arm64" }, "sha512-5ZiiecKH2DXAVJTNN13gNMUcCDg4Jy8ZjbXEsPnqa248wgOVeYRX0iqXXD5Jz4bI9BFHgKsI2qmyJynstbmr+g=="],
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.15", "", { "os": "android", "cpu": "arm64" }, "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA=="],
"@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.13", "", { "os": "darwin", "cpu": "arm64" }, "sha512-tz/v/8G77seu8zAB3A5sK3UFoOl06zcshEzhUO62sAEtrEuW/H1CcyoupOrD+NbQJytYgA4CppXPzlrmp4JZKA=="],
"@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg=="],
"@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-rc.13", "", { "os": "darwin", "cpu": "x64" }, "sha512-8DakphqOz8JrMYWTJmWA+vDJxut6LijZ8Xcdc4flOlAhU7PNVwo2MaWBF9iXjJAPo5rC/IxEFZDhJ3GC7NHvug=="],
"@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-rc.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw=="],
"@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-rc.13", "", { "os": "freebsd", "cpu": "x64" }, "sha512-4wBQFfjDuXYN/SVI8inBF3Aa+isq40rc6VMFbk5jcpolUBTe5cYnMsHZ51nFWsx3PVyyNN3vgoESki0Hmr/4BA=="],
"@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-rc.15", "", { "os": "freebsd", "cpu": "x64" }, "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw=="],
"@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.13", "", { "os": "linux", "cpu": "arm" }, "sha512-JW/e4yPIXLms+jmnbwwy5LA/LxVwZUWLN8xug+V200wzaVi5TEGIWQlh8o91gWYFxW609euI98OCCemmWGuPrw=="],
"@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.15", "", { "os": "linux", "cpu": "arm" }, "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA=="],
"@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-rc.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-ZfKWpXiUymDnavepCaM6KG/uGydJ4l2nBmMxg60Ci4CbeefpqjPWpfaZM7PThOhk2dssqBAcwLc6rAyr0uTdXg=="],
"@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w=="],
"@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-rc.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-bmRg3O6Z0gq9yodKKWCIpnlH051sEfdVwt+6m5UDffAQMUUqU0xjnQqqAUm+Gu7ofAAly9DqiQDtKu2nPDEABA=="],
"@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-rc.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ=="],
"@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.13", "", { "os": "linux", "cpu": "ppc64" }, "sha512-8Wtnbw4k7pMYN9B/mOEAsQ8HOiq7AZ31Ig4M9BKn2So4xRaFEhtCSa4ZJaOutOWq50zpgR4N5+L/opnlaCx8wQ=="],
"@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "ppc64" }, "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ=="],
"@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0-rc.13", "", { "os": "linux", "cpu": "s390x" }, "sha512-D/0Nlo8mQuxSMohNJUF2lDXWRsFDsHldfRRgD9bRgktj+EndGPj4DOV37LqDKPYS+osdyhZEH7fTakTAEcW7qg=="],
"@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "s390x" }, "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ=="],
"@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-rc.13", "", { "os": "linux", "cpu": "x64" }, "sha512-eRrPvat2YaVQcwwKi/JzOP6MKf1WRnOCr+VaI3cTWz3ZoLcP/654z90lVCJ4dAuMEpPdke0n+qyAqXDZdIC4rA=="],
"@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "x64" }, "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA=="],
"@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-rc.13", "", { "os": "linux", "cpu": "x64" }, "sha512-PsdONiFRp8hR8KgVjTWjZ9s7uA3uueWL0t74/cKHfM4dR5zXYv4AjB8BvA+QDToqxAFg4ZkcVEqeu5F7inoz5w=="],
"@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-rc.15", "", { "os": "linux", "cpu": "x64" }, "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw=="],
"@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-rc.13", "", { "os": "none", "cpu": "arm64" }, "sha512-hCNXgC5dI3TVOLrPT++PKFNZ+1EtS0mLQwfXXXSUD/+rGlB65gZDwN/IDuxLpQP4x8RYYHqGomlUXzpO8aVI2w=="],
"@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-rc.15", "", { "os": "none", "cpu": "arm64" }, "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg=="],
"@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-rc.13", "", { "dependencies": { "@emnapi/core": "1.9.1", "@emnapi/runtime": "1.9.1", "@napi-rs/wasm-runtime": "^1.1.2" }, "cpu": "none" }, "sha512-viLS5C5et8NFtLWw9Sw3M/w4vvnVkbWkO7wSNh3C+7G1+uCkGpr6PcjNDSFcNtmXY/4trjPBqUfcOL+P3sWy/g=="],
"@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-rc.15", "", { "dependencies": { "@emnapi/core": "1.9.2", "@emnapi/runtime": "1.9.2", "@napi-rs/wasm-runtime": "^1.1.3" }, "cpu": "none" }, "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q=="],
"@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-rc.13", "", { "os": "win32", "cpu": "arm64" }, "sha512-Fqa3Tlt1xL4wzmAYxGNFV36Hb+VfPc9PYU+E25DAnswXv3ODDu/yyWjQDbXMo5AGWkQVjLgQExuVu8I/UaZhPQ=="],
"@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-rc.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA=="],
"@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-rc.13", "", { "os": "win32", "cpu": "x64" }, "sha512-/pLI5kPkGEi44TDlnbio3St/5gUFeN51YWNAk/Gnv6mEQBOahRBh52qVFVBpmrnU01n2yysvBML9Ynu7K4kGAQ=="],
"@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-rc.15", "", { "os": "win32", "cpu": "x64" }, "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g=="],
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.13", "", {}, "sha512-3ngTAv6F/Py35BsYbeeLeecvhMKdsKm4AoOETVhAA+Qc8nrA2I0kF7oa93mE9qnIurngOSpMnQ0x2nQY2FPviA=="],
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.15", "", {}, "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g=="],
"@sentry-internal/browser-utils": ["@sentry-internal/browser-utils@10.47.0", "", { "dependencies": { "@sentry/core": "10.47.0" } }, "sha512-bVFRAeJWMBcBCvJKIFCMJ1/yQToL4vPGqfmlnDZeypcxkqUDKQ/Y3ziLHXoDL2sx0lagcgU2vH1QhCQ67Aujjw=="],
"@sentry-internal/browser-utils": ["@sentry-internal/browser-utils@10.48.0", "", { "dependencies": { "@sentry/core": "10.48.0" } }, "sha512-SCiTLBXzugFKxev6NoKYBIhQoDk0gUh0AVVVepCBqfCJiWBG01Zvv0R5tCVohr4cWRllkQ8mlBdNQd/I7s9tdA=="],
"@sentry-internal/feedback": ["@sentry-internal/feedback@10.47.0", "", { "dependencies": { "@sentry/core": "10.47.0" } }, "sha512-pdvMmi4dQpX5S/vAAzrhHPIw3T3HjUgDNgUiCBrlp7N9/6zGO2gNPhUnNekP+CjgI/z0rvf49RLqlDenpNrMOg=="],
"@sentry-internal/feedback": ["@sentry-internal/feedback@10.48.0", "", { "dependencies": { "@sentry/core": "10.48.0" } }, "sha512-tGkEyOM1HDS9qebDphUMEnyk3qq/50AnuTBiFmMJyjNzowylVGmRRk0sr3xkmbVHCDXQCiYnDmSVlJ2x4SDMrQ=="],
"@sentry-internal/replay": ["@sentry-internal/replay@10.47.0", "", { "dependencies": { "@sentry-internal/browser-utils": "10.47.0", "@sentry/core": "10.47.0" } }, "sha512-ScdovxP7hJxgMt70+7hFvwT02GIaIUAxdEM/YPsayZBeCoAukPW8WiwztJfoKtsfPyKJ5A6f0H3PIxTPcA9Row=="],
"@sentry-internal/replay": ["@sentry-internal/replay@10.48.0", "", { "dependencies": { "@sentry-internal/browser-utils": "10.48.0", "@sentry/core": "10.48.0" } }, "sha512-sevRTePfuk4PNuz9KAKpmTZEomAU0aLXyIhOwA0OnUDdxPhkY8kq5lwDbuxTHv6DQUjUX3YgFbY45VH1JEqHKA=="],
"@sentry-internal/replay-canvas": ["@sentry-internal/replay-canvas@10.47.0", "", { "dependencies": { "@sentry-internal/replay": "10.47.0", "@sentry/core": "10.47.0" } }, "sha512-A5OY8friSe6g8WAK4L8IeOPiEd9D3Ps40DzRH5j2f6SUja0t90mKMvHRcRf8zq0d4BkdB+JM7tjOkwxpuv8heA=="],
"@sentry-internal/replay-canvas": ["@sentry-internal/replay-canvas@10.48.0", "", { "dependencies": { "@sentry-internal/replay": "10.48.0", "@sentry/core": "10.48.0" } }, "sha512-9nWuN2z4O+iwbTfuYV5ZmngBgJU/ZxfOo47A5RJP3Nu/kl59aJ1lUhILYOKyeNOIC/JyeERmpIcTxnlPXQzZ3Q=="],
"@sentry/browser": ["@sentry/browser@10.47.0", "", { "dependencies": { "@sentry-internal/browser-utils": "10.47.0", "@sentry-internal/feedback": "10.47.0", "@sentry-internal/replay": "10.47.0", "@sentry-internal/replay-canvas": "10.47.0", "@sentry/core": "10.47.0" } }, "sha512-rC0agZdxKA5XWfL4VwPOr/rJMogXDqZgnVzr93YWpFn9DMZT/7LzxSJVPIJwRUjx3bFEby3PcTa3YaX7pxm1AA=="],
"@sentry/browser": ["@sentry/browser@10.48.0", "", { "dependencies": { "@sentry-internal/browser-utils": "10.48.0", "@sentry-internal/feedback": "10.48.0", "@sentry-internal/replay": "10.48.0", "@sentry-internal/replay-canvas": "10.48.0", "@sentry/core": "10.48.0" } }, "sha512-4jt2zX2ExgFcNe2x+W+/k81fmDUsOrquGtt028CiGuDuma6kEsWBI4JbooT1jhj2T+eeUxe3YGbM23Zhh7Ghhw=="],
"@sentry/core": ["@sentry/core@10.47.0", "", {}, "sha512-nsYRAx3EWezDut+Zl+UwwP07thh9uY7CfSAi2whTdcJl5hu1nSp2z8bba7Vq/MGbNLnazkd3A+GITBEML924JA=="],
"@sentry/core": ["@sentry/core@10.48.0", "", {}, "sha512-h8F+fXVwYC9ro5ZaO8V+v3vqc0awlXHGblEAuVxSGgh4IV/oFX+QVzXeDTTrFOFS6v/Vn5vAyu240eJrJAS6/g=="],
"@sindresorhus/base62": ["@sindresorhus/base62@1.0.0", "", {}, "sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA=="],
@ -566,7 +566,7 @@
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
"@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
"@types/bun": ["@types/bun@1.3.12", "", { "dependencies": { "bun-types": "1.3.12" } }, "sha512-DBv81elK+/VSwXHDlnH3Qduw+KxkTIWi7TXkAeh24zpi5l0B2kUg9Ga3tb4nJaPcOFswflgi/yAvMVBPrxMB+A=="],
"@types/debug": ["@types/debug@4.1.13", "", { "dependencies": { "@types/ms": "*" } }, "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw=="],
@ -580,7 +580,7 @@
"@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
"@types/node": ["@types/node@25.5.2", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg=="],
"@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="],
"@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="],
@ -592,17 +592,17 @@
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.58.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.58.1", "@typescript-eslint/types": "^8.58.1", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-gfQ8fk6cxhtptek+/8ZIqw8YrRW5048Gug8Ts5IYcMLCw18iUgrZAEY/D7s4hkI0FxEfGakKuPK/XUMPzPxi5g=="],
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.58.0", "", { "dependencies": { "@typescript-eslint/types": "8.58.0", "@typescript-eslint/visitor-keys": "8.58.0" } }, "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ=="],
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.58.1", "", { "dependencies": { "@typescript-eslint/types": "8.58.1", "@typescript-eslint/visitor-keys": "8.58.1" } }, "sha512-TPYUEqJK6avLcEjumWsIuTpuYODTTDAtoMdt8ZZa93uWMTX13Nb8L5leSje1NluammvU+oI3QRr5lLXPgihX3w=="],
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.58.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-JAr2hOIct2Q+qk3G+8YFfqkqi7sC86uNryT+2i5HzMa2MPjw4qNFvtjnw1IiA1rP7QhNKVe21mSSLaSjwA1Olw=="],
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.58.0", "", { "dependencies": { "@typescript-eslint/types": "8.58.0", "@typescript-eslint/typescript-estree": "8.58.0", "@typescript-eslint/utils": "8.58.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg=="],
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.58.1", "", { "dependencies": { "@typescript-eslint/types": "8.58.1", "@typescript-eslint/typescript-estree": "8.58.1", "@typescript-eslint/utils": "8.58.1", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-HUFxvTJVroT+0rXVJC7eD5zol6ID+Sn5npVPWoFuHGg9Ncq5Q4EYstqR+UOqaNRFXi5TYkpXXkLhoCHe3G0+7w=="],
"@typescript-eslint/types": ["@typescript-eslint/types@8.58.0", "", {}, "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww=="],
"@typescript-eslint/types": ["@typescript-eslint/types@8.58.1", "", {}, "sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw=="],
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.58.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.58.1", "@typescript-eslint/tsconfig-utils": "8.58.1", "@typescript-eslint/types": "8.58.1", "@typescript-eslint/visitor-keys": "8.58.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-w4w7WR7GHOjqqPnvAYbazq+Y5oS68b9CzasGtnd6jIeOIeKUzYzupGTB2T4LTPSv4d+WPeccbxuneTFHYgAAWg=="],
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.58.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.58.0", "@typescript-eslint/types": "8.58.0", "@typescript-eslint/typescript-estree": "8.58.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA=="],
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.58.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.58.1", "@typescript-eslint/types": "8.58.1", "@typescript-eslint/typescript-estree": "8.58.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-Ln8R0tmWC7pTtLOzgJzYTXSCjJ9rDNHAqTaVONF4FEi2qwce8mD9iSOxOpLFFvWp/wBFlew0mjM1L1ihYWfBdQ=="],
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.58.1", "", { "dependencies": { "@typescript-eslint/types": "8.58.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-y+vH7QE8ycjoa0bWciFg7OpFcipUuem1ujhrdLtq1gByKwfbC7bPeKsiny9e0urg93DqwGcHey+bGRKCnF1nZQ=="],
@ -694,7 +694,7 @@
"balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.13", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-BL2sTuHOdy0YT1lYieUxTw/QMtPBC3pmlJC6xk8BBYVv6vcw3SGdKemQ+Xsx9ik2F/lYDO9tqsFQH1r9PFuHKw=="],
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.17", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-HdrkN8eVG2CXxeifv/VdJ4A4RSra1DTW8dc/hdxzhGHN8QePs6gKaWM9pHPcpCoxYZJuOZ8drHmbdpLHjCYjLA=="],
"better-typescript-lib": ["better-typescript-lib@2.12.0", "", { "dependencies": { "@typescript/lib-decorators": "npm:@better-typescript-lib/decorators@2.12.0", "@typescript/lib-dom": "npm:@better-typescript-lib/dom@2.12.0", "@typescript/lib-es2015": "npm:@better-typescript-lib/es2015@2.12.0", "@typescript/lib-es2016": "npm:@better-typescript-lib/es2016@2.12.0", "@typescript/lib-es2017": "npm:@better-typescript-lib/es2017@2.12.0", "@typescript/lib-es2018": "npm:@better-typescript-lib/es2018@2.12.0", "@typescript/lib-es2019": "npm:@better-typescript-lib/es2019@2.12.0", "@typescript/lib-es2020": "npm:@better-typescript-lib/es2020@2.12.0", "@typescript/lib-es2021": "npm:@better-typescript-lib/es2021@2.12.0", "@typescript/lib-es2022": "npm:@better-typescript-lib/es2022@2.12.0", "@typescript/lib-es2023": "npm:@better-typescript-lib/es2023@2.12.0", "@typescript/lib-es2024": "npm:@better-typescript-lib/es2024@2.12.0", "@typescript/lib-es5": "npm:@better-typescript-lib/es5@2.12.0", "@typescript/lib-es6": "npm:@better-typescript-lib/es6@2.12.0", "@typescript/lib-esnext": "npm:@better-typescript-lib/esnext@2.12.0", "@typescript/lib-scripthost": "npm:@better-typescript-lib/scripthost@2.12.0", "@typescript/lib-webworker": "npm:@better-typescript-lib/webworker@2.12.0" }, "peerDependencies": { "typescript": ">=4.5.2" } }, "sha512-f7eO5Xs6Cczfx5eDRuDw/JYCrsdiC6gXdleB2KFZ5ZYgU/RRoev9swjt/eD0xo9PRDqNDwyjKx0n27CDHRZwvQ=="],
@ -712,13 +712,13 @@
"builtin-modules": ["builtin-modules@3.3.0", "", {}, "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw=="],
"bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
"bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="],
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
"cacheable": ["cacheable@2.3.4", "", { "dependencies": { "@cacheable/memory": "^2.0.8", "@cacheable/utils": "^2.4.0", "hookified": "^1.15.0", "keyv": "^5.6.0", "qified": "^0.9.0" } }, "sha512-djgxybDbw9fL/ZWMI3+CE8ZilNxcwFkVtDc1gJ+IlOSSWkSMPQabhV/XCHTQ6pwwN6aivXPZ43omTooZiX06Ew=="],
"call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="],
"call-bind": ["call-bind@1.0.9", "", { "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" } }, "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ=="],
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
@ -796,9 +796,9 @@
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
"effect": ["effect@4.0.0-beta.43", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.5.3", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.8", "multipasta": "^0.2.7", "toml": "^3.0.0", "uuid": "^13.0.0", "yaml": "^2.8.2" } }, "sha512-AJYyDimIwJOn87uUz/JzmgDc5GfjxJbXvEbTvNzMa+M3Uer344bLo/O5mMRkqc1vBleA+Ygs4+dbE3QsqOkKTQ=="],
"effect": ["effect@4.0.0-beta.46", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.5.3", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.8", "multipasta": "^0.2.7", "toml": "^3.0.0", "uuid": "^13.0.0", "yaml": "^2.8.2" } }, "sha512-3f6gXvvUMtEueCRY0tU76Vq2Pej1SAwwE+s0Owd5nD53yS5n4RZhUA1rlCGFuSbQFA225pGy8vO72+lpvu7u5A=="],
"electron-to-chromium": ["electron-to-chromium@1.5.331", "", {}, "sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q=="],
"electron-to-chromium": ["electron-to-chromium@1.5.334", "", {}, "sha512-mgjZAz7Jyx1SRCwEpy9wefDS7GvNPazLthHg8eQMJ76wBdGQQDW33TCrUTvQ4wzpmOrv2zrFoD3oNufMdyMpog=="],
"emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
@ -808,7 +808,7 @@
"error-ex": ["error-ex@1.3.4", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ=="],
"es-abstract": ["es-abstract@1.24.1", "", { "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" } }, "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw=="],
"es-abstract": ["es-abstract@1.24.2", "", { "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" } }, "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg=="],
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
@ -830,7 +830,7 @@
"eslint-compat-utils": ["eslint-compat-utils@0.6.5", "", { "dependencies": { "semver": "^7.5.4" }, "peerDependencies": { "eslint": ">=6.0.0" } }, "sha512-vAUHYzue4YAa2hNACjB8HvUQj5yehAZgiClyFVVom9cP8z5NSFq3PwB/TtJslN2zAMgRX6FCFCjYBbQh71g5RQ=="],
"eslint-plugin-astro": ["eslint-plugin-astro@1.6.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@jridgewell/sourcemap-codec": "^1.4.14", "@typescript-eslint/types": "^7.7.1 || ^8", "astro-eslint-parser": "^1.3.0", "eslint-compat-utils": "^0.6.0", "globals": "^16.0.0", "postcss": "^8.4.14", "postcss-selector-parser": "^7.0.0" }, "peerDependencies": { "eslint": ">=8.57.0" } }, "sha512-yGIbLHuj5MOUXa0s4sZ6cVhv6ehb+WLF80tsrGaxMk6VTUExruMzubQDzhOYt8fbR1c9vILCCRSCsKI7M1whig=="],
"eslint-plugin-astro": ["eslint-plugin-astro@1.7.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@jridgewell/sourcemap-codec": "^1.4.14", "@typescript-eslint/types": "^7.7.1 || ^8", "astro-eslint-parser": "^1.3.0", "eslint-compat-utils": "^0.6.0", "globals": "^16.0.0", "postcss": "^8.4.14", "postcss-selector-parser": "^7.0.0" }, "peerDependencies": { "eslint": ">=8.57.0" } }, "sha512-89xpAn528UKCdmyysbg0AHHqi6sqcK89wXnJIpu4F0mFBN03zATEBNK7pRtMfl6gwtMOm5ECXs+Wz5qDHhwTFw=="],
"eslint-plugin-functional": ["eslint-plugin-functional@9.0.4", "", { "dependencies": { "@typescript-eslint/utils": "^8.26.0", "deepmerge-ts": "^7.1.5", "escape-string-regexp": "^5.0.0", "is-immutable-type": "^5.0.1", "ts-api-utils": "^2.0.1", "ts-declaration-location": "^1.0.6" }, "peerDependencies": { "eslint": "^9.0.0 || ^10.0.0", "typescript": ">=4.7.4" }, "optionalPeers": ["typescript"] }, "sha512-zm4qaoqb2r50V4WXxt0Mj92buXGMECYvMxGQ6sSb+XeJ+Eec6zCHuMY2+AWK1mqiApvUz2tCtp1P3zcEPU0huw=="],
@ -1290,7 +1290,7 @@
"possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="],
"postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="],
"postcss": ["postcss@8.5.9", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw=="],
"postcss-media-query-parser": ["postcss-media-query-parser@0.2.3", "", {}, "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig=="],
@ -1308,7 +1308,7 @@
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
"prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="],
"prettier": ["prettier@3.8.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-8c3mgTe0ASwWAJK+78dpviD+A8EqhndQPUBpNUIPt6+xWlIigCwfN01lWr9MAede4uqXGTEKeQWTvzb3vjia0Q=="],
"prettier-plugin-curly": ["prettier-plugin-curly@0.4.1", "", { "peerDependencies": { "prettier": "^3" } }, "sha512-Xc7zatoD0/08zYFv+hwnlqT5ekM81DCbBr73CWAsr1Fmx7qLQT/M0wfPx6w/+zfnmXH009xYvjzLUPcwzq7Fbw=="],
@ -1354,7 +1354,7 @@
"regjsgen": ["regjsgen@0.8.0", "", {}, "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q=="],
"regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="],
"regjsparser": ["regjsparser@0.13.1", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw=="],
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
@ -1368,7 +1368,7 @@
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
"rolldown": ["rolldown@1.0.0-rc.13", "", { "dependencies": { "@oxc-project/types": "=0.123.0", "@rolldown/pluginutils": "1.0.0-rc.13" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.13", "@rolldown/binding-darwin-arm64": "1.0.0-rc.13", "@rolldown/binding-darwin-x64": "1.0.0-rc.13", "@rolldown/binding-freebsd-x64": "1.0.0-rc.13", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.13", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.13", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.13", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.13", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.13", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.13", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.13", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.13", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.13", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.13", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.13" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-bvVj8YJmf0rq4pSFmH7laLa6pYrhghv3PRzrCdRAr23g66zOKVJ4wkvFtgohtPLWmthgg8/rkaqRHrpUEh0Zbw=="],
"rolldown": ["rolldown@1.0.0-rc.15", "", { "dependencies": { "@oxc-project/types": "=0.124.0", "@rolldown/pluginutils": "1.0.0-rc.15" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.15", "@rolldown/binding-darwin-arm64": "1.0.0-rc.15", "@rolldown/binding-darwin-x64": "1.0.0-rc.15", "@rolldown/binding-freebsd-x64": "1.0.0-rc.15", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.15", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.15", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.15", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g=="],
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
@ -1438,7 +1438,7 @@
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
"side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="],
"side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="],
"side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="],
@ -1490,7 +1490,7 @@
"stylelint-config-recommended": ["stylelint-config-recommended@18.0.0", "", { "peerDependencies": { "stylelint": "^17.0.0" } }, "sha512-mxgT2XY6YZ3HWWe3Di8umG6aBmWmHTblTgu/f10rqFXnyWxjKWwNdjSWkgkwCtxIKnqjSJzvFmPT5yabVIRxZg=="],
"stylelint-config-recommended-scss": ["stylelint-config-recommended-scss@17.0.0", "", { "dependencies": { "postcss-scss": "^4.0.9", "stylelint-config-recommended": "^18.0.0", "stylelint-scss": "^7.0.0" }, "peerDependencies": { "postcss": "^8.3.3", "stylelint": "^17.0.0" }, "optionalPeers": ["postcss"] }, "sha512-VkVD9r7jfUT/dq3mA3/I1WXXk2U71rO5wvU2yIil9PW5o1g3UM7Xc82vHmuVJHV7Y8ok5K137fmW5u3HbhtTOA=="],
"stylelint-config-recommended-scss": ["stylelint-config-recommended-scss@17.0.1", "", { "dependencies": { "postcss-scss": "^4.0.9", "stylelint-config-recommended": "^18.0.0", "stylelint-scss": "^7.0.0" }, "peerDependencies": { "postcss": "^8.3.3", "stylelint": "^17.0.0" }, "optionalPeers": ["postcss"] }, "sha512-x5DVehzJudcwF0od3sGpgkln2PLLranFE7twwbp7dqDINCyZvwzFkMc6TLhNOvazRiVBJYATQLouJY0xPGB8WA=="],
"stylelint-config-sass-guidelines": ["stylelint-config-sass-guidelines@13.0.0", "", { "dependencies": { "@stylistic/stylelint-plugin": "^5.0.1", "postcss-scss": "^4.0.9", "stylelint-scss": "^7.0.0" }, "peerDependencies": { "postcss": "^8.4.21", "stylelint": "^17.1.0" } }, "sha512-YJT0X8h0OqyEo7ys3EycV5CGWt2rrkEYE8sHSN6sFnrxbXvHiy4KJFHDOWfyb3eWR6wtYpM+yIyvR2Plc8+pCg=="],
@ -1526,7 +1526,7 @@
"terser": ["terser@5.46.1", "", { "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" } }, "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ=="],
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
"tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="],
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
@ -1562,7 +1562,7 @@
"unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="],
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
"undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="],
"unicode-canonical-property-names-ecmascript": ["unicode-canonical-property-names-ecmascript@2.0.1", "", {}, "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg=="],
@ -1588,7 +1588,7 @@
"varint": ["varint@6.0.0", "", {}, "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg=="],
"vite": ["vite@8.0.7", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.8", "rolldown": "1.0.0-rc.13", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-P1PbweD+2/udplnThz3btF4cf6AgPky7kk23RtHUkJIU5BIxwPprhRGmOAHs6FTI7UiGbTNrgNP6jSYD6JaRnw=="],
"vite": ["vite@8.0.8", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.8", "rolldown": "1.0.0-rc.15", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw=="],
"vite-tsconfig-paths": ["vite-tsconfig-paths@6.1.1", "", { "dependencies": { "debug": "^4.1.1", "globrex": "^0.1.2", "tsconfck": "^3.0.3" }, "peerDependencies": { "vite": "*" } }, "sha512-2cihq7zliibCCZ8P9cKJrQBkfgdvcFkOOc3Y02o3GWUDLgqjWsZudaoiuOwO/gzTzy17cS5F7ZPo4bsnS4DGkg=="],
@ -1632,38 +1632,10 @@
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
"@gcch/configuration-eslint/eslint": ["eslint@10.1.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.3", "@eslint/config-helpers": "^0.5.3", "@eslint/core": "^1.1.1", "@eslint/plugin-kit": "^0.6.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", "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": "^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", "minimatch": "^10.2.4", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-S9jlY/ELKEUwwQnqWDO+f+m6sercqOPSqXM5Go94l7DOmxHVDgmSFGWEzeE/gwgTAr0W103BWt0QLe/7mabIvA=="],
"@gcch/configuration-eslint/typescript-eslint": ["typescript-eslint@8.58.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.58.0", "@typescript-eslint/parser": "8.58.0", "@typescript-eslint/typescript-estree": "8.58.0", "@typescript-eslint/utils": "8.58.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA=="],
"@gcch/configuration-prettier/prettier-plugin-sh": ["prettier-plugin-sh@0.18.0", "", { "dependencies": { "@reteps/dockerfmt": "^0.3.6", "sh-syntax": "^0.5.8" }, "peerDependencies": { "prettier": "^3.6.0" } }, "sha512-cW1XL27FOJQ/qGHOW6IHwdCiNWQsAgK+feA8V6+xUTaH0cD3Mh+tFAtBvEEWvuY6hTDzRV943Fzeii+qMOh7nQ=="],
"@keyv/bigmap/keyv": ["keyv@5.6.0", "", { "dependencies": { "@keyv/serialize": "^1.1.1" } }, "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw=="],
"@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.58.1", "", { "dependencies": { "@typescript-eslint/types": "8.58.1", "@typescript-eslint/visitor-keys": "8.58.1" } }, "sha512-TPYUEqJK6avLcEjumWsIuTpuYODTTDAtoMdt8ZZa93uWMTX13Nb8L5leSje1NluammvU+oI3QRr5lLXPgihX3w=="],
"@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.58.1", "", { "dependencies": { "@typescript-eslint/types": "8.58.1", "@typescript-eslint/typescript-estree": "8.58.1", "@typescript-eslint/utils": "8.58.1", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-HUFxvTJVroT+0rXVJC7eD5zol6ID+Sn5npVPWoFuHGg9Ncq5Q4EYstqR+UOqaNRFXi5TYkpXXkLhoCHe3G0+7w=="],
"@typescript-eslint/eslint-plugin/@typescript-eslint/utils": ["@typescript-eslint/utils@8.58.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.58.1", "@typescript-eslint/types": "8.58.1", "@typescript-eslint/typescript-estree": "8.58.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-Ln8R0tmWC7pTtLOzgJzYTXSCjJ9rDNHAqTaVONF4FEi2qwce8mD9iSOxOpLFFvWp/wBFlew0mjM1L1ihYWfBdQ=="],
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
"@typescript-eslint/parser/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.58.1", "", { "dependencies": { "@typescript-eslint/types": "8.58.1", "@typescript-eslint/visitor-keys": "8.58.1" } }, "sha512-TPYUEqJK6avLcEjumWsIuTpuYODTTDAtoMdt8ZZa93uWMTX13Nb8L5leSje1NluammvU+oI3QRr5lLXPgihX3w=="],
"@typescript-eslint/parser/@typescript-eslint/types": ["@typescript-eslint/types@8.58.1", "", {}, "sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw=="],
"@typescript-eslint/project-service/@typescript-eslint/types": ["@typescript-eslint/types@8.58.1", "", {}, "sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw=="],
"@typescript-eslint/scope-manager/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.58.0", "", { "dependencies": { "@typescript-eslint/types": "8.58.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ=="],
"@typescript-eslint/type-utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.58.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.58.0", "@typescript-eslint/tsconfig-utils": "8.58.0", "@typescript-eslint/types": "8.58.0", "@typescript-eslint/visitor-keys": "8.58.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA=="],
"@typescript-eslint/typescript-estree/@typescript-eslint/types": ["@typescript-eslint/types@8.58.1", "", {}, "sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw=="],
"@typescript-eslint/utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.58.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.58.0", "@typescript-eslint/tsconfig-utils": "8.58.0", "@typescript-eslint/types": "8.58.0", "@typescript-eslint/visitor-keys": "8.58.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA=="],
"@typescript-eslint/visitor-keys/@typescript-eslint/types": ["@typescript-eslint/types@8.58.1", "", {}, "sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw=="],
"astro-eslint-parser/eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="],
"astro-eslint-parser/eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="],
@ -1672,8 +1644,6 @@
"babel-plugin-polyfill-corejs2/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
"browserslist/caniuse-lite": ["caniuse-lite@1.0.30001784", "", {}, "sha512-WU346nBTklUV9YfUl60fqRbU5ZqyXlqvo1SgigE1OAXK5bFL8LL9q1K7aap3N739l4BvNqnkm3YrGHiY9sfUQw=="],
"browserslist-to-esbuild/meow": ["meow@13.2.0", "", {}, "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA=="],
"cacheable/keyv": ["keyv@5.6.0", "", { "dependencies": { "@keyv/serialize": "^1.1.1" } }, "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw=="],
@ -1702,7 +1672,7 @@
"qified/hookified": ["hookified@2.1.1", "", {}, "sha512-AHb76R16GB5EsPBE2J7Ko5kiEyXwviB9P5SMrAKcuAu4vJPZttViAbj9+tZeaQE5zjDme+1vcHP78Yj/WoAveA=="],
"rolldown/@oxc-project/types": ["@oxc-project/types@0.123.0", "", {}, "sha512-YtECP/y8Mj1lSHiUWGSRzy/C6teUKlS87dEfuVKT09LgQbUsBW1rNg+MiJ4buGu3yuADV60gbIvo9/HplA56Ew=="],
"rolldown/@oxc-project/types": ["@oxc-project/types@0.124.0", "", {}, "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg=="],
"stylelint/file-entry-cache": ["file-entry-cache@11.1.2", "", { "dependencies": { "flat-cache": "^6.1.20" } }, "sha512-N2WFfK12gmrK1c1GXOqiAJ1tc5YE+R53zvQ+t5P8S5XhnmKYVB5eZEiLNZKDSmoG8wqqbF9EXYBBW/nef19log=="],
@ -1718,44 +1688,8 @@
"table/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"typescript-eslint/@typescript-eslint/utils": ["@typescript-eslint/utils@8.58.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.58.1", "@typescript-eslint/types": "8.58.1", "@typescript-eslint/typescript-estree": "8.58.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-Ln8R0tmWC7pTtLOzgJzYTXSCjJ9rDNHAqTaVONF4FEi2qwce8mD9iSOxOpLFFvWp/wBFlew0mjM1L1ihYWfBdQ=="],
"vite/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
"@gcch/configuration-eslint/eslint/@eslint/config-array": ["@eslint/config-array@0.23.3", "", { "dependencies": { "@eslint/object-schema": "^3.0.3", "debug": "^4.3.1", "minimatch": "^10.2.4" } }, "sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw=="],
"@gcch/configuration-eslint/eslint/@eslint/config-helpers": ["@eslint/config-helpers@0.5.3", "", { "dependencies": { "@eslint/core": "^1.1.1" } }, "sha512-lzGN0onllOZCGroKJmRwY6QcEHxbjBw1gwB8SgRSqK8YbbtEXMvKynsXc3553ckIEBxsbMBU7oOZXKIPGZNeZw=="],
"@gcch/configuration-eslint/eslint/@eslint/core": ["@eslint/core@1.1.1", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ=="],
"@gcch/configuration-eslint/eslint/@eslint/plugin-kit": ["@eslint/plugin-kit@0.6.1", "", { "dependencies": { "@eslint/core": "^1.1.1", "levn": "^0.4.1" } }, "sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ=="],
"@gcch/configuration-eslint/typescript-eslint/@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.58.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.58.0", "@typescript-eslint/type-utils": "8.58.0", "@typescript-eslint/utils": "8.58.0", "@typescript-eslint/visitor-keys": "8.58.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.58.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg=="],
"@gcch/configuration-eslint/typescript-eslint/@typescript-eslint/parser": ["@typescript-eslint/parser@8.58.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.58.0", "@typescript-eslint/types": "8.58.0", "@typescript-eslint/typescript-estree": "8.58.0", "@typescript-eslint/visitor-keys": "8.58.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA=="],
"@gcch/configuration-eslint/typescript-eslint/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.58.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.58.0", "@typescript-eslint/tsconfig-utils": "8.58.0", "@typescript-eslint/types": "8.58.0", "@typescript-eslint/visitor-keys": "8.58.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA=="],
"@gcch/configuration-prettier/prettier-plugin-sh/@reteps/dockerfmt": ["@reteps/dockerfmt@0.3.6", "", {}, "sha512-Tb5wIMvBf/nLejTQ61krK644/CEMB/cpiaIFXqGApfGqO3GwcR3qnI0DbmkFVCl2OyEp8LnLX3EkucoL0+tbFg=="],
"@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager/@typescript-eslint/types": ["@typescript-eslint/types@8.58.1", "", {}, "sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw=="],
"@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils/@typescript-eslint/types": ["@typescript-eslint/types@8.58.1", "", {}, "sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw=="],
"@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/types": ["@typescript-eslint/types@8.58.1", "", {}, "sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw=="],
"@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.58.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.58.0", "@typescript-eslint/types": "^8.58.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg=="],
"@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.58.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A=="],
"@typescript-eslint/type-utils/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.58.0", "", { "dependencies": { "@typescript-eslint/types": "8.58.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ=="],
"@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.58.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.58.0", "@typescript-eslint/types": "^8.58.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg=="],
"@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.58.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A=="],
"@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.58.0", "", { "dependencies": { "@typescript-eslint/types": "8.58.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ=="],
"eslint-plugin-jsx-a11y/minimatch/brace-expansion": ["brace-expansion@1.1.13", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w=="],
"stylelint/file-entry-cache/flat-cache": ["flat-cache@6.1.22", "", { "dependencies": { "cacheable": "^2.3.4", "flatted": "^3.4.2", "hookified": "^1.15.0" } }, "sha512-N2dnzVJIphnNsjHcrxGW7DePckJ6haPrSFqpsBUhHYgwtKGVq4JrBGielEGD2fCVnsGm1zlBVZ8wGhkyuetgug=="],
@ -1766,24 +1700,6 @@
"table/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"typescript-eslint/@typescript-eslint/utils/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.58.1", "", { "dependencies": { "@typescript-eslint/types": "8.58.1", "@typescript-eslint/visitor-keys": "8.58.1" } }, "sha512-TPYUEqJK6avLcEjumWsIuTpuYODTTDAtoMdt8ZZa93uWMTX13Nb8L5leSje1NluammvU+oI3QRr5lLXPgihX3w=="],
"typescript-eslint/@typescript-eslint/utils/@typescript-eslint/types": ["@typescript-eslint/types@8.58.1", "", {}, "sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw=="],
"@gcch/configuration-eslint/eslint/@eslint/config-array/@eslint/object-schema": ["@eslint/object-schema@3.0.3", "", {}, "sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ=="],
"@gcch/configuration-eslint/typescript-eslint/@typescript-eslint/eslint-plugin/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.58.0", "", { "dependencies": { "@typescript-eslint/types": "8.58.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ=="],
"@gcch/configuration-eslint/typescript-eslint/@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
"@gcch/configuration-eslint/typescript-eslint/@typescript-eslint/parser/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.58.0", "", { "dependencies": { "@typescript-eslint/types": "8.58.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ=="],
"@gcch/configuration-eslint/typescript-eslint/@typescript-eslint/typescript-estree/@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.58.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.58.0", "@typescript-eslint/types": "^8.58.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg=="],
"@gcch/configuration-eslint/typescript-eslint/@typescript-eslint/typescript-estree/@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.58.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A=="],
"@gcch/configuration-eslint/typescript-eslint/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.58.0", "", { "dependencies": { "@typescript-eslint/types": "8.58.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ=="],
"eslint-plugin-jsx-a11y/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
}
}

View file

@ -1,5 +1,5 @@
import gcchConfig from "@gcch/configuration-oxlint";
import { OxlintConfig } from "oxlint";
import type { OxlintConfig } from "oxlint";
const config: OxlintConfig = {
...gcchConfig,

View file

@ -5,7 +5,7 @@ export default defineConfig({
projects: [
{
name: "desktop-chromium-1920",
use: { ...devices["Desktop Chrome"], viewport: { width: 1920, height: 1080 } },
use: { ...devices["Desktop Chrome"], viewport: { height: 1080, width: 1920 } },
},
// {
// name: "desktop-chromium-1536",
@ -17,7 +17,7 @@ export default defineConfig({
// },
{
name: "desktop-firefox-1920",
use: { ...devices["Desktop Firefox"], viewport: { width: 1920, height: 1080 } },
use: { ...devices["Desktop Firefox"], viewport: { height: 1080, width: 1920 } },
},
// {
// name: "desktop-firefox-1536",
@ -51,7 +51,6 @@ export default defineConfig({
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: "https://haikuatelier.gcch.local",
trace: "retry-with-trace",
clientCertificates: [
{
origin: "https://haikuatelier.gcch.local",
@ -60,6 +59,7 @@ export default defineConfig({
},
],
ignoreHTTPSErrors: true,
trace: "retry-with-trace",
},
workers: "100%",
});

View file

@ -1,4 +1,4 @@
import { pipe, Array as FxArray } from "effect";
import { Array as FxArray, pipe } from "effect";
import type stylelint from "stylelint";
import { propertyGroups } from "stylelint-config-clean-order";

View file

@ -1,9 +1,4 @@
{
"autoload": {
"psr-4": {
"HaikuAtelier\\": "web/app/themes/haiku-atelier-2024/src/inc/"
}
},
"authors": [
{
"email": "scott.walkinshaw@gmail.com",
@ -16,6 +11,11 @@
"name": "Ben Word"
}
],
"autoload": {
"psr-4": {
"HaikuAtelier\\": "web/app/themes/haiku-atelier-2024/src/inc/"
}
},
"config": {
"allow-plugins": {
"carthage-software/mago": true,
@ -31,28 +31,45 @@
"description": "WordPress boilerplate with Composer, easier configuration, and an improved folder structure",
"extra": {
"installer-paths": {
"web/app/mu-plugins/{$name}/": ["type:wordpress-muplugin"],
"web/app/plugins/{$name}/": ["type:wordpress-plugin"],
"web/app/themes/{$name}/": ["type:wordpress-theme"],
"web/vendor/{$vendor}/{$name}": ["htmlburger/carbon-fields"]
"web/app/mu-plugins/{$name}/": [
"type:wordpress-muplugin"
],
"web/app/plugins/{$name}/": [
"type:wordpress-plugin"
],
"web/app/themes/{$name}/": [
"type:wordpress-theme"
],
"web/vendor/{$vendor}/{$name}": [
"htmlburger/carbon-fields"
]
},
"wordpress-install-dir": "web/wp"
},
"homepage": "https://roots.io/bedrock/",
"keywords": ["bedrock", "composer", "roots", "wordpress", "wp", "wp-config"],
"keywords": [
"bedrock",
"composer",
"roots",
"wordpress",
"wp",
"wp-config"
],
"license": "MIT",
"minimum-stability": "dev",
"name": "roots/bedrock",
"prefer-stable": true,
"repositories": [
{
"only": ["wpackagist-plugin/*", "wpackagist-theme/*"],
"only": [
"wpackagist-plugin/*",
"wpackagist-theme/*"
],
"type": "composer",
"url": "https://wpackagist.org"
}
],
"require": {
"php": ">=8.5",
"composer/installers": "^2.3",
"crell/fp": "^1.0",
"htmlburger/carbon-fields": "^3.6.9",
@ -63,10 +80,11 @@
"mnsami/composer-custom-directory-installer": "^2.0",
"nesbot/carbon": "^3.11.3",
"oscarotero/env": "^2.1.1",
"php": ">=8.5",
"php-standard-library/php-standard-library": "^6.1.1",
"roots/bedrock-autoloader": "^1.1.0",
"roots/bedrock-disallow-indexing": "^2.1",
"roots/wordpress": "^6.9.4",
"php-standard-library/php-standard-library": "^6.1.1",
"roots/wp-config": "^1.0",
"stripe/stripe-php": "^19.4.1",
"symfony/uid": "^8.0.8",

35
composer.lock generated
View file

@ -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": "8fa8994b91f6fdfb99db59a67eb54ac5",
"content-hash": "bf6e098198b957782555eeb97479b37e",
"packages": [
{
"name": "carbonphp/carbon-doctrine-types",
@ -5129,12 +5129,12 @@
"source": {
"type": "git",
"url": "https://github.com/Roave/SecurityAdvisories.git",
"reference": "7c570124dcf5c6834d78348d6817f32cd6b537d7"
"reference": "d830a949e5c180e97c2245221daf8b589552cc2c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/7c570124dcf5c6834d78348d6817f32cd6b537d7",
"reference": "7c570124dcf5c6834d78348d6817f32cd6b537d7",
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/d830a949e5c180e97c2245221daf8b589552cc2c",
"reference": "d830a949e5c180e97c2245221daf8b589552cc2c",
"shasum": ""
},
"conflict": {
@ -5190,7 +5190,6 @@
"auth0/wordpress": "<=5.5",
"automad/automad": "<2.0.0.0-alpha5",
"automattic/jetpack": "<9.8",
"avideo/avideo": "<=26",
"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",
@ -5249,7 +5248,7 @@
"cesnet/simplesamlphp-module-proxystatistics": "<3.1",
"chriskacerguis/codeigniter-restserver": "<=2.7.1",
"chrome-php/chrome": "<1.14",
"ci4-cms-erp/ci4ms": "<=0.31.1",
"ci4-cms-erp/ci4ms": "<=0.31.3",
"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",
@ -5544,6 +5543,7 @@
"juzaweb/cms": "<=3.4.2",
"jweiland/events2": "<8.3.8|>=9,<9.0.6",
"jweiland/kk-downloader": "<1.2.2",
"kantorge/yaffa": "<=2",
"kazist/phpwhois": "<=4.2.6",
"kelvinmo/simplejwt": "<=1.1",
"kelvinmo/simplexrd": "<3.1.1",
@ -5567,6 +5567,7 @@
"laravel/fortify": "<1.11.1",
"laravel/framework": "<10.48.29|>=11,<11.44.1|>=12,<12.1.1",
"laravel/laravel": ">=5.4,<5.4.22",
"laravel/passport": "<13.7.1",
"laravel/pulse": "<1.3.1",
"laravel/reverb": "<1.7",
"laravel/socialite": ">=1,<2.0.10",
@ -5747,7 +5748,7 @@
"phpoffice/phpexcel": "<=1.8.2",
"phpoffice/phpspreadsheet": "<1.30|>=2,<2.1.12|>=2.2,<2.4|>=3,<3.10|>=4,<5",
"phppgadmin/phppgadmin": "<=7.13",
"phpseclib/phpseclib": "<=2.0.51|>=3,<=3.0.49",
"phpseclib/phpseclib": "<2.0.53|>=3,<3.0.51",
"phpservermon/phpservermon": "<3.6",
"phpsysinfo/phpsysinfo": "<3.4.3",
"phpunit/phpunit": "<8.5.52|>=9,<9.6.33|>=10,<10.5.62|>=11,<11.5.50|>=12,<12.5.8",
@ -5809,11 +5810,11 @@
"rap2hpoutre/laravel-log-viewer": "<0.13",
"react/http": ">=0.7,<1.9",
"really-simple-plugins/complianz-gdpr": "<6.4.2",
"redaxo/source": "<=5.20.1",
"redaxo/source": "<5.21",
"remdex/livehelperchat": "<4.29",
"renolit/reint-downloadmanager": "<4.0.2|>=5,<5.0.1",
"reportico-web/reportico": "<=8.1",
"rhukster/dom-sanitizer": "<1.0.7",
"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",
"robrichards/xmlseclibs": "<3.1.5",
@ -6171,7 +6172,7 @@
"type": "tidelift"
}
],
"time": "2026-04-06T23:13:58+00:00"
"time": "2026-04-10T21:13:58+00:00"
},
{
"name": "sebastian/diff",
@ -7410,16 +7411,16 @@
},
{
"name": "webmozart/assert",
"version": "2.1.6",
"version": "2.2.0",
"source": {
"type": "git",
"url": "https://github.com/webmozarts/assert.git",
"reference": "ff31ad6efc62e66e518fbab1cde3453d389bcdc8"
"reference": "1b99650e7ffcad232624a260bc7fbdec2ffc407c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/ff31ad6efc62e66e518fbab1cde3453d389bcdc8",
"reference": "ff31ad6efc62e66e518fbab1cde3453d389bcdc8",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/1b99650e7ffcad232624a260bc7fbdec2ffc407c",
"reference": "1b99650e7ffcad232624a260bc7fbdec2ffc407c",
"shasum": ""
},
"require": {
@ -7466,9 +7467,9 @@
],
"support": {
"issues": "https://github.com/webmozarts/assert/issues",
"source": "https://github.com/webmozarts/assert/tree/2.1.6"
"source": "https://github.com/webmozarts/assert/tree/2.2.0"
},
"time": "2026-02-27T10:28:38+00:00"
"time": "2026-04-09T16:54:47+00:00"
}
],
"aliases": [],
@ -7483,5 +7484,5 @@
"php": ">=8.5"
},
"platform-dev": {},
"plugin-api-version": "2.9.0"
"plugin-api-version": "2.6.0"
}

View file

@ -1,49 +1,52 @@
{
"dictionaries": ["fr-fr", "en-gb"],
"dictionaries": [
"en-gb",
"fr-fr"
],
"userWords": [
"lightningcss",
"codesniffer",
"curryfication",
"eilandert",
"ERRMODE",
"gcch",
"giftcard",
"haikuatelier",
"healthcheck",
"traefik",
"innodb",
"jaegertracing",
"eilandert",
"valkey",
"somaxconn",
"woocommerce",
"Squiz",
"twentytwentyfour",
"symfony",
"phpstan",
"codesniffer",
"muplugin",
"wpautop",
"ERRMODE",
"laravel",
"multishipping",
"lightningcss",
"multiformats",
"curryfication",
"giftcard",
"taplo",
"multishipping",
"muplugin",
"phpactor",
"gcch"
"phpstan",
"somaxconn",
"Squiz",
"symfony",
"taplo",
"traefik",
"twentytwentyfour",
"valkey",
"woocommerce",
"wpautop"
],
"words": [
"GLITCHTIP",
"Vali",
"classlike",
"Crell",
"Eles",
"fdir",
"friendsofphp",
"GLITCHTIP",
"htmlburger",
"logtape",
"mobily",
"oxlint",
"valibot",
"zstandard",
"Eles",
"logtape",
"wpackagist",
"phpdotenv",
"friendsofphp",
"htmlburger",
"Crell",
"Vali",
"valibot",
"wpackagist",
"wpdb",
"classlike"
"zstandard"
]
}

View file

@ -105,6 +105,12 @@ lint-js:
--config cfg/oxlint.config.ts \
--format stylish
fix-js:
bun --bun oxlint \
--config cfg/oxlint.config.ts \
--format stylish \
--fix --fix-suggestions --fix-dangerously
# Vérifie le code Sass avec Stylelint.
[group('css')]
[group('qualité')]

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -17,7 +17,10 @@
"baseline": {
"default": null,
"description": "Path to a baseline file to ignore listed issues.",
"type": ["string", "null"]
"type": [
"null",
"string"
]
},
"baseline-variant": {
"$ref": "#/$defs/BaselineVariant",
@ -135,7 +138,10 @@
},
"perform-heuristic-checks": {
"description": "**Deprecated**: Use `check-missing-override` and `find-unused-parameters` instead.\n\nWhen set to `true`, enables both `check-missing-override` and `find-unused-parameters`.\nWhen set to `false`, disables both.\n\nThis option is kept for backwards compatibility with existing configurations.",
"type": ["boolean", "null"],
"type": [
"boolean",
"null"
],
"writeOnly": true
},
"performance": {
@ -200,11 +206,18 @@
"type": "object"
},
"ArrayStyleOption": {
"enum": ["short", "long"],
"enum": [
"long",
"short"
],
"type": "string"
},
"AssertionStyle": {
"enum": ["static", "self_", "this"],
"enum": [
"self_",
"static",
"this"
],
"type": "string"
},
"BaselineVariant": {
@ -224,7 +237,11 @@
},
"BraceStyle": {
"description": "Specifies brace placement style for various constructs.\n\n- `SameLine`: Opening brace on the same line as the declaration\n- `NextLine`: Opening brace on the next line for single-line signatures;\n on the same line when the signature breaks across multiple lines\n- `AlwaysNextLine`: Opening brace always on the next line, regardless of\n whether the signature breaks",
"enum": ["same_line", "next_line", "always_next_line"],
"enum": [
"always_next_line",
"next_line",
"same_line"
],
"type": "string"
},
"DisallowedEntry": {
@ -237,13 +254,18 @@
"description": "Entry with name and optional help message.",
"properties": {
"help": {
"type": ["string", "null"]
"type": [
"null",
"string"
]
},
"name": {
"type": "string"
}
},
"required": ["name"],
"required": [
"name"
],
"type": "object"
}
],
@ -251,7 +273,12 @@
},
"EndOfLine": {
"description": "Specifies the style of line endings.",
"enum": ["auto", "lf", "crlf", "cr"],
"enum": [
"auto",
"cr",
"crlf",
"lf"
],
"type": "string"
},
"FormatterConfiguration": {
@ -735,7 +762,10 @@
"baseline": {
"default": null,
"description": "Path to a baseline file to ignore listed issues.",
"type": ["string", "null"]
"type": [
"null",
"string"
]
},
"baseline-variant": {
"$ref": "#/$defs/BaselineVariant",
@ -815,7 +845,10 @@
"type": "array"
}
},
"required": ["code", "in"],
"required": [
"code",
"in"
],
"type": "object"
}
],
@ -852,7 +885,10 @@
"baseline": {
"default": null,
"description": "Path to a baseline file to ignore listed issues.",
"type": ["string", "null"]
"type": [
"null",
"string"
]
},
"baseline-variant": {
"$ref": "#/$defs/BaselineVariant",
@ -994,11 +1030,11 @@
"level": "Help"
},
"halstead": {
"difficulty-threshold": 12.0,
"effort-threshold": 5000.0,
"difficulty-threshold": 12,
"effort-threshold": 5000,
"enabled": true,
"level": "Warning",
"volume-threshold": 1000.0
"volume-threshold": 1000
},
"identity-comparison": {
"enabled": true,
@ -1514,7 +1550,9 @@
},
"tainted-data-to-sink": {
"enabled": true,
"known-sink-functions": ["printf"],
"known-sink-functions": [
"printf"
],
"level": "Error"
},
"too-many-enum-cases": {
@ -1585,12 +1623,19 @@
"type": "object"
},
"MethodChainBreakingStyle": {
"enum": ["same_line", "next_line"],
"enum": [
"next_line",
"same_line"
],
"type": "string"
},
"NullTypeHint": {
"description": "Specifies null type hint style.",
"enum": ["null_pipe", "null_pipe_last", "question"],
"enum": [
"null_pipe",
"null_pipe_last",
"question"
],
"type": "string"
},
"PHPVersion": {
@ -1697,7 +1742,10 @@
"type": "array"
}
},
"required": ["namespace", "permit"],
"required": [
"namespace",
"permit"
],
"type": "object"
},
"PerimeterSettings": {
@ -1747,14 +1795,22 @@
"$ref": "#/$defs/Path"
}
},
"required": ["path", "kinds"],
"required": [
"kinds",
"path"
],
"type": "object"
}
]
},
"PermittedDependencyKind": {
"description": "Represents the specific types of symbols allowed from a path.",
"enum": ["class-like", "function", "constant", "attribute"],
"enum": [
"attribute",
"class-like",
"constant",
"function"
],
"type": "string"
},
"RuleSettings": {
@ -2654,7 +2710,9 @@
"type": "array"
},
"known-sink-functions": {
"default": ["printf"],
"default": [
"printf"
],
"items": {
"type": "string"
},
@ -2758,7 +2816,10 @@
"description": "Maximum cyclomatic complexity allowed for a single method.\n\nWhen set, each method in a class-like is checked individually against this threshold,\nin addition to the class-level `threshold` check.\n\nDefault: `None` (methods are only checked as part of the class-level total).",
"format": "uint",
"minimum": 0,
"type": ["integer", "null"]
"type": [
"integer",
"null"
]
},
"threshold": {
"default": 15,
@ -3308,7 +3369,10 @@
"description": "Maximum nesting depth allowed inside a single function, method, closure, or arrow function.\n\nWhen set, each function-like body is checked independently against this threshold,\nwith nesting counted from the function body (not the file root).\n\nDefault: `None` (function-like bodies are only checked against the global `threshold`).",
"format": "uint",
"minimum": 0,
"type": ["integer", "null"]
"type": [
"integer",
"null"
]
},
"level": {
"$ref": "#/$defs/Level",
@ -3331,7 +3395,10 @@
"format": "uint8",
"maximum": 255,
"minimum": 0,
"type": ["integer", "null"]
"type": [
"integer",
"null"
]
},
"enabled": {
"default": true,
@ -3401,12 +3468,12 @@
"additionalProperties": false,
"properties": {
"difficulty-threshold": {
"default": 12.0,
"default": 12,
"format": "double",
"type": "number"
},
"effort-threshold": {
"default": 5000.0,
"default": 5000,
"format": "double",
"type": "number"
},
@ -3425,7 +3492,7 @@
"default": "Warning"
},
"volume-threshold": {
"default": 1000.0,
"default": 1000,
"format": "double",
"type": "number"
}
@ -5417,11 +5484,11 @@
"halstead": {
"$ref": "#/$defs/RuleSettings20",
"default": {
"difficulty-threshold": 12.0,
"effort-threshold": 5000.0,
"difficulty-threshold": 12,
"effort-threshold": 5000,
"enabled": true,
"level": "Warning",
"volume-threshold": 1000.0
"volume-threshold": 1000
}
},
"identity-comparison": {
@ -6303,7 +6370,9 @@
"$ref": "#/$defs/RuleSettings136",
"default": {
"enabled": true,
"known-sink-functions": ["printf"],
"known-sink-functions": [
"printf"
],
"level": "Error"
}
},
@ -6423,7 +6492,9 @@
"type": "array"
},
"extensions": {
"default": ["php"],
"default": [
"php"
],
"description": "File extensions to filter by.\n\nDefaults to `[\".php\"]`.",
"items": {
"type": "string"
@ -6461,7 +6532,9 @@
"type": "string"
}
},
"required": ["workspace"],
"required": [
"workspace"
],
"type": "object"
},
"StructuralInheritanceConstraint": {
@ -6504,27 +6577,42 @@
"items": {
"$ref": "#/$defs/StructuralSymbolKind"
},
"type": ["array", "null"]
"type": [
"array",
"null"
]
},
"must-be-abstract": {
"default": null,
"description": "If true, the symbol must be declared `abstract`.",
"type": ["boolean", "null"]
"type": [
"boolean",
"null"
]
},
"must-be-final": {
"default": null,
"description": "If true, the symbol must be declared `final`.",
"type": ["boolean", "null"]
"type": [
"boolean",
"null"
]
},
"must-be-named": {
"default": null,
"description": "Optional naming pattern the symbol's name must match.",
"type": ["string", "null"]
"type": [
"null",
"string"
]
},
"must-be-readonly": {
"default": null,
"description": "If true, the symbol must be declared `readonly`.",
"type": ["boolean", "null"]
"type": [
"boolean",
"null"
]
},
"must-extend": {
"anyOf": [
@ -6577,7 +6665,10 @@
"not-on": {
"default": null,
"description": "An optional exclusion pattern; if the namespace matches this, the rule is skipped.",
"type": ["string", "null"]
"type": [
"null",
"string"
]
},
"on": {
"default": "",
@ -6587,7 +6678,10 @@
"reason": {
"default": null,
"description": "A human-readable reason for this rule.",
"type": ["string", "null"]
"type": [
"null",
"string"
]
},
"target": {
"anyOf": [
@ -6619,7 +6713,15 @@
"type": "object"
},
"StructuralSymbolKind": {
"enum": ["class-like", "class", "interface", "trait", "enum", "constant", "function"],
"enum": [
"class",
"class-like",
"constant",
"enum",
"function",
"interface",
"trait"
],
"type": "string"
}
},
@ -6682,7 +6784,10 @@
"editor-url": {
"default": null,
"description": "Editor URL template for OSC 8 terminal hyperlinks on file paths in diagnostics.\n\nWhen set, file paths in diagnostic output become clickable links in terminals\nthat support OSC 8 hyperlinks (e.g., iTerm2, Wezterm, Kitty, Windows Terminal).\n\nSupported placeholders:\n- `%file%` — absolute file path\n- `%line%` — line number\n- `%column%` — column number\n\nCan be set via `MAGO_EDITOR_URL` environment variable or `editor-url` in `mago.toml`.",
"type": ["string", "null"]
"type": [
"null",
"string"
]
},
"formatter": {
"$ref": "#/$defs/FormatterConfiguration",
@ -6830,11 +6935,11 @@
"level": "Help"
},
"halstead": {
"difficulty-threshold": 12.0,
"effort-threshold": 5000.0,
"difficulty-threshold": 12,
"effort-threshold": 5000,
"enabled": true,
"level": "Warning",
"volume-threshold": 1000.0
"volume-threshold": 1000
},
"identity-comparison": {
"enabled": true,
@ -7350,7 +7455,9 @@
},
"tainted-data-to-sink": {
"enabled": true,
"known-sink-functions": ["printf"],
"known-sink-functions": [
"printf"
],
"level": "Error"
},
"too-many-enum-cases": {
@ -7434,7 +7541,9 @@
"$ref": "#/$defs/SourceConfiguration",
"default": {
"excludes": [],
"extensions": ["php"],
"extensions": [
"php"
],
"glob": {
"backslash-escape": true,
"case-insensitive": false,

View file

@ -5,7 +5,9 @@ stack-size = 0
threads = 0
[source]
excludes = ["web/wp/wp-admin/includes/noop.php"]
excludes = [
"web/wp/wp-admin/includes/noop.php",
]
extensions = ["php"]
includes = ["config", "vendor", "web/app/plugins", "web/vendor", "web/wp"]
paths = ["web/app/themes/haiku-atelier-2024"]
@ -66,3 +68,9 @@ threads = 0
negation-complexity-threshold = 8192
saturation-complexity-threshold = 16384
string-combination-threshold = 256
[formatter]
excludes = [
"web/app/mu-plugins",
"web/app/plugins",
]

View file

@ -21,7 +21,7 @@
"valibot": "1.1.0"
},
"devDependencies": {
"@effect/language-service": "^0.84.3",
"@effect/language-service": "^0.85.0",
"@gcch/configuration-eslint": "git+https://git.gcch.fr/gcch/configuration-eslint#62ee424274",
"@gcch/configuration-oxlint": "git+https://git.gcch.fr/gcch/configuration-oxlint#0968f683",
"@gcch/configuration-prettier": "git+https://git.gcch.fr/gcch/configuration-prettier#8de937e801",
@ -60,7 +60,7 @@
"stylelint-plugin-logical-css": "^2.1.0",
"typescript": "6.0.2",
"typescript-eslint": "^8.58.1",
"vite": "^8.0.7",
"vite": "^8.0.8",
"vite-tsconfig-paths": "^6.1.1"
},
"browserslist": [

View file

@ -1,12 +1,12 @@
{
"$schema": "https:\/\/json-schema.org\/draft-07\/schema",
"$schema": "https://json-schema.org/draft-07/schema",
"properties": {
"$schema": {
"default": "",
"description": "Path to JSON schema, which can be used for config autocompletion, use phpactor config:initialize to update"
},
"behat.config_path": {
"default": "%project_root%\/behat.yml",
"default": "%project_root%/behat.yml",
"description": "Path to the main behat.yml (including the filename behat.yml)"
},
"behat.symfony.di_xml_path": {
@ -61,11 +61,19 @@
"description": "Object fill refactoring: use named parameters"
},
"code_transform.template_paths": {
"default": ["%project_config%\/templates", "%config%\/templates"],
"default": [
"%config%/templates",
"%project_config%/templates"
],
"description": "Paths in which to look for code templates"
},
"command": { "description": "Internal use only - name of the command which was executed" },
"completion.dedupe": { "default": true, "description": "If results should be de-duplicated" },
"command": {
"description": "Internal use only - name of the command which was executed"
},
"completion.dedupe": {
"default": true,
"description": "If results should be de-duplicated"
},
"completion.dedupe_match_fqn": {
"default": true,
"description": "If ``completion.dedupe``, consider the class FQN in addition to the completion suggestion"
@ -73,9 +81,14 @@
"completion.label_formatter": {
"default": "helpful",
"description": "Definition of how to format entries in the completion list",
"enum": ["helpful", "fqn"]
"enum": [
"fqn",
"helpful"
]
},
"completion.limit": {
"description": "Sets a limit on the number of completion suggestions for any request"
},
"completion.limit": { "description": "Sets a limit on the number of completion suggestions for any request" },
"completion_worse.completor.attribute.enabled": {
"default": true,
"description": "Enable or disable the ``attribute`` completor.\n\nCompletion for attribute class names."
@ -92,7 +105,10 @@
"default": true,
"description": "Enable or disable the ``class_member`` completor.\n\nCompletion for class members."
},
"completion_worse.completor.constant.enabled": { "default": false, "description": null },
"completion_worse.completor.constant.enabled": {
"default": false,
"description": null
},
"completion_worse.completor.constructor.enabled": {
"default": true,
"description": "Enable or disable the ``constructor`` completor.\n\nCompletion for constructors."
@ -147,7 +163,7 @@
},
"completion_worse.completor.symfony.enabled": {
"default": true,
"description": "Enable\/disable the Symfony completor - depends on Symfony extension being enabled"
"description": "Enable/disable the Symfony completor - depends on Symfony extension being enabled"
},
"completion_worse.completor.type.enabled": {
"default": true,
@ -161,19 +177,28 @@
"default": true,
"description": "Enable or disable the ``worse_parameter`` completor.\n\nCompletion for method or function parameters."
},
"completion_worse.debug": { "default": false, "description": "Include debug info in completion results" },
"completion_worse.experimantal": { "default": false, "description": "Enable experimental functionality" },
"completion_worse.debug": {
"default": false,
"description": "Include debug info in completion results"
},
"completion_worse.experimantal": {
"default": false,
"description": "Enable experimental functionality"
},
"completion_worse.name_completion_priority": {
"default": "proximity",
"description": "Strategy to use when ordering completion results for classes and functions:\n\n- `proximity`: Classes and functions will be ordered by their proximity to the text document being edited.\n- `none`: No ordering will be applied."
},
"completion_worse.snippets": { "default": true, "description": "Enable or disable completion snippets" },
"completion_worse.snippets": {
"default": true,
"description": "Enable or disable completion snippets"
},
"composer.autoload_deregister": {
"default": true,
"description": "Immediately de-register the autoloader once it has been included (prevent conflicts with Phpactor's autoloader). Some platforms may require this to be disabled"
},
"composer.autoloader_path": {
"default": "%project_root%\/vendor\/autoload.php",
"default": "%project_root%/vendor/autoload.php",
"description": "Path to project's autoloader, can be an array"
},
"composer.class_maps_only": {
@ -186,9 +211,23 @@
},
"console.decorated": {
"description": "Whether to decorate messages (null for auto-guessing)",
"enum": [true, false, null]
"enum": [
true,
false,
null
]
},
"console.verbosity": {
"default": 32,
"description": "Verbosity level",
"enum": [
16,
32,
64,
128,
256
]
},
"console.verbosity": { "default": 32, "description": "Verbosity level", "enum": [16, 32, 64, 128, 256] },
"console_dumper_default": {
"default": "indented",
"description": "Name of the \"dumper\" (renderer) to use for some CLI commands"
@ -197,75 +236,131 @@
"default": 1610612736,
"description": "Ensure that PHP has a memory_limit of at least this amount in bytes"
},
"file_path_resolver.app_name": { "default": "phpactor", "description": null },
"file_path_resolver.application_root": { "description": null },
"file_path_resolver.enable_cache": { "default": true, "description": null },
"file_path_resolver.enable_logging": { "default": true, "description": null },
"file_path_resolver.project_root": { "default": "\/opt\/phpactor", "description": null },
"file_path_resolver.app_name": {
"default": "phpactor",
"description": null
},
"file_path_resolver.application_root": {
"description": null
},
"file_path_resolver.enable_cache": {
"default": true,
"description": null
},
"file_path_resolver.enable_logging": {
"default": true,
"description": null
},
"file_path_resolver.project_root": {
"default": "/opt/phpactor",
"description": null
},
"indexer.buffer_time": {
"default": 500,
"description": "For real-time indexers only: the time, in milliseconds, to buffer the results",
"type": ["integer"]
"type": [
"integer"
]
},
"indexer.enabled_watchers": {
"default": ["inotify", "watchman", "find", "php"],
"default": [
"find",
"inotify",
"php",
"watchman"
],
"description": "List of allowed watchers. The first watcher that supports the current system will be used",
"type": ["object"]
"type": [
"object"
]
},
"indexer.exclude_patterns": {
"default": ["\/vendor\/**\/Tests\/**\/*", "\/vendor\/**\/tests\/**\/*", "\/vendor\/composer\/**\/*"],
"default": [
"/vendor/**/tests/**/*",
"/vendor/**/Tests/**/*",
"/vendor/composer/**/*"
],
"description": "Glob patterns to exclude while indexing",
"type": ["object"]
"type": [
"object"
]
},
"indexer.follow_symlinks": {
"default": false,
"description": "To allow indexer to follow symlinks",
"type": ["boolean"]
"type": [
"boolean"
]
},
"indexer.implementation_finder.deep": {
"default": true,
"description": "Recurse over class implementations to resolve all class implementations (not just the classes directly implementing the subject)",
"type": ["boolean"]
"type": [
"boolean"
]
},
"indexer.include_patterns": {
"default": ["\/**\/*.php", "\/**\/*.phar"],
"default": [
"/**/*.phar",
"/**/*.php"
],
"description": "Glob patterns to include while indexing",
"type": ["object"]
"type": [
"object"
]
},
"indexer.index_path": {
"default": "%cache%\/index\/%project_id%",
"default": "%cache%/index/%project_id%",
"description": "Path where the index should be saved",
"type": ["string"]
"type": [
"string"
]
},
"indexer.poll_time": {
"default": 5000,
"description": "For polling indexers only: the time, in milliseconds, between polls (e.g. filesystem scans)",
"type": ["integer"]
"type": [
"integer"
]
},
"indexer.project_root": {
"default": "%project_root%",
"description": "The root path to use for scanning the index",
"type": ["string"]
"type": [
"string"
]
},
"indexer.reference_finder.deep": {
"default": true,
"description": "Recurse over class implementations to resolve all references",
"type": ["boolean"]
"type": [
"boolean"
]
},
"indexer.stub_paths": {
"default": [],
"description": "Paths to external folders to index. They will be indexed only once, if you want to take any changes into account you will have to reindex your project manually.",
"type": ["object"]
"type": [
"object"
]
},
"indexer.supported_extensions": {
"default": ["php", "phar"],
"default": [
"phar",
"php"
],
"description": "File extensions (e.g. `php`) for files that should be indexed",
"type": ["object"]
"type": [
"object"
]
},
"language_server.catch_errors": {
"default": true,
"description": null
},
"language_server.catch_errors": { "default": true, "description": null },
"language_server.diagnostic_exclude_paths": {
"default": [],
"description": "List of paths to exclude from diagnostics, e.g. `vendor\/**\/*`"
"description": "List of paths to exclude from diagnostics, e.g. `vendor/**/*`"
},
"language_server.diagnostic_outsource": {
"default": true,
@ -296,19 +391,30 @@
},
"language_server.enable_workspace": {
"default": true,
"description": "If workspace management \/ text synchronization should be enabled (this isn't required for some language server implementations, e.g. static analyzers)"
"description": "If workspace management / text synchronization should be enabled (this isn't required for some language server implementations, e.g. static analyzers)"
},
"language_server.file_event_globs": {
"default": [
"**/*.php"
],
"description": null
},
"language_server.file_events": {
"default": true,
"description": "Register to receive file events"
},
"language_server.file_event_globs": { "default": ["**\/*.php"], "description": null },
"language_server.file_events": { "default": true, "description": "Register to receive file events" },
"language_server.method_alias_map": {
"default": [],
"description": "Allow method names to be re-mapped. Useful for maintaining backwards compatibility"
},
"language_server.phpactor_bin": {
"default": "\/opt\/phpactor\/lib\/Extension\/LanguageServer\/..\/..\/..\/bin\/phpactor",
"default": "/opt/phpactor/lib/Extension/LanguageServer/../../../bin/phpactor",
"description": "Internal use only - name path to Phpactor binary"
},
"language_server.profile": { "default": false, "description": "Logs timing information for incoming LSP requests" },
"language_server.profile": {
"default": false,
"description": "Logs timing information for incoming LSP requests"
},
"language_server.self_destruct_timeout": {
"default": 2500,
"description": "Wait this amount of time (in milliseconds) after a shutdown request before self-destructing"
@ -340,22 +446,30 @@
"language_server_configuration.auto_config": {
"default": true,
"description": "Prompt to enable extensions which apply to your project on language server start",
"type": ["boolean"]
"type": [
"boolean"
]
},
"language_server_indexer.reindex_timeout": {
"default": 300,
"description": "Unconditionally reindex modified files every N seconds"
},
"language_server_indexer.workspace_symbol_search_limit": { "default": 250, "description": null },
"language_server_indexer.workspace_symbol_search_limit": {
"default": 250,
"description": null
},
"language_server_php_cs_fixer.bin": {
"default": "%project_root%\/vendor\/bin\/php-cs-fixer",
"default": "%project_root%/vendor/bin/php-cs-fixer",
"description": "Path to the php-cs-fixer executable"
},
"language_server_php_cs_fixer.config": {
"description": "Set custom PHP CS config path. Ex., %project_root%\/.php-cs-fixer.php"
"description": "Set custom PHP CS config path. Ex., %project_root%/.php-cs-fixer.php"
},
"language_server_php_cs_fixer.env": {
"default": { "PHP_CS_FIXER_IGNORE_ENV": true, "XDEBUG_MODE": "off" },
"default": {
"PHP_CS_FIXER_IGNORE_ENV": true,
"XDEBUG_MODE": "off"
},
"description": "Environment for PHP CS Fixer (e.g. to set PHP_CS_FIXER_IGNORE_ENV)"
},
"language_server_php_cs_fixer.show_diagnostics": {
@ -363,16 +477,24 @@
"description": "Whether PHP CS Fixer diagnostics are shown"
},
"language_server_phpstan.bin": {
"default": "%project_root%\/vendor\/bin\/phpstan",
"default": "%project_root%/vendor/bin/phpstan",
"description": "Path to the PHPStan executable"
},
"language_server_phpstan.config": { "description": "Override the PHPStan configuration file" },
"language_server_phpstan.level": { "description": "Override the PHPStan level" },
"language_server_phpstan.mem_limit": { "description": "Override the PHPStan memory limit" },
"language_server_phpstan.config": {
"description": "Override the PHPStan configuration file"
},
"language_server_phpstan.level": {
"description": "Override the PHPStan level"
},
"language_server_phpstan.mem_limit": {
"description": "Override the PHPStan memory limit"
},
"language_server_psalm.bin": {
"default": "%project_root%\/vendor\/bin\/psalm",
"description": "Path to psalm if different from vendor\/bin\/psalm",
"type": ["string"]
"default": "%project_root%/vendor/bin/psalm",
"description": "Path to psalm if different from vendor/bin/psalm",
"type": [
"string"
]
},
"language_server_psalm.error_level": {
"description": "Override level at which Psalm should report errors (lower => more errors)"
@ -380,28 +502,39 @@
"language_server_psalm.show_info": {
"default": true,
"description": "If infos from psalm should be displayed",
"type": ["boolean"]
"type": [
"boolean"
]
},
"language_server_psalm.threads": {
"default": 1,
"description": "Set the number of threads Psalm should use. Warning: NULL will use as many as possible and may crash your computer",
"type": ["integer"]
"type": [
"integer"
]
},
"language_server_psalm.timeout": {
"default": 15,
"description": "Kill the psalm process after this number of seconds",
"type": ["integer"]
"type": [
"integer"
]
},
"language_server_psalm.use_cache": {
"default": true,
"description": "If the Psalm cache should be used (see the `--no-cache` option)",
"type": ["boolean"]
"type": [
"boolean"
]
},
"language_server_reference_reference_finder.reference_timeout": {
"default": 60,
"description": "Stop searching for references after this time (in seconds) has expired"
},
"language_server_worse_reflection.diagnostics.enable": { "default": true, "description": "Enable diagnostics" },
"language_server_worse_reflection.diagnostics.enable": {
"default": true,
"description": "Enable diagnostics"
},
"language_server_worse_reflection.inlay_hints.enable": {
"default": false,
"description": "Enable inlay hints (experimental)"
@ -418,34 +551,87 @@
"default": 100,
"description": "Minimum interval to update the workspace index as documents are updated (in milliseconds)"
},
"logger.name": { "default": "logger", "description": null, "type": ["string"] },
"logging.enabled": { "default": false, "description": null, "type": ["boolean"] },
"logging.fingers_crossed": { "default": false, "description": null, "type": ["boolean"] },
"logging.formatter": { "description": null },
"logger.name": {
"default": "logger",
"description": null,
"type": [
"string"
]
},
"logging.enabled": {
"default": false,
"description": null,
"type": [
"boolean"
]
},
"logging.fingers_crossed": {
"default": false,
"description": null,
"type": [
"boolean"
]
},
"logging.formatter": {
"description": null
},
"logging.level": {
"default": "warning",
"description": null,
"enum": ["emergency", "alert", "critical", "error", "warning", "notice", "info", "debug"],
"type": ["string"]
"enum": [
"alert",
"critical",
"debug",
"emergency",
"error",
"info",
"notice",
"warning"
],
"type": [
"string"
]
},
"logging.path": {
"default": "application.log",
"description": null,
"type": [
"string"
]
},
"navigator.autocreate": {
"default": [],
"description": null
},
"navigator.destinations": {
"default": [],
"description": null
},
"logging.path": { "default": "application.log", "description": null, "type": ["string"] },
"navigator.autocreate": { "default": [], "description": null },
"navigator.destinations": { "default": [], "description": null },
"object_renderer.template_paths.markdown": {
"default": ["%project_config%\/templates\/markdown", "%config%\/templates\/markdown"],
"default": [
"%config%/templates/markdown",
"%project_config%/templates/markdown"
],
"description": "Paths in which to look for templates for hover information."
},
"php.version": {
"description": "Consider this value to be the project\\'s version of PHP (e.g. `7.4`). If omitted\nit will check `composer.json` (by the configured platform then the PHP requirement) before\nfalling back to the PHP version of the current process."
},
"php_code_sniffer.args": { "default": [], "description": "Additional arguments to pass to the PHPCS process" },
"php_code_sniffer.args": {
"default": [],
"description": "Additional arguments to pass to the PHPCS process"
},
"php_code_sniffer.bin": {
"default": "%project_root%\/vendor\/bin\/phpcs",
"default": "%project_root%/vendor/bin/phpcs",
"description": "Path to the phpcs executable"
},
"php_code_sniffer.cwd": { "description": "Working directory for PHPCS" },
"php_code_sniffer.cwd": {
"description": "Working directory for PHPCS"
},
"php_code_sniffer.env": {
"default": { "XDEBUG_MODE": "off" },
"default": {
"XDEBUG_MODE": "off"
},
"description": "Environment for PHP_CodeSniffer (e.g. to set XDEBUG_MODE)"
},
"php_code_sniffer.show_diagnostics": {
@ -456,15 +642,24 @@
"default": false,
"description": "Only consider public services when providing analysis for the service locator"
},
"rpc.replay_path": { "default": "%cache%\/replay.json", "description": "Path where the replays should be stored" },
"rpc.store_replay": { "default": false, "description": "Should replays be stored?" },
"source_code_filesystem.project_root": { "default": "%project_root%", "description": null },
"rpc.replay_path": {
"default": "%cache%/replay.json",
"description": "Path where the replays should be stored"
},
"rpc.store_replay": {
"default": false,
"description": "Should replays be stored?"
},
"source_code_filesystem.project_root": {
"default": "%project_root%",
"description": null
},
"symfony.xml_path": {
"default": "%project_root%\/var\/cache\/dev\/App_KernelDevDebugContainer.xml",
"default": "%project_root%/var/cache/dev/App_KernelDevDebugContainer.xml",
"description": "Path to the Symfony container XML dump file"
},
"worse_reflection.cache_dir": {
"default": "%cache%\/worse-reflection",
"default": "%cache%/worse-reflection",
"description": "Cache directory for stubs"
},
"worse_reflection.cache_lifetime": {
@ -474,18 +669,26 @@
"worse_reflection.diagnostics.undefined_variable.suggestion_levenshtein_disatance": {
"default": 4,
"description": "Levenshtein distance to use when suggesting corrections for variable names",
"type": ["integer"]
"type": [
"integer"
]
},
"worse_reflection.enable_cache": {
"default": true,
"description": "If reflection caching should be enabled"
},
"worse_reflection.enable_cache": { "default": true, "description": "If reflection caching should be enabled" },
"worse_reflection.enable_context_location": {
"default": true,
"description": "If source code is passed to a ``Reflector`` then temporarily make it available as a\nsource location. Note this should NOT be enabled if the source code can be\nlocated in another (e.g. when running a Language Server)"
},
"worse_reflection.stub_dir": {
"default": "%application_root%\/vendor\/jetbrains\/phpstorm-stubs",
"default": "%application_root%/vendor/jetbrains/phpstorm-stubs",
"description": "Location of the core PHP stubs - these will be scanned and cached on the first request"
},
"xdebug_disable": { "default": true, "description": "If XDebug should be automatically disabled" }
"xdebug_disable": {
"default": true,
"description": "If XDebug should be automatically disabled"
}
},
"title": "Phpactor Configuration Schema",
"type": "object"

View file

@ -1,13 +1,14 @@
import { $ } from "bun";
import { Array, Console, Effect, Layer, ManagedRuntime, Option, Order, pipe, Schema, ServiceMap } from "effect";
import { UnknownError } from "effect/Cause";
import type { Option } from "effect";
import { Array as FxArray, Console, Effect, Layer, ManagedRuntime, Order, pipe, Schema, ServiceMap } from "effect";
import type { UnknownError } from "effect/Cause";
import { readdir } from "node:fs/promises";
class PodmanError extends Schema.TaggedErrorClass<PodmanError>()("PodmanError", {
cause: Schema.Error,
}) {}
class FileSystemError extends Schema.TaggedErrorClass<FileSystemError>()("FileSystemError", {
class FSError extends Schema.TaggedErrorClass<FSError>()("FSError", {
cause: Schema.Error,
}) {}
@ -21,21 +22,21 @@ class Podman extends ServiceMap.Service<
static readonly layer = Layer.effect(
Podman,
// oxlint-disable-next-line require-yield
Effect.gen(function* () {
const launchContainers = Effect.fn("launchContainers")(function* () {
Effect.gen(function*() {
const launchContainers = Effect.fn("launchContainers")(function*() {
return yield* pipe(
Effect.tryPromise(() => $`podman compose up -d &> /dev/null`),
Effect.tryPromise(async () => $`podman compose up -d &> /dev/null`),
Effect.map((shell: $.ShellOutput) => shell.text()),
Effect.mapError((error: UnknownError) => new PodmanError({ cause: error })),
);
});
const importLatestDbInWordPressContainer = Effect.fn("importLatestDbInWordPressContainer")(function* (
const importLatestDbInWordPressContainer = Effect.fn("importLatestDbInWordPressContainer")(function*(
exportPath: string,
) {
return yield* pipe(
Effect.tryPromise(
() =>
async () =>
$`podman exec -it haikuatelier.fr-wordpress fish -c "cd web && wp --allow-root db import ${exportPath} > /dev/null"`,
),
Effect.map((shell: $.ShellOutput) => shell.text()),
@ -51,47 +52,44 @@ class Podman extends ServiceMap.Service<
);
}
class FileSystem extends ServiceMap.Service<
FileSystem,
class FS extends ServiceMap.Service<
FS,
{
getLatestDbExport(): Effect.Effect<string, FileSystemError>;
getLatestDbExport(): Effect.Effect<string, FSError>;
}
>()("haikuatelier.fr/scripts/importe-dernier-export-bdd/FileSystem") {
>()("haikuatelier.fr/scripts/importe-dernier-export-bdd/FS") {
static readonly layer = Layer.effect(
FileSystem,
FS,
// oxlint-disable-next-line require-yield
Effect.gen(function* () {
const getLatestDbExport = Effect.fn("getLatestDbExport")(function* () {
Effect.gen(function*() {
const getLatestDbExport = Effect.fn("getLatestDbExport")(function*() {
return yield* pipe(
Effect.tryPromise(() => readdir(`./db`)),
Effect.map((paths: ReadonlyArray<string>) => Array.sort(paths, Order.String)),
Effect.map((sortedPaths: ReadonlyArray<string>) => Array.last(sortedPaths)),
Effect.tryPromise(async () => readdir(`./db`)),
Effect.map((paths: ReadonlyArray<string>) => FxArray.sort(paths, Order.String)),
Effect.map((sortedPaths: ReadonlyArray<string>) => FxArray.last(sortedPaths)),
Effect.flatMap((path: Option.Option<string>) => Effect.fromOption(path)),
Effect.mapError((_) => new FileSystemError({ cause: new Error("Aucun export de BDD n'est disponible.") })),
Effect.mapError(_ => new FSError({ cause: new Error("Aucun export de BDD n'est disponible.") })),
);
});
return FileSystem.of({
return FS.of({
getLatestDbExport,
});
}),
);
}
const mainLayer = Layer.mergeAll(Podman.layer, FileSystem.layer);
const mainLayer = Layer.mergeAll(Podman.layer, FS.layer);
const runtime = ManagedRuntime.make(mainLayer);
const program = Effect.fn("program")(function* () {
yield* Podman.use((podman) => podman.launchContainers());
const program = Effect.fn("program")(function*() {
yield* Podman.use(podman => podman.launchContainers());
yield* Console.log("Containers are launched.");
const latestExportPath: string = pipe(
yield* FileSystem.use((fs) => fs.getLatestDbExport()),
(path) => `../db/${path}`,
);
const latestExportPath: string = pipe(yield* FS.use(fs => fs.getLatestDbExport()), path => `../db/${path}`);
yield* Console.log(latestExportPath);
yield* Podman.use((podman) => podman.importLatestDbInWordPressContainer(latestExportPath));
yield* Podman.use(podman => podman.importLatestDbInWordPressContainer(latestExportPath));
yield* Console.log("Import done.");
});

View file

@ -1,5 +1,5 @@
import { YAML } from "bun";
import { Console, Data, Effect, Array as EffectArray, pipe, Record, Schema, SchemaIssue } from "effect";
import { Array as EffectArray, Console, Data, Effect, pipe, Record, Schema, SchemaIssue } from "effect";
import { SchemaError } from "effect/Schema";
const COMPOSE_PATH = "compose.yaml";
@ -21,7 +21,7 @@ class ScriptError extends Data.TaggedError("ScriptError")<{ cause: unknown }> {}
* @param compose Le fichier _Compose_ sous forme d'objet.
* @returns Les noms des Services sous forme de tableau.
*/
const getServicesFromComposeYaml: (compose: Compose) => ReadonlyArray<string> = (compose) =>
const getServicesFromComposeYaml: (compose: Compose) => ReadonlyArray<string> = compose =>
Record.keys(compose.services);
/**
@ -31,7 +31,7 @@ const getServicesFromComposeYaml: (compose: Compose) => ReadonlyArray<string> =
* @returns Le contenu textuel du fichier sous forme de chaîne de caractères.
*/
const getFileContent: (filePath: string) => Effect.Effect<string, ScriptError> = Effect.fn("getFileContent")(
function* (filePath) {
function*(filePath) {
const fileRef: Bun.BunFile = Bun.file(filePath);
yield* Effect.tryPromise({
@ -56,12 +56,12 @@ const getFileContent: (filePath: string) => Effect.Effect<string, ScriptError> =
const getComposeYaml: <ComposeSchema>(
path: string,
schema: Schema.Schema<ComposeSchema>,
) => Effect.Effect<ComposeSchema, ScriptError, unknown> = Effect.fn("getComposeYaml")(function* (path, schema) {
) => Effect.Effect<ComposeSchema, ScriptError, unknown> = Effect.fn("getComposeYaml")(function*(path, schema) {
return yield* pipe(
getFileContent(path),
Effect.map((text: string): unknown => YAML.parse(text)),
Effect.flatMap((yaml: unknown) =>
Schema.decodeUnknownEffect(schema)(yaml, { errors: "all", onExcessProperty: "ignore" }),
Schema.decodeUnknownEffect(schema)(yaml, { errors: "all", onExcessProperty: "ignore" })
),
Effect.mapError((error): ScriptError => {
if (error instanceof SchemaError) {
@ -76,7 +76,7 @@ const getComposeYaml: <ComposeSchema>(
const program: Effect.Effect<ReadonlyArray<string>, ScriptError> = pipe(
getComposeYaml(COMPOSE_PATH, Compose),
Effect.map((compose: Compose) => getServicesFromComposeYaml(compose)),
Effect.map((keys: ReadonlyArray<string>) => EffectArray.filter(keys, (key) => key !== "wordpress")),
Effect.map((keys: ReadonlyArray<string>) => EffectArray.filter(keys, key => key !== "wordpress")),
Effect.orElseSucceed(() => [""]),
Effect.tap((services: ReadonlyArray<string>) => {
Bun.spawn({ cmd: ["podman", "compose", "pull", ...services], timeout: DEFAULT_CMD_TIMEOUT });

View file

@ -37,7 +37,7 @@ Array.from<TestPage>([
},
]).forEach(({ pageName, url }) => {
test.skip(pageName, async ({ page }, testInfo) => {
await page.goto(url);
await page["goto"](url);
const projectName = testInfo.project.name;
const timestamp: string = genTimestamp();

View file

@ -1,11 +1,13 @@
import { test as base, expect, Response } from "@playwright/test";
import {
import type { Response } from "@playwright/test";
import { expect, test as base } from "@playwright/test";
import { pipe } from "effect";
import { not } from "effect/Boolean";
import type {
WCV3Product,
WCV3Products,
} from "../../web/app/themes/haiku-atelier-2024/src/scripts/lib/types/api/v3/products";
import { BackendHeaders, getBackendHeadersFromHtml } from "./utils.ts";
import { pipe } from "effect";
import { not } from "effect/Boolean";
import type { BackendHeaders } from "./utils.ts";
import { getBackendHeadersFromHtml } from "./utils.ts";
/*
* Faire un premier test simple l'on clic sur la première carte du shop
@ -30,7 +32,7 @@ type ProductsKinds = {
export const test = base.extend<ProductsFixture>({
products: async ({ page, request }, use) => {
await page.goto("/shop");
await page["goto"]("/shop");
const backendHeaders: BackendHeaders = await getBackendHeadersFromHtml(page);
const response = await request.get("/wp-json/wc/v3/products?page=1&per_page=100&status=publish", {
@ -68,7 +70,7 @@ test("can add a Product without variation with stock to the Cart", async ({ page
}
// Va à la page du Produit.
await page.goto(randomProduct.permalink);
await page["goto"](randomProduct.permalink);
// Vérifie le bon état du bouton de l'ajout au Panier.
const addToCartButton = page.getByRole("button", { disabled: false, name: "Add to cart" });
@ -101,7 +103,7 @@ test("can't add a Product without variation without stock to the Cart", async ({
}
// Va à la page du Produit.
await page.goto(randomProduct.permalink);
await page["goto"](randomProduct.permalink);
// Vérifie le bon état du bouton de l'ajout au Panier.
const outOfStockButton = page.getByRole("button", { disabled: true, name: "Out of stock" });

View file

@ -1,6 +1,8 @@
import { APIRequestContext, expect, Locator, Page, Response, test } from "@playwright/test";
import { WCV3Products } from "../../web/app/themes/haiku-atelier-2024/src/scripts/lib/types/api/v3/products";
import { BackendHeaders, getBackendHeadersFromHtml } from "./utils.ts";
import type { APIRequestContext, Locator, Page, Response } from "@playwright/test";
import { expect, test } from "@playwright/test";
import type { WCV3Products } from "../../web/app/themes/haiku-atelier-2024/src/scripts/lib/types/api/v3/products";
import type { BackendHeaders } from "./utils.ts";
import { getBackendHeadersFromHtml } from "./utils.ts";
test.describe.configure({ mode: "parallel", timeout: 60_000 });
@ -9,13 +11,13 @@ test("can scroll to the end of the grid", async ({ page }): Promise<void> => {
});
test.skip("can access all Products' pages", async ({ page, request }): Promise<void> => {
await page.goto("https://haikuatelier.gcch.local/shop/");
await page["goto"]("https://haikuatelier.gcch.local/shop/");
const links = await getAllProductsLinks(page, request);
for (const link of links) {
// Vérifie que le lien de la page retourne OK.
const req = await request.get(link as string);
expect(req, "The Product's page is accessible").toBeOK();
const req = await request.get(link);
await expect(req, "The Product's page is accessible").toBeOK();
}
});
@ -25,11 +27,11 @@ const getAllProductsLinks = async (page: Page, request: APIRequestContext): Prom
headers: { Authorization: `Basic ${backendHeaders.authString}`, Nonce: backendHeaders.nonce },
});
const json = (await response.json()) as WCV3Products;
return json.map((p) => p.permalink);
return json.map(p => p.permalink);
};
const scrollToGridsEnd = async (page: Page): Promise<void> => {
await page.goto("https://haikuatelier.gcch.local/shop/");
await page["goto"]("https://haikuatelier.gcch.local/shop/");
let hasMoreProducts = true;
let currentPageNumber = "1";

View file

@ -1,5 +1,5 @@
import { Option, pipe } from "effect";
import { Page } from "playwright/test";
import type { Page } from "playwright/test";
export type BackendHeaders = {
authString: string;
@ -12,7 +12,7 @@ export type BackendHeaders = {
export const getBackendHeadersFromHtml = async (page: Page): Promise<BackendHeaders> => {
const backendHeaders: BackendHeaders | undefined = pipe(
Option.fromNullishOr(await page.locator("#injection-v2").textContent()),
Option.andThen((j) => JSON.parse(j) as BackendHeaders),
Option.andThen(j => JSON.parse(j) as BackendHeaders),
Option.getOrUndefined,
);

View file

@ -12,7 +12,12 @@
"exactOptionalPropertyTypes": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"lib": ["DOM", "DOM.Iterable", "DOM.AsyncIterable", "ESNext"],
"lib": [
"DOM",
"DOM.AsyncIterable",
"DOM.Iterable",
"ESNext"
],
"libReplacement": true,
"module": "ESNext",
"moduleDetection": "force",
@ -41,10 +46,20 @@
"strictNullChecks": true,
"strictPropertyInitialization": true,
"target": "ESNext",
"types": ["node", "vite/client"],
"types": [
"node",
"vite/client"
],
"useDefineForClassFields": true,
"useUnknownInCatchVariables": true
},
"exclude": ["vendor", "web/app/plugins", "web/wp"],
"include": ["**/*.js", "**/*.ts"]
"exclude": [
"vendor",
"web/app/plugins",
"web/wp"
],
"include": [
"**/*.js",
"**/*.ts"
]
}

View file

@ -22,7 +22,8 @@ $templates = ['404.twig'];
*
* @throws Exception une exception est levée s'il est impossible d'obtenir la date de modification du fichier à charger
*/
function load_page_resources(): void {
function load_page_resources(): void
{
Resource::enqueue_style_file(
handle: 'haiku-atelier-2024-styles-page-a-propos',
path: '/assets/css/pages/page-modele-simple.css',

View file

@ -1,4 +1,4 @@
jQuery(document).ready(function ($) {
jQuery(document).ready(function($) {
"use strict";
/**
@ -9,7 +9,7 @@ jQuery(document).ready(function ($) {
* @link https://github.com/maddisondesigns
*/
$(".customize-control-tinymce-editor").each(function () {
$(".customize-control-tinymce-editor").each(function() {
// Get the toolbar strings that were passed from the PHP Class
const tinyMCEToolbar1String = _wpCustomizeSettings.controls[$(this).attr("id")].skyrockettinymcetoolbar1;
const tinyMCEToolbar2String = _wpCustomizeSettings.controls[$(this).attr("id")].skyrockettinymcetoolbar2;
@ -19,14 +19,14 @@ jQuery(document).ready(function ($) {
mediaButtons: tinyMCEMediaButtons,
quicktags: true,
tinymce: {
wpautop: true,
toolbar1: tinyMCEToolbar1String,
toolbar2: tinyMCEToolbar2String,
wpautop: true,
},
});
});
$(document).on("tinymce-editor-init", function (event, editor) {
editor.on("change", function (e) {
$(document).on("tinymce-editor-init", function(event, editor) {
editor.on("change", function(e) {
tinyMCE.triggerSave();
$("#" + editor.id).trigger("change");
});

View file

@ -22,7 +22,8 @@ use WC_Session_Handler;
header('Content-Type: application/json; charset=utf-8');
// TODO: Appliquer le bon calcul pour les montants vs. percentages
function get_discount_amount(WC_Coupon $coupon) {
function get_discount_amount(WC_Coupon $coupon)
{
if ($coupon->get_discount_type() === 'fixed_cart') {
return $coupon->get_amount() * 100;
} else {
@ -30,7 +31,8 @@ function get_discount_amount(WC_Coupon $coupon) {
}
}
function get_discount_duration(WC_Coupon $coupon): string {
function get_discount_duration(WC_Coupon $coupon): string
{
if ($coupon->get_discount_type() === 'fixed_cart') {
return 'once';
} else {

View file

@ -45,6 +45,7 @@ $maximum_price = collect($product->variations)->max('price');
$same_collection_products = Product::get_same_collection_products($product->collection)($product->id)
|> function (/** @var list<WC_Product>|stdClass */ mixed $products): array {
assert(is_array($products), 'Les Produits de la même collection doivent être un tableau.');
return $products;
}
|> (static fn(/** @var list<WC_Product> */ array $products): array => Arr::map(

View file

@ -170,6 +170,7 @@ final class StarterSite extends Site {
public function maj_environnement_twig(array $options): array {
return $options;
}
// public function charge_traductions_theme(): void {
// load_theme_textdomain("haiku-atelier-2024", get_template_directory() . "/languages");
// }

View file

@ -9,7 +9,8 @@ declare(strict_types=1);
use Carbon_Fields\Container;
use Carbon_Fields\Field;
function cree_champs_personnalises_produit(): void {
function cree_champs_personnalises_produit(): void
{
Container::make('post_meta', "Product's Details")
->where('post_type', '=', 'product')
->add_fields([
@ -26,7 +27,8 @@ function cree_champs_personnalises_produit(): void {
]);
}
function cree_champ_personnalise_commande($order): void {
function cree_champ_personnalise_commande($order): void
{
woocommerce_wp_text_input([
'id' => 'tracking_number',
'label' => 'Tracking Number:',
@ -35,7 +37,8 @@ function cree_champ_personnalise_commande($order): void {
]);
}
function maj_champ_personnalise_commande($order_id): void {
function maj_champ_personnalise_commande($order_id): void
{
$order = wc_get_order($order_id);
$order->update_meta_data('tracking_number', wc_clean($_POST['tracking_number']));
$order->save();

View file

@ -8,7 +8,8 @@
declare(strict_types=1);
function enregistre_controle_personnalise_tinymce(): void {
function enregistre_controle_personnalise_tinymce(): void
{
/**
* TinyMCE Custom Control.
*
@ -17,14 +18,16 @@ function enregistre_controle_personnalise_tinymce(): void {
*
* @see https://github.com/maddisondesigns
*/
final class ControlesPersonnalises extends WP_Customize_Control {
final class ControlesPersonnalises extends WP_Customize_Control
{
/** The type of control being rendered. */
public $type = 'editeur_tinymce';
/**
* Enqueue our scripts and styles.
*/
public function enqueue(): void {
public function enqueue(): void
{
wp_enqueue_script(
handle: 'controle-personnalise-tinymce',
src: get_template_directory_uri() . '/assets/vendor/controle-personnalise-tinymce.js',
@ -38,7 +41,8 @@ function enregistre_controle_personnalise_tinymce(): void {
/**
* Render the control in the customizer.
*/
public function render_content(): void { ?>
public function render_content(): void
{ ?>
<div class="tinymce-control">
<span class="customize-control-title"><?php echo esc_html($this->label); ?></span>
<?php if (!empty($this->description)) { ?>
@ -55,7 +59,8 @@ function enregistre_controle_personnalise_tinymce(): void {
/**
* Pass our TinyMCE toolbar string to JavaScript.
*/
public function to_json(): void {
public function to_json(): void
{
parent::to_json();
$this->json['skyrockettinymcetoolbar1'] = isset($this->input_attrs['toolbar1'])

View file

@ -11,7 +11,8 @@ use function is_float;
use function is_int;
use function is_string;
final readonly class Cart {
final readonly class Cart
{
public function __construct() {}
/** La valeur par défaut d'une donnée invalide du Panier. */
@ -22,7 +23,8 @@ final readonly class Cart {
*
* @return array<int,string>
*/
public static function get_allowed_countries(): array {
public static function get_allowed_countries(): array
{
return [
'AD',
'AL',
@ -95,7 +97,8 @@ final readonly class Cart {
];
}
public static function parse_cart_value(int|float|string|bool $cart_value): string {
public static function parse_cart_value(int|float|string|bool $cart_value): string
{
if (is_int($cart_value) || is_float($cart_value)) {
return self::format_number($cart_value);
}
@ -110,7 +113,8 @@ final readonly class Cart {
return '0.00';
}
private static function format_number(int|float $number): string {
private static function format_number(int|float $number): string
{
$formatted_number = Number::format(
number: $number,
// precision et max_precision sont mutuellement exclusifs.

View file

@ -6,7 +6,8 @@ namespace HaikuAtelier\Data;
use WC_Product;
final readonly class ProductVariation {
final readonly class ProductVariation
{
/**
* @param int $id L'ID de la Variation
* @param string $price Le prix de la Variation
@ -21,7 +22,8 @@ final readonly class ProductVariation {
/**
* Créé une nouvelle instance de `ProductVariation` à partir d'un `WC_Product`.
*/
public static function new(WC_Product $product): self {
public static function new(WC_Product $product): self
{
$id = $product->get_id();
$price = $product->get_price();
/** @var list<ProductVariationAttribute> */

View file

@ -4,7 +4,8 @@ declare(strict_types=1);
namespace HaikuAtelier\Data;
final readonly class ProductVariationAttribute {
final readonly class ProductVariationAttribute
{
/**
* @param string $attribute Le slug de l'Attribut
* @param string $value Le slug de la valeur de l'Attribut

View file

@ -9,7 +9,8 @@ declare(strict_types=1);
namespace HaikuAtelier;
// Désactive divers transformations du contenu par WordPress
function desactive_wpautop(): void {
function desactive_wpautop(): void
{
remove_filter('the_content', 'wpautop');
}
@ -20,7 +21,8 @@ function desactive_wpautop(): void {
*
* @return array<string, bool> le même tableau avec des configurations en plus
*/
function desactive_transformation_contenu_tinymce(array $configuration): array {
function desactive_transformation_contenu_tinymce(array $configuration): array
{
// Ne supprime pas les retours à la ligne
$configuration['remove_linebreaks'] = false;
// Convertis les caractères de retours à la ligne en <br>
@ -39,18 +41,21 @@ function desactive_transformation_contenu_tinymce(array $configuration): array {
*
* @return array<string, string> le même tableau avec SVG en plus
*/
function autorise_import_svg_mediatheque(array $file_types): array {
function autorise_import_svg_mediatheque(array $file_types): array
{
$new_filetypes = [];
$new_filetypes['svg'] = 'image/svg+xml';
return [...$file_types, ...$new_filetypes];
}
function retire_motifs_blocs_gutenberg(): void {
function retire_motifs_blocs_gutenberg(): void
{
remove_theme_support('core-block-patterns');
}
function retire_styles_core_block(): void {
function retire_styles_core_block(): void
{
wp_dequeue_style('core-block-supports');
}

View file

@ -14,7 +14,8 @@ use function register_taxonomy;
/**
* Enregistre la Taxonomie « Collection ».
*/
function enregistre_taxonomie_collection(): void {
function enregistre_taxonomie_collection(): void
{
$labels = [
'add_new_item' => __('Add New Collection'),
'all_items' => __('All Collections'),

View file

@ -14,11 +14,13 @@ use function is_array;
use function Psl\Option\none;
use function Psl\Option\some;
final readonly class Post {
final readonly class Post
{
/**
* @return Option\Option<mixed>
*/
public static function get_post_meta(int $post_id, string $key): Option\Option {
public static function get_post_meta(int $post_id, string $key): Option\Option
{
/** @var false|mixed|string */
$value = get_post_meta($post_id, $key, true);
@ -32,7 +34,8 @@ final readonly class Post {
/**
* @return Option\Option<array<mixed>>
*/
public static function get_post_meta_array(int $post_id, string $key): Option\Option {
public static function get_post_meta_array(int $post_id, string $key): Option\Option
{
/** @var array<mixed>|false */
$value = get_post_meta($post_id, $key, false);
@ -46,7 +49,8 @@ final readonly class Post {
/**
* @return Option\Option<array<mixed>>
*/
public static function get_terms(int $post_id, string $taxonomy_name): Option\Option {
public static function get_terms(int $post_id, string $taxonomy_name): Option\Option
{
/** @var false|list<WP_Term>|WP_Error */
$terms = get_the_terms($post_id, $taxonomy_name);

View file

@ -7,13 +7,11 @@ import { getOptionOrThrowWithError } from "./utils.ts";
type ParentElement = Document | Element;
const getFirstSelectorFromParent =
(parent: ParentElement) =>
<E extends Element = Element>(selector: string): Option.Option<NonNullable<E>> =>
(parent: ParentElement) => <E extends Element = Element>(selector: string): Option.Option<NonNullable<E>> =>
Option.fromNullishOr(parent.querySelector<E>(selector));
const getFirstSelectorFromParentOrThrow =
(parent: ParentElement) =>
<E extends Element = Element>(selector: string): NonNullable<E> =>
(parent: ParentElement) => <E extends Element = Element>(selector: string): NonNullable<E> =>
pipe(
getFirstSelectorFromParent(parent)<E>(selector),
getOptionOrThrowWithError(`Il n'y a pas d'Élément dans le parent avec le sélecteur suivant : ${selector}.`),
@ -29,8 +27,7 @@ const getFirstSelectorFromDocumentOrThrow = <E extends Element = Element>(select
);
const getAllSelectorFromParent =
(parent: ParentElement) =>
<E extends Element = Element>(selector: string): Option.Option<NonEmptyReadonlyArray<E>> =>
(parent: ParentElement) => <E extends Element = Element>(selector: string): Option.Option<NonEmptyReadonlyArray<E>> =>
pipe(
parent.querySelectorAll<E>(selector),
// Convertis NodeListOf en Array.

View file

@ -1,8 +1,6 @@
import { Option, pipe } from "effect";
export const getOptionOrThrowWithError =
(message: string) =>
<T>(option: Option.Option<T>): T =>
export const getOptionOrThrowWithError = (message: string) => <T>(option: Option.Option<T>): T =>
pipe(
option,
Option.getOrThrowWith(() => new Error(message)),

View file

@ -1,18 +1,12 @@
export const forEach =
<T>(fn: (_1: T) => void) =>
(xs: Array<T>): void => {
export const forEach = <T>(fn: (_1: T) => void) => (xs: Array<T>): void => {
xs.forEach(fn);
};
};
export const forEachWithIndex =
<T>(fn: (_1: T, _2: number) => void) =>
(xs: Array<T>): void => {
export const forEachWithIndex = <T>(fn: (_1: T, _2: number) => void) => (xs: Array<T>): void => {
xs.forEach(fn);
};
};
export const map =
<T>(fn: (_1: T) => void) =>
(xs: Array<T>): Array<T> => {
export const map = <T>(fn: (_1: T) => void) => (xs: Array<T>): Array<T> => {
xs.map(fn);
return xs;
};
};

View file

@ -18,26 +18,22 @@ import {
} from "./erreurs";
export const recupereElementAvecSelecteur =
(parent: ParentElement) =>
<E extends Element = Element>(selecteur: string): Either<SyntaxError, E> =>
(parent: ParentElement) => <E extends Element = Element>(selecteur: string): Either<SyntaxError, E> =>
Either
// Retourne une SyntaxError dans un Left si le sélecteur est invalide
.encase(() => parent.querySelector<E>(selecteur))
// Transforme le Left en une erreur plus sympathique
.mapLeft((_) => creeSyntaxError(ERREUR_SYNTAXE_INVALIDE(selecteur)))
.mapLeft(_ => creeSyntaxError(ERREUR_SYNTAXE_INVALIDE(selecteur)))
// Retourne une SyntaxError si l'Élément est null
.chain((e: E | null) =>
G.isNotNullable(e) ? Right(e) : Left(creeSyntaxError(ERREUR_DOM_INEXISTANT(selecteur))),
);
.chain((e: E | null) => G.isNotNullable(e) ? Right(e) : Left(creeSyntaxError(ERREUR_DOM_INEXISTANT(selecteur))));
export const getDOMElementsWithSelector =
(parent: ParentElement) =>
<E extends Element = Element>(selecteur: string): Either<SyntaxError, Array<E>> =>
(parent: ParentElement) => <E extends Element = Element>(selecteur: string): Either<SyntaxError, Array<E>> =>
Either
// Retourne une SyntaxError dans un Left si le sélecteur est invalide
.encase(() => pipe(parent.querySelectorAll<E>(selecteur), Array.from<E>))
// Transforme le Left en une erreur plus sympathique
.mapLeft((_) => creeSyntaxError(ERREUR_SYNTAXE_INVALIDE(selecteur)))
.mapLeft(_ => creeSyntaxError(ERREUR_SYNTAXE_INVALIDE(selecteur)))
// Retourne une SyntaxError si le tableau est vide
.chain((e: Array<E>) => (A.isEmpty(e) ? Left(creeSyntaxError(ERREUR_DOM_INEXISTANT(selecteur))) : Right(e)));
@ -55,20 +51,18 @@ export const recupereElementsOuLeve = <E extends Element = Element>(
Right: identity,
});
export const majElementInnerHtml =
<T extends HTMLElement>(element: T) =>
(innerHtml: string) => {
export const majElementInnerHtml = <T extends HTMLElement>(element: T) => (innerHtml: string) => {
element.innerHTML = innerHtml;
return element;
};
};
// Merci facon (https://github.com/terkelg/facon)
export const html = (strings: TemplateStringsArray, ...args: Array<string>) =>
pipe(
document.createElement("template"),
(template) =>
template =>
majElementInnerHtml(template)(args.reduce((prev, value, i) => prev + value + strings[i + 1], strings[0])),
(template) => template.content,
template => template.content,
);
/**
@ -86,10 +80,8 @@ export const safeJsonParse = (chaine: string): Either<SyntaxError, JSONValue> =>
*
* @returns Un booléen
*/
export const targetMatchesSelector = <E extends HTMLElement = HTMLElement>(
cible: EventTarget | null,
selecteur: string,
): cible is E => cible !== null && (cible as HTMLElement).matches(selecteur);
export const targetMatchesSelector = (cible: EventTarget | null, selecteur: string): cible is HTMLElement =>
(cible as HTMLElement)?.matches(selecteur);
export const recupereElementsDocumentEither: <E extends Element = Element>(
selecteur: string,
@ -105,13 +97,11 @@ export const recupereElementDocumentEither: <E extends Element = Element>(select
* @throws Une SyntaxError si l'Élément n'est pas trouvé.
* @returns Un Élément.
*/
export const mustGetEleInDocument = <E extends Element = Element>(selecteur: string): E =>
pipe(recupereElementDocumentEither<E>(selecteur), recupereElementOuLeve);
export const mustGetEleInDocument = (selecteur: string): Element =>
pipe(recupereElementDocumentEither<Element>(selecteur), recupereElementOuLeve);
export const mustGetEleInParent =
(parent: ParentElement) =>
<E extends HTMLElement>(selector: string) =>
pipe(recupereElementAvecSelecteur(parent)<E>(selector), recupereElementOuLeve);
export const mustGetEleInParent = (parent: ParentElement) => (selector: string) =>
pipe(recupereElementAvecSelecteur(parent)<HTMLElement>(selector), recupereElementOuLeve);
/**
* Fonction utilitaire pour récupérer des Éléments selon un sélecteur au sein du Document.
@ -138,11 +128,11 @@ export const setButtonLoadingState = (button: HTMLButtonElement, isLoading: bool
};
export const estErreurHttp = (erreur: unknown) =>
erreur instanceof BadRequestError ||
erreur instanceof ForbiddenError ||
erreur instanceof NotFoundError ||
erreur instanceof ServerError ||
erreur instanceof UnauthorizedError;
erreur instanceof BadRequestError
|| erreur instanceof ForbiddenError
|| erreur instanceof NotFoundError
|| erreur instanceof ServerError
|| erreur instanceof UnauthorizedError;
export const estErreurFetch = (erreur: unknown) =>
erreur instanceof DOMException || erreur instanceof TypeError || erreur instanceof Error;

View file

@ -78,7 +78,7 @@ export const Erreur = (message: string): Error => new Error(message);
export const ErreurInconnue = (erreur: unknown): UnknownError => new UnknownError(erreur);
export const ErreurEntreeInexistante = (message: string): NonExistingKeyError => new NonExistingKeyError(message);
export const leveErreur = <E extends Error = Error>(erreur: E): never => {
export const leveErreur = (erreur: Error): never => {
throw erreur;
};
export const leveBadRequestError = (erreur: WCErrorBody): never => {
@ -106,7 +106,7 @@ export const leveNonExistingKeyError = (message: string): never => {
* @param erreur
* @returns L'ID Sentry de l'évènement capturé.
*/
export const reporteErreur = <E extends Error>(erreur: E): string => captureException(erreur);
export const reporteErreur = (erreur: Error): string => captureException(erreur);
/**
* Reporte une Erreur, sous forme d'erreur console et au service GlitchTip, puis la lève sous forme
@ -115,12 +115,12 @@ export const reporteErreur = <E extends Error>(erreur: E): string => captureExce
* @param erreur
* @returns never Lève une Erreur et ne retourne donc rien.
*/
export const reporteEtLeveErreur = <E extends Error>(erreur: E): never => {
export const reporteEtLeveErreur = (erreur: Error): never => {
reporteErreur(erreur);
throw erreur;
};
export const reporteEtJournaliseErreur = <E extends Error>(erreur: E): void => {
export const reporteEtJournaliseErreur = (erreur: Error): void => {
reporteErreur(erreur);
console.error(erreur);
if (erreur instanceof ValiError) {

View file

@ -7,12 +7,12 @@ export const CODE_PROMO_MAJ_EVENT = new CustomEvent(CODE_PROMO_MAJ, {});
// Interfaces
export type UpdatedShippingRatesEvent = {
export type UpdatedShippingRatesEvent = Event & {
detail: { refresh_methods: boolean; shipping_rates: ReadonlyArray<WCStoreShippingRateShippingRate> };
} & Event;
export type UpdatedTotalsEvent = {
};
export type UpdatedTotalsEvent = Event & {
detail: { totals: WCStoreCartTotals };
} & Event;
};
// Méthodes

View file

@ -1,8 +1,6 @@
import type { Constructor } from "./types/classes";
const estElement =
<T extends HTMLElement>(typeElement: Constructor<T>) =>
(element: unknown): element is T =>
const estElement = <T extends HTMLElement>(typeElement: Constructor<T>) => (element: unknown): element is T =>
element instanceof typeElement;
export const estHTMLSelectElement = estElement<HTMLSelectElement>(HTMLSelectElement);

View file

@ -55,30 +55,31 @@ export const emetMessageMajContenuPanier = (args: MessageMajContenuPanierDonnees
* @param message Le message émis.
* @return void
*/
export const emetUniqueMessageBroadcastChannel = <M>(nomCanal: string, message: M): void =>
export const emetUniqueMessageBroadcastChannel = (nomCanal: string, message: unknown): void => {
pipe(
new BroadcastChannel(nomCanal),
(canal) => canalPostMessage(canal, message),
(canal) => canal.close(),
canal => canalPostMessage(canal, message),
canal => canal.close(),
);
};
// Validations
export const valideMessageMajBoutonPanier = (
evenementMessage: MessageEvent<unknown>,
): Either<ValiError<typeof MessageMajBoutonPanierSchema>, MessageMajBoutonPanier> =>
Either.of<ValiError<typeof MessageMajBoutonPanierSchema>, MessageMajBoutonPanier>(
Either.of<ValiError<typeof MessageMajBoutonPanierSchema>>(
parse(MessageMajBoutonPanierSchema, evenementMessage.data),
).ifLeft((erreur) => reporteErreur(erreur));
).ifLeft(erreur => reporteErreur(erreur));
export const valideMessageMajContenuPanier = (
evenementMessage: MessageEvent<unknown>,
): Either<ValiError<typeof MessageMajContenuPanierSchema>, MessageMajContenuPanier> =>
Either.of<ValiError<typeof MessageMajContenuPanierSchema>, MessageMajContenuPanier>(
Either.of<ValiError<typeof MessageMajContenuPanierSchema>>(
parse(MessageMajContenuPanierSchema, evenementMessage.data),
).ifLeft((erreur) => reporteErreur(erreur));
).ifLeft(erreur => reporteErreur(erreur));
// Correspondances
export const reponseEstCodeErreurWC = (reponse: SimplifiedResponse, codeErreurWC: string): boolean =>
safeSchemaParse(reponse, WCErrorSchema)
.map((v) => v.body.code === codeErreurWC)
.map(v => v.body.code === codeErreurWC)
.orDefault(false);

View file

@ -5,10 +5,10 @@ export const estEntreDeuxNombres = (nombre: number, min: number, max: number): b
export const diviseParCent = (nombre: number | string): number => Number(nombre) / 100;
export const arrondisADeuxDecimales = (nombre: number | string) => pipe(Number(nombre), (n) => n.toFixed(2));
export const arrondisADeuxDecimales = (nombre: number | string) => pipe(Number(nombre), n => n.toFixed(2));
export const arrondisAZeroOuDeuxDecimales = (nombre: number | string): string =>
pipe(Number(nombre), (n) => (n / Math.round(n) === 1 ? n.toFixed(0) : n.toFixed(2)));
pipe(Number(nombre), n => (n / Math.round(n) === 1 ? n.toFixed(0) : n.toFixed(2)));
export const inverseNombre = (nombre: number | string): number => Number(nombre) * -1;

View file

@ -38,7 +38,7 @@ type ArgumentsPostBackendWC = {
// Fetch
export const getBackend = (args: ArgumentsGetBackendWC): Promise<Response> =>
export const getBackend = async (args: ArgumentsGetBackendWC): Promise<Response> =>
fetch(args.route, {
credentials: "same-origin",
headers: {
@ -53,7 +53,7 @@ export const getBackend = (args: ArgumentsGetBackendWC): Promise<Response> =>
signal: AbortSignal.timeout(5000),
});
export const getBackendAvecParametresUrl = (args: ArgumentsGetBackendWC): Promise<Response> =>
export const getBackendAvecParametresUrl = async (args: ArgumentsGetBackendWC): Promise<Response> =>
fetch(`${args.route}?${args.searchParams}`, {
credentials: "same-origin",
headers: {
@ -68,7 +68,7 @@ export const getBackendAvecParametresUrl = (args: ArgumentsGetBackendWC): Promis
signal: AbortSignal.timeout(5000),
});
export const deleteBackend = (args: ArgumentsDeleteBackendWC): Promise<Response> =>
export const deleteBackend = async (args: ArgumentsDeleteBackendWC): Promise<Response> =>
fetch(args.route, {
credentials: "same-origin",
headers: {
@ -83,7 +83,7 @@ export const deleteBackend = (args: ArgumentsDeleteBackendWC): Promise<Response>
signal: AbortSignal.timeout(5000),
});
export const postBackend = (args: ArgumentsPostBackendWC): Promise<Response> =>
export const postBackend = async (args: ArgumentsPostBackendWC): Promise<Response> =>
fetch(args.route, {
body: args.corps,
credentials: "same-origin",
@ -101,7 +101,7 @@ export const postBackend = (args: ArgumentsPostBackendWC): Promise<Response> =>
export const prefilledPostBackend =
(nonce: string, authString?: string) =>
(route: string, body: BodyInit, needsAuthString: boolean): Promise<Response> =>
async (route: string, body: BodyInit, needsAuthString: boolean): Promise<Response> =>
fetch(route, {
body: body,
credentials: "same-origin",
@ -127,9 +127,9 @@ export const newPartialResponse = async (reponse: Response): Promise<SimplifiedR
export const traiteErreursBackendWooCommerce = (rs: SimplifiedResponse): HttpCodeErrors =>
match(rs)
.with({ status: 400 }, () => new BadRequestError())
.with({ status: 401 }, () => new UnauthorizedError())
.with({ status: 403 }, () => new ForbiddenError())
.with({ status: 404 }, () => new NotFoundError())
.with({ status: 500 }, () => new ServerError())
.otherwise((rs) => new Error(String(rs.status)));
["with"]({ status: 400 }, () => new BadRequestError())
["with"]({ status: 401 }, () => new UnauthorizedError())
["with"]({ status: 403 }, () => new ForbiddenError())
["with"]({ status: 404 }, () => new NotFoundError())
["with"]({ status: 500 }, () => new ServerError())
.otherwise(rs => new Error(String(rs.status)));

View file

@ -7,7 +7,5 @@ import { Maybe } from "purify-ts";
*/
export const first = <T>(xs: Array<T>): Maybe<T> => Maybe.fromNullable(xs.at(0));
export const find =
<T>(predicateFn: (_1: T) => boolean) =>
(xs: Array<T>): Maybe<T> =>
export const find = <T>(predicateFn: (_1: T) => boolean) => (xs: Array<T>): Maybe<T> =>
Maybe.fromNullable(xs.find(predicateFn));

View file

@ -20,24 +20,24 @@ export const WCStoreCartItemTotalsSchema = v.object({
});
export const WCStoreCartItemSchema = v.object({
backorders_allowed: v.boolean(),
catalog_visibility: v.enum(CATALOG_VISIBILITIES),
backorders_allowed: v["boolean"](),
catalog_visibility: v["enum"](CATALOG_VISIBILITIES),
description: v.string(),
extensions: v.unknown(),
id: v.number(),
images: v.array(v.unknown()),
item_data: v.array(v.unknown()),
key: v.string(),
low_stock_remaining: v.union([v.number(), v.null()]),
low_stock_remaining: v.union([v.number(), v["null"]()]),
name: v.string(),
permalink: v.pipe(v.string(), v.url()),
prices: v.unknown(),
quantity: v.number(),
quantity_limits: v.unknown(),
short_description: v.string(),
show_backorder_badge: v.boolean(),
show_backorder_badge: v["boolean"](),
sku: v.string(),
sold_individually: v.boolean(),
sold_individually: v["boolean"](),
totals: WCStoreCartItemTotalsSchema,
type: v.string(),
variation: v.array(v.unknown()),
@ -60,10 +60,10 @@ export const WCStoreCartTotalsSchema = v.object({
total_items_tax: v.string(),
total_price: v.pipe(v.union([v.string(), v.number()]), v.transform(Number)),
total_shipping: v.pipe(
v.union([v.string(), v.number(), v.null()]),
v.transform((n) => (n ? Number(n) : 0)),
v.union([v.string(), v.number(), v["null"]()]),
v.transform(n => (n ? Number(n) : 0)),
),
total_shipping_tax: v.union([v.string(), v.null()]),
total_shipping_tax: v.union([v.string(), v["null"]()]),
total_tax: v.string(),
});
@ -75,12 +75,12 @@ export const WCStoreCartSchema = v.object({
errors: v.unknown(),
extensions: v.unknown(),
fees: v.unknown(),
has_calculated_shipping: v.boolean(),
has_calculated_shipping: v["boolean"](),
items: v.array(WCStoreCartItemSchema),
items_count: v.pipe(v.number(), v.integer()),
items_weight: v.pipe(v.number(), v.integer()),
needs_payment: v.boolean(),
needs_shipping: v.boolean(),
needs_payment: v["boolean"](),
needs_shipping: v["boolean"](),
payment_methods: v.unknown(),
payment_requirements: v.unknown(),
shipping_address: WCStoreShippingAddressSchema,

View file

@ -26,7 +26,7 @@ export const WCStoreShippingRateShippingRateSchema = v.object({
name: v.string(),
price: v.pipe(v.union([v.string(), v.number()]), v.transform(Number)),
rate_id: v.string(),
selected: v.boolean(),
selected: v["boolean"](),
taxes: v.string(),
});

View file

@ -24,14 +24,14 @@ export const WCAddressErrorSchema = v.object({
billing: v.optional(
v.object({
code: v.string(),
data: v.union([v.null(), v.string()]),
data: v.union([v["null"](), v.string()]),
message: v.string(),
}),
),
shipping: v.optional(
v.object({
code: v.string(),
data: v.union([v.null(), v.string()]),
data: v.union([v["null"](), v.string()]),
message: v.string(),
}),
),

View file

@ -17,36 +17,36 @@ import {
export const WCProductsArgsSchema = v.object({
// Date ISO8601
after: v.optional(v.optional(v.string())),
attribute_relation: v.optional(v.enum(ATTRIBUTES_RELATIONS)),
attribute_relation: v.optional(v["enum"](ATTRIBUTES_RELATIONS)),
attributes: v.optional(v.array(v.unknown())),
// Date ISO8601
before: v.optional(v.string()),
catalog_visibility: v.optional(v.enum(CATALOG_VISIBILITIES)),
catalog_visibility: v.optional(v["enum"](CATALOG_VISIBILITIES)),
category: v.optional(v.string()),
category_operator: v.optional(v.enum(CATEGORY_OPERATORS)),
context: v.optional(v.enum(PRODUCTS_CONTEXTES)),
date_column: v.optional(v.enum(DATE_COLUMN_VALUES)),
category_operator: v.optional(v["enum"](CATEGORY_OPERATORS)),
context: v.optional(v["enum"](PRODUCTS_CONTEXTES)),
date_column: v.optional(v["enum"](DATE_COLUMN_VALUES)),
exclude: v.optional(v.array(v.pipe(v.number(), v.integer()))),
featured: v.optional(v.boolean()),
featured: v.optional(v["boolean"]()),
include: v.optional(v.array(v.pipe(v.number(), v.integer()))),
max_price: v.optional(v.string()),
min_price: v.optional(v.string()),
offset: v.optional(v.number()),
on_sale: v.optional(v.boolean()),
order: v.optional(v.enum(ORDER_VALUES)),
orderby: v.optional(v.enum(ORDERBY_VALUES)),
on_sale: v.optional(v["boolean"]()),
order: v.optional(v["enum"](ORDER_VALUES)),
orderby: v.optional(v["enum"](ORDERBY_VALUES)),
page: v.optional(v.pipe(v.number(), v.minValue(1))),
parent: v.optional(v.array(v.pipe(v.number(), v.integer()))),
parent_exclude: v.optional(v.array(v.pipe(v.number(), v.integer()))),
per_page: v.optional(v.pipe(v.number(), v.minValue(0), v.maxValue(100))),
rating: v.optional(v.array(v.enum(RATINGS))),
rating: v.optional(v.array(v["enum"](RATINGS))),
search: v.optional(v.string()),
sku: v.optional(v.string()),
slug: v.optional(v.string()),
stock_status: v.optional(v.array(v.enum(STOCK_STATUSES))),
stock_status: v.optional(v.array(v["enum"](STOCK_STATUSES))),
tag: v.optional(v.string()),
tag_operator: v.optional(v.enum(TAG_OPERATORS)),
type: v.optional(v.enum(PRODUCT_TYPES)),
tag_operator: v.optional(v["enum"](TAG_OPERATORS)),
type: v.optional(v["enum"](PRODUCT_TYPES)),
});
export const WCProductSchema = v.object({
@ -70,7 +70,7 @@ export const WCProductSchema = v.object({
),
description: v.string(),
extensions: v.unknown(),
has_options: v.boolean(),
has_options: v["boolean"](),
id: v.number(),
images: v.array(
v.object({
@ -83,12 +83,12 @@ export const WCProductSchema = v.object({
thumbnail: v.string(),
}),
),
is_in_stock: v.boolean(),
is_on_backorder: v.boolean(),
is_purchasable: v.boolean(),
low_stock_remaining: v.union([v.number(), v.null()]),
is_in_stock: v["boolean"](),
is_on_backorder: v["boolean"](),
is_purchasable: v["boolean"](),
low_stock_remaining: v.union([v.number(), v["null"]()]),
name: v.string(),
on_sale: v.boolean(),
on_sale: v["boolean"](),
parent: v.number(),
permalink: v.string(),
price_html: v.string(),
@ -109,7 +109,7 @@ export const WCProductSchema = v.object({
short_description: v.string(),
sku: v.string(),
slug: v.string(),
sold_individually: v.boolean(),
sold_individually: v["boolean"](),
tags: v.array(v.string()),
type: v.string(),
variation: v.unknown(),

View file

@ -14,7 +14,7 @@ export const WCV3OrdersCouponLineSchema = v.object({
discount: v.string(),
discount_tax: v.string(),
discount_type: v.string(),
free_shipping: v.boolean(),
free_shipping: v["boolean"](),
id: v.pipe(v.number(), v.integer()),
meta_data: v.array(WCV3OrdersCouponLineMetaDataSchema),
nominal_amount: v.number(),
@ -37,7 +37,7 @@ export const WCV3OrdersFeeLineSchema = v.object({
meta_data: v.array(WCV3OrdersFeeLineMetaDataSchema),
name: v.string(),
tax_class: v.string(),
tax_status: v.enum(TAX_STATUSES),
tax_status: v["enum"](TAX_STATUSES),
taxes: v.array(WCV3OrdersFeeLineTaxSchema),
total: v.string(),
total_tax: v.string(),
@ -88,7 +88,7 @@ export const WCV3OrdersLineItemSchema = v.object({
image: v.optional(WCV3OrdersLineItemImageSchema),
meta_data: v.optional(v.array(WCV3OrdersLineItemMetaDataSchema)),
name: v.optional(v.string()),
parent_name: v.optional(v.union([v.string(), v.null()])),
parent_name: v.optional(v.union([v.string(), v["null"]()])),
price: v.optional(v.number()),
product_id: v.optional(v.pipe(v.number(), v.integer())),
quantity: v.optional(v.pipe(v.number(), v.integer())),
@ -116,14 +116,14 @@ export const WCV3OrdersArgsSchema = v.object({
customer_note: v.optional(v.string()),
fee_lines: v.optional(v.array(WCV3OrdersFeeLineSchema)),
line_items: v.optional(v.array(WCV3OrdersLineItemSchema)),
manual_update: v.optional(v.boolean()),
manual_update: v.optional(v["boolean"]()),
parent_id: v.optional(v.pipe(v.number(), v.integer())),
payment_method: v.optional(v.string()),
payment_method_title: v.optional(v.string()),
set_paid: v.optional(v.boolean()),
set_paid: v.optional(v["boolean"]()),
shipping: v.optional(WCStoreShippingAddressSchema),
shipping_lines: v.optional(v.array(WCV3OrdersShippingLineSchema)),
status: v.optional(v.enum(ORDER_STATUSES)),
status: v.optional(v["enum"](ORDER_STATUSES)),
transaction_id: v.optional(v.string()),
});
@ -139,37 +139,37 @@ export const WCV3OrderSchema = v.object({
customer_ip_address: v.string(),
customer_note: v.string(),
customer_user_agent: v.string(),
date_completed: v.union([v.string(), v.null()]),
date_completed_gmt: v.union([v.string(), v.null()]),
date_completed: v.union([v.string(), v["null"]()]),
date_completed_gmt: v.union([v.string(), v["null"]()]),
// Date
date_created: v.string(),
date_created_gmt: v.string(),
date_modified: v.string(),
date_modified_gmt: v.string(),
date_paid: v.union([v.string(), v.null()]),
date_paid_gmt: v.union([v.string(), v.null()]),
date_paid: v.union([v.string(), v["null"]()]),
date_paid_gmt: v.union([v.string(), v["null"]()]),
discount_tax: v.string(),
discount_total: v.string(),
fee_lines: v.array(WCV3OrdersFeeLineSchema),
id: v.pipe(v.number(), v.integer()),
is_editable: v.boolean(),
is_editable: v["boolean"](),
line_items: v.array(WCV3OrdersLineItemSchema),
meta_data: v.unknown(),
needs_payment: v.boolean(),
needs_processing: v.boolean(),
needs_payment: v["boolean"](),
needs_processing: v["boolean"](),
number: v.string(),
order_key: v.string(),
parent_id: v.pipe(v.number(), v.integer()),
payment_method: v.string(),
payment_method_title: v.string(),
payment_url: v.string(),
prices_include_tax: v.boolean(),
prices_include_tax: v["boolean"](),
refunds: v.array(v.unknown()),
shipping: WCStoreShippingAddressSchema,
shipping_lines: v.array(WCV3OrdersShippingLineSchema),
shipping_tax: v.string(),
shipping_total: v.string(),
status: v.enum(ORDER_STATUSES),
status: v["enum"](ORDER_STATUSES),
tax_lines: v.array(v.unknown()),
total: v.string(),
total_tax: v.string(),

View file

@ -21,20 +21,20 @@ export const WCV3ProductsArgsSchema = v.object({
// Date ISO8601
after: v.optional(v.string()),
attribute: v.optional(v.string()),
attribute_relation: v.optional(v.enum(ATTRIBUTES_RELATIONS)),
attribute_relation: v.optional(v["enum"](ATTRIBUTES_RELATIONS)),
attribute_term: v.optional(v.string()),
attributes: v.optional(v.array(v.unknown())),
// Date ISO8601
before: v.optional(v.string()),
catalog_visibility: v.optional(v.enum(CATALOG_VISIBILITIES)),
catalog_visibility: v.optional(v["enum"](CATALOG_VISIBILITIES)),
category: v.optional(v.string()),
category_operator: v.optional(v.enum(CATEGORY_OPERATORS)),
context: v.optional(v.enum(PRODUCTS_CONTEXTES)),
date_column: v.optional(v.enum(DATE_COLUMN_VALUES)),
dates_are_gmt: v.optional(v.boolean()),
category_operator: v.optional(v["enum"](CATEGORY_OPERATORS)),
context: v.optional(v["enum"](PRODUCTS_CONTEXTES)),
date_column: v.optional(v["enum"](DATE_COLUMN_VALUES)),
dates_are_gmt: v.optional(v["boolean"]()),
exclude: v.optional(v.array(v.pipe(v.number(), v.integer()))),
exclude_meta: v.optional(v.array(v.string())),
featured: v.optional(v.boolean()),
featured: v.optional(v["boolean"]()),
include: v.optional(v.array(v.pipe(v.number(), v.integer()))),
include_meta: v.optional(v.array(v.string())),
max_price: v.optional(v.string()),
@ -44,24 +44,24 @@ export const WCV3ProductsArgsSchema = v.object({
// Date ISO8601
modified_before: v.optional(v.string()),
offset: v.optional(v.pipe(v.number(), v.integer())),
on_sale: v.optional(v.boolean()),
order: v.optional(v.enum(ORDER_VALUES)),
orderby: v.optional(v.enum(ORDERBY_VALUES)),
on_sale: v.optional(v["boolean"]()),
order: v.optional(v["enum"](ORDER_VALUES)),
orderby: v.optional(v["enum"](ORDERBY_VALUES)),
page: v.optional(v.pipe(v.number(), v.minValue(1))),
parent: v.optional(v.array(v.pipe(v.number(), v.integer()))),
parent_exclude: v.optional(v.array(v.pipe(v.number(), v.integer()))),
per_page: v.optional(v.pipe(v.number(), v.minValue(0), v.maxValue(100))),
rating: v.optional(v.array(v.enum(RATINGS))),
rating: v.optional(v.array(v["enum"](RATINGS))),
search: v.optional(v.string()),
search_sku: v.optional(v.string()),
shipping_class: v.optional(v.string()),
sku: v.optional(v.string()),
slug: v.optional(v.string()),
status: v.optional(v.enum(PRODUCT_STATUTES)),
stock_status: v.optional(v.array(v.enum(STOCK_STATUSES))),
status: v.optional(v["enum"](PRODUCT_STATUTES)),
stock_status: v.optional(v.array(v["enum"](STOCK_STATUSES))),
tag: v.optional(v.string()),
tag_operator: v.optional(v.enum(TAG_OPERATORS)),
type: v.optional(v.enum(PRODUCT_TYPES)),
tag_operator: v.optional(v["enum"](TAG_OPERATORS)),
type: v.optional(v["enum"](PRODUCT_TYPES)),
});
export const WCV3ProductDownloadsSchema = v.object({
@ -99,8 +99,8 @@ export const WCV3ProductAttributeSchema = v.object({
name: v.string(),
options: v.array(v.string()),
position: v.pipe(v.number(), v.integer()),
variation: v.boolean(),
visible: v.boolean(),
variation: v["boolean"](),
visible: v["boolean"](),
});
export const WCV3ProductDefaultAttributeSchema = v.object({
id: v.pipe(v.number(), v.integer()),
@ -116,46 +116,46 @@ export const WCV3ProductMetaDataSchema = v.object({
export const WCV3ProductSchema = v.object({
attributes: v.array(WCV3ProductAttributeSchema),
average_rating: v.string(),
backordered: v.boolean(),
backorders: v.enum(BACKORDERS_SETTINGS),
backorders_allowed: v.boolean(),
backordered: v["boolean"](),
backorders: v["enum"](BACKORDERS_SETTINGS),
backorders_allowed: v["boolean"](),
button_text: v.string(),
catalog_visibility: v.enum(CATALOG_VISIBILITIES),
catalog_visibility: v["enum"](CATALOG_VISIBILITIES),
categories: v.array(WCV3ProductCategorySchema),
cross_sell_ids: v.array(v.pipe(v.number(), v.integer())),
date_created: v.string(),
date_created_gmt: v.string(),
date_modified: v.string(),
date_modified_gmt: v.string(),
date_on_sale_from: v.union([v.string(), v.null()]),
date_on_sale_from_gmt: v.union([v.string(), v.null()]),
date_on_sale_to: v.union([v.string(), v.null()]),
date_on_sale_to_gmt: v.union([v.string(), v.null()]),
date_on_sale_from: v.union([v.string(), v["null"]()]),
date_on_sale_from_gmt: v.union([v.string(), v["null"]()]),
date_on_sale_to: v.union([v.string(), v["null"]()]),
date_on_sale_to_gmt: v.union([v.string(), v["null"]()]),
default_attributes: v.array(WCV3ProductDefaultAttributeSchema),
description: v.string(),
dimensions: WCV3ProductDimensionsSchema,
download_expiry: v.number(),
download_limit: v.number(),
downloadable: v.boolean(),
downloadable: v["boolean"](),
downloads: v.array(WCV3ProductDownloadsSchema),
external_url: v.string(),
featured: v.boolean(),
featured: v["boolean"](),
generated_slug: v.optional(v.string()),
global_unique_id: v.string(),
grouped_products: v.array(v.pipe(v.number(), v.integer())),
has_options: v.boolean(),
has_options: v["boolean"](),
id: v.pipe(v.number(), v.integer()),
// NOTE: Ajouté par mes soins
image_repos: v.union([v.string(), v.null()]),
image_repos: v.union([v.string(), v["null"]()]),
// NOTE: Ajouté par mes soins
image_survol: v.union([v.string(), v.null()]),
image_survol: v.union([v.string(), v["null"]()]),
images: v.array(WCV3ProductImageSchema),
low_stock_amount: v.union([v.number(), v.null()]),
manage_stock: v.boolean(),
low_stock_amount: v.union([v.number(), v["null"]()]),
manage_stock: v["boolean"](),
menu_order: v.pipe(v.number(), v.integer()),
meta_data: v.array(WCV3ProductMetaDataSchema),
name: v.string(),
on_sale: v.boolean(),
on_sale: v["boolean"](),
parent_id: v.pipe(v.number(), v.integer()),
permalink: v.pipe(v.string(), v.url()),
permalink_template: v.optional(v.string()),
@ -163,32 +163,32 @@ export const WCV3ProductSchema = v.object({
price: v.string(),
price_html: v.string(),
prix_maximal: v.string(),
purchasable: v.boolean(),
purchasable: v["boolean"](),
purchase_note: v.string(),
rating_count: v.pipe(v.number(), v.integer()),
regular_price: v.string(),
related_ids: v.array(v.pipe(v.number(), v.integer())),
reviews_allowed: v.boolean(),
reviews_allowed: v["boolean"](),
sale_price: v.string(),
shipping_class: v.string(),
shipping_class_id: v.pipe(v.number(), v.integer()),
shipping_required: v.boolean(),
shipping_taxable: v.boolean(),
shipping_required: v["boolean"](),
shipping_taxable: v["boolean"](),
short_description: v.string(),
sku: v.string(),
slug: v.string(),
sold_individually: v.boolean(),
status: v.enum(PRODUCT_STATUTES),
stock_quantity: v.union([v.number(), v.null()]),
stock_status: v.enum(STOCK_STATUSES),
sold_individually: v["boolean"](),
status: v["enum"](PRODUCT_STATUTES),
stock_quantity: v.union([v.number(), v["null"]()]),
stock_status: v["enum"](STOCK_STATUSES),
tags: v.array(WCV3ProductTagSchema),
tax_class: v.string(),
tax_status: v.enum(TAX_STATUTES),
tax_status: v["enum"](TAX_STATUTES),
total_sales: v.pipe(v.number(), v.integer()),
type: v.enum(PRODUCT_TYPES),
type: v["enum"](PRODUCT_TYPES),
upsell_ids: v.array(v.pipe(v.number(), v.integer())),
variations: v.array(v.pipe(v.number(), v.integer())),
virtual: v.boolean(),
virtual: v["boolean"](),
weight: v.string(),
});

View file

@ -5,7 +5,7 @@ import * as v from "valibot";
import { TYPES_MESSAGES } from "../../constantes/messages.ts";
import { WCStoreCartItemSchema } from "./api/cart.ts";
export const TypesMessagesSchema = v.enum(TYPES_MESSAGES);
export const TypesMessagesSchema = v["enum"](TYPES_MESSAGES);
export const MessageMajBoutonPanierDonneesSchema = v.object({
quantiteProduits: v.number(),

View file

@ -35,4 +35,4 @@ export const getSessionStorageByKey = <S extends GenericSchema>(key: string, sch
export const setSessionStorageByKey =
<S extends GenericSchema>(key: string, schema: S) =>
(value: unknown): Either<DOMException | ValiError<S>, InferOutput<S>> =>
safeSchemaParse(value, schema).chain((v) => eitherSetSessionStorage(key, v));
safeSchemaParse(value, schema).chain(v => eitherSetSessionStorage(key, v));

View file

@ -1 +1 @@
export type Constructor<T> = new (...args: Array<unknown>) => T;
export type Constructor<T> = new(...args: Array<unknown>) => T;

View file

@ -7,9 +7,7 @@ import { CleNonTrouveError } from "./erreurs";
/**
* TODO
*/
export const propEither =
<T, K extends keyof T>(cle: K) =>
(donnees: T): Either<CleNonTrouveError, T[K]> =>
export const propEither = <T, K extends keyof T>(cle: K) => (donnees: T): Either<CleNonTrouveError, T[K]> =>
Maybe.fromNullable(D.getUnsafe(donnees, cle)).toEither(
new CleNonTrouveError(`La clé « ${String(cle)} » n'a pas été trouvé dans l'objet.`),
);

View file

@ -12,6 +12,5 @@ export const safeSchemaParse = <Schema extends GenericSchema>(
): Either<ValiError<Schema>, InferOutput<Schema>> => Either.encase(() => parse(schema, valeur));
export const safeSchemaParseCurried =
<S extends GenericSchema>(schema: S) =>
(valeur: unknown): Either<ValiError<S>, InferOutput<S>> =>
<S extends GenericSchema>(schema: S) => (valeur: unknown): Either<ValiError<S>, InferOutput<S>> =>
Either.encase(() => parse(schema, valeur));

View file

@ -8,15 +8,16 @@ import { ValiError } from "valibot";
import type { AnySchema } from "valibot";
import type { WCStoreBillingAddress, WCStoreShippingAddress } from "../lib/types/api/adresses.ts";
import type { WCStoreCart, WCStoreShippingRate, WCStoreShippingRateShippingRate } from "../lib/types/api/cart.ts";
import type { WCStoreCartUpdateCustomerArgs } from "../lib/types/api/cart-update-customer.ts";
import type { WCStoreCart, WCStoreShippingRate, WCStoreShippingRateShippingRate } from "../lib/types/api/cart.ts";
import type { WCV3Order, WCV3OrdersArgs } from "../lib/types/api/v3/orders.ts";
import type { GenericPageState } from "../lib/types/pages.ts";
import type { FetchErrors, HttpCodeErrors } from "../lib/types/reseau.ts";
import { Console, Effect, Stream } from "effect";
import { ReadonlyRecord } from "effect/Record";
import { ROUTE_API_MAJ_CLIENT, ROUTE_API_NOUVELLE_COMMANDES } from "../constantes/api.ts";
import { ATTRIBUT_CHARGEMENT, ATTRIBUT_LIVRAISON_VALIDEE } from "../constantes/dom.ts";
import { NOM_CANAL_REVALIDATION_LIVRAISON } from "../constantes/messages.ts";
import {
ERREUR_ADRESSE_GENERIQUE,
ERREUR_ADRESSE_MAUVAIS_CODE_POSTAL,
@ -24,6 +25,7 @@ import {
ERREUR_GENERIQUE_RESEAU,
ERREUR_GENERIQUE_SOUMISSION_ADRESSES,
} from "../constantes/messages-utilisateur.ts";
import { NOM_CANAL_REVALIDATION_LIVRAISON } from "../constantes/messages.ts";
import { estErreurFetch, estErreurHttp, setButtonLoadingState } from "../lib/dom.ts";
import { reporteEtJournaliseErreur } from "../lib/erreurs.ts";
import { ErreurAdresseInvalide } from "../lib/erreurs/adresses.ts";
@ -36,15 +38,13 @@ import { emetUniqueMessageBroadcastChannel } from "../lib/messages.ts";
import { diviseParCent } from "../lib/nombres.ts";
import { newPartialResponse, prefilledPostBackend, safeFetch, traiteErreursBackendWooCommerce } from "../lib/reseau.ts";
import { find, first } from "../lib/safe-arrays.ts";
import { WCStoreCartSchema } from "../lib/schemas/api/cart.ts";
import { WCStoreCartUpdateCustomerArgsSchema } from "../lib/schemas/api/cart-update-customer.ts";
import { WCStoreCartSchema } from "../lib/schemas/api/cart.ts";
import { estWCAddressError } from "../lib/schemas/api/erreurs.ts";
import { WCV3OrdersArgsSchema, WCV3OrderSchema } from "../lib/schemas/api/v3/orders.ts";
import { safeSchemaParse } from "../lib/validation.ts";
import { E } from "./scripts-page-panier-elements.ts";
import { getShippingRatesLS } from "./scripts-page-panier-local-storage.ts";
import { ReadonlyRecord } from "effect/Record";
import { Console, Effect, Stream } from "effect";
type Addresses = {
billing_address: WCStoreBillingAddress;
@ -60,7 +60,7 @@ const postBackend = prefilledPostBackend(ETATS_PAGE.nonce, ETATS_PAGE.authString
*
* @returns Un `Effect` ne retournant rien et ne pouvant échouer.
*/
export const initCartFormEventEmitters = Effect.fn("initCartFormEventEmitters")(function* () {
export const initCartFormEventEmitters = Effect.fn("initCartFormEventEmitters")(function*() {
return yield* pipe(
Stream.fromEventListener(E.FORMULAIRE_PANIER, "change"),
Stream.tap((event: Event) => {
@ -125,7 +125,7 @@ export const initShippingCalculationButton = (): void => {
.fromFalsy(E.FORMULAIRE_PANIER.checkValidity())
// Ne fais rien si la livraison a déjà été validée
.chainNullable((): boolean | undefined =>
E.BOUTON_ACTIONS_FORMULAIRE.hasAttribute(ATTRIBUT_LIVRAISON_VALIDEE) ? undefined : true,
E.BOUTON_ACTIONS_FORMULAIRE.hasAttribute(ATTRIBUT_LIVRAISON_VALIDEE) ? undefined : true
)
.ifJust((): void => {
event.preventDefault();
@ -133,8 +133,8 @@ export const initShippingCalculationButton = (): void => {
/** Les données du Formulaire transformées pour la requête vers le Backend. */
const formArgs: WCStoreCartUpdateCustomerArgs = pipe(
Object.fromEntries(new FormData(E.FORMULAIRE_PANIER)) as Record<string, string>,
(fields) => dictMap(fields, stringTrim),
(fields) => getAddressesFromForm(fields, E.BOUTON_SEPARATION_ADRESSES.checked),
fields => dictMap(fields, stringTrim),
fields => getAddressesFromForm(fields, E.BOUTON_SEPARATION_ADRESSES.checked),
);
// Réalise la requête et traite sa réponse
@ -142,7 +142,7 @@ export const initShippingCalculationButton = (): void => {
// Désactive le Bouton pour empêcher des requêtes concurrentes
.ifRight((): void => setButtonLoadingState(E.BOUTON_ACTIONS_FORMULAIRE, true))
.chain((args: WCStoreCartUpdateCustomerArgs) =>
safeFetch(postBackend(ROUTE_API_MAJ_CLIENT, JSON.stringify(args), false)),
safeFetch(postBackend(ROUTE_API_MAJ_CLIENT, JSON.stringify(args), false))
)
.chain((rs: Response) =>
EitherAsync<ErreurAdresseInvalide | HttpCodeErrors, unknown>(
@ -151,18 +151,18 @@ export const initShippingCalculationButton = (): void => {
.with({ status: 200 }, (rs): unknown => rs.body)
.with(
{
body: P.when((body) => estWCAddressError(body)),
body: P.when(body => estWCAddressError(body)),
status: 400,
},
(rs): never => throwE(new ErreurAdresseInvalide(rs.body.data.params)),
)
.otherwise((rs): never => throwE(traiteErreursBackendWooCommerce(rs))),
),
)
)
.chain((b: unknown) => EitherAsync.liftEither(safeSchemaParse(b, WCStoreCartSchema)))
.ifRight((cart: WCStoreCart): void => {
/** La méthode de livraison sélectionnée dans le SessionStorage */
const oldSelectedRateLS = getShippingRatesLS().chain(find((sr) => sr.selected));
const oldSelectedRateLS = getShippingRatesLS().chain(find(sr => sr.selected));
/* Les méthodes de livraison mises à jour avec le nouveau choix de l'Utilisateur. */
const updatedRates = first(cart.shipping_rates)
@ -171,7 +171,7 @@ export const initShippingCalculationButton = (): void => {
srs.map((sr: WCStoreShippingRateShippingRate, index: number) => {
// Sélectionne la nouvelle méthode demandée OU la première si le SessionStorage n'a pas été défini
oldSelectedRateLS.caseOf({
Just: (sm) => {
Just: sm => {
sr.selected = sr.method_id === sm.method_id;
},
Nothing: () => {
@ -183,7 +183,7 @@ export const initShippingCalculationButton = (): void => {
sr.price = diviseParCent(sr.price);
return sr;
}),
})
)
.orDefault([]);
@ -191,13 +191,14 @@ export const initShippingCalculationButton = (): void => {
window.dispatchEvent(createUpdatedShippingRatesEvent(updatedRates, true));
// Met à jour les Totaux
const newShippingPrice = updatedRates.find((m) => m.selected)?.price ?? 0;
const newShippingPrice = updatedRates.find(m => m.selected)?.price ?? 0;
const newTotals = {
...cart.totals,
total_discount: diviseParCent(cart.totals.total_discount),
total_items: diviseParCent(cart.totals.total_items),
total_price:
diviseParCent(cart.totals.total_items) - diviseParCent(cart.totals.total_discount) + newShippingPrice,
total_price: diviseParCent(cart.totals.total_items)
- diviseParCent(cart.totals.total_discount)
+ newShippingPrice,
total_shipping: newShippingPrice,
};
@ -222,10 +223,10 @@ export const initShippingCalculationButton = (): void => {
match(e.problemes)
.when(
// TODO: Créer une fonction utilitaire
(p) =>
p =>
pipe(
dictValues(p),
arrayFind((c) => c === "The provided postcode is not valid"),
arrayFind(c => c === "The provided postcode is not valid"),
),
// TODO: Créer une fonction utilitaire pour fixer le texte d'un message
(): void => {
@ -315,14 +316,14 @@ export const initOrderCreationButton = (): void => {
};
// Retire toute méthode de livraison invalide.
formArgs.shipping_lines = formArgs.shipping_lines.filter((line) => line.method_id !== undefined);
formArgs.shipping_lines = formArgs.shipping_lines.filter(line => line.method_id !== undefined);
// Réalise la requête et traite sa réponse
void EitherAsync.liftEither(safeSchemaParse(formArgs, WCV3OrdersArgsSchema))
// Désactive le Bouton pour empêcher des requêtes concurrentes
.ifRight((): void => setButtonLoadingState(E.BOUTON_ACTIONS_FORMULAIRE, true))
.chain((args: WCV3OrdersArgs) =>
safeFetch(postBackend(ROUTE_API_NOUVELLE_COMMANDES, JSON.stringify(args), true)),
safeFetch(postBackend(ROUTE_API_NOUVELLE_COMMANDES, JSON.stringify(args), true))
)
.chain((rs: Response) =>
EitherAsync<HttpCodeErrors, unknown>(
@ -330,7 +331,7 @@ export const initOrderCreationButton = (): void => {
match(await newPartialResponse(rs))
.with({ status: 201 }, (rs): unknown => rs.body)
.otherwise((rs): never => throwE(traiteErreursBackendWooCommerce(rs))),
),
)
)
.chain((b: unknown) => EitherAsync.liftEither(safeSchemaParse(b, WCV3OrderSchema)))
.ifRight((order: WCV3Order): void => {
@ -342,9 +343,9 @@ export const initOrderCreationButton = (): void => {
// Redirige vers Stripe
Maybe.fromNullable(new URL(`https://${window.location.host}/checkout`))
.ifJust((url) => url.searchParams.append("order_key", order.order_key))
.ifJust((url) => url.searchParams.append("order_id", String(order.id)))
.ifJust((url) => location.assign(url));
.ifJust(url => url.searchParams.append("order_key", order.order_key))
.ifJust(url => url.searchParams.append("order_id", String(order.id)))
.ifJust(url => location.assign(url));
})
.ifLeft((err: FetchErrors | HttpCodeErrors | ValiError<AnySchema>): void => {
match(err)

View file

@ -54,193 +54,34 @@ export const initialiseElementsCodePromo = (): void => {
codePromoPresent: recuperePresenceCodePromo(),
valeurCodePromo: recupereValeurCodePromo(),
})
[
// Un code promo doit être ajouté
// Aucun code promo n'est déjà présent et une valeur acceptable existe
.with(
"with"
](
{
cible: P.when((cible: EventTarget | null) =>
targetMatchesSelector<HTMLButtonElement>(cible, DOM_BOUTON_CODE_PROMO),
targetMatchesSelector<HTMLButtonElement>(cible, DOM_BOUTON_CODE_PROMO)
),
codePromoPresent: false,
valeurCodePromo: P.string,
},
({ valeurCodePromo }) =>
void EitherAsync
// Vérifie le Schéma des arguments
.liftEither(safeSchemaParse({ code: valeurCodePromo }, WCStoreCartApplyCouponArgsSchema))
.ifRight(() => {
// Désactive le Bouton pour empêcher des requêtes concurrentes
E.BOUTON_CODE_PROMO.setAttribute(ATTRIBUT_DESACTIVE, "");
E.BOUTON_CODE_PROMO.setAttribute(ATTRIBUT_CHARGEMENT, "");
// Réinitialise le Message à l'Utilisateur
E.MESSAGE_CODE_PROMO.textContent = "";
// Lance un cycle d'animation sur le texte de chargement
lanceAnimationCycleLoading(E.BOUTON_CODE_PROMO, 500);
})
// Réalise la requête auprès du backend
.map((args: WCStoreCartApplyCouponArgs) =>
postBackend({
corps: JSON.stringify(args),
nonce: ETATS_PAGE.nonce,
route: ROUTE_API_APPLIQUE_COUPON,
}),
)
// Traite les cas d'Erreur
.chain((reponse: Response) =>
EitherAsync<ErreurCodePromoInvalide | ServerError, unknown>(async ({ throwE }) => {
const reponseSimplifiee: SimplifiedResponse = {
body: await reponse.json(),
status: reponse.status,
};
return match(reponseSimplifiee)
.with({ status: 500 }, () => throwE(new ServerError("500 Server Error")))
.with(
{
body: P.when(() => reponseEstCodeErreurWC(reponseSimplifiee, ERREUR_CODE_PROMO_INVALIDE)),
status: 400,
},
() => throwE(new ErreurCodePromoInvalide(recupereValeurCodePromo() ?? "")),
)
.with({ status: 200 }, () => reponseSimplifiee.body)
.run();
}),
)
// Vérifie le Schéma de la Réponse du backend
.chain((corpsReponse: unknown) => EitherAsync.liftEither(safeSchemaParse(corpsReponse, WCStoreCartSchema)))
// Déclenche les mises à jour du DOM avec les données du nouveau Panier
.ifRight((panier: WCStoreCart) => {
E.ENSEMBLE_CODE_PROMO.toggleAttribute(ATTRIBUT_CODE_PROMO_PRESENT);
E.CHAMP_CODE_PROMO.toggleAttribute(ATTRIBUT_DESACTIVE);
E.CHAMP_CODE_PROMO.value = panier.coupons[0]?.code ?? "";
E.BOUTON_CODE_PROMO.textContent = "Remove";
E.TOTAL_PANIER.textContent = pipe(
diviseParCent(panier.totals.total_price),
arrondisADeuxDecimales,
formateEnEuros,
);
E.TOTAL_REDUCTION_LIGNE.toggleAttribute(ATTRIBUT_HIDDEN);
E.TOTAL_REDUCTION_VALEUR.textContent = pipe(
diviseParCent(panier.totals.total_discount),
inverseNombre,
arrondisADeuxDecimales,
formateEnEuros,
);
window.dispatchEvent(CODE_PROMO_MAJ_EVENT);
// EmetUniqueMessageBroadcastChannel(NOM_CANAL_REVALIDATION_LIVRAISON, true);
})
.ifLeft((erreur) => {
// Rétablis le texte d'origine
E.BOUTON_CODE_PROMO.textContent = "Apply";
// Traite les Erreurs et affiche un Message à l'Utilisateur
match(erreur)
.with(P.instanceOf(ValiError), (e) => {
reporteErreur(e);
console.error("ValiError", e.issues);
})
.with(P.instanceOf(ErreurCodePromoInvalide), (e) => {
E.MESSAGE_CODE_PROMO.textContent = "This promo code does not exist.";
reporteErreur(e);
console.error(e);
})
.with(P.instanceOf(ServerError), (e) => {
E.MESSAGE_CODE_PROMO.textContent =
"Sorry, something went wrong! Please refresh the page and try again.";
reporteErreur(e);
console.error(e);
})
.with(P.instanceOf(TypeError), (e) => {
E.MESSAGE_CODE_PROMO.textContent =
"Sorry, something went wrong! Please refresh the page and try again.";
reporteErreur(e);
console.error(e);
})
.exhaustive();
})
.finally(() => {
// Désactive l'animation de chargement et rend le Bouton de nouveau cliquable
// TODO: Créer un type d'Événement ?
E.BOUTON_CODE_PROMO.removeAttribute(ATTRIBUT_CHARGEMENT);
E.BOUTON_CODE_PROMO.removeAttribute(ATTRIBUT_DESACTIVE);
})
.run(),
({ valeurCodePromo }) => undefined,
)
[
// Un code promo doit être retiré
// Un code promo est présent sous forme de chaîne
.with(
"with"
](
{
cible: P.when((cible) => targetMatchesSelector<HTMLButtonElement>(cible, DOM_BOUTON_CODE_PROMO)),
cible: P.when(cible => targetMatchesSelector<HTMLButtonElement>(cible, DOM_BOUTON_CODE_PROMO)),
codePromoPresent: true,
valeurCodePromo: P.string,
},
({ valeurCodePromo }) =>
void EitherAsync.liftEither(safeSchemaParse({ code: valeurCodePromo }, WCStoreCartRemoveCouponArgsSchema))
.ifRight(() => {
E.BOUTON_CODE_PROMO.setAttribute(ATTRIBUT_DESACTIVE, "");
E.BOUTON_CODE_PROMO.setAttribute(ATTRIBUT_CHARGEMENT, "");
lanceAnimationCycleLoading(E.BOUTON_CODE_PROMO, 500);
})
.map((args: WCStoreCartRemoveCouponArgs) =>
postBackend({
corps: JSON.stringify(args),
nonce: ETATS_PAGE.nonce,
route: ROUTE_API_RETIRE_COUPON,
}),
)
.chain((reponse: Response) =>
EitherAsync<ServerError, unknown>(async ({ throwE }) => {
if (estReponse500(reponse)) {
throwE(new ServerError("500 server Error"));
}
return await reponse.json();
}),
)
.chain((corpsReponse: unknown) => EitherAsync.liftEither(safeSchemaParse(corpsReponse, WCStoreCartSchema)))
.ifRight((panier: WCStoreCart) => {
E.ENSEMBLE_CODE_PROMO.toggleAttribute(ATTRIBUT_CODE_PROMO_PRESENT);
E.ENSEMBLE_CODE_PROMO.reset();
E.CHAMP_CODE_PROMO.toggleAttribute(ATTRIBUT_DESACTIVE);
E.CHAMP_CODE_PROMO.textContent = "";
E.BOUTON_CODE_PROMO.textContent = "Apply";
E.TOTAL_PANIER.textContent = pipe(
diviseParCent(panier.totals.total_price),
arrondisADeuxDecimales,
formateEnEuros,
);
E.TOTAL_REDUCTION_LIGNE.toggleAttribute(ATTRIBUT_HIDDEN);
E.TOTAL_REDUCTION_VALEUR.textContent = "-0€";
emetUniqueMessageBroadcastChannel(NOM_CANAL_REVALIDATION_LIVRAISON, true);
})
.ifLeft((erreur) =>
match(erreur)
.with(P.instanceOf(ValiError), (e) => {
reporteErreur(e);
console.error("retour ajout code promo", e.issues);
})
.with(P.instanceOf(ServerError), (e) => {
reporteErreur(e);
console.error("retour ajout code promo", e);
})
.with(P.instanceOf(TypeError), (e) => {
reporteErreur(e);
console.error("retour ajout code promo", e);
})
.exhaustive(),
)
.finally(() => {
E.BOUTON_CODE_PROMO.removeAttribute(ATTRIBUT_CHARGEMENT);
E.BOUTON_CODE_PROMO.removeAttribute(ATTRIBUT_DESACTIVE);
})
.run(),
({ valeurCodePromo }) => undefined,
)
[
// Ne rien faire en dehors de ces deux situations
.with(P._, identity),
);
"with"
](P._, identity));
};

View file

@ -26,20 +26,20 @@ export const reinitialiseValidationLivraison = (): void => {
* @returns void
*/
export const souscrisEvenementsPanier = (): void => {
window.addEventListener(ADRESSES_MAJ, (): void => {
globalThis.addEventListener(ADRESSES_MAJ, (): void => {
reinitialiseValidationLivraison();
});
window.addEventListener(CODE_PROMO_MAJ, (): void => {
globalThis.addEventListener(CODE_PROMO_MAJ, (): void => {
reinitialiseValidationLivraison();
});
window.addEventListener(SHIPPING_RATES_UPDATED, (event: Event): void => {
globalThis.addEventListener(SHIPPING_RATES_UPDATED, (event: Event): void => {
Either
// La vérification du schéma se fait à l'émission
.encase(() => (event as UpdatedShippingRatesEvent).detail)
// Met à jour le DOM
.ifRight((event) => {
.ifRight(event => {
// Met à jour les Méthodes à l'Utilisateur si demandé
// Il peut y en avoir aucune
if (event.refresh_methods) {
@ -47,18 +47,18 @@ export const souscrisEvenementsPanier = (): void => {
}
})
// Met à jour le SessionStorage
.chain((event) => eitherSetSessionStorage("shipping_rates", event.shipping_rates))
.chain(event => eitherSetSessionStorage("shipping_rates", event.shipping_rates))
.ifLeft(reporteEtJournaliseErreur);
});
window.addEventListener(TOTALS_UPDATED, (event: Event): void => {
globalThis.addEventListener(TOTALS_UPDATED, (event: Event): void => {
Either
// La vérification du Schéma se fait à l'émission
.encase(() => (event as UpdatedTotalsEvent).detail.totals)
.chain((ts) => eitherSetSessionStorage("totals", ts))
.chain(ts => eitherSetSessionStorage("totals", ts))
.ifLeft(reporteEtJournaliseErreur)
// Met à jour le DOM
.ifRight((ts) => {
.ifRight(ts => {
E.SOUS_TOTAL_LIVRAISON_VALEUR.textContent = formateEnEuros(ts.total_shipping);
E.SOUS_TOTAL_PRODUITS_VALEUR.textContent = formateEnEuros(ts.total_items);
E.SOUS_TOTAL_REDUCTION_VALEUR.textContent = formateEnEuros(ts.total_discount * -1);

View file

@ -19,7 +19,7 @@ import { getShippingRatesLS } from "./scripts-page-panier-local-storage";
export const initShippingRatesChoicesActions = (): void => {
getDOMElementsWithSelector(E.CONTENEUR_METHODES_LIVRAISON)<HTMLInputElement>("input").ifRight(
forEach((el: HTMLInputElement): void =>
forEach((el: HTMLInputElement): void => {
el.addEventListener("click", (event: MouseEvent): void => {
// Récupère les méthodes du SessionStorage et les met à jour avec le nouveau choix
getShippingRatesLS()
@ -32,10 +32,10 @@ export const initShippingRatesChoicesActions = (): void => {
)
// Met à jour les Méthodes de livraison dans le SessionStorage et le DOM
.ifJust((srs: WCStoreShippingRateShippingRates): void => {
window.dispatchEvent(createUpdatedShippingRatesEvent(srs, false));
globalThis.dispatchEvent(createUpdatedShippingRatesEvent(srs, false));
})
// Met à jour les totaux dans le SessionStorage et le DOM
.chain(find((sr) => sr.selected))
.chain(find(sr => sr.selected))
.ifJust((sr: WCStoreShippingRateShippingRate): void => {
getSessionStorageByKey("totals", WCStoreCartTotalsSchema)
.ifLeft(reporteEtJournaliseErreur)
@ -45,11 +45,11 @@ export const initShippingRatesChoicesActions = (): void => {
return ts;
})
.ifRight((ts: WCStoreCartTotals): void => {
window.dispatchEvent(createUpdatedTotalsEvent(ts));
globalThis.dispatchEvent(createUpdatedTotalsEvent(ts));
});
});
});
}),
),
);
};
@ -64,12 +64,17 @@ export const generateShippingRatesHTML = (
}
// Retire les méthodes de livraison initiales
getDOMElementsWithSelector(container)("div[data-methode-initiale]").ifRight(arrayForEach((div) => div.remove()));
getDOMElementsWithSelector(container)("div[data-methode-initiale]").ifRight(
arrayForEach(div => {
div.remove();
}),
);
const selectedShippingRate: string = shippingRates.find((sr) => sr.selected)?.method_id ?? "";
const selectedShippingRate: string = shippingRates.find(sr => sr.selected)?.method_id ?? "";
const shippingRatesHTML: ReadonlyArray<TemplateResult> = arrayMap(
shippingRates,
(methode) => html` <div>
methode =>
html` <div>
<input
id="methode-livraison-${methode.method_id}"
name="choix-methode-livraison"

View file

@ -7,12 +7,13 @@ import { match, P } from "ts-pattern";
import { ValiError } from "valibot";
import type { AnySchema } from "valibot";
import type { WCStoreCart } from "../lib/types/api/cart.ts";
import type { WCStoreCartRemoveItemArgs } from "../lib/types/api/cart-remove-item.ts";
import type { WCStoreCartUpdateItemArgs } from "../lib/types/api/cart-update-item.ts";
import type { WCStoreCart } from "../lib/types/api/cart.ts";
import type { GenericPageState } from "../lib/types/pages.ts";
import type { FetchErrors, HttpCodeErrors } from "../lib/types/reseau.ts";
import { getFirstSelectorFromParentOrThrow } from "../../scripts-effect/lib/dom.ts";
import { ROUTE_API_MAJ_ARTICLE_PANIER, ROUTE_API_RETIRE_ARTICLE_PANIER } from "../constantes/api.ts";
import {
ATTRIBUT_CLE_PANIER,
@ -31,12 +32,11 @@ import {
} from "../lib/messages.ts";
import { diviseParCent } from "../lib/nombres.ts";
import { newPartialResponse, postBackend, safeFetch, traiteErreursBackendWooCommerce } from "../lib/reseau.ts";
import { WCStoreCartSchema } from "../lib/schemas/api/cart.ts";
import { WCStoreCartRemoveItemArgsSchema } from "../lib/schemas/api/cart-remove-item.ts";
import { WCStoreCartUpdateItemArgsSchema } from "../lib/schemas/api/cart-update-item.ts";
import { WCStoreCartSchema } from "../lib/schemas/api/cart.ts";
import { safeSchemaParse } from "../lib/validation.ts";
import { E } from "./scripts-page-panier-elements.ts";
import { getFirstSelectorFromParentOrThrow } from "../../scripts-effect/lib/dom.ts";
// @ts-expect-error -- États injectés par le modèle PHP
const PAGE_STATE: GenericPageState = _etats;
@ -65,8 +65,7 @@ const getCartEntryInteractiveEles = (entry: HTMLElement): CartEntryInteractiveEl
* @returns Rien.
*/
const toggleCartEntryButtons =
(activated: boolean) =>
(cartEntries: ReadonlyArray<CartEntryInteractiveElements>): void => {
(activated: boolean) => (cartEntries: ReadonlyArray<CartEntryInteractiveElements>): void => {
arrayForEach(cartEntries, (e: CartEntryInteractiveElements): void => {
if (activated) {
// Active les Boutons
@ -121,10 +120,10 @@ const initActionsOnCartEntries = (): void => {
.fromNullable(entryButtons.quantityInput.valueAsNumber)
.toEither(new Error("Quantité manquante pour cette ligne du Panier !")),
)
.chain((q) =>
.chain(q =>
EitherAsync.liftEither(
safeSchemaParse({ key: entryKey, quantity: q + 1 }, WCStoreCartUpdateItemArgsSchema),
),
)
)
.ifRight(() => {
pipe(cartEntries, arrayMap(getCartEntryInteractiveEles), toggleCartEntryButtons(false));
@ -136,14 +135,14 @@ const initActionsOnCartEntries = (): void => {
nonce: PAGE_STATE.nonce,
route: ROUTE_API_MAJ_ARTICLE_PANIER,
}),
),
)
)
.chain((r: Response) =>
EitherAsync<ServerError, unknown>(async ({ throwE }) =>
match(await newPartialResponse(r))
["with"]({ status: 200 }, (r) => r.body)
.otherwise((rs): never => throwE(traiteErreursBackendWooCommerce(rs))),
),
["with"]({ status: 200 }, r => r.body)
.otherwise((rs): never => throwE(traiteErreursBackendWooCommerce(rs)))
)
)
.chain((b: unknown) => EitherAsync.liftEither(safeSchemaParse(b, WCStoreCartSchema)))
.ifRight((c: WCStoreCart): void => {
@ -161,17 +160,17 @@ const initActionsOnCartEntries = (): void => {
})
.ifLeft((err: FetchErrors | HttpCodeErrors | ValiError<AnySchema>): void => {
match(err)
["with"](P.instanceOf(ValiError), (e) => {
["with"](P.instanceOf(ValiError), e => {
reporteErreur(e);
console.error(e.issues);
// E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_SOUMISSION_ADRESSES;
})
["with"](P.instanceOf(ServerError), P.instanceOf(BadRequestError), (e) => {
["with"](P.instanceOf(ServerError), P.instanceOf(BadRequestError), e => {
reporteErreur(e);
console.error(e);
// E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_SOUMISSION_ADRESSES;
})
["with"](P.instanceOf(DOMException), P.instanceOf(TypeError), P.instanceOf(Error), (e) => {
["with"](P.instanceOf(DOMException), P.instanceOf(TypeError), P.instanceOf(Error), e => {
reporteErreur(e);
console.error(e);
// E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_RESEAU;
@ -191,7 +190,7 @@ const initActionsOnCartEntries = (): void => {
Maybe
// Nécessaire pour que l'on ait une valeur à incrémenter
.fromNullable(entryButtons.quantityInput.valueAsNumber)
.filter((valeur) => valeur > 1)
.filter(valeur => valeur > 1)
.ifJust((valeur: number) => {
// Réalise la requête et traite sa réponse
void EitherAsync
@ -211,7 +210,7 @@ const initActionsOnCartEntries = (): void => {
nonce: PAGE_STATE.nonce,
route: ROUTE_API_MAJ_ARTICLE_PANIER,
}),
),
)
)
// 4. Traite les cas d'Erreurs et récupère le Corps de la Réponse
.chain((reponse: Response) =>
@ -220,9 +219,9 @@ const initActionsOnCartEntries = (): void => {
match(await newPartialResponse(reponse))
["with"]({ status: 500 }, () => throwE(new ServerError("500 Server Error")))
["with"]({ status: 400 }, () => throwE(new BadRequestError("400 Bad Request Error")))
["with"]({ status: 200 }, (r) => r.body)
.otherwise((erreur) => throwE(new Error(`Erreur inconnue ${String(erreur.status)}`))),
),
["with"]({ status: 200 }, r => r.body)
.otherwise(erreur => throwE(new Error(`Erreur inconnue ${String(erreur.status)}`)))
)
)
// 5. Vérifie le Schéma de la Réponse
.chain((corps: unknown) => EitherAsync.liftEither(safeSchemaParse(corps, WCStoreCartSchema)))
@ -243,17 +242,17 @@ const initActionsOnCartEntries = (): void => {
// 7. Traite les Erreurs et affiche un message à l'Utilisateur
.ifLeft((erreur: BadRequestError | FetchErrors | ServerError | ValiError<AnySchema>): void => {
match(erreur)
["with"](P.instanceOf(ValiError), (e) => {
["with"](P.instanceOf(ValiError), e => {
reporteErreur(e);
console.error(e.issues);
// E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_SOUMISSION_ADRESSES;
})
["with"](P.instanceOf(ServerError), P.instanceOf(BadRequestError), (e) => {
["with"](P.instanceOf(ServerError), P.instanceOf(BadRequestError), e => {
reporteErreur(e);
console.error(e);
// E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_SOUMISSION_ADRESSES;
})
["with"](P.instanceOf(DOMException), P.instanceOf(TypeError), P.instanceOf(Error), (e) => {
["with"](P.instanceOf(DOMException), P.instanceOf(TypeError), P.instanceOf(Error), e => {
reporteErreur(e);
console.error(e);
// E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_RESEAU;
@ -292,7 +291,7 @@ const initActionsOnCartEntries = (): void => {
nonce: PAGE_STATE.nonce,
route: ROUTE_API_RETIRE_ARTICLE_PANIER,
}),
),
)
)
// 4. Traite les cas d'Erreurs et récupère le Corps de la Réponse
.chain((reponse: Response) =>
@ -301,9 +300,9 @@ const initActionsOnCartEntries = (): void => {
match(await newPartialResponse(reponse))
["with"]({ status: 500 }, () => throwE(new ServerError("500 Server Error")))
["with"]({ status: 400 }, () => throwE(new BadRequestError("400 Bad Request Error")))
["with"]({ status: 200 }, (r) => r.body)
.otherwise((erreur) => throwE(new Error(`Erreur inconnue ${String(erreur.status)}`))),
),
["with"]({ status: 200 }, r => r.body)
.otherwise(erreur => throwE(new Error(`Erreur inconnue ${String(erreur.status)}`)))
)
)
// 5. Vérifie le Schéma de la Réponse
.chain((corps: unknown) => EitherAsync.liftEither(safeSchemaParse(corps, WCStoreCartSchema)))
@ -327,17 +326,17 @@ const initActionsOnCartEntries = (): void => {
// 7. Traite les Erreurs et affiche un message à l'Utilisateur
.ifLeft((erreur: BadRequestError | FetchErrors | ServerError | ValiError<AnySchema>): void => {
match(erreur)
["with"](P.instanceOf(ValiError), (e) => {
["with"](P.instanceOf(ValiError), e => {
reporteErreur(e);
console.error(e.issues);
// E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_SOUMISSION_ADRESSES;
})
["with"](P.instanceOf(ServerError), P.instanceOf(BadRequestError), (e) => {
["with"](P.instanceOf(ServerError), P.instanceOf(BadRequestError), e => {
reporteErreur(e);
console.error(e);
// E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_SOUMISSION_ADRESSES;
})
["with"](P.instanceOf(DOMException), P.instanceOf(TypeError), P.instanceOf(Error), (e) => {
["with"](P.instanceOf(DOMException), P.instanceOf(TypeError), P.instanceOf(Error), e => {
reporteErreur(e);
console.error(e);
// E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_RESEAU;
@ -352,10 +351,10 @@ const initActionsOnCartEntries = (): void => {
});
},
)
.otherwise((_) => {});
.otherwise(_ => {});
});
});
});
};
export { toggleCartEntryButtons, initActionsOnCartEntries as initialiseActionsEntreesPanier };
export { initActionsOnCartEntries as initialiseActionsEntreesPanier, toggleCartEntryButtons };

View file

@ -68,9 +68,9 @@ const initialiseObservationFenetre = (): void => {
}
etapePlanifiee = true;
requestAnimationFrame((): void =>
majVisibiliteBouton(defilementY > window.innerHeight * RATIO_MINIMUM_PAGE_PAR_FENETRE),
);
requestAnimationFrame((): void => {
majVisibiliteBouton(defilementY > window.innerHeight * RATIO_MINIMUM_PAGE_PAR_FENETRE);
});
});
new ResizeObserver((entrees: Array<ResizeObserverEntry>): void => {

View file

@ -2,13 +2,14 @@
import { Array as EffectArray, Match, Predicate } from "effect";
import { DOM_ENTREES_MENU_CATEGORIES_PRODUITS, DOM_MENU_CATEGORIES_PRODUITS } from "./constantes/dom.ts";
import { getAllSelectorFromDocumentOrThrow, getFirstSelectorFromDocumentOrThrow } from "../scripts-effect/lib/dom.ts";
import { DOM_ENTREES_MENU_CATEGORIES_PRODUITS, DOM_MENU_CATEGORIES_PRODUITS } from "./constantes/dom.ts";
// Initialise les attributs HTML pour l'affichage initiale des flèches de défilement du menu de catégories de Produits.
document.addEventListener("DOMContentLoaded", (): void => {
const productsCategoriesMenu: HTMLElement =
getFirstSelectorFromDocumentOrThrow<HTMLElement>(DOM_MENU_CATEGORIES_PRODUITS);
const productsCategoriesMenu: HTMLElement = getFirstSelectorFromDocumentOrThrow<HTMLElement>(
DOM_MENU_CATEGORIES_PRODUITS,
);
const menuEntries: ReadonlyArray<HTMLAnchorElement> = getAllSelectorFromDocumentOrThrow(
DOM_ENTREES_MENU_CATEGORIES_PRODUITS,
);
@ -22,17 +23,25 @@ document.addEventListener("DOMContentLoaded", (): void => {
}
new IntersectionObserver(
EffectArray.forEach((intersectionEntry) => {
EffectArray.forEach(intersectionEntry => {
// Ne déclenche rien si le scroll n'est pas horizontal
if (intersectionEntry.boundingClientRect.top <= 0) {
return;
}
Match.value([intersectionEntry.isIntersecting]).pipe(
Match.when([true, 0], () => productsCategoriesMenu.removeAttribute("data-entrees-presentes-debut")),
Match.when([true, 1], () => productsCategoriesMenu.removeAttribute("data-entrees-presentes-fin")),
Match.when([false, 0], () => productsCategoriesMenu.setAttribute("data-entrees-presentes-debut", "")),
Match.when([false, 1], () => productsCategoriesMenu.setAttribute("data-entrees-presentes-fin", "")),
Match.when([true, 0], () => {
productsCategoriesMenu.removeAttribute("data-entrees-presentes-debut");
}),
Match.when([true, 1], () => {
productsCategoriesMenu.removeAttribute("data-entrees-presentes-fin");
}),
Match.when([false, 0], () => {
productsCategoriesMenu.setAttribute("data-entrees-presentes-debut", "");
}),
Match.when([false, 1], () => {
productsCategoriesMenu.setAttribute("data-entrees-presentes-fin", "");
}),
Match.orElse(() => {}),
);
}),

View file

@ -18,19 +18,19 @@ const E = {
const initialiseBoutonMenuMobile = (): void => {
const menuMobile = new A11yDialog(E.MENU_MOBILE);
new ResizeObserver((entrees) =>
new ResizeObserver(entrees =>
// Cache le Menu mobile pour les grandes tailles d'écrans
pipe(
A.head(entrees),
O.filter((entree: ResizeObserverEntry) => entree.borderBoxSize[0]!.inlineSize > 1000),
O.tap((_) => menuMobile.hide()),
),
O.tap(_ => menuMobile.hide()),
)
).observe(E.CORPS_HTML);
E.BOUTON_MENU_MOBILE.addEventListener("click", (): void => {
// Renvoie à la Page d'accueil pour les grandes tailles d'écrans
if (window.innerWidth > 1000) {
window.location.href = "/";
globalThis.location.href = "/";
return;
}
// Cache le Menu mobile s'il est actif

View file

@ -2,7 +2,7 @@
* Scripts pour les fonctionnalités de la Page À Propos (« About »).
*/
import { A, pipe as beltPipe, O } from "@mobily/ts-belt";
import { A, O, pipe as beltPipe } from "@mobily/ts-belt";
import {
ATTRIBUT_ENSEMBLE_EPINGLE_BOITE_ACTIF,
@ -48,7 +48,11 @@ document.addEventListener("DOMContentLoaded", (): void => {
O.tap((id: string) => {
beltPipe(
O.fromNullable(ENSEMBLES_EPINGLES_BOITES_TEXTE.get(id)),
O.tap(A.forEach((element) => element.removeAttribute(ATTRIBUT_ENSEMBLE_EPINGLE_BOITE_ACTIF))),
O.tap(
A.forEach(element => {
element.removeAttribute(ATTRIBUT_ENSEMBLE_EPINGLE_BOITE_ACTIF);
}),
),
);
}),
);
@ -64,7 +68,11 @@ document.addEventListener("DOMContentLoaded", (): void => {
if (cible.hasAttribute(ATTRIBUT_ENSEMBLE_EPINGLE_BOITE_ACTIF)) {
beltPipe(
O.fromNullable(ENSEMBLES_EPINGLES_BOITES_TEXTE.get(id)),
O.tap(A.forEach((element) => element.removeAttribute(ATTRIBUT_ENSEMBLE_EPINGLE_BOITE_ACTIF))),
O.tap(
A.forEach(element => {
element.removeAttribute(ATTRIBUT_ENSEMBLE_EPINGLE_BOITE_ACTIF);
}),
),
);
return;
}
@ -73,12 +81,14 @@ document.addEventListener("DOMContentLoaded", (): void => {
beltPipe(
Array.from(ENSEMBLES_EPINGLES_BOITES_TEXTE.values()),
A.flat,
A.forEach((element) => element.removeAttribute(ATTRIBUT_ENSEMBLE_EPINGLE_BOITE_ACTIF)),
A.forEach(element => {
element.removeAttribute(ATTRIBUT_ENSEMBLE_EPINGLE_BOITE_ACTIF);
}),
);
// Active l'Attribut sur l'Ensemble
beltPipe(
O.fromNullable(ENSEMBLES_EPINGLES_BOITES_TEXTE.get(id)),
O.tap(A.forEach((element) => element.toggleAttribute(ATTRIBUT_ENSEMBLE_EPINGLE_BOITE_ACTIF))),
O.tap(A.forEach(element => element.toggleAttribute(ATTRIBUT_ENSEMBLE_EPINGLE_BOITE_ACTIF))),
);
}),
);

View file

@ -94,23 +94,25 @@ const initDefilementStorytelling = (): void => {
}).observe(E.STORYTELLING);
// Initialise la mise à jour des images au défilement sur le Conteneur.
E.STORYTELLING.addEventListener("scroll", (): void => majVisibilitéImagesStorytelling());
E.STORYTELLING.addEventListener("scroll", (): void => {
majVisibilitéImagesStorytelling();
});
};
const initGestionAnimation = (): void => {
pipe(
A.at(E.IMAGES_STORYTELLING, 0),
O.tap((img) => {
O.tap(img => {
const options: IntersectionObserverInit = {
root: undefined,
rootMargin: "0px",
threshold: 0,
};
const callback = (entries: Array<IntersectionObserverEntry>) => {
A.forEach(entries, (e) => {
e.intersectionRatio >= 0.9
? E.CONTENEUR_ANIMATION.removeAttribute(ATTRIBUT_HIDDEN)
: E.CONTENEUR_ANIMATION.setAttribute(ATTRIBUT_HIDDEN, "");
A.forEach(entries, e => {
e.intersectionRatio >= 0.9 ?
E.CONTENEUR_ANIMATION.removeAttribute(ATTRIBUT_HIDDEN) :
E.CONTENEUR_ANIMATION.setAttribute(ATTRIBUT_HIDDEN, "");
});
};

View file

@ -13,6 +13,7 @@ import type { WCV3Products, WCV3ProductsArgs } from "./lib/types/api/v3/products
import type { GenericPageState } from "./lib/types/pages";
import { ROUTE_API_NOUVELLE_PRODUCTS } from "./constantes/api.ts";
import { PRODUCT_STATUTES } from "./constantes/api/products.ts";
import {
ATTRIBUT_CHARGEMENT,
ATTRIBUT_DESACTIVE,
@ -28,7 +29,6 @@ import { BadRequestError, reporteErreur, ServerError } from "./lib/erreurs.ts";
import { getBackendAvecParametresUrl, newPartialResponse } from "./lib/reseau.ts";
import { WCV3ProductsArgsSchema, WCV3ProductsSchema } from "./lib/schemas/api/v3/products.ts";
import { safeSchemaParse } from "./lib/validation.ts";
import { PRODUCT_STATUTES } from "./constantes/api/products.ts";
type APIProductsErrors =
| APIFetchErrors
@ -67,109 +67,7 @@ const initialisePageBoutique = (): void => {
...(idCategorieProduits && { category: idCategorieProduits }),
};
void EitherAsync
// 1. Valide les Arguments de la Requête
.liftEither(safeSchemaParse(args, WCV3ProductsArgsSchema))
// 2. Exécute un Effet pour empêcher les requêtes concurrentes et lancer une animation de chargement
.ifRight((): void => {
// Désactive le Bouton pour empêcher des requêtes concurrentes
E.BOUTON_PLUS_DE_PRODUITS.setAttribute(ATTRIBUT_DESACTIVE, "");
E.BOUTON_PLUS_DE_PRODUITS.setAttribute(ATTRIBUT_CHARGEMENT, "");
// Lance un cycle d'animation sur le texte de chargement
lanceAnimationCycleLoading(E.BOUTON_PLUS_DE_PRODUITS, 500);
})
// 3. Exécute la requête via fetch sous forme d'EitherAsync
.chain((args: WCV3ProductsArgs) =>
EitherAsync<DOMException | Error, Response>(() =>
getBackendAvecParametresUrl({
authString: ETATS_PAGE.authString,
nonce: ETATS_PAGE.nonce,
route: ROUTE_API_NOUVELLE_PRODUCTS,
searchParams: new URLSearchParams(args).toString(),
}),
),
)
// 4. Traite les cas d'Erreurs et récupère le Corps de la Réponse
.chain((reponse: Response) =>
EitherAsync<APIFetchErrors, unknown>(async ({ throwE }) =>
match(await newPartialResponse(reponse))
.with({ status: 500 }, () => throwE(new ServerError("500 Server Error")))
.with({ status: 400 }, () => throwE(new BadRequestError("400 Server Error")))
.with({ status: 200 }, (r) => r.body)
.run(),
),
)
// 5. Vérifie le Schéma de la Réponse
.chain((corpsReponse: unknown) => EitherAsync.liftEither(safeSchemaParse(corpsReponse, WCV3ProductsSchema)))
// 6. Exécute un Effet pour la mise à jour du DOM avec les Résultats
.ifRight((donnees: WCV3Products) => {
// Cache le bouton s'il y a moins de PRODUCTS_PER_PAGE Produits disponibles (que l'on est à la dernière page)
if (donnees.length < PRODUCTS_PER_PAGE) {
E.BOUTON_PLUS_DE_PRODUITS.toggleAttribute(ATTRIBUT_HIDDEN);
}
// Créé un DocumentFragment qui recevra tous les nouveaux Produits
const fragment: DocumentFragment = document.createDocumentFragment();
// Créé les Éléments <article> à insérer
for (const produit of donnees.slice(0, PRODUCTS_PER_PAGE)) {
pipe(
html`
<article class="produit">
<figure>
<a href="/product/${produit.slug}">
<picture class="produit__illustration produit__illustration__principale">
${produit.image_repos ?? ""}
</picture>
<picture class="produit__illustration produit__illustration__survol">
${produit.image_survol ?? ""}
</picture>
</a>
<figcaption class="produit__textuel">
<h3 class="produit__textuel__titre">
<a href="${produit.permalink}">${produit.name}</a>
</h3>
<p class="produit__textuel__prix">
${produit.prix_maximal}
</p>
</figcaption>
</figure>
</article>
`,
tap((article) => fragment.append(article)),
);
}
// Ajoute les nouveaux Produits dans le DOM
E.GRILLE_PRODUITS.append(fragment);
E.GRILLE_PRODUITS.setAttribute(ATTRIBUT_PAGE, String(nouveauNumeroPage));
E.BOUTON_PLUS_DE_PRODUITS.textContent = "Show more";
})
// 7. Traite les Erreurs et affiche un Message à l'Utilisateur
.ifLeft((erreur: APIProductsErrors) => {
match(erreur)
.with(P.instanceOf(ValiError), (e) => {
reporteErreur(e);
console.error("ValiError", e.issues);
})
.otherwise((e) => {
reporteErreur(e);
console.error("Erreur", e);
});
E.BOUTON_PLUS_DE_PRODUITS.textContent = "Error, try again?";
})
// 8. Quel que soit le résultat, réactiver le Bouton et arrêter l'animation
.finally(() => {
// Désactive l'animation de chargement et rend le Bouton de nouveau cliquable
E.BOUTON_PLUS_DE_PRODUITS.removeAttribute(ATTRIBUT_CHARGEMENT);
E.BOUTON_PLUS_DE_PRODUITS.removeAttribute(ATTRIBUT_DESACTIVE);
})
.run();
undefined;
});
};

View file

@ -8,6 +8,8 @@ import type { MessageMajContenuPanierSchema } from "./lib/schemas/messages.ts";
import type { WCStoreCartItem } from "./lib/types/api/cart";
import type { MessageMajBoutonPanierDonnees, MessageMajContenuPanierDonnees } from "./lib/types/messages";
import { Effect } from "effect";
import { Effect } from "effect";
import {
ATTRIBUT_CLE_PANIER,
ATTRIBUT_CONTIENT_ARTICLES,
@ -36,7 +38,6 @@ import { E } from "./page-panier/scripts-page-panier-elements.ts";
import { souscrisEvenementsPanier } from "./page-panier/scripts-page-panier-evenement.ts";
import { initShippingRatesChoicesActions } from "./page-panier/scripts-page-panier-methodes-livraison.ts";
import { initialiseActionsEntreesPanier } from "./page-panier/scripts-page-panier-panneau-produits.ts";
import { Effect } from "effect";
type ElementsEntreePanier = {
boutonAddition: HTMLButtonElement;
@ -54,7 +55,6 @@ type EtatsPage = {
// @ts-expect-error -- États injectés par le modèle PHP
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- États injectés par le modèle PHP
const ETATS_PAGE: EtatsPage = _etats;
/**
* Fonction utilitaire pour récupérer un Élément dans une ligne (entrée) du Panier, en levant une
@ -64,13 +64,11 @@ const ETATS_PAGE: EtatsPage = _etats;
* @returns L'Élément demandé.
* @throws Une SyntaxError si l'Élément n'est pas trouvé.
*/
const recupereElementDansEntreePanierOuLeve =
(entree: HTMLElement) =>
<E extends Element = Element>(selecteur: string) =>
pipe(recupereElementAvecSelecteur(entree)<E>(selecteur), recupereElementOuLeve);
const recupereElementDansEntreePanierOuLeve = (entree: HTMLElement) => (selecteur: string) =>
pipe(recupereElementAvecSelecteur(entree)<Element>(selecteur), recupereElementOuLeve);
// NOTE: Nécessaire pour éviter une condition de course entre la réussite de la requête et l'émission effective du Message
const majEtatsActivationBoutons = (entrees: Array<HTMLElement>): void =>
const majEtatsActivationBoutons = (entrees: Array<HTMLElement>): void => {
entrees.forEach((entree: HTMLElement) => {
// Fonction utilitaire
const recupereElementDansEntree = recupereElementDansEntreePanierOuLeve(entree);
@ -82,13 +80,14 @@ const majEtatsActivationBoutons = (entrees: Array<HTMLElement>): void =>
champQuantite: recupereElementDansEntree<HTMLInputElement>(DOM_CHAMP_QUANTITE_LIGNE_PANIER),
};
Number(elements.champQuantite?.value) === 1
? elements.boutonSoustraction.setAttribute(ATTRIBUT_DESACTIVE, "")
: elements.boutonSoustraction.removeAttribute(ATTRIBUT_DESACTIVE);
Number(elements.champQuantite?.value) === 1 ?
elements.boutonSoustraction.setAttribute(ATTRIBUT_DESACTIVE, "") :
elements.boutonSoustraction.removeAttribute(ATTRIBUT_DESACTIVE);
elements.boutonAddition.removeAttribute(ATTRIBUT_DESACTIVE);
elements.boutonSuppression.removeAttribute(ATTRIBUT_DESACTIVE);
elements.boutonSuppression.textContent = "Remove";
});
};
const initialiseMajConteneurPanier = (): void => {
new BroadcastChannel(NOM_CANAL_BOUTON_PANIER).onmessage = (evenementMessage: MessageEvent<unknown>): void => {
@ -111,7 +110,7 @@ const initialiseMajContenuPanier = (): void => {
donnees.produits.forEach((ligne: WCStoreCartItem) => {
// Met à jour les entrées du Panier
E.ENTREES_PANIER.ifRight((entrees: Array<HTMLElement>) => {
Maybe.fromNullable(entrees.find((entree) => entree.getAttribute(ATTRIBUT_CLE_PANIER) === ligne.key)).ifJust(
Maybe.fromNullable(entrees.find(entree => entree.getAttribute(ATTRIBUT_CLE_PANIER) === ligne.key)).ifJust(
(entree: HTMLElement) => {
// Fonction utilitaire
const recupereElementDansEntree = recupereElementDansEntreePanierOuLeve(entree);
@ -144,7 +143,9 @@ const initialiseMajContenuPanier = (): void => {
// Reporte tout Erreur et réactive les Boutons
.ifLeft((erreur: CleNonTrouveError | ValiError<typeof MessageMajContenuPanierSchema>) => {
reporteErreur(erreur);
E.ENTREES_PANIER.ifRight((entrees) => majEtatsActivationBoutons(entrees));
E.ENTREES_PANIER.ifRight(entrees => {
majEtatsActivationBoutons(entrees);
});
});
};
};
@ -157,7 +158,9 @@ const initialiseMajFormulairesPanier = (): void => {
// Rend visible le formulaire de facturation.
E.FORMULAIRE_FACTURATION.removeAttribute(ATTRIBUT_HIDDEN);
getDOMElementsWithSelector(E.FORMULAIRE_FACTURATION)("input, select").ifRight(
arrayForEach((champ) => champ.removeAttribute(ATTRIBUT_DESACTIVE)),
arrayForEach(champ => {
champ.removeAttribute(ATTRIBUT_DESACTIVE);
}),
);
})
// Les Adresses sont combinées.
@ -167,7 +170,7 @@ const initialiseMajFormulairesPanier = (): void => {
getDOMElementsWithSelector(E.FORMULAIRE_FACTURATION)<HTMLInputElement | HTMLSelectElement>(
"input, select",
).ifRight(
arrayForEach((champ) => {
arrayForEach(champ => {
champ.setAttribute(ATTRIBUT_DESACTIVE, "");
champ.value = "";
}),

View file

@ -0,0 +1,132 @@
import { Array as FxArray, Console, Effect, HashMap, Layer, ManagedRuntime, Option, pipe, ServiceMap } from "effect";
import { NonEmptyReadonlyArray } from "effect/Array";
import { NoSuchElementError } from "effect/Cause";
import { getAllSelectorFromDocument, getFirstSelectorFromDocument } from "../scripts-effect/lib/dom.ts";
import {
ATTRIBUT_ARIA_CONTROLS,
ATTRIBUT_ARIA_EXPANDED,
ATTRIBUT_HIDDEN,
DOM_BOUTON_AJOUT_PANIER,
DOM_BOUTONS_ACCORDEON,
DOM_CONTENUS_ACCORDEON,
DOM_PRIX_PRODUIT,
} from "./constantes/dom.ts";
import { WCStoreCartAddItemArgsItems } from "./lib/types/api/cart-add-item";
/** Représente un ensemble bouton-contenu d'une Section dans la description du Produit. */
type DetailEnsemble = {
button: HTMLButtonElement;
content: HTMLDivElement;
};
class ProductPageElements extends ServiceMap.Service<
ProductPageElements,
{
AddProductButton: HTMLButtonElement;
DetailsButtons: NonEmptyReadonlyArray<HTMLButtonElement>;
DetailsContents: NonEmptyReadonlyArray<HTMLDivElement>;
Details: HashMap.HashMap<string, DetailEnsemble>;
ProductPrice: HTMLParagraphElement;
ProductRawJson: HTMLScriptElement;
VariationChoiceForm: HTMLFormElement;
VariationSelectors: ReadonlyArray<HTMLSelectElement>;
}
>()("haikuatelier.fr/Produit/ProductPageElements") {
static readonly layer = Layer.effect(
ProductPageElements,
Effect.gen(function*() {
const AddProductButton = yield* getFirstSelectorFromDocument<HTMLButtonElement>(DOM_BOUTON_AJOUT_PANIER);
const DetailsButtons = yield* getAllSelectorFromDocument<HTMLButtonElement>(DOM_BOUTONS_ACCORDEON);
const DetailsContents = yield* getAllSelectorFromDocument<HTMLDivElement>(DOM_CONTENUS_ACCORDEON);
const ProductPrice = yield* getFirstSelectorFromDocument<HTMLParagraphElement>(DOM_PRIX_PRODUIT);
const ProductRawJson = yield* getFirstSelectorFromDocument<HTMLScriptElement>("#product-json");
const VariationChoiceForm = yield* getFirstSelectorFromDocument<HTMLFormElement>("#variation-choice");
const VariationSelectors = yield* pipe(
getAllSelectorFromDocument<HTMLSelectElement>(".selecteur-produit select"),
Option.orElseSome(() => FxArray.empty<HTMLSelectElement>()),
);
const Details = yield* pipe(
DetailsButtons,
FxArray.map(
(button: HTMLButtonElement, index: number): Effect.Effect<[string, DetailEnsemble], NoSuchElementError> =>
Effect.gen(function*() {
const contentId = yield* Option.fromNullishOr(button.getAttribute(ATTRIBUT_ARIA_CONTROLS));
const content = yield* FxArray.get(DetailsContents, index);
return [contentId, { button, content } satisfies DetailEnsemble];
}),
),
Effect.all,
Effect.map(HashMap.fromIterable<string, DetailEnsemble>),
);
return ProductPageElements.of({
AddProductButton,
DetailsButtons,
DetailsContents,
Details,
ProductPrice,
ProductRawJson,
VariationChoiceForm,
VariationSelectors,
});
}),
);
}
class ProductPageDOM extends ServiceMap.Service<
ProductPageDOM,
{
/**
* Replie toutes les sections de la description du Produit.
*/
toggleAllDetails: () => Effect.Effect<void>;
/**
* Récupère les Attributs du Produit depuis les Elements au sein du DOM.
*/
getProductAttributesFromDOM: () => Effect.Effect<ReadonlyArray<WCStoreCartAddItemArgsItems>>;
}
>()("haikuatelier.fr/Produit/ProductPageDOM") {
static readonly layer = Layer.effect(
ProductPageDOM,
Effect.gen(function*() {
const { Details, VariationSelectors } = yield* ProductPageElements;
const toggleAllDetails: () => Effect.Effect<void> = () =>
Effect.sync((): void => {
pipe(
// Récupère les Sections sous forme d'Ensembles.
[...HashMap.values(Details)],
FxArray.forEach((detail: DetailEnsemble) => {
detail.button.toggleAttribute(ATTRIBUT_ARIA_EXPANDED, false);
detail.content.toggleAttribute(ATTRIBUT_HIDDEN, true);
}),
);
});
const getProductAttributesFromDOM: () => Effect.Effect<ReadonlyArray<WCStoreCartAddItemArgsItems>> = () =>
Effect.sync(() =>
FxArray.map(VariationSelectors, (select: HTMLSelectElement) => ({
attribute: select.id,
value: select.value,
}))
);
return ProductPageDOM.of({
getProductAttributesFromDOM,
toggleAllDetails,
});
}),
);
}
const ProductPageRuntime = ManagedRuntime.make(
pipe(
ProductPageDOM.layer,
Layer.provide(ProductPageElements.layer),
Layer.tapError(error => Console.error("ManagedRuntime", "Impossible de créer le Layer :", error.name)),
),
);
export { type DetailEnsemble, ProductPageDOM, ProductPageElements, ProductPageRuntime };

View file

@ -3,25 +3,14 @@
import { pipe } from "@mobily/ts-belt";
import { get as dictGet } from "@mobily/ts-belt/Dict";
import { tap as optionTap } from "@mobily/ts-belt/Option";
import {
Array as FxArray,
Effect,
pipe as epipe,
Option,
Stream,
ServiceMap,
Layer,
ManagedRuntime,
Console,
HashMap,
} from "effect";
import { Array as FxArray, Console, Effect, HashMap, Option, pipe as epipe, Stream } from "effect";
import { EitherAsync } from "purify-ts";
import { match, P } from "ts-pattern";
import { ValiError } from "valibot";
import type { AnySchema } from "valibot";
import type { WCStoreCart } from "./lib/types/api/cart.ts";
import type { WCStoreCartAddItemArgs, WCStoreCartAddItemArgsItems } from "./lib/types/api/cart-add-item.ts";
import type { WCStoreCart } from "./lib/types/api/cart.ts";
import type { FetchErrors } from "./lib/types/reseau.ts";
import { ROUTE_API_AJOUTE_ARTICLE_PANIER } from "./constantes/api.ts";
@ -45,14 +34,7 @@ import { newPartialResponse, postBackend, safeFetch } from "./lib/reseau.ts";
import { WCStoreCartAddItemArgsSchema } from "./lib/schemas/api/cart-add-item.ts";
import { WCStoreCartSchema } from "./lib/schemas/api/cart.ts";
import { safeSchemaParse } from "./lib/validation";
import { getAllSelectorFromDocument, getFirstSelectorFromDocument } from "../scripts-effect/lib/dom.ts";
import { NonEmptyReadonlyArray } from "effect/Array";
import { NoSuchElementError } from "effect/Cause";
type DetailEnsemble = {
button: HTMLButtonElement;
content: HTMLDivElement;
};
import { ProductPageElements, ProductPageRuntime } from "./scripts-page-produit-service.ts";
/** États utiles pour les scripts de la page. */
type EtatsPage = {
@ -65,69 +47,6 @@ type EtatsPage = {
// @ts-expect-error -- États injectés par le modèle PHP
const ETATS_PAGE: EtatsPage = _etats;
class ProductPageElements extends ServiceMap.Service<
ProductPageElements,
{
AddProductButton: HTMLButtonElement;
DetailsButtons: NonEmptyReadonlyArray<HTMLButtonElement>;
DetailsContents: NonEmptyReadonlyArray<HTMLDivElement>;
Details: HashMap.HashMap<string, DetailEnsemble>;
ProductPrice: HTMLParagraphElement;
ProductRawJson: HTMLScriptElement;
VariationChoiceForm: HTMLFormElement;
VariationSelectors: ReadonlyArray<HTMLSelectElement>;
}
>()("haikuatelier.fr/Produit/ProductPageElements") {
static readonly layer = Layer.effect(
ProductPageElements,
Effect.gen(function* () {
const AddProductButton = yield* getFirstSelectorFromDocument<HTMLButtonElement>(DOM_BOUTON_AJOUT_PANIER);
const DetailsButtons = yield* getAllSelectorFromDocument<HTMLButtonElement>(DOM_BOUTONS_ACCORDEON);
const DetailsContents = yield* getAllSelectorFromDocument<HTMLDivElement>(DOM_CONTENUS_ACCORDEON);
const ProductPrice = yield* getFirstSelectorFromDocument<HTMLParagraphElement>(DOM_PRIX_PRODUIT);
const ProductRawJson = yield* getFirstSelectorFromDocument<HTMLScriptElement>("#product-json");
const VariationChoiceForm = yield* getFirstSelectorFromDocument<HTMLFormElement>("#variation-choice");
const VariationSelectors = yield* pipe(
getAllSelectorFromDocument<HTMLSelectElement>(".selecteur-produit select"),
Option.orElseSome(() => FxArray.empty<HTMLSelectElement>()),
);
const Details = yield* pipe(
DetailsButtons,
FxArray.map(
(button: HTMLButtonElement, index: number): Effect.Effect<[string, DetailEnsemble], NoSuchElementError> =>
Effect.gen(function* () {
const contentId = yield* Option.fromNullishOr(button.getAttribute(ATTRIBUT_ARIA_CONTROLS));
const content = yield* FxArray.get(DetailsContents, index);
return [contentId, { button, content } satisfies DetailEnsemble];
}),
),
Effect.all,
Effect.map(HashMap.fromIterable<string, DetailEnsemble>),
);
return {
AddProductButton,
DetailsButtons,
DetailsContents,
Details,
ProductPrice,
ProductRawJson,
VariationChoiceForm,
VariationSelectors,
};
}),
);
}
const ProductPageRuntime = ManagedRuntime.make(
pipe(
ProductPageElements.layer,
Layer.tapError((error) => Console.error("ManagedRuntime", "Impossible de créer le Layer :", error.name)),
),
);
// Éléments d'intérêt
const E = {
BOUTON_AJOUT_PANIER: mustGetEleInDocument<HTMLButtonElement>(DOM_BOUTON_AJOUT_PANIER),
@ -139,35 +58,6 @@ const E = {
VARIATION_CHOICE_FORM: mustGetEleInDocument<HTMLFormElement>("#variation-choice"),
};
const toggleAllDetails = Effect.fn("toggleAllDetails")(function* () {
const PageElements = yield* ProductPageElements;
// Récupère les Ensembles sous forme de tableau.
const details = [...HashMap.values(PageElements.Details)];
FxArray.forEach(details, (detail: DetailEnsemble) => {
detail.button.toggleAttribute(ATTRIBUT_ARIA_EXPANDED, false);
detail.content.toggleAttribute(ATTRIBUT_HIDDEN, true);
});
});
// TODO: Utiliser Effect.
const getAttributesFromDom = (): ReadonlyArray<WCStoreCartAddItemArgsItems> => {
const selectElements = epipe(
document.querySelectorAll<HTMLSelectElement>(".selecteur-produit select"),
Array.from<HTMLSelectElement>,
);
if (selectElements.length === 0) {
return [];
}
const attributes = selectElements.map((select: HTMLSelectElement) => ({
attribute: select.id,
value: select.value,
}));
return attributes;
};
const areArraysEqual = <T>(array1: Array<T>, array2: Array<T>): boolean => {
if (array1 !== array2) {
const a1 = JSON.stringify(array1.toSorted());
@ -186,7 +76,7 @@ const updatePriceOnAttributeChange = (): void => {
const productVariations: Array<unknown> = epipe(E.PRODUCT_JSON.textContent, JSON.parse)?.variations;
const chosenAttributes = getAttributesFromDom();
const chosenVariation = productVariations.find((v) => areArraysEqual(v.attributes, chosenAttributes));
const chosenVariation = productVariations.find(v => areArraysEqual(v.attributes, chosenAttributes));
const newPrice: string = chosenVariation.price;
E.PRIX_PRODUIT.textContent = `${newPrice}`;
@ -227,7 +117,7 @@ const ajouteProduitAuPanier = (event: MouseEvent): void => {
nonce: ETATS_PAGE.nonce,
route: ROUTE_API_AJOUTE_ARTICLE_PANIER,
}),
),
)
)
// 4. Traite les cas d'Erreurs et récupère le Corps de la Réponse
.chain((reponse: Response) =>
@ -236,9 +126,9 @@ const ajouteProduitAuPanier = (event: MouseEvent): void => {
match(await newPartialResponse(reponse))
.with({ status: 500 }, () => throwE(new ServerError("500 Server Error")))
.with({ status: 400 }, () => throwE(new BadRequestError("400 Bad Request Error")))
.with({ status: 201 }, (r) => r.body)
.otherwise((erreur) => throwE(new Error(`Erreur inconnue ${String(erreur.status)}`))),
),
.with({ status: 201 }, r => r.body)
.otherwise(erreur => throwE(new Error(`Erreur inconnue ${String(erreur.status)}`)))
)
)
// 5. Vérifie le Schéma de la Réponse
.chain((corpsReponse: unknown) => EitherAsync.liftEither(safeSchemaParse(corpsReponse, WCStoreCartSchema)))
@ -250,21 +140,21 @@ const ajouteProduitAuPanier = (event: MouseEvent): void => {
E.BOUTON_AJOUT_PANIER.textContent = "Added to cart!";
emetMessageMajBoutonPanier({ quantiteProduits: totalArticles });
}),
),
)
)
.ifLeft((erreur: BadRequestError | FetchErrors | ServerError | ValiError<AnySchema>) => {
match(erreur)
.with(P.instanceOf(ValiError), (e) => {
.with(P.instanceOf(ValiError), e => {
reporteErreur(e);
console.error(e.issues);
// E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_SOUMISSION_ADRESSES;
})
.with(P.instanceOf(ServerError), P.instanceOf(BadRequestError), (e) => {
.with(P.instanceOf(ServerError), P.instanceOf(BadRequestError), e => {
reporteErreur(e);
console.error(e);
// E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_SOUMISSION_ADRESSES;
})
.with(P.instanceOf(DOMException), P.instanceOf(TypeError), P.instanceOf(Error), (e) => {
.with(P.instanceOf(DOMException), P.instanceOf(TypeError), P.instanceOf(Error), e => {
reporteErreur(e);
console.error(e);
// E.MESSAGE_ADRESSES.textContent = ERREUR_GENERIQUE_RESEAU;
@ -284,7 +174,7 @@ const ajouteProduitAuPanier = (event: MouseEvent): void => {
/**
* Initialise l'état initial d'interactivité du Bouton d'ajout de Produit au Panier.
*/
const initAddToCartButton = Effect.fn("initAddToCartButton")(function* () {
const initAddToCartButton = Effect.fn("initAddToCartButton")(function*() {
const { AddProductButton, VariationSelectors } = yield* ProductPageElements;
/** Est-ce que le Produit affiché est en stock ? */
const isProductInStock = AddProductButton.hasAttribute("data-in-stock") === true;
@ -304,7 +194,7 @@ const initAddToCartButton = Effect.fn("initAddToCartButton")(function* () {
return yield* Effect.void;
});
const onFormChange = Effect.fnUntraced(function* (event: Event) {
const onFormChange = Effect.fnUntraced(function*(event: Event) {
const { AddProductButton } = yield* ProductPageElements;
// La cible ne peut qu'être un Formulaire.
const target = event.target as HTMLFormElement;
@ -319,7 +209,7 @@ const onFormChange = Effect.fnUntraced(function* (event: Event) {
/**
* Initialise la mise à jour de l'état d'interactivité du Bouton d'ajout de Produit au Panier en fonction des actions de l'Utilisateur.
*/
const initAddToCartInteractionUpdates = Effect.fn("initAddToCartInteractionUpdates")(function* () {
const initAddToCartInteractionUpdates = Effect.fn("initAddToCartInteractionUpdates")(function*() {
return yield* pipe(
Stream.fromEventListener(E.VARIATION_CHOICE_FORM, "change"),
Stream.tap(onFormChange),
@ -327,7 +217,7 @@ const initAddToCartInteractionUpdates = Effect.fn("initAddToCartInteractionUpdat
);
});
const onDetailButtonClick = Effect.fnUntraced(function* (event: Event) {
const onDetailButtonClick = Effect.fnUntraced(function*(event: Event) {
const { Details } = yield* ProductPageElements;
// Empêche la pollution de l'historique de navigation
event.preventDefault();
@ -357,18 +247,36 @@ const onDetailButtonClick = Effect.fnUntraced(function* (event: Event) {
return yield* Effect.void;
});
const initDetailInteractions = Effect.fn("initDetailInteractions")(function* () {
const initDetailInteractions = Effect.fn("initDetailInteractions")(function*() {
const PageElements = yield* ProductPageElements;
return yield* pipe(
FxArray.map(PageElements.DetailsButtons, (button: HTMLButtonElement) =>
pipe(Stream.fromEventListener(button, "click"), Stream.tap(onDetailButtonClick)),
FxArray.map(
PageElements.DetailsButtons,
(button: HTMLButtonElement) => pipe(Stream.fromEventListener(button, "click"), Stream.tap(onDetailButtonClick)),
),
Stream.mergeAll({ concurrency: "unbounded" }),
Stream.runDrain,
);
});
const getAttributesFromDom = (): ReadonlyArray<WCStoreCartAddItemArgsItems> => {
const selectElements = epipe(
document.querySelectorAll<HTMLSelectElement>(".selecteur-produit select"),
Array.from<HTMLSelectElement>,
);
if (selectElements.length === 0) {
return [];
}
const attributes = selectElements.map((select: HTMLSelectElement) => ({
attribute: select.id,
value: select.value,
}));
return attributes;
};
document.addEventListener("DOMContentLoaded", (): void => {
ProductPageRuntime.runFork(pipe(initAddToCartButton(), Effect.tapCause(Console.error)));
ProductPageRuntime.runFork(pipe(initAddToCartInteractionUpdates(), Effect.tapCause(Console.error)));

View file

@ -1,7 +1,7 @@
/// <reference types="vite/client" />
type ImportMeta = {
readonly env: ImportMetaEnv;
};
type ImportMeta = Readonly<{
env: ImportMetaEnv;
}>;
type ImportMetaEnv = {};

View file

@ -37,6 +37,7 @@ $products = wc_get_products([
])
|> function (/** @var list<WC_Product>|stdClass */ mixed $products): array {
assert(is_array($products), 'Les Produits de la Catégorie doivent être un tableau.');
return $products;
}
|> (static fn(/** @var list<WC_Product> */ array $products): array => Arr::map(

View file

@ -31,7 +31,10 @@ $commande = $order;
$date = new Carbon($commande->get_date_created());
$email = [
'adresses' => ['facturation' => $commande->get_address('billing'), 'livraison' => $commande->get_address('shipping')],
'adresses' => [
'facturation' => $commande->get_address('billing'),
'livraison' => $commande->get_address('shipping'),
],
'commande' => ['date' => $date->toDateString(), 'id' => $commande->get_id()],
'livraison' => [
'methode' => $commande->get_shipping_method(),
@ -62,7 +65,9 @@ $email = [
];
}),
'totaux' => [
'sous_total_livraison' => '0' === $commande->get_shipping_total() ? 'Free' : $commande->get_shipping_total() . '€',
'sous_total_livraison' => '0' === $commande->get_shipping_total()
? 'Free'
: $commande->get_shipping_total() . '€',
'sous_total_produits' => $commande->get_subtotal() . '€',
'sous_total_reduction' => '0.00' === $commande->get_discount_total()
? '0'

View file

@ -31,7 +31,10 @@ $commande = $order;
$date = new Carbon($commande->get_date_created());
$email = [
'adresses' => ['facturation' => $commande->get_address('billing'), 'livraison' => $commande->get_address('shipping')],
'adresses' => [
'facturation' => $commande->get_address('billing'),
'livraison' => $commande->get_address('shipping'),
],
'commande' => ['date' => $date->toDateString(), 'id' => $commande->get_id()],
'paiement' => ['methode' => ''],
'produits' => collect($commande->get_items())->map(static function (WC_Order_Item_Product $article) {
@ -58,7 +61,9 @@ $email = [
];
}),
'totaux' => [
'sous_total_livraison' => '0' === $commande->get_shipping_total() ? 'Free' : $commande->get_shipping_total() . '€',
'sous_total_livraison' => '0' === $commande->get_shipping_total()
? 'Free'
: $commande->get_shipping_total() . '€',
'sous_total_produits' => $commande->get_subtotal() . '€',
'sous_total_reduction' => '0.00' === $commande->get_discount_total()
? '0'