From ea0108650b84feab7035c13c56c8d5c32871f062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 7 Jun 2026 14:51:18 +0200 Subject: [PATCH 001/133] Add tests --- bun.lock | 279 +++++++++++++++++++-- packages/effect-fc/package.json | 6 +- packages/effect-fc/test/Component.test.tsx | 133 ++++++++++ packages/effect-fc/vitest.config.ts | 9 + 4 files changed, 411 insertions(+), 16 deletions(-) create mode 100644 packages/effect-fc/test/Component.test.tsx create mode 100644 packages/effect-fc/vitest.config.ts diff --git a/bun.lock b/bun.lock index 2fb16f8..ccc7b94 100644 --- a/bun.lock +++ b/bun.lock @@ -36,12 +36,15 @@ }, "packages/effect-fc": { "name": "effect-fc", - "version": "0.2.6", + "version": "0.3.0", "dependencies": { "effect-lens": "^0.2.0", }, "devDependencies": { "@effect/platform-browser": "^0.76.0", + "@testing-library/react": "^16.3.0", + "jsdom": "^26.1.0", + "vitest": "^3.2.4", }, "peerDependencies": { "@types/react": "^19.2.0", @@ -113,6 +116,8 @@ "@algolia/requester-node-http": ["@algolia/requester-node-http@5.50.0", "", { "dependencies": { "@algolia/client-common": "5.50.0" } }, "sha512-xpwefe4fCOWnZgXCbkGpqQY6jgBSCf2hmgnySbyzZIccrv3SoashHKGPE4x6vVG+gdHrGciMTAcDo9HOZwH22Q=="], + "@asamuzakjp/css-color": ["@asamuzakjp/css-color@3.2.0", "", { "dependencies": { "@csstools/css-calc": "^2.1.3", "@csstools/css-color-parser": "^3.0.9", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "lru-cache": "^10.4.3" } }, "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw=="], + "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], "@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="], @@ -515,6 +520,58 @@ "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.7", "", { "os": "aix", "cpu": "ppc64" }, "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.27.7", "", { "os": "android", "cpu": "arm" }, "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.7", "", { "os": "android", "cpu": "arm64" }, "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.27.7", "", { "os": "android", "cpu": "x64" }, "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.7", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.7", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.7", "", { "os": "linux", "cpu": "arm" }, "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.7", "", { "os": "linux", "cpu": "ia32" }, "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.7", "", { "os": "linux", "cpu": "ppc64" }, "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.7", "", { "os": "linux", "cpu": "s390x" }, "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.7", "", { "os": "linux", "cpu": "x64" }, "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.7", "", { "os": "none", "cpu": "x64" }, "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.7", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.7", "", { "os": "openbsd", "cpu": "x64" }, "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.7", "", { "os": "sunos", "cpu": "x64" }, "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.7", "", { "os": "win32", "cpu": "ia32" }, "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.7", "", { "os": "win32", "cpu": "x64" }, "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg=="], + "@floating-ui/core": ["@floating-ui/core@1.7.5", "", { "dependencies": { "@floating-ui/utils": "^0.2.11" } }, "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ=="], "@floating-ui/dom": ["@floating-ui/dom@1.7.6", "", { "dependencies": { "@floating-ui/core": "^1.7.5", "@floating-ui/utils": "^0.2.11" } }, "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ=="], @@ -799,6 +856,56 @@ "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.61.1", "", { "os": "android", "cpu": "arm" }, "sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.61.1", "", { "os": "android", "cpu": "arm64" }, "sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.61.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.61.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.61.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.61.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.61.1", "", { "os": "linux", "cpu": "arm" }, "sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.61.1", "", { "os": "linux", "cpu": "arm" }, "sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.61.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.61.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w=="], + + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.61.1", "", { "os": "linux", "cpu": "none" }, "sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ=="], + + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.61.1", "", { "os": "linux", "cpu": "none" }, "sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ=="], + + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.61.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g=="], + + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.61.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.61.1", "", { "os": "linux", "cpu": "none" }, "sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g=="], + + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.61.1", "", { "os": "linux", "cpu": "none" }, "sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.61.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.61.1", "", { "os": "linux", "cpu": "x64" }, "sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.61.1", "", { "os": "linux", "cpu": "x64" }, "sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw=="], + + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.61.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg=="], + + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.61.1", "", { "os": "none", "cpu": "arm64" }, "sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.61.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.61.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ=="], + + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.61.1", "", { "os": "win32", "cpu": "x64" }, "sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.61.1", "", { "os": "win32", "cpu": "x64" }, "sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw=="], + "@rspack/binding": ["@rspack/binding@1.7.11", "", { "optionalDependencies": { "@rspack/binding-darwin-arm64": "1.7.11", "@rspack/binding-darwin-x64": "1.7.11", "@rspack/binding-linux-arm64-gnu": "1.7.11", "@rspack/binding-linux-arm64-musl": "1.7.11", "@rspack/binding-linux-x64-gnu": "1.7.11", "@rspack/binding-linux-x64-musl": "1.7.11", "@rspack/binding-wasm32-wasi": "1.7.11", "@rspack/binding-win32-arm64-msvc": "1.7.11", "@rspack/binding-win32-ia32-msvc": "1.7.11", "@rspack/binding-win32-x64-msvc": "1.7.11" } }, "sha512-2MGdy2s2HimsDT444Bp5XnALzNRxuBNc7y0JzyuqKbHBywd4x2NeXyhWXXoxufaCFu5PBc9Qq9jyfjW2Aeh06Q=="], "@rspack/binding-darwin-arm64": ["@rspack/binding-darwin-arm64@1.7.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oduECiZVqbO5zlVw+q7Vy65sJFth99fWPTyucwvLJJtJkPL5n17Uiql2cYP6Ijn0pkqtf1SXgK8WjiKLG5bIig=="], @@ -947,6 +1054,10 @@ "@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.162.0", "", {}, "sha512-uhOeFyxLcU41HzvrxsGpiWdcMbScY1EDgbZ5K7DVRMYInbLYWAC0EA/kx9wXAoSM8q82bUG2hRl8+EAjE6XAbA=="], + "@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="], + + "@testing-library/react": ["@testing-library/react@16.3.2", "", { "dependencies": { "@babel/runtime": "^7.12.5" }, "peerDependencies": { "@testing-library/dom": "^10.0.0", "@types/react": "^18.0.0 || ^19.0.0", "@types/react-dom": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g=="], + "@turbo/darwin-64": ["@turbo/darwin-64@2.9.16", "", { "os": "darwin", "cpu": "x64" }, "sha512-jLjApWTSNd7JZ5JaLYfelW1ytnGQOvB7ivl+2RD1xQvJTbi8I9gBjzcga7tDZVPyaxpl10YTfJt3BrYXR18KDw=="], "@turbo/darwin-arm64": ["@turbo/darwin-arm64@2.9.16", "", { "os": "darwin", "cpu": "arm64" }, "sha512-YPgrn+5HIGzrx0O2a631SV4MBQUe4W/DafMFUuBVgaU32PW9/OTT0ehviF0QSxTXuRJlHvW2eUTemddF5/spmw=="], @@ -963,18 +1074,24 @@ "@typed/id": ["@typed/id@0.17.2", "", { "peerDependencies": { "effect": "^3.14.7" } }, "sha512-z/Z14/moeu9x45IpkGaRwuvb+CQ3s3UCc/agcpZibTz1yPb3RgSDXx4rOHIuyb6hG6oNzqe9yY4GbbMq3Hb5Ug=="], + "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], + "@types/body-parser": ["@types/body-parser@1.19.6", "", { "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g=="], "@types/bonjour": ["@types/bonjour@3.5.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ=="], "@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="], + "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], + "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], "@types/connect-history-api-fallback": ["@types/connect-history-api-fallback@1.5.4", "", { "dependencies": { "@types/express-serve-static-core": "*", "@types/node": "*" } }, "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw=="], "@types/debug": ["@types/debug@4.1.13", "", { "dependencies": { "@types/ms": "*" } }, "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw=="], + "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], + "@types/eslint": ["@types/eslint@9.6.1", "", { "dependencies": { "@types/estree": "*", "@types/json-schema": "*" } }, "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag=="], "@types/eslint-scope": ["@types/eslint-scope@3.7.7", "", { "dependencies": { "@types/eslint": "*", "@types/estree": "*" } }, "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg=="], @@ -1059,6 +1176,20 @@ "@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.2", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.0" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler"] }, "sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg=="], + "@vitest/expect": ["@vitest/expect@3.2.6", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.6", "@vitest/utils": "3.2.6", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-1+7q9BtaKzEmO+fmNT3kYvoNn5Y71XWAx2Q5HRim4tTVRQVRv4uJFAQ5FbK0OPUeNP/WmVCpxYxoJdvuHVjzBQ=="], + + "@vitest/mocker": ["@vitest/mocker@3.2.6", "", { "dependencies": { "@vitest/spy": "3.2.6", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-EZOrpDbkKotFAP7wPAQV1UIyoGOk4oX7ynWhBhLB7v+meMHbQhU16oPpIYGTTe4oFlhpryGpgpcZP/sin3hYuw=="], + + "@vitest/pretty-format": ["@vitest/pretty-format@3.2.6", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-lb7XXXzmm2h2ASzFnRvQpDo6onT1NmMJA3tkGTWiBFtRJ9lxGY3d3mm/Apt36gej2bkkOVLL/yTOtufDaFa/jA=="], + + "@vitest/runner": ["@vitest/runner@3.2.6", "", { "dependencies": { "@vitest/utils": "3.2.6", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-HYcoSj1w5tcgUnzoF0HcyaAQjpA1gj9ftUJ7iSJSuipc02jW9gKkigwZbjFldAfYHA1fa8UZVRftdMY5msWM9Q=="], + + "@vitest/snapshot": ["@vitest/snapshot@3.2.6", "", { "dependencies": { "@vitest/pretty-format": "3.2.6", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-H+ZjNTWGpObenh0YnlBctAPnJSI20P81PL8BPzWpx54YXLLTm8hEsWawtcYLMrwvpK48hGxLLbCS+1KRXhsKhw=="], + + "@vitest/spy": ["@vitest/spy@3.2.6", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-oq6BbH68WzcWmwtBrU9nqLeaXTR4XwJF7FSLkKEZo4i6eoXcrxjcwSuTvWBIRUTC6VC72nXYunzqgZA+IKdtxg=="], + + "@vitest/utils": ["@vitest/utils@3.2.6", "", { "dependencies": { "@vitest/pretty-format": "3.2.6", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-lI23nIs4bnT3T8NIoh+vFaz5s2/DdP0Jgt2jxwgWljvwn82cLJtyi/If+fjFyoLMGIOz0U/fKvWE0d4jsNQEfg=="], + "@webassemblyjs/ast": ["@webassemblyjs/ast@1.14.1", "", { "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ=="], "@webassemblyjs/floating-point-hex-parser": ["@webassemblyjs/floating-point-hex-parser@1.13.2", "", {}, "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA=="], @@ -1105,6 +1236,8 @@ "address": ["address@1.2.2", "", {}, "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA=="], + "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], + "aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="], "ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], @@ -1121,7 +1254,7 @@ "ansi-html-community": ["ansi-html-community@0.0.8", "", { "bin": { "ansi-html": "bin/ansi-html" } }, "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw=="], - "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], @@ -1135,12 +1268,16 @@ "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="], + "aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], + "array-flatten": ["array-flatten@1.1.1", "", {}, "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="], "array-union": ["array-union@2.1.0", "", {}, "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="], "asn1js": ["asn1js@3.0.7", "", { "dependencies": { "pvtsutils": "^1.3.6", "pvutils": "^1.1.3", "tslib": "^2.8.1" } }, "sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ=="], + "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], + "astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="], "autoprefixer": ["autoprefixer@10.4.27", "", { "dependencies": { "browserslist": "^4.28.1", "caniuse-lite": "^1.0.30001774", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA=="], @@ -1193,6 +1330,8 @@ "bytestreamjs": ["bytestreamjs@2.0.1", "", {}, "sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ=="], + "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], + "cacheable-lookup": ["cacheable-lookup@7.0.0", "", {}, "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w=="], "cacheable-request": ["cacheable-request@10.2.14", "", { "dependencies": { "@types/http-cache-semantics": "^4.0.2", "get-stream": "^6.0.1", "http-cache-semantics": "^4.1.1", "keyv": "^4.5.3", "mimic-response": "^4.0.0", "normalize-url": "^8.0.0", "responselike": "^3.0.0" } }, "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ=="], @@ -1215,6 +1354,8 @@ "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], + "chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "char-regex": ["char-regex@1.0.2", "", {}, "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw=="], @@ -1227,6 +1368,8 @@ "character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="], + "check-error": ["check-error@2.1.3", "", {}, "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA=="], + "cheerio": ["cheerio@1.0.0-rc.12", "", { "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.0.1", "htmlparser2": "^8.0.1", "parse5": "^7.0.0", "parse5-htmlparser2-tree-adapter": "^7.0.0" } }, "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q=="], "cheerio-select": ["cheerio-select@2.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", "css-what": "^6.1.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1" } }, "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g=="], @@ -1343,16 +1486,24 @@ "csso": ["csso@5.0.5", "", { "dependencies": { "css-tree": "~2.2.0" } }, "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ=="], + "cssstyle": ["cssstyle@4.6.0", "", { "dependencies": { "@asamuzakjp/css-color": "^3.2.0", "rrweb-cssom": "^0.8.0" } }, "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg=="], + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + "data-urls": ["data-urls@5.0.0", "", { "dependencies": { "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.0.0" } }, "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg=="], + "debounce": ["debounce@1.2.1", "", {}, "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="], "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "decimal.js": ["decimal.js@10.6.0", "", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="], + "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="], "decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="], + "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], + "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="], "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], @@ -1393,6 +1544,8 @@ "docs": ["docs@workspace:packages/docs"], + "dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], + "dom-converter": ["dom-converter@0.2.0", "", { "dependencies": { "utila": "~0.4" } }, "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA=="], "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="], @@ -1435,7 +1588,7 @@ "enhanced-resolve": ["enhanced-resolve@5.20.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA=="], - "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], "error-ex": ["error-ex@1.3.4", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ=="], @@ -1451,6 +1604,8 @@ "esast-util-from-js": ["esast-util-from-js@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "acorn": "^8.0.0", "esast-util-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw=="], + "esbuild": ["esbuild@0.27.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.7", "@esbuild/android-arm": "0.27.7", "@esbuild/android-arm64": "0.27.7", "@esbuild/android-x64": "0.27.7", "@esbuild/darwin-arm64": "0.27.7", "@esbuild/darwin-x64": "0.27.7", "@esbuild/freebsd-arm64": "0.27.7", "@esbuild/freebsd-x64": "0.27.7", "@esbuild/linux-arm": "0.27.7", "@esbuild/linux-arm64": "0.27.7", "@esbuild/linux-ia32": "0.27.7", "@esbuild/linux-loong64": "0.27.7", "@esbuild/linux-mips64el": "0.27.7", "@esbuild/linux-ppc64": "0.27.7", "@esbuild/linux-riscv64": "0.27.7", "@esbuild/linux-s390x": "0.27.7", "@esbuild/linux-x64": "0.27.7", "@esbuild/netbsd-arm64": "0.27.7", "@esbuild/netbsd-x64": "0.27.7", "@esbuild/openbsd-arm64": "0.27.7", "@esbuild/openbsd-x64": "0.27.7", "@esbuild/openharmony-arm64": "0.27.7", "@esbuild/sunos-x64": "0.27.7", "@esbuild/win32-arm64": "0.27.7", "@esbuild/win32-ia32": "0.27.7", "@esbuild/win32-x64": "0.27.7" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w=="], + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], "escape-goat": ["escape-goat@4.0.0", "", {}, "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg=="], @@ -1497,6 +1652,8 @@ "execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], + "expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="], + "express": ["express@4.22.1", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "~1.20.3", "content-disposition": "~0.5.4", "content-type": "~1.0.4", "cookie": "~0.7.1", "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "~1.3.1", "fresh": "~0.5.2", "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", "qs": "~6.14.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", "serve-static": "~1.16.2", "setprototypeof": "1.2.0", "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g=="], "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], @@ -1629,6 +1786,8 @@ "hpack.js": ["hpack.js@2.1.6", "", { "dependencies": { "inherits": "^2.0.1", "obuf": "^1.0.0", "readable-stream": "^2.0.1", "wbuf": "^1.1.0" } }, "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ=="], + "html-encoding-sniffer": ["html-encoding-sniffer@4.0.0", "", { "dependencies": { "whatwg-encoding": "^3.1.1" } }, "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ=="], + "html-escaper": ["html-escaper@2.0.2", "", {}, "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg=="], "html-minifier-terser": ["html-minifier-terser@7.2.0", "", { "dependencies": { "camel-case": "^4.1.2", "clean-css": "~5.3.2", "commander": "^10.0.0", "entities": "^4.4.0", "param-case": "^3.0.4", "relateurl": "^0.2.7", "terser": "^5.15.1" }, "bin": { "html-minifier-terser": "cli.js" } }, "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA=="], @@ -1651,15 +1810,19 @@ "http-proxy": ["http-proxy@1.18.1", "", { "dependencies": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", "requires-port": "^1.0.0" } }, "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ=="], + "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], + "http-proxy-middleware": ["http-proxy-middleware@2.0.9", "", { "dependencies": { "@types/http-proxy": "^1.17.8", "http-proxy": "^1.18.1", "is-glob": "^4.0.1", "is-plain-obj": "^3.0.0", "micromatch": "^4.0.2" }, "peerDependencies": { "@types/express": "^4.17.13" }, "optionalPeers": ["@types/express"] }, "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q=="], "http2-wrapper": ["http2-wrapper@2.2.1", "", { "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.2.0" } }, "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ=="], + "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + "human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], "hyperdyperid": ["hyperdyperid@1.2.0", "", {}, "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A=="], - "iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], + "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], "icss-utils": ["icss-utils@5.1.0", "", { "peerDependencies": { "postcss": "^8.1.0" } }, "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA=="], @@ -1731,6 +1894,8 @@ "is-plain-object": ["is-plain-object@2.0.4", "", { "dependencies": { "isobject": "^3.0.1" } }, "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og=="], + "is-potential-custom-element-name": ["is-potential-custom-element-name@1.0.1", "", {}, "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="], + "is-regexp": ["is-regexp@1.0.0", "", {}, "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA=="], "is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], @@ -1761,6 +1926,8 @@ "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + "jsdom": ["jsdom@26.1.0", "", { "dependencies": { "cssstyle": "^4.2.1", "data-urls": "^5.0.0", "decimal.js": "^10.5.0", "html-encoding-sniffer": "^4.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", "nwsapi": "^2.2.16", "parse5": "^7.2.1", "rrweb-cssom": "^0.8.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^5.1.1", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^7.0.0", "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.1.1", "ws": "^8.18.0", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.0.0" }, "optionalPeers": ["canvas"] }, "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg=="], + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], @@ -1831,12 +1998,16 @@ "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], + "lower-case": ["lower-case@2.0.2", "", { "dependencies": { "tslib": "^2.0.3" } }, "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg=="], "lowercase-keys": ["lowercase-keys@3.0.0", "", {}, "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ=="], "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], "markdown-extensions": ["markdown-extensions@2.0.0", "", {}, "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q=="], @@ -2031,6 +2202,8 @@ "null-loader": ["null-loader@4.0.1", "", { "dependencies": { "loader-utils": "^2.0.0", "schema-utils": "^3.0.0" }, "peerDependencies": { "webpack": "^4.0.0 || ^5.0.0" } }, "sha512-pxqVbi4U6N26lq+LmgIbB5XATP0VdZKOG25DhHi8btMmJJefGArFyDg1yc4U3hWCJbMqSrw0qyrz1UQX+qYXqg=="], + "nwsapi": ["nwsapi@2.2.24", "", {}, "sha512-7YRhZ3jS45LwmSCT4b2sVFHt/WuovaktDU07QrtOBY2PXskss5a9jfmR9jptyumwXST+rFjrmppMY1KT/yn35A=="], + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], @@ -2101,6 +2274,8 @@ "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], @@ -2245,6 +2420,8 @@ "pretty-error": ["pretty-error@4.0.0", "", { "dependencies": { "lodash": "^4.17.20", "renderkid": "^3.0.0" } }, "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw=="], + "pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], + "pretty-time": ["pretty-time@1.1.0", "", {}, "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA=="], "prism-react-renderer": ["prism-react-renderer@2.4.1", "", { "dependencies": { "@types/prismjs": "^1.26.0", "clsx": "^2.0.0" }, "peerDependencies": { "react": ">=16.0.0" } }, "sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig=="], @@ -2393,6 +2570,10 @@ "rolldown": ["rolldown@1.0.3", "", { "dependencies": { "@oxc-project/types": "=0.133.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.3", "@rolldown/binding-darwin-arm64": "1.0.3", "@rolldown/binding-darwin-x64": "1.0.3", "@rolldown/binding-freebsd-x64": "1.0.3", "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", "@rolldown/binding-linux-arm64-gnu": "1.0.3", "@rolldown/binding-linux-arm64-musl": "1.0.3", "@rolldown/binding-linux-ppc64-gnu": "1.0.3", "@rolldown/binding-linux-s390x-gnu": "1.0.3", "@rolldown/binding-linux-x64-gnu": "1.0.3", "@rolldown/binding-linux-x64-musl": "1.0.3", "@rolldown/binding-openharmony-arm64": "1.0.3", "@rolldown/binding-wasm32-wasi": "1.0.3", "@rolldown/binding-win32-arm64-msvc": "1.0.3", "@rolldown/binding-win32-x64-msvc": "1.0.3" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g=="], + "rollup": ["rollup@4.61.1", "", { "dependencies": { "@types/estree": "1.0.9" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.61.1", "@rollup/rollup-android-arm64": "4.61.1", "@rollup/rollup-darwin-arm64": "4.61.1", "@rollup/rollup-darwin-x64": "4.61.1", "@rollup/rollup-freebsd-arm64": "4.61.1", "@rollup/rollup-freebsd-x64": "4.61.1", "@rollup/rollup-linux-arm-gnueabihf": "4.61.1", "@rollup/rollup-linux-arm-musleabihf": "4.61.1", "@rollup/rollup-linux-arm64-gnu": "4.61.1", "@rollup/rollup-linux-arm64-musl": "4.61.1", "@rollup/rollup-linux-loong64-gnu": "4.61.1", "@rollup/rollup-linux-loong64-musl": "4.61.1", "@rollup/rollup-linux-ppc64-gnu": "4.61.1", "@rollup/rollup-linux-ppc64-musl": "4.61.1", "@rollup/rollup-linux-riscv64-gnu": "4.61.1", "@rollup/rollup-linux-riscv64-musl": "4.61.1", "@rollup/rollup-linux-s390x-gnu": "4.61.1", "@rollup/rollup-linux-x64-gnu": "4.61.1", "@rollup/rollup-linux-x64-musl": "4.61.1", "@rollup/rollup-openbsd-x64": "4.61.1", "@rollup/rollup-openharmony-arm64": "4.61.1", "@rollup/rollup-win32-arm64-msvc": "4.61.1", "@rollup/rollup-win32-ia32-msvc": "4.61.1", "@rollup/rollup-win32-x64-gnu": "4.61.1", "@rollup/rollup-win32-x64-msvc": "4.61.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA=="], + + "rrweb-cssom": ["rrweb-cssom@0.8.0", "", {}, "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw=="], + "rtlcss": ["rtlcss@4.3.0", "", { "dependencies": { "escalade": "^3.1.1", "picocolors": "^1.0.0", "postcss": "^8.4.21", "strip-json-comments": "^3.1.1" }, "bin": { "rtlcss": "bin/rtlcss.js" } }, "sha512-FI+pHEn7Wc4NqKXMXFM+VAYKEj/mRIcW4h24YVwVtyjI+EqGrLc2Hx/Ny0lrZ21cBWU2goLy36eqMcNj3AQJig=="], "run-applescript": ["run-applescript@7.1.0", "", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="], @@ -2405,6 +2586,8 @@ "sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="], + "saxes": ["saxes@6.0.0", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="], + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], "schema-dts": ["schema-dts@1.1.5", "", {}, "sha512-RJr9EaCmsLzBX2NDiO5Z3ux2BVosNZN5jo0gWgsyKvxKIUL5R3swNvoorulAeL9kLB0iTSX7V6aokhla2m7xbg=="], @@ -2459,6 +2642,8 @@ "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], + "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], "sirv": ["sirv@2.0.4", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ=="], @@ -2493,6 +2678,8 @@ "srcset": ["srcset@4.0.0", "", {}, "sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw=="], + "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], @@ -2513,6 +2700,8 @@ "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], + "strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="], + "style-to-js": ["style-to-js@1.1.21", "", { "dependencies": { "style-to-object": "1.0.14" } }, "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ=="], "style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="], @@ -2529,6 +2718,8 @@ "swc-loader": ["swc-loader@0.2.7", "", { "dependencies": { "@swc/counter": "^0.1.3" }, "peerDependencies": { "@swc/core": "^1.2.147", "webpack": ">=2" } }, "sha512-nwYWw3Fh9ame3Rtm7StS9SBLpHRRnYcK7bnpF3UKZmesAK0gw2/ADvlURFAINmPvKtDLzp+GBiP9yLoEjg6S9w=="], + "symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="], + "tagged-tag": ["tagged-tag@1.0.0", "", {}, "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng=="], "tapable": ["tapable@2.3.2", "", {}, "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA=="], @@ -2545,16 +2736,32 @@ "tiny-warning": ["tiny-warning@1.0.3", "", {}, "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="], + "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], + + "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], + "tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], "tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], + "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], + + "tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], + + "tldts": ["tldts@6.1.86", "", { "dependencies": { "tldts-core": "^6.1.86" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ=="], + + "tldts-core": ["tldts-core@6.1.86", "", {}, "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA=="], + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], + "tough-cookie": ["tough-cookie@5.1.2", "", { "dependencies": { "tldts": "^6.1.32" } }, "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A=="], + + "tr46": ["tr46@5.1.1", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw=="], + "tree-dump": ["tree-dump@1.1.0", "", { "peerDependencies": { "tslib": "2" } }, "sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA=="], "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], @@ -2645,12 +2852,20 @@ "vite": ["vite@8.0.16", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.15", "rolldown": "1.0.3", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.18", "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-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw=="], + "vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], + + "vitest": ["vitest@3.2.6", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.6", "@vitest/mocker": "3.2.6", "@vitest/pretty-format": "^3.2.6", "@vitest/runner": "3.2.6", "@vitest/snapshot": "3.2.6", "@vitest/spy": "3.2.6", "@vitest/utils": "3.2.6", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.6", "@vitest/ui": "3.2.6", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-xejya+bT/j/+R/AGa1XOfRxLmNUlLtlwjRsFUILF+xHfzElmGcmFydy2gqqIrd62ptIEfwVMofd19uNWD9L7Nw=="], + + "w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="], + "watchpack": ["watchpack@2.5.1", "", { "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" } }, "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg=="], "wbuf": ["wbuf@1.7.3", "", { "dependencies": { "minimalistic-assert": "^1.0.0" } }, "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA=="], "web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="], + "webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="], + "webpack": ["webpack@5.105.4", "", { "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.16.0", "acorn-import-phases": "^1.0.3", "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.20.0", "es-module-lexer": "^2.0.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.3.1", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^4.3.3", "tapable": "^2.3.0", "terser-webpack-plugin": "^5.3.17", "watchpack": "^2.5.1", "webpack-sources": "^3.3.4" }, "bin": { "webpack": "bin/webpack.js" } }, "sha512-jTywjboN9aHxFlToqb0K0Zs9SbBoW4zRUlGzI2tYNxVYcEi/IPpn+Xi4ye5jTLvX2YeLuic/IvxNot+Q1jMoOw=="], "webpack-bundle-analyzer": ["webpack-bundle-analyzer@4.10.2", "", { "dependencies": { "@discoveryjs/json-ext": "0.5.7", "acorn": "^8.0.4", "acorn-walk": "^8.0.0", "commander": "^7.2.0", "debounce": "^1.2.1", "escape-string-regexp": "^4.0.0", "gzip-size": "^6.0.0", "html-escaper": "^2.0.2", "opener": "^1.5.2", "picocolors": "^1.0.0", "sirv": "^2.0.3", "ws": "^7.3.1" }, "bin": { "webpack-bundle-analyzer": "lib/bin/analyzer.js" } }, "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw=="], @@ -2671,8 +2886,16 @@ "websocket-extensions": ["websocket-extensions@0.1.4", "", {}, "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg=="], + "whatwg-encoding": ["whatwg-encoding@3.1.1", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="], + + "whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="], + + "whatwg-url": ["whatwg-url@14.2.0", "", { "dependencies": { "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" } }, "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw=="], + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], + "widest-line": ["widest-line@4.0.1", "", { "dependencies": { "string-width": "^5.0.1" } }, "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig=="], "wildcard": ["wildcard@2.0.1", "", {}, "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ=="], @@ -2681,7 +2904,7 @@ "write-file-atomic": ["write-file-atomic@3.0.3", "", { "dependencies": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", "signal-exit": "^3.0.2", "typedarray-to-buffer": "^3.1.5" } }, "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q=="], - "ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + "ws": ["ws@8.20.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA=="], "wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="], @@ -2689,6 +2912,10 @@ "xml-js": ["xml-js@1.6.11", "", { "dependencies": { "sax": "^1.2.4" }, "bin": { "xml-js": "./bin/cli.js" } }, "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g=="], + "xml-name-validator": ["xml-name-validator@5.0.0", "", {}, "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="], + + "xmlchars": ["xmlchars@2.2.0", "", {}, "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="], + "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], "yocto-queue": ["yocto-queue@1.2.2", "", {}, "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ=="], @@ -2697,6 +2924,8 @@ "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], + "@asamuzakjp/css-color/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], @@ -2741,6 +2970,8 @@ "@rspack/binding-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.0.7", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@tybys/wasm-util": "^0.10.1" } }, "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw=="], + "@svgr/hast-util-to-babel-ast/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "@types/react-router/@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], "@types/react-router-config/@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], @@ -2759,6 +2990,8 @@ "body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + "body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], + "boxen/type-fest": ["type-fest@2.19.0", "", {}, "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="], "cheerio-select/css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="], @@ -2799,6 +3032,8 @@ "docs/react-dom": ["react-dom@19.2.5", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.5" } }, "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag=="], + "dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "dot-prop/is-obj": ["is-obj@2.0.0", "", {}, "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="], "esrecurse/estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], @@ -2823,8 +3058,12 @@ "html-minifier-terser/commander": ["commander@10.0.1", "", {}, "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="], + "html-minifier-terser/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "html-webpack-plugin/html-minifier-terser": ["html-minifier-terser@6.1.0", "", { "dependencies": { "camel-case": "^4.1.2", "clean-css": "^5.2.2", "commander": "^8.3.0", "he": "^1.2.0", "param-case": "^3.0.4", "relateurl": "^0.2.7", "terser": "^5.10.0" }, "bin": { "html-minifier-terser": "cli.js" } }, "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw=="], + "htmlparser2/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "http-proxy-middleware/is-plain-obj": ["is-plain-obj@3.0.0", "", {}, "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA=="], "is-inside-container/is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], @@ -2961,8 +3200,6 @@ "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], - "parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], - "postcss-calc/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], "postcss-discard-unused/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], @@ -2975,10 +3212,16 @@ "postcss-unique-selectors/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], + "pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "pretty-format/react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], + "proxy-addr/ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], "raw-body/bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + "raw-body/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], + "rc/ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], "rc/strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], @@ -2989,6 +3232,8 @@ "renderkid/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "rollup/@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], + "rtlcss/postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="], "send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], @@ -3007,6 +3252,10 @@ "source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + "strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + + "strip-literal/js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], + "stylehacks/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], "svgo/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], @@ -3023,8 +3272,16 @@ "url-loader/schema-utils": ["schema-utils@3.3.0", "", { "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" } }, "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg=="], + "vite-node/es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], + + "vite-node/vite": ["vite@7.3.5", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.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", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww=="], + + "vitest/vite": ["vite@7.3.5", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.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", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww=="], + "webpack-bundle-analyzer/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], + "webpack-bundle-analyzer/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + "webpack-dev-middleware/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], "webpack-dev-middleware/range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], @@ -3033,8 +3290,6 @@ "webpack-dev-server/open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="], - "webpack-dev-server/ws": ["ws@8.20.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA=="], - "webpackbar/ansis": ["ansis@3.17.0", "", {}, "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg=="], "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], @@ -3111,8 +3366,6 @@ "renderkid/htmlparser2/entities": ["entities@2.2.0", "", {}, "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="], - "renderkid/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "rtlcss/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], "send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], @@ -3141,10 +3394,6 @@ "@docusaurus/core/chokidar/readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], - "ansi-align/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - - "cli-table3/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "css-select/domutils/dom-serializer/entities": ["entities@2.2.0", "", {}, "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="], "file-loader/schema-utils/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], diff --git a/packages/effect-fc/package.json b/packages/effect-fc/package.json index 27d6a09..f729dad 100644 --- a/packages/effect-fc/package.json +++ b/packages/effect-fc/package.json @@ -32,13 +32,17 @@ "build": "tsc", "lint:tsc": "tsc --noEmit", "lint:biome": "biome lint", + "test": "vitest run", "pack": "npm pack", "clean:cache": "rm -rf .turbo tsconfig.tsbuildinfo", "clean:dist": "rm -rf dist", "clean:modules": "rm -rf node_modules" }, "devDependencies": { - "@effect/platform-browser": "^0.76.0" + "@effect/platform-browser": "^0.76.0", + "@testing-library/react": "^16.3.0", + "jsdom": "^26.1.0", + "vitest": "^3.2.4" }, "peerDependencies": { "@types/react": "^19.2.0", diff --git a/packages/effect-fc/test/Component.test.tsx b/packages/effect-fc/test/Component.test.tsx new file mode 100644 index 0000000..7a7658b --- /dev/null +++ b/packages/effect-fc/test/Component.test.tsx @@ -0,0 +1,133 @@ +import { render, screen, waitFor } from "@testing-library/react" +import { Effect, Layer } from "effect" +import { describe, expect, it, vi } from "vitest" +import * as Component from "../src/Component.js" +import * as ReactRuntime from "../src/ReactRuntime.js" + + +describe("Component", () => { + it("runs useOnMount only once across rerenders", async () => { + const onMount = vi.fn(() => Effect.succeed("mounted")) + const runtime = ReactRuntime.make(Layer.empty) + const effectRuntime = await Effect.runPromise(runtime.runtime.runtimeEffect) + + const Probe = Component.makeUntraced("UseOnMountProbe")(function*() { + const value = yield* Component.useOnMount(onMount) + return
{value}
+ }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("mounted") + expect(onMount).toHaveBeenCalledTimes(1) + + view.rerender( + + + + ) + expect(await screen.findByText("mounted")).toBeTruthy() + expect(onMount).toHaveBeenCalledTimes(1) + + view.unmount() + await Effect.runPromise(runtime.runtime.disposeEffect) + }) + + it("recomputes useOnChange only when dependencies change", async () => { + const onChange = vi.fn((value: number) => Effect.succeed(`value:${value}`)) + const runtime = ReactRuntime.make(Layer.empty) + const effectRuntime = await Effect.runPromise(runtime.runtime.runtimeEffect) + + const Probe = Component.makeUntraced("UseOnChangeProbe")(function*(props: { readonly value: number }) { + const result = yield* Component.useOnChange(() => onChange(props.value), [props.value], { + finalizerExecutionDebounce: 0, + }) + + return
{result}
+ }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("value:1") + expect(onChange).toHaveBeenCalledTimes(1) + + view.rerender( + + + + ) + + expect(await screen.findByText("value:1")).toBeTruthy() + expect(onChange).toHaveBeenCalledTimes(1) + + view.rerender( + + + + ) + + await screen.findByText("value:2") + expect(onChange).toHaveBeenCalledTimes(2) + + view.unmount() + await Effect.runPromise(runtime.runtime.disposeEffect) + }) + + it("closes the previous scope on dependency changes and unmount", async () => { + const cleanup = vi.fn() + const runtime = ReactRuntime.make(Layer.empty) + const effectRuntime = await Effect.runPromise(runtime.runtime.runtimeEffect) + + const Probe = Component.makeUntraced("ScopeCleanupProbe")(function*(props: { readonly value: string }) { + const result = yield* Component.useOnChange( + () => Effect.gen(function*() { + yield* Effect.addFinalizer(() => Effect.sync(() => cleanup(props.value))) + return props.value + }), + [props.value], + { finalizerExecutionDebounce: 0 }, + ) + + return
{result}
+ }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("first") + expect(cleanup).not.toHaveBeenCalled() + + view.rerender( + + + + ) + + await screen.findByText("second") + await waitFor(() => expect(cleanup).toHaveBeenCalledWith("first")) + expect(cleanup).toHaveBeenCalledTimes(1) + + view.unmount() + + await waitFor(() => expect(cleanup).toHaveBeenCalledWith("second")) + expect(cleanup).toHaveBeenCalledTimes(2) + await Effect.runPromise(runtime.runtime.disposeEffect) + }) +}) diff --git a/packages/effect-fc/vitest.config.ts b/packages/effect-fc/vitest.config.ts new file mode 100644 index 0000000..cec0e39 --- /dev/null +++ b/packages/effect-fc/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config" + + +export default defineConfig({ + test: { + environment: "jsdom", + include: ["test/**/*.test.ts?(x)"], + }, +}) -- 2.52.0 From fcb29c0d76508b89bda60f04a72592e102e77df0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 7 Jun 2026 15:01:07 +0200 Subject: [PATCH 002/133] Add tests --- packages/effect-fc/test/Component.test.tsx | 225 ++++++++++++++++++++- 1 file changed, 223 insertions(+), 2 deletions(-) diff --git a/packages/effect-fc/test/Component.test.tsx b/packages/effect-fc/test/Component.test.tsx index 7a7658b..eb846d9 100644 --- a/packages/effect-fc/test/Component.test.tsx +++ b/packages/effect-fc/test/Component.test.tsx @@ -1,10 +1,17 @@ import { render, screen, waitFor } from "@testing-library/react" -import { Effect, Layer } from "effect" -import { describe, expect, it, vi } from "vitest" +import { Context, Effect, Layer } from "effect" +import * as React from "react" +import { afterEach, describe, expect, it, vi } from "vitest" import * as Component from "../src/Component.js" import * as ReactRuntime from "../src/ReactRuntime.js" +class ValueService extends Context.Tag("ValueService")() {} + +afterEach(() => { + vi.useRealTimers() +}) + describe("Component", () => { it("runs useOnMount only once across rerenders", async () => { const onMount = vi.fn(() => Effect.succeed("mounted")) @@ -130,4 +137,218 @@ describe("Component", () => { expect(cleanup).toHaveBeenCalledTimes(2) await Effect.runPromise(runtime.runtime.disposeEffect) }) + + it("runs useReactEffect setup and cleanup when dependencies change", async () => { + const lifecycle = vi.fn<(message: string) => void>() + const runtime = ReactRuntime.make(Layer.empty) + const effectRuntime = await Effect.runPromise(runtime.runtime.runtimeEffect) + + const Probe = Component.makeUntraced("UseReactEffectProbe")(function*(props: { readonly value: string }) { + yield* Component.useReactEffect(() => + Effect.gen(function*() { + yield* Effect.sync(() => lifecycle(`mount:${props.value}`)) + yield* Effect.addFinalizer(() => Effect.sync(() => lifecycle(`cleanup:${props.value}`))) + }), + [props.value]) + + return
{props.value}
+ }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("first") + await waitFor(() => expect(lifecycle).toHaveBeenCalledWith("mount:first")) + + view.rerender( + + + + ) + + await screen.findByText("second") + await waitFor(() => expect(lifecycle).toHaveBeenCalledWith("cleanup:first")) + await waitFor(() => expect(lifecycle).toHaveBeenCalledWith("mount:second")) + + view.unmount() + + await waitFor(() => expect(lifecycle).toHaveBeenCalledWith("cleanup:second")) + expect(lifecycle.mock.calls.map(([message]) => message)).toEqual([ + "mount:first", + "cleanup:first", + "mount:second", + "cleanup:second", + ]) + await Effect.runPromise(runtime.runtime.disposeEffect) + }) + + it("keeps useCallbackSync stable until dependencies change", async () => { + const runtime = ReactRuntime.make(Layer.empty) + const effectRuntime = await Effect.runPromise(runtime.runtime.runtimeEffect) + const seenCallbacks: Array<(value: number) => string> = [] + + const Probe = Component.makeUntraced("UseCallbackSyncProbe")(function*(props: { readonly prefix: string }) { + const callback = yield* Component.useCallbackSync( + (value: number) => Effect.succeed(`${props.prefix}:${value}`), + [props.prefix], + ) + + yield* Component.useOnMount(() => Effect.sync(() => { + seenCallbacks.push(callback) + })) + + React.useEffect(() => { + seenCallbacks.push(callback) + }, [callback]) + + return
{callback(1)}
+ }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("a:1") + expect(seenCallbacks).toHaveLength(2) + expect(seenCallbacks[0]).toBe(seenCallbacks[1]) + expect(seenCallbacks[0]?.(2)).toBe("a:2") + + view.rerender( + + + + ) + + await screen.findByText("a:1") + expect(seenCallbacks).toHaveLength(2) + + view.rerender( + + + + ) + + await screen.findByText("b:1") + await waitFor(() => expect(seenCallbacks).toHaveLength(3)) + expect(seenCallbacks[2]).not.toBe(seenCallbacks[1]) + expect(seenCallbacks[2]?.(2)).toBe("b:2") + + view.unmount() + await Effect.runPromise(runtime.runtime.disposeEffect) + }) + + it("delays cleanup according to finalizerExecutionDebounce", async () => { + const cleanup = vi.fn() + const runtime = ReactRuntime.make(Layer.empty) + const effectRuntime = await Effect.runPromise(runtime.runtime.runtimeEffect) + + const Probe = Component.makeUntraced("DebouncedCleanupProbe")(function*(props: { readonly value: string }) { + const result = yield* Component.useOnChange( + () => Effect.gen(function*() { + yield* Effect.addFinalizer(() => Effect.sync(() => cleanup(props.value))) + return props.value + }), + [props.value], + { finalizerExecutionDebounce: "20 millis" }, + ) + + return
{result}
+ }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("first") + + view.rerender( + + + + ) + + await screen.findByText("second") + expect(cleanup).not.toHaveBeenCalled() + + await new Promise(resolve => setTimeout(resolve, 5)) + expect(cleanup).not.toHaveBeenCalled() + + await waitFor(() => expect(cleanup).toHaveBeenCalledWith("first"), { timeout: 100 }) + + view.unmount() + await waitFor(() => expect(cleanup).toHaveBeenCalledWith("second"), { timeout: 100 }) + await Effect.runPromise(runtime.runtime.disposeEffect) + }) + + it("does not remount a component when only nonReactiveTags change", async () => { + const mounts = vi.fn() + const unmounts = vi.fn() + const runtime = ReactRuntime.make(Layer.empty) + const effectRuntime = await Effect.runPromise(runtime.runtime.runtimeEffect) + + const SubComponent = Component.makeUntraced("NonReactiveSubComponent")(function*() { + const service = yield* ValueService + + yield* Component.useOnMount(() => Effect.gen(function*() { + yield* Effect.sync(() => mounts()) + yield* Effect.addFinalizer(() => Effect.sync(() => unmounts())) + })) + + return
{service.value}
+ }).pipe( + Component.withOptions({ nonReactiveTags: [ValueService] }) + ) + + const Parent = Component.makeUntraced("NonReactiveParent")(function*(props: { readonly value: string }) { + const serviceLayer = React.useMemo( + () => Layer.succeed(ValueService, { value: props.value }), + [props.value], + ) + const context = yield* Component.useContextFromLayer(serviceLayer, { + finalizerExecutionDebounce: 0, + }) + const Child = yield* Effect.provide(SubComponent.use, context) + + return + }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("first") + expect(mounts).toHaveBeenCalledTimes(1) + expect(unmounts).not.toHaveBeenCalled() + + view.rerender( + + + + ) + + await screen.findByText("second") + expect(mounts).toHaveBeenCalledTimes(1) + expect(unmounts).not.toHaveBeenCalled() + + view.unmount() + await waitFor(() => expect(unmounts).toHaveBeenCalledTimes(1)) + await Effect.runPromise(runtime.runtime.disposeEffect) + }) }) -- 2.52.0 From 7638324f2f8046eeb5b274a51ba3edf5f77140b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 7 Jun 2026 15:10:26 +0200 Subject: [PATCH 003/133] Add tests --- packages/effect-fc/test/Lens.test.tsx | 166 ++++++++++++++++++ packages/effect-fc/test/Subscribable.test.tsx | 130 ++++++++++++++ 2 files changed, 296 insertions(+) create mode 100644 packages/effect-fc/test/Lens.test.tsx create mode 100644 packages/effect-fc/test/Subscribable.test.tsx diff --git a/packages/effect-fc/test/Lens.test.tsx b/packages/effect-fc/test/Lens.test.tsx new file mode 100644 index 0000000..4728bfc --- /dev/null +++ b/packages/effect-fc/test/Lens.test.tsx @@ -0,0 +1,166 @@ +import { fireEvent, render, screen, waitFor } from "@testing-library/react" +import { Effect, Layer, SubscriptionRef } from "effect" +import { Lens } from "effect-lens" +import * as React from "react" +import { describe, expect, it } from "vitest" +import * as Component from "../src/Component.js" +import * as LensModule from "../src/Lens.js" +import * as ReactRuntime from "../src/ReactRuntime.js" + + +const makeRuntime = async () => { + const runtime = ReactRuntime.make(Layer.empty) + const effectRuntime = await Effect.runPromise(runtime.runtime.runtimeEffect) + + return { + runtime, + effectRuntime, + dispose: () => Effect.runPromise(runtime.runtime.disposeEffect), + } +} + +describe("Lens", () => { + it("useState stays in sync with lens updates in both directions", async () => { + const { runtime, effectRuntime, dispose } = await makeRuntime() + const ref = await Effect.runPromise(SubscriptionRef.make(0)) + const lens = Lens.fromSubscriptionRef(ref) + + const Probe = Component.makeUntraced("LensUseStateProbe")(function*() { + const [value, setValue] = yield* LensModule.useState(lens) + + return ( + <> +
{value}
+ + + ) + }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("0") + + await Effect.runPromise(Lens.set(lens, 5)) + await screen.findByText("5") + + fireEvent.click(screen.getByRole("button", { name: "increment" })) + await screen.findByText("6") + expect(await Effect.runPromise(Lens.get(lens))).toBe(6) + + view.unmount() + await dispose() + }) + + it("useState respects the provided equivalence when subscribing to lens changes", async () => { + const { runtime, effectRuntime, dispose } = await makeRuntime() + const ref = await Effect.runPromise(SubscriptionRef.make({ id: 1, label: "first" })) + const lens = Lens.fromSubscriptionRef(ref) + + const Probe = Component.makeUntraced("LensUseStateEquivalenceProbe")(function*() { + const [value] = yield* LensModule.useState(lens, { + equivalence: (self, that) => self.id === that.id, + }) + + return
{value.label}
+ }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("first") + + await Effect.runPromise(Lens.set(lens, { id: 1, label: "ignored" })) + await waitFor(() => expect(screen.getByText("first")).toBeTruthy()) + expect(screen.queryByText("ignored")).toBeNull() + + await Effect.runPromise(Lens.set(lens, { id: 2, label: "updated" })) + await screen.findByText("updated") + + view.unmount() + await dispose() + }) + + it("useFromReactState writes React state changes into the returned lens", async () => { + const { runtime, effectRuntime, dispose } = await makeRuntime() + let lens: Lens.Lens | undefined + + const Probe = Component.makeUntraced("LensUseFromReactStateProbe")(function*() { + const [value, setValue] = React.useState("hello") + const reactLens = yield* LensModule.useFromReactState([value, setValue]) + + yield* Component.useOnMount(() => Effect.sync(() => { + lens = reactLens + })) + + return + }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("hello") + await waitFor(() => expect(lens).toBeDefined()) + + fireEvent.click(screen.getByRole("button", { name: "hello" })) + await screen.findByText("hello!") + await waitFor(async () => expect(await Effect.runPromise(Lens.get(lens!))).toBe("hello!")) + + view.unmount() + await dispose() + }) + + it("useFromReactState respects equivalence when lens updates flow back into React state", async () => { + const { runtime, effectRuntime, dispose } = await makeRuntime() + let lens: Lens.Lens<{ readonly id: number; readonly label: string }, never, never, never, never> | undefined + + const Probe = Component.makeUntraced("LensUseFromReactStateEquivalenceProbe")(function*() { + const [value, setValue] = React.useState({ id: 1, label: "first" }) + const reactLens = yield* LensModule.useFromReactState([value, setValue], { + equivalence: (self, that) => self.id === that.id, + }) + + yield* Component.useOnMount(() => Effect.sync(() => { + lens = reactLens + })) + + return
{value.label}
+ }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("first") + await waitFor(() => expect(lens).toBeDefined()) + + await Effect.runPromise(Lens.set(lens!, { id: 1, label: "ignored" })) + await waitFor(() => expect(screen.getByText("first")).toBeTruthy()) + expect(screen.queryByText("ignored")).toBeNull() + + await Effect.runPromise(Lens.set(lens!, { id: 2, label: "updated" })) + await screen.findByText("updated") + + view.unmount() + await dispose() + }) +}) diff --git a/packages/effect-fc/test/Subscribable.test.tsx b/packages/effect-fc/test/Subscribable.test.tsx new file mode 100644 index 0000000..eec6289 --- /dev/null +++ b/packages/effect-fc/test/Subscribable.test.tsx @@ -0,0 +1,130 @@ +import { render, screen, waitFor } from "@testing-library/react" +import { Effect, Fiber, Layer, Stream, SubscriptionRef } from "effect" +import { Lens } from "effect-lens" +import * as React from "react" +import { describe, expect, it } from "vitest" +import * as Component from "../src/Component.js" +import * as ReactRuntime from "../src/ReactRuntime.js" +import * as SubscribableModule from "../src/Subscribable.js" + + +const makeRuntime = async () => { + const runtime = ReactRuntime.make(Layer.empty) + const effectRuntime = await Effect.runPromise(runtime.runtime.runtimeEffect) + + return { + runtime, + effectRuntime, + dispose: () => Effect.runPromise(runtime.runtime.disposeEffect), + } +} + +describe("Subscribable", () => { + it("zipLatestAll reads current values from all inputs", async () => { + const leftRef = await Effect.runPromise(SubscriptionRef.make(1)) + const rightRef = await Effect.runPromise(SubscriptionRef.make("a")) + const left = Lens.fromSubscriptionRef(leftRef) + const right = Lens.fromSubscriptionRef(rightRef) + + const zipped = SubscribableModule.zipLatestAll(left, right) + + expect(await Effect.runPromise(zipped.get)).toEqual([1, "a"]) + }) + + it("zipLatestAll emits updates when any input changes", async () => { + const leftRef = await Effect.runPromise(SubscriptionRef.make(1)) + const rightRef = await Effect.runPromise(SubscriptionRef.make("a")) + const left = Lens.fromSubscriptionRef(leftRef) + const right = Lens.fromSubscriptionRef(rightRef) + + const zipped = SubscribableModule.zipLatestAll(left, right) + const values: Array = [] + + const collector = Effect.runFork(Effect.scoped(zipped.changes.pipe( + Stream.runForEach(value => Effect.sync(() => { + values.push(value as readonly [number, string]) + })), + ))) + + await Effect.runPromise(Lens.set(left, 2)) + await waitFor(() => expect(values).toContainEqual([2, "a"])) + + await Effect.runPromise(Lens.set(right, "b")) + await waitFor(() => expect(values).toContainEqual([2, "b"])) + + Fiber.interruptFork(collector) + }) + + it("useAll returns the latest values and rerenders when any input changes", async () => { + const { runtime, effectRuntime, dispose } = await makeRuntime() + const countRef = await Effect.runPromise(SubscriptionRef.make(1)) + const labelRef = await Effect.runPromise(SubscriptionRef.make("a")) + const count = Lens.fromSubscriptionRef(countRef) + const label = Lens.fromSubscriptionRef(labelRef) + + const Probe = Component.makeUntraced("SubscribableUseAllProbe")(function*() { + const [currentCount, currentLabel] = yield* SubscribableModule.useAll([count, label]) + + return
{`${currentCount}:${currentLabel}`}
+ }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("1:a") + + await Effect.runPromise(Lens.set(count, 2)) + await screen.findByText("2:a") + + await Effect.runPromise(Lens.set(label, "b")) + await screen.findByText("2:b") + + view.unmount() + await dispose() + }) + + it("useAll respects the provided equivalence when processing updates", async () => { + const { runtime, effectRuntime, dispose } = await makeRuntime() + const itemRef = await Effect.runPromise(SubscriptionRef.make({ id: 1, label: "first" })) + const flagRef = await Effect.runPromise(SubscriptionRef.make(true)) + const item = Lens.fromSubscriptionRef(itemRef) + const flag = Lens.fromSubscriptionRef(flagRef) + + const Probe = Component.makeUntraced("SubscribableUseAllEquivalenceProbe")(function*() { + const [currentItem, currentFlag] = yield* SubscribableModule.useAll([item, flag], { + equivalence: ([selfItem, selfFlag], [thatItem, thatFlag]) => + selfItem.id === thatItem.id && selfFlag === thatFlag, + }) + + return
{`${currentItem.label}:${currentFlag ? "on" : "off"}`}
+ }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("first:on") + + await Effect.runPromise(Lens.set(item, { id: 1, label: "ignored" })) + await waitFor(() => expect(screen.getByText("first:on")).toBeTruthy()) + expect(screen.queryByText("ignored:on")).toBeNull() + + await Effect.runPromise(Lens.set(flag, false)) + await screen.findByText("ignored:off") + + await Effect.runPromise(Lens.set(item, { id: 2, label: "updated" })) + await screen.findByText("updated:off") + + view.unmount() + await dispose() + }) +}) -- 2.52.0 From 658e6bb8ea6ccf37e763b9cc9a2cae2b9f75d00d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 7 Jun 2026 15:15:57 +0200 Subject: [PATCH 004/133] Fix tests --- packages/effect-fc/test/Lens.test.tsx | 11 +++++------ packages/effect-fc/test/Subscribable.test.tsx | 11 +++++------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/packages/effect-fc/test/Lens.test.tsx b/packages/effect-fc/test/Lens.test.tsx index 4728bfc..9bf6233 100644 --- a/packages/effect-fc/test/Lens.test.tsx +++ b/packages/effect-fc/test/Lens.test.tsx @@ -1,10 +1,9 @@ import { fireEvent, render, screen, waitFor } from "@testing-library/react" import { Effect, Layer, SubscriptionRef } from "effect" -import { Lens } from "effect-lens" import * as React from "react" import { describe, expect, it } from "vitest" import * as Component from "../src/Component.js" -import * as LensModule from "../src/Lens.js" +import * as Lens from "../src/Lens.js" import * as ReactRuntime from "../src/ReactRuntime.js" @@ -26,7 +25,7 @@ describe("Lens", () => { const lens = Lens.fromSubscriptionRef(ref) const Probe = Component.makeUntraced("LensUseStateProbe")(function*() { - const [value, setValue] = yield* LensModule.useState(lens) + const [value, setValue] = yield* Lens.useState(lens) return ( <> @@ -63,7 +62,7 @@ describe("Lens", () => { const lens = Lens.fromSubscriptionRef(ref) const Probe = Component.makeUntraced("LensUseStateEquivalenceProbe")(function*() { - const [value] = yield* LensModule.useState(lens, { + const [value] = yield* Lens.useState(lens, { equivalence: (self, that) => self.id === that.id, }) @@ -97,7 +96,7 @@ describe("Lens", () => { const Probe = Component.makeUntraced("LensUseFromReactStateProbe")(function*() { const [value, setValue] = React.useState("hello") - const reactLens = yield* LensModule.useFromReactState([value, setValue]) + const reactLens = yield* Lens.useFromReactState([value, setValue]) yield* Component.useOnMount(() => Effect.sync(() => { lens = reactLens @@ -131,7 +130,7 @@ describe("Lens", () => { const Probe = Component.makeUntraced("LensUseFromReactStateEquivalenceProbe")(function*() { const [value, setValue] = React.useState({ id: 1, label: "first" }) - const reactLens = yield* LensModule.useFromReactState([value, setValue], { + const reactLens = yield* Lens.useFromReactState([value, setValue], { equivalence: (self, that) => self.id === that.id, }) diff --git a/packages/effect-fc/test/Subscribable.test.tsx b/packages/effect-fc/test/Subscribable.test.tsx index eec6289..772d330 100644 --- a/packages/effect-fc/test/Subscribable.test.tsx +++ b/packages/effect-fc/test/Subscribable.test.tsx @@ -1,11 +1,10 @@ import { render, screen, waitFor } from "@testing-library/react" import { Effect, Fiber, Layer, Stream, SubscriptionRef } from "effect" import { Lens } from "effect-lens" -import * as React from "react" import { describe, expect, it } from "vitest" import * as Component from "../src/Component.js" import * as ReactRuntime from "../src/ReactRuntime.js" -import * as SubscribableModule from "../src/Subscribable.js" +import * as Subscribable from "../src/Subscribable.js" const makeRuntime = async () => { @@ -26,7 +25,7 @@ describe("Subscribable", () => { const left = Lens.fromSubscriptionRef(leftRef) const right = Lens.fromSubscriptionRef(rightRef) - const zipped = SubscribableModule.zipLatestAll(left, right) + const zipped = Subscribable.zipLatestAll(left, right) expect(await Effect.runPromise(zipped.get)).toEqual([1, "a"]) }) @@ -37,7 +36,7 @@ describe("Subscribable", () => { const left = Lens.fromSubscriptionRef(leftRef) const right = Lens.fromSubscriptionRef(rightRef) - const zipped = SubscribableModule.zipLatestAll(left, right) + const zipped = Subscribable.zipLatestAll(left, right) const values: Array = [] const collector = Effect.runFork(Effect.scoped(zipped.changes.pipe( @@ -63,7 +62,7 @@ describe("Subscribable", () => { const label = Lens.fromSubscriptionRef(labelRef) const Probe = Component.makeUntraced("SubscribableUseAllProbe")(function*() { - const [currentCount, currentLabel] = yield* SubscribableModule.useAll([count, label]) + const [currentCount, currentLabel] = yield* Subscribable.useAll([count, label]) return
{`${currentCount}:${currentLabel}`}
}).pipe( @@ -96,7 +95,7 @@ describe("Subscribable", () => { const flag = Lens.fromSubscriptionRef(flagRef) const Probe = Component.makeUntraced("SubscribableUseAllEquivalenceProbe")(function*() { - const [currentItem, currentFlag] = yield* SubscribableModule.useAll([item, flag], { + const [currentItem, currentFlag] = yield* Subscribable.useAll([item, flag], { equivalence: ([selfItem, selfFlag], [thatItem, thatFlag]) => selfItem.id === thatItem.id && selfFlag === thatFlag, }) -- 2.52.0 From 1292b9885a94a35d01777d8e2eedff8d2678fdde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 8 Jun 2026 11:14:31 +0200 Subject: [PATCH 005/133] Fix --- packages/effect-fc/test/Query.test.ts | 169 ++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 packages/effect-fc/test/Query.test.ts diff --git a/packages/effect-fc/test/Query.test.ts b/packages/effect-fc/test/Query.test.ts new file mode 100644 index 0000000..ee6544f --- /dev/null +++ b/packages/effect-fc/test/Query.test.ts @@ -0,0 +1,169 @@ +import { Effect, Option, type Scope, Stream } from "effect" +import { describe, expect, it } from "vitest" +import * as Query from "../src/Query.js" +import * as QueryClient from "../src/QueryClient.js" +import * as Result from "../src/Result.js" + + +const runQueryTest = (effect: Effect.Effect) => + Effect.runPromise(Effect.scoped(effect.pipe( + Effect.provide(QueryClient.QueryClient.Default), + ))) + +const expectSuccessValue = ( + result: Result.Result, +): A => { + expect(Result.isSuccess(result)).toBe(true) + + if (!Result.isSuccess(result)) + throw new Error(`Expected Success result, received ${result._tag}`) + + return result.value +} + +const expectSomeValue = (option: Option.Option): A => { + expect(Option.isSome(option)).toBe(true) + + if (!Option.isSome(option)) + throw new Error("Expected Some option, received None") + + return option.value +} + +describe("Query", () => { + it("fetch caches successful results until they are invalidated or stale", async () => { + let calls = 0 + const key = Stream.empty as Stream.Stream + + const result = await runQueryTest(Effect.gen(function*() { + const query = yield* Query.make({ + key, + f: ([id]: readonly [number]) => Effect.sync(() => { + calls += 1 + return `value:${id}:${calls}` + }), + staleTime: "1 minute", + }) + + const first = yield* query.fetch([1]) + const second = yield* query.fetch([1]) + + return [first, second] as const + })) + + expect(calls).toBe(1) + expect(result[0]._tag).toBe("Success") + expect(result[1]._tag).toBe("Success") + expect(expectSuccessValue(result[0])).toBe("value:1:1") + expect(expectSuccessValue(result[1])).toBe("value:1:1") + }) + + it("refresh reruns the latest query key", async () => { + let calls = 0 + const key = Stream.empty as Stream.Stream + + const result = await runQueryTest(Effect.gen(function*() { + const query = yield* Query.make({ + key, + f: ([id]: readonly [number]) => Effect.sync(() => { + calls += 1 + return `value:${id}:${calls}` + }), + staleTime: "0 millis", + }) + + const first = yield* query.fetch([1]) + yield* Effect.sleep("1 millis") + const refreshed = yield* query.refresh + + return [first, refreshed] as const + })) + + expect(calls).toBe(2) + expect(expectSuccessValue(result[0])).toBe("value:1:1") + expect(expectSuccessValue(result[1])).toBe("value:1:2") + }) + + it("invalidateCacheEntry forces the next fetch for that key to rerun", async () => { + let calls = 0 + const key = Stream.empty as Stream.Stream + + const result = await runQueryTest(Effect.gen(function*() { + const query = yield* Query.make({ + key, + f: ([id]: readonly [number]) => Effect.sync(() => { + calls += 1 + return `value:${id}:${calls}` + }), + staleTime: "1 minute", + }) + + const first = yield* query.fetch([1]) + yield* query.invalidateCacheEntry([1]) + const second = yield* query.fetch([1]) + + return [first, second] as const + })) + + expect(calls).toBe(2) + expect(expectSuccessValue(result[0])).toBe("value:1:1") + expect(expectSuccessValue(result[1])).toBe("value:1:2") + }) + + it("invalidateCache clears cached entries for the query function", async () => { + let calls = 0 + const key = Stream.empty as Stream.Stream + + const result = await runQueryTest(Effect.gen(function*() { + const query = yield* Query.make({ + key, + f: ([id]: readonly [number]) => Effect.sync(() => { + calls += 1 + return `value:${id}:${calls}` + }), + staleTime: "1 minute", + }) + + const first = yield* query.fetch([1]) + yield* query.invalidateCache + const second = yield* query.fetch([1]) + + return [first, second] as const + })) + + expect(calls).toBe(2) + expect(expectSuccessValue(result[0])).toBe("value:1:1") + expect(expectSuccessValue(result[1])).toBe("value:1:2") + }) + + it("service starts the key stream automatically and updates latest state", async () => { + let calls = 0 + const key = Stream.make([1] as const) as Stream.Stream + + const effect = Effect.gen(function*() { + const query = yield* Query.service({ + key, + f: ([id]: readonly [number]) => Effect.sync(() => { + calls += 1 + return `value:${id}:${calls}` + }), + staleTime: "1 minute", + }) + + yield* Effect.sleep("10 millis") + + return { + final: yield* query.result.get, + latestKey: yield* query.latestKey.get, + latestFinalResult: yield* query.latestFinalResult.get, + } + }) + + const result = await runQueryTest(effect) + + expect(calls).toBe(1) + expect(expectSuccessValue(result.final)).toBe("value:1:1") + expect(expectSomeValue(result.latestKey)).toEqual([1]) + expect(expectSuccessValue(expectSomeValue(result.latestFinalResult))).toBe("value:1:1") + }) +}) -- 2.52.0 From 845aa193ba4e85b01c1f9e1f6eb429a571f6fd9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 8 Jun 2026 13:55:16 +0200 Subject: [PATCH 006/133] Add getting started docs --- bun.lock | 6 +- packages/docs/README.md | 34 +-- .../docs/blog/2019-05-28-first-blog-post.md | 10 - .../docs/blog/2019-05-29-long-blog-post.md | 42 ---- .../docs/blog/2021-08-01-mdx-blog-post.mdx | 24 -- .../docusaurus-plushie-banner.jpeg | Bin 96122 -> 0 bytes .../docs/blog/2021-08-26-welcome/index.md | 27 --- packages/docs/blog/authors.yml | 25 --- packages/docs/blog/tags.yml | 19 -- packages/docs/docs/getting-started.md | 208 ++++++++++++++++++ packages/docs/docs/intro.md | 108 --------- .../docs/docs/tutorial-basics/_category_.json | 8 - .../docs/tutorial-basics/congratulations.md | 23 -- .../tutorial-basics/create-a-blog-post.md | 34 --- .../docs/tutorial-basics/create-a-document.md | 57 ----- .../docs/tutorial-basics/create-a-page.md | 43 ---- .../docs/tutorial-basics/deploy-your-site.md | 31 --- .../tutorial-basics/markdown-features.mdx | 152 ------------- .../docs/docs/tutorial-extras/_category_.json | 7 - .../img/docsVersionDropdown.png | Bin 25427 -> 0 bytes .../tutorial-extras/img/localeDropdown.png | Bin 27841 -> 0 bytes .../tutorial-extras/manage-docs-versions.md | 55 ----- .../tutorial-extras/translate-your-site.md | 88 -------- packages/docs/docusaurus.config.ts | 112 ++++------ packages/docs/package.json | 2 + packages/docs/sidebars.ts | 32 +-- .../src/components/HomepageFeatures/index.tsx | 71 ------ .../HomepageFeatures/styles.module.css | 11 - packages/docs/src/css/custom.css | 57 ++--- packages/docs/src/pages/index.module.css | 91 ++++++-- packages/docs/src/pages/index.tsx | 90 +++++--- packages/docs/src/pages/markdown-page.md | 7 - .../static/img/docusaurus-social-card.jpg | Bin 55746 -> 0 bytes packages/docs/static/img/docusaurus.png | Bin 5142 -> 0 bytes packages/docs/static/img/logo.svg | 7 +- .../static/img/undraw_docusaurus_mountain.svg | 171 -------------- .../static/img/undraw_docusaurus_react.svg | 170 -------------- .../static/img/undraw_docusaurus_tree.svg | 40 ---- packages/docs/tsconfig.json | 3 +- 39 files changed, 434 insertions(+), 1431 deletions(-) delete mode 100644 packages/docs/blog/2019-05-28-first-blog-post.md delete mode 100644 packages/docs/blog/2019-05-29-long-blog-post.md delete mode 100644 packages/docs/blog/2021-08-01-mdx-blog-post.mdx delete mode 100644 packages/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg delete mode 100644 packages/docs/blog/2021-08-26-welcome/index.md delete mode 100644 packages/docs/blog/authors.yml delete mode 100644 packages/docs/blog/tags.yml create mode 100644 packages/docs/docs/getting-started.md delete mode 100644 packages/docs/docs/intro.md delete mode 100644 packages/docs/docs/tutorial-basics/_category_.json delete mode 100644 packages/docs/docs/tutorial-basics/congratulations.md delete mode 100644 packages/docs/docs/tutorial-basics/create-a-blog-post.md delete mode 100644 packages/docs/docs/tutorial-basics/create-a-document.md delete mode 100644 packages/docs/docs/tutorial-basics/create-a-page.md delete mode 100644 packages/docs/docs/tutorial-basics/deploy-your-site.md delete mode 100644 packages/docs/docs/tutorial-basics/markdown-features.mdx delete mode 100644 packages/docs/docs/tutorial-extras/_category_.json delete mode 100644 packages/docs/docs/tutorial-extras/img/docsVersionDropdown.png delete mode 100644 packages/docs/docs/tutorial-extras/img/localeDropdown.png delete mode 100644 packages/docs/docs/tutorial-extras/manage-docs-versions.md delete mode 100644 packages/docs/docs/tutorial-extras/translate-your-site.md delete mode 100644 packages/docs/src/components/HomepageFeatures/index.tsx delete mode 100644 packages/docs/src/components/HomepageFeatures/styles.module.css delete mode 100644 packages/docs/src/pages/markdown-page.md delete mode 100644 packages/docs/static/img/docusaurus-social-card.jpg delete mode 100644 packages/docs/static/img/docusaurus.png delete mode 100644 packages/docs/static/img/undraw_docusaurus_mountain.svg delete mode 100644 packages/docs/static/img/undraw_docusaurus_react.svg delete mode 100644 packages/docs/static/img/undraw_docusaurus_tree.svg diff --git a/bun.lock b/bun.lock index ccc7b94..bc1bfa4 100644 --- a/bun.lock +++ b/bun.lock @@ -31,6 +31,8 @@ "@docusaurus/module-type-aliases": "3.10.1", "@docusaurus/tsconfig": "3.10.1", "@docusaurus/types": "3.10.1", + "@types/react": "^19.2.15", + "@types/react-dom": "^19.2.3", "typescript": "~6.0.3", }, }, @@ -3028,10 +3030,6 @@ "decompress-response/mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], - "docs/react": ["react@19.2.5", "", {}, "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA=="], - - "docs/react-dom": ["react-dom@19.2.5", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.5" } }, "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag=="], - "dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], "dot-prop/is-obj": ["is-obj@2.0.0", "", {}, "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="], diff --git a/packages/docs/README.md b/packages/docs/README.md index b28211a..189dc20 100644 --- a/packages/docs/README.md +++ b/packages/docs/README.md @@ -1,41 +1,17 @@ -# Website +# effect-fc Docs -This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator. - -## Installation - -```bash -yarn -``` +The documentation site is built with Docusaurus. ## Local Development ```bash -yarn start +bun run --cwd packages/docs start ``` -This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. - ## Build ```bash -yarn build +bun run --cwd packages/docs build ``` -This command generates static content into the `build` directory and can be served using any static contents hosting service. - -## Deployment - -Using SSH: - -```bash -USE_SSH=true yarn deploy -``` - -Not using SSH: - -```bash -GIT_USER= yarn deploy -``` - -If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. +The static site is written to `packages/docs/build`. diff --git a/packages/docs/blog/2019-05-28-first-blog-post.md b/packages/docs/blog/2019-05-28-first-blog-post.md deleted file mode 100644 index 8fc0194..0000000 --- a/packages/docs/blog/2019-05-28-first-blog-post.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -slug: first-blog-post -title: First Blog Post -authors: [slorber, yangshun] -tags: [hola, docusaurus] ---- - -Lorem ipsum dolor sit amet... - -...consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet diff --git a/packages/docs/blog/2019-05-29-long-blog-post.md b/packages/docs/blog/2019-05-29-long-blog-post.md deleted file mode 100644 index 0705713..0000000 --- a/packages/docs/blog/2019-05-29-long-blog-post.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -slug: long-blog-post -title: Long Blog Post -authors: yangshun -tags: [hello, docusaurus] ---- - -This is the summary of a very long blog post, - -Use a `` comment to limit blog post size in the list view. - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet diff --git a/packages/docs/blog/2021-08-01-mdx-blog-post.mdx b/packages/docs/blog/2021-08-01-mdx-blog-post.mdx deleted file mode 100644 index 0c4b4a4..0000000 --- a/packages/docs/blog/2021-08-01-mdx-blog-post.mdx +++ /dev/null @@ -1,24 +0,0 @@ ---- -slug: mdx-blog-post -title: MDX Blog Post -authors: [slorber] -tags: [docusaurus] ---- - -Blog posts support [Docusaurus Markdown features](https://docusaurus.io/docs/markdown-features), such as [MDX](https://mdxjs.com/). - -:::tip - -Use the power of React to create interactive blog posts. - -::: - -{/* truncate */} - -For example, use JSX to create an interactive button: - -```js - -``` - - diff --git a/packages/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg b/packages/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg deleted file mode 100644 index 11bda0928456b12f8e53d0ba5709212a4058d449..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 96122 zcmb4pbySp3_%AIb($d}CN{6sCNbJIblrCK=AuXwZ)Y2^7EXyvibPLiUv2=*iETNcDDZ-!M(5gfan1QF);-jEfp=>|F`_>!=WO^Jtthn$K}Goqr%0f!u{8e!-9i@ zhmU(NIR8g*@o?}7?okromonkv{J(|wy~6vi^xrZLIX*599wk2Ieb#lAbZ*fz97a4{ zJY7PbSOUsOwNy1OwNzXx4iXOC|2z)keOwmKpd-&ia_{g7{tN#ng-gPNcc1#tlkjM! zO6lT6;ZU0JB&4eA(n2(-bp-FTi8b+f7%9WKh({QCB8bELa9lXp#GSXVPIvbL=ZA)_ zoqe{#7VMtQs`;Ng5O8q3j-8IgrN#}94v)TX4^NlszBRSzdq}A`TxwFd3|y~ciPQw? z%W89mZQrCUNI$g^7Oh9(UFDIP_r7lI7lWz&hZ1*kZ$baGz-#@nL4S(s3tjnk2vk5* zGnL>!jFf8k?c!+McUT=ympT%ld*3}>E?g-5z9LI_yzT>@2o6r3i2v)t?KwGOxzsp5 z--7^Xa4<>>P6hlaW!G1-kpn0Y2dq(kdhFvvV+2FM0)3np}3GKzTt;)#GZ=Z?W z!}GMkBmSB3taZb*d{@PnL&d_l(Ks(Z2Nbb?3HFfuIKl`Y+P!9$uuAsc53|NzT!gCE z{M_rr@ucO9AC$3tNI(^d8!3^&0lCM-kw_(|g&{O!)%`pqf8E|0W;wYyy}6&z6(2B; zRYt1FlHZ2C7vc@FdKzC@n?}jobe2D9^;P-sa5`IfwpE1e6#N|6qQw8o+38045pxM* z_59Aq@8~>dJCtqhns#jEI~z0hACBNUZ;I~qj_$}bPXswGCwZz`c=)~lO#R;=sD(%9 za&bUY81NY4aNY25K5M9{QQ`EOS{V4jzXdWnDdV2b8HKe6T<|X$Q%nTAemPnPhtCab z@I(`E5U22@kW&(;Pynv}zWp62&;CfRX7N~Ze4eAlaDu!0dW=(x2_An*}x3G&V2kUsI=T|3LqH$PFPB?r*Kh zT<(BanS8n8ZL2f{u<*C=c;#&Iv3z05|BtwHPyLVX$JfSZ-nPRGyw_WdBUAS?NhDHJ zmzyA*oPZ~V;9d%;G25NPBOfQ-_D`B?F5{09Gw9nt9ehQ4_7uLZZQvbQt_P+|;LlMZ8=jss zF^Gm7)AuJd!9`>njaJZ$iVyWbd6|Twl_cKuZ2N()vsz1j@E37vPyKyt=e2GqZ^MR~ zXIy^LItyv$VNEn)MYm=|*3p-TDZIgKxoy7MI3JQa*lF%)ARPfF;fs*DQ?da`y7oEU zh_lgIWD}kW>MyGS)zaY65j&?~?T{j(I0L8nXp-HVZ_c&_z>K4Vi_<5qV_D*Pmntfm zcZuH8?M-w;z;3X$(8R`DMJ?#^m#o9ZLE0Ismu8& zDF)Q?Teh3z;(@8v6Q-&8=w`afg3mLQ85XKF=>ht;Mk<9C({@^a!<@Wn&e@#S*tGZT zflx~uFh89d7#69BINhL^;7=1nNyD(`#`N(kcJFxJH1wC-G z;3~)5?Zx+e8gBGJEGIZpXCR@*4E3T{e~F3|np7zaFTW*H$6lk=q&W<9@%|HhT)JsG zi?G)xD*Su@aGq|R2%ww6-{29RSlN?n22{r1v7(>8AqB`_W!ed6MbYgY>Lr~WdJ&67xXmBw;p)KRhD8c| zJPCE$_%TC!QMW^NN%e0n5R2!O>QuB$oNP`QHKU(-$F6g084quR%O&2C0<#jZqHNw4 zg}XntN)!#<#jr(XMe}^|UlLdeBP*t#i${&;_yuBmDs$W2O;1E|sSj=;W^ zSyF|!M=xm-QCXVU7mQ}V(~7UrsKOIK5r4^7F*g0VH)w1<|34dC_`UQC*oTu=+B`9* z4Jh>4me{%44wl;7BDJkvDDWJ6SL?-=_fdbjK&XRp5Vk`9;#>i?%Motv>V(|7;A}}O zU8%V37GK!!mZHZ`7L5Ns*ztfB%;y+ar#4rSN%qi@zDw*8HNT7L@UTW-9V>6VIrIS2`w$ZVxrD_Pvo4;!t)?he`;kX47HQS z-ZH7w(v&VJyMNj9a9hr72G+d({AQb?zG8>o3fA&C9sA)(_LXsqbK3q#_q2In;XuQA z;NKnzM$3uO)*k{JyOnxO7id4ceg~27qWT|x^KLg)9iN9N9QmA0xoo+VRJA$ z_etyG#Z~#aXRpU(?tAXq{@pX43OnVh@LXP_K@+?k9bogc$6N&(^|_I7ezWOoTLFK- zq`ji~=M!@gj*9u2?}O^~rbKuIaGHS#4~<7S&j`ui!Fw}>9T~O9Fj^ zyN};L5Oen^`4*<%c5`ifzl|RH{yv(l$yZoAGe7Vxi@NG$b$bfy@^r|37dNU}^yhDP zg3>=6>ltZV(tkMK&y2yjHjZAHEU1)`Px7LL-ApPAQyMeeb~^%^Tw+x_#AO& zwY9CqLCRqDuj8Hhori(`zOq4#X2@itHGeu;Oe8noy z;iV-)*{@MgVV=ZE;SQoB`g@sly`(oumzOeyw^%x9Ge`JZfNAQ3n*xKER#RJN$@N3` zX|n~{{3NG=HSLm3|GFI)m9jjMj&1 zi`#yIC*L7GD%~$4EPts}*Rd@VTe(M6jJF8MDif>-iGqb9>Q9zYo92egEmZacG>pIx zT3XS%Wn7uU37^#?IO>Y1N%%BY>lt24Jq!#rl0 zE|_4f751``XY#Kqndv+Y0tJc@_=K|OoS7Hcx$j7now-)jIS@SJ7Z`qR{;qwEN!yw( zrtTrDt}LdyQl>pCJEisU{ExS-0(RC(8z?xeh0uYie&4|@NL1Kt!PTFRbK~9VJLd%? zyjj}ixr`csCmc9SDb<>2>GnCHm-i(a=t69-_MDt5ksjAVU7k>i!(BOET#;8#cwKh0 zjS=YVlpYl!E7+!y;RpeY=C=*|<%&Oh2+5qCv^JIR3Of1ue9k7N`?6YW;A+{c(pyeP z^ZpjVK^#7%E}QYRtS*uaK_K$Oyoq3%xOCV3?n&qBv}Qc;N8FQ2O#u{>slaV21l1Fc)AyIlbfdX7AExO{F?eOvERYJb;Ni zckPYRgfT@0Y4PwO%7BY@l#2<^fKapIft)oU2O*-JU&?8;Z7Q467Gqyc1RGqTp3zqn z_F<{stV*oYnEE+<1}A|K7({3kbdJ=r67p>3|7YtA6(Iw>`GxKnm1Ve>A@&z9Vvu8H`OuD7{B zMq(lkGSK&awU^aqf~Hx?^P4cUl^^fU&*kPEt$t4z0-PMDv!U}pIKO<9Sv;GRJ{qnc zM#0V^%Zxa5H(Iv{@2xzz5#$zpTWxaaiu@Y4QU89(yi{9^PHM{|J_i?6y zgf4QjZLTyomqcSjIJKGS3lb zSwmVhHvq>|mo6iNA+%kh;XIm9P0(Wjl%N@e!Uo|`7fqKQ0Yb{?nwhp%!%@R7IgQ(J zLdJbRkfT+8-daWy0_~Aj4@&Z<8;^K*_MKdo=%J+qo&7AP5Y>3CZDQwLk>VrP-iE3l z8mvBgeWl{(67&r>s zolqo}wttX5$056wr+?q;8$fEMMrSIe%AQCqi$0{Qt{6t|=rBnTL`u#0;b>^^q~bHE zp{uMeEEOF+C@Bea`ih=v`oWzl`fF0@xNrw_gl78Y95SqUn_wnsHu&(x4lD7hc2>u& z+c4)a*}b=lY{4v4Y@S1w5Z2f!Jq8LAqHhf&HyFe+xH zbfYn zuHOaD(3Z44uZnBo`1Un7x{2QW9QCOpsNS-qWe%Q$F)qV<&9q&PJhD?RJ@V!6b{5RuzyJ7cBd?%j{&sd zks}NY{pGQJFNu*E%g=q^iNCa_pTISw{g5lr<;sbC9@&D4|{$QCRNde}1aaR*iIJ>SkWWj9GmQq+0=}_`Y_Ek-oPg#tRE%68|XT zB;g{AmDK0gbP&>?-)o<(f8r}>S&x@WpxLhLJ6!VHvd^8m{d!dr7T3pz$ zkn$>3T~Nk?bRK9XEGr-E(p1z!l=>NOIE93eV1Q}%M}o=Jc(kJdFI%%?IHjKWBv=F- zs0kf#$k+|N^0Kmxpqs_13OW!7mM)n&4n{0j?O}zqJVqRfO0L;*JN}9tgHPRp+@oVB zL^!D_@iZhfor|uMCvR_WYBUa3qK1;a0Sidz=3nvFUmND_0QX-%no0}PDmmBm$!Q>E22?Y^dsKW0G}?bkHM8iy?HUZJe3D3p>1 z{o>d|o2RGDul?wm_UifFO%C!~|FkRJ8a~u-1G`aKtr9TmNLt2fx<)$)zT|Y_bZ~;j zZ}|?5bT+5#t2#Z&ZjZ&(>}e~tx(OssxQ3R?$4(c{8| zA{yv+v62$*(TsZHW7*HdBc_*TZp57AA09eH5#R)*7`b!#100}{HOmdQKm_miUqlBW zZD@x|#G<>fCMXis0q5cF%MdAB0y4U4`ufgyXagAF75QILp?OQMg)oJ-I5tcXNTV3c z^LdROg=LH8OWSuduIFYH>yoIy>?K#m=7i9g&A;qZckd=Qq`Af993c<1HC+HF3?3TA z@mXTS>d{;Y^&|CQE)x8(;Ecs0QHElH1xI&d6&Uq}k*an~<;wvD&Gm?=IaRXC4_2t+ z687TAZDvFH`P_rv+O+vii*ILLDq&e;Enb4GCZxSUyr*?BG*S{dy(~hS+d8%Ae9{Q0 zDFTsg9%WffrG!4@g#5<1DSfOuyKOqS6anp;I0|{^ z)V|zlQP!t&b3wI~7AJ(b|n}V$)IB5Fya)0*qVbt^^Xy>&KoM5@G zgv~8hvW8mIQ#^U!=(x z9?eBPZ$ao`DWyTW$iz!Q`hLz+KZ&*med242vVjHA{9$>d~E!>k~8H`e}5Ob?c^7D<+;Pp*!^~!b~jcszphKaneeErmWa|Ii2Oi~ ztGB4PTrExmF%PO~Rlw{5G?R45H%J2)zC4d?gLsc0?I}+&@ z{srJv;THoXHj*l`5Q|Tga(WP!7MOqS|4vLj8TW$CZa(*>1?6`$ z@pb*I!r>YumfjryY$QPZ&5ybh7ImdJ=}jf0R&Il)Rm8;{T#`EZ(8$4xK5)i|(J2>A zM(ECw(3nO!P|NY%80nn9)0)$_wQ6EY)@tA=fiw6Ckl?6%O@ z>iR~gE<@*gj8f=2)9R#xOOTiDw+cG>OO%J1<=dA?ehZH`uc}v z5rU~T1mqht0WB?l44gV3*5~ubC7^VJ?0P zaXK-^Pxha#1TpdkU7p`ESsU|D+8lTCPuba3r1}NxZiE&_I8Tx1G@)B3Ie#b@e%d`@ znIB6?VVd@|FiiIY5+r1dt`0*7CSknIt4x^I8lcbofDCyRBVB4u4goFQzHpkSVflWC zwCjG0O1Gn0h4%24jU*=Xv{Dg1GblXO54Wq$@-$o{ecO2#8L)Ph46``+>pER>c+GW$ zM(_lX8sW#qMTjI&_xnpy7&J=2N6?X_`pi{1qV%(bZ`?B|_=-Wqy}i#QMBhD-9s2~c zy7b9>k)dilS&g_J-(ltH!~Gud%K0oYXy7WObRVqWIQWFXU?{rDV z3ggo;zJQqxIwniw*YYRCIa)*_EWpICGC#=Rny3r;`R@LdNvYW-FgcO%z3NicRCZ1~ zr^>u8=iAvGHtZ*OTiMpv9AW!t^yU%s#0J_1Jj(G-;n1NVwt|-9p@r5g=&hhj z1nyyZ3~Dv2^qB>>zG(RzSlG|YU8v?0scfBa?5rKq+S(q|BL=E&8z;zIi-JpLE}t{X zC$jXzp9eAMETY=;3mQg({0eFdgYQ^9w`8`P{pXzAibKLGsLZIHeGwLV?3;0NhcJD* zW=jF6I?uh7cnonu|01<_;8Y**Gym3BCvZ@ivavgH{8Ys)L0)!KpF3kN<)NbxWqoIg zk}H!2P(+*L^U;+}sAL7~{4z9T$5;N&FXJ@lEb!F(Tz^mLXIY+Xoa8TCE}?oMt@2dF zf>B7vRnrXYt*^{_10oHxyR&QIX*_A69}X}I)WsaK?lU?w zy$^EMqSM;=o9rGpvC;Y5hd$=({MVCGg0~qSRl?QF2fWElYI_6-(v`Ds8JXMNUh~@d zWH?o5p$-i}&}iI?V3Q`#uX{eS$DhkUlnCO>r#B_^e^(O7Q{_t^=vWq6c#OCzKhoO0 z>32c(onMuwu)W}-EUGQg%KW%{PX{kY`i8q`F3DM`^r z!$)9ld2-fLN3WUry+VwXhmA^BUOO{*tc=o0;~`%Ca<(w=m6pWoO?LAFnnITD$;4f1 zdH)T)1!-l2iUHo|F5wV+q=!``)Qy~Ut5}0LPVcL+PVN=`-kE|*wA&=vLJE}>MFf9) zLt!6O^ZQ)(vglM}uzOPd0QN`M;WPw^X&aoW#x|kYoR#)bCHgEbGjry|844*9YTYBCxxj0&FM9T;FV9bu>;C5|_XUj%`lRr>o+m|j2w35a*LG`KiegseN*Vq||f zpKo+14SwyV7d7ICZYcB%nnqii`@U>;LT4X6c&u$(mMQCPn=5W1>fVq*>-%eSmqRPC z!MqV{0CK-po#-m}|GiC9*)!(f7%0~@X2uh8`BJ~{dz*Ync9O1wkf5C)WL3naIzopG zHvd`1UOoEtlLa?}QOao@HL{F{mI*K65TO$*SkruGJ9cH}2ju9?KuX(8@a1Zyo$)6p zZyW0qF;H_NM7dV)Yj^I?H(w9Wej^ra@(z+8`+Jgw!rYedJu7|k=mo4iUFPzl(M6VS zbbu2fb6_=)UQm-WUL;&3oCNw^s!y0Hb?(x+elVSM>w^f#=jtvUb~6Iia>Q`3alZ4| z!j996r)(u@83OLDw6YetLb4iWm7+S)t#!mEva~OF7%~>=+DuYL@me!-;)J-gNC*Ur zA|;5H1@Y8rW7RV?MKh$mP_*+bS%!1)S_h2SJYQ~+R#cC`zu~d? zOI^f%5GtC|SSF%ErwSjA*`s8rtbF=>d9`-kELhy1S3P;&3;1gB$_sWdlY5=>)|YCs zaAGeo=f|WwwRBBaT#s|qO#D)%Q;5EdbB`@>l^)%EEnYRfsTcDFB&!5TF%z-b@a2FtQSU0aD;eRfc&CPic*R+ zQbd1TSU857kART6jzOmnmq^G8r~e1=S?LE$yfUi^VJk6D{f@%0hFYyxTKCqM!_Lku zY?H0EO#0bF4(UWmhPVFYySswtbAxQ}j15fDU32FbfyU}l-O@JSrLX?sX!Q*h5_tkQ zCtcr27j3zI(b3|TZI*t(-ta7BCGeIEc_ZQV{Wlg-iBLFWy!|NdWvue9$0BQj_1$Bp zr`qiuEt0~v+OhZwhq8Mi1 zIw8~;Sm0}2 z`#Z_V*`Gtl7e<#qj`xO|P7M?WmGffQxcNF+x<%-$!L__0mD(0f9Rop;vZfa(V)yz1 zE-cIPoYeHN29k7N$0WLjCYs!YP+iwDozf(gSe6H*1g^^7?82$E% zS+c>;5q8OK9qMVDD}$)M@dR40nw293G2)zguH2&?cwoLJ@+eF4v=>g#%A}>R(~ovXE-mGs73s_&xby_%f}MF1omBoV~8zG)9FCUxZl+03&8 zMo*Rg6u22p>bxtf#)@PI_~o$3n#$C2TEy|2cqEvo=<>YQ3@_0OPn8mh1#_wmn~5Yn z(=m}EIZ6e^^W+<*D*Jjsy+Jv`4jwSyeGF%ijP4W1RK5u=$1-9FkUWy?o?OtxR0Px>TvF0%+;luL8uZWYWuM&>2#N1M!zIM~ zhjVaUQF{cRG%+=sIXEzp>C($LdH*Y4BMVuE%5!^vX=7DW4mYLY6uXrMul&O?U)Dw# zT)+#OII#l7ZY~8)(sLEwpPp#0)67O3m?;PGuT61U+pnzyzr?t(-rRHH-%+c;ob;ZTF5`H3a7k^Wg8X94FwFi1kV+$_Yy zXTvfH$(d}PRhZAsIbAPRB9M;(jZWnP1ImuH&&>3^RlXX)u(sWW=FPKFU!tUjb@pL} zM|#Mo$rf7F^D~+khXrUzlW0<>wk`hb=gjg)=96tX2ReSt$^b7Zi2q0`^>L2Mr9tR% z440)8CVH`A)GyCarH4?V9@etZ*faJIXV6V}Fcnz?m-2gUUh~mrxZIeajFUNrlTk{Z zd8sQm@el1OA7qu!%gLx;NRQwm8FDb6!>VPO-c&0AgXL|~UNoYcW=DhKeWW1RH!C%o zA;q+nA4?I~DVn>yGN`g6aYj&?iA7Z#onO?v!NtxbNE^W&*y$}dlE!C{o7m@c%*fS0 zz_~2;b#I7Ri799%3IhVZ4E5H3XZZel*OWLYUV9D0Tcg>O##T|P>{`(AY+jFhL5fu` zuynS{@E;DK%W}HBYW8cB&UoQgH6{>)SrjCR^|%5U4({A*VAW|PXETk@a8a6(dRzwt z#{=^6uZG6(CCb&TCN=!S5#mZI6Qm5iRyHud%LsK8(y}cz$?%hxRVbYcSk(jQ)Hf*q zwl`RXgq%Vq2>?qiQLj(sikZ5M2--71+VIB4>t#QF5kY>+0 zvdrvFUKb|@`qYA_DY~F8uSs*wtSyZjru;0Jd3f;q2xc^|l4;ainHm0GyTBPE^x351Nfhu+U_zM%JNv5tRNY(SJLI>_cH|`_% zBv}sM>s)u6&ftbT2iCAIbVYfaUdPKoAvKRr(h$g%l=euf!4+uP{uuJ2-j;C-gh79tNgvD!v);u3L54L8bMpdHOxBezyB$J z6t|CIWiq(2k-xMuIlq+@%c*oUf)auDn&NzqLb-t?B`)P6`sEjdLaw{t=0WE!psHKgYc`L8 zG7f5fbN<5Tc|Sc;VfuD8K7LsFY}c)XgtW)}UzLZ%PN2{=X%SF}l%n5@+mX^Tghf)C zQT&=hLLvxe&MK4|eJ=aMDkZi-%i5#;LRBB}9{5$@0{+NM_YoNPz_<(gyMe8_SQH4* zYs|(<2TOk`SN+|6){TN8HLBf=AL?Q5Wca0h;$bU05=f4Q$Ce1foxm6^F#KFxsX?$Dq%n7L@)AR}- z&sp2&#EosZM2gM29vW25{lhV-Z1N)rJ*7vJCt41#dOcxI`~uT!F-f|GtYZ5$j>V<= zK@HEb<0GW9P6e=bcVm#Ty6$x8j)|034zm=W^ZG!o-(MwhvzB207jL{j#Wr zf3d4_jvjQH2}PJ^fXo642QaQa6SIkfo=`<$&eyhn3IQPVc8GcDB52|H1>8Iut^!rs zC*ZD{x=G}jXK(yQf)&(+qxcckLnigZ_sae;{8ma1@=cIYvEfv1*!;%B!dd$t&bjiX zjLpiO1-g7WV!!s2{{sGJM4)42K)c}T-{uU*qv<>aOU}lXLmg2AOHj#J zki~HRbZ)>CvNm`r6BJX`hu2KeqCd0XlcA$ofF_0`t48MYK62h`5peGP1hV>0lG|m| zgWJRC+n9plKb-fsjCaB)bz?)}0q9?6jnI+-?$-r+K$|Br+H^=3@NtAFT4l z2Pi-M&*wPOB{W@wZ-O;n;LC&fOFKV-3^r~IIPJgH(Qpu5xoI2h@Hq2uu%{?y_46MT z`3othZz2iH{As=P+;}S0rE#`E2WqQPfr4&cPe(9Ktb~6jBPFsV>h*v;I40yZ>^Xz|QmC-`*#T zuCmXO#@x)`YmiZR8qy(gIa|mxze9-8a>4X|+Ry(%r`IIcXF4{gloG(w0Zv|e)-5$B zFR9*Ql(r&d+E;8rd(IRG-B*ayI(PfB-?UL~Sow+1Y4{mk=}6!wG{<3bm8%d8uUrRX zmFS*Vz0j+ynQUc{u++Nh%~FHPUOSb49r9StxA6XyKILE2qHS&1_qO5K(7%#T@HtKcx?+ZQBOAI6 zjSor!Q1@$2J=(O_HaIy^gFP2A$xAdmljhq5dELa!}A8tv_9E>5Ol!F@<`mu)dHKWLPv8lunR z;OOt%(~^s#z~1uT!@rASj6#`Nmj}}IFv3aFcO!H^@q(MZJTTgRp^!Gf+__|qf~;VN zi>pFV$ZLa%?x)U?-2o`@C8FW}Sz-J?zzrs5rzwS@>I5oZ6ywRw%hp6$!RgmP|KjOf z!Sh%rRz+hvQp&hGy~Ukxr0p=@*{0=yDy-nJ>BKdX*G$(+(b3QMum+kWNg2&~*QLko z*W@&s%qtW~J;Y)|y`9@2H=L8(Ewaykmwe8eGoQM|69>+i-|K}6x>gKS#w+7x7QlqV zWPRPKP-iA@jC;mm8gxvChZQj)VB*g`$U?84Q`ZhG`5L zQy;))-`BdwToBd$!x@&Xywj>yJyqDa&Man!bBR~&6<*P2C(knRy+@s&_;u$^UKHfL zNBExjJ*17XN{9=moVp>;T)*+>pweV zkqpPE)($ap_+Oan)#DL9H~w}L?k(hvtBW4IV&9$Cr4Od_f)RzC^~L1!`|># z%$v-L4zH~s{FG?hm6~J@(`5 z@`I*$QL}m!U@6E;u3tZdA;Zy|LK$qFd~)|2nDUAgHx~`vsT?0SUx3qCZrY@j7kjfD*hyUc~L86s!14rk9 zgm*6%*gqkK0`bL+Zg+j~XHVFSQIBw7*$Z#)kkG2!y5a9)CjoMF^wVLI<^@ zIG0@Qu4%nMp-ild>IADcH2JQf~6e)%OI_(LGI%=;Kq6B!MtwqJ^yI{BcJTot62W z%=0 zbQhF7T1G#I`ri6IHd>meOq$Q8)X(GW#bd(F)mbI8kpinT ztcWRAGA676;jNDmc4Og6y_9kq(M=rWX@cp?m6rf0*rdu-)K<>Pl>UVBuCkK;` zE%u(=@;kY8LZ<%Va5u)$DW+4IR+nq}t^s|@&qsqC0%3oF0?sUF&WnEMCqfs>yj(5T znL-zyT3Tji@~Wl=s}l>LUS5xfJ{EDzVgjIvR62OTN4g;;v})iI#h>;DcD@91_qzDW z4k~tTj{CRg!qXZztF^-rE9H6ZkV_hxOJEk=Evxad%L7+x-rYG^W}-O~#KxuhzLF(Q zs@zanss)5G^SfRH11hS^wy?u*oxD&rZ7PiIDg?raN(ethc!mQqycn%QvGm*LuxCLD zSnd~+!|TdT&_PGUrD7M!_R2e-i#>k5rw$dZnE-)||r z{~(#lp0ApHDfmZ|v2cj{#F@HP=l}0w(_) zGeJ5XB1na1WHT-Z-S)q+lLKXa>`ib2Ks?g;6g6K7UV(DTZiQ6)YLAW~{sVO{hYd#3 zxUvg3(}g)twI|k_tgjwEIH^zN3E8*vHGATJvELu65&wMd`D?_S%K!-5w1suU8oUi` ze#ByP=JKgEAxBE((U*1&>YvH3Bymg9d5uVGeH@#^EbZs)3=vj* zwK7Csa~K^WrQcd8S1V4_4*G|KzI{^6qEcA(=|(7*p9RcL zvH#{5WVmcVY}8!{9QfO2t#ViWuM{KKGl8%<_ak8SSHNo3moDDO%2O5h$Y#+KsI|&? ze>BfDv$!X*$H?PlKE0qos)z)U-*J(|1BTX=yj(npJQR-8lIjmR~dItB?C2n@$pB!cNsR5 zK5{z!)dO;|_`@(l%_Dfkl9vsQpgZZ=+>PHA7I#=nI{A%u8aDU@(3|CE;ITiS_g}K+ z+j4HWL_5PSZR!s@B$tiWPD0Y0Z_}Fd-{&w@#=qKXeV*iq;n?4!o31ITo~peGdD6RP zL)JRZF7#(0r7Tb-Kr(K*VL&y?pk6%z%B2P3q%w?8Pi}!)7^{%(h3#lLetDvy86fV= zrzs3s^%Cwm**F+$JcQCJO8#;Rt$F>2{lVg71E1WJ5ODHmq}=-@={M!K)74q;j?S0e z{7ybdS+(1Cdd|64Th+$dym>)4mx78OKXo2~2b3+wzb|Fv(u^B4^*uj>xB}!R{kTk= z5X_rHExdjM(p>%_CNwOCEIDYjlpG%f)zddv6IYKmnwEl0@*iz!Y}9hgO_DFw*LREf zYcNJ!8GQ3yZMOKS^m=7-|Bv^A*d-P=>?-pQ$7r9g2zkL`vD&gc9(x<(oi=9c9fijw ztSC)C`wxeP^F~-QweLweujxbKcM@FW3#O~3o4dOo$jJxR>uHqeN;u!Xd-W=WMhY^4 zwzy-o=FUFO&d*6xIy=%{^8Z7(cCx}^13R{V#lww>EBP?0N)vi`_;Dcc+B3|g#X1c> z?~C|Le+_+~7RfF5=J8@31G7m zM=`oCXAzQ74^b>8J$whv-7@|-LM!YgpgMGINiCOaz`eVy+37UX05SMx+!HKgZ}EzE zXNHLfss0ZK$^>_^T_bD{@@p~lt~&2|Q+)m2Plw5B#Mq zZ%U1q1Enk~em{-#KOgChb5IgWUoza8W1|)l!K8=E_lMkx{V67XAqnBMY1pPw2~;c* z0sT#HyrV1RcXU45((e1-3Q7Au$iHSspbL&YRT&I!OI+b@jM>!dSg55jX{HyC%DIoW`z`S5PqL@5|`)uqbMf)IUiAjl;~6xqZl`ucoX92I1oFr{e5CZMaKqh zaBpKe73<%LGi-4hUkb>Ih1u==f!_p&GBIB?kIcGjBxUWhDz11}vH$R3IPQ!;Np_4V zc`ldT7@(aOVv{iUUPv>fSx-+WC|&F%{x8+j`!ebzQeg_aV(Q9*QWmnl#*CcP){tLU zR~k085wAh-AomA&?#&hkEAJCb7~%`-wDA4qci?Q~M(B+93x1=WkMj2SqdrsrWyz#} zI26mgu$dFH%geihk2g(DeoMDI4Y~kYfkO7@ozI?3bX%n19Sw~{u>@Oh+q{8R-47(q zPLm-teKi5*Hb&bS@|QZ}uC=~P+;IN6Gcs6uTs%6+Z%*d~kT(Tn)X;pA% z@}8fJt{Dg0EWPo+x@z|y_@zpXK0Y3g9X^UcDB8c`LLWjS5&h1~q00VQad&-}rYd=r zR|t2ZY8eGQI2`-Fd2P~DH1|kG4~#nixZCj|wWVA>OiyIeciM;`m~@F*R!=o31(^br*KA?tX^-F7{h&T8AWNnC z)f%$21ZI#-3XqVEC>E@qENo=z-09+Mk^O6uc5IdhslPlUAxa?+l>VvL|u z8XD#0Diu)I?e&Lmz^RRfM@}4F!fpj$Ra&D=fkE#uex+uWcBtLytOCZzVeCp4EIG&7 z1;)85WaVQ6;vBQ?O``-V{cpl;3l!E?bv8E1pf z*4-Cr;l6Of{#z-GK3{%o%^0`MZ@uHF}IQSMGprgcE&ew-Cphi;0hR`(ZS zXjyl6HW@|_ESk`<()^;l5zWoOmjChlmeTlaWRAGD=+4|^vEsmq&)?eRyTO;3nAaQVVFDfhL%CP|I)%{xfOuOruQNZ}KD?m$g{&_zMl)R6hSBpM$^)r{ zGSEAdwFY|ZtniZbSfz5I0#f(|s1rqAK!&cbO5;H%=|`e!>=D^;e5-DVZE6{8JDot5 zPP^(jzI+x|l4x$vDlpzojUBG3M8tRSD!AD?_?VtUK6@#Y|5@jUA=J!g<4Ka%)D3W4 zaxQe)eR;!hjBF(Ohl1o#rhOO%xfxh6Mpr@)NI*7@9ju()M@uy-dfJ{1!r-ie8XkRq zc3lN8jY`9c1^%QfgUb5(CJkLjFJGrmh;TNp)7GIzI0W>YRqMqn~7A3Kc3Xb6IsnPY)5Q z+NbAt(vD3^bM&3eHH$+PR@*C?l0)$&x8;|jcMH9z!9w1}p@J<{Vy#?+Yo*mKZ68Zi zOQ*bV5>6jt3`;2S68F-H0({j*N-#zP*pjnPn%$yBe-#-H5t(IuVzx~pt=_g#8m`h& zHn`MeHJo>=R$RHX=3vC}?PK(EiZJZe%liLmw7ew z9}2#c6s5xQ4=FCqY2`OF9Kk+fVaFT#SqnQ3{y)z``V!0W5K=r+9@f^Z&d3OR+R@BC z!>-!0eCND--r(&w23n6U#NDhVU_N-8L>EGvKayuTGkY!&q zNl|s@s~RtY=O}bfjBOTgE_KD80$3M)gi`Y6;DQ}4CU3gC7A>GBVk`P}KYrziiiA5l zoYydmN>Sge+r}7{Av1)H@Z)Pk95g})syE^(YU5tBWfhh z1QzZdYqg&?(|FH!XUd5POA-C77~7#x-2N$@J=T1 zxAtN;sT!ToKa`X*9?@p#UaT+ErD{tHk02)KgtND3R?u@E){-k`~{iv`-7Cb(UPvIz*x+y`H8^t|47Z4le2s+UkiDJYZ(N8!{YizpWTUjBdkS^RX z#0UJokY?3#(K)^rYgLA*6;bLp9n0oVrBfrSkkE!CcX4rXQ7&geQbxYKx(y|DO6^#F zeP-tSm8%bDDGVSh_UdE7J)o)g;ygr%tV~(CQ^|QAqE!)`$Ire055+cFm94?vrn$Gw zVw7OkDxeKLzMP37gkeu*uF$f+KSWNCew;;Fpi%Ee2-Zwiv0{fzOb8>ph#I49hDB17 zQU^_q0xWcY!4xmMc>NiFIL~vEZds67CBT72Y!0)SQ-{6bTIUuwB3SmrrNrMU= zZj%Or_i%oRoB4!V`3Jz!RqHs zEHAY2{A*C-hK+mqwCDT=T&V&gOUrd8`Hjl|*z#p4p3dM+gQH+pHoJQAs-jNHhRWMs zqNpT#bPlD^Day3yabbN^(7|1;(6Huam5Qstv@7KqlWby7UD}0w{$RVo3*2KIyiR)D zlc}-k*u-7{DBT0vF==T=``f`Kp{{YhPqThlC@>mHVZ0V$OgZ@#LrBXnGHxI{oTDyP zG`*4_{-a{R0+sLUnQ{kWEL-X?G&S?5$!GeFP{X{%El@ zN0y7Qh;!aS2Iqoa+F_UUeHxlL5w%W^yJ_G9Wq18sde^>(tP0oL85 zy5&d$<6$S|elkNp9&xGCSc2yUI3DnJ55V0|mcD&w8VXge6xo>AysBYrQ}y-y-QD}6 zq>h+>g8?R7nN$HbCC49kKanFY@ng+8Or02L?-=dYeL{+G{Fp`MH4W8CPB`lt>lf-( zpa%i&rbDjpm$y7pmyzja`=EF)UMGLW3N_V6Bq|g}8BfWI>OsYcU@>G9SolRNLa z17o9N-_<(uFKeW0MQ=(sW^qa167e-5*((q@jQWR?x7oyB>ER6>W0a6Sr~&Vk^RW%L zLf4|Cg(B&Wh{Xz@Bmu(8QNLV9(us+k?J)y5V#+aFH#T`W5OXNlG$NqGV`&Upg< z3HLO}e1}G0-4fWW|LhitCa(naUZrkxiPY5At-`?lRuX=Lx}gaB zLsmh|$EMgm$mn1Hh4Ma}2XCUl&B=Bl+Sc}Ta)~t+DoK##lYeoBG zjY>Ao4es9^4Vo%O37SozE6)u5uN9dyc58^UQCOD#^YOt>1$d0|GZOgwk3iykY3ihV zT}H^K>55;Wfb+FZePC4({9b^hMm=QUC|()QL*eZgau-W&MvCGpGaJ#t^myz)Rm7D+ zauZ>OI}GvUetbi3V>#E*W9~RUI4<{M?Dw_Dl#4qlIge~An7dAmCYj_?><4f4-0}G_ zwWY<7%pVLzk+mhDn}g#ic`fglH8=x3wN?c%i)<^P-z~oART{apnwNjty}HT{ZhH*g zYvtMh9XgSdQ;_ALz=2tfE0B;#3V>t__fEYGWCJ;)HA3k88h1>GUI$QQ2E~?N*!?~+5@A<5|!P`no!y(nP zEbQ7gl5`3>Ge9vTHnV!|^HC~9FV5Ry(X!to8(Y`;pG94H%X{6;zot{BzbgmhvdlX~ zI<&01@H(q`n~yrAtHg}%FiKBbsF3a?Y7RpA`Odlfb6xt=Gkt!_>ei6&9`~#k zX^hp@6K4!nI7vzrzprD2u-}tN6eamOC_{>uKF$vtRL>)^A5eUYhj4-7i-9baE+1fE z0LV&Mz)8&dx5^z+LJGT(>HT)~r-gj}eMqiL?bjsptZqhQN@}}mOT~M9grvZX;u@in zB-3zBZLIQvPWmx@fh0eS)R+`MicJOTeS>|>Zew4~g+oWjq^PNk%SL(7sC-=ihi;9& zIp@U3N&rN+&pJF!zhp_db*-00BPoIB#amiy+hl^>M;Q-@D+j+vQlycX^Z$(=iStnM z`I;BK%$P%*PJy5@kSj`E|aXm;pN7{3qg_jw0(b8EmBxvA~odK89odU>E? z<$q7s%0RGg`Y~uuvD#Tu6h2!W(n@kx$KVA0tHQcACy5KGK?lF@*s<0%t>5QUeN z{~O`|d7C}5CUfQPa~r1}A*@&E|ME#+C=Gw@@M?bsIKP>_aplB9CG+`T_M zfQFexK`k6JcqQ%0AVrj#D!l9iKBoqoa#=tZ$UaUz#IDxK07O?74zqa!6J353i`5;Ns zkO{}Z`qYu?e8fWPX|KuM-HzPRk=ndt*!Q<;b5Qs=B&R*V?}mn+jH^JdopCOxU~xyFVA z9^{5Lh4Sf>;5*T+0=|>Nkb&0Zzw(V4S8|-TT~rS?_G(E<0=v=ix6I58OgA2;I6tc{ zRCQSQZzz8R#!?|KpdwM8O?(a;y?ph^s6}C@aMF5Ug=VcG#kC6|lhzF%WWiW8Z!rb` zu{iZf66-I0z8Udamig4BQq;oY2S0ZGiF=a+>o=AB1uJegziiIzh&B?` z{h3qveWx{8Q3daH$@pJ`cu;>#=2Gf3t>J zwsT>#q~cLEZ4Adh8!-KDIPi$)OxyutdGl>lGQ^*`F)LPh{Cw|^Z|lWB6iXn}n@We@ zOA59NYzi@_a7vaMf*2DH#sYNs&0+K3E;}8QJl6iCsqrHZLhk}l^(arcJwH4|%<{qQ zEb+MYD(rXeshQ^Rl_VxlB&^(jv8m_uG1nxAt3|tGwm>|s{5eS2Ojz3U%yDtgIuP4& zWXJO&q%wZjU4P<3&T-l#X9x^G@LnOrptddyMrm-+?QNZ%rvi%5zEC{=wVx76O`b`7 zM=tsi`@_IuJ^xTuH&NOjWBaPbLdojE&%f-NGH*jBkb_v5_?uVa2l~Yna+=zkd-V4o z%AKYGl|pSIQ4!_U;Psl;d@@xYa^jkf+fD(;e^p?0y5(J$rP9`Hf2&dsg(&-Zs>>Sl zi|0%_ccxSHOO0DmFy|s{;?II-$=7wK^&WgdA{~}1VP;s_y>3jrTj}g)8^qJe!5K@k zR6j9EyLE{o)`AJv>NpOZOB)5DhK|Pj_2}q^4u%#S2gLngzutG7fYrDHLpsdRs44 zZ3m8$EKX(?q_qV}rgd5~0z2ndVfMkP#rOHt6qcq?pe@^QR9^71Ah+XwNQ?liVn;uP z*koOot=<3=+=<+CL-se3EH#D_bLWap{4YyTGk~A|<*yGnU*`9`deuFjO$Sfgje)=`^V|HS6u@z>eQ*WsnF~3x zy+VIFFEM-EX+x^pz%k)4i2orm9Vds8L;~o#&pdv8bnTY;=1W?T`|^V)lU6$f00`jy ztK6rq!#^lL#~^zHd9*eJq-LkK+&2BRmOfU4->hF*QD&z$S5#foEX z!L6;N?it3Qln1}!$wFvVYX;Fh5VW5_#dm)YaU!d|k^d{q;WR2L1pwrzyKK#2XAIZu zXRJw5vwzr>-q%cTYDo9xNY8?Ci4X4wFTfy?l2oCo?IlMU<>NFf*Bsey0KgU0R#BVv zt$4I~xAUNi%&U;BFl+A_#VW#CWw*M48bDd{ui(WN-*{97Hw>3pys={{K_ME&NaZEq z!S}GVpjmkrBeDQti;L%BsTg{|sa$1cCUY*yl=&j{*6v=!xV;@FnRCqK!?bfxXpLyj841U};$t1xVqn=gPpETH4SEv;qm6nDt;5hN= zK=;=I5^mLh6iGrALZrtJkUFU}C+qf{Ge8hmT3a~QU54*%x-{DAFk`?g?y>z3gMJeK+Su$@X*Vv5Vo4B$Ka$lY+0TR@;Yj-aG;x zqIzLm!CMglHkljED?|!{#iLYwY~}vzs;lXhSq2&kstw=|Dxw<13HyjRgxcBn`IJYd z9l5w&_iiR;H{W2-@)Y9E5@wfLSHW4%W-BYJApTDBs~=4bcCBghvo$L&5{}Rd_d<|@ z=(B33K<$~_Y8&!$i>gpl(~ss$UrCl|!&dkd<7ac#!2z_GF^YHzZ3&!~IU{AjsD#yo zjbHL)ZRH|>(;+FF^)ga9y7zEATvBMlehwIp1g4=Lg7*UcV4EBdKAaoA-J#tk2D=zD z%o=%Gk6pFq@s*hg$`I9$EHQ));IeWp37i|=)(mo0yV|v-^+1Oq{{SPk!=?c3=~DObIBN^b_8H}Waj9&;f3{}) zn98RvNZIj_@kfE~7_CAA`y=J`yO(z&f~cg$9iCz;9^GvD zJbUMW(BWo^z|gtixNm2I&+~?-8)sb4B?q^xBSRpp66Co+W~S@_lox2Im@ocIO#hdc zB2BiDnJE!5$tzwy8Afz|Sr{o0L(2m4zqAzfzqIsuv|9&_*x@E*H%!M&*%t z_ihG`=RoFd&h0!Mk}`8VFi7snEcN;05K^(YM|O8^$o)p?0G(hMyh=)UVWE=Eo-MPf zV>(w<_pATi;8>I}{_bp`NjZ|sa`X}IQG#Ln>u$ssFz?u56e1EPJckbAjw*i9FuNxZ zyy+*vlJ&mprb-qrfaKIKTh*y=QLFr+f=s$HIbd&Lk~^seuV!9kn*^^GlpgcEpzfpo z@Fsq(>KBbBLu(npRyW1@nZ!*^PR~yWrF+d5G_>eS z)T1Ie#uYs}gG0+`d?r=RUHb)RNK00wU*BjP4|~P^B4z^^pAvTwZ5Prwhd>T&nnSd4 z7ojq#;T?tXExMj`5my{ku<#%+NJ@2E0j+JRoBQ*QXbl6YEFfAbB7%q3UgWJ}d-+}E zPq*-}`-}-uBYHFIMSqERaB}YKycS7W3+M@uvm!D~_eg7a85wBT(# zHBf$S3cISPKi}?@70(i}fFuw7uIxUx;uu|)WEG_Yec;xT5=P-RbeQ1!ZSjE=yzClF z2KHLxi|fypEHf{oCpv_w1MJi7kI>hO0m6gW9*fCDk?tLTFk?$_3K;1FxpssHM@bk6C)*^B5v^>{;ll zUpVFO=t_a?o3}HG=;xe*S(}358(rS*i3J7~@nhNKh_Sk(0^Ny^%E$OP*>nkAuNny; z>4sn!9#`#)z{X2SB9f=No{gp~hp!!QMCY+cGNH5*FA((`yM^K#qf%yEXc_d?S5o_E z3hY#J8pawOoesHzIq;>$820+_T2o<#cT%oM><@;06Z0PCpi^F@h5jn0w%cD1<42!o zhgiY+T)=`LUCergd-Y)>7spWZHlXP`aott0c>oeGBcmrex2DU`I=C{GIXTt$eUp0! ze0&c-&rik^KeqB%!z2 zydJ{VhI6VC=OMPzGC*leTsj+L*D$$?PPX;dzD-Q`bY zCz9Y=36=*-!qaHX=$til9$e)1RX>J)@`^J((VrsaK010&qh0cAaATRD|JD6sM9Ap+ z0v#IzS^8uAzg>LD=*oyj^ooxd$jdJys|7g12YRMol{Zmn+7y%Y<0Cm6ltcYm9< z5qSPw7wxOPrDj^}5}ZS08%4!ouH);a!bIOc;#6YLR-hnS@7NV(8X`6giQCC{OYua_ zU~csVM|$cj8$~Nyd4`RPwEFkP2YyC8iKf2x=cc3w+H?t?HtJ?}J^9Vw zajDo>jX&MPj>9yOM{Kf4UE4l3>6YD#Ji-y7Vd#az?0UNQ7NjL5*vzMaQFlwe{2xkJ zxi4_)kyaz!C~c;-SY`1@OoLav7J=Zt5!6MX9q3Qgj&Epf<J#!@j{ zr^gzU)Fo5VD)(Np z%sZQqPLy9y=LJqggM9tALED^$>U^5vMd&)|AaHxhW>R~C%^B`T_dW9^DMwSJ%)UXK z-BmHoe=`C3!d6I?7swFp|cZmq3TDEZ~z#)U*hF3_xl zo-*DgX>##9sgw6r=O}^Ya*3&ocwF>i&|C}x^jD#z8(2(Gm;?F}-T>onfVdQDCD(yM zJc`u?``X8$-@)`&tjZ0AC;Q6tOzEtVTDipth=!Ss@%&s-K8BdQi~} z$*Nf2V|p~16L0(k*h+X}R&A0R;{ghF0%_lU{VPNx)^t$2*i-LMUC4PWf$xe4MKK=7 z$BnI{lvLsQQMp5I{>#prOI%i)6lpm-Y{fBaki-9D0X)m0F&CRFKkJ@dI)h2^?v<@D znP(|`mY&D*fv=PJ)e7P;B8%>|c|C}tJZH;#u$)hNE>}SHi@NWyjLF^tN5s^3NnX7^ zTa`t}Q{K7L?|wG@hL0DnXxP55_r0{a=bqU;jDj{Q1;`A)b*AJ<&gXr~W+!#`#ypNr z*F$)dsWOk&=3!^r>MO=^KZ&R&%pxjW%coNj+apkV#TU4Ix?pK+%-=>D(+v5ujq6Vz zvp+LB9LyRX*7mbmBPAhP*aYhlRUhbS!p}zp={X6>oN?|A`yGWvrbpUw)Hqg=?UO~|FfB1A z&NhSl&bzw$bVtvzC0o4r=i7m7PB_W>=}jS47uuwaXMLI*x5qmG`~pqa&4>lr3wJj~ zyIwJZcwXS*>_hnfn2UG#z4ENvhXwDPV~HCkv`49Fhmz+6^@VCSk4>MpBjZ?Wh`4m~ z1G&>v1L0G4FiF^FgFeDvMw@_tC>RF)YhlsGcpew+E{ae3zyG1YLkz+!%*-Bn{&4DE z3Y)FBy1WV119(h;q863N`sb(i7FAq%oEe+Yv+sttUs2ES-CLSIwiqS(3!wag?Q)vV z1?j05^nKo>=~u6b8`uAo|BJ@)j}h$?kvY2JYuJuU%gXYVY%y@^^J=A`k?3C*!=rm) zs{ArL+hsJG&mGBPHq#9!t3AO@6h;n&Zz~jCKkTiSMQz7K-^DQ7i~NeHa%(?FbljO; zKYV9!Aa!&RESVfS;xhG%Y!y~)785qLvXO6i%qfaS zqWip9C?u#MSvOx}EsScvh+>heH|+Cy>HQxX8mYMg^4LX8#2`#D{!){ZE;rYDgZx6s z9rvx{{8eh>m5iM>g)4HuQR1UB;hpE3Yfy^Zp-zhoabuLwDh7jrjotk1sP&jBcC$ zHXiPT(iPS_{$=lJ{D1@bXLeQ7Zl)QqRxWPVDr`SX>xf>|96 z%biHutnmDk?EJK>%<4}GblY`O?>8!9yjwN~C0)}PVXmVSb!sA4*!X$?8J)YCYuEXzGQR z?61(MkNp;5F3i-jk+X8en%X7Hg6g*&my0{=A+Gn!y0s4Fd5R5+r?|72>%I#Pe$7~8 z@#m$>Vlc0=3OLjo;(9+!si{Yhy3DmUSsBAcBaE4Nlh2IGKJ0Q}_bqrgo3%+?k>l#; z*R#_f)+zp`TPlqG3M)gmrw+bX`D9r2;%m1-Se~RWqo0-dpO-#YaI5%JZR78)k=HWo zCvuX?)r;2_g)hJUvDadENnCwsBz;=6$MxIcivR97 zqkW$2?H?R+_5x+Nyizdu^v4ZDf<*E{W>imh!>C%%Lq{;s#~rCSMRzGahYs%a6e_Nv z8M8zL64AE{-%*v*>teBEaPhV#Z71%#`AA-cAK$y9x!L^;NlkhIA4LlyloIE}@AzwK zyKMo}jjkn1TCm7c`V}H(eZ%e!a={%yYeN5cX@OLU1sgH#Bzt5Vo7$a8OG&r z2W=h^HAyHx{y`kth|EXd^)c0>6Hu8hTkvhr7f6lx+^=D2yy1LA!)i!yDS981cskt6 zwmR?XR<)DDn?n8YmSPNTiS|0*n{98ppL@+n`qSs{DevvGo%Xm4QO>s!eqZq4R-9+X zbXQ^FZa`JO|M^C{(A}<`V(;xhE6Y|f?`)#*yDsR2=0u0k)1CL>?AZH)yJL4&yq@~t zRrDtLr}~U)*F~br>MunLCnPLdKfls_&b}>;4`)lRY>P!x{6Krh?mRV?0>0}TXh<(B${6&2%$5mSf@9kBynHoD^M~e&UD>OQiJ*#3GfmIFEzesmu zdSmjJ2OF3zG88K%!LsT%5--66kAj1b0omnXGCHYoBYjmNUG6y>F06albWKM^3YzAM zLOA_T!#?f#M=n1Kc3zj3Zt#(I?1yi%Edu%fP)^8Q@4C24b|N3hVdYGvLodl?_FrtX z+KF!c^62Y9^ayo+glGKLu?4>^ zvyf3glsq-BRP&^~BK-3NF#g+88Dh)){I`1&VM{SAxWU*jyz=Es&R-@TEy>*n)+Q=}>w4j6hk6Tb3dlPf8OM)5yd7paA_**}u%{1BF0#La$^j*VR-lM-H< zAQ3}ju6h!e8b3Y?dWBqZoX=SPsB;rpws-OG2=$I7ame=*EHD_y0545{3eICGzW(}K ziM#52b_(2d>LOBuN3-nB8nhiAB?zW%*7kr*Vnxlors=s&wmm!%#a>l^E_C%gDk2IG zcrG4BT5JHA;#hRllgsQeopgu&og9+(`-NS(xg<9uTjZJoy7)f-Dop??;+%7*MRv!p zMy@-vkg{)X>4;(_MjjYZ|1I5#eD2tD$q^k0xgd$^Q~;yuu64Xg8T#;-=UbYjml3%A zuC#PN(W%^V6UEywyEy&*yTsTSk6UcbST8%^cG)J~!0%ZN_!TXeWbO?;+tA$1cLMcQ z)da~-_Ol9Q2N68Ys=ax09%h(`lP#|ih3#q-D_?k?nzxZ(ycmA+`Xu@MTO0H6w(lv}WphpkSk2R%y@a+}w%=Dj=ra|FO z9KI?qO4^(~4$j1-H{mqQ^6LL3S1!gju(NqQ#7#-NWtwkPMn+@kHQZd5U5{ckwG%w_ z{Q;b3JbT&@_I{_~A4)faQwk33oe57t!I}R*6io;3j&BK0ij2{F-`yc8f~PXSn(@Cm zO6R=zswtn_f$^E0dNEH=LZiS_dXLhlie}B)Bd89y-2iLo1>Hx?t_u$_Qg4dnq|zU! zl39PgIU%{9rpAj_0bO2%bf}o0CbNP=5NR0BKNK5P5iUESF9!~K=Qk?`;uX!+V&Ja# zvNvD1$ZR)Q4Hy2ty8TPbJX`#|5W~I0x%9l=YW@yy?}f(*x=BFZwqu!fvmu*lLIV@{ zv+jO5{z~nkH@F8TV<|{n?^vUf5Zuor%GALH`oqQd_r{iU6Br^>o(j3A5zQYn9zXr?utt7`pgFS}tHP z;>eod$#{kfkk?y?A|f_(1)1AAx@yw0c|ZOlGm=>Vx5~CkR@ac8I!@uT!@0pHAkL^= zr9S%Art?Zq*bvCWkD1ZBVYcMgqE*q{TWYU&W6(68ZBJfQKvV+`a95 z$kg?1+}?_bcy%*t>AmP`GEVu+wU}Q?MnL3h!&V;CuV4Vv-`*L;^205&)prsqngQ2C z!ZWI_cH6PFe1dAl#V-C<+2Fl-%6TI(n?7AHQ>X2@k5R*(w-JO*~_p*_8r)rEdvt)(%1opc+d;mAL6X zuE-s5WJH{OFm}$_Hcs?#Z5r$#-`2HXE76m@kkjx}GI~qHYyjEFM&Zn9U*>WYk_&V& z>JLOh)@y;+zW-3hvH$cg1g0e8x|PoXRcavO{6^;WJ=aQWI> zl@Qxl*oxEN*lX!CLxH-dSLsR)NY>RQ%=Zi2yRzt~doHvkB!dm_!b*^pT_+n^Cq6dw zePq9<`0Is)$=AtPp_w0G>|w~arFoTzMn`-BWOiG9D6cB0=2 zb|L%sOU})ZA^RVS>}#RxpAVTs&+Q8&Kb>{+u0Si|#1hgc(+h|LdWDy-7#FD_`Lq@h z#LAH8ol9vAw8sLk>u6rqy57BnFO2ITqLLT#@U~z3?QBOl8p&y$_T4<^GBa<_9+T_e zMKPDFbl|;OKY()SC^^NnH!6pTS=}sb{Y%+DluM5% zq+2E7s&WkJJr>1nvSH0QNg8L>Eh&ZOY|qkiPTUCbwH#u9e0lYR?Kt^^@L!6w*Hwmi z4r_VKx1$#^yShXaixB>dQyUVunc7?)h+>Q~Q-(5AW&0t}{HyMk`PdRIVsi;b8h`TDOn2|f0oOrC$ zFEBlF#WT=0ppub>;GlO;_BKC0zVu!z^`9i8 zD}UyS+ZB^dF?k=Zdn@s9Y3G1QF9T@zD^8YJ3ah`qH>46UrOJc8ToLJu@=xrrlX70ch-_HhY%Lo>p(GxYhWuWSgV@DB(- zxz-lO9|CKujx?}_G3T{dN!1QADJ|1Y=_W#FrST;QxOvWg?YCAA2C(qvgf9lp&SZ7^jU^RI9&##^FcmXpC}1m${*k6P)UTgRc>tUmRR?1bMvNXV=e$bWNV+9C zWOf=EQu@s%O8d!LXfBS&8c1WzOqoKRp6){dML+CIfmEJ45$WW}!kkH1Z&4F87%d>a z{8n)JnjbMn-_TNXbBF(&Rpq2-{f%|JwgIsfTCe9+Jq>pTg?3mzP;0Ug2FY1{X(4$X z_SH>mInwo`TsMy#>8RkkBaH8C=74YEF^5ajjS&-*U2!;y<=1jljylOihO)#cQwH;1 zOzt`#o6ERW+9ovaI5}>fGKMHh)LOo@Y!OtK;a>qCM;HD*kPZ;k$;$(8mry1{iAX35 zB0qIeQ{zzKV_y$t+E;(`u2hXGjs`Nq+Q@!iVeo%d%TV5qdU_Ef(r;~92r;4}2ryzX z6lQg#Y}?Lo=TyVbCt>~CPg3rJlL`NN)`~3)W?3gHOc|=o{RU!TotZ{(hU<`s5oN{y zaK?!%iCZ4)T!TLrX98UZFor^gvdC)EfsMV(k85C~m+GuFVI%)g5arsV8Gj>Tf2NhT z8RjL%}d(D883%z*1Q^w|z9+c2rYR8X*&mYd5HOgdWqHod9!4+O- z9c--@h;1K}DiJ4xZbZy4&WC@HGqY`qWke#ls@u#>G#JT3nYHYS9knaWXo)q8b2S|S zy>?YdN0rq{H%SS%Q|3&WNK~goPRDdW1z5rRfe!;IoqlkFFQ_$azb}Zf%@^BAa1MCx z6~eRa&pJGH(u}3E{x&7<9_|GQj#I`QXvB$Emf9}t6n&DaV=Adja_rzwDq{+TCaOjM zz%Je355aO$Yn*c{r(A!F@Wy6#I~mw1z2~!XT5w7~e7&otoRY3G)J{hH<$xejTa_{5 zBBtO{0Mjur+-xEghZ?t#yC}&z7ZnCHw*>kZGmtDdvqA!?Cp^?MV#MSu1Nk*6?5&jc zca~#gh>6{ySDG22$Xf&+V}m=r?ui{-R$hab_kk=<6*%mfW%!MvIP;joEJ_)>{G#(r zIi`c(NI=3CWHJL%3hOvaFOzL!!lMSQR4~6`9V8GJI2b9T1AtX>jLUHYWCLh~Xlv?P zm9ne0Y;oC4-A)ho%GOZ@Qt2d5kp>aR1P4v`lv|jT`mfB8&M(|FM@499#iBT_CU7SB z5NhT0UFuK1i+Ae02EYYuV+5^6J$-0wEB^9TwJ$EG1s}bvuM&=#OtdPGrHMTMu(+21 zt+JiEG>~s1&)XcSW;c)(kCcS~4VrP9ccThDWGdj0nD|-V*VeIC-T`zV`QA6_Y5ksz z;c$^}yULUUbg#1PHH1w-zazp*@ty6I!s4UE8^6W8`t+P)jFX&vFI5^0gEQ%JUd5#t z2g~D|h0_mbF=p(jk$yecROsSub}LgMDkx0QdS8Rd0=|-4#f@tqitZza>@)TuO`J+T z$dfTz6+Wg=>&8HWi*_-Kie(M0ev`z%hFNF$bWt&5YwN>afT1{5P*=NWywAySJ1L$JcBw^{`n+U-#An5|U zd8?3OQxeh1WO2d&m{h(g-`!D`(aI~7JVtIEA!@Ib%XE>9cU+c?i(!gY2EG~mI-mn; zPa!1^-yE}7d{0VaX&1vR0Zee$l7Qi$S1D=qvv6ala^QOjQA^~6nR7RWPDWhdZ@xLu zkwEirWBO#%7B51OE*;r2axH;l!i@?4?q9$f1ynfA@V9!NW>}^iuYUja(g6^~0N;ha zdQ5}w_Zz<7TbRSsVdh62yAJ2LK(@$J4~%@-HQ^AZdZBOmQT8RPoGzupRMgMq2nDDy zr+S*e$cX!T+4f9JVW!Z~(2-k&(T)hZ`*&p!Is4Ogc4_O)%;l0uGxBH!i!GP0O96l)v0d$r%oTK=iW>cW(`SkYIV{J z84N;GoK;qK<-?mtKd6A=qg~=GD`xM$YubvQHnZBu1u?}!1P2lhpYUJWLwy@lR0gZL zI1zd3`I$gb2$i`8PII_6`gg2U5ZgZ3S(`yndRm-1*f<>7%nD+_ihzuK;=(p!{yZzK zMGA81mm-hZms32I|Ap-cxYBUR@RoWN!9W@-_z*#0#tP@pyP~sx4OrT{f{AG51)Ta8 zDE84U%wX+K$q;a9Gvv#0>VQ zb($|PezRL|f3OaFdl?wssRqNlV_9cZ+A*XOKx-cuTT@F{PiESPE03CRE{~s8@@2<^ zD|^s>vtEjD`S}a2u7*!c;wjEGQ`ly54QUWXmM)f_VR5BtNx}i~7V(|Li^@&HHxtgr90J5Xt^1nt zsYDhvJ8`+Ngdn0T(|5(}1ed9$!z#&;0YaKHjd8&QjX#lA9$J_u&D$Zg{qQ6F^=tVk zD-#?QOPTanCrml$Oi=9i5v^14Ygn!r_lz=LyoaBR%)R-*0LFMZzORcW_D~OQR(MPj zlE+OXM76@dC?P|VB0IS^Ta-zGlrB5{5cRe=d+Suk1Wfmw=@xiz-t1?5+t7aYpJA9+ z;@dgu*ev3Phm_f}%mQQcB&IcNGH{Z&zydg193PJ*0+`aTo~Ink&B~N9$}*~)S;;Er zziZvkV3|h}jh;xZjx)Q@{hWlCoJV=pQN{UpWD9fXj_1cFUTIS-i6R8fQa$oP*8qNz zxoeFU#PJdf)98`Jy{~e>?(Ge5bSmB<3|2vHqk2EI|toYyXGB z`keTfH2DSivi&>`{yXsw^ep#CeAyFL7L{#pC0+B}|4bT|d3(fS69!TXLLdCtP7?OM z+G(3BTZ%LQE-hzh2_xuRqPnAYRgH;PdLYbvz(8kq5mK?Hh!S&!F0VjEW_NtWw$&vv z6PdqeE!pD1#b`2w)ud;$D6y5I1n+6i)tI-)`P@CkC`&L~XLs4+Njz*x#%f6ghDks; zBj0E}yEF46!o04PLBVVs2JilWWMIH?s%9NLRIjD`IFAJMv$#~Wow+uf0=0O@Ad)o| z=GN2*rdn@ctf?x$U|Yi5gD4jq9BB*9ALO!fM=YK$uSVI8GMc8a<$0AquB~10Kmdnv zJ5j~Bz~x=}RL)wugdL?kkA5z-cp%Y0RMx93=6DIBf#}5rAiaE@gs}AzE$%WRh*yF| zM$Xb!&f0^;GR~6n{l-g{E%cuW)V!1zU>lq_H0b8KwaH^WKtDN%z&zP3`WaCnU|Wfs z`&F1!<+y+VI$vQYydg(mTd-_G)%t|;BYHye1`jZ=Kv_cNs5_Edp}%irJko^N+EGej z&(P{45-}*obdTv!K=tL&y?gtKbyHPhr0gP=d@#dSen1yqsnLV;6yL#OU%I?O-^mg) zN)z5muIvSd|4wrDL|5v9ey|->r(r$VAowcrX02^GozdEA5XLD18CB9yuO<2xwj&!6 zo3?`cwVFhJ>^`w9Em~H0R?c>wbo^7sqBC><%UBBz^bDbiZ37~}wMu$#R+_faeHjtm zz>#KV&PoUo=Mv`oLW)ce?!?_A<^cL3A`=QsxX%B>(YePn`M-a>5F5r04s*8I<}{}{ z=4=}_XHroVHgXP0M29hB7&hl)hKf=-C6(lSPIIV;GEu2ilB80fpYQLV`>*@HACLDR z_x--E*ZXxnU#*((&QNyl0Iuosd?x+2YDlL=fu^ckws`d5+SCC!jQCAasaxSsF^qCw z4zEyqHD(@Ji+7cL$pNWl0g>nL*T5& zOuDk>Upu7k^-SZ)t61Xoxy`{+Kg$A6I7k$@3nJb}ox-@)^usa;IJ7pJPx^%!SnR-# z_yrRDSwH%fu~%Ah1J#24Ozxm~6dCsfd%Z%P@5mDoaypSqhqSiT=&a}d%>K?d`aeXf zY6+2Ut`Y&H6gd&L*vD!p6WT*Q#+vuq^@27?m>61H4s{APdoM-?5yY?mlo6tPV2Vb$ z-#_}wAPT8@6}ZDj-8rBZP)V<;9~#M@4N#{bRL<;0i&EYAwK@eDkv{4s3>6u{ZRr-~ zr^R7&PS&jk3Ti2zj6FawwO%=5`#VRy6-`)B+Z1;3V53n^#zI$DJ1$5c)G<6s++aB8 z_IV7Z?eCO71U=OfFe&UZl(JFd*&4&z_{KemfiuCcKmb?EyqIKIw`wjWv!Je$w{J~9J99(VL0!cqt{~Lo1S#^2gAVgg z|JVRzuH?5=ZF#g%MXbv}QJ+1BHczFa&E-QIZVT~q53mvT>tO(`H=VxV0ix^)rNPXc3b8Ub;afd z`18;Zbw8)$@~TTpLaT%pbHv&UwwGc*A+DOy8m;OHCVFSm=N33F`O!q%7f=JNtFmCN zO$-GduA4#r02IaCw95Q;I5J`}?xC`1BmA;uV?i%;WtG514-F3eD+Hc*$Um{xF>m5^ zq~N})tL*9#+=+~H_GuH*3zT*FSOKR1Gzul7`V5R&9hEXj1pCG!jrb1u-`G>53=R0u z&Sd_MpIobk(@4;pL<>K;7QL$|bpJ@vQz)yqh3Z(MKG1o1DAXx3dfofAeJX&fcu1aW zD5!rB>IX6A4%F4$H9#g}O6*Z!We7u)BG@l$IKgr7q>nrw+&Ae>?K5q;WtH1aLN|fG z_nsBBxx6}eD?uv>LmZ=wJ{98T^T``@EZi^h8ZMFJiM+cdUUSc|Z{oLvK?e7t9l5^U zU!l*x^^)3YM;fbf>^wLg&Mu~*A##A!ukv!H+wXGUuDR@_p` z3!M!aa;J=t6OG)5t`9ykE;qKVP*qf|8nIiSVtt{j91cG+ny}-8S#!p@+P2zn`w)7A z2>yVf2Qm&+cY7DZ8%TW_hckrCTpiLF4r5qg+m4Po+7~1mb4*$;W}Fo_WxY(?4_yjw%I@FYP~n4dfG??^|TLYyP{8NX97=Hn;>dOsRA9z2!dsVJ?r8d_UasGA%~s}_DdW#dF;a?~Se zQu6#=5rRss@RKB*R!ORP1i+aS=9X?>CYlA_(hGKH%g_V$(m{99f=9pRY&7Pa_Oq0< zNIaeh?`PCr?`uc}<&8;<`R1oNt33#8^(bT-K)jWHDV#$69n{U8h{rTltMMbHHW5Y} zcQjgJE~j4I*a-0DhcKa>{ipyBUk)G_wt+E61<9Kn5AQ5c3wqOOx}=7!6~94&rXNE8b13#U6)az z$u-~M(_d0|+kCXyvC|`i{gH<^g%rq*mk94q;w_bl!yK@dN6n>Gtq_lc=Y!A#*^Vv2 zIl&Y|-k0atBSFU=<-FcFJ*rpuL?T>Hd)<=_r5>rzdK>f0-2U?LV_s>Fm8pG@L%p@f zL&RWN$v|u08RaJqzOQod$~RF<>yeXY8cYSfnT!>6b_(k!M1#bolGtn+9R&?E%o5}% z#IVmiq#j6i%}z(g(qbXNAia<41=RjfZ`Dqz4fPZ?cEH%&TD0fN{tX|jmt{_sm`t9c zLxzzSabv1I!{lOc=DYOWO!O*KULnr?B*#_!G?5zP8cOTg9P-fQSjh2yD>Xs4wLE{~ z`=Sax4BfEn5ubuo{md&O=shLocm*)<<&kJ$O-b9j)!aS&N1-M5GsAH|$){pSg^aYe zxWJ0cEvg&T$yYQ<)!QReD95)+-lZBxt zIIGH;K1`a{FAuV{JL+*Swv0V-$Xr?`31l=-z*eVg!)RV(k!0YacnVp3pdWcS*AmzQ zY>`B*ouqjh4(M8Lgtq`obLku2GGW)|cFa>Rla=%jQ9)wt4Hh#qaT!=hy_6(M0G=55 zRNd*61$CE)GfS1}jVd8Tswvf)&Z)JM6n|I=VA@mauQ{;i?$Vl0sdW}r+y+#@8Z+-r zZ=MpZ%yO~|E>mk$`|UB63%N@sYk7QwtzOog*6YCe1kil(hDF*7`lUP$l9~Mjk2#;$5 z{erdi-29?`3;36z{V7H6rBC~5^xT?)Yn-t}9vi6)NCZ*;{<63r zk*Nck(#)*yv}e26;a$RvjQvapI3^hoZHJsY;_YDb= z{@cf;zg1481cl^?rn_WG@*Y?Mj~QZyW_qQO!o~5<+(`Vk(I=+HHZGEwJ4|aE1tagH zHI^N2I0LVzeJ%A2*;4&#cXebj^CbSa@-O<8G75>>KqA;p8}yHAw9Y-ARqVGv$<6H6 z0VLB6?Msyd+_F=%MM|3F2Ub;>5ENH;LP-4Qm$J z0{d&f^N-xg1iuzyl}-U+G3KGP?85jmF>=RoeO!i9flhHA&~y(haGt-RxvZeg9X~Tn z%m2k5cok9P&Hi$$Vx&XTakEj8*Xz0elZ z&R1{*vv)pJk$RH7U+TO<=m^j24A-)-U*=gZ+X1#tCOexGP}_F3V9MhmEHTm*hc1V9hoz&eRC4s^ z>N6E3=U%a7VvwHpB1ngc)##zs_#G2h_7M|Ayl(m-$^e-naE1ul!8)}XxrmR9%=E++ zwTS~*Vzl;R&l0Orf6fMaj`x?1f9}dprKTtiY#vP|;}%C?VQrD-Wrnq|pcG1f7hub> z+;9kHcJh6QTCc!X(RX|nr}by`je6+U482}I3`25-0A!9G7gW=;_%?qvS}QYj8`iUT0^5MOll@y^iX(yy zAs)<;7jaWP@_YH1CKqCoOr*X`HU*_a{xbJ&eNG*=6qdnM6y#sCNb z3IxI)2fk&B9WX?2R0j}kW^&iafBw0c8GcqMVU>(=vgodWFhhCmHALLddFY?akYXG; zG$iYqBNcJ8SEu0+PP_HEeKm`$I8dIkQ}rdT0x^1zmwA~q znxJWNK)%xpX;(i2NmXNR*7wUTHiVXCX;LOb;J0?O@k$WJY7(?#b!-&f-%gzrx`%>X zB-YnT)s2MSU?0xBCv~4+Xh}}h}KW4Vio*14ljj_ggT6X=hH1gPFnoPF~HCtV}l>OO^TZG6LFX8LuT$nLeDZx z{;lSYW*8HUZoA_U^5|@LEk;x5Z6j99El!q6=w5zrkMV8G20E2jMFLe7c!B2{oGZm-k-^NKFR`1Hsx<_9D;~hRA&^3{VC-dV7}y!1-oK3uA)!-8>HJQk$SdAn2awW55ppcuH z;R~_!PmGHbOkWObgL6|zF9>!1nx_3ooALptf8-`wdr|^nt&~CB@NQW|dCI~~5KJs% zU>W1oJ;!73(^fDY>Lg}whVR_aJiTdEm|ZmXa!(m++rg}3v>B)ib{5-a8dxx96ww9R z1(~%E`{_Q3y(=&gL(`ITFe59jo}&d!=ERI@=6@S~wGo}?R)WsX<*nfsUbe~?t$w^K z7}?`>>VZr>s!B=JB`D%crWclUIT`vB1k3U|i@v)?3XN+VW{*haH?eNTh5oV3+a zPWRRU%(bBdtxefYV%+x0`vD0smnw;9eP_7OaIA~*ycRWD5ytB#J{1w#?5jOcYnjiX zUDeGI>7}fFO^aEJ9_nn`;Ly;|fJmdKHcm$^AG|Fd%e0E&;|$f}5JPiwUnzduCuZzx zUKw`H+tAbu_}Ku& z64on&PP%m^Fj+(GYtJhPzD#vmCd&7*8tLJ6%XW(uu~q7V7kHE;oT40P82){{Wv04jhEqF6O|W=PjvBan$Gr->phV@BQ7D zAusP|u6w4Kq#y3<74X+4lUX6dmmi>friZRvqDantAZxGV>v}MbOd$KWmiD>y@NT?>SuxdX|8wH2x^m^4Qs;E=WaV$kI+DB%)9nc7#-vB^29KEeFQ>w^ohg!=N6i3)} zz>k!3w9cuB5k}tSo;LQovD$c+&mxObnBBbiTy$7dp=6 zB;gNYwKy|Qs~c{o7N6flq4WxfD!BfE9dzui+8R@FpMnf*`P^q;o7+e-fHoA!0&RQT zR#s16?$jE{^gg||q_7MklI0`#_oN8$BhPLS{Ugz1afkn1@6h>| zOEZJcVb`ZO@N(m6y`sg|;*EINqG)^rBdq;uWCbfGzYC61pEv9WSNkC&@$ZqpTAFux z&GWRAf?*y<5T<%Sxu<-0bQ?ZqH&2u2G>AtT-lIWX+~gYQP8vj+N#8?zL@*il>TY(9 z9QS=*b3c9-j2U3f?1>dp<~ZdpC+%h!t2Xx>0NeRo@_YIP^8}JWiIAe;OY;3j;lKSxXkIN5c1-;;6gb?{ZGxBrt>nJV zy8ZQE%GJ4k)YV*mdPVtZu@{?K%K>LP${o7B=n>~C23V~j z*ZJWCQj>#^%G|WXk@o&jtkr=`E?>8>rxiIM(TGe+ITG;2Mp)pQ#`%fPDa($TIb3K) zP`M_5WVO^;?QdCL%`Ij>tIFByc!2L#ogj}}d(Kc`1L0+NCk^yVj<}*mE1_zpLQ;r0282sjj4Q6ZNRm#iyVPZ={o!fxIE7 zYdJB6(h>TEcf)zVU1Q0mt;WBlg$iPaJO2S!@K@!=l2NOdEKB9mA!@^E-toB7U8U>% zD^zBM{5#-$!COOup)gWZ0#&rBF*MMK46fBBKgp4LNP(%C|MD&KI1T*mVe?I*#&mTr zz^)bL&2%0u&u@XCq-?R@gU(|kUlz<21@LJHm3t$`m7Br{+|F^qv9!}6C+Hu2+wH4_ zYBINiOzeB5;`hucQBcd!`?av<>#KwaLTvDCaRD~lpvNpUEZ<5rm>KD%d@T)Qf0s{k zr&>rqOcFfU1)nP{RXr<(>UB_m0ghfvU%OxzU{%c;Z+h-H%^QnT|JJE!ZIHfme{2*in3c3D{f$I z?whD5D{u+1YI>nnV(-8U1NkH9^Tt9BB$?2<)m~$QYs~1|m)QnovX&@Yre13cKru`Q z+))X__Vx#(`%VAbCl9-sTs-K|lzAPs(#{NqB8PL7tmSu==W+5e=p85`1R$3vCS$5$ z2hWKuM@-Cp{?RvNHUWoe93k*#DyER=`=gdxbwTkdw$sr7&sO3!BeZA^wI)As(h687 zn53`S%)^WV-#EJAZxBG=DFP=y?I0$XJKlS-c3?kl)Zjv>xd1vICTH>h=f7CVN zti4-s_9U=~*n4@(W3i>7W%1>P2b01seZ~aa=08^@J|sgVPV((jkMxmrvPy*UK;NM_ zWGTU`*|Lk-uZ2-8O`QloL@0OWdqcy|BUyG!3NjZU7XhfAX?}{(OG@&X{3crby0azH zz6^&x)#|@an=zu|*J8fon!C7(f^v9cwU&T*TSD`cGZhH-meCe1 z0mU$?STgdSYG`bk!QcpwHLsFuKpdZMnb{_54j7DYSRP@PSY<&=Us}oLr#&_3kEONz z;%|$VrY5MaL61(AKzz;L5PwA`ea#9ly@EPGo$3{5Lo`*?rNkZvmso58vhfcv~>@h&0N1OHt7A>fP%yY^|{pyU|!4W&@J^oBEYoZ=d}ru{6znBOXo z{Y0o#T}0|2jmQQ$HMuYPF`CF$kCr|hQt--wo1ynr@EfR-#fW8%OKYR%%}c-1T~A1` zAReKO0J_2j;rpViS%ft zZyiN#MBt_BKEf7oB{Ql;e%o>!$5hcb7f0)O=UNhBhuC>mk~bkw;cBDbdu)=}wrr;$)<9o~gCe zwRfyup=!Q`fZ0Ar;5P6L^!zR6FiP3vG)0tDYS156dh7v-d zooj9*L%S?tZ)2it+9ox;vZo=4zBZWYMlT+m2QP8exw&<{COPB0d`(4gkQmjQqfSI% zex!}Pq6AU?2#nsc?0pu6O8R0DGT`1O`ADsgpG`#Ef=N*uV(Q@hTKRp0NYWa^1x6@%2PIeIsQtkOmuL7CRI)Ky#0mEA5nI#= z#xNzFci>3B`?hAEf1y}DO@h$#ToKXYp}hl-^C3!Kz?#;D05mb}=JLG}{ootd}AJ&qfWu(d0)-=(MIWjm^lD6TqD~Xi4#|`$MB|{UX3ICldkN;<%%|y5_b!@}4S4 z7Gy$9T)(N0s!{s=aDmKOR->G_QwHZC&N-;xAz9jhnc5GIxOwvDT<38_&Dzsy_`A;i zez(6Pb_`=)iLJA?vr3SOqJZt0yj7iXJLISv|0a&@6S#Q7YxGjj^LNXW_T9BQI!2hgfW84SgoB z$F(*y@W0j*=s$bcnwwW@3Iw689KYoGP$YuTM+oi^y{}6>{#2;LPiNP*S*0 zHT4QN@}3ajk14)2B+8Aa+a=WGvP(2LD9?=()GoB~u3$|29Y;fChfFk5ZG?AR*vAMf z2#@Fl!g&(|eu}&tSsP7Vvz$zw7$t#Xg(d91smUeW!;QAwTV(SdsInDe!W_8xUeq|? zO2X^*;{Wy`#g_y%%`fcn7wIP9<9R%u9j`V@WON$-xq!b(ID=XWIih~79v4_#EE4Nd z*iK&@qIcS^tJW&9J@n#CHf&N9tWgC7VQGQqSS7mTaWKP1us!c?GVa|YpijENY{M>ELgzoir)r)8&@im zyUX!P+^K{6adkjZTOjJypkj_?R9OB^L{r8Xr2%ntnV+8`U`r2mi__hC1|W~o z)Ok%~BW|h=GeoWya=oOd%MFzMrV!0OK=mF@Ri)v|29!Xq6*Pel`D?F*nn>H`p0mfm z7_$~gAFtURE^F?~5AN0UnQniQ70~JHg3UN`P4HNm!bypaP>R{wsLh6Z7~y`hGRfIw z11$=GXL@_%wd+;~;$7|V$3rH7Z|F7UsOX{5$6Sv2=Mj7H|MsnO68hMs;sy$YK#QQv zY2wH|Xdi4!r9T~A-5f1b{L?z|S|yeG zid*J22A{pDn(RPph-Tc>`I?FSgFm#P!7D;S;t3<~(c#Xe@VV?wLinDrEv<&wxYh4N zh|5Y3`NFI{lCh`RxmmW#tMaBZgc?QlQDt-23p@rqW?Bq7m0ki7LT)X%_frBBgZI@> z9S<%03jmajJioK8>f%b+vt7{OHjnqAbptK4A|Z+^y3q5oz$evy$Qt%td*M+L;K=JEC}K-NZX=+SO6rkP4Ch1f;xUMa(6w&DFUo5$x0*Y+gu zyS)WpQ(Wxl1xB+JL zQI+s>XHf__>n`qKrBCHij$UtFu;5{2{7}J~pAKlQnN<4C(H@Q6xJ#OPK!Lm?r?lzQ zU5CDP=R^zGb?o-0KYv{jIzxA z3kV zkBi{v=Z{nDO8SZ5`cHIn*wd0pI~@HtchRD!waC4I@(Y!b z=hFo4A05BMAJHu>t5DVt_6e>tBI<4+!!Z04PC88#0=WBH5#gxU2tUKexKE;1YX)*3p{Q(!^Q$?k)aQ|>ZCW1g9ayrMgr-7xOgnE*`2cpqH#1ujhnsfr zyWGDPh;A#9)X$K~SoM)9rmL^(=@Qf3V_ePH1|AS;ci>+gj^X}Af(HKSb5l>vag2vK z`^mz{Fe*uOGbn@4u7;0P8dbZ#)+!uoi^4s((| z8F5V*^8gjIB2DSIA9vyMoKJchgB`y2e>cYkTMM7r2TjPLo8xn1%5CUi%VW zWnhlxu;p~Ha(}ltA}JuXT6DJ5)y)K|0EiFBQr3bbH%4v*;i4b ziOC=_6ZKfsVYPRrKoFn;4X7R&hTB^Xsw=L%1!SBNc(|!=JXq@U0fT>9pr&$_Gn1?# zmS%qa@Am}gu1vfhhDdN0xV8)A#_7=G47ct3ltupJn#f9y8ZU`vjWiW(2c5&j5L3ir zu*EKYmA4N(uHh(r?}us~xdHVcqp$N>quBz#E8u70ZFGn9$>;7D8hC|eYF*jt;*)bN zet2jusu%}djXcVao;sK-VH)r5ryd@2kRw`7GifYWyd%MEtog7D6E5UEG#!UO14=k~ z_9cribg?#O4ca$;kndegV;Dt_A<*c;)u!irqZOczWl~JQAS=CKeMtDgbK;@Z!`WU( zVrF`A4fQSjHh|PR3j~YvSBiTRmY@~4o8Q!I0y*VG6WjlGJxA3YBh*_};Fe#Ki(`4N z({0%%!x+8vK4U8L6|0j@2@#ABK=?t(8wg*j`x@TKtmjLI`4k%{W-#?f7~I<4)r#vZ z;1^o3R?3cE=Db;ZDlo;H;^eJnb2~}dM-G-6pla9ro&x3;@1Q|rjAfSdbCA%`&~Heu zAk(l#oAN<4VG63F;AuI3P<;(*g0OL)n?jxp!_rBwqzzj=K9pJ^O+vUD$NX%#X4@vW z%03PTJ%UD7O>?ZKLQq!tB98oK9TwZkD>HpNz+uK{j14eDX}}X1=^yP)>M;xk^2Nop zlf9`2VNJ0xp=Wujg*(-KWJAi;`(^w`RmG&}JXX2JUOpvUEvOO_uoN>v4-G6PsRyk)fiv$?f=gfZLycGc z>n7X={wR|=<)tL=hlF9A$<{~rBztyUHmo+_mDpQ%!T93f7DG}6@87%3`;t`C(d7z^;+F?d+=c@mD4-J6(>NI*NhWwXV?CDG)t~E4HP5T8x&7?3 z3zNdF1$P<(*z;;SW#!{oB@xX+27_PHvk>Ih22(zyJj9TfDG^L9GqTNR@aU*ME!3S;v}!NF70Pw?Uh*dq zw}AKfiXl!Q%Zv$E{6gItSsE6-5;&~SsK>Olu1mWC$msN%tU}^~c5PacOLF@l_W}5M z)VfQ3sYl)!an>4ce-3fA-*s2wX{CWn{#7K>C~%P3n-tnQm@^UXAh2rs6ZEnmP}Oxw zoYr?vfbijM&N$ge;ZpunqvWZH2^zVX5n<|523u-9V#K8GDbdH$T#(A{839$tIP8X z8kmku>;`O@Zp;2fC+Mr&ak;rug+@lIStuun+NzWtv)8t&BsYVuDLWO!EqPxHCj|j3 zk>M_`j|ylSi8iAGlfuT+_>d!KgC?a=Y>j~q9};!}O6t25+n$;u>gwY3tmPDi>cQ+a z4Te{6kMc`gxBVVi0?Z^;0Mnw7@-7AB6cpbFcLJBGHqHbChzLM6IZ?&Vj56}QU-~Y( z<_}2Y#%UWG?|Uq_rM58qJGH4T}R3u26> z>L4oX1%_Okc;$veqz`s#;cw|?ZNI>o>we;yWc!sRQY zrS?!z1ofW~om7jUJ&-*cr0?Z{1qnXEQCWa|Qn`GLvC+X?MG1OGK(JbfFG|(_Rvk15 zFimbfjRa@0xGlwn_lg*rMkz8=drbn~Y2rrXi6v_H$ZrjUhWxR=VulJX>#pMLHZF%V zH(TSn9c@+~lVh1#&s}Hu+RYW9#Rp0!?Nim{EKsLHAnI#HMwwxbF3ulB^_86^n%GIk zlk2{B-Gw4@Vv=^8xD)p5`he`~aH1I8$Py$KL+2(cY@8y6Z)0}$wiQ^}yYBh{gB|rk zt>xR)kf*;`Dm#!BIMZ|01N?B!F2)$I+YlV?sh^-4Jq(i5qZV9xj&AW0C8M0;3TbKf z^e9uooov-~h_(FnyN>2OD#s)9uy0gGka~JV&6C4d)P>kcQsSX z>1@{Zb@_gIm6~VWqke_Iq$Vp4n`pjonYWZ>&At>r7{+o+l<-`eJSntGcsn;jscAHi z@G!=E$%lLpCkuCpmdQB00&S{UzzY3BYXf(dEfn(fa?=eQ@&sIWMF&m`IXD|_wHups zuA7qNrQZmBONq!-7>g}TRHc}jS*PWfvkE&gBZqUdbDiI6FRSN z&NA!q9vB*8ANOL1wMj7070r`RxYK(xy7!EjX}VCwTzm4{ag zNghP~{x@M#&l=%-dJ{v7$hc4eX3vK~Z#G8&hT~K6lmNKyENeO|f7+_4&~|A*On=_J zwJlZbLR7K!jxU2X1;s{Lv;*VM0s6*drz32kw#saC6` zq(Vr13OwszIG0D%Q`{rq0?U>^_ljKWYqfj4F_}Mh#i7RSpnWJI!ib)gBPScERS4)z zJ1Q_@K`MUB_VVaGxU}f{)_NdYK(gI*H*<=dr?MuMcBN3i9aE$O)GAr@?0C_fd$oj} z-m|%FMUEYW}_1B%NYY3|y2_nrsaa%2L6$_Jm1d_l_XmsZFyz43$xf)Jf zi_R21x*0lRm<>B?oB*$OD6lND=NRA!d!GJNwZ}cSP&~F($tOty4jhouj~zoE5VJ&{ z@GjRt1&;nqmuHZvuQL=(Q{_Xf1r8NlSaYL4AfA{=Ux*yFgHjG!rX<)y9R|6La3Uvgej zc+}Wk%_ig$S|z zj3EMw0Ei<1PXyZu5Wx|p@=z6!?g`;gH*w;w+A;mYUJdC^MSqT5BL`A%a?s(TQ{5AY z1F#4)*c&q7AVNx0I;3W_R3Qf_#xS{+5(ekx-v~3<`vnj+x6{EjbbFRB#EVPr(}rRO zY1-1{lBc3vYf%U-?ohiuXK%L`1|aVffj@=~2E>ZSe(xbrUhWg$LthK*6WqgJg9Cv8 zA+0PDqW_=Gk8@V9{@eGj;-B%}P5XZSx9{TJpMTB!g)V&k^XGN+mTHR~w7pu>tKTx> zR`;JTwZBhgm@lvB=B=?WyU2gM9w}krWNpIX}$T4=-%j5Q+-GB|6ZkI`t$Ff z!KNzf9KX?|*LKj=+jzq=*%6_9{`<}Ka;rS6`M0GXL)SX)5?|E}N)J$fM|B{AIGq~o zTif4tg0foAyt&_X{?o<3=VpFevuwrB@%^mLg+LJ_rFZFRvd%yOeXQtudr~S`w#z`hF04T>8~vA!_V&3&Zk&%(Qdf!3+2z}PyYS%YVcgva(l19 zh(EY*{PaW%P~;NmzRERpWLnj8n>yxQBfkx7v6tCHek$NbI3+y4tE=U#;1z8HIW_<0 zvVAiH^&*B}(#mFaHS5nku-mbVyn;zpsj!Ywf7a#vDLJK{)CpWj8KyUp;9u6HW0kw5 zx+k7SE}H&4T=+QYrEk-Qy+AWUI&J3X8NZX*FVf4OV+KRWQVvq(E)e_d{r~N&fxw(D zI=0rW(Ynq(EU9un<+un~sdsJ>GeEuZpSc#hQfB1YuR(B?3i56idUrDSn)S^}fvc6R zFiE97QVjbHS+S4!$yXQju9OKBx<~Q7-DYG%>b>Fm>lY-eY{}HcT`<9S`4W7^d*Q4o zCm-x#`IVo}`SoQ{W>U)Xk7HERmop=`d?kE9&KD#vEXCj^f5Cmr>I{ahSC(Fi$=rD~ z8Jm0{grj(A|NK;bp^Jj~na?x7%)fTOS)WW7Z2Tdb>SdLG)vA##JSDE7;d-Xrdz{>T zJ67@Et(1`d`M-cischRxl=VauWI_6G-I}aeZN}1Tm&hN9cOU4TbdLP^S~PrOMd);b z|0Utay_#8+!|dBd0>_1pzD-T6b5bpX+3fE>_MBst_@eiecKhw*vyPTV-Ou+$(NhKv zMZ7TbmNCHm&Qi*K)(%pcsatryTwLDROqcFMD=Xg!vMCM8etA)zqiN&6D|IDuxTFRk z^dYVJkNCZUq%PWC9K4>1_NTO@-xjINKir2Jk0MPZmG=h>ZC_$utp2ca*zO4V8Zu8D zmEDk~`+oIL@(xD{8&I&piiNkGIsB=5)2MB+z=Kyfe1QM4{~c?y1LB`8(gJ{}2W$|@ z`!77RHa}dcerGS;d0qDb8M&K1`$n5m>)!k%?=9X0u0Auv3$Pk)~zR^KT=PlEzYTq8*vU?-&C-qC|0yRiST+=v3cpzs}DbCWt6iS zK3E^S>S!g8Kbpro>-y0PVZ>^|Ae~i0$JGxFmmfGpJ~FV% zu3KVyav;*H#Fn$smD7uFqfbSCNT}P@-wb!eHhnIfXT2|J{GMARLrT5T2Y6(8JN3%- z{$94iv!QzlGBeem9Mx~mL~U65$7uK+I-Bog`|XfU5}AGBo}OR#_B`$Jn#eVBMB~Rt zuhW*{qDOtXWTxdkF=eRf9{62*2oj?Burh6Ynwx4Ov07x?@niHcjxhv1&aOB`|QOp$1WB0tMLRKE0ZhAnL9C z1K9NRnw5$1O?{d6L@&{k#F@ghkQ>5`rU`S$l?n^~#HsnfNy5;&mj)p zY7w)EK3i)OXVR-gzeKG5^gV3-X!aBQsb%KQ4Uszhgji}FMRAUWAibS@c<8rE&)MUZ zDS)A0{#{)sY>kiJtFu>*Pq@PF-Q-#ABAwn9qsI$Zm9G{RT^oM$%bIed1#3{DeNQdw zo$e2-OvjXscTMQyL^0vZqA?`@;KbaAn|$q|LTY>?p5TMMlrB6n0h9&8NF&MF+gaOBTG`xEzIa5v}ucLVO8 zY5$x@i|D_9rpon&;+#dL;%b@W|GIle0!zN-H+Y<3%z0Z2Xj|8b?Oy1NdbaO5Kw0jM ze=+U-&1rd9qe+!hFWUI!%060*YTpTM^A2;v(gJ9gEsWTh#3=Da&Rfr)M&K0Obye}89o{9ol!(Kat#z+L2f zNSSeAhVSrK^Jl^L{MFOH7PQmNGGngoA*z%p;COa8d6`1G8oyzX2^v8L42bsbjpbd1Be;IPnaYHE4#C$s6Bx1@`Vs^1TW-?zX(q=E6>7u`($&|t>eP%85PTR)RjW<8$XDVTWUQ%T`-lkQ9Bje z8p)$ZBjbm8_|+a|4w3xRZANaz+%Ut~Y)S4&lVagb1&V3qW7jj!=T`uizGvH*$*lM+ zp8Yh4{CxJo>cGMCCx)$ilXjoBxL~H;0r-6^hug@0pM+-`uf5*cm6*}@J^uFJK0HI^ zwS>rpXStrkK4VpIDM%=xhw$m@bcxC z7x#Bxtsh}MPHVlfwqrsA3FOdAoMl9@Q>QV zm_1V5zoUD?{Bx%ZOv&PlLwn8H!leiqk;d-lIaG0UW)Nlva8E*`^!lZ%GYRSsT+c3q z)L*&_N~OO2(f_#lZt&muyf;6OJZ&pmbQw>{0Nv}`z<%j_76`nr&@|7&3Vu+(^zC!U zX34ED_x#SC?FBz}{($a6T3&e}`^3Kw>_=fnbu63~dM$KK^{0Sycc&PK&iK(EwQ7(< zlstN4eBZfCm68Q-AAwfBb-Ywx@aX9N(xgKuXgtYI{gQmnq4VYON|Ddc7av+ZRu}6d zuzng%)P)6{_-|hiH#us>cB5!nZGF_!-FIoBs}zZC%UMC#pS}btU@e+$X1)d|jJcls zykchi>())94q(N2y=%uj{}SS1!op1vhjTAqo6K#699^Bd8>THVC30yVGMYFkVYn@} zTHE~Vw8sgdKrf2sBli|zxI^C(JpTPn-U*R7%a2?0i&qf1ww5kKz~kSDQ@bjEF6t?b zp)KUxm;cg?O2a(ge!>Cr=W`~$1;=Hq7;4m|4^?}F@n-*Xq*B%!Q;UzKEo z_UG(g>wBhJ5|i;pvb$6#A?D(F7iH7*d+FJME3T)-*mt%A4-R}>-@GPN;6Wp>G`vkuD~d0($$Y zAH;Gq{!C&StyuzCHCD&o5~89Q$AkaEWEQ~BkG4%82{cU$sonf(kzef_u)KmCS3SEu zEusA7)_iM5g8j5*v)<<9CmFlm;7UuSx{<`(;yxuS4*&69S)Z(O?=S8W;7{hs@T(T+ zvxN^FkG%S{Xa)1XKr5D!E1qNDwz{=?rt0n9ceC(+lv^ zku0_R7a`|mv-uMn56Ba>{;ag*m$n!{z8(av>VF|&UvC^QaPm*Qo=a>z5JPyFb%-|4 z&X;}{oa`0RZeFWu$@VC-f!vrzImj{xZ)46`!th_g)Vsjtve}*s$Za?s%dz<_lc5-q zLGpUwvd*tKZ#`|cAG`oxW2c?`ZzB;7u8$7{OKE%Ty!UQ^XB0AbVW0Bz1cw`6Em|Se z6YxYGM1Paj_m$ziZS9|jhJBn`%VbPjWSN_<5gEw}S$X)$>PAFvbq>Y$z))&-_2FvH<^N4m` z;WNpc`5?p%pJe5`$F>GPWyZ-qM6hG8!Mn%XW&MCdKlOmNEz3;wpE=oQmCDSVX>41B z@SVd_J>}55XYpXKXRa5hm|&mr#!P?-ivJ&Ym zmt+`at1=`T63|=3TPtS9CJE)5>{wc6KlJi$ye#mx%Rhm)hGwwCZLE9BAO_1}uXa%D zWfv~q!j4}*0yr*=vhk8n8PqWGnZ%Cxg9JOgZ2HAi?bJiIP3A)x+zApFii@)G79DV% z@w+k9@XyO;i_2}?6&Z&dkE!Qn&R!V7V`mN0aKs6>BfRA{xE`UGY|nAj=!nZ__&H`1 z{pSuAVeSJS^$s_QdX3ujztkBt)=lcbfPu9#$GEn>*oqJT}Z6G5F3I;V#)2g)0Zv0(N#%cW87leQk$>CSoox$+lY@VD7{U%WRW_ zp+2LB$m3UzAZ`tpsY2_!#^^@!-@tVcK@xRlaL;V8gQ-Cl%sM6|;&^D{~=v-!c>RBFog z80%<4gO=-6TJ!0bw>-{kuK0OJ@c?z()$uva2QaF5yb=`7?(I(hh&OYJy(m+umC? zcpW@tl32jUc3Eak;z7Xm2XaGvnZSqdF7f4$)$#TV;yi_%C_}RB&L7U#ZC_hwa#m$|@Gi;By+XNaHnxFToT9reNFE*+!`w2@)pIFDjm+%#~U-#d}0DWkq={!mFJ0jXKcOvvGNz#`FdTx zkC6APA%l3&#&hoglYnxYCj(#1^=}>7_*?y?=%UE*mJ_Tk00@N7{dSrB;rzHX-!Y&` zs2I#H#QU3iE?W^2FD+{A;;rE4>i5pRK8xwl5vp8U7uK@+pALa(#tHU0Ar@G(AhU;t&V5@8+VMM@b<3e*We%JijhS|ncm;&^xP1g?P?FWMBrJoy zSrIS?oFC{UBzTuk2B!OxEV>qzZqbV*l63=vsl}38bz&KX=2<&z_T-e2O`H#PhgVT~ zY_aNl)WXLCA**DZW=SQY)w68m>aTr~?SPH8SvqzLQ{EQY!rv`|%OJXP42GRU6GWUc z-a8)NEQQ8pIpG1n+j&>dY+fNFW@L7bF8Dq9Lfh4=lGxb&SkG3G8~Y*CsY9#!S%&7{ zKkDdSxZq^4i0o$7j7dGG5^>U9vN#A&x$=F>yaxr+81_w)>BB9Z!3Bk!WH)ICQQAs7 z!^@+9nZg&rni^6D`EA?~A=4&iol7pH$UaZ-q|s((b!7Q}iw4~ekL(T4z&E6?#HNT^ z?({G7KmKKP-2V4CgQ5-UafS9cC1=a{!!c~J zm&A)x*d($R852DD5&c7E+aswh-NwPJ7kSqBP&^=(IAX>AR=+JiLHvO71ZBKq`A44- zlc(^#g(b02BE= zD(4V#;>%hYon=eoO zd*p-chwT1DFVm6)e$k&HKI0E?Ag15xZ-(;^Wc|I`@Y`*++k6mxzt#-@0775Gg1@t` z*>Bb{XBOSy#=-vIO87D9y`Azr-{IRy53D)6P{l1ewfo5XY@>lj3^(HNk_euP-{GUW#p37e~183V|B0|XisWa^NJPt7Nlj0q_ z{o17XEQR&swh#72sz^f1>=sG3OgWrq7+Debfs`|s?ukno>qry(KZ8T;AK5>X{R#Xn zKX3Gv{k{IrKkA9~Exsd6k7TraA^pGJ_zzgU6UA8z^27H0A7|9rWt}bNSM-PMYGz?6B8GSYx|F_^q}M zZ*wfHXITVIB|o&g!zpk-WsRBePdw&$`U@n*RM?P$3csyHt5(_NbGJ2%Nh_YM% z0J&)OKkEk%hIl?7_kRO1#lDemIc{H8$ChEyIFEmCdi=AGi^KRm*=6dTApZbs`y}2o zn`sXGw*0mHxBZp%uwPgw)9Tf^BuBZCgZ z4>Q#MtJCRV%=z9X**y~J5d-xy+N??MUYaXJiwNIW(eg}i@q zi2m4m;m3@SN!0FH(#t%bKAEq$1Lp(#gnYFx4+I}ze#rbldi7?y^I_uf;CYK>l1L!% z4-A4Nk5+hPgtmBiU!aUg^~a&t?_R&aaJ~@?mrMukq4E>!ZulrkePsR<`4Yae-@GQn z4}#&s+hvY1=0|cloyeOk^7)vbR&7T!e7qYZgNZXN<8SaCKJ*@McFFb=u-Cy#+LNn~(s^LX1b9iME-j^&ZzmO&BYmP~NNS%)Fm9Xau2%Pb(-jz%N+ z8!Vo;%zeaiDTJlE>u-nKB$JtE4xA!-m^fg+-H>~OfgH#`go4RCoO;-XBi0(*FAgT5 z65*T-UC%eK8Q?#8hoaT(khX6}8#dc)JUAnpo+N6_vTksNTfHw12Xo7KLyrz*oI3d^ zdh+%$d-3(~COAy><1vToVf)i5BS%gX;CMYtICIf9b0jl`553rk=G$*}8#p!$i##kTKaC)7K|gb#AqL)vG}$JzMU-bNP@eI1v#IoM7={VJZE= zt?}W$?|)Fi$LBuHwto)!KPTxu5+G0L)?$#ex@gQyvy5|i-x%NIln`Wi+B%=DqAL3c&S;00-58DGi zrhSF#{fJ8&*!3inF~hkJuNRwaG18hG;eEal0?q}f)qyz+XAt07)#^SHBaQjQ*fLz6 zbR+IymLaAP^=CfZ$%%!Q6Em-dUpCn`p3>*Z#$jf%^xn=MeBs=VF!6Zwi(&2#ggHf_ z@)f72t04Q(JOgDPY?6MLpl{A9-+UslzTt`3-bK{2x9~K^<{o@1O zjG2&qw{N?47Ed#oXLp47=MFPu$QQJ~*MSA}*pG|uwnQzrgiZG#n8>k>Fug>NP9>9j zu;XF>0Niu^N?)6M^YEK5WW&Mlct_6%>m&fXL|GPllJxY-p=1U>1sf2wmxTL_mh5Jix$hh z8*R2(d6r(Rw@3KQ&lnd7c|@7W)S?Y?5UlOA^^_{gV7`Bkj8n zch?UL_Z%|GEGH#7oC^pbvdcK^N$+eL`+_!gmRV;5VU~36Pm3J)J#3kZEaMvyA4XYx zj_lc-&TYIpI2&vM#uwO2X&h7IwsA8l!JYMW3nZUX%(K9=fzg(teV0S>ACV7S1Rm_> zM3zJx%Oi&}dgIiTpDmZZq)PmK zjQg3E5_AjW!W+x>QLF8S!pMy9ho|hXlWBfihYO?pLgOE>3nz*i!O0Koe1(zj%Pg`8 zEVH>`7FolISRsVWyxVQJo50I*{n)Z;93_(GJg))zUe}~Y)DYx)iIN@&Pfy$Ntw*X@ z$?q}=(6EFcvMz5&8ntb!(_tB5dbZyJ`|#fmCkgo+A|v=8m+bTFtnvOoi}pCg40wI? z`xnGT_0l81M^1?A{{Vyk!~iG|0RRF50s;X90|5a60RR910RRypF+ovbae)w#p|Qcy z@ZliwF#p;B2mt{A0Y4CoX5sYB{{ZXf{{Sa*iJz$d0Ok7J-X(o2>NAMF#fHD~f8}#6 zgZ}`dar$xfZ|FlmUOue(mpK0b(#yZ7eGUCD=tc~4xvB0M`f6X$htP8j{Y*(+E%~ZC zF-o>(G+y~5{{UjmrDyp;Bn61?>#`7>#e`w?BXHl;hkr-Et^WYvaXF6RxVVSVjJW*{ zrAU_sjG1t+4rlsbmsP}(EfBpn>1L?1= zVpsk%a^k`+CHRK_0QZljqra`fBr1yU)NgtnwS3ohY+?ni|StdKu771CMO~u zvf@CZyGuWYB?b?gnqvtS6}&lp*4xjZlUzA zqc0y*UrLoV1(|@?{z-lyXpCWc`qp9eKK{4#VZWtz%o$QsSMe;@F^Xp}@{-QUa_SNd ztDgZE$&_B;*NTc2Y_UnEnq|Q|BfqV}57OU>hv?E?F6F`Z1}-Wt+FR$6*Njv&P7lOx z1=bqeDFGvXBO@ZGJan$Q9}u{cNbX^_UM0(?GUbzboJ+*MK9}?s{{ZkgoK7W@@fR?g zeI5k7T*DnrM)Un9q;8%=aJsKS%!n zVjd&ErqS2cX8!>3S^AM@GVfpbU!kA;4uA8n{V)WfxpvbueGmQa5gO_S-?RWYVZdC) z#No+hVKrz75~6cpF+CHNSSQGt#0)6eXk5H^aPkw9Ebs+E3hm>#$1wRWG?Xi%dq~0% zt<9}}*mkN2oy6f`B}4wGlz*&`-emc)ZDvRYbDHr18v;0si}`9Yt8hamXjp$US1|*b zPrL%+Fo>8EK6074?uH`sJ{)}NAJmX%G=G_a&^xjlVy|+GBKO3@oX4b_W}5zxcS2V8 zG{2)sT|g4G^bUT7%)h+3ad8Z@23)w^!aA21nSbyFnLy{XMI%A+8G*YN#j8U_7dM38 zS#eVNgWWXz%LuO8VAKln2&$&DE(Vm~n|$771}EGKg}mw{7TiIXJk+}@-r}L>s93b- zR!}$G5e1_168q@88NcnHz*=>0VwdOej zx~T0*r9+wLZ_+ckU0z_$?ROmA#TF^_!2V&XVn6xc*NE%r{T)k}oP9GZ{{R;lW9!U* zmr=}N{{V{mA6cPMs?l}EdeqMq0dkwZIv*i;DJI6n|6sW@-kJQtxN z21)O5$}3hi4*|K4h&yuwE3GxS$Tul~2MvtEosd*s97I!<6v65+I=ht%B1EOO{7REJ zik1V~x8S3$|)F;WZGvGaiRIjgZtTvA4Lr6gyz< znyTH)Fyqw6phZdz^~4b|O;o+}2ISYdODROzv6UD5hWJ3x*~BHVp_l&vrc^B+)jMLa zl<_YD)xzM0IfDZu8$g%HWopx;FhXXyeaC`}2ySk9PWcTyWIqs7GjL4(SZZnX@$|2& z0Em5EL;nC5IE(atyOs61$I{N`FX&2QR^~g*+N<0v8RW&v>wv(SdLhKk+!CO00ySgs zQg0u%9JD<~M+7L2)oBx`Q7aEQRVis-cpzI6$HW-9xP5Q`04Bbxh&E0oMvncw61=N{ zs+0t$-P|XTQwmI7A~k`>gg^sPg4NLQ_u_`cf?h@m@(jYJjMeF z64Sgw<1+g-pq{6x8JQTCmlx(N5;={RQ0JTx)uWf>%m5KYFmJTn8Xj--r!Zf{f_Z%pEpeSYT<7?Y<162DX!lEnzo#rhGYwid)eqbkF zBNSnAq6S?#g$g-EfGbVGTQpU+%h9=3L7_6{7AoD6#SmU|JfM{Fy$B1%@etZSFvTa? zFb)1AyEX9)Imft$#2H1F^M2+MQ!&+$h}P~74MGqDs|6`&bU3(_U2~YuifDo@wz!o5 zvDnncRYCZVa4B^Fv^&vgnjW}ym+CDN<-`q$FFhQ77`0ETDj zafZIH(JoeEGdxFAiOe4TqfsW4)Cei?7Yce+(E~tw4902w(;U+fim#XG+G+Jd?x2|! z*$}GNc?`WJs=xU{i>=(5xNgQ}VTIDa+J&^ol*BN*I)BW3OkfG}{{YCm&Y;-OIz9d( zsurnF-ck~apxxs1^ZAafAMf)mAy=mi0CUJ`*QbAYb*o6+AbW}sT~807i|SlSDcq!F zrmIJu67NsQW&rPe#d2_QDnZCr_>R{+cFag>RF}3#8Y*24tf5{YeHbE9aI|ir3lwIX z&-sW@ZnL?P!xEk>2rxKaNMg2>OQfdEVidC9?kjPXmJ@DefUlU1r*eb2QH_~dPFrOw zrc;sxp!u0H!74WqwgA}KF<)`wh#D6aD=#n^3ohUdkyXaj+uX#{Q5nk`u|8pN(ap?= z3+gZ41sCQ8RXzkn3UchZKnI9l4Se$|ex2vEFx(53t-~$O)=aZbHe;E4$x=sf#} zAYF5a#Tz+cK%-+xtVYD`{7O-mZsP1x>4X|VSqkoR2f5jAs+n%F%|#gjjY|`_(cCWi z^BloY+QBF-&9N?+xZ8Ejut1}b)W(B)t|j4cd5U3YbpTdsCJoL3s&O8-UgJe~?}#v6 z#u~yW!u1A_j~3lQkjoIkG4U-F*(?LeMj`+e`uD#X$M_kA3VS0Wb?#H6--vEdWNiHI z0dTnhj{gAUDanGDL3r7l_#h>vP=P%7my>m`h1b8_am9Lx6x7rTbW0?NS<>PX4tK~w z{&fX8?pyRH?l<+f>h4@pZTdT(GknjKb^v+AD$07tsk7X@3+n#>`aoyhp)x9a7&rLk ztQ1)YJP}6A6^un&%p)egSVdZ(yvx{@UobA|FGHW3Ii%Wc^ti=~FX+Fbn|PZr$3`HU ztZ8(nAJ^Ivbnd`uCe7h>aQj*nGF7aP-577jlPjiDCy2dFKDSGa9sLYo**U60vB2Q& z{{Z<=iE`xgGYw=u8G=Z3aB7$+wT4V$DQKdHDJc|7QnKaluTZoQBDThP^weHft+#&S z2rkQZLNrF(Z0EQzmP~e$aJD@m-9%kn5sbN*?g-ORySk`oO3bv$xEs#n88B9-BDa^Q zBLAuukZl9MTw80X_tboQX~ zL8V-Za9GQZGbp_ROWTj;J7UX_z8ci9agZDw7vD9~dBHR@`n zp2@fp!wyF9ML^bdtNUn<(#rGy0Eb^wd5wJ=pE8c%j(CI*y<=o+*D$|mhg>AkBPxU8 z)Y-dj23Tb=GQCH$0|PR?B8AuHSmc$uZXnw!S97pInTla%B9O6z&>-d7B6}TmoYD2U zafTJoIdE1<}{u5sDECVF8x7Ns1f(V`z!0 zj2HYrXp)O)UFF_9B{D$xg#wVxG5!5ku4`2nv<5|e_>@a0AzY_>ElrkmMW%7Ti9iCk zoXSvfH=Mck6tQaMR$FjE+Q%~YB&g!zsP4%~qnFDlxT=ZKjR7T`GkU3+;km zC29jp#HDRe1U{gSE-Pk)QLwX9JXPFS0wqks++VT@&VzARS40M8EjTzya6U{L5z8q9 zRHocZx)xQ~1mAPoX^D9Ep3?C0sDqgEjT5<#3v{C5XH2`l>^Pn@6EoNR+<_;!%+cItxvANV_S6Y-iIfV+TVML(ij^|Dw=G%sW zzr0d~!7WO24HszU2|)ZsaNRnG2C6e+;8H#oXkbAxt5N#C~R8nl!0|~ z2S403x$5FJVO;H*5C#Fmt~JG9pHYkc#7@<}{=rUw8Mw_ln6qCp+LyTpbR7Ebqqee^ zd_y5EvR#*qho5rB(mF#q$58W>&^I;X`s?%T?WHYP2^g^V=7^XlB1(;h*S~xD@db3Qr8v}T3K*Wn9*sb zEpsR?R;mk{Dqw`>(TQdRR%vDBxR?wC7U|Iz%H?$e!?{aa@g3-z0*K9k7|R$#HW(@a>=;E=P)Ck%8LrG zh`9uO&ZQ?NCAaey6x2mrHbw5ia7FRdxt8?6gk?sS{$PV;3M}R~TIrPDU%WuuG7V}_ zHGjn8i)IyhnKDdY`w;N%A*Sdz9S-l9SWi|@@BIuL4Of5lXU_&WlSL4!2=U` zTimLuc$8tG?3|IALt^4o3;CB-Wqs;Z^QgK*TkZvoQEbAvses#N*iIG`H8mYf{v%{d z{!Mo=&i<7vG1R%V zeA8t%kduj0iNX&dY){Tq0Mp#Hjy%qAja*u}WI2$+&$?p^Q-qd*^v2+=*>9Pxd=^?7 zc1wFr@e@U;yP6yMim(h#VpL-3@e8=KsO_}OwcJ2v;*flhO5C)U5&j%RU!{E=M}Jhz zaK^r6N`xFkOfN)bvI|K~D*)0rgzt6siIOKo)UZl^A_NryWtEh%izTR6V_))84wHyA zV|CQVFA?Ytdx^7H(-=~BZ{{7(DLGz#mTbx?EbFt5AH=DpF;KF#m_p<45DIfX$?hW= z%aZt;VfsK1_4g96Hfmv6$=W#l!>wzM0W}=%7{*A}D|PBpD$$By9Rp;j!9ZqZVB%!J z%+L9#Wdk%f@c~;2O(HHPOJu|%(?T_Cn%s56wphmEmlVM)6U11m%u`)J z(8km#svN?lEy1vRluF<^gMvGXz?6h-G-_XPZ#>Lda|h{aMsPG>l%jx3tPO0haka-t zUQwy#jrPtVfELEv!H-==6$FblFKM7(H7&M41^YkpY%oPtw>XqmTi=LhiDQXthb#d% z@=Gt6o*>4eP@BNiO%CPJo@W=UlqfTs%oVW$VQ0*?YMwib0>whD#CY9qq9hrvqtSta z+qQ8l@p9G+TrjLES1_X#VpWEHK|2SSU?BxlX_!(!2bgvR9M)<8+1pVSuNi}ubY3`s zNrv-ram`BfOB3(z3bS$0x8`I3W;i7r!4EQvgi2gOq=2A1bDy{7Wcn=-yg6x0hEvqq z8n}7X#Ipv64xu3}(5;N50*)37rM$dF;OCyEU{e`*mKKoo#lTEs9Kl5@>A7!lv{{9a zg&PONb#4up5Zuks*HIrR3NSR=%mYi5R=9_Wd*&9dq1m4TCz2u79%bUk5h+5*?ZFiN ztmEl_TaNyT6U0_8(543AhK3U`6C`2v?J2sBf;r3l#4H?mhp+7lwg8m0QI0;FfEmYE zf*A!pj0Rm(1hFrfcEnzMedjBmM9$?!6^ux?9^l#9K(8waqXrkp`!NQMN~A;FZ!PX* zD_P=TbV~|#=23WAeT@v^80QF6gk~B}@6)|H>N*+=QPi(hoREht-eLu}TY?H2Du|5; zE3-$5pT%khpm9*D7rTPp#X^C2hK?7BQ#7E=!n{}7RAiP_lx|qS_Y`UNh9k_nLmkzZ z<`;D2f%;UitdJv47>WuYXlu+Usjg*^tz0V?#BNm^$LyB48oJLh7S197yhSe0m=^6^WU7@;pvuV~DDlJVlyl2-EhAFQ)3?SQVVH3&AQ7Z^`OgBTe@f zR1xN0GEWhqeAWo5cW_7@a|*0npmD5`S`V0taZ;w@84NaWJV0MC5UeD47016rTaHoO ztLI>~aZ(oB$`ei-&Ss$Ld4Pfq;P`=8yk<0EIg3JQ>zI~atyLvoIuT(WwO%v zmTH0j0LY`J)??*(KN8)g<2*|hk1fL+7v>WiEEv?wKd@uvl@Ri8DQTS|Y2<|(qU0;V z5d@>$aC(Romm3dq#LFk*3LMj1a}bt*OFU1@@c5Q0v*+R}F`nh&4g^sVvKvm=cXs#3 zKX~#YEh!p>u(S!l6)a16EQGtlKwxnN1zg231D)pCfLil0vAd~JrZ`^_TSnbXD$TPQ zUoPNbc;+nMGbj0uRWU~91|loVxZ|9~rN&6DD=-f81589wM($lKYWEqO;4>BkHyWT< zn3L4ndw(JBh))L9s07Z9U+f?Q;anseh)i4$%JjFrfD zy1~n6dyEwZfU6s?AMC|NHa!*5nVsEWFa_E3kFzQasYNTcjYl)GSsQIH9v~N)>~WOm zQwlgO2D=P8Hx)T)W>qpsq{~$)VNqZJ#lYM~g1neK?r3!20#X81brE^gO@SD#?WyOt zzq~-kJG)VFx3!F#frbY(;s|q}a@B$)0v>J&l|02hjm#W3&FUbax~j}}f*vDWwOGBe z6d}(LH9vWs_<$>zR@x$8cPyhW!U}&;fH;89o?>Lzlv)=L8iGM=K%7Lub_k{I)7fKG zwltFzpzoNX-JQVAKJyO1a~e^yHWaL8nARirm(VEXsMJwVAaJ8I$hZz%F>ehJUKJka z%y=1wi>iv*W-3Q86*7yb5vEe17r2xInL{&-K)Do)X5gAb!H(LOXPAJUQISp{#s2`y zxr>};BzF>2w!Vdk?FCH5W#(x4WaI4p<(-Ju`HU!+pNT@wdbwh>rUXe;!{n8zed6M& z97gDDh^zY7nDDaPPh{-0d4kRr+uHvC*luF;biFVh>n$A{{-V6UFp4!TT|)sfZ}(9e zv978rZIh^*T`J6y&DPhKCr`{Q+W@rqP3{?R;KMhFm1Jsy-anYy-Q;#|z2;Fz1wmGA z>IHkd|{Dfl(sjW6p8JwcZfsQk)KWfU)y7 zYNLPy%(!kB#ygdxWMh@wqbHa)*)>4!cT4D_X?=tVhxp}d7Hc>g<8intGo5KpY z=a%Nr1Z>?F!Axkxtw7)LMa1KSo-Z-ZBL&P=ajvHX>%_`MT<2^}2Ly8GQRr^y%bS+& z09P6R0PJ?7a^?Gk917k8H5z7vcNwFg7ay=;n_$x4jKpd+RRC)S<7IxDZq`g4z!W70 z7SHqZ0AG7Ubum!&1rdfVqfy4*^MY7%X3Yl(Jqpl@tG-BavWg}g|a^hxZJ$?b4;Ws=2)YM1TmH6VeW3xhh#X{B%MUqlvmjIV083i;Hd}S3C1*aMY%DNsO;)9g zbe3Y^0aauD{^|@Zh-527m1?H}EMOI+00Ix6Skbn1KArS)oawQ8Aa5j4jatDy2s)qjmdoeAO)?#hX!C$aOza^ScWIbQzo~> z1@x4`*`_U{-p} z?2x`X{lV6ofA$DG7!^ileBjm4#rH zSR-ha(H*r4)Wtxqi1sF~fIeA8F=DVwKoPi13AQc0SAmsSe-oye5F5TVj9LsZNrE}(tvhm16xtg-Xi$N7r8*H zlof_B&SGbY%{5BRb0es zP>7;pp-}5r9mpwK!e0JmZKJf}T*`-{_=kX8&r6M)#dQLdZ%`>h4(n0Mu_<)u!3nj; zm?Z&=5JJqx!1L55D&FP98lW**S*$(70@{EgtTdaS#U0u zvm+Rn;gy(bR2hiFGXh>em;x~zgk}Jv%o<9ULkkunS^P^&OLqB%LSfa*ma^SuVFT`H zY-xr8RS;FG#13Ub*)(+OR#w!dq6jHf8%mZDOjMPKNG^r|g~k3N2QW$vMPOa6q7vYvio zn`b=B)kJ7YMPEWJpounkz%_0-D|s;nW`SivtQl#xv_YfhI2kvptlsJ=cmr7r#Z672 zGL+h}1G^Xr=FBZyTyr!TsnX?iOzE?LV#C5q1XZTh&|ypon&4@M?@@F+M7 zcl|QdtvOQhN3h|(rE=WHU8~yW0~J`6Wk7cbA-_}ZBh0pSv{WU)1aXsa1p@13!2PBh z>luyK2RjII+hgF~#qn7MVOsEb8haP@pcWrp~Mu;v+Fo@EUv z{w2C(h`G4d%X~@#5QE<_FVhqYXpK)d3Oq!{b2>Ve8EwFKre$6w+6XHgOAKJQH2`g6 zhXG5p=bai9|aw%(PdFEMAh7wm;gJV4*2)gA(lVXljgxYUaIF-Nl-%QRWT|HCmVjuBD+e!LW9`#4Tm`Aq2cdNfs|2Hj5fTDf#+hdF~GmqRUWbTz%kxvfA; z9mFmKzz#EW0N{f06N<7Mig+SC*SO7(OOC3=N;!)I&_s=a6v>vNw6grg05a~Qu|H8I z@G*f-W=gM^Wo~98++-h#XCWCiokS6!v+*4=mSHV}V!^$&8F!H}q`hH=MYNEGs*0S) z@Cw$gJVkY3Hf>Oj2uNH_;Rh@$Ox*xo^$!9P#CH)CIa-#B!zYcll*@Y8ve2%)v}J#Y zU=O;BXk?_-l>XUFwuTp6(rfbp#}9XF9k{$rO@4HmRlFa!a=wWF0Gh{%R}}&RQW3!t zu~k)N_*qMU1vq;k&;x>0(Nd^}NYv0+f>~~eoK04~T7ms27Oc*Ee&en{EC3Eyqbu6*}05a=$1$c= zV^LcauxTT*olFCD$%}zo7%`p7bEWN`rNO`qTr<#01<>;ssbOCeJQF-hfwdq$PrU2_ z03e`M)OEnVV?xuHH3J1Ns4BJZ7Rn_qUCU@SUwFfG-RHPj0|v1$t3!Ew!p8-|iB1w4 z${r${CzdLJ-*V`9Ato@+Wom zLYH-vWqre@Hx5A#syfVW%U1IhbbOY}dkMG-ux;L23->CNDiK{)BaNSJ!Szz*pujvT z`o{RZVzQzN5{+fM@$6N=q1x?kQdHU`F$mqqP$Wpfjbc|bH}tqdDa$S}%49bfK-_3* zH0jsgauJsn_{18KV(q2D z>#3!tp~+HmV*}=Js-m}sdW}O36xJiK;rv7vRk)N4Ke%`g?q5h?L{(f8)0vaka=XTN zFws>DBdMQwm#P_9_Z;*@4DK>rrg0qQrztSek<<#{Z&;at>vt~D-O_a}gBkA9HNQrG(nh3`MYS1a4ukFrRFNs#)bilp9I!!iJZ*tW%sD4wQYz1Qu6>6 zWOsr9ps7+P5lp;6a~hOgsGR1(WpEs^ZwMNs>~_!kp`Zi;rCihP3@`+#jj?zlS!-7s za}J_Ybq5f%4%vA?m;w8!p~w^hEh%qM{{XONDP3e;XZ_T?np#@ruG+86Yz~*Wpbs4V z%~Y`5vN3)&D6r<&zr1fVzPXpLdovcwiPUIuD79MS#HpH`iXF=vCz(ONF+f{8iKiK% zC4ow#RPh6qn8e&)v_9?tBg_d%8;QkgT-dkNt`&&O6|SRiH7cS4x`Ykl3YnC`wG^g~ zD&5AkXoWVXzj2njS(RNv+kRjGL`u3mLtWbVg|%m#&7N4;#G;I1A&OQiTEQ1EQvxov zEN$i>wNkBF@e5K0`L8nR=3%2KiM5t_g;#RL=H@~o@0iEz|AHt0;iP zO;fKBnG0{96Cku)M#9L}UFeGn?{x^%CINdzM~6{-L-7#M+lqo@r@X|A^{Ci4hY^L= zI*hrXR<&7KV5^GdgwNhlcM0ji`+-OlR)8q|N(JO~{Kfmh(Q9*wY5Yu;OmPqZtUOLU z+(&%DWaY%QMUD@eiYrboF&pkNb6UHK(Kv{-o6JW_gi@EL=ii};8epmSAqo%n%0+Zz z9%YG+o+82WU;uI%x!Aep4XFieI$>;NmtQB$qWCOV%%JA4b;}M#D=WmuR|`-+@N--C z%#F0xBJ6$Q90ld-V1+9;3aCB6QO~(dLrB#{D@$EUrV531fC|JH_AuTU)|;#1a^5D4 z#8FcVT+5jtUmV%z5CB|+S9Z8R67?MkaW8n8f3h!4%nkT$kIW=E2viS@lIwEi1!!93 ztr7i+kg;33?h0l#)?9_^j`p&kfl{*2AQ!*9w|~$rVGeLrd0@Cu0Xcw0Ql$hPrpPx0 zlnlW%+Dja(#SjY^XPDM#G)&-un5@X@M*cg8EmTz)rmyBUA}sQJ{6{jW*gn$#04paM zRyS?U#G6uLZdZK~+n%DN>BU7?n~AIE_Y0RT_CpJA%zT1wxPld1>SG3oF4cTWEWD)^ z5VqG1#xgaC?RCBR+zz%y zz9F~aHJ`M2wp$P?Y>m|!%n6&DTw94^u4S~ki>L)-dXFi@TsG*$Q&z>D;wJ_(#CKJb z++wv6EyMzrh^p##>49!>Fe~qH08PgY8uJoSG5VR=Cg8yuIcCIiz0^*SCMv|3AB1l1 z<%0!!i7tir73WVdAflg{Yg(tNbRT&|s?O%JT?xQbikNUUxmm11r#OMGbBHZ1x!k*JnWwzOS?Xl_Z2QcJ z!M763T$L2E>2Ik(S&G~_3*@C&;7ZY~aPC@_?mS1N1HwIlcHQ6m%vz!axGgz(mJ*DV zm2llkOdN9>jXfgtFYhTzbK-8zXRSfgnD^M+p;%Sj#RnGgEEz|;fXZEJB2w+kh+Dp} z1icpLrUw@dORZdUP|)!eY_xL>4c9k0XP8mOFA!xbeMX}+yddWD%op_JBkSwI(GCKAy$SU9*bmu# z6>PSi&dLu5>Z5LLERhizozC?(%^~9M{avsiXrFASo`qtTc5`x>M71uO7ah^7sGxh` z`m|&ENz+nA7*d0EJ4;ZBlb#?Y$@-q838Xvi4s4;tzreTy&Y{JQn*ylYEUKyq7A6oA zO?z(104Jm}kWm~uMKmyqE&V&OUTjZ0+WL*EO-Qfg?9{W0E_$+xas@No@jiAX@RzJY zEwUo3A{FlX5h`Guq96AwUO8In@lYvFn>(($^mNR zKzSjOsH;p3Pv6Aof*H} zx#CMxxTX}FMnkn(>xR;`RYJCFy+~y3$tsw|8Rn(}Ca-S!#C*kka5* zzQWkG%UEx}bVa^@Wm#Me=}>F&rvRH)C4{a{1e}t>PC@*Opvwv))Ps%Wb0hj9Y&+tU zwY#=LMt2hvp^OX=3iVccg)0t)06!6Ae;9~Buph#^yU56nDnFb&F8RezbQwrpsxnV@HG*d=CKY z%e!R*eGfw3XJZTEIi1(Wg_>yS6c?ZmkG1u`eykT$!VL46iqE(9rjbTw(DpVZ5KA<* z%xDiL;ImNHE>LI0i#8QK}RNgVCf}h66>Q`|`=tXrUfIbU~vn9ykA|s0(`iRv@ z&@*y8y9-+Rks`hvlVs*V8dVZb)-*ax&<(_IaJ%_SJ3Ns*H2F%1egs*VJ3+G}>ga?O z(%haO1E9xY69vP=Q$rqC9JLJHcjEgmY-b6hMTNI-)JBfItg1h$eSZ$e`(}f*c-Bn$ z@aK}JN$=$fv>=D{b`6?@TG<@g0x_21R2BU+n7tb%{L>EJOvVekD)@1pU8e6IA6}a( zI0{e)iRM+3&Ks7Bg9M=Ej~a$h|B}sg4>(9$XxSESthCN)4m|N;vMxHCO@O*!guq(E z?~Ht-98)xJe1KAN6A*@*XuqW>A|DwT&nfbL!!vIIbl_&J>8K_n5!J>(ng0L;4R&lY z!Zk`4`#s4-+(!xH1*-Ir>|zFo3Y9=7|7He%+!FJ$mOZ2|VCX@2yxex`JEY;9Rya^( z6C||On|6oI5k%aOJUTl4o^Xff*NE{SC6C2)y0hI7U7g}1>;`*ko1Jg3PQp=yJhCdE zurG@vp?Ga-npYH=+5eW5ugFV-dw2+={r2SU#i<&l;hsIQV55+T&(7j`jB-kKUPuPjO<_Z6!nANLoHi@K~*m;gUNVE>&?=`=K22 z9fNCD-9Xjrqy5XKz(|&k09_c^r6<$&8SE=rw+cERA zy!QXcLP8=@KCS=?J`Nm4X$rJ3J3l*@@L zbk|m{hIFkNFNOV&6W9^Iz%{Z`2<3h3n2jly`XgzZVn<*Mts z;{nUR3f|F80tHikkHt;$=N}1s=37L@K1#i#o!j10*yHQ9$6r`@Ocm6ksg&*Rv-vGq zQHhh(71A%`C6OH1aL9q++hc^C8=V?!7C#YyT_e8x#I+2AI7H8(nl;0?+eJs`yRCi* z{|CrxW{Ojr95p%4HcP73zI!jHm*OVhuWa-1g}frvdfU}((8twvf^Ik)(~YP^DQBe^ zr&;tQGWT@9XHdhn$O7>R@Wn_njnbaiCL&0*wN5b8!NHu9`uMC6^>T;(A30@p9*oKK z9oq1I=yL!$v@Cv*OJ-aM#JYgC8^7cyyGa?RbswrxRrJq!Cc543Z%2ig|6lQN+8M)^PH}U&^sOr;=m4fsD zQ^Y(kr9^gx`hFInc99f+R&tQK+?cuwyX_yVGU@dY#`>t|#MhYj{}Q1e510c=G8`tc zF3KH1{Q%W|+Ce_~1Fkk~6;^3P!GU^TGkk(>-GHR@r;r-vI!9#y^Sup91mDKCnk^(y ze{JM&tP3SHu%@1oXgQ-Y?rH`SnI;9ssmIs9`+oQ=OU@hLw}MEqk#)A0Y~o^ec&wf2_PjvmfEl3*w2FTlLtAV8@(P z(rA8&bvMN92DTO-EGOQgM3Xltx&Y8U8>-4u2$st_DYoWd_tgd^sG3jp$3s7(p;6Hf zG5HFyNBj@sx(NWQC<@O5TR|UJoBsfPmfgB(CU%+wSgDvPFQPM3^%;)4YJ*d@lZWp} zss4b;eqH96q*LzDTi9YA2~qwVjMk?hz{Fa|&;v1Gi1WtXm-$2XZ*Z0xoR;iFm8tce z_?zZ--d}LA6QqQnT|`SLXI$_aEKgwbSkPSZq_hYUP&c5qko+|T-m}crN!SgONP`Y@ zZ5=B-zIqxAaSp`YT}V7AX4TWc6S@1PB(Mew%4I3b}*P8R)5BWWNr#-|(IcZ@Ox`;h-h9VBH zEhi*&qD=P|G8tqS^Ex)Sjg6~3tfAgWfrX`kpXP=GBe-i#zF#Qg(SfGCYat8k$F0m# z8U|bH#i_i*v1;n%A$39n_-_~_viT~%mEZKSKSFlp#tL_W=+k{`m(oEy7PBUMt`@BI zIQ-m*Sz*@t7VE+!d|(W)FOia(^iCU2r>bJ`i<)oQF@A%SS8~axe5S{IGleNcDwe*~ z2w3X?C=-2x+{wG#tS_9e#{h<#$MRMG74mSjJf2`gRAdRP($~E)$I=RThsJXR(L839 zd3tD2d<^VgqOv-qqrc~&@=KA|ST&+TLCF!NJV`%jS+tWe)r5BWO6Coo2PqA@@S%$v zTi8q!>S~;ig{#j8M@k3GFLI$LvF=;VdKhvzZQt z*SPle6Pg)(nG(d#n9aVr^GE@?D4i&v0osTL=MoJxJ5zjkzdhHQtUQo)Q8aEnB@Ssn zJK*YCXx4u6&NeWI!fds|Luz!lOT(E6(18A6W7efi&2Wkx(l?iv$+^n662i}d$%lEg3hH8mw;X>USf zo^{oa;>=Jh5DMGHLJzfhQ2m7K>zk>Us{EXV1tjH3+vZCIz`YLG~f1r zV^G+k+HP4vpk88fE?&|l`W3fl&-{J&y9KqFY8l|_Ss~xSg<;_9X8FKqE@;3XxOjQ# zQ^A0f9BlsZTy4^Qy$tBkn!4OLr|?L7enZ0nK#OVe@_^}%YnUqwSkW<6MT7*QV#g-( zW*JdcTuiubN02qiHlB`(ZeEeG$?K9|{@nk<05XZGXEI)im6TRZ7+04aP9|J@`jWhl zUuykzOS1Lyy~k}uFs3a3cbsY%5K$Os1j9v>^^?tB64FMfqRw*aQUeNwdM6Hv_4E;H zypHN26p5f5iI6}jk7LN<_ctUf?NqaObz0Xz1LBCI?^FRLP_UVgahmqkbTm^W^dD|V z#_x6*PwO@1~n3Er0LHqF_$mw(re`)Ccn4? z0;zv0D0?W&7qI)IPy`hn?;j_6p!R4+NG|67W>RbIXq@p_k$q7(#{9l#qj$d5E)m+ttYj)StP8dB9Ie6*9bYs+V+5+QBBz?E6}C&KffgP0dR5KIV-onex|`jVSF2%g(#{JiN+ZC1&3$ zSBOIMQvw7zr-Ln?l^hEFLFw{$y3d|Zy5PLSIB@g^4M%e`WY~9c2;M>`hOWRc ztb=kscT)@nX)EazqPPlS$UZoA;cJtUIE3c2BQ@sdee>du(FBQMb=*VD&nHU>abT3P z9AN<%g2}Z3bQcOK-^Q|HLibrTp{yl!Yg#S~(NrBjgbHsA+Z25gDuP67@@Ai+4NK(t zg;5vchq?~$_&=Sdn{eXSxT9I}Y?M^jB+_h&5l;|ql_ep}_ruAbv$)w06)kRke11b0 z>5eRWT2K8&=)Q33N4PQN&mrCR*^GsL-J}>NFHEmC85NV6KCMD#6m9&R*D0!ePFm!s z!{1=Z-4*oAf)Emo7;a#9e}vhfqYtP%!sx(0kGGX-A8g3cxWQ1b>kgn_Qp-d{EP)Q9 z6ghCM3DH(oBJ|ZEJ7GZO6>;fKvmVCoy-9Rp+EudDosc89O{u$!6pKD3 z!-Dn@sm3uyf1*9;=FX!+<)*gFv#Gix*q3WJ;w;_X+R2THbM38o@VWT1z(t0y;6KZ* zKl31$#h05OBXavXtM5f3w4sBFFT(<-)HyMd9mUXx%)XO7cHI*6(UH zp#<+UBi@TL{S|TRlQkk%B;Ynbsmk}IG)u7xL|=G_tNGRp61*k}ud@KJ=CkmI=Uaiw z3AKGnmRI?9&Ix{BZgK5hfr#u0=SxYanm~$oy{KZPHXEH}g;U%SAI;NuN%U3~jpCSU zw^>)6I1{>t(;Q~y_YV+zE*_{f=Yqjde1)J{rCnx{xEi7?D$=rP&!;Z^@#IHUxZ!6_ z;@Al!FIiszwD{1Y%0q9g>~ktD;kwmK_OO$JyWheLbX&;n&aW67N7=;?( zX)0KQ+QUa^BYUsunAA@7d7-cUTgof1{5p8UPqeAZAGD9co*-A9&T`D3pCklEkRkzF zwPAzv3}G6>!@rIE11hch4i)6%42{20ZdMeiuPv`rmA;y-O6UWVBqHYH(mYgy4!N4? z@J3Z}*Ek!3mVJCx!cXdAJS8^g1XX6qo>`0LK!f>r%3Sd-%9q9O9B`__Pr zXN?rfVFE=4_FWgP@#H(;cS5RLfcPOUb8LD$@<{&);^{-Ow|4l<6II?$eKeD2JkE~E z&Pa&=md_(i*9ckH+cDZ8r|d20`^qaAxkK=duQ7?bgXg_zq-ZRzV2y+~>LSd$=@$Um zara>KE#1-6Wg@%GNRN&YD1}h?iUf^8C>;=^b8#l6qLy4w`@k!c7|)WzGQQISHYdkL z#YeS{`zt_BqTO5BWk9{B8hCiRP37K;u?K;8C)f8Z{7!4FG$I|!bsM>AS!rVmLn7b@ zz4iE)^i~tKiaSJ(zxv5<7Y<_5(UsHG=uc5B_^yt%&O5e!d$hwJ&AXv&-t%XEF3vLh&g+wyn_1u}j-eSMzDs=0+VJfcor5S} zr%l2_$77TI8Xyq(1X+d1q_G+=8$M(XwtIrGe-8$)Xad_+^EwXHM!amLx%DudLb1g$ zM6Oo)Lq+?P9!?9265pu&4_^}W)WqSkHb8mzZ^WxH%BXVSoonZ=^V|Ff!-hbRZ%0Sbnxk^mXjaMJi5(twBM2duLttLrp?4=w4&Visn5`^Ah|_HvgcV?Z#DjjKElPD1iY&Jab;B*)gsa-(}@LNT>QUCP>N1i%!NC?Z4ZT zqMz4#aWykZd#XoL4|Dy2r+;96%fn`-?J}O@k7X2)>R5E^ayXgFOq8>#<;j!ZKsVc$ zQq|8G(7bmaEf7D4HhE&o9+zOe3lWaU{JWF*neuO`yqWQwR;Sz27NM=DMIzD>g2`_u zs;;r{1G#=ZGlDzDKM|+NGBl`MI6YAGnF?X@u9{?x*|nMNNWpYXzYj?4br@j^2!VQf zbuVquR-D8ZRlVUl@x9rTgtPI{M+nmIb+I<)39#AAYQw0a)Z_+iOU;^>mZIYG9Pl)^FYg|H*xL8*ciMMWeA@1zLY6Yd;az&OX+4p4h>z(t?ZJ6c~|gGl9()EDRq8 zLasK9WGxLHHogyAN357L3w{ZP*m-fUNV{7UdioVo2ge~$^?~wc(xW=AKYX+S-)j-8 zp?SJ=Iu;N^ZzemUNz};CXt4ra^|lL}s-JUYYRjkUzUh|`DzArUPo?W0Zd@bNB?cD! zxCr~wKYou~ROZ7QU~(_ZNMYF48;o=nk7A7qH89tVd2$HeBoWj#$XD)_IHH2U3^rF| zSG=)SWGDO^57p;M-WOjgp+9?cNlJln9Xww~Mub4^YcR#uDD|@>ar(oEu;)dw?WSy z*n1>taP}HgtuiZ^Y1+&)u!q(EFQv=q@xn>M=UNJfenpTrSy~$PH{GF4&E zSJB0lpFfIJ!tTpk@*N2YAHOgZ?zjMly*~!<6wK2WrCam4ouK{uIK-%QB|?OfE-Xph z*NR`*57^)@lP|}wi}?z z&VR)MPY|;9_em3&)=AAvDK#y^n>i)J!S}e}3RgJw_UONY%+zU5j%L-;(YvhKV}pjZ zyIu|1KB9pKw4ehFb~*o%sOjv&CseP^>MM{9_P*Pf0`UP=DzjXuOC&ZO-S~M({Kq=E z!d>m%_i?AsGbfB`txz7iFn$%vQgU$xx7mLH@2RgJRP74e=$=Ipz(y!BP^e7qha>k^PkwXU?HJfh_VPMFmheI zsm}#Kry^DtphKK(7M>BQ$Li~@ZPL?NKemKjlyRN1z4L75KcsmYgLZQ}$Xsi$E?vlb zUH|!YZ;(ynI65(42I3@tAZ+WdhovhD#MVuaMRLPn<~J>^1ITmm)}%=e*e?VMr7p!8 z+X}ZxOJ}?KpEeCOIXQlx9}PY?Ol6bu`c4}W98~$FE&OZJ!i4cs1U!Dpe^hPGf4{c3 z(WB2;_RA+Mjeqi7wd4d&id!dBlr_gATG=fecZmr3tpDT9ngc&D5A$^gjwRalZe68< zwfbH522N#}<+p}IoYpi+SZ?;l=pDq5j@FU-jA~JcI*oL6x)2>cMOq150L)W1hj8EXxf0 zW57Pk$8)mK^SF4Bkt4XbC+PI0OFfves@z3GlwM8EqY!uL3z>l{+%-IDcJHLtBF^E1jhGzQ{ znN~uvjYzkpW?QYWIY)?G(wTR-R;WKGm9)~ky|qPh&?@zbRr#e>_5fUY#P}lTK5}%p zQwvd7`P`I(SR^#m#V8^7`Z5zs$7mZh6wLN$HNbVvC=0G}nXrM0AYh!*M9d429d z>Fs@xvBHXvQcskC7V{>V$FY6pVn~#^SiIqt)`%>dB!C@FBRUc4NtSh-GSxi8CwU{O z_w2u7Bps%bToy!7RNeOPqw?)zuR3z@Be7>vOurVjR#q820V+5%;4jNALItK>u^aNv zQ$dw)>7F{ENK7v=e^Xh9x^hyD^_HgtFK2VK*|&MH^8Ab2WFE<)d~yY6_O&(2(zS?7 zh>_pa@LWyg)y;%-C0*y$zgf|lp)>*sQ4GD@I20RRL~95lQ-O5{LaXU(wTrroOLf77 z9HzjS(l{}3mIYr`o~oV4lg83M)A0*(dEYnCi<2nmdhpBJoP~rGz!x$%9lw~|efanv zjnM_KZhIHB+dDq}%*9H&*mzrIa!}bZl~t4IC4AT_vx$(Dy$E4?$03ORc#4p7PT(bm zJO7#?T627UJCux^>%hEs=O@|!@2NtyEJ6Lz#mQxrY&PAv!SFJ~(AqSP*rWFJiz@XM z(LsMpnsxU1(~hm$#J+AHcZzdyiIp+q&EZdX-5L=Q!DnJAJ8HsPb2yrlLf+uK}I ze=bZ-5M9JuBLtq-eIwpNNRe7oD@k6%N{%?>=x8lIz{%Gz9-+6n3wZfZ4{fHD>ThrQ zn(AT<*1I2rE@%bsZQbW%1L$)rQkgCFQao^EPkn|w!>mlzFkky z?EvkflOwZL;>s8S!Bc+m2S8o8zJT39UqJkE3 zQYfxuGaltmaJTc-ZkGMQ%c80ZvrLpvevpHy&W-oBWK<4S^+C*b9WpcZx=r6~t$HP# z@BKA1aN2WPWnST3sH!DzrwzW2?8@UpY^}dyv|wUDI=A-TsmgmY!51m*L*PeMD* zs{MZeRfR-z-i$KiE^Gs#D@f!MghPHY&{pP1;BWAOO5)%AyuvGXMNuIFOY);F74~#T zbV0)ktb?wh0d_FGg2b|rSfX`WkE0Rx?X^7RV2=43c^}rq?^mP&)A#U&i9+bz^=P2Y z`>f$qg&Fl99)u{0o{rRq+a!XEn#8XCImZHt>eh>5{8o=_E>~gu0ZCW$aFr-lY{20=~CDAo|=w5S(Mprftcb_8lY;5ySDET_ekFc1^ zW%}@u0GFw?HcxLbzd37&n$Ddj3mJLqF4jOaeWvh|F|Qy+yesnX#n5p9!YOWebT~Y= zL@_RIP=n`Nev#*)oRx#OFfF`ZF!LEqfKLo=_YUSIIyka(Z&-)MJ0ozVhUjrba7~21cfB z5B61U7ZB|z0W`xGTkCvfTEhWx#6)Iq4IwcfvpKEDYkd?*pbS(*gIc~Npw z`C-QE)lRw84M^A=&bN!}OjY@Y+UE_ZtnDVmGcayG_9QcjmSJY+VOD9QoK-;S(|HlQ zAdA5(X^^~6D?fKI?WV|SH27? zh_R{|uhcMKrmlFZT;;6(5=rF{iJ~%5$mFe%7>QLx*OQDG|9wKinqTdcZH*$Lb|sCh z1XCgc-Vo^nafUT)O@OC?ha!h~6GstqvrkGc^?jV%b;lyx^E%AZBW&mQFW)2Km}>$l zt!~FmU`PLBxe30Lw3Q?MDwlk(>W{$*(|`(5*!$@+yUyyk{{YJ=b?Ns(KcNh|gdxMd zONsff+`1AUky#KW6w%H;&h*(}K!9nte8UA%$~nl6sQTy|k|t>`0}oq&6UOJx|LWQw zJyw)^{FzW?Ou%#ntYFl#eRG3fwxiokrcwJnfQnA2XH7}`-ZhS~T#T1v)w(Km?PIh| z!E;@F4I(fPe}P@z*1_}bl?qw zL;|I<;aVU68!Se?pUtx(d`?-hl5!nTD7y#PamTV`Dbv&FYuga2^yaCOSw7aAU=ooB zT;#OeAeagc+_1x|K&!5%-d1bAQ4J&aOU@PdcCV;CcM{tKmPDXgogp@)15tB!T*}Pu z_AdT236?NJdj0NOeVRrrizt<`;yd9sqMW!>v2GeTRz2nfJ&o4+do!OJBiO&Dr0@gIY-jWv7Z9icwrk}FsPrsG7H?V%fb$=%H7FOB6q(hAlpuZA%MhL^)Y>X!ICz#qw5jzFI z&)JHA(P%PtVOl5I*?RmT0a4fGYN|R(td(Z)_7qeuwGFAQ|06_J&-@o+v+3haU$dtrbvx7T$p+qzOlV;m`X~}pRo-Sk_d_{ zv$|s~+|V(7EKucoiZ<$T*0M5-+2c&zu)gJy{~Wl>QwSfiDKb*Ky!>sSr0urUUHIee zyJ4PYpZ#vijG~UAl({uuIF8d4^Ma%hh^h^@h*R z)`0cZ?TcjNH||$Neq?P@LC3FbjE*9PT|yzsTuOW0cLnQp4&A(o@YlHZ}E+t!yms#?9fx%HOGUCxj4J zTnmntD#{rvY<*~L3I5oNc3EmJZ12p8gA}ZU*bKAdjw{bdvR!qA)iB!!0p4YAL`;pG zv=zIST`>{SGo)Rt=U`>7%&^%=>1qgx{iG<)D;}Ga4=d29M?MV%#5Gs?xPwMi&e*I7 zd(vgD(j_YY5L_u<&iS5d2#tzqUNV5{&)`SkGL$9f!qDllo%8T9Ph>@_J4N5o`vbcC zj*Y40%v)~G_oAw+vci8L&YRxSR4!}n_ogYb@{N~LW!r+>j~UbYPasi9O%wh#X+l#U@v z=PkWvEr{wGzmR(EVFUHM%828mMEALVj;}~Ko+ju>l0C{*nA|p3Up7avNU42WY|qc# z_*3ZIne95sm}OA4^}R5p#SO8+^4qZPl}fhZAo!kM!5@ed_|c@6a^q*q-*ZNtjvpI* z)kp#wB9m15fQup4B@j(U`9{?+*;DJ7?N`YW4bIYz^q_Gqz-x8mNLJZg3P^lE>6oe{ z=Rhm`x+Z?!XVkdh?{7mAO|@}T+kXJbve}NmI0>wsUaE@nXY!52LEXad#$@_4O*GQ^ zi6nGAM&>O{Q*Ms*i7JY3jeJD&AHY+&=#m7NH8}N=?Ap8T6%7iJ0zTL$QXB6mPP6p7 zoh7Vno}CW`EboCLLjwI*>7=c*bBSKO&P^_FC~_iH-9DOrw|<*d2gtKC@nlEvXli^$ z#h%^9#Z9Xf#Z4%+3>x$FX@)uyvPE(XHVy%eBG>Sovn}&gbdg?}NF)2vwrl9dpbi+b zSd;x)efnc!Snw?gD{gbH(Z05RvV~H*LKe~cOUoUfptO&2B!0V^`<%O&mFIY18Dv_X z9p#yN4cEZG41mMh_B8WO^Ie@zQZ?iepq@R3C`GO-FO7%Ghdp?0e>J;8nhVV{EU>*_ zQr4m93JVJIXfTzTwg%fj%=w>~MEM*Cz<=0Xt)SBuRy(-(){-X!Zsb247`d-jt#oc& zmFpX(SQ@_m+t{p0_-e;)(Kp_ElkC{UYVk3X@Rx?dR6Np~uQEF5xYwc|lWDg1Acr2D)J4|^}?re-Rq)2x@ro$JO$K!s3Kr|6N zH-bT;K-XFrvmgfW{#t{(RN=t;e{QcLzYc1`~CyJqUR_@ zzzzMdfsJ(-4>S2B+Zq0YBUQ=O^^k*uzC{_5fx57eTs+hU+Pg7U$U2c^y_xa`IH{uC zZXpRY1P9AL7y94Mjf=O$-IybZ;S5g@LF{;GX5Otg5rv=1t%J%wMKFZfq?9rDmA$5J zB=-D%6i!@n$y6}!Nfz+w##tDI2tf}s(w#Cu&wxFIY&+He04)-&>DrDx=g-77>?zl$ z1rftX@dR>}%ldYWg1n@H(E|U*5l7PKme&PZ`PYW3hRb&9T}Os6Kk$tf>jfpoe%J+P zittAT;ab1BwmrCNwp}3JEzClK?(HN)M(__stFptzE%i`Mlu1JM0Ea4)1{nnvF{x-5 z%$G~OKjrkVL=ar{Qs8`~1f&~C_W507lRgry~ zY&5Re{M2-VnPI-=l8fADK0)0w&e4%$8(_1+=`8Y7g{AISwl+O6NQA9SR%nmHCTQ3j zNNTk;q1y}2NSm&p%b*C@=7byzAUluOgzwpudsL>AwFJ}ym7b9pU3w@^&^zEcnl2Nbc(KNrPSzoHSe8G}BvCte0gVF#b=L?}@z0dS&ytd%%kd_AjDEY<;LgHbKB0;n~f=kk;jKBWz*j@0G ztzy|dZ4g8OCg<$xF!YK7n57OzgQ|Sm`FEY{`$+2{x-C25tuAjkR@-nEbl;LJ zSk=;x8R&Pl6yp%o5z0twiNwM1$p;J!#?UPGYmuYMxjlvAR4jMic@H`l_E+H@(Ze)0j3VaM?i`Kz?V!dK>aE5p) zXO)il?u6hc^hx5p@3yRYOl}-dA5~w8G&yUncCh)Nny>|+Tf3RFxNyNcsA5`?Ht(}> zMWdf6o-Oa*4GzEh{01Lyf!>sQ>05*G9MuJTI*htb&UD}6QPXuQB}wao5Cj!m%(Knr zT-q>VwB_!IG);Z1egEyxRPy?Or_FAm*C?1+h7N_I$jKxzS)!|2cm~>iajx z>p<$c-c>cZz|8**%LY?uUC>XTGZh!mYCbLx*8YKCF>%01Rmna=n=;2-mPsWaC^b_Q zvb>;0o?mF(eEo!KaXv}AB6RejL{+5rE7=QQOY=R1|eX0f6 z&k_w1a+e?E_4Kn?yz6R7pPocrc<_pIwwNhFqe-~9#XV1xy757m+OXLw0vh=<#dZ%X z(GBmfQsVGp6^jRj2_&{oJYIHj$=VO^r8~t~ua&1z&$6qIPO{qfjm6!P;yZ1ylm#~R zCYHaC%d6%q9)a4@VQV*!u)5TJV^g_e+g^n)8meG|%K(~=SYo8B#cF(Q2lb0}N^g4s z%KocIjuKvU*>RWLb4yZ>nxPX&==X_nLxP1>ROxb)+d-0)O-FSnJq#i-rCc)Yi=3bj zfZ5=)RXw;q6X84@b?L!l{MoI^2^oxL?t#9$_Vb=)UGF%lE%0w*+sh|5sg0fq?|g6M z@k^{S1>W0Et33vZZ850B$3XKMGFEF%GIlpKlaF-rnZ?ZiydDZz87FuFAPlu#bd%{~ zFU+H3^HIOe1jbg&j#PMHBo z`8GZ00DS{SER~Iuoe`jv1Q&a^`&U$L-DH?zO91uPs^_c^yB#wXda~rdY5WK1Q1MLH zQ3nVwtyd^mu5;*ZhP=Xx$vrGykBdz-dAPaOV)dxd26!manCmCoE2hjN=rjPa&y+_B zK!b%e<3_zY@kEw>a}*+1riIGfbkIyN`_KL_dc>C=5i@4kd|B0~q5gVx$aH0>!3X~C zswmlPgDRAE_yj>rzLy{nj0>J5YBEO?japp(1CUvU*#WnF9CM(11aVp>cmDf(Viubj zU6!wR9j!|dk{n@T$N_~|PNYl7;`STA1H0`sdUy7fn@l1h>Mk7RxBh$?OueXxR&n>h zNww=yeQYFe8CxMcy3Qr@Q#=f$u7NhFm*NLT$jKo#3tdjwH2=l701D(PmVt3Qd*Ey)M>tfE?%!=mqxQKJZXdi z<6E`9Gg>-KZB5j%kbRG=UGPK{j=D#$(~po&kC8( zC5X9>3a75!J)2BMlrbAIS5RjnpS+l?_tKB0}oM`2vAgDK^Z%uH8P_@PFFaE z*E|oFVu`V004+{-)3Xg^?{z(Xi}M z1J_aJ(8KNr2mNjpozMSD&;q^{2!7n38Xh<5FHf3yL;*CFh*7{dA0_prK`Zoxb+K%s zC_2H%o8~@_4+G?bCP*$)$kU;7yB;Dw!^8OpX^=LKIO$v%oMy|<`!`j(ZgL+A@?|D$ z6&20STiDQPe;|a0aDaZtYs)KOXG=DJxpTNaTbADsA52arD9{8hR=K%C0-gAOjtEDG z^x*1Pd$RJ~o_w5@&F(rW`q_1c^$)!@`_w-3!q884`t3cEm%2goV#HWwMbUZX%v8j# z?H$_>>OwU}n8Yye`EPu>G@u}EqCAWKye4cs$O{exC3sHSn}%5wx7G_4E8Le5TIz8V ze{b}SETa8t&Ft?F)po7eQv7_y?Bx+v@^-#G_F(9Ct!;_}V{liDPO8UtjkSr1S4ocl z+i)}X);)kzS$zQ9C_D_3>Y<{BKkW=CG4pm!2ZQ6T;lG7H>MrGcvUR<4`V_rtsHM|w zl>DV&^I;N@p4<3>l=&Y({P3FUH>xc{1w*C0uqWBG%m-%L7XTvHho|`m?=es8qbC$1 z!JWHrx&xXCrC0$CX$d}dP(|a!*Q+TlKlqr1>-p`Nz-ccJ@V=sf-=WQBDgi*JFUfES z0~zoOWtElT(Dcprbd_<&)y&RFrg}cF(*(7xOh>J6<;|qFECnZwqE;)u(-An%LyWNM z;+w-?+3;#OVvEg)c9U&(r&$vY62w-7LTv5(cvZ{izqkQhHCcZOl^pn;=XZ>!syv?+Sd2oO6{&dCRXR$-1voG6STs8i8HA zW`I<*^8{P^Qosk5H zvvBq8Wwqpyvvx+|?t24*=`?PyjT3?ycRo-y`OCAGd;p~ipcLtQj>_jz03OvIukz%_ zhCud&v_G}RKGPo8kD-+V?On`nOVmr5hF%tQj6D8}Z?K9=l?0lE8g#eFTAfnm4rl-1 z=$LHs^L}(iE;h63HhN|06495NqRDSmY&L$t6H?&8cNixxVa531P%iSduK36Z^|&L-Muv& zHHTa$8O_TtE0i{RF^PkdSJx&fR$@}ZogEpTW}fN|C=xZ4OmRnht=mU_eda&@;4AC})i?F&DU)Y#~@q(CLX79Tk4 z9r~q5-<=37IcFsjmBU$<&PNQ+Ku0v?TLO1#yh3cFR1o^6G7R_6NbeF1T8Cwsk7eii zN_{FLKMY~#fy3fjj(lO$A^{3YQKU9Iv*`^eEzs?g8Wvw!s2akeak8iG@#vmnOg6)w zDQviqBH!I%@L4M zoUStoFa2mLjGz3JKO$s7hw>}xw5pXNXlKiuc6dKNW1 zk2t9Fve}IZg8-uMN8rIJi%5GB*uw&ekb~ScAtn1GVXeU0IC7b=h$aoqGZu>$n8=`u zVbCGeIw-(ZLy>?Edwtg=m~6j}h2I9XN1~t#s<9H8p3i@hLYGCfy;fz%3gA{hp`%e0 zo9>>vxGA=Ci#L2R;zJ!mo`H#7w`8OtHzQ>Ee!d+H3MdkoQIt>2QVjvbPOWL>i}JbO zFMybayK7C-0{eVXoQOrnn#2?e;1OCPF-ptqgl6Qi1b$c%GEQ9; zrC~v}-K{OC6zYx|6mZG+x1tHUSE9?=I(|$1(N;sqfOSwq!JUhWv}ffmo*t=m1)q7l zU5YwpOKOOdZF`mM$%G=i@$g0J`AnoLs{>n|dw_jhYyNvBqr`@YAZCvadl?Oloh0fB z$p}tZ;33P4n7&ErVo^)s*D;0v(<=nNJLaBYUA=-3<0fv7eR=`GfTH~~3#0z#2<%bi zs>)UE?8{<)!Hw8NAul|kc8vA`%t*_p^~VBWm)A8_RpZT=(mgrNwc(90zHONfn{q%` zj5+>mT!(>}y2{HcriUU66js@pI_abr4c%nhD43_={#FpUkcX#Ux&+57Z!dKD8p*j& zeQw0zXGh(X{V+eNgbYY3H&7Us{~upW2%l7&)nt9rOUB{Rxj)H%=R_Fw2 zmn!kuZZZ0YDP zCLxz8mBHC{BFH70S+9P=M54E~Lkt?|iKZSTTI)VC0%lY_{tW48V0~_~7{cuORWIL! z5B@z%^|_qfq{q(!ba}0vX{B3*2xeDy3FLfav;LZ-E!hm5+2cqy5E8m^Jx&U9|i z7M72_<*}M~IXkcY6>&rRFr&o@Qq7~A|9YmU8=Tz&m38SC{|n;qUl^@udJ{e$JkSS& zvW)Smy&#KNi>xEAgS6?b#|29xl9k2H&;@U>X){?Cbo4KqHi)Lp7{#jN+M%-gGdW0smx0BQj*inTgqG)PZCr85`GGRY zC<=VlgvkOp;3fl`jg109GE!HfulDwsg@qi{Kg`cn7!FaJQ6=}mtlcCGx z7!%Kkuz+5S2M0gCpdlwh#d++i3#n2VU!rp{%9R>64LhBddCBwgnn*7;hK9*^gYHKZtl>VY;vGX1L}B zFUgOp@K&wUj?gB%ggTRYntS+bt}P!YB-oc05RUCZHf8!dN3sc1I&S6d%qId4C1zd| zSKXTd*6@B1aw8#}G>`>!^-?jD_~pTOQ*sWygO=lVNsNiTtOScfkreq_9fbJI@t&wi zgd%fK-D#@e@YkF0_X}z1{_j3V%eGF=)VgK=&I}l9=q&39=#B=K$-ccJLARYsty`84 z0G4i{;hmN>%|t|Rc@tS{YnqZkJ{7lrANT@{2+T0eUigKgE_Z<$*vWwfbi+)U8lfgo zH|j&>1l+%NVKX~`2Pb6Gxf}i=OWRtC_eE92uJhA<<518v<~qM zNGfg@f5bu6z~l%CllO{VNpe)v#T_5#a;eiE{{U<;aA8&cr zWJ?WU5~{{4GLG)EQh>o%648XbOiLiVzz9ouTGtmqN9 zsM)+g;bq>Trm!yaF2DoKxzfGWK?JLvX7wrY?Uz`rc2sl{soZ3sYFlju%+AILWwivf z@P@jV*~AnrR@cl_#u%g6neskmjU0Bx45t`PL8Za%F9waW!_;v3AyIb77}RoKUTfk4 zmWxk-H<#@VzZpP16~D~yJy>!me$tE+xI^H8Od_mMjbVOZIDUaQ%viH5rvS~hVBo%Y zH!!NmAT%l*Sr&;<7!R74V|4n3l;^2J#-BY!?f8agvRw_!IlTCa1%n}Et(XYzYzxRn zU8~$pqG0>YD$e7OMr^O{6Dx7KLZhVfsLT|~uf%9yj^{G-`-s2X1r%RUvkHpAl|xiV z7^Y;k_?3qk?l+OQ>HyRO``i#lQe~=h@d#2{%#|=PNJke;d2RWMvZ+O_4S~lQhP+vc zGu!wjvLI{O`OSyK3DP=Tv`Uo9^ZuebEm`;f094N5tavXjIGYy*T(F%u2w8wkrg2^_ z0@+wI#K|Av@8J03Ei+PY6u4)lEz< z!VPUyWz9!ms?|V87j^sn#g$f+HmQgZF}swurcMY_*6&Ozn?B$I?)`3I71qWfC?mz* zC0lnxsPuvH2Z=t>B{1wT%i*U7a^Y2P23XP^Gc~YH2p(o!D_bQam5Ex_5!I2qw^3Ub z2b$b#Xw(>TTqc3|ltk3G%XyhY9bSGTvQFxtd2{Xn1RoO9)vL%mSOREQUe9k478Tw+ z#?=eJu(+w99Whw>fi^mq<6}uvelfW~jDEWrWm-1H-O|C#w;qP#9?z0NL z&@R6sC{k;gou!=o#Y&V{nR382 zALcc3EF0cGeMCbGd!Y*;cuVbN0k~$mY?<8Eq%of{wU9bss%oE5S!JN$6apz=BWjW5 z`enTRp@IfQ&e)fVbJRmP+%_)!Ooo$d9rBi1vVk zMS~vY;^bBu$+w6gc14WB>P6D(EY-j;p}qS50BSm=yJGl>#X5i(Ri)xBOLXFziUnUa z)Eo3sbnz*o&e_Nz?g}a#tOAn9d4)ol9lMH!M7&J`9Mt5SbQLXi1O27(n4pS);ZD+N6{v|rvhSpe$j`I~QR@a$Y zg%!Y&Ay?T?a1lz%5Z|a&V;}wf#cZJmFv6(}S@81>#_ha>!v{9qx_EvXejza6FJWl# z%a@8;h~SSLOPBB&ZHuoF$-YTbTwb0Tm8#-cm>a#tvvpWbVM~Jer_8G83$~y_Ta1t{ znXSSMa-R{{Q^D?8y>kJK8k9{f!COoB;wnnLd10`!opUQ-w3L}+aREz?K4OZ{_TmIs zSXLt)tJ(yVHqFD}2Q?JwUN2DC1^q^bZ2sWDConX#E0PceS*jwCsI9XB1;;RNF#wD* z;3A7$_H`8MiDndhY6Mn((@Sm_q2Zi`XDB+Q>_Yxp3ki@_a7vgQMZ`_O?geTs%M7yy zD5Wx%V%1?L>@ecD_NHLCP!BD)2m)Vlnv&IFBqT7Ya^mr?H}3wXf^{xx>!KOXy`c4} zYz!v};_%}-rB?BWY}=WGQzS{yXsk$SFnJ;d_)qx zuA&y3>Y|jTV6IS?(yY|6Wn`$Cv+XpBzT;VVgLpG6EpFxem5%cT_!uo0^A;+}VBU~I z!e)h*RlaTE{KV)uWM23s@*ux#HBWk+HpLCpFjjQ#KY8jag8VS~)y&Cj;h9BW#LM)j z5s2ScnDTU2+Y>PrYOje;x>JZ(n3s)?ArhR9B`(aSi?qbpS7OYh5+;GQU*$2ZSic?4 z1HbArl-mx;d`hT%3v0$ouQM}86P&~zQDVZFe((vQqAkt7Kr;~T=9a+DE-GThOpHDc z*i2iq`k!ll&_|)pWtmx6C4n-mtzvGixrHuYw8X4mBxui4tIi;+9^`b605*4f zmKCV;h`F0B%^EpFL5XS<i!dp)B?vne8#9S6vW*(`CxXOnfDgLFMz6mTv2+= z@*scqW?E3rVGTIFuo`mRJ;wku6`Riz)KMDs0;@H831!;0xF`j7xaP|=QN*QKQ_Mx( zRc0}4g;n^7qGtEzS}Vq}D1|9snQRnL-NujkGqBI`D2a06fmC}hRJ2bHcLE%++Rq|E0nvB)m~%O)mIhF$ZG1EqxUKjTiuB(8mqrV<`;#TxrflQXs`+@+7se>3`Ck6?U z+|(-qk1cpS>JR~P);r_nn}z3@$mYDo0{0ZP)t)0ocYTcAqOJ<_cFYLo$k?p^01@cC zVl1vb6C-xY0l&y6O zt6ll#V5srpb4ogxXm3#AS8g-7O7U}uYU$lT2NH#c$t%Alv-3Gfi#kUrv^nk1^A;Nm z)LzMEkBR+#;kOyw*0qeq46#+>Z3U{K`F9;a&{>P71W4rHdz5X3`w(n3ajA8=Q1O~8 zyaxQt1e?XYgEbiTredE=#&H*AWDP<#W>G*?1G-h>?uAw>Jo6Ql*tXrwVE!UfR(ZZ7 zYc1Ab7n0Twa7Ed*uB9~!G{JTUIXuC>6U0WU>FH-moi~o4Y@zYFmV#d*FPVXeDYuP5 z`P{b(H8n*FtV3YrY2M+gQO!gZZuypluNaj9k>EIj6m8cLDS2~I(?-ndDrE^^arS{u z*Aeip9WQ3D0tn@p8#@-sS3&)I?3+ne!INu36fb@=9oj>@H>;!MNJy zQ04(D7lRLBpbfEVP*KpZRsGOtG@)XYWH3GqADD_plN=A2_yDxNSN)H|gACh1n;7I7 z%zFpOhufZ0)YMwJ2?`{q+dSEvLA?9&AmSzXE$TUAV=TkwLLc+7r_ z+(MT8*D+fm>|a^wwMOv9rYmN1KgDDcDv7EDmGe=r-^EGlz^{{Y0Y zc#c zQ2+{+i)CsA1;KDSRIr##&m;oYQ8=JxqA+}l^m&38CpjKp#ATT+F77r`zvQ*xS82>c zKjkt}M|D!$b8@3deKEy{xlToc7lXHOQl%auZCaGW;#F<|wWH=fwyV2Qk5Z*<75gA; zox;V8z^73(g$kuW_vo=*y{{S%rJ>&BLxF%okF`7=>9j>3xiw*D;|N3?wer7=S~2P2OH*dZetTLlUj5Y-%k+(n4WYxR%1dFcK74+@Z8OyC>Y%!A1&d1aKj4!Jh z%ZCK0T6{|JP}kA7D5#V*=C$&5?ISa4!steqv*pSUz+Sud&OEYZf|DBV>>64$ZCuD>6dh9cC) z=urv|`j(G&?6+UUO1uNEBc)YwqVN)sg=5~ifb!fRT?vDj`L@6Qz(B<-jlb-~wpjHR zM7qH)6foB$Zm-Qp9)?{5KNf!bh%L7;Xbe7M0-f#(&0^*u<_#7ATjmu)v(%_rywoF$ zm&5K{PyzFA`{o!?sMX^4`Invm1F$jtLnfv|9cA?`$;u*&A=`psy_3rWCz}3#)mVyh znvURN15vcFFbEwVsHnhODb2L>>VNedh*@9UYpC02KhyzcmywNN<0r(j;HP&Rk(ax^ zVuW6K;st?J3L{!q@WH1i^A(I#bDQH+b&}?8+l{RrC6=r&m;j(x7kP;&^F+38f##!s zEdo6b?p%Xy3B&-#j^Lm`e&1)Nzf7j$6;t~j|NfSP`ttSoI1QLR#{=>R>R|>h}twvSltBC4%0Fv1R3*lI0 zzO*rByDgZ@J9%`Dy_K8*&jH>NdEHM&kQ}Zuz_fTSx15vtO zt1_O;+Q$iXYUl1?j>##n4{;VDjeRfiF3yUd5xkHfoh*<_tg@@QWtDCslRqq4SyA%G zOD-Bm#GotZaZn{WF$JeyKF}(Hjbi1|pc-WbZ)_U?w{sDxEY}gx<0a_gHCOz?5mYNs z>ImZwPk4$J5iGATs@5Q3VCv%6h@r1ETNRIGS+FFGrhxHu)H;}?ihI)P{s&zzr-BQkKP=O5w#9yB8Z z!>PxaxWFB~!faaZGt^r7#JxJJEz6@7q3Tv#hQR*-f36!w`VMg&8PGhyaq@oYaYzgA z%&!+Y+(mfNWf#P)0kw4vmoM7}JAu~ig;3JULbrDi{h)Z(pW`re9_msvqeBy6jqRBL zJ1bsbh^ovsYSbuzTwOfND@A-%EYp01S`62BQKg(BVL;X1E+UHA!NjUJek$M%X{}q# zK%&#ja?HAeYl16KMfsG}juuOI4P9s7#LOtw zI)W4fX4!Q~W$`dCF69x7jI}b-n`H}K6?%cRVdhjVhWok5QbJmCRKa#xETY|&9Vk(l z((V`|{{T{hl6gm#(+RWGEkOY2h6P;?#I~9YuHYzfC?Uk4$qKe1)F@!$VU)cyn*cjy zfikr|-RwJ=g2gWYPVQnJsa%KNQQ5qUQp;5X02O3YW>;n6F|a}|Dp=C*{KuiDEem?o z`DYgdCBYBBFoboUM*jdZiy3PAORBu@7X&#HsGFmQ?J3x=52*Z=)67!d1W+==#6QhS z5C}RU-^4>c?=fzbh6m31C^D2N@=!e+^2Gp927;;IYzvsK?94y_H5{PJsG{#@ycm58 z_=;_LS~#03n%%*!bFq!4{6N3ga*(U_6s%Pj8n44?%O+{~zO?A(R!Bv(rn<>|nfwJ;hnSf<)@lvuSp-bQT<@f>GImf&t2&><2 zDiY32yp0sYxZS&3VbeaP%cqG-B$V@|on=lwNZl+3+A52EROE9IxuPS&SxaiN+#(T0 z0+#fh4x{>K4duvnU4@mM^-DSNw=FtVaCi@^>sE-cc>J=H(Q=?khwS znZiA~eLzMukC27RzmhisJ8|Lv%+Hrpys-c`DO*srdN&4_d4Q2{!kN5 zOPcOJtXK;!HbaZxMhN5Xe?nDVW$G=D3w^u(@VLr8u5MTZb{{2dEZs z3@5}x)1sxDP-i-eNQ!eF&)NR~c$cIFVZ`Eu=H<>put%(=FHi-img-xQb6z2A3=8Dfn%tGqQt6amdP~@mc;$mgrQl=M&QQxUp z?hm1HrLnnyA$7j>(=n=8W&~japHPMTk@_OPh>R@lvQ&dq=jKrj6}57lexb94%30=D zXjaK*!>NFnnh8e)3CATL#AVA-w|O4AwxJsljOH43bjph;7Q!tl{6tG>gH<`(isB7A zN&|qwN_i0v78QaB^mAO#1kAx1)G!Vhz~jbcySuqjy$#LHo)z&6Lj?Uqu&WnS8CMNe zRT@0SV+7c3U3h>MtK8)>=3ZE5#MrFenMH8;bfN4$K z9ba=eZhm7_v4$&TsVi*FH)x~aV*c`BD#FVw{37`8vNfepqvSxM?#LG zfi|Bo|14YzI^_b+=VG#q+^X@d|K|%ph>rvc#Eck!{ zII2_(6@9_0j=pA0(!fc6e9Y<_x^WpOFL#+;MK-I#!~oFk6NsTzZReQi*4r#oEGd5j zGdU`c)YaSfE3P8WxSV+|UmFM#<^7aU!`7(1_&?cN;3{sDs3$ z#eD=Bs8|(0kbk(C489J0^{5$T!5z6fsG?-I+jlp#QKDewzAK^P~k@QI1k*S z1Y`&I{{6}_(YBuvVIxf`1_Ei$#xms~RYF%c+%}AyjJ*r(EMaf&1n&>#TolDCrZ5Uy z69`wydy1+ud(5|Hd6Xf^<|7EzTvZqGJr%`q0l-$+IW1CucTBA4IN2F>K+Fzkg$I@*bzAMv?g7<3L&9H3ZIdJ<$P&85diMpR z?zI$*qm#Jld?+q7ETErgh=$xwZHvD3I97!)#*Xy(jVLCSb+X{|5|wG66FqF=TG>#( zMHR^urG^Y`26K!aU^&#-xpxR97Z<6IY)QId2tvg4kv5pZ9XgOHkAbY<5u^3fY*W*Ku3~qd1;I+runFjJoO| zS5d-+nG~=!eZzNHd0+;a7QH@ZGmzZh)OAaiftNXskYkvGPueGVmnU#m@!Z%{E0`E+ z*ecwD{_MHNI3=xa%HN4k1_jahxabSgjmIk6rwkvMM}fd780xqUkVG4E>RohYh6*cu z%R`Xw&UWxWcv&wOsLF;&a7r$c=5z(E?r^X-a6o$Ci~wP=P8oF=4K*27%$4&AE5F2` zPAgLqwi4hrT?>{$4XNaZ#1$yZ9snb5>Nq;W@C2ygLpf9#nQN>}(ok}Fh~P7IEZQ%u zT*@lASQ;m+wk(o=6zhBBd^B@_WJHDFk6#6^PS6uOO?wF+*J{<9k{ zLf9&@n;v4;Qc(3Osw{VlfUq}I5KILMlqD!P=2(Ub=3rNHkCb&wVJT5_AbVP)(6H%_ zd1J4cUd}7i9xIt)8?`ijiD*O5pDgZp!xJN76TH6Vrm2Hrj#eC{qp3nQdYOg5ODbA7 z<^o-Z?nF}A;FQ=XMC?Q;tO{=lBTC$JnaSPE=eRZESmY`qvGX_P zE9Cf>+6?-gFtONtl@g&AThy@_Ji=KHS^offju#TVo>_XbQOA3jeqgUNsW2Tt#YJ3h zP-loD#mvfEu^eG(!MFex+_j<^p|0iRrHZ&#AeQE$*Oi2=f(sZ5^zM}Yp&CCZtpQJ+ z#mXlk=K;#_MgV&}4tOrG54c4Ut{5nZYZB({t|RKVmqTxhjqe=J;7XuLrme+;9dlG`*_@U9Hx?F>rXz#x@3(9&7u6E8*mgRKw(-;$pmm2`s&0 z^AlQH>6o!X5mK;Qx7UzmQN?UBK^2`;Kvv706DV>ea8OG*ocU8;`o3zxyc-CTe zeX{psUS^anolC16kVKsZWt5|B5#FAqkOJGb6sw|9>bE@sTyqRodLflg4B{v&2ksOW zJsXBZG07QWlda4{B5qfDnc5sap}v$z8b(v|s zkg{@dFr^uKOUtQXC*mZx;y(pKgwe^UmZ!uPjZx2;nM`*L(aU5gL!jbiOzvyV`pi!N z#J78SgFzNp4_C|-!ZoLND$MTs+zyJ&rJ(zb^rgf{Mz;$Fv2oJsqiYCRrtn!~`w(hrxz+PdbxP}z~ zEeyW+{6QQ8j;aRLm&kV?Q3fsqN}M@`*yV=ot-{zP)^epg?Kc_)n;d>+7aY?9gaaU6 zpbE=;)DG@blJYX`U9+gq7twl;gO+nKw6*sF2Gn_tuIem}_C<8nFLBE@x*&l=c+3%_ zUCPB*)Iku#R}euQ<~eRUt|Jz=GwCi1mR!rLo`Z8ibbqlaYYV`!=ohM%4u7Q2Ys$M; zDJdwRmJC#=;}J$pO2~yozyr)c7kml3GreXY(R9S&mBT#`ZXg@4dV#&`j?c&u)cM_y z)TPZB1_Lw8NZ(SPsqShz>_sCnN1GTikzH~*lsOJP$D{#7fphajW`(w^mAJyRtBF9) zYcj;QYX&)%u;S^$LTMCc-4lqwEQTx$u7V`#9I_$}CDcv<90WCsLTCnQn2ZQU$tgA+ zO=z?}ceQZ?sOD53Yl_J}U=(eMk(~Y`%GwEZbGVolX6uL?v>Ra4Wntz8tqWfSqE(hl zus5gy(7CW~v0m>Kw|``VO@fxFH3ph*xR?VFDV#(ys<)U$r=~F@Z&9hU z5p68bC{Bu=5!`ufioX%4>R?=>;s)-YnaWrlcOA+H`MOaC zDR_u%A)VC8U+y;xa>9o!489_&yj;%((J&G{)~Zqco|wq-Eh_##(W6niy~0qPVpys~ z$QTBU$2mL1>-;12D~i}ha%rqb0m;EoUPmNmm=21K3JHrKR#Vw4_exWAS>F?RHK>g+p6QHbTJ2Jmvl%5N{skO@ZNOl`(WY#@$KWuz>W~ z{7Rrb=FGu^+LwlZpkoZ<_Y?3acvT<7E3mGjf?X9dX_y>Cx~ZE4 zB(>CP5OyXbe9+1zoK~ftIl9acG9#$|Zf` zmmzQm2bp@+LljC3*OsPWvpFBn9BKfK?LXbcA*X4@`-6x~Cp()!EGg<)V4;|>!-LtN znG5Y0hT;fz> ztNVpt%mVwZE_}qyOvkIo6N(u57CwmYX}%%`O?7c?9A-ODlv`bnCL7dI4?I)@1%DGZ z3^DB-;>Wks6QMLWG;6EsHgd5CZdXBr}W${SKp5GW%dtg%Yg<~2k4hN|woOKUC7 z-EZ7#ZvOGL)?d^Nuq_epD6v+<)K6pyrFe)Li@V0+;)!0L1gf4Pb^Dj}1;D%9#bz{m zj%Jqx{?kx5|2zfn9@ZGMJa8TERob~OMfccE&sg@2Uygzwg6pF;U0q8(TIl&moZLm}$5SSa1^& zE;mZusc)LI2A|0jfmmt7BR;;-imEo70lS(-K#4(LVC+C3d=j9?_YB@eMI`rE zr$jItF*cYR9oASCa!hrIkyi0|gh+=c20!er1azVXjbP8XE#$bwx;`@$6UjBMes?g} z2BNq(`IjA?VqJkxFH*;y(H)%Y7RwxufYMs9^GmoI(Vfz0DI)9n1}7U=MMQk6TtGFgZ5$D)scle$oJOV<=ZKqg>}oD*{OdB2qxy-l zn%rtuy&{EH!k8}Ac$U?p3WgRtmqA6yxGJvU75YIIS-n{=P7U~jWmdS9sL}bG6teV+ zmg@B{pkCu;HsPqsfnv*L#d^dHTT}auT{?r5IGJ(zWxIE&NMV$DnKS#9wzVycVra&3 z4Pl7eTbJFk<>d^p&jABMs`<84ZH+F;{6!)aE0zA(lMc*k`;D-alE+z^FYzCY2zD+| zurON)P(WLWPKn;7cTqhKBG90Lre5QoV^gTO#cNc?q)d7GKvJBL2T?Le!H$=fIr9(| z6|0*oSYtjy*if}PbIfUe8;zq?@f!uUvo8bsT+Pun3(y|pqf|f@!K01FgVW|ry&i-vBY z_yh-OntWnqr2_~cOm@nQwqnyu-f9|Z)og0V4|vyC16v!kfyyYtzS)&`VqpOLo?@$Q zfvJOZuTiVB?9^siS}x(V3h@+7G(@E}7`MxXg-1K?D~Lown&UE;gFm>bZgDM0Vc@tV z=yQl@ej13?uNRSKnp(7#4C7&$N(5!a~`2afKbeV`bP6zcLdPt8Gp|*@(+p* z`R}H^kSieb!r6^QHmOl6hCKk*POj2R{k5DEQEv&I9#@io?AnP<>paTP>8#{@~k zSehkDR;p61l&)r5sDcZ2M7>7fjZ{T5%&Xi-X=7Yg|%{xp@ z*u+~c>gsfQ=m}u0?48lhgW+Q>(;w-0l+}H(lL4ppa z_-9}0EoKF%6GmV*%MWlWWxA9NK=qlqQj|(o3Bbxbnrazq=m!3S!eaps2o9h?xm7Ln zFv|w*XF5BE(c2dQ!_)%2t1W4oC8uh%TPo2zJ7w!I$yt?DIO++u|X^d{4ah=t4y^DzTf;vD;&vC2PlaWV?)e8kRpiA=800ae2`87!LI z8I#wkg-aFPPJZzWpAyWb1qse-XHDI*s)1|^31_$nenrgi)V5=YZ6Gl`P@o_n8AurS5h+m! zsDz{ng#jqJ)|6UADiQ|~2nY&NWP^Y z=GfoNR2lVO2 z6m&+2aRHFc@isxRZC7#1&R_#wB4x`lwrXrdCEnvL7^q6aCU*o3_=VbqMkNhO5nVt$ wKpILcj-V)js1>P1Lda?XP~+)J)HMLPN~z2RY67P)Dhz!OpoHO^!co-!*$upTsQ>@~ diff --git a/packages/docs/blog/2021-08-26-welcome/index.md b/packages/docs/blog/2021-08-26-welcome/index.md deleted file mode 100644 index 69ca7be..0000000 --- a/packages/docs/blog/2021-08-26-welcome/index.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -slug: welcome -title: Welcome -authors: [slorber, yangshun] -tags: [facebook, hello, docusaurus] ---- - -[Docusaurus blogging features](https://docusaurus.io/docs/blog) are powered by the [blog plugin](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog). - -Here are a few tips you might find useful. - -Simply add Markdown files (or folders) to the `blog` directory. - -Regular blog authors can be added to `authors.yml`. - -The blog post date can be extracted from filenames, such as: - -- `2019-05-30-welcome.md` -- `2019-05-30-welcome/index.md` - -A blog post folder can be convenient to co-locate blog post images: - -![Docusaurus Plushie](./docusaurus-plushie-banner.jpeg) - -The blog supports tags as well! - -**And if you don't want a blog**: just delete this directory, and use `blog: false` in your Docusaurus config. diff --git a/packages/docs/blog/authors.yml b/packages/docs/blog/authors.yml deleted file mode 100644 index 0fd3987..0000000 --- a/packages/docs/blog/authors.yml +++ /dev/null @@ -1,25 +0,0 @@ -yangshun: - name: Yangshun Tay - title: Ex-Meta Staff Engineer, Co-founder GreatFrontEnd - url: https://linkedin.com/in/yangshun - image_url: https://github.com/yangshun.png - page: true - socials: - x: yangshunz - linkedin: yangshun - github: yangshun - newsletter: https://www.greatfrontend.com - -slorber: - name: Sébastien Lorber - title: Docusaurus maintainer - url: https://sebastienlorber.com - image_url: https://github.com/slorber.png - page: - # customize the url of the author page at /blog/authors/ - permalink: '/all-sebastien-lorber-articles' - socials: - x: sebastienlorber - linkedin: sebastienlorber - github: slorber - newsletter: https://thisweekinreact.com diff --git a/packages/docs/blog/tags.yml b/packages/docs/blog/tags.yml deleted file mode 100644 index bfaa778..0000000 --- a/packages/docs/blog/tags.yml +++ /dev/null @@ -1,19 +0,0 @@ -facebook: - label: Facebook - permalink: /facebook - description: Facebook tag description - -hello: - label: Hello - permalink: /hello - description: Hello tag description - -docusaurus: - label: Docusaurus - permalink: /docusaurus - description: Docusaurus tag description - -hola: - label: Hola - permalink: /hola - description: Hola tag description diff --git a/packages/docs/docs/getting-started.md b/packages/docs/docs/getting-started.md new file mode 100644 index 0000000..0f3eecb --- /dev/null +++ b/packages/docs/docs/getting-started.md @@ -0,0 +1,208 @@ +--- +sidebar_position: 1 +title: Getting Started +--- + +# Getting Started + +`effect-fc` lets React components be written as Effect programs. Inside a +component body you can yield services, run Effects, subscribe to Effect-powered +state, and still export a normal React function component at the edge of your +app. + +This guide starts with the smallest useful setup: + +1. Install `effect-fc` with its peer dependencies. +2. Create a React runtime from an Effect `Layer`. +3. Wrap your React app with `ReactRuntime.Provider`. +4. Write a component with `Component.make`. +5. Convert it to a React component with `Component.withRuntime`. + +## Install + +Install `effect-fc` alongside `effect` and React 19.2 or newer: + +```bash npm2yarn +npm install effect-fc effect react react-dom +``` + +If your project uses TypeScript, also install React's type packages: + +```bash npm2yarn +npm install --save-dev @types/react @types/react-dom +``` + +## Create A Runtime + +An Effect-FC app needs an Effect runtime. Build one from the services your UI +needs, then share it with React through `ReactRuntime.Provider`. + +For an empty app, `Layer.empty` is enough: + +```tsx title="src/runtime.ts" +import { Layer } from "effect" +import { ReactRuntime } from "effect-fc" + +export const runtime = ReactRuntime.make(Layer.empty) +``` + +As your app grows, add services to the layer: + +```tsx title="src/runtime.ts" +import { FetchHttpClient } from "@effect/platform" +import { Layer } from "effect" +import { ReactRuntime } from "effect-fc" + +const AppLive = Layer.empty.pipe( + Layer.provideMerge(FetchHttpClient.layer), +) + +export const runtime = ReactRuntime.make(AppLive) +``` + +## Provide The Runtime + +At the React root, wrap your app with `ReactRuntime.Provider`: + +```tsx title="src/main.tsx" +import { StrictMode } from "react" +import { createRoot } from "react-dom/client" +import { ReactRuntime } from "effect-fc" +import { App } from "./App" +import { runtime } from "./runtime" + +createRoot(document.getElementById("root")!).render( + + + + + , +) +``` + +`ReactRuntime.Provider` also works with routers. Keep it above your router +provider so route components can be converted with the same runtime context. + +## Write Your First Component + +Use `Component.make` when you want automatic tracing spans, or +`Component.makeUntraced` when you only want the component behavior. + +```tsx title="src/Hello.tsx" +import { Effect } from "effect" +import { Component } from "effect-fc" +import { runtime } from "./runtime" + +const HelloEffect = Component.make("HelloEffect")(function* (props: { + readonly name: string +}) { + const message = yield* Effect.succeed(`Hello, ${props.name}`) + + return

{message}

+}) + +export const Hello = HelloEffect.pipe( + Component.withRuntime(runtime.context), +) +``` + +`Hello` is now a regular React component: + +```tsx title="src/App.tsx" +import { Hello } from "./Hello" + +export function App() { + return +} +``` + +## Use Services + +Components can yield Effect services directly. Define services with Effect, +provide them in your runtime layer, then consume them from the component body. + +```ts title="src/services.ts" +import { Effect } from "effect" + +export class GreetingService extends Effect.Service()( + "GreetingService", + { + succeed: { + greet: (name: string) => `Welcome, ${name}`, + }, + }, +) {} +``` + +Provide the service in your runtime: + +```tsx title="src/runtime.ts" +import { Layer } from "effect" +import { ReactRuntime } from "effect-fc" +import { GreetingService } from "./services" + +const AppLive = Layer.empty.pipe( + Layer.provideMerge(GreetingService.Default), +) + +export const runtime = ReactRuntime.make(AppLive) +``` + +Then read it inside a component: + +```tsx title="src/Greeting.tsx" +import { Component } from "effect-fc" +import { runtime } from "./runtime" +import { GreetingService } from "./services" + +const GreetingEffect = Component.make("Greeting")(function* (props: { + readonly name: string +}) { + const greeting = yield* GreetingService + + return

{greeting.greet(props.name)}

+}) + +export const Greeting = GreetingEffect.pipe( + Component.withRuntime(runtime.context), +) +``` + +## Mount And Cleanup Effects + +Use `Component.useOnMount` for scoped work that should start when the component +mounts and finalize when it unmounts. + +```tsx +import { Console, Effect } from "effect" +import { Component } from "effect-fc" + +const Mounted = Component.make("Mounted")(function* () { + yield* Component.useOnMount(() => + Effect.gen(function* () { + yield* Console.log("Mounted") + yield* Effect.addFinalizer(() => Console.log("Unmounted")) + }), + ) + + return

Open the console, then unmount me.

+}) +``` + +Finalizers are tied to the component scope, so this is the right place for +subscriptions, resources, and other lifecycle-bound Effects. + +## Where To Go Next + +Once the runtime and component boundary are in place, the rest of the library +builds on the same idea: + +- `Subscribable.useAll` reads Effect subscribables and rerenders when they + change. +- `Lens` connects React state and Effect `SubscriptionRef` values. +- `Query` and `Mutation` model async data and user-triggered operations. +- `Form`, `SubmittableForm`, and `SynchronizedForm` help build Effect-backed + forms. + +The important pattern is small and repeatable: write Effect-FC components inside +the runtime, then use `Component.withRuntime` at React boundaries. diff --git a/packages/docs/docs/intro.md b/packages/docs/docs/intro.md deleted file mode 100644 index 9c3cd3d..0000000 --- a/packages/docs/docs/intro.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -sidebar_position: 1 ---- - -# Effect FC - -Welcome to **Effect FC** (as in: Effect **F**unction **C**omponent) – a powerful integration of [Effect](https://effect.website/) with React 19.2+ that enables you to write React function components using Effect generators. - -## What is Effect FC? - -Effect FC allows you to harness the full power of Effect-TS within your React components. Instead of writing traditional React hooks, you can use Effect generators to compose complex, type-safe component logic with built-in error handling, resource management, and dependency injection. - -### Key Features - -- **Effect Integration**: Write your function component logic using Effect. -- **Type Safety**: Full TypeScript support with Effect's comprehensive type system -- **Dependency Injection**: Built-in support for providing dependencies to components using Effect services. -- **Resource Management**: Automatic cleanup and finalization of component resources using the `Scope` API. - -## Quick Example - -Here's what writing an Effect FC component looks like: - -```typescript -export class TodosView extends Component.make("TodosView")(function*() { - const state = yield* TodosState - const [todos] = yield* Component.useSubscribables([state.subscriptionRef]) - - yield* Component.useOnMount(() => Effect.andThen( - Console.log("Todos mounted"), - Effect.addFinalizer(() => Console.log("Todos unmounted")), - )) - - const Todo = yield* TodoView.use - - return ( - - Todos - - - - - {Chunk.map(todos, todo => - - )} - - - ) -}) {} - -const Index = Component.make("IndexView")(function*() { - const context = yield* Component.useContextFromLayer(TodosState.Default) - const Todos = yield* Effect.provide(TodosView.use, context) - - return -}).pipe( - Component.withRuntime(runtime.context) -) - -export const Route = createFileRoute("/")({ - component: Index -}) -``` - -## Getting Started - -### Prerequisites - -Before using Effect FC, make sure you have: - -- **Node.js** version 20.0 or above -- **React** 19.2 or higher -- **Effect** 3.19 or higher - -### Installation - -Install Effect FC and its peer dependencies: - -```bash -npm install effect-fc effect react -``` - -Or with your preferred package manager: - -```bash -yarn add effect-fc effect react -bun add effect-fc effect react -pnpm add effect-fc effect react -``` - -### Next Steps - -- Explore the [Tutorial Basics](./tutorial-basics/create-a-document.md) to learn the fundamentals -- Check out the [Example Project](https://github.com/your-repo/packages/example) for a complete working application - -## Important Notes - -:::info Early Development -This library is in early development. While it is mostly feature-complete and usable, expect bugs and quirks. Things are still being ironed out, so ideas and criticisms are welcome! -::: - -:::warning Known Issues -- React Refresh doesn't work for Effect FC components yet. Page reload is required to view changes. Regular React components are unaffected. -::: - -## Community & Support - -Have questions or want to contribute? We'd love to hear from you! Check out the project repository and feel free to open issues or discussions. diff --git a/packages/docs/docs/tutorial-basics/_category_.json b/packages/docs/docs/tutorial-basics/_category_.json deleted file mode 100644 index 2e6db55..0000000 --- a/packages/docs/docs/tutorial-basics/_category_.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "label": "Tutorial - Basics", - "position": 2, - "link": { - "type": "generated-index", - "description": "5 minutes to learn the most important Docusaurus concepts." - } -} diff --git a/packages/docs/docs/tutorial-basics/congratulations.md b/packages/docs/docs/tutorial-basics/congratulations.md deleted file mode 100644 index 04771a0..0000000 --- a/packages/docs/docs/tutorial-basics/congratulations.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -sidebar_position: 6 ---- - -# Congratulations! - -You have just learned the **basics of Docusaurus** and made some changes to the **initial template**. - -Docusaurus has **much more to offer**! - -Have **5 more minutes**? Take a look at **[versioning](../tutorial-extras/manage-docs-versions.md)** and **[i18n](../tutorial-extras/translate-your-site.md)**. - -Anything **unclear** or **buggy** in this tutorial? [Please report it!](https://github.com/facebook/docusaurus/discussions/4610) - -## What's next? - -- Read the [official documentation](https://docusaurus.io/) -- Modify your site configuration with [`docusaurus.config.js`](https://docusaurus.io/docs/api/docusaurus-config) -- Add navbar and footer items with [`themeConfig`](https://docusaurus.io/docs/api/themes/configuration) -- Add a custom [Design and Layout](https://docusaurus.io/docs/styling-layout) -- Add a [search bar](https://docusaurus.io/docs/search) -- Find inspirations in the [Docusaurus showcase](https://docusaurus.io/showcase) -- Get involved in the [Docusaurus Community](https://docusaurus.io/community/support) diff --git a/packages/docs/docs/tutorial-basics/create-a-blog-post.md b/packages/docs/docs/tutorial-basics/create-a-blog-post.md deleted file mode 100644 index 550ae17..0000000 --- a/packages/docs/docs/tutorial-basics/create-a-blog-post.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -sidebar_position: 3 ---- - -# Create a Blog Post - -Docusaurus creates a **page for each blog post**, but also a **blog index page**, a **tag system**, an **RSS** feed... - -## Create your first Post - -Create a file at `blog/2021-02-28-greetings.md`: - -```md title="blog/2021-02-28-greetings.md" ---- -slug: greetings -title: Greetings! -authors: - - name: Joel Marcey - title: Co-creator of Docusaurus 1 - url: https://github.com/JoelMarcey - image_url: https://github.com/JoelMarcey.png - - name: Sébastien Lorber - title: Docusaurus maintainer - url: https://sebastienlorber.com - image_url: https://github.com/slorber.png -tags: [greetings] ---- - -Congratulations, you have made your first post! - -Feel free to play around and edit this post as much as you like. -``` - -A new blog post is now available at [http://localhost:3000/blog/greetings](http://localhost:3000/blog/greetings). diff --git a/packages/docs/docs/tutorial-basics/create-a-document.md b/packages/docs/docs/tutorial-basics/create-a-document.md deleted file mode 100644 index c22fe29..0000000 --- a/packages/docs/docs/tutorial-basics/create-a-document.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -sidebar_position: 2 ---- - -# Create a Document - -Documents are **groups of pages** connected through: - -- a **sidebar** -- **previous/next navigation** -- **versioning** - -## Create your first Doc - -Create a Markdown file at `docs/hello.md`: - -```md title="docs/hello.md" -# Hello - -This is my **first Docusaurus document**! -``` - -A new document is now available at [http://localhost:3000/docs/hello](http://localhost:3000/docs/hello). - -## Configure the Sidebar - -Docusaurus automatically **creates a sidebar** from the `docs` folder. - -Add metadata to customize the sidebar label and position: - -```md title="docs/hello.md" {1-4} ---- -sidebar_label: 'Hi!' -sidebar_position: 3 ---- - -# Hello - -This is my **first Docusaurus document**! -``` - -It is also possible to create your sidebar explicitly in `sidebars.js`: - -```js title="sidebars.js" -export default { - tutorialSidebar: [ - 'intro', - // highlight-next-line - 'hello', - { - type: 'category', - label: 'Tutorial', - items: ['tutorial-basics/create-a-document'], - }, - ], -}; -``` diff --git a/packages/docs/docs/tutorial-basics/create-a-page.md b/packages/docs/docs/tutorial-basics/create-a-page.md deleted file mode 100644 index 20e2ac3..0000000 --- a/packages/docs/docs/tutorial-basics/create-a-page.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -sidebar_position: 1 ---- - -# Create a Page - -Add **Markdown or React** files to `src/pages` to create a **standalone page**: - -- `src/pages/index.js` → `localhost:3000/` -- `src/pages/foo.md` → `localhost:3000/foo` -- `src/pages/foo/bar.js` → `localhost:3000/foo/bar` - -## Create your first React Page - -Create a file at `src/pages/my-react-page.js`: - -```jsx title="src/pages/my-react-page.js" -import React from 'react'; -import Layout from '@theme/Layout'; - -export default function MyReactPage() { - return ( - -

My React page

-

This is a React page

-
- ); -} -``` - -A new page is now available at [http://localhost:3000/my-react-page](http://localhost:3000/my-react-page). - -## Create your first Markdown Page - -Create a file at `src/pages/my-markdown-page.md`: - -```mdx title="src/pages/my-markdown-page.md" -# My Markdown page - -This is a Markdown page -``` - -A new page is now available at [http://localhost:3000/my-markdown-page](http://localhost:3000/my-markdown-page). diff --git a/packages/docs/docs/tutorial-basics/deploy-your-site.md b/packages/docs/docs/tutorial-basics/deploy-your-site.md deleted file mode 100644 index 1c50ee0..0000000 --- a/packages/docs/docs/tutorial-basics/deploy-your-site.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -sidebar_position: 5 ---- - -# Deploy your site - -Docusaurus is a **static-site-generator** (also called **[Jamstack](https://jamstack.org/)**). - -It builds your site as simple **static HTML, JavaScript and CSS files**. - -## Build your site - -Build your site **for production**: - -```bash -npm run build -``` - -The static files are generated in the `build` folder. - -## Deploy your site - -Test your production build locally: - -```bash -npm run serve -``` - -The `build` folder is now served at [http://localhost:3000/](http://localhost:3000/). - -You can now deploy the `build` folder **almost anywhere** easily, **for free** or very small cost (read the **[Deployment Guide](https://docusaurus.io/docs/deployment)**). diff --git a/packages/docs/docs/tutorial-basics/markdown-features.mdx b/packages/docs/docs/tutorial-basics/markdown-features.mdx deleted file mode 100644 index 35e0082..0000000 --- a/packages/docs/docs/tutorial-basics/markdown-features.mdx +++ /dev/null @@ -1,152 +0,0 @@ ---- -sidebar_position: 4 ---- - -# Markdown Features - -Docusaurus supports **[Markdown](https://daringfireball.net/projects/markdown/syntax)** and a few **additional features**. - -## Front Matter - -Markdown documents have metadata at the top called [Front Matter](https://jekyllrb.com/docs/front-matter/): - -```text title="my-doc.md" -// highlight-start ---- -id: my-doc-id -title: My document title -description: My document description -slug: /my-custom-url ---- -// highlight-end - -## Markdown heading - -Markdown text with [links](./hello.md) -``` - -## Links - -Regular Markdown links are supported, using url paths or relative file paths. - -```md -Let's see how to [Create a page](/create-a-page). -``` - -```md -Let's see how to [Create a page](./create-a-page.md). -``` - -**Result:** Let's see how to [Create a page](./create-a-page.md). - -## Images - -Regular Markdown images are supported. - -You can use absolute paths to reference images in the static directory (`static/img/docusaurus.png`): - -```md -![Docusaurus logo](/img/docusaurus.png) -``` - -![Docusaurus logo](/img/docusaurus.png) - -You can reference images relative to the current file as well. This is particularly useful to colocate images close to the Markdown files using them: - -```md -![Docusaurus logo](./img/docusaurus.png) -``` - -## Code Blocks - -Markdown code blocks are supported with Syntax highlighting. - -````md -```jsx title="src/components/HelloDocusaurus.js" -function HelloDocusaurus() { - return

Hello, Docusaurus!

; -} -``` -```` - -```jsx title="src/components/HelloDocusaurus.js" -function HelloDocusaurus() { - return

Hello, Docusaurus!

; -} -``` - -## Admonitions - -Docusaurus has a special syntax to create admonitions and callouts: - -```md -:::tip My tip - -Use this awesome feature option - -::: - -:::danger Take care - -This action is dangerous - -::: -``` - -:::tip My tip - -Use this awesome feature option - -::: - -:::danger Take care - -This action is dangerous - -::: - -## MDX and React Components - -[MDX](https://mdxjs.com/) can make your documentation more **interactive** and allows using any **React components inside Markdown**: - -```jsx -export const Highlight = ({children, color}) => ( - { - alert(`You clicked the color ${color} with label ${children}`) - }}> - {children} - -); - -This is Docusaurus green ! - -This is Facebook blue ! -``` - -export const Highlight = ({children, color}) => ( - { - alert(`You clicked the color ${color} with label ${children}`); - }}> - {children} - -); - -This is Docusaurus green ! - -This is Facebook blue ! diff --git a/packages/docs/docs/tutorial-extras/_category_.json b/packages/docs/docs/tutorial-extras/_category_.json deleted file mode 100644 index a8ffcc1..0000000 --- a/packages/docs/docs/tutorial-extras/_category_.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "label": "Tutorial - Extras", - "position": 3, - "link": { - "type": "generated-index" - } -} diff --git a/packages/docs/docs/tutorial-extras/img/docsVersionDropdown.png b/packages/docs/docs/tutorial-extras/img/docsVersionDropdown.png deleted file mode 100644 index 97e4164618b5f8beda34cfa699720aba0ad2e342..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25427 zcmXte1yoes_ckHYAgy#tNK1DKBBcTn3PU5^T}n!qfaD-4ozfv4LwDEEJq$50_3{4x z>pN@insx5o``P<>PR`sD{a#y*n1Gf50|SFt{jJJJ3=B;7$BQ2i`|(aulU?)U*ArVs zEkz8BxRInHAp)8nI>5=Qj|{SgKRHpY8Ry*F2n1^VBGL?Y2BGzx`!tfBuaC=?of zbp?T3T_F&N$J!O-3J!-uAdp9^hx>=e$CsB7C=`18SZ;0}9^jW37uVO<=jZ2lcXu$@ zJsO3CUO~?u%jxN3Xeb0~W^VNu>-zc%jYJ_3NaW)Og*rVsy}P|ZAyHRQ=>7dY5`lPt zBOb#d9uO!r^6>ERF~*}E?CuV73AuO-adQoSc(}f~eKdXqKq64r*Ec7}r}qyJ7w4C& zYnwMWH~06jqoX6}6$F7oAQAA>v$K`84HOb_2fMqxfLvZ)Jm!ypKhlC99vsjyFhih^ zw5~26sa{^4o}S)ZUq8CfFD$QZY~RD-k7(-~+Y5^;Xe9d4YHDVFW_Dp}dhY!E;t~Sc z-`_twJHLiPPmYftdEeaJot~XuLN5Ok;SP3xcYk(%{;1g9?cL4o&HBdH!NCE4sP5eS z5)5{?w7d>Sz@gXBqvPX;d)V3e*~!Vt`NbpN`QF~%>G8?k?d{p=+05MH^2++^>gL7y z`OWR^!qO_h+;V4U=ltx9H&l0NdF}M{WO-%d{NfymLh?uGFRreeSy+L=;K`|3Bnl0M zUM>D-bGEXv<>loyv#@k=dAYW}1%W`P<`!PiGcK&G-`-w7>aw=6xwN*)z{qlNbg;3t z^O)Pi!#xywEfk@@yuK+QDEwCaUH{;SoPy%*&Fy2_>@T??kjrXND+-B>Ysz{4{Q2bO zytdB!)SqeR7Z*b#V`wz;Q9sbwBsm#*a%;Z0xa6Pm3dtYF3Ne7}oV>>#H$FLyfFpTc z@fjI^X>4kV`VsTHpy&bqaD992>*x36$&m_u8MOgAKnr zix1C^4Kv*>^8IV-8_jZkZSn%yscddBFqkpaRTTAnS5A$!9KdgBseck^JSIQS`wRWHIZ&85f`i++% z68t8XiOy$@M67#u+Xi6bxpuq+`HWa<2?N@OcnUhX?Fa0ucuMgFJFc-@1+=(NlQ>>F zRDxG-|GOh}P`zp=#(X0xY7b!pCjittaWhLjHXBB#-Po`?sO81ZebXXp;sg3B6U;yT z7ltQRr)1+s9JQ^V!592xtqynFYr$yy)8J4=_Fovpb*N%#EBk3~TNxng@wp@YN7Lqp zrjUU+o-9X*B{;#FfWF+8xsS-jI`K=*Kw`Xfb@RSO_U)QsNHa<|mWk9yQ?OwtR*_xq zmD=jg&|q#_bdPo=j-*xO@t@Lx#ApL+J`iqWlGkq6;4fv@4RCK_O9tc(xtrrh=-c5R z69GA#i8S&gK?|;>DM8&0G0qF?C*`-kOcVP3)1oi%f47pC4CS=HBdpf`E)$Hno3D*LM*Mxsl@|fX(Xf%aXWP!}X9^S#Vk`h=79=r%L^l^YWXw_fRl+4teQ3x9_*k%}TKmP12k&)U zMNC;?1$T%`tp^#EZUUbydm4SOs@A)}3PP>tiL3j_W06pb3vSHu)DJU-0m)ledRGV0 zJ|rcZ1U@_hCyPE6_-wiimvjR3t);y*Qdi`BKX*PP29RBAsD8W-^u0fLrRq zwCLWC=t#&Nb(JimFikS-+jq}=-klKJuPf|#4pY8f?a%e6U2$1>GPfs~QJLAlns4;O zgz6*qdCCdKNu92Gtjo^ob%T4S7Qi-4NMGg1!+m0yH08I3TITyT6-g}m=2u_lckZ^e zq;^$v+pjrNbh#BOPdii=sJ1bq8F?sZTJcTI5o-P0V#bJPYY`?awnv-41^CJh$BpLP z@aNtrc;&0^lO>O1M4Is=8YA9!yo9_AI^mA7`Aw!579-QByLL>P$1D=@r}QPn38D;% zpBWvkXSRS?b^4Pq$yjf%7Lcq#0#b>rLc!^-G|4-BD83fHp~~6CQ_U~u{@(n0go&P^ zDHT6>h=0KJ)xPF^Wh5@tUEbM@gb&7vU*9YcX;|;ESv3bj^6HmWbTMt;Zj&y(k;?)$ z!J2pIQeCULGqRb5%F}d?EV$v(x+Zqs7+Bj<=5FIW5H^? z1(+h@*b0z+BK^~jWy5DgMK&%&%93L?Zf|KQ%UaTMX@IwfuOw_Jnn?~71naulqtvrM zCrF)bGcGsZVHx6K%gUR%o`btyOIb@);w*? z0002^Q&|A-)1GGX(5lYp#|Rrzxbtv$Z=Yht;8I!nB~-^7QUe4_dcuTfjZzN&*WCjy z{r9Sr^dv=I%5Td#cFz>iZ_RSAK?IMTz<%#W)!YSnmft3Nlq~(I`{`Uk-Wm83Cik$W zA>ZEh#UqV*jtmtV`p(`VsJb>H>??z9lR#V(`9^UEGvTix4$!-_w1?L1)oZ^W!E0k* zCB7_q(G~1Q3x6mPdH1`hse+Jq;+?Cw?F&D*LQhHFoFJdd@$J@~sOg%)cymn7a4znI zCjvkBKBOSb2*i~|Qom$yT*r{rc!0nX+M`4zPT|h~`eXtS!4FPTH0(?%$=fr9Tr*nb z(TR6>{L$7k2WHlqIT4J->W-mYgM)ac(R(z56AY2Kiex&W>I$p+&x#bMNS&|p@eWOy zGD7es5=6U#uG^J26B@SERc=i`I+l4_*`E_OxW=&=4|rH=p;$GB!%As!i|~ypyq`M{ zX5L!TI*|QR-pt7Y$irT5b=w9KcWKG5oX;$>v|GNckJ5XfdZ#KHirMyigcqZ9UvabrO{ z8rDp1z0Fr%{{|@&ZFm^_46S#?HL)}=bp45eUvA1gf(mODfe+cGcF$6-ZaI;NvMu;v zcbHrkC+lE z7RwO#m?)*hw^|}s-z?wPDEMJ2%Ne3)j0Dnt?e(@i?bf<+s^BM?g^S5YKU~rg%aeTl zJf0#GyUY|~Y;9SV_?#uV9<{xsFjl^YeW{@1$61GkUgc9Xv6cL@uB^M?d@o7H zHKV^XV(Q|Q%Geas3dw$Jn&atPqxYB>>Ii<#Zv+@N8GYs#vrxfbS_%zJ#18<+55b3yBCV#A}|5J8EAtdUd zn{=~8r&YaM_GB^l@6D_xfSvmbrbJP^&RZ{np(I^~Osf9d>=xz;@EnY?(Egg`%_&Vt zJA2@>$gsV@XFKh@>0z#d4B>B{^W%bCgT;)f6R|f%yK=!bN2w`BOC_5VHz(Q+!7ID^ zl#oQ>nDe2!w&7tLJ8#8wzN%$7@_>{Hh2xdID<0$kb*>G$17$S3grFXLJQ>4!n!>-B zn>~N~Ri%vU@ccS?y8BTR)1#fe2q zlqzp;&z9I1lrZ*4NJn00*0|iPY)Z0d$3NTJ9HNQ+?JI;37?VSbqMkdoqyCsG=yp1B z-3WO8>t^=Fj^?PT?(-0dZ8y_FL2Z9`D!m-7Dgr7r>V~Rm8RQ@w>_PrbFo$N_#jGzx zKC&6u^^M`8cdv1&AJ-O}jSqCR94J?FnYw!JN3(k7cejfuS`7-j*t4GNaKH@|kkrB_uY?<%tF27r;kVj(nzxph1JsFr z#*%R0;+(NAevpx|F8|sz9}SI%^z@E#+KR{}h1fyNXo6z$e*+nNx|qKR4DoCl0?&Q@ zs8_MHOw&gA$VQz4yIo@Zg{!M@m9v_4{_V!x@I>5ZaG$rcOvUm9O0DW9tR>#oyg@l8O!7%+a(wcN zU}SdcI3?TjNeNXmMJ!GUx@tFbszrKU5?ewMLA zJ)^SSUMDXb)yO8<*A&?2bBN&NEk{+9q~*w%k^+OUs)b@Fs#!)#9E-|}*u zWAn}H61Uy!41$}d1d44D;guxTx^kD367XWM%5Dea)6$5&n;))D;D^r~G=m$CqS7L! zmLX|kejC<`PU-rS#;n2Y0*4;&?(ROps&9eVSDoY%G@-4kyG5AX|Fu&1M5Gm0(-Z6v%1@fS9$`LGCB zlH8i;1e!(dUd#1c@G(-^QedB)$yJ~Yke{h3 z$#|*Md8c7)??v!utM3QJT7mN@DE%_r@BYhvf))3qME|n>shVP(03fO0{Iye<3)wv9 zoYDZ$wDak&n*QW`-s6KKDk5X1OQ_ramOCv4gjh1}jy%9GX!s!hq`NW)&%o9y+YrmT z+u!YGVhHBA*{|c;^}Xg)elpF+dMcpHNALqheHQIX<8J#~;Ah^+Dw~L#CynKWfTWCu zCEbY3ybkQ225nUxd$i6(3SN^?}z{r>!_8$YiwX~LE`rzuT=q!8;h{UbMWDGL@VpWm; zZtr3$23sHj`&Co0No!R|5#Vt7{9}j|TwplkHdT=aUeQ*;9XQ2uW1WUTbA%kHwMR|UUq0xTEetKps9KmNYAS5aY+L31z8w-k=r7r5hSK=6A!^nU z8C>n~S?X}?D5`5c5&2wA0cxo;KgFAi4N2T%LF4fWoMQ=CTo>=1mjvBvW;|iPUB>xW z?K5>~6VIpJYo28I)EFl&7dAhqrB6A-(e-)leVf;X*$GA~eVokc6j+rvRq{{fZth{*dW0`N_!2w6Ll9fV z{aJuKFd-zavy0~QH9hD;H%Q(_Zn7nY>AkaeKuL7Q@G02wArkDPH53Qg5JGaH{_ehi z35yHf_=pB1wY&Ak3EZ-^Ml}MxJh6d_Z}jDN7RTDy68ton&H$4=>#b4w904+;t6CcZ zMtV{hLGR06a?g$sZA#7RlKPF4Bqk=}`#oc=#~O;oUX7hbb^NY3f2Nin?(&;E?zVkm zN}OTyV%mP6T5(MT-syZn(K?c9sk)z$K0AQvvk9#%4%)evu)aOXbB;x-*G5ljx|A;$ zZmCV}y(IS$SYPVS%g#3~I9lE#erA)7BgOkZC}~2)7B_BBStEVtr1+0nv{(A%zhmjT zsE;^zwY5(ZCyf%wwr*SJyK_?Gv_p!Oc-8$W?a03T_8q zb=XB6)**gF9AoG(=dN9-4yO7)FI}g2!0UFua`5ASTp*W2K#(fpZHPv2}6 zuI3YRPb*T9uhpKUc zPNT}NbGpABC}F~2UYA?vuN z*c2)mWKvZn<+PL%-Oq3lAhrw_j}+<$Tfvgoo)dRh((_MP7Iz=PwI|1>aObW5-b8qW zI@O0@c{EbVHN5a6k}i4y2?Jh~=Jd-MZnv)h^T1;2CAllrl%EHm`1{XUiW<7g+6{XS z&hVyh5*+TiVaO)+4PE3HcnsJajGx>gwo1EcWg^*Rn0l!#MVM%(Ywui_UjM8Dgspk@ z4`gne14lZ*`698%UOOx^(v_~kQiYj`WkY>(f5KDC5I{-Wi!KoINK)H^9m|SUliD=d zE;N>?`0x*{61(==UBrN}mpsdhOZ2N~I>oQ1avz|nvyfQQW_R6VAnn;IzqlxDB)0_Zw_Csf#5sdmb4LBwIyBk zv$NL*@acUJc4`FtA^-PzoHR zKXm{;9xP9kWW6MEPYuCeDqX@UiY(8GShF|L{-)R4_acdmp+&W~4nBxde z;pI70##wwE$hfIrpx@VQ`Yc>|xSP$S8~WoVKTg5Z*KMWE)Yp>$m>ZoNQ(u!z-#`mL z1jJZHKZ}Tc5Ap^(*KIg6ol~wx)s~So91kdWaF2c{?F58%EDiT9uV&xYWvS{aFS{hE zg--eu{(>bL!0h)=md^{aR(APus_Mr}+}|%Rb(>B&dHn3fw9>d3rkDH6x0-@)^Dkwj zjb75;-8>7gmW&$y_4x~rPX!&!>l3d<-kfo+g{PIl%s;UQ)Y+u z4&z}r;Sd{hco!{2a3}F*4CAcydj7`#V0_iRg%G&NxtQpm=(5VbGfiRW^NoBJ1rPE# zzYktZRk7>`{fdU((V`a+T{&n=cnr4LaS!S|hDOtXWb>_e-LwH+@FmdGw>6+B9J6~} zcBaNb(<-c6&|ghc-%o3xG(Op-q&pXd1CfV zgPNdKX~vGy-LS;4Q=161sLAoMaXGG7weBcT%KmWHZ${+6bC6yehCjqK36LdH>fR!{ z>Xe}eUaWsRp8U1&?E`K@0*oHDY-p{^+u0T&$b)J}|G6C(lSRuN&WgUd(rH=0h9hUz zj|U@1UmNWdbn)SLk^KR_nRxbB`hNKP>?@ocdEL;;1l||Q0{~Zx5N5FT_ z8{|xM9~@McIdv|?#WPK>1b&f`?=bvMO>?(;W^}|VZ|%*&C_rsnS5&E~%`>$1I#;~* zn=Wx?omuI3X^Q4D$;n_~HEv`6`Rwl7C)iTwB5O~BB+$PgQTGE~V(6h;78q+*a8tK* zi)1P_7BY;9ea2|o@l#u>z4b#X%;a|nTq^l*V({7P;k z=t-%I--DL{uv#dVtaWg|q`lNci7#N7sC(@vBesWbHEY@Gb4`DozcU20N<=vl;-%s5 z!WzFm74mydG1Hjwdk!c_6!|q+Noz5>DrCZ!jSQ+Yjti$3pBqeRl}Wv|eimpd!GOY~ zDw@@tGZHFbmVLNc^ilgjPQ1os7*AOkb2*LRb{O-+C97i_n z2I@>^O)#WwMhxr4s;^U&se%2V#g)$UMXcXHU)C<7ih`meC7t?9h6U9|gRL%vjBW=4 zyJ(KaCRlNg`fO6a(x7h==WMvQG|_Skr4D&0<8t`N`#*Y0lJn{f4xjR5Q%h*qiJ!9l z{{3xuZ%nm38N+XqLO_y}X{{=Z1sg+iy?Wk0(xmzIV8KVwj}M}&csjjc2tOdzyInRf zj&mB~+`^C>=hnyxW|Ah^U8Pcl0}jx|K^QWjuTpX%S?_Y({asp@tk2!qmNiJscA|3v`}jyo*ALZ(Rr*ar91T`}p~N<62j4RJ|PDBQI3t8Cdh) z?R$X25f31}sp@&0jG5+in zs$WmohuauhuK4uZ1iNJsy2T@EuDDT=`&$LT=jKS^o}44OK5cA$zAzZq&gS)a(=xC7 zC(q}(#ncl6@1^p;YG?lVnJ)t^7Ky53%ZtMKP6FKlx|zSaeDQD~}Xbf@cZU>-AI+P+4hN52dWFDA$qg=0!5}U9qLoblC z?2V$GDKb=Lv@me&d%DST)ouSOrEAoGtLxcGg1~Kmzbq?}YUf=NjR9D?F9<}N_ZiNa zZhdC>2_z-iy!(9g9{n11i3|~!hxmAYX6z9olmC=&YcsiKI;&XK#&iSd&6&{u1@Hd^ z&}sU>_G+y}Gi-8`-k*Exr{a$>MNGj_u%u$;s_fOjknwYR-qt1G|mi}nQ%CB|0Vp`=0tc2y(3 zJ}XmzSQQ~(SfJW-|mT1TaDmxNCml#nWVyhIvX z5(>8xARd*joOU-U;Dfj+E+nUJC25bpe>!0L^f@BXZEW73UVfjT$=FTfw8u@h@$hDQ zVua*ub@?Dlc%%H2Kt+bYLb>$(@roZ+vrM&so0RO(eTY12?=Hk4*qI39-0yU@%aQU) zh(=Pxi6yISqhKQ$i^SEeyiioo-1GNY25sM+qoj*Y3&qp^8_)87sMwbecGG~;>|9TP zREo(Axioj6Z+vp*b2~Yp&YghcPwB1H+J6C`1#2tPkLCkZ%eJSah9>34C6}Wx52PW# z^-a1fn~bY&PC$SE9!mvprG5JAMZ8#PQ1utYB%g4fm*YwmC=|j!Ynky<|7ZL;!BWr3 zFawY3dr};&T$Ip3YmV+)De<*8`l~v0VwiNIPNf3|&X$o&6@|n6LRM@CjYQR1 zWBH=K@#i3!;27}0=N!39tP9ZWSn8M>14nC%WHmBMuFJAk%Lb z3uC1S9h$5}_+BVizP47z7mQl9&0QY+JB+^dI{s zw`OaYK6by8i7`3&)Phx%c((j7B1YUWiF2MMqu4sv*rJ!i;BLj(fq}XbxPz*4fPY?O z@*Ky#cmpT^|NpZ9uUqz`68dgR9jtzXj=}e&QRIn}pQRT9PLxt|PUrc*i*0b!XrG!5 zn0}>27K&TEtQcrzD<@JD6Z~^YE+@bp^w7O54P0!hf0Y2>E)Q-^2GDnxCg+6##J=z7 z@ngMS&`rDgl6d+JcSuka%Z?(3I;F~=S0|1#j5>jeKEQlh=sBqfv!hBN|;yTWLomu=my`^LYikzJ(>0epsIY)kU18UXtB-3pcSlnHT_D|^@nAOvSZ&U8G z2j{}BU*x=`J<)n1d{C?*L9G7(UY zOa>7`PWnsf0_A36hyo=b^S{8-brz>TuX+X?u5rOaa-i+Qwt#GO{msTqNOcGW+e>Es zB9jlrN(d>)QU5{6)p@F-7=X4^mJ_o0PmD`XJxKX3yEPtUxGs`3c=nmm=R})T1N{pn z-4`5~hgSH{OLb&X7JJ{Kc!m~cw^Px|bf;E_^&_m2-RyF$>hpwb^&OK2x<&5mZY$DQ zM*Ba9X2yg~f2CrRi%7#Gmj8ToW&RX3woB;vaQS~RStNrN_ip=L(D5O`5ARa1*tbl$ zz*z9~cch#eZ(SfXecVU8>@a)YoW^a+0f3~j0Y?^-$NJeZx)){fSvT?~Oz zr|rs5)}M)5nL!oe|LIs_Tje3%Izv_8s~up;gZHa$tJ2apK4+*%@ezaqN}(Z)Knf?w z50}vMb<0<55q_7mTNOQDi&W|)caK!E^KS2+JE#Q+@^xmQv>inXC5o`mvE&$TOke$B zV8GSwhlTR2rzJ#_;)bk${WP%Ih)i=EYN8{o&z8%2I_q?VymrtR;v$zLkjrg{wpYbS zvAcy#5)@jAvZp4FuHHU2=>%7yAaF;Pr;R4Fs{JD~J3=fZ1&XUJg-%A~!KmHC3n)>YIEi}NEb z%--g1St?_*DOh+gnZHtmEkxs@isI}eRrc0wU8l;2b@mCiAM#Nn997Q+LV*)|qbtKQkb_f0o-p5pdd)@GMF*DshM3Aa+3F#`qRIwJ0hm)o|YEL#OaBEakx*CoYj z!aPt=uH3>5{Lo)X0vnhRQ)s3fJD8{|J(JOpEw+)Rk z`bt&Qmfn=@fB#v0H(jRr&%qMgqOh#^u@wR@511#rdFm|rRDW^uR0I;SFNFONvL|T< zNgTUA$F0a)aQgw8fuB6MGPB@qT?~BCYk5+Jsf=?}Mb;HKNTkLenT0K8t8|H}D?|hE zSgX!{rJBv{`q@9kgrWLKN$Lc=(eX|?lLDj zTIgDs2{@)$i(H$~)t&t0ljddg!CF6;h;#+vfsiOq1m6z-@3HjZf9Cwjssl8*? z-Zk;h*SQd?Jne_EnSeuFHFb<4o#^De>LcvXXN-SWl?t8{*wYg3myaD#!ASmyRX(M* zGTP9W!pDwsi#ZmX__)rLPoItw3NlJ2we~Weclgdr7?3%+JE=SOCt;iGP}}vJ5Q|LG zVyV6tvP?5JtW=tF&6vZPw&HPWnzz1x|7JWQiR85>W`0|GOLyooBAJSsXr;fTClQ*2 zaK)sev-vb*PP9gBV5`_Qo%^@(nz4=7wneRMzW!+lzgV`U{S>?Un=WkYC)GrP*^Co~ z39gtoderj4l0kRRPB`Ahk_XC*5YRAEO&?q0Mzru!IeuE^lBSp;^j8_6-!y50K|n_p zGMdRWFh-Fi>Ry&?gYb(4RdA{FOqob;0q^4FiX*<}mB;zWot5?G&X7RqtC)_A4|jTu z$#`}>b~R$z#yqsMjRktG(!I2WS~hnaPgt1B%D#`8tL9}l{0BaIb*@{Pzt#{=K}Oe* zDAsQ#vX=-a{P_Eyl10+;FIVppTs>K45GY321_I8QO(l>aZ1$65njm1IL>Tmd^bv>K zqvaOE2UgLp-Yu%rF$JfIMhMuRr(^h3Hp`{LBoH54u5@YGjy6Wg?Q*O?XEIX6kMCO~ z<_kZcb1u98AU{a8r7g=xIgs_PH3)hJ5I+6utGV-%RP@*Qi)z02$Wuo9%2dn$3FhdS z;i52o@P_mdzh~c5s^ah~8Ps7Wp+76`e#%y5agtQuPd3{4@zh;+PJ;Ul(o51qE_WV^ zg+~a_eJ|*Xi=4jabrA&e^&&@I6=VSbgQoPeA2W5wnF#LY-O>}Ljj#`MCRMaV%vO{76cz-Og(S_6~uR>qnR(*x+nLISCR#;o3%W_6?D!w;_CpEp6{@(I+A~0_7 zs}lPdr=NoC&$L2h;r!KHMBq)8eU7#yV&?{?? z=4x^BMDRXs3k2G`S|TGIzZ0Hg;o-%T^9GFBO*20Lb>W?krt$`*_Y)pIqLTXjE~di< ziI$JBW{M?JgMOp7XK0RqD!` zyjnzWp^?d+&R3;V!S}YBsE3^$ov%4ipg*$x>0&cLpey(^IE*D!A^->G&P+M7+J2(; zwd>Ep{Zo-~HYh#S%R%s38W8{Ca=WoD??Y3{$m(9%xV*`*LEmoP1$uIW>TgrB$+onv z_ndvbMOIqVFhw~TrM%u2A6A4v!m5V5;SK21dr|_++u|ReV)&#sK6$=&(H*ZZXM7U< z=e@Z}9GCKoq)cAQ9euu8+|}amPkIa3BNZHT6d18a1P&$d5_02Ht2I0xoGDxi-;5;j0tI=XFRNl62_x%#|RTOCW zg*`>@ux)y<;|r##9cIl^Q&4#~Z3CkHHz`X=;xCJy_@caXbk+{w{=u4_bgn+6>EKRa z8dA{~?4*L&vu;0?5LGS{cbn;+@q!-7usGB$?e_1K0#gE|Ot9ixD#X(4>uu)f#}~A3 z3@nGY`HD_hpAqWw8U%*?yVSuzvJm;5G+nq@Cd+=}W!n*06lvdQCuXal{9Xs<5I5oC zcw%nh=Wg?~Ugk@T1@^y}Np7w%vxB-A9tdKDt{<)FX^ubm$7SZacAr-%L-a1JwG)#C1c0gU_I^Cd_qciW@*(2ezbRpD6!<$ zQ+C*RGs|w;)ZO`^revsDl);H7f(3E%K@i2Y%eE!3cq&}mnmjtQ*Z=hEWe2W_A^XH?Nys^bJZp5h>K5an>5p6yjNY zREWvikLx;$(K_`V*R=<8<|J@62`31~=7iCV$p6c%Lg1YAc$h-uj ziA#pcUoF0HIj*$$+!IpLE!H*6%e?c8aHZ~W{8>f@QlFmqcJUBtER_3}jheE>hx}mv zf%%k^5;hsmrzrQC;sDn(d(nBjd1K!gR*&*-DQ4;zv;)vaatjg36nGZ?Rq_l;c6lQA zQhH0eWpKygvHd1%l_?G78|(|eJ53Tsg#N4Hvjo0QDebJQL;DKH#&_8b>p%_AdE^@3 zLP(ASqIYgP6n3POQ=*_HPw&ScHtu&nQK-?0+ z8>8|df?xb$oR$yQ8MoZfbQyr0elR$(MT?`-AAlb&Ga4F{{$^zoyi|S#Y2?CZrv_8g zaK5GIo1kiS5{V~y@0UpiT9TI|Vx*t!eaK9kRthIgdFvr#q?-1&t(a;pT=yrB*xZmb zYw8R5P*fjZoZoV$hSYocS7&0+G_-lb)kFC+Q>p$|lmq`}9KRe3H$HuG_y|Xz*Ykic zBp$CVTqZL0olc9!_rqG86IPu{8Iq!Y?GKoMknsM|jFN<nmkWW$R)0;=-v0xAm_otSVoWlb^RlPVJ7p1U|d^4=E>-zP*-Rmrv6} ze|&GPS7f_&uWb1R`Q&)TSwU~0v1a<`-)o6LgtM9rGA0LiJ@Ue`$XcxSFf)nQC^6NuI4*n18HDDl~3>VPbX+k7zOT>bP zjw?xBP7GAvQDt>BQx!=@sw8)=gBtaH=3ce`T>Xns6feL{J+BW8)Q#=W-7NmHaV*F~ z>UmFhh7MkTGy+xsl^XpR;qG_do8Awha7b-nS4*taqw15O=A{`zjy!fUT4*O~Px9G* z&%KU#?o;#N;>89$=?gplzj3XFNdj^3RMIHRL=~;oyK7Quk=^>0g#CAZ(QGGeUGLU* zWPaROHN4T{eRhQdB8Y!9jcDKvnUVfi)uLU;QxRVsz{0S7@3sEf+Q?Ls|HWY4W83@} zlSXj&#g|UeKk!d^F8}ntYOtDT?R^m4cwFr4JG~o|z8Zm1yM5aW({Yy@f~BU11L!v#Td7eeD4W$>lcjaG!42YE?~f3MI=4r% zoOf_vBji`oQ?lj_PxRf%pt#H=+;A1r#K4^1?Htf{euOeDW4^2m#LA%gz+PfcvYKB@ z{l5(10Q&Plb>;K9_`Jn-xRvcD^qdB-b$9yeMaHX`lv9~f(0}6fFn#1NHFDl)U4XX~ zltY}5+&}s?L_h~eET8)X6I%nfweCW?o!6vD{DiG}w?pr%+YfFCFf-a6yId6Ra|pe; zDl_g&Cv!gUMl0Z_t9nh5KE)coN>{ zg&1(j`%gkFBL`Uj=dI12!|rM*w?!U{waw}fJ_H(zB}-9=p|eJ;sfV<_S)YhAe7eDS z{-N^pB#iLATr#NLu{RO!>S;pwW=9=;trCin9igtoOlB&izD{7ASKh z(CzzkugUVut^bL;3>2f~%R9WEhM%m4uk8P(3g_CM>~SJy%}G!J2{hm1T1XXM;$Nx< zvJ>kKg7*&8803!xLR5KkS8}@!TpVFYhM@Q4tv7{NMwN?-8Ku8G-eOxwZUgt(3=6ku z31x;jRmhmiv^Xlb2w?7W5OlqdT#XaE5q-_MGSi%fF7Ds>Ic$5Otyo1~V#Yyo$>HZh zPZe}g8O%F1w+%SQX;*l^WxmvUQ&N5%JYQ;hfA9Y5s8Xx?TASV~=_EpR32`iLB7uC4Lj=X$lBnh3I zAtk%flc?{lm>QjJhL6FP*IzJugn z5FL63L);PtTf0G#iPK0T&aY7OESEL@kG;N>SRc>->6$NM z2j0(*rwMhfDRh0gf$lx8dvfpYx#D2>k7XT8!~5PqGifS5zl^X|?z;dW>t6;)d<#^U zqpau3c!`tBk%yTSPM>VZLXi$PMqeV1LgvwnFtkPxPgjRfvVg7ax0Xr^R;&%IPtWN` zA5SCheRx72%iHFEbeJaExY1ElK+?^&?iS>TAUdMBcMr@A%n{(^2RH+ud)j7?B;I^^ z7rkfli|k(%_b%e@w{>p57WU-$O{YdI+TV+mby<|-#*lt?XmB#+(b(wfKEBm`AY(B} zAZnYZD|DDnpBb>>Q7ZEq95BDq z&uh}x=%dYlNY1S?M_&pI&)5JYVBPFYqUc-8!Vem&)86BebiW?QAtFDVy}0NH26r_( zC_^CO?cMW|=e_!Nd;`}}wIe#2rjbs;ifve-VvB7)GI_S+Nsq$S5JY$8#w^grTZsOb zUyoAYclwpn;7>Ci@(v@DI(;8$4<&tHXlW*;hWslB|D-5>6-zKX+2bVjkSQ8?!9MgK zl=N~I!}?@~Kx<^NrI^q0srRS28Q~9lflYBLXVmE~H-TOQPE~(*4@#$PheP8^EAU}f zm+WSP;g*ei&p2L;l@4F7HzwvVyZLh&&an%n~F2LIKZGsoGGdXNS^^gkCKD8wC{ zOn978*5SMH1Cf!Pil1ixa+!!Ro4xRSy)@zYLPs7Fyinlr`RnQAu(hV9V3Uz}C;^ z-~Y9jxm+%8+u;v_3xQt^9}E{~dg`y&k_IL-boMLUMr9GA>}o>^!B)g*B8rgz=En8c zEK9pm`|y*X?2q_#wSx_BP5}w*8X6!2tqcCUtG(2FdmF>*`x6R~l!xbak@?Q#VXxG=k(YY-43Z+D2$B08B6(u7e=DG~ z*%5MY)s?k;<$!wd{Mz})9SNS2BBclkhNAYGR=Yc9eI@Gtv!DgL3xps?>l1#V*6K|I z@g6biLi{Ynk8TBO%+c=d^WA~VrcEsG)?TmrPdXwVR*O*orI~)IESKLQEv<$euHRV0 zUPn>T+x>w-@sS`pGlN?9>_rh7SfhqmoWUbl!t=cqsYqT!VHZ?eccRCm5S-9?!v&=- z+Jeh%?!&){ecKh#*;pOrlRLHF|528F&6}$#V0U~vK(#a_$BEQ`{zWkUKYenVJE9>7;rk|eSgj=7Uhnz3xm0Qy^^Hui9 zY7}x$DkL_sWncCgDbupk5VZMn-;o*FQ1Mt z2U`xQCp(2}Bg4`+`iC%H9Tf4sY*L~$W{*be^*Y%4MZV8(`SR)b@`qbsSWL5$uZ%GF zjM=n+$!a%_F=CE3MuW3+McnFQ1MtXU-E6p(YrX)pV>Dqtp-+cnY_W zd6t8G6`!Bvka-in3^?bveED>Ixf3Gl)fQG*Y`aenBlz0qAXALrc|ep17;{X9@R-8v zbs8||w|x0@eEHTEGPjTjRUj%~kJ_aIh4Cph9?uqYMFN32jbQ<|1u4J2l3al~zvauP z$SrpD^VHWJ3&Q$?NSEJQ}*?%ctYZ@oc|`spkf7Fia_oS2yFCcrly1 z1B*s!8Iz$^^q*A|3`=7QzC4t=pD)K`zthg^Ep3E}5G|MBU&RLp#o|IPI}ghR$q+u@ zJc5{|sde-oO!?>VTH%FCKcI-(x=FE!a+1wn)^OP3S z(e#KhTllu^uAeWD&p01Gr5^Y5;c%fFa$K72}j&d--OdYuktp4cwI{afY9wWwjpF#aIES^M$8mK{XJxHGf9|=N=EJAbe+>37@0iVs&W_;h*kQQ?1r-@eW+XFHl4c>?#k=+r=%NW>Ns-Y9A@!k)T?e6*WHg!^ zZ*0Y^BoAG^SUXT#3*y5Xg0uru4D^-_w7Ja<7f}O-7K+riTwU5)p$~=j{lfnLnTbiJ ztqb?QEjgM@GJobA=9_=M^Pe-{{NpBw-~L>F?&eA9|5hLVo9&$cPoK+Qju$*3*X&2z2QXa0Jn?Fjrh&=BsW6$h6(K|%>!6&+!pvWwM{YSE z-2liDar?!20&>3lzSo(znGVlddBXUF`MD5V%%BUKj&q%DB? z?(HOR|MMsL%d7R%4K@2w_Mb<|Q^^Uhgn&XATZ;2|AYPH?##y0*@^LUOfpalPq!6JvF303@uKISoQlV}P z;dN)hq%Sw?ryFYaqwE5Y!yq-CZt6$H z#2>jt`9vS*VVD%krkk(_CHEw{n=AF@X8p8Te_pef?agkSTuDb&SHOk(^L9eyq9lor z*!d1Y5E7ImLI=ua!rZa?6dV^A1}7KA)>ih>xDY`v_jyH+B!yE9gV&ovv`fV)MfWhzOU)&HxmiDL)}Pnx zy8SCjpR-l1*1x;@QGd?Z+JU#FR!L$ZLW}^hTu4yAh@yn@#CC>hw6)NkH2692`O@_X zew2#*_2<$AS*3p3tUs^W8yf!5EHv``gq`TK@^r`*qK;7+j`0vpxpx(Yp5vD$g-eM9 zH6}_iz+3_=Lp3!9T4*(@5+yFCWwqN^Fip$M%(wVx5R#GzQ$J5ljbNE2WqEdanY@g$ zu#n9z9G3g#<^B8jjTQHY4oh$-iHqcKEKeMcz4u4{La%=)7%a6{daG(5?Aa&#PYOXf zh(*(6@=2C8MOG9gPWF`SH10itp@(GrL@D{qK-xH#q@m^9#<5jU(+%Vb85aHSqaLE@AhvVfD_AhL| zf45ltDTva)W|!2{Sm z86>a_1xtQO>^f??ee3bw!=voDab>}uYT0#Y%du9`e(>NYhh83JWevavq&4tvcmd#d z;_(p^-~jm#SBQ@2sfOHC z02lPvx8w_uh2!BT_A)%xW$S;~Ki&T6n&S|1S*MR69`L{Ipy8nczO7)95$-tB%3$2U zd*s~dA7J10>>uCu04Os918r@$0P*WMeK>5jMAh@O1%{n}WWo%C-6V9DbE_=dA^3$v z;=&0(5DPo+ljeOMpEF#a$)zYN0HaVf+J~XyG=CjMy90W5)~h{-pd0i8zCK%x`Yd`n zK(4#{!m{D+`j_%&8Bbr$ID<6}(a6Gy{ft2J7Iu7JKjROc7Z9o;&2Z2{K}W6dJXyxG zWPkS|TMhC-R;OdAAK!qUvB@Mux{Nz{)tT7JFeV`qmK^`4#L|A!aY(Z zaXnwzl^OErpkBLubZKJRdfmO5Co{G%2x?@Qb{mG|qB!qc9iQ|^#ydJrbay9CA>?1f zae%Nz^5qyO>Zb!3wO9aiYuC~eZ@1sF542&fQ0zr}DnZvt-Ej2^*wM>@Xpn4X&Ax6x zj^3q_y~U4m$C*7o)K3-1wcLetu|!?CmVkU);Bh*Pg)FRWKEN|l}@@xnE+VKi1y@|grKE@d29@hVW94nddvm$4qF@#)iA38?`kMa(2 zYwTE)C8**5;vjk5s9+S_|0@ts!2e0iPma&S#*51^=serm*Vs>^+9ku}GMrO_zSE2N zLeCi)PjsKS-2Lz4)Ht~L7z+a;>_RyPM?`hUC>Rl?t)a7BdVJ2?r|sk+=H#KEGo(#& zZW*p_5X@n?UdWo5=92Q)dx8-r=HGd__BDaOFbg${6W zaB?IT;lI3HZAe>L8kYUhKZR}xNvu)P^hf_V7!U?*tOKbv=?^6{11&C*FmiFa+Qv+@ z7TuBr{1{sGj^3^$5iF%wRu?7}XP1$wRwqA7M_Ee?L)mJ}^v?7{7=|v>|Al>?_axO0 z`)^@RYQE07_w+vJxzGE)=bpS5m=6p#whwX|*Bx~(JGp+^cBp%CA>X@EzGo?k?$@gM@@XA3JdtC;1BMaq#z94|#pA zSblq+=4^r@uwC3NLk-o3i=cwX==$aF$juKEYOkB@LO z7Ru4DiFqxeK}|GB3gE`WD&pP4-20>QyG~EoQ+-|lFE5`t>DzEHBLy#Z9w@1G%48NW z4Fp{9R${JLU#Kz(+d1sDLs(*P8P~=FjiqaTe}ntR0cRE0Paiud(=7|WF6K9%o~&*` zcr_OfXP{w#T_ye($O-!CJ-WlTZ*J}r_{;R(FYiO2PYLk^_T*9^r?R}9cp$nmk)TxE zLLpP%2;{HliSvXw)n`_ot#Y&k@&p^-=P1m7357@`u3-dd{0QX(?jMi&NMt_owo5|3 z*FRbQ1L`B1uw2QBL9`9cGBndP3JQ)x?&0xgGBwP|*TSTH%uha9w%}Mi_NO)kopsCt z;=F-KhpRpVuFnPrE0P2CaLM~C`vWxqiCa z)@^h2N`CV)-;8g%d}i8HJw2X*q-RD2bs6@z0&|KP{-tbg?pOHJ^6z~N!Rd3wLBO$S z^XlB?I}nt%ipoO$T_Fqr@6Ha(vz?t+i7f@Wz?Im3dH=a+dqg1Lo>xfI-hD;v=LtDD zJ1>w&G!Wb}*b)8+tQFA+`M&-sX8b=H*wGowqLyfuX_U}X1aW3DnI#R-NCv%*Pj!=2C7QHA3)eS_FkwD{$YQAhj%#G^mTu*B-j@lfSkj3 z^poc>p?)_aRqt;;}`z4RAb{PNh?NI+sq*GA2=eIP*7E%lh$h$p-J6 zTv%Li*t$ErJGuTGKHrT7KVTg6w+F^JnMHgnlc8X!Y1rF>9YegHyH#;ht;kU+hIMes8y?Bjt{=Q~0N`J=28lA*{@BFxf?_V00KyGLc zZ!t8Y6OU8Fump1KRzYqU7>Rplr7P*iDnO2RteG&496k42uW71pli)@!mDYiGPEYHz zvss;xd*U^jxlu4~T5g*v6i4L3x!SVMHrp{-e}03%PyuZbbs`2@8wA5c6|oD!%H)ON zCa>2XeDX&?-hZL5qGBvYp@(xG@WX>|a8^aDBtJL&%tK{7aX5v}+zO&DBQ4|A>6bG(`TZ# z#t%;m-+#Mn7y>yUeB1c`r%>W+0;pyQN~bEcll z0dO;&0@kxSo^;(a2ZABC$8ooW$?$@v^dd}$sMr?UB)@sI%E<_*!OaUnH>boQzc3I= zChIHVk~evWKeit(Nmd4vNlu>M0^GN@#H<4M9;G?N{~!BNH))$pu}_A84zGYu^bDV0mm14lT~SlmoA^kU z@1T)|%^uvM@w{{OEZPX<+`iEGr-zhaLeBjQTEF##Q7qsqij4$vZMHe8|-k-8PCs6~sXt@<3^0X#ifJ zYmAfRN$PmA!`syV!4tdP4wiQ$JNkIFA5EYwXd7@ti=auhPDut>XRFK8MPGDqE!Rot zOZ7#ldYDe*h{U9xj6|jkl15M9Z)=MwqKDoV1-v>57)+cRO6SNW92t%_ZKebcv*00+ zh{Ar$c=+b=t|9Dvw_bboV3YM`PQFz24}X2U{pq{gt9n?#t!=0TWWvl*ogvb1``_9| z|2e!*?|%R6`=4`JAP%T!iMFo)0<>GRt-rK#D&;&Syo-d}DBJLr`-F##e(Lg)-+Y}rKBaBHumqDMK=C9B_F zbjmb!IpS1`Fy!t_OJe}Be}msy8?CC9{M~t5XJ==f4P zs|jyy6^trzzoPUe!!NF=Q8+RB7aW)HNzUF>+RWv|JxHUZ;3TB!nc-c^)Ct%BSx?@I zC>MIn3WN9hf46=q+e~h^egS%Cv(3$|&0n#Hg&*X`TF?3?Dpd&cCR-X><=ZmswITz)b-g- zsQHweYoeX&QRlMC-_2D;2Rj!&bSyaXBI%OZ;`2$l?=xI=YWu~J>N!LSaX=2^PR_?Y zO6O0|tG!Yf2EzVVIY`oqq>_V`lNlTz;ewUr2KTbx-AMfU)^1L@B(UeDw;(`zj{5M*?krKO|L&2$Sxi)o#+n zncgm~q*C7@`JV5o_kG^C-n>B|3azO3xLkTX&ia-=$o}21SrCi^<^Wntv@SlM$an>| zsxUEcwian+o^b&tE-nx)J^2$<6;@yh;lnd1EW~VYpZq9n|C6^5U-7CH(@X#7XPTLJ zKi@#X$DiK)B%UQazkWRZDxH+?1vv4(uNrsXACLb#o=jh-0d(WE0gBtrrgil9ojoDK z_m)K9vlLl^4G+uu@ggYx$C95n-TZyT_}C6>yz@4jDbEVmnMmZJ5MywiiSwA^Fu%eQ zWFXG-nKDs_J%8z5*AExwS^6KJ9_KAl*}wZSP#@v z4OsJ))wG(nW!uS4AR6$|o6zL@H#G{q^A5Y_P^u?qMx{r5_@EDnVfSSytzg{ky{~EmH3< zISG2j=?e(ZWr7#Mfn|ZYNne@+1LX0zKLi~0!wK_OHn}Rk>r9v7^$>oWr#54tv1AZ-) zPmP)NvCQ*~NGm>gNhhl73+p!(|lwi6D8DHy?kYV`#y z9(4PM4}qQU18+e6RX9}m*R8G9?XB%apuhNr(K7be4KX`82S9; zP1um;k%fPd+aT(Nf@RqS<9$^802Vc2r7hmE1p3(l5n zFN3N47|aLpO=z)8Zz6H2Y@90&ubB^pOwc@K=IgVpe}2B}e%f=3s3;yM=%W7I)%V}@ z?_OC^bCIH2q)~@h_f;g(&wRW;jn7uC0`eCkB(843&A$kU1W=Vh6fSUp0m0IeD1VGb z*`Hzm16P5V@9nGx&H}@YH?LRaVKp$tDK?L6!6%?$+nhQKC(+=6FASA ztfDNRJ5IEOxf#;nQS*Skp3ey70>pQPL|>Qn=U{ucG)W~i?BC7$>2OXh!k_rsEoXbh zNzvXC>8}s_csvuNkM7B9Alf>ME=h|h8wBoDC*IqJMT<$o*}S9y#1W72hhyx&%XmR< zhTJVfKr9)}2V*$i=@bgs|Hb~}&hY5t@CcRiaQ>xf%0ky1#k8m&pZ7qekgLQm2sKi# zn`0q3%8hX8;S#7^irtCd}uAhI4M}>Md9A9L0MApc=UB@7ro?1Tm%E- z`q;l4pz}jSL=vX$qicb^YdI_X`>p8Sqn)#l2%o|1?C^=Y_K|S89RHys=WdWywjn2P z$juTI`#+3#q`FshJiC;Z426ZTa zH4`AX7TeU6Wo1UVPp@_v+stDzHbY}r8ev;%wY8W0YRjQpkAvwRkNDXqe;i9&0_d*W z{@sxkFg+Y@5AdPDbt&61nZH~))@PP=!`{!ShA-6$Lx_V0#p%#reg`w<}`0l9$Q+4@@8d9r^X0tj&>w3wavvd2eQAFk%q+^7nQ zN7UQ?<>SNov)Ygel`Dx4G>7}J)(i3u5QF>-*sFz1VaKs~&l8Gr{tY;;+;e#0OL1;f z6G3SzMeR~AXP5#DvL4{6yT|%y&wP(p(d3-&clBM}exJ3|cl&$i?lXru;607vKlY17 z6};!}Z22laDw~K1TPqPtEoY_DTH;I2`^y-=`}x(!x1axR|8m##L0{ay>GB>i;Q-jI z&u5mFHU%O6S}>TZv-U7WII&B7V>85i`F!Iq_Z$jN#OP4-=2vC{#)VF_z7~}AMNEjX zXb~6AmCh16e;f{DQj)zpJvn~xX@BoraiD(p9X~(fvysSvGzqH%JV(@AF}%WYIQ=hv z{L}vBu09kS1WK2`c-wC_U&3OKcm3m&U045; z{@&kyEBbpwzCRv~jKCP;5@i}6v*dh6N5aLH$}9Iv8~^40)- diff --git a/packages/docs/docs/tutorial-extras/img/localeDropdown.png b/packages/docs/docs/tutorial-extras/img/localeDropdown.png deleted file mode 100644 index e257edc1f932985396bf59584c7ccfaddf955779..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27841 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 JrC|B>e*nH7H{}2T diff --git a/packages/docs/docs/tutorial-extras/manage-docs-versions.md b/packages/docs/docs/tutorial-extras/manage-docs-versions.md deleted file mode 100644 index ccda0b9..0000000 --- a/packages/docs/docs/tutorial-extras/manage-docs-versions.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -sidebar_position: 1 ---- - -# Manage Docs Versions - -Docusaurus can manage multiple versions of your docs. - -## Create a docs version - -Release a version 1.0 of your project: - -```bash -npm run docusaurus docs:version 1.0 -``` - -The `docs` folder is copied into `versioned_docs/version-1.0` and `versions.json` is created. - -Your docs now have 2 versions: - -- `1.0` at `http://localhost:3000/docs/` for the version 1.0 docs -- `current` at `http://localhost:3000/docs/next/` for the **upcoming, unreleased docs** - -## Add a Version Dropdown - -To navigate seamlessly across versions, add a version dropdown. - -Modify the `docusaurus.config.js` file: - -```js title="docusaurus.config.js" -export default { - themeConfig: { - navbar: { - items: [ - // highlight-start - { - type: 'docsVersionDropdown', - }, - // highlight-end - ], - }, - }, -}; -``` - -The docs version dropdown appears in your navbar: - -![Docs Version Dropdown](./img/docsVersionDropdown.png) - -## Update an existing version - -It is possible to edit versioned docs in their respective folder: - -- `versioned_docs/version-1.0/hello.md` updates `http://localhost:3000/docs/hello` -- `docs/hello.md` updates `http://localhost:3000/docs/next/hello` diff --git a/packages/docs/docs/tutorial-extras/translate-your-site.md b/packages/docs/docs/tutorial-extras/translate-your-site.md deleted file mode 100644 index b5a644a..0000000 --- a/packages/docs/docs/tutorial-extras/translate-your-site.md +++ /dev/null @@ -1,88 +0,0 @@ ---- -sidebar_position: 2 ---- - -# Translate your site - -Let's translate `docs/intro.md` to French. - -## Configure i18n - -Modify `docusaurus.config.js` to add support for the `fr` locale: - -```js title="docusaurus.config.js" -export default { - i18n: { - defaultLocale: 'en', - locales: ['en', 'fr'], - }, -}; -``` - -## Translate a doc - -Copy the `docs/intro.md` file to the `i18n/fr` folder: - -```bash -mkdir -p i18n/fr/docusaurus-plugin-content-docs/current/ - -cp docs/intro.md i18n/fr/docusaurus-plugin-content-docs/current/intro.md -``` - -Translate `i18n/fr/docusaurus-plugin-content-docs/current/intro.md` in French. - -## Start your localized site - -Start your site on the French locale: - -```bash -npm run start -- --locale fr -``` - -Your localized site is accessible at [http://localhost:3000/fr/](http://localhost:3000/fr/) and the `Getting Started` page is translated. - -:::caution - -In development, you can only use one locale at a time. - -::: - -## Add a Locale Dropdown - -To navigate seamlessly across languages, add a locale dropdown. - -Modify the `docusaurus.config.js` file: - -```js title="docusaurus.config.js" -export default { - themeConfig: { - navbar: { - items: [ - // highlight-start - { - type: 'localeDropdown', - }, - // highlight-end - ], - }, - }, -}; -``` - -The locale dropdown now appears in your navbar: - -![Locale Dropdown](./img/localeDropdown.png) - -## Build your localized site - -Build your site for a specific locale: - -```bash -npm run build -- --locale fr -``` - -Or build your site to include all the locales at once: - -```bash -npm run build -``` diff --git a/packages/docs/docusaurus.config.ts b/packages/docs/docusaurus.config.ts index dc37f1d..b8e2078 100644 --- a/packages/docs/docusaurus.config.ts +++ b/packages/docs/docusaurus.config.ts @@ -1,13 +1,13 @@ -import type * as Preset from '@docusaurus/preset-classic'; -import type {Config} from '@docusaurus/types'; -import {themes as prismThemes} from 'prism-react-renderer'; +import type * as Preset from "@docusaurus/preset-classic" +import type { Config } from "@docusaurus/types" +import { themes as prismThemes } from "prism-react-renderer" // This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) const config: Config = { - title: 'My Site', - tagline: 'Dinosaurs are cool', - favicon: 'img/favicon.ico', + title: "effect-fc", + tagline: "Write React function components with Effect", + favicon: "img/favicon.ico", // Future flags, see https://docusaurus.io/docs/api/docusaurus-config#future future: { @@ -15,54 +15,38 @@ const config: Config = { }, // Set the production url of your site here - url: 'https://your-docusaurus-site.example.com', + url: "https://thiladev.github.io", // Set the // pathname under which your site is served // For GitHub pages deployment, it is often '//' - baseUrl: '/', + baseUrl: "/effect-fc/", // GitHub pages deployment config. // If you aren't using GitHub pages, you don't need these. - organizationName: 'facebook', // Usually your GitHub org/user name. - projectName: 'docusaurus', // Usually your repo name. + organizationName: "Thiladev", // Usually your GitHub org/user name. + projectName: "effect-fc", // Usually your repo name. - onBrokenLinks: 'throw', + onBrokenLinks: "throw", // Even if you don't use internationalization, you can use this field to set // useful metadata like html lang. For example, if your site is Chinese, you // may want to replace "en" with "zh-Hans". i18n: { - defaultLocale: 'en', - locales: ['en'], + defaultLocale: "en", + locales: ["en"], }, presets: [ [ - 'classic', + "classic", { docs: { - sidebarPath: './sidebars.ts', - // Please change this to your repo. - // Remove this to remove the "edit this page" links. + sidebarPath: "./sidebars.ts", editUrl: - 'https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/', - }, - blog: { - showReadingTime: true, - feedOptions: { - type: ['rss', 'atom'], - xslt: true, - }, - // Please change this to your repo. - // Remove this to remove the "edit this page" links. - editUrl: - 'https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/', - // Useful options to enforce blogging best practices - onInlineTags: 'warn', - onInlineAuthors: 'warn', - onUntruncatedBlogPosts: 'warn', + "https://github.com/Thiladev/effect-fc/tree/main/packages/docs/", }, + blog: false, theme: { - customCss: './src/css/custom.css', + customCss: "./src/css/custom.css", }, } satisfies Preset.Options, ], @@ -70,81 +54,75 @@ const config: Config = { themeConfig: { // Replace with your project's social card - image: 'img/docusaurus-social-card.jpg', colorMode: { respectPrefersColorScheme: true, }, navbar: { - title: 'My Site', + title: "effect-fc", logo: { - alt: 'My Site Logo', - src: 'img/logo.svg', + alt: "effect-fc logo", + src: "img/logo.svg", }, items: [ { - type: 'docSidebar', - sidebarId: 'tutorialSidebar', - position: 'left', - label: 'Tutorial', + type: "docSidebar", + sidebarId: "docsSidebar", + position: "left", + label: "Docs", }, - {to: '/blog', label: 'Blog', position: 'left'}, { - href: 'https://github.com/facebook/docusaurus', - label: 'GitHub', - position: 'right', + href: "https://github.com/Thiladev/effect-fc", + label: "GitHub", + position: "right", }, ], }, footer: { - style: 'dark', + style: "dark", links: [ { - title: 'Docs', + title: "Docs", items: [ { - label: 'Tutorial', - to: '/docs/intro', + label: "Getting Started", + to: "/docs/getting-started", }, ], }, { - title: 'Community', + title: "Project", items: [ { - label: 'Stack Overflow', - href: 'https://stackoverflow.com/questions/tagged/docusaurus', + label: "GitHub", + href: "https://github.com/Thiladev/effect-fc", }, { - label: 'Discord', - href: 'https://discordapp.com/invite/docusaurus', - }, - { - label: 'X', - href: 'https://x.com/docusaurus', + label: "Example App", + href: "https://github.com/Thiladev/effect-fc/tree/main/packages/example", }, ], }, { - title: 'More', + title: "Ecosystem", items: [ { - label: 'Blog', - to: '/blog', + label: "Effect", + href: "https://effect.website/", }, { - label: 'GitHub', - href: 'https://github.com/facebook/docusaurus', + label: "React", + href: "https://react.dev/", }, ], }, ], - copyright: `Copyright © ${new Date().getFullYear()} My Project, Inc. Built with Docusaurus.`, + copyright: `Copyright © ${new Date().getFullYear()} effect-fc contributors. Built with Docusaurus.`, }, prism: { theme: prismThemes.github, darkTheme: prismThemes.dracula, }, } satisfies Preset.ThemeConfig, -}; +} -export default config; +export default config diff --git a/packages/docs/package.json b/packages/docs/package.json index c7a516c..52feaa1 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -28,6 +28,8 @@ "@docusaurus/module-type-aliases": "3.10.1", "@docusaurus/tsconfig": "3.10.1", "@docusaurus/types": "3.10.1", + "@types/react": "^19.2.15", + "@types/react-dom": "^19.2.3", "typescript": "~6.0.3" }, "browserslist": { diff --git a/packages/docs/sidebars.ts b/packages/docs/sidebars.ts index 2897139..7efd336 100644 --- a/packages/docs/sidebars.ts +++ b/packages/docs/sidebars.ts @@ -1,33 +1,9 @@ -import type {SidebarsConfig} from '@docusaurus/plugin-content-docs'; +import type { SidebarsConfig } from "@docusaurus/plugin-content-docs" // This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) -/** - * Creating a sidebar enables you to: - - create an ordered group of docs - - render a sidebar for each doc of that group - - provide next/previous navigation - - The sidebars can be generated from the filesystem, or explicitly defined here. - - Create as many sidebars as you want. - */ const sidebars: SidebarsConfig = { - // By default, Docusaurus generates a sidebar from the docs folder structure - tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], + docsSidebar: ["getting-started"], +} - // But you can create a sidebar manually - /* - tutorialSidebar: [ - 'intro', - 'hello', - { - type: 'category', - label: 'Tutorial', - items: ['tutorial-basics/create-a-document'], - }, - ], - */ -}; - -export default sidebars; +export default sidebars diff --git a/packages/docs/src/components/HomepageFeatures/index.tsx b/packages/docs/src/components/HomepageFeatures/index.tsx deleted file mode 100644 index c2551fb..0000000 --- a/packages/docs/src/components/HomepageFeatures/index.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import type {ReactNode} from 'react'; -import clsx from 'clsx'; -import Heading from '@theme/Heading'; -import styles from './styles.module.css'; - -type FeatureItem = { - title: string; - Svg: React.ComponentType>; - description: ReactNode; -}; - -const FeatureList: FeatureItem[] = [ - { - title: 'Easy to Use', - Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default, - description: ( - <> - Docusaurus was designed from the ground up to be easily installed and - used to get your website up and running quickly. - - ), - }, - { - title: 'Focus on What Matters', - Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default, - description: ( - <> - Docusaurus lets you focus on your docs, and we'll do the chores. Go - ahead and move your docs into the docs directory. - - ), - }, - { - title: 'Powered by React', - Svg: require('@site/static/img/undraw_docusaurus_react.svg').default, - description: ( - <> - Extend or customize your website layout by reusing React. Docusaurus can - be extended while reusing the same header and footer. - - ), - }, -]; - -function Feature({title, Svg, description}: FeatureItem) { - return ( -
-
- -
-
- {title} -

{description}

-
-
- ); -} - -export default function HomepageFeatures(): ReactNode { - return ( -
-
-
- {FeatureList.map((props, idx) => ( - - ))} -
-
-
- ); -} diff --git a/packages/docs/src/components/HomepageFeatures/styles.module.css b/packages/docs/src/components/HomepageFeatures/styles.module.css deleted file mode 100644 index b248eb2..0000000 --- a/packages/docs/src/components/HomepageFeatures/styles.module.css +++ /dev/null @@ -1,11 +0,0 @@ -.features { - display: flex; - align-items: center; - padding: 2rem 0; - width: 100%; -} - -.featureSvg { - height: 200px; - width: 200px; -} diff --git a/packages/docs/src/css/custom.css b/packages/docs/src/css/custom.css index 2bc6a4c..aa3946b 100644 --- a/packages/docs/src/css/custom.css +++ b/packages/docs/src/css/custom.css @@ -1,30 +1,37 @@ -/** - * Any CSS included here will be global. The classic template - * bundles Infima by default. Infima is a CSS framework designed to - * work well for content-centric websites. - */ - -/* You can override the default Infima variables here. */ :root { - --ifm-color-primary: #2e8555; - --ifm-color-primary-dark: #29784c; - --ifm-color-primary-darker: #277148; - --ifm-color-primary-darkest: #205d3b; - --ifm-color-primary-light: #33925d; - --ifm-color-primary-lighter: #359962; - --ifm-color-primary-lightest: #3cad6e; + --ifm-color-primary: #0b6f74; + --ifm-color-primary-dark: #096469; + --ifm-color-primary-darker: #085e63; + --ifm-color-primary-darkest: #074d51; + --ifm-color-primary-light: #0d7a7f; + --ifm-color-primary-lighter: #0e8085; + --ifm-color-primary-lightest: #109096; + --ifm-background-color: #fffaf1; --ifm-code-font-size: 95%; - --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); + --ifm-font-family-base: + "Avenir Next", "Segoe UI", "Helvetica Neue", sans-serif; + --docusaurus-highlighted-code-line-bg: rgba(11, 111, 116, 0.1); } -/* For readability concerns, you should choose a lighter palette in dark mode. */ -[data-theme='dark'] { - --ifm-color-primary: #25c2a0; - --ifm-color-primary-dark: #21af90; - --ifm-color-primary-darker: #1fa588; - --ifm-color-primary-darkest: #1a8870; - --ifm-color-primary-light: #29d5b0; - --ifm-color-primary-lighter: #32d8b4; - --ifm-color-primary-lightest: #4fddbf; - --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); +.button--primary { + --ifm-button-background-color: #0b6f74; + --ifm-button-border-color: #0b6f74; +} + +.button--secondary { + --ifm-button-background-color: #f4a636; + --ifm-button-border-color: #f4a636; + --ifm-button-color: #1f2526; +} + +[data-theme="dark"] { + --ifm-color-primary: #7ad6d4; + --ifm-color-primary-dark: #5cccca; + --ifm-color-primary-darker: #4cc6c4; + --ifm-color-primary-darkest: #34aaa8; + --ifm-color-primary-light: #98e0de; + --ifm-color-primary-lighter: #a8e6e4; + --ifm-color-primary-lightest: #d5f4f3; + --ifm-background-color: #111c1f; + --docusaurus-highlighted-code-line-bg: rgba(122, 214, 212, 0.18); } diff --git a/packages/docs/src/pages/index.module.css b/packages/docs/src/pages/index.module.css index 9f71a5d..8d4db16 100644 --- a/packages/docs/src/pages/index.module.css +++ b/packages/docs/src/pages/index.module.css @@ -1,23 +1,80 @@ -/** - * CSS files with the .module.css suffix will be treated as CSS modules - * and scoped locally. - */ +.page { + min-height: calc(100vh - var(--ifm-navbar-height)); + background: + radial-gradient(circle at top left, rgba(11, 111, 116, 0.22), transparent 32rem), + radial-gradient(circle at 85% 20%, rgba(244, 166, 54, 0.18), transparent 28rem), + linear-gradient(135deg, #f8f4ea 0%, #eef8f7 48%, #fffaf1 100%); +} -.heroBanner { - padding: 4rem 0; - text-align: center; - position: relative; - overflow: hidden; +.hero { + padding: 8rem 1rem 5rem; +} + +.eyebrow { + color: #0b6f74; + font-size: 0.85rem; + font-weight: 800; + letter-spacing: 0.16em; + margin-bottom: 1.25rem; + text-transform: uppercase; +} + +.hero h1 { + color: #132c2f; + font-size: clamp(3rem, 9vw, 6.75rem); + letter-spacing: -0.08em; + line-height: 0.9; + margin: 0; + max-width: 880px; +} + +.lede { + color: #385256; + font-size: clamp(1.15rem, 2vw, 1.45rem); + line-height: 1.65; + margin: 2rem 0 0; + max-width: 720px; +} + +.actions { + display: flex; + flex-wrap: wrap; + gap: 1rem; + margin-top: 2.5rem; +} + +.cards { + display: grid; + gap: 1.25rem; + grid-template-columns: repeat(3, minmax(0, 1fr)); + padding: 0 1rem 6rem; +} + +.cards article { + background: rgba(255, 255, 255, 0.74); + border: 1px solid rgba(19, 44, 47, 0.12); + border-radius: 1.5rem; + box-shadow: 0 24px 80px rgba(19, 44, 47, 0.08); + padding: 1.5rem; +} + +.cards h2 { + color: #132c2f; + font-size: 1.2rem; + margin-bottom: 0.75rem; +} + +.cards p { + color: #486267; + margin: 0; } @media screen and (max-width: 996px) { - .heroBanner { - padding: 2rem; + .hero { + padding-top: 5rem; + } + + .cards { + grid-template-columns: 1fr; } } - -.buttons { - display: flex; - align-items: center; - justify-content: center; -} diff --git a/packages/docs/src/pages/index.tsx b/packages/docs/src/pages/index.tsx index 0a49f5d..90d003f 100644 --- a/packages/docs/src/pages/index.tsx +++ b/packages/docs/src/pages/index.tsx @@ -1,44 +1,62 @@ -import Link from '@docusaurus/Link'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import HomepageFeatures from '@site/src/components/HomepageFeatures'; -import Heading from '@theme/Heading'; -import Layout from '@theme/Layout'; -import clsx from 'clsx'; -import type {ReactNode} from 'react'; +import Link from "@docusaurus/Link" +import Layout from "@theme/Layout" +import clsx from "clsx" +import type { ReactNode } from "react" -import styles from './index.module.css'; - -function HomepageHeader() { - const {siteConfig} = useDocusaurusContext(); - return ( -
-
- - {siteConfig.title} - -

{siteConfig.tagline}

-
- - Docusaurus Tutorial - 5min ⏱️ - -
-
-
- ); -} +import styles from "./index.module.css" export default function Home(): ReactNode { - const {siteConfig} = useDocusaurusContext(); return ( - -
-- 2.52.0 From 8f8a66f7a9a80570b4b92878e76c0a4cc18c7492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 22 Jul 2026 04:51:05 +0200 Subject: [PATCH 089/133] Add logo --- packages/docs/docusaurus.config.ts | 2 +- packages/docs/static/img/logo.svg | 51 +++++++++++++++++++++++++++--- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/packages/docs/docusaurus.config.ts b/packages/docs/docusaurus.config.ts index 45c8b00..28bc15d 100644 --- a/packages/docs/docusaurus.config.ts +++ b/packages/docs/docusaurus.config.ts @@ -70,7 +70,7 @@ const config: Config = { navbar: { title: "effect-fc", logo: { - alt: "effect-fc logo", + alt: "Effect View logo", src: "img/logo.svg", }, items: [ diff --git a/packages/docs/static/img/logo.svg b/packages/docs/static/img/logo.svg index 9fb9b2b..85294cf 100644 --- a/packages/docs/static/img/logo.svg +++ b/packages/docs/static/img/logo.svg @@ -1,6 +1,47 @@ - - - - - + + Effect View + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- 2.52.0 From 5f3e56096944cf0550ef2194be45bdde2db590aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 22 Jul 2026 04:52:40 +0200 Subject: [PATCH 090/133] Fix docs --- packages/docs/docusaurus.config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/docs/docusaurus.config.ts b/packages/docs/docusaurus.config.ts index 28bc15d..6f8113f 100644 --- a/packages/docs/docusaurus.config.ts +++ b/packages/docs/docusaurus.config.ts @@ -5,7 +5,7 @@ import { themes as prismThemes } from "prism-react-renderer" // This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) const config: Config = { - title: "effect-fc", + title: "Effect View", tagline: "Write React function components with Effect", favicon: "img/favicon.ico", @@ -68,7 +68,7 @@ const config: Config = { respectPrefersColorScheme: true, }, navbar: { - title: "effect-fc", + title: "Effect View", logo: { alt: "Effect View logo", src: "img/logo.svg", -- 2.52.0 From b91729888c1d6f8d6ed3ac37c1229f7f68a99677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 22 Jul 2026 04:56:32 +0200 Subject: [PATCH 091/133] Fix --- packages/docs/docusaurus.config.ts | 14 +++++++------- packages/docs/src/pages/index.tsx | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/docs/docusaurus.config.ts b/packages/docs/docusaurus.config.ts index 6f8113f..f56ea3c 100644 --- a/packages/docs/docusaurus.config.ts +++ b/packages/docs/docusaurus.config.ts @@ -18,12 +18,12 @@ const config: Config = { url: "https://thiladev.github.io", // Set the // pathname under which your site is served // For GitHub pages deployment, it is often '//' - baseUrl: "/effect-fc/", + baseUrl: "/effect-view/", // GitHub pages deployment config. // If you aren't using GitHub pages, you don't need these. organizationName: "Thiladev", // Usually your GitHub org/user name. - projectName: "effect-fc", // Usually your repo name. + projectName: "effect-view", // Usually your repo name. onBrokenLinks: "throw", @@ -42,7 +42,7 @@ const config: Config = { docs: { sidebarPath: "./sidebars.ts", editUrl: - "https://github.com/Thiladev/effect-fc/tree/main/packages/docs/", + "https://github.com/Thiladev/effect-view/tree/main/packages/docs/", lastVersion: "current", versions: { current: { @@ -85,7 +85,7 @@ const config: Config = { position: "right", }, { - href: "https://github.com/Thiladev/effect-fc", + href: "https://github.com/Thiladev/effect-view", label: "GitHub", position: "right", }, @@ -108,11 +108,11 @@ const config: Config = { items: [ { label: "GitHub", - href: "https://github.com/Thiladev/effect-fc", + href: "https://github.com/Thiladev/effect-view", }, { label: "Example App", - href: "https://github.com/Thiladev/effect-fc/tree/main/packages/example", + href: "https://github.com/Thiladev/effect-view/tree/main/packages/example", }, ], }, @@ -130,7 +130,7 @@ const config: Config = { ], }, ], - copyright: `Copyright © ${new Date().getFullYear()} effect-fc contributors. Built with Docusaurus.`, + copyright: `Copyright © ${new Date().getFullYear()} Effect View contributors. Built with Docusaurus.`, }, prism: { theme: prismThemes.github, diff --git a/packages/docs/src/pages/index.tsx b/packages/docs/src/pages/index.tsx index 129f8e7..ab0e5d6 100644 --- a/packages/docs/src/pages/index.tsx +++ b/packages/docs/src/pages/index.tsx @@ -116,7 +116,7 @@ export default function Home(): ReactNode { View on GitHub -- 2.52.0 From 6e7172a24e9192515ed42b3d7c825d6fdd46179e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 22 Jul 2026 05:11:45 +0200 Subject: [PATCH 092/133] Add React Refresh feasibility report --- REACT_REFRESH_FEASIBILITY.md | 345 +++++++++++++++++++++++++++++++++++ 1 file changed, 345 insertions(+) create mode 100644 REACT_REFRESH_FEASIBILITY.md diff --git a/REACT_REFRESH_FEASIBILITY.md b/REACT_REFRESH_FEASIBILITY.md new file mode 100644 index 0000000..b14f4ba --- /dev/null +++ b/REACT_REFRESH_FEASIBILITY.md @@ -0,0 +1,345 @@ +# React Fast Refresh for Effect View components + +Date: 2026-07-22 + +## Executive summary + +Implementing Fast Refresh for Effect View components is feasible, but it is not a one-line change to React Refresh's component detection. + +The current API has two identities: + +1. `Component.make` / `Component.makeUntraced` creates an Effect View descriptor. +2. `.use` later synthesizes the React function component that is actually mounted. + +Vite's React Refresh transform sees the descriptor definition, while React reconciles the synthesized function. In addition, `.use` deliberately caches that function. A newly evaluated module therefore creates a new descriptor, but the mounted function continues to close over the old descriptor. + +The recommended implementation is a small development-only runtime bridge plus a Vite compiler plugin. The plugin should assign stable source IDs and hook signatures to Effect View definitions. The runtime should retain a hot cell for each definition, notify mounted instances after an update, preserve state when the signature is compatible, and remount when it is not. + +My feasibility assessment is: + +- **Safe refresh with remount on every edit:** high confidence, moderate effort. +- **State-preserving refresh with React-like hook signature handling:** high confidence, moderate-to-high effort. +- **A runtime-only or export-detection-only fix:** not sufficient. +- **Bundler-independent support in the first release:** not recommended; establish the protocol with Vite first. + +A production-quality Vite implementation is likely **10–18 engineer-days**, including the runtime bridge, transform, lifecycle tests, examples, and documentation. A remounting proof of concept should take **2–4 days**. + +## Scope examined + +The findings apply to both implementations in this repository: + +- `effect-fc` (Effect 3) +- `effect-fc-next` (Effect 4 beta) + +The two packages differ in Effect runtime details, but their React component identity model is the same. The examples also use the same Vite 8 and `@vitejs/plugin-react` 6 setup. + +## How Effect View rendering works today + +`Component.make` creates a function-shaped descriptor with a `body` property and `ComponentPrototype`; it does not create the React component that will be mounted. See [`packages/effect-fc/src/Component.ts`](packages/effect-fc/src/Component.ts), around `make`, `makeUntraced`, and `ComponentImplPrototype`. + +The relevant path is: + +```text +source definition + -> Effect View descriptor (`body`, options, prototype traits) + -> `.use` + -> synthesized React function bound to an Effect runtime/context + -> optional `React.memo` / async transformation + -> mounted React fiber +``` + +In `effect-fc`: + +- `asFunctionComponent` creates a closure over `this` and `runtimeRef`. +- The closure invokes `this.body(props)`. +- `.use` stores a cached function in `React.useState` and keys its internal cache by reactive Effect services. +- `withRuntime` renders the function returned by `.use`. + +In `effect-fc-next`, `.use` uses `componentRef` and `previousServicesRef` instead of `useState` plus `Effect.cachedFunction`, but it still retains the previously synthesized function when service identities are unchanged. + +This caching is useful during ordinary rendering: without it, React would see a new component type and remount on every parent render. It is also why a newly evaluated module cannot replace the mounted implementation by itself. + +## What the current Vite transform does + +I inspected Vite's development transform for representative files with the repository's actual configuration and dependency versions. + +For a class definition such as `TodosView`, the transform produced the equivalent of: + +```js +var _s = $RefreshSig$() +export class TodosView extends _s( + Component.make("TodosView")( + _s(function* () { + _s() + // component body + }, "", false, () => [ + Subscribable.useAll, + Component.useOnMount, + ]) + ), + "", + false, + () => [Subscribable.useAll, Component.useOnMount], +) {} +``` + +The transform recognizes hook-shaped calls and calculates a signature, but it emits no `$RefreshReg$` call and no refresh boundary for the Effect View. The same was true for `const` definitions created with `Component.make` and for route components ending in `.pipe(Component.withRuntime(...))`. + +This happens because `Component.make(...)` and Effect's `pipe(...)` are not React component patterns known to the transform. Class-style Effect Views also inherit from a non-React base, so React Refresh's runtime heuristic deliberately does not classify them as React classes. + +Even if export registration were forced, registering the descriptor would not be enough. The descriptor is not the type stored in the mounted React fiber; the lazily synthesized function is. + +Consequently, edits currently propagate through ordinary Vite HMR invalidation. Whether they eventually update through an importer or cause a page reload depends on the surrounding module graph and router integration, but React Refresh cannot directly reconcile the edited Effect View family. + +## Runtime experiment + +I ran a focused jsdom/React experiment against `effect-fc`: + +1. Render an Effect View containing `React.useState(0)`. +2. Increment it to `old:1`. +3. Create a replacement descriptor with a body rendering `new:`. +4. Rerender the existing entry component. +5. Copy the replacement `body` onto the descriptor retained by the mounted component and rerender again. + +Observed output: + +```text +after local state update: old:1 +new descriptor, old mounted entry: old:1 +mutated descriptor, stable entry: new:1 +``` + +This proves two things: + +- Module reevaluation alone cannot update a mounted Effect View because its synthesized function retained the old descriptor. +- Updating the retained implementation and scheduling a render can apply new code while preserving React state. + +The experiment was temporary and left no probe files in the repository. + +## Required semantics + +A complete implementation should provide the same safety contract developers expect from Fast Refresh: + +- Apply edits without reloading the page. +- Preserve React state when the hook signature is compatible. +- Remount when hooks are added, removed, reordered, or otherwise become incompatible. +- Recover after a syntax or render error. +- Support both `const` and class definition styles. +- Support local/non-exported child Views, not only module exports. +- Support `make`, `makeUntraced`, `withOptions`, `Memoized.memoized`, async traits, and `withRuntime` / `withContext` entrypoints. +- Work when one View is materialized under multiple Effect service contexts. +- Preserve Effect scopes on compatible edits and close them exactly once on a reset/remount. +- Offer an explicit reset escape hatch equivalent to a refresh-reset directive. + +## Recommended design + +### 1. Add an Effect View refresh transform + +Ship a Vite plugin, for example `@effect-fc/vite`, placed before `react()`: + +```ts +plugins: [ + effectViewRefresh(), + react(), +] +``` + +It should recognize: + +- `Component.make(...)` +- `Component.makeUntraced(...)` +- class declarations extending either factory result +- definitions followed by Effect `pipe` transformations +- aliased imports from both `effect-fc` and `effect-fc-next` + +For each definition it should inject development-only metadata containing: + +- A stable ID based on normalized module path plus local binding. +- A hook signature derived from the generator/body. +- The HMR context or a small generated accept handler. +- Optional reset metadata. + +Detection must be binding-aware rather than matching text. Otherwise unrelated `Component.make` functions, renamed imports, and namespace aliases will cause false positives or missed Views. + +The plugin must instrument local Views as well as exports. React Refresh works on component families, not only refresh-boundary exports, and most nested Views in this repository are consumed through `.use`. + +### 2. Add a development-only hot cell + +The component metadata should connect to a cell retained in `import.meta.hot.data`: + +```ts +interface HotViewCell { + current: Component.Any + revision: number + resetRevision: number + signature: string + subscribe(listener: () => void): () => void +} +``` + +On module reevaluation, the new descriptor updates `cell.current`. If the signature is unchanged, `revision` changes. If the signature changes, `resetRevision` changes as well. + +Keeping the cell rather than only mutating the old descriptor is important for class inheritance and traits. Class-style Views inherit `body` and options through the generated base class, while memoized and async Views alter prototype behavior. A cell can point to the complete new descriptor without attempting to copy an unknown prototype graph onto the old one. + +### 3. Split the development component into a stable shell and keyed implementation + +In development, the function materialized by `.use` should become a stable shell: + +```text +stable shell + -> subscribes to HotViewCell + -> reads current descriptor and resetRevision + -> renders keyed implementation + -> runs useScope + -> runs the current Effect View body + -> owns the View's React hooks and Effect resources +``` + +On a compatible edit, the cell subscription rerenders the shell and the implementation keeps the same type/key, preserving state. On an incompatible edit, the key changes and React remounts the implementation safely. + +This extra fiber and subscription should exist only in development builds. The production path can remain unchanged. + +This design avoids depending on React Refresh's private family maps. It can coexist with the normal Vite React plugin, which remains responsible for standard React components and the error overlay. The Effect View plugin self-accepts only the modules it successfully instruments; unsupported patterns should fall back to normal HMR invalidation rather than accepting an update it cannot apply. + +### 4. Reuse or reproduce hook signatures carefully + +This is the hardest part of state-preserving support. + +The current Vite/OXC transform already sees calls such as `React.useState`, `Component.useOnMount`, and `Subscribable.useAll`, but its computed signature is attached to the descriptor expression rather than the lazily created React function. There is no public runtime API for reading that signature back. + +The robust choices are: + +1. Run a small binding-aware AST pass that computes Effect View hook signatures and feeds them to the hot cell. +2. Integrate with a public transform hook if Vite/React exposes one in a future version. + +Parsing generated OXC text to steal `_s(...)` arguments would be brittle and is not recommended. + +An MVP may conservatively remount on every edit. The next step can preserve state only when the plugin can prove that the ordered hook signature is unchanged. False negatives cost state; false positives can violate the Rules of Hooks, so the algorithm must favor remounting. + +The signature pass should cover: + +- Direct React hooks. +- Effect View hooks named `useX` even when used through `yield*`. +- Imported custom hooks and their identities where possible. +- Hooks introduced by traits such as async rendering. +- An explicit force-reset directive. + +### 5. Keep Effect lifecycle behavior explicit + +Compatible refresh should retain existing `useOnMount` values and open scopes, just as React Fast Refresh retains hook state. Editing the acquisition code inside `useOnMount` should not silently rerun it unless the View resets. + +On a reset, the existing `useScope` effect cleanup should close the old scope and its finalizers. Tests need to account for `finalizerExecutionDebounce` (100 ms by default), Strict Mode, and rapid consecutive edits. The implementation must not create a second close path in the HMR layer. + +## Why smaller fixes are insufficient + +| Option | Result | Assessment | +| --- | --- | --- | +| Teach Vite that `Component.make` is a component factory | Registers the descriptor, not the mounted synthesized function | Insufficient alone | +| Export every View or rename it to PascalCase | May improve boundary heuristics, but does not bridge the two identities | Insufficient | +| Wrap only route entrypoints with a normal React component | Can refresh the entry wrapper, but nested `.use` components still retain old implementations and state may remount | Partial workaround | +| Remove caching from `.use` | New type on every ordinary render; all child Views remount continuously | Incorrect | +| Mutate the old descriptor in a manual HMR handler | Demonstrably updates code and can preserve state, but lacks scheduling, hook safety, class/trait handling, and ergonomics | Useful prototype only | +| Register every synthesized function directly with React Refresh | Possible, but needs stable IDs per materialization, replacement creation for every Effect context, signature propagation, and private/runtime coupling | Viable but more coupled | +| Stable hot cell plus development shell | Explicitly bridges identities and controls preserve-versus-reset behavior | Recommended | +| Redesign Effect Views to be ordinary React component types | Makes React Refresh natural, but conflicts with context binding through `.use` and is a large API/architecture change | Not justified for this feature | + +## Proposed delivery plan + +### Phase 0: executable spike (2–4 days) + +- Instrument one `const` View and one class View in the example app. +- Retain a hot cell in `import.meta.hot.data`. +- Refresh with a guaranteed remount. +- Verify edits through a nested `.use` View and a `withRuntime` / `withContext` route. +- Verify a finalizer runs exactly once on reset. + +Exit criterion: editing render text updates without a page reload, and incompatible edits remount safely. + +### Phase 1: Vite MVP (4–7 additional days) + +- Implement binding-aware AST detection for all public construction styles. +- Add stable shell/keyed implementation support to both Component implementations, or share the dev bridge in a small internal module. +- Cover memoized and async traits. +- Add safe self-accept/fallback behavior. +- Add browser integration tests against the example app. + +Exit criterion: all supported Effect View forms refresh reliably, initially with conservative remounting where signature information is unavailable. + +### Phase 2: state preservation (4–7 additional days) + +- Compute ordered hook signatures. +- Preserve local state for compatible edits. +- Reset for hook-order changes and the force-reset directive. +- Test custom hooks, multiple service contexts, syntax-error recovery, and rapid edits. +- Document expected `useOnMount` and scope behavior. + +Exit criterion: the behavior matches React Fast Refresh closely enough that state preservation is predictable and hook-safe. + +### Later: other bundlers + +Once the runtime metadata protocol is stable, adapters for webpack/Rspack, Rolldown, or other environments can emit the same metadata. Building those in parallel with the first Vite version would enlarge the test matrix before the semantics have settled. + +## Test matrix + +The feature should have real browser HMR tests; unit tests cannot fully validate React Refresh scheduling and module replacement. + +Minimum cases: + +- `const` and class View definitions. +- `make` and `makeUntraced`. +- Root entrypoint and nested `.use` child. +- A View imported through another module. +- Local, exported, default-exported, and renamed Views. +- Direct React state preserved after a render-only edit. +- State reset after adding/removing/reordering a hook. +- `useOnMount`, `useOnChange`, `useReactEffect`, and `useReactLayoutEffect` cleanup. +- Default and zero finalizer debounce. +- Memoized View with unchanged props. +- Async View and Suspense/error recovery. +- Two materializations of the same View under different Effect contexts. +- Reactive and `nonReactiveTags` service changes. +- TanStack Router code splitting used by both example applications. +- Successive edits before the previous refresh finishes. +- Syntax error followed by recovery. +- Production build contains no HMR registry/subscription code. + +## Repository test status during this study + +- `packages/effect-fc`: `bun run test` passed all 20 tests. +- `packages/effect-fc-next`: the existing suite was not green before any implementation work: 4 Component tests failed because mount/cached values were observed twice, and `Query.test.ts` could not resolve the absent `src/Result.js`. The remaining 9 tests passed. These failures are unrelated to this report, but a clean baseline or documented exclusions will be needed before implementing refresh support in the beta package. + +The Vite development server also regenerated `packages/example/src/routeTree.gen.ts` with ordering-only changes while inspecting transforms; that generated file was restored, so this report is the only intended worktree change. + +## Risks and open questions + +### Hook signature ownership + +The plugin must decide which `useX` calls represent React hook state. Effect View intentionally makes React hooks appear inside generator bodies, so relying only on standard React source patterns will remain incomplete. + +### Trait composition + +`Memoized.memoized`, async behavior, `withOptions`, and future traits can replace `transformFunctionComponent` or alter prototypes. The hot cell should point to the newest complete descriptor, and the development shell must define whether a trait change is compatible or forces reset. + +### Development tree shape + +A stable shell plus keyed implementation adds one fiber in development. This is a reasonable tradeoff, but React DevTools naming should make the extra layer understandable. + +### React/Vite version coupling + +The current plugin stack uses Vite's OXC refresh transform and a bundled/simplified React Refresh runtime. Depending on private runtime functions or generated transform text would make the feature sensitive to Vite upgrades. A standalone Effect View metadata protocol minimizes that coupling. + +### Multiple materializations + +One View definition can produce more than one function identity because `.use` is evaluated under different Effect service sets and parent instances. A solution based on direct React Refresh registration must track every materialization. The hot-cell subscription model naturally fans one update out to all mounted materializations. + +### Resource expectations + +Preserving state also preserves acquired resources. Developers will need a reset directive or a documented way to force a remount when they edit acquisition logic and want it rerun immediately. + +## Recommendation + +Proceed with a Vite-first proof of concept using a stable hot cell and a development-only stable shell/keyed implementation. + +Do not begin by patching component-name heuristics or by registering the Effect View descriptor with React Refresh; those changes target the wrong identity. Also avoid coupling the initial implementation to private React Refresh family maps. + +The first milestone should always remount safely. Once browser tests prove update delivery and Effect scope cleanup, add signature-aware state preservation. This order isolates the HMR transport/identity problem from the hook-compatibility problem and gives the project a useful, releasable intermediate result. -- 2.52.0 From c48ca0acd330c5b6d7bbc9f6612267188e5d218e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 22 Jul 2026 06:04:37 +0200 Subject: [PATCH 093/133] Add ErrorHandler report --- EFFECT_FC_NEXT_ERROR_HANDLING_REPORT.md | 175 ++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 EFFECT_FC_NEXT_ERROR_HANDLING_REPORT.md diff --git a/EFFECT_FC_NEXT_ERROR_HANDLING_REPORT.md b/EFFECT_FC_NEXT_ERROR_HANDLING_REPORT.md new file mode 100644 index 0000000..246b1a5 --- /dev/null +++ b/EFFECT_FC_NEXT_ERROR_HANDLING_REPORT.md @@ -0,0 +1,175 @@ +# Effect FC Next error-handling review + +## Verdict + +`ErrorObserver` is a reasonable *opt-in failure notification bus*, but it is +not an application error handler. It neither recovers from errors nor, in +`effect-fc-next`, observes the application's effects by default. Keeping it +as the central error-handling mechanism would make error delivery implicit, +incomplete, and easy to duplicate. + +The better model is: + +1. Handle expected, typed failures where the operation is rendered or invoked. +2. Report unhandled/background failures through one explicit runtime boundary. +3. Let React error boundaries render defects and render-time failures. + +In particular, queries, mutations, and forms should keep their current +`AsyncResult`/validation state as the user-facing error path; a global service +should not replace that state. + +## What exists today + +`packages/effect-fc-next/src/ErrorObserver.ts` provides a context service +backed by an unbounded `PubSub>`: + +- `handle(effect)` uses `Effect.tapCause` to publish every non-successful + cause, then preserves the original result; +- `subscribe` exposes a scoped subscription; +- module-level `handle(effect)` is a no-op when the service is absent. + +This is narrower than the similarly named service in the legacy +`packages/effect-fc` package. The legacy default runtime layer installs an +Effect supervisor that attempts to publish every failed fiber. The Next +runtime's `preludeLayer` contains only `Component.ScopeMap`; it does not +install `ErrorObserver.layer`. There are also no `ErrorObserver.handle(...)` +call sites in `effect-fc-next`. + +Therefore, as checked at this revision, a consumer can create and subscribe to +the service manually, but no failure is automatically delivered to it. It is +not currently an app-wide error mechanism. + +## What is good about the pattern + +- It keeps the original failure intact. Observability does not accidentally + turn a failing effect into a successful one. +- It publishes `Cause`, not only `E`, so defects, interruption, parallel + failures, and traces are not discarded. +- The service is optional, which is useful for a library: applications that do + not want reporting do not need to install a logger/telemetry dependency. +- A scoped subscription has the right basic lifetime shape for a React + component or a root-level reporting worker. + +## Main issues + +### It is opt-in at every execution site + +Wrapping effects with `ErrorObserver.handle` is both easy to forget and +ambiguous. It does not cover failures from `Component.useCallbackPromise`, +`Async.async`, `useReactEffect`, or a fork unless each path explicitly wraps +the relevant effect. Conversely, adding wrappers at multiple levels reports +the same failure more than once. + +### Its type promise is unsound + +`handle` accepts any `E1` then casts its `Cause` to +`Cause`. A value retrieved as `ErrorObserver` can receive a +`ValidationError` or a defect. `Cause` is correctly broader than a single +app-error type, but the generic parameter suggests filtering that the service +does not perform. + +### "Error" and "handle" blur distinct responsibilities + +The code only observes/reports failures; it does not decide a fallback, show a +toast, retry, recover, log, or rethrow. Calling it a handler encourages using +a global side channel for errors that should remain part of local UI state. + +### An unbounded, raw pub/sub is a risky public policy + +A slow or absent subscriber can retain an unbounded backlog. Consumers must +also inspect every `Cause`, decide whether interruption is meaningful, dedupe +retries, redact data, and ensure their own reporting failure does not affect +the application. Those are application policy choices, not library defaults. + +### It is not a React error boundary + +React error boundaries cover render/lifecycle errors in the React tree. They +do not replace reporting of failures from detached/background Effect fibers, +and an Effect failure should not be blindly thrown into render merely to reach +one. These are complementary boundaries. + +## Recommended replacement + +Replace the public observer with a small **`FailureReporter`** service. Make +reporting explicit at the handful of Effect-to-JavaScript runtime boundaries, +not at every business effect. + +```ts +interface FailureReporter { + readonly report: (event: FailureEvent) => Effect.Effect +} + +interface FailureEvent { + readonly cause: Cause.Cause + readonly source: "event" | "effect" | "async-component" | "background" + readonly componentName?: string + readonly operation?: string +} +``` + +The default implementation should be a no-op. Application layers can add +logging, Sentry/OpenTelemetry, a toast dispatcher, or a bounded in-memory +development sink. `report` should be made best-effort at the boundary +(`Effect.ignore`/equivalent after recording its own diagnostics) so reporting +outages never replace the original failure. + +Provide one internal combinator, for example `reportFailure(effect, metadata)`, +which reports only a non-interruption failure and then re-fails with exactly +the original cause. Use it in these adapters: + +- `Component.useCallbackPromise` and any promise-returning event callback; +- `Component.useReactEffect` / layout-effect execution; +- `Async.async` before the promise is handed to React; +- library-created detached fibers whose failures are not represented in an + `AsyncResult`. + +Do **not** use it for query/mutation/form operations whose failure is already +captured and displayed in `AsyncResult`; report only when the caller abandons +or explicitly escalates that result. This avoids a failed request producing a +toast/log and an inline error merely because it was expected. + +At the application root, pair this with a normal React `ErrorBoundary` that +reports render defects and presents the recovery UI. Keep its reporting +separate from the Effect service; optionally normalize both into the same +application telemetry event schema. + +## Lower-risk incremental path + +If replacing the API now is too disruptive: + +1. Do not add `ErrorObserver.layer` to the Next prelude merely to make it look + global. A hidden global bus still has unclear coverage and duplicate-event + semantics. +2. Rename `handle` to `observeFailure` and document that it is an explicit + notification wrapper, not recovery or global supervision. +3. Remove the generic `E`, or expose `Cause.Cause`; alternatively + accept a predicate/decoder that actually filters before publication. +4. Make the queue policy explicit: preferably no public queue, or a bounded, + dropping development event sink with a dropped-event counter. +5. Add tests for: preservation of the original exit, defects, interruption + filtering, a failing reporter, duplicate wrapping, and teardown of a scoped + subscriber. + +## Names + +`ErrorHandler` is inaccurate, and `ErrorObserver` is acceptable only for the +current low-level notification primitive. Recommended names by role: + +| Role | Recommended name | Why | +| --- | --- | --- | +| Application service that sends telemetry/logs | `FailureReporter` | Says it reports an Effect failure without claiming recovery. | +| Wrapper combinator | `reportFailure` | Describes the side effect and retains the original failure. | +| Optional stream-like dev/testing primitive | `FailureEvents` or `FailureEventBus` | Makes the pub/sub nature explicit. | +| React UI component | `AppErrorBoundary` | Uses React's established boundary terminology. | + +I would use **`FailureReporter`** for the replacement. "Failure" is more +accurate than "error" in Effect because it can intentionally retain the full +`Cause`, while "reporter" makes the non-recovery responsibility clear. + +## Decision + +Treat `ErrorObserver` as an experimental diagnostic primitive, not the app +error architecture. Design expected errors into local state, add a +best-effort `FailureReporter` at the few execution boundaries, and use a React +error boundary for render failures. This gives complete, testable coverage +without turning every domain failure into a global event. -- 2.52.0 From ffed4bda40dfa91a09a1c1d2ea1288aca75f6c3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 22 Jul 2026 20:56:42 +0200 Subject: [PATCH 094/133] Improve README --- packages/effect-fc-next/README.md | 181 +++++++++++++++++++++++------- 1 file changed, 140 insertions(+), 41 deletions(-) diff --git a/packages/effect-fc-next/README.md b/packages/effect-fc-next/README.md index 1c613a0..fccb0d5 100644 --- a/packages/effect-fc-next/README.md +++ b/packages/effect-fc-next/README.md @@ -1,56 +1,155 @@ -# Effect FC Next +

+ + Effect View logo + +

-[Effect-TS](https://effect.website/) integration for React 19.2+ that allows you to write function components using Effect generators. +

Effect View

-This library is in early development. While it is (almost) feature complete and mostly usable, expect bugs and quirks. Things are still being ironed out, so ideas and criticisms are more than welcome. +

+ Write React components as typed Effect programs. +

-Documentation is currently being written. In the meantime, you can take a look at the `packages/example` directory. +

+ npm version + Effect View documentation + MIT license +

-## Peer dependencies -- `effect` 3.19+ -- `react` & `@types/react` 19.2+ +

+ Read the documentation → +

-## Known issues -- React Refresh doesn't work for Effect FC's yet. Page reload is required to view changes. Regular React components are unaffected. +Effect View brings Effect's typed services, resource safety, concurrency, and +data modeling into React 19 without replacing React's component model. Yield +Effects and services from a component body, let scopes follow the React +lifecycle, and return ordinary JSX. -## What writing components looks like -```typescript -export class TodosView extends Component.make("TodosView")(function*() { - const state = yield* TodosState - const [todos] = yield* Component.useSubscribables([state.subscriptionRef]) +> **Effect v4 beta:** Effect View is built for the Effect v4 beta release. If +> your application uses Effect v3, use the legacy +> [`effect-fc` package](https://www.npmjs.com/package/effect-fc) instead. - yield* Component.useOnMount(() => Effect.andThen( - Console.log("Todos mounted"), - Effect.addFinalizer(() => Console.log("Todos unmounted")), - )) +```bash +npm install effect-view effect@beta react +``` - const Todo = yield* TodoView.use +Effect View does not depend on `react-dom`. Install the renderer used by your +application—for example, `react-dom` for the web: + +```bash +npm install react-dom +``` + +You can use Effect View with React Native or any other React renderer instead. + +## What it looks like + +An Effect View component is an Effect program that produces JSX. It can create +scoped state, access services, and use React-facing hooks without leaving the +generator model: + +```tsx +import { Effect, SubscriptionRef } from "effect" +import { Component, Lens } from "effect-view" +import { runtime } from "./runtime" + +const CounterValueView = Component.make("CounterValue")( + function* ({ count }: { readonly count: Lens.Lens }) { + yield* Component.useOnMount(() => + Effect.addFinalizer(() => + Effect.log("CounterValue unmounted"), + ), + ) + + const [value, setValue] = yield* Lens.useState(count) return ( - - Todos - - - - - {Chunk.map(todos, todo => - - )} - - + ) -}) {} - -const Index = Component.make("IndexView")(function*() { - const context = yield* Component.useLayer(TodosState.Default) - const Todos = yield* Effect.provide(TodosView.use, context) - - return -}).pipe( - Component.withContext(runtime.context) + }, ) -export const Route = createFileRoute("/")({ - component: Index +const CounterView = Component.make("Counter")(function* () { + const count = yield* Component.useOnMount(() => + Effect.map( + SubscriptionRef.make(0), + Lens.fromSubscriptionRef, + ), + ) + + const CounterValue = yield* CounterValueView.use + + return }) + +export const Counter = CounterView.pipe( + Component.withContext(runtime.context), +) ``` + +`CounterView` composes another Effect View component by yielding its `.use` +Effect, then renders the resulting component as ordinary JSX. The child can +access the current Effect context and receives its own component scope. + +`Effect.addFinalizer` uses the scope already provided to the component body, so +the finalizer above runs when `CounterValue` unmounts. + +At the outer boundary, `Counter` is still a normal React component and its Effect requirements remain +typed until they reach the runtime. + +[Build the runtime and render your first component →](https://thila.dev/effect-view/docs/getting-started) + +## Why Effect View + +| | Capability | What it gives you | +| --- | --- | --- | +| **Effect components** | `Component.make` | Yield Effects and typed services directly, then return JSX. | +| **Managed lifecycles** | `Scope.Scope` | Finalizers, fibers, subscriptions, and resources close with their component or dependency lifecycle. | +| **One application runtime** | `ReactRuntime` | Build your Layer once and make its services available throughout the UI. | +| **Reactive state** | `Lens` and `View` | Focus large state models into small writable values and subscribe only where React needs them. | +| **Server state** | `Query` and `Mutation` | TanStack Query-style caching, invalidation, staleness, and mutations with typed Effects underneath. | +| **Schema-driven forms** | `MutationForm` and `LensForm` | Keep input-friendly encoded values while application code receives validated, decoded types. | +| **Async rendering** | `Async` and `Memoized` | Integrate asynchronous Effects with Suspense while retaining Effect errors and cancellation. | + +## Designed for both ecosystems + +Effect View does not hide Effect behind callbacks or recreate React around a +new rendering model. React still owns rendering, JSX, hooks, Suspense, and +events. Effect owns services, errors, resource safety, concurrency, streams, +and schemas. Effect View provides the lifecycle-aware boundary between them. + +## Documentation + +- [Getting started](https://thila.dev/effect-view/docs/getting-started) +- [State management](https://thila.dev/effect-view/docs/state-management) +- [Queries](https://thila.dev/effect-view/docs/query) +- [Mutations](https://thila.dev/effect-view/docs/mutation) +- [Forms](https://thila.dev/effect-view/docs/forms) + +The complete documentation is available at +**[thila.dev/effect-view](https://thila.dev/effect-view)**. + +## Requirements + +- React 19.2 or newer +- **Effect v4 beta** (`effect@beta`) +- TypeScript and `@types/react` for TypeScript projects + +Effect View is renderer-independent and does not require `react-dom`. + +## Project status + +Effect View is currently beta software. The main APIs are available, but +breaking changes and rough edges are still possible before a stable release. +React Fast Refresh does not yet recognize Effect View component definitions, so +changes to them currently require a page reload; ordinary React components are +unaffected (currently being worked on). + +Issues, ideas, and contributions are welcome on +[GitHub](https://github.com/Thiladev/effect-view). + +## License + +[MIT](https://github.com/Thiladev/effect-view/blob/main/LICENSE) -- 2.52.0 From fa058ecf532c13072e2c0bc94445e49becedd50b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 22 Jul 2026 20:56:51 +0200 Subject: [PATCH 095/133] Improve README --- packages/effect-fc-next/README.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/effect-fc-next/README.md b/packages/effect-fc-next/README.md index fccb0d5..cb11b8f 100644 --- a/packages/effect-fc-next/README.md +++ b/packages/effect-fc-next/README.md @@ -122,14 +122,7 @@ and schemas. Effect View provides the lifecycle-aware boundary between them. ## Documentation -- [Getting started](https://thila.dev/effect-view/docs/getting-started) -- [State management](https://thila.dev/effect-view/docs/state-management) -- [Queries](https://thila.dev/effect-view/docs/query) -- [Mutations](https://thila.dev/effect-view/docs/mutation) -- [Forms](https://thila.dev/effect-view/docs/forms) - -The complete documentation is available at -**[thila.dev/effect-view](https://thila.dev/effect-view)**. +The complete documentation is available at **[thila.dev/effect-view](https://thila.dev/effect-view)**. ## Requirements -- 2.52.0 From a5867ef36a93a55f0347abfeec201b60dececdf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 22 Jul 2026 21:07:16 +0200 Subject: [PATCH 096/133] Fix Query tests --- packages/effect-fc-next/test/Query.test.ts | 64 +++++++++++----------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/packages/effect-fc-next/test/Query.test.ts b/packages/effect-fc-next/test/Query.test.ts index ee6544f..66724d1 100644 --- a/packages/effect-fc-next/test/Query.test.ts +++ b/packages/effect-fc-next/test/Query.test.ts @@ -1,39 +1,36 @@ -import { Effect, Option, type Scope, Stream } from "effect" +import { Effect, type Scope, Stream } from "effect" +import { AsyncResult } from "effect/unstable/reactivity" import { describe, expect, it } from "vitest" import * as Query from "../src/Query.js" import * as QueryClient from "../src/QueryClient.js" -import * as Result from "../src/Result.js" +import * as View from "../src/View.js" const runQueryTest = (effect: Effect.Effect) => Effect.runPromise(Effect.scoped(effect.pipe( - Effect.provide(QueryClient.QueryClient.Default), + Effect.provide(QueryClient.layer()), ))) -const expectSuccessValue = ( - result: Result.Result, +const staticKey = (key: K): View.View => View.make({ + get: Effect.succeed(key), + changes: Stream.make(key), +}) + +const expectSuccessValue = ( + state: Query.FinalQueryState, ): A => { - expect(Result.isSuccess(result)).toBe(true) + expect(AsyncResult.isSuccess(state.result)).toBe(true) - if (!Result.isSuccess(result)) - throw new Error(`Expected Success result, received ${result._tag}`) + if (!AsyncResult.isSuccess(state.result)) + throw new Error(`Expected Success result, received ${state.result._tag}`) - return result.value -} - -const expectSomeValue = (option: Option.Option): A => { - expect(Option.isSome(option)).toBe(true) - - if (!Option.isSome(option)) - throw new Error("Expected Some option, received None") - - return option.value + return state.result.value } describe("Query", () => { it("fetch caches successful results until they are invalidated or stale", async () => { let calls = 0 - const key = Stream.empty as Stream.Stream + const key = staticKey([1]) const result = await runQueryTest(Effect.gen(function*() { const query = yield* Query.make({ @@ -52,15 +49,13 @@ describe("Query", () => { })) expect(calls).toBe(1) - expect(result[0]._tag).toBe("Success") - expect(result[1]._tag).toBe("Success") expect(expectSuccessValue(result[0])).toBe("value:1:1") expect(expectSuccessValue(result[1])).toBe("value:1:1") }) it("refresh reruns the latest query key", async () => { let calls = 0 - const key = Stream.empty as Stream.Stream + const key = staticKey([1]) const result = await runQueryTest(Effect.gen(function*() { const query = yield* Query.make({ @@ -86,7 +81,7 @@ describe("Query", () => { it("invalidateCacheEntry forces the next fetch for that key to rerun", async () => { let calls = 0 - const key = Stream.empty as Stream.Stream + const key = staticKey([1]) const result = await runQueryTest(Effect.gen(function*() { const query = yield* Query.make({ @@ -112,7 +107,7 @@ describe("Query", () => { it("invalidateCache clears cached entries for the query function", async () => { let calls = 0 - const key = Stream.empty as Stream.Stream + const key = staticKey([1]) const result = await runQueryTest(Effect.gen(function*() { const query = yield* Query.make({ @@ -136,9 +131,9 @@ describe("Query", () => { expect(expectSuccessValue(result[1])).toBe("value:1:2") }) - it("service starts the key stream automatically and updates latest state", async () => { + it("service starts the key view automatically and records its latest final state", async () => { let calls = 0 - const key = Stream.make([1] as const) as Stream.Stream + const key = staticKey([1]) const effect = Effect.gen(function*() { const query = yield* Query.service({ @@ -150,20 +145,23 @@ describe("Query", () => { staleTime: "1 minute", }) - yield* Effect.sleep("10 millis") + const latestFinalState = yield* Effect.sleep("1 millis").pipe( + Effect.andThen(View.get(query.latestFinalState)), + Effect.flatMap(Effect.fromOption), + Effect.eventually, + Effect.timeout("1 second"), + ) return { - final: yield* query.result.get, - latestKey: yield* query.latestKey.get, - latestFinalResult: yield* query.latestFinalResult.get, + state: yield* View.get(query.state), + latestFinalState, } }) const result = await runQueryTest(effect) expect(calls).toBe(1) - expect(expectSuccessValue(result.final)).toBe("value:1:1") - expect(expectSomeValue(result.latestKey)).toEqual([1]) - expect(expectSuccessValue(expectSomeValue(result.latestFinalResult))).toBe("value:1:1") + expect(result.state.key).toEqual([1]) + expect(expectSuccessValue(result.latestFinalState)).toBe("value:1:1") }) }) -- 2.52.0 From cce1d6e48583fc08b33e5e6dadf0268c09dda5d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 22 Jul 2026 21:41:26 +0200 Subject: [PATCH 097/133] Fix --- packages/effect-fc-next/src/Component.ts | 4 +- .../effect-fc-next/test/Component.test.tsx | 140 +++++++++++++++--- packages/effect-fc-next/test/setup.ts | 4 + packages/effect-fc-next/vitest.config.ts | 1 + 4 files changed, 126 insertions(+), 23 deletions(-) create mode 100644 packages/effect-fc-next/test/setup.ts diff --git a/packages/effect-fc-next/src/Component.ts b/packages/effect-fc-next/src/Component.ts index c9a45be..0147258 100644 --- a/packages/effect-fc-next/src/Component.ts +++ b/packages/effect-fc-next/src/Component.ts @@ -1,6 +1,6 @@ /** biome-ignore-all lint/complexity/noBannedTypes: {} is the default type for React props */ /** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */ -import { Cause, Context, type Duration, Effect, Equal, Equivalence, Exit, Fiber, Function, HashMap, identity, Layer, Option, Pipeable, Predicate, Ref, Scope, Tracer } from "effect" +import { Cause, Context, type Duration, Effect, Equal, Equivalence, Exit, Fiber, Function, HashMap, identity, Layer, Option, Pipeable, Predicate, Ref, Scheduler, Scope, Tracer } from "effect" import * as React from "react" @@ -155,7 +155,7 @@ export interface ComponentOptions { } export const defaultOptions: ComponentOptions = { - nonReactiveTags: [Tracer.ParentSpan], + nonReactiveTags: [Tracer.ParentSpan, Scheduler.Scheduler, Layer.CurrentMemoMap], finalizerExecutionStrategy: "sequential", finalizerExecutionDebounce: "100 millis", } diff --git a/packages/effect-fc-next/test/Component.test.tsx b/packages/effect-fc-next/test/Component.test.tsx index 34393f0..615953e 100644 --- a/packages/effect-fc-next/test/Component.test.tsx +++ b/packages/effect-fc-next/test/Component.test.tsx @@ -1,5 +1,5 @@ -import { render, screen, waitFor } from "@testing-library/react" -import { Context, Effect, Layer } from "effect" +import { act, fireEvent, render, screen, waitFor } from "@testing-library/react" +import { Context, Effect, HashMap, Layer, Ref } from "effect" import * as React from "react" import { afterEach, describe, expect, it, vi } from "vitest" import * as Component from "../src/Component.js" @@ -13,7 +13,7 @@ afterEach(() => { }) describe("Component", () => { - it("runs useOnMount only once across rerenders", async () => { + it("does not rerun useOnMount across rerenders after Strict Mode initialization", async () => { const onMount = vi.fn(() => Effect.succeed("mounted")) const runtime = ReactRuntime.make(Layer.empty) const effectRuntime = await runtime.runtime.context() @@ -32,7 +32,7 @@ describe("Component", () => { ) await screen.findByText("mounted") - expect(onMount).toHaveBeenCalledTimes(1) + expect(onMount).toHaveBeenCalledTimes(2) view.rerender( @@ -40,7 +40,7 @@ describe("Component", () => { ) expect(await screen.findByText("mounted")).toBeTruthy() - expect(onMount).toHaveBeenCalledTimes(1) + expect(onMount).toHaveBeenCalledTimes(2) view.unmount() await runtime.runtime.dispose() @@ -68,7 +68,7 @@ describe("Component", () => { ) await screen.findByText("value:1") - expect(onChange).toHaveBeenCalledTimes(1) + expect(onChange).toHaveBeenCalledTimes(2) view.rerender( @@ -77,7 +77,7 @@ describe("Component", () => { ) expect(await screen.findByText("value:1")).toBeTruthy() - expect(onChange).toHaveBeenCalledTimes(1) + expect(onChange).toHaveBeenCalledTimes(2) view.rerender( @@ -86,7 +86,7 @@ describe("Component", () => { ) await screen.findByText("value:2") - expect(onChange).toHaveBeenCalledTimes(2) + expect(onChange).toHaveBeenCalledTimes(4) view.unmount() await runtime.runtime.dispose() @@ -179,6 +179,8 @@ describe("Component", () => { await waitFor(() => expect(lifecycle).toHaveBeenCalledWith("cleanup:second")) expect(lifecycle.mock.calls.map(([message]) => message)).toEqual([ + "mount:first", + "cleanup:first", "mount:first", "cleanup:first", "mount:second", @@ -218,9 +220,9 @@ describe("Component", () => { ) await screen.findByText("a:1") - expect(seenCallbacks).toHaveLength(2) - expect(seenCallbacks[0]).toBe(seenCallbacks[1]) - expect(seenCallbacks[0]?.(2)).toBe("a:2") + const initialLength = seenCallbacks.length + const initialCallback = seenCallbacks.at(-1) + expect(initialCallback?.(2)).toBe("a:2") view.rerender( @@ -229,7 +231,7 @@ describe("Component", () => { ) await screen.findByText("a:1") - expect(seenCallbacks).toHaveLength(2) + expect(seenCallbacks).toHaveLength(initialLength) view.rerender( @@ -238,9 +240,9 @@ describe("Component", () => { ) await screen.findByText("b:1") - await waitFor(() => expect(seenCallbacks).toHaveLength(3)) - expect(seenCallbacks[2]).not.toBe(seenCallbacks[1]) - expect(seenCallbacks[2]?.(2)).toBe("b:2") + await waitFor(() => expect(seenCallbacks.length).toBeGreaterThan(initialLength)) + expect(seenCallbacks.at(-1)).not.toBe(initialCallback) + expect(seenCallbacks.at(-1)?.(2)).toBe("b:2") view.unmount() await runtime.runtime.dispose() @@ -301,15 +303,18 @@ describe("Component", () => { const SubComponent = Component.makeUntraced("NonReactiveSubComponent")(function*() { const service = yield* ValueService + const [count, setCount] = React.useState(0) yield* Component.useOnMount(() => Effect.gen(function*() { yield* Effect.sync(() => mounts()) yield* Effect.addFinalizer(() => Effect.sync(() => unmounts())) })) - return
{service.value}
+ return }).pipe( - Component.withOptions({ nonReactiveTags: [ValueService] }) + Component.withOptions({ + nonReactiveTags: [...Component.defaultOptions.nonReactiveTags, ValueService], + }) ) const Parent = Component.makeUntraced("NonReactiveParent")(function*(props: { readonly value: string }) { @@ -333,22 +338,115 @@ describe("Component", () => {
) - await screen.findByText("first") - expect(mounts).toHaveBeenCalledTimes(1) + await screen.findByText("first:0") + const initialMounts = mounts.mock.calls.length + expect(initialMounts).toBeGreaterThan(0) expect(unmounts).not.toHaveBeenCalled() + fireEvent.click(screen.getByRole("button", { name: "first:0" })) + await screen.findByText("first:1") + view.rerender( ) - await screen.findByText("second") - expect(mounts).toHaveBeenCalledTimes(1) + await screen.findByText("second:1") + expect(mounts).toHaveBeenCalledTimes(initialMounts) expect(unmounts).not.toHaveBeenCalled() view.unmount() await waitFor(() => expect(unmounts).toHaveBeenCalledTimes(1)) await runtime.runtime.dispose() }) + + it.fails("does not commit effects or retain registered scopes for a discarded Suspense render", async () => { + const setup = vi.fn() + const runtime = ReactRuntime.make(Layer.empty) + const effectRuntime = await runtime.runtime.context() + const scopeMap = Context.get(effectRuntime, Component.ScopeMap) + const pending = new Promise(() => {}) + + const Probe = Component.makeUntraced("DiscardedSuspenseProbe")(function*() { + yield* Component.useReactEffect(() => Effect.sync(setup), []) + React.use(pending) + return
committed
+ }).pipe( + Component.withContext(runtime.context) + ) + + let view!: ReturnType + await act(async () => { + view = render( + fallback}> + + + + + ) + }) + + try { + await screen.findByText("fallback") + expect(setup).not.toHaveBeenCalled() + expect(HashMap.size(Effect.runSync(Ref.get(scopeMap.ref)))).toBe(0) + } + finally { + view.unmount() + await runtime.runtime.dispose() + } + }) + + it.fails("clears ScopeMap after a suspended render retries, commits, and unmounts", async () => { + const lifecycle = vi.fn<(message: string) => void>() + const runtime = ReactRuntime.make(Layer.empty) + const effectRuntime = await runtime.runtime.context() + const scopeMap = Context.get(effectRuntime, Component.ScopeMap) + let resolve!: () => void + const pending = new Promise(complete => { + resolve = complete + }) + + const Probe = Component.makeUntraced("RetriedSuspenseProbe")(function*() { + React.use(pending) + yield* Component.useReactEffect(() => Effect.gen(function*() { + yield* Effect.sync(() => lifecycle("mount")) + yield* Effect.addFinalizer(() => Effect.sync(() => lifecycle("cleanup"))) + }), []) + return
committed
+ }).pipe( + Component.withContext(runtime.context) + ) + + let view!: ReturnType + await act(async () => { + view = render( + fallback}> + + + + + ) + }) + + try { + await screen.findByText("fallback") + await act(async () => { + resolve() + await pending + }) + await screen.findByText("committed") + await waitFor(() => expect(lifecycle).toHaveBeenCalledWith("mount")) + + view.unmount() + await waitFor(() => expect(HashMap.size(Effect.runSync(Ref.get(scopeMap.ref)))).toBe(0)) + expect(lifecycle.mock.calls.filter(([message]) => message === "mount")).toHaveLength(2) + expect(lifecycle.mock.calls.filter(([message]) => message === "cleanup")).toHaveLength(2) + } + finally { + view.unmount() + await runtime.runtime.dispose() + } + }) }) diff --git a/packages/effect-fc-next/test/setup.ts b/packages/effect-fc-next/test/setup.ts new file mode 100644 index 0000000..839583d --- /dev/null +++ b/packages/effect-fc-next/test/setup.ts @@ -0,0 +1,4 @@ +import { configure } from "@testing-library/react" + + +configure({ reactStrictMode: true }) diff --git a/packages/effect-fc-next/vitest.config.ts b/packages/effect-fc-next/vitest.config.ts index cec0e39..1374d42 100644 --- a/packages/effect-fc-next/vitest.config.ts +++ b/packages/effect-fc-next/vitest.config.ts @@ -5,5 +5,6 @@ export default defineConfig({ test: { environment: "jsdom", include: ["test/**/*.test.ts?(x)"], + setupFiles: ["test/setup.ts"], }, }) -- 2.52.0 From c151b4608f1accb7bff16d05c57f4c9ac5d7f21b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 22 Jul 2026 22:22:25 +0200 Subject: [PATCH 098/133] Add report --- EFFECT_REACT_LIFECYCLE_REPORT.md | 484 +++++++++++++++++++++++++++++++ 1 file changed, 484 insertions(+) create mode 100644 EFFECT_REACT_LIFECYCLE_REPORT.md diff --git a/EFFECT_REACT_LIFECYCLE_REPORT.md b/EFFECT_REACT_LIFECYCLE_REPORT.md new file mode 100644 index 0000000..216c7f0 --- /dev/null +++ b/EFFECT_REACT_LIFECYCLE_REPORT.md @@ -0,0 +1,484 @@ +# Effect–React Render Lifecycle Report + +## Purpose + +This report describes the lifecycle problem in `effect-fc-next`, why abandoned React renders leak Effect scopes, what the experimental implementations attempted, why those implementations are not satisfactory, and how a robust system should be designed. + +The experimental implementation discussed here has been removed from the source tree. This document is intended as design material for a clean reimplementation. + +## Executive summary + +The fundamental problem is not scope disposal by itself. It is that `effect-fc-next` performs Effect work and creates resource scopes while React is rendering. + +React distinguishes between two broad phases: + +1. **Render:** React evaluates components and builds a prospective tree. React may retry, pause, duplicate, or permanently abandon this work. +2. **Commit:** React accepts a rendered tree and runs layout and passive effects. Cleanup callbacks only exist for work that reached this phase. + +The existing `Component.useScope` creates and registers an Effect scope during render, but installs its cleanup from `React.useEffect`. If React abandons the render, the effect is never mounted, its cleanup is never installed, and the render-created scope remains registered indefinitely. + +There is no public React callback for “this render attempt was abandoned.” Consequently, an object created only for a render attempt cannot receive deterministic abandonment notification. + +A correct architecture must therefore choose one of these models: + +- Do not acquire resources until commit. +- Store render-time resources in an external registry under an explicit, stable identity. +- Treat render-time resources as speculative cached entries and garbage-collect idle entries, while ensuring an expired entry can always be recreated safely. + +The first model is simplest. The second is the model used by atom/resource systems. The third is necessarily a cache/TTL design rather than exact component-lifecycle tracking. + +## The original `useScope` lifecycle + +The implementation in `Component.ts` performs approximately the following work: + +```ts +function useScope(dependencies) { + const { key, scope } = React.useMemo(() => { + const key = makeIdentity() + const scope = makeEffectScope() + scopeMap.set(key, { scope, closeFiber: none() }) + + return { key, scope } + }, dependencies) + + React.useEffect(() => { + cancelPreviouslyScheduledClose(key) + + return () => { + const closeFiber = sleep(debounce).andThen(close(scope)).fork() + scopeMap.set(key, { scope, closeFiber: some(closeFiber) }) + } + }, [key]) + + return scope +} +``` + +The intended committed lifecycle is: + +```text +render + create scope + insert scope into ScopeMap + +commit + run useEffect setup + cancel any pending close left by a rapid remount + +unmount or dependency change + run useEffect cleanup + wait for the debounce + close the scope + remove it from ScopeMap +``` + +For an ordinary committed component, this works. The debounce also bridges React Strict Mode’s development-only effect cycle: + +```text +effect setup +effect cleanup schedules close +effect setup again cancels close +``` + +The failing lifecycle is: + +```text +render + create scope + insert scope into ScopeMap + +React abandons render + useEffect setup never runs + useEffect cleanup therefore never exists + no close is scheduled + ScopeMap retains the scope forever +``` + +Suspense can trigger this by throwing a promise before the component commits. Concurrent rendering and failed/erroring renders can produce the same category of problem. + +## Why React cannot directly report abandonment + +Hooks such as `useEffect` and `useLayoutEffect` describe the lifetime of a committed component. They are deliberately not executed for render work that never commits. + +Render-time hook state does not solve this: + +- `useMemo` may be discarded with the render attempt. +- `useRef` belongs to the same provisional fiber state. +- `useState` belongs to the same provisional fiber state. +- Cleanup returned from `useEffect` only exists after effect setup has run. +- A component cannot read its React `key`; React consumes it before props reach the component. + +JavaScript garbage collection is also insufficient. `FinalizationRegistry` is nondeterministic and cannot be used to guarantee timely execution of Effect finalizers. + +This means there is no reliable per-render-attempt destructor available to application code. + +## Why Effect Atom can use an external lifecycle + +Effect Atom has something a component scope does not: an atom is an explicit object with stable identity outside React. + +The conceptual flow is: + +```text +React receives an Atom object + │ + ▼ +AtomRegistry.getOrCreate(atom) + │ + ├── returns an existing external node + └── or creates a new external node and schedules idle removal + +React commit subscribes to the node + │ + └── subscription prevents idle removal + +React unmount unsubscribes + │ + └── node becomes eligible for removal +``` + +If a render is abandoned, no subscription is established. The node is still owned by the external registry and can remove itself when idle. If React retries later, it asks the registry for the atom again. The registry either returns the still-live node or creates a replacement. + +`useSyncExternalStore` belongs naturally in this design because an atom node has a changing snapshot. React must both subscribe to changes and read a consistent value. + +A component scope does not naturally have either property: + +- It has no explicit identity external to React. +- Its scope object is not a changing UI snapshot. + +Therefore, copying the Atom mechanism without first solving identity produces abstractions that look like an external store but are actually render-attempt bookkeeping. + +## The experimental external-store implementation + +The first experiment introduced three concepts: + +- A runtime-owned registry. +- A render-owned store/handle created with `useMemo`. +- A registry entry containing the Effect scope and its cleanup state. + +It used `useSyncExternalStore` as follows: + +```text +getSnapshot during render + create a scope entry + register it externally + immediately schedule idle cleanup + +subscribe during commit + retain the entry + cancel idle cleanup + +unsubscribe during unmount + release the entry + schedule cleanup +``` + +This fixed the abandoned-render leak because an uncommitted entry had no subscriber and its initial cleanup remained scheduled. + +It also allowed a closed snapshot to be replaced: if cleanup won a race before subscription, a later `getSnapshot` could create another entry. + +However, it had several design problems: + +1. The “store” was created by `useMemo`, so it was not truly an externally identified store. +2. `useSyncExternalStore` was being used primarily as a commit/unmount detector rather than for observable state. +3. The names “store,” “entry,” and “snapshot” obscured the resource lifecycle. +4. It introduced listener notification machinery even though a scope has no meaningful changing value for the UI. +5. It gave the appearance of Atom’s semantics without possessing Atom’s stable external identity. + +It was functional as a self-healing speculative cache, but it was not a clean model for a component instance. + +## The experimental retain/release implementation + +The second experiment removed `useSyncExternalStore` and represented the lifecycle directly: + +```ts +const registration = React.useMemo( + () => scopeMap.register(context, options), + dependencies, +) + +React.useLayoutEffect(() => { + registration.retain() + return registration.release +}, [registration]) + +return registration.scope +``` + +Registration immediately scheduled its own close. A committed layout effect cancelled that close through `retain()`. Unmount called `release()` and scheduled it again. + +This was much easier to understand: + +```text +registration created → provisional +layout effect setup → retained +layout effect cleanup → released +idle timeout → closed +``` + +It still had a serious race: + +```text +render creates provisional registration +React delays commit longer than the cleanup debounce +registration closes +React eventually commits the same render +layout effect calls retain on an already-closed registration +component now holds a closed Effect scope +``` + +The external-store version could recover by returning a new snapshot. The direct retain/release version could not trigger a render-time replacement after the scope had closed. + +Increasing the timeout reduces the likelihood but does not make the system correct. A timeout cannot prove that React has abandoned work. + +## What the bookkeeping in the experiment was doing + +The experimental implementation contained more machinery than the conceptual model suggested. Each part existed for a specific race: + +### Runtime registry + +The registry retained every scope that might still need disposal. It also allowed `ReactRuntime` disposal to close every remaining scope. + +A `HashMap` and `Ref` were used, but neither is essential for a synchronous JavaScript registry. A `Set` would express the ownership more clearly unless atomic Effect composition is specifically required. + +### Unique registration key + +Each speculative render needed a separate map entry. Otherwise two simultaneous attempts could overwrite or close one another. + +### Cleanup fiber + +The fiber represented the debounce period followed by scope closure. Retaining the registration interrupted this fiber. + +### Cleanup generation + +Interrupting an Effect fiber is not equivalent to synchronously erasing every continuation it may execute. Consider: + +```text +cleanup A is running +retain interrupts cleanup A +release schedules cleanup B +cleanup A's ensuring handler finishes afterward +``` + +If cleanup A blindly clears the `closeFiber` field, it erases the reference to cleanup B. A monotonically increasing generation lets a finalizer clear the field only if it still represents the current scheduled close. + +### Retain count + +A count allows repeated retain/release pairs, including Strict Mode effect simulation, without closing while at least one committed lease remains. A boolean is sufficient only if the code can prove that setup and cleanup are perfectly paired and never nested. + +These details are legitimate in a speculative cache, but they should be encapsulated behind a much smaller domain model. + +## The identity problem + +An external registry needs a key that means “this logical resource.” Possible candidates all have limitations: + +### Component definition + +Using the `Component` object would make every mounted instance of the same component share one scope. That breaks per-instance mount state and cleanup. + +### Props or dependency values + +Two component instances can have equal props while requiring independent scopes. Object props may also be recreated on every render. + +### `useId` + +`useId` is intended for accessibility and hydration identifiers, not general cache identity. Concurrent versions of the same logical component can receive the same identifier, and uniqueness across multiple roots depends on root configuration. It should not silently become a resource-ownership contract. + +### React `key` + +This would be conceptually useful, but React does not expose it to the component. Requiring a separate explicit resource key would be an API change. + +### A value made by `useMemo`, `useRef`, or `useState` + +These values identify a React fiber lifetime only after React preserves that hook state. They do not survive an abandoned initial render and therefore cannot provide Atom-style external identity. + +The clean external design consequently requires an explicit identity supplied by the application or created outside the speculative render. + +## Recommended architecture + +The API should distinguish committed lifecycle work from render-time resources instead of trying to give them identical semantics. + +### 1. Commit-only component scopes + +Use this model for resources that do not need to affect the current render synchronously. + +```ts +function useCommittedScope(options): ScopeState { + const [scope, setScope] = React.useState() + + React.useLayoutEffect(() => { + const scope = makeScope(options) + setScope(scope) + + return () => runClose(scope) + }, dependencies) + + return scope +} +``` + +The exact API need not use state, but the invariant should be: + +> No resource is acquired until React commits the component. + +An abandoned render then has nothing to clean up. + +This may require changing APIs that currently expect acquisition results during the same render. Such APIs cannot be made commit-only without representing an uninitialized/loading state or causing a subsequent render. + +### 2. Explicit external resources for Suspense + +Use this model when rendering depends on asynchronous acquisition or a promise. + +```ts +interface ResourceKey { + readonly id: string +} + +interface ResourceNode
{ + readonly read: () => A + readonly subscribe: (listener: () => void) => () => void +} + +const node = resourceRegistry.getOrCreate(key, createResource) +const value = React.useSyncExternalStore(node.subscribe, node.read, node.read) +``` + +Here `useSyncExternalStore` is justified because the node is truly external, has a stable explicit key, and has a changing observable result. + +The node lifecycle should be: + +```text +absent + │ getOrCreate + ▼ +idle/pending ── subscribe ──► retained + ▲ │ + └──── unsubscribe ──────────┘ + │ + └── idle TTL expires ──► disposed and removed +``` + +If a node expires before a retry, `getOrCreate` constructs a new node. No React render can remain permanently bound to an expired object without revalidating it. + +### 3. Make `useOnMount` semantics explicit + +The name `useOnMount` suggests commit-time execution, but if its Effect is evaluated from `useMemo` during render, it is actually speculative render-time execution. + +The library should choose and document one of these contracts: + +- `useOnMount` runs only after commit and cannot synchronously return an acquisition result during the initial render. +- A separate render-safe primitive accepts only pure/synchronous computation. +- A resource primitive uses an explicit external resource key and supports Suspense. + +Allowing arbitrary scoped Effects to execute during render while promising exact component cleanup is the combination that creates the unsolvable abandonment problem. + +## Required invariants for an external resource registry + +If an Atom-style registry is implemented, it should satisfy all of the following: + +1. **Stable identity:** Every node is addressed by an explicit resource key independent of React hook state. +2. **Idempotent lookup:** Repeated reads of the same key return the same live node. +3. **Recoverable expiry:** A lookup after disposal returns a new live node, never the disposed node. +4. **Commit retention:** A committed subscriber prevents idle disposal. +5. **Abandoned-render collection:** A node read by an abandoned render has no subscriber and becomes eligible for disposal. +6. **Reference-counted ownership:** One unmount cannot dispose a node still used by another subscriber. +7. **Idempotent disposal:** Closing a node multiple times has the same result as closing it once. +8. **Registry disposal:** Disposing the runtime closes every node regardless of subscriber state. +9. **Generation safety:** Completion of an older cleanup task cannot overwrite or cancel newer lifecycle state. +10. **Consistent snapshot:** React must never receive a closed node as the current live snapshot. + +## Required test matrix + +A replacement should be tested against lifecycle sequences, not only final rendered output. + +### Normal lifecycle + +- Initial commit acquires exactly the intended resources. +- Rerender with equivalent dependencies does not reacquire. +- Dependency change releases the old resource and acquires the new one. +- Unmount runs every finalizer once. + +### Strict Mode + +- Development render duplication does not leak the discarded attempt. +- Effect setup/cleanup/setup simulation does not prematurely close the committed resource. +- Final unmount closes all remaining resources. + +### Suspense + +- A render that suspends forever does not retain resources indefinitely. +- A suspended render that retries can obtain a live resource after any idle cleanup. +- Resolving an obsolete promise cannot mutate or resurrect a newer node. +- Changing props while pending disposes or detaches the obsolete computation. + +### Concurrent replacement + +- The current committed tree remains usable while a replacement tree renders. +- Abandoning the replacement does not affect the current tree. +- Committing the replacement releases the previous tree only after the replacement commits. + +### Runtime disposal + +- Disposing `ReactRuntime` closes committed resources. +- It also closes idle, pending, and speculative registry nodes. +- Pending async fibers are interrupted without unhandled promise rejections. + +### Timing races + +- Retain racing with scheduled disposal has a deterministic winner. +- Disposal racing with retry either preserves the live node or forces recreation. +- An old cleanup task cannot clear the handle for a newer cleanup task. + +## Suggested minimal domain model + +For a real external resource cache, the implementation can remain small if the names match the responsibilities: + +```ts +interface ResourceRegistry { + getOrCreate(key: ResourceKey, create: () => ResourceNode): ResourceNode + dispose: Effect.Effect +} + +interface ResourceNode { + readonly key: ResourceKey + readonly read: () => A + readonly subscribe: (listener: () => void) => () => void + readonly dispose: Effect.Effect +} +``` + +Internally, a node can own: + +- Its Effect scope. +- Its current result or promise. +- Its subscribers. +- Its idle cleanup task. +- Its lifecycle generation. + +There should not be a separate render-owned object called a “store.” React should interact directly with the externally owned node. + +For commit-only scopes, the model is even smaller: + +```ts +interface ComponentScope { + readonly scope: Scope.Closeable + readonly close: Effect.Effect +} +``` + +Create it at commit and close it at effect cleanup. No registry, snapshot, subscription, speculative timer, or abandonment recovery is necessary unless runtime-wide disposal must independently own it. + +## Final recommendation for `effect-fc-next` + +Do not attempt to hide both committed component lifecycles and render-time Suspense resources behind one implicit `Scope` mechanism. + +The most defensible direction is: + +1. Make normal component acquisition commit-driven. +2. Prevent or clearly label arbitrary Effect acquisition during render. +3. Introduce a separate external resource abstraction for async/Suspense work. +4. Require that abstraction to have an explicit stable key. +5. Use `useSyncExternalStore` only for those externally owned nodes whose snapshots can change. +6. Treat idle timeout as cache eviction, not as proof that React abandoned a render. +7. Make every read capable of replacing an expired node. + +If backward compatibility requires speculative per-render scopes, document that the debounce is heuristic and retain the recovery behavior that can replace a scope which expires before commit. That model can prevent leaks, but it should not be presented as exact React component-instance lifecycle tracking. -- 2.52.0 From 8fed17789a27d92b48e9b5e4d788eb50f891f512 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 22 Jul 2026 22:51:38 +0200 Subject: [PATCH 099/133] ScopeMap -> ScopeRegistry --- packages/effect-fc-next/src/Component.ts | 40 ++++--------------- packages/effect-fc-next/src/ReactRuntime.ts | 3 +- packages/effect-fc-next/src/ScopeRegistry.ts | 26 ++++++++++++ packages/effect-fc-next/src/index.ts | 1 + .../effect-fc-next/test/Component.test.tsx | 11 ++--- 5 files changed, 43 insertions(+), 38 deletions(-) create mode 100644 packages/effect-fc-next/src/ScopeRegistry.ts diff --git a/packages/effect-fc-next/src/Component.ts b/packages/effect-fc-next/src/Component.ts index 0147258..11e8a02 100644 --- a/packages/effect-fc-next/src/Component.ts +++ b/packages/effect-fc-next/src/Component.ts @@ -2,6 +2,7 @@ /** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */ import { Cause, Context, type Duration, Effect, Equal, Equivalence, Exit, Fiber, Function, HashMap, identity, Layer, Option, Pipeable, Predicate, Ref, Scheduler, Scope, Tracer } from "effect" import * as React from "react" +import * as ScopeRegistry from "./ScopeRegistry.js" export const ComponentTypeId: unique symbol = Symbol.for("@effect-fc/Component/Component") @@ -636,31 +637,6 @@ export const withContext: { }) -/** - * Internal Effect service that maintains a registry of scopes associated with React component instances. - * - * This service is used internally by the `useScope` hook to manage the lifecycle of component scopes, - * including tracking active scopes and coordinating their cleanup when components unmount or dependencies change. - */ -export class ScopeMap extends Context.Service> -}>()( - "@effect-fc/Component/ScopeMap" -) { - static readonly layer = Layer.effect(ScopeMap, Effect.map( - Ref.make(HashMap.empty()), - ref => ({ ref }), - )) -} - -export declare namespace ScopeMap { - export interface Entry { - readonly scope: Scope.Closeable - readonly closeFiber: Option.Option> - } -} - - export declare namespace useScope { export interface Options { readonly finalizerExecutionStrategy?: "sequential" | "parallel" @@ -694,14 +670,14 @@ export const useScope = Effect.fnUntraced(function*( contextRef.current = yield* Effect.context() const { key, scope } = React.useMemo(() => Effect.runSyncWith(contextRef.current)(Effect.Do.pipe( - Effect.bind("scopeMapRef", () => Effect.map( - ScopeMap as unknown as Effect.Effect, - scopeMap => scopeMap.ref, + Effect.bind("scopeRegistryRef", () => Effect.map( + ScopeRegistry.ScopeRegistry as unknown as Effect.Effect, + scopeRegistry => scopeRegistry.ref, )), Effect.let("key", () => Equal.byReference({})), Effect.bind("scope", () => Scope.make(options?.finalizerExecutionStrategy ?? defaultOptions.finalizerExecutionStrategy)), - Effect.tap(({ scopeMapRef, key, scope }) => - Ref.update(scopeMapRef, HashMap.set(key, Equal.byReference({ + Effect.tap(({ scopeRegistryRef, key, scope }) => + Ref.update(scopeRegistryRef, HashMap.set(key, Equal.byReference({ scope, closeFiber: Option.none(), }))) @@ -711,8 +687,8 @@ export const useScope = Effect.fnUntraced(function*( // biome-ignore lint/correctness/useExhaustiveDependencies: only reactive on "key" React.useEffect(() => Effect.runSyncWith(contextRef.current)( - (ScopeMap as unknown as Effect.Effect).pipe( - Effect.map(scopeMap => scopeMap.ref), + (ScopeRegistry.ScopeRegistry as unknown as Effect.Effect).pipe( + Effect.map(scopeRegistry => scopeRegistry.ref), Effect.tap(ref => Ref.get(ref).pipe( Effect.flatMap(map => Effect.fromOption(HashMap.get(map, key))), Effect.flatMap(entry => Option.match(entry.closeFiber, { diff --git a/packages/effect-fc-next/src/ReactRuntime.ts b/packages/effect-fc-next/src/ReactRuntime.ts index 0cee1e0..e9e18aa 100644 --- a/packages/effect-fc-next/src/ReactRuntime.ts +++ b/packages/effect-fc-next/src/ReactRuntime.ts @@ -2,6 +2,7 @@ import { type Context, Effect, Layer, ManagedRuntime, Predicate } from "effect" import * as React from "react" import * as Component from "./Component.js" +import * as ScopeRegistry from "./ScopeRegistry.js" export const ReactRuntimeTypeId: unique symbol = Symbol.for("@effect-fc/ReactRuntime/ReactRuntime") @@ -16,7 +17,7 @@ export interface ReactRuntime { const ReactRuntimePrototype = Object.freeze({ [ReactRuntimeTypeId]: ReactRuntimeTypeId } as const) -export const preludeLayer: Layer.Layer = Component.ScopeMap.layer +export const preludeLayer: Layer.Layer = ScopeRegistry.ScopeRegistry.layer export const isReactRuntime = (u: unknown): u is ReactRuntime => Predicate.hasProperty(u, ReactRuntimeTypeId) diff --git a/packages/effect-fc-next/src/ScopeRegistry.ts b/packages/effect-fc-next/src/ScopeRegistry.ts new file mode 100644 index 0000000..d664426 --- /dev/null +++ b/packages/effect-fc-next/src/ScopeRegistry.ts @@ -0,0 +1,26 @@ +import { Context, Effect, type Fiber, HashMap, Layer, type Option, Ref, type Scope } from "effect" + + +/** + * Internal Effect service that maintains a registry of scopes associated with React component instances. + * + * This service is used internally by the `Component.useScope` hook to manage the lifecycle of component scopes, + * including tracking active scopes and coordinating their cleanup when components unmount or dependencies change. + */ +export class ScopeRegistry extends Context.Service> +}>()( + "@effect-fc/ScopeRegistry/ScopeRegistry" +) { + static readonly layer = Layer.effect(ScopeRegistry, Effect.map( + Ref.make(HashMap.empty()), + ref => ({ ref }), + )) +} + +export declare namespace ScopeRegistry { + export interface Entry { + readonly scope: Scope.Closeable + readonly closeFiber: Option.Option> + } +} diff --git a/packages/effect-fc-next/src/index.ts b/packages/effect-fc-next/src/index.ts index 33039ed..014737b 100644 --- a/packages/effect-fc-next/src/index.ts +++ b/packages/effect-fc-next/src/index.ts @@ -11,6 +11,7 @@ export * as PubSub from "./PubSub.js" export * as Query from "./Query.js" export * as QueryClient from "./QueryClient.js" export * as ReactRuntime from "./ReactRuntime.js" +export * as ScopeRegistry from "./ScopeRegistry.js" export * as SetStateAction from "./SetStateAction.js" export * as Stream from "./Stream.js" export * as View from "./View.js" diff --git a/packages/effect-fc-next/test/Component.test.tsx b/packages/effect-fc-next/test/Component.test.tsx index 615953e..7e777f2 100644 --- a/packages/effect-fc-next/test/Component.test.tsx +++ b/packages/effect-fc-next/test/Component.test.tsx @@ -4,6 +4,7 @@ import * as React from "react" import { afterEach, describe, expect, it, vi } from "vitest" import * as Component from "../src/Component.js" import * as ReactRuntime from "../src/ReactRuntime.js" +import * as ScopeRegistry from "../src/ScopeRegistry.js" class ValueService extends Context.Service()("ValueService") {} @@ -365,7 +366,7 @@ describe("Component", () => { const setup = vi.fn() const runtime = ReactRuntime.make(Layer.empty) const effectRuntime = await runtime.runtime.context() - const scopeMap = Context.get(effectRuntime, Component.ScopeMap) + const scopeRegistry = Context.get(effectRuntime, ScopeRegistry.ScopeRegistry) const pending = new Promise(() => {}) const Probe = Component.makeUntraced("DiscardedSuspenseProbe")(function*() { @@ -390,7 +391,7 @@ describe("Component", () => { try { await screen.findByText("fallback") expect(setup).not.toHaveBeenCalled() - expect(HashMap.size(Effect.runSync(Ref.get(scopeMap.ref)))).toBe(0) + expect(HashMap.size(Effect.runSync(Ref.get(scopeRegistry.ref)))).toBe(0) } finally { view.unmount() @@ -398,11 +399,11 @@ describe("Component", () => { } }) - it.fails("clears ScopeMap after a suspended render retries, commits, and unmounts", async () => { + it.fails("clears ScopeRegistry after a suspended render retries, commits, and unmounts", async () => { const lifecycle = vi.fn<(message: string) => void>() const runtime = ReactRuntime.make(Layer.empty) const effectRuntime = await runtime.runtime.context() - const scopeMap = Context.get(effectRuntime, Component.ScopeMap) + const scopeRegistry = Context.get(effectRuntime, ScopeRegistry.ScopeRegistry) let resolve!: () => void const pending = new Promise(complete => { resolve = complete @@ -440,7 +441,7 @@ describe("Component", () => { await waitFor(() => expect(lifecycle).toHaveBeenCalledWith("mount")) view.unmount() - await waitFor(() => expect(HashMap.size(Effect.runSync(Ref.get(scopeMap.ref)))).toBe(0)) + await waitFor(() => expect(HashMap.size(Effect.runSync(Ref.get(scopeRegistry.ref)))).toBe(0)) expect(lifecycle.mock.calls.filter(([message]) => message === "mount")).toHaveLength(2) expect(lifecycle.mock.calls.filter(([message]) => message === "cleanup")).toHaveLength(2) } -- 2.52.0 From cb626398297859ba97cab1fb6afe6f6eb57dc01d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 23 Jul 2026 01:22:14 +0200 Subject: [PATCH 100/133] Implement ScopeRegistry --- packages/effect-fc-next/src/Component.ts | 62 ++---- packages/effect-fc-next/src/ReactRuntime.ts | 2 +- packages/effect-fc-next/src/ScopeRegistry.ts | 209 ++++++++++++++++-- .../effect-fc-next/test/Component.test.tsx | 48 +++- 4 files changed, 253 insertions(+), 68 deletions(-) diff --git a/packages/effect-fc-next/src/Component.ts b/packages/effect-fc-next/src/Component.ts index 11e8a02..6023781 100644 --- a/packages/effect-fc-next/src/Component.ts +++ b/packages/effect-fc-next/src/Component.ts @@ -1,6 +1,6 @@ /** biome-ignore-all lint/complexity/noBannedTypes: {} is the default type for React props */ /** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */ -import { Cause, Context, type Duration, Effect, Equal, Equivalence, Exit, Fiber, Function, HashMap, identity, Layer, Option, Pipeable, Predicate, Ref, Scheduler, Scope, Tracer } from "effect" +import { Context, type Duration, Effect, Equivalence, Exit, Function, identity, Layer, Pipeable, Predicate, Scheduler, Scope, Tracer } from "effect" import * as React from "react" import * as ScopeRegistry from "./ScopeRegistry.js" @@ -668,54 +668,26 @@ export const useScope = Effect.fnUntraced(function*( // biome-ignore lint/style/noNonNullAssertion: context initialization const contextRef = React.useRef>(null!) contextRef.current = yield* Effect.context() + const registry = yield* ScopeRegistry.ScopeRegistry as unknown as Effect.Effect - const { key, scope } = React.useMemo(() => Effect.runSyncWith(contextRef.current)(Effect.Do.pipe( - Effect.bind("scopeRegistryRef", () => Effect.map( - ScopeRegistry.ScopeRegistry as unknown as Effect.Effect, - scopeRegistry => scopeRegistry.ref, - )), - Effect.let("key", () => Equal.byReference({})), - Effect.bind("scope", () => Scope.make(options?.finalizerExecutionStrategy ?? defaultOptions.finalizerExecutionStrategy)), - Effect.tap(({ scopeRegistryRef, key, scope }) => - Ref.update(scopeRegistryRef, HashMap.set(key, Equal.byReference({ - scope, - closeFiber: Option.none(), - }))) - ), + const [key, scope] = React.useMemo(() => Effect.runSyncWith(contextRef.current)(Effect.gen(function*() { + const key = ScopeRegistry.makeKey() + const entry = yield* registry.register(key, { + finalizerExecutionStrategy: options?.finalizerExecutionStrategy ?? defaultOptions.finalizerExecutionStrategy, + finalizerExecutionDebounce: options?.finalizerExecutionDebounce ?? defaultOptions.finalizerExecutionDebounce, + }) + + return [key, entry.scope] // biome-ignore lint/correctness/useExhaustiveDependencies: use of React.DependencyList - )), deps) + })), deps) // biome-ignore lint/correctness/useExhaustiveDependencies: only reactive on "key" - React.useEffect(() => Effect.runSyncWith(contextRef.current)( - (ScopeRegistry.ScopeRegistry as unknown as Effect.Effect).pipe( - Effect.map(scopeRegistry => scopeRegistry.ref), - Effect.tap(ref => Ref.get(ref).pipe( - Effect.flatMap(map => Effect.fromOption(HashMap.get(map, key))), - Effect.flatMap(entry => Option.match(entry.closeFiber, { - onSome: fiber => Effect.forkDetach(Fiber.interrupt(fiber)), - onNone: () => Effect.void, - })), - )), - Effect.map(ref => - () => Effect.runSyncWith(contextRef.current)(Effect.flatMap( - Effect.sleep(options?.finalizerExecutionDebounce ?? defaultOptions.finalizerExecutionDebounce).pipe( - Effect.andThen(Scope.close(scope, Exit.void)), - Effect.onExit(exit => Exit.match(exit, { - onSuccess: () => Ref.update(ref, HashMap.remove(key)), - onFailure: cause => Cause.hasInterruptsOnly(cause) - ? Effect.void - : Ref.update(ref, HashMap.remove(key)), - })), - Effect.forkDetach, - ), - fiber => Ref.update(ref, HashMap.set(key, Equal.byReference({ - scope, - closeFiber: Option.some(fiber), - }))), - )) - ), - ) - ), [key]) + React.useEffect(() => { + Effect.runSyncWith(contextRef.current)(registry.commit(key)) + return () => { + Effect.runSyncWith(contextRef.current)(registry.release(key)) + } + }, [key]) return scope }) diff --git a/packages/effect-fc-next/src/ReactRuntime.ts b/packages/effect-fc-next/src/ReactRuntime.ts index e9e18aa..52fd82c 100644 --- a/packages/effect-fc-next/src/ReactRuntime.ts +++ b/packages/effect-fc-next/src/ReactRuntime.ts @@ -17,7 +17,7 @@ export interface ReactRuntime { const ReactRuntimePrototype = Object.freeze({ [ReactRuntimeTypeId]: ReactRuntimeTypeId } as const) -export const preludeLayer: Layer.Layer = ScopeRegistry.ScopeRegistry.layer +export const preludeLayer: Layer.Layer = ScopeRegistry.layer export const isReactRuntime = (u: unknown): u is ReactRuntime => Predicate.hasProperty(u, ReactRuntimeTypeId) diff --git a/packages/effect-fc-next/src/ScopeRegistry.ts b/packages/effect-fc-next/src/ScopeRegistry.ts index d664426..7cbb846 100644 --- a/packages/effect-fc-next/src/ScopeRegistry.ts +++ b/packages/effect-fc-next/src/ScopeRegistry.ts @@ -1,4 +1,185 @@ -import { Context, Effect, type Fiber, HashMap, Layer, type Option, Ref, type Scope } from "effect" +import { type Cause, Context, DateTime, type Duration, Effect, Equal, Exit, HashMap, Layer, Option, Scope, Semaphore, Stream, SubscriptionRef } from "effect" + + +export interface ScopeRegistryService { + readonly ref: SubscriptionRef.SubscriptionRef> + + register( + key: ScopeRegistryService.Key, + options: ScopeRegistryService.RegisterOptions, + ): Effect.Effect + commit(key: ScopeRegistryService.Key): Effect.Effect + release(key: ScopeRegistryService.Key): Effect.Effect + + readonly run: Effect.Effect + readonly dispose: Effect.Effect +} + +export declare namespace ScopeRegistryService { + export type Key = object + + export interface RegisterOptions { + readonly finalizerExecutionStrategy: "sequential" | "parallel" + readonly finalizerExecutionDebounce: Duration.Input + } + + export interface Entry { + readonly scope: Scope.Closeable + readonly leases: number + readonly expiresAt: Option.Option + readonly finalizerExecutionDebounce: Duration.Input + } +} + +export const makeKey = (): ScopeRegistryService.Key => Equal.byReference({}) + + +export class ScopeRegistryServiceImpl implements ScopeRegistryService { + constructor( + readonly ref: SubscriptionRef.SubscriptionRef>, + readonly runSemaphore: Semaphore.Semaphore, + ) {} + + + register( + key: ScopeRegistryService.Key, + options: ScopeRegistryService.RegisterOptions, + ): Effect.Effect { + return Effect.gen({ self: this }, function*() { + const now = yield* DateTime.now + const entry = Equal.byReference({ + scope: yield* Scope.make(options.finalizerExecutionStrategy), + leases: 0, + expiresAt: Option.some(DateTime.addDuration(now, options.finalizerExecutionDebounce)), + finalizerExecutionDebounce: options.finalizerExecutionDebounce, + }) + + yield* SubscriptionRef.update(this.ref, HashMap.set(key, entry)) + + return entry + }) + } + + commit(key: ScopeRegistryService.Key): Effect.Effect { + return this.updateEntry(key, entry => Equal.byReference({ + ...entry, + leases: entry.leases + 1, + expiresAt: Option.none(), + })) + } + + release(key: ScopeRegistryService.Key): Effect.Effect { + return Effect.flatMap(DateTime.now, now => this.updateEntry(key, entry => { + if (entry.leases === 0) + return entry + + const leases = entry.leases - 1 + return Equal.byReference({ + ...entry, + leases, + expiresAt: leases === 0 + ? Option.some(DateTime.addDuration(now, entry.finalizerExecutionDebounce)) + : Option.none(), + }) + })) + } + + get run(): Effect.Effect { + return this.runSemaphore.withPermits(1)(SubscriptionRef.changes(this.ref).pipe( + Stream.switchMap(entries => Option.match(this.getNextExpiration(entries), { + onNone: () => Stream.never, + onSome: expiresAt => Stream.fromEffect(DateTime.now.pipe( + Effect.flatMap(now => DateTime.isLessThan(now, expiresAt) + ? Effect.sleep(DateTime.distance(now, expiresAt)) + : Effect.void), + Effect.andThen(Effect.uninterruptible(this.closeExpired)), + )), + })), + Stream.runDrain, + )) + } + + get dispose(): Effect.Effect { + return SubscriptionRef.getAndSet( + this.ref, + HashMap.empty(), + ).pipe( + Effect.flatMap(entries => Effect.forEach( + HashMap.values(entries), + entry => Scope.close(entry.scope, Exit.void), + )), + Effect.asVoid, + ) + } + + + updateEntry( + key: ScopeRegistryService.Key, + f: (entry: ScopeRegistryService.Entry) => ScopeRegistryService.Entry, + ): Effect.Effect { + return SubscriptionRef.modify(this.ref, entries => { + const current = HashMap.get(entries, key) + + if (current._tag === "None") + return [Option.none(), entries] + + const entry = f(current.value) + return [Option.some(entry), HashMap.set(entries, key, entry)] + }).pipe(Effect.flatMap(Effect.fromOption)) + } + + get closeExpired(): Effect.Effect { + return Effect.flatMap(DateTime.now, now => SubscriptionRef.modify( + this.ref, + entries => { + const expired: ScopeRegistryService.Entry[] = [] + const remaining = HashMap.filter(entries, entry => { + const shouldClose = entry.leases === 0 && Option.exists( + entry.expiresAt, + expiresAt => DateTime.isLessThanOrEqualTo(expiresAt, now), + ) + + if (shouldClose) + expired.push(entry) + + return !shouldClose + }) + + return [expired, remaining] + }, + )).pipe( + Effect.flatMap(entries => Effect.forEach( + entries, + entry => Scope.close(entry.scope, Exit.void), + )), + Effect.asVoid, + ) + } + + getNextExpiration( + entries: HashMap.HashMap, + ): Option.Option { + let earliest: DateTime.Utc | undefined + + for (const entry of HashMap.values(entries)) { + if ( + entry.leases === 0 && + entry.expiresAt._tag === "Some" && + (!earliest || DateTime.isLessThan(entry.expiresAt.value, earliest)) + ) + earliest = entry.expiresAt.value + } + + return Option.fromNullishOr(earliest) + } +} + +export const make: Effect.Effect = Effect.gen(function*() { + return new ScopeRegistryServiceImpl( + yield* SubscriptionRef.make(HashMap.empty()), + yield* Semaphore.make(1), + ) +}) /** @@ -7,20 +188,14 @@ import { Context, Effect, type Fiber, HashMap, Layer, type Option, Ref, type Sco * This service is used internally by the `Component.useScope` hook to manage the lifecycle of component scopes, * including tracking active scopes and coordinating their cleanup when components unmount or dependencies change. */ -export class ScopeRegistry extends Context.Service> -}>()( - "@effect-fc/ScopeRegistry/ScopeRegistry" -) { - static readonly layer = Layer.effect(ScopeRegistry, Effect.map( - Ref.make(HashMap.empty()), - ref => ({ ref }), - )) -} +export class ScopeRegistry extends Context.Service()( + "@effect-view/ScopeRegistry/ScopeRegistry" +) {} -export declare namespace ScopeRegistry { - export interface Entry { - readonly scope: Scope.Closeable - readonly closeFiber: Option.Option> - } -} +export const layer = Layer.effect(ScopeRegistry, Effect.tap( + make, + registry => Effect.andThen( + Effect.addFinalizer(() => registry.dispose), + Effect.forkScoped(registry.run), + ), +)) diff --git a/packages/effect-fc-next/test/Component.test.tsx b/packages/effect-fc-next/test/Component.test.tsx index 7e777f2..e015212 100644 --- a/packages/effect-fc-next/test/Component.test.tsx +++ b/packages/effect-fc-next/test/Component.test.tsx @@ -1,5 +1,5 @@ import { act, fireEvent, render, screen, waitFor } from "@testing-library/react" -import { Context, Effect, HashMap, Layer, Ref } from "effect" +import { Context, Effect, HashMap, Layer, SubscriptionRef } from "effect" import * as React from "react" import { afterEach, describe, expect, it, vi } from "vitest" import * as Component from "../src/Component.js" @@ -362,7 +362,7 @@ describe("Component", () => { await runtime.runtime.dispose() }) - it.fails("does not commit effects or retain registered scopes for a discarded Suspense render", async () => { + it("does not commit effects or retain registered scopes for a discarded Suspense render", async () => { const setup = vi.fn() const runtime = ReactRuntime.make(Layer.empty) const effectRuntime = await runtime.runtime.context() @@ -391,7 +391,7 @@ describe("Component", () => { try { await screen.findByText("fallback") expect(setup).not.toHaveBeenCalled() - expect(HashMap.size(Effect.runSync(Ref.get(scopeRegistry.ref)))).toBe(0) + await waitFor(() => expect(HashMap.size(Effect.runSync(SubscriptionRef.get(scopeRegistry.ref)))).toBe(0)) } finally { view.unmount() @@ -399,7 +399,45 @@ describe("Component", () => { } }) - it.fails("clears ScopeRegistry after a suspended render retries, commits, and unmounts", async () => { + it("keeps committed scopes alive without heartbeats", async () => { + const acquisitions = vi.fn() + const releases = vi.fn() + const runtime = ReactRuntime.make(Layer.empty) + const effectRuntime = await runtime.runtime.context() + const scopeRegistry = Context.get(effectRuntime, ScopeRegistry.ScopeRegistry) + + const Probe = Component.makeUntraced("CommittedScopeProbe")(function*() { + yield* Component.useOnMount(() => Effect.gen(function*() { + yield* Effect.sync(acquisitions) + yield* Effect.addFinalizer(() => Effect.sync(releases)) + })) + + return
committed
+ }).pipe( + Component.withOptions({ finalizerExecutionDebounce: "10 millis" }), + Component.withContext(runtime.context), + ) + + const view = render( + + + + ) + + await screen.findByText("committed") + await waitFor(() => expect(releases).toHaveBeenCalledTimes(acquisitions.mock.calls.length - 1)) + await new Promise(resolve => setTimeout(resolve, 30)) + + expect(HashMap.size(Effect.runSync(SubscriptionRef.get(scopeRegistry.ref)))).toBe(1) + expect(releases).toHaveBeenCalledTimes(acquisitions.mock.calls.length - 1) + + view.unmount() + await waitFor(() => expect(releases).toHaveBeenCalledTimes(acquisitions.mock.calls.length)) + await waitFor(() => expect(HashMap.size(Effect.runSync(SubscriptionRef.get(scopeRegistry.ref)))).toBe(0)) + await runtime.runtime.dispose() + }) + + it("clears ScopeRegistry after a suspended render retries, commits, and unmounts", async () => { const lifecycle = vi.fn<(message: string) => void>() const runtime = ReactRuntime.make(Layer.empty) const effectRuntime = await runtime.runtime.context() @@ -441,7 +479,7 @@ describe("Component", () => { await waitFor(() => expect(lifecycle).toHaveBeenCalledWith("mount")) view.unmount() - await waitFor(() => expect(HashMap.size(Effect.runSync(Ref.get(scopeRegistry.ref)))).toBe(0)) + await waitFor(() => expect(HashMap.size(Effect.runSync(SubscriptionRef.get(scopeRegistry.ref)))).toBe(0)) expect(lifecycle.mock.calls.filter(([message]) => message === "mount")).toHaveLength(2) expect(lifecycle.mock.calls.filter(([message]) => message === "cleanup")).toHaveLength(2) } -- 2.52.0 From 3516ba05f8501f7a6e4caee786b31ef07eac7e47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 23 Jul 2026 01:30:29 +0200 Subject: [PATCH 101/133] Fix --- packages/effect-fc-next/src/ScopeRegistry.ts | 28 +++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/packages/effect-fc-next/src/ScopeRegistry.ts b/packages/effect-fc-next/src/ScopeRegistry.ts index 7cbb846..58e9a75 100644 --- a/packages/effect-fc-next/src/ScopeRegistry.ts +++ b/packages/effect-fc-next/src/ScopeRegistry.ts @@ -1,4 +1,4 @@ -import { type Cause, Context, DateTime, type Duration, Effect, Equal, Exit, HashMap, Layer, Option, Scope, Semaphore, Stream, SubscriptionRef } from "effect" +import { type Cause, Context, DateTime, type Duration, Effect, Equal, Exit, HashMap, Layer, Option, Order, Scope, Semaphore, Stream, SubscriptionRef } from "effect" export interface ScopeRegistryService { @@ -159,18 +159,20 @@ export class ScopeRegistryServiceImpl implements ScopeRegistryService { getNextExpiration( entries: HashMap.HashMap, ): Option.Option { - let earliest: DateTime.Utc | undefined - - for (const entry of HashMap.values(entries)) { - if ( - entry.leases === 0 && - entry.expiresAt._tag === "Some" && - (!earliest || DateTime.isLessThan(entry.expiresAt.value, earliest)) - ) - earliest = entry.expiresAt.value - } - - return Option.fromNullishOr(earliest) + return HashMap.reduce( + entries, + Option.none(), + (earliest, entry) => Option.match( + Option.filter(entry.expiresAt, () => entry.leases === 0), + { + onNone: () => earliest, + onSome: expiresAt => Option.some(Option.match(earliest, { + onNone: () => expiresAt, + onSome: Order.min(DateTime.Order)(expiresAt), + })), + }, + ), + ) } } -- 2.52.0 From ccb33973db8170ff1022d6dbf8ac4503acc7df9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 23 Jul 2026 01:34:17 +0200 Subject: [PATCH 102/133] Fix --- packages/effect-fc-next/src/ScopeRegistry.ts | 40 ++++++-------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/packages/effect-fc-next/src/ScopeRegistry.ts b/packages/effect-fc-next/src/ScopeRegistry.ts index 58e9a75..2cb0ffd 100644 --- a/packages/effect-fc-next/src/ScopeRegistry.ts +++ b/packages/effect-fc-next/src/ScopeRegistry.ts @@ -25,7 +25,6 @@ export declare namespace ScopeRegistryService { export interface Entry { readonly scope: Scope.Closeable - readonly leases: number readonly expiresAt: Option.Option readonly finalizerExecutionDebounce: Duration.Input } @@ -49,13 +48,11 @@ export class ScopeRegistryServiceImpl implements ScopeRegistryService { const now = yield* DateTime.now const entry = Equal.byReference({ scope: yield* Scope.make(options.finalizerExecutionStrategy), - leases: 0, expiresAt: Option.some(DateTime.addDuration(now, options.finalizerExecutionDebounce)), finalizerExecutionDebounce: options.finalizerExecutionDebounce, }) yield* SubscriptionRef.update(this.ref, HashMap.set(key, entry)) - return entry }) } @@ -63,25 +60,15 @@ export class ScopeRegistryServiceImpl implements ScopeRegistryService { commit(key: ScopeRegistryService.Key): Effect.Effect { return this.updateEntry(key, entry => Equal.byReference({ ...entry, - leases: entry.leases + 1, expiresAt: Option.none(), })) } release(key: ScopeRegistryService.Key): Effect.Effect { - return Effect.flatMap(DateTime.now, now => this.updateEntry(key, entry => { - if (entry.leases === 0) - return entry - - const leases = entry.leases - 1 - return Equal.byReference({ - ...entry, - leases, - expiresAt: leases === 0 - ? Option.some(DateTime.addDuration(now, entry.finalizerExecutionDebounce)) - : Option.none(), - }) - })) + return Effect.flatMap(DateTime.now, now => this.updateEntry(key, entry => Equal.byReference({ + ...entry, + expiresAt: Option.some(DateTime.addDuration(now, entry.finalizerExecutionDebounce)), + }))) } get run(): Effect.Effect { @@ -134,7 +121,7 @@ export class ScopeRegistryServiceImpl implements ScopeRegistryService { entries => { const expired: ScopeRegistryService.Entry[] = [] const remaining = HashMap.filter(entries, entry => { - const shouldClose = entry.leases === 0 && Option.exists( + const shouldClose = Option.exists( entry.expiresAt, expiresAt => DateTime.isLessThanOrEqualTo(expiresAt, now), ) @@ -162,16 +149,13 @@ export class ScopeRegistryServiceImpl implements ScopeRegistryService { return HashMap.reduce( entries, Option.none(), - (earliest, entry) => Option.match( - Option.filter(entry.expiresAt, () => entry.leases === 0), - { - onNone: () => earliest, - onSome: expiresAt => Option.some(Option.match(earliest, { - onNone: () => expiresAt, - onSome: Order.min(DateTime.Order)(expiresAt), - })), - }, - ), + (earliest, entry) => Option.match(entry.expiresAt, { + onNone: () => earliest, + onSome: expiresAt => Option.some(Option.match(earliest, { + onNone: () => expiresAt, + onSome: Order.min(DateTime.Order)(expiresAt), + })), + }), ) } } -- 2.52.0 From c6eeb7b8bcb7169bc30d85b2bbbeb53cc57b9517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 23 Jul 2026 03:08:42 +0200 Subject: [PATCH 103/133] Fix --- packages/effect-fc-next/src/ScopeRegistry.ts | 55 +++++++++----------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/packages/effect-fc-next/src/ScopeRegistry.ts b/packages/effect-fc-next/src/ScopeRegistry.ts index 2cb0ffd..643f1d8 100644 --- a/packages/effect-fc-next/src/ScopeRegistry.ts +++ b/packages/effect-fc-next/src/ScopeRegistry.ts @@ -1,4 +1,4 @@ -import { type Cause, Context, DateTime, type Duration, Effect, Equal, Exit, HashMap, Layer, Option, Order, Scope, Semaphore, Stream, SubscriptionRef } from "effect" +import { type Cause, Context, DateTime, type Duration, Effect, Equal, Exit, flow, HashMap, Layer, Option, Order, Scope, Semaphore, Stream, SubscriptionRef } from "effect" export interface ScopeRegistryService { @@ -39,14 +39,13 @@ export class ScopeRegistryServiceImpl implements ScopeRegistryService { readonly runSemaphore: Semaphore.Semaphore, ) {} - register( key: ScopeRegistryService.Key, options: ScopeRegistryService.RegisterOptions, ): Effect.Effect { return Effect.gen({ self: this }, function*() { const now = yield* DateTime.now - const entry = Equal.byReference({ + const entry = Equal.byReference({ scope: yield* Scope.make(options.finalizerExecutionStrategy), expiresAt: Option.some(DateTime.addDuration(now, options.finalizerExecutionDebounce)), finalizerExecutionDebounce: options.finalizerExecutionDebounce, @@ -58,21 +57,34 @@ export class ScopeRegistryServiceImpl implements ScopeRegistryService { } commit(key: ScopeRegistryService.Key): Effect.Effect { - return this.updateEntry(key, entry => Equal.byReference({ - ...entry, - expiresAt: Option.none(), - })) + return SubscriptionRef.get(this.ref).pipe( + Effect.map(HashMap.get(key)), + Effect.flatMap(Effect.fromOption), + Effect.map(entry => Equal.byReference({ + ...entry, + expiresAt: Option.none(), + })), + Effect.tap(entry => SubscriptionRef.update(this.ref, HashMap.set(key, entry))), + ) } release(key: ScopeRegistryService.Key): Effect.Effect { - return Effect.flatMap(DateTime.now, now => this.updateEntry(key, entry => Equal.byReference({ - ...entry, - expiresAt: Option.some(DateTime.addDuration(now, entry.finalizerExecutionDebounce)), - }))) + return SubscriptionRef.get(this.ref).pipe( + Effect.map(HashMap.get(key)), + Effect.flatMap(option => Effect.all([ + DateTime.now, + Effect.fromOption(option), + ])), + Effect.map(([now, entry]) => Equal.byReference({ + ...entry, + expiresAt: Option.some(DateTime.addDuration(now, entry.finalizerExecutionDebounce)), + })), + Effect.tap(entry => SubscriptionRef.update(this.ref, HashMap.set(key, entry))), + ) } get run(): Effect.Effect { - return this.runSemaphore.withPermits(1)(SubscriptionRef.changes(this.ref).pipe( + return SubscriptionRef.changes(this.ref).pipe( Stream.switchMap(entries => Option.match(this.getNextExpiration(entries), { onNone: () => Stream.never, onSome: expiresAt => Stream.fromEffect(DateTime.now.pipe( @@ -83,7 +95,8 @@ export class ScopeRegistryServiceImpl implements ScopeRegistryService { )), })), Stream.runDrain, - )) + this.runSemaphore.withPermit, + ) } get dispose(): Effect.Effect { @@ -99,22 +112,6 @@ export class ScopeRegistryServiceImpl implements ScopeRegistryService { ) } - - updateEntry( - key: ScopeRegistryService.Key, - f: (entry: ScopeRegistryService.Entry) => ScopeRegistryService.Entry, - ): Effect.Effect { - return SubscriptionRef.modify(this.ref, entries => { - const current = HashMap.get(entries, key) - - if (current._tag === "None") - return [Option.none(), entries] - - const entry = f(current.value) - return [Option.some(entry), HashMap.set(entries, key, entry)] - }).pipe(Effect.flatMap(Effect.fromOption)) - } - get closeExpired(): Effect.Effect { return Effect.flatMap(DateTime.now, now => SubscriptionRef.modify( this.ref, -- 2.52.0 From 7e816d041e1900756faa855b974869d592f6586d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 23 Jul 2026 03:13:17 +0200 Subject: [PATCH 104/133] Fix --- packages/effect-fc-next/src/ScopeRegistry.ts | 31 +++++++++----------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/packages/effect-fc-next/src/ScopeRegistry.ts b/packages/effect-fc-next/src/ScopeRegistry.ts index 643f1d8..7720aaf 100644 --- a/packages/effect-fc-next/src/ScopeRegistry.ts +++ b/packages/effect-fc-next/src/ScopeRegistry.ts @@ -1,4 +1,4 @@ -import { type Cause, Context, DateTime, type Duration, Effect, Equal, Exit, flow, HashMap, Layer, Option, Order, Scope, Semaphore, Stream, SubscriptionRef } from "effect" +import { type Cause, Chunk, Context, DateTime, type Duration, Effect, Equal, Exit, HashMap, Layer, Option, Order, Scope, Semaphore, Stream, SubscriptionRef } from "effect" export interface ScopeRegistryService { @@ -115,22 +115,19 @@ export class ScopeRegistryServiceImpl implements ScopeRegistryService { get closeExpired(): Effect.Effect { return Effect.flatMap(DateTime.now, now => SubscriptionRef.modify( this.ref, - entries => { - const expired: ScopeRegistryService.Entry[] = [] - const remaining = HashMap.filter(entries, entry => { - const shouldClose = Option.exists( - entry.expiresAt, - expiresAt => DateTime.isLessThanOrEqualTo(expiresAt, now), - ) - - if (shouldClose) - expired.push(entry) - - return !shouldClose - }) - - return [expired, remaining] - }, + entries => HashMap.reduce( + entries, + [ + Chunk.empty(), + HashMap.empty(), + ] as const, + ([expired, remaining], entry, key) => Option.exists( + entry.expiresAt, + expiresAt => DateTime.isLessThanOrEqualTo(expiresAt, now), + ) + ? [Chunk.append(expired, entry), remaining] as const + : [expired, HashMap.set(remaining, key, entry)] as const, + ), )).pipe( Effect.flatMap(entries => Effect.forEach( entries, -- 2.52.0 From cce0a47ea553cfcf15ce706462e396100533a34e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 23 Jul 2026 03:22:16 +0200 Subject: [PATCH 105/133] Fix --- packages/effect-fc-next/src/ScopeRegistry.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/effect-fc-next/src/ScopeRegistry.ts b/packages/effect-fc-next/src/ScopeRegistry.ts index 7720aaf..dc5cb67 100644 --- a/packages/effect-fc-next/src/ScopeRegistry.ts +++ b/packages/effect-fc-next/src/ScopeRegistry.ts @@ -44,10 +44,9 @@ export class ScopeRegistryServiceImpl implements ScopeRegistryService { options: ScopeRegistryService.RegisterOptions, ): Effect.Effect { return Effect.gen({ self: this }, function*() { - const now = yield* DateTime.now const entry = Equal.byReference({ scope: yield* Scope.make(options.finalizerExecutionStrategy), - expiresAt: Option.some(DateTime.addDuration(now, options.finalizerExecutionDebounce)), + expiresAt: Option.some(DateTime.addDuration(yield* DateTime.now, options.finalizerExecutionDebounce)), finalizerExecutionDebounce: options.finalizerExecutionDebounce, }) @@ -115,12 +114,12 @@ export class ScopeRegistryServiceImpl implements ScopeRegistryService { get closeExpired(): Effect.Effect { return Effect.flatMap(DateTime.now, now => SubscriptionRef.modify( this.ref, - entries => HashMap.reduce( - entries, + HashMap.reduce( [ Chunk.empty(), HashMap.empty(), ] as const, + ([expired, remaining], entry, key) => Option.exists( entry.expiresAt, expiresAt => DateTime.isLessThanOrEqualTo(expiresAt, now), -- 2.52.0 From d09c17e1c233361a41ddd20f628195f9651cac80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 23 Jul 2026 03:26:14 +0200 Subject: [PATCH 106/133] Fix --- packages/effect-fc-next/src/ScopeRegistry.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/effect-fc-next/src/ScopeRegistry.ts b/packages/effect-fc-next/src/ScopeRegistry.ts index dc5cb67..0bdee76 100644 --- a/packages/effect-fc-next/src/ScopeRegistry.ts +++ b/packages/effect-fc-next/src/ScopeRegistry.ts @@ -12,7 +12,6 @@ export interface ScopeRegistryService { release(key: ScopeRegistryService.Key): Effect.Effect readonly run: Effect.Effect - readonly dispose: Effect.Effect } export declare namespace ScopeRegistryService { @@ -94,6 +93,7 @@ export class ScopeRegistryServiceImpl implements ScopeRegistryService { )), })), Stream.runDrain, + Effect.ensuring(this.dispose), this.runSemaphore.withPermit, ) } @@ -173,8 +173,5 @@ export class ScopeRegistry extends Context.Service Effect.andThen( - Effect.addFinalizer(() => registry.dispose), - Effect.forkScoped(registry.run), - ), + registry => Effect.forkScoped(registry.run), )) -- 2.52.0 From 71f66cd5bc948cb37d3d38d2963b8634589ac657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 23 Jul 2026 03:33:15 +0200 Subject: [PATCH 107/133] Fix --- packages/effect-fc-next/src/View.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/effect-fc-next/src/View.ts b/packages/effect-fc-next/src/View.ts index 04ab6f3..e44d62a 100644 --- a/packages/effect-fc-next/src/View.ts +++ b/packages/effect-fc-next/src/View.ts @@ -28,8 +28,10 @@ export const useAll = Effect.fnUntraced(function* Effect.all(elements.map(View.get))) ) - yield* Component.useReactEffect(() => View.changes(View.zipLatestAll(...elements)).pipe( + yield* Component.useReactEffect(() => Stream.make(reactStateValue).pipe( + Stream.concat(View.changes(View.zipLatestAll(...elements))), Stream.changesWith((options?.equivalence as Equivalence.Equivalence | undefined) ?? Equivalence.Array(Equivalence.strictEqual())), + Stream.drop(1), Stream.runForEach(v => Effect.sync(() => setReactStateValue(v)) ), -- 2.52.0 From d8b9c0394c3feb1ce4972ccf9a696ee16b31f4e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 23 Jul 2026 03:35:38 +0200 Subject: [PATCH 108/133] Fix --- packages/effect-fc-next/src/ScopeRegistry.ts | 29 ++++++++++---------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/packages/effect-fc-next/src/ScopeRegistry.ts b/packages/effect-fc-next/src/ScopeRegistry.ts index 0bdee76..1506987 100644 --- a/packages/effect-fc-next/src/ScopeRegistry.ts +++ b/packages/effect-fc-next/src/ScopeRegistry.ts @@ -11,7 +11,7 @@ export interface ScopeRegistryService { commit(key: ScopeRegistryService.Key): Effect.Effect release(key: ScopeRegistryService.Key): Effect.Effect - readonly run: Effect.Effect + readonly run: Effect.Effect } export declare namespace ScopeRegistryService { @@ -81,19 +81,20 @@ export class ScopeRegistryServiceImpl implements ScopeRegistryService { ) } - get run(): Effect.Effect { - return SubscriptionRef.changes(this.ref).pipe( - Stream.switchMap(entries => Option.match(this.getNextExpiration(entries), { - onNone: () => Stream.never, - onSome: expiresAt => Stream.fromEffect(DateTime.now.pipe( - Effect.flatMap(now => DateTime.isLessThan(now, expiresAt) - ? Effect.sleep(DateTime.distance(now, expiresAt)) - : Effect.void), - Effect.andThen(Effect.uninterruptible(this.closeExpired)), - )), - })), - Stream.runDrain, - Effect.ensuring(this.dispose), + get run(): Effect.Effect { + return Effect.addFinalizer(() => this.dispose).pipe( + Effect.andThen(SubscriptionRef.changes(this.ref).pipe( + Stream.switchMap(entries => Option.match(this.getNextExpiration(entries), { + onNone: () => Stream.never, + onSome: expiresAt => Stream.fromEffect(DateTime.now.pipe( + Effect.flatMap(now => DateTime.isLessThan(now, expiresAt) + ? Effect.sleep(DateTime.distance(now, expiresAt)) + : Effect.void), + Effect.andThen(Effect.uninterruptible(this.closeExpired)), + )), + })), + Stream.runDrain, + )), this.runSemaphore.withPermit, ) } -- 2.52.0 From 296f65ee1b3e0cf12349ae1ffd5010df1c0a7ba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 23 Jul 2026 04:13:27 +0200 Subject: [PATCH 109/133] Fix --- packages/effect-fc-next/src/Component.ts | 10 +++++ packages/effect-fc-next/src/ScopeRegistry.ts | 3 +- .../effect-fc-next/test/Component.test.tsx | 43 ++++++++++++++++++- 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/packages/effect-fc-next/src/Component.ts b/packages/effect-fc-next/src/Component.ts index 6023781..adcafd1 100644 --- a/packages/effect-fc-next/src/Component.ts +++ b/packages/effect-fc-next/src/Component.ts @@ -153,12 +153,20 @@ export interface ComponentOptions { * @default "100 millis" */ readonly finalizerExecutionDebounce: Duration.Input + + /** + * Maximum duration an uncommitted scope may remain registered before it is considered abandoned. + * + * @default "30 seconds" + */ + readonly scopeCommitTimeout: Duration.Input } export const defaultOptions: ComponentOptions = { nonReactiveTags: [Tracer.ParentSpan, Scheduler.Scheduler, Layer.CurrentMemoMap], finalizerExecutionStrategy: "sequential", finalizerExecutionDebounce: "100 millis", + scopeCommitTimeout: "1 minute", } @@ -641,6 +649,7 @@ export declare namespace useScope { export interface Options { readonly finalizerExecutionStrategy?: "sequential" | "parallel" readonly finalizerExecutionDebounce?: Duration.Input + readonly scopeCommitTimeout?: Duration.Input } } @@ -675,6 +684,7 @@ export const useScope = Effect.fnUntraced(function*( const entry = yield* registry.register(key, { finalizerExecutionStrategy: options?.finalizerExecutionStrategy ?? defaultOptions.finalizerExecutionStrategy, finalizerExecutionDebounce: options?.finalizerExecutionDebounce ?? defaultOptions.finalizerExecutionDebounce, + scopeCommitTimeout: options?.scopeCommitTimeout ?? defaultOptions.scopeCommitTimeout, }) return [key, entry.scope] diff --git a/packages/effect-fc-next/src/ScopeRegistry.ts b/packages/effect-fc-next/src/ScopeRegistry.ts index 1506987..e5307b3 100644 --- a/packages/effect-fc-next/src/ScopeRegistry.ts +++ b/packages/effect-fc-next/src/ScopeRegistry.ts @@ -20,6 +20,7 @@ export declare namespace ScopeRegistryService { export interface RegisterOptions { readonly finalizerExecutionStrategy: "sequential" | "parallel" readonly finalizerExecutionDebounce: Duration.Input + readonly scopeCommitTimeout: Duration.Input } export interface Entry { @@ -45,7 +46,7 @@ export class ScopeRegistryServiceImpl implements ScopeRegistryService { return Effect.gen({ self: this }, function*() { const entry = Equal.byReference({ scope: yield* Scope.make(options.finalizerExecutionStrategy), - expiresAt: Option.some(DateTime.addDuration(yield* DateTime.now, options.finalizerExecutionDebounce)), + expiresAt: Option.some(DateTime.addDuration(yield* DateTime.now, options.scopeCommitTimeout)), finalizerExecutionDebounce: options.finalizerExecutionDebounce, }) diff --git a/packages/effect-fc-next/test/Component.test.tsx b/packages/effect-fc-next/test/Component.test.tsx index e015212..dffcc0b 100644 --- a/packages/effect-fc-next/test/Component.test.tsx +++ b/packages/effect-fc-next/test/Component.test.tsx @@ -374,6 +374,7 @@ describe("Component", () => { React.use(pending) return
committed
}).pipe( + Component.withOptions({ scopeCommitTimeout: "10 millis" }), Component.withContext(runtime.context) ) @@ -414,7 +415,10 @@ describe("Component", () => { return
committed
}).pipe( - Component.withOptions({ finalizerExecutionDebounce: "10 millis" }), + Component.withOptions({ + finalizerExecutionDebounce: "10 millis", + scopeCommitTimeout: "10 millis", + }), Component.withContext(runtime.context), ) @@ -437,6 +441,42 @@ describe("Component", () => { await runtime.runtime.dispose() }) + it("does not expire a scope while a descendant is suspended past the finalizer debounce", async () => { + const runtime = ReactRuntime.make(Layer.empty) + let resolve!: () => void + const pending = new Promise(complete => { + resolve = complete + }) + + const Suspended = () => { + React.use(pending) + return
committed
+ } + + let view!: ReturnType + await act(async () => { + view = render( + fallback}> + + + ) + }) + + try { + await screen.findByText("fallback") + await new Promise(resolve => setTimeout(resolve, 150)) + await act(async () => { + resolve() + await pending + }) + await screen.findByText("committed") + } + finally { + view.unmount() + await runtime.runtime.dispose() + } + }) + it("clears ScopeRegistry after a suspended render retries, commits, and unmounts", async () => { const lifecycle = vi.fn<(message: string) => void>() const runtime = ReactRuntime.make(Layer.empty) @@ -455,6 +495,7 @@ describe("Component", () => { }), []) return
committed
}).pipe( + Component.withOptions({ scopeCommitTimeout: "10 millis" }), Component.withContext(runtime.context) ) -- 2.52.0 From 3a25b1a34e33a009042d44754e200b6e616abc1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 23 Jul 2026 04:36:31 +0200 Subject: [PATCH 110/133] Fix example --- packages/example-next/src/routeTree.gen.ts | 33 ++------------- packages/example-next/src/routes/__root.tsx | 1 - packages/example-next/src/routes/async.tsx | 17 +++++++- packages/example-next/src/routes/result.tsx | 45 --------------------- 4 files changed, 18 insertions(+), 78 deletions(-) delete mode 100644 packages/example-next/src/routes/result.tsx diff --git a/packages/example-next/src/routeTree.gen.ts b/packages/example-next/src/routeTree.gen.ts index 5904b93..b70ab97 100644 --- a/packages/example-next/src/routeTree.gen.ts +++ b/packages/example-next/src/routeTree.gen.ts @@ -15,7 +15,6 @@ import { Route as BlankRouteImport } from './routes/blank' import { Route as FormRouteImport } from './routes/form' import { Route as LensformRouteImport } from './routes/lensform' import { Route as QueryRouteImport } from './routes/query' -import { Route as ResultRouteImport } from './routes/result' const IndexRoute = IndexRouteImport.update({ id: '/', @@ -47,11 +46,6 @@ const QueryRoute = QueryRouteImport.update({ path: '/query', getParentRoute: () => rootRouteImport, } as any) -const ResultRoute = ResultRouteImport.update({ - id: '/result', - path: '/result', - getParentRoute: () => rootRouteImport, -} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -60,7 +54,6 @@ export interface FileRoutesByFullPath { '/form': typeof FormRoute '/lensform': typeof LensformRoute '/query': typeof QueryRoute - '/result': typeof ResultRoute } export interface FileRoutesByTo { '/': typeof IndexRoute @@ -69,7 +62,6 @@ export interface FileRoutesByTo { '/form': typeof FormRoute '/lensform': typeof LensformRoute '/query': typeof QueryRoute - '/result': typeof ResultRoute } export interface FileRoutesById { __root__: typeof rootRouteImport @@ -79,23 +71,13 @@ export interface FileRoutesById { '/form': typeof FormRoute '/lensform': typeof LensformRoute '/query': typeof QueryRoute - '/result': typeof ResultRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: - '/' | '/async' | '/blank' | '/form' | '/lensform' | '/query' | '/result' + fullPaths: '/' | '/async' | '/blank' | '/form' | '/lensform' | '/query' fileRoutesByTo: FileRoutesByTo - to: '/' | '/async' | '/blank' | '/form' | '/lensform' | '/query' | '/result' - id: - | '__root__' - | '/' - | '/async' - | '/blank' - | '/form' - | '/lensform' - | '/query' - | '/result' + to: '/' | '/async' | '/blank' | '/form' | '/lensform' | '/query' + id: '__root__' | '/' | '/async' | '/blank' | '/form' | '/lensform' | '/query' fileRoutesById: FileRoutesById } export interface RootRouteChildren { @@ -105,7 +87,6 @@ export interface RootRouteChildren { FormRoute: typeof FormRoute LensformRoute: typeof LensformRoute QueryRoute: typeof QueryRoute - ResultRoute: typeof ResultRoute } declare module '@tanstack/react-router' { @@ -152,13 +133,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof QueryRouteImport parentRoute: typeof rootRouteImport } - '/result': { - id: '/result' - path: '/result' - fullPath: '/result' - preLoaderRoute: typeof ResultRouteImport - parentRoute: typeof rootRouteImport - } } } @@ -169,7 +143,6 @@ const rootRouteChildren: RootRouteChildren = { FormRoute: FormRoute, LensformRoute: LensformRoute, QueryRoute: QueryRoute, - ResultRoute: ResultRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) diff --git a/packages/example-next/src/routes/__root.tsx b/packages/example-next/src/routes/__root.tsx index 783b032..aba3a8d 100644 --- a/packages/example-next/src/routes/__root.tsx +++ b/packages/example-next/src/routes/__root.tsx @@ -19,7 +19,6 @@ function Root() { Blank Async Query - Result Form LensForm diff --git a/packages/example-next/src/routes/async.tsx b/packages/example-next/src/routes/async.tsx index 44c41a9..45c12c2 100644 --- a/packages/example-next/src/routes/async.tsx +++ b/packages/example-next/src/routes/async.tsx @@ -1,11 +1,19 @@ import { Container, Flex, Heading, Slider, Text, TextField } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" +import { Console, Effect } from "effect" import { Async, Component, Memoized } from "effect-fc-next" import * as React from "react" -import { fetchPost } from "@/post" import { runtime } from "@/runtime" +const fetchPost = (id: number) => Effect.sleep("500 millis").pipe( + Effect.as({ + title: `Post ${id}`, + body: `This is the content of post ${id}.`, + }), +) + + interface AsyncFetchPostViewProps { readonly id: number } @@ -13,6 +21,11 @@ interface AsyncFetchPostViewProps { const AsyncFetchPostView = Component.make("AsyncFetchPostView")(function*( props: AsyncFetchPostViewProps, ) { + yield* Component.useOnMount(() => Effect.gen(function*() { + yield* Effect.addFinalizer(() => Console.log("AsyncFetchPostView unmounted")) + yield* Console.log("AsyncFetchPostView mounted") + })) + const post = yield* Component.useOnChange( () => fetchPost(props.id), [props.id], @@ -51,7 +64,7 @@ const AsyncRouteComponent = Component.make("AsyncRouteView")(function*() { onValueChange={([value]) => setId(value ?? 1)} /> - Loading post...} /> + ) diff --git a/packages/example-next/src/routes/result.tsx b/packages/example-next/src/routes/result.tsx deleted file mode 100644 index 3ca96cb..0000000 --- a/packages/example-next/src/routes/result.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { Button, Container, Flex, Heading, Text } from "@radix-ui/themes" -import { createFileRoute } from "@tanstack/react-router" -import { AsyncResult } from "effect/unstable/reactivity" -import { Component, Mutation, View } from "effect-fc-next" -import { fetchPost, type Post } from "@/post" -import { runtime } from "@/runtime" - - -const PostResultView = (props: { readonly result: AsyncResult.AsyncResult }) => - AsyncResult.match(props.result, { - onInitial: () => Ready to load., - onFailure: () => Request failed., - onSuccess: result => ( - <> - {result.value.title} - {result.value.body} - - ), - }) - -const ResultRouteComponent = Component.make("ResultRouteView")(function*() { - const mutation = yield* Component.useOnMount(() => Mutation.make({ - f: (_: undefined) => fetchPost(1), - })) - const [result] = yield* View.useAll([mutation.state]) - const runPromise = yield* Component.useRunPromise() - - return ( - - - - - - - - ) -}).pipe( - Component.withContext(runtime.context), -) - -export const Route = createFileRoute("/result")({ - component: ResultRouteComponent, -}) -- 2.52.0 From faac0215272c9e020266078dfe596631fb834441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 23 Jul 2026 04:58:41 +0200 Subject: [PATCH 111/133] Fix Async --- packages/effect-fc-next/src/Component.ts | 12 +++- packages/effect-fc-next/test/Async.test.tsx | 63 +++++++++++++++++++++ 2 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 packages/effect-fc-next/test/Async.test.tsx diff --git a/packages/effect-fc-next/src/Component.ts b/packages/effect-fc-next/src/Component.ts index adcafd1..e17caf2 100644 --- a/packages/effect-fc-next/src/Component.ts +++ b/packages/effect-fc-next/src/Component.ts @@ -1,6 +1,6 @@ /** biome-ignore-all lint/complexity/noBannedTypes: {} is the default type for React props */ /** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */ -import { Context, type Duration, Effect, Equivalence, Exit, Function, identity, Layer, Pipeable, Predicate, Scheduler, Scope, Tracer } from "effect" +import { Context, type Duration, Effect, Equivalence, Exit, Function, identity, Layer, Pipeable, Predicate, References, Scheduler, Scope, Tracer } from "effect" import * as React from "react" import * as ScopeRegistry from "./ScopeRegistry.js" @@ -133,7 +133,7 @@ export interface ComponentOptions { /** * Context tags that should not trigger component remount when their values change. * - * @default [Tracer.ParentSpan] + * @default [Tracer.ParentSpan, References.CurrentStackFrame, Scheduler.Scheduler, Layer.CurrentMemoMap, Scope.Scope] */ readonly nonReactiveTags: readonly Context.Key[] @@ -163,7 +163,13 @@ export interface ComponentOptions { } export const defaultOptions: ComponentOptions = { - nonReactiveTags: [Tracer.ParentSpan, Scheduler.Scheduler, Layer.CurrentMemoMap], + nonReactiveTags: [ + Tracer.ParentSpan, + References.CurrentStackFrame, + Scheduler.Scheduler, + Layer.CurrentMemoMap, + Scope.Scope, + ], finalizerExecutionStrategy: "sequential", finalizerExecutionDebounce: "100 millis", scopeCommitTimeout: "1 minute", diff --git a/packages/effect-fc-next/test/Async.test.tsx b/packages/effect-fc-next/test/Async.test.tsx new file mode 100644 index 0000000..91182d2 --- /dev/null +++ b/packages/effect-fc-next/test/Async.test.tsx @@ -0,0 +1,63 @@ +import { act, fireEvent, render, screen } from "@testing-library/react" +import { Effect, Layer } from "effect" +import * as React from "react" +import { describe, expect, it, vi } from "vitest" +import * as Async from "../src/Async.js" +import * as Component from "../src/Component.js" +import * as Memoized from "../src/Memoized.js" +import * as ReactRuntime from "../src/ReactRuntime.js" + +describe("Async", () => { + it("does not rerun for an unrelated parent state update", async () => { + const load = vi.fn((_id: number) => Effect.never) + const renderPost = vi.fn() + const runtime = ReactRuntime.make(Layer.empty) + const context = await runtime.runtime.context() + + const Post = Component.make("Post")(function*(props: { readonly id: number }) { + renderPost() + const value = yield* Component.useOnChange(() => load(props.id), [props.id]) + return
{value}
+ }).pipe( + Async.async, + Memoized.memoized, + ) + + const Parent = Component.make("Parent")(function*() { + const [text, setText] = React.useState("") + const AsyncPost = yield* Post.use + + return <> + setText(event.currentTarget.value)} + /> + loading} /> + + }).pipe(Component.withContext(runtime.context)) + + let view!: ReturnType + await act(async () => { + view = render( + + + , + ) + }) + + expect(screen.getByText("loading")).toBeTruthy() + expect(load).toHaveBeenCalledTimes(2) + const callsAfterLoad = load.mock.calls.length + const rendersAfterLoad = renderPost.mock.calls.length + + await act(async () => { + fireEvent.change(screen.getByLabelText("text"), { target: { value: "a" } }) + }) + expect(load).toHaveBeenCalledTimes(callsAfterLoad) + expect(renderPost).toHaveBeenCalledTimes(rendersAfterLoad) + + view.unmount() + await runtime.runtime.dispose() + }) +}) -- 2.52.0 From 7d6bab81bead1c8274b33b4bd8557a4be7d72155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 24 Jul 2026 23:07:06 +0200 Subject: [PATCH 112/133] Fix ScopeRegistry --- packages/effect-fc-next/src/ScopeRegistry.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/effect-fc-next/src/ScopeRegistry.ts b/packages/effect-fc-next/src/ScopeRegistry.ts index e5307b3..cdd2035 100644 --- a/packages/effect-fc-next/src/ScopeRegistry.ts +++ b/packages/effect-fc-next/src/ScopeRegistry.ts @@ -1,7 +1,11 @@ -import { type Cause, Chunk, Context, DateTime, type Duration, Effect, Equal, Exit, HashMap, Layer, Option, Order, Scope, Semaphore, Stream, SubscriptionRef } from "effect" +import { type Cause, Chunk, Context, DateTime, type Duration, Effect, Equal, Exit, HashMap, Layer, Option, Order, Predicate, Scope, Semaphore, Stream, SubscriptionRef } from "effect" +export const ScopeRegistryServiceTypeId: unique symbol = Symbol.for("@effect-view/ScopeRegistryService/ScopeRegistryService") +export type ScopeRegistryServiceTypeId = typeof ScopeRegistryServiceTypeId + export interface ScopeRegistryService { + readonly [ScopeRegistryServiceTypeId]: ScopeRegistryServiceTypeId readonly ref: SubscriptionRef.SubscriptionRef> register( @@ -30,10 +34,13 @@ export declare namespace ScopeRegistryService { } } +export const isScopeRegistryService = (u: unknown): u is ScopeRegistryService => Predicate.hasProperty(u, ScopeRegistryServiceTypeId) export const makeKey = (): ScopeRegistryService.Key => Equal.byReference({}) export class ScopeRegistryServiceImpl implements ScopeRegistryService { + readonly [ScopeRegistryServiceTypeId]: ScopeRegistryServiceTypeId = ScopeRegistryServiceTypeId + constructor( readonly ref: SubscriptionRef.SubscriptionRef>, readonly runSemaphore: Semaphore.Semaphore, -- 2.52.0 From 116c1b440d16a73b4eb549a5749acddb617c63c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 24 Jul 2026 23:42:22 +0200 Subject: [PATCH 113/133] Add tests to CI --- .gitea/workflows/lint.yaml | 2 ++ package.json | 3 ++- packages/effect-fc-next/package.json | 2 +- turbo.json | 3 +++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/lint.yaml b/.gitea/workflows/lint.yaml index c13cd5d..0a3d7b6 100644 --- a/.gitea/workflows/lint.yaml +++ b/.gitea/workflows/lint.yaml @@ -16,3 +16,5 @@ jobs: run: bun lint:tsc - name: Lint Biome run: bun lint:biome + - name: Test + run: bun run test diff --git a/package.json b/package.json index 3005ac9..b50664d 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,10 @@ "./packages/*" ], "scripts": { - "build": "turbo build", "lint:tsc": "turbo lint:tsc", "lint:biome": "turbo lint:biome", + "test": "turbo test", + "build": "turbo build", "pack": "turbo pack", "clean:cache": "turbo clean:cache", "clean:dist": "turbo clean:dist", diff --git a/packages/effect-fc-next/package.json b/packages/effect-fc-next/package.json index 91b4e19..e60d411 100644 --- a/packages/effect-fc-next/package.json +++ b/packages/effect-fc-next/package.json @@ -29,10 +29,10 @@ ] }, "scripts": { - "build": "tsc", "lint:tsc": "tsc --noEmit", "lint:biome": "biome lint", "test": "vitest run", + "build": "tsc", "pack": "npm pack", "clean:cache": "rm -rf .turbo tsconfig.tsbuildinfo", "clean:dist": "rm -rf dist", diff --git a/turbo.json b/turbo.json index 75a43f8..fcbaf52 100644 --- a/turbo.json +++ b/turbo.json @@ -8,6 +8,9 @@ "lint:biome": { "cache": false }, + "test": { + "cache": false + }, "build": { "dependsOn": ["^build"], "inputs": ["./src/**"], -- 2.52.0 From 462895a3c234bb56fa34060e2379881e6a8aa49f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 25 Jul 2026 02:02:17 +0200 Subject: [PATCH 114/133] Add Refreshable trait --- REACT_REFRESH_FEASIBILITY.md | 54 +-- bun.lock | 19 + packages/effect-fc-next/package.json | 82 +++- packages/effect-fc-next/src/Component.ts | 41 +- packages/effect-fc-next/src/Refreshable.ts | 129 ++++++ packages/effect-fc-next/src/index.ts | 1 + .../effect-fc-next/test/Component.test.tsx | 39 ++ .../effect-fc-next/test/Refreshable.test.ts | 40 ++ packages/example-next/package.json | 1 + packages/example-next/vite.config.ts | 2 + packages/vite/README.md | 42 ++ packages/vite/biome.json | 8 + packages/vite/package.json | 47 ++ packages/vite/src/index.ts | 435 ++++++++++++++++++ packages/vite/src/runtime.ts | 85 ++++ packages/vite/test/plugin.test.ts | 132 ++++++ packages/vite/test/runtime.test.ts | 68 +++ packages/vite/tsconfig.json | 19 + packages/vite/vitest.config.ts | 8 + 19 files changed, 1209 insertions(+), 43 deletions(-) create mode 100644 packages/effect-fc-next/src/Refreshable.ts create mode 100644 packages/effect-fc-next/test/Refreshable.test.ts create mode 100644 packages/vite/README.md create mode 100644 packages/vite/biome.json create mode 100644 packages/vite/package.json create mode 100644 packages/vite/src/index.ts create mode 100644 packages/vite/src/runtime.ts create mode 100644 packages/vite/test/plugin.test.ts create mode 100644 packages/vite/test/runtime.test.ts create mode 100644 packages/vite/tsconfig.json create mode 100644 packages/vite/vitest.config.ts diff --git a/REACT_REFRESH_FEASIBILITY.md b/REACT_REFRESH_FEASIBILITY.md index b14f4ba..3ae7583 100644 --- a/REACT_REFRESH_FEASIBILITY.md +++ b/REACT_REFRESH_FEASIBILITY.md @@ -13,7 +13,7 @@ The current API has two identities: Vite's React Refresh transform sees the descriptor definition, while React reconciles the synthesized function. In addition, `.use` deliberately caches that function. A newly evaluated module therefore creates a new descriptor, but the mounted function continues to close over the old descriptor. -The recommended implementation is a small development-only runtime bridge plus a Vite compiler plugin. The plugin should assign stable source IDs and hook signatures to Effect View definitions. The runtime should retain a hot cell for each definition, notify mounted instances after an update, preserve state when the signature is compatible, and remount when it is not. +The recommended implementation is a bundler-neutral development refresh protocol in Effect View plus a Vite compiler adapter. The plugin should assign stable source IDs and hook signatures to Effect View definitions. The main library's protocol should retain the current descriptor, notify mounted instances after an update, preserve state when the signature is compatible, and remount when it is not. The Vite adapter should retain those cells in `import.meta.hot.data` and manage accept/invalidate behavior. My feasibility assessment is: @@ -26,16 +26,13 @@ A production-quality Vite implementation is likely **10–18 engineer-days**, in ## Scope examined -The findings apply to both implementations in this repository: - -- `effect-fc` (Effect 3) -- `effect-fc-next` (Effect 4 beta) - -The two packages differ in Effect runtime details, but their React component identity model is the same. The examples also use the same Vite 8 and `@vitejs/plugin-react` 6 setup. +The implementation target is `effect-fc-next` (Effect 4 beta), which is +expected to become `effect-view`. The legacy `effect-fc` package is not +supported by the Vite plugin. ## How Effect View rendering works today -`Component.make` creates a function-shaped descriptor with a `body` property and `ComponentPrototype`; it does not create the React component that will be mounted. See [`packages/effect-fc/src/Component.ts`](packages/effect-fc/src/Component.ts), around `make`, `makeUntraced`, and `ComponentImplPrototype`. +`Component.make` creates a function-shaped descriptor with a `body` property and `ComponentPrototype`; it does not create the React component that will be mounted. See [`packages/effect-fc-next/src/Component.ts`](packages/effect-fc-next/src/Component.ts), around `make`, `makeUntraced`, and `ComponentImplPrototype`. The relevant path is: @@ -48,14 +45,10 @@ source definition -> mounted React fiber ``` -In `effect-fc`: - -- `asFunctionComponent` creates a closure over `this` and `runtimeRef`. -- The closure invokes `this.body(props)`. -- `.use` stores a cached function in `React.useState` and keys its internal cache by reactive Effect services. -- `withRuntime` renders the function returned by `.use`. - -In `effect-fc-next`, `.use` uses `componentRef` and `previousServicesRef` instead of `useState` plus `Effect.cachedFunction`, but it still retains the previously synthesized function when service identities are unchanged. +In `effect-fc-next`, `asFunctionComponent` creates a closure over the descriptor +and Effect context. `.use` retains that synthesized function in `componentRef` +while the relevant Effect service identities remain unchanged, and +`withContext` renders the function returned by `.use`. This caching is useful during ordinary rendering: without it, React would see a new component type and remount on every parent render. It is also why a newly evaluated module cannot replace the mounted implementation by itself. @@ -93,7 +86,9 @@ Consequently, edits currently propagate through ordinary Vite HMR invalidation. ## Runtime experiment -I ran a focused jsdom/React experiment against `effect-fc`: +The initial feasibility study used the legacy implementation for a focused +jsdom/React identity experiment. Its descriptor-caching result also applies to +`effect-fc-next`, but the legacy package is not an implementation target: 1. Render an Effect View containing `React.useState(0)`. 2. Increment it to `old:1`. @@ -135,7 +130,7 @@ A complete implementation should provide the same safety contract developers exp ### 1. Add an Effect View refresh transform -Ship a Vite plugin, for example `@effect-fc/vite`, placed before `react()`: +Ship a Vite plugin, for example `@effect-view/vite`, placed before `react()`: ```ts plugins: [ @@ -150,7 +145,7 @@ It should recognize: - `Component.makeUntraced(...)` - class declarations extending either factory result - definitions followed by Effect `pipe` transformations -- aliased imports from both `effect-fc` and `effect-fc-next` +- aliased imports from `effect-fc-next` For each definition it should inject development-only metadata containing: @@ -163,9 +158,9 @@ Detection must be binding-aware rather than matching text. Otherwise unrelated ` The plugin must instrument local Views as well as exports. React Refresh works on component families, not only refresh-boundary exports, and most nested Views in this repository are consumed through `.use`. -### 2. Add a development-only hot cell +### 2. Add a development-only hot cell protocol to Effect View -The component metadata should connect to a cell retained in `import.meta.hot.data`: +The main library should expose the bundler-neutral cell contract from its `Refreshable` module and the component shell should consume it directly: ```ts interface HotViewCell { @@ -181,6 +176,8 @@ On module reevaluation, the new descriptor updates `cell.current`. If the signat Keeping the cell rather than only mutating the old descriptor is important for class inheritance and traits. Class-style Views inherit `body` and options through the generated base class, while memoized and async Views alter prototype behavior. A cell can point to the complete new descriptor without attempting to copy an unknown prototype graph onto the old one. +Bundler integrations should depend on this public protocol rather than define a structurally duplicated symbol and cell. The Vite adapter owns only Vite-specific storage in `import.meta.hot.data`, registration IDs, and accept/invalidate decisions. + ### 3. Split the development component into a stable shell and keyed implementation In development, the function materialized by `.use` should become a stable shell: @@ -258,7 +255,7 @@ Exit criterion: editing render text updates without a page reload, and incompati ### Phase 1: Vite MVP (4–7 additional days) - Implement binding-aware AST detection for all public construction styles. -- Add stable shell/keyed implementation support to both Component implementations, or share the dev bridge in a small internal module. +- Add stable shell/keyed implementation support to the supported `effect-fc-next` Component implementation. - Cover memoized and async traits. - Add safe self-accept/fallback behavior. - Add browser integration tests against the example app. @@ -277,7 +274,7 @@ Exit criterion: the behavior matches React Fast Refresh closely enough that stat ### Later: other bundlers -Once the runtime metadata protocol is stable, adapters for webpack/Rspack, Rolldown, or other environments can emit the same metadata. Building those in parallel with the first Vite version would enlarge the test matrix before the semantics have settled. +Because the main Effect View library owns the runtime metadata protocol, adapters for webpack/Rspack, Rolldown, or other environments can emit the same metadata without depending on Vite. Building those in parallel with the first Vite version would enlarge the test matrix before the semantics have settled. ## Test matrix @@ -298,18 +295,11 @@ Minimum cases: - Async View and Suspense/error recovery. - Two materializations of the same View under different Effect contexts. - Reactive and `nonReactiveTags` service changes. -- TanStack Router code splitting used by both example applications. +- TanStack Router code splitting used by the `example-next` application. - Successive edits before the previous refresh finishes. - Syntax error followed by recovery. - Production build contains no HMR registry/subscription code. -## Repository test status during this study - -- `packages/effect-fc`: `bun run test` passed all 20 tests. -- `packages/effect-fc-next`: the existing suite was not green before any implementation work: 4 Component tests failed because mount/cached values were observed twice, and `Query.test.ts` could not resolve the absent `src/Result.js`. The remaining 9 tests passed. These failures are unrelated to this report, but a clean baseline or documented exclusions will be needed before implementing refresh support in the beta package. - -The Vite development server also regenerated `packages/example/src/routeTree.gen.ts` with ordering-only changes while inspecting transforms; that generated file was restored, so this report is the only intended worktree change. - ## Risks and open questions ### Hook signature ownership @@ -326,7 +316,7 @@ A stable shell plus keyed implementation adds one fiber in development. This is ### React/Vite version coupling -The current plugin stack uses Vite's OXC refresh transform and a bundled/simplified React Refresh runtime. Depending on private runtime functions or generated transform text would make the feature sensitive to Vite upgrades. A standalone Effect View metadata protocol minimizes that coupling. +The current plugin stack uses Vite's OXC refresh transform and a bundled/simplified React Refresh runtime. Depending on private runtime functions or generated transform text would make the feature sensitive to Vite upgrades. The main Effect View package should therefore own the standalone metadata protocol, while the Vite plugin depends on and adapts it. ### Multiple materializations diff --git a/bun.lock b/bun.lock index 2a9a52f..230a672 100644 --- a/bun.lock +++ b/bun.lock @@ -111,6 +111,7 @@ "react-icons": "^5.6.0", }, "devDependencies": { + "@effect-view/vite": "workspace:*", "@tanstack/react-router": "^1.170.10", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/router-plugin": "^1.168.13", @@ -123,6 +124,22 @@ "vite": "^8.0.16", }, }, + "packages/vite": { + "name": "@effect-view/vite", + "version": "0.0.0", + "dependencies": { + "typescript": "^6.0.3", + }, + "devDependencies": { + "effect-fc-next": "workspace:*", + "vite": "^8.0.16", + "vitest": "^3.2.4", + }, + "peerDependencies": { + "effect-fc-next": "0.1.0-beta.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + }, + }, }, "packages": { "@algolia/abtesting": ["@algolia/abtesting@1.22.0", "", { "dependencies": { "@algolia/client-common": "5.56.0", "@algolia/requester-browser-xhr": "5.56.0", "@algolia/requester-fetch": "5.56.0", "@algolia/requester-node-http": "5.56.0" } }, "sha512-BFR6zNowNKcY7Ou7TaJc9QWexES4YKPbmf/OTFofpdsdhz4x6q0lbxp3duO0EHnyrN7rE4ba/TSXuY+BDGu4+g=="], @@ -557,6 +574,8 @@ "@effect-fc/example-next": ["@effect-fc/example-next@workspace:packages/example-next"], + "@effect-view/vite": ["@effect-view/vite@workspace:packages/vite"], + "@effect/language-service": ["@effect/language-service@0.86.6", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-uwXbp+lWzt60bZnm6lG6S5DWy0m6TthUzV2hcQoiS6NzieEDm07tuBakscxOXoO+AU9+yTH+0TSIa9mEDt9dFA=="], "@effect/platform": ["@effect/platform@0.96.3", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.10", "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^3.21.5" } }, "sha512-LzvIj4HYE++TcTv/cVTCI/GRvQTV0ymwRmgjZMzNB5dU4OS05pTN36RKN0npiOm5orLJgw4yjIv1dNZoYGh/vg=="], diff --git a/packages/effect-fc-next/package.json b/packages/effect-fc-next/package.json index e60d411..73f5427 100644 --- a/packages/effect-fc-next/package.json +++ b/packages/effect-fc-next/package.json @@ -17,16 +17,78 @@ "types": "./dist/index.d.ts", "default": "./dist/index.js" }, - "./*": [ - { - "types": "./dist/*/index.d.ts", - "default": "./dist/*/index.js" - }, - { - "types": "./dist/*.d.ts", - "default": "./dist/*.js" - } - ] + "./Async": { + "types": "./dist/Async.d.ts", + "default": "./dist/Async.js" + }, + "./Component": { + "types": "./dist/Component.d.ts", + "default": "./dist/Component.js" + }, + "./ErrorObserver": { + "types": "./dist/ErrorObserver.d.ts", + "default": "./dist/ErrorObserver.js" + }, + "./Form": { + "types": "./dist/Form.d.ts", + "default": "./dist/Form.js" + }, + "./Lens": { + "types": "./dist/Lens.d.ts", + "default": "./dist/Lens.js" + }, + "./LensForm": { + "types": "./dist/LensForm.d.ts", + "default": "./dist/LensForm.js" + }, + "./Memoized": { + "types": "./dist/Memoized.d.ts", + "default": "./dist/Memoized.js" + }, + "./Mutation": { + "types": "./dist/Mutation.d.ts", + "default": "./dist/Mutation.js" + }, + "./MutationForm": { + "types": "./dist/MutationForm.d.ts", + "default": "./dist/MutationForm.js" + }, + "./PubSub": { + "types": "./dist/PubSub.d.ts", + "default": "./dist/PubSub.js" + }, + "./Query": { + "types": "./dist/Query.d.ts", + "default": "./dist/Query.js" + }, + "./QueryClient": { + "types": "./dist/QueryClient.d.ts", + "default": "./dist/QueryClient.js" + }, + "./ReactRuntime": { + "types": "./dist/ReactRuntime.d.ts", + "default": "./dist/ReactRuntime.js" + }, + "./Refreshable": { + "types": "./dist/Refreshable.d.ts", + "default": "./dist/Refreshable.js" + }, + "./ScopeRegistry": { + "types": "./dist/ScopeRegistry.d.ts", + "default": "./dist/ScopeRegistry.js" + }, + "./SetStateAction": { + "types": "./dist/SetStateAction.d.ts", + "default": "./dist/SetStateAction.js" + }, + "./Stream": { + "types": "./dist/Stream.d.ts", + "default": "./dist/Stream.js" + }, + "./View": { + "types": "./dist/View.d.ts", + "default": "./dist/View.js" + } }, "scripts": { "lint:tsc": "tsc --noEmit", diff --git a/packages/effect-fc-next/src/Component.ts b/packages/effect-fc-next/src/Component.ts index e17caf2..1e9c32f 100644 --- a/packages/effect-fc-next/src/Component.ts +++ b/packages/effect-fc-next/src/Component.ts @@ -2,6 +2,7 @@ /** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */ import { Context, type Duration, Effect, Equivalence, Exit, Function, identity, Layer, Pipeable, Predicate, References, Scheduler, Scope, Tracer } from "effect" import * as React from "react" +import * as Refreshable from "./Refreshable.js" import * as ScopeRegistry from "./ScopeRegistry.js" @@ -48,6 +49,7 @@ export interface ComponentImplPrototype { readonly use: Effect.Effect> asFunctionComponent(contextRef: React.Ref>>): F + asRefreshableFunctionComponent(contextRef: React.Ref>>): F setFunctionComponentName(f: F): void transformFunctionComponent(f: F): F } @@ -67,6 +69,43 @@ export const ComponentImplPrototype: ComponentImplPrototype = Object.f ) }, + asRefreshableFunctionComponent

( + this: ComponentImpl, + contextRef: React.RefObject>>, + ) { + if (!Refreshable.isRefreshable(this)) + return this.asFunctionComponent(contextRef) + + const cell = this[Refreshable.RefreshableTypeId] + + let current = cell.current + let functionComponent = current.asFunctionComponent(contextRef) + + // Calling the current renderer inside this stable component deliberately + // keeps its hooks on the same fiber until resetRevision changes. + const Implementation = (props: P) => { + if (current !== cell.current) { + current = cell.current + functionComponent = current.asFunctionComponent(contextRef) + } + return functionComponent(props) + } + + const RefreshableComponent = (props: P) => { + const snapshot = React.useSyncExternalStore( + cell.subscribe, + cell.getSnapshot, + cell.getSnapshot, + ) + return React.createElement(Implementation, { + ...props, + key: snapshot.resetRevision, + }) + } + + return RefreshableComponent as F + }, + setFunctionComponentName

( this: ComponentImpl, f: React.FC

, @@ -98,7 +137,7 @@ const use = Effect.fnUntraced(function*

{ + current: A + signature: string + forceReset: boolean + snapshot: Snapshot + readonly subscribe: (listener: () => void) => () => void + readonly getSnapshot: () => Snapshot + readonly update: ( + component: A, + signature: string, + forceReset: boolean, + ) => void +} + +/** + * A descriptor that can be connected to a development refresh cell. + */ +export interface Refreshable { + readonly [RefreshableTypeId]: Cell +} + +/** + * Checks whether a descriptor has been connected to a development refresh + * cell. + */ +export const isRefreshable = ( + value: A, +): value is A & Refreshable => Object.hasOwn(value, RefreshableTypeId) + +/** + * Creates a refresh cell for an Effect View descriptor. + * + * This low-level API is intended for development-server integrations. + */ +export const makeCell = ( + component: A, + signature: string, + forceReset: boolean, +): Cell => { + const listeners = new Set<() => void>() + let notificationPending = false + + const cell: Cell = { + current: component, + signature, + forceReset, + snapshot: { + revision: 0, + resetRevision: 0, + }, + subscribe(listener) { + listeners.add(listener) + return () => { + listeners.delete(listener) + } + }, + getSnapshot() { + return cell.snapshot + }, + update(nextComponent, nextSignature, nextForceReset) { + const shouldReset = cell.forceReset + || nextForceReset + || cell.signature !== nextSignature + + cell.current = nextComponent + cell.signature = nextSignature + cell.forceReset = nextForceReset + cell.snapshot = { + revision: cell.snapshot.revision + 1, + resetRevision: cell.snapshot.resetRevision + (shouldReset ? 1 : 0), + } + + if (!notificationPending) { + notificationPending = true + queueMicrotask(() => { + notificationPending = false + for (const listener of listeners) + listener() + }) + } + }, + } + + return cell +} + +/** + * Associates a descriptor with a refresh cell and returns the descriptor. + * + * This low-level API is intended for development-server integrations. + */ +export const attach = ( + component: A, + cell: Cell, +): A & Refreshable => { + Object.defineProperty(component, RefreshableTypeId, { + configurable: true, + enumerable: true, + value: cell, + }) + return component as A & Refreshable +} diff --git a/packages/effect-fc-next/src/index.ts b/packages/effect-fc-next/src/index.ts index 014737b..49f882e 100644 --- a/packages/effect-fc-next/src/index.ts +++ b/packages/effect-fc-next/src/index.ts @@ -11,6 +11,7 @@ export * as PubSub from "./PubSub.js" export * as Query from "./Query.js" export * as QueryClient from "./QueryClient.js" export * as ReactRuntime from "./ReactRuntime.js" +export * as Refreshable from "./Refreshable.js" export * as ScopeRegistry from "./ScopeRegistry.js" export * as SetStateAction from "./SetStateAction.js" export * as Stream from "./Stream.js" diff --git a/packages/effect-fc-next/test/Component.test.tsx b/packages/effect-fc-next/test/Component.test.tsx index dffcc0b..4087766 100644 --- a/packages/effect-fc-next/test/Component.test.tsx +++ b/packages/effect-fc-next/test/Component.test.tsx @@ -4,6 +4,7 @@ import * as React from "react" import { afterEach, describe, expect, it, vi } from "vitest" import * as Component from "../src/Component.js" import * as ReactRuntime from "../src/ReactRuntime.js" +import * as Refreshable from "../src/Refreshable.js" import * as ScopeRegistry from "../src/ScopeRegistry.js" @@ -529,4 +530,42 @@ describe("Component", () => { await runtime.runtime.dispose() } }) + + it("refreshes a registered body while preserving or resetting local state", async () => { + const runtime = ReactRuntime.make(Layer.empty) + const effectRuntime = await runtime.runtime.context() + + const makeProbe = (label: string) => Component.makeUntraced("RefreshProbe")(function*() { + const [count, setCount] = React.useState(0) + return + }) + const original = makeProbe("old") + const cell = Refreshable.makeCell(original, "hooks", false) + Refreshable.attach(original, cell) + + const Probe = Component.withContext(original, runtime.context) + const view = render( + + + + ) + + fireEvent.click(await screen.findByRole("button")) + expect(screen.getByRole("button").textContent).toBe("old:1") + + await act(async () => { + cell.update(makeProbe("compatible"), "hooks", false) + await Promise.resolve() + }) + expect(screen.getByRole("button").textContent).toBe("compatible:1") + + await act(async () => { + cell.update(makeProbe("reset"), "changed-hooks", false) + await Promise.resolve() + }) + expect(screen.getByRole("button").textContent).toBe("reset:0") + + view.unmount() + await runtime.runtime.dispose() + }) }) diff --git a/packages/effect-fc-next/test/Refreshable.test.ts b/packages/effect-fc-next/test/Refreshable.test.ts new file mode 100644 index 0000000..b094d3d --- /dev/null +++ b/packages/effect-fc-next/test/Refreshable.test.ts @@ -0,0 +1,40 @@ +import { describe, expect, it, vi } from "vitest" +import * as Refreshable from "../src/Refreshable.js" + + +describe("Refreshable", () => { + it("attaches a cell and notifies subscribers after a compatible update", async () => { + const first = {} + const cell = Refreshable.makeCell(first, "hooks", false) + const listener = vi.fn() + + expect(Refreshable.isRefreshable(first)).toBe(false) + const attached = Refreshable.attach(first, cell) + expect(attached).toBe(first) + expect(Refreshable.isRefreshable(first)).toBe(true) + expect(attached[Refreshable.RefreshableTypeId]).toBe(cell) + + cell.subscribe(listener) + const second = {} + cell.update(second, "hooks", false) + + expect(cell.current).toBe(second) + expect(cell.snapshot).toEqual({ + revision: 1, + resetRevision: 0, + }) + + await Promise.resolve() + expect(listener).toHaveBeenCalledTimes(1) + }) + + it("requests a remount when a signature changes or reset is forced", () => { + const cell = Refreshable.makeCell({}, "one", false) + + cell.update({}, "two", false) + expect(cell.snapshot.resetRevision).toBe(1) + + cell.update({}, "two", true) + expect(cell.snapshot.resetRevision).toBe(2) + }) +}) diff --git a/packages/example-next/package.json b/packages/example-next/package.json index 26a9e01..bfc7e9c 100644 --- a/packages/example-next/package.json +++ b/packages/example-next/package.json @@ -14,6 +14,7 @@ "clean:modules": "rm -rf node_modules" }, "devDependencies": { + "@effect-view/vite": "workspace:*", "@tanstack/react-router": "^1.170.10", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/router-plugin": "^1.168.13", diff --git a/packages/example-next/vite.config.ts b/packages/example-next/vite.config.ts index 79367b3..2875ccf 100644 --- a/packages/example-next/vite.config.ts +++ b/packages/example-next/vite.config.ts @@ -1,5 +1,6 @@ import { tanstackRouter } from "@tanstack/router-plugin/vite" import react from "@vitejs/plugin-react" +import effectViewRefresh from "@effect-view/vite" import path from "node:path" import { defineConfig } from "vite" @@ -7,6 +8,7 @@ import { defineConfig } from "vite" // https://vite.dev/config/ export default defineConfig({ plugins: [ + effectViewRefresh(), tanstackRouter({ target: "react", autoCodeSplitting: true, diff --git a/packages/vite/README.md b/packages/vite/README.md new file mode 100644 index 0000000..8ddb2cd --- /dev/null +++ b/packages/vite/README.md @@ -0,0 +1,42 @@ +# `@effect-view/vite` + +Experimental Vite Fast Refresh support for Effect View components. + +```ts +import effectViewRefresh from "@effect-view/vite" +import react from "@vitejs/plugin-react" +import { defineConfig } from "vite" + +export default defineConfig({ + plugins: [ + effectViewRefresh(), + react(), + ], +}) +``` + +The plugin recognizes `Component.make` and `Component.makeUntraced` +definitions imported from `effect-fc-next`. The legacy `effect-fc` package is +not supported. `effect-fc-next` owns the bundler-neutral refresh cell protocol; +this package is the Vite adapter that retains those cells in HMR data. The +plugin assigns stable development IDs and self-accepts successfully +instrumented modules. + +The adapter-facing protocol is exported from `effect-fc-next/Refreshable`. + +Renaming or removing an instrumented View invalidates the module upward instead +of leaving a stale mounted descriptor. + +React state is retained when the ordered hook signature is unchanged. Adding, +removing, or changing a hook call resets the Effect View implementation. Add +`// @refresh reset` to a module to reset its Views on every update. + +## Current limitations + +- Vite is the only supported bundler. +- Definitions must be top-level variable, class, or default-export + declarations. +- Hook signature analysis is conservative and based on `useX` call syntax. +- Changing a descriptor's trait pipeline during a refresh is not guaranteed to + preserve state. +- Source maps for the injected transform are not generated yet. diff --git a/packages/vite/biome.json b/packages/vite/biome.json new file mode 100644 index 0000000..868f17a --- /dev/null +++ b/packages/vite/biome.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://biomejs.dev/schemas/latest/schema.json", + "root": false, + "extends": "//", + "files": { + "includes": ["./src/**", "./test/**"] + } +} diff --git a/packages/vite/package.json b/packages/vite/package.json new file mode 100644 index 0000000..f19e24d --- /dev/null +++ b/packages/vite/package.json @@ -0,0 +1,47 @@ +{ + "name": "@effect-view/vite", + "description": "Vite Fast Refresh support for Effect View components", + "version": "0.0.0", + "type": "module", + "files": [ + "./README.md", + "./dist" + ], + "license": "MIT", + "repository": { + "url": "git+https://github.com/Thiladev/effect-fc.git" + }, + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "./runtime": { + "types": "./dist/runtime.d.ts", + "default": "./dist/runtime.js" + } + }, + "scripts": { + "lint:tsc": "tsc --noEmit", + "lint:biome": "biome lint", + "test": "vitest run", + "build": "tsc", + "pack": "npm pack", + "clean:cache": "rm -rf .turbo tsconfig.tsbuildinfo", + "clean:dist": "rm -rf dist", + "clean:modules": "rm -rf node_modules" + }, + "dependencies": { + "typescript": "^6.0.3" + }, + "devDependencies": { + "effect-fc-next": "workspace:*", + "vite": "^8.0.16", + "vitest": "^3.2.4" + }, + "peerDependencies": { + "effect-fc-next": "0.1.0-beta.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + } +} diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts new file mode 100644 index 0000000..9f9c722 --- /dev/null +++ b/packages/vite/src/index.ts @@ -0,0 +1,435 @@ +import path from "node:path" +import ts from "typescript" +import type { Plugin } from "vite" + + +export interface EffectViewRefreshOptions { + /** + * Include filter for source files. + * + * @default /\.[cm]?[jt]sx?$/ + */ + readonly include?: RegExp + + /** + * Exclude filter for source files. + * + * @default /\/node_modules\// + */ + readonly exclude?: RegExp +} + +interface Edit { + readonly start: number + readonly end: number + readonly content: string +} + +interface ComponentImports { + readonly componentNamespaces: Set + readonly packageNamespaces: Set + readonly directFactories: Set +} + +interface Definition { + readonly expression: ts.Expression + readonly factoryCall: ts.CallExpression + readonly body: ts.FunctionLikeDeclaration | undefined + readonly id: string + readonly pipeline: ts.CallExpression | undefined + readonly entrypointIndex: number +} + +const defaultInclude = /\.[cm]?[jt]sx?$/ +const defaultExclude = /\/node_modules\// +const runtimeImport = "@effect-view/vite/runtime" +const refreshIdentifier = "__effectViewRefresh" +const acceptIdentifier = "__effectViewAccept" + +const isSupportedPackage = (source: string): boolean => + source === "effect-fc-next" + || source === "effect-fc-next/Component" + +const collectImports = ( + sourceFile: ts.SourceFile, +): ComponentImports => { + const componentNamespaces = new Set() + const packageNamespaces = new Set() + const directFactories = new Set() + + for (const statement of sourceFile.statements) { + if (!ts.isImportDeclaration(statement) + || !ts.isStringLiteral(statement.moduleSpecifier) + || !isSupportedPackage(statement.moduleSpecifier.text)) + continue + + const source = statement.moduleSpecifier.text + const clause = statement.importClause + if (!clause) + continue + + if (clause.namedBindings && ts.isNamespaceImport(clause.namedBindings)) { + if (source.endsWith("/Component")) + componentNamespaces.add(clause.namedBindings.name.text) + else + packageNamespaces.add(clause.namedBindings.name.text) + continue + } + + if (!clause.namedBindings || !ts.isNamedImports(clause.namedBindings)) + continue + + for (const element of clause.namedBindings.elements) { + const importedName = element.propertyName?.text ?? element.name.text + if (source.endsWith("/Component") && (importedName === "make" || importedName === "makeUntraced")) + directFactories.add(element.name.text) + else if (!source.endsWith("/Component") && importedName === "Component") + componentNamespaces.add(element.name.text) + } + } + + return { + componentNamespaces, + packageNamespaces, + directFactories, + } +} + +const isFactoryCallee = ( + expression: ts.Expression, + imports: ComponentImports, +): boolean => { + if (ts.isIdentifier(expression)) + return imports.directFactories.has(expression.text) + + if (!ts.isPropertyAccessExpression(expression)) + return false + + if (expression.name.text !== "make" && expression.name.text !== "makeUntraced") + return false + + if (ts.isIdentifier(expression.expression)) + return imports.componentNamespaces.has(expression.expression.text) + + return ts.isPropertyAccessExpression(expression.expression) + && expression.expression.name.text === "Component" + && ts.isIdentifier(expression.expression.expression) + && imports.packageNamespaces.has(expression.expression.expression.text) +} + +const functionArgument = ( + call: ts.CallExpression, +): ts.FunctionLikeDeclaration | undefined => call.arguments.find(argument => + ts.isArrowFunction(argument) + || ts.isFunctionExpression(argument) +) as ts.FunctionLikeDeclaration | undefined + +const findFactoryCall = ( + root: ts.Node, + imports: ComponentImports, +): { + readonly factoryCall: ts.CallExpression + readonly body: ts.FunctionLikeDeclaration | undefined +} | undefined => { + let result: { + readonly factoryCall: ts.CallExpression + readonly body: ts.FunctionLikeDeclaration | undefined + } | undefined + + const visit = (node: ts.Node): void => { + if (result) + return + + if (ts.isCallExpression(node)) { + if (isFactoryCallee(node.expression, imports)) { + result = { + factoryCall: node, + body: functionArgument(node), + } + return + } + + if (ts.isCallExpression(node.expression) && isFactoryCallee(node.expression.expression, imports)) { + result = { + factoryCall: node, + body: functionArgument(node), + } + return + } + } + + ts.forEachChild(node, visit) + } + + visit(root) + return result +} + +const isPipeline = (expression: ts.Expression): expression is ts.CallExpression => + ts.isCallExpression(expression) + && ts.isPropertyAccessExpression(expression.expression) + && expression.expression.name.text === "pipe" + +const isEntrypoint = (expression: ts.Expression): boolean => { + if (!ts.isCallExpression(expression)) + return false + + const callee = expression.expression + return ts.isPropertyAccessExpression(callee) + && (callee.name.text === "withRuntime" || callee.name.text === "withContext") +} + +const makeDefinition = ( + expression: ts.Expression, + id: string, + imports: ComponentImports, +): Definition | undefined => { + const factory = findFactoryCall(expression, imports) + if (!factory) + return undefined + + const pipeline = isPipeline(expression) ? expression : undefined + if (expression !== factory.factoryCall && !pipeline) + return undefined + + const entrypointIndex = pipeline + ? pipeline.arguments.findIndex(isEntrypoint) + : -1 + + return { + expression, + factoryCall: factory.factoryCall, + body: factory.body, + id, + pipeline, + entrypointIndex, + } +} + +const collectDefinitions = ( + sourceFile: ts.SourceFile, + imports: ComponentImports, + moduleId: string, +): readonly Definition[] => { + const definitions: Definition[] = [] + + for (const statement of sourceFile.statements) { + if (ts.isVariableStatement(statement)) { + for (const declaration of statement.declarationList.declarations) { + if (!declaration.initializer || !ts.isIdentifier(declaration.name)) + continue + const definition = makeDefinition( + declaration.initializer, + `${moduleId}:${declaration.name.text}`, + imports, + ) + if (definition) + definitions.push(definition) + } + continue + } + + if (ts.isClassDeclaration(statement) && statement.name) { + const heritage = statement.heritageClauses + ?.find(clause => clause.token === ts.SyntaxKind.ExtendsKeyword) + ?.types[0] + ?.expression + if (!heritage) + continue + const definition = makeDefinition( + heritage, + `${moduleId}:${statement.name.text}`, + imports, + ) + if (definition) + definitions.push(definition) + continue + } + + if (ts.isExportAssignment(statement)) { + const definition = makeDefinition( + statement.expression, + `${moduleId}:default`, + imports, + ) + if (definition) + definitions.push(definition) + } + } + + return definitions +} + +const hookSignature = ( + body: ts.FunctionLikeDeclaration | undefined, + sourceFile: ts.SourceFile, +): string => { + if (!body?.body) + return "unknown" + + const hooks: string[] = [] + const root = body.body + + const visit = (node: ts.Node): void => { + if (node !== root && ts.isFunctionLike(node)) + return + + if (ts.isCallExpression(node)) { + const callee = node.expression + const name = ts.isIdentifier(callee) + ? callee.text + : ts.isPropertyAccessExpression(callee) + ? callee.name.text + : undefined + + if (name && /^use[A-Z0-9]/.test(name)) + hooks.push(node.getText(sourceFile)) + } + + ts.forEachChild(node, visit) + } + + visit(root) + return hash(hooks.join("\n")) +} + +const hash = (value: string): string => { + let result = 0x811c9dc5 + for (let index = 0; index < value.length; index++) { + result ^= value.charCodeAt(index) + result = Math.imul(result, 0x01000193) + } + return (result >>> 0).toString(36) +} + +const quote = (value: string): string => JSON.stringify(value) + +const registration = ( + expression: string, + definition: Definition, + signature: string, + forceReset: boolean, +): string => `${refreshIdentifier}(${expression}, import.meta.hot, ${quote(definition.id)}, ${quote(signature)}, ${forceReset})` + +const applyEdits = (code: string, edits: readonly Edit[]): string => { + let output = code + for (const edit of [...edits].sort((self, that) => that.start - self.start)) + output = output.slice(0, edit.start) + edit.content + output.slice(edit.end) + return output +} + +const importPosition = (sourceFile: ts.SourceFile): number => { + let position = 0 + for (const statement of sourceFile.statements) { + if (ts.isImportDeclaration(statement)) + position = statement.end + else if (position > 0) + break + } + return position +} + +const scriptKind = (id: string): ts.ScriptKind => id.endsWith(".tsx") + ? ts.ScriptKind.TSX + : id.endsWith(".jsx") + ? ts.ScriptKind.JSX + : id.endsWith(".js") || id.endsWith(".mjs") || id.endsWith(".cjs") + ? ts.ScriptKind.JS + : ts.ScriptKind.TS + +/** + * Adds Fast Refresh support for Effect View components. + * + * Place this plugin before `@vitejs/plugin-react`. + */ +export default function effectViewRefresh( + options: EffectViewRefreshOptions = {}, +): Plugin { + const include = options.include ?? defaultInclude + const exclude = options.exclude ?? defaultExclude + const instrumentedModules = new Set() + + return { + name: "effect-view:refresh", + enforce: "pre", + apply: "serve", + transform(code, rawId) { + const [id] = rawId.split("?", 1) + if (!id) + return null + if (!include.test(id) || exclude.test(id)) + return null + + const sourceFile = ts.createSourceFile( + id, + code, + ts.ScriptTarget.Latest, + true, + scriptKind(id), + ) + const imports = collectImports(sourceFile) + if (imports.componentNamespaces.size === 0 + && imports.packageNamespaces.size === 0 + && imports.directFactories.size === 0 + && !instrumentedModules.has(id)) + return null + + const moduleId = path.relative(process.cwd(), id).split(path.sep).join("/") + const definitions = collectDefinitions(sourceFile, imports, moduleId) + if (definitions.length === 0 && !instrumentedModules.has(id)) + return null + instrumentedModules.add(id) + + const forceReset = /@refresh\s+reset\b/.test(code) + const edits: Edit[] = [] + + for (const definition of definitions) { + const signature = hookSignature(definition.body, sourceFile) + + if (definition.pipeline && definition.entrypointIndex >= 0) { + const entrypoint = definition.pipeline.arguments[definition.entrypointIndex] + if (!entrypoint) + continue + edits.push({ + start: entrypoint.getStart(sourceFile), + end: entrypoint.getStart(sourceFile), + content: `(${refreshIdentifier}View) => ${registration( + `${refreshIdentifier}View`, + definition, + signature, + forceReset, + )}, `, + }) + continue + } + + const start = definition.expression.getStart(sourceFile) + const end = definition.expression.end + edits.push({ + start, + end, + content: registration( + code.slice(start, end), + definition, + signature, + forceReset, + ), + }) + } + + const position = importPosition(sourceFile) + edits.push({ + start: position, + end: position, + content: `${position === 0 ? "" : "\n"}import { accept as ${acceptIdentifier}, register as ${refreshIdentifier} } from ${quote(runtimeImport)};\n`, + }) + + const transformed = applyEdits(code, edits) + const definitionIds = definitions.map(definition => quote(definition.id)).join(", ") + return { + code: `${transformed}\n${acceptIdentifier}(import.meta.hot, [${definitionIds}])\n`, + map: null, + } + }, + } +} diff --git a/packages/vite/src/runtime.ts b/packages/vite/src/runtime.ts new file mode 100644 index 0000000..be62b49 --- /dev/null +++ b/packages/vite/src/runtime.ts @@ -0,0 +1,85 @@ +import * as Refreshable from "effect-fc-next/Refreshable" + +export interface HotContext { + readonly data: Record + accept(): void + invalidate(message?: string): void +} + +interface RefreshData { + readonly cells: Map> + ids?: readonly string[] +} + +const hotDataKey = "__effectFcRefresh" + +const getRefreshData = (hot: HotContext): RefreshData => { + const data = hot.data as Record + const current = data[hotDataKey] as RefreshData | undefined + if (current) + return current + + const refreshData: RefreshData = { + cells: new Map(), + } + data[hotDataKey] = refreshData + return refreshData +} + +/** + * Registers an Effect View descriptor in a Vite HMR data cell. + * + * This API is injected by `@effect-view/vite`; applications should not need to + * call it directly. + */ +export const register = ( + component: A, + hot: HotContext | undefined, + id: string, + signature: string, + forceReset = false, +): A => { + if (!hot) + return component + + const refreshData = getRefreshData(hot) + const previous = refreshData.cells.get(id) + if (!previous) { + const cell = Refreshable.makeCell(component, signature, forceReset) + refreshData.cells.set(id, cell) + return Refreshable.attach(component, cell) + } + + previous.update(component, signature, forceReset) + return Refreshable.attach(component, previous) +} + +const sameIds = ( + self: readonly string[], + that: readonly string[], +): boolean => self.length === that.length + && self.every((id, index) => id === that[index]) + +/** + * Accepts an instrumented module when its Effect View definition set is + * unchanged. Renames and removals invalidate upward so a stale cell cannot + * remain mounted indefinitely. + */ +export const accept = ( + hot: HotContext | undefined, + ids: readonly string[], +): void => { + if (!hot) + return + + const refreshData = getRefreshData(hot) + const previousIds = refreshData.ids + refreshData.ids = ids + + if (previousIds && !sameIds(previousIds, ids)) { + hot.invalidate("[effect-view] Effect View exports changed") + return + } + + hot.accept() +} diff --git a/packages/vite/test/plugin.test.ts b/packages/vite/test/plugin.test.ts new file mode 100644 index 0000000..4213cd7 --- /dev/null +++ b/packages/vite/test/plugin.test.ts @@ -0,0 +1,132 @@ +import path from "node:path" +import type { Plugin, TransformPluginContext } from "vite" +import { describe, expect, it } from "vitest" +import effectViewRefresh from "../src/index.js" + + +const runTransform = async ( + plugin: Plugin, + code: string, + id: string, +): Promise => { + const hook = plugin.transform + if (!hook) + return undefined + + const result = typeof hook === "function" + ? await hook.call({} as TransformPluginContext, code, id) + : await hook.handler.call({} as TransformPluginContext, code, id) + + if (!result) + return undefined + return typeof result === "string" ? result : result.code +} + +const transform = ( + code: string, + id = path.join(process.cwd(), "src/View.tsx"), +): Promise => runTransform(effectViewRefresh(), code, id) + +describe("effectViewRefresh", () => { + it("wraps a const Effect View descriptor", async () => { + const result = await transform(` +import { Component } from "effect-fc-next" +import * as React from "react" + +export const CounterView = Component.make("CounterView")(function*() { + const [count] = React.useState(0) + return

+}) +`) + + expect(result).toContain("import { accept as __effectViewAccept, register as __effectViewRefresh } from \"@effect-view/vite/runtime\"") + expect(result).toContain("__effectViewRefresh(Component.make(\"CounterView\")") + expect(result).toContain("\"src/View.tsx:CounterView\"") + expect(result).toContain("__effectViewAccept(import.meta.hot, [\"src/View.tsx:CounterView\"])") + }) + + it("registers a pipeline before withContext", async () => { + const result = await transform(` +import { Component } from "effect-fc-next" + +const Page = Component.make("Page")(function*() { + return
+}).pipe( + Component.withContext(runtime.context), +) +`) + + expect(result).toContain("(__effectViewRefreshView) => __effectViewRefresh(__effectViewRefreshView") + expect(result).toContain("Component.withContext(runtime.context)") + expect(result).not.toContain("__effectViewRefresh(Component.make(\"Page\")") + }) + + it("wraps a class's complete trait pipeline", async () => { + const result = await transform(` +import { Async, Component, Memoized } from "effect-fc-next" + +class PostView extends Component.make("PostView")(function*() { + const value = yield* Component.useOnMount(load) + return
{value}
+}).pipe( + Async.async, + Memoized.memoized, +) {} +`) + + expect(result).toContain("class PostView extends __effectViewRefresh(Component.make(\"PostView\")") + expect(result).toContain("Memoized.memoized,") + expect(result).toContain("\"src/View.tsx:PostView\"") + }) + + it("supports aliased Component imports and refresh reset", async () => { + const result = await transform(` +// @refresh reset +import { Component as View } from "effect-fc-next" + +const Probe = View.makeUntraced(function*() { + return null +}) +`) + + expect(result).toContain("__effectViewRefresh(View.makeUntraced") + expect(result).toMatch(/"src\/View\.tsx:Probe", "[a-z0-9]+", true/) + }) + + it("ignores modules without Effect View definitions", async () => { + expect(await transform(` +import * as React from "react" +export function View() { + return
+} +`)).toBeUndefined() + }) + + it("keeps instrumenting a module when all Effect Views are removed", async () => { + const plugin = effectViewRefresh() as Plugin + const id = path.join(process.cwd(), "src/Removed.tsx") + + await runTransform(plugin, ` +import { Component } from "effect-fc-next" +export const Probe = Component.makeUntraced(function*() { + return null +}) +`, id) + + const result = await runTransform(plugin, ` +export const value = 1 +`, id) + + expect(result).toContain("__effectViewAccept(import.meta.hot, [])") + expect(result).not.toContain("__effectViewRefresh(") + }) + + it("ignores the legacy effect-fc package", async () => { + expect(await transform(` +import { Component } from "effect-fc" +export const Legacy = Component.makeUntraced(function*() { + return null +}) +`)).toBeUndefined() + }) +}) diff --git a/packages/vite/test/runtime.test.ts b/packages/vite/test/runtime.test.ts new file mode 100644 index 0000000..8acf1a5 --- /dev/null +++ b/packages/vite/test/runtime.test.ts @@ -0,0 +1,68 @@ +import * as Refreshable from "effect-fc-next/Refreshable" +import { describe, expect, it, vi } from "vitest" +import { accept, register } from "../src/runtime.js" + + +describe("refresh runtime", () => { + it("retains a cell and notifies subscribers for compatible updates", async () => { + const hot = { + data: {}, + accept: vi.fn(), + invalidate: vi.fn(), + } + const first = register({ body: "first" }, hot, "module:View", "hooks") + const cell = (first as Record)[Refreshable.RefreshableTypeId] as Refreshable.Cell + const listener = vi.fn() + cell.subscribe(listener) + + const second = register({ body: "second" }, hot, "module:View", "hooks") + const secondCell = (second as Record)[Refreshable.RefreshableTypeId] + + expect(secondCell).toBe(cell) + expect(cell.current).toBe(second) + expect(cell.snapshot).toEqual({ + revision: 1, + resetRevision: 0, + }) + + await Promise.resolve() + expect(listener).toHaveBeenCalledTimes(1) + }) + + it("increments resetRevision for incompatible and forced updates", () => { + const hot = { + data: {}, + accept: vi.fn(), + invalidate: vi.fn(), + } + const first = register({}, hot, "module:View", "one") + const cell = (first as Record)[Refreshable.RefreshableTypeId] as Refreshable.Cell + + register({}, hot, "module:View", "two") + expect(cell.snapshot.resetRevision).toBe(1) + + register({}, hot, "module:View", "two", true) + expect(cell.snapshot.resetRevision).toBe(2) + }) + + it("is inert outside a Vite hot context", () => { + const component = {} + expect(register(component, undefined, "module:View", "hooks")).toBe(component) + expect((component as Record)[Refreshable.RefreshableTypeId]).toBeUndefined() + }) + + it("invalidates when the module's View IDs change", () => { + const hot = { + data: {}, + accept: vi.fn(), + invalidate: vi.fn(), + } + + accept(hot, ["module:First"]) + expect(hot.accept).toHaveBeenCalledTimes(1) + expect(hot.invalidate).not.toHaveBeenCalled() + + accept(hot, ["module:Second"]) + expect(hot.invalidate).toHaveBeenCalledWith("[effect-view] Effect View exports changed") + }) +}) diff --git a/packages/vite/tsconfig.json b/packages/vite/tsconfig.json new file mode 100644 index 0000000..625da67 --- /dev/null +++ b/packages/vite/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "lib": ["ESNext"], + "target": "ESNext", + "module": "NodeNext", + "moduleDetection": "force", + "moduleResolution": "NodeNext", + "verbatimModuleSyntax": true, + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + "rootDir": "./src", + "outDir": "./dist", + "declaration": true, + "sourceMap": true + }, + "include": ["./src"], + "exclude": ["**/*.test.ts", "**/*.spec.ts"] +} diff --git a/packages/vite/vitest.config.ts b/packages/vite/vitest.config.ts new file mode 100644 index 0000000..78ac2cb --- /dev/null +++ b/packages/vite/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "vitest/config" + + +export default defineConfig({ + test: { + include: ["test/**/*.test.ts"], + }, +}) -- 2.52.0 From 23547afe9126c598c4a82b1d5ffdced1341acd2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 25 Jul 2026 22:27:11 +0200 Subject: [PATCH 115/133] Fix refresh plugin --- packages/effect-fc-next/src/Async.ts | 2 +- packages/effect-fc-next/src/Component.ts | 45 ++-------- packages/effect-fc-next/src/Refreshable.ts | 84 +++++++++++++++---- .../effect-fc-next/test/Refreshable.test.ts | 30 +++++-- packages/vite/src/runtime.ts | 7 +- packages/vite/test/runtime.test.ts | 20 +++-- 6 files changed, 119 insertions(+), 69 deletions(-) diff --git a/packages/effect-fc-next/src/Async.ts b/packages/effect-fc-next/src/Async.ts index 58b7f5a..bf4661f 100644 --- a/packages/effect-fc-next/src/Async.ts +++ b/packages/effect-fc-next/src/Async.ts @@ -37,7 +37,7 @@ export type AsyncProps = Omit export const AsyncPrototype: AsyncPrototype = Object.freeze({ [AsyncTypeId]: AsyncTypeId, - asFunctionComponent

( + makeFunctionComponent

( this: Component.Component & Async, contextRef: React.RefObject>>, ) { diff --git a/packages/effect-fc-next/src/Component.ts b/packages/effect-fc-next/src/Component.ts index 1e9c32f..6325f16 100644 --- a/packages/effect-fc-next/src/Component.ts +++ b/packages/effect-fc-next/src/Component.ts @@ -2,7 +2,6 @@ /** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */ import { Context, type Duration, Effect, Equivalence, Exit, Function, identity, Layer, Pipeable, Predicate, References, Scheduler, Scope, Tracer } from "effect" import * as React from "react" -import * as Refreshable from "./Refreshable.js" import * as ScopeRegistry from "./ScopeRegistry.js" @@ -45,11 +44,15 @@ export declare namespace Component { export interface ComponentImpl

extends Component, ComponentImplPrototype {} +export declare namespace ComponentImpl { + export type Any = ComponentImpl +} + export interface ComponentImplPrototype { readonly use: Effect.Effect> asFunctionComponent(contextRef: React.Ref>>): F - asRefreshableFunctionComponent(contextRef: React.Ref>>): F + makeFunctionComponent(contextRef: React.Ref>>): F setFunctionComponentName(f: F): void transformFunctionComponent(f: F): F } @@ -57,7 +60,7 @@ export interface ComponentImplPrototype { export const ComponentImplPrototype: ComponentImplPrototype = Object.freeze({ get use() { return use(this) }, - asFunctionComponent

( + makeFunctionComponent

( this: ComponentImpl, contextRef: React.RefObject>>, ) { @@ -69,41 +72,11 @@ export const ComponentImplPrototype: ComponentImplPrototype = Object.f ) }, - asRefreshableFunctionComponent

( + asFunctionComponent

( this: ComponentImpl, contextRef: React.RefObject>>, ) { - if (!Refreshable.isRefreshable(this)) - return this.asFunctionComponent(contextRef) - - const cell = this[Refreshable.RefreshableTypeId] - - let current = cell.current - let functionComponent = current.asFunctionComponent(contextRef) - - // Calling the current renderer inside this stable component deliberately - // keeps its hooks on the same fiber until resetRevision changes. - const Implementation = (props: P) => { - if (current !== cell.current) { - current = cell.current - functionComponent = current.asFunctionComponent(contextRef) - } - return functionComponent(props) - } - - const RefreshableComponent = (props: P) => { - const snapshot = React.useSyncExternalStore( - cell.subscribe, - cell.getSnapshot, - cell.getSnapshot, - ) - return React.createElement(Implementation, { - ...props, - key: snapshot.resetRevision, - }) - } - - return RefreshableComponent as F + return this.makeFunctionComponent(contextRef) }, setFunctionComponentName

( @@ -137,7 +110,7 @@ const use = Effect.fnUntraced(function*

{ - current: A +export interface Cell { + current: Component.ComponentImpl.Any signature: string forceReset: boolean snapshot: Snapshot readonly subscribe: (listener: () => void) => () => void readonly getSnapshot: () => Snapshot readonly update: ( - component: A, + component: Component.Component.Any, signature: string, forceReset: boolean, ) => void } +export const RefreshablePrototype = Object.freeze({ + asFunctionComponent

( + this: Component.ComponentImpl & Refreshable, + contextRef: React.RefObject>>, + ) { + const cell = this[RefreshableTypeId] + let current = cell.current + let functionComponent = current.makeFunctionComponent(contextRef) + + // Calling the current renderer inside this stable component deliberately + // keeps its hooks on the same fiber until resetRevision changes. + const Implementation = (props: P) => { + if (current !== cell.current) { + current = cell.current + functionComponent = current.makeFunctionComponent(contextRef) + } + return functionComponent(props) + } + + const RefreshableComponent = (props: P) => { + const snapshot = React.useSyncExternalStore( + cell.subscribe, + cell.getSnapshot, + cell.getSnapshot, + ) + return React.createElement(Implementation, { + ...props, + key: snapshot.resetRevision, + }) + } + + return RefreshableComponent as F + }, +} as const) + +export type RefreshablePrototype = typeof RefreshablePrototype + /** * A descriptor that can be connected to a development refresh cell. */ -export interface Refreshable { - readonly [RefreshableTypeId]: Cell +export interface Refreshable extends RefreshablePrototype { + readonly [RefreshableTypeId]: Cell } /** @@ -52,23 +94,23 @@ export interface Refreshable { */ export const isRefreshable = ( value: A, -): value is A & Refreshable => Object.hasOwn(value, RefreshableTypeId) +): value is A & Refreshable => Object.hasOwn(value, RefreshableTypeId) /** * Creates a refresh cell for an Effect View descriptor. * * This low-level API is intended for development-server integrations. */ -export const makeCell = ( - component: A, +export const makeCell = ( + component: Component.Component.Any, signature: string, forceReset: boolean, -): Cell => { +): Cell => { const listeners = new Set<() => void>() let notificationPending = false - const cell: Cell = { - current: component, + const cell: Cell = { + current: component as Component.ComponentImpl.Any, signature, forceReset, snapshot: { @@ -89,7 +131,7 @@ export const makeCell = ( || nextForceReset || cell.signature !== nextSignature - cell.current = nextComponent + cell.current = nextComponent as Component.ComponentImpl.Any cell.signature = nextSignature cell.forceReset = nextForceReset cell.snapshot = { @@ -116,14 +158,24 @@ export const makeCell = ( * * This low-level API is intended for development-server integrations. */ -export const attach = ( +export const attach = ( component: A, - cell: Cell, -): A & Refreshable => { + cell: Cell, +): A & Refreshable => { + if (!isRefreshable(component)) { + Object.setPrototypeOf( + component, + Object.freeze(Object.setPrototypeOf( + Object.assign({}, RefreshablePrototype), + Object.getPrototypeOf(component), + )), + ) + } + Object.defineProperty(component, RefreshableTypeId, { configurable: true, enumerable: true, value: cell, }) - return component as A & Refreshable + return component as A & Refreshable } diff --git a/packages/effect-fc-next/test/Refreshable.test.ts b/packages/effect-fc-next/test/Refreshable.test.ts index b094d3d..e756d7c 100644 --- a/packages/effect-fc-next/test/Refreshable.test.ts +++ b/packages/effect-fc-next/test/Refreshable.test.ts @@ -1,10 +1,11 @@ import { describe, expect, it, vi } from "vitest" +import type * as Component from "../src/Component.js" import * as Refreshable from "../src/Refreshable.js" describe("Refreshable", () => { it("attaches a cell and notifies subscribers after a compatible update", async () => { - const first = {} + const first = {} as Component.Component.Any const cell = Refreshable.makeCell(first, "hooks", false) const listener = vi.fn() @@ -13,9 +14,11 @@ describe("Refreshable", () => { expect(attached).toBe(first) expect(Refreshable.isRefreshable(first)).toBe(true) expect(attached[Refreshable.RefreshableTypeId]).toBe(cell) + expect(Object.getPrototypeOf(attached).asFunctionComponent) + .toBe(Refreshable.RefreshablePrototype.asFunctionComponent) cell.subscribe(listener) - const second = {} + const second = {} as Component.Component.Any cell.update(second, "hooks", false) expect(cell.current).toBe(second) @@ -29,12 +32,29 @@ describe("Refreshable", () => { }) it("requests a remount when a signature changes or reset is forced", () => { - const cell = Refreshable.makeCell({}, "one", false) + const component = {} as Component.Component.Any + const cell = Refreshable.makeCell(component, "one", false) - cell.update({}, "two", false) + cell.update({} as Component.Component.Any, "two", false) expect(cell.snapshot.resetRevision).toBe(1) - cell.update({}, "two", true) + cell.update({} as Component.Component.Any, "two", true) expect(cell.snapshot.resetRevision).toBe(2) }) + + it("builds the refresh shell from the current descriptor implementation", () => { + const implementation = () => null + const makeFunctionComponent = vi.fn(() => implementation) + const component = { + makeFunctionComponent, + } as unknown as Component.ComponentImpl.Any + const contextRef = { + current: {}, + } as never + const cell = Refreshable.makeCell(component, "hooks", false) + const attached = Refreshable.attach(component, cell) + + expect(attached.asFunctionComponent(contextRef)).not.toBe(implementation) + expect(makeFunctionComponent).toHaveBeenCalledWith(contextRef) + }) }) diff --git a/packages/vite/src/runtime.ts b/packages/vite/src/runtime.ts index be62b49..a7db583 100644 --- a/packages/vite/src/runtime.ts +++ b/packages/vite/src/runtime.ts @@ -1,3 +1,4 @@ +import type * as Component from "effect-fc-next/Component" import * as Refreshable from "effect-fc-next/Refreshable" export interface HotContext { @@ -7,11 +8,11 @@ export interface HotContext { } interface RefreshData { - readonly cells: Map> + readonly cells: Map ids?: readonly string[] } -const hotDataKey = "__effectFcRefresh" +const hotDataKey = "__effectViewRefresh" const getRefreshData = (hot: HotContext): RefreshData => { const data = hot.data as Record @@ -32,7 +33,7 @@ const getRefreshData = (hot: HotContext): RefreshData => { * This API is injected by `@effect-view/vite`; applications should not need to * call it directly. */ -export const register = ( +export const register = ( component: A, hot: HotContext | undefined, id: string, diff --git a/packages/vite/test/runtime.test.ts b/packages/vite/test/runtime.test.ts index 8acf1a5..ff200ef 100644 --- a/packages/vite/test/runtime.test.ts +++ b/packages/vite/test/runtime.test.ts @@ -1,21 +1,25 @@ +import type * as Component from "effect-fc-next/Component" import * as Refreshable from "effect-fc-next/Refreshable" import { describe, expect, it, vi } from "vitest" import { accept, register } from "../src/runtime.js" describe("refresh runtime", () => { + const component = (value: A): A & Component.Component.Any => + value as A & Component.Component.Any + it("retains a cell and notifies subscribers for compatible updates", async () => { const hot = { data: {}, accept: vi.fn(), invalidate: vi.fn(), } - const first = register({ body: "first" }, hot, "module:View", "hooks") + const first = register(component({ body: "first" }), hot, "module:View", "hooks") const cell = (first as Record)[Refreshable.RefreshableTypeId] as Refreshable.Cell const listener = vi.fn() cell.subscribe(listener) - const second = register({ body: "second" }, hot, "module:View", "hooks") + const second = register(component({ body: "second" }), hot, "module:View", "hooks") const secondCell = (second as Record)[Refreshable.RefreshableTypeId] expect(secondCell).toBe(cell) @@ -35,20 +39,20 @@ describe("refresh runtime", () => { accept: vi.fn(), invalidate: vi.fn(), } - const first = register({}, hot, "module:View", "one") + const first = register(component({}), hot, "module:View", "one") const cell = (first as Record)[Refreshable.RefreshableTypeId] as Refreshable.Cell - register({}, hot, "module:View", "two") + register(component({}), hot, "module:View", "two") expect(cell.snapshot.resetRevision).toBe(1) - register({}, hot, "module:View", "two", true) + register(component({}), hot, "module:View", "two", true) expect(cell.snapshot.resetRevision).toBe(2) }) it("is inert outside a Vite hot context", () => { - const component = {} - expect(register(component, undefined, "module:View", "hooks")).toBe(component) - expect((component as Record)[Refreshable.RefreshableTypeId]).toBeUndefined() + const descriptor = component({}) + expect(register(descriptor, undefined, "module:View", "hooks")).toBe(descriptor) + expect((descriptor as Record)[Refreshable.RefreshableTypeId]).toBeUndefined() }) it("invalidates when the module's View IDs change", () => { -- 2.52.0 From c40df980ed2ba489226b410feaa9e2bae2c36075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 25 Jul 2026 22:44:09 +0200 Subject: [PATCH 116/133] Rename vite plugin --- REACT_REFRESH_FEASIBILITY.md | 4 ++-- bun.lock | 8 ++++---- packages/effect-fc-next/src/Refreshable.ts | 2 +- packages/example-next/package.json | 2 +- packages/example-next/vite.config.ts | 4 ++-- packages/{vite => vite-plugin}/README.md | 6 +++--- packages/{vite => vite-plugin}/biome.json | 0 packages/{vite => vite-plugin}/package.json | 2 +- packages/{vite => vite-plugin}/src/index.ts | 4 ++-- packages/{vite => vite-plugin}/src/runtime.ts | 2 +- packages/{vite => vite-plugin}/test/plugin.test.ts | 10 +++++----- packages/{vite => vite-plugin}/test/runtime.test.ts | 0 packages/{vite => vite-plugin}/tsconfig.json | 0 packages/{vite => vite-plugin}/vitest.config.ts | 0 14 files changed, 22 insertions(+), 22 deletions(-) rename packages/{vite => vite-plugin}/README.md (92%) rename packages/{vite => vite-plugin}/biome.json (100%) rename packages/{vite => vite-plugin}/package.json (96%) rename packages/{vite => vite-plugin}/src/index.ts (99%) rename packages/{vite => vite-plugin}/src/runtime.ts (96%) rename packages/{vite => vite-plugin}/test/plugin.test.ts (94%) rename packages/{vite => vite-plugin}/test/runtime.test.ts (100%) rename packages/{vite => vite-plugin}/tsconfig.json (100%) rename packages/{vite => vite-plugin}/vitest.config.ts (100%) diff --git a/REACT_REFRESH_FEASIBILITY.md b/REACT_REFRESH_FEASIBILITY.md index 3ae7583..641e754 100644 --- a/REACT_REFRESH_FEASIBILITY.md +++ b/REACT_REFRESH_FEASIBILITY.md @@ -130,11 +130,11 @@ A complete implementation should provide the same safety contract developers exp ### 1. Add an Effect View refresh transform -Ship a Vite plugin, for example `@effect-view/vite`, placed before `react()`: +Ship a Vite plugin, `@effect-view/vite-plugin`, placed before `react()`: ```ts plugins: [ - effectViewRefresh(), + effectViewPlugin(), react(), ] ``` diff --git a/bun.lock b/bun.lock index 230a672..ae1600b 100644 --- a/bun.lock +++ b/bun.lock @@ -111,7 +111,7 @@ "react-icons": "^5.6.0", }, "devDependencies": { - "@effect-view/vite": "workspace:*", + "@effect-view/vite-plugin": "workspace:*", "@tanstack/react-router": "^1.170.10", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/router-plugin": "^1.168.13", @@ -124,8 +124,8 @@ "vite": "^8.0.16", }, }, - "packages/vite": { - "name": "@effect-view/vite", + "packages/vite-plugin": { + "name": "@effect-view/vite-plugin", "version": "0.0.0", "dependencies": { "typescript": "^6.0.3", @@ -574,7 +574,7 @@ "@effect-fc/example-next": ["@effect-fc/example-next@workspace:packages/example-next"], - "@effect-view/vite": ["@effect-view/vite@workspace:packages/vite"], + "@effect-view/vite-plugin": ["@effect-view/vite-plugin@workspace:packages/vite-plugin"], "@effect/language-service": ["@effect/language-service@0.86.6", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-uwXbp+lWzt60bZnm6lG6S5DWy0m6TthUzV2hcQoiS6NzieEDm07tuBakscxOXoO+AU9+yTH+0TSIa9mEDt9dFA=="], diff --git a/packages/effect-fc-next/src/Refreshable.ts b/packages/effect-fc-next/src/Refreshable.ts index e9b5f00..781f895 100644 --- a/packages/effect-fc-next/src/Refreshable.ts +++ b/packages/effect-fc-next/src/Refreshable.ts @@ -8,7 +8,7 @@ import type * as Component from "./Component.js" * development refresh cell. * * This low-level API is intended for development-server integrations such as - * `@effect-view/vite`. + * `@effect-view/vite-plugin`. */ export const RefreshableTypeId: unique symbol = Symbol.for("@effect-view/Refreshable/Refreshable") export type RefreshableTypeId = typeof RefreshableTypeId diff --git a/packages/example-next/package.json b/packages/example-next/package.json index bfc7e9c..5e96b7a 100644 --- a/packages/example-next/package.json +++ b/packages/example-next/package.json @@ -14,7 +14,7 @@ "clean:modules": "rm -rf node_modules" }, "devDependencies": { - "@effect-view/vite": "workspace:*", + "@effect-view/vite-plugin": "workspace:*", "@tanstack/react-router": "^1.170.10", "@tanstack/react-router-devtools": "^1.167.0", "@tanstack/router-plugin": "^1.168.13", diff --git a/packages/example-next/vite.config.ts b/packages/example-next/vite.config.ts index 2875ccf..45684ff 100644 --- a/packages/example-next/vite.config.ts +++ b/packages/example-next/vite.config.ts @@ -1,6 +1,6 @@ import { tanstackRouter } from "@tanstack/router-plugin/vite" import react from "@vitejs/plugin-react" -import effectViewRefresh from "@effect-view/vite" +import { effectViewPlugin } from "@effect-view/vite-plugin" import path from "node:path" import { defineConfig } from "vite" @@ -8,7 +8,7 @@ import { defineConfig } from "vite" // https://vite.dev/config/ export default defineConfig({ plugins: [ - effectViewRefresh(), + effectViewPlugin(), tanstackRouter({ target: "react", autoCodeSplitting: true, diff --git a/packages/vite/README.md b/packages/vite-plugin/README.md similarity index 92% rename from packages/vite/README.md rename to packages/vite-plugin/README.md index 8ddb2cd..65b9f09 100644 --- a/packages/vite/README.md +++ b/packages/vite-plugin/README.md @@ -1,15 +1,15 @@ -# `@effect-view/vite` +# `@effect-view/vite-plugin` Experimental Vite Fast Refresh support for Effect View components. ```ts -import effectViewRefresh from "@effect-view/vite" +import { effectViewPlugin } from "@effect-view/vite-plugin" import react from "@vitejs/plugin-react" import { defineConfig } from "vite" export default defineConfig({ plugins: [ - effectViewRefresh(), + effectViewPlugin(), react(), ], }) diff --git a/packages/vite/biome.json b/packages/vite-plugin/biome.json similarity index 100% rename from packages/vite/biome.json rename to packages/vite-plugin/biome.json diff --git a/packages/vite/package.json b/packages/vite-plugin/package.json similarity index 96% rename from packages/vite/package.json rename to packages/vite-plugin/package.json index f19e24d..e863379 100644 --- a/packages/vite/package.json +++ b/packages/vite-plugin/package.json @@ -1,5 +1,5 @@ { - "name": "@effect-view/vite", + "name": "@effect-view/vite-plugin", "description": "Vite Fast Refresh support for Effect View components", "version": "0.0.0", "type": "module", diff --git a/packages/vite/src/index.ts b/packages/vite-plugin/src/index.ts similarity index 99% rename from packages/vite/src/index.ts rename to packages/vite-plugin/src/index.ts index 9f9c722..cd6db1a 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite-plugin/src/index.ts @@ -42,7 +42,7 @@ interface Definition { const defaultInclude = /\.[cm]?[jt]sx?$/ const defaultExclude = /\/node_modules\// -const runtimeImport = "@effect-view/vite/runtime" +const runtimeImport = "@effect-view/vite-plugin/runtime" const refreshIdentifier = "__effectViewRefresh" const acceptIdentifier = "__effectViewAccept" @@ -342,7 +342,7 @@ const scriptKind = (id: string): ts.ScriptKind => id.endsWith(".tsx") * * Place this plugin before `@vitejs/plugin-react`. */ -export default function effectViewRefresh( +export function effectViewPlugin( options: EffectViewRefreshOptions = {}, ): Plugin { const include = options.include ?? defaultInclude diff --git a/packages/vite/src/runtime.ts b/packages/vite-plugin/src/runtime.ts similarity index 96% rename from packages/vite/src/runtime.ts rename to packages/vite-plugin/src/runtime.ts index a7db583..a6faa36 100644 --- a/packages/vite/src/runtime.ts +++ b/packages/vite-plugin/src/runtime.ts @@ -30,7 +30,7 @@ const getRefreshData = (hot: HotContext): RefreshData => { /** * Registers an Effect View descriptor in a Vite HMR data cell. * - * This API is injected by `@effect-view/vite`; applications should not need to + * This API is injected by `@effect-view/vite-plugin`; applications should not need to * call it directly. */ export const register = ( diff --git a/packages/vite/test/plugin.test.ts b/packages/vite-plugin/test/plugin.test.ts similarity index 94% rename from packages/vite/test/plugin.test.ts rename to packages/vite-plugin/test/plugin.test.ts index 4213cd7..ce2fdbc 100644 --- a/packages/vite/test/plugin.test.ts +++ b/packages/vite-plugin/test/plugin.test.ts @@ -1,7 +1,7 @@ import path from "node:path" import type { Plugin, TransformPluginContext } from "vite" import { describe, expect, it } from "vitest" -import effectViewRefresh from "../src/index.js" +import { effectViewPlugin } from "../src/index.js" const runTransform = async ( @@ -25,9 +25,9 @@ const runTransform = async ( const transform = ( code: string, id = path.join(process.cwd(), "src/View.tsx"), -): Promise => runTransform(effectViewRefresh(), code, id) +): Promise => runTransform(effectViewPlugin(), code, id) -describe("effectViewRefresh", () => { +describe("effectViewPlugin", () => { it("wraps a const Effect View descriptor", async () => { const result = await transform(` import { Component } from "effect-fc-next" @@ -39,7 +39,7 @@ export const CounterView = Component.make("CounterView")(function*() { }) `) - expect(result).toContain("import { accept as __effectViewAccept, register as __effectViewRefresh } from \"@effect-view/vite/runtime\"") + expect(result).toContain("import { accept as __effectViewAccept, register as __effectViewRefresh } from \"@effect-view/vite-plugin/runtime\"") expect(result).toContain("__effectViewRefresh(Component.make(\"CounterView\")") expect(result).toContain("\"src/View.tsx:CounterView\"") expect(result).toContain("__effectViewAccept(import.meta.hot, [\"src/View.tsx:CounterView\"])") @@ -103,7 +103,7 @@ export function View() { }) it("keeps instrumenting a module when all Effect Views are removed", async () => { - const plugin = effectViewRefresh() as Plugin + const plugin = effectViewPlugin() as Plugin const id = path.join(process.cwd(), "src/Removed.tsx") await runTransform(plugin, ` diff --git a/packages/vite/test/runtime.test.ts b/packages/vite-plugin/test/runtime.test.ts similarity index 100% rename from packages/vite/test/runtime.test.ts rename to packages/vite-plugin/test/runtime.test.ts diff --git a/packages/vite/tsconfig.json b/packages/vite-plugin/tsconfig.json similarity index 100% rename from packages/vite/tsconfig.json rename to packages/vite-plugin/tsconfig.json diff --git a/packages/vite/vitest.config.ts b/packages/vite-plugin/vitest.config.ts similarity index 100% rename from packages/vite/vitest.config.ts rename to packages/vite-plugin/vitest.config.ts -- 2.52.0 From 3533fa0c844d4d6a3f5fe15a1afe4ca430fe48af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 25 Jul 2026 22:48:42 +0200 Subject: [PATCH 117/133] Remove ErrorObserver --- packages/effect-fc-next/package.json | 4 -- packages/effect-fc-next/src/ErrorObserver.ts | 47 -------------------- packages/effect-fc-next/src/index.ts | 1 - 3 files changed, 52 deletions(-) delete mode 100644 packages/effect-fc-next/src/ErrorObserver.ts diff --git a/packages/effect-fc-next/package.json b/packages/effect-fc-next/package.json index 73f5427..ce2152c 100644 --- a/packages/effect-fc-next/package.json +++ b/packages/effect-fc-next/package.json @@ -25,10 +25,6 @@ "types": "./dist/Component.d.ts", "default": "./dist/Component.js" }, - "./ErrorObserver": { - "types": "./dist/ErrorObserver.d.ts", - "default": "./dist/ErrorObserver.js" - }, "./Form": { "types": "./dist/Form.d.ts", "default": "./dist/Form.js" diff --git a/packages/effect-fc-next/src/ErrorObserver.ts b/packages/effect-fc-next/src/ErrorObserver.ts deleted file mode 100644 index 0fd19aa..0000000 --- a/packages/effect-fc-next/src/ErrorObserver.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { type Cause, Context, Effect, Layer, Option, Pipeable, Predicate, PubSub, type Scope } from "effect" - - -export const ErrorObserverTypeId: unique symbol = Symbol.for("@effect-fc/ErrorObserver/ErrorObserver") -export type ErrorObserverTypeId = typeof ErrorObserverTypeId - -export interface ErrorObserver extends Pipeable.Pipeable { - readonly [ErrorObserverTypeId]: ErrorObserverTypeId - handle(effect: Effect.Effect): Effect.Effect - readonly subscribe: Effect.Effect>, never, Scope.Scope> -} - -export const ErrorObserver = () => Context.Service>( - "@effect-fc/ErrorObserver/ErrorObserver", -) - -export class ErrorObserverImpl extends Pipeable.Class implements ErrorObserver { - readonly [ErrorObserverTypeId]: ErrorObserverTypeId = ErrorObserverTypeId - - constructor(readonly pubsub: PubSub.PubSub>) { - super() - } - - get subscribe(): Effect.Effect>, never, Scope.Scope> { - return PubSub.subscribe(this.pubsub) - } - - handle(effect: Effect.Effect): Effect.Effect { - return Effect.tapCause(effect, cause => Effect.asVoid( - PubSub.publish(this.pubsub, cause as unknown as Cause.Cause), - )) - } -} - -export const isErrorObserver = (u: unknown): u is ErrorObserver => Predicate.hasProperty(u, ErrorObserverTypeId) - -export const layer: Layer.Layer = Layer.effect(ErrorObserver())( - Effect.map(PubSub.unbounded>(), pubsub => new ErrorObserverImpl(pubsub)), -) - -export const handle = (effect: Effect.Effect): Effect.Effect => Effect.flatMap( - Effect.serviceOption(ErrorObserver()), - Option.match({ - onSome: observer => observer.handle(effect), - onNone: () => effect, - }), -) diff --git a/packages/effect-fc-next/src/index.ts b/packages/effect-fc-next/src/index.ts index 49f882e..a2d3bda 100644 --- a/packages/effect-fc-next/src/index.ts +++ b/packages/effect-fc-next/src/index.ts @@ -1,6 +1,5 @@ export * as Async from "./Async.js" export * as Component from "./Component.js" -export * as ErrorObserver from "./ErrorObserver.js" export * as Form from "./Form.js" export * as Lens from "./Lens.js" export * as LensForm from "./LensForm.js" -- 2.52.0 From 65e90325d0e786a56d1ffc1ecdc06d3e3610bd2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 25 Jul 2026 23:47:16 +0200 Subject: [PATCH 118/133] Change build system --- packages/effect-fc-next/package.json | 4 +-- .../{test => src}/Async.test.tsx | 8 +++--- .../{test => src}/Component.test.tsx | 8 +++--- .../{test => src}/Lens.test.tsx | 6 ++--- .../{test => src}/Query.test.ts | 6 ++--- .../{test => src}/Refreshable.test.ts | 4 +-- .../{test => src}/View.test.tsx | 6 ++--- .../{test/setup.ts => src/setup-tests.ts} | 0 packages/effect-fc-next/tsconfig.build.json | 4 +++ packages/effect-fc-next/tsconfig.json | 1 - packages/effect-fc-next/vitest.config.ts | 4 +-- packages/effect-fc/package.json | 4 +-- .../{test => src}/Component.test.tsx | 4 +-- .../effect-fc/{test => src}/Lens.test.tsx | 6 ++--- .../effect-fc/{test => src}/Query.test.ts | 6 ++--- .../{test => src}/Subscribable.test.tsx | 6 ++--- packages/effect-fc/tsconfig.build.json | 4 +++ packages/effect-fc/tsconfig.json | 1 - packages/effect-fc/vitest.config.ts | 2 +- packages/vite-plugin/biome.json | 2 +- packages/vite-plugin/package.json | 4 +-- .../vite-plugin/{test => src}/plugin.test.ts | 2 +- .../vite-plugin/{test => src}/runtime.test.ts | 2 +- packages/vite-plugin/tsconfig.build.json | 4 +++ packages/vite-plugin/tsconfig.json | 26 ++++++++++++++++--- packages/vite-plugin/vitest.config.ts | 2 +- 26 files changed, 78 insertions(+), 48 deletions(-) rename packages/effect-fc-next/{test => src}/Async.test.tsx (91%) rename packages/effect-fc-next/{test => src}/Component.test.tsx (98%) rename packages/effect-fc-next/{test => src}/Lens.test.tsx (97%) rename packages/effect-fc-next/{test => src}/Query.test.ts (97%) rename packages/effect-fc-next/{test => src}/Refreshable.test.ts (95%) rename packages/effect-fc-next/{test => src}/View.test.tsx (95%) rename packages/effect-fc-next/{test/setup.ts => src/setup-tests.ts} (100%) create mode 100644 packages/effect-fc-next/tsconfig.build.json rename packages/effect-fc/{test => src}/Component.test.tsx (99%) rename packages/effect-fc/{test => src}/Lens.test.tsx (97%) rename packages/effect-fc/{test => src}/Query.test.ts (97%) rename packages/effect-fc/{test => src}/Subscribable.test.tsx (96%) create mode 100644 packages/effect-fc/tsconfig.build.json rename packages/vite-plugin/{test => src}/plugin.test.ts (98%) rename packages/vite-plugin/{test => src}/runtime.test.ts (98%) create mode 100644 packages/vite-plugin/tsconfig.build.json diff --git a/packages/effect-fc-next/package.json b/packages/effect-fc-next/package.json index ce2152c..091e545 100644 --- a/packages/effect-fc-next/package.json +++ b/packages/effect-fc-next/package.json @@ -90,9 +90,9 @@ "lint:tsc": "tsc --noEmit", "lint:biome": "biome lint", "test": "vitest run", - "build": "tsc", + "build": "tsc -b tsconfig.build.json", "pack": "npm pack", - "clean:cache": "rm -rf .turbo tsconfig.tsbuildinfo", + "clean:cache": "rm -rf .turbo *.tsbuildinfo", "clean:dist": "rm -rf dist", "clean:modules": "rm -rf node_modules" }, diff --git a/packages/effect-fc-next/test/Async.test.tsx b/packages/effect-fc-next/src/Async.test.tsx similarity index 91% rename from packages/effect-fc-next/test/Async.test.tsx rename to packages/effect-fc-next/src/Async.test.tsx index 91182d2..eb41758 100644 --- a/packages/effect-fc-next/test/Async.test.tsx +++ b/packages/effect-fc-next/src/Async.test.tsx @@ -2,10 +2,10 @@ import { act, fireEvent, render, screen } from "@testing-library/react" import { Effect, Layer } from "effect" import * as React from "react" import { describe, expect, it, vi } from "vitest" -import * as Async from "../src/Async.js" -import * as Component from "../src/Component.js" -import * as Memoized from "../src/Memoized.js" -import * as ReactRuntime from "../src/ReactRuntime.js" +import * as Async from "./Async.js" +import * as Component from "./Component.js" +import * as Memoized from "./Memoized.js" +import * as ReactRuntime from "./ReactRuntime.js" describe("Async", () => { it("does not rerun for an unrelated parent state update", async () => { diff --git a/packages/effect-fc-next/test/Component.test.tsx b/packages/effect-fc-next/src/Component.test.tsx similarity index 98% rename from packages/effect-fc-next/test/Component.test.tsx rename to packages/effect-fc-next/src/Component.test.tsx index 4087766..b15d3ef 100644 --- a/packages/effect-fc-next/test/Component.test.tsx +++ b/packages/effect-fc-next/src/Component.test.tsx @@ -2,10 +2,10 @@ import { act, fireEvent, render, screen, waitFor } from "@testing-library/react" import { Context, Effect, HashMap, Layer, SubscriptionRef } from "effect" import * as React from "react" import { afterEach, describe, expect, it, vi } from "vitest" -import * as Component from "../src/Component.js" -import * as ReactRuntime from "../src/ReactRuntime.js" -import * as Refreshable from "../src/Refreshable.js" -import * as ScopeRegistry from "../src/ScopeRegistry.js" +import * as Component from "./Component.js" +import * as ReactRuntime from "./ReactRuntime.js" +import * as Refreshable from "./Refreshable.js" +import * as ScopeRegistry from "./ScopeRegistry.js" class ValueService extends Context.Service()("ValueService") {} diff --git a/packages/effect-fc-next/test/Lens.test.tsx b/packages/effect-fc-next/src/Lens.test.tsx similarity index 97% rename from packages/effect-fc-next/test/Lens.test.tsx rename to packages/effect-fc-next/src/Lens.test.tsx index 093b7bf..82e161d 100644 --- a/packages/effect-fc-next/test/Lens.test.tsx +++ b/packages/effect-fc-next/src/Lens.test.tsx @@ -2,9 +2,9 @@ import { fireEvent, render, screen, waitFor } from "@testing-library/react" import { Effect, Layer, SubscriptionRef } from "effect" import * as React from "react" import { describe, expect, it } from "vitest" -import * as Component from "../src/Component.js" -import * as Lens from "../src/Lens.js" -import * as ReactRuntime from "../src/ReactRuntime.js" +import * as Component from "./Component.js" +import * as Lens from "./Lens.js" +import * as ReactRuntime from "./ReactRuntime.js" const makeRuntime = async () => { diff --git a/packages/effect-fc-next/test/Query.test.ts b/packages/effect-fc-next/src/Query.test.ts similarity index 97% rename from packages/effect-fc-next/test/Query.test.ts rename to packages/effect-fc-next/src/Query.test.ts index 66724d1..b518c0d 100644 --- a/packages/effect-fc-next/test/Query.test.ts +++ b/packages/effect-fc-next/src/Query.test.ts @@ -1,9 +1,9 @@ import { Effect, type Scope, Stream } from "effect" import { AsyncResult } from "effect/unstable/reactivity" import { describe, expect, it } from "vitest" -import * as Query from "../src/Query.js" -import * as QueryClient from "../src/QueryClient.js" -import * as View from "../src/View.js" +import * as Query from "./Query.js" +import * as QueryClient from "./QueryClient.js" +import * as View from "./View.js" const runQueryTest = (effect: Effect.Effect) => diff --git a/packages/effect-fc-next/test/Refreshable.test.ts b/packages/effect-fc-next/src/Refreshable.test.ts similarity index 95% rename from packages/effect-fc-next/test/Refreshable.test.ts rename to packages/effect-fc-next/src/Refreshable.test.ts index e756d7c..110ca74 100644 --- a/packages/effect-fc-next/test/Refreshable.test.ts +++ b/packages/effect-fc-next/src/Refreshable.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from "vitest" -import type * as Component from "../src/Component.js" -import * as Refreshable from "../src/Refreshable.js" +import type * as Component from "./Component.js" +import * as Refreshable from "./Refreshable.js" describe("Refreshable", () => { diff --git a/packages/effect-fc-next/test/View.test.tsx b/packages/effect-fc-next/src/View.test.tsx similarity index 95% rename from packages/effect-fc-next/test/View.test.tsx rename to packages/effect-fc-next/src/View.test.tsx index 301b6de..4dbd72b 100644 --- a/packages/effect-fc-next/test/View.test.tsx +++ b/packages/effect-fc-next/src/View.test.tsx @@ -2,9 +2,9 @@ import { render, screen, waitFor } from "@testing-library/react" import { Effect, Layer, SubscriptionRef } from "effect" import { Lens } from "effect-lens" import { describe, expect, it } from "vitest" -import * as Component from "../src/Component.js" -import * as ReactRuntime from "../src/ReactRuntime.js" -import * as View from "../src/View.js" +import * as Component from "./Component.js" +import * as ReactRuntime from "./ReactRuntime.js" +import * as View from "./View.js" const makeRuntime = async () => { diff --git a/packages/effect-fc-next/test/setup.ts b/packages/effect-fc-next/src/setup-tests.ts similarity index 100% rename from packages/effect-fc-next/test/setup.ts rename to packages/effect-fc-next/src/setup-tests.ts diff --git a/packages/effect-fc-next/tsconfig.build.json b/packages/effect-fc-next/tsconfig.build.json new file mode 100644 index 0000000..a79c9cd --- /dev/null +++ b/packages/effect-fc-next/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["**/setup-tests.ts", "**/*.test.ts", "**/*.test.tsx", "**/*.spec.ts", "**/*.spec.tsx"] +} diff --git a/packages/effect-fc-next/tsconfig.json b/packages/effect-fc-next/tsconfig.json index 6ad0810..f5ac024 100644 --- a/packages/effect-fc-next/tsconfig.json +++ b/packages/effect-fc-next/tsconfig.json @@ -36,5 +36,4 @@ }, "include": ["./src"], - "exclude": ["**/*.test.ts", "**/*.spec.ts"] } diff --git a/packages/effect-fc-next/vitest.config.ts b/packages/effect-fc-next/vitest.config.ts index 1374d42..b59fb83 100644 --- a/packages/effect-fc-next/vitest.config.ts +++ b/packages/effect-fc-next/vitest.config.ts @@ -4,7 +4,7 @@ import { defineConfig } from "vitest/config" export default defineConfig({ test: { environment: "jsdom", - include: ["test/**/*.test.ts?(x)"], - setupFiles: ["test/setup.ts"], + include: ["./src/**/*.test.ts?(x)"], + setupFiles: ["./src/setup-tests.ts"], }, }) diff --git a/packages/effect-fc/package.json b/packages/effect-fc/package.json index f729dad..f49579e 100644 --- a/packages/effect-fc/package.json +++ b/packages/effect-fc/package.json @@ -29,12 +29,12 @@ ] }, "scripts": { - "build": "tsc", "lint:tsc": "tsc --noEmit", "lint:biome": "biome lint", "test": "vitest run", + "build": "tsc -b tsconfig.build.json", "pack": "npm pack", - "clean:cache": "rm -rf .turbo tsconfig.tsbuildinfo", + "clean:cache": "rm -rf .turbo *.tsbuildinfo", "clean:dist": "rm -rf dist", "clean:modules": "rm -rf node_modules" }, diff --git a/packages/effect-fc/test/Component.test.tsx b/packages/effect-fc/src/Component.test.tsx similarity index 99% rename from packages/effect-fc/test/Component.test.tsx rename to packages/effect-fc/src/Component.test.tsx index f870806..8ab9af1 100644 --- a/packages/effect-fc/test/Component.test.tsx +++ b/packages/effect-fc/src/Component.test.tsx @@ -2,8 +2,8 @@ import { render, screen, waitFor } from "@testing-library/react" import { Context, Effect, Layer } from "effect" import * as React from "react" import { afterEach, describe, expect, it, vi } from "vitest" -import * as Component from "../src/Component.js" -import * as ReactRuntime from "../src/ReactRuntime.js" +import * as Component from "./Component.js" +import * as ReactRuntime from "./ReactRuntime.js" class ValueService extends Context.Tag("ValueService")() {} diff --git a/packages/effect-fc/test/Lens.test.tsx b/packages/effect-fc/src/Lens.test.tsx similarity index 97% rename from packages/effect-fc/test/Lens.test.tsx rename to packages/effect-fc/src/Lens.test.tsx index 9bf6233..e6ce6bb 100644 --- a/packages/effect-fc/test/Lens.test.tsx +++ b/packages/effect-fc/src/Lens.test.tsx @@ -2,9 +2,9 @@ import { fireEvent, render, screen, waitFor } from "@testing-library/react" import { Effect, Layer, SubscriptionRef } from "effect" import * as React from "react" import { describe, expect, it } from "vitest" -import * as Component from "../src/Component.js" -import * as Lens from "../src/Lens.js" -import * as ReactRuntime from "../src/ReactRuntime.js" +import * as Component from "./Component.js" +import * as Lens from "./Lens.js" +import * as ReactRuntime from "./ReactRuntime.js" const makeRuntime = async () => { diff --git a/packages/effect-fc/test/Query.test.ts b/packages/effect-fc/src/Query.test.ts similarity index 97% rename from packages/effect-fc/test/Query.test.ts rename to packages/effect-fc/src/Query.test.ts index ee6544f..c5d37be 100644 --- a/packages/effect-fc/test/Query.test.ts +++ b/packages/effect-fc/src/Query.test.ts @@ -1,8 +1,8 @@ import { Effect, Option, type Scope, Stream } from "effect" import { describe, expect, it } from "vitest" -import * as Query from "../src/Query.js" -import * as QueryClient from "../src/QueryClient.js" -import * as Result from "../src/Result.js" +import * as Query from "./Query.js" +import * as QueryClient from "./QueryClient.js" +import * as Result from "./Result.js" const runQueryTest = (effect: Effect.Effect) => diff --git a/packages/effect-fc/test/Subscribable.test.tsx b/packages/effect-fc/src/Subscribable.test.tsx similarity index 96% rename from packages/effect-fc/test/Subscribable.test.tsx rename to packages/effect-fc/src/Subscribable.test.tsx index 772d330..c76132a 100644 --- a/packages/effect-fc/test/Subscribable.test.tsx +++ b/packages/effect-fc/src/Subscribable.test.tsx @@ -2,9 +2,9 @@ import { render, screen, waitFor } from "@testing-library/react" import { Effect, Fiber, Layer, Stream, SubscriptionRef } from "effect" import { Lens } from "effect-lens" import { describe, expect, it } from "vitest" -import * as Component from "../src/Component.js" -import * as ReactRuntime from "../src/ReactRuntime.js" -import * as Subscribable from "../src/Subscribable.js" +import * as Component from "./Component.js" +import * as ReactRuntime from "./ReactRuntime.js" +import * as Subscribable from "./Subscribable.js" const makeRuntime = async () => { diff --git a/packages/effect-fc/tsconfig.build.json b/packages/effect-fc/tsconfig.build.json new file mode 100644 index 0000000..a79c9cd --- /dev/null +++ b/packages/effect-fc/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["**/setup-tests.ts", "**/*.test.ts", "**/*.test.tsx", "**/*.spec.ts", "**/*.spec.tsx"] +} diff --git a/packages/effect-fc/tsconfig.json b/packages/effect-fc/tsconfig.json index 6ad0810..f5ac024 100644 --- a/packages/effect-fc/tsconfig.json +++ b/packages/effect-fc/tsconfig.json @@ -36,5 +36,4 @@ }, "include": ["./src"], - "exclude": ["**/*.test.ts", "**/*.spec.ts"] } diff --git a/packages/effect-fc/vitest.config.ts b/packages/effect-fc/vitest.config.ts index cec0e39..75476d8 100644 --- a/packages/effect-fc/vitest.config.ts +++ b/packages/effect-fc/vitest.config.ts @@ -4,6 +4,6 @@ import { defineConfig } from "vitest/config" export default defineConfig({ test: { environment: "jsdom", - include: ["test/**/*.test.ts?(x)"], + include: ["./src/**/*.test.ts?(x)"], }, }) diff --git a/packages/vite-plugin/biome.json b/packages/vite-plugin/biome.json index 868f17a..41d707b 100644 --- a/packages/vite-plugin/biome.json +++ b/packages/vite-plugin/biome.json @@ -3,6 +3,6 @@ "root": false, "extends": "//", "files": { - "includes": ["./src/**", "./test/**"] + "includes": ["./src/**"] } } diff --git a/packages/vite-plugin/package.json b/packages/vite-plugin/package.json index e863379..74c9c4b 100644 --- a/packages/vite-plugin/package.json +++ b/packages/vite-plugin/package.json @@ -26,9 +26,9 @@ "lint:tsc": "tsc --noEmit", "lint:biome": "biome lint", "test": "vitest run", - "build": "tsc", + "build": "tsc -b tsconfig.build.json", "pack": "npm pack", - "clean:cache": "rm -rf .turbo tsconfig.tsbuildinfo", + "clean:cache": "rm -rf .turbo *.tsbuildinfo", "clean:dist": "rm -rf dist", "clean:modules": "rm -rf node_modules" }, diff --git a/packages/vite-plugin/test/plugin.test.ts b/packages/vite-plugin/src/plugin.test.ts similarity index 98% rename from packages/vite-plugin/test/plugin.test.ts rename to packages/vite-plugin/src/plugin.test.ts index ce2fdbc..f78486c 100644 --- a/packages/vite-plugin/test/plugin.test.ts +++ b/packages/vite-plugin/src/plugin.test.ts @@ -1,7 +1,7 @@ import path from "node:path" import type { Plugin, TransformPluginContext } from "vite" import { describe, expect, it } from "vitest" -import { effectViewPlugin } from "../src/index.js" +import { effectViewPlugin } from "./index.js" const runTransform = async ( diff --git a/packages/vite-plugin/test/runtime.test.ts b/packages/vite-plugin/src/runtime.test.ts similarity index 98% rename from packages/vite-plugin/test/runtime.test.ts rename to packages/vite-plugin/src/runtime.test.ts index ff200ef..65be4a7 100644 --- a/packages/vite-plugin/test/runtime.test.ts +++ b/packages/vite-plugin/src/runtime.test.ts @@ -1,7 +1,7 @@ import type * as Component from "effect-fc-next/Component" import * as Refreshable from "effect-fc-next/Refreshable" import { describe, expect, it, vi } from "vitest" -import { accept, register } from "../src/runtime.js" +import { accept, register } from "./runtime.js" describe("refresh runtime", () => { diff --git a/packages/vite-plugin/tsconfig.build.json b/packages/vite-plugin/tsconfig.build.json new file mode 100644 index 0000000..a79c9cd --- /dev/null +++ b/packages/vite-plugin/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["**/setup-tests.ts", "**/*.test.ts", "**/*.test.tsx", "**/*.spec.ts", "**/*.spec.tsx"] +} diff --git a/packages/vite-plugin/tsconfig.json b/packages/vite-plugin/tsconfig.json index 625da67..f5ac024 100644 --- a/packages/vite-plugin/tsconfig.json +++ b/packages/vite-plugin/tsconfig.json @@ -1,19 +1,39 @@ { "compilerOptions": { - "lib": ["ESNext"], + // Enable latest features + "lib": ["ESNext", "DOM"], "target": "ESNext", "module": "NodeNext", "moduleDetection": "force", + "jsx": "react-jsx", + // "allowJs": true, + + // Bundler mode "moduleResolution": "NodeNext", + // "allowImportingTsExtensions": true, "verbatimModuleSyntax": true, + // "noEmit": true, + + // Best practices "strict": true, "skipLibCheck": true, "noFallthroughCasesInSwitch": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false, + + // Build "rootDir": "./src", "outDir": "./dist", "declaration": true, - "sourceMap": true + "sourceMap": true, + + "plugins": [ + { "name": "@effect/language-service" } + ] }, + "include": ["./src"], - "exclude": ["**/*.test.ts", "**/*.spec.ts"] } diff --git a/packages/vite-plugin/vitest.config.ts b/packages/vite-plugin/vitest.config.ts index 78ac2cb..a5cdb63 100644 --- a/packages/vite-plugin/vitest.config.ts +++ b/packages/vite-plugin/vitest.config.ts @@ -3,6 +3,6 @@ import { defineConfig } from "vitest/config" export default defineConfig({ test: { - include: ["test/**/*.test.ts"], + include: ["./src/**/*.test.ts?(x)"], }, }) -- 2.52.0 From 73399c1b2441ef5063224b90392a40fb3b537cb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 25 Jul 2026 23:52:11 +0200 Subject: [PATCH 119/133] Fix tests --- packages/vite-plugin/src/plugin.test.ts | 10 ++++++++-- packages/vite-plugin/src/runtime.test.ts | 14 ++++++++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/vite-plugin/src/plugin.test.ts b/packages/vite-plugin/src/plugin.test.ts index f78486c..822391f 100644 --- a/packages/vite-plugin/src/plugin.test.ts +++ b/packages/vite-plugin/src/plugin.test.ts @@ -1,9 +1,15 @@ import path from "node:path" -import type { Plugin, TransformPluginContext } from "vite" +import type { Plugin } from "vite" import { describe, expect, it } from "vitest" import { effectViewPlugin } from "./index.js" +type TransformHook = Extract< + NonNullable, + (...args: never[]) => unknown +> +type TransformPluginContext = ThisParameterType + const runTransform = async ( plugin: Plugin, code: string, @@ -19,7 +25,7 @@ const runTransform = async ( if (!result) return undefined - return typeof result === "string" ? result : result.code + return typeof result === "string" ? result : result.code?.toString() } const transform = ( diff --git a/packages/vite-plugin/src/runtime.test.ts b/packages/vite-plugin/src/runtime.test.ts index 65be4a7..da5745b 100644 --- a/packages/vite-plugin/src/runtime.test.ts +++ b/packages/vite-plugin/src/runtime.test.ts @@ -8,6 +8,12 @@ describe("refresh runtime", () => { const component = (value: A): A & Component.Component.Any => value as A & Component.Component.Any + const refreshCell = (value: Component.Component.Any): Refreshable.Cell => { + if (!Refreshable.isRefreshable(value)) + throw new Error("Expected a refreshable component") + return value[Refreshable.RefreshableTypeId] + } + it("retains a cell and notifies subscribers for compatible updates", async () => { const hot = { data: {}, @@ -15,12 +21,12 @@ describe("refresh runtime", () => { invalidate: vi.fn(), } const first = register(component({ body: "first" }), hot, "module:View", "hooks") - const cell = (first as Record)[Refreshable.RefreshableTypeId] as Refreshable.Cell + const cell = refreshCell(first) const listener = vi.fn() cell.subscribe(listener) const second = register(component({ body: "second" }), hot, "module:View", "hooks") - const secondCell = (second as Record)[Refreshable.RefreshableTypeId] + const secondCell = refreshCell(second) expect(secondCell).toBe(cell) expect(cell.current).toBe(second) @@ -40,7 +46,7 @@ describe("refresh runtime", () => { invalidate: vi.fn(), } const first = register(component({}), hot, "module:View", "one") - const cell = (first as Record)[Refreshable.RefreshableTypeId] as Refreshable.Cell + const cell = refreshCell(first) register(component({}), hot, "module:View", "two") expect(cell.snapshot.resetRevision).toBe(1) @@ -52,7 +58,7 @@ describe("refresh runtime", () => { it("is inert outside a Vite hot context", () => { const descriptor = component({}) expect(register(descriptor, undefined, "module:View", "hooks")).toBe(descriptor) - expect((descriptor as Record)[Refreshable.RefreshableTypeId]).toBeUndefined() + expect(Refreshable.isRefreshable(descriptor)).toBe(false) }) it("invalidates when the module's View IDs change", () => { -- 2.52.0 From 2b677f88109dead22f669a381fc3149327fc03db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 25 Jul 2026 23:54:24 +0200 Subject: [PATCH 120/133] Fix tests --- packages/effect-fc-next/src/Component.test.tsx | 2 +- packages/effect-fc-next/src/Lens.test.tsx | 16 +++++++++++----- packages/effect-fc/src/Lens.test.tsx | 16 +++++++++++----- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/packages/effect-fc-next/src/Component.test.tsx b/packages/effect-fc-next/src/Component.test.tsx index b15d3ef..69d0063 100644 --- a/packages/effect-fc-next/src/Component.test.tsx +++ b/packages/effect-fc-next/src/Component.test.tsx @@ -312,7 +312,7 @@ describe("Component", () => { yield* Effect.addFinalizer(() => Effect.sync(() => unmounts())) })) - return + return }).pipe( Component.withOptions({ nonReactiveTags: [...Component.defaultOptions.nonReactiveTags, ValueService], diff --git a/packages/effect-fc-next/src/Lens.test.tsx b/packages/effect-fc-next/src/Lens.test.tsx index 82e161d..c59c686 100644 --- a/packages/effect-fc-next/src/Lens.test.tsx +++ b/packages/effect-fc-next/src/Lens.test.tsx @@ -18,6 +18,12 @@ const makeRuntime = async () => { } } +const expectDefined = (value: A | undefined): A => { + if (value === undefined) + throw new Error("Expected value to be defined") + return value +} + describe("Lens", () => { it("useState stays in sync with lens updates in both directions", async () => { const { runtime, effectRuntime, dispose } = await makeRuntime() @@ -30,7 +36,7 @@ describe("Lens", () => { return ( <>

- + ) }).pipe( @@ -102,7 +108,7 @@ describe("Lens", () => { lens = reactLens })) - return + return }).pipe( Component.withContext(runtime.context) ) @@ -118,7 +124,7 @@ describe("Lens", () => { fireEvent.click(screen.getByRole("button", { name: "hello" })) await screen.findByText("hello!") - await waitFor(async () => expect(await Effect.runPromise(Lens.get(lens!))).toBe("hello!")) + await waitFor(async () => expect(await Effect.runPromise(Lens.get(expectDefined(lens)))).toBe("hello!")) view.unmount() await dispose() @@ -152,11 +158,11 @@ describe("Lens", () => { await screen.findByText("first") await waitFor(() => expect(lens).toBeDefined()) - await Effect.runPromise(Lens.set(lens!, { id: 1, label: "ignored" })) + await Effect.runPromise(Lens.set(expectDefined(lens), { id: 1, label: "ignored" })) await waitFor(() => expect(screen.getByText("first")).toBeTruthy()) expect(screen.queryByText("ignored")).toBeNull() - await Effect.runPromise(Lens.set(lens!, { id: 2, label: "updated" })) + await Effect.runPromise(Lens.set(expectDefined(lens), { id: 2, label: "updated" })) await screen.findByText("updated") view.unmount() diff --git a/packages/effect-fc/src/Lens.test.tsx b/packages/effect-fc/src/Lens.test.tsx index e6ce6bb..e3cff38 100644 --- a/packages/effect-fc/src/Lens.test.tsx +++ b/packages/effect-fc/src/Lens.test.tsx @@ -18,6 +18,12 @@ const makeRuntime = async () => { } } +const expectDefined = (value: A | undefined): A => { + if (value === undefined) + throw new Error("Expected value to be defined") + return value +} + describe("Lens", () => { it("useState stays in sync with lens updates in both directions", async () => { const { runtime, effectRuntime, dispose } = await makeRuntime() @@ -30,7 +36,7 @@ describe("Lens", () => { return ( <>
{value}
- + ) }).pipe( @@ -102,7 +108,7 @@ describe("Lens", () => { lens = reactLens })) - return + return }).pipe( Component.withRuntime(runtime.context) ) @@ -118,7 +124,7 @@ describe("Lens", () => { fireEvent.click(screen.getByRole("button", { name: "hello" })) await screen.findByText("hello!") - await waitFor(async () => expect(await Effect.runPromise(Lens.get(lens!))).toBe("hello!")) + await waitFor(async () => expect(await Effect.runPromise(Lens.get(expectDefined(lens)))).toBe("hello!")) view.unmount() await dispose() @@ -152,11 +158,11 @@ describe("Lens", () => { await screen.findByText("first") await waitFor(() => expect(lens).toBeDefined()) - await Effect.runPromise(Lens.set(lens!, { id: 1, label: "ignored" })) + await Effect.runPromise(Lens.set(expectDefined(lens), { id: 1, label: "ignored" })) await waitFor(() => expect(screen.getByText("first")).toBeTruthy()) expect(screen.queryByText("ignored")).toBeNull() - await Effect.runPromise(Lens.set(lens!, { id: 2, label: "updated" })) + await Effect.runPromise(Lens.set(expectDefined(lens), { id: 2, label: "updated" })) await screen.findByText("updated") view.unmount() -- 2.52.0 From 5e9c25641f2382b6d9e5d08608c20ad3bdc45596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 25 Jul 2026 23:56:44 +0200 Subject: [PATCH 121/133] Fix --- packages/effect-fc-next/package.json | 2 +- packages/effect-fc/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/effect-fc-next/package.json b/packages/effect-fc-next/package.json index 091e545..c6f0226 100644 --- a/packages/effect-fc-next/package.json +++ b/packages/effect-fc-next/package.json @@ -87,7 +87,7 @@ } }, "scripts": { - "lint:tsc": "tsc --noEmit", + "lint:tsc": "tsc -b --noEmit", "lint:biome": "biome lint", "test": "vitest run", "build": "tsc -b tsconfig.build.json", diff --git a/packages/effect-fc/package.json b/packages/effect-fc/package.json index f49579e..881729d 100644 --- a/packages/effect-fc/package.json +++ b/packages/effect-fc/package.json @@ -29,7 +29,7 @@ ] }, "scripts": { - "lint:tsc": "tsc --noEmit", + "lint:tsc": "tsc -b --noEmit", "lint:biome": "biome lint", "test": "vitest run", "build": "tsc -b tsconfig.build.json", -- 2.52.0 From d32389fa81bba5b4e3069e2f5257dd8a0c9ed1cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 26 Jul 2026 00:13:35 +0200 Subject: [PATCH 122/133] Fix CI --- turbo.json | 1 + 1 file changed, 1 insertion(+) diff --git a/turbo.json b/turbo.json index fcbaf52..6a2ee39 100644 --- a/turbo.json +++ b/turbo.json @@ -3,6 +3,7 @@ "tasks": { "lint:tsc": { + "dependsOn": ["^build"], "cache": false }, "lint:biome": { -- 2.52.0 From 926eba057061e06b4e1a7d5049734f0979bdf57a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 26 Jul 2026 00:19:10 +0200 Subject: [PATCH 123/133] Fix README --- README.md | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3d7e9bb..9d4f452 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,61 @@ -# Effect FC Monorepo +

+ + Effect View logo + +

-[Effect-TS](https://effect.website/) integration for React 19.2+ that allows you to write function components using Effect generators. +

Effect View Monorepo

-This monorepo contains: -- [The `effect-fc` library](packages/effect-fc) -- [An example project](packages/example) +

+ Write React components as typed Effect programs. +

+ +This repository contains Effect View, its documentation, development examples, +and supporting tooling. Effect View brings Effect's typed services, resource +safety, concurrency, and data modeling to React while keeping ordinary React +components, hooks, and JSX. + +## Packages + +| Package | Description | +| --- | --- | +| [`effect-view`](packages/effect-fc-next) | The Effect View library for Effect v4 and React 19. | +| [`@effect-view/vite-plugin`](packages/vite-plugin) | Vite Fast Refresh support for Effect View components. | +| [`docs`](packages/docs) | The [Effect View documentation](https://thila.dev/effect-view), built with Docusaurus. | +| [`example-next`](packages/example-next) | Example application using Effect View and Effect v4. | +| [`effect-fc`](packages/effect-fc) | Legacy Effect v3 package. | +| [`example`](packages/example) | Example application for the legacy package. | + +## Development + +The monorepo uses [Bun](https://bun.sh/) workspaces and +[Turborepo](https://turbo.build/). + +```bash +bun install +``` + +Run checks and builds from the repository root: + +```bash +bun run lint:tsc +bun run lint:biome +bun run test +bun run build +``` + +Start the Effect View example or documentation site: + +```bash +bun run --cwd packages/example-next dev +bun run --cwd packages/docs start +``` + +## Documentation + +Read the documentation at +[thila.dev/effect-view](https://thila.dev/effect-view). + +## License + +[MIT](LICENSE) -- 2.52.0 From d0fa8f04a2ba77dd88968c0ed633cc25f044eac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 26 Jul 2026 00:32:33 +0200 Subject: [PATCH 124/133] Update docs --- packages/docs/docs/getting-started.md | 27 +++++++++++++++++++++++++++ packages/effect-fc-next/README.md | 3 --- packages/vite-plugin/README.md | 1 - packages/vite-plugin/package.json | 4 ++-- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/packages/docs/docs/getting-started.md b/packages/docs/docs/getting-started.md index a6ab807..393e36b 100644 --- a/packages/docs/docs/getting-started.md +++ b/packages/docs/docs/getting-started.md @@ -35,6 +35,33 @@ Effect View is not tied to React DOM. For React Native or another renderer, install that renderer instead of `react-dom` and keep the rest of the setup the same. +## Set up hot reloading with Vite + +Install the Effect View Vite plugin to preserve component state while editing +Effect View components: + +```bash npm2yarn +npm install --save-dev @effect-view/vite-plugin +``` + +Add `effectViewPlugin()` before the React plugin in your Vite configuration: + +```ts title="vite.config.ts" +import { effectViewPlugin } from "@effect-view/vite-plugin" +import react from "@vitejs/plugin-react" +import { defineConfig } from "vite" + +export default defineConfig({ + plugins: [ + effectViewPlugin(), + react(), + ], +}) +``` + +Vite is the only bundler supported for Effect View hot reloading at the moment. +Support for other bundlers will follow. + ## Create the runtime `ReactRuntime` owns a managed Effect runtime. Define it at module scope from the diff --git a/packages/effect-fc-next/README.md b/packages/effect-fc-next/README.md index cb11b8f..7fc8b85 100644 --- a/packages/effect-fc-next/README.md +++ b/packages/effect-fc-next/README.md @@ -136,9 +136,6 @@ Effect View is renderer-independent and does not require `react-dom`. Effect View is currently beta software. The main APIs are available, but breaking changes and rough edges are still possible before a stable release. -React Fast Refresh does not yet recognize Effect View component definitions, so -changes to them currently require a page reload; ordinary React components are -unaffected (currently being worked on). Issues, ideas, and contributions are welcome on [GitHub](https://github.com/Thiladev/effect-view). diff --git a/packages/vite-plugin/README.md b/packages/vite-plugin/README.md index 65b9f09..8eb6160 100644 --- a/packages/vite-plugin/README.md +++ b/packages/vite-plugin/README.md @@ -33,7 +33,6 @@ removing, or changing a hook call resets the Effect View implementation. Add ## Current limitations -- Vite is the only supported bundler. - Definitions must be top-level variable, class, or default-export declarations. - Hook signature analysis is conservative and based on `useX` call syntax. diff --git a/packages/vite-plugin/package.json b/packages/vite-plugin/package.json index 74c9c4b..1fc14c6 100644 --- a/packages/vite-plugin/package.json +++ b/packages/vite-plugin/package.json @@ -1,7 +1,7 @@ { "name": "@effect-view/vite-plugin", "description": "Vite Fast Refresh support for Effect View components", - "version": "0.0.0", + "version": "0.0.1", "type": "module", "files": [ "./README.md", @@ -23,7 +23,7 @@ } }, "scripts": { - "lint:tsc": "tsc --noEmit", + "lint:tsc": "tsc -b --noEmit", "lint:biome": "biome lint", "test": "vitest run", "build": "tsc -b tsconfig.build.json", -- 2.52.0 From cd2115b49a3e7fafce09cd22d33f2e6dfcc0b657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 26 Jul 2026 00:36:41 +0200 Subject: [PATCH 125/133] Fix docs version --- packages/docs/docusaurus.config.ts | 8 ++++---- .../forms.md | 0 .../getting-started.md | 0 .../state-management.md | 0 ...0-sidebars.json => version-effect-fc-v0-sidebars.json} | 0 packages/docs/versions.json | 2 +- 6 files changed, 5 insertions(+), 5 deletions(-) rename packages/docs/versioned_docs/{version-effect-fc-1.0 => version-effect-fc-v0}/forms.md (100%) rename packages/docs/versioned_docs/{version-effect-fc-1.0 => version-effect-fc-v0}/getting-started.md (100%) rename packages/docs/versioned_docs/{version-effect-fc-1.0 => version-effect-fc-v0}/state-management.md (100%) rename packages/docs/versioned_sidebars/{version-effect-fc-1.0-sidebars.json => version-effect-fc-v0-sidebars.json} (100%) diff --git a/packages/docs/docusaurus.config.ts b/packages/docs/docusaurus.config.ts index f56ea3c..366df46 100644 --- a/packages/docs/docusaurus.config.ts +++ b/packages/docs/docusaurus.config.ts @@ -46,11 +46,11 @@ const config: Config = { lastVersion: "current", versions: { current: { - label: "effect-view v1", + label: "effect-view v0", }, - "effect-fc-1.0": { - label: "effect-fc 1.0", - path: "effect-fc/1.0", + "effect-fc-v0": { + label: "effect-fc v0", + path: "effect-fc/v0", }, }, }, diff --git a/packages/docs/versioned_docs/version-effect-fc-1.0/forms.md b/packages/docs/versioned_docs/version-effect-fc-v0/forms.md similarity index 100% rename from packages/docs/versioned_docs/version-effect-fc-1.0/forms.md rename to packages/docs/versioned_docs/version-effect-fc-v0/forms.md diff --git a/packages/docs/versioned_docs/version-effect-fc-1.0/getting-started.md b/packages/docs/versioned_docs/version-effect-fc-v0/getting-started.md similarity index 100% rename from packages/docs/versioned_docs/version-effect-fc-1.0/getting-started.md rename to packages/docs/versioned_docs/version-effect-fc-v0/getting-started.md diff --git a/packages/docs/versioned_docs/version-effect-fc-1.0/state-management.md b/packages/docs/versioned_docs/version-effect-fc-v0/state-management.md similarity index 100% rename from packages/docs/versioned_docs/version-effect-fc-1.0/state-management.md rename to packages/docs/versioned_docs/version-effect-fc-v0/state-management.md diff --git a/packages/docs/versioned_sidebars/version-effect-fc-1.0-sidebars.json b/packages/docs/versioned_sidebars/version-effect-fc-v0-sidebars.json similarity index 100% rename from packages/docs/versioned_sidebars/version-effect-fc-1.0-sidebars.json rename to packages/docs/versioned_sidebars/version-effect-fc-v0-sidebars.json diff --git a/packages/docs/versions.json b/packages/docs/versions.json index 775c1db..d4333ff 100644 --- a/packages/docs/versions.json +++ b/packages/docs/versions.json @@ -1,3 +1,3 @@ [ - "effect-fc-1.0" + "effect-fc-v0" ] -- 2.52.0 From 1a7c15d7775208d08ce97ac01f240916a75745b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 26 Jul 2026 00:50:43 +0200 Subject: [PATCH 126/133] Fix docs --- packages/docs/src/pages/index.module.css | 20 ++++++++++++++++++++ packages/docs/src/pages/index.tsx | 7 +++++++ 2 files changed, 27 insertions(+) diff --git a/packages/docs/src/pages/index.module.css b/packages/docs/src/pages/index.module.css index 1104372..042c345 100644 --- a/packages/docs/src/pages/index.module.css +++ b/packages/docs/src/pages/index.module.css @@ -157,6 +157,26 @@ padding: 0; } +.hotReload { + align-items: center; + color: var(--home-teal); + display: inline-flex; + font-size: 0.82rem; + font-weight: 750; + gap: 0.5rem; + margin-top: 0.75rem; + text-decoration: none !important; +} + +.hotReload span { + color: var(--home-amber); + font-size: 1rem; +} + +.hotReload:hover { + color: var(--home-teal-bright); +} + .codeStage { min-width: 0; perspective: 1200px; diff --git a/packages/docs/src/pages/index.tsx b/packages/docs/src/pages/index.tsx index ab0e5d6..7119772 100644 --- a/packages/docs/src/pages/index.tsx +++ b/packages/docs/src/pages/index.tsx @@ -126,6 +126,13 @@ export default function Home(): ReactNode { npm install effect-view effect@beta
+ + + Hot reload with Vite Fast Refresh +
-- 2.52.0 From bcc3fe859f11e79e7e3a0631d48af04b8e7a64bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 26 Jul 2026 00:53:03 +0200 Subject: [PATCH 127/133] Update Docusaurus --- bun.lock | 88 ++++++++++++++++---------------------- packages/docs/package.json | 12 +++--- 2 files changed, 44 insertions(+), 56 deletions(-) diff --git a/bun.lock b/bun.lock index ae1600b..ee0d7d8 100644 --- a/bun.lock +++ b/bun.lock @@ -18,9 +18,9 @@ "name": "docs", "version": "0.0.0", "dependencies": { - "@docusaurus/core": "3.10.1", - "@docusaurus/faster": "^3.10.1", - "@docusaurus/preset-classic": "3.10.1", + "@docusaurus/core": "^3.10.2", + "@docusaurus/faster": "^3.10.2", + "@docusaurus/preset-classic": "^3.10.2", "@mdx-js/react": "^3.1.1", "clsx": "^2.1.1", "prism-react-renderer": "^2.4.1", @@ -28,9 +28,9 @@ "react-dom": "^19.2.5", }, "devDependencies": { - "@docusaurus/module-type-aliases": "3.10.1", - "@docusaurus/tsconfig": "3.10.1", - "@docusaurus/types": "3.10.1", + "@docusaurus/module-type-aliases": "^3.10.2", + "@docusaurus/tsconfig": "^3.10.2", + "@docusaurus/types": "^3.10.2", "@types/react": "^19.2.15", "@types/react-dom": "^19.2.3", "typescript": "~6.0.3", @@ -126,7 +126,7 @@ }, "packages/vite-plugin": { "name": "@effect-view/vite-plugin", - "version": "0.0.0", + "version": "0.0.1", "dependencies": { "typescript": "^6.0.3", }, @@ -142,6 +142,8 @@ }, }, "packages": { + "@11ty/gray-matter": ["@11ty/gray-matter@1.0.0", "", { "dependencies": { "js-yaml": "^4.1.0", "kind-of": "^6.0.3", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" } }, "sha512-7mJJl+wf1AByoT0PknQiQfOPnVNT4fevGrUBVWO4HXsnYn1aQPyRyrELYrNUFleUBM++KzMKN6QaxHPk0t/6/g=="], + "@algolia/abtesting": ["@algolia/abtesting@1.22.0", "", { "dependencies": { "@algolia/client-common": "5.56.0", "@algolia/requester-browser-xhr": "5.56.0", "@algolia/requester-fetch": "5.56.0", "@algolia/requester-node-http": "5.56.0" } }, "sha512-BFR6zNowNKcY7Ou7TaJc9QWexES4YKPbmf/OTFofpdsdhz4x6q0lbxp3duO0EHnyrN7rE4ba/TSXuY+BDGu4+g=="], "@algolia/autocomplete-core": ["@algolia/autocomplete-core@1.19.9", "", { "dependencies": { "@algolia/autocomplete-plugin-algolia-insights": "1.19.9", "@algolia/autocomplete-shared": "1.19.9" } }, "sha512-4U2JKLMWlDu0CotYyUkWakDxr8AIav3QtIUXXRpfavYN29aVWfzlwJp9T0rPKEf/dO2QCPAUc0Kq1Tj1GJxo2A=="], @@ -514,61 +516,61 @@ "@docsearch/react": ["@docsearch/react@4.6.3", "", { "dependencies": { "@algolia/autocomplete-core": "1.19.2", "@docsearch/core": "4.6.3", "@docsearch/css": "4.6.3" }, "peerDependencies": { "@types/react": ">= 16.8.0 < 20.0.0", "react": ">= 16.8.0 < 20.0.0", "react-dom": ">= 16.8.0 < 20.0.0", "search-insights": ">= 1 < 3" }, "optionalPeers": ["@types/react", "react", "react-dom", "search-insights"] }, "sha512-Bg2wdDsoQVlNCcEKuEJAU04tvHCqgx8rIu+uIoM4pRtcx3TBKJuXutJik3LTA8LRc9YEyHkrYUrmcC0D7BYf+g=="], - "@docusaurus/babel": ["@docusaurus/babel@3.10.1", "", { "dependencies": { "@babel/core": "^7.25.9", "@babel/generator": "^7.25.9", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-transform-runtime": "^7.25.9", "@babel/preset-env": "^7.25.9", "@babel/preset-react": "^7.25.9", "@babel/preset-typescript": "^7.25.9", "@babel/runtime": "^7.25.9", "@babel/traverse": "^7.25.9", "@docusaurus/logger": "3.10.1", "@docusaurus/utils": "3.10.1", "babel-plugin-dynamic-import-node": "^2.3.3", "fs-extra": "^11.1.1", "tslib": "^2.6.0" } }, "sha512-DZzFO1K3v/GoEt1fx1DiYHF4en+PuhtQf1AkQJa5zu3CoeKSpr5cpQRUlz3jr0m44wyzmSXu9bVpfir+N4+8bg=="], + "@docusaurus/babel": ["@docusaurus/babel@3.10.2", "", { "dependencies": { "@babel/core": "^7.25.9", "@babel/generator": "^7.25.9", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-transform-runtime": "^7.25.9", "@babel/preset-env": "^7.25.9", "@babel/preset-react": "^7.25.9", "@babel/preset-typescript": "^7.25.9", "@babel/runtime": "^7.25.9", "@babel/traverse": "^7.25.9", "@docusaurus/logger": "3.10.2", "@docusaurus/utils": "3.10.2", "babel-plugin-dynamic-import-node": "^2.3.3", "fs-extra": "^11.1.1", "tslib": "^2.6.0" } }, "sha512-aJ1hpGyvfkte3dDAfNbWM4biW4yWZBVz7TIGLZP+v+tWOBgxX3e0N5ZIXHIvmfNNXTI77pcHUx3KmtOk05Ze3Q=="], - "@docusaurus/bundler": ["@docusaurus/bundler@3.10.1", "", { "dependencies": { "@babel/core": "^7.25.9", "@docusaurus/babel": "3.10.1", "@docusaurus/cssnano-preset": "3.10.1", "@docusaurus/logger": "3.10.1", "@docusaurus/types": "3.10.1", "@docusaurus/utils": "3.10.1", "babel-loader": "^9.2.1", "clean-css": "^5.3.3", "copy-webpack-plugin": "^11.0.0", "css-loader": "^6.11.0", "css-minimizer-webpack-plugin": "^5.0.1", "cssnano": "^6.1.2", "file-loader": "^6.2.0", "html-minifier-terser": "^7.2.0", "mini-css-extract-plugin": "^2.9.2", "null-loader": "^4.0.1", "postcss": "^8.5.4", "postcss-loader": "^7.3.4", "postcss-preset-env": "^10.2.1", "terser-webpack-plugin": "^5.3.9", "tslib": "^2.6.0", "url-loader": "^4.1.1", "webpack": "^5.95.0", "webpackbar": "^7.0.0" }, "peerDependencies": { "@docusaurus/faster": "*" }, "optionalPeers": ["@docusaurus/faster"] }, "sha512-HIqQPvbqnnQRe4NsBd1774KRarjXqS6wHsWELtyuSs1gCfvixJO2jUGH/OEBtr1Gvzpw+ze5CjGMvSJ8UE1KUw=="], + "@docusaurus/bundler": ["@docusaurus/bundler@3.10.2", "", { "dependencies": { "@babel/core": "^7.25.9", "@docusaurus/babel": "3.10.2", "@docusaurus/cssnano-preset": "3.10.2", "@docusaurus/logger": "3.10.2", "@docusaurus/types": "3.10.2", "@docusaurus/utils": "3.10.2", "babel-loader": "^9.2.1", "clean-css": "^5.3.3", "copy-webpack-plugin": "^11.0.0", "css-loader": "^6.11.0", "css-minimizer-webpack-plugin": "^5.0.1", "cssnano": "^6.1.2", "file-loader": "^6.2.0", "html-minifier-terser": "^7.2.0", "mini-css-extract-plugin": "^2.9.2", "null-loader": "^4.0.1", "postcss": "^8.5.4", "postcss-loader": "^7.3.4", "postcss-preset-env": "^10.2.1", "terser-webpack-plugin": "^5.3.9", "tslib": "^2.6.0", "url-loader": "^4.1.1", "webpack": "^5.95.0", "webpackbar": "^7.0.0" }, "peerDependencies": { "@docusaurus/faster": "*" }, "optionalPeers": ["@docusaurus/faster"] }, "sha512-i0ZNcy0f0WhaOlYVgzLsWhIoEXO9kS3HRoKPtgE6vQtZUq7arKZaYdNBudr3mqCmd+TyOkwtwfHgs1ENj07r5g=="], - "@docusaurus/core": ["@docusaurus/core@3.10.1", "", { "dependencies": { "@docusaurus/babel": "3.10.1", "@docusaurus/bundler": "3.10.1", "@docusaurus/logger": "3.10.1", "@docusaurus/mdx-loader": "3.10.1", "@docusaurus/utils": "3.10.1", "@docusaurus/utils-common": "3.10.1", "@docusaurus/utils-validation": "3.10.1", "boxen": "^6.2.1", "chalk": "^4.1.2", "chokidar": "^3.5.3", "cli-table3": "^0.6.3", "combine-promises": "^1.1.0", "commander": "^5.1.0", "core-js": "^3.31.1", "detect-port": "^1.5.1", "escape-html": "^1.0.3", "eta": "^2.2.0", "eval": "^0.1.8", "execa": "^5.1.1", "fs-extra": "^11.1.1", "html-tags": "^3.3.1", "html-webpack-plugin": "^5.6.0", "leven": "^3.1.0", "lodash": "^4.17.21", "open": "^8.4.0", "p-map": "^4.0.0", "prompts": "^2.4.2", "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0", "react-loadable": "npm:@docusaurus/react-loadable@6.0.0", "react-loadable-ssr-addon-v5-slorber": "^1.0.3", "react-router": "^5.3.4", "react-router-config": "^5.1.1", "react-router-dom": "^5.3.4", "semver": "^7.5.4", "serve-handler": "^6.1.7", "tinypool": "^1.0.2", "tslib": "^2.6.0", "update-notifier": "^6.0.2", "webpack": "^5.95.0", "webpack-bundle-analyzer": "^4.10.2", "webpack-dev-server": "^5.2.2", "webpack-merge": "^6.0.1" }, "peerDependencies": { "@docusaurus/faster": "*", "@mdx-js/react": "^3.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@docusaurus/faster"], "bin": { "docusaurus": "bin/docusaurus.mjs" } }, "sha512-3pf2fXXw0eVk8WnC3T4LIigRDupcpvngpKo9Vy7mYyBhuddc0klDUuZAIfzMoK6z05pdlk6EFC/vBSX43+1O5w=="], + "@docusaurus/core": ["@docusaurus/core@3.10.2", "", { "dependencies": { "@docusaurus/babel": "3.10.2", "@docusaurus/bundler": "3.10.2", "@docusaurus/logger": "3.10.2", "@docusaurus/mdx-loader": "3.10.2", "@docusaurus/utils": "3.10.2", "@docusaurus/utils-common": "3.10.2", "@docusaurus/utils-validation": "3.10.2", "boxen": "^6.2.1", "chalk": "^4.1.2", "chokidar": "^3.5.3", "cli-table3": "^0.6.3", "combine-promises": "^1.1.0", "commander": "^5.1.0", "core-js": "^3.31.1", "detect-port": "^2.1.0", "escape-html": "^1.0.3", "eta": "^2.2.0", "eval": "^0.1.8", "execa": "^5.1.1", "fs-extra": "^11.1.1", "html-tags": "^3.3.1", "html-webpack-plugin": "^5.6.0", "leven": "^3.1.0", "lodash": "^4.17.21", "open": "^8.4.0", "p-map": "^4.0.0", "prompts": "^2.4.2", "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0", "react-loadable": "npm:@docusaurus/react-loadable@6.0.0", "react-loadable-ssr-addon-v5-slorber": "^1.0.3", "react-router": "^5.3.4", "react-router-config": "^5.1.1", "react-router-dom": "^5.3.4", "semver": "^7.5.4", "serve-handler": "^6.1.7", "tinypool": "^1.0.2", "tslib": "^2.6.0", "update-notifier": "^6.0.2", "webpack": "^5.95.0", "webpack-bundle-analyzer": "^4.10.2", "webpack-dev-server": "^5.2.2", "webpack-merge": "^6.0.1" }, "peerDependencies": { "@docusaurus/faster": "*", "@mdx-js/react": "^3.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@docusaurus/faster"], "bin": { "docusaurus": "bin/docusaurus.mjs" } }, "sha512-EYByj6nk+aD9KeVxV6Hmo2/nAAT79P21Y82ycTBOBtrmqilloIbIEhgL2/8Xpt2Jz/pgNqHAwyusOGwmbKeJmA=="], - "@docusaurus/cssnano-preset": ["@docusaurus/cssnano-preset@3.10.1", "", { "dependencies": { "cssnano-preset-advanced": "^6.1.2", "postcss": "^8.5.4", "postcss-sort-media-queries": "^5.2.0", "tslib": "^2.6.0" } }, "sha512-eNfHGcTKCSq6xmcavAkX3RRclHaE2xRCMParlDXLdXVP01/a2e/jKXMj/0ULnLFQSNwwuI62L0Ge8J+nZsR7UQ=="], + "@docusaurus/cssnano-preset": ["@docusaurus/cssnano-preset@3.10.2", "", { "dependencies": { "cssnano-preset-advanced": "^6.1.2", "postcss": "^8.5.4", "postcss-sort-media-queries": "^5.2.0", "tslib": "^2.6.0" } }, "sha512-4gCnHRbJLTloiwfvFAa92tgb2gI4KYhvjfQVYnEaiMO/EgvWfCo1LwytHXen+1oZAN0VAlS0JAPxp3MsvKDa3A=="], "@docusaurus/faster": ["@docusaurus/faster@3.10.2", "", { "dependencies": { "@docusaurus/types": "3.10.2", "@rspack/core": "^1.7.10", "@swc/core": "^1.15.40", "@swc/html": "^1.15.40", "browserslist": "^4.24.2", "lightningcss": "^1.27.0", "semver": "^7.5.4", "swc-loader": "^0.2.6", "tslib": "^2.6.0", "webpack": "^5.95.0" } }, "sha512-p/5E5/RyHv+QWusJMPN5i3OMJTqTgkhuwzVbB1AReDWTUHXQCmf5mlTFzGiDrWeQWIDOKsuOPn1jJh0s9LUOHA=="], - "@docusaurus/logger": ["@docusaurus/logger@3.10.1", "", { "dependencies": { "chalk": "^4.1.2", "tslib": "^2.6.0" } }, "sha512-oPjNFnfJsRCkePVjkGrxWGq4MvJKRQT0r9jOP0eRBTZ7Wr9FAbzdP/Gjs0I2Ss6YRkPoEgygKG112OkE6skvJw=="], + "@docusaurus/logger": ["@docusaurus/logger@3.10.2", "", { "dependencies": { "chalk": "^4.1.2", "tslib": "^2.6.0" } }, "sha512-gSEwqtPfCAnC3ZSJY6xL7tcIfgg0vFD39jbv93eakuweyvO2864xR0K+kmKwBhkTCtWRNjuGGnb5rdmkD/ndqw=="], - "@docusaurus/mdx-loader": ["@docusaurus/mdx-loader@3.10.1", "", { "dependencies": { "@docusaurus/logger": "3.10.1", "@docusaurus/utils": "3.10.1", "@docusaurus/utils-validation": "3.10.1", "@mdx-js/mdx": "^3.0.0", "@slorber/remark-comment": "^1.0.0", "escape-html": "^1.0.3", "estree-util-value-to-estree": "^3.0.1", "file-loader": "^6.2.0", "fs-extra": "^11.1.1", "image-size": "^2.0.2", "mdast-util-mdx": "^3.0.0", "mdast-util-to-string": "^4.0.0", "rehype-raw": "^7.0.0", "remark-directive": "^3.0.0", "remark-emoji": "^4.0.0", "remark-frontmatter": "^5.0.0", "remark-gfm": "^4.0.0", "stringify-object": "^3.3.0", "tslib": "^2.6.0", "unified": "^11.0.3", "unist-util-visit": "^5.0.0", "url-loader": "^4.1.1", "vfile": "^6.0.1", "webpack": "^5.88.1" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-GRmeb/wQ+iXRrFwcHBfgQhrJxGElgCsoTWZYDhccjsZVne1p8MK/EpQVIloXttz76TCe78kKD5AEG9n1xc1oxQ=="], + "@docusaurus/mdx-loader": ["@docusaurus/mdx-loader@3.10.2", "", { "dependencies": { "@docusaurus/logger": "3.10.2", "@docusaurus/utils": "3.10.2", "@docusaurus/utils-validation": "3.10.2", "@mdx-js/mdx": "^3.0.0", "@slorber/remark-comment": "^1.0.0", "escape-html": "^1.0.3", "estree-util-value-to-estree": "^3.0.1", "file-loader": "^6.2.0", "fs-extra": "^11.1.1", "image-size": "^2.0.2", "mdast-util-mdx": "^3.0.0", "mdast-util-to-string": "^4.0.0", "rehype-raw": "^7.0.0", "remark-directive": "^3.0.0", "remark-emoji": "^4.0.0", "remark-frontmatter": "^5.0.0", "remark-gfm": "^4.0.0", "stringify-object": "^3.3.0", "tslib": "^2.6.0", "unified": "^11.0.3", "unist-util-visit": "^5.0.0", "url-loader": "^4.1.1", "vfile": "^6.0.1", "webpack": "^5.88.1" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-9Fd4V/SFjfrVQ0JH5EN0+iPWyFunvTeQE3gfyFeetqPaXMP0OylIjOw16dCuXG4NZJrYdBqwzjh18/h3gRi47w=="], - "@docusaurus/module-type-aliases": ["@docusaurus/module-type-aliases@3.10.1", "", { "dependencies": { "@docusaurus/types": "3.10.1", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", "@types/react-router-dom": "*", "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0", "react-loadable": "npm:@docusaurus/react-loadable@6.0.0" }, "peerDependencies": { "react": "*", "react-dom": "*" } }, "sha512-YoOZKUdGlp8xSYhuAkGdSo5Ydkbq4V4eK3sD8v0a2hloxCWdQbNBhkc+Ko9QyjpESc0BYcIGM5iHVAy5hdFV6w=="], + "@docusaurus/module-type-aliases": ["@docusaurus/module-type-aliases@3.10.2", "", { "dependencies": { "@docusaurus/types": "3.10.2", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", "@types/react-router-dom": "*", "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0", "react-loadable": "npm:@docusaurus/react-loadable@6.0.0" }, "peerDependencies": { "react": "*", "react-dom": "*" } }, "sha512-h/I5e4jaAhDHW4vaLENi1i2hnOEnXY1t9R+nnRTbgUl7ymVRzN/HF7dDfj8rKYGj8gfIge+Ef+iYRAMtbGvsrQ=="], - "@docusaurus/plugin-content-blog": ["@docusaurus/plugin-content-blog@3.10.1", "", { "dependencies": { "@docusaurus/core": "3.10.1", "@docusaurus/logger": "3.10.1", "@docusaurus/mdx-loader": "3.10.1", "@docusaurus/theme-common": "3.10.1", "@docusaurus/types": "3.10.1", "@docusaurus/utils": "3.10.1", "@docusaurus/utils-common": "3.10.1", "@docusaurus/utils-validation": "3.10.1", "cheerio": "1.0.0-rc.12", "combine-promises": "^1.1.0", "feed": "^4.2.2", "fs-extra": "^11.1.1", "lodash": "^4.17.21", "schema-dts": "^1.1.2", "srcset": "^4.0.0", "tslib": "^2.6.0", "unist-util-visit": "^5.0.0", "utility-types": "^3.10.0", "webpack": "^5.88.1" }, "peerDependencies": { "@docusaurus/plugin-content-docs": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-mmkgE6Q2+K74tnkou7tXlpDLvoCU/qkSa2GSQ3XUiHWvcebCoDQzS670RR3tO8PmaWlIyWWISYWzZLuMfxunRA=="], + "@docusaurus/plugin-content-blog": ["@docusaurus/plugin-content-blog@3.10.2", "", { "dependencies": { "@docusaurus/core": "3.10.2", "@docusaurus/logger": "3.10.2", "@docusaurus/mdx-loader": "3.10.2", "@docusaurus/theme-common": "3.10.2", "@docusaurus/types": "3.10.2", "@docusaurus/utils": "3.10.2", "@docusaurus/utils-common": "3.10.2", "@docusaurus/utils-validation": "3.10.2", "cheerio": "1.0.0-rc.12", "combine-promises": "^1.1.0", "feed": "^4.2.2", "fs-extra": "^11.1.1", "lodash": "^4.17.21", "schema-dts": "^1.1.2", "srcset": "^4.0.0", "tslib": "^2.6.0", "unist-util-visit": "^5.0.0", "utility-types": "^3.10.0", "webpack": "^5.88.1" }, "peerDependencies": { "@docusaurus/plugin-content-docs": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-0cbEnNKf0InmLkhj/+nVRmqEnWEoOE8Mh+2x1qOXI0qYpCnphq4RXknVJ8BvybKRXqYVvbmdMfiJSup+k4tm5w=="], - "@docusaurus/plugin-content-docs": ["@docusaurus/plugin-content-docs@3.10.1", "", { "dependencies": { "@docusaurus/core": "3.10.1", "@docusaurus/logger": "3.10.1", "@docusaurus/mdx-loader": "3.10.1", "@docusaurus/module-type-aliases": "3.10.1", "@docusaurus/theme-common": "3.10.1", "@docusaurus/types": "3.10.1", "@docusaurus/utils": "3.10.1", "@docusaurus/utils-common": "3.10.1", "@docusaurus/utils-validation": "3.10.1", "@types/react-router-config": "^5.0.7", "combine-promises": "^1.1.0", "fs-extra": "^11.1.1", "js-yaml": "^4.1.0", "lodash": "^4.17.21", "schema-dts": "^1.1.2", "tslib": "^2.6.0", "utility-types": "^3.10.0", "webpack": "^5.88.1" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-2jRVrtzjf8LClGTHQlwlwuD3wQXRx3WEoF7XUarJ8Ou+0onV+SLtejsyfY9JLpfUh9hPhXM4pbBGkyAY4Bi3HQ=="], + "@docusaurus/plugin-content-docs": ["@docusaurus/plugin-content-docs@3.10.2", "", { "dependencies": { "@docusaurus/core": "3.10.2", "@docusaurus/logger": "3.10.2", "@docusaurus/mdx-loader": "3.10.2", "@docusaurus/module-type-aliases": "3.10.2", "@docusaurus/theme-common": "3.10.2", "@docusaurus/types": "3.10.2", "@docusaurus/utils": "3.10.2", "@docusaurus/utils-common": "3.10.2", "@docusaurus/utils-validation": "3.10.2", "@types/react-router-config": "^5.0.7", "combine-promises": "^1.1.0", "fs-extra": "^11.1.1", "js-yaml": "^4.1.0", "lodash": "^4.17.21", "schema-dts": "^1.1.2", "tslib": "^2.6.0", "utility-types": "^3.10.0", "webpack": "^5.88.1" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-Sqwl4FPoZBDrlY8I2VU2H8O0M91CHp9T8ToMSkTZmjvHCif+1laqfXi6sTk8IfyVS/trN5yNjcWd1bFsGB6W5Q=="], - "@docusaurus/plugin-content-pages": ["@docusaurus/plugin-content-pages@3.10.1", "", { "dependencies": { "@docusaurus/core": "3.10.1", "@docusaurus/mdx-loader": "3.10.1", "@docusaurus/types": "3.10.1", "@docusaurus/utils": "3.10.1", "@docusaurus/utils-validation": "3.10.1", "fs-extra": "^11.1.1", "tslib": "^2.6.0", "webpack": "^5.88.1" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-huJpaRPMl42nsFwuCXvV8bVDj2MazuwRJIUylI/RSlmZeJssVoZXeCjVf1y+1Drtpa9SKcdGn8yoJ76IRJijtw=="], + "@docusaurus/plugin-content-pages": ["@docusaurus/plugin-content-pages@3.10.2", "", { "dependencies": { "@docusaurus/core": "3.10.2", "@docusaurus/mdx-loader": "3.10.2", "@docusaurus/types": "3.10.2", "@docusaurus/utils": "3.10.2", "@docusaurus/utils-validation": "3.10.2", "fs-extra": "^11.1.1", "tslib": "^2.6.0", "webpack": "^5.88.1" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-h5R12sZ/vV9EPiVjvIl9YFCOwkpwXes7dQMYt3EvP6Pphu4amHxxTqWxf08Fl5DR8h+oZMbWpFTNw5vKEYfvzQ=="], - "@docusaurus/plugin-css-cascade-layers": ["@docusaurus/plugin-css-cascade-layers@3.10.1", "", { "dependencies": { "@docusaurus/core": "3.10.1", "@docusaurus/types": "3.10.1", "@docusaurus/utils": "3.10.1", "@docusaurus/utils-validation": "3.10.1", "tslib": "^2.6.0" } }, "sha512-r//fn+MNHkE1wCof8T29VAQezt1enGCpsFxoziBbvLgBM4JfXN2P3rxrBaavHmvLvm7lYkpJeitcDthwnmWCTw=="], + "@docusaurus/plugin-css-cascade-layers": ["@docusaurus/plugin-css-cascade-layers@3.10.2", "", { "dependencies": { "@docusaurus/core": "3.10.2", "@docusaurus/types": "3.10.2", "@docusaurus/utils": "3.10.2", "@docusaurus/utils-validation": "3.10.2", "tslib": "^2.6.0" } }, "sha512-UkdvQby5OQUKWrw3lLnSTJXQ6VETaUVTuPQX9AABtmFm5h+ifEBx1OQ+LN726Q4byuwBf2ElHkf4qU4hTxdvRg=="], - "@docusaurus/plugin-debug": ["@docusaurus/plugin-debug@3.10.1", "", { "dependencies": { "@docusaurus/core": "3.10.1", "@docusaurus/types": "3.10.1", "@docusaurus/utils": "3.10.1", "fs-extra": "^11.1.1", "react-json-view-lite": "^2.3.0", "tslib": "^2.6.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-9KqOpKNfAyqGZykRb9LhIT/vyRF6sm/ykhjj/39JvaJahDS+jZJE0Z1Wfz9q3DUNDTMNN0Q7u/kk4rKKU+IJuA=="], + "@docusaurus/plugin-debug": ["@docusaurus/plugin-debug@3.10.2", "", { "dependencies": { "@docusaurus/core": "3.10.2", "@docusaurus/types": "3.10.2", "@docusaurus/utils": "3.10.2", "fs-extra": "^11.1.1", "react-json-view-lite": "^2.3.0", "tslib": "^2.6.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-8vbZNOSCpnsT57EY6CgN7sgRVmx3KTYwO8Uvo2pbxOyb8tbqAwtT9SslqaQ41HbA1v1hpn5RP7u5s2KvRwAFpQ=="], - "@docusaurus/plugin-google-analytics": ["@docusaurus/plugin-google-analytics@3.10.1", "", { "dependencies": { "@docusaurus/core": "3.10.1", "@docusaurus/types": "3.10.1", "@docusaurus/utils-validation": "3.10.1", "tslib": "^2.6.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-8o0P1KtmgdYQHH+oInitPpRWI0Of5XednAX4+DMhQNSmGSRNrsEEHg1ebv35m9AgRClfAytCJ5jA9KvcASTyuA=="], + "@docusaurus/plugin-google-analytics": ["@docusaurus/plugin-google-analytics@3.10.2", "", { "dependencies": { "@docusaurus/core": "3.10.2", "@docusaurus/types": "3.10.2", "@docusaurus/utils-validation": "3.10.2", "tslib": "^2.6.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-kMHMBK9j4VAtgd5owwrRLRIi0EjkrpXlX7ePj1+y68XfVZV9I1T4S+koPDm+Hfw2TtnyHvh0uNrDvjz+DjQGVA=="], - "@docusaurus/plugin-google-gtag": ["@docusaurus/plugin-google-gtag@3.10.1", "", { "dependencies": { "@docusaurus/core": "3.10.1", "@docusaurus/types": "3.10.1", "@docusaurus/utils-validation": "3.10.1", "@types/gtag.js": "^0.0.20", "tslib": "^2.6.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-pu3xIUo5o/zCMLfUY9BO5KOwSH0zIsAGyFRPvXHayFSA5XIhCU/SFuB0g0ZNjFn9niZLCaNvoeAuOGFJZq0fdw=="], + "@docusaurus/plugin-google-gtag": ["@docusaurus/plugin-google-gtag@3.10.2", "", { "dependencies": { "@docusaurus/core": "3.10.2", "@docusaurus/types": "3.10.2", "@docusaurus/utils-validation": "3.10.2", "tslib": "^2.6.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-Vt90nNFhtAChRe9+it1hcHFgFvETdSnOkL5Bma+p6E/yU2tAYrvvyk+gv+LJGM2ZUkyKuKXLRsZ2Lb0bO7+Vog=="], - "@docusaurus/plugin-google-tag-manager": ["@docusaurus/plugin-google-tag-manager@3.10.1", "", { "dependencies": { "@docusaurus/core": "3.10.1", "@docusaurus/types": "3.10.1", "@docusaurus/utils-validation": "3.10.1", "tslib": "^2.6.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-f6fyGHiCm7kJHBtAisGQS5oNBnpnMTYQZxDXeVrnw/3zWU+LMA22pr6UHGYkBKDbN+qPC5QHG3NuOfzQLq3+Lw=="], + "@docusaurus/plugin-google-tag-manager": ["@docusaurus/plugin-google-tag-manager@3.10.2", "", { "dependencies": { "@docusaurus/core": "3.10.2", "@docusaurus/types": "3.10.2", "@docusaurus/utils-validation": "3.10.2", "tslib": "^2.6.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-MLCffCldysi/R0nzJQP7ZWd0xAoGNnSTiVOo6TTR6mKVGFhE+/XArGe67ZcaZv1uytgQXoXs92VJrgVDrz80rQ=="], - "@docusaurus/plugin-sitemap": ["@docusaurus/plugin-sitemap@3.10.1", "", { "dependencies": { "@docusaurus/core": "3.10.1", "@docusaurus/logger": "3.10.1", "@docusaurus/types": "3.10.1", "@docusaurus/utils": "3.10.1", "@docusaurus/utils-common": "3.10.1", "@docusaurus/utils-validation": "3.10.1", "fs-extra": "^11.1.1", "sitemap": "^7.1.1", "tslib": "^2.6.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-C26MbmmqgdjkDq1htaZ3aD7LzEDKFWXfpyQpt0EOUThuq5nV77zDaedV20yHcVo9p+3ey9aZ4pbHA0D3QcZTzg=="], + "@docusaurus/plugin-sitemap": ["@docusaurus/plugin-sitemap@3.10.2", "", { "dependencies": { "@docusaurus/core": "3.10.2", "@docusaurus/logger": "3.10.2", "@docusaurus/types": "3.10.2", "@docusaurus/utils": "3.10.2", "@docusaurus/utils-common": "3.10.2", "@docusaurus/utils-validation": "3.10.2", "fs-extra": "^11.1.1", "sitemap": "^7.1.1", "tslib": "^2.6.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-PODkwg5XetLML3hU/3xpCKJUZ9cqExLaBnD/Fzzwj2VHogLeqnDisLIujae87zuze7T4mCm2A6KEqZkyiz07EQ=="], - "@docusaurus/plugin-svgr": ["@docusaurus/plugin-svgr@3.10.1", "", { "dependencies": { "@docusaurus/core": "3.10.1", "@docusaurus/types": "3.10.1", "@docusaurus/utils": "3.10.1", "@docusaurus/utils-validation": "3.10.1", "@svgr/core": "8.1.0", "@svgr/webpack": "^8.1.0", "tslib": "^2.6.0", "webpack": "^5.88.1" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-6SFxsmjWFkVLDmBUvFK6i72QjUwqyQFe4Ovz+SUJophJjOyVG3ZZG5IQpBC/kX/Gfv1yWeU9nWauH6F6Q7QX/Q=="], + "@docusaurus/plugin-svgr": ["@docusaurus/plugin-svgr@3.10.2", "", { "dependencies": { "@docusaurus/core": "3.10.2", "@docusaurus/types": "3.10.2", "@docusaurus/utils": "3.10.2", "@docusaurus/utils-validation": "3.10.2", "@svgr/core": "8.1.0", "@svgr/webpack": "^8.1.0", "tslib": "^2.6.0", "webpack": "^5.88.1" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-JgfT3jWM0TJ8Uw0cEcqxHpybngQY1vlBYpuuNO+gEh5iPh5Ar+vxq/u9CFrYsWeXy48BN7Db76Pzp2edNXUQ8A=="], - "@docusaurus/preset-classic": ["@docusaurus/preset-classic@3.10.1", "", { "dependencies": { "@docusaurus/core": "3.10.1", "@docusaurus/plugin-content-blog": "3.10.1", "@docusaurus/plugin-content-docs": "3.10.1", "@docusaurus/plugin-content-pages": "3.10.1", "@docusaurus/plugin-css-cascade-layers": "3.10.1", "@docusaurus/plugin-debug": "3.10.1", "@docusaurus/plugin-google-analytics": "3.10.1", "@docusaurus/plugin-google-gtag": "3.10.1", "@docusaurus/plugin-google-tag-manager": "3.10.1", "@docusaurus/plugin-sitemap": "3.10.1", "@docusaurus/plugin-svgr": "3.10.1", "@docusaurus/theme-classic": "3.10.1", "@docusaurus/theme-common": "3.10.1", "@docusaurus/theme-search-algolia": "3.10.1", "@docusaurus/types": "3.10.1" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-YO/FL8v1zmbxoTso6mjMz/RDjhaTJxb1UpFFTDdY5847LLDCeyYiYlrhyTbgN1RIN3xnkLKZ9Lj1x8hUzI4JOg=="], + "@docusaurus/preset-classic": ["@docusaurus/preset-classic@3.10.2", "", { "dependencies": { "@docusaurus/core": "3.10.2", "@docusaurus/plugin-content-blog": "3.10.2", "@docusaurus/plugin-content-docs": "3.10.2", "@docusaurus/plugin-content-pages": "3.10.2", "@docusaurus/plugin-css-cascade-layers": "3.10.2", "@docusaurus/plugin-debug": "3.10.2", "@docusaurus/plugin-google-analytics": "3.10.2", "@docusaurus/plugin-google-gtag": "3.10.2", "@docusaurus/plugin-google-tag-manager": "3.10.2", "@docusaurus/plugin-sitemap": "3.10.2", "@docusaurus/plugin-svgr": "3.10.2", "@docusaurus/theme-classic": "3.10.2", "@docusaurus/theme-common": "3.10.2", "@docusaurus/theme-search-algolia": "3.10.2", "@docusaurus/types": "3.10.2" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-a4B3VczmDl99zK0EufDQYomdJ186WDingjmDXxhN2PNPS9Ty/Y2M5CLFX1KQMRKqRTLiRDKfutzG5IY1FC/ceg=="], - "@docusaurus/theme-classic": ["@docusaurus/theme-classic@3.10.1", "", { "dependencies": { "@docusaurus/core": "3.10.1", "@docusaurus/logger": "3.10.1", "@docusaurus/mdx-loader": "3.10.1", "@docusaurus/module-type-aliases": "3.10.1", "@docusaurus/plugin-content-blog": "3.10.1", "@docusaurus/plugin-content-docs": "3.10.1", "@docusaurus/plugin-content-pages": "3.10.1", "@docusaurus/theme-common": "3.10.1", "@docusaurus/theme-translations": "3.10.1", "@docusaurus/types": "3.10.1", "@docusaurus/utils": "3.10.1", "@docusaurus/utils-common": "3.10.1", "@docusaurus/utils-validation": "3.10.1", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "copy-text-to-clipboard": "^3.2.0", "infima": "0.2.0-alpha.45", "lodash": "^4.17.21", "nprogress": "^0.2.0", "postcss": "^8.5.4", "prism-react-renderer": "^2.3.0", "prismjs": "^1.29.0", "react-router-dom": "^5.3.4", "rtlcss": "^4.1.0", "tslib": "^2.6.0", "utility-types": "^3.10.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-VU1RK0qb2pab0si4r7HFK37cYco8VzqLj3u1PspVipSr/z/GPVKHO4/HXbnePqHoWDk8urjyGSeatH0NIMBM1A=="], + "@docusaurus/theme-classic": ["@docusaurus/theme-classic@3.10.2", "", { "dependencies": { "@docusaurus/core": "3.10.2", "@docusaurus/logger": "3.10.2", "@docusaurus/mdx-loader": "3.10.2", "@docusaurus/module-type-aliases": "3.10.2", "@docusaurus/plugin-content-blog": "3.10.2", "@docusaurus/plugin-content-docs": "3.10.2", "@docusaurus/plugin-content-pages": "3.10.2", "@docusaurus/theme-common": "3.10.2", "@docusaurus/theme-translations": "3.10.2", "@docusaurus/types": "3.10.2", "@docusaurus/utils": "3.10.2", "@docusaurus/utils-common": "3.10.2", "@docusaurus/utils-validation": "3.10.2", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "copy-text-to-clipboard": "^3.2.0", "infima": "0.2.0-alpha.45", "lodash": "^4.17.21", "nprogress": "^0.2.0", "postcss": "^8.5.4", "prism-react-renderer": "^2.3.0", "prismjs": "^1.29.0", "react-router-dom": "^5.3.4", "rtlcss": "^4.1.0", "tslib": "^2.6.0", "utility-types": "^3.10.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-JqTSLQmqmA9uKWZsD5iwBGJ4JyKB4/yTw6PsSXVPRJG/6GAm/u+add9Iip+hvwP12/AnPNztrdxsI14NJW4KeA=="], - "@docusaurus/theme-common": ["@docusaurus/theme-common@3.10.1", "", { "dependencies": { "@docusaurus/mdx-loader": "3.10.1", "@docusaurus/module-type-aliases": "3.10.1", "@docusaurus/utils": "3.10.1", "@docusaurus/utils-common": "3.10.1", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", "clsx": "^2.0.0", "parse-numeric-range": "^1.3.0", "prism-react-renderer": "^2.3.0", "tslib": "^2.6.0", "utility-types": "^3.10.0" }, "peerDependencies": { "@docusaurus/plugin-content-docs": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-0YtmIeoNo1fIw65LO8+/1dPgmDV86UmhMkow37gzjytuiCSQm9xob6PJy0L4kuQEMTLfUOGvkXvZr7GPrHquMA=="], + "@docusaurus/theme-common": ["@docusaurus/theme-common@3.10.2", "", { "dependencies": { "@docusaurus/mdx-loader": "3.10.2", "@docusaurus/module-type-aliases": "3.10.2", "@docusaurus/utils": "3.10.2", "@docusaurus/utils-common": "3.10.2", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", "clsx": "^2.0.0", "parse-numeric-range": "^1.3.0", "prism-react-renderer": "^2.3.0", "tslib": "^2.6.0", "utility-types": "^3.10.0" }, "peerDependencies": { "@docusaurus/plugin-content-docs": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-R9b/vMpK1yye6hNZTA6x/ivRv+at6GhxnXcxkpzCGzO1R1RwiquqiFg2wMFh6aqlJTpWRFKpFD2TzCDQcyOU0A=="], - "@docusaurus/theme-search-algolia": ["@docusaurus/theme-search-algolia@3.10.1", "", { "dependencies": { "@algolia/autocomplete-core": "^1.19.2", "@docsearch/react": "^3.9.0 || ^4.3.2", "@docusaurus/core": "3.10.1", "@docusaurus/logger": "3.10.1", "@docusaurus/plugin-content-docs": "3.10.1", "@docusaurus/theme-common": "3.10.1", "@docusaurus/theme-translations": "3.10.1", "@docusaurus/utils": "3.10.1", "@docusaurus/utils-validation": "3.10.1", "algoliasearch": "^5.37.0", "algoliasearch-helper": "^3.26.0", "clsx": "^2.0.0", "eta": "^2.2.0", "fs-extra": "^11.1.1", "lodash": "^4.17.21", "tslib": "^2.6.0", "utility-types": "^3.10.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-OTaARARVZj2GvkJQjB+1jOIxntRaXea+G+fMsNqrZBAU1O1vJKDW22R7kECOHW27oJCLFN9HKaZeRrfAUyviug=="], + "@docusaurus/theme-search-algolia": ["@docusaurus/theme-search-algolia@3.10.2", "", { "dependencies": { "@algolia/autocomplete-core": "^1.19.2", "@docsearch/react": "^3.9.0 || ^4.3.2", "@docusaurus/core": "3.10.2", "@docusaurus/logger": "3.10.2", "@docusaurus/plugin-content-docs": "3.10.2", "@docusaurus/theme-common": "3.10.2", "@docusaurus/theme-translations": "3.10.2", "@docusaurus/utils": "3.10.2", "@docusaurus/utils-validation": "3.10.2", "algoliasearch": "^5.37.0", "algoliasearch-helper": "^3.26.0", "clsx": "^2.0.0", "eta": "^2.2.0", "fs-extra": "^11.1.1", "lodash": "^4.17.21", "tslib": "^2.6.0", "utility-types": "^3.10.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-1msxllyhi/5m77JukXtp5UFnUAriwZIC1oJ7MTnpQpCwLTbclJi5BK5n28CTZuSXpQN2ewbbnqRgAhMM6c6ihg=="], - "@docusaurus/theme-translations": ["@docusaurus/theme-translations@3.10.1", "", { "dependencies": { "fs-extra": "^11.1.1", "tslib": "^2.6.0" } }, "sha512-cLMyaKivjBVWKMJuWqyFVVgtqe8DPJNPkog0bn8W1MDVAKcPdxRFycBfC1We1RaNp7Rdk513bmtW78RR6OBxBw=="], + "@docusaurus/theme-translations": ["@docusaurus/theme-translations@3.10.2", "", { "dependencies": { "fs-extra": "^11.1.1", "tslib": "^2.6.0" } }, "sha512-iv20wrxnyXkY89LM3TzRlzGlt5fIGO5UnaR6UL1ZVfB9RRFjxQFQ6awDrwAc6Km8Y5gD8pInuwYPF+6/TiCxXA=="], - "@docusaurus/tsconfig": ["@docusaurus/tsconfig@3.10.1", "", {}, "sha512-rYvB7yqkdqWIpAbDzQljGfM4cDBkLTbhmagZBEcsyj6oPUsz47lmW2pYdN1j+7sGFgltbAmQH62xfbrij4Eh6Q=="], + "@docusaurus/tsconfig": ["@docusaurus/tsconfig@3.10.2", "", {}, "sha512-5GiB7h/nFsMFPO9mCqcRNE1yA5TSXXNCshNIgHPL6fCPOjcTDixs6qjQBu8ddkgPcicwCvOA7n3jeK2rGdJk6g=="], - "@docusaurus/types": ["@docusaurus/types@3.10.1", "", { "dependencies": { "@mdx-js/mdx": "^3.0.0", "@types/history": "^4.7.11", "@types/mdast": "^4.0.2", "@types/react": "*", "commander": "^5.1.0", "joi": "^17.9.2", "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0", "utility-types": "^3.10.0", "webpack": "^5.95.0", "webpack-merge": "^5.9.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-XYMK8k1szDCFMw2V+Xyen0g7Kee1sP3dtFnl7vkGkZOkeAJ/oPDQPL8iz4HBKOo/cwU8QeV6onVjMqtP+tFzsw=="], + "@docusaurus/types": ["@docusaurus/types@3.10.2", "", { "dependencies": { "@mdx-js/mdx": "^3.0.0", "@types/history": "^4.7.11", "@types/mdast": "^4.0.2", "@types/react": "*", "commander": "^5.1.0", "joi": "^17.9.2", "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0", "utility-types": "^3.10.0", "webpack": "^5.95.0", "webpack-merge": "^5.9.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-B6rvfwIFSapUqUJjMriZswX13K8l5Z7AcmVE6uTEJpYddQieSTR12DsGaFtcZAIDsQd4p+0WTl0Vc6jmZK0Trw=="], - "@docusaurus/utils": ["@docusaurus/utils@3.10.1", "", { "dependencies": { "@docusaurus/logger": "3.10.1", "@docusaurus/types": "3.10.1", "@docusaurus/utils-common": "3.10.1", "escape-string-regexp": "^4.0.0", "execa": "^5.1.1", "file-loader": "^6.2.0", "fs-extra": "^11.1.1", "github-slugger": "^1.5.0", "globby": "^11.1.0", "gray-matter": "^4.0.3", "jiti": "^1.20.0", "js-yaml": "^4.1.0", "lodash": "^4.17.21", "micromatch": "^4.0.5", "p-queue": "^6.6.2", "prompts": "^2.4.2", "resolve-pathname": "^3.0.0", "tslib": "^2.6.0", "url-loader": "^4.1.1", "utility-types": "^3.10.0", "webpack": "^5.88.1" } }, "sha512-3ojeJry9xBYdJO6qoyyzqeJFSJBVx2mXhyDzSdjwL2+URFQMf+h25gG38iswGImicK0ELjTd1EL2xzk8hf3QPw=="], + "@docusaurus/utils": ["@docusaurus/utils@3.10.2", "", { "dependencies": { "@11ty/gray-matter": "^1.0.0", "@docusaurus/logger": "3.10.2", "@docusaurus/types": "3.10.2", "@docusaurus/utils-common": "3.10.2", "escape-string-regexp": "^4.0.0", "execa": "^5.1.1", "file-loader": "^6.2.0", "fs-extra": "^11.1.1", "github-slugger": "^1.5.0", "globby": "^11.1.0", "jiti": "^1.20.0", "js-yaml": "^4.1.0", "lodash": "^4.17.21", "micromatch": "^4.0.5", "p-queue": "^6.6.2", "prompts": "^2.4.2", "resolve-pathname": "^3.0.0", "tslib": "^2.6.0", "url-loader": "^4.1.1", "utility-types": "^3.10.0", "webpack": "^5.88.1" } }, "sha512-xx0W3eav2uW1NRIpuHJWNwLTC15xPNjU4Uxi9NSnd3swYC96BE3vFiT93SD8s24kmAAWNwgZwfZ2fghGZ01Lcw=="], - "@docusaurus/utils-common": ["@docusaurus/utils-common@3.10.1", "", { "dependencies": { "@docusaurus/types": "3.10.1", "tslib": "^2.6.0" } }, "sha512-5mFSgEADtnFxFH7RLw02QA5MpU5JVUCj0MPeIvi/aF4Fi45tQRIuTwXoXDqJ+1VfQJuYJGz3SI63wmGz4HvXzA=="], + "@docusaurus/utils-common": ["@docusaurus/utils-common@3.10.2", "", { "dependencies": { "@docusaurus/types": "3.10.2", "tslib": "^2.6.0" } }, "sha512-x3Dz6jv6iQKBNjBmVTu8p57abMp/VNTUgKBMgRVXJc5444orBTsArv0+cdfrXTiz/VMmHfDRVkPbL7GH2B7T7w=="], - "@docusaurus/utils-validation": ["@docusaurus/utils-validation@3.10.1", "", { "dependencies": { "@docusaurus/logger": "3.10.1", "@docusaurus/utils": "3.10.1", "@docusaurus/utils-common": "3.10.1", "fs-extra": "^11.2.0", "joi": "^17.9.2", "js-yaml": "^4.1.0", "lodash": "^4.17.21", "tslib": "^2.6.0" } }, "sha512-cRv1X69jwaWv47waglllgZVWzeBFLhl53XT/XED/83BerVBTC5FTP8WTcVl8Z6sZOegDSwitu/wpCSPCDOT6lg=="], + "@docusaurus/utils-validation": ["@docusaurus/utils-validation@3.10.2", "", { "dependencies": { "@docusaurus/logger": "3.10.2", "@docusaurus/utils": "3.10.2", "@docusaurus/utils-common": "3.10.2", "fs-extra": "^11.2.0", "joi": "^17.9.2", "js-yaml": "^4.1.0", "lodash": "^4.17.21", "tslib": "^2.6.0" } }, "sha512-sn8unbDfUL585NtR3cwHefPicOyaHvPaX7VD0aOg/siIxUBoKyKKaGEqzJZDS64mM43TnxurkYDtmB1wsJlZsw=="], "@effect-fc/example": ["@effect-fc/example@workspace:packages/example"], @@ -1170,8 +1172,6 @@ "@types/express-serve-static-core": ["@types/express-serve-static-core@4.19.9", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-QP2ESEe/ImWY0HDwNAnK9PvEffUyhLTnWkk7KXzHfyeWAnlrDe1fN77bXl6ia8KT3wPlmA7t9/VPRpnf4Ex9sg=="], - "@types/gtag.js": ["@types/gtag.js@0.0.20", "", {}, "sha512-wwAbk3SA2QeU67unN7zPxjEHmPmlXwZXZvQEpbEUQuMCRGgKyE1m6XDuTUA9b6pCGb/GqJmdfMOY5LuDjJSbbg=="], - "@types/hast": ["@types/hast@3.0.5", "", { "dependencies": { "@types/unist": "*" } }, "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g=="], "@types/history": ["@types/history@4.7.11", "", {}, "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA=="], @@ -1300,7 +1300,7 @@ "acorn-walk": ["acorn-walk@8.3.5", "", { "dependencies": { "acorn": "^8.11.0" } }, "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw=="], - "address": ["address@1.2.2", "", {}, "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA=="], + "address": ["address@2.0.3", "", {}, "sha512-XNAb/a6TCqou+TufU8/u11HCu9x1gYvOoxLwtlXgIqmkrYQADVv6ljyW2zwiPhHz9R1gItAWpuDrdJMmrOBFEA=="], "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], @@ -1598,7 +1598,7 @@ "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], - "detect-port": ["detect-port@1.6.1", "", { "dependencies": { "address": "^1.0.1", "debug": "4" }, "bin": { "detect": "bin/detect-port.js", "detect-port": "bin/detect-port.js" } }, "sha512-CmnVc+Hek2egPx1PeTFVta2W78xy2K/9Rkf6cC4T59S50tVnzKj+tnx5mmx5lwvCkujZ4uRrpRSuV+IVs3f90Q=="], + "detect-port": ["detect-port@2.1.0", "", { "dependencies": { "address": "^2.0.1" }, "bin": { "detect": "dist/commonjs/bin/detect-port.js", "detect-port": "dist/commonjs/bin/detect-port.js" } }, "sha512-epZuWb/6Q62L+nDHJc/hQAqf8pylsqgk3BpZXVBx1CDnr3nkrVNn73Uu1rXcFzkNcc+hkP3whuOg7JZYaQB65Q=="], "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], @@ -1684,8 +1684,6 @@ "eslint-scope": ["eslint-scope@5.1.1", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw=="], - "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], - "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], "estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="], @@ -1812,8 +1810,6 @@ "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], - "gray-matter": ["gray-matter@4.0.3", "", { "dependencies": { "js-yaml": "^3.13.1", "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" } }, "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q=="], - "gzip-size": ["gzip-size@6.0.0", "", { "dependencies": { "duplexer": "^0.1.2" } }, "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q=="], "handle-thing": ["handle-thing@2.0.1", "", {}, "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg=="], @@ -2744,8 +2740,6 @@ "spdy-transport": ["spdy-transport@3.0.0", "", { "dependencies": { "debug": "^4.1.0", "detect-node": "^2.0.4", "hpack.js": "^2.1.6", "obuf": "^1.1.2", "readable-stream": "^3.0.6", "wbuf": "^1.7.3" } }, "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw=="], - "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], - "srcset": ["srcset@4.0.0", "", {}, "sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw=="], "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], @@ -3020,8 +3014,6 @@ "@docusaurus/core/webpack-merge": ["webpack-merge@6.0.1", "", { "dependencies": { "clone-deep": "^4.0.1", "flat": "^5.0.2", "wildcard": "^2.0.1" } }, "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg=="], - "@docusaurus/faster/@docusaurus/types": ["@docusaurus/types@3.10.2", "", { "dependencies": { "@mdx-js/mdx": "^3.0.0", "@types/history": "^4.7.11", "@types/mdast": "^4.0.2", "@types/react": "*", "commander": "^5.1.0", "joi": "^17.9.2", "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0", "utility-types": "^3.10.0", "webpack": "^5.95.0", "webpack-merge": "^5.9.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-B6rvfwIFSapUqUJjMriZswX13K8l5Z7AcmVE6uTEJpYddQieSTR12DsGaFtcZAIDsQd4p+0WTl0Vc6jmZK0Trw=="], - "@docusaurus/utils/jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], "@effect-fc/example-next/@effect/platform-browser": ["@effect/platform-browser@4.0.0-beta.98", "", { "dependencies": { "multipasta": "^0.2.8" }, "peerDependencies": { "effect": "^4.0.0-beta.98" } }, "sha512-Y0BEe0N8clKmJTHqmZpS85pLZbqAxP5uV3IYF0tDw/1dcs8AbTRHjbRmOLdyLtmU32NBVAiaUD3Vopb5Et5R/g=="], @@ -3114,8 +3106,6 @@ "got/@sindresorhus/is": ["@sindresorhus/is@5.6.0", "", {}, "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g=="], - "gray-matter/js-yaml": ["js-yaml@3.15.0", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog=="], - "hpack.js/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], "html-minifier-terser/commander": ["commander@10.0.1", "", {}, "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="], @@ -3412,8 +3402,6 @@ "finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], - "gray-matter/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], - "hpack.js/readable-stream/isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], "hpack.js/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], diff --git a/packages/docs/package.json b/packages/docs/package.json index 52feaa1..27d3ce7 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -15,9 +15,9 @@ "typecheck": "tsc" }, "dependencies": { - "@docusaurus/core": "3.10.1", - "@docusaurus/faster": "^3.10.1", - "@docusaurus/preset-classic": "3.10.1", + "@docusaurus/core": "^3.10.2", + "@docusaurus/faster": "^3.10.2", + "@docusaurus/preset-classic": "^3.10.2", "@mdx-js/react": "^3.1.1", "clsx": "^2.1.1", "prism-react-renderer": "^2.4.1", @@ -25,9 +25,9 @@ "react-dom": "^19.2.5" }, "devDependencies": { - "@docusaurus/module-type-aliases": "3.10.1", - "@docusaurus/tsconfig": "3.10.1", - "@docusaurus/types": "3.10.1", + "@docusaurus/module-type-aliases": "^3.10.2", + "@docusaurus/tsconfig": "^3.10.2", + "@docusaurus/types": "^3.10.2", "@types/react": "^19.2.15", "@types/react-dom": "^19.2.3", "typescript": "~6.0.3" -- 2.52.0 From 86e0bfb73311c37961c68d3eedab0d0be0b83e9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 26 Jul 2026 01:30:34 +0200 Subject: [PATCH 128/133] Add CI for Docker --- .gitea/workflows/publish.yaml | 36 ++++++++++++++++++++++++++++++-- .gitea/workflows/test-build.yaml | 32 +++++++++++++++++++++++++--- Dockerfile | 12 +++++++++++ 3 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 Dockerfile diff --git a/.gitea/workflows/publish.yaml b/.gitea/workflows/publish.yaml index f20291c..f6c832f 100644 --- a/.gitea/workflows/publish.yaml +++ b/.gitea/workflows/publish.yaml @@ -11,16 +11,26 @@ jobs: steps: - name: Setup Bun uses: oven-sh/setup-bun@v2 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Login to Container Registry + uses: docker/login-action@v3 + with: + registry: docker.valverde.cloud + username: ${{ secrets.DOCKER_REGISTRY_USERNAME }} + password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }} + - name: Clone repo uses: actions/checkout@v6 - name: Install dependencies run: bun install --frozen-lockfile + - name: Lint TypeScript run: bun lint:tsc - name: Lint Biome run: bun lint:biome - - name: Build - run: bun run build + - name: Test + run: bun run test - name: Publish effect-fc uses: JS-DevTools/npm-publish@v4 with: @@ -28,3 +38,25 @@ jobs: access: public token: ${{ secrets.NPM_TOKEN }} registry: https://registry.npmjs.org + + - name: Generate Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: | + docker.valverde.cloud/${{ gitea.repository }} + tags: | + type=ref,event=branch + type=ref,event=tag + type=ref,event=pr + type=sha + flavor: | + latest=true + - name: Build Docker image + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.gitea/workflows/test-build.yaml b/.gitea/workflows/test-build.yaml index 729eb00..ec63b12 100644 --- a/.gitea/workflows/test-build.yaml +++ b/.gitea/workflows/test-build.yaml @@ -12,16 +12,42 @@ jobs: - name: Setup Node uses: actions/setup-node@v6 with: - node-version: "24" + node-version: "22" + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Clone repo uses: actions/checkout@v6 - name: Install dependencies run: bun install --frozen-lockfile + - name: Lint TypeScript run: bun lint:tsc - name: Lint Biome run: bun lint:biome - - name: Build - run: bun run build + - name: Test + run: bun run test - name: Pack run: bun pack + + - name: Generate Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: | + docker.valverde.cloud/${{ gitea.repository }} + tags: | + type=ref,event=branch + type=ref,event=tag + type=ref,event=pr + type=sha + flavor: | + latest=true + - name: Build Docker image + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: false + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6dac0e3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM oven/bun:1.3.12-debian@sha256:1b709c9dd883fc1af38c210f7ea5222c552a8d470ea73efbd4b8fcfee798a64b AS bun + +FROM node:22.21.1-trixie-slim@sha256:98e1429d1a0b99378b4de43fa385f0746fd6276faf4feeb6104d91f6bad290f9 +COPY --from=bun /usr/local/bin/bun /usr/local/bin/bunx /usr/local/bin/ +COPY . /app +WORKDIR /app + +RUN bun install --frozen-lockfile && \ + bun run build && \ + bun clean:cache && \ + bun clean:modules && \ + bun install --production --frozen-lockfile -- 2.52.0 From 0af88596fe1ebae09a15927f50b241b0fa630ba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 26 Jul 2026 01:48:08 +0200 Subject: [PATCH 129/133] Fix docs package --- packages/docs/package.json | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/docs/package.json b/packages/docs/package.json index 27d3ce7..726f33d 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -3,16 +3,20 @@ "version": "0.0.0", "private": true, "scripts": { + "lint:tsc": "tsc -b --noEmit", + "lint:biome": "biome lint", "docusaurus": "docusaurus", "start": "docusaurus start", - "build": "docusaurus build", + "build": "docusaurus build --out-dir dist", "swizzle": "docusaurus swizzle", "deploy": "docusaurus deploy", "clear": "docusaurus clear", - "serve": "docusaurus serve", + "serve": "docusaurus serve --dir dist", "write-translations": "docusaurus write-translations", "write-heading-ids": "docusaurus write-heading-ids", - "typecheck": "tsc" + "clean:cache": "rm -rf .turbo *.tsbuildinfo && docusaurus clear", + "clean:dist": "rm -rf dist", + "clean:modules": "rm -rf node_modules" }, "dependencies": { "@docusaurus/core": "^3.10.2", -- 2.52.0 From 7982a568f7f52671ed4aa3f3d79ef0306007339d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 26 Jul 2026 02:07:53 +0200 Subject: [PATCH 130/133] Fix docs --- packages/docs/biome.json | 10 ++++++++++ packages/docs/docusaurus.config.ts | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/packages/docs/biome.json b/packages/docs/biome.json index 41d707b..8182858 100644 --- a/packages/docs/biome.json +++ b/packages/docs/biome.json @@ -4,5 +4,15 @@ "extends": "//", "files": { "includes": ["./src/**"] + }, + "linter": { + "rules": { + "complexity": { + "noImportantStyles": "off" + }, + "style": { + "noDescendingSpecificity": "off" + } + } } } diff --git a/packages/docs/docusaurus.config.ts b/packages/docs/docusaurus.config.ts index 366df46..90ee9f8 100644 --- a/packages/docs/docusaurus.config.ts +++ b/packages/docs/docusaurus.config.ts @@ -84,6 +84,11 @@ const config: Config = { type: "docsVersionDropdown", position: "right", }, + { + href: "https://www.npmjs.com/package/effect-view", + label: "npm", + position: "right", + }, { href: "https://github.com/Thiladev/effect-view", label: "GitHub", -- 2.52.0 From 01477c304e813f3525eedad2ce583206fb11b1b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 26 Jul 2026 02:15:43 +0200 Subject: [PATCH 131/133] Fix CI --- .gitea/workflows/publish.yaml | 2 ++ .gitea/workflows/test-build.yaml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.gitea/workflows/publish.yaml b/.gitea/workflows/publish.yaml index f6c832f..0a4a88a 100644 --- a/.gitea/workflows/publish.yaml +++ b/.gitea/workflows/publish.yaml @@ -31,6 +31,8 @@ jobs: run: bun lint:biome - name: Test run: bun run test + - name: Build + run: bun run build - name: Publish effect-fc uses: JS-DevTools/npm-publish@v4 with: diff --git a/.gitea/workflows/test-build.yaml b/.gitea/workflows/test-build.yaml index ec63b12..bd21c81 100644 --- a/.gitea/workflows/test-build.yaml +++ b/.gitea/workflows/test-build.yaml @@ -27,6 +27,8 @@ jobs: run: bun lint:biome - name: Test run: bun run test + - name: Build + run: bun run build - name: Pack run: bun pack -- 2.52.0 From 85af8ecb97561473d361038541f188c67348649c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 26 Jul 2026 02:22:52 +0200 Subject: [PATCH 132/133] Cleanup --- .codex | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .codex diff --git a/.codex b/.codex deleted file mode 100644 index e69de29..0000000 -- 2.52.0 From 7b6fa23343fa02072ec35082b78c49e7b139eee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 26 Jul 2026 02:28:28 +0200 Subject: [PATCH 133/133] Fix CI --- .gitea/workflows/publish.yaml | 5 +++++ .gitea/workflows/test-build.yaml | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/.gitea/workflows/publish.yaml b/.gitea/workflows/publish.yaml index 0a4a88a..4154609 100644 --- a/.gitea/workflows/publish.yaml +++ b/.gitea/workflows/publish.yaml @@ -41,6 +41,11 @@ jobs: token: ${{ secrets.NPM_TOKEN }} registry: https://registry.npmjs.org + - name: Clean before Docker build + run: | + bun clean:cache + bun clean:dist + bun clean:modules - name: Generate Docker metadata id: meta uses: docker/metadata-action@v5 diff --git a/.gitea/workflows/test-build.yaml b/.gitea/workflows/test-build.yaml index bd21c81..87f6c74 100644 --- a/.gitea/workflows/test-build.yaml +++ b/.gitea/workflows/test-build.yaml @@ -32,6 +32,11 @@ jobs: - name: Pack run: bun pack + - name: Clean before Docker build + run: | + bun clean:cache + bun clean:dist + bun clean:modules - name: Generate Docker metadata id: meta uses: docker/metadata-action@v5 -- 2.52.0
- + title="effect-fc" + description="Write React function components with Effect" + > +
+
+

Effect for React function components

+

Write components as Effect programs.

+

+ effect-fc gives React 19 components access to Effect services, + scopes, subscriptions, and async workflows without giving up normal + React boundaries. +

+
+ + Get Started + + + GitHub + +
+
+ +
+
+

Generator components

+

+ Use Component.make to yield Effects and return JSX + from the same component body. +

+
+
+

Runtime at the edge

+

+ Provide your app layer once with ReactRuntime.Provider + and convert Effect-FC components at React boundaries. +

+
+
+

Scoped lifecycles

+

+ Tie subscriptions and resources to component scopes so finalizers + run when React unmounts. +

+
+
- ); + ) } diff --git a/packages/docs/src/pages/markdown-page.md b/packages/docs/src/pages/markdown-page.md deleted file mode 100644 index 9756c5b..0000000 --- a/packages/docs/src/pages/markdown-page.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Markdown page example ---- - -# Markdown page example - -You don't need React to write simple standalone pages. diff --git a/packages/docs/static/img/docusaurus-social-card.jpg b/packages/docs/static/img/docusaurus-social-card.jpg deleted file mode 100644 index ffcb448210e1a456cb3588ae8b396a597501f187..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55746 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
sH`pxVp$E*LSx0bY$^ho1gZ(Ce+BX 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 fgzs%Vr*-jYG(YMS<ttdtee# diff --git a/packages/docs/static/img/docusaurus.png b/packages/docs/static/img/docusaurus.png deleted file mode 100644 index f458149e3c8f53335f28fbc162ae67f55575c881..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5142 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 F{0H{b<4^zq diff --git a/packages/docs/static/img/logo.svg b/packages/docs/static/img/logo.svg index 9db6d0d..9fb9b2b 100644 --- a/packages/docs/static/img/logo.svg +++ b/packages/docs/static/img/logo.svg @@ -1 +1,6 @@ - \ No newline at end of file + + + + + + diff --git a/packages/docs/static/img/undraw_docusaurus_mountain.svg b/packages/docs/static/img/undraw_docusaurus_mountain.svg deleted file mode 100644 index af961c4..0000000 --- a/packages/docs/static/img/undraw_docusaurus_mountain.svg +++ /dev/null @@ -1,171 +0,0 @@ - - Easy to Use - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/docs/static/img/undraw_docusaurus_react.svg b/packages/docs/static/img/undraw_docusaurus_react.svg deleted file mode 100644 index 94b5cf0..0000000 --- a/packages/docs/static/img/undraw_docusaurus_react.svg +++ /dev/null @@ -1,170 +0,0 @@ - - Powered by React - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/docs/static/img/undraw_docusaurus_tree.svg b/packages/docs/static/img/undraw_docusaurus_tree.svg deleted file mode 100644 index d9161d3..0000000 --- a/packages/docs/static/img/undraw_docusaurus_tree.svg +++ /dev/null @@ -1,40 +0,0 @@ - - Focus on What Matters - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/docs/tsconfig.json b/packages/docs/tsconfig.json index 920d7a6..ae701a6 100644 --- a/packages/docs/tsconfig.json +++ b/packages/docs/tsconfig.json @@ -2,7 +2,8 @@ // This file is not used in compilation. It is here just for a nice editor experience. "extends": "@docusaurus/tsconfig", "compilerOptions": { - "baseUrl": "." + "baseUrl": ".", + "ignoreDeprecations": "6.0" }, "exclude": [".docusaurus", "build"] } -- 2.52.0 From 25fac0ca328bfbcfeee49a5ba12da4d50d51c749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 8 Jun 2026 15:35:15 +0200 Subject: [PATCH 007/133] Fix --- bun.lock | 686 +++++++++++++++++++++++++++---------------------------- 1 file changed, 332 insertions(+), 354 deletions(-) diff --git a/bun.lock b/bun.lock index bc1bfa4..a30a084 100644 --- a/bun.lock +++ b/bun.lock @@ -82,247 +82,249 @@ }, }, "packages": { - "@algolia/abtesting": ["@algolia/abtesting@1.16.0", "", { "dependencies": { "@algolia/client-common": "5.50.0", "@algolia/requester-browser-xhr": "5.50.0", "@algolia/requester-fetch": "5.50.0", "@algolia/requester-node-http": "5.50.0" } }, "sha512-alHFZ68/i9qLC/muEB07VQ9r7cB8AvCcGX6dVQi2PNHhc/ZQRmmFAv8KK1ay4UiseGSFr7f0nXBKsZ/jRg7e4g=="], + "@algolia/abtesting": ["@algolia/abtesting@1.19.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-Lhnez3hhXHk25lfxLAMxvkP4fmN3+1RgADhD2ssMDBYuAsDVReeyP+3SGRx+ntq8ijMrLqUyfvO72TB6jsTteQ=="], - "@algolia/autocomplete-core": ["@algolia/autocomplete-core@1.19.2", "", { "dependencies": { "@algolia/autocomplete-plugin-algolia-insights": "1.19.2", "@algolia/autocomplete-shared": "1.19.2" } }, "sha512-mKv7RyuAzXvwmq+0XRK8HqZXt9iZ5Kkm2huLjgn5JoCPtDy+oh9yxUMfDDaVCw0oyzZ1isdJBc7l9nuCyyR7Nw=="], + "@algolia/autocomplete-core": ["@algolia/autocomplete-core@1.19.8", "", { "dependencies": { "@algolia/autocomplete-plugin-algolia-insights": "1.19.8", "@algolia/autocomplete-shared": "1.19.8" } }, "sha512-3YEorYg44niXcm7gkft3nXYItHd44e8tmh4D33CTszPgP0QWkaLEaFywiNyJBo7UL/mqObA/G9RYuU7R8tN1IA=="], - "@algolia/autocomplete-plugin-algolia-insights": ["@algolia/autocomplete-plugin-algolia-insights@1.19.2", "", { "dependencies": { "@algolia/autocomplete-shared": "1.19.2" }, "peerDependencies": { "search-insights": ">= 1 < 3" } }, "sha512-TjxbcC/r4vwmnZaPwrHtkXNeqvlpdyR+oR9Wi2XyfORkiGkLTVhX2j+O9SaCCINbKoDfc+c2PB8NjfOnz7+oKg=="], + "@algolia/autocomplete-plugin-algolia-insights": ["@algolia/autocomplete-plugin-algolia-insights@1.19.8", "", { "dependencies": { "@algolia/autocomplete-shared": "1.19.8" }, "peerDependencies": { "search-insights": ">= 1 < 3" } }, "sha512-ZvJWO8ZZJDpc1LNM2TTBdmQsZBLMR4rU5iNR2OYvEeFBiaf/0ESnRSSLQbryarJY4SVxtoz6A2ZtDMNM+iQEAA=="], - "@algolia/autocomplete-shared": ["@algolia/autocomplete-shared@1.19.2", "", { "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, "sha512-jEazxZTVD2nLrC+wYlVHQgpBoBB5KPStrJxLzsIFl6Kqd1AlG9sIAGl39V5tECLpIQzB3Qa2T6ZPJ1ChkwMK/w=="], + "@algolia/autocomplete-shared": ["@algolia/autocomplete-shared@1.19.8", "", { "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, "sha512-h5hf2t8ejF6vlOgvLaZzQbWs5SyH2z4PAWygNAvvD/2RI29hdQ54ldUGwqVuj9Srs+n8XUKTPUqb7fvhBhQrnQ=="], - "@algolia/client-abtesting": ["@algolia/client-abtesting@5.50.0", "", { "dependencies": { "@algolia/client-common": "5.50.0", "@algolia/requester-browser-xhr": "5.50.0", "@algolia/requester-fetch": "5.50.0", "@algolia/requester-node-http": "5.50.0" } }, "sha512-mfgUdLQNxOAvCZUGzPQxjahEWEPuQkKlV0ZtGmePOa9ZxIQZlk31vRBNbM6ScU8jTH41SCYE77G/lCifDr1SVw=="], + "@algolia/client-abtesting": ["@algolia/client-abtesting@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-0ZjA5Hcmaoz5Lj6OG0zhfIyeqzJZnLW2CRJA1W17UwMFGRtZAJ9yJKRvPEDA6gkpsIoQxORTSW6sWFiuYncPNQ=="], - "@algolia/client-analytics": ["@algolia/client-analytics@5.50.0", "", { "dependencies": { "@algolia/client-common": "5.50.0", "@algolia/requester-browser-xhr": "5.50.0", "@algolia/requester-fetch": "5.50.0", "@algolia/requester-node-http": "5.50.0" } }, "sha512-5mjokeKYyPaP3Q8IYJEnutI+O4dW/Ixxx5IgsSxT04pCfGqPXxTOH311hTQxyNpcGGEOGrMv8n8Z+UMTPamioQ=="], + "@algolia/client-analytics": ["@algolia/client-analytics@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-kWNodP75iiEaOtemC9F/hlxNBG5E2QUjN1BusnE6m2b4l7Qh/BUO3fGCVsmKJI65VO4VKGGmT43ICvHtTcJ2JQ=="], - "@algolia/client-common": ["@algolia/client-common@5.50.0", "", {}, "sha512-emtOvR6dl3rX3sBJXXbofMNHU1qMQqQSWu319RMrNL5BWoBqyiq7y0Zn6cjJm7aGHV/Qbf+KCCYeWNKEMPI3BQ=="], + "@algolia/client-common": ["@algolia/client-common@5.53.0", "", {}, "sha512-YPN45TXD9Wrse185t/Ta7nktZsqpv97oOjCzp2sblHnCL6rBc9TDeJAg1IGl2UpdwnSD05Zu/5wLB4watOUMyg=="], - "@algolia/client-insights": ["@algolia/client-insights@5.50.0", "", { "dependencies": { "@algolia/client-common": "5.50.0", "@algolia/requester-browser-xhr": "5.50.0", "@algolia/requester-fetch": "5.50.0", "@algolia/requester-node-http": "5.50.0" } }, "sha512-IerGH2/hcj/6bwkpQg/HHRqmlGN1XwygQWythAk0gZFBrghs9danJaYuSS3ShzLSVoIVth4jY5GDPX9Lbw5cgg=="], + "@algolia/client-insights": ["@algolia/client-insights@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-qAcYTDJE6m924FDDUQvdD6vh7DYaqOeSpFS74IP37/JRV0v4cGBauyxTF2WzDnokUylQDbqreoFIJZfg0Fitmw=="], - "@algolia/client-personalization": ["@algolia/client-personalization@5.50.0", "", { "dependencies": { "@algolia/client-common": "5.50.0", "@algolia/requester-browser-xhr": "5.50.0", "@algolia/requester-fetch": "5.50.0", "@algolia/requester-node-http": "5.50.0" } }, "sha512-3idPJeXn5L0MmgP9jk9JJqblrQ/SguN93dNK9z9gfgyupBhHnJMOEjrRYcVgTIfvG13Y04wO+Q0FxE2Ut8PVbA=="], + "@algolia/client-personalization": ["@algolia/client-personalization@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-fQaY+DkSJOpuUVUe8MQTwrdiKAqkJGhpDarB08duBn/sUv7Bkib6MDRQauCcWTWTe4HIW+EbwQP9R4kci1V/Yw=="], - "@algolia/client-query-suggestions": ["@algolia/client-query-suggestions@5.50.0", "", { "dependencies": { "@algolia/client-common": "5.50.0", "@algolia/requester-browser-xhr": "5.50.0", "@algolia/requester-fetch": "5.50.0", "@algolia/requester-node-http": "5.50.0" } }, "sha512-q7qRoWrQK1a8m5EFQEmPlo7+pg9mVQ8X5jsChtChERre0uS2pdYEDixBBl0ydBSGkdGbLUDufcACIhH/077E4g=="], + "@algolia/client-query-suggestions": ["@algolia/client-query-suggestions@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-o72tsiEZGfeS/dxL9IADfzcZWGEwKDEe5CvtrBuT//3JR+SHuTtHRI2ZTf7D7bcKagcbojvO8hnkHdfoakSlYg=="], - "@algolia/client-search": ["@algolia/client-search@5.50.0", "", { "dependencies": { "@algolia/client-common": "5.50.0", "@algolia/requester-browser-xhr": "5.50.0", "@algolia/requester-fetch": "5.50.0", "@algolia/requester-node-http": "5.50.0" } }, "sha512-Jc360x4yqb3eEg4OY4KEIdGePBxZogivKI+OGIU8aLXgAYPTECvzeOBc90312yHA1hr3AeRlAFl0rIc8lQaIrQ=="], + "@algolia/client-search": ["@algolia/client-search@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-Ds16IyPm/dNJPCU8OzApo2gwGrgWT5BYHhE3NFwZbpCveqyvPDB9sZDDkJ5DsdOGT2aC+R3i0/M1OVXF2qdgPg=="], "@algolia/events": ["@algolia/events@4.0.1", "", {}, "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ=="], - "@algolia/ingestion": ["@algolia/ingestion@1.50.0", "", { "dependencies": { "@algolia/client-common": "5.50.0", "@algolia/requester-browser-xhr": "5.50.0", "@algolia/requester-fetch": "5.50.0", "@algolia/requester-node-http": "5.50.0" } }, "sha512-OS3/Viao+NPpyBbEY3tf6hLewppG+UclD+9i0ju56mq2DrdMJFCkEky6Sk9S5VPcbLzxzg3BqBX6u9Q35w19aQ=="], + "@algolia/ingestion": ["@algolia/ingestion@1.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-oNbT6z4NwD8Pou9VPINGlN/tlG1afESh2EbxqnP6rwl95xKVD/Zlciis1PpNeO/9U/rrajc1+7DcfKi03tX1KQ=="], - "@algolia/monitoring": ["@algolia/monitoring@1.50.0", "", { "dependencies": { "@algolia/client-common": "5.50.0", "@algolia/requester-browser-xhr": "5.50.0", "@algolia/requester-fetch": "5.50.0", "@algolia/requester-node-http": "5.50.0" } }, "sha512-/znwgSiGufpbJVIoDmeQaHtTq+OMdDawFRbMSJVv+12n79hW+qdQXS8/Uu3BD3yn0BzgVFJEvrsHrCsInZKdhw=="], + "@algolia/monitoring": ["@algolia/monitoring@1.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-G+KZb/yd+qAOFn/cEvTGeLxQm8aP3a0od50l3z/ylccY+/o4YG3TNcjU1tFQHW4mXC137GPyR7W70R0kRQDLnA=="], - "@algolia/recommend": ["@algolia/recommend@5.50.0", "", { "dependencies": { "@algolia/client-common": "5.50.0", "@algolia/requester-browser-xhr": "5.50.0", "@algolia/requester-fetch": "5.50.0", "@algolia/requester-node-http": "5.50.0" } }, "sha512-dHjUfu4jfjdQiKDpCpAnM7LP5yfG0oNShtfpF5rMCel6/4HIoqJ4DC4h5GKDzgrvJYtgAhblo0AYBmOM00T+lQ=="], + "@algolia/recommend": ["@algolia/recommend@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-6aVfYd55Un6IUgPLbo84WfgFZlS3L0vA1ttzXL5vahHewUJ8jYgd89TzlWRTeej7w70mb9RWsVlFYGmJ/diQww=="], - "@algolia/requester-browser-xhr": ["@algolia/requester-browser-xhr@5.50.0", "", { "dependencies": { "@algolia/client-common": "5.50.0" } }, "sha512-bffIbUljAWnh/Ctu5uScORajuUavqmZ0ACYd1fQQeSSYA9NNN83ynO26pSc2dZRXpSK0fkc1//qSSFXMKGu+aw=="], + "@algolia/requester-browser-xhr": ["@algolia/requester-browser-xhr@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0" } }, "sha512-ke27DqgzCOlt+RbeEdCxtXxMQOnAOi8ujr2wid0DmDKzR95Kw/f9sBsuhBxtjevCqJRJszfRTLY0B1pbO6IhkA=="], - "@algolia/requester-fetch": ["@algolia/requester-fetch@5.50.0", "", { "dependencies": { "@algolia/client-common": "5.50.0" } }, "sha512-y0EwNvPGvkM+yTAqqO6Gpt9wVGm3CLDtpLvNEiB3VGvN3WzfkjZGtLUsG/ru2kVJIIU7QcV0puuYgEpBeFxcJg=="], + "@algolia/requester-fetch": ["@algolia/requester-fetch@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0" } }, "sha512-GngiOqt2Gq4oLno6yXQVj9om+qSO9SWAoduoTOEg79dKZ62brB8OOIvSJG/vDNoanYi6a7Al9uDZwXvi+bcVTg=="], - "@algolia/requester-node-http": ["@algolia/requester-node-http@5.50.0", "", { "dependencies": { "@algolia/client-common": "5.50.0" } }, "sha512-xpwefe4fCOWnZgXCbkGpqQY6jgBSCf2hmgnySbyzZIccrv3SoashHKGPE4x6vVG+gdHrGciMTAcDo9HOZwH22Q=="], + "@algolia/requester-node-http": ["@algolia/requester-node-http@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0" } }, "sha512-6mF9LZMUk0QqWvrnxkxBqhswwz6Xfiwy6/gmTzL5HrlhdVG3ITAqGV2k3XmVThP1h0Ulc3VQwiNCD7/Nr4JNlQ=="], "@asamuzakjp/css-color": ["@asamuzakjp/css-color@3.2.0", "", { "dependencies": { "@csstools/css-calc": "^2.1.3", "@csstools/css-color-parser": "^3.0.9", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "lru-cache": "^10.4.3" } }, "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw=="], - "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], + "@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], - "@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="], + "@babel/compat-data": ["@babel/compat-data@7.29.7", "", {}, "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg=="], - "@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="], + "@babel/core": ["@babel/core@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-compilation-targets": "^7.29.7", "@babel/helper-module-transforms": "^7.29.7", "@babel/helpers": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA=="], - "@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], + "@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], - "@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], + "@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" } }, "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw=="], - "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="], + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.29.7", "", { "dependencies": { "@babel/compat-data": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g=="], - "@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.6", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.6", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow=="], + "@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.29.7", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-member-expression-to-functions": "^7.29.7", "@babel/helper-optimise-call-expression": "^7.29.7", "@babel/helper-replace-supers": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", "@babel/traverse": "^7.29.7", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg=="], - "@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], + "@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.29.7", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg=="], "@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.8", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", "debug": "^4.4.3", "lodash.debounce": "^4.0.8", "resolve": "^1.22.11" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA=="], - "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], + "@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], - "@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], + "@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.29.7", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg=="], - "@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="], + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.29.7", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g=="], - "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="], + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.29.7", "", { "dependencies": { "@babel/helper-module-imports": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg=="], - "@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], + "@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" } }, "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong=="], - "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.28.6", "", {}, "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug=="], + "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.29.7", "", {}, "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw=="], - "@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-wrap-function": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA=="], + "@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.29.7", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-wrap-function": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og=="], - "@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.28.6", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg=="], + "@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.29.7", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.29.7", "@babel/helper-optimise-call-expression": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ=="], - "@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], + "@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.29.7", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ=="], - "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.29.7", "", {}, "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw=="], - "@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ=="], + "@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.29.7", "", { "dependencies": { "@babel/template": "^7.29.7", "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw=="], - "@babel/helpers": ["@babel/helpers@7.29.2", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.29.0" } }, "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw=="], + "@babel/helpers": ["@babel/helpers@7.29.7", "", { "dependencies": { "@babel/template": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg=="], - "@babel/parser": ["@babel/parser@7.29.2", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="], + "@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": ["@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q=="], + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": ["@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w=="], - "@babel/plugin-bugfix-safari-class-field-initializer-scope": ["@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA=="], + "@babel/plugin-bugfix-safari-class-field-initializer-scope": ["@babel/plugin-bugfix-safari-class-field-initializer-scope@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ=="], - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ["@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA=="], + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ["@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ=="], - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ["@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-transform-optional-chaining": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.13.0" } }, "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw=="], + "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": ["@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA=="], - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": ["@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g=="], + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ["@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", "@babel/plugin-transform-optional-chaining": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.13.0" } }, "sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw=="], + + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": ["@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw=="], "@babel/plugin-proposal-private-property-in-object": ["@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2", "", { "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w=="], "@babel/plugin-syntax-dynamic-import": ["@babel/plugin-syntax-dynamic-import@7.8.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ=="], - "@babel/plugin-syntax-import-assertions": ["@babel/plugin-syntax-import-assertions@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw=="], + "@babel/plugin-syntax-import-assertions": ["@babel/plugin-syntax-import-assertions@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw=="], - "@babel/plugin-syntax-import-attributes": ["@babel/plugin-syntax-import-attributes@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw=="], + "@babel/plugin-syntax-import-attributes": ["@babel/plugin-syntax-import-attributes@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg=="], - "@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w=="], + "@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A=="], - "@babel/plugin-syntax-typescript": ["@babel/plugin-syntax-typescript@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A=="], + "@babel/plugin-syntax-typescript": ["@babel/plugin-syntax-typescript@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA=="], "@babel/plugin-syntax-unicode-sets-regex": ["@babel/plugin-syntax-unicode-sets-regex@7.18.6", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg=="], - "@babel/plugin-transform-arrow-functions": ["@babel/plugin-transform-arrow-functions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA=="], + "@babel/plugin-transform-arrow-functions": ["@babel/plugin-transform-arrow-functions@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ=="], - "@babel/plugin-transform-async-generator-functions": ["@babel/plugin-transform-async-generator-functions@7.29.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-remap-async-to-generator": "^7.27.1", "@babel/traverse": "^7.29.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w=="], + "@babel/plugin-transform-async-generator-functions": ["@babel/plugin-transform-async-generator-functions@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-remap-async-to-generator": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA=="], - "@babel/plugin-transform-async-to-generator": ["@babel/plugin-transform-async-to-generator@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-remap-async-to-generator": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g=="], + "@babel/plugin-transform-async-to-generator": ["@babel/plugin-transform-async-to-generator@7.29.7", "", { "dependencies": { "@babel/helper-module-imports": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-remap-async-to-generator": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w=="], - "@babel/plugin-transform-block-scoped-functions": ["@babel/plugin-transform-block-scoped-functions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg=="], + "@babel/plugin-transform-block-scoped-functions": ["@babel/plugin-transform-block-scoped-functions@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA=="], - "@babel/plugin-transform-block-scoping": ["@babel/plugin-transform-block-scoping@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw=="], + "@babel/plugin-transform-block-scoping": ["@babel/plugin-transform-block-scoping@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ=="], - "@babel/plugin-transform-class-properties": ["@babel/plugin-transform-class-properties@7.28.6", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw=="], + "@babel/plugin-transform-class-properties": ["@babel/plugin-transform-class-properties@7.29.7", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA=="], - "@babel/plugin-transform-class-static-block": ["@babel/plugin-transform-class-static-block@7.28.6", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.12.0" } }, "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ=="], + "@babel/plugin-transform-class-static-block": ["@babel/plugin-transform-class-static-block@7.29.7", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.12.0" } }, "sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A=="], - "@babel/plugin-transform-classes": ["@babel/plugin-transform-classes@7.28.6", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-globals": "^7.28.0", "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-replace-supers": "^7.28.6", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q=="], + "@babel/plugin-transform-classes": ["@babel/plugin-transform-classes@7.29.7", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-compilation-targets": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-replace-supers": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g=="], - "@babel/plugin-transform-computed-properties": ["@babel/plugin-transform-computed-properties@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6", "@babel/template": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ=="], + "@babel/plugin-transform-computed-properties": ["@babel/plugin-transform-computed-properties@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/template": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA=="], - "@babel/plugin-transform-destructuring": ["@babel/plugin-transform-destructuring@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw=="], + "@babel/plugin-transform-destructuring": ["@babel/plugin-transform-destructuring@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg=="], - "@babel/plugin-transform-dotall-regex": ["@babel/plugin-transform-dotall-regex@7.28.6", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg=="], + "@babel/plugin-transform-dotall-regex": ["@babel/plugin-transform-dotall-regex@7.29.7", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ=="], - "@babel/plugin-transform-duplicate-keys": ["@babel/plugin-transform-duplicate-keys@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q=="], + "@babel/plugin-transform-duplicate-keys": ["@babel/plugin-transform-duplicate-keys@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ=="], - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": ["@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw=="], + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": ["@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.7", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA=="], - "@babel/plugin-transform-dynamic-import": ["@babel/plugin-transform-dynamic-import@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A=="], + "@babel/plugin-transform-dynamic-import": ["@babel/plugin-transform-dynamic-import@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg=="], - "@babel/plugin-transform-explicit-resource-management": ["@babel/plugin-transform-explicit-resource-management@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6", "@babel/plugin-transform-destructuring": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg=="], + "@babel/plugin-transform-explicit-resource-management": ["@babel/plugin-transform-explicit-resource-management@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/plugin-transform-destructuring": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw=="], - "@babel/plugin-transform-exponentiation-operator": ["@babel/plugin-transform-exponentiation-operator@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw=="], + "@babel/plugin-transform-exponentiation-operator": ["@babel/plugin-transform-exponentiation-operator@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ=="], - "@babel/plugin-transform-export-namespace-from": ["@babel/plugin-transform-export-namespace-from@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ=="], + "@babel/plugin-transform-export-namespace-from": ["@babel/plugin-transform-export-namespace-from@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA=="], - "@babel/plugin-transform-for-of": ["@babel/plugin-transform-for-of@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw=="], + "@babel/plugin-transform-for-of": ["@babel/plugin-transform-for-of@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ=="], - "@babel/plugin-transform-function-name": ["@babel/plugin-transform-function-name@7.27.1", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ=="], + "@babel/plugin-transform-function-name": ["@babel/plugin-transform-function-name@7.29.7", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg=="], - "@babel/plugin-transform-json-strings": ["@babel/plugin-transform-json-strings@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw=="], + "@babel/plugin-transform-json-strings": ["@babel/plugin-transform-json-strings@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg=="], - "@babel/plugin-transform-literals": ["@babel/plugin-transform-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA=="], + "@babel/plugin-transform-literals": ["@babel/plugin-transform-literals@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw=="], - "@babel/plugin-transform-logical-assignment-operators": ["@babel/plugin-transform-logical-assignment-operators@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A=="], + "@babel/plugin-transform-logical-assignment-operators": ["@babel/plugin-transform-logical-assignment-operators@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q=="], - "@babel/plugin-transform-member-expression-literals": ["@babel/plugin-transform-member-expression-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ=="], + "@babel/plugin-transform-member-expression-literals": ["@babel/plugin-transform-member-expression-literals@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg=="], - "@babel/plugin-transform-modules-amd": ["@babel/plugin-transform-modules-amd@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA=="], + "@babel/plugin-transform-modules-amd": ["@babel/plugin-transform-modules-amd@7.29.7", "", { "dependencies": { "@babel/helper-module-transforms": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew=="], - "@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.28.6", "", { "dependencies": { "@babel/helper-module-transforms": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA=="], + "@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.29.7", "", { "dependencies": { "@babel/helper-module-transforms": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ=="], - "@babel/plugin-transform-modules-systemjs": ["@babel/plugin-transform-modules-systemjs@7.29.0", "", { "dependencies": { "@babel/helper-module-transforms": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.29.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ=="], + "@babel/plugin-transform-modules-systemjs": ["@babel/plugin-transform-modules-systemjs@7.29.7", "", { "dependencies": { "@babel/helper-module-transforms": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ=="], - "@babel/plugin-transform-modules-umd": ["@babel/plugin-transform-modules-umd@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w=="], + "@babel/plugin-transform-modules-umd": ["@babel/plugin-transform-modules-umd@7.29.7", "", { "dependencies": { "@babel/helper-module-transforms": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA=="], - "@babel/plugin-transform-named-capturing-groups-regex": ["@babel/plugin-transform-named-capturing-groups-regex@7.29.0", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ=="], + "@babel/plugin-transform-named-capturing-groups-regex": ["@babel/plugin-transform-named-capturing-groups-regex@7.29.7", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ=="], - "@babel/plugin-transform-new-target": ["@babel/plugin-transform-new-target@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ=="], + "@babel/plugin-transform-new-target": ["@babel/plugin-transform-new-target@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A=="], - "@babel/plugin-transform-nullish-coalescing-operator": ["@babel/plugin-transform-nullish-coalescing-operator@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg=="], + "@babel/plugin-transform-nullish-coalescing-operator": ["@babel/plugin-transform-nullish-coalescing-operator@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg=="], - "@babel/plugin-transform-numeric-separator": ["@babel/plugin-transform-numeric-separator@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w=="], + "@babel/plugin-transform-numeric-separator": ["@babel/plugin-transform-numeric-separator@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw=="], - "@babel/plugin-transform-object-rest-spread": ["@babel/plugin-transform-object-rest-spread@7.28.6", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", "@babel/plugin-transform-destructuring": "^7.28.5", "@babel/plugin-transform-parameters": "^7.27.7", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA=="], + "@babel/plugin-transform-object-rest-spread": ["@babel/plugin-transform-object-rest-spread@7.29.7", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", "@babel/plugin-transform-destructuring": "^7.29.7", "@babel/plugin-transform-parameters": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A=="], - "@babel/plugin-transform-object-super": ["@babel/plugin-transform-object-super@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng=="], + "@babel/plugin-transform-object-super": ["@babel/plugin-transform-object-super@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-replace-supers": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA=="], - "@babel/plugin-transform-optional-catch-binding": ["@babel/plugin-transform-optional-catch-binding@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ=="], + "@babel/plugin-transform-optional-catch-binding": ["@babel/plugin-transform-optional-catch-binding@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng=="], - "@babel/plugin-transform-optional-chaining": ["@babel/plugin-transform-optional-chaining@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w=="], + "@babel/plugin-transform-optional-chaining": ["@babel/plugin-transform-optional-chaining@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ=="], - "@babel/plugin-transform-parameters": ["@babel/plugin-transform-parameters@7.27.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg=="], + "@babel/plugin-transform-parameters": ["@babel/plugin-transform-parameters@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g=="], - "@babel/plugin-transform-private-methods": ["@babel/plugin-transform-private-methods@7.28.6", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg=="], + "@babel/plugin-transform-private-methods": ["@babel/plugin-transform-private-methods@7.29.7", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug=="], - "@babel/plugin-transform-private-property-in-object": ["@babel/plugin-transform-private-property-in-object@7.28.6", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-create-class-features-plugin": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA=="], + "@babel/plugin-transform-private-property-in-object": ["@babel/plugin-transform-private-property-in-object@7.29.7", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-create-class-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA=="], - "@babel/plugin-transform-property-literals": ["@babel/plugin-transform-property-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ=="], + "@babel/plugin-transform-property-literals": ["@babel/plugin-transform-property-literals@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA=="], - "@babel/plugin-transform-react-constant-elements": ["@babel/plugin-transform-react-constant-elements@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-edoidOjl/ZxvYo4lSBOQGDSyToYVkTAwyVoa2tkuYTSmjrB1+uAedoL5iROVLXkxH+vRgA7uP4tMg2pUJpZ3Ug=="], + "@babel/plugin-transform-react-constant-elements": ["@babel/plugin-transform-react-constant-elements@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-J0wGhKan+rIiE2OhfhRptySLrJ6SjQYM6b6N1FMlhyhCcw1Mig8vQjWchyB+bgHGDvaWo6Diu6CLRMra2uMtmg=="], - "@babel/plugin-transform-react-display-name": ["@babel/plugin-transform-react-display-name@7.28.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA=="], + "@babel/plugin-transform-react-display-name": ["@babel/plugin-transform-react-display-name@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+1wdDMGNb4UPeY3Q4L5yLiYe6TXPXubs4NjrgRFw13hPRLJfEMw2Q5OXkee6/IfdqePIeW4Jjwe3aBh7SdKz4Q=="], - "@babel/plugin-transform-react-jsx": ["@babel/plugin-transform-react-jsx@7.28.6", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-module-imports": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", "@babel/plugin-syntax-jsx": "^7.28.6", "@babel/types": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow=="], + "@babel/plugin-transform-react-jsx": ["@babel/plugin-transform-react-jsx@7.29.7", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-module-imports": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", "@babel/plugin-syntax-jsx": "^7.29.7", "@babel/types": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-WsZulLVBUHXVj2cUcPVx6UE21TpalB6bHbSFErKT0Ib++ax24jjXe73FqlWvdylFOjiuPHYi6VCcgRad1ItN+A=="], - "@babel/plugin-transform-react-jsx-development": ["@babel/plugin-transform-react-jsx-development@7.27.1", "", { "dependencies": { "@babel/plugin-transform-react-jsx": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q=="], + "@babel/plugin-transform-react-jsx-development": ["@babel/plugin-transform-react-jsx-development@7.29.7", "", { "dependencies": { "@babel/plugin-transform-react-jsx": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Xfy3UVMF04+ypnFbkhvfqtmvwfe92qwQdbGZVonhE+6v35GzlofmOnA1szaZqzb9xYWr0nl1e5EMmzi0DNON1g=="], - "@babel/plugin-transform-react-pure-annotations": ["@babel/plugin-transform-react-pure-annotations@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA=="], + "@babel/plugin-transform-react-pure-annotations": ["@babel/plugin-transform-react-pure-annotations@7.29.7", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-H5E+HBgDpr6Q5t+Aj11tL7XkIui1jhbIoArVQnqjgXo5/3YxkN7ZEBcWF4RQlB0T4rrxJQbXS6kiFV6B7XTqUA=="], - "@babel/plugin-transform-regenerator": ["@babel/plugin-transform-regenerator@7.29.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog=="], + "@babel/plugin-transform-regenerator": ["@babel/plugin-transform-regenerator@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw=="], - "@babel/plugin-transform-regexp-modifiers": ["@babel/plugin-transform-regexp-modifiers@7.28.6", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg=="], + "@babel/plugin-transform-regexp-modifiers": ["@babel/plugin-transform-regexp-modifiers@7.29.7", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ=="], - "@babel/plugin-transform-reserved-words": ["@babel/plugin-transform-reserved-words@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw=="], + "@babel/plugin-transform-reserved-words": ["@babel/plugin-transform-reserved-words@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA=="], - "@babel/plugin-transform-runtime": ["@babel/plugin-transform-runtime@7.29.0", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", "babel-plugin-polyfill-corejs2": "^0.4.14", "babel-plugin-polyfill-corejs3": "^0.13.0", "babel-plugin-polyfill-regenerator": "^0.6.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-jlaRT5dJtMaMCV6fAuLbsQMSwz/QkvaHOHOSXRitGGwSpR1blCY4KUKoyP2tYO8vJcqYe8cEj96cqSztv3uF9w=="], + "@babel/plugin-transform-runtime": ["@babel/plugin-transform-runtime@7.29.7", "", { "dependencies": { "@babel/helper-module-imports": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", "babel-plugin-polyfill-corejs2": "^0.4.14", "babel-plugin-polyfill-corejs3": "^0.13.0", "babel-plugin-polyfill-regenerator": "^0.6.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-xmAscdE/AsqRW7vutbPNoUmu/nF5SrLKPs7aoJgEjo35lLKA/Bc0i2rMv/hr1+Y0o1bQCiVtith3u2vdgRL39Q=="], - "@babel/plugin-transform-shorthand-properties": ["@babel/plugin-transform-shorthand-properties@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ=="], + "@babel/plugin-transform-shorthand-properties": ["@babel/plugin-transform-shorthand-properties@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg=="], - "@babel/plugin-transform-spread": ["@babel/plugin-transform-spread@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA=="], + "@babel/plugin-transform-spread": ["@babel/plugin-transform-spread@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-/u5K1QWada7tbYNqTjMh96718g9NTwh9tfPJMsSmVsQwGT447FskV+KcfeXkXq2GWki4EM/MuTdmBec+hOuVTQ=="], - "@babel/plugin-transform-sticky-regex": ["@babel/plugin-transform-sticky-regex@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g=="], + "@babel/plugin-transform-sticky-regex": ["@babel/plugin-transform-sticky-regex@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA=="], - "@babel/plugin-transform-template-literals": ["@babel/plugin-transform-template-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg=="], + "@babel/plugin-transform-template-literals": ["@babel/plugin-transform-template-literals@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA=="], - "@babel/plugin-transform-typeof-symbol": ["@babel/plugin-transform-typeof-symbol@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw=="], + "@babel/plugin-transform-typeof-symbol": ["@babel/plugin-transform-typeof-symbol@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A=="], - "@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.28.6", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-create-class-features-plugin": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw=="], + "@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.29.7", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-create-class-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", "@babel/plugin-syntax-typescript": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw=="], - "@babel/plugin-transform-unicode-escapes": ["@babel/plugin-transform-unicode-escapes@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg=="], + "@babel/plugin-transform-unicode-escapes": ["@babel/plugin-transform-unicode-escapes@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA=="], - "@babel/plugin-transform-unicode-property-regex": ["@babel/plugin-transform-unicode-property-regex@7.28.6", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A=="], + "@babel/plugin-transform-unicode-property-regex": ["@babel/plugin-transform-unicode-property-regex@7.29.7", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw=="], - "@babel/plugin-transform-unicode-regex": ["@babel/plugin-transform-unicode-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw=="], + "@babel/plugin-transform-unicode-regex": ["@babel/plugin-transform-unicode-regex@7.29.7", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA=="], - "@babel/plugin-transform-unicode-sets-regex": ["@babel/plugin-transform-unicode-sets-regex@7.28.6", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q=="], + "@babel/plugin-transform-unicode-sets-regex": ["@babel/plugin-transform-unicode-sets-regex@7.29.7", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg=="], - "@babel/preset-env": ["@babel/preset-env@7.29.2", "", { "dependencies": { "@babel/compat-data": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-import-assertions": "^7.28.6", "@babel/plugin-syntax-import-attributes": "^7.28.6", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.27.1", "@babel/plugin-transform-async-generator-functions": "^7.29.0", "@babel/plugin-transform-async-to-generator": "^7.28.6", "@babel/plugin-transform-block-scoped-functions": "^7.27.1", "@babel/plugin-transform-block-scoping": "^7.28.6", "@babel/plugin-transform-class-properties": "^7.28.6", "@babel/plugin-transform-class-static-block": "^7.28.6", "@babel/plugin-transform-classes": "^7.28.6", "@babel/plugin-transform-computed-properties": "^7.28.6", "@babel/plugin-transform-destructuring": "^7.28.5", "@babel/plugin-transform-dotall-regex": "^7.28.6", "@babel/plugin-transform-duplicate-keys": "^7.27.1", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.0", "@babel/plugin-transform-dynamic-import": "^7.27.1", "@babel/plugin-transform-explicit-resource-management": "^7.28.6", "@babel/plugin-transform-exponentiation-operator": "^7.28.6", "@babel/plugin-transform-export-namespace-from": "^7.27.1", "@babel/plugin-transform-for-of": "^7.27.1", "@babel/plugin-transform-function-name": "^7.27.1", "@babel/plugin-transform-json-strings": "^7.28.6", "@babel/plugin-transform-literals": "^7.27.1", "@babel/plugin-transform-logical-assignment-operators": "^7.28.6", "@babel/plugin-transform-member-expression-literals": "^7.27.1", "@babel/plugin-transform-modules-amd": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.28.6", "@babel/plugin-transform-modules-systemjs": "^7.29.0", "@babel/plugin-transform-modules-umd": "^7.27.1", "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.0", "@babel/plugin-transform-new-target": "^7.27.1", "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6", "@babel/plugin-transform-numeric-separator": "^7.28.6", "@babel/plugin-transform-object-rest-spread": "^7.28.6", "@babel/plugin-transform-object-super": "^7.27.1", "@babel/plugin-transform-optional-catch-binding": "^7.28.6", "@babel/plugin-transform-optional-chaining": "^7.28.6", "@babel/plugin-transform-parameters": "^7.27.7", "@babel/plugin-transform-private-methods": "^7.28.6", "@babel/plugin-transform-private-property-in-object": "^7.28.6", "@babel/plugin-transform-property-literals": "^7.27.1", "@babel/plugin-transform-regenerator": "^7.29.0", "@babel/plugin-transform-regexp-modifiers": "^7.28.6", "@babel/plugin-transform-reserved-words": "^7.27.1", "@babel/plugin-transform-shorthand-properties": "^7.27.1", "@babel/plugin-transform-spread": "^7.28.6", "@babel/plugin-transform-sticky-regex": "^7.27.1", "@babel/plugin-transform-template-literals": "^7.27.1", "@babel/plugin-transform-typeof-symbol": "^7.27.1", "@babel/plugin-transform-unicode-escapes": "^7.27.1", "@babel/plugin-transform-unicode-property-regex": "^7.28.6", "@babel/plugin-transform-unicode-regex": "^7.27.1", "@babel/plugin-transform-unicode-sets-regex": "^7.28.6", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.15", "babel-plugin-polyfill-corejs3": "^0.14.0", "babel-plugin-polyfill-regenerator": "^0.6.6", "core-js-compat": "^3.48.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw=="], + "@babel/preset-env": ["@babel/preset-env@7.29.7", "", { "dependencies": { "@babel/compat-data": "^7.29.7", "@babel/helper-compilation-targets": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.29.7", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.29.7", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.29.7", "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^7.29.7", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.29.7", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.29.7", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-import-assertions": "^7.29.7", "@babel/plugin-syntax-import-attributes": "^7.29.7", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.29.7", "@babel/plugin-transform-async-generator-functions": "^7.29.7", "@babel/plugin-transform-async-to-generator": "^7.29.7", "@babel/plugin-transform-block-scoped-functions": "^7.29.7", "@babel/plugin-transform-block-scoping": "^7.29.7", "@babel/plugin-transform-class-properties": "^7.29.7", "@babel/plugin-transform-class-static-block": "^7.29.7", "@babel/plugin-transform-classes": "^7.29.7", "@babel/plugin-transform-computed-properties": "^7.29.7", "@babel/plugin-transform-destructuring": "^7.29.7", "@babel/plugin-transform-dotall-regex": "^7.29.7", "@babel/plugin-transform-duplicate-keys": "^7.29.7", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.7", "@babel/plugin-transform-dynamic-import": "^7.29.7", "@babel/plugin-transform-explicit-resource-management": "^7.29.7", "@babel/plugin-transform-exponentiation-operator": "^7.29.7", "@babel/plugin-transform-export-namespace-from": "^7.29.7", "@babel/plugin-transform-for-of": "^7.29.7", "@babel/plugin-transform-function-name": "^7.29.7", "@babel/plugin-transform-json-strings": "^7.29.7", "@babel/plugin-transform-literals": "^7.29.7", "@babel/plugin-transform-logical-assignment-operators": "^7.29.7", "@babel/plugin-transform-member-expression-literals": "^7.29.7", "@babel/plugin-transform-modules-amd": "^7.29.7", "@babel/plugin-transform-modules-commonjs": "^7.29.7", "@babel/plugin-transform-modules-systemjs": "^7.29.7", "@babel/plugin-transform-modules-umd": "^7.29.7", "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.7", "@babel/plugin-transform-new-target": "^7.29.7", "@babel/plugin-transform-nullish-coalescing-operator": "^7.29.7", "@babel/plugin-transform-numeric-separator": "^7.29.7", "@babel/plugin-transform-object-rest-spread": "^7.29.7", "@babel/plugin-transform-object-super": "^7.29.7", "@babel/plugin-transform-optional-catch-binding": "^7.29.7", "@babel/plugin-transform-optional-chaining": "^7.29.7", "@babel/plugin-transform-parameters": "^7.29.7", "@babel/plugin-transform-private-methods": "^7.29.7", "@babel/plugin-transform-private-property-in-object": "^7.29.7", "@babel/plugin-transform-property-literals": "^7.29.7", "@babel/plugin-transform-regenerator": "^7.29.7", "@babel/plugin-transform-regexp-modifiers": "^7.29.7", "@babel/plugin-transform-reserved-words": "^7.29.7", "@babel/plugin-transform-shorthand-properties": "^7.29.7", "@babel/plugin-transform-spread": "^7.29.7", "@babel/plugin-transform-sticky-regex": "^7.29.7", "@babel/plugin-transform-template-literals": "^7.29.7", "@babel/plugin-transform-typeof-symbol": "^7.29.7", "@babel/plugin-transform-unicode-escapes": "^7.29.7", "@babel/plugin-transform-unicode-property-regex": "^7.29.7", "@babel/plugin-transform-unicode-regex": "^7.29.7", "@babel/plugin-transform-unicode-sets-regex": "^7.29.7", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.15", "babel-plugin-polyfill-corejs3": "^0.14.0", "babel-plugin-polyfill-regenerator": "^0.6.6", "core-js-compat": "^3.48.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA=="], "@babel/preset-modules": ["@babel/preset-modules@0.1.6-no-external-plugins", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/types": "^7.4.4", "esutils": "^2.0.2" }, "peerDependencies": { "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA=="], - "@babel/preset-react": ["@babel/preset-react@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-transform-react-display-name": "^7.28.0", "@babel/plugin-transform-react-jsx": "^7.27.1", "@babel/plugin-transform-react-jsx-development": "^7.27.1", "@babel/plugin-transform-react-pure-annotations": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ=="], + "@babel/preset-react": ["@babel/preset-react@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", "@babel/plugin-transform-react-display-name": "^7.29.7", "@babel/plugin-transform-react-jsx": "^7.29.7", "@babel/plugin-transform-react-jsx-development": "^7.29.7", "@babel/plugin-transform-react-pure-annotations": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-C+PV1TFUPTmBQGoPBL8j2QmLpZ117YTCwxIZeJOM96GbYMFSc7/pOXU5lVykwnZxyTqQxRsvoRk6f2FktZgGHA=="], - "@babel/preset-typescript": ["@babel/preset-typescript@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g=="], + "@babel/preset-typescript": ["@babel/preset-typescript@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", "@babel/plugin-syntax-jsx": "^7.29.7", "@babel/plugin-transform-modules-commonjs": "^7.29.7", "@babel/plugin-transform-typescript": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ=="], - "@babel/runtime": ["@babel/runtime@7.29.2", "", {}, "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g=="], + "@babel/runtime": ["@babel/runtime@7.29.7", "", {}, "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw=="], - "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="], + "@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], - "@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="], + "@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="], - "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], + "@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], "@biomejs/biome": ["@biomejs/biome@2.4.16", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.16", "@biomejs/cli-darwin-x64": "2.4.16", "@biomejs/cli-linux-arm64": "2.4.16", "@biomejs/cli-linux-arm64-musl": "2.4.16", "@biomejs/cli-linux-x64": "2.4.16", "@biomejs/cli-linux-x64-musl": "2.4.16", "@biomejs/cli-win32-arm64": "2.4.16", "@biomejs/cli-win32-x64": "2.4.16" }, "bin": { "biome": "bin/biome" } }, "sha512-x9ajFh1zChVybCiM3TN6OD4phAqLgtPZjFrZF+aTMYCPjwBO+k529TX7PPsAqtGNLeV4UgzwQnowEgS7bGmzcA=="], @@ -446,11 +448,11 @@ "@discoveryjs/json-ext": ["@discoveryjs/json-ext@0.5.7", "", {}, "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw=="], - "@docsearch/core": ["@docsearch/core@4.6.2", "", { "peerDependencies": { "@types/react": ">= 16.8.0 < 20.0.0", "react": ">= 16.8.0 < 20.0.0", "react-dom": ">= 16.8.0 < 20.0.0" }, "optionalPeers": ["@types/react", "react", "react-dom"] }, "sha512-/S0e6Dj7Zcm8m9Rru49YEX49dhU11be68c+S/BCyN8zQsTTgkKzXlhRbVL5mV6lOLC2+ZRRryaTdcm070Ug2oA=="], + "@docsearch/core": ["@docsearch/core@4.6.3", "", { "peerDependencies": { "@types/react": ">= 16.8.0 < 20.0.0", "react": ">= 16.8.0 < 20.0.0", "react-dom": ">= 16.8.0 < 20.0.0" }, "optionalPeers": ["@types/react", "react", "react-dom"] }, "sha512-rUOujwIpxJRgD7+kicVsI3D5sqBvdiRTquzWBpTEXZs8ZXfGbfzpus5HqumaNYTppN2HvH8E2yNuRwYdHJeOlA=="], - "@docsearch/css": ["@docsearch/css@4.6.2", "", {}, "sha512-fH/cn8BjEEdM2nJdjNMHIvOVYupG6AIDtFVDgIZrNzdCSj4KXr9kd+hsehqsNGYjpUjObeKYKvgy/IwCb1jZYQ=="], + "@docsearch/css": ["@docsearch/css@4.6.3", "", {}, "sha512-nlOwcXcsNAptQl4vlL4MA78qNJKO0Qlds5GuBjCoePgkebTXLSf8Qt1oyZ3YBshYupKXG9VRGEsk1zr23d+bzQ=="], - "@docsearch/react": ["@docsearch/react@4.6.2", "", { "dependencies": { "@algolia/autocomplete-core": "1.19.2", "@docsearch/core": "4.6.2", "@docsearch/css": "4.6.2" }, "peerDependencies": { "@types/react": ">= 16.8.0 < 20.0.0", "react": ">= 16.8.0 < 20.0.0", "react-dom": ">= 16.8.0 < 20.0.0", "search-insights": ">= 1 < 3" }, "optionalPeers": ["@types/react", "react", "react-dom", "search-insights"] }, "sha512-/BbtGFtqVOGwZx0dw/UfhN/0/DmMQYnulY4iv0tPRhC2JCXv0ka/+izwt3Jzo1ZxXS/2eMvv9zHsBJOK1I9f/w=="], + "@docsearch/react": ["@docsearch/react@4.6.3", "", { "dependencies": { "@algolia/autocomplete-core": "1.19.2", "@docsearch/core": "4.6.3", "@docsearch/css": "4.6.3" }, "peerDependencies": { "@types/react": ">= 16.8.0 < 20.0.0", "react": ">= 16.8.0 < 20.0.0", "react-dom": ">= 16.8.0 < 20.0.0", "search-insights": ">= 1 < 3" }, "optionalPeers": ["@types/react", "react", "react-dom", "search-insights"] }, "sha512-Bg2wdDsoQVlNCcEKuEJAU04tvHCqgx8rIu+uIoM4pRtcx3TBKJuXutJik3LTA8LRc9YEyHkrYUrmcC0D7BYf+g=="], "@docusaurus/babel": ["@docusaurus/babel@3.10.1", "", { "dependencies": { "@babel/core": "^7.25.9", "@babel/generator": "^7.25.9", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-transform-runtime": "^7.25.9", "@babel/preset-env": "^7.25.9", "@babel/preset-react": "^7.25.9", "@babel/preset-typescript": "^7.25.9", "@babel/runtime": "^7.25.9", "@babel/traverse": "^7.25.9", "@docusaurus/logger": "3.10.1", "@docusaurus/utils": "3.10.1", "babel-plugin-dynamic-import-node": "^2.3.3", "fs-extra": "^11.1.1", "tslib": "^2.6.0" } }, "sha512-DZzFO1K3v/GoEt1fx1DiYHF4en+PuhtQf1AkQJa5zu3CoeKSpr5cpQRUlz3jr0m44wyzmSXu9bVpfir+N4+8bg=="], @@ -608,21 +610,21 @@ "@jsonjoy.com/codegen": ["@jsonjoy.com/codegen@1.0.0", "", { "peerDependencies": { "tslib": "2" } }, "sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g=="], - "@jsonjoy.com/fs-core": ["@jsonjoy.com/fs-core@4.57.1", "", { "dependencies": { "@jsonjoy.com/fs-node-builtins": "4.57.1", "@jsonjoy.com/fs-node-utils": "4.57.1", "thingies": "^2.5.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-YrEi/ZPmgc+GfdO0esBF04qv8boK9Dg9WpRQw/+vM8Qt3nnVIJWIa8HwZ/LXVZ0DB11XUROM8El/7yYTJX+WtA=="], + "@jsonjoy.com/fs-core": ["@jsonjoy.com/fs-core@4.57.6", "", { "dependencies": { "@jsonjoy.com/fs-node-builtins": "4.57.6", "@jsonjoy.com/fs-node-utils": "4.57.6", "thingies": "^2.5.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-uI++Wx6VkBJqVmkb4ZeExwAVpZiA2Do5NrEtXoDk0Pdvce3ytFXJoviT1sLOj16+qDIMnD5nWPfOhVpnDmRJKg=="], - "@jsonjoy.com/fs-fsa": ["@jsonjoy.com/fs-fsa@4.57.1", "", { "dependencies": { "@jsonjoy.com/fs-core": "4.57.1", "@jsonjoy.com/fs-node-builtins": "4.57.1", "@jsonjoy.com/fs-node-utils": "4.57.1", "thingies": "^2.5.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-ooEPvSW/HQDivPDPZMibHGKZf/QS4WRir1czGZmXmp3MsQqLECZEpN0JobrD8iV9BzsuwdIv+PxtWX9WpPLsIA=="], + "@jsonjoy.com/fs-fsa": ["@jsonjoy.com/fs-fsa@4.57.6", "", { "dependencies": { "@jsonjoy.com/fs-core": "4.57.6", "@jsonjoy.com/fs-node-builtins": "4.57.6", "@jsonjoy.com/fs-node-utils": "4.57.6", "thingies": "^2.5.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-pKkw/yC5CzSZKhIIUIsH1przOa+K5jGmZIg1sWaSF24JojyrUFbjcQv7QrcGAudriei6HQ6R0BFj+V8NbQinJw=="], - "@jsonjoy.com/fs-node": ["@jsonjoy.com/fs-node@4.57.1", "", { "dependencies": { "@jsonjoy.com/fs-core": "4.57.1", "@jsonjoy.com/fs-node-builtins": "4.57.1", "@jsonjoy.com/fs-node-utils": "4.57.1", "@jsonjoy.com/fs-print": "4.57.1", "@jsonjoy.com/fs-snapshot": "4.57.1", "glob-to-regex.js": "^1.0.0", "thingies": "^2.5.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-3YaKhP8gXEKN+2O49GLNfNb5l2gbnCFHyAaybbA2JkkbQP3dpdef7WcUaHAulg/c5Dg4VncHsA3NWAUSZMR5KQ=="], + "@jsonjoy.com/fs-node": ["@jsonjoy.com/fs-node@4.57.6", "", { "dependencies": { "@jsonjoy.com/fs-core": "4.57.6", "@jsonjoy.com/fs-node-builtins": "4.57.6", "@jsonjoy.com/fs-node-utils": "4.57.6", "@jsonjoy.com/fs-print": "4.57.6", "@jsonjoy.com/fs-snapshot": "4.57.6", "glob-to-regex.js": "^1.0.0", "thingies": "^2.5.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-Kbn1jdkvDN4F2+BhoB6mMu7NCbhP0bgA5NcI1aJj/Q5UcU+I1JLLW+dEQean33iV4tXv35AzBVKPICnDltBpxw=="], - "@jsonjoy.com/fs-node-builtins": ["@jsonjoy.com/fs-node-builtins@4.57.1", "", { "peerDependencies": { "tslib": "2" } }, "sha512-XHkFKQ5GSH3uxm8c3ZYXVrexGdscpWKIcMWKFQpMpMJc8gA3AwOMBJXJlgpdJqmrhPyQXxaY9nbkNeYpacC0Og=="], + "@jsonjoy.com/fs-node-builtins": ["@jsonjoy.com/fs-node-builtins@4.57.6", "", { "peerDependencies": { "tslib": "2" } }, "sha512-V4DgEFT3Cg5S9fCMOZSCVdTxdJWWLBO0WnAazV7hnCM96u5zXHyW/ubDAfcSVwqjkMJ50W1Y44IXtxRoIwaCVg=="], - "@jsonjoy.com/fs-node-to-fsa": ["@jsonjoy.com/fs-node-to-fsa@4.57.1", "", { "dependencies": { "@jsonjoy.com/fs-fsa": "4.57.1", "@jsonjoy.com/fs-node-builtins": "4.57.1", "@jsonjoy.com/fs-node-utils": "4.57.1" }, "peerDependencies": { "tslib": "2" } }, "sha512-pqGHyWWzNck4jRfaGV39hkqpY5QjRUQ/nRbNT7FYbBa0xf4bDG+TE1Gt2KWZrSkrkZZDE3qZUjYMbjwSliX6pg=="], + "@jsonjoy.com/fs-node-to-fsa": ["@jsonjoy.com/fs-node-to-fsa@4.57.6", "", { "dependencies": { "@jsonjoy.com/fs-fsa": "4.57.6", "@jsonjoy.com/fs-node-builtins": "4.57.6", "@jsonjoy.com/fs-node-utils": "4.57.6" }, "peerDependencies": { "tslib": "2" } }, "sha512-+JptNw3iifihxH2rEXrninDzX4FFVW8JD/wPR8GbJPAeL9CQUSblrlumOPB5gZuS7tYRX+PJPLtT7XzKoRhv/Q=="], - "@jsonjoy.com/fs-node-utils": ["@jsonjoy.com/fs-node-utils@4.57.1", "", { "dependencies": { "@jsonjoy.com/fs-node-builtins": "4.57.1" }, "peerDependencies": { "tslib": "2" } }, "sha512-vp+7ZzIB8v43G+GLXTS4oDUSQmhAsRz532QmmWBbdYA20s465JvwhkSFvX9cVTqRRAQg+vZ7zWDaIEh0lFe2gw=="], + "@jsonjoy.com/fs-node-utils": ["@jsonjoy.com/fs-node-utils@4.57.6", "", { "dependencies": { "@jsonjoy.com/fs-node-builtins": "4.57.6" }, "peerDependencies": { "tslib": "2" } }, "sha512-foyUrfS7WmYEUzqYXSNxmJBcSj04TABrkpFabwO9SCDCpVCfJ+qG+2sk5FjfiflG2n0SDFZDCJ6vYlJAEpxJFg=="], - "@jsonjoy.com/fs-print": ["@jsonjoy.com/fs-print@4.57.1", "", { "dependencies": { "@jsonjoy.com/fs-node-utils": "4.57.1", "tree-dump": "^1.1.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-Ynct7ZJmfk6qoXDOKfpovNA36ITUx8rChLmRQtW08J73VOiuNsU8PB6d/Xs7fxJC2ohWR3a5AqyjmLojfrw5yw=="], + "@jsonjoy.com/fs-print": ["@jsonjoy.com/fs-print@4.57.6", "", { "dependencies": { "@jsonjoy.com/fs-node-utils": "4.57.6", "tree-dump": "^1.1.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-96eAn4Dudtt67LTeuU47yUD+pg9/G/oKpI10zei9ljk3X3WK4lYKc+n3cpaPCAbKPzoyfxl0mXm8f8Y7BOSFXw=="], - "@jsonjoy.com/fs-snapshot": ["@jsonjoy.com/fs-snapshot@4.57.1", "", { "dependencies": { "@jsonjoy.com/buffers": "^17.65.0", "@jsonjoy.com/fs-node-utils": "4.57.1", "@jsonjoy.com/json-pack": "^17.65.0", "@jsonjoy.com/util": "^17.65.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-/oG8xBNFMbDXTq9J7vepSA1kerS5vpgd3p5QZSPd+nX59uwodGJftI51gDYyHRpP57P3WCQf7LHtBYPqwUg2Bg=="], + "@jsonjoy.com/fs-snapshot": ["@jsonjoy.com/fs-snapshot@4.57.6", "", { "dependencies": { "@jsonjoy.com/buffers": "^17.65.0", "@jsonjoy.com/fs-node-utils": "4.57.6", "@jsonjoy.com/json-pack": "^17.65.0", "@jsonjoy.com/util": "^17.65.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-V57CMzbOgTzUWGOWQ8GzHQdpJP6JnrYVNCtTBNxVYEnlVRvo4uEJqHhtAT8vhDFrIuJOXLrTL1Fki4h5oI7xxg=="], "@jsonjoy.com/json-pack": ["@jsonjoy.com/json-pack@1.21.0", "", { "dependencies": { "@jsonjoy.com/base64": "^1.1.2", "@jsonjoy.com/buffers": "^1.2.0", "@jsonjoy.com/codegen": "^1.0.0", "@jsonjoy.com/json-pointer": "^1.0.2", "@jsonjoy.com/util": "^1.9.0", "hyperdyperid": "^1.2.0", "thingies": "^2.5.0", "tree-dump": "^1.1.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg=="], @@ -648,17 +650,17 @@ "@module-federation/webpack-bundler-runtime": ["@module-federation/webpack-bundler-runtime@0.22.0", "", { "dependencies": { "@module-federation/runtime": "0.22.0", "@module-federation/sdk": "0.22.0" } }, "sha512-aM8gCqXu+/4wBmJtVeMeeMN5guw3chf+2i6HajKtQv7SJfxV/f4IyNQJUeUQu9HfiAZHjqtMV5Lvq/Lvh8LdyA=="], - "@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="], + "@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ=="], - "@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw=="], + "@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w=="], - "@msgpackr-extract/msgpackr-extract-linux-arm": ["@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3", "", { "os": "linux", "cpu": "arm" }, "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw=="], + "@msgpackr-extract/msgpackr-extract-linux-arm": ["@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4", "", { "os": "linux", "cpu": "arm" }, "sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw=="], - "@msgpackr-extract/msgpackr-extract-linux-arm64": ["@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg=="], + "@msgpackr-extract/msgpackr-extract-linux-arm64": ["@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw=="], - "@msgpackr-extract/msgpackr-extract-linux-x64": ["@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg=="], + "@msgpackr-extract/msgpackr-extract-linux-x64": ["@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ=="], - "@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ=="], + "@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4", "", { "os": "win32", "cpu": "x64" }, "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ=="], "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], @@ -672,25 +674,27 @@ "@oxc-project/types": ["@oxc-project/types@0.133.0", "", {}, "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA=="], - "@peculiar/asn1-cms": ["@peculiar/asn1-cms@2.6.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.1", "@peculiar/asn1-x509-attr": "^2.6.1", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-vdG4fBF6Lkirkcl53q6eOdn3XYKt+kJTG59edgRZORlg/3atWWEReRCx5rYE1ZzTTX6vLK5zDMjHh7vbrcXGtw=="], + "@peculiar/asn1-cms": ["@peculiar/asn1-cms@2.7.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.7.0", "@peculiar/asn1-x509": "^2.7.0", "@peculiar/asn1-x509-attr": "^2.7.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-hew63shtzzvBcSHbhm+cyAmKe6AIfinT9hzEqSPjDC6opTTMKmTkQ0gHuN2KsWlvqiKw1S/fS94fhag/FJkioQ=="], - "@peculiar/asn1-csr": ["@peculiar/asn1-csr@2.6.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.1", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-WRWnKfIocHyzFYQTka8O/tXCiBquAPSrRjXbOkHbO4qdmS6loffCEGs+rby6WxxGdJCuunnhS2duHURhjyio6w=="], + "@peculiar/asn1-csr": ["@peculiar/asn1-csr@2.7.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.7.0", "@peculiar/asn1-x509": "^2.7.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-VVsAyGqErT9D1SY4aEqozThXMVI+ssVRiv2DDeYuvpBKLIgZ3hYs3Ay3u/VSoKq6ESFi9cf6rf3IOOzfwh7oMA=="], - "@peculiar/asn1-ecc": ["@peculiar/asn1-ecc@2.6.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.1", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-+Vqw8WFxrtDIN5ehUdvlN2m73exS2JVG0UAyfVB31gIfor3zWEAQPD+K9ydCxaj3MLen9k0JhKpu9LqviuCE1g=="], + "@peculiar/asn1-ecc": ["@peculiar/asn1-ecc@2.7.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.7.0", "@peculiar/asn1-x509": "^2.7.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-n7KEs/Q/wrB415cxy4fHOBhegp4NdJ15fkJPwcB/3/8iNBQC2L/N7SChJPKDJPZGYH0jD4Tg4/0vnHmwghnbKw=="], - "@peculiar/asn1-pfx": ["@peculiar/asn1-pfx@2.6.1", "", { "dependencies": { "@peculiar/asn1-cms": "^2.6.1", "@peculiar/asn1-pkcs8": "^2.6.1", "@peculiar/asn1-rsa": "^2.6.1", "@peculiar/asn1-schema": "^2.6.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-nB5jVQy3MAAWvq0KY0R2JUZG8bO/bTLpnwyOzXyEh/e54ynGTatAR+csOnXkkVD9AFZ2uL8Z7EV918+qB1qDvw=="], + "@peculiar/asn1-pfx": ["@peculiar/asn1-pfx@2.7.0", "", { "dependencies": { "@peculiar/asn1-cms": "^2.7.0", "@peculiar/asn1-pkcs8": "^2.7.0", "@peculiar/asn1-rsa": "^2.7.0", "@peculiar/asn1-schema": "^2.7.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-V/nrlQVmhg7lYAsM7E13UDL5erAwFv6kCIVFqNaMIHSVi7dngcT839JkRTkQBqznMG98l2XjxYk74ZztAohZzA=="], - "@peculiar/asn1-pkcs8": ["@peculiar/asn1-pkcs8@2.6.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.1", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-JB5iQ9Izn5yGMw3ZG4Nw3Xn/hb/G38GYF3lf7WmJb8JZUydhVGEjK/ZlFSWhnlB7K/4oqEs8HnfFIKklhR58Tw=="], + "@peculiar/asn1-pkcs8": ["@peculiar/asn1-pkcs8@2.7.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.7.0", "@peculiar/asn1-x509": "^2.7.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-9GTl1nE8Mx1kTZ+7QyYatDyKsm34QcWRBFkY1iPvWC3X4Dona5s/tlLiQsx5WzVdZqiMBZNYT0buyw4/vbhnjw=="], - "@peculiar/asn1-pkcs9": ["@peculiar/asn1-pkcs9@2.6.1", "", { "dependencies": { "@peculiar/asn1-cms": "^2.6.1", "@peculiar/asn1-pfx": "^2.6.1", "@peculiar/asn1-pkcs8": "^2.6.1", "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.1", "@peculiar/asn1-x509-attr": "^2.6.1", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-5EV8nZoMSxeWmcxWmmcolg22ojZRgJg+Y9MX2fnE2bGRo5KQLqV5IL9kdSQDZxlHz95tHvIq9F//bvL1OeNILw=="], + "@peculiar/asn1-pkcs9": ["@peculiar/asn1-pkcs9@2.7.0", "", { "dependencies": { "@peculiar/asn1-cms": "^2.7.0", "@peculiar/asn1-pfx": "^2.7.0", "@peculiar/asn1-pkcs8": "^2.7.0", "@peculiar/asn1-schema": "^2.7.0", "@peculiar/asn1-x509": "^2.7.0", "@peculiar/asn1-x509-attr": "^2.7.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-Bh7m+OuIaSEllPQcSd9OSp93F4ROWH7sbITWV8MI+8dwsjE5111/87VxiWVvYFKyww3vp39geLv9ENqhwWHcew=="], - "@peculiar/asn1-rsa": ["@peculiar/asn1-rsa@2.6.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.1", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-1nVMEh46SElUt5CB3RUTV4EG/z7iYc7EoaDY5ECwganibQPkZ/Y2eMsTKB/LeyrUJ+W/tKoD9WUqIy8vB+CEdA=="], + "@peculiar/asn1-rsa": ["@peculiar/asn1-rsa@2.7.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.7.0", "@peculiar/asn1-x509": "^2.7.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-/qvENQrXyTZURjMqSeofHul0JJt2sNSzSwk36pl2olkHbaioMQgrASDZAlHXl0xUlnVbHj0uGgOrBMTb5x2aJQ=="], - "@peculiar/asn1-schema": ["@peculiar/asn1-schema@2.6.0", "", { "dependencies": { "asn1js": "^3.0.6", "pvtsutils": "^1.3.6", "tslib": "^2.8.1" } }, "sha512-xNLYLBFTBKkCzEZIw842BxytQQATQv+lDTCEMZ8C196iJcJJMBUZxrhSTxLaohMyKK8QlzRNTRkUmanucnDSqg=="], + "@peculiar/asn1-schema": ["@peculiar/asn1-schema@2.7.0", "", { "dependencies": { "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-W8ZfWzLmQnrcky+eh3tni4IozMdqBDiHWU0N+vve/UGjMaUs8c0L7A2oEdkBXS8rTpWDpK/aoI3DG/L/hxmxPg=="], - "@peculiar/asn1-x509": ["@peculiar/asn1-x509@2.6.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.6.0", "asn1js": "^3.0.6", "pvtsutils": "^1.3.6", "tslib": "^2.8.1" } }, "sha512-O9jT5F1A2+t3r7C4VT7LYGXqkGLK7Kj1xFpz7U0isPrubwU5PbDoyYtx6MiGst29yq7pXN5vZbQFKRCP+lLZlA=="], + "@peculiar/asn1-x509": ["@peculiar/asn1-x509@2.7.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.7.0", "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-mUn9RRrkGDnG4ALfunDmzyRW5dg+sWCj/pfnCCqEHYbkGxEpvUt6iVJv8Yw1cyp6SWZ26ZE5oSmI5SqEaen15g=="], - "@peculiar/asn1-x509-attr": ["@peculiar/asn1-x509-attr@2.6.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.1", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-tlW6cxoHwgcQghnJwv3YS+9OO1737zgPogZ+CgWRUK4roEwIPzRH4JEiG770xe5HX2ATfCpmX60gurfWIF9dcQ=="], + "@peculiar/asn1-x509-attr": ["@peculiar/asn1-x509-attr@2.7.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.7.0", "@peculiar/asn1-x509": "^2.7.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-NS8e7SOgXipkzUPLF/sce7ukpMpWjhxYsH0n6Y+bHYo4TTxOb95Zv7hqwSuL212mj5YxovjdOKQOgH1As3E94w=="], + + "@peculiar/utils": ["@peculiar/utils@2.0.3", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ=="], "@peculiar/x509": ["@peculiar/x509@1.14.3", "", { "dependencies": { "@peculiar/asn1-cms": "^2.6.0", "@peculiar/asn1-csr": "^2.6.0", "@peculiar/asn1-ecc": "^2.6.0", "@peculiar/asn1-pkcs9": "^2.6.0", "@peculiar/asn1-rsa": "^2.6.0", "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.0", "pvtsutils": "^1.3.6", "reflect-metadata": "^0.2.2", "tslib": "^2.8.1", "tsyringe": "^4.10.0" } }, "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA=="], @@ -704,125 +708,125 @@ "@radix-ui/colors": ["@radix-ui/colors@3.0.0", "", {}, "sha512-FUOsGBkHrYJwCSEtWRCIfQbZG7q1e6DgxCIOe1SUQzDe/7rXXeA47s8yCn6fuTNQAj1Zq4oTFi9Yjp3wzElcxg=="], - "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="], + "@radix-ui/number": ["@radix-ui/number@1.1.2", "", {}, "sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig=="], - "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + "@radix-ui/primitive": ["@radix-ui/primitive@1.1.4", "", {}, "sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ=="], - "@radix-ui/react-accessible-icon": ["@radix-ui/react-accessible-icon@1.1.7", "", { "dependencies": { "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-XM+E4WXl0OqUJFovy6GjmxxFyx9opfCAIUku4dlKRd5YEPqt4kALOkQOp0Of6reHuUkJuiPBEc5k0o4z4lTC8A=="], + "@radix-ui/react-accessible-icon": ["@radix-ui/react-accessible-icon@1.1.9", "", { "dependencies": { "@radix-ui/react-visually-hidden": "1.2.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-5W9KzJz/3DeYbGJHbZv8Q6AkxMOKUmALfc+PRg9dWwJZMk6zD37Sz8sZrF7UD6CBkiJvn7dNeRzn5G7XiCMyig=="], - "@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.2.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA=="], + "@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.2.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-collapsible": "1.1.13", "@radix-ui/react-collection": "1.1.9", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-xITxBB2p5m5tAe7M0F95kb4uAh7jSIKGlExMEm93HlW+XxZHV2eXFbPWLktd4JhRiwcnXNbO7iekcrbZy6ZCvA=="], - "@radix-ui/react-alert-dialog": ["@radix-ui/react-alert-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dialog": "1.1.15", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw=="], + "@radix-ui/react-alert-dialog": ["@radix-ui/react-alert-dialog@1.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-dialog": "1.1.16", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-slot": "1.2.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-vPaIgo0mxYlvcFaM9jB2Uot9TjGXMuAPEvrc6BOLeV+I5U8s1dkIoouYaa6lmSfc5SPMo5x5djOTOTvaigdGMQ=="], - "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="], + "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-yqHW5WQ/cTpU/un7dqqIKNy2iRU8BC0JB78PEzTfCCYvZu1U6W9KwObAniMk9nhSfyotKPQTYaUD/HB0f5muig=="], - "@radix-ui/react-aspect-ratio": ["@radix-ui/react-aspect-ratio@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g=="], + "@radix-ui/react-aspect-ratio": ["@radix-ui/react-aspect-ratio@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Xy+Dpxt/5n9rVTdPrNFmf8GwG1NlT1pzCF/z1MgOGZMLZWdWl+km+ZRWGQAPEhbkzSwYEsfYmTca8NhUtVxqnw=="], - "@radix-ui/react-avatar": ["@radix-ui/react-avatar@1.1.10", "", { "dependencies": { "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog=="], + "@radix-ui/react-avatar": ["@radix-ui/react-avatar@1.1.12", "", { "dependencies": { "@radix-ui/react-context": "1.1.4", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-is-hydrated": "0.1.1", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-NQCQyWC7QrDPhjMn8hUqFeU0lUrprIgm1AyMgLbzuQJibNnatdc3SSMo3/UGFu/eUkJUU1cEcKCnyhXTQzq6tA=="], - "@radix-ui/react-checkbox": ["@radix-ui/react-checkbox@1.3.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw=="], + "@radix-ui/react-checkbox": ["@radix-ui/react-checkbox@1.3.4", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-previous": "1.1.2", "@radix-ui/react-use-size": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m3JmIOAX5ZzZ6VPjxEU2dbTOhoHi0nT5riwcDwe8idocsWf4a5DXJLDtZ6LfJwMBx7W+A2b7kp2TgPEKtaiF6A=="], - "@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA=="], + "@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F0s8+p2XNpfc3k02zBfB0jPWbkHVG162+p7BdUMyJ2308QMqZ+oaclX+FAzKFovgL5OqRU+Rvy6f/vbdlJVaqA=="], - "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], + "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.9", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-slot": "1.2.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zuSVi7ziP7uQRqc+yGxsKJfNkdyHv3ZKDaHe0gzg4dRgws96TPKWIiz84tVHP4GEcEl8bC0mdt17NkcxaJHmaQ=="], - "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="], - "@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + "@radix-ui/react-context": ["@radix-ui/react-context@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg=="], - "@radix-ui/react-context-menu": ["@radix-ui/react-context-menu@2.2.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww=="], + "@radix-ui/react-context-menu": ["@radix-ui/react-context-menu@2.3.0", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-menu": "2.1.17", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-d7CouXhAW+CGmFOqmB+IEvd3E9GcaqfgvfjCc3hfulp2pkaUCEVEGa0SN5nNWYA+IvQ6g1Pt+S5dpNn1AoY9hg=="], - "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw=="], + "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-dismissable-layer": "1.1.12", "@radix-ui/react-focus-guards": "1.1.4", "@radix-ui/react-focus-scope": "1.1.9", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-portal": "1.1.11", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-slot": "1.2.5", "@radix-ui/react-use-controllable-state": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-l9ok83YBclEZhbjgzt76Hw733e6cvRKPNgO6GJ/IETlufXG9p+fRu2wlvpImQvR6xdJ8h7J8J2DBvsPEiEsKMw=="], - "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], + "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA=="], - "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="], + "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-escape-keydown": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-MhoruH6xEzsbvOmo4TNgMfmtvRGyDZw4MDSdf4ybMHfezjqwzv6hyd4lsMzBp8K9Sn6sGzCF62x1I7BYUECXOg=="], - "@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw=="], + "@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.17", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-menu": "2.1.17", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-S6b3Jm57sY5EdDyOMLkacbB0qMnKhy1RCKZCt795ZkmtUOAvojYIZ5p7dXHIh5Cyr3jCLLI5/g64V3FKLudZmw=="], - "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="], + "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q=="], - "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="], + "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.9", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-callback-ref": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9Se8t+Zry+1rEOL7Y6l/4ANYU/TOtAtf8O2fKdwLltcaMcm6kOqYGbzO4tMFQ0bvzO920pRAoHpFZ4W85S3keQ=="], - "@radix-ui/react-form": ["@radix-ui/react-form@0.1.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-label": "2.1.7", "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-QM70k4Zwjttifr5a4sZFts9fn8FzHYvQ5PiB19O2HsYibaHSVt9fH9rzB0XZo/YcM+b7t/p7lYCT/F5eOeF5yQ=="], + "@radix-ui/react-form": ["@radix-ui/react-form@0.1.9", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-label": "2.1.9", "@radix-ui/react-primitive": "2.1.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-eTPyThIKDacJ3mJDvYwf/PSmsEYlOyA2Qcb+aGyWwYv+P5w57VPUkMVA2XJ9z0Du2KBY1HoHQzhPV9iYL/r4hg=="], - "@radix-ui/react-hover-card": ["@radix-ui/react-hover-card@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg=="], + "@radix-ui/react-hover-card": ["@radix-ui/react-hover-card@1.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-dismissable-layer": "1.1.12", "@radix-ui/react-popper": "1.3.0", "@radix-ui/react-portal": "1.1.11", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-hAileDBtd6CX7nlZOarOnISQ6PP4q0e16BX51ulzdZ+7IzjL0sDTVpFdmSYrIjw6zVNsfQBao5gG6AWr3qwfvA=="], - "@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + "@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="], - "@radix-ui/react-label": ["@radix-ui/react-label@2.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ=="], + "@radix-ui/react-label": ["@radix-ui/react-label@2.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-rDoTeMbCwRVcnmo7NGT9IlPo1yXmEI+xc1URP3oeewwZEV4mdTp1dYUhYbQdo4D1q2SjKVvv4N1gNY77QAQtjA=="], - "@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg=="], + "@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.17", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-collection": "1.1.9", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.12", "@radix-ui/react-focus-guards": "1.1.4", "@radix-ui/react-focus-scope": "1.1.9", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-popper": "1.3.0", "@radix-ui/react-portal": "1.1.11", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-roving-focus": "1.1.12", "@radix-ui/react-slot": "1.2.5", "@radix-ui/react-use-callback-ref": "1.1.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-fmbNnFyf+JYCN0DhhWnEdUTDnZD1mXaPQWivdsPIb8oOSbARfD3LIQJbLCG8a8QLCwoMxiJ7GVPIFcC8Dw8v2Q=="], - "@radix-ui/react-menubar": ["@radix-ui/react-menubar@1.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-EB1FktTz5xRRi2Er974AUQZWg2yVBb1yjip38/lgwtCVRd3a+maUoGHN/xs9Yv8SY8QwbSEb+YrxGadVWbEutA=="], + "@radix-ui/react-menubar": ["@radix-ui/react-menubar@1.1.17", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-collection": "1.1.9", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-menu": "2.1.17", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-roving-focus": "1.1.12", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-AKtZ4O782yO7qwIyq73WpulYt1IHhQ0htDb6wNcxzxnSDCcSWMVBiU9ycpcA90XzQO4IVIxIErtak6Kg/Vt0rQ=="], - "@radix-ui/react-navigation-menu": ["@radix-ui/react-navigation-menu@1.2.14", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w=="], + "@radix-ui/react-navigation-menu": ["@radix-ui/react-navigation-menu@1.2.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-collection": "1.1.9", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.12", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-use-previous": "1.1.2", "@radix-ui/react-visually-hidden": "1.2.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/fS8hKCcRt4DwCGa5QIB3juRXmfYSOk4a2AEe/BDIyy7Hm+eje2Y13oUx5zejl+wFt1owrM7E8NWlbaEl5EGpg=="], - "@radix-ui/react-one-time-password-field": ["@radix-ui/react-one-time-password-field@0.1.8", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ycS4rbwURavDPVjCb5iS3aG4lURFDILi6sKI/WITUMZ13gMmn/xGjpLoqBAalhJaDk8I3UbCM5GzKHrnzwHbvg=="], + "@radix-ui/react-one-time-password-field": ["@radix-ui/react-one-time-password-field@0.1.9", "", { "dependencies": { "@radix-ui/number": "1.1.2", "@radix-ui/primitive": "1.1.4", "@radix-ui/react-collection": "1.1.9", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-roving-focus": "1.1.12", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-effect-event": "0.0.3", "@radix-ui/react-use-is-hydrated": "0.1.1", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-fvCzA9hm7yN5xxTPJIi4VhSmH5gv+76ILsxguBK3cm3icD5BR4vW7POQmu8Zio0yh91uuouG/Kang40IbMkaSQ=="], - "@radix-ui/react-password-toggle-field": ["@radix-ui/react-password-toggle-field@0.1.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-is-hydrated": "0.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/UuCrDBWravcaMix4TdT+qlNdVwOM1Nck9kWx/vafXsdfj1ChfhOdfi3cy9SGBpWgTXwYCuboT/oYpJy3clqfw=="], + "@radix-ui/react-password-toggle-field": ["@radix-ui/react-password-toggle-field@0.1.4", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-effect-event": "0.0.3", "@radix-ui/react-use-is-hydrated": "0.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-qoDSkObZ9faJlsjlwyBH6ia7kq9vaJ2QwWTowT3nQpzPvUTAKesmWuGJYpd91HIoJqS+5ZPXy5uFPp+HlwdaAg=="], - "@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA=="], + "@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-dismissable-layer": "1.1.12", "@radix-ui/react-focus-guards": "1.1.4", "@radix-ui/react-focus-scope": "1.1.9", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-popper": "1.3.0", "@radix-ui/react-portal": "1.1.11", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-slot": "1.2.5", "@radix-ui/react-use-controllable-state": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-8brVpAU5Uq7Bh0c8EFc4ZTf2JJTYn0o+1L+CUJB3UYIOkTjKGMgoHvduylrahdmNlr3DfH0rFq2DrbNZXgaspw=="], - "@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.8", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw=="], + "@radix-ui/react-popper": ["@radix-ui/react-popper@1.3.0", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.9", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-use-rect": "1.1.2", "@radix-ui/react-use-size": "1.1.2", "@radix-ui/rect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9PB589e1aWZbrlFUHdz6WiPCL+xLZHQFX7oibqG/6Q0SwOkxDyQX9W/cyPa+sAPPKuC8cpLCpRczE5a/1DiwVQ=="], - "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="], + "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.11", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-UEytdjgEh2tJGgD/gZK4FUx6t1rNIlM3U0DENhSrG7I75FGm1DnaDuVUWF1pWAWUwGmn1sCJ1VGHn8LhN1aTOw=="], - "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="], + "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.6", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ=="], - "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.5", "", { "dependencies": { "@radix-ui/react-slot": "1.2.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zifXeB8Y88qCYx8PLZ5oQb32KwZub+s925mMoZsBBq9KUQqWKkREubTfs6ASjRPPBe7Jt9O8OHH89+95VG+grA=="], - "@radix-ui/react-progress": ["@radix-ui/react-progress@1.1.7", "", { "dependencies": { "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg=="], + "@radix-ui/react-progress": ["@radix-ui/react-progress@1.1.9", "", { "dependencies": { "@radix-ui/react-context": "1.1.4", "@radix-ui/react-primitive": "2.1.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-+EOkvg1Zn1vI1+fRDfRSAiJ7BWfcDAo5ASMmbqrcLZ4s4USk2FGkoHgeb2X+CkUgo2zJMiyObwf1k44CrRWsyw=="], - "@radix-ui/react-radio-group": ["@radix-ui/react-radio-group@1.3.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ=="], + "@radix-ui/react-radio-group": ["@radix-ui/react-radio-group@1.4.0", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-roving-focus": "1.1.12", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-previous": "1.1.2", "@radix-ui/react-use-size": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-eHdV5bLx9sH+tBnbDjkIBdvQEH/c6MEtQYhTbxkaDK9qsIFFLtmJYEQFVdwhnruWotLfQmIuWEL/J+L3utE8rQ=="], - "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="], + "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-collection": "1.1.9", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-FvgPt1bRmg8Xt2QpF7NUZW3dE0ZQHGm41dAdgT2J2GJPoIXz+9Em3NobAxf4fupcxhgHu03E5CRiU2MWvObXyg=="], - "@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.2.10", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A=="], + "@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.2.11", "", { "dependencies": { "@radix-ui/number": "1.1.2", "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-DS39ziOgea75U/TrXKU2/oKp0be2jrDHnzFLvahg/0iNAT1Zq16e4Uw0WXwyXvsK+mG3BRyMb7A3NRZMDuEXtQ=="], - "@radix-ui/react-select": ["@radix-ui/react-select@2.2.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ=="], + "@radix-ui/react-select": ["@radix-ui/react-select@2.3.0", "", { "dependencies": { "@radix-ui/number": "1.1.2", "@radix-ui/primitive": "1.1.4", "@radix-ui/react-collection": "1.1.9", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.12", "@radix-ui/react-focus-guards": "1.1.4", "@radix-ui/react-focus-scope": "1.1.9", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-popper": "1.3.0", "@radix-ui/react-portal": "1.1.11", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-slot": "1.2.5", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-use-previous": "1.1.2", "@radix-ui/react-visually-hidden": "1.2.5", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-mENc7WpJvJcW8hlMpzfFcHcEhTvYS5JMBmi9HVC1Q00uhBwML086MHYUV8QQdQv6lcu0Wg8dzd1RB8AFADcG/g=="], - "@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA=="], + "@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-gvgW+JV/Mbjj6darztTetnmElpQEzZrXpJvfj+dOxNAxiyHEAyUvEjjl4zxblvmjmKmi3jfPoy7ZdxzCuUBJSA=="], - "@radix-ui/react-slider": ["@radix-ui/react-slider@1.3.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw=="], + "@radix-ui/react-slider": ["@radix-ui/react-slider@1.4.0", "", { "dependencies": { "@radix-ui/number": "1.1.2", "@radix-ui/primitive": "1.1.4", "@radix-ui/react-collection": "1.1.9", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-use-previous": "1.1.2", "@radix-ui/react-use-size": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-RHcPlLOThRJM51DSIC33ZnpDEBYhyEFroVWkd2P54PGGjkmAt14RboYUU9E1MFst666zFHM0tGtWvMjSOtU1pw=="], - "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rCMO3QsIVKv5JTY5CVbo2MvO77SpEqqYc8AvRE7OWqRDOIqAKjsp+DrmnY9uc8NPdxB5E2z47HTYGeE2+NTptg=="], - "@radix-ui/react-switch": ["@radix-ui/react-switch@1.2.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ=="], + "@radix-ui/react-switch": ["@radix-ui/react-switch@1.3.0", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-previous": "1.1.2", "@radix-ui/react-use-size": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-GP1EZwhoZO/GGnhM1P5/2Vpm8iN8EnngyU0oezn2l78kN8tj25pyrvjIaT7azBhK615KSt+P2w39y57YV5jVkA=="], - "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A=="], + "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.14", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-roving-focus": "1.1.12", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-D5jwp9JNuwDeCw3CYD2Fz+sSHo0droQjC8u75dJHe4aWr5q6yBiXZU+hurXnKudRgEpUkD5TsI6bjHPo5ThUxA=="], - "@radix-ui/react-toast": ["@radix-ui/react-toast@1.2.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g=="], + "@radix-ui/react-toast": ["@radix-ui/react-toast@1.2.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-collection": "1.1.9", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-dismissable-layer": "1.1.12", "@radix-ui/react-portal": "1.1.11", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-visually-hidden": "1.2.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-WUymDDiN2DpoGudRN1aW4wF5O3BNQjZZO/5nngPoNiEVqjyOzirvZZNO0R6dC1ifucSINVaSv8JX1aq47VGgiA=="], - "@radix-ui/react-toggle": ["@radix-ui/react-toggle@1.1.10", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ=="], + "@radix-ui/react-toggle": ["@radix-ui/react-toggle@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-FikrKJemoBGZQ6uRID0HJqSPBP6D7OppdD2OhLl0ZYLlAyPXI7MezoYGmumwNkrAoRm35xXkb4C8JPfJZZzcaw=="], - "@radix-ui/react-toggle-group": ["@radix-ui/react-toggle-group@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-toggle": "1.1.10", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q=="], + "@radix-ui/react-toggle-group": ["@radix-ui/react-toggle-group@1.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-roving-focus": "1.1.12", "@radix-ui/react-toggle": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TEgECgJaWGAHJJZGzNNEYTNBdIXqX7LchANycpyP7DkfjmuiSN7ISt1k/ZRGVJgVJonsgP4vwaiKMn5utrcwWQ=="], - "@radix-ui/react-toolbar": ["@radix-ui/react-toolbar@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-separator": "1.1.7", "@radix-ui/react-toggle-group": "1.1.11" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-4ol06/1bLoFu1nwUqzdD4Y5RZ9oDdKeiHIsntug54Hcr1pgaHiPqHFEaXI1IFP/EsOfROQZ8Mig9VTIRza6Tjg=="], + "@radix-ui/react-toolbar": ["@radix-ui/react-toolbar@1.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-roving-focus": "1.1.12", "@radix-ui/react-separator": "1.1.9", "@radix-ui/react-toggle-group": "1.1.12" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-4wHtJVdIgqMmEwUvxA0BYg/2JMRbt0L3+8UD8Ml/nhKkfXtiZcM8u/S15gQ5xj9YEd/0qlrm5bE805LsjQ+J8A=="], - "@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.2.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg=="], + "@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.2.9", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-dismissable-layer": "1.1.12", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-popper": "1.3.0", "@radix-ui/react-portal": "1.1.11", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-slot": "1.2.5", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-visually-hidden": "1.2.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-u6F9MmTtBSLkiXNVDrtB/yPCZarM9smNswC24YYLV/M+bth6J3Gs3vlJezEoFwKZvPvxhCpUYdUnOsNG/0XOlA=="], - "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw=="], - "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.3", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.3", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA=="], - "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="], + "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.3", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA=="], - "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="], + "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.2", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2uVLvLjgO7NZCWw01/FdqRwmA42J0BcjPMUCA+koFEOAb+zjqIP7SiFz/7zWPrKnVmSqr76Omq2ALyCuX4dhLw=="], - "@radix-ui/react-use-is-hydrated": ["@radix-ui/react-use-is-hydrated@0.1.0", "", { "dependencies": { "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA=="], + "@radix-ui/react-use-is-hydrated": ["@radix-ui/react-use-is-hydrated@0.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-qwOiz4Tjo8CNnrOLAYUMXeZwDzXgXpvK4TKQPmWLECM9XoWvA6+0Z2/7Ag3A4ivjS4ovbLJPbskkxioFyBhr8A=="], - "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA=="], - "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="], + "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-IGBQPtRFdhN6MQ8dbegVmBq1LVZluya3F1jWY+puIcQC3MHctRwTDSBWCkL/3ZcnMJLTMJ++Z+ktmvg0F89iCw=="], - "@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.1", "", { "dependencies": { "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w=="], + "@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.2", "", { "dependencies": { "@radix-ui/rect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw=="], - "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="], + "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w=="], - "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="], + "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.5", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tPcHNI3FajdDBFpl/Ez1m2WL0ufJqBKyHxMDBvKitopamK36WwBGOMicuMEZKkM5Wce41QxUyv6BsiqfrWBiGg=="], - "@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="], + "@radix-ui/rect": ["@radix-ui/rect@1.1.2", "", {}, "sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA=="], "@radix-ui/themes": ["@radix-ui/themes@3.3.0", "", { "dependencies": { "@radix-ui/colors": "^3.0.0", "classnames": "^2.3.2", "radix-ui": "^1.1.3", "react-remove-scroll-bar": "^2.3.8" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-I0/h2CRNTpYNB7Mi3xFIvSsQq5a108d7kK8dTO5zp5b9HR5QJXKag6B8tjpz2ITkVYkFdkGk45doNkSr7OxwNw=="], @@ -976,59 +980,59 @@ "@svgr/webpack": ["@svgr/webpack@8.1.0", "", { "dependencies": { "@babel/core": "^7.21.3", "@babel/plugin-transform-react-constant-elements": "^7.21.3", "@babel/preset-env": "^7.20.2", "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.21.0", "@svgr/core": "8.1.0", "@svgr/plugin-jsx": "8.1.0", "@svgr/plugin-svgo": "8.1.0" } }, "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA=="], - "@swc/core": ["@swc/core@1.15.33", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.26" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.15.33", "@swc/core-darwin-x64": "1.15.33", "@swc/core-linux-arm-gnueabihf": "1.15.33", "@swc/core-linux-arm64-gnu": "1.15.33", "@swc/core-linux-arm64-musl": "1.15.33", "@swc/core-linux-ppc64-gnu": "1.15.33", "@swc/core-linux-s390x-gnu": "1.15.33", "@swc/core-linux-x64-gnu": "1.15.33", "@swc/core-linux-x64-musl": "1.15.33", "@swc/core-win32-arm64-msvc": "1.15.33", "@swc/core-win32-ia32-msvc": "1.15.33", "@swc/core-win32-x64-msvc": "1.15.33" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-jOlwnFV2xhuuZeAUILGFULeR6vDPfijEJ57evfocwznQldLU3w2cZ9bSDryY9ip+AsM3r1NJKzf47V2NXebkeQ=="], + "@swc/core": ["@swc/core@1.15.40", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.26" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.15.40", "@swc/core-darwin-x64": "1.15.40", "@swc/core-linux-arm-gnueabihf": "1.15.40", "@swc/core-linux-arm64-gnu": "1.15.40", "@swc/core-linux-arm64-musl": "1.15.40", "@swc/core-linux-ppc64-gnu": "1.15.40", "@swc/core-linux-s390x-gnu": "1.15.40", "@swc/core-linux-x64-gnu": "1.15.40", "@swc/core-linux-x64-musl": "1.15.40", "@swc/core-win32-arm64-msvc": "1.15.40", "@swc/core-win32-ia32-msvc": "1.15.40", "@swc/core-win32-x64-msvc": "1.15.40" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-2kwzJikRvgtNAG7MwVZY2vEzZjTxKIq5jXOihuSV/8U+Hej8Va22t65aKnJZs3P+NwojZvR8Mf8kyM7O+V8sQg=="], - "@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.15.33", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N+L0uXhuO7FIfzqwgxmzv0zIpV0qEp8wPX3QQs2p4atjMoywup2JTeDlXPw+z9pWJGCae3JjM+tZ6myclI+2gA=="], + "@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.15.40", "", { "os": "darwin", "cpu": "arm64" }, "sha512-PaYyclfmQ++77D8ityYvmmVzHv9aG8ROwt2GfG6/ccloy4Hgf80qtOnzb9VYvPsUT7Ty1uhuDRhv3XYpf62qhQ=="], - "@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.15.33", "", { "os": "darwin", "cpu": "x64" }, "sha512-/Il4QHSOhV4FekbsDtkrNmKbsX26oSysvgrRswa/RYOHXAkwXDbB4jaeKq6PsJLSPkzJ2KzQ061gtBnk0vNHfA=="], + "@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.15.40", "", { "os": "darwin", "cpu": "x64" }, "sha512-HbbPzvfLBUXjIB1Ezks+//lNUjmLjfyd63XSwprJgrZaXYdm70kohXPJUWdqKZozolFxbPaO+xtBaiUp6BoueA=="], - "@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.15.33", "", { "os": "linux", "cpu": "arm" }, "sha512-C64hBnBxq4viOPQ8hlx+2lJ23bzZBGnjw7ryALmS+0Q3zHmwO8lw1/DArLENw4Q18/0w5wdEO1k3m1wWNtKGqQ=="], + "@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.15.40", "", { "os": "linux", "cpu": "arm" }, "sha512-SlRZsCjOCPR2LvFs0Ri/Xrx/5o5TCt8vl4gW6mX1hEZOG0a625RxzRHpHdAQNGykmAN/7IeaFAJG+QnNmxlHcA=="], - "@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.15.33", "", { "os": "linux", "cpu": "arm64" }, "sha512-TRJfnJbX3jqpxRDRoieMzRiCBS5jOmXNb3iQXmcgjFEHKLnAgK1RZRU8Cq1MsPqO4jAJp/ld1G4O3fXuxv85uw=="], + "@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.15.40", "", { "os": "linux", "cpu": "arm64" }, "sha512-Q8byxJt2fh8CR3EUX6snBpy47AoBVm+In/+Z3rjDHMjC38ZvR9/gtUUNCT0tfrn4EdVsO8/QPi59nxrxvqxvBQ=="], - "@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.15.33", "", { "os": "linux", "cpu": "arm64" }, "sha512-il7tYM+CpUNzieQbwAjFT1P8zqAhmGWNAGhQZBnxurXZ0aNn+5nqYFTEUKNZl7QibtT0uQXzTZrNGHCIj6Y1Og=="], + "@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.15.40", "", { "os": "linux", "cpu": "arm64" }, "sha512-4z0MgHU+7M0pZDqBN1El7mFXDI1SBwinfcUkAyA4v8QrhOIUOZltySt2aStQLZGrdXVXM4Y4ylfiTC04ED+MoQ=="], - "@swc/core-linux-ppc64-gnu": ["@swc/core-linux-ppc64-gnu@1.15.33", "", { "os": "linux", "cpu": "ppc64" }, "sha512-ZtNBwN0Z7CFj9Il0FcPaKdjgP7URyKu/3RfH46vq+0paOBqLj4NYldD6Qo//Duif/7IOtAraUfDOmp0PLAufog=="], + "@swc/core-linux-ppc64-gnu": ["@swc/core-linux-ppc64-gnu@1.15.40", "", { "os": "linux", "cpu": "ppc64" }, "sha512-fLI4iUgeSZu0eRWUXwe6YzPFx9gHbFiPkl8Rp3mJfP8OpNR3nTQCGPvHdDh9xniW7mVvgMY4ni7A4VzqI1KrpA=="], - "@swc/core-linux-s390x-gnu": ["@swc/core-linux-s390x-gnu@1.15.33", "", { "os": "linux", "cpu": "s390x" }, "sha512-De1IyajoOmhOYYjw/lx66bKlyDpHZTueqwpDrWgf5O7T6d1ODeJJO9/OqMBmrBQc5C+dNnlmIufHsp4QVCWufA=="], + "@swc/core-linux-s390x-gnu": ["@swc/core-linux-s390x-gnu@1.15.40", "", { "os": "linux", "cpu": "s390x" }, "sha512-YqeKMAb7d4nQSGMJQ454IlaCENpzcDqhvBE9+CPfdnYpnUXxd+BSrB6Xk0YjW8UyoEhUj4p6quATCxbsp6J3jg=="], - "@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.15.33", "", { "os": "linux", "cpu": "x64" }, "sha512-mGTH0YxmUN+x6vRN/I6NOk5X0ogNktkwPnJ94IMvR7QjhRDwL0O8RXEDhyUM0YtwWrryBOqaJQBX4zruxEPRGw=="], + "@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.15.40", "", { "os": "linux", "cpu": "x64" }, "sha512-7HOuS1iGcme/j/TuL1TfmmLGiMQrjv/GmjyZeydl00FKPtpGXEldwqfI56xgd1YzrzoB2svWjxbGGyQ0TEASxg=="], - "@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.15.33", "", { "os": "linux", "cpu": "x64" }, "sha512-hj628ZkSEJf6zMf5VMbYrG2O6QqyTIp2qwY6VlCjvIa9lAEZ5c2lfPblCLVGYubTeLJDxadLB/CxqQYOQABeEQ=="], + "@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.15.40", "", { "os": "linux", "cpu": "x64" }, "sha512-h4kZYHc7dpc9P9u4brRJaS8Pl7tPVHAeiLSzw7T5RfIJgAoSdaCMKzI/2Uay9gFhaw8uyCDl0L5q37r0EpAfIA=="], - "@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.15.33", "", { "os": "win32", "cpu": "arm64" }, "sha512-GV2oohtN2/5+KSccl86VULu3aT+LrISC8uzgSq0FRnikpD+Zwc+sBlXmoKQ+Db6jI57ITUOIB8jRkdGMABC29g=="], + "@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.15.40", "", { "os": "win32", "cpu": "arm64" }, "sha512-+mQgKZXSj6mV38Zh05QaxSjUDmGP/R2JWlXZTDLSPkDzHU6p3GxN9eeSf5dfyDVU86946fmCvSzyl/ucImx8+A=="], - "@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.15.33", "", { "os": "win32", "cpu": "ia32" }, "sha512-gtyvzSNR8DHKfFEA2uqb8Ld1myqi6uEg2jyeUq3ikn5ytYs7H8RpZYC8mdy4NXr8hfcdJfCLXPlYaqqfBXpoEQ=="], + "@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.15.40", "", { "os": "win32", "cpu": "ia32" }, "sha512-yvwdPLGd25mcj/mNatjNQ0lZujtQD6psH3v9PNmMb+fSzjbNG8KIDxjFWrcV+fsFVLOkyOmdJsFmX7NAFjVyPw=="], - "@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.15.33", "", { "os": "win32", "cpu": "x64" }, "sha512-d6fRqQSkJI+kmMEBWaDQ7TMl8+YjLYbwRUPZQ9DY0ORBJeTzOrG0twvfvlZ2xgw6jA0ScQKgfBm4vHLSLl5Hqg=="], + "@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.15.40", "", { "os": "win32", "cpu": "x64" }, "sha512-OXtKsLU1bVtInzzDEAY2sYiF/rl4tvAnLLLpuMp3HzAOQZ5A+i69AKDhA1YLQTaMAqO3vzyYNVAYVRMPtSYD4w=="], "@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="], - "@swc/html": ["@swc/html@1.15.33", "", { "dependencies": { "@swc/counter": "^0.1.3" }, "optionalDependencies": { "@swc/html-darwin-arm64": "1.15.33", "@swc/html-darwin-x64": "1.15.33", "@swc/html-linux-arm-gnueabihf": "1.15.33", "@swc/html-linux-arm64-gnu": "1.15.33", "@swc/html-linux-arm64-musl": "1.15.33", "@swc/html-linux-ppc64-gnu": "1.15.33", "@swc/html-linux-s390x-gnu": "1.15.33", "@swc/html-linux-x64-gnu": "1.15.33", "@swc/html-linux-x64-musl": "1.15.33", "@swc/html-win32-arm64-msvc": "1.15.33", "@swc/html-win32-ia32-msvc": "1.15.33", "@swc/html-win32-x64-msvc": "1.15.33" } }, "sha512-PZIfmj5zYpAJ2eMptf0My2q9Bl8bkraW28+FD1pRnxOiYMrKrP5vL2tB2PdxMRjS0ziLFVM5HEuGFw8PxEDOaw=="], + "@swc/html": ["@swc/html@1.15.40", "", { "dependencies": { "@swc/counter": "^0.1.3" }, "optionalDependencies": { "@swc/html-darwin-arm64": "1.15.40", "@swc/html-darwin-x64": "1.15.40", "@swc/html-linux-arm-gnueabihf": "1.15.40", "@swc/html-linux-arm64-gnu": "1.15.40", "@swc/html-linux-arm64-musl": "1.15.40", "@swc/html-linux-ppc64-gnu": "1.15.40", "@swc/html-linux-s390x-gnu": "1.15.40", "@swc/html-linux-x64-gnu": "1.15.40", "@swc/html-linux-x64-musl": "1.15.40", "@swc/html-win32-arm64-msvc": "1.15.40", "@swc/html-win32-ia32-msvc": "1.15.40", "@swc/html-win32-x64-msvc": "1.15.40" } }, "sha512-YVWNrh3wZWH2oXz7uL+s1WZbFIsnAp9cCl/b+C7ufDJqhsOvbr0un9IOYZ/CxRIRdnHGfhVD199n8baD/kRQew=="], - "@swc/html-darwin-arm64": ["@swc/html-darwin-arm64@1.15.33", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zyO6uMBfLyCh55wundAxKX+8P/f98ecuyir4VX6nTmn6y7x37ndB8f01LUrd9Tiq6eEAvDXLiqEUvuGjEc7Pmg=="], + "@swc/html-darwin-arm64": ["@swc/html-darwin-arm64@1.15.40", "", { "os": "darwin", "cpu": "arm64" }, "sha512-A9oxSh60pMEsX/4VBpn3e6s8DfGWsHzAeG+Vd9mXgkTqt/ouX12owP6RyqfqK8v0WuEsBKkGY0+MbwLvlOG8NQ=="], - "@swc/html-darwin-x64": ["@swc/html-darwin-x64@1.15.33", "", { "os": "darwin", "cpu": "x64" }, "sha512-MaGunsY/J5l7Rb5OmoztEWh+ikooydT7nWkjiDovj7UfkB9HLk5sLr9O7ZdNGJ2u9dD6FX89SzMdA0Psm9NJrQ=="], + "@swc/html-darwin-x64": ["@swc/html-darwin-x64@1.15.40", "", { "os": "darwin", "cpu": "x64" }, "sha512-GZ5KxJ/1pqnpOAjaa9hnkSk3M1LHjc5ci5GCt5/llAgDKFLDHzydIflHXTOIaS4nRCtfVMmD0joKX+ZWruSkJw=="], - "@swc/html-linux-arm-gnueabihf": ["@swc/html-linux-arm-gnueabihf@1.15.33", "", { "os": "linux", "cpu": "arm" }, "sha512-CrbUDjVl6/hQ1C5KPMiK4vxk/eOMjxkVELqwnOxsZ+aFVTv3L3YrGMaJ5H47vvIihkPhqiSOUPmMEFqxvqKmXg=="], + "@swc/html-linux-arm-gnueabihf": ["@swc/html-linux-arm-gnueabihf@1.15.40", "", { "os": "linux", "cpu": "arm" }, "sha512-Bbwtl0G6vYcWlDQmjeuyFx8YIvqi1StlQWjRNB1ZtvrRNL3JadFARhijBONiUIyTK9MgUbWtTV3QU69qzjNENQ=="], - "@swc/html-linux-arm64-gnu": ["@swc/html-linux-arm64-gnu@1.15.33", "", { "os": "linux", "cpu": "arm64" }, "sha512-7tZ0IgmUslI9Extu/TpxJS0GjJoDx0j9zeq2cIidPdM/njSBpyRB7n4B292Q5WFVh7PcZl7WXqqqMczibQ27aA=="], + "@swc/html-linux-arm64-gnu": ["@swc/html-linux-arm64-gnu@1.15.40", "", { "os": "linux", "cpu": "arm64" }, "sha512-buu4fGyCIhkwmCetI4CfWOPn7cphJHwQ9ksK685hlL0R0PUaDCNdIxo+JAz0mK2JgPhoGqnKsVEmV6gLIL5GBw=="], - "@swc/html-linux-arm64-musl": ["@swc/html-linux-arm64-musl@1.15.33", "", { "os": "linux", "cpu": "arm64" }, "sha512-gYi2ainYZV2z+jwjp9UKuPVOf3c5q+NkH3QRDjqDrIPLagqDsYNjobi8p5oajGcPGFLNTcVw08VTcubJGChReA=="], + "@swc/html-linux-arm64-musl": ["@swc/html-linux-arm64-musl@1.15.40", "", { "os": "linux", "cpu": "arm64" }, "sha512-BwWtpAGEHIP1QwKExWqBIjGpAHMahjT26ORZv3+TbwW4IuEl2QP5FrcYc30O8abCG5J1lWRl1vXKYDpzbIDz1A=="], - "@swc/html-linux-ppc64-gnu": ["@swc/html-linux-ppc64-gnu@1.15.33", "", { "os": "linux", "cpu": "ppc64" }, "sha512-6CfzyVQSdD8ezFdxFve4J/b6qTgXIwYFWEvSdaJvXSgwTy976uUV5Ff1LOF86mt2zWMhZJX9DqmkGyIhepbyWw=="], + "@swc/html-linux-ppc64-gnu": ["@swc/html-linux-ppc64-gnu@1.15.40", "", { "os": "linux", "cpu": "ppc64" }, "sha512-wLtHgwmZu5K7PvuTROklgWJO6D+KwywpYR1geI8dSWX/AYnx/KCGVK9ncPzWVfnLXWhXbkgua3Ujdrfoxf+qSw=="], - "@swc/html-linux-s390x-gnu": ["@swc/html-linux-s390x-gnu@1.15.33", "", { "os": "linux", "cpu": "s390x" }, "sha512-Msx1eniw95lhMHUSe3D5FXweKHtkHtzJLsHJDj920uL4Dm7UHqzwaCuZdCmzbkHnO96YjjQvAm266djg8wupmQ=="], + "@swc/html-linux-s390x-gnu": ["@swc/html-linux-s390x-gnu@1.15.40", "", { "os": "linux", "cpu": "s390x" }, "sha512-WbWzQBCmvECZG6ep9D5SMMZTFCbFmRSCc7lPD3YhhaK16BNqr1AybvybZYOgxX9Ot5tq7gTWCgjFZXLDOn6Y/g=="], - "@swc/html-linux-x64-gnu": ["@swc/html-linux-x64-gnu@1.15.33", "", { "os": "linux", "cpu": "x64" }, "sha512-JDNb4Uq+7g+23QuOtwWnP0/EqztWIHFFdQdeBIS5zx83YBG2dYRMdPAjnHJWh2YRZxdepd8q6S9MUIxpSrouAg=="], + "@swc/html-linux-x64-gnu": ["@swc/html-linux-x64-gnu@1.15.40", "", { "os": "linux", "cpu": "x64" }, "sha512-i+/JqL5j9mTGMcVshWLMzciqyE3Np+gN5+UqMyO1D5zSmniJD5MHRlGXCtLaWx/MIKX0Q4Xkre33o56fmxp+uA=="], - "@swc/html-linux-x64-musl": ["@swc/html-linux-x64-musl@1.15.33", "", { "os": "linux", "cpu": "x64" }, "sha512-NSpZdbz4dj0pu1A0Z9l68Bll5HAzEMtBAeMe6jc4GEVfpIw6eeafQHm2/yMUEh09tgl8t9LzM9DycfdTZDjM4g=="], + "@swc/html-linux-x64-musl": ["@swc/html-linux-x64-musl@1.15.40", "", { "os": "linux", "cpu": "x64" }, "sha512-mroZnb5omFc4iE01UsJgta+Nm0sIFWpGfTPk+BHfH165MZAM4wNndeczX5RpGZbQSF1nNpCoNgqJyT7l3hITMg=="], - "@swc/html-win32-arm64-msvc": ["@swc/html-win32-arm64-msvc@1.15.33", "", { "os": "win32", "cpu": "arm64" }, "sha512-w7iho3/zS3lCDqgUZMDLMBO0ElX7j+KgvMb8BOrKqLDOSTDDj3lY/BClNJ7vBpAliI2kPQs/mUikdZyzi4MBjQ=="], + "@swc/html-win32-arm64-msvc": ["@swc/html-win32-arm64-msvc@1.15.40", "", { "os": "win32", "cpu": "arm64" }, "sha512-qg1LXRrsY9FXy0S7vouSF0kkn1vdkTD//FW66iuezH7HMm5/DJGwEj7oc1zTKPWU0HnwOINOUbl6rUHrRuXHAQ=="], - "@swc/html-win32-ia32-msvc": ["@swc/html-win32-ia32-msvc@1.15.33", "", { "os": "win32", "cpu": "ia32" }, "sha512-6hJ2pBweSfZ38trYHXmzTBDpRNvqJgFl2PkIWdy4IXbV/Fv0v9Dqe0t9Gi2ZVEBpgI7PD6pF42AT4HmrNTVFyQ=="], + "@swc/html-win32-ia32-msvc": ["@swc/html-win32-ia32-msvc@1.15.40", "", { "os": "win32", "cpu": "ia32" }, "sha512-5NwMmgt9A70LQzcYOl4OiIJk5AhsGP69fNRfkmXXop+iIcAGUmYy+d6F2/Gc0u05j9G0R04NPWcHBQrHQhr0Gw=="], - "@swc/html-win32-x64-msvc": ["@swc/html-win32-x64-msvc@1.15.33", "", { "os": "win32", "cpu": "x64" }, "sha512-eaY/vNE7rkPKluJYjhOiQOA1tto5VbJOoD1C1xFTBmr9t7WsqYUfbQhYQy5A26/z83NNgtDwELM85rkMB+/vWA=="], + "@swc/html-win32-x64-msvc": ["@swc/html-win32-x64-msvc@1.15.40", "", { "os": "win32", "cpu": "x64" }, "sha512-WUNZqCZ+8WY1pr8t3X3fq4gI5+FL/tCCzlrJ2lLn0s+TyA2SLm/RPfXxQIr94EnjJWcIV21G//6/uXeBN8cPzw=="], "@swc/types": ["@swc/types@0.1.26", "", { "dependencies": { "@swc/counter": "^0.1.3" } }, "sha512-lyMwd7WGgG79RS7EERZV3T8wMdmPq3xwyg+1nmAM64kIhx5yl+juO2PYIHb7vTiPgPCj8LYjsNV2T5wiQHUEaw=="], @@ -1036,21 +1040,21 @@ "@tanstack/history": ["@tanstack/history@1.162.0", "", {}, "sha512-79pf/RkhteYZTRgcR4F9kbk84P2N8rugQJswxfIqovlbRiT3yI7eBE+5QorIrZaOKktsgzRlXh1l/du/xpl4iA=="], - "@tanstack/react-router": ["@tanstack/react-router@1.170.10", "", { "dependencies": { "@tanstack/history": "1.162.0", "@tanstack/react-store": "^0.9.3", "@tanstack/router-core": "1.171.8", "isbot": "^5.1.22" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-gVmWYq0ucWr+OB97Nud0YhKa9NOipB7/QrWI7wRZJJWEL0qUS8WPqAs0vA1f3IBXZpXmf8xxzf/tl5cmo4tlmA=="], + "@tanstack/react-router": ["@tanstack/react-router@1.170.15", "", { "dependencies": { "@tanstack/history": "1.162.0", "@tanstack/react-store": "^0.9.3", "@tanstack/router-core": "1.171.13", "isbot": "^5.1.22" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-GawYz7HEjj8rTUUDoT/SemDEVm63pZUO+2mOcXHY9Jl3EwMS5gFBnPu/2UvcrwRm1jN1k79fokc0d4aFmrLatg=="], "@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.167.0", "", { "dependencies": { "@tanstack/router-devtools-core": "1.168.0" }, "peerDependencies": { "@tanstack/react-router": "^1.170.0", "@tanstack/router-core": "^1.170.0", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" }, "optionalPeers": ["@tanstack/router-core"] }, "sha512-nGw095EG7IHx0h5NtlEmzf6vcCTaFNPWdTSuDKazajhN0ct/v/TkekJ9J6KYUCeV1a8/2ZmToc58M+0rrOyn7w=="], "@tanstack/react-store": ["@tanstack/react-store@0.9.3", "", { "dependencies": { "@tanstack/store": "0.9.3", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg=="], - "@tanstack/router-core": ["@tanstack/router-core@1.171.8", "", { "dependencies": { "@tanstack/history": "1.162.0", "cookie-es": "^3.0.0", "seroval": "^1.5.4", "seroval-plugins": "^1.5.4" } }, "sha512-PbrTBbofFcacrH3RLgHYILRqTFnAGq+gXrXoA/vo7qUSkJpSO4GWfLtrtCahD4VayzRm19IPwcjPPLEugag6pw=="], + "@tanstack/router-core": ["@tanstack/router-core@1.171.13", "", { "dependencies": { "@tanstack/history": "1.162.0", "cookie-es": "^3.0.0", "seroval": "^1.5.4", "seroval-plugins": "^1.5.4" } }, "sha512-+NOwEj1kO/6IGmpHRIZHasYxYWpyBQGNIZAST9aNrk9Q3YlU9SgqVnl1pbLa9qAKfeNdXQIRve0RQb/0kyDeDA=="], "@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.168.0", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16" }, "peerDependencies": { "@tanstack/router-core": "^1.170.0", "csstype": "^3.0.10" }, "optionalPeers": ["csstype"] }, "sha512-wQoQhlBK7nlZgqzaqdYXKWNTpdHdsaREdaPhFZVH0/Ador+F+eM3/NF2i3f2LPeS0GgKraZUQXe1Q/1+KHyEYg=="], - "@tanstack/router-generator": ["@tanstack/router-generator@1.167.12", "", { "dependencies": { "@babel/types": "^7.28.5", "@tanstack/router-core": "1.171.8", "@tanstack/router-utils": "1.162.1", "@tanstack/virtual-file-routes": "1.162.0", "jiti": "^2.7.0", "magic-string": "^0.30.21", "prettier": "^3.5.0", "zod": "^4.4.3" } }, "sha512-FGr7nn6VhjL53TUCTyDgApSkAYRxhId+v0HVQdSu0ADkNuHY+sUnYEMqiF6aN82jYWuXzrSL1xazg6/rfEP82g=="], + "@tanstack/router-generator": ["@tanstack/router-generator@1.167.17", "", { "dependencies": { "@babel/types": "^7.28.5", "@tanstack/router-core": "1.171.13", "@tanstack/router-utils": "1.162.2", "@tanstack/virtual-file-routes": "1.162.0", "jiti": "^2.7.0", "magic-string": "^0.30.21", "prettier": "^3.5.0", "zod": "^4.4.3" } }, "sha512-xtB9tB2Ws0tWR6Pi7nc3Qk9IYgoh1mQCKWjHqIl9tf6BNUpKoqniJoPAQ4+LGrK8FeZYU0o0p/qlZEyj9FAulA=="], - "@tanstack/router-plugin": ["@tanstack/router-plugin@1.168.13", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@tanstack/router-core": "1.171.8", "@tanstack/router-generator": "1.167.12", "@tanstack/router-utils": "1.162.1", "@tanstack/virtual-file-routes": "1.162.0", "chokidar": "^5.0.0", "unplugin": "^3.0.0", "zod": "^4.4.3" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2 || ^2.0.0", "@tanstack/react-router": "^1.170.10", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0 || >=8.0.0", "vite-plugin-solid": "^2.11.10 || ^3.0.0-0", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-LnepwDai+TaC4K3aZeXrrKpnGoP8xGGilVGFfa5flGgC3+jCSBysb8SktidRE8eF2/iOzCQC0LIGirtMyZepSA=="], + "@tanstack/router-plugin": ["@tanstack/router-plugin@1.168.18", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "@tanstack/router-core": "1.171.13", "@tanstack/router-generator": "1.167.17", "@tanstack/router-utils": "1.162.2", "chokidar": "^5.0.0", "unplugin": "^3.0.0", "zod": "^4.4.3" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2 || ^2.0.0", "@tanstack/react-router": "^1.170.15", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0 || >=8.0.0", "vite-plugin-solid": "^2.11.10 || ^3.0.0-0", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-MofS28/axfnfnhOD2RSgJEaU882aX5RsAzhGz5Vc4XhAmvCjy919u9JrNs4QsTWFbTD1P7IJ8WFlFVsrg0pStg=="], - "@tanstack/router-utils": ["@tanstack/router-utils@1.162.1", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/generator": "^7.28.5", "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "ansis": "^4.1.0", "babel-dead-code-elimination": "^1.0.12", "diff": "^8.0.2", "pathe": "^2.0.3", "tinyglobby": "^0.2.15" } }, "sha512-62layyTGmclHDQS/eidwKRfN1hhCKwViG7iEBcVmL0MXgcAB3OOucWCEcDDGd9Cu11H6b4QQ5oOo47MWIqwz0A=="], + "@tanstack/router-utils": ["@tanstack/router-utils@1.162.2", "", { "dependencies": { "@babel/generator": "^7.28.5", "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "ansis": "^4.1.0", "babel-dead-code-elimination": "^1.0.12", "diff": "^8.0.2", "pathe": "^2.0.3", "tinyglobby": "^0.2.15" } }, "sha512-hTWqJtqIFFdvuCl8WXNyrodp2L9zo2G37xKRrcVmVRWpAB2h+U1LuRAfS4tsFTiWOIoE/B+WDVFB8JpoEdw6jQ=="], "@tanstack/store": ["@tanstack/store@0.9.3", "", {}, "sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw=="], @@ -1072,7 +1076,7 @@ "@turbo/windows-arm64": ["@turbo/windows-arm64@2.9.16", "", { "os": "win32", "cpu": "arm64" }, "sha512-Y7SJppD0Z8wjO3Ec0ZGd9KQ4Yv0BMnA8CIowj5Vp+OEVsosXDG2weK6/t1RRLfJmc2Ozrnd6y4DOgQys+mn3WQ=="], - "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + "@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], "@typed/id": ["@typed/id@0.17.2", "", { "peerDependencies": { "effect": "^3.14.7" } }, "sha512-z/Z14/moeu9x45IpkGaRwuvb+CQ3s3UCc/agcpZibTz1yPb3RgSDXx4rOHIuyb6hG6oNzqe9yY4GbbMq3Hb5Ug=="], @@ -1094,11 +1098,7 @@ "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], - "@types/eslint": ["@types/eslint@9.6.1", "", { "dependencies": { "@types/estree": "*", "@types/json-schema": "*" } }, "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag=="], - - "@types/eslint-scope": ["@types/eslint-scope@3.7.7", "", { "dependencies": { "@types/eslint": "*", "@types/estree": "*" } }, "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg=="], - - "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], "@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="], @@ -1130,21 +1130,21 @@ "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], - "@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], + "@types/mdx": ["@types/mdx@2.0.14", "", {}, "sha512-T48PeuJtvLosNTPVhfnIp3i/n3a4g4Bad7YCq5k64D4u7NwDrAotikQ+5+sjtUvBmxCMlbo3dVL+C2dP0rWHzg=="], "@types/mime": ["@types/mime@1.3.5", "", {}, "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="], "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], - "@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="], + "@types/node": ["@types/node@25.9.2", "", { "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } }, "sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw=="], "@types/prismjs": ["@types/prismjs@1.26.6", "", {}, "sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw=="], - "@types/qs": ["@types/qs@6.15.0", "", {}, "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow=="], + "@types/qs": ["@types/qs@6.15.1", "", {}, "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw=="], "@types/range-parser": ["@types/range-parser@1.2.7", "", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="], - "@types/react": ["@types/react@19.2.15", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q=="], + "@types/react": ["@types/react@19.2.17", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw=="], "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], @@ -1158,7 +1158,7 @@ "@types/sax": ["@types/sax@1.2.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A=="], - "@types/send": ["@types/send@0.17.6", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og=="], + "@types/send": ["@types/send@1.2.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ=="], "@types/serve-index": ["@types/serve-index@1.9.4", "", { "dependencies": { "@types/express": "*" } }, "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug=="], @@ -1174,7 +1174,7 @@ "@types/yargs-parser": ["@types/yargs-parser@21.0.3", "", {}, "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ=="], - "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.1", "", {}, "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ=="], "@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.2", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.0" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler"] }, "sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg=="], @@ -1242,15 +1242,15 @@ "aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="], - "ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], + "ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], "ajv-formats": ["ajv-formats@2.1.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA=="], "ajv-keywords": ["ajv-keywords@5.1.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3" }, "peerDependencies": { "ajv": "^8.8.2" } }, "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw=="], - "algoliasearch": ["algoliasearch@5.50.0", "", { "dependencies": { "@algolia/abtesting": "1.16.0", "@algolia/client-abtesting": "5.50.0", "@algolia/client-analytics": "5.50.0", "@algolia/client-common": "5.50.0", "@algolia/client-insights": "5.50.0", "@algolia/client-personalization": "5.50.0", "@algolia/client-query-suggestions": "5.50.0", "@algolia/client-search": "5.50.0", "@algolia/ingestion": "1.50.0", "@algolia/monitoring": "1.50.0", "@algolia/recommend": "5.50.0", "@algolia/requester-browser-xhr": "5.50.0", "@algolia/requester-fetch": "5.50.0", "@algolia/requester-node-http": "5.50.0" } }, "sha512-yE5I83Q2s8euVou8Y3feXK08wyZInJWLYXgWO6Xti9jBUEZAGUahyeQ7wSZWkifLWVnQVKEz5RAmBlXG5nqxog=="], + "algoliasearch": ["algoliasearch@5.53.0", "", { "dependencies": { "@algolia/abtesting": "1.19.0", "@algolia/client-abtesting": "5.53.0", "@algolia/client-analytics": "5.53.0", "@algolia/client-common": "5.53.0", "@algolia/client-insights": "5.53.0", "@algolia/client-personalization": "5.53.0", "@algolia/client-query-suggestions": "5.53.0", "@algolia/client-search": "5.53.0", "@algolia/ingestion": "1.53.0", "@algolia/monitoring": "1.53.0", "@algolia/recommend": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-OGW1q6b91CRSSeiOnM8LxuR5NYJ2esvw66jUZ4IIvdv+ItNkx3pwLuyR+jaCdbGee4ov5WgUnyPryyh11xvByQ=="], - "algoliasearch-helper": ["algoliasearch-helper@3.28.1", "", { "dependencies": { "@algolia/events": "^4.0.1" }, "peerDependencies": { "algoliasearch": ">= 3.1 < 6" } }, "sha512-6iXpbkkrAI5HFpCWXlNmIDSBuoN/U1XnEvb2yJAoWfqrZ+DrybI7MQ5P5mthFaprmocq+zbi6HxnR28xnZAYBw=="], + "algoliasearch-helper": ["algoliasearch-helper@3.29.1", "", { "dependencies": { "@algolia/events": "^4.0.1" }, "peerDependencies": { "algoliasearch": ">= 3.1 < 6" } }, "sha512-6ck2YFudF2Pje7szQoPBiRFTGfd+1I+0I/WfLPGn0bj1kvrFoOQmNyedNiDxTk3/r4IfSLDYk+RA4G7u8H6+yA=="], "ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="], @@ -1260,7 +1260,7 @@ "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - "ansis": ["ansis@4.2.0", "", {}, "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig=="], + "ansis": ["ansis@4.3.1", "", {}, "sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA=="], "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], @@ -1276,13 +1276,13 @@ "array-union": ["array-union@2.1.0", "", {}, "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="], - "asn1js": ["asn1js@3.0.7", "", { "dependencies": { "pvtsutils": "^1.3.6", "pvutils": "^1.1.3", "tslib": "^2.8.1" } }, "sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ=="], + "asn1js": ["asn1js@3.0.10", "", { "dependencies": { "pvtsutils": "^1.3.6", "pvutils": "^1.1.5", "tslib": "^2.8.1" } }, "sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg=="], "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], "astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="], - "autoprefixer": ["autoprefixer@10.4.27", "", { "dependencies": { "browserslist": "^4.28.1", "caniuse-lite": "^1.0.30001774", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA=="], + "autoprefixer": ["autoprefixer@10.5.0", "", { "dependencies": { "browserslist": "^4.28.2", "caniuse-lite": "^1.0.30001787", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong=="], "babel-dead-code-elimination": ["babel-dead-code-elimination@1.0.12", "", { "dependencies": { "@babel/core": "^7.23.7", "@babel/parser": "^7.23.6", "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6" } }, "sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig=="], @@ -1300,7 +1300,7 @@ "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - "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.34", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-IMDedajPifLnHNY0X9n8hKxRTQ6/eTHwr5bDo04WnuqxyKw6LYtQywCuuqPZwhl3aBXMvQpJov42GLCwRRdQzw=="], "batch": ["batch@0.6.1", "", {}, "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw=="], @@ -1308,15 +1308,15 @@ "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], - "body-parser": ["body-parser@1.20.4", "", { "dependencies": { "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "~1.2.0", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", "qs": "~6.14.0", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" } }, "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA=="], + "body-parser": ["body-parser@1.20.5", "", { "dependencies": { "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "~1.2.0", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", "qs": "~6.15.1", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" } }, "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA=="], - "bonjour-service": ["bonjour-service@1.3.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "multicast-dns": "^7.2.5" } }, "sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA=="], + "bonjour-service": ["bonjour-service@1.4.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "multicast-dns": "^7.2.5" } }, "sha512-fGQtj1qdR9vIKjFiWPQd52qIqwjaYqhcI40JEiDuvlZ86E7ZBPBwY9fPgHy9r2rYGIjiRfctNPYz6OQU73ww2w=="], "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], "boxen": ["boxen@6.2.1", "", { "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^6.2.0", "chalk": "^4.1.2", "cli-boxes": "^3.0.0", "string-width": "^5.0.1", "type-fest": "^2.5.0", "widest-line": "^4.0.1", "wrap-ansi": "^8.0.1" } }, "sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw=="], - "brace-expansion": ["brace-expansion@1.1.13", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w=="], + "brace-expansion": ["brace-expansion@1.1.15", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], @@ -1338,7 +1338,7 @@ "cacheable-request": ["cacheable-request@10.2.14", "", { "dependencies": { "@types/http-cache-semantics": "^4.0.2", "get-stream": "^6.0.1", "http-cache-semantics": "^4.1.1", "keyv": "^4.5.3", "mimic-response": "^4.0.0", "normalize-url": "^8.0.0", "responselike": "^3.0.0" } }, "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ=="], - "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=="], @@ -1352,7 +1352,7 @@ "caniuse-api": ["caniuse-api@3.0.0", "", { "dependencies": { "browserslist": "^4.0.0", "caniuse-lite": "^1.0.0", "lodash.memoize": "^4.1.2", "lodash.uniq": "^4.5.0" } }, "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw=="], - "caniuse-lite": ["caniuse-lite@1.0.30001782", "", {}, "sha512-dZcaJLJeDMh4rELYFw1tvSn1bhZWYFOt468FcbHHxx/Z/dFidd1I6ciyFdi3iwfQCyOjqo9upF6lGQYtMiJWxw=="], + "caniuse-lite": ["caniuse-lite@1.0.30001797", "", {}, "sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w=="], "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], @@ -1458,7 +1458,7 @@ "css-blank-pseudo": ["css-blank-pseudo@7.0.1", "", { "dependencies": { "postcss-selector-parser": "^7.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-jf+twWGDf6LDoXDUode+nc7ZlrqfaNphrBIBrcmeP3D8yw1uPaix1gCC8LUQUGQ6CycuK2opkbFFWFuq/a94ag=="], - "css-declaration-sorter": ["css-declaration-sorter@7.3.1", "", { "peerDependencies": { "postcss": "^8.0.9" } }, "sha512-gz6x+KkgNCjxq3Var03pRYLhyNfwhkKF1g/yoLgDNtFvVu0/fOLV9C8fFEZRjACp/XQLumjAYo7JVjzH3wLbxA=="], + "css-declaration-sorter": ["css-declaration-sorter@7.4.0", "", { "peerDependencies": { "postcss": "^8.0.9" } }, "sha512-LTuzjPoyA2vMGKKcaOqKSp7Ub2eGrNfKiZH4LpezxpNrsICGCSFvsQOI29psISxNZtaXibkC2CXzrQ5enMeGGw=="], "css-has-pseudo": ["css-has-pseudo@7.0.3", "", { "dependencies": { "@csstools/selector-specificity": "^5.0.0", "postcss-selector-parser": "^7.0.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-oG+vKuGyqe/xvEMoxAQrhi7uY16deJR3i7wwhBerVrGQKSqUC5GiOVxTpM9F9B9hw0J+eKeOWLH7E9gZ1Dr5rA=="], @@ -1474,7 +1474,7 @@ "css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="], - "cssdb": ["cssdb@8.8.0", "", {}, "sha512-QbLeyz2Bgso1iRlh7IpWk6OKa3lLNGXsujVjDMPl9rOZpxKeiG69icLpbLCFxeURwmcdIfZqQyhlooKJYM4f8Q=="], + "cssdb": ["cssdb@8.9.0", "", {}, "sha512-J8jOU/hLjaXcO1LldOLraJSQpfLXRKof0I7mtbRyOy2AAXgqst0x9rlgi2qXeD6d0ou3ZLqcPAMqYVbpCbrxEw=="], "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], @@ -1570,13 +1570,13 @@ "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], - "effect": ["effect@3.21.2", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-rXd2FGDM8KdjSIrc+mqEELo7ScW7xTVxEf1iInmPSpIde9/nyGuFM710cjTo7/EreGXiUX2MOonPpprbz2XHCg=="], + "effect": ["effect@3.21.3", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-RqwU7WnJ6CqYhyjpOVJA5vh1Sgkn6eVECO6mnD0EjlbWcC2M3LJaPglXXr13Rdo/Y+B+wTEPzGRYFNL2xKxNeQ=="], "effect-fc": ["effect-fc@workspace:packages/effect-fc"], "effect-lens": ["effect-lens@0.2.0", "", { "peerDependencies": { "effect": "^3.21.0" } }, "sha512-Cfyps811WQVSxnbxm6xqJldL08YXUU1jAzWtdKXSKWZfF6LAEqm1l8swnw4ltVky1TSfi2vvS+I/tFPqwObrMg=="], - "electron-to-chromium": ["electron-to-chromium@1.5.329", "", {}, "sha512-/4t+AS1l4S3ZC0Ja7PHFIWeBIxGA3QGqV8/yKsP36v7NcyUCl+bIcmw6s5zVuMIECWwBrAK/6QLzTmbJChBboQ=="], + "electron-to-chromium": ["electron-to-chromium@1.5.368", "", {}, "sha512-7RckJJK4uESJF9PxvfMWd3TGqIiieUTG4HxnKaKuIpGbcr+r2ZEB3g2gAhCP3Fqm42vJSzLfgab9eva/C4/XVw=="], "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], @@ -1588,7 +1588,7 @@ "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], - "enhanced-resolve": ["enhanced-resolve@5.20.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA=="], + "enhanced-resolve": ["enhanced-resolve@5.23.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-yJN/BOOLxcOW2aQgeif9mSnaUB8KtvmMMp56oA1kx1CRfBKbhZm2pJ+NBY+3eOboHxix8lfjWpHE0Ei5U8RbSA=="], "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], @@ -1598,9 +1598,9 @@ "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], - "es-module-lexer": ["es-module-lexer@2.0.0", "", {}, "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw=="], + "es-module-lexer": ["es-module-lexer@2.1.0", "", {}, "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ=="], - "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + "es-object-atoms": ["es-object-atoms@1.1.2", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="], "esast-util-from-estree": ["esast-util-from-estree@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-visit": "^2.0.0", "unist-util-position-from-estree": "^2.0.0" } }, "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ=="], @@ -1656,7 +1656,7 @@ "expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="], - "express": ["express@4.22.1", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "~1.20.3", "content-disposition": "~0.5.4", "content-type": "~1.0.4", "cookie": "~0.7.1", "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "~1.3.1", "fresh": "~0.5.2", "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", "qs": "~6.14.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", "serve-static": "~1.16.2", "setprototypeof": "1.2.0", "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g=="], + "express": ["express@4.22.2", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "~1.20.5", "content-disposition": "~0.5.4", "content-type": "~1.0.4", "cookie": "~0.7.1", "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "~1.3.1", "fresh": "~0.5.2", "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", "qs": "~6.15.1", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", "serve-static": "~1.16.2", "setprototypeof": "1.2.0", "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q=="], "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], @@ -1670,7 +1670,7 @@ "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], - "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], + "fast-uri": ["fast-uri@3.1.2", "", {}, "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ=="], "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], @@ -1696,7 +1696,7 @@ "flat": ["flat@5.0.2", "", { "bin": { "flat": "cli.js" } }, "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ=="], - "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], + "follow-redirects": ["follow-redirects@1.16.0", "", {}, "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw=="], "form-data-encoder": ["form-data-encoder@2.1.4", "", {}, "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw=="], @@ -1708,7 +1708,7 @@ "fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="], - "fs-extra": ["fs-extra@11.3.4", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA=="], + "fs-extra": ["fs-extra@11.3.5", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg=="], "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], @@ -1740,7 +1740,7 @@ "globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="], - "goober": ["goober@2.1.18", "", { "peerDependencies": { "csstype": "^3.0.10" } }, "sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw=="], + "goober": ["goober@2.1.19", "", { "peerDependencies": { "csstype": "^3.0.10" } }, "sha512-U7veizMqxyKlM58+Z5j2ngJBH/r9siDmxpvNxSw0PylF6WQvrASJEZrxh1hidRBJc2jqoBVSyOban5u8m+6Rxg=="], "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], @@ -1762,7 +1762,7 @@ "has-yarn": ["has-yarn@3.0.0", "", {}, "sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA=="], - "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + "hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="], "hast-util-from-parse5": ["hast-util-from-parse5@8.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "hastscript": "^9.0.0", "property-information": "^7.0.0", "vfile": "^6.0.0", "vfile-location": "^5.0.0", "web-namespaces": "^2.0.0" } }, "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg=="], @@ -1798,7 +1798,7 @@ "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], - "html-webpack-plugin": ["html-webpack-plugin@5.6.6", "", { "dependencies": { "@types/html-minifier-terser": "^6.0.0", "html-minifier-terser": "^6.0.2", "lodash": "^4.17.21", "pretty-error": "^4.0.0", "tapable": "^2.0.0" }, "peerDependencies": { "@rspack/core": "0.x || 1.x", "webpack": "^5.20.0" }, "optionalPeers": ["@rspack/core", "webpack"] }, "sha512-bLjW01UTrvoWTJQL5LsMRo1SypHW80FTm12OJRSnr3v6YHNhfe+1r0MYUZJMACxnCHURVnBWRwAsWs2yPU9Ezw=="], + "html-webpack-plugin": ["html-webpack-plugin@5.6.7", "", { "dependencies": { "@types/html-minifier-terser": "^6.0.0", "html-minifier-terser": "^6.0.2", "lodash": "^4.17.21", "pretty-error": "^4.0.0", "tapable": "^2.0.0" }, "peerDependencies": { "@rspack/core": "0.x || 1.x", "webpack": "^5.20.0" }, "optionalPeers": ["@rspack/core", "webpack"] }, "sha512-md+vXtdCAe60s1k6AU3dUyMJnDxUyQAwfwPKoLisvgUF1IXjtlLsk2se54+qfL9Mdm26bbwvjJybpNx48NKRLw=="], "htmlparser2": ["htmlparser2@8.0.2", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1", "entities": "^4.4.0" } }, "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA=="], @@ -1850,7 +1850,7 @@ "invariant": ["invariant@2.2.4", "", { "dependencies": { "loose-envify": "^1.0.0" } }, "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA=="], - "ipaddr.js": ["ipaddr.js@2.3.0", "", {}, "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg=="], + "ipaddr.js": ["ipaddr.js@2.4.0", "", {}, "sha512-9VGk3HGanVE6JoZXHiCpnGy5X0jYDnN4EA4lntFPj+1vIWlFhIylq2CrrCOJH9EAhc5CYhq18F2Av2tgoAPsYQ=="], "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="], @@ -1862,7 +1862,7 @@ "is-ci": ["is-ci@3.0.1", "", { "dependencies": { "ci-info": "^3.2.0" }, "bin": { "is-ci": "bin.js" } }, "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ=="], - "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], + "is-core-module": ["is-core-module@2.16.2", "", { "dependencies": { "hasown": "^2.0.3" } }, "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA=="], "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="], @@ -1882,7 +1882,7 @@ "is-installed-globally": ["is-installed-globally@0.4.0", "", { "dependencies": { "global-dirs": "^3.0.0", "is-path-inside": "^3.0.2" } }, "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ=="], - "is-network-error": ["is-network-error@1.3.1", "", {}, "sha512-6QCxa49rQbmUWLfk0nuGqzql9U8uaV2H6279bRErPBHe/109hCzsLUBUHfbEtvLIHBd6hyXbgedBSHevm43Edw=="], + "is-network-error": ["is-network-error@1.3.2", "", {}, "sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA=="], "is-npm": ["is-npm@6.1.0", "", {}, "sha512-O2z4/kNgyjhQwVR1Wpkbfc19JIhggF97NZNCpWTnjH7kVcZMUrnut9XSN7txI7VdyIYk5ZatOq3zvSuWpU8hoA=="], @@ -1910,7 +1910,7 @@ "isarray": ["isarray@0.0.1", "", {}, "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="], - "isbot": ["isbot@5.1.37", "", {}, "sha512-5bcicX81xf6NlTEV8rWdg7Pk01LFizDetuYGHx6d/f6y3lR2/oo8IfxjzJqn1UdDEyCcwT9e7NRloj8DwCYujQ=="], + "isbot": ["isbot@5.1.41", "", {}, "sha512-9WFV/Vhh0FEj6CQ7MoHweEL9/vLKPjeoD2I2htbAjX7kbW7VJs3OCpWOVyd+JraNTWVU6/DRx2MZy2KaUNXHcg=="], "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], @@ -1926,7 +1926,7 @@ "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + "js-yaml": ["js-yaml@4.2.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw=="], "jsdom": ["jsdom@26.1.0", "", { "dependencies": { "cssstyle": "^4.2.1", "data-urls": "^5.0.0", "decimal.js": "^10.5.0", "html-encoding-sniffer": "^4.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", "nwsapi": "^2.2.16", "parse5": "^7.2.1", "rrweb-cssom": "^0.8.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^5.1.1", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^7.0.0", "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.1.1", "ws": "^8.18.0", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.0.0" }, "optionalPeers": ["canvas"] }, "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg=="], @@ -1940,7 +1940,7 @@ "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], - "jsonfile": ["jsonfile@6.2.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="], + "jsonfile": ["jsonfile@6.2.1", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q=="], "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], @@ -1950,7 +1950,7 @@ "latest-version": ["latest-version@7.0.0", "", { "dependencies": { "package-json": "^8.1.0" } }, "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg=="], - "launch-editor": ["launch-editor@2.13.2", "", { "dependencies": { "picocolors": "^1.1.1", "shell-quote": "^1.8.3" } }, "sha512-4VVDnbOpLXy/s8rdRCSXb+zfMeFR0WlJWpET1iA9CQdlZDfwyLjUuGQzXU4VeOoey6AicSAluWan7Etga6Kcmg=="], + "launch-editor": ["launch-editor@2.14.1", "", { "dependencies": { "picocolors": "^1.1.1", "shell-quote": "^1.8.4" } }, "sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA=="], "leven": ["leven@3.1.0", "", {}, "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A=="], @@ -1982,13 +1982,13 @@ "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], - "loader-runner": ["loader-runner@4.3.1", "", {}, "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q=="], + "loader-runner": ["loader-runner@4.3.2", "", {}, "sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w=="], "loader-utils": ["loader-utils@2.0.4", "", { "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", "json5": "^2.1.2" } }, "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw=="], "locate-path": ["locate-path@7.2.0", "", { "dependencies": { "p-locate": "^6.0.0" } }, "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA=="], - "lodash": ["lodash@4.18.0", "", {}, "sha512-l1mfj2atMqndAHI3ls7XqPxEjV2J9ZkcNyHpoZA3r2T1LLwDB69jgkMWh71YKwhBbK0G2f4WSn05ahmQXVxupA=="], + "lodash": ["lodash@4.18.1", "", {}, "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="], "lodash.debounce": ["lodash.debounce@4.0.8", "", {}, "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="], @@ -2058,7 +2058,7 @@ "media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="], - "memfs": ["memfs@4.57.1", "", { "dependencies": { "@jsonjoy.com/fs-core": "4.57.1", "@jsonjoy.com/fs-fsa": "4.57.1", "@jsonjoy.com/fs-node": "4.57.1", "@jsonjoy.com/fs-node-builtins": "4.57.1", "@jsonjoy.com/fs-node-to-fsa": "4.57.1", "@jsonjoy.com/fs-node-utils": "4.57.1", "@jsonjoy.com/fs-print": "4.57.1", "@jsonjoy.com/fs-snapshot": "4.57.1", "@jsonjoy.com/json-pack": "^1.11.0", "@jsonjoy.com/util": "^1.9.0", "glob-to-regex.js": "^1.0.1", "thingies": "^2.5.0", "tree-dump": "^1.0.3", "tslib": "^2.0.0" } }, "sha512-WvzrWPwMQT+PtbX2Et64R4qXKK0fj/8pO85MrUCzymX3twwCiJCdvntW3HdhG1teLJcHDDLIKx5+c3HckWYZtQ=="], + "memfs": ["memfs@4.57.6", "", { "dependencies": { "@jsonjoy.com/fs-core": "4.57.6", "@jsonjoy.com/fs-fsa": "4.57.6", "@jsonjoy.com/fs-node": "4.57.6", "@jsonjoy.com/fs-node-builtins": "4.57.6", "@jsonjoy.com/fs-node-to-fsa": "4.57.6", "@jsonjoy.com/fs-node-utils": "4.57.6", "@jsonjoy.com/fs-print": "4.57.6", "@jsonjoy.com/fs-snapshot": "4.57.6", "@jsonjoy.com/json-pack": "^1.11.0", "@jsonjoy.com/util": "^1.9.0", "glob-to-regex.js": "^1.0.1", "thingies": "^2.5.0", "tree-dump": "^1.0.3", "tslib": "^2.0.0" } }, "sha512-WQK+DGjKCnPdpSyJUXphz+COF2uEhhsxQ3VIWBSbzpbbXuch3h4FePMqXrXGdLjsTgo4JFzBFsP6AWd9pVazGw=="], "merge-descriptors": ["merge-descriptors@1.0.3", "", {}, "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ=="], @@ -2146,9 +2146,9 @@ "mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="], - "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], - "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "mime-types": ["mime-types@2.1.18", "", { "dependencies": { "mime-db": "~1.33.0" } }, "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ=="], "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], @@ -2166,9 +2166,9 @@ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - "msgpackr": ["msgpackr@1.11.10", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-iCZNq+HszvF+fC3anCm4nBmWEnbeIAfpDs6IStAEKhQ2YSgkjzVG2FF9XJqwwQh5bH3N9OUTUt4QwVN6MLMLtA=="], + "msgpackr": ["msgpackr@1.11.14", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-suPZQcjFtPGp0cksn70ICfLuxsO9F2/sRrbJzeNepojZ+OPwGzA0lNdLyU4SJUKAd5ZgvUWWPojzzdlVuOYcrQ=="], - "msgpackr-extract": ["msgpackr-extract@3.0.3", "", { "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, "optionalDependencies": { "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" }, "bin": { "download-msgpackr-prebuilds": "bin/download-prebuilds.js" } }, "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA=="], + "msgpackr-extract": ["msgpackr-extract@3.0.4", "", { "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, "optionalDependencies": { "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.4", "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.4", "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.4", "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.4", "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.4", "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4" }, "bin": { "download-msgpackr-prebuilds": "bin/download-prebuilds.js" } }, "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw=="], "multicast-dns": ["multicast-dns@7.2.5", "", { "dependencies": { "dns-packet": "^5.2.2", "thunky": "^1.0.2" }, "bin": { "multicast-dns": "cli.js" } }, "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg=="], @@ -2186,13 +2186,13 @@ "node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="], - "node-releases": ["node-releases@2.0.36", "", {}, "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA=="], + "node-releases": ["node-releases@2.0.47", "", {}, "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og=="], "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], "normalize-url": ["normalize-url@8.1.1", "", {}, "sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ=="], - "npm-check-updates": ["npm-check-updates@22.2.1", "", { "bin": { "npm-check-updates": "build/cli.js", "ncu": "build/cli.js" } }, "sha512-mGdIJfhtg+q0BzhbOpbOL73zhMZlgBQMG4wnBwPMMD5k96028UCuV0753YeSYk9odoh7HWK6/cY69bWxT7o+yg=="], + "npm-check-updates": ["npm-check-updates@22.2.3", "", { "bin": { "npm-check-updates": "build/cli.js", "ncu": "build/cli.js" } }, "sha512-n8HZ0ywZugYRgEqIm67hbZC5L/9S/gLX+MkGchJB43pP2xib1+u76OpVEJRZ4QP2BksPRi5M1CCStJVfc16+/A=="], "npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], @@ -2418,7 +2418,7 @@ "postcss-zindex": ["postcss-zindex@6.0.2", "", { "peerDependencies": { "postcss": "^8.4.31" } }, "sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg=="], - "prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="], + "prettier": ["prettier@3.8.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw=="], "pretty-error": ["pretty-error@4.0.0", "", { "dependencies": { "lodash": "^4.17.20", "renderkid": "^3.0.0" } }, "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw=="], @@ -2436,7 +2436,7 @@ "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], - "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], + "property-information": ["property-information@7.2.0", "", {}, "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg=="], "proto-list": ["proto-list@1.2.4", "", {}, "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA=="], @@ -2452,13 +2452,13 @@ "pvutils": ["pvutils@1.1.5", "", {}, "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA=="], - "qs": ["qs@6.14.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q=="], + "qs": ["qs@6.15.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw=="], "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], "quick-lru": ["quick-lru@5.1.1", "", {}, "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="], - "radix-ui": ["radix-ui@1.4.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-accessible-icon": "1.1.7", "@radix-ui/react-accordion": "1.2.12", "@radix-ui/react-alert-dialog": "1.1.15", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-aspect-ratio": "1.1.7", "@radix-ui/react-avatar": "1.1.10", "@radix-ui/react-checkbox": "1.3.3", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-context-menu": "2.2.16", "@radix-ui/react-dialog": "1.1.15", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-dropdown-menu": "2.1.16", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-form": "0.1.8", "@radix-ui/react-hover-card": "1.1.15", "@radix-ui/react-label": "2.1.7", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-menubar": "1.1.16", "@radix-ui/react-navigation-menu": "1.2.14", "@radix-ui/react-one-time-password-field": "0.1.8", "@radix-ui/react-password-toggle-field": "0.1.3", "@radix-ui/react-popover": "1.1.15", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-progress": "1.1.7", "@radix-ui/react-radio-group": "1.3.8", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-scroll-area": "1.2.10", "@radix-ui/react-select": "2.2.6", "@radix-ui/react-separator": "1.1.7", "@radix-ui/react-slider": "1.3.6", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-switch": "1.2.6", "@radix-ui/react-tabs": "1.1.13", "@radix-ui/react-toast": "1.2.15", "@radix-ui/react-toggle": "1.1.10", "@radix-ui/react-toggle-group": "1.1.11", "@radix-ui/react-toolbar": "1.1.11", "@radix-ui/react-tooltip": "1.2.8", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-escape-keydown": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-aWizCQiyeAenIdUbqEpXgRA1ya65P13NKn/W8rWkcN0OPkRDxdBVLWnIEDsS2RpwCK2nobI7oMUSmexzTDyAmA=="], + "radix-ui": ["radix-ui@1.5.0", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-accessible-icon": "1.1.9", "@radix-ui/react-accordion": "1.2.13", "@radix-ui/react-alert-dialog": "1.1.16", "@radix-ui/react-arrow": "1.1.9", "@radix-ui/react-aspect-ratio": "1.1.9", "@radix-ui/react-avatar": "1.1.12", "@radix-ui/react-checkbox": "1.3.4", "@radix-ui/react-collapsible": "1.1.13", "@radix-ui/react-collection": "1.1.9", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-context-menu": "2.3.0", "@radix-ui/react-dialog": "1.1.16", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.12", "@radix-ui/react-dropdown-menu": "2.1.17", "@radix-ui/react-focus-guards": "1.1.4", "@radix-ui/react-focus-scope": "1.1.9", "@radix-ui/react-form": "0.1.9", "@radix-ui/react-hover-card": "1.1.16", "@radix-ui/react-label": "2.1.9", "@radix-ui/react-menu": "2.1.17", "@radix-ui/react-menubar": "1.1.17", "@radix-ui/react-navigation-menu": "1.2.15", "@radix-ui/react-one-time-password-field": "0.1.9", "@radix-ui/react-password-toggle-field": "0.1.4", "@radix-ui/react-popover": "1.1.16", "@radix-ui/react-popper": "1.3.0", "@radix-ui/react-portal": "1.1.11", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-progress": "1.1.9", "@radix-ui/react-radio-group": "1.4.0", "@radix-ui/react-roving-focus": "1.1.12", "@radix-ui/react-scroll-area": "1.2.11", "@radix-ui/react-select": "2.3.0", "@radix-ui/react-separator": "1.1.9", "@radix-ui/react-slider": "1.4.0", "@radix-ui/react-slot": "1.2.5", "@radix-ui/react-switch": "1.3.0", "@radix-ui/react-tabs": "1.1.14", "@radix-ui/react-toast": "1.2.16", "@radix-ui/react-toggle": "1.1.11", "@radix-ui/react-toggle-group": "1.1.12", "@radix-ui/react-toolbar": "1.1.12", "@radix-ui/react-tooltip": "1.2.9", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-effect-event": "0.0.3", "@radix-ui/react-use-escape-keydown": "1.1.2", "@radix-ui/react-use-is-hydrated": "0.1.1", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-use-size": "1.1.2", "@radix-ui/react-visually-hidden": "1.2.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nzh2HNpClgB31FBHRqt2xG8XNUfVfQRpf34hACC5PNrXTd5JdXdqOXwLs3BL+D8CNYiNQiJiT8QGr5Q4vq+00w=="], "randombytes": ["randombytes@2.1.0", "", { "dependencies": { "safe-buffer": "^5.1.0" } }, "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ=="], @@ -2468,9 +2468,9 @@ "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], - "react": ["react@19.2.6", "", {}, "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q=="], + "react": ["react@19.2.7", "", {}, "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ=="], - "react-dom": ["react-dom@19.2.6", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.6" } }, "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g=="], + "react-dom": ["react-dom@19.2.7", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.7" } }, "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ=="], "react-fast-compare": ["react-fast-compare@3.2.2", "", {}, "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="], @@ -2524,7 +2524,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=="], "rehype-raw": ["rehype-raw@7.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-raw": "^9.0.0", "vfile": "^6.0.0" } }, "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww=="], @@ -2556,7 +2556,7 @@ "requires-port": ["requires-port@1.0.0", "", {}, "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="], - "resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], + "resolve": ["resolve@1.22.12", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA=="], "resolve-alpn": ["resolve-alpn@1.2.1", "", {}, "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="], @@ -2604,7 +2604,7 @@ "selfsigned": ["selfsigned@5.5.0", "", { "dependencies": { "@peculiar/x509": "^1.14.2", "pkijs": "^3.3.3" } }, "sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew=="], - "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "semver": ["semver@7.8.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ=="], "semver-diff": ["semver-diff@4.0.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA=="], @@ -2634,11 +2634,11 @@ "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - "shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="], + "shell-quote": ["shell-quote@1.8.4", "", {}, "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ=="], "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=="], @@ -2724,11 +2724,11 @@ "tagged-tag": ["tagged-tag@1.0.0", "", {}, "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng=="], - "tapable": ["tapable@2.3.2", "", {}, "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA=="], + "tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="], - "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=="], + "terser": ["terser@5.48.0", "", { "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-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q=="], - "terser-webpack-plugin": ["terser-webpack-plugin@5.4.0", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", "schema-utils": "^4.3.0", "terser": "^5.31.1" }, "peerDependencies": { "webpack": "^5.1.0" } }, "sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g=="], + "terser-webpack-plugin": ["terser-webpack-plugin@5.6.1", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", "schema-utils": "^4.3.0", "terser": "^5.31.1" }, "peerDependencies": { "webpack": "^5.1.0" } }, "sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ=="], "thingies": ["thingies@2.6.0", "", { "peerDependencies": { "tslib": "^2" } }, "sha512-rMHRjmlFLM1R96UYPvpmnc3LYtdFrT33JIB7L9hetGue1qAPfn1N2LJeEjxUSidu1Iku+haLZXDuEXUHNGO/lg=="], @@ -2784,7 +2784,7 @@ "typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], - "undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="], + "undici-types": ["undici-types@7.24.6", "", {}, "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg=="], "unicode-canonical-property-names-ecmascript": ["unicode-canonical-property-names-ecmascript@2.0.1", "", {}, "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg=="], @@ -2868,23 +2868,23 @@ "webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="], - "webpack": ["webpack@5.105.4", "", { "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.16.0", "acorn-import-phases": "^1.0.3", "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.20.0", "es-module-lexer": "^2.0.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.3.1", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^4.3.3", "tapable": "^2.3.0", "terser-webpack-plugin": "^5.3.17", "watchpack": "^2.5.1", "webpack-sources": "^3.3.4" }, "bin": { "webpack": "bin/webpack.js" } }, "sha512-jTywjboN9aHxFlToqb0K0Zs9SbBoW4zRUlGzI2tYNxVYcEi/IPpn+Xi4ye5jTLvX2YeLuic/IvxNot+Q1jMoOw=="], + "webpack": ["webpack@5.107.2", "", { "dependencies": { "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.16.0", "acorn-import-phases": "^1.0.3", "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.22.0", "es-module-lexer": "^2.1.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "loader-runner": "^4.3.2", "mime-db": "^1.54.0", "neo-async": "^2.6.2", "schema-utils": "^4.3.3", "tapable": "^2.3.0", "terser-webpack-plugin": "^5.5.0", "watchpack": "^2.5.1", "webpack-sources": "^3.5.0" }, "bin": { "webpack": "bin/webpack.js" } }, "sha512-v7RhXaJbpMlV0D7hC7lb2EbnxkoeUqf9qhKr6lozx3Q48pmFrqqNRmZFUEGmi7pSwm6fCQ2H1IjvCkHqdpVdjQ=="], "webpack-bundle-analyzer": ["webpack-bundle-analyzer@4.10.2", "", { "dependencies": { "@discoveryjs/json-ext": "0.5.7", "acorn": "^8.0.4", "acorn-walk": "^8.0.0", "commander": "^7.2.0", "debounce": "^1.2.1", "escape-string-regexp": "^4.0.0", "gzip-size": "^6.0.0", "html-escaper": "^2.0.2", "opener": "^1.5.2", "picocolors": "^1.0.0", "sirv": "^2.0.3", "ws": "^7.3.1" }, "bin": { "webpack-bundle-analyzer": "lib/bin/analyzer.js" } }, "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw=="], "webpack-dev-middleware": ["webpack-dev-middleware@7.4.5", "", { "dependencies": { "colorette": "^2.0.10", "memfs": "^4.43.1", "mime-types": "^3.0.1", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "schema-utils": "^4.0.0" }, "peerDependencies": { "webpack": "^5.0.0" }, "optionalPeers": ["webpack"] }, "sha512-uxQ6YqGdE4hgDKNf7hUiPXOdtkXvBJXrfEGYSx7P7LC8hnUYGK70X6xQXUvXeNyBDDcsiQXpG2m3G9vxowaEuA=="], - "webpack-dev-server": ["webpack-dev-server@5.2.3", "", { "dependencies": { "@types/bonjour": "^3.5.13", "@types/connect-history-api-fallback": "^1.5.4", "@types/express": "^4.17.25", "@types/express-serve-static-core": "^4.17.21", "@types/serve-index": "^1.9.4", "@types/serve-static": "^1.15.5", "@types/sockjs": "^0.3.36", "@types/ws": "^8.5.10", "ansi-html-community": "^0.0.8", "bonjour-service": "^1.2.1", "chokidar": "^3.6.0", "colorette": "^2.0.10", "compression": "^1.8.1", "connect-history-api-fallback": "^2.0.0", "express": "^4.22.1", "graceful-fs": "^4.2.6", "http-proxy-middleware": "^2.0.9", "ipaddr.js": "^2.1.0", "launch-editor": "^2.6.1", "open": "^10.0.3", "p-retry": "^6.2.0", "schema-utils": "^4.2.0", "selfsigned": "^5.5.0", "serve-index": "^1.9.1", "sockjs": "^0.3.24", "spdy": "^4.0.2", "webpack-dev-middleware": "^7.4.2", "ws": "^8.18.0" }, "peerDependencies": { "webpack": "^5.0.0" }, "optionalPeers": ["webpack"], "bin": { "webpack-dev-server": "bin/webpack-dev-server.js" } }, "sha512-9Gyu2F7+bg4Vv+pjbovuYDhHX+mqdqITykfzdM9UyKqKHlsE5aAjRhR+oOEfXW5vBeu8tarzlJFIZva4ZjAdrQ=="], + "webpack-dev-server": ["webpack-dev-server@5.2.4", "", { "dependencies": { "@types/bonjour": "^3.5.13", "@types/connect-history-api-fallback": "^1.5.4", "@types/express": "^4.17.25", "@types/express-serve-static-core": "^4.17.21", "@types/serve-index": "^1.9.4", "@types/serve-static": "^1.15.5", "@types/sockjs": "^0.3.36", "@types/ws": "^8.5.10", "ansi-html-community": "^0.0.8", "bonjour-service": "^1.2.1", "chokidar": "^3.6.0", "colorette": "^2.0.10", "compression": "^1.8.1", "connect-history-api-fallback": "^2.0.0", "express": "^4.22.1", "graceful-fs": "^4.2.6", "http-proxy-middleware": "^2.0.9", "ipaddr.js": "^2.1.0", "launch-editor": "^2.6.1", "open": "^10.0.3", "p-retry": "^6.2.0", "schema-utils": "^4.2.0", "selfsigned": "^5.5.0", "serve-index": "^1.9.1", "sockjs": "^0.3.24", "spdy": "^4.0.2", "webpack-dev-middleware": "^7.4.2", "ws": "^8.18.0" }, "peerDependencies": { "webpack": "^5.0.0" }, "optionalPeers": ["webpack"], "bin": { "webpack-dev-server": "bin/webpack-dev-server.js" } }, "sha512-GqDPGZN9bRqKBTkp4aWkobDDHMsrXKoGSdOH56smIri8qR0JG8gfL8/v/f/OZR3/OKXjG8uwJbFVhKm/FNU/UA=="], "webpack-merge": ["webpack-merge@5.10.0", "", { "dependencies": { "clone-deep": "^4.0.1", "flat": "^5.0.2", "wildcard": "^2.0.0" } }, "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA=="], - "webpack-sources": ["webpack-sources@3.3.4", "", {}, "sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q=="], + "webpack-sources": ["webpack-sources@3.5.0", "", {}, "sha512-HPuy+uuoTCaaoEoI1LQ3JN9+vrPBvEesnnX1jADHy728cHSMlq4wUc4afYqahq2B1mhQVZxCXOkNTnXltr+2vQ=="], "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], "webpackbar": ["webpackbar@7.0.0", "", { "dependencies": { "ansis": "^3.2.0", "consola": "^3.2.3", "pretty-time": "^1.1.0", "std-env": "^3.7.0" }, "peerDependencies": { "@rspack/core": "*", "webpack": "3 || 4 || 5" }, "optionalPeers": ["@rspack/core", "webpack"] }, "sha512-aS9soqSO2iCHgqHoCrj4LbfGQUboDCYJPSFOAchEK+9psIjNrfSWW4Y0YEz67MKURNvMmfo0ycOg9d/+OOf9/Q=="], - "websocket-driver": ["websocket-driver@0.7.4", "", { "dependencies": { "http-parser-js": ">=0.5.1", "safe-buffer": ">=5.1.0", "websocket-extensions": ">=0.1.1" } }, "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg=="], + "websocket-driver": ["websocket-driver@0.7.5", "", { "dependencies": { "http-parser-js": ">=0.5.1", "safe-buffer": ">=5.1.0", "websocket-extensions": ">=0.1.1" } }, "sha512-ZL2+3c7kMBdIRCMz6l8jQMHyGVxj+UL+xVk74Ombiciboca8rHa15L86B19E5oh1pL9Ii/uj54gtsIrZGMo6zA=="], "websocket-extensions": ["websocket-extensions@0.1.4", "", {}, "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg=="], @@ -2906,7 +2906,7 @@ "write-file-atomic": ["write-file-atomic@3.0.3", "", { "dependencies": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", "signal-exit": "^3.0.2", "typedarray-to-buffer": "^3.1.5" } }, "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q=="], - "ws": ["ws@8.20.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA=="], + "ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], "wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="], @@ -2942,22 +2942,12 @@ "@babel/preset-env/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "@docusaurus/bundler/postcss": ["postcss@8.5.12", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA=="], + "@docsearch/react/@algolia/autocomplete-core": ["@algolia/autocomplete-core@1.19.2", "", { "dependencies": { "@algolia/autocomplete-plugin-algolia-insights": "1.19.2", "@algolia/autocomplete-shared": "1.19.2" } }, "sha512-mKv7RyuAzXvwmq+0XRK8HqZXt9iZ5Kkm2huLjgn5JoCPtDy+oh9yxUMfDDaVCw0oyzZ1isdJBc7l9nuCyyR7Nw=="], "@docusaurus/core/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], "@docusaurus/core/webpack-merge": ["webpack-merge@6.0.1", "", { "dependencies": { "clone-deep": "^4.0.1", "flat": "^5.0.2", "wildcard": "^2.0.1" } }, "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg=="], - "@docusaurus/cssnano-preset/postcss": ["postcss@8.5.12", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA=="], - - "@docusaurus/module-type-aliases/@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], - - "@docusaurus/theme-classic/postcss": ["postcss@8.5.12", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA=="], - - "@docusaurus/theme-common/@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], - - "@docusaurus/types/@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], - "@docusaurus/utils/jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], "@jsonjoy.com/fs-snapshot/@jsonjoy.com/json-pack": ["@jsonjoy.com/json-pack@17.67.0", "", { "dependencies": { "@jsonjoy.com/base64": "17.67.0", "@jsonjoy.com/buffers": "17.67.0", "@jsonjoy.com/codegen": "17.67.0", "@jsonjoy.com/json-pointer": "17.67.0", "@jsonjoy.com/util": "17.67.0", "hyperdyperid": "^1.2.0", "thingies": "^2.5.0", "tree-dump": "^1.1.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w=="], @@ -2974,11 +2964,9 @@ "@svgr/hast-util-to-babel-ast/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], - "@types/react-router/@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], + "@types/serve-static/@types/send": ["@types/send@0.17.6", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og=="], - "@types/react-router-config/@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], - - "@types/react-router-dom/@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], + "accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], "accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], @@ -3002,8 +2990,6 @@ "cli-table3/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - "compressible/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], - "compression/bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], "compression/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], @@ -3016,12 +3002,8 @@ "crypto-random-string/type-fest": ["type-fest@1.4.0", "", {}, "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA=="], - "css-loader/postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="], - "css-minimizer-webpack-plugin/jest-worker": ["jest-worker@29.7.0", "", { "dependencies": { "@types/node": "*", "jest-util": "^29.7.0", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" } }, "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw=="], - "css-minimizer-webpack-plugin/postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="], - "css-select/domhandler": ["domhandler@4.3.1", "", { "dependencies": { "domelementtype": "^2.2.0" } }, "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ=="], "css-select/domutils": ["domutils@2.8.0", "", { "dependencies": { "dom-serializer": "^1.0.1", "domelementtype": "^2.2.0", "domhandler": "^4.2.0" } }, "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A=="], @@ -3194,6 +3176,8 @@ "micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + "mime-types/mime-db": ["mime-db@1.33.0", "", {}, "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ=="], + "null-loader/schema-utils": ["schema-utils@3.3.0", "", { "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" } }, "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg=="], "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], @@ -3224,28 +3208,22 @@ "rc/strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], - "react-loadable/@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], - "renderkid/htmlparser2": ["htmlparser2@6.1.0", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.0.0", "domutils": "^2.5.2", "entities": "^2.0.0" } }, "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A=="], "renderkid/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "rollup/@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], - - "rtlcss/postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="], - "send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], "send/range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], - "serve-handler/mime-types": ["mime-types@2.1.18", "", { "dependencies": { "mime-db": "~1.33.0" } }, "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ=="], - "serve-handler/path-to-regexp": ["path-to-regexp@3.3.0", "", {}, "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw=="], "serve-index/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], "serve-index/http-errors": ["http-errors@1.8.1", "", { "dependencies": { "depd": "~1.1.2", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": ">= 1.5.0 < 2", "toidentifier": "1.0.1" } }, "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g=="], + "serve-index/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "sitemap/@types/node": ["@types/node@17.0.45", "", {}, "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="], "source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], @@ -3264,10 +3242,14 @@ "tsyringe/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], + "type-is/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "update-notifier/boxen": ["boxen@7.1.1", "", { "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^7.0.1", "chalk": "^5.2.0", "cli-boxes": "^3.0.0", "string-width": "^5.1.2", "type-fest": "^2.13.0", "widest-line": "^4.0.1", "wrap-ansi": "^8.1.0" } }, "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog=="], "update-notifier/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + "url-loader/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "url-loader/schema-utils": ["schema-utils@3.3.0", "", { "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" } }, "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg=="], "vite-node/es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], @@ -3278,7 +3260,7 @@ "webpack-bundle-analyzer/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], - "webpack-bundle-analyzer/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + "webpack-bundle-analyzer/ws": ["ws@7.5.11", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA=="], "webpack-dev-middleware/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], @@ -3294,14 +3276,12 @@ "wsl-utils/is-wsl": ["is-wsl@3.1.1", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw=="], - "@docusaurus/bundler/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "@docsearch/react/@algolia/autocomplete-core/@algolia/autocomplete-plugin-algolia-insights": ["@algolia/autocomplete-plugin-algolia-insights@1.19.2", "", { "dependencies": { "@algolia/autocomplete-shared": "1.19.2" }, "peerDependencies": { "search-insights": ">= 1 < 3" } }, "sha512-TjxbcC/r4vwmnZaPwrHtkXNeqvlpdyR+oR9Wi2XyfORkiGkLTVhX2j+O9SaCCINbKoDfc+c2PB8NjfOnz7+oKg=="], + + "@docsearch/react/@algolia/autocomplete-core/@algolia/autocomplete-shared": ["@algolia/autocomplete-shared@1.19.2", "", { "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, "sha512-jEazxZTVD2nLrC+wYlVHQgpBoBB5KPStrJxLzsIFl6Kqd1AlG9sIAGl39V5tECLpIQzB3Qa2T6ZPJ1ChkwMK/w=="], "@docusaurus/core/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], - "@docusaurus/cssnano-preset/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - - "@docusaurus/theme-classic/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - "@jsonjoy.com/fs-snapshot/@jsonjoy.com/json-pack/@jsonjoy.com/base64": ["@jsonjoy.com/base64@17.67.0", "", { "peerDependencies": { "tslib": "2" } }, "sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw=="], "@jsonjoy.com/fs-snapshot/@jsonjoy.com/json-pack/@jsonjoy.com/codegen": ["@jsonjoy.com/codegen@17.67.0", "", { "peerDependencies": { "tslib": "2" } }, "sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q=="], @@ -3310,6 +3290,8 @@ "@jsonjoy.com/fs-snapshot/@jsonjoy.com/util/@jsonjoy.com/codegen": ["@jsonjoy.com/codegen@17.67.0", "", { "peerDependencies": { "tslib": "2" } }, "sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q=="], + "accepts/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + "ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "ansi-align/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], @@ -3324,19 +3306,15 @@ "copy-webpack-plugin/globby/slash": ["slash@4.0.0", "", {}, "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew=="], - "css-loader/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - "css-minimizer-webpack-plugin/jest-worker/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], - "css-minimizer-webpack-plugin/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - "css-select/domutils/dom-serializer": ["dom-serializer@1.4.1", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", "entities": "^2.0.0" } }, "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag=="], "csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="], "express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], - "file-loader/schema-utils/ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="], + "file-loader/schema-utils/ajv": ["ajv@6.15.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw=="], "file-loader/schema-utils/ajv-keywords": ["ajv-keywords@3.5.2", "", { "peerDependencies": { "ajv": "^6.9.1" } }, "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="], @@ -3354,7 +3332,7 @@ "mdast-util-gfm-autolink-literal/micromark-util-character/micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="], - "null-loader/schema-utils/ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="], + "null-loader/schema-utils/ajv": ["ajv@6.15.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw=="], "null-loader/schema-utils/ajv-keywords": ["ajv-keywords@3.5.2", "", { "peerDependencies": { "ajv": "^6.9.1" } }, "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="], @@ -3364,28 +3342,28 @@ "renderkid/htmlparser2/entities": ["entities@2.2.0", "", {}, "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="], - "rtlcss/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - "send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], - "serve-handler/mime-types/mime-db": ["mime-db@1.33.0", "", {}, "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ=="], - "serve-index/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "serve-index/http-errors/depd": ["depd@1.1.2", "", {}, "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="], "serve-index/http-errors/statuses": ["statuses@1.5.0", "", {}, "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA=="], + "serve-index/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "type-is/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + "update-notifier/boxen/camelcase": ["camelcase@7.0.1", "", {}, "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw=="], "update-notifier/boxen/type-fest": ["type-fest@2.19.0", "", {}, "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="], - "url-loader/schema-utils/ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="], + "url-loader/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "url-loader/schema-utils/ajv": ["ajv@6.15.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw=="], "url-loader/schema-utils/ajv-keywords": ["ajv-keywords@3.5.2", "", { "peerDependencies": { "ajv": "^6.9.1" } }, "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="], - "webpack-dev-middleware/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], - "webpack-dev-server/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], "webpack-dev-server/open/define-lazy-prop": ["define-lazy-prop@3.0.0", "", {}, "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="], -- 2.52.0 From 05a8ae9ae4ac3f5cdb4494d1e1c070d5d3e6b4b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 8 Jun 2026 21:47:27 +0200 Subject: [PATCH 008/133] Docs --- packages/docs/docs/getting-started.md | 61 ++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/packages/docs/docs/getting-started.md b/packages/docs/docs/getting-started.md index 0f3eecb..ef07f77 100644 --- a/packages/docs/docs/getting-started.md +++ b/packages/docs/docs/getting-started.md @@ -86,27 +86,41 @@ provider so route components can be converted with the same runtime context. ## Write Your First Component Use `Component.make` when you want automatic tracing spans, or -`Component.makeUntraced` when you only want the component behavior. +`Component.makeUntraced` when you only want the component behavior. This creates +an Effect-FC component, not a plain React component yet. -```tsx title="src/Hello.tsx" +```tsx title="src/HelloEffect.tsx" import { Effect } from "effect" import { Component } from "effect-fc" -import { runtime } from "./runtime" -const HelloEffect = Component.make("HelloEffect")(function* (props: { +export const HelloEffect = Component.make("HelloEffect")(function* (props: { readonly name: string }) { const message = yield* Effect.succeed(`Hello, ${props.name}`) return

{message}

}) +``` + +At this point `HelloEffect` can yield Effects, services, and scoped lifecycle +work, but React cannot render it directly. + +## Apply A Runtime + +Use `Component.withRuntime` at the boundary where an Effect-FC component needs +to become a regular React function component. + +```tsx title="src/Hello.tsx" +import { Component } from "effect-fc" +import { HelloEffect } from "./HelloEffect" +import { runtime } from "./runtime" export const Hello = HelloEffect.pipe( Component.withRuntime(runtime.context), ) ``` -`Hello` is now a regular React component: +`Hello` is now a normal React component: ```tsx title="src/App.tsx" import { Hello } from "./Hello" @@ -116,6 +130,43 @@ export function App() { } ``` +## Use Effect-FC Components Together + +Inside an Effect-FC component, other Effect-FC components are available through +their `.use` effect. Yield `.use` to get a React component for the current +runtime context, then render it like JSX. + +```tsx title="src/GreetingCardEffect.tsx" +import { Component } from "effect-fc" +import { HelloEffect } from "./HelloEffect" + +export const GreetingCardEffect = Component.make("GreetingCard")( + function* () { + const Hello = yield* HelloEffect.use + + return ( +
+ +

This component is still running inside Effect-FC.

+
+ ) + }, +) +``` + +Use `Component.withRuntime` only when you leave the Effect-FC tree and need a +normal React component again: + +```tsx title="src/GreetingCard.tsx" +import { Component } from "effect-fc" +import { GreetingCardEffect } from "./GreetingCardEffect" +import { runtime } from "./runtime" + +export const GreetingCard = GreetingCardEffect.pipe( + Component.withRuntime(runtime.context), +) +``` + ## Use Services Components can yield Effect services directly. Define services with Effect, -- 2.52.0 From d40ac326ec4ff2452af3818c4fdbf57cca58ea72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 8 Jun 2026 22:01:58 +0200 Subject: [PATCH 009/133] Docs --- packages/docs/docs/getting-started.md | 53 +++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/packages/docs/docs/getting-started.md b/packages/docs/docs/getting-started.md index ef07f77..5076eb5 100644 --- a/packages/docs/docs/getting-started.md +++ b/packages/docs/docs/getting-started.md @@ -167,6 +167,59 @@ export const GreetingCard = GreetingCardEffect.pipe( ) ``` +## Component Lifecycle + +Every Effect-FC component instance exposes an Effect `Scope`. Effects that need +`Scope.Scope` can use that component scope to register finalizers, fork scoped +work, or acquire scoped resources. + +The component scope is created when React mounts the component and closes when +React unmounts it. When the scope closes, Effect runs the finalizers registered +inside that scope. + +```tsx title="src/MountedMessageEffect.tsx" +import { Console, Effect } from "effect" +import { Component } from "effect-fc" + +export const MountedMessageEffect = Component.make("MountedMessage")( + function* () { + yield* Component.useOnMount(() => + Effect.gen(function* () { + yield* Console.log("MountedMessage mounted") + yield* Effect.addFinalizer(() => + Console.log("MountedMessage unmounted"), + ) + }), + ) + + return

This component owns an Effect scope.

+ }, +) +``` + +Use `Component.useOnMount` for work that should run once for the component +instance. Use `Component.useOnChange` when the scoped work should be recreated +when dependencies change. + +```tsx +const UserPanelEffect = Component.make("UserPanel")( + function* (props: { readonly userId: string }) { + const user = yield* Component.useOnChange( + () => fetchUser(props.userId), + [props.userId], + ) + + return

{user.name}

+ }, +) +``` + +In this example, changing `userId` closes the previous dependency scope before +creating a new one. Unlike `useOnMount`, `useOnChange` does not expose the component's root scope directly. +It creates and provides its own scope for that dependency window. Some other Effect-FC hooks +follow the same pattern when they need a lifecycle that is narrower than the +whole component instance. + ## Use Services Components can yield Effect services directly. Define services with Effect, -- 2.52.0 From a8d4520fed1fb691efd29dd9bcc8c06b3cf33af8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 8 Jun 2026 22:03:42 +0200 Subject: [PATCH 010/133] Docs --- packages/docs/docs/getting-started.md | 34 +++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/packages/docs/docs/getting-started.md b/packages/docs/docs/getting-started.md index 5076eb5..554183c 100644 --- a/packages/docs/docs/getting-started.md +++ b/packages/docs/docs/getting-started.md @@ -215,10 +215,36 @@ const UserPanelEffect = Component.make("UserPanel")( ``` In this example, changing `userId` closes the previous dependency scope before -creating a new one. Unlike `useOnMount`, `useOnChange` does not expose the component's root scope directly. -It creates and provides its own scope for that dependency window. Some other Effect-FC hooks -follow the same pattern when they need a lifecycle that is narrower than the -whole component instance. +creating a new one. Unlike `useOnMount`, `useOnChange` does not expose the +component's root scope directly. It creates and provides its own scope for that +dependency window. Some other Effect-FC hooks follow the same pattern when they +need a lifecycle that is narrower than the whole component instance. + +## Use React Normally + +An Effect-FC component is still a React function component. Anything you can do +in a regular React component can also be done in an Effect-FC component, +including React hooks, refs, event handlers, context, and JSX composition. + +```tsx +import { Component } from "effect-fc" +import * as React from "react" + +const CounterEffect = Component.make("Counter")(function* () { + const [count, setCount] = React.useState(0) + const buttonRef = React.useRef(null) + + return ( + + ) +}) +``` + +Use regular React hooks for local UI concerns, and reach for Effect-FC helpers +when the component needs Effect services, scopes, resources, or Effect-powered +callbacks. ## Use Services -- 2.52.0 From c941e5970af24f2f3259aa41e9b5788aa21c9618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 8 Jun 2026 22:19:58 +0200 Subject: [PATCH 011/133] Docs --- packages/docs/docs/getting-started.md | 63 +++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/packages/docs/docs/getting-started.md b/packages/docs/docs/getting-started.md index 554183c..cae6a2e 100644 --- a/packages/docs/docs/getting-started.md +++ b/packages/docs/docs/getting-started.md @@ -220,6 +220,69 @@ component's root scope directly. It creates and provides its own scope for that dependency window. Some other Effect-FC hooks follow the same pattern when they need a lifecycle that is narrower than the whole component instance. +## Useful Effect-FC Hooks + +The most common Effect-FC hooks are: + +- `Component.useOnMount`: run an Effect once for the component instance and + return its value. + +```tsx +const initialData = yield* Component.useOnMount(() => loadInitialData) +``` + +- `Component.useOnChange`: run an Effect again when dependencies change and + return its latest value. + +```tsx +const user = yield* Component.useOnChange(() => fetchUser(id), [id]) +``` + +- `Component.useReactEffect`: Effect-powered `React.useEffect` with scoped + finalizers. + +```tsx +yield* Component.useReactEffect(() => subscribe(id), [id]) +``` + +- `Component.useReactLayoutEffect`: Effect-powered `React.useLayoutEffect`. + +```tsx +yield* Component.useReactLayoutEffect(() => measure(ref), []) +``` + +- `Component.useRunSync` and `Component.useRunPromise`: run Effects from React + event handlers. + +```tsx +const runPromise = yield* Component.useRunPromise() + +return ( + +) +``` + +- `Component.useCallbackSync` and `Component.useCallbackPromise`: create stable + React callbacks. + +```tsx +const save = yield* Component.useCallbackPromise( + (user: User) => saveUser(user), + [], +) + +return +``` + +- `Component.useContextFromLayer`: create a React runtime context from an Effect + `Layer`. + +```tsx +yield* Component.useContextFromLayer(UserLive) +``` + ## Use React Normally An Effect-FC component is still a React function component. Anything you can do -- 2.52.0 From 8303c1f70e0bfe6174c8efda021f69eb43dd3bbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 8 Jun 2026 22:23:49 +0200 Subject: [PATCH 012/133] Docs --- packages/docs/docs/getting-started.md | 28 ++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/packages/docs/docs/getting-started.md b/packages/docs/docs/getting-started.md index cae6a2e..68c9d7f 100644 --- a/packages/docs/docs/getting-started.md +++ b/packages/docs/docs/getting-started.md @@ -183,29 +183,42 @@ import { Component } from "effect-fc" export const MountedMessageEffect = Component.make("MountedMessage")( function* () { - yield* Component.useOnMount(() => + const message = yield* Component.useOnMount(() => Effect.gen(function* () { yield* Console.log("MountedMessage mounted") yield* Effect.addFinalizer(() => Console.log("MountedMessage unmounted"), ) + + return "This value was loaded on mount." }), ) - return

This component owns an Effect scope.

+ return

{message}

}, ) ``` Use `Component.useOnMount` for work that should run once for the component -instance. Use `Component.useOnChange` when the scoped work should be recreated -when dependencies change. +instance and return a cached value. Use `Component.useOnChange` when the scoped +work should be recreated when dependencies change. ```tsx +import { Console, Effect } from "effect" +import { Component } from "effect-fc" + const UserPanelEffect = Component.make("UserPanel")( function* (props: { readonly userId: string }) { const user = yield* Component.useOnChange( - () => fetchUser(props.userId), + () => + Effect.gen(function* () { + yield* Console.log(`Loading ${props.userId}`) + yield* Effect.addFinalizer(() => + Console.log(`Cleaning up ${props.userId}`), + ) + + return yield* fetchUser(props.userId) + }), [props.userId], ) @@ -214,8 +227,9 @@ const UserPanelEffect = Component.make("UserPanel")( ) ``` -In this example, changing `userId` closes the previous dependency scope before -creating a new one. Unlike `useOnMount`, `useOnChange` does not expose the +In this example, each `userId` gets its own scope. When `userId` changes, +Effect-FC closes the previous scope, runs its finalizers, and creates a new +scope for the next load. Unlike `useOnMount`, `useOnChange` does not expose the component's root scope directly. It creates and provides its own scope for that dependency window. Some other Effect-FC hooks follow the same pattern when they need a lifecycle that is narrower than the whole component instance. -- 2.52.0 From 74a8e3a102898d97c504ed53e0714ebdb9ee4da6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 9 Jun 2026 10:42:42 +0200 Subject: [PATCH 013/133] Fix --- packages/docs/docs/getting-started.md | 58 ++++++++------------------- 1 file changed, 17 insertions(+), 41 deletions(-) diff --git a/packages/docs/docs/getting-started.md b/packages/docs/docs/getting-started.md index 68c9d7f..574adb5 100644 --- a/packages/docs/docs/getting-started.md +++ b/packages/docs/docs/getting-started.md @@ -89,11 +89,11 @@ Use `Component.make` when you want automatic tracing spans, or `Component.makeUntraced` when you only want the component behavior. This creates an Effect-FC component, not a plain React component yet. -```tsx title="src/HelloEffect.tsx" +```tsx title="src/HelloView.tsx" import { Effect } from "effect" import { Component } from "effect-fc" -export const HelloEffect = Component.make("HelloEffect")(function* (props: { +export const HelloView = Component.make("HelloView")(function* (props: { readonly name: string }) { const message = yield* Effect.succeed(`Hello, ${props.name}`) @@ -102,7 +102,7 @@ export const HelloEffect = Component.make("HelloEffect")(function* (props: { }) ``` -At this point `HelloEffect` can yield Effects, services, and scoped lifecycle +At this point `HelloView` can yield Effects, services, and scoped lifecycle work, but React cannot render it directly. ## Apply A Runtime @@ -112,10 +112,10 @@ to become a regular React function component. ```tsx title="src/Hello.tsx" import { Component } from "effect-fc" -import { HelloEffect } from "./HelloEffect" +import { HelloView } from "./HelloView" import { runtime } from "./runtime" -export const Hello = HelloEffect.pipe( +export const Hello = HelloView.pipe( Component.withRuntime(runtime.context), ) ``` @@ -136,13 +136,13 @@ Inside an Effect-FC component, other Effect-FC components are available through their `.use` effect. Yield `.use` to get a React component for the current runtime context, then render it like JSX. -```tsx title="src/GreetingCardEffect.tsx" +```tsx title="src/GreetingCardView.tsx" import { Component } from "effect-fc" -import { HelloEffect } from "./HelloEffect" +import { HelloView } from "./HelloView" -export const GreetingCardEffect = Component.make("GreetingCard")( +export const GreetingCardView = Component.make("GreetingCard")( function* () { - const Hello = yield* HelloEffect.use + const Hello = yield* HelloView.use return (
@@ -159,10 +159,10 @@ normal React component again: ```tsx title="src/GreetingCard.tsx" import { Component } from "effect-fc" -import { GreetingCardEffect } from "./GreetingCardEffect" +import { GreetingCardView } from "./GreetingCardView" import { runtime } from "./runtime" -export const GreetingCard = GreetingCardEffect.pipe( +export const GreetingCard = GreetingCardView.pipe( Component.withRuntime(runtime.context), ) ``` @@ -177,11 +177,11 @@ The component scope is created when React mounts the component and closes when React unmounts it. When the scope closes, Effect runs the finalizers registered inside that scope. -```tsx title="src/MountedMessageEffect.tsx" +```tsx title="src/MountedMessageView.tsx" import { Console, Effect } from "effect" import { Component } from "effect-fc" -export const MountedMessageEffect = Component.make("MountedMessage")( +export const MountedMessageView = Component.make("MountedMessage")( function* () { const message = yield* Component.useOnMount(() => Effect.gen(function* () { @@ -207,7 +207,7 @@ work should be recreated when dependencies change. import { Console, Effect } from "effect" import { Component } from "effect-fc" -const UserPanelEffect = Component.make("UserPanel")( +const UserPanelView = Component.make("UserPanel")( function* (props: { readonly userId: string }) { const user = yield* Component.useOnChange( () => @@ -307,7 +307,7 @@ including React hooks, refs, event handlers, context, and JSX composition. import { Component } from "effect-fc" import * as React from "react" -const CounterEffect = Component.make("Counter")(function* () { +const CounterView = Component.make("Counter")(function* () { const [count, setCount] = React.useState(0) const buttonRef = React.useRef(null) @@ -362,7 +362,7 @@ import { Component } from "effect-fc" import { runtime } from "./runtime" import { GreetingService } from "./services" -const GreetingEffect = Component.make("Greeting")(function* (props: { +const GreetingView = Component.make("Greeting")(function* (props: { readonly name: string }) { const greeting = yield* GreetingService @@ -370,35 +370,11 @@ const GreetingEffect = Component.make("Greeting")(function* (props: { return

{greeting.greet(props.name)}

}) -export const Greeting = GreetingEffect.pipe( +export const Greeting = GreetingView.pipe( Component.withRuntime(runtime.context), ) ``` -## Mount And Cleanup Effects - -Use `Component.useOnMount` for scoped work that should start when the component -mounts and finalize when it unmounts. - -```tsx -import { Console, Effect } from "effect" -import { Component } from "effect-fc" - -const Mounted = Component.make("Mounted")(function* () { - yield* Component.useOnMount(() => - Effect.gen(function* () { - yield* Console.log("Mounted") - yield* Effect.addFinalizer(() => Console.log("Unmounted")) - }), - ) - - return

Open the console, then unmount me.

-}) -``` - -Finalizers are tied to the component scope, so this is the right place for -subscriptions, resources, and other lifecycle-bound Effects. - ## Where To Go Next Once the runtime and component boundary are in place, the rest of the library -- 2.52.0 From 88b3a1c98a3f291c62fba7f994a52b8dd97c9f4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 9 Jun 2026 10:48:46 +0200 Subject: [PATCH 014/133] Docs --- packages/docs/docs/getting-started.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/docs/docs/getting-started.md b/packages/docs/docs/getting-started.md index 574adb5..98b84d3 100644 --- a/packages/docs/docs/getting-started.md +++ b/packages/docs/docs/getting-started.md @@ -89,6 +89,11 @@ Use `Component.make` when you want automatic tracing spans, or `Component.makeUntraced` when you only want the component behavior. This creates an Effect-FC component, not a plain React component yet. +The Effect run during render must be synchronous. Effect-FC follows React's +render model, so component bodies should produce JSX without waiting on async +work. Use lifecycle hooks, callbacks, queries, or other Effect-FC helpers for +async work that happens outside render. + ```tsx title="src/HelloView.tsx" import { Effect } from "effect" import { Component } from "effect-fc" -- 2.52.0 From 0a59fdf2b40b77ce15e37c7d289ceb3bae3577eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 9 Jun 2026 10:54:00 +0200 Subject: [PATCH 015/133] Docs --- packages/docs/docs/getting-started.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/docs/docs/getting-started.md b/packages/docs/docs/getting-started.md index 98b84d3..388ac9f 100644 --- a/packages/docs/docs/getting-started.md +++ b/packages/docs/docs/getting-started.md @@ -214,20 +214,20 @@ import { Component } from "effect-fc" const UserPanelView = Component.make("UserPanel")( function* (props: { readonly userId: string }) { - const user = yield* Component.useOnChange( + const label = yield* Component.useOnChange( () => Effect.gen(function* () { - yield* Console.log(`Loading ${props.userId}`) + yield* Console.log(`Preparing view for ${props.userId}`) yield* Effect.addFinalizer(() => Console.log(`Cleaning up ${props.userId}`), ) - return yield* fetchUser(props.userId) + return `Viewing user ${props.userId}` }), [props.userId], ) - return

{user.name}

+ return

{label}

}, ) ``` -- 2.52.0 From d3525aaad7b5b0033351d2ac86691078602579ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 9 Jun 2026 11:09:47 +0200 Subject: [PATCH 016/133] Docs --- packages/docs/docs/getting-started.md | 28 ++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/packages/docs/docs/getting-started.md b/packages/docs/docs/getting-started.md index 388ac9f..192fc1a 100644 --- a/packages/docs/docs/getting-started.md +++ b/packages/docs/docs/getting-started.md @@ -204,9 +204,14 @@ export const MountedMessageView = Component.make("MountedMessage")( ) ``` -Use `Component.useOnMount` for work that should run once for the component -instance and return a cached value. Use `Component.useOnChange` when the scoped -work should be recreated when dependencies change. +Use `Component.useOnMount` when the component needs a value produced by an +Effect during its first render. The value is then cached for the component +instance. + +Use `Component.useOnChange` when render needs the value computed by +scoped work that depends on changing inputs. When a dependency changes, React +re-renders the component and the hook computes the next value during that +render. ```tsx import { Console, Effect } from "effect" @@ -243,18 +248,22 @@ need a lifecycle that is narrower than the whole component instance. The most common Effect-FC hooks are: -- `Component.useOnMount`: run an Effect once for the component instance and - return its value. +Unlike plain React hooks, Effect-FC hooks return Effects. Use `yield*` to run +them inside the component body. + +- `Component.useOnMount`: run an Effect once during the component's first render + after mount, and return its value. The Effect must be synchronous. ```tsx const initialData = yield* Component.useOnMount(() => loadInitialData) ``` -- `Component.useOnChange`: run an Effect again when dependencies change and - return its latest value. +- `Component.useOnChange`: when a dependency changes, React re-renders the + component and the Effect is run again inside the component body. It returns + the latest value, so the Effect must be synchronous too. ```tsx -const user = yield* Component.useOnChange(() => fetchUser(id), [id]) +const label = yield* Component.useOnChange(() => formatUserLabel(id), [id]) ``` - `Component.useReactEffect`: Effect-powered `React.useEffect` with scoped @@ -264,7 +273,8 @@ const user = yield* Component.useOnChange(() => fetchUser(id), [id]) yield* Component.useReactEffect(() => subscribe(id), [id]) ``` -- `Component.useReactLayoutEffect`: Effect-powered `React.useLayoutEffect`. +- `Component.useReactLayoutEffect`: Effect-powered `React.useLayoutEffect` with scoped + finalizers. ```tsx yield* Component.useReactLayoutEffect(() => measure(ref), []) -- 2.52.0 From 91670779fe82152f791185b3702e4c70313ba4a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 9 Jun 2026 11:16:33 +0200 Subject: [PATCH 017/133] Docs --- packages/docs/docs/getting-started.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/docs/docs/getting-started.md b/packages/docs/docs/getting-started.md index 192fc1a..d2856f3 100644 --- a/packages/docs/docs/getting-started.md +++ b/packages/docs/docs/getting-started.md @@ -206,7 +206,8 @@ export const MountedMessageView = Component.make("MountedMessage")( Use `Component.useOnMount` when the component needs a value produced by an Effect during its first render. The value is then cached for the component -instance. +instance. You can also use it to set up scoped component logic, subscriptions, +or resources that should live for the lifetime of that component instance. Use `Component.useOnChange` when render needs the value computed by scoped work that depends on changing inputs. When a dependency changes, React -- 2.52.0 From 3000ff2d875634f621c24398e0921f812963f5eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 9 Jun 2026 11:18:08 +0200 Subject: [PATCH 018/133] Docs --- packages/docs/docs/getting-started.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/docs/docs/getting-started.md b/packages/docs/docs/getting-started.md index d2856f3..d2ab3f4 100644 --- a/packages/docs/docs/getting-started.md +++ b/packages/docs/docs/getting-started.md @@ -182,6 +182,10 @@ The component scope is created when React mounts the component and closes when React unmounts it. When the scope closes, Effect runs the finalizers registered inside that scope. +Finalizers are forked when the scope closes, so cleanup logic can run +asynchronous Effects even though the component body itself must stay +synchronous. + ```tsx title="src/MountedMessageView.tsx" import { Console, Effect } from "effect" import { Component } from "effect-fc" -- 2.52.0 From eb81ff16d2f4cad22e74241dd12916c99879f3f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 9 Jun 2026 11:27:39 +0200 Subject: [PATCH 019/133] Docs --- packages/docs/docs/getting-started.md | 51 ++++++--------------------- 1 file changed, 11 insertions(+), 40 deletions(-) diff --git a/packages/docs/docs/getting-started.md b/packages/docs/docs/getting-started.md index d2ab3f4..af28d4f 100644 --- a/packages/docs/docs/getting-started.md +++ b/packages/docs/docs/getting-started.md @@ -343,45 +343,13 @@ Use regular React hooks for local UI concerns, and reach for Effect-FC helpers when the component needs Effect services, scopes, resources, or Effect-powered callbacks. -## Use Services +## Use Tags -Components can yield Effect services directly. Define services with Effect, -provide them in your runtime layer, then consume them from the component body. - -```ts title="src/services.ts" -import { Effect } from "effect" - -export class GreetingService extends Effect.Service()( - "GreetingService", - { - succeed: { - greet: (name: string) => `Welcome, ${name}`, - }, - }, -) {} -``` - -Provide the service in your runtime: - -```tsx title="src/runtime.ts" -import { Layer } from "effect" -import { ReactRuntime } from "effect-fc" -import { GreetingService } from "./services" - -const AppLive = Layer.empty.pipe( - Layer.provideMerge(GreetingService.Default), -) - -export const runtime = ReactRuntime.make(AppLive) -``` - -Then read it inside a component: +Components can yield Effect tags directly in the component body. There is no +need to memoize the service yourself; just yield the tag wherever the component +needs it. ```tsx title="src/Greeting.tsx" -import { Component } from "effect-fc" -import { runtime } from "./runtime" -import { GreetingService } from "./services" - const GreetingView = Component.make("Greeting")(function* (props: { readonly name: string }) { @@ -389,12 +357,15 @@ const GreetingView = Component.make("Greeting")(function* (props: { return

{greeting.greet(props.name)}

}) - -export const Greeting = GreetingView.pipe( - Component.withRuntime(runtime.context), -) ``` +Service values in the runtime context are reactive by default. If a provided +service instance changes, Effect-FC recreates the React component function for +components that depend on that context, so the component reads the new service +and its scoped lifecycle can restart with the new environment. For services that +should not trigger that behavior, pass their tags with `Component.withOptions({ +nonReactiveTags: [...] })`. + ## Where To Go Next Once the runtime and component boundary are in place, the rest of the library -- 2.52.0 From 054f8773f07ffb925eec5e4cf67d8d9d6db7a3fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 9 Jun 2026 11:34:13 +0200 Subject: [PATCH 020/133] Docs --- packages/docs/docs/getting-started.md | 29 +++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/packages/docs/docs/getting-started.md b/packages/docs/docs/getting-started.md index af28d4f..be478f3 100644 --- a/packages/docs/docs/getting-started.md +++ b/packages/docs/docs/getting-started.md @@ -366,6 +366,35 @@ and its scoped lifecycle can restart with the new environment. For services that should not trigger that behavior, pass their tags with `Component.withOptions({ nonReactiveTags: [...] })`. +## Provide Services To A Subtree + +Use `Component.useContextFromLayer` when an Effect-FC component should provide +extra services to another Effect-FC component. It turns a `Layer` into a runtime +context that can be provided to `.use`. + +```tsx title="src/GreetingPageView.tsx" +import { Effect } from "effect" +import { Component } from "effect-fc" +import { GreetingView } from "./Greeting" +import { GreetingService } from "./services" + +const GreetingLive = GreetingService.Default({ + greet: (name: string) => `Welcome, ${name}`, +}) + +export const GreetingPageView = Component.make("GreetingPage")(function* () { + const context = yield* Component.useContextFromLayer(GreetingLive) + const Greeting = yield* Effect.provide(GreetingView.use, context) + + return +}) +``` + +The layer passed to `useContextFromLayer` should be stable. If a new layer value +is created on every render, Effect-FC has to build a new context, which can +cause unnecessary re-renders and scoped lifecycle restarts. Define static layers +outside the component, or memoize layers that depend on React props or state. + ## Where To Go Next Once the runtime and component boundary are in place, the rest of the library -- 2.52.0 From 86bdb5374eea7932591f5b871890967ba70b44d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 9 Jun 2026 13:35:58 +0200 Subject: [PATCH 021/133] Docs --- packages/docs/docs/getting-started.md | 32 ++++++++++++++------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/packages/docs/docs/getting-started.md b/packages/docs/docs/getting-started.md index be478f3..ec691f4 100644 --- a/packages/docs/docs/getting-started.md +++ b/packages/docs/docs/getting-started.md @@ -271,11 +271,15 @@ const initialData = yield* Component.useOnMount(() => loadInitialData) const label = yield* Component.useOnChange(() => formatUserLabel(id), [id]) ``` -- `Component.useReactEffect`: Effect-powered `React.useEffect` with scoped - finalizers. +- `Component.useReactEffect`: Effect-powered `React.useEffect` for side effects + that do not need to compute render output or trigger a re-render. The Effect + must be synchronous and can register scoped finalizers. ```tsx -yield* Component.useReactEffect(() => subscribe(id), [id]) +yield* Component.useReactEffect( + () => Effect.forkScoped(subscribeToUser(id)), + [id], +) ``` - `Component.useReactLayoutEffect`: Effect-powered `React.useLayoutEffect` with scoped @@ -310,13 +314,6 @@ const save = yield* Component.useCallbackPromise( return ``` -- `Component.useContextFromLayer`: create a React runtime context from an Effect - `Layer`. - -```tsx -yield* Component.useContextFromLayer(UserLive) -``` - ## Use React Normally An Effect-FC component is still a React function component. Anything you can do @@ -360,11 +357,11 @@ const GreetingView = Component.make("Greeting")(function* (props: { ``` Service values in the runtime context are reactive by default. If a provided -service instance changes, Effect-FC recreates the React component function for -components that depend on that context, so the component reads the new service -and its scoped lifecycle can restart with the new environment. For services that -should not trigger that behavior, pass their tags with `Component.withOptions({ -nonReactiveTags: [...] })`. +service instance changes, Effect-FC unmounts and mounts again the components +that depend on that context, so they read the new service and restart their +scoped lifecycle with the new environment. For services that should not trigger +that behavior, pass their tags with `Component.withOptions({ nonReactiveTags: +[...] })`. ## Provide Services To A Subtree @@ -395,6 +392,11 @@ is created on every render, Effect-FC has to build a new context, which can cause unnecessary re-renders and scoped lifecycle restarts. Define static layers outside the component, or memoize layers that depend on React props or state. +Layers built with `useContextFromLayer` are scoped to the component that builds +them. That means service construction can register scoped logic, acquire +resources, fork scoped fibers, and add finalizers that are tied to that +component's lifecycle. + ## Where To Go Next Once the runtime and component boundary are in place, the rest of the library -- 2.52.0 From c492408969c819f3efcdccd96ba8b0c36d3e489e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 9 Jun 2026 14:50:25 +0200 Subject: [PATCH 022/133] Docs --- packages/docs/docs/getting-started.md | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/docs/docs/getting-started.md b/packages/docs/docs/getting-started.md index ec691f4..be19515 100644 --- a/packages/docs/docs/getting-started.md +++ b/packages/docs/docs/getting-started.md @@ -10,26 +10,28 @@ component body you can yield services, run Effects, subscribe to Effect-powered state, and still export a normal React function component at the edge of your app. -This guide starts with the smallest useful setup: - -1. Install `effect-fc` with its peer dependencies. -2. Create a React runtime from an Effect `Layer`. -3. Wrap your React app with `ReactRuntime.Provider`. -4. Write a component with `Component.make`. -5. Convert it to a React component with `Component.withRuntime`. - ## Install Install `effect-fc` alongside `effect` and React 19.2 or newer: ```bash npm2yarn -npm install effect-fc effect react react-dom +npm install effect-fc effect react +``` +```bash npm2yarn +npm install --save-dev @types/react ``` -If your project uses TypeScript, also install React's type packages: +`effect-fc` is not opinionated about the React platform. Use it with web, +native, custom renderers, or any environment where React components can run. +Then install the platform-specific React packages for your target. + +For web apps, install React DOM: ```bash npm2yarn -npm install --save-dev @types/react @types/react-dom +npm install react-dom +``` +```bash npm2yarn +npm install --save-dev @types/react-dom ``` ## Create A Runtime -- 2.52.0 From fe65cd96aa99f6035c856eaa5467b34416e5dbed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 9 Jun 2026 15:30:00 +0200 Subject: [PATCH 023/133] Docs --- packages/docs/docs/state-management.md | 114 +++++++++++++++++++++++++ packages/docs/sidebars.ts | 2 +- 2 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 packages/docs/docs/state-management.md diff --git a/packages/docs/docs/state-management.md b/packages/docs/docs/state-management.md new file mode 100644 index 0000000..cd62f75 --- /dev/null +++ b/packages/docs/docs/state-management.md @@ -0,0 +1,114 @@ +--- +sidebar_position: 2 +title: State Management +--- + +# State Management + +`effect-fc` works well with state that lives in Effect services, layers, and +scopes. The usual pattern is: + +1. Create state with Effect primitives such as `SubscriptionRef`. +2. Expose read-only reactive state as a `Subscribable`. +3. Expose read/write reactive state as a `Lens`. +4. Bind values into components with `Subscribable.useAll`. + +`effect-fc` re-exports the `Lens` and `Subscribable` modules from +[`effect-lens`](https://github.com/Thiladev/effect-lens) for convenience. The +core data model and transformation APIs belong to `effect-lens`, so check the +`effect-lens` documentation for the full Lens/Subscribable API. + +## Subscribables + +A `Subscribable
` is reactive state with a current value and a stream of +changes. Use `Subscribable.useAll` whenever a component needs to bind +subscribable values into render output. + +`Lens` is a `Subscribable`, so this is also the default way to read Lens values +from a component. + +```tsx +import { Effect, SubscriptionRef } from "effect" +import { Component, Lens, Subscribable } from "effect-fc" + +class CounterState extends Effect.Service()("CounterState", { + effect: Effect.gen(function* () { + const count = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(0)) + const doubled = Subscribable.map(count, (n) => n * 2) + + return { count, doubled } as const + }), +}) {} + +const CounterReadOnlyView = Component.make("CounterReadOnly")( + function* () { + const state = yield* CounterState + const [doubled] = yield* Subscribable.useAll([state.doubled]) + + return

Doubled: {doubled}

+ }, +) +``` + +Use `Subscribable.useAll` instead of subscribing manually. It reads the current +values during render and uses scoped subscriptions to update React state when +changes arrive. + +## Lenses + +A `Lens
` is a `Subscribable` that can also be written to. If a component +only needs to display the value, keep using `Subscribable.useAll`. + +Use `Lens.useState` when React needs a read/write tuple, especially for inputs +that require synchronous updates such as controlled text inputs. + +```tsx +import { Effect, SubscriptionRef } from "effect" +import { Component, Lens } from "effect-fc" + +class FormState extends Effect.Service()("FormState", { + effect: Effect.gen(function* () { + const name = Lens.fromSubscriptionRef(yield* SubscriptionRef.make("")) + + return { name } as const + }), +}) {} + +const NameInputView = Component.make("NameInput")(function* () { + const state = yield* FormState + const [name, setName] = yield* Lens.useState(state.name) + + return ( + setName(event.currentTarget.value)} + /> + ) +}) +``` + +`Lens.useState` returns the current value and a React-compatible setter. Calling +the setter writes through the Lens, so every other component subscribed to the +same Lens sees the update. + +## Choosing One + +Use `Subscribable.useAll` when render needs to read values: + +```tsx +const [count, doubled] = yield* Subscribable.useAll([ + state.count, + state.doubled, +]) +``` + +Use `Lens.useState` when JSX needs both the current value and a synchronous +setter: + +```tsx +const [name, setName] = yield* Lens.useState(state.name) +``` + +For focusing into nested state, deriving lenses, custom write behavior, and the +complete API, refer to the +[`effect-lens` documentation](https://github.com/Thiladev/effect-lens). diff --git a/packages/docs/sidebars.ts b/packages/docs/sidebars.ts index 7efd336..e774757 100644 --- a/packages/docs/sidebars.ts +++ b/packages/docs/sidebars.ts @@ -3,7 +3,7 @@ import type { SidebarsConfig } from "@docusaurus/plugin-content-docs" // This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) const sidebars: SidebarsConfig = { - docsSidebar: ["getting-started"], + docsSidebar: ["getting-started", "state-management"], } export default sidebars -- 2.52.0 From a74ec6e398ad1a11abda92856e3d4148a523fe01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 9 Jun 2026 18:09:47 +0200 Subject: [PATCH 024/133] Docs --- packages/docs/docs/state-management.md | 56 +++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/packages/docs/docs/state-management.md b/packages/docs/docs/state-management.md index cd62f75..bcb0c49 100644 --- a/packages/docs/docs/state-management.md +++ b/packages/docs/docs/state-management.md @@ -18,6 +18,55 @@ scopes. The usual pattern is: core data model and transformation APIs belong to `effect-lens`, so check the `effect-lens` documentation for the full Lens/Subscribable API. +## Where To Store State + +State can live pretty much anywhere: in a service, in a layer, in a component +scope, or in plain React state. Pick the owner based on who needs the state. + +If state is shared by multiple components or belongs to application logic, store +it in an Effect service: + +```tsx +import { Effect, SubscriptionRef } from "effect" +import { Component, Lens, Subscribable } from "effect-fc" + +class CounterState extends Effect.Service()("CounterState", { + effect: Effect.gen(function* () { + const count = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(0)) + + return { count } as const + }), +}) {} + +const CounterValueView = Component.make("CounterValue")(function* () { + const state = yield* CounterState + const [count] = yield* Subscribable.useAll([state.count]) + + return

Count: {count}

+}) +``` + +If state belongs to a single Effect-FC component instance but still benefits +from Lens/Subscribable APIs, create it with `Component.useOnMount`: + +```tsx +import { Effect, SubscriptionRef } from "effect" +import { Component, Lens, Subscribable } from "effect-fc" + +const LocalCounterView = Component.make("LocalCounter")(function* () { + const countLens = yield* Component.useOnMount(() => + Effect.map(SubscriptionRef.make(0), Lens.fromSubscriptionRef), + ) + const [count] = yield* Subscribable.useAll([countLens]) + + return

Count: {count}

+}) +``` + +For simple UI state that is not shared and does not need Effect integration, +prefer regular React state. A local "show details" toggle is usually better as +`React.useState(false)` than as a Lens. + ## Subscribables A `Subscribable
` is reactive state with a current value and a stream of @@ -43,9 +92,12 @@ class CounterState extends Effect.Service()("CounterState", { const CounterReadOnlyView = Component.make("CounterReadOnly")( function* () { const state = yield* CounterState - const [doubled] = yield* Subscribable.useAll([state.doubled]) + const [count, doubled] = yield* Subscribable.useAll([ + state.count, + state.doubled, + ]) - return

Doubled: {doubled}

+ return

Count: {count}, doubled: {doubled}

}, ) ``` -- 2.52.0 From 5791c08b51f51695dcf13c6c53b1f4be2cd540a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 9 Jun 2026 19:00:09 +0200 Subject: [PATCH 025/133] Docs --- packages/docs/docs/state-management.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/docs/docs/state-management.md b/packages/docs/docs/state-management.md index bcb0c49..1806b47 100644 --- a/packages/docs/docs/state-management.md +++ b/packages/docs/docs/state-management.md @@ -9,9 +9,11 @@ title: State Management scopes. The usual pattern is: 1. Create state with Effect primitives such as `SubscriptionRef`. -2. Expose read-only reactive state as a `Subscribable`. -3. Expose read/write reactive state as a `Lens`. -4. Bind values into components with `Subscribable.useAll`. +2. Turn the Effect primitive into a `Lens` with the matching constructor, such + as `Lens.fromSubscriptionRef`. +3. Expose read-only reactive state as a `Subscribable`. +4. Expose read/write reactive state as a `Lens`. +5. Bind values into components with `Subscribable.useAll`. `effect-fc` re-exports the `Lens` and `Subscribable` modules from [`effect-lens`](https://github.com/Thiladev/effect-lens) for convenience. The @@ -102,9 +104,7 @@ const CounterReadOnlyView = Component.make("CounterReadOnly")( ) ``` -Use `Subscribable.useAll` instead of subscribing manually. It reads the current -values during render and uses scoped subscriptions to update React state when -changes arrive. +`Subscribable.useAll` reads the current values during render and uses scoped subscriptions to update React state when changes arrive. ## Lenses -- 2.52.0 From 436dc275b3fb60167200a7a930d9deaf762ce813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 9 Jun 2026 20:34:24 +0200 Subject: [PATCH 026/133] Docs --- packages/docs/docs/state-management.md | 31 +++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/packages/docs/docs/state-management.md b/packages/docs/docs/state-management.md index 1806b47..8fa58ea 100644 --- a/packages/docs/docs/state-management.md +++ b/packages/docs/docs/state-management.md @@ -69,7 +69,7 @@ For simple UI state that is not shared and does not need Effect integration, prefer regular React state. A local "show details" toggle is usually better as `React.useState(false)` than as a Lens. -## Subscribables +## Subscribable.useAll A `Subscribable
` is reactive state with a current value and a stream of changes. Use `Subscribable.useAll` whenever a component needs to bind @@ -106,6 +106,35 @@ const CounterReadOnlyView = Component.make("CounterReadOnly")( `Subscribable.useAll` reads the current values during render and uses scoped subscriptions to update React state when changes arrive. +When the state is a `Lens`, keep binding it with `Subscribable.useAll` and +write to it with `Lens.set` or `Lens.update` from an Effect-FC callback: + +```tsx +const CounterControlsView = Component.make("CounterControls")( + function* () { + const state = yield* CounterState + const [count] = yield* Subscribable.useAll([state.count]) + + const increment = yield* Component.useCallbackSync( + () => Lens.update(state.count, (n) => n + 1), + [state.count], + ) + const reset = yield* Component.useCallbackSync( + () => Lens.set(state.count, 0), + [state.count], + ) + + return ( +
+

Count: {count}

+ + +
+ ) + }, +) +``` + ## Lenses A `Lens
` is a `Subscribable` that can also be written to. If a component -- 2.52.0 From c90c5f9532b87e5d50fae35564207356a3f3a8c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 9 Jun 2026 21:44:08 +0200 Subject: [PATCH 027/133] Docs --- packages/docs/docs/state-management.md | 35 ++++++++------------------ 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/packages/docs/docs/state-management.md b/packages/docs/docs/state-management.md index 8fa58ea..2bc1280 100644 --- a/packages/docs/docs/state-management.md +++ b/packages/docs/docs/state-management.md @@ -106,8 +106,8 @@ const CounterReadOnlyView = Component.make("CounterReadOnly")( `Subscribable.useAll` reads the current values during render and uses scoped subscriptions to update React state when changes arrive. -When the state is a `Lens`, keep binding it with `Subscribable.useAll` and -write to it with `Lens.set` or `Lens.update` from an Effect-FC callback: +When you need to modify state, write to the Lens with `Lens.set` or +`Lens.update` from an Effect-FC callback: ```tsx const CounterControlsView = Component.make("CounterControls")( @@ -135,13 +135,16 @@ const CounterControlsView = Component.make("CounterControls")( ) ``` -## Lenses +## Lens.useState -A `Lens` is a `Subscribable` that can also be written to. If a component -only needs to display the value, keep using `Subscribable.useAll`. +`Lens.useState` is useful when React needs the familiar `[value, setValue]` +tuple, backed by a Lens. Reach for it when the JSX API expects a synchronous +setter, especially controlled inputs such as text fields, checkboxes, selects, +or third-party components with `value` / `onChange` props. -Use `Lens.useState` when React needs a read/write tuple, especially for inputs -that require synchronous updates such as controlled text inputs. +If a component only needs to display the value, prefer `Subscribable.useAll`. +`Lens.useState` is for places where reading and writing need to be wired +together in React's local-state shape. ```tsx import { Effect, SubscriptionRef } from "effect" @@ -172,24 +175,6 @@ const NameInputView = Component.make("NameInput")(function* () { the setter writes through the Lens, so every other component subscribed to the same Lens sees the update. -## Choosing One - -Use `Subscribable.useAll` when render needs to read values: - -```tsx -const [count, doubled] = yield* Subscribable.useAll([ - state.count, - state.doubled, -]) -``` - -Use `Lens.useState` when JSX needs both the current value and a synchronous -setter: - -```tsx -const [name, setName] = yield* Lens.useState(state.name) -``` - For focusing into nested state, deriving lenses, custom write behavior, and the complete API, refer to the [`effect-lens` documentation](https://github.com/Thiladev/effect-lens). -- 2.52.0 From 81fb1dcf4239874b912c56391d0388f1b59006ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 9 Jun 2026 21:59:12 +0200 Subject: [PATCH 028/133] Docs --- packages/docs/docs/state-management.md | 55 ++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/packages/docs/docs/state-management.md b/packages/docs/docs/state-management.md index 2bc1280..72f0f7c 100644 --- a/packages/docs/docs/state-management.md +++ b/packages/docs/docs/state-management.md @@ -175,6 +175,61 @@ const NameInputView = Component.make("NameInput")(function* () { the setter writes through the Lens, so every other component subscribed to the same Lens sees the update. +## Focused Lenses + +Use focused Lenses when a component should work with one part of a larger state +object. A focused Lens is still a Lens, so it can be read with +`Subscribable.useAll` or used with `Lens.useState` when React needs a +read/write tuple. + +```tsx +import { Effect, SubscriptionRef } from "effect" +import { Component, Lens, Subscribable } from "effect-fc" + +interface UserProfile { + readonly name: string + readonly email: string + readonly role: string +} + +class ProfileState extends Effect.Service()("ProfileState", { + effect: Effect.gen(function* () { + const profile = Lens.fromSubscriptionRef( + yield* SubscriptionRef.make({ + name: "", + email: "", + role: "reader", + }), + ) + const name = Lens.focusObjectOn(profile, "name") + const role = Lens.focusObjectOn(profile, "role") + + return { profile, name, role } as const + }), +}) {} + +const ProfileNameView = Component.make("ProfileName")(function* () { + const state = yield* ProfileState + const [name, setName] = yield* Lens.useState(state.name) + const [role] = yield* Subscribable.useAll([state.role]) + + return ( + + ) +}) +``` + +Updating the focused `name` Lens through `Lens.useState` updates the parent +`profile` Lens. The focused `role` Lens is only read, so it stays on the simpler +`Subscribable.useAll` path. + For focusing into nested state, deriving lenses, custom write behavior, and the complete API, refer to the [`effect-lens` documentation](https://github.com/Thiladev/effect-lens). -- 2.52.0 From ad1ef4a73b4fb3d545d9ee80433e77e342306f8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 9 Jun 2026 22:15:41 +0200 Subject: [PATCH 029/133] Docs --- packages/docs/docs/state-management.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/docs/docs/state-management.md b/packages/docs/docs/state-management.md index 72f0f7c..c1f685e 100644 --- a/packages/docs/docs/state-management.md +++ b/packages/docs/docs/state-management.md @@ -22,8 +22,10 @@ core data model and transformation APIs belong to `effect-lens`, so check the ## Where To Store State -State can live pretty much anywhere: in a service, in a layer, in a component -scope, or in plain React state. Pick the owner based on who needs the state. +State can live pretty much anywhere as a `Lens` or `Subscribable`: in a +service, in a layer, in a component scope, or alongside plain React state. Pick +the owner based on who needs the state. Once you have a Lens/Subscribable +handle, pass it around however you like, including through React props. If state is shared by multiple components or belongs to application logic, store it in an Effect service: @@ -56,10 +58,14 @@ import { Effect, SubscriptionRef } from "effect" import { Component, Lens, Subscribable } from "effect-fc" const LocalCounterView = Component.make("LocalCounter")(function* () { - const countLens = yield* Component.useOnMount(() => - Effect.map(SubscriptionRef.make(0), Lens.fromSubscriptionRef), + const state = yield* Component.useOnMount(() => + Effect.gen(function* () { + const count = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(0)) + + return { count } as const + }), ) - const [count] = yield* Subscribable.useAll([countLens]) + const [count] = yield* Subscribable.useAll([state.count]) return

Count: {count}

}) -- 2.52.0 From 1cead4bb7bfd1cba0aab150408ca03d00b4a3c8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 9 Jun 2026 22:21:26 +0200 Subject: [PATCH 030/133] Docs --- packages/docs/docs/state-management.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/docs/docs/state-management.md b/packages/docs/docs/state-management.md index c1f685e..8e2a84c 100644 --- a/packages/docs/docs/state-management.md +++ b/packages/docs/docs/state-management.md @@ -50,8 +50,9 @@ const CounterValueView = Component.make("CounterValue")(function* () { }) ``` -If state belongs to a single Effect-FC component instance but still benefits -from Lens/Subscribable APIs, create it with `Component.useOnMount`: +If state belongs to a single Effect-FC component instance, or to a shallow +hierarchy of subcomponents that receive it through props, create it with +`Component.useOnMount`: ```tsx import { Effect, SubscriptionRef } from "effect" -- 2.52.0 From 9fb56da120ec4ce018fedc48564702e3d8f99fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 9 Jun 2026 22:25:53 +0200 Subject: [PATCH 031/133] Docs --- packages/docs/docs/getting-started.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/docs/docs/getting-started.md b/packages/docs/docs/getting-started.md index be19515..2c4141c 100644 --- a/packages/docs/docs/getting-started.md +++ b/packages/docs/docs/getting-started.md @@ -253,11 +253,11 @@ need a lifecycle that is narrower than the whole component instance. ## Useful Effect-FC Hooks -The most common Effect-FC hooks are: - Unlike plain React hooks, Effect-FC hooks return Effects. Use `yield*` to run them inside the component body. +The most common Effect-FC hooks are: + - `Component.useOnMount`: run an Effect once during the component's first render after mount, and return its value. The Effect must be synchronous. -- 2.52.0 From 7ae5d085553424503844e78eef113ea9e4fb365e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 11 Jun 2026 12:29:10 +0200 Subject: [PATCH 032/133] Docs --- packages/docs/docs/state-management.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/docs/docs/state-management.md b/packages/docs/docs/state-management.md index 8e2a84c..dde9485 100644 --- a/packages/docs/docs/state-management.md +++ b/packages/docs/docs/state-management.md @@ -18,7 +18,7 @@ scopes. The usual pattern is: `effect-fc` re-exports the `Lens` and `Subscribable` modules from [`effect-lens`](https://github.com/Thiladev/effect-lens) for convenience. The core data model and transformation APIs belong to `effect-lens`, so check the -`effect-lens` documentation for the full Lens/Subscribable API. +[`effect-lens` documentation](https://github.com/Thiladev/effect-lens/tree/master/packages/effect-lens) for the full Lens/Subscribable API. ## Where To Store State @@ -124,11 +124,11 @@ const CounterControlsView = Component.make("CounterControls")( const increment = yield* Component.useCallbackSync( () => Lens.update(state.count, (n) => n + 1), - [state.count], + [], ) const reset = yield* Component.useCallbackSync( () => Lens.set(state.count, 0), - [state.count], + [], ) return ( @@ -239,4 +239,4 @@ Updating the focused `name` Lens through `Lens.useState` updates the parent For focusing into nested state, deriving lenses, custom write behavior, and the complete API, refer to the -[`effect-lens` documentation](https://github.com/Thiladev/effect-lens). +[`effect-lens` documentation](https://github.com/Thiladev/effect-lens/tree/master/packages/effect-lens). -- 2.52.0 From cf2cb8bf09375a1d51ca8610ca023cf3244f6729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 11 Jun 2026 14:25:00 +0200 Subject: [PATCH 033/133] Docs --- packages/docs/docs/state-management.md | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/docs/docs/state-management.md b/packages/docs/docs/state-management.md index dde9485..618a08b 100644 --- a/packages/docs/docs/state-management.md +++ b/packages/docs/docs/state-management.md @@ -5,15 +5,20 @@ title: State Management # State Management -`effect-fc` works well with state that lives in Effect services, layers, and -scopes. The usual pattern is: +`Lens` is the main type used for state management in `effect-fc`. -1. Create state with Effect primitives such as `SubscriptionRef`. -2. Turn the Effect primitive into a `Lens` with the matching constructor, such - as `Lens.fromSubscriptionRef`. -3. Expose read-only reactive state as a `Subscribable`. -4. Expose read/write reactive state as a `Lens`. -5. Bind values into components with `Subscribable.useAll`. +A Lens is an effectful handle to a piece of state. It can read the current value, +subscribe to changes, and write updates back to the underlying source. A Lens +can point at a whole state object or focus on one nested field inside it. + +The usual pattern is to create state with Effect primitives such as +`SubscriptionRef`, turn that primitive into a Lens with a matching constructor +such as `Lens.fromSubscriptionRef`, and bind Lens values into components with +`Subscribable.useAll`. + +`Subscribable` is the read-only side of this model. Every Lens is also a +Subscribable, so use `Subscribable.useAll` whenever a component only needs to +read reactive state. `effect-fc` re-exports the `Lens` and `Subscribable` modules from [`effect-lens`](https://github.com/Thiladev/effect-lens) for convenience. The @@ -113,8 +118,7 @@ const CounterReadOnlyView = Component.make("CounterReadOnly")( `Subscribable.useAll` reads the current values during render and uses scoped subscriptions to update React state when changes arrive. -When you need to modify state, write to the Lens with `Lens.set` or -`Lens.update` from an Effect-FC callback: +When you need to modify state, write to the Lens with `Lens.set` or `Lens.update`: ```tsx const CounterControlsView = Component.make("CounterControls")( -- 2.52.0 From 241d489b0c4b53bce7e7105a7308376955049710 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 12 Jun 2026 01:44:40 +0200 Subject: [PATCH 034/133] Docs --- packages/docs/docs/state-management.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/docs/docs/state-management.md b/packages/docs/docs/state-management.md index 618a08b..160bf96 100644 --- a/packages/docs/docs/state-management.md +++ b/packages/docs/docs/state-management.md @@ -17,8 +17,7 @@ such as `Lens.fromSubscriptionRef`, and bind Lens values into components with `Subscribable.useAll`. `Subscribable` is the read-only side of this model. Every Lens is also a -Subscribable, so use `Subscribable.useAll` whenever a component only needs to -read reactive state. +Subscribable. `effect-fc` re-exports the `Lens` and `Subscribable` modules from [`effect-lens`](https://github.com/Thiladev/effect-lens) for convenience. The -- 2.52.0 From 257d505fdfb5a4b9f6f289e171989fa1abccdfcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 12 Jun 2026 01:51:30 +0200 Subject: [PATCH 035/133] Docs --- packages/docs/docs/forms.md | 153 ++++++++++++++++++++++++++++++++++++ packages/docs/sidebars.ts | 2 +- 2 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 packages/docs/docs/forms.md diff --git a/packages/docs/docs/forms.md b/packages/docs/docs/forms.md new file mode 100644 index 0000000..37eca25 --- /dev/null +++ b/packages/docs/docs/forms.md @@ -0,0 +1,153 @@ +--- +sidebar_position: 3 +title: Forms +--- + +# Forms + +The `Form` module is the low-level field model used by Effect-FC forms. A form +field is built on the same state model as the rest of Effect-FC: + +- `encodedValue`: a `Lens` containing the raw value used by the input. +- `value`: a `Subscribable` containing the decoded value as an `Option`. +- `issues`: a `Subscribable` containing schema validation issues for that field. +- `canCommit`, `isValidating`, and `isCommitting`: `Subscribable` flags for UI + state. + +Most apps create a root form with `SubmittableForm` or `SynchronizedForm`, then +focus it into fields with `Form.focusObjectOn`, `Form.focusArrayAt`, or the +other focusing helpers. + +## Field Inputs + +Use `Form.useInput` to connect a `Form` field to a controlled input. It returns +a React-style `{ value, setValue }` pair and writes changes back to the field's +`encodedValue` Lens. + +```tsx +import { Component, Form, Subscribable } from "effect-fc" + +const TextInputView = Component.make("TextInput")( + function* (props: { + readonly form: Form.Form + readonly label: string + }) { + const input = yield* Form.useInput(props.form, { + debounce: "250 millis", + }) + const [issues] = yield* Subscribable.useAll([props.form.issues]) + + return ( + + ) + }, +) +``` + +Use `Form.useOptionalInput` for `Option` fields. It returns the same value/setter +pair plus `enabled` and `setEnabled`, which is useful for optional inputs that +can be toggled on and off. + +## Submittable Forms + +Use `SubmittableForm` when the user edits local encoded form state, validates it +with a schema, and then submits a decoded value. + +```tsx +import { Effect, Schema } from "effect" +import { Component, Form, SubmittableForm, Subscribable } from "effect-fc" +import { TextInputView } from "./TextInputView" + +const RegisterSchema = Schema.Struct({ + email: Schema.String, + password: Schema.String, +}) + +class RegisterFormState extends Effect.Service()( + "RegisterFormState", + { + scoped: Effect.gen(function* () { + const form = yield* SubmittableForm.service({ + schema: RegisterSchema, + initialEncodedValue: { + email: "", + password: "", + }, + f: ([value]) => + Effect.log(`Registering ${value.email}`), + }) + + return { + form, + email: Form.focusObjectOn(form, "email"), + password: Form.focusObjectOn(form, "password"), + } as const + }), + }, +) {} + +const RegisterFormView = Component.make("RegisterForm")( + function* () { + const state = yield* RegisterFormState + const [canCommit, isCommitting] = yield* Subscribable.useAll([ + state.form.canCommit, + state.form.isCommitting, + ]) + const runPromise = yield* Component.useRunPromise() + const TextInput = yield* TextInputView.use + + return ( +
{ + event.preventDefault() + void runPromise(state.form.submit) + }} + > + + + + + ) + }, +) +``` + +`SubmittableForm.service` starts validation in the form's scope. The form keeps +its encoded input state, decoded value, validation issues, submit mutation, and +commit flags available as Lenses/Subscribables. + +## Synchronized Forms + +Use `SynchronizedForm` when a form should edit an existing target `Lens`. +Instead of submitting a final value, valid encoded changes are synchronized back +to the target Lens. + +This is useful for edit screens where the form is a validated view over existing +state. Use `SubmittableForm` for "submit this draft" flows, and +`SynchronizedForm` for "keep this target state synchronized when valid" flows. + +## Focusing Fields + +Forms can be focused just like Lenses: + +```tsx +const emailField = Form.focusObjectOn(form, "email") +const firstItemField = Form.focusArrayAt(form, 0) +``` + +Focused fields keep the parent form's validation and commit state, but narrow +`encodedValue`, `value`, and `issues` to the field path. This lets each input +component receive only the field it needs. diff --git a/packages/docs/sidebars.ts b/packages/docs/sidebars.ts index e774757..58f6b8a 100644 --- a/packages/docs/sidebars.ts +++ b/packages/docs/sidebars.ts @@ -3,7 +3,7 @@ import type { SidebarsConfig } from "@docusaurus/plugin-content-docs" // This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) const sidebars: SidebarsConfig = { - docsSidebar: ["getting-started", "state-management"], + docsSidebar: ["getting-started", "state-management", "forms"], } export default sidebars -- 2.52.0 From b7ea35006db8232458f0133dcba92b8153c04e52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 22 Jun 2026 01:38:06 +0200 Subject: [PATCH 036/133] Add forms doc --- packages/docs/docs/forms.md | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/packages/docs/docs/forms.md b/packages/docs/docs/forms.md index 37eca25..944234f 100644 --- a/packages/docs/docs/forms.md +++ b/packages/docs/docs/forms.md @@ -5,18 +5,33 @@ title: Forms # Forms -The `Form` module is the low-level field model used by Effect-FC forms. A form -field is built on the same state model as the rest of Effect-FC: +The form modules provide an Effect-first model for editable, schema-validated +state. They are not a visual component library. Instead, they give you +Lenses/Subscribables for form values, validation issues, and commit state, so +you can build the UI with whatever components your app already uses. -- `encodedValue`: a `Lens` containing the raw value used by the input. +The base `Form` type represents a field. A field tracks both the raw encoded +value used by inputs and the decoded value produced by an Effect `Schema`: + +- `encodedValue`: a `Lens` containing the raw input value. - `value`: a `Subscribable` containing the decoded value as an `Option`. - `issues`: a `Subscribable` containing schema validation issues for that field. - `canCommit`, `isValidating`, and `isCommitting`: `Subscribable` flags for UI state. -Most apps create a root form with `SubmittableForm` or `SynchronizedForm`, then -focus it into fields with `Form.focusObjectOn`, `Form.focusArrayAt`, or the -other focusing helpers. +There are two root form flavors: + +- `SubmittableForm`: owns local encoded form state, validates it, and submits a + decoded value through a mutation. +- `SynchronizedForm`: wraps an existing target `Lens` and writes valid changes + back to that target. + +Most apps create one of those root forms, then focus it into fields with +`Form.focusObjectOn`, `Form.focusArrayAt`, or the other focusing helpers. + +The core form model is Effect code and can technically be used outside +Effect-FC. Effect-FC adds the React-facing hooks, such as `Form.useInput`, that +make those forms convenient to bind to components. ## Field Inputs -- 2.52.0 From 091e102b2321be2802531529fced921fea7b4a26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 22 Jun 2026 02:04:20 +0200 Subject: [PATCH 037/133] Add Effect v4 version --- bun.lock | 70 +++- packages/effect-fc-next/README.md | 56 +++ packages/effect-fc-next/biome.json | 8 + packages/effect-fc-next/package.json | 54 +++ packages/effect-fc-next/src/Async.ts | 171 ++++++++ packages/effect-fc-next/src/Component.ts | 385 ++++++++++++++++++ packages/effect-fc-next/src/ErrorObserver.ts | 47 +++ packages/effect-fc-next/src/Form.ts | 279 +++++++++++++ packages/effect-fc-next/src/Lens.ts | 62 +++ packages/effect-fc-next/src/Memoized.ts | 112 +++++ packages/effect-fc-next/src/Mutation.ts | 146 +++++++ packages/effect-fc-next/src/PubSub.ts | 17 + packages/effect-fc-next/src/Query.ts | 283 +++++++++++++ packages/effect-fc-next/src/QueryClient.ts | 191 +++++++++ packages/effect-fc-next/src/ReactRuntime.ts | 71 ++++ packages/effect-fc-next/src/Result.ts | 249 +++++++++++ packages/effect-fc-next/src/SetStateAction.ts | 12 + packages/effect-fc-next/src/Stream.ts | 33 ++ .../effect-fc-next/src/SubmittableForm.ts | 212 ++++++++++ packages/effect-fc-next/src/Subscribable.ts | 53 +++ .../effect-fc-next/src/SynchronizedForm.ts | 204 ++++++++++ packages/effect-fc-next/src/index.ts | 17 + packages/effect-fc-next/src/utils.ts | 3 + .../effect-fc-next/test/Component.test.tsx | 354 ++++++++++++++++ packages/effect-fc-next/test/Lens.test.tsx | 165 ++++++++ packages/effect-fc-next/test/Query.test.ts | 169 ++++++++ .../effect-fc-next/test/Subscribable.test.tsx | 129 ++++++ packages/effect-fc-next/tsconfig.json | 40 ++ packages/effect-fc-next/vitest.config.ts | 9 + packages/example-next/.gitignore | 26 ++ packages/example-next/README.md | 10 + packages/example-next/biome.json | 8 + packages/example-next/index.html | 13 + packages/example-next/package.json | 34 ++ packages/example-next/src/index.css | 28 ++ packages/example-next/src/main.tsx | 40 ++ packages/example-next/src/vite-env.d.ts | 1 + packages/example-next/tsconfig.app.json | 31 ++ packages/example-next/tsconfig.json | 7 + packages/example-next/tsconfig.node.json | 25 ++ packages/example-next/vite.config.ts | 10 + 41 files changed, 3832 insertions(+), 2 deletions(-) create mode 100644 packages/effect-fc-next/README.md create mode 100644 packages/effect-fc-next/biome.json create mode 100644 packages/effect-fc-next/package.json create mode 100644 packages/effect-fc-next/src/Async.ts create mode 100644 packages/effect-fc-next/src/Component.ts create mode 100644 packages/effect-fc-next/src/ErrorObserver.ts create mode 100644 packages/effect-fc-next/src/Form.ts create mode 100644 packages/effect-fc-next/src/Lens.ts create mode 100644 packages/effect-fc-next/src/Memoized.ts create mode 100644 packages/effect-fc-next/src/Mutation.ts create mode 100644 packages/effect-fc-next/src/PubSub.ts create mode 100644 packages/effect-fc-next/src/Query.ts create mode 100644 packages/effect-fc-next/src/QueryClient.ts create mode 100644 packages/effect-fc-next/src/ReactRuntime.ts create mode 100644 packages/effect-fc-next/src/Result.ts create mode 100644 packages/effect-fc-next/src/SetStateAction.ts create mode 100644 packages/effect-fc-next/src/Stream.ts create mode 100644 packages/effect-fc-next/src/SubmittableForm.ts create mode 100644 packages/effect-fc-next/src/Subscribable.ts create mode 100644 packages/effect-fc-next/src/SynchronizedForm.ts create mode 100644 packages/effect-fc-next/src/index.ts create mode 100644 packages/effect-fc-next/src/utils.ts create mode 100644 packages/effect-fc-next/test/Component.test.tsx create mode 100644 packages/effect-fc-next/test/Lens.test.tsx create mode 100644 packages/effect-fc-next/test/Query.test.ts create mode 100644 packages/effect-fc-next/test/Subscribable.test.tsx create mode 100644 packages/effect-fc-next/tsconfig.json create mode 100644 packages/effect-fc-next/vitest.config.ts create mode 100644 packages/example-next/.gitignore create mode 100644 packages/example-next/README.md create mode 100644 packages/example-next/biome.json create mode 100644 packages/example-next/index.html create mode 100644 packages/example-next/package.json create mode 100644 packages/example-next/src/index.css create mode 100644 packages/example-next/src/main.tsx create mode 100644 packages/example-next/src/vite-env.d.ts create mode 100644 packages/example-next/tsconfig.app.json create mode 100644 packages/example-next/tsconfig.json create mode 100644 packages/example-next/tsconfig.node.json create mode 100644 packages/example-next/vite.config.ts diff --git a/bun.lock b/bun.lock index a30a084..cf8ffc5 100644 --- a/bun.lock +++ b/bun.lock @@ -54,6 +54,23 @@ "react": "^19.2.0", }, }, + "packages/effect-fc-next": { + "name": "effect-fc-next", + "version": "0.1.0-beta.0", + "dependencies": { + "effect-lens": "2.0.0-beta.0", + }, + "devDependencies": { + "@testing-library/react": "^16.3.0", + "jsdom": "^26.1.0", + "vitest": "^3.2.4", + }, + "peerDependencies": { + "@types/react": "^19.2.0", + "effect": "4.0.0-beta.85", + "react": "^19.2.0", + }, + }, "packages/example": { "name": "@effect-fc/example", "version": "0.0.0", @@ -80,6 +97,23 @@ "vite": "^8.0.16", }, }, + "packages/example-next": { + "name": "@effect-fc/example-next", + "version": "0.0.0", + "dependencies": { + "effect": "4.0.0-beta.85", + "effect-fc-next": "workspace:*", + }, + "devDependencies": { + "@types/react": "^19.2.15", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.2", + "globals": "^17.6.0", + "react": "^19.2.6", + "react-dom": "^19.2.6", + "vite": "^8.0.16", + }, + }, }, "packages": { "@algolia/abtesting": ["@algolia/abtesting@1.19.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-Lhnez3hhXHk25lfxLAMxvkP4fmN3+1RgADhD2ssMDBYuAsDVReeyP+3SGRx+ntq8ijMrLqUyfvO72TB6jsTteQ=="], @@ -512,6 +546,8 @@ "@effect-fc/example": ["@effect-fc/example@workspace:packages/example"], + "@effect-fc/example-next": ["@effect-fc/example-next@workspace:packages/example-next"], + "@effect/language-service": ["@effect/language-service@0.86.2", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-SaPln+8srOqDJDUwNTDmP5e+IYpEDr9+1epGznnsLqu8xvo6VnxyWARdeLpqvZJlb0Pgy9ca7ppqvvdWbHPXAg=="], "@effect/platform": ["@effect/platform@0.96.1", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.10", "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^3.21.2" } }, "sha512-cjB1QZZYEP8JXCFNGvBLVi0T6YUBQTmOVEUA3SDbiQ6RUO+p6CE3eyD2vMWmrz5nE8yY5QSAuOV9v0boEcUv+A=="], @@ -1574,6 +1610,8 @@ "effect-fc": ["effect-fc@workspace:packages/effect-fc"], + "effect-fc-next": ["effect-fc-next@workspace:packages/effect-fc-next"], + "effect-lens": ["effect-lens@0.2.0", "", { "peerDependencies": { "effect": "^3.21.0" } }, "sha512-Cfyps811WQVSxnbxm6xqJldL08YXUU1jAzWtdKXSKWZfF6LAEqm1l8swnw4ltVky1TSfi2vvS+I/tFPqwObrMg=="], "electron-to-chromium": ["electron-to-chromium@1.5.368", "", {}, "sha512-7RckJJK4uESJF9PxvfMWd3TGqIiieUTG4HxnKaKuIpGbcr+r2ZEB3g2gAhCP3Fqm42vJSzLfgab9eva/C4/XVw=="], @@ -1844,7 +1882,7 @@ "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], - "ini": ["ini@2.0.0", "", {}, "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA=="], + "ini": ["ini@7.0.0", "", {}, "sha512-ifK0CgjALofS5bkrcTy4RaQ9Vx2Knf/eLeIO+NaswQEpH1UblrtTSCIvN71qQDMq0PeQ/SSPojvEJp9vvvfr+w=="], "inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="], @@ -1948,6 +1986,8 @@ "kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], + "kubernetes-types": ["kubernetes-types@1.30.0", "", {}, "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q=="], + "latest-version": ["latest-version@7.0.0", "", { "dependencies": { "package-json": "^8.1.0" } }, "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg=="], "launch-editor": ["launch-editor@2.14.1", "", { "dependencies": { "picocolors": "^1.1.1", "shell-quote": "^1.8.4" } }, "sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA=="], @@ -2758,6 +2798,8 @@ "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + "toml": ["toml@4.1.1", "", {}, "sha512-EBJnVBr3dTXdA89WVFoAIPUqkBjxPMwRqsfuo1r240tKFHXv3zgca4+NJib/h6TyvGF7vOawz0jGuryJCdNHrw=="], + "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], "tough-cookie": ["tough-cookie@5.1.2", "", { "dependencies": { "tldts": "^6.1.32" } }, "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A=="], @@ -2840,7 +2882,7 @@ "utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="], - "uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], + "uuid": ["uuid@14.0.1", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew=="], "value-equal": ["value-equal@1.0.1", "", {}, "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="], @@ -2920,6 +2962,8 @@ "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + "yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="], + "yocto-queue": ["yocto-queue@1.2.2", "", {}, "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ=="], "zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], @@ -2950,6 +2994,8 @@ "@docusaurus/utils/jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], + "@effect-fc/example-next/effect": ["effect@4.0.0-beta.85", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.8.0", "find-my-way-ts": "^0.1.6", "ini": "^7.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^2.0.1", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^14.0.0", "yaml": "^2.9.0" } }, "sha512-Cjv9YQyv4CiIccmRAQIWAoeESCpCpiuHYY8zb5vqiYs3Ac2yE5RQAnKq7z4Ir/3VJYZb8kxh6rS8czDDnpTdkQ=="], + "@jsonjoy.com/fs-snapshot/@jsonjoy.com/json-pack": ["@jsonjoy.com/json-pack@17.67.0", "", { "dependencies": { "@jsonjoy.com/base64": "17.67.0", "@jsonjoy.com/buffers": "17.67.0", "@jsonjoy.com/codegen": "17.67.0", "@jsonjoy.com/json-pointer": "17.67.0", "@jsonjoy.com/util": "17.67.0", "hyperdyperid": "^1.2.0", "thingies": "^2.5.0", "tree-dump": "^1.1.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w=="], "@jsonjoy.com/fs-snapshot/@jsonjoy.com/util": ["@jsonjoy.com/util@17.67.0", "", { "dependencies": { "@jsonjoy.com/buffers": "17.67.0", "@jsonjoy.com/codegen": "17.67.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew=="], @@ -3016,6 +3062,10 @@ "dot-prop/is-obj": ["is-obj@2.0.0", "", {}, "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="], + "effect-fc-next/effect": ["effect@4.0.0-beta.85", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.8.0", "find-my-way-ts": "^0.1.6", "ini": "^7.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^2.0.1", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^14.0.0", "yaml": "^2.9.0" } }, "sha512-Cjv9YQyv4CiIccmRAQIWAoeESCpCpiuHYY8zb5vqiYs3Ac2yE5RQAnKq7z4Ir/3VJYZb8kxh6rS8czDDnpTdkQ=="], + + "effect-fc-next/effect-lens": ["effect-lens@2.0.0-beta.0", "", { "peerDependencies": { "effect": "4.0.0-beta.85" } }, "sha512-ntsRQSrzoX+GsNpkNVEGqebl/g1YOEDPIFRAHigf+4P6AtGnnOFcklqDHLrbm4zqnaxuWEgQvvn1J6S3eyC4yw=="], + "esrecurse/estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], "express/content-disposition": ["content-disposition@0.5.4", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ=="], @@ -3030,6 +3080,8 @@ "finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + "global-dirs/ini": ["ini@2.0.0", "", {}, "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA=="], + "got/@sindresorhus/is": ["@sindresorhus/is@5.6.0", "", {}, "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g=="], "gray-matter/js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="], @@ -3226,6 +3278,8 @@ "sitemap/@types/node": ["@types/node@17.0.45", "", {}, "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="], + "sockjs/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], + "source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], "strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], @@ -3282,6 +3336,10 @@ "@docusaurus/core/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], + "@effect-fc/example-next/effect/fast-check": ["fast-check@4.8.0", "", { "dependencies": { "pure-rand": "^8.0.0" } }, "sha512-GOJ158CUMnN6cSahsv4+ExARvIDuzzinFjkp0E9WtiBa5zcVeLozVkWaE4IzFcc+Y48Wp1EDlUZsXRyAztQcSg=="], + + "@effect-fc/example-next/effect/msgpackr": ["msgpackr@2.0.4", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.4" } }, "sha512-o1C5KRmuRt+apqMr1HuGSqWStZoRBUpEsCsl15uM9VdAF1qHLtvMOU2En747EnTyEl6c4pzPewRMFF31s1CNbA=="], + "@jsonjoy.com/fs-snapshot/@jsonjoy.com/json-pack/@jsonjoy.com/base64": ["@jsonjoy.com/base64@17.67.0", "", { "peerDependencies": { "tslib": "2" } }, "sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw=="], "@jsonjoy.com/fs-snapshot/@jsonjoy.com/json-pack/@jsonjoy.com/codegen": ["@jsonjoy.com/codegen@17.67.0", "", { "peerDependencies": { "tslib": "2" } }, "sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q=="], @@ -3312,6 +3370,10 @@ "csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="], + "effect-fc-next/effect/fast-check": ["fast-check@4.8.0", "", { "dependencies": { "pure-rand": "^8.0.0" } }, "sha512-GOJ158CUMnN6cSahsv4+ExARvIDuzzinFjkp0E9WtiBa5zcVeLozVkWaE4IzFcc+Y48Wp1EDlUZsXRyAztQcSg=="], + + "effect-fc-next/effect/msgpackr": ["msgpackr@2.0.4", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.4" } }, "sha512-o1C5KRmuRt+apqMr1HuGSqWStZoRBUpEsCsl15uM9VdAF1qHLtvMOU2En747EnTyEl6c4pzPewRMFF31s1CNbA=="], + "express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "file-loader/schema-utils/ajv": ["ajv@6.15.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw=="], @@ -3370,8 +3432,12 @@ "@docusaurus/core/chokidar/readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + "@effect-fc/example-next/effect/fast-check/pure-rand": ["pure-rand@8.4.0", "", {}, "sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A=="], + "css-select/domutils/dom-serializer/entities": ["entities@2.2.0", "", {}, "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="], + "effect-fc-next/effect/fast-check/pure-rand": ["pure-rand@8.4.0", "", {}, "sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A=="], + "file-loader/schema-utils/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], "null-loader/schema-utils/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], diff --git a/packages/effect-fc-next/README.md b/packages/effect-fc-next/README.md new file mode 100644 index 0000000..5c164f8 --- /dev/null +++ b/packages/effect-fc-next/README.md @@ -0,0 +1,56 @@ +# Effect FC Next + +[Effect-TS](https://effect.website/) integration for React 19.2+ that allows you to write function components using Effect generators. + +This library is in early development. While it is (almost) feature complete and mostly usable, expect bugs and quirks. Things are still being ironed out, so ideas and criticisms are more than welcome. + +Documentation is currently being written. In the meantime, you can take a look at the `packages/example` directory. + +## Peer dependencies +- `effect` 3.19+ +- `react` & `@types/react` 19.2+ + +## Known issues +- React Refresh doesn't work for Effect FC's yet. Page reload is required to view changes. Regular React components are unaffected. + +## What writing components looks like +```typescript +export class TodosView extends Component.make("TodosView")(function*() { + const state = yield* TodosState + const [todos] = yield* Component.useSubscribables([state.subscriptionRef]) + + yield* Component.useOnMount(() => Effect.andThen( + Console.log("Todos mounted"), + Effect.addFinalizer(() => Console.log("Todos unmounted")), + )) + + const Todo = yield* TodoView.use + + return ( + + Todos + + + + + {Chunk.map(todos, todo => + + )} + + + ) +}) {} + +const Index = Component.make("IndexView")(function*() { + const context = yield* Component.useContextFromLayer(TodosState.Default) + const Todos = yield* Effect.provide(TodosView.use, context) + + return +}).pipe( + Component.withRuntime(runtime.context) +) + +export const Route = createFileRoute("/")({ + component: Index +}) +``` diff --git a/packages/effect-fc-next/biome.json b/packages/effect-fc-next/biome.json new file mode 100644 index 0000000..41d707b --- /dev/null +++ b/packages/effect-fc-next/biome.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://biomejs.dev/schemas/latest/schema.json", + "root": false, + "extends": "//", + "files": { + "includes": ["./src/**"] + } +} diff --git a/packages/effect-fc-next/package.json b/packages/effect-fc-next/package.json new file mode 100644 index 0000000..f29a70b --- /dev/null +++ b/packages/effect-fc-next/package.json @@ -0,0 +1,54 @@ +{ + "name": "effect-fc-next", + "description": "Write React function components with Effect", + "version": "0.1.0-beta.0", + "type": "module", + "files": [ + "./README.md", + "./dist" + ], + "license": "MIT", + "repository": { + "url": "git+https://github.com/Thiladev/effect-fc.git" + }, + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "./*": [ + { + "types": "./dist/*/index.d.ts", + "default": "./dist/*/index.js" + }, + { + "types": "./dist/*.d.ts", + "default": "./dist/*.js" + } + ] + }, + "scripts": { + "build": "tsc", + "lint:tsc": "tsc --noEmit", + "lint:biome": "biome lint", + "test": "vitest run", + "pack": "npm pack", + "clean:cache": "rm -rf .turbo tsconfig.tsbuildinfo", + "clean:dist": "rm -rf dist", + "clean:modules": "rm -rf node_modules" + }, + "devDependencies": { + "@testing-library/react": "^16.3.0", + "jsdom": "^26.1.0", + "vitest": "^3.2.4" + }, + "peerDependencies": { + "@types/react": "^19.2.0", + "effect": "4.0.0-beta.85", + "react": "^19.2.0" + }, + "dependencies": { + "effect-lens": "2.0.0-beta.0" + } +} diff --git a/packages/effect-fc-next/src/Async.ts b/packages/effect-fc-next/src/Async.ts new file mode 100644 index 0000000..6495308 --- /dev/null +++ b/packages/effect-fc-next/src/Async.ts @@ -0,0 +1,171 @@ +/** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */ +import { Context, Effect, type Equivalence, Function, Predicate, Scope } from "effect" +import * as React from "react" +import * as Component from "./Component.js" + + +export const AsyncTypeId: unique symbol = Symbol.for("@effect-fc/Async/Async") +export type AsyncTypeId = typeof AsyncTypeId + + +/** + * A trait for `Component`'s that allows them running asynchronous effects. + */ +export interface Async extends AsyncPrototype, AsyncOptions {} + +export interface AsyncPrototype { + readonly [AsyncTypeId]: AsyncTypeId +} + +/** + * Configuration options for `Async` components. + */ +export interface AsyncOptions { + /** + * The default fallback React node to display while the async operation is pending. + * Used if no fallback is provided to the component when rendering. + */ + readonly defaultFallback?: React.ReactNode +} + +/** + * Props for `Async` components. + */ +export type AsyncProps = Omit + + +export const AsyncPrototype: AsyncPrototype = Object.freeze({ + [AsyncTypeId]: AsyncTypeId, + + asFunctionComponent

( + this: Component.Component & Async, + contextRef: React.RefObject>>, + ) { + const Inner = (props: { readonly promise: Promise }) => React.use(props.promise) + + return ({ fallback, name, ...props }: AsyncProps) => { + const promise = Effect.runPromiseWith(contextRef.current)( + Effect.andThen( + Component.useScope([], this), + scope => Effect.provideService(this.body(props as P), Scope.Scope, scope), + ) + ) + + return React.createElement( + React.Suspense, + { fallback: fallback ?? this.defaultFallback, name }, + React.createElement(Inner, { promise }), + ) + } + }, +} as const) + +/** + * An equivalence function for comparing `AsyncProps` that ignores the `fallback` property. + * Used by default by async components with `Memoized.memoized` applied. + */ +export const defaultPropsEquivalence: Equivalence.Equivalence = ( + self: Record, + that: Record, +) => { + if (self === that) + return true + + for (const key in self) { + if (key === "fallback") + continue + if (!(key in that) || !Object.is(self[key], that[key])) + return false + } + + for (const key in that) { + if (key === "fallback") + continue + if (!(key in self)) + return false + } + + return true +} + + +export const isAsync = (u: unknown): u is Async => Predicate.hasProperty(u, AsyncTypeId) + +/** + * Converts a Component into an `Async` component that supports running asynchronous effects. + * + * Note: The component cannot have a prop named "promise" as it's reserved for internal use. + * + * @param self - The component to convert to an Async component + * @returns A new `Async` component with the same body, error, and context types as the input + * + * @example + * ```ts + * const MyAsyncComponent = MyComponent.pipe( + * Async.async, + * ) + * ``` + */ +export const async = ( + self: T & ( + "promise" extends keyof Component.Component.Props + ? "The 'promise' prop name is restricted for Async components. Please rename the 'promise' prop to something else." + : T + ) +): ( + & Omit> + & Component.Component< + Component.Component.Props & AsyncProps, + Component.Component.Success, + Component.Component.Error, + Component.Component.Context, + Component.Component.DefaultSignature & AsyncProps, Component.Component.Success> + > + & Async +) => Object.setPrototypeOf( + Object.assign(function() {}, self, { propsEquivalence: defaultPropsEquivalence }), + Object.freeze(Object.setPrototypeOf( + Object.assign({}, AsyncPrototype), + Object.getPrototypeOf(self), + )), +) + +/** + * Applies options to an Async component, returning a new Async component with the updated configuration. + * + * Supports both curried and uncurried application styles. + * + * @param self - The Async component to apply options to (in uncurried form) + * @param options - The options to apply to the component + * @returns An Async component with the applied options + * + * @example + * ```ts + * // Curried + * const MyAsyncComponent = MyComponent.pipe( + * Async.async, + * Async.withOptions({ defaultFallback:

Loading...

}), + * ) + * + * // Uncurried + * const MyAsyncComponent = Async.withOptions( + * Async.async(MyComponent), + * { defaultFallback:

Loading...

}, + * ) + * ``` + */ +export const withOptions: { + ( + options: Partial + ): (self: T) => T + ( + self: T, + options: Partial, + ): T +} = Function.dual(2, ( + self: T, + options: Partial, +): T => Object.setPrototypeOf( + Object.assign(function() {}, self, options), + Object.getPrototypeOf(self), +)) diff --git a/packages/effect-fc-next/src/Component.ts b/packages/effect-fc-next/src/Component.ts new file mode 100644 index 0000000..6bf9d93 --- /dev/null +++ b/packages/effect-fc-next/src/Component.ts @@ -0,0 +1,385 @@ +/** biome-ignore-all lint/complexity/noBannedTypes: {} is the default type for React props */ +import { + Context, + type Duration, + Effect, + Exit, + Function, + identity, + Layer, + Pipeable, + Predicate, + Scope, + Tracer, +} from "effect" +import * as React from "react" + + +export const ComponentTypeId: unique symbol = Symbol.for("@effect-fc/Component/Component") +export type ComponentTypeId = typeof ComponentTypeId + +export interface Component

+extends ComponentPrototype, ComponentOptions { + new(_: never): Record + readonly [ComponentTypeId]: ComponentTypeId + readonly "~Props": P + readonly "~Success": A + readonly "~Error": E + readonly "~Context": R + readonly "~Function": F + readonly body: (props: P) => Effect.Effect +} + +export declare namespace Component { + export type Default

= Component> + export type Any = Component + export type Signature = (props: any) => React.ReactNode + export type DefaultSignature

= (props: P) => A + export type Props = T["~Props"] + export type Success = T["~Success"] + export type Error = T["~Error"] + export type Context = T["~Context"] + export type Function = T["~Function"] + export type AsComponent = Component, Success, Error, Context, Function> +} + +export interface ComponentOptions { + readonly displayName?: string + readonly nonReactiveTags: readonly Context.Key[] + readonly finalizerExecutionStrategy: "sequential" | "parallel" + readonly finalizerExecutionDebounce: Duration.Input +} + +export const defaultOptions: ComponentOptions = { + nonReactiveTags: [Tracer.ParentSpan], + finalizerExecutionStrategy: "sequential", + finalizerExecutionDebounce: "100 millis", +} + +export interface ComponentPrototype extends Pipeable.Pipeable { + readonly [ComponentTypeId]: ComponentTypeId + readonly use: Effect.Effect> +} + +type ComponentImpl = Component + +const makeFunctionComponent = ( + self: ComponentImpl, + contextRef: React.RefObject>, +): Component.Signature => { + if ("asFunctionComponent" in self && typeof self.asFunctionComponent === "function") { + return self.asFunctionComponent(contextRef) + } + const FunctionComponent = (props: {}) => Effect.runSyncWith(contextRef.current)( + Effect.flatMap( + useScope([], self), + scope => Effect.provideService(self.body(props), Scope.Scope, scope), + ), + ) + FunctionComponent.displayName = self.displayName ?? "Anonymous" + return "transformFunctionComponent" in self && typeof self.transformFunctionComponent === "function" + ? self.transformFunctionComponent(FunctionComponent) + : FunctionComponent +} + +const use = Effect.fnUntraced(function* (self: ComponentImpl) { + const context = yield* Effect.context() + const cached = componentCache.get(self) + if (cached !== undefined) { + cached.contextRef.current = context + return cached.component + } + const contextRef = { current: context } + const component = makeFunctionComponent(self, contextRef) + componentCache.set(self, { contextRef, component }) + return component +}) + +const componentCache = new WeakMap } + readonly component: Component.Signature +}>() + +export const ComponentPrototype = Object.freeze({ + [ComponentTypeId]: ComponentTypeId, + ...Pipeable.Prototype, + get use() { + return use(this as ComponentImpl) + }, +}) as unknown as ComponentPrototype + +export const isComponent = (u: unknown): u is Component.Any => Predicate.hasProperty(u, ComponentTypeId) + +type GeneratorBody

= ( + props: P, +) => Effect.fn.Return + +type EffectBody

= ( + props: P, +) => Effect.Effect + +export interface Make { +

( + body: GeneratorBody | EffectBody, + ...pipeables: readonly Function[] + ): Component.Default + (name: string, options?: Tracer.SpanOptionsNoTrace):

( + body: GeneratorBody | EffectBody, + ...pipeables: readonly Function[] + ) => Component.Default +} + +const component = ( + body: Function, + displayName: string | undefined, + traced: boolean, + pipeables: readonly Function[], +): Component.Any => Object.setPrototypeOf( + Object.assign(function() {}, defaultOptions, { + body: traced && displayName + ? Effect.fn(displayName)(body as never, ...pipeables as []) + : Effect.fnUntraced(body as never, ...pipeables as []), + displayName, + }), + ComponentPrototype, +) + +export const make: Make = ((nameOrBody: string | Function, ...args: readonly unknown[]) => { + if (typeof nameOrBody === "string") { + return (body: Function, ...pipeables: readonly Function[]) => component(body, nameOrBody, true, pipeables) + } + return component(nameOrBody, undefined, true, args as readonly Function[]) +}) as Make + +export const makeUntraced: Make = ((nameOrBody: string | Function, ...args: readonly unknown[]) => { + if (typeof nameOrBody === "string") { + return (body: Function, ...pipeables: readonly Function[]) => component(body, nameOrBody, false, pipeables) + } + return component(nameOrBody, undefined, false, args as readonly Function[]) +}) as Make + +export declare namespace withSignature { + export type Result = ( + & Omit> + & Component, Component.Success, Component.Error, Component.Context, F> + ) +} + +export const withSignature: { + (): (self: T) => withSignature.Result + (self: T): withSignature.Result +} = (self?: Component.Any): any => self === undefined ? identity : self + +export const withOptions: { + (options: Partial): (self: T) => T + (self: T, options: Partial): T +} = Function.dual(2, (self: T, options: Partial): T => Object.setPrototypeOf( + Object.assign(function() {}, self, options), + Object.getPrototypeOf(self), +)) + +export const withRuntime: { +

( + context: React.Context>, + ): (self: Component, F>) => F +

( + self: Component, F>, + context: React.Context>, + ): F +} = Function.dual(2,

( + self: Component, + context: React.Context>, +) => function WithRuntime(props: P) { + return React.createElement( + Effect.runSyncWith(React.useContext(context))(self.use) as React.FC

, + props, + ) +}) + +export declare namespace useScope { + export interface Options { + readonly finalizerExecutionStrategy?: "sequential" | "parallel" + readonly finalizerExecutionDebounce?: Duration.Input + } +} + +export const useScope = Effect.fnUntraced(function* ( + deps: React.DependencyList, + options?: useScope.Options, +): Effect.fn.Return { + const context = yield* Effect.context() + const contextRef = React.useRef(context) + contextRef.current = context + const scope = React.useMemo( + () => Scope.makeUnsafe(options?.finalizerExecutionStrategy ?? defaultOptions.finalizerExecutionStrategy), + // biome-ignore lint/correctness/useExhaustiveDependencies: caller controls scope lifetime + deps, + ) + + React.useEffect(() => { + const pending = scopeCleanupTimers.get(scope) + if (pending !== undefined) clearTimeout(pending) + return () => { + const timer = setTimeout(() => { + Effect.runSyncWith(contextRef.current)(Scope.close(scope, Exit.succeed(undefined))) + scopeCleanupTimers.delete(scope) + }, durationMillis(options?.finalizerExecutionDebounce ?? defaultOptions.finalizerExecutionDebounce)) + scopeCleanupTimers.set(scope, timer) + } + }, [scope, options?.finalizerExecutionDebounce]) + + return scope +}) + +const scopeCleanupTimers = new WeakMap>() + +const durationMillis = (input: Duration.Input): number => { + if (typeof input === "number") return input + return Number(Effect.runSync(Effect.map(Effect.succeed(input), value => { + const match = typeof value === "string" ? /([\d.]+)\s*(millis|seconds?)/.exec(value) : undefined + if (!match) return 0 + return Number(match[1]) * (match[2].startsWith("second") ? 1_000 : 1) + }))) +} + +export const useOnMount = Effect.fnUntraced(function* ( + f: () => Effect.Effect, +): Effect.fn.Return { + const context = yield* Effect.context() + const id = React.useId() + let cached = mountCache.get(id) + if (cached === undefined) { + cached = { + effect: Effect.runSyncWith(context)(Effect.cached(f())), + } + mountCache.set(id, cached) + } + React.useEffect(() => { + if (cached?.cleanup !== undefined) clearTimeout(cached.cleanup) + const entry = cached + return () => { + entry.cleanup = setTimeout(() => mountCache.delete(id), 0) + } + }, [id, cached]) + return yield* cached.effect as Effect.Effect +}) + +const mountCache = new Map + cleanup?: ReturnType +}>() + +export declare namespace useOnChange { + export interface Options extends useScope.Options {} +} + +export const useOnChange = Effect.fnUntraced(function* ( + f: () => Effect.Effect, + deps: React.DependencyList, + options?: useOnChange.Options, +): Effect.fn.Return> { + const context = yield* Effect.context>() + const scope = yield* useScope(deps, options) + const cached = + // biome-ignore lint/correctness/useExhaustiveDependencies: scope tracks the caller-provided dependency list + React.useMemo( + () => Effect.runSyncWith(context)(Effect.cached(Effect.provideService(f(), Scope.Scope, scope))), + [scope], + ) + return yield* cached +}) + +export declare namespace useReactEffect { + export interface Options { + readonly finalizerExecutionMode?: "sync" | "fork" + readonly finalizerExecutionStrategy?: "sequential" | "parallel" + } +} + +const runReactEffect = ( + context: Context.Context>, + f: () => Effect.Effect, + options?: useReactEffect.Options, +): (() => void) => { + const scope = Scope.makeUnsafe(options?.finalizerExecutionStrategy ?? defaultOptions.finalizerExecutionStrategy) + Effect.runSyncWith(context)(Effect.exit(Effect.provideService(f(), Scope.Scope, scope))) + return () => { + const close = Scope.close(scope, Exit.succeed(undefined)) + if ((options?.finalizerExecutionMode ?? "fork") === "sync") Effect.runSyncWith(context)(close) + else Effect.runForkWith(context)(close) + } +} + +export const useReactEffect = Effect.fnUntraced(function* ( + f: () => Effect.Effect, + deps?: React.DependencyList, + options?: useReactEffect.Options, +): Effect.fn.Return> { + const context = yield* Effect.context>() + // biome-ignore lint/correctness/useExhaustiveDependencies: caller supplies React dependencies + React.useEffect(() => runReactEffect(context, f, options), deps) +}) + +export declare namespace useReactLayoutEffect { + export interface Options extends useReactEffect.Options {} +} + +export const useReactLayoutEffect = Effect.fnUntraced(function* ( + f: () => Effect.Effect, + deps?: React.DependencyList, + options?: useReactLayoutEffect.Options, +): Effect.fn.Return> { + const context = yield* Effect.context>() + // biome-ignore lint/correctness/useExhaustiveDependencies: caller supplies React dependencies + React.useLayoutEffect(() => runReactEffect(context, f, options), deps) +}) + +export const useRunSync = (): Effect.Effect< + (effect: Effect.Effect) => A, + never, + R +> => Effect.map(Effect.context(), Effect.runSyncWith) + +export const useRunPromise = (): Effect.Effect< + (effect: Effect.Effect) => Promise, + never, + R +> => Effect.map(Effect.context(), Effect.runPromiseWith) + +export const useCallbackSync = Effect.fnUntraced(function* ( + f: (...args: Args) => Effect.Effect, + deps: React.DependencyList, +): Effect.fn.Return<(...args: Args) => A, never, R> { + const context = yield* Effect.context() + const contextRef = React.useRef(context) + contextRef.current = context + // biome-ignore lint/correctness/useExhaustiveDependencies: caller supplies React dependencies + return React.useCallback((...args: Args) => Effect.runSyncWith(contextRef.current)(f(...args)), deps) +}) + +export const useCallbackPromise = Effect.fnUntraced(function* ( + f: (...args: Args) => Effect.Effect, + deps: React.DependencyList, +): Effect.fn.Return<(...args: Args) => Promise, never, R> { + const context = yield* Effect.context() + const contextRef = React.useRef(context) + contextRef.current = context + // biome-ignore lint/correctness/useExhaustiveDependencies: caller supplies React dependencies + return React.useCallback((...args: Args) => Effect.runPromiseWith(contextRef.current)(f(...args)), deps) +}) + +export declare namespace useContext { + export interface Options extends useOnChange.Options {} +} + +export const useContextFromLayer = ( + layer: Layer.Layer, + options?: useContext.Options, +): Effect.Effect, E, RIn | Scope.Scope> => useOnChange( + () => Effect.flatMap( + Effect.context(), + context => Layer.build(Layer.provide(layer, Layer.succeedContext(context))), + ), + [layer], + options, +) diff --git a/packages/effect-fc-next/src/ErrorObserver.ts b/packages/effect-fc-next/src/ErrorObserver.ts new file mode 100644 index 0000000..0fd19aa --- /dev/null +++ b/packages/effect-fc-next/src/ErrorObserver.ts @@ -0,0 +1,47 @@ +import { type Cause, Context, Effect, Layer, Option, Pipeable, Predicate, PubSub, type Scope } from "effect" + + +export const ErrorObserverTypeId: unique symbol = Symbol.for("@effect-fc/ErrorObserver/ErrorObserver") +export type ErrorObserverTypeId = typeof ErrorObserverTypeId + +export interface ErrorObserver extends Pipeable.Pipeable { + readonly [ErrorObserverTypeId]: ErrorObserverTypeId + handle(effect: Effect.Effect): Effect.Effect + readonly subscribe: Effect.Effect>, never, Scope.Scope> +} + +export const ErrorObserver = () => Context.Service>( + "@effect-fc/ErrorObserver/ErrorObserver", +) + +export class ErrorObserverImpl extends Pipeable.Class implements ErrorObserver { + readonly [ErrorObserverTypeId]: ErrorObserverTypeId = ErrorObserverTypeId + + constructor(readonly pubsub: PubSub.PubSub>) { + super() + } + + get subscribe(): Effect.Effect>, never, Scope.Scope> { + return PubSub.subscribe(this.pubsub) + } + + handle(effect: Effect.Effect): Effect.Effect { + return Effect.tapCause(effect, cause => Effect.asVoid( + PubSub.publish(this.pubsub, cause as unknown as Cause.Cause), + )) + } +} + +export const isErrorObserver = (u: unknown): u is ErrorObserver => Predicate.hasProperty(u, ErrorObserverTypeId) + +export const layer: Layer.Layer = Layer.effect(ErrorObserver())( + Effect.map(PubSub.unbounded>(), pubsub => new ErrorObserverImpl(pubsub)), +) + +export const handle = (effect: Effect.Effect): Effect.Effect => Effect.flatMap( + Effect.serviceOption(ErrorObserver()), + Option.match({ + onSome: observer => observer.handle(effect), + onNone: () => effect, + }), +) diff --git a/packages/effect-fc-next/src/Form.ts b/packages/effect-fc-next/src/Form.ts new file mode 100644 index 0000000..30039aa --- /dev/null +++ b/packages/effect-fc-next/src/Form.ts @@ -0,0 +1,279 @@ +import { Array, type Cause, Chunk, type Duration, Effect, Equal, Function, identity, Option, Pipeable, Predicate, type Scope, Stream, SubscriptionRef } from "effect" +import type * as React from "react" +import * as Component from "./Component.js" +import * as Lens from "./Lens.js" +import * as Subscribable from "./Subscribable.js" + + +export const FormTypeId: unique symbol = Symbol.for("@effect-fc/Form/Form") +export type FormTypeId = typeof FormTypeId + +export interface FormIssue { + readonly path: readonly PropertyKey[] + readonly message: string +} + +export interface Form +extends Pipeable.Pipeable { + readonly [FormTypeId]: FormTypeId + + readonly path: P + readonly value: Subscribable.Subscribable, ER, never> + readonly encodedValue: Lens.Lens + readonly issues: Subscribable.Subscribable + readonly isValidating: Subscribable.Subscribable + readonly canCommit: Subscribable.Subscribable + readonly isCommitting: Subscribable.Subscribable +} + +export class FormImpl +extends Pipeable.Class implements Form { + readonly [FormTypeId]: FormTypeId = FormTypeId + + constructor( + readonly path: P, + readonly value: Subscribable.Subscribable, ER, never>, + readonly encodedValue: Lens.Lens, + readonly issues: Subscribable.Subscribable, + readonly isValidating: Subscribable.Subscribable, + readonly canCommit: Subscribable.Subscribable, + readonly isCommitting: Subscribable.Subscribable, + ) { + super() + } +} + + +export const isForm = (u: unknown): u is Form => Predicate.hasProperty(u, FormTypeId) + + +const filterIssuesByPath = ( + issues: readonly FormIssue[], + path: readonly PropertyKey[], +): readonly FormIssue[] => Array.filter(issues, issue => + issue.path.length >= path.length && Array.every(path, (p, i) => p === issue.path[i]) +) + +export const focusObjectOn: { +

( + self: Form, + key: K, + ): Form +

( + key: K, + ): (self: Form) => Form +} = Function.dual(2,

( + self: Form, + key: K, +): Form => { + const form = self as unknown as FormImpl + const path = [...form.path, key] as const + + return new FormImpl( + path, + Subscribable.mapOption(form.value, a => a[key]), + Lens.focusObjectOn(form.encodedValue, key), + Subscribable.map(form.issues, issues => filterIssuesByPath(issues, path)), + form.isValidating, + form.canCommit, + form.isCommitting, + ) +}) + +export const focusArrayAt: { +

( + self: Form, + index: number, + ): Form +

( + index: number, + ): (self: Form) => Form +} = Function.dual(2,

( + self: Form, + index: number, +): Form => { + const form = self as unknown as FormImpl + const path = [...form.path, index] as const + + return new FormImpl( + path, + Subscribable.mapOptionEffect(form.value, values => Effect.fromOption(Array.get(values, index))), + Lens.focusArrayAt(form.encodedValue, index), + Subscribable.map(form.issues, issues => filterIssuesByPath(issues, path)), + form.isValidating, + form.canCommit, + form.isCommitting, + ) +}) + +export const focusTupleAt: { +

( + self: Form, + index: K, + ): Form +

( + index: K, + ): (self: Form) => Form +} = Function.dual(2,

( + self: Form, + index: K, +): Form => { + const form = self as unknown as FormImpl + const path = [...form.path, index] as const + + return new FormImpl( + path, + Subscribable.mapOption(form.value, values => values[index]), + Lens.focusTupleAt(form.encodedValue, index), + Subscribable.map(form.issues, issues => filterIssuesByPath(issues, path)), + form.isValidating, + form.canCommit, + form.isCommitting, + ) +}) + +export const focusChunkAt: { +

( + self: Form, Chunk.Chunk, ER, EW>, + index: number, + ): Form +

( + index: number, + ): (self: Form, Chunk.Chunk, ER, EW>) => Form +} = Function.dual(2,

( + self: Form, Chunk.Chunk, ER, EW>, + index: number, +): Form => { + const form = self as unknown as FormImpl, Chunk.Chunk, ER, EW> + const path = [...form.path, index] as const + + return new FormImpl( + path, + Subscribable.mapOptionEffect(form.value, values => Effect.fromOption(Chunk.get(values, index))), + Lens.focusChunkAt(form.encodedValue, index), + Subscribable.map(form.issues, issues => filterIssuesByPath(issues, path)), + form.isValidating, + form.canCommit, + form.isCommitting, + ) +}) + + +export namespace useInput { + export interface Options { + readonly debounce?: Duration.Input + } + + export interface Success { + readonly value: T + readonly setValue: React.Dispatch> + } +} + +export const useInput = Effect.fnUntraced(function*

( + form: Form, + options?: useInput.Options, +): Effect.fn.Return, ER, Scope.Scope> { + const internalValueLens = yield* Component.useOnChange(() => Effect.gen(function*() { + const internalValueLens = yield* Lens.get(form.encodedValue).pipe( + Effect.flatMap(SubscriptionRef.make), + Effect.map(Lens.fromSubscriptionRef), + ) + + yield* Effect.forkScoped(Effect.all([ + Stream.runForEach( + Stream.drop(form.encodedValue.changes, 1), + upstreamEncodedValue => Effect.flatMap( + Lens.get(internalValueLens), + internalValue => !Equal.equals(upstreamEncodedValue, internalValue) + ? Lens.set(internalValueLens, upstreamEncodedValue) + : Effect.succeed(undefined), + ), + ), + + Stream.runForEach( + internalValueLens.changes.pipe( + Stream.drop(1), + Stream.changesWith(Equal.asEquivalence()), + options?.debounce ? Stream.debounce(options.debounce) : identity, + ), + internalValue => Lens.set(form.encodedValue, internalValue), + ), + ], { concurrency: "unbounded", discard: true })) + + return internalValueLens + }), [form, options?.debounce]) + + const [value, setValue] = yield* Lens.useState(internalValueLens) + return { value, setValue } +}) + +export namespace useOptionalInput { + export interface Options extends useInput.Options { + readonly defaultValue: T + } + + export interface Success extends useInput.Success { + readonly enabled: boolean + readonly setEnabled: React.Dispatch> + } +} + +export const useOptionalInput = Effect.fnUntraced(function*

( + field: Form, ER, EW>, + options: useOptionalInput.Options, +): Effect.fn.Return, ER, Scope.Scope> { + const [enabledLens, internalValueLens] = yield* Component.useOnChange(() => Effect.gen(function*() { + const [enabledLens, internalValueLens] = yield* Effect.flatMap( + Lens.get(field.encodedValue), + Option.match({ + onSome: v => Effect.all([ + Effect.map(SubscriptionRef.make(true), Lens.fromSubscriptionRef), + Effect.map(SubscriptionRef.make(v), Lens.fromSubscriptionRef), + ]), + onNone: () => Effect.all([ + Effect.map(SubscriptionRef.make(false), Lens.fromSubscriptionRef), + Effect.map(SubscriptionRef.make(options.defaultValue), Lens.fromSubscriptionRef), + ]), + }), + ) + + yield* Effect.forkScoped(Effect.all([ + Stream.runForEach( + Stream.drop(field.encodedValue.changes, 1), + + upstreamEncodedValue => Effect.flatMap( + Effect.all([Lens.get(enabledLens), Lens.get(internalValueLens)]), + ([enabled, internalValue]) => Equal.equals(upstreamEncodedValue, enabled ? Option.some(internalValue) : Option.none()) + ? Effect.succeed(undefined) + : Option.match(upstreamEncodedValue, { + onSome: v => Effect.andThen( + Lens.set(enabledLens, true), + Lens.set(internalValueLens, v), + ), + onNone: () => Effect.andThen( + Lens.set(enabledLens, false), + Lens.set(internalValueLens, options.defaultValue), + ), + }), + ), + ), + + Stream.runForEach( + enabledLens.changes.pipe( + Stream.zipLatest(internalValueLens.changes), + Stream.drop(1), + Stream.changesWith(Equal.asEquivalence()), + options?.debounce ? Stream.debounce(options.debounce) : identity, + ), + ([enabled, internalValue]) => Lens.set(field.encodedValue, enabled ? Option.some(internalValue) : Option.none()), + ), + ], { concurrency: "unbounded" })) + + return [enabledLens, internalValueLens] as const + }), [field, options.debounce]) + + const [enabled, setEnabled] = yield* Lens.useState(enabledLens) + const [value, setValue] = yield* Lens.useState(internalValueLens) + return { enabled, setEnabled, value, setValue } +}) diff --git a/packages/effect-fc-next/src/Lens.ts b/packages/effect-fc-next/src/Lens.ts new file mode 100644 index 0000000..4941dea --- /dev/null +++ b/packages/effect-fc-next/src/Lens.ts @@ -0,0 +1,62 @@ +import { Effect, Equivalence, Stream, SubscriptionRef } from "effect" +import { Lens } from "effect-lens" +import * as React from "react" +import * as Component from "./Component.js" +import * as SetStateAction from "./SetStateAction.js" + + +export * from "effect-lens/Lens" + +export declare namespace useState { + export interface Options { + readonly equivalence?: Equivalence.Equivalence + } +} + +export const useState = Effect.fnUntraced(function* ( + lens: Lens.Lens, + options?: useState.Options>, +): Effect.fn.Return>], ER | EW, RR | RW> { + const [reactStateValue, setReactStateValue] = React.useState(yield* Component.useOnMount(() => Lens.get(lens))) + + yield* Component.useReactEffect(() => Effect.forkScoped( + Stream.runForEach( + Stream.changesWith(lens.changes, options?.equivalence ?? Equivalence.strictEqual()), + v => Effect.sync(() => setReactStateValue(v)), + ) + ), [lens]) + + const setValue = yield* Component.useCallbackSync( + (setStateAction: React.SetStateAction) => Effect.tap( + Lens.updateAndGet(lens, prevState => SetStateAction.value(setStateAction, prevState)), + v => Effect.sync(() => setReactStateValue(v)), + ), + [lens], + ) + + return [reactStateValue, setValue] +}) + +export declare namespace useFromReactState { + export interface Options { + readonly equivalence?: Equivalence.Equivalence + } +} + +export const useFromReactState = Effect.fnUntraced(function* ( + [value, setValue]: readonly [A, React.Dispatch>], + options?: useFromReactState.Options>, +): Effect.fn.Return> { + const lens = yield* Component.useOnMount(() => Effect.map( + SubscriptionRef.make(value), + Lens.fromSubscriptionRef, + )) + + yield* Component.useReactEffect(() => Effect.forkScoped(Stream.runForEach( + Stream.changesWith(lens.changes, options?.equivalence ?? Equivalence.strictEqual()), + v => Effect.sync(() => setValue(v)), + )), [setValue]) + yield* Component.useReactEffect(() => Lens.set(lens, value), [value]) + + return lens +}) diff --git a/packages/effect-fc-next/src/Memoized.ts b/packages/effect-fc-next/src/Memoized.ts new file mode 100644 index 0000000..0c981c8 --- /dev/null +++ b/packages/effect-fc-next/src/Memoized.ts @@ -0,0 +1,112 @@ +/** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */ +import { type Equivalence, Function, Predicate } from "effect" +import * as React from "react" +import type * as Component from "./Component.js" + + +export const MemoizedTypeId: unique symbol = Symbol.for("@effect-fc/Memoized/Memoized") +export type MemoizedTypeId = typeof MemoizedTypeId + + +/** + * A trait for `Component`'s that uses `React.memo` to optimize re-renders based on prop equality. + * + * @template P The props type of the component + */ +export interface Memoized

extends MemoizedPrototype, MemoizedOptions

{} + +export interface MemoizedPrototype { + readonly [MemoizedTypeId]: MemoizedTypeId +} + +/** + * Configuration options for Memoized components. + * + * @template P The props type of the component + */ +export interface MemoizedOptions

{ + /** + * An optional equivalence function for comparing component props. + * If provided, this function is used by React.memo to determine if props have changed. + * Returns `true` if props are equivalent (no re-render), `false` if they differ (re-render). + */ + readonly propsEquivalence?: Equivalence.Equivalence

+} + + +export const MemoizedPrototype: MemoizedPrototype = Object.freeze({ + [MemoizedTypeId]: MemoizedTypeId, + + transformFunctionComponent

( + this: Memoized

, + f: React.FC

, + ) { + return React.memo(f, this.propsEquivalence) + }, +} as const) + + +export const isMemoized = (u: unknown): u is Memoized => Predicate.hasProperty(u, MemoizedTypeId) + +/** + * Converts a Component into a `Memoized` component that optimizes re-renders using `React.memo`. + * + * @param self - The component to convert to a Memoized component + * @returns A new `Memoized` component with the same body, error, and context types as the input + * + * @example + * ```ts + * const MyMemoizedComponent = MyComponent.pipe( + * Memoized.memoized, + * ) + * ``` + */ +export const memoized = ( + self: T +): T & Memoized> => Object.setPrototypeOf( + Object.assign(function() {}, self), + Object.freeze(Object.setPrototypeOf( + Object.assign({}, MemoizedPrototype), + Object.getPrototypeOf(self), + )), +) + +/** + * Applies options to a Memoized component, returning a new Memoized component with the updated configuration. + * + * Supports both curried and uncurried application styles. + * + * @param self - The Memoized component to apply options to (in uncurried form) + * @param options - The options to apply to the component + * @returns A Memoized component with the applied options + * + * @example + * ```ts + * // Curried + * const MyMemoizedComponent = MyComponent.pipe( + * Memoized.memoized, + * Memoized.withOptions({ propsEquivalence: (a, b) => a.id === b.id }), + * ) + * + * // Uncurried + * const MyMemoizedComponent = Memoized.withOptions( + * Memoized.memoized(MyComponent), + * { propsEquivalence: (a, b) => a.id === b.id }, + * ) + * ``` + */ +export const withOptions: { + >( + options: Partial>> + ): (self: T) => T + >( + self: T, + options: Partial>>, + ): T +} = Function.dual(2, >( + self: T, + options: Partial>>, +): T => Object.setPrototypeOf( + Object.assign(function() {}, self, options), + Object.getPrototypeOf(self), +)) diff --git a/packages/effect-fc-next/src/Mutation.ts b/packages/effect-fc-next/src/Mutation.ts new file mode 100644 index 0000000..0d873eb --- /dev/null +++ b/packages/effect-fc-next/src/Mutation.ts @@ -0,0 +1,146 @@ +import { type Context, Effect, Equal, type Fiber, Option, Pipeable, Predicate, type Scope, Stream, SubscriptionRef } from "effect" +import { Subscribable } from "effect-lens" +import * as Result from "./Result.js" + + +export const MutationTypeId: unique symbol = Symbol.for("@effect-fc/Mutation/Mutation") +export type MutationTypeId = typeof MutationTypeId + +export interface Mutation +extends Pipeable.Pipeable { + readonly [MutationTypeId]: MutationTypeId + + readonly context: Context.Context + readonly f: (key: K) => Effect.Effect + readonly initialProgress: P + + readonly latestKey: Subscribable.Subscribable> + readonly fiber: Subscribable.Subscribable>> + readonly result: Subscribable.Subscribable> + readonly latestFinalResult: Subscribable.Subscribable>> + + mutate(key: K): Effect.Effect> + mutateSubscribable(key: K): Effect.Effect>> +} + +export declare namespace Mutation { + export type AnyKey = readonly any[] +} + +export class MutationImpl +extends Pipeable.Class implements Mutation { + readonly [MutationTypeId]: MutationTypeId = MutationTypeId + readonly latestKey: Subscribable.Subscribable> + readonly fiber: Subscribable.Subscribable>> + readonly result: Subscribable.Subscribable> + readonly latestFinalResult: Subscribable.Subscribable>> + + constructor( + readonly context: Context.Context, + readonly f: (key: K) => Effect.Effect, + readonly initialProgress: P, + + readonly latestKeyRef: SubscriptionRef.SubscriptionRef>, + readonly fiberRef: SubscriptionRef.SubscriptionRef>>, + readonly resultRef: SubscriptionRef.SubscriptionRef>, + readonly latestFinalResultRef: SubscriptionRef.SubscriptionRef>>, + ) { + super() + this.latestKey = fromSubscriptionRef(latestKeyRef) + this.fiber = fromSubscriptionRef(fiberRef) + this.result = fromSubscriptionRef(resultRef) + this.latestFinalResult = fromSubscriptionRef(latestFinalResultRef) + } + + mutate(key: K): Effect.Effect> { + return SubscriptionRef.set(this.latestKeyRef, Option.some(key)).pipe( + Effect.andThen(this.start(key)), + Effect.andThen(sub => this.watch(sub)), + Effect.provide(this.context), + ) + } + mutateSubscribable(key: K): Effect.Effect>> { + return SubscriptionRef.set(this.latestKeyRef, Option.some(key)).pipe( + Effect.andThen(this.start(key)), + Effect.tap(sub => Effect.forkScoped(this.watch(sub))), + Effect.provide(this.context), + ) + } + + start(key: K): Effect.Effect< + Subscribable.Subscribable>, + never, + Scope.Scope | R + > { + const self = this + return Effect.gen(function*() { + const initial = yield* SubscriptionRef.get(self.latestFinalResultRef) + const [sub, fiber] = yield* Result.unsafeForkEffect( + Effect.onExit(self.f(key), () => Effect.andThen( + Effect.all([Effect.fiberId, SubscriptionRef.get(self.fiberRef)]), + ([currentFiberId, fiber]) => Option.match(fiber, { + onSome: v => Equal.equals(currentFiberId, v.id) + ? SubscriptionRef.set(self.fiberRef, Option.none()) + : Effect.succeed(undefined), + onNone: () => Effect.succeed(undefined), + }), + )), + + { + initial: Option.isSome(initial) ? Result.willFetch(initial.value) : Result.initial(), + initialProgress: self.initialProgress, + } as Result.unsafeForkEffect.Options, + ) + yield* SubscriptionRef.set(self.fiberRef, Option.some(fiber)) + return sub + }) + } + + watch( + sub: Subscribable.Subscribable> + ): Effect.Effect> { + return sub.get.pipe( + Effect.andThen(initial => Stream.runFoldEffect( + Stream.takeUntil(sub.changes, result => Result.isFinal(result) && !Result.hasFlag(result)), + () => initial, + (_, result) => Effect.as(SubscriptionRef.set(this.resultRef, result), result), + ) as Effect.Effect>), + Effect.tap(result => SubscriptionRef.set(this.latestFinalResultRef, Option.some(result))), + ) + } +} + + +export const isMutation = (u: unknown): u is Mutation => Predicate.hasProperty(u, MutationTypeId) + + +export declare namespace make { + export interface Options { + readonly f: (key: K) => Effect.Effect>> + readonly initialProgress?: P + } +} + +export const make = Effect.fnUntraced(function* ( + options: make.Options +): Effect.fn.Return< + Mutation, P>, + never, + Scope.Scope | Result.forkEffect.OutputContext +> { + return new MutationImpl( + yield* Effect.context>(), + options.f as any, + options.initialProgress as P, + + yield* SubscriptionRef.make(Option.none()), + yield* SubscriptionRef.make(Option.none>()), + yield* SubscriptionRef.make(Result.initial()), + yield* SubscriptionRef.make(Option.none>()), + ) +}) + +const fromSubscriptionRef = (ref: SubscriptionRef.SubscriptionRef): Subscribable.Subscribable => Subscribable.make({ + get: SubscriptionRef.get(ref), + changes: SubscriptionRef.changes(ref), +}) diff --git a/packages/effect-fc-next/src/PubSub.ts b/packages/effect-fc-next/src/PubSub.ts new file mode 100644 index 0000000..27401bc --- /dev/null +++ b/packages/effect-fc-next/src/PubSub.ts @@ -0,0 +1,17 @@ +import { Effect, PubSub, type Scope } from "effect" +import type * as React from "react" +import * as Component from "./Component.js" + + +export const useFromReactiveValues = Effect.fnUntraced(function* ( + values: A +): Effect.fn.Return, never, Scope.Scope> { + const pubsub = yield* Component.useOnMount(() => Effect.acquireRelease(PubSub.unbounded(), PubSub.shutdown)) + yield* Component.useReactEffect(() => Effect.flatMap( + PubSub.isShutdown(pubsub), + shutdown => shutdown ? Effect.succeed(undefined) : Effect.asVoid(PubSub.publish(pubsub, values)), + ), values) + return pubsub +}) + +export * from "effect/PubSub" diff --git a/packages/effect-fc-next/src/Query.ts b/packages/effect-fc-next/src/Query.ts new file mode 100644 index 0000000..e9e317b --- /dev/null +++ b/packages/effect-fc-next/src/Query.ts @@ -0,0 +1,283 @@ +import { + type Cause, + type Context, + type Duration, + Effect, + Equal, + Fiber, + Option, + Pipeable, + Predicate, + type Scope, + Semaphore, + Stream, + SubscriptionRef, +} from "effect" +import { Subscribable } from "effect-lens" +import * as QueryClient from "./QueryClient.js" +import * as Result from "./Result.js" + + +export const QueryTypeId: unique symbol = Symbol.for("@effect-fc/Query/Query") +export type QueryTypeId = typeof QueryTypeId + +export interface Query +extends Pipeable.Pipeable { + readonly [QueryTypeId]: QueryTypeId + readonly context: Context.Context + readonly key: Stream.Stream + readonly f: (key: K) => Effect.Effect + readonly initialProgress: P + readonly staleTime: Duration.Input + readonly refreshOnWindowFocus: boolean + readonly latestKey: Subscribable.Subscribable> + readonly fiber: Subscribable.Subscribable>> + readonly result: Subscribable.Subscribable> + readonly latestFinalResult: Subscribable.Subscribable>> + readonly run: Effect.Effect + fetch(key: K): Effect.Effect> + fetchSubscribable(key: K): Effect.Effect>> + readonly refresh: Effect.Effect, Cause.NoSuchElementError> + readonly refreshSubscribable: Effect.Effect>, Cause.NoSuchElementError> + readonly invalidateCache: Effect.Effect + invalidateCacheEntry(key: K): Effect.Effect +} + +export declare namespace Query { + export type AnyKey = readonly any[] +} + +export class QueryImpl +extends Pipeable.Class implements Query { + readonly [QueryTypeId]: QueryTypeId = QueryTypeId + readonly latestKey: Subscribable.Subscribable> + readonly fiber: Subscribable.Subscribable>> + readonly result: Subscribable.Subscribable> + readonly latestFinalResult: Subscribable.Subscribable>> + + constructor( + readonly context: Context.Context, + readonly key: Stream.Stream, + readonly f: (key: K) => Effect.Effect, + readonly initialProgress: P, + readonly staleTime: Duration.Input, + readonly refreshOnWindowFocus: boolean, + readonly latestKeyRef: SubscriptionRef.SubscriptionRef>, + readonly fiberRef: SubscriptionRef.SubscriptionRef>>, + readonly resultRef: SubscriptionRef.SubscriptionRef>, + readonly latestFinalResultRef: SubscriptionRef.SubscriptionRef>>, + readonly runSemaphore: Semaphore.Semaphore, + ) { + super() + this.latestKey = fromSubscriptionRef(latestKeyRef) + this.fiber = fromSubscriptionRef(fiberRef) + this.result = fromSubscriptionRef(resultRef) + this.latestFinalResult = fromSubscriptionRef(latestFinalResultRef) + } + + get run(): Effect.Effect { + const focus = this.refreshOnWindowFocus && typeof window !== "undefined" + ? Stream.runForEach(Stream.fromEventListener(window, "focus"), () => this.refreshSubscribable) + : Effect.succeed(undefined) + return Effect.all([ + Stream.runForEach(this.key, key => this.fetchSubscribable(key)), + focus, + ], { concurrency: "unbounded", discard: true }).pipe( + Effect.ignore, + this.runSemaphore.withPermits(1), + Effect.provide(this.context), + ) + } + + get interrupt(): Effect.Effect { + return Effect.flatMap(SubscriptionRef.get(this.fiberRef), Option.match({ + onSome: Fiber.interrupt, + onNone: () => Effect.succeed(undefined), + })) + } + + fetch(key: K): Effect.Effect> { + const self = this + return Effect.gen(function*() { + yield* self.interrupt + yield* SubscriptionRef.set(self.latestKeyRef, Option.some(key)) + const previous = yield* SubscriptionRef.get(self.latestFinalResultRef) + const sub = yield* self.startCached(key, Option.isSome(previous) + ? Result.willFetch(previous.value) as Result.Final + : Result.initial()) + return yield* self.watch(key, sub) + }).pipe(Effect.provide(this.context)) + } + + fetchSubscribable(key: K): Effect.Effect>> { + const self = this + return Effect.gen(function*() { + yield* self.interrupt + yield* SubscriptionRef.set(self.latestKeyRef, Option.some(key)) + const previous = yield* SubscriptionRef.get(self.latestFinalResultRef) + const sub = yield* self.startCached(key, Option.isSome(previous) + ? Result.willFetch(previous.value) as Result.Final + : Result.initial()) + yield* Effect.forkScoped(self.watch(key, sub)) + return sub + }).pipe(Effect.provide(this.context)) + } + + get refresh(): Effect.Effect, Cause.NoSuchElementError> { + const self = this + return Effect.gen(function*() { + yield* self.interrupt + const key = yield* Effect.fromOption(yield* SubscriptionRef.get(self.latestKeyRef)) + const previous = yield* SubscriptionRef.get(self.latestFinalResultRef) + const sub = yield* self.startCached(key, Option.isSome(previous) + ? Result.willRefresh(previous.value) as Result.Final + : Result.initial()) + return yield* self.watch(key, sub) + }).pipe(Effect.provide(this.context)) + } + + get refreshSubscribable(): Effect.Effect>, Cause.NoSuchElementError> { + const self = this + return Effect.gen(function*() { + yield* self.interrupt + const key = yield* Effect.fromOption(yield* SubscriptionRef.get(self.latestKeyRef)) + const previous = yield* SubscriptionRef.get(self.latestFinalResultRef) + const sub = yield* self.startCached(key, Option.isSome(previous) + ? Result.willRefresh(previous.value) as Result.Final + : Result.initial()) + yield* Effect.forkScoped(self.watch(key, sub)) + return sub + }).pipe(Effect.provide(this.context)) + } + + startCached( + key: K, + initial: Result.Initial | Result.Final, + ): Effect.Effect>, never, Scope.Scope | QueryClient.QueryClient | R> { + return Effect.flatMap(this.getCacheEntry(key), Option.match({ + onSome: entry => Effect.flatMap( + QueryClient.isQueryClientCacheEntryStale(entry), + isStale => isStale + ? this.start(key, Result.willRefresh(entry.result) as Result.Final) + : Effect.succeed(Subscribable.make({ + get: Effect.succeed(entry.result as Result.Result), + changes: Stream.make(entry.result as Result.Result), + })), + ), + onNone: () => this.start(key, initial), + })) + } + + start( + key: K, + initial: Result.Initial | Result.Final, + ): Effect.Effect>, never, Scope.Scope | R> { + const self = this + return Effect.gen(function*() { + const [sub, fiber] = yield* Result.unsafeForkEffect( + Effect.onExit(self.f(key), () => Effect.flatMap( + Effect.all([Effect.fiberId, SubscriptionRef.get(self.fiberRef)]), + ([currentFiberId, current]) => Option.match(current, { + onSome: value => Equal.equals(currentFiberId, value.id) + ? SubscriptionRef.set(self.fiberRef, Option.none()) + : Effect.succeed(undefined), + onNone: () => Effect.succeed(undefined), + }), + )), + { initial, initialProgress: self.initialProgress }, + ) + yield* SubscriptionRef.set(self.fiberRef, Option.some(fiber)) + return sub + }) + } + + watch( + key: K, + sub: Subscribable.Subscribable>, + ): Effect.Effect, never, QueryClient.QueryClient> { + return Effect.flatMap(sub.get, initial => Stream.runFoldEffect( + Stream.takeUntil(sub.changes, result => Result.isFinal(result) && !Result.hasFlag(result)), + () => initial, + (_, result) => Effect.as(SubscriptionRef.set(this.resultRef, result), result), + ) as Effect.Effect>).pipe( + Effect.tap(result => SubscriptionRef.set(this.latestFinalResultRef, Option.some(result))), + Effect.tap(result => Result.isSuccess(result) + ? Effect.asVoid(this.setCacheEntry(key, result)) + : Effect.succeed(undefined)), + ) + } + + makeCacheKey(key: K): QueryClient.QueryClientCacheKey { + return new QueryClient.QueryClientCacheKey(key, this.f as (key: Query.AnyKey) => Effect.Effect) + } + + getCacheEntry(key: K): Effect.Effect, never, QueryClient.QueryClient> { + return Effect.flatMap(QueryClient.QueryClient, client => client.getCacheEntry(this.makeCacheKey(key))) + } + + setCacheEntry(key: K, result: Result.Success): Effect.Effect { + return Effect.flatMap(QueryClient.QueryClient, client => client.setCacheEntry(this.makeCacheKey(key), result, this.staleTime)) + } + + get invalidateCache(): Effect.Effect { + return Effect.flatMap( + QueryClient.QueryClient, + client => client.invalidateCacheEntries(this.f as (key: Query.AnyKey) => Effect.Effect), + ).pipe(Effect.provide(this.context)) + } + + invalidateCacheEntry(key: K): Effect.Effect { + return Effect.flatMap( + QueryClient.QueryClient, + client => client.invalidateCacheEntry(this.makeCacheKey(key)), + ).pipe(Effect.provide(this.context)) + } +} + +export const isQuery = (u: unknown): u is Query => Predicate.hasProperty(u, QueryTypeId) + +export declare namespace make { + export interface Options { + readonly key: Stream.Stream + readonly f: (key: NoInfer) => Effect.Effect>> + readonly initialProgress?: P + readonly staleTime?: Duration.Input + readonly refreshOnWindowFocus?: boolean + } +} + +export const make = Effect.fnUntraced(function* ( + options: make.Options, +): Effect.fn.Return< + Query, P>, + never, + Scope.Scope | QueryClient.QueryClient | KR | Result.forkEffect.OutputContext +> { + const client = yield* QueryClient.QueryClient + return new QueryImpl, P>( + yield* Effect.context>(), + options.key, + options.f as any, + options.initialProgress as P, + options.staleTime ?? client.defaultStaleTime, + options.refreshOnWindowFocus ?? client.defaultRefreshOnWindowFocus, + yield* SubscriptionRef.make(Option.none()), + yield* SubscriptionRef.make(Option.none>()), + yield* SubscriptionRef.make(Result.initial()), + yield* SubscriptionRef.make(Option.none>()), + yield* Semaphore.make(1), + ) +}) + +export const service = ( + options: make.Options, +): Effect.Effect< + Query, P>, + never, + Scope.Scope | QueryClient.QueryClient | KR | Result.forkEffect.OutputContext +> => Effect.tap(make(options), query => Effect.asVoid(Effect.forkScoped(query.run))) + +const fromSubscriptionRef = (ref: SubscriptionRef.SubscriptionRef): Subscribable.Subscribable => Subscribable.make({ + get: SubscriptionRef.get(ref), + changes: SubscriptionRef.changes(ref), +}) diff --git a/packages/effect-fc-next/src/QueryClient.ts b/packages/effect-fc-next/src/QueryClient.ts new file mode 100644 index 0000000..b811138 --- /dev/null +++ b/packages/effect-fc-next/src/QueryClient.ts @@ -0,0 +1,191 @@ +import { + Context, + DateTime, + Duration, + Effect, + Equal, + Equivalence, + Hash, + HashMap, + Layer, + Option, + Pipeable, + Predicate, + Schedule, + Scope, + Semaphore, + SubscriptionRef, +} from "effect" +import { Subscribable } from "effect-lens" +import type * as Query from "./Query.js" +import type * as Result from "./Result.js" + + +export const QueryClientServiceTypeId: unique symbol = Symbol.for("@effect-fc/QueryClient/QueryClientService") +export type QueryClientServiceTypeId = typeof QueryClientServiceTypeId + +export interface QueryClientService extends Pipeable.Pipeable { + readonly [QueryClientServiceTypeId]: QueryClientServiceTypeId + readonly cache: Subscribable.Subscribable> + readonly cacheGcTime: Duration.Input + readonly defaultStaleTime: Duration.Input + readonly defaultRefreshOnWindowFocus: boolean + readonly run: Effect.Effect + getCacheEntry(key: QueryClientCacheKey): Effect.Effect> + setCacheEntry(key: QueryClientCacheKey, result: Result.Success, staleTime: Duration.Input): Effect.Effect + invalidateCacheEntries(f: (key: Query.Query.AnyKey) => Effect.Effect): Effect.Effect + invalidateCacheEntry(key: QueryClientCacheKey): Effect.Effect +} + +export class QueryClient extends Context.Service()( + "@effect-fc/QueryClient/QueryClient", +) { + static get Default(): Layer.Layer { + return Layer.effect(QueryClient)(service()) + } +} + +export class QueryClientServiceImpl extends Pipeable.Class implements QueryClientService { + readonly [QueryClientServiceTypeId]: QueryClientServiceTypeId = QueryClientServiceTypeId + readonly cache: Subscribable.Subscribable> + + constructor( + readonly cacheRef: SubscriptionRef.SubscriptionRef>, + readonly cacheGcTime: Duration.Input, + readonly defaultStaleTime: Duration.Input, + readonly defaultRefreshOnWindowFocus: boolean, + readonly runSemaphore: Semaphore.Semaphore, + ) { + super() + this.cache = Subscribable.make({ + get: SubscriptionRef.get(cacheRef), + changes: SubscriptionRef.changes(cacheRef), + }) + } + + get run(): Effect.Effect { + return this.runSemaphore.withPermits(1)(Effect.repeat( + Effect.flatMap(DateTime.now, now => SubscriptionRef.update(this.cacheRef, HashMap.filter(entry => + Duration.isLessThan( + DateTime.distance(entry.lastAccessedAt, now), + Duration.sum(Duration.fromInputUnsafe(entry.staleTime), Duration.fromInputUnsafe(this.cacheGcTime)), + ) + ))), + Schedule.spaced("30 seconds"), + )) + } + + getCacheEntry(key: QueryClientCacheKey): Effect.Effect> { + const self = this + return Effect.gen(function*() { + const entry = HashMap.get(yield* SubscriptionRef.get(self.cacheRef), key) + if (Option.isNone(entry)) return Option.none() + const now = yield* DateTime.now + const accessed = new QueryClientCacheEntry( + entry.value.result, + entry.value.staleTime, + entry.value.createdAt, + now, + ) + yield* SubscriptionRef.update(self.cacheRef, HashMap.set(key, accessed)) + return Option.some(accessed) + }) + } + + setCacheEntry( + key: QueryClientCacheKey, + result: Result.Success, + staleTime: Duration.Input, + ): Effect.Effect { + return Effect.flatMap(DateTime.now, now => { + const entry = new QueryClientCacheEntry(result, staleTime, now, now) + return Effect.as(SubscriptionRef.update(this.cacheRef, HashMap.set(key, entry)), entry) + }) + } + + invalidateCacheEntries(f: (key: Query.Query.AnyKey) => Effect.Effect): Effect.Effect { + return SubscriptionRef.update(this.cacheRef, HashMap.filter((_, key) => !Equivalence.strictEqual()(key.f, f))) + } + + invalidateCacheEntry(key: QueryClientCacheKey): Effect.Effect { + return SubscriptionRef.update(this.cacheRef, HashMap.remove(key)) + } +} + +export const isQueryClientService = (u: unknown): u is QueryClientService => Predicate.hasProperty(u, QueryClientServiceTypeId) + +export declare namespace make { + export interface Options { + readonly cacheGcTime?: Duration.Input + readonly defaultStaleTime?: Duration.Input + readonly defaultRefreshOnWindowFocus?: boolean + } +} + +export const make = Effect.fnUntraced(function* (options: make.Options = {}): Effect.fn.Return { + return new QueryClientServiceImpl( + yield* SubscriptionRef.make(HashMap.empty()), + options.cacheGcTime ?? "5 minutes", + options.defaultStaleTime ?? "0 minutes", + options.defaultRefreshOnWindowFocus ?? true, + yield* Semaphore.make(1), + ) +}) + +export declare namespace service { + export interface Options extends make.Options {} +} + +export const service = (options?: service.Options): Effect.Effect => Effect.tap( + make(options), + client => Effect.asVoid(Effect.forkScoped(client.run)), +) + +export const QueryClientCacheKeyTypeId: unique symbol = Symbol.for("@effect-fc/QueryClient/QueryClientCacheKey") +export type QueryClientCacheKeyTypeId = typeof QueryClientCacheKeyTypeId + +export class QueryClientCacheKey extends Pipeable.Class implements Equal.Equal { + readonly [QueryClientCacheKeyTypeId]: QueryClientCacheKeyTypeId = QueryClientCacheKeyTypeId + + constructor( + readonly key: Query.Query.AnyKey, + readonly f: (key: Query.Query.AnyKey) => Effect.Effect, + ) { + super() + } + + [Equal.symbol](that: Equal.Equal): boolean { + return isQueryClientCacheKey(that) + && Equivalence.Array(Equal.asEquivalence())(this.key, that.key) + && Equivalence.strictEqual()(this.f, that.f) + } + + [Hash.symbol](): number { + return Hash.combine(Hash.hash(this.f))(Hash.array(this.key)) + } +} + +export const isQueryClientCacheKey = (u: unknown): u is QueryClientCacheKey => Predicate.hasProperty(u, QueryClientCacheKeyTypeId) + +export const QueryClientCacheEntryTypeId: unique symbol = Symbol.for("@effect-fc/QueryClient/QueryClientCacheEntry") +export type QueryClientCacheEntryTypeId = typeof QueryClientCacheEntryTypeId + +export class QueryClientCacheEntry extends Pipeable.Class { + readonly [QueryClientCacheEntryTypeId]: QueryClientCacheEntryTypeId = QueryClientCacheEntryTypeId + + constructor( + readonly result: Result.Success, + readonly staleTime: Duration.Input, + readonly createdAt: DateTime.DateTime, + readonly lastAccessedAt: DateTime.DateTime, + ) { + super() + } +} + +export const isQueryClientCacheEntry = (u: unknown): u is QueryClientCacheEntry => Predicate.hasProperty(u, QueryClientCacheEntryTypeId) + +export const isQueryClientCacheEntryStale = (self: QueryClientCacheEntry): Effect.Effect => Effect.map( + DateTime.now, + now => Duration.isGreaterThanOrEqualTo(DateTime.distance(self.createdAt, now), Duration.fromInputUnsafe(self.staleTime)), +) diff --git a/packages/effect-fc-next/src/ReactRuntime.ts b/packages/effect-fc-next/src/ReactRuntime.ts new file mode 100644 index 0000000..92f9108 --- /dev/null +++ b/packages/effect-fc-next/src/ReactRuntime.ts @@ -0,0 +1,71 @@ +/** biome-ignore-all lint/complexity/useArrowFunction: React component names are intentional */ +import { Context, Layer, ManagedRuntime, Predicate } from "effect" +import * as React from "react" +import * as ErrorObserver from "./ErrorObserver.js" +import * as QueryClient from "./QueryClient.js" + + +export const TypeId: unique symbol = Symbol.for("@effect-fc/ReactRuntime/ReactRuntime") +export type TypeId = typeof TypeId + +export interface ReactRuntime { + new(_: never): Record + readonly [TypeId]: TypeId + readonly runtime: ManagedRuntime.ManagedRuntime + readonly context: React.Context> +} + +const ReactRuntimeProto = Object.freeze({ [TypeId]: TypeId } as const) + +export const Prelude: Layer.Layer = Layer.merge( + ErrorObserver.layer, + QueryClient.QueryClient.Default, +) + +export const isReactRuntime = (u: unknown): u is ReactRuntime => Predicate.hasProperty(u, TypeId) + +export const make = ( + layer: Layer.Layer, + memoMap?: Layer.MemoMap, +): ReactRuntime => Object.setPrototypeOf( + Object.assign(function() {}, { + runtime: ManagedRuntime.make(Layer.merge(layer, Prelude), { memoMap }), + // biome-ignore lint/style/noNonNullAssertion: initialized by Provider before consumers render + context: React.createContext>(null!), + }), + ReactRuntimeProto, +) + +export namespace Provider { + export interface Props extends React.SuspenseProps { + readonly runtime: ReactRuntime + readonly children?: React.ReactNode + } +} + +export const Provider = ( + { runtime, children, ...suspenseProps }: Provider.Props, +): React.ReactNode => { + const promise = React.useMemo(() => runtime.runtime.context(), [runtime]) + + return React.createElement( + React.Suspense, + suspenseProps, + React.createElement(ProviderInner, { runtime, promise, children }), + ) +} + +const ProviderInner = ( + { runtime, promise, children }: { + readonly runtime: ReactRuntime + readonly promise: Promise> + readonly children?: React.ReactNode + }, +): React.ReactNode => { + const context = React.use(promise) + React.useEffect(() => () => { + void runtime.runtime.dispose() + }, [runtime]) + + return React.createElement(runtime.context, { value: context }, children) +} diff --git a/packages/effect-fc-next/src/Result.ts b/packages/effect-fc-next/src/Result.ts new file mode 100644 index 0000000..672ae18 --- /dev/null +++ b/packages/effect-fc-next/src/Result.ts @@ -0,0 +1,249 @@ +import { Cause, Context, Data, Effect, Equal, Exit, type Fiber, Hash, Layer, Match, Pipeable, Predicate, pipe, type Scope, SubscriptionRef } from "effect" +import { Lens, Subscribable } from "effect-lens" + + +export const ResultTypeId: unique symbol = Symbol.for("@effect-fc/Result/Result") +export type ResultTypeId = typeof ResultTypeId + +export type Result = ( + | Initial + | Running

+ | Final +) + +// biome-ignore lint/complexity/noBannedTypes: "{}" is relevant here +export type Final = (Success | Failure) & ({} | Flags

) +export type Flags

= WillFetch | WillRefresh | Refreshing

+ +export declare namespace Result { + export type Success> = [R] extends [Result] ? A : never + export type Failure> = [R] extends [Result] ? E : never + export type Progress> = [R] extends [Result] ? P : never +} + +export declare namespace Flags { + export type Keys = keyof WillFetch & WillRefresh & Refreshing +} + +export interface Initial extends ResultPrototype { + readonly _tag: "Initial" +} + +export interface Running

extends ResultPrototype { + readonly _tag: "Running" + readonly progress: P +} + +export interface Success extends ResultPrototype { + readonly _tag: "Success" + readonly value: A +} + +export interface Failure extends ResultPrototype { + readonly _tag: "Failure" + readonly cause: Cause.Cause +} + +export interface WillFetch { + readonly _flag: "WillFetch" +} + +export interface WillRefresh { + readonly _flag: "WillRefresh" +} + +export interface Refreshing

{ + readonly _flag: "Refreshing" + readonly progress: P +} + + +export interface ResultPrototype extends Pipeable.Pipeable, Equal.Equal { + readonly [ResultTypeId]: ResultTypeId +} + +export const ResultPrototype: ResultPrototype = Object.freeze({ + ...Pipeable.Prototype, + [ResultTypeId]: ResultTypeId, + + [Equal.symbol](this: Result, that: Result): boolean { + if (this._tag !== that._tag || (this as Flags)._flag !== (that as Flags)._flag) + return false + if (hasRefreshingFlag(this) && !Equal.equals(this.progress, (that as Refreshing).progress)) + return false + return Match.value(this).pipe( + Match.tag("Initial", () => true), + Match.tag("Running", self => Equal.equals(self.progress, (that as Running).progress)), + Match.tag("Success", self => Equal.equals(self.value, (that as Success).value)), + Match.tag("Failure", self => Equal.equals(self.cause, (that as Failure).cause)), + Match.exhaustive, + ) + }, + + [Hash.symbol](this: Result): number { + return pipe(Hash.string(this._tag), + tagHash => Match.value(this).pipe( + Match.tag("Initial", () => tagHash), + Match.tag("Running", self => Hash.combine(Hash.hash(self.progress))(tagHash)), + Match.tag("Success", self => Hash.combine(Hash.hash(self.value))(tagHash)), + Match.tag("Failure", self => Hash.combine(Hash.hash(self.cause))(tagHash)), + Match.exhaustive, + ), + Hash.combine(Hash.hash((this as Flags)._flag)), + hash => hasRefreshingFlag(this) + ? Hash.combine(Hash.hash(this.progress))(hash) + : hash, + ) + }, +} as const) + + +export const isResult = (u: unknown): u is Result => Predicate.hasProperty(u, ResultTypeId) +export const isFinal = (u: unknown): u is Final => isResult(u) && (isSuccess(u) || isFailure(u)) +export const isInitial = (u: unknown): u is Initial => isResult(u) && u._tag === "Initial" +export const isRunning = (u: unknown): u is Running => isResult(u) && u._tag === "Running" +export const isSuccess = (u: unknown): u is Success => isResult(u) && u._tag === "Success" +export const isFailure = (u: unknown): u is Failure => isResult(u) && u._tag === "Failure" +export const hasFlag = (u: unknown): u is Flags => isResult(u) && Predicate.hasProperty(u, "_flag") +export const hasWillFetchFlag = (u: unknown): u is WillFetch => isResult(u) && Predicate.hasProperty(u, "_flag") && u._flag === "WillFetch" +export const hasWillRefreshFlag = (u: unknown): u is WillRefresh => isResult(u) && Predicate.hasProperty(u, "_flag") && u._flag === "WillRefresh" +export const hasRefreshingFlag = (u: unknown): u is Refreshing => isResult(u) && Predicate.hasProperty(u, "_flag") && u._flag === "Refreshing" + +export const initial: { + (): Initial + (): Result +} = (): Initial => Object.setPrototypeOf({ _tag: "Initial" }, ResultPrototype) +export const running =

(progress?: P): Running

=> Object.setPrototypeOf({ _tag: "Running", progress }, ResultPrototype) +export const succeed = (value: A): Success => Object.setPrototypeOf({ _tag: "Success", value }, ResultPrototype) +export const fail = (cause: Cause.Cause ): Failure => Object.setPrototypeOf({ _tag: "Failure", cause }, ResultPrototype) + +export const willFetch = >( + result: R +): Omit & WillFetch => Object.setPrototypeOf( + Object.assign({}, result, { _flag: "WillFetch" }), + Object.getPrototypeOf(result), +) + +export const willRefresh = >( + result: R +): Omit & WillRefresh => Object.setPrototypeOf( + Object.assign({}, result, { _flag: "WillRefresh" }), + Object.getPrototypeOf(result), +) + +export const refreshing = , P = never>( + result: R, + progress?: P, +): Omit & Refreshing

=> Object.setPrototypeOf( + Object.assign({}, result, { _flag: "Refreshing", progress }), + Object.getPrototypeOf(result), +) + +export const fromExit: { + (exit: Exit.Success): Success + (exit: Exit.Failure): Failure + (exit: Exit.Exit): Success | Failure +} = exit => (exit._tag === "Success" ? succeed(exit.value) : fail(exit.cause)) as any + +export const toExit: { + (self: Success): Exit.Success + (self: Failure): Exit.Failure + (self: Final): Exit.Exit + (self: Result): Exit.Exit +} = (self: Result): any => { + switch (self._tag) { + case "Success": + return Exit.succeed(self.value) + case "Failure": + return Exit.failCause(self.cause) + default: + return Exit.fail(new Cause.NoSuchElementError()) + } +} + + +export interface Progress

{ + readonly progress: Lens.Lens +} +export const Progress =

() => Context.Service>("@effect-fc/Result/Progress") + +export class PreviousResultNotRunningNorRefreshing extends Data.TaggedError("@effect-fc/Result/PreviousResultNotRunningNorRefreshing")<{ + readonly previous: Result +}> {} + +export const makeProgressLayer = ( + state: Lens.Lens, never, never, never, never> +): Layer.Layer | Progress, never, never> => Layer.succeed( + Progress

() as Context.Service | Progress, Progress

| Progress>, + { + progress: state.pipe( + Lens.mapEffect( + a => (isRunning(a) || hasRefreshingFlag(a)) + ? Effect.succeed(a) + : Effect.fail(new PreviousResultNotRunningNorRefreshing({ previous: a })), + (_, b) => Effect.succeed(b), + ), + Lens.map( + a => a.progress, + (a, b) => isRunning(a) + ? running(b) + : refreshing(a, b) as Final & Refreshing

, + ), + ) + }, +) + + +export namespace unsafeForkEffect { + export type OutputContext = Exclude | Progress> + + export interface Options { + readonly initial?: Initial | Final + readonly initialProgress?: P + } +} + +export const unsafeForkEffect = Effect.fnUntraced(function* ( + effect: Effect.Effect, + options?: unsafeForkEffect.Options, NoInfer, P>, +): Effect.fn.Return< + readonly [result: Subscribable.Subscribable, never, never>, fiber: Fiber.Fiber], + never, + Scope.Scope | unsafeForkEffect.OutputContext +> { + const state = Lens.fromSubscriptionRef(yield* SubscriptionRef.make>( + options?.initial ?? initial(), + )) + + const fiber = yield* Effect.gen(function*() { + yield* Lens.set( + state, + (isFinal(options?.initial) && hasWillRefreshFlag(options?.initial)) + ? refreshing(options.initial, options?.initialProgress) as Result + : running(options?.initialProgress), + ) + return yield* Effect.onExit(effect, exit => Lens.set(state, fromExit(exit))) + }).pipe( + Effect.forkScoped, + Effect.provide(makeProgressLayer(state)), + ) + + return [state, fiber] as const +}) + +export namespace forkEffect { + export type InputContext = R extends Progress ? [X] extends [P] ? R : never : R + export type OutputContext = unsafeForkEffect.OutputContext + export interface Options extends unsafeForkEffect.Options {} +} + +export const forkEffect: { + ( + effect: Effect.Effect>>, + options?: forkEffect.Options, NoInfer, P>, + ): Effect.Effect< + readonly [result: Subscribable.Subscribable, never, never>, fiber: Fiber.Fiber], + never, + Scope.Scope | forkEffect.OutputContext + > +} = unsafeForkEffect diff --git a/packages/effect-fc-next/src/SetStateAction.ts b/packages/effect-fc-next/src/SetStateAction.ts new file mode 100644 index 0000000..ee5db34 --- /dev/null +++ b/packages/effect-fc-next/src/SetStateAction.ts @@ -0,0 +1,12 @@ +import { Function } from "effect" +import type * as React from "react" + + +export const value: { + (self: React.SetStateAction, prevState: S): S + (prevState: S): (self: React.SetStateAction) => S +} = Function.dual(2, (self: React.SetStateAction, prevState: S): S => + typeof self === "function" + ? (self as (prevState: S) => S)(prevState) + : self +) diff --git a/packages/effect-fc-next/src/Stream.ts b/packages/effect-fc-next/src/Stream.ts new file mode 100644 index 0000000..67bf82e --- /dev/null +++ b/packages/effect-fc-next/src/Stream.ts @@ -0,0 +1,33 @@ +import { Effect, Equivalence, Option, Stream } from "effect" +import * as React from "react" +import * as Component from "./Component.js" + + +export const use: { + ( + stream: Stream.Stream + ): Effect.Effect, never, R> + , E, R>( + stream: Stream.Stream, + initialValue: A, + ): Effect.Effect, never, R> +} = Effect.fnUntraced(function* , E, R>( + stream: Stream.Stream, + initialValue?: A, +) { + const [reactStateValue, setReactStateValue] = React.useState(() => initialValue + ? Option.some(initialValue) + : Option.none() + ) + + yield* Component.useReactEffect(() => Effect.forkScoped( + Stream.runForEach( + Stream.changesWith(stream, Equivalence.strictEqual()), + v => Effect.sync(() => setReactStateValue(Option.some(v))), + ) + ), [stream]) + + return reactStateValue as Option.Some +}) + +export * from "effect/Stream" diff --git a/packages/effect-fc-next/src/SubmittableForm.ts b/packages/effect-fc-next/src/SubmittableForm.ts new file mode 100644 index 0000000..79dfe46 --- /dev/null +++ b/packages/effect-fc-next/src/SubmittableForm.ts @@ -0,0 +1,212 @@ +import { + Array, + Cause, + type Context, + Effect, + Fiber, + Option, + Pipeable, + Predicate, + Schema, + SchemaIssue, + SchemaParser, + type Scope, + Semaphore, + SubscriptionRef, +} from "effect" +import * as Form from "./Form.js" +import * as Lens from "./Lens.js" +import * as Mutation from "./Mutation.js" +import * as Result from "./Result.js" +import * as Subscribable from "./Subscribable.js" + + +type FormSchema = Schema.Top & { + readonly Type: A + readonly Encoded: I + readonly DecodingServices: R +} + +export const SubmittableFormTypeId: unique symbol = Symbol.for("@effect-fc/Form/SubmittableForm") +export type SubmittableFormTypeId = typeof SubmittableFormTypeId + +export interface SubmittableForm +extends Form.Form { + readonly [SubmittableFormTypeId]: SubmittableFormTypeId + readonly schema: FormSchema + readonly context: Context.Context + readonly mutation: Mutation.Mutation< + readonly [value: A, form: SubmittableForm], + MA, ME, MR, MP + > + readonly validationFiber: Subscribable.Subscribable>, never, never> + readonly run: Effect.Effect + readonly submit: Effect.Effect>, Cause.NoSuchElementError> +} + +export class SubmittableFormImpl +extends Pipeable.Class implements SubmittableForm { + readonly [Form.FormTypeId]: Form.FormTypeId = Form.FormTypeId + readonly [SubmittableFormTypeId]: SubmittableFormTypeId = SubmittableFormTypeId + readonly path = [] as const + readonly encodedValue: Lens.Lens + readonly isValidating: Subscribable.Subscribable + readonly canCommit: Subscribable.Subscribable + readonly isCommitting: Subscribable.Subscribable + + constructor( + readonly schema: FormSchema, + readonly context: Context.Context, + readonly mutation: Mutation.Mutation< + readonly [value: A, form: SubmittableForm], + MA, ME, MR, MP + >, + readonly value: Lens.Lens, never, never, never, never>, + readonly internalEncodedValue: Lens.Lens, + readonly issues: Lens.Lens, + readonly validationFiber: Lens.Lens>, never, never, never, never>, + readonly runSemaphore: Semaphore.Semaphore, + ) { + super() + this.encodedValue = Lens.make({ + get: Lens.get(internalEncodedValue), + changes: internalEncodedValue.changes, + commit: encoded => Effect.andThen( + Lens.set(internalEncodedValue, encoded), + this.synchronizeEncodedValue(encoded), + ), + lock: Lens.asLensImpl(internalEncodedValue).lock, + }) + this.isValidating = Subscribable.map(validationFiber, Option.isSome) + const commitState = Subscribable.zipLatestAll( + value as any, + issues as any, + validationFiber as any, + mutation.result as any, + ) as unknown as Subscribable.Subscribable, + readonly Form.FormIssue[], + Option.Option>, + Result.Result, + ]> + this.canCommit = Subscribable.map( + commitState, + ([current, currentIssues, fiber, result]: readonly [Option.Option, readonly Form.FormIssue[], Option.Option>, Result.Result]) => Option.isSome(current) + && currentIssues.length === 0 + && Option.isNone(fiber) + && !(Result.isRunning(result) || Result.hasRefreshingFlag(result)), + ) + this.isCommitting = Subscribable.map( + mutation.result, + result => Result.isRunning(result) || Result.hasRefreshingFlag(result), + ) + } + + synchronizeEncodedValue(encodedValue: I): Effect.Effect { + const self = this + return Effect.gen(function*() { + const current = yield* Lens.get(self.validationFiber) + if (Option.isSome(current)) yield* Fiber.interrupt(current.value) + const fiber = yield* Effect.forkScoped( + Effect.ensuring( + SchemaParser.decodeEffect(self.schema)(encodedValue), + Lens.set(self.validationFiber, Option.none()), + ), + ) + yield* Lens.set(self.validationFiber, Option.some(fiber)) + const decoded = yield* Fiber.join(fiber).pipe( + Effect.tap(value => Effect.andThen( + Lens.set(self.issues, Array.empty()), + Lens.set(self.value, Option.some(value)), + )), + Effect.catchIf(SchemaIssue.isIssue, issue => Lens.set(self.issues, formatIssue(issue))), + ) + void decoded + }).pipe(Effect.provide(this.context)) as Effect.Effect + } + + get run(): Effect.Effect { + return Effect.flatMap( + Lens.get(this.encodedValue), + SchemaParser.decodeEffect(this.schema), + ).pipe( + Effect.option, + Effect.flatMap(value => Lens.set(this.value, value)), + Effect.provide(this.context), + this.runSemaphore.withPermits(1), + ) + } + + get submit(): Effect.Effect>, Cause.NoSuchElementError> { + return Effect.flatMap(Lens.get(this.value), value => Effect.flatMap(Effect.fromOption(value), decoded => this.submitValue(decoded))) + } + + submitValue(value: A): Effect.Effect>> { + return Effect.flatMap(this.canCommit.get, canCommit => { + if (!canCommit) return Effect.succeed(Option.none()) + return Effect.map( + Effect.tap(this.mutation.mutate([value, this as any]), result => { + if (!Result.isFailure(result)) return Effect.succeed(undefined) + const issue = Cause.findErrorOption(result.cause) + return Option.isSome(issue) && SchemaIssue.isIssue(issue.value) + ? Lens.set(this.issues, formatIssue(issue.value)) + : Effect.succeed(undefined) + }), + Option.some, + ) + }) + } +} + +const formatIssue = (issue: SchemaIssue.Issue): readonly Form.FormIssue[] => { + const formatted = SchemaIssue.makeFormatterStandardSchemaV1()(issue) + return formatted.issues.map(item => ({ + path: (item.path ?? []) as readonly PropertyKey[], + message: item.message, + })) +} + +export const isSubmittableForm = (u: unknown): u is SubmittableForm => Predicate.hasProperty(u, SubmittableFormTypeId) + +export declare namespace make { + export interface Options + extends Mutation.make.Options< + readonly [value: NoInfer, form: SubmittableForm, NoInfer, NoInfer, unknown, unknown, unknown>], + MA, ME, MR, MP + > { + readonly schema: FormSchema + readonly initialEncodedValue: NoInfer + } +} + +export const make = Effect.fnUntraced(function* ( + options: make.Options, +): Effect.fn.Return< + SubmittableForm, MP>, + never, + Scope.Scope | R | Result.forkEffect.OutputContext +> { + return new SubmittableFormImpl( + options.schema, + yield* Effect.context(), + yield* Mutation.make(options), + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none())), + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(options.initialEncodedValue)), + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Array.empty())), + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none>())), + yield* Semaphore.make(1), + ) +}) + +export declare namespace service { + export interface Options + extends make.Options {} +} + +export const service = ( + options: service.Options, +): Effect.Effect< + SubmittableForm, MP>, + never, + Scope.Scope | R | Result.forkEffect.OutputContext +> => Effect.tap(make(options), form => Effect.asVoid(Effect.forkScoped(form.run))) diff --git a/packages/effect-fc-next/src/Subscribable.ts b/packages/effect-fc-next/src/Subscribable.ts new file mode 100644 index 0000000..e8fafe6 --- /dev/null +++ b/packages/effect-fc-next/src/Subscribable.ts @@ -0,0 +1,53 @@ +import { Effect, Equivalence, Stream } from "effect" +import { Subscribable } from "effect-lens" +import * as React from "react" +import * as Component from "./Component.js" + + +export * from "effect-lens/Subscribable" + +export const zipLatestAll = []>( + ...elements: T +): Subscribable.Subscribable< + [T[number]] extends [never] + ? never + : { [K in keyof T]: T[K] extends Subscribable.Subscribable ? A : never }, + [T[number]] extends [never] ? never : T[number] extends Subscribable.Subscribable ? E : never, + [T[number]] extends [never] ? never : T[number] extends Subscribable.Subscribable ? R : never +> => Subscribable.make({ + get: Effect.all(elements.map(v => v.get)), + changes: Stream.zipLatestAll(...elements.map(v => v.changes)), +}) as any + +export declare namespace useAll { + export type Success[]> = [T[number]] extends [never] + ? never + : { [K in keyof T]: T[K] extends Subscribable.Subscribable ? A : never } + + export interface Options { + readonly equivalence?: Equivalence.Equivalence + } +} + +export const useAll = Effect.fnUntraced(function* []>( + elements: T, + options?: useAll.Options>>, +): Effect.fn.Return< + useAll.Success, + [T[number]] extends [never] ? never : T[number] extends Subscribable.Subscribable ? E : never, + [T[number]] extends [never] ? never : T[number] extends Subscribable.Subscribable ? R : never +> { + const [reactStateValue, setReactStateValue] = React.useState( + yield* Component.useOnMount(() => Effect.all(elements.map(v => v.get))) + ) + + yield* Component.useReactEffect(() => Stream.zipLatestAll(...elements.map(ref => ref.changes)).pipe( + Stream.changesWith((options?.equivalence as Equivalence.Equivalence | undefined) ?? Equivalence.Array(Equivalence.strictEqual())), + Stream.runForEach(v => + Effect.sync(() => setReactStateValue(v)) + ), + Effect.forkScoped, + ), elements) + + return reactStateValue as any +}) diff --git a/packages/effect-fc-next/src/SynchronizedForm.ts b/packages/effect-fc-next/src/SynchronizedForm.ts new file mode 100644 index 0000000..330628a --- /dev/null +++ b/packages/effect-fc-next/src/SynchronizedForm.ts @@ -0,0 +1,204 @@ +import { + Array, + type Context, + Effect, + Equal, + Fiber, + Option, + Pipeable, + Predicate, + Schema, + SchemaIssue, + SchemaParser, + type Scope, + Semaphore, + Stream, + SubscriptionRef, +} from "effect" +import * as Form from "./Form.js" +import * as Lens from "./Lens.js" +import * as Subscribable from "./Subscribable.js" + + +type FormSchema = Schema.Top & { + readonly Type: A + readonly Encoded: I + readonly DecodingServices: R + readonly EncodingServices: R +} + +export const SynchronizedFormTypeId: unique symbol = Symbol.for("@effect-fc/Form/SynchronizedForm") +export type SynchronizedFormTypeId = typeof SynchronizedFormTypeId + +export interface SynchronizedForm< + in out A, + in out I = A, + in out R = never, + in out TER = never, + in out TEW = never, + in out TRR = never, + in out TRW = never, +> extends Form.Form { + readonly [SynchronizedFormTypeId]: SynchronizedFormTypeId + readonly schema: FormSchema + readonly context: Context.Context + readonly target: Lens.Lens + readonly validationFiber: Subscribable.Subscribable>, never, never> + readonly run: Effect.Effect +} + +export class SynchronizedFormImpl< + in out A, + in out I = A, + in out R = never, + in out TER = never, + in out TEW = never, + in out TRR = never, + in out TRW = never, +> extends Pipeable.Class implements SynchronizedForm { + readonly [Form.FormTypeId]: Form.FormTypeId = Form.FormTypeId + readonly [SynchronizedFormTypeId]: SynchronizedFormTypeId = SynchronizedFormTypeId + readonly path = [] as const + readonly value: Subscribable.Subscribable, TER, never> + readonly encodedValue: Lens.Lens + readonly isValidating: Subscribable.Subscribable + readonly canCommit: Subscribable.Subscribable + + constructor( + readonly schema: FormSchema, + readonly context: Context.Context, + readonly target: Lens.Lens, + readonly internalEncodedValue: Lens.Lens, + readonly issues: Lens.Lens, + readonly validationFiber: Lens.Lens>, never, never, never, never>, + readonly isCommitting: Lens.Lens, + readonly runSemaphore: Semaphore.Semaphore, + ) { + super() + this.value = Subscribable.make({ + get: Effect.provide(Effect.map(target.get, Option.some), context), + changes: Stream.provideContext( + target.changes.pipe( + Stream.map(Option.some), + Stream.catchCause(() => Stream.make(Option.none())), + ), + context, + ), + }) + this.encodedValue = Lens.make({ + get: Lens.get(internalEncodedValue), + changes: internalEncodedValue.changes, + commit: encoded => Effect.andThen( + Lens.set(internalEncodedValue, encoded), + this.synchronizeEncodedValue(encoded), + ), + lock: Lens.asLensImpl(internalEncodedValue).lock, + }) as unknown as Lens.Lens + this.isValidating = Subscribable.map(validationFiber, Option.isSome) + const commitState = Subscribable.zipLatestAll(issues as any, validationFiber as any, isCommitting as any) as unknown as Subscribable.Subscribable>, + boolean, + ]> + this.canCommit = Subscribable.map( + commitState, + ([currentIssues, fiber, committing]) => currentIssues.length === 0 && Option.isNone(fiber) && !committing, + ) + } + + synchronizeEncodedValue(encodedValue: I): Effect.Effect { + const self = this + return Effect.gen(function*() { + const current = yield* Lens.get(self.validationFiber) + if (Option.isSome(current)) yield* Fiber.interrupt(current.value) + const fiber = yield* Effect.forkScoped( + Effect.ensuring( + SchemaParser.decodeEffect(self.schema)(encodedValue), + Lens.set(self.validationFiber, Option.none()), + ), + ) + yield* Lens.set(self.validationFiber, Option.some(fiber)) + yield* Fiber.join(fiber).pipe( + Effect.flatMap(value => Effect.ensuring( + Effect.andThen( + Lens.set(self.isCommitting, true), + Effect.andThen(Lens.set(self.issues, Array.empty()), Lens.set(self.target, value)), + ), + Lens.set(self.isCommitting, false), + )), + Effect.catchIf(SchemaIssue.isIssue, issue => Lens.set(self.issues, formatIssue(issue))), + ) + }).pipe(Effect.provide(this.context)) as Effect.Effect + } + + get run(): Effect.Effect { + return this.runSemaphore.withPermits(1)(Effect.provide( + Stream.runForEach(Stream.drop(this.target.changes, 1), targetValue => Effect.ignore( + Effect.flatMap(SchemaParser.encodeEffect(this.schema)(targetValue), encodedValue => Effect.flatMap( + Lens.get(this.internalEncodedValue), + current => Equal.equals(encodedValue, current) + ? Effect.succeed(undefined) + : Effect.andThen( + Lens.set(this.issues, Array.empty()), + Lens.set(this.internalEncodedValue, encodedValue), + ), + )), + )), + this.context, + )) + } +} + +const formatIssue = (issue: SchemaIssue.Issue): readonly Form.FormIssue[] => { + const formatted = SchemaIssue.makeFormatterStandardSchemaV1()(issue) + return formatted.issues.map(item => ({ + path: (item.path ?? []) as readonly PropertyKey[], + message: item.message, + })) +} + +export const isSynchronizedForm = (u: unknown): u is SynchronizedForm => Predicate.hasProperty(u, SynchronizedFormTypeId) + +export declare namespace make { + export interface Options { + readonly schema: FormSchema + readonly target: Lens.Lens + readonly initialEncodedValue?: NoInfer + } +} + +export const make = Effect.fnUntraced(function* ( + options: make.Options, +): Effect.fn.Return< + SynchronizedForm, + SchemaIssue.Issue | TER, + Scope.Scope | R | TRR | TRW +> { + const initialEncodedValue = options.initialEncodedValue !== undefined + ? options.initialEncodedValue + : yield* Effect.flatMap(Lens.get(options.target), SchemaParser.encodeEffect(options.schema)) + + return new SynchronizedFormImpl( + options.schema, + yield* Effect.context(), + options.target, + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(initialEncodedValue)), + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Array.empty())), + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none>())), + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(false)), + yield* Semaphore.make(1), + ) +}) + +export declare namespace service { + export interface Options + extends make.Options {} +} + +export const service = ( + options: service.Options, +): Effect.Effect< + SynchronizedForm, + SchemaIssue.Issue | TER, + Scope.Scope | R | TRR | TRW +> => Effect.tap(make(options), form => Effect.asVoid(Effect.forkScoped(form.run))) diff --git a/packages/effect-fc-next/src/index.ts b/packages/effect-fc-next/src/index.ts new file mode 100644 index 0000000..8829e81 --- /dev/null +++ b/packages/effect-fc-next/src/index.ts @@ -0,0 +1,17 @@ +export * as Async from "./Async.js" +export * as Component from "./Component.js" +export * as ErrorObserver from "./ErrorObserver.js" +export * as Form from "./Form.js" +export * as Lens from "./Lens.js" +export * as Memoized from "./Memoized.js" +export * as Mutation from "./Mutation.js" +export * as PubSub from "./PubSub.js" +export * as Query from "./Query.js" +export * as QueryClient from "./QueryClient.js" +export * as ReactRuntime from "./ReactRuntime.js" +export * as Result from "./Result.js" +export * as SetStateAction from "./SetStateAction.js" +export * as Stream from "./Stream.js" +export * as SubmittableForm from "./SubmittableForm.js" +export * as Subscribable from "./Subscribable.js" +export * as SynchronizedForm from "./SynchronizedForm.js" diff --git a/packages/effect-fc-next/src/utils.ts b/packages/effect-fc-next/src/utils.ts new file mode 100644 index 0000000..44ed408 --- /dev/null +++ b/packages/effect-fc-next/src/utils.ts @@ -0,0 +1,3 @@ +export type ExcludeKeys = K extends keyof T ? ( + { [P in K]?: never } & Omit +) : T diff --git a/packages/effect-fc-next/test/Component.test.tsx b/packages/effect-fc-next/test/Component.test.tsx new file mode 100644 index 0000000..9401608 --- /dev/null +++ b/packages/effect-fc-next/test/Component.test.tsx @@ -0,0 +1,354 @@ +import { render, screen, waitFor } from "@testing-library/react" +import { Context, Effect, Layer } from "effect" +import * as React from "react" +import { afterEach, describe, expect, it, vi } from "vitest" +import * as Component from "../src/Component.js" +import * as ReactRuntime from "../src/ReactRuntime.js" + + +class ValueService extends Context.Service()("ValueService") {} + +afterEach(() => { + vi.useRealTimers() +}) + +describe("Component", () => { + it("runs useOnMount only once across rerenders", async () => { + const onMount = vi.fn(() => Effect.succeed("mounted")) + const runtime = ReactRuntime.make(Layer.empty) + const effectRuntime = await runtime.runtime.context() + + const Probe = Component.makeUntraced("UseOnMountProbe")(function*() { + const value = yield* Component.useOnMount(onMount) + return

+ }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("mounted") + expect(onMount).toHaveBeenCalledTimes(1) + + view.rerender( + + + + ) + expect(await screen.findByText("mounted")).toBeTruthy() + expect(onMount).toHaveBeenCalledTimes(1) + + view.unmount() + await runtime.runtime.dispose() + }) + + it("recomputes useOnChange only when dependencies change", async () => { + const onChange = vi.fn((value: number) => Effect.succeed(`value:${value}`)) + const runtime = ReactRuntime.make(Layer.empty) + const effectRuntime = await runtime.runtime.context() + + const Probe = Component.makeUntraced("UseOnChangeProbe")(function*(props: { readonly value: number }) { + const result = yield* Component.useOnChange(() => onChange(props.value), [props.value], { + finalizerExecutionDebounce: 0, + }) + + return
{result}
+ }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("value:1") + expect(onChange).toHaveBeenCalledTimes(1) + + view.rerender( + + + + ) + + expect(await screen.findByText("value:1")).toBeTruthy() + expect(onChange).toHaveBeenCalledTimes(1) + + view.rerender( + + + + ) + + await screen.findByText("value:2") + expect(onChange).toHaveBeenCalledTimes(2) + + view.unmount() + await runtime.runtime.dispose() + }) + + it("closes the previous scope on dependency changes and unmount", async () => { + const cleanup = vi.fn() + const runtime = ReactRuntime.make(Layer.empty) + const effectRuntime = await runtime.runtime.context() + + const Probe = Component.makeUntraced("ScopeCleanupProbe")(function*(props: { readonly value: string }) { + const result = yield* Component.useOnChange( + () => Effect.gen(function*() { + yield* Effect.addFinalizer(() => Effect.sync(() => cleanup(props.value))) + return props.value + }), + [props.value], + { finalizerExecutionDebounce: 0 }, + ) + + return
{result}
+ }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("first") + expect(cleanup).not.toHaveBeenCalled() + + view.rerender( + + + + ) + + await screen.findByText("second") + await waitFor(() => expect(cleanup).toHaveBeenCalledWith("first")) + expect(cleanup).toHaveBeenCalledTimes(1) + + view.unmount() + + await waitFor(() => expect(cleanup).toHaveBeenCalledWith("second")) + expect(cleanup).toHaveBeenCalledTimes(2) + await runtime.runtime.dispose() + }) + + it("runs useReactEffect setup and cleanup when dependencies change", async () => { + const lifecycle = vi.fn<(message: string) => void>() + const runtime = ReactRuntime.make(Layer.empty) + const effectRuntime = await runtime.runtime.context() + + const Probe = Component.makeUntraced("UseReactEffectProbe")(function*(props: { readonly value: string }) { + yield* Component.useReactEffect(() => + Effect.gen(function*() { + yield* Effect.sync(() => lifecycle(`mount:${props.value}`)) + yield* Effect.addFinalizer(() => Effect.sync(() => lifecycle(`cleanup:${props.value}`))) + }), + [props.value]) + + return
{props.value}
+ }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("first") + await waitFor(() => expect(lifecycle).toHaveBeenCalledWith("mount:first")) + + view.rerender( + + + + ) + + await screen.findByText("second") + await waitFor(() => expect(lifecycle).toHaveBeenCalledWith("cleanup:first")) + await waitFor(() => expect(lifecycle).toHaveBeenCalledWith("mount:second")) + + view.unmount() + + await waitFor(() => expect(lifecycle).toHaveBeenCalledWith("cleanup:second")) + expect(lifecycle.mock.calls.map(([message]) => message)).toEqual([ + "mount:first", + "cleanup:first", + "mount:second", + "cleanup:second", + ]) + await runtime.runtime.dispose() + }) + + it("keeps useCallbackSync stable until dependencies change", async () => { + const runtime = ReactRuntime.make(Layer.empty) + const effectRuntime = await runtime.runtime.context() + const seenCallbacks: Array<(value: number) => string> = [] + + const Probe = Component.makeUntraced("UseCallbackSyncProbe")(function*(props: { readonly prefix: string }) { + const callback = yield* Component.useCallbackSync( + (value: number) => Effect.succeed(`${props.prefix}:${value}`), + [props.prefix], + ) + + yield* Component.useOnMount(() => Effect.sync(() => { + seenCallbacks.push(callback) + })) + + React.useEffect(() => { + seenCallbacks.push(callback) + }, [callback]) + + return
{callback(1)}
+ }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("a:1") + expect(seenCallbacks).toHaveLength(2) + expect(seenCallbacks[0]).toBe(seenCallbacks[1]) + expect(seenCallbacks[0]?.(2)).toBe("a:2") + + view.rerender( + + + + ) + + await screen.findByText("a:1") + expect(seenCallbacks).toHaveLength(2) + + view.rerender( + + + + ) + + await screen.findByText("b:1") + await waitFor(() => expect(seenCallbacks).toHaveLength(3)) + expect(seenCallbacks[2]).not.toBe(seenCallbacks[1]) + expect(seenCallbacks[2]?.(2)).toBe("b:2") + + view.unmount() + await runtime.runtime.dispose() + }) + + it("delays cleanup according to finalizerExecutionDebounce", async () => { + const cleanup = vi.fn() + const runtime = ReactRuntime.make(Layer.empty) + const effectRuntime = await runtime.runtime.context() + + const Probe = Component.makeUntraced("DebouncedCleanupProbe")(function*(props: { readonly value: string }) { + const result = yield* Component.useOnChange( + () => Effect.gen(function*() { + yield* Effect.addFinalizer(() => Effect.sync(() => cleanup(props.value))) + return props.value + }), + [props.value], + { finalizerExecutionDebounce: "20 millis" }, + ) + + return
{result}
+ }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("first") + + view.rerender( + + + + ) + + await screen.findByText("second") + expect(cleanup).not.toHaveBeenCalled() + + await new Promise(resolve => setTimeout(resolve, 5)) + expect(cleanup).not.toHaveBeenCalled() + + await waitFor(() => expect(cleanup).toHaveBeenCalledWith("first"), { timeout: 100 }) + + view.unmount() + await waitFor(() => expect(cleanup).toHaveBeenCalledWith("second"), { timeout: 100 }) + await runtime.runtime.dispose() + }) + + it("does not remount a component when only nonReactiveTags change", async () => { + const mounts = vi.fn() + const unmounts = vi.fn() + const runtime = ReactRuntime.make(Layer.empty) + const effectRuntime = await runtime.runtime.context() + + const SubComponent = Component.makeUntraced("NonReactiveSubComponent")(function*() { + const service = yield* ValueService + + yield* Component.useOnMount(() => Effect.gen(function*() { + yield* Effect.sync(() => mounts()) + yield* Effect.addFinalizer(() => Effect.sync(() => unmounts())) + })) + + return
{service.value}
+ }).pipe( + Component.withOptions({ nonReactiveTags: [ValueService] }) + ) + + const Parent = Component.makeUntraced("NonReactiveParent")(function*(props: { readonly value: string }) { + const serviceLayer = React.useMemo( + () => Layer.succeed(ValueService, { value: props.value }), + [props.value], + ) + const context = yield* Component.useContextFromLayer(serviceLayer, { + finalizerExecutionDebounce: 0, + }) + const Child = yield* Effect.provide(SubComponent.use, context) + + return + }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("first") + expect(mounts).toHaveBeenCalledTimes(1) + expect(unmounts).not.toHaveBeenCalled() + + view.rerender( + + + + ) + + await screen.findByText("second") + expect(mounts).toHaveBeenCalledTimes(1) + expect(unmounts).not.toHaveBeenCalled() + + view.unmount() + await waitFor(() => expect(unmounts).toHaveBeenCalledTimes(1)) + await runtime.runtime.dispose() + }) +}) diff --git a/packages/effect-fc-next/test/Lens.test.tsx b/packages/effect-fc-next/test/Lens.test.tsx new file mode 100644 index 0000000..3407f14 --- /dev/null +++ b/packages/effect-fc-next/test/Lens.test.tsx @@ -0,0 +1,165 @@ +import { fireEvent, render, screen, waitFor } from "@testing-library/react" +import { Effect, Layer, SubscriptionRef } from "effect" +import * as React from "react" +import { describe, expect, it } from "vitest" +import * as Component from "../src/Component.js" +import * as Lens from "../src/Lens.js" +import * as ReactRuntime from "../src/ReactRuntime.js" + + +const makeRuntime = async () => { + const runtime = ReactRuntime.make(Layer.empty) + const effectRuntime = await runtime.runtime.context() + + return { + runtime, + effectRuntime, + dispose: () => runtime.runtime.dispose(), + } +} + +describe("Lens", () => { + it("useState stays in sync with lens updates in both directions", async () => { + const { runtime, effectRuntime, dispose } = await makeRuntime() + const ref = await Effect.runPromise(SubscriptionRef.make(0)) + const lens = Lens.fromSubscriptionRef(ref) + + const Probe = Component.makeUntraced("LensUseStateProbe")(function*() { + const [value, setValue] = yield* Lens.useState(lens) + + return ( + <> +
{value}
+ + + ) + }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("0") + + await Effect.runPromise(Lens.set(lens, 5)) + await screen.findByText("5") + + fireEvent.click(screen.getByRole("button", { name: "increment" })) + await screen.findByText("6") + expect(await Effect.runPromise(Lens.get(lens))).toBe(6) + + view.unmount() + await dispose() + }) + + it("useState respects the provided equivalence when subscribing to lens changes", async () => { + const { runtime, effectRuntime, dispose } = await makeRuntime() + const ref = await Effect.runPromise(SubscriptionRef.make({ id: 1, label: "first" })) + const lens = Lens.fromSubscriptionRef(ref) + + const Probe = Component.makeUntraced("LensUseStateEquivalenceProbe")(function*() { + const [value] = yield* Lens.useState(lens, { + equivalence: (self, that) => self.id === that.id, + }) + + return
{value.label}
+ }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("first") + + await Effect.runPromise(Lens.set(lens, { id: 1, label: "ignored" })) + await waitFor(() => expect(screen.getByText("first")).toBeTruthy()) + expect(screen.queryByText("ignored")).toBeNull() + + await Effect.runPromise(Lens.set(lens, { id: 2, label: "updated" })) + await screen.findByText("updated") + + view.unmount() + await dispose() + }) + + it("useFromReactState writes React state changes into the returned lens", async () => { + const { runtime, effectRuntime, dispose } = await makeRuntime() + let lens: Lens.Lens | undefined + + const Probe = Component.makeUntraced("LensUseFromReactStateProbe")(function*() { + const [value, setValue] = React.useState("hello") + const reactLens = yield* Lens.useFromReactState([value, setValue]) + + yield* Component.useOnMount(() => Effect.sync(() => { + lens = reactLens + })) + + return + }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("hello") + await waitFor(() => expect(lens).toBeDefined()) + + fireEvent.click(screen.getByRole("button", { name: "hello" })) + await screen.findByText("hello!") + await waitFor(async () => expect(await Effect.runPromise(Lens.get(lens!))).toBe("hello!")) + + view.unmount() + await dispose() + }) + + it("useFromReactState respects equivalence when lens updates flow back into React state", async () => { + const { runtime, effectRuntime, dispose } = await makeRuntime() + let lens: Lens.Lens<{ readonly id: number; readonly label: string }, never, never, never, never> | undefined + + const Probe = Component.makeUntraced("LensUseFromReactStateEquivalenceProbe")(function*() { + const [value, setValue] = React.useState({ id: 1, label: "first" }) + const reactLens = yield* Lens.useFromReactState([value, setValue], { + equivalence: (self, that) => self.id === that.id, + }) + + yield* Component.useOnMount(() => Effect.sync(() => { + lens = reactLens + })) + + return
{value.label}
+ }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("first") + await waitFor(() => expect(lens).toBeDefined()) + + await Effect.runPromise(Lens.set(lens!, { id: 1, label: "ignored" })) + await waitFor(() => expect(screen.getByText("first")).toBeTruthy()) + expect(screen.queryByText("ignored")).toBeNull() + + await Effect.runPromise(Lens.set(lens!, { id: 2, label: "updated" })) + await screen.findByText("updated") + + view.unmount() + await dispose() + }) +}) diff --git a/packages/effect-fc-next/test/Query.test.ts b/packages/effect-fc-next/test/Query.test.ts new file mode 100644 index 0000000..ee6544f --- /dev/null +++ b/packages/effect-fc-next/test/Query.test.ts @@ -0,0 +1,169 @@ +import { Effect, Option, type Scope, Stream } from "effect" +import { describe, expect, it } from "vitest" +import * as Query from "../src/Query.js" +import * as QueryClient from "../src/QueryClient.js" +import * as Result from "../src/Result.js" + + +const runQueryTest = (effect: Effect.Effect) => + Effect.runPromise(Effect.scoped(effect.pipe( + Effect.provide(QueryClient.QueryClient.Default), + ))) + +const expectSuccessValue = ( + result: Result.Result, +): A => { + expect(Result.isSuccess(result)).toBe(true) + + if (!Result.isSuccess(result)) + throw new Error(`Expected Success result, received ${result._tag}`) + + return result.value +} + +const expectSomeValue =
(option: Option.Option): A => { + expect(Option.isSome(option)).toBe(true) + + if (!Option.isSome(option)) + throw new Error("Expected Some option, received None") + + return option.value +} + +describe("Query", () => { + it("fetch caches successful results until they are invalidated or stale", async () => { + let calls = 0 + const key = Stream.empty as Stream.Stream + + const result = await runQueryTest(Effect.gen(function*() { + const query = yield* Query.make({ + key, + f: ([id]: readonly [number]) => Effect.sync(() => { + calls += 1 + return `value:${id}:${calls}` + }), + staleTime: "1 minute", + }) + + const first = yield* query.fetch([1]) + const second = yield* query.fetch([1]) + + return [first, second] as const + })) + + expect(calls).toBe(1) + expect(result[0]._tag).toBe("Success") + expect(result[1]._tag).toBe("Success") + expect(expectSuccessValue(result[0])).toBe("value:1:1") + expect(expectSuccessValue(result[1])).toBe("value:1:1") + }) + + it("refresh reruns the latest query key", async () => { + let calls = 0 + const key = Stream.empty as Stream.Stream + + const result = await runQueryTest(Effect.gen(function*() { + const query = yield* Query.make({ + key, + f: ([id]: readonly [number]) => Effect.sync(() => { + calls += 1 + return `value:${id}:${calls}` + }), + staleTime: "0 millis", + }) + + const first = yield* query.fetch([1]) + yield* Effect.sleep("1 millis") + const refreshed = yield* query.refresh + + return [first, refreshed] as const + })) + + expect(calls).toBe(2) + expect(expectSuccessValue(result[0])).toBe("value:1:1") + expect(expectSuccessValue(result[1])).toBe("value:1:2") + }) + + it("invalidateCacheEntry forces the next fetch for that key to rerun", async () => { + let calls = 0 + const key = Stream.empty as Stream.Stream + + const result = await runQueryTest(Effect.gen(function*() { + const query = yield* Query.make({ + key, + f: ([id]: readonly [number]) => Effect.sync(() => { + calls += 1 + return `value:${id}:${calls}` + }), + staleTime: "1 minute", + }) + + const first = yield* query.fetch([1]) + yield* query.invalidateCacheEntry([1]) + const second = yield* query.fetch([1]) + + return [first, second] as const + })) + + expect(calls).toBe(2) + expect(expectSuccessValue(result[0])).toBe("value:1:1") + expect(expectSuccessValue(result[1])).toBe("value:1:2") + }) + + it("invalidateCache clears cached entries for the query function", async () => { + let calls = 0 + const key = Stream.empty as Stream.Stream + + const result = await runQueryTest(Effect.gen(function*() { + const query = yield* Query.make({ + key, + f: ([id]: readonly [number]) => Effect.sync(() => { + calls += 1 + return `value:${id}:${calls}` + }), + staleTime: "1 minute", + }) + + const first = yield* query.fetch([1]) + yield* query.invalidateCache + const second = yield* query.fetch([1]) + + return [first, second] as const + })) + + expect(calls).toBe(2) + expect(expectSuccessValue(result[0])).toBe("value:1:1") + expect(expectSuccessValue(result[1])).toBe("value:1:2") + }) + + it("service starts the key stream automatically and updates latest state", async () => { + let calls = 0 + const key = Stream.make([1] as const) as Stream.Stream + + const effect = Effect.gen(function*() { + const query = yield* Query.service({ + key, + f: ([id]: readonly [number]) => Effect.sync(() => { + calls += 1 + return `value:${id}:${calls}` + }), + staleTime: "1 minute", + }) + + yield* Effect.sleep("10 millis") + + return { + final: yield* query.result.get, + latestKey: yield* query.latestKey.get, + latestFinalResult: yield* query.latestFinalResult.get, + } + }) + + const result = await runQueryTest(effect) + + expect(calls).toBe(1) + expect(expectSuccessValue(result.final)).toBe("value:1:1") + expect(expectSomeValue(result.latestKey)).toEqual([1]) + expect(expectSuccessValue(expectSomeValue(result.latestFinalResult))).toBe("value:1:1") + }) +}) diff --git a/packages/effect-fc-next/test/Subscribable.test.tsx b/packages/effect-fc-next/test/Subscribable.test.tsx new file mode 100644 index 0000000..5641d1b --- /dev/null +++ b/packages/effect-fc-next/test/Subscribable.test.tsx @@ -0,0 +1,129 @@ +import { render, screen, waitFor } from "@testing-library/react" +import { Effect, Fiber, Layer, Stream, SubscriptionRef } from "effect" +import { Lens } from "effect-lens" +import { describe, expect, it } from "vitest" +import * as Component from "../src/Component.js" +import * as ReactRuntime from "../src/ReactRuntime.js" +import * as Subscribable from "../src/Subscribable.js" + + +const makeRuntime = async () => { + const runtime = ReactRuntime.make(Layer.empty) + const effectRuntime = await runtime.runtime.context() + + return { + runtime, + effectRuntime, + dispose: () => runtime.runtime.dispose(), + } +} + +describe("Subscribable", () => { + it("zipLatestAll reads current values from all inputs", async () => { + const leftRef = await Effect.runPromise(SubscriptionRef.make(1)) + const rightRef = await Effect.runPromise(SubscriptionRef.make("a")) + const left = Lens.fromSubscriptionRef(leftRef) + const right = Lens.fromSubscriptionRef(rightRef) + + const zipped = Subscribable.zipLatestAll(left, right) + + expect(await Effect.runPromise(zipped.get)).toEqual([1, "a"]) + }) + + it("zipLatestAll emits updates when any input changes", async () => { + const leftRef = await Effect.runPromise(SubscriptionRef.make(1)) + const rightRef = await Effect.runPromise(SubscriptionRef.make("a")) + const left = Lens.fromSubscriptionRef(leftRef) + const right = Lens.fromSubscriptionRef(rightRef) + + const zipped = Subscribable.zipLatestAll(left, right) + const values: Array = [] + + const collector = Effect.runFork(Effect.scoped(zipped.changes.pipe( + Stream.runForEach(value => Effect.sync(() => { + values.push(value as readonly [number, string]) + })), + ))) + + await Effect.runPromise(Lens.set(left, 2)) + await waitFor(() => expect(values).toContainEqual([2, "a"])) + + await Effect.runPromise(Lens.set(right, "b")) + await waitFor(() => expect(values).toContainEqual([2, "b"])) + + await Effect.runPromise(Fiber.interrupt(collector)) + }) + + it("useAll returns the latest values and rerenders when any input changes", async () => { + const { runtime, effectRuntime, dispose } = await makeRuntime() + const countRef = await Effect.runPromise(SubscriptionRef.make(1)) + const labelRef = await Effect.runPromise(SubscriptionRef.make("a")) + const count = Lens.fromSubscriptionRef(countRef) + const label = Lens.fromSubscriptionRef(labelRef) + + const Probe = Component.makeUntraced("SubscribableUseAllProbe")(function*() { + const [currentCount, currentLabel] = yield* Subscribable.useAll([count, label]) + + return
{`${currentCount}:${currentLabel}`}
+ }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("1:a") + + await Effect.runPromise(Lens.set(count, 2)) + await screen.findByText("2:a") + + await Effect.runPromise(Lens.set(label, "b")) + await screen.findByText("2:b") + + view.unmount() + await dispose() + }) + + it("useAll respects the provided equivalence when processing updates", async () => { + const { runtime, effectRuntime, dispose } = await makeRuntime() + const itemRef = await Effect.runPromise(SubscriptionRef.make({ id: 1, label: "first" })) + const flagRef = await Effect.runPromise(SubscriptionRef.make(true)) + const item = Lens.fromSubscriptionRef(itemRef) + const flag = Lens.fromSubscriptionRef(flagRef) + + const Probe = Component.makeUntraced("SubscribableUseAllEquivalenceProbe")(function*() { + const [currentItem, currentFlag] = yield* Subscribable.useAll([item, flag], { + equivalence: ([selfItem, selfFlag], [thatItem, thatFlag]) => + selfItem.id === thatItem.id && selfFlag === thatFlag, + }) + + return
{`${currentItem.label}:${currentFlag ? "on" : "off"}`}
+ }).pipe( + Component.withRuntime(runtime.context) + ) + + const view = render( + + + + ) + + await screen.findByText("first:on") + + await Effect.runPromise(Lens.set(item, { id: 1, label: "ignored" })) + await waitFor(() => expect(screen.getByText("first:on")).toBeTruthy()) + expect(screen.queryByText("ignored:on")).toBeNull() + + await Effect.runPromise(Lens.set(flag, false)) + await screen.findByText("ignored:off") + + await Effect.runPromise(Lens.set(item, { id: 2, label: "updated" })) + await screen.findByText("updated:off") + + view.unmount() + await dispose() + }) +}) diff --git a/packages/effect-fc-next/tsconfig.json b/packages/effect-fc-next/tsconfig.json new file mode 100644 index 0000000..6ad0810 --- /dev/null +++ b/packages/effect-fc-next/tsconfig.json @@ -0,0 +1,40 @@ +{ + "compilerOptions": { + // Enable latest features + "lib": ["ESNext", "DOM"], + "target": "ESNext", + "module": "NodeNext", + "moduleDetection": "force", + "jsx": "react-jsx", + // "allowJs": true, + + // Bundler mode + "moduleResolution": "NodeNext", + // "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + // "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false, + + // Build + "rootDir": "./src", + "outDir": "./dist", + "declaration": true, + "sourceMap": true, + + "plugins": [ + { "name": "@effect/language-service" } + ] + }, + + "include": ["./src"], + "exclude": ["**/*.test.ts", "**/*.spec.ts"] +} diff --git a/packages/effect-fc-next/vitest.config.ts b/packages/effect-fc-next/vitest.config.ts new file mode 100644 index 0000000..cec0e39 --- /dev/null +++ b/packages/effect-fc-next/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config" + + +export default defineConfig({ + test: { + environment: "jsdom", + include: ["test/**/*.test.ts?(x)"], + }, +}) diff --git a/packages/example-next/.gitignore b/packages/example-next/.gitignore new file mode 100644 index 0000000..a014dba --- /dev/null +++ b/packages/example-next/.gitignore @@ -0,0 +1,26 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +.tanstack diff --git a/packages/example-next/README.md b/packages/example-next/README.md new file mode 100644 index 0000000..aef5174 --- /dev/null +++ b/packages/example-next/README.md @@ -0,0 +1,10 @@ +# Effect FC Next Example + +Minimal React example for `effect-fc-next`, Effect V4, and `effect-lens@2`. + +```bash +bun run dev +``` + +The counter demonstrates a V4 `SubscriptionRef` exposed as an Effect Lens and +rendered through an Effect-FC component. diff --git a/packages/example-next/biome.json b/packages/example-next/biome.json new file mode 100644 index 0000000..77ba81c --- /dev/null +++ b/packages/example-next/biome.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://biomejs.dev/schemas/latest/schema.json", + "root": false, + "extends": "//", + "files": { + "includes": ["./src/**", "!src/routeTree.gen.ts"] + } +} diff --git a/packages/example-next/index.html b/packages/example-next/index.html new file mode 100644 index 0000000..e7e38b3 --- /dev/null +++ b/packages/example-next/index.html @@ -0,0 +1,13 @@ + + + + + + + Effect FC Next Example + + +
+ + + diff --git a/packages/example-next/package.json b/packages/example-next/package.json new file mode 100644 index 0000000..a37ebbd --- /dev/null +++ b/packages/example-next/package.json @@ -0,0 +1,34 @@ +{ + "name": "@effect-fc/example-next", + "version": "0.0.0", + "type": "module", + "private": true, + "scripts": { + "build": "tsc -b && vite build", + "dev": "vite", + "lint:tsc": "tsc --noEmit", + "lint:biome": "biome lint", + "preview": "vite preview", + "clean:cache": "rm -rf .turbo node_modules/.tmp node_modules/.vite* .tanstack", + "clean:dist": "rm -rf dist", + "clean:modules": "rm -rf node_modules" + }, + "devDependencies": { + "@types/react": "^19.2.15", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.2", + "globals": "^17.6.0", + "react": "^19.2.6", + "react-dom": "^19.2.6", + "vite": "^8.0.16" + }, + "dependencies": { + "effect": "4.0.0-beta.85", + "effect-fc-next": "workspace:*" + }, + "overrides": { + "@types/react": "^19.2.15", + "effect": "4.0.0-beta.85", + "react": "^19.2.6" + } +} diff --git a/packages/example-next/src/index.css b/packages/example-next/src/index.css new file mode 100644 index 0000000..8f84e7d --- /dev/null +++ b/packages/example-next/src/index.css @@ -0,0 +1,28 @@ +:root { + color: #17202a; + font-family: system-ui, sans-serif; + background: #f4f6f7; +} + +body { + display: grid; + min-width: 320px; + min-height: 100vh; + margin: 0; + place-items: center; +} + +main { + padding: 2rem; + text-align: center; +} + +button { + padding: 0.65rem 1rem; + border: 0; + border-radius: 0.5rem; + color: white; + font: inherit; + background: #7d3c98; + cursor: pointer; +} diff --git a/packages/example-next/src/main.tsx b/packages/example-next/src/main.tsx new file mode 100644 index 0000000..125ca5b --- /dev/null +++ b/packages/example-next/src/main.tsx @@ -0,0 +1,40 @@ +import { Effect, Layer, SubscriptionRef } from "effect" +import { Component, Lens, ReactRuntime, Subscribable } from "effect-fc-next" +import { StrictMode } from "react" +import { createRoot } from "react-dom/client" +import "./index.css" + + +const Counter = Component.make("Counter")(function*() { + const count = yield* Component.useOnMount(() => Effect.map( + SubscriptionRef.make(0), + Lens.fromSubscriptionRef, + )) + const [value] = yield* Subscribable.useAll([count]) + const increment = yield* Component.useCallbackSync( + () => Lens.update(count, n => n + 1), + [count], + ) + + return ( +
+

Effect FC Next

+

Running on Effect V4.

+ +
+ ) +}) + +const runtime = ReactRuntime.make(Layer.empty) +const CounterApp = Counter.pipe(Component.withRuntime(runtime.context)) + +// biome-ignore lint/style/noNonNullAssertion: the Vite template provides this element +createRoot(document.getElementById("root")!).render( + + + + + , +) diff --git a/packages/example-next/src/vite-env.d.ts b/packages/example-next/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/packages/example-next/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/packages/example-next/tsconfig.app.json b/packages/example-next/tsconfig.app.json new file mode 100644 index 0000000..f95336a --- /dev/null +++ b/packages/example-next/tsconfig.app.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true, + + "plugins": [ + { "name": "@effect/language-service" } + ] + }, + + "include": ["src"] +} diff --git a/packages/example-next/tsconfig.json b/packages/example-next/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/packages/example-next/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/packages/example-next/tsconfig.node.json b/packages/example-next/tsconfig.node.json new file mode 100644 index 0000000..0d067e8 --- /dev/null +++ b/packages/example-next/tsconfig.node.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + + "include": ["vite.config.ts"] +} diff --git a/packages/example-next/vite.config.ts b/packages/example-next/vite.config.ts new file mode 100644 index 0000000..5c94b15 --- /dev/null +++ b/packages/example-next/vite.config.ts @@ -0,0 +1,10 @@ +import react from "@vitejs/plugin-react" +import { defineConfig } from "vite" + + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [ + react(), + ], +}) -- 2.52.0 From a38be0b1722c299e4b555142e5f8ed28eed7a3c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 22 Jun 2026 06:36:32 +0200 Subject: [PATCH 038/133] Component v4 migration work --- packages/effect-fc-next/src/Component.new.ts | 385 ++++++ packages/effect-fc-next/src/Component.ts | 1193 ++++++++++++++---- 2 files changed, 1354 insertions(+), 224 deletions(-) create mode 100644 packages/effect-fc-next/src/Component.new.ts diff --git a/packages/effect-fc-next/src/Component.new.ts b/packages/effect-fc-next/src/Component.new.ts new file mode 100644 index 0000000..a8f3d4a --- /dev/null +++ b/packages/effect-fc-next/src/Component.new.ts @@ -0,0 +1,385 @@ +/** biome-ignore-all lint/complexity/noBannedTypes: {} is the default type for React props */ +import { + type Context, + type Duration, + Effect, + Exit, + Function, + identity, + Layer, + Pipeable, + Predicate, + Scope, + Tracer, +} from "effect" +import * as React from "react" + + +export const ComponentTypeId: unique symbol = Symbol.for("@effect-fc/Component/Component") +export type ComponentTypeId = typeof ComponentTypeId + +export interface Component

+extends ComponentPrototype, ComponentOptions { + new(_: never): Record + readonly [ComponentTypeId]: ComponentTypeId + readonly "~Props": P + readonly "~Success": A + readonly "~Error": E + readonly "~Context": R + readonly "~Function": F + readonly body: (props: P) => Effect.Effect +} + +export declare namespace Component { + export type Default

= Component> + export type Any = Component + export type Signature = (props: any) => React.ReactNode + export type DefaultSignature

= (props: P) => A + export type Props = T["~Props"] + export type Success = T["~Success"] + export type Error = T["~Error"] + export type Context = T["~Context"] + export type Function = T["~Function"] + export type AsComponent = Component, Success, Error, Context, Function> +} + +export interface ComponentOptions { + readonly displayName?: string + readonly nonReactiveTags: readonly Context.Key[] + readonly finalizerExecutionStrategy: "sequential" | "parallel" + readonly finalizerExecutionDebounce: Duration.Input +} + +export const defaultOptions: ComponentOptions = { + nonReactiveTags: [Tracer.ParentSpan], + finalizerExecutionStrategy: "sequential", + finalizerExecutionDebounce: "100 millis", +} + +export interface ComponentPrototype extends Pipeable.Pipeable { + readonly [ComponentTypeId]: ComponentTypeId + readonly use: Effect.Effect> +} + +type ComponentImpl = Component + +const makeFunctionComponent = ( + self: ComponentImpl, + contextRef: React.RefObject>, +): Component.Signature => { + if ("asFunctionComponent" in self && typeof self.asFunctionComponent === "function") { + return self.asFunctionComponent(contextRef) + } + const FunctionComponent = (props: {}) => Effect.runSyncWith(contextRef.current)( + Effect.flatMap( + useScope([], self), + scope => Effect.provideService(self.body(props), Scope.Scope, scope), + ), + ) + FunctionComponent.displayName = self.displayName ?? "Anonymous" + return "transformFunctionComponent" in self && typeof self.transformFunctionComponent === "function" + ? self.transformFunctionComponent(FunctionComponent) + : FunctionComponent +} + +const use = Effect.fnUntraced(function* (self: ComponentImpl) { + const context = yield* Effect.context() + const cached = componentCache.get(self) + if (cached !== undefined) { + cached.contextRef.current = context + return cached.component + } + const contextRef = { current: context } + const component = makeFunctionComponent(self, contextRef) + componentCache.set(self, { contextRef, component }) + return component +}) + +const componentCache = new WeakMap } + readonly component: Component.Signature +}>() + +export const ComponentPrototype = Object.freeze({ + [ComponentTypeId]: ComponentTypeId, + ...Pipeable.Prototype, + get use() { + return use(this as ComponentImpl) + }, +}) as unknown as ComponentPrototype + +export const isComponent = (u: unknown): u is Component.Any => Predicate.hasProperty(u, ComponentTypeId) + +type GeneratorBody

= ( + props: P, +) => Effect.fn.Return + +type EffectBody

= ( + props: P, +) => Effect.Effect + +export interface Make { +

( + body: GeneratorBody | EffectBody, + ...pipeables: readonly Function[] + ): Component.Default + (name: string, options?: Tracer.SpanOptionsNoTrace):

( + body: GeneratorBody | EffectBody, + ...pipeables: readonly Function[] + ) => Component.Default +} + +const component = ( + body: Function, + displayName: string | undefined, + traced: boolean, + pipeables: readonly Function[], +): Component.Any => Object.setPrototypeOf( + Object.assign(() => {}, defaultOptions, { + body: traced && displayName + ? Effect.fn(displayName)(body as never, ...pipeables as []) + : Effect.fnUntraced(body as never, ...pipeables as []), + displayName, + }), + ComponentPrototype, +) + +export const make: Make = ((nameOrBody: string | Function, ...args: readonly unknown[]) => { + if (typeof nameOrBody === "string") { + return (body: Function, ...pipeables: readonly Function[]) => component(body, nameOrBody, true, pipeables) + } + return component(nameOrBody, undefined, true, args as readonly Function[]) +}) as Make + +export const makeUntraced: Make = ((nameOrBody: string | Function, ...args: readonly unknown[]) => { + if (typeof nameOrBody === "string") { + return (body: Function, ...pipeables: readonly Function[]) => component(body, nameOrBody, false, pipeables) + } + return component(nameOrBody, undefined, false, args as readonly Function[]) +}) as Make + +export declare namespace withSignature { + export type Result = ( + & Omit> + & Component, Component.Success, Component.Error, Component.Context, F> + ) +} + +export const withSignature: { + (): (self: T) => withSignature.Result + (self: T): withSignature.Result +} = (self?: Component.Any): any => self === undefined ? identity : self + +export const withOptions: { + (options: Partial): (self: T) => T + (self: T, options: Partial): T +} = Function.dual(2, (self: T, options: Partial): T => Object.setPrototypeOf( + Object.assign(() => {}, self, options), + Object.getPrototypeOf(self), +)) + +export const withRuntime: { +

( + context: React.Context>, + ): (self: Component, F>) => F +

( + self: Component, F>, + context: React.Context>, + ): F +} = Function.dual(2,

( + self: Component, + context: React.Context>, +) => function WithRuntime(props: P) { + return React.createElement( + Effect.runSyncWith(React.useContext(context))(self.use) as React.FC

, + props, + ) +}) + +export declare namespace useScope { + export interface Options { + readonly finalizerExecutionStrategy?: "sequential" | "parallel" + readonly finalizerExecutionDebounce?: Duration.Input + } +} + +export const useScope = Effect.fnUntraced(function* ( + deps: React.DependencyList, + options?: useScope.Options, +): Effect.fn.Return { + const context = yield* Effect.context() + const contextRef = React.useRef(context) + contextRef.current = context + const scope = React.useMemo( + () => Scope.makeUnsafe(options?.finalizerExecutionStrategy ?? defaultOptions.finalizerExecutionStrategy), + // biome-ignore lint/correctness/useExhaustiveDependencies: caller controls scope lifetime + deps, + ) + + React.useEffect(() => { + const pending = scopeCleanupTimers.get(scope) + if (pending !== undefined) clearTimeout(pending) + return () => { + const timer = setTimeout(() => { + Effect.runSyncWith(contextRef.current)(Scope.close(scope, Exit.succeed(undefined))) + scopeCleanupTimers.delete(scope) + }, durationMillis(options?.finalizerExecutionDebounce ?? defaultOptions.finalizerExecutionDebounce)) + scopeCleanupTimers.set(scope, timer) + } + }, [scope, options?.finalizerExecutionDebounce]) + + return scope +}) + +const scopeCleanupTimers = new WeakMap>() + +const durationMillis = (input: Duration.Input): number => { + if (typeof input === "number") return input + return Number(Effect.runSync(Effect.map(Effect.succeed(input), value => { + const match = typeof value === "string" ? /([\d.]+)\s*(millis|seconds?)/.exec(value) : undefined + if (!match) return 0 + return Number(match[1]) * (match[2].startsWith("second") ? 1_000 : 1) + }))) +} + +export const useOnMount = Effect.fnUntraced(function* ( + f: () => Effect.Effect, +): Effect.fn.Return { + const context = yield* Effect.context() + const id = React.useId() + let cached = mountCache.get(id) + if (cached === undefined) { + cached = { + effect: Effect.runSyncWith(context)(Effect.cached(f())), + } + mountCache.set(id, cached) + } + React.useEffect(() => { + if (cached?.cleanup !== undefined) clearTimeout(cached.cleanup) + const entry = cached + return () => { + entry.cleanup = setTimeout(() => mountCache.delete(id), 0) + } + }, [id, cached]) + return yield* cached.effect as Effect.Effect +}) + +const mountCache = new Map + cleanup?: ReturnType +}>() + +export declare namespace useOnChange { + export interface Options extends useScope.Options {} +} + +export const useOnChange = Effect.fnUntraced(function* ( + f: () => Effect.Effect, + deps: React.DependencyList, + options?: useOnChange.Options, +): Effect.fn.Return> { + const context = yield* Effect.context>() + const scope = yield* useScope(deps, options) + const cached = + // biome-ignore lint/correctness/useExhaustiveDependencies: scope tracks the caller-provided dependency list + React.useMemo( + () => Effect.runSyncWith(context)(Effect.cached(Effect.provideService(f(), Scope.Scope, scope))), + [scope], + ) + return yield* cached +}) + +export declare namespace useReactEffect { + export interface Options { + readonly finalizerExecutionMode?: "sync" | "fork" + readonly finalizerExecutionStrategy?: "sequential" | "parallel" + } +} + +const runReactEffect = ( + context: Context.Context>, + f: () => Effect.Effect, + options?: useReactEffect.Options, +): (() => void) => { + const scope = Scope.makeUnsafe(options?.finalizerExecutionStrategy ?? defaultOptions.finalizerExecutionStrategy) + Effect.runSyncWith(context)(Effect.exit(Effect.provideService(f(), Scope.Scope, scope))) + return () => { + const close = Scope.close(scope, Exit.succeed(undefined)) + if ((options?.finalizerExecutionMode ?? "fork") === "sync") Effect.runSyncWith(context)(close) + else Effect.runForkWith(context)(close) + } +} + +export const useReactEffect = Effect.fnUntraced(function* ( + f: () => Effect.Effect, + deps?: React.DependencyList, + options?: useReactEffect.Options, +): Effect.fn.Return> { + const context = yield* Effect.context>() + // biome-ignore lint/correctness/useExhaustiveDependencies: caller supplies React dependencies + React.useEffect(() => runReactEffect(context, f, options), deps) +}) + +export declare namespace useReactLayoutEffect { + export interface Options extends useReactEffect.Options {} +} + +export const useReactLayoutEffect = Effect.fnUntraced(function* ( + f: () => Effect.Effect, + deps?: React.DependencyList, + options?: useReactLayoutEffect.Options, +): Effect.fn.Return> { + const context = yield* Effect.context>() + // biome-ignore lint/correctness/useExhaustiveDependencies: caller supplies React dependencies + React.useLayoutEffect(() => runReactEffect(context, f, options), deps) +}) + +export const useRunSync = (): Effect.Effect< + (effect: Effect.Effect) => A, + never, + R +> => Effect.map(Effect.context(), Effect.runSyncWith) + +export const useRunPromise = (): Effect.Effect< + (effect: Effect.Effect) => Promise, + never, + R +> => Effect.map(Effect.context(), Effect.runPromiseWith) + +export const useCallbackSync = Effect.fnUntraced(function* ( + f: (...args: Args) => Effect.Effect, + deps: React.DependencyList, +): Effect.fn.Return<(...args: Args) => A, never, R> { + const context = yield* Effect.context() + const contextRef = React.useRef(context) + contextRef.current = context + // biome-ignore lint/correctness/useExhaustiveDependencies: caller supplies React dependencies + return React.useCallback((...args: Args) => Effect.runSyncWith(contextRef.current)(f(...args)), deps) +}) + +export const useCallbackPromise = Effect.fnUntraced(function* ( + f: (...args: Args) => Effect.Effect, + deps: React.DependencyList, +): Effect.fn.Return<(...args: Args) => Promise, never, R> { + const context = yield* Effect.context() + const contextRef = React.useRef(context) + contextRef.current = context + // biome-ignore lint/correctness/useExhaustiveDependencies: caller supplies React dependencies + return React.useCallback((...args: Args) => Effect.runPromiseWith(contextRef.current)(f(...args)), deps) +}) + +export declare namespace useContext { + export interface Options extends useOnChange.Options {} +} + +export const useContextFromLayer = ( + layer: Layer.Layer, + options?: useContext.Options, +): Effect.Effect, E, RIn | Scope.Scope> => useOnChange( + () => Effect.flatMap( + Effect.context(), + context => Layer.build(Layer.provide(layer, Layer.succeedContext(context))), + ), + [layer], + options, +) diff --git a/packages/effect-fc-next/src/Component.ts b/packages/effect-fc-next/src/Component.ts index 6bf9d93..ed24870 100644 --- a/packages/effect-fc-next/src/Component.ts +++ b/packages/effect-fc-next/src/Component.ts @@ -1,23 +1,15 @@ /** biome-ignore-all lint/complexity/noBannedTypes: {} is the default type for React props */ -import { - Context, - type Duration, - Effect, - Exit, - Function, - identity, - Layer, - Pipeable, - Predicate, - Scope, - Tracer, -} from "effect" +/** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */ +import { Context, type Duration, Effect, Equivalence, ExecutionStrategy, Exit, Fiber, Function, HashMap, identity, Layer, Option, Pipeable, Predicate, Ref, Runtime, Scope, Tracer, type Utils } from "effect" import * as React from "react" export const ComponentTypeId: unique symbol = Symbol.for("@effect-fc/Component/Component") export type ComponentTypeId = typeof ComponentTypeId +/** + * Represents an Effect-based React Component that integrates the Effect system with React. + */ export interface Component

extends ComponentPrototype, ComponentOptions { new(_: never): Record @@ -27,137 +19,507 @@ extends ComponentPrototype, ComponentOptions { readonly "~Error": E readonly "~Context": R readonly "~Function": F + readonly body: (props: P) => Effect.Effect } export declare namespace Component { export type Default

= Component> export type Any = Component + export type Signature = (props: any) => React.ReactNode export type DefaultSignature

= (props: P) => A - export type Props = T["~Props"] - export type Success = T["~Success"] - export type Error = T["~Error"] - export type Context = T["~Context"] - export type Function = T["~Function"] + + export type Props = [T] extends [Component] ? P : never + export type Success = [T] extends [Component] ? A : never + export type Error = [T] extends [Component] ? E : never + export type Context = [T] extends [Component] ? R : never + export type Function = [T] extends [Component] ? F : never + export type AsComponent = Component, Success, Error, Context, Function> } + +export interface ComponentImpl

+extends Component, ComponentImplPrototype {} + +export interface ComponentImplPrototype { + readonly use: Effect.Effect> + + asFunctionComponent(contextRef: React.Ref>>): F + setFunctionComponentName(f: F): void + transformFunctionComponent(f: F): F +} + +export const ComponentImplPrototype: ComponentImplPrototype = Object.freeze({ + get use() { return use(this) }, + + asFunctionComponent

( + this: ComponentImpl, + contextRef: React.RefObject>>, + ) { + return (props: P) => Effect.runSyncWith(contextRef.current)( + Effect.andThen( + useScope([], this), + scope => Effect.provideService(this.body(props), Scope.Scope, scope), + ) + ) + }, + + setFunctionComponentName

( + this: ComponentImpl, + f: React.FC

, + ) { + f.displayName = this.displayName ?? "Anonymous" + }, + + transformFunctionComponent: identity, +} as const) + +const use = Effect.fnUntraced(function*

( + self: ComponentImpl +) { + // biome-ignore lint/style/noNonNullAssertion: React ref initialization + const contextRef = React.useRef>>(null!) + contextRef.current = yield* Effect.context>() + + return yield* React.useState(() => Effect.runSyncWith(contextRef.current)(Effect.cachedFunction( + (_services: readonly any[]) => Effect.sync(() => { + const f = self.asFunctionComponent(contextRef) + self.setFunctionComponentName(f) + return self.transformFunctionComponent(f) + }), + Equivalence.array(Equivalence.strictEqual()), + )))[0](Array.from( + Context.omit(...self.nonReactiveTags)(contextRef.current).mapUnsafe.values() + )) +}) + + +export interface ComponentPrototype +extends Pipeable.Pipeable { + readonly [ComponentTypeId]: ComponentTypeId + readonly use: Effect.Effect> +} + +export const ComponentPrototype: ComponentPrototype = Object.freeze( + Object.defineProperties( + { + [ComponentTypeId]: ComponentTypeId, + ...Pipeable.Prototype, + }, + Object.getOwnPropertyDescriptors(ComponentImplPrototype), + ) as ComponentPrototype +) + + export interface ComponentOptions { + /** + * Custom display name for the component in React DevTools and debugging utilities. + */ readonly displayName?: string + + /** + * Context tags that should not trigger component remount when their values change. + * + * @default [Tracer.ParentSpan] + */ readonly nonReactiveTags: readonly Context.Key[] - readonly finalizerExecutionStrategy: "sequential" | "parallel" + + /** + * Specifies the execution strategy for finalizers when the component unmounts or its scope closes. + * Determines whether finalizers execute sequentially or in parallel. + * + * @default ExecutionStrategy.sequential + */ + readonly finalizerExecutionStrategy: ExecutionStrategy.ExecutionStrategy + + /** + * Debounce duration before executing finalizers after component unmount. + * Prevents unnecessary cleanup work during rapid remount/unmount cycles, + * which is common in development and certain UI patterns. + * + * @default "100 millis" + */ readonly finalizerExecutionDebounce: Duration.Input } export const defaultOptions: ComponentOptions = { nonReactiveTags: [Tracer.ParentSpan], - finalizerExecutionStrategy: "sequential", + finalizerExecutionStrategy: ExecutionStrategy.sequential, finalizerExecutionDebounce: "100 millis", } -export interface ComponentPrototype extends Pipeable.Pipeable { - readonly [ComponentTypeId]: ComponentTypeId - readonly use: Effect.Effect> -} -type ComponentImpl = Component +export const isComponent = (u: unknown): u is Component.Default<{}, React.ReactNode, unknown, unknown> => Predicate.hasProperty(u, ComponentTypeId) -const makeFunctionComponent = ( - self: ComponentImpl, - contextRef: React.RefObject>, -): Component.Signature => { - if ("asFunctionComponent" in self && typeof self.asFunctionComponent === "function") { - return self.asFunctionComponent(contextRef) +export declare namespace make { + export type Gen = { + >, A extends React.ReactNode, P extends {} = {}>( + body: (props: P) => Generator + ): Component.Default< + P, A, + [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, + [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never + > + >, A, B extends Effect.Effect, P extends {} = {}>( + body: (props: P) => Generator, + a: ( + _: Effect.Effect< + A, + [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, + [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never + >, + props: NoInfer

, + ) => B, + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + >, A, B, C extends Effect.Effect, P extends {} = {}>( + body: (props: P) => Generator, + a: ( + _: Effect.Effect< + A, + [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, + [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never + >, + props: NoInfer

, + ) => B, + b: (_: B, props: NoInfer

) => C, + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + >, A, B, C, D extends Effect.Effect, P extends {} = {}>( + body: (props: P) => Generator, + a: ( + _: Effect.Effect< + A, + [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, + [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never + >, + props: NoInfer

, + ) => B, + b: (_: B, props: NoInfer

) => C, + c: (_: C, props: NoInfer

) => D, + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + >, A, B, C, D, E extends Effect.Effect, P extends {} = {}>( + body: (props: P) => Generator, + a: ( + _: Effect.Effect< + A, + [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, + [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never + >, + props: NoInfer

, + ) => B, + b: (_: B, props: NoInfer

) => C, + c: (_: C, props: NoInfer

) => D, + d: (_: D, props: NoInfer

) => E, + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + >, A, B, C, D, E, F extends Effect.Effect, P extends {} = {}>( + body: (props: P) => Generator, + a: ( + _: Effect.Effect< + A, + [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, + [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never + >, + props: NoInfer

, + ) => B, + b: (_: B, props: NoInfer

) => C, + c: (_: C, props: NoInfer

) => D, + d: (_: D, props: NoInfer

) => E, + e: (_: E, props: NoInfer

) => F, + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + >, A, B, C, D, E, F, G extends Effect.Effect, P extends {} = {}>( + body: (props: P) => Generator, + a: ( + _: Effect.Effect< + A, + [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, + [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never + >, + props: NoInfer

, + ) => B, + b: (_: B, props: NoInfer

) => C, + c: (_: C, props: NoInfer

) => D, + d: (_: D, props: NoInfer

) => E, + e: (_: E, props: NoInfer

) => F, + f: (_: F, props: NoInfer

) => G, + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + >, A, B, C, D, E, F, G, H extends Effect.Effect, P extends {} = {}>( + body: (props: P) => Generator, + a: ( + _: Effect.Effect< + A, + [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, + [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never + >, + props: NoInfer

, + ) => B, + b: (_: B, props: NoInfer

) => C, + c: (_: C, props: NoInfer

) => D, + d: (_: D, props: NoInfer

) => E, + e: (_: E, props: NoInfer

) => F, + f: (_: F, props: NoInfer

) => G, + g: (_: G, props: NoInfer

) => H, + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + >, A, B, C, D, E, F, G, H, I extends Effect.Effect, P extends {} = {}>( + body: (props: P) => Generator, + a: ( + _: Effect.Effect< + A, + [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, + [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never + >, + props: NoInfer

, + ) => B, + b: (_: B, props: NoInfer

) => C, + c: (_: C, props: NoInfer

) => D, + d: (_: D, props: NoInfer

) => E, + e: (_: E, props: NoInfer

) => F, + f: (_: F, props: NoInfer

) => G, + g: (_: G, props: NoInfer

) => H, + h: (_: H, props: NoInfer

) => I, + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + >, A, B, C, D, E, F, G, H, I, J extends Effect.Effect, P extends {} = {}>( + body: (props: P) => Generator, + a: ( + _: Effect.Effect< + A, + [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, + [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never + >, + props: NoInfer

, + ) => B, + b: (_: B, props: NoInfer

) => C, + c: (_: C, props: NoInfer

) => D, + d: (_: D, props: NoInfer

) => E, + e: (_: E, props: NoInfer

) => F, + f: (_: F, props: NoInfer

) => G, + g: (_: G, props: NoInfer

) => H, + h: (_: H, props: NoInfer

) => I, + i: (_: I, props: NoInfer

) => J, + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> } - const FunctionComponent = (props: {}) => Effect.runSyncWith(contextRef.current)( - Effect.flatMap( - useScope([], self), - scope => Effect.provideService(self.body(props), Scope.Scope, scope), - ), - ) - FunctionComponent.displayName = self.displayName ?? "Anonymous" - return "transformFunctionComponent" in self && typeof self.transformFunctionComponent === "function" - ? self.transformFunctionComponent(FunctionComponent) - : FunctionComponent -} -const use = Effect.fnUntraced(function* (self: ComponentImpl) { - const context = yield* Effect.context() - const cached = componentCache.get(self) - if (cached !== undefined) { - cached.contextRef.current = context - return cached.component + export type NonGen = { + , P extends {} = {}>( + body: (props: P) => Eff + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + , A, P extends {} = {}>( + body: (props: P) => A, + a: (_: A, props: NoInfer

) => Eff, + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + , A, B, P extends {} = {}>( + body: (props: P) => A, + a: (_: A, props: NoInfer

) => B, + b: (_: B, props: NoInfer

) => Eff, + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + , A, B, C, P extends {} = {}>( + body: (props: P) => A, + a: (_: A, props: NoInfer

) => B, + b: (_: B, props: NoInfer

) => C, + c: (_: C, props: NoInfer

) => Eff, + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + , A, B, C, D, P extends {} = {}>( + body: (props: P) => A, + a: (_: A, props: NoInfer

) => B, + b: (_: B, props: NoInfer

) => C, + c: (_: C, props: NoInfer

) => D, + d: (_: D, props: NoInfer

) => Eff, + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + , A, B, C, D, E, P extends {} = {}>( + body: (props: P) => A, + a: (_: A, props: NoInfer

) => B, + b: (_: B, props: NoInfer

) => C, + c: (_: C, props: NoInfer

) => D, + d: (_: D, props: NoInfer

) => E, + e: (_: E, props: NoInfer

) => Eff, + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + , A, B, C, D, E, F, P extends {} = {}>( + body: (props: P) => A, + a: (_: A, props: NoInfer

) => B, + b: (_: B, props: NoInfer

) => C, + c: (_: C, props: NoInfer

) => D, + d: (_: D, props: NoInfer

) => E, + e: (_: E, props: NoInfer

) => F, + f: (_: F, props: NoInfer

) => Eff, + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + , A, B, C, D, E, F, G, P extends {} = {}>( + body: (props: P) => A, + a: (_: A, props: NoInfer

) => B, + b: (_: B, props: NoInfer

) => C, + c: (_: C, props: NoInfer

) => D, + d: (_: D, props: NoInfer

) => E, + e: (_: E, props: NoInfer

) => F, + f: (_: F, props: NoInfer

) => G, + g: (_: G, props: NoInfer

) => Eff, + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + , A, B, C, D, E, F, G, H, P extends {} = {}>( + body: (props: P) => A, + a: (_: A, props: NoInfer

) => B, + b: (_: B, props: NoInfer

) => C, + c: (_: C, props: NoInfer

) => D, + d: (_: D, props: NoInfer

) => E, + e: (_: E, props: NoInfer

) => F, + f: (_: F, props: NoInfer

) => G, + g: (_: G, props: NoInfer

) => H, + h: (_: H, props: NoInfer

) => Eff, + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + , A, B, C, D, E, F, G, H, I, P extends {} = {}>( + body: (props: P) => A, + a: (_: A, props: NoInfer

) => B, + b: (_: B, props: NoInfer

) => C, + c: (_: C, props: NoInfer

) => D, + d: (_: D, props: NoInfer

) => E, + e: (_: E, props: NoInfer

) => F, + f: (_: F, props: NoInfer

) => G, + g: (_: G, props: NoInfer

) => H, + h: (_: H, props: NoInfer

) => I, + i: (_: I, props: NoInfer

) => Eff, + ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> } - const contextRef = { current: context } - const component = makeFunctionComponent(self, contextRef) - componentCache.set(self, { contextRef, component }) - return component -}) - -const componentCache = new WeakMap } - readonly component: Component.Signature -}>() - -export const ComponentPrototype = Object.freeze({ - [ComponentTypeId]: ComponentTypeId, - ...Pipeable.Prototype, - get use() { - return use(this as ComponentImpl) - }, -}) as unknown as ComponentPrototype - -export const isComponent = (u: unknown): u is Component.Any => Predicate.hasProperty(u, ComponentTypeId) - -type GeneratorBody

= ( - props: P, -) => Effect.fn.Return - -type EffectBody

= ( - props: P, -) => Effect.Effect - -export interface Make { -

( - body: GeneratorBody | EffectBody, - ...pipeables: readonly Function[] - ): Component.Default - (name: string, options?: Tracer.SpanOptionsNoTrace):

( - body: GeneratorBody | EffectBody, - ...pipeables: readonly Function[] - ) => Component.Default } -const component = ( - body: Function, - displayName: string | undefined, - traced: boolean, - pipeables: readonly Function[], -): Component.Any => Object.setPrototypeOf( - Object.assign(function() {}, defaultOptions, { - body: traced && displayName - ? Effect.fn(displayName)(body as never, ...pipeables as []) - : Effect.fnUntraced(body as never, ...pipeables as []), - displayName, - }), - ComponentPrototype, +/** + * Creates an Effect-FC Component using the same overloads and pipeline composition style as `Effect.fn`. + * + * This is the **recommended** approach for defining Effect-FC components. It provides comprehensive + * support for multiple component definition patterns: + * + * - **Generator syntax** (yield* style): Most ergonomic and readable approach for sequential operations + * - **Direct Effect return**: For simple components that return an Effect directly + * - **Chained transformation functions**: Enables Effect.fn-style pipelines for composable transformations + * - **Automatic tracing**: Optional tracing span creation with automatic `displayName` assignment + * + * When a `spanName` string is provided, the following occurs automatically: + * 1. A distributed tracing span is created with the specified name + * 2. The resulting React component receives `displayName = spanName` for DevTools visibility + * + * @example + * ```tsx + * const MyComponent = Component.make("MyComponent")(function* (props: { count: number }) { + * const value = yield* someEffect + * return

+ * }) + * ``` + * + * @example As an opaque type using class syntax + * ```tsx + * class MyComponent extends Component.make("MyComponent")(function* (props: { count: number }) { + * const value = yield* someEffect + * return
{value}
+ * }) {} + * ``` + * + * @example Without name + * ```tsx + * class MyComponent extends Component.make(function* (props: { count: number }) { + * const value = yield* someEffect + * return
{value}
+ * }) {} + * ``` + * + * @example Using pipeline + * ```tsx + * class MyComponent extends Component.make("MyComponent")( + * (props: { count: number }) => someEffect, + * Effect.map(value =>
{value}
), + * ) {} + * ``` + */ +export const make: ( + & make.Gen + & make.NonGen + & (( + spanName: string, + spanOptions?: Tracer.SpanOptions, + ) => make.Gen & make.NonGen) +) = (spanNameOrBody: Function | string, ...pipeables: any[]): any => { + if (typeof spanNameOrBody !== "string") { + return Object.setPrototypeOf( + Object.assign(function() {}, defaultOptions, { + body: Effect.fn(spanNameOrBody as any, ...pipeables), + }), + ComponentPrototype, + ) + } + else { + const spanOptions = pipeables[0] + return (body: any, ...pipeables: any[]) => Object.setPrototypeOf( + Object.assign(function() {}, defaultOptions, { + body: Effect.fn(spanNameOrBody, spanOptions)(body, ...pipeables as []), + displayName: spanNameOrBody, + }), + ComponentPrototype, + ) + } +} + +/** + * Creates an Effect-FC Component without automatic distributed tracing. + * + * This function provides the same API surface as `make`, but does not create automatic tracing spans. + * It follows the exact same overload structure as `Effect.fnUntraced`. + * + * Use this variant when you need: + * - Full manual control over tracing instrumentation + * - To reduce tracing overhead in deeply nested component hierarchies + * - To avoid span noise in performance-sensitive applications + * + * When a `spanName` string is provided, it is used **exclusively** as the React component's + * `displayName` for DevTools identification. No tracing span is created. + * + * @example + * ```tsx + * const MyComponent = Component.makeUntraced("MyComponent")(function* (props: { count: number }) { + * const value = yield* someEffect + * return
{value}
+ * }) + * ``` + * + * @example As an opaque type using class syntax + * ```tsx + * class MyComponent extends Component.makeUntraced("MyComponent")(function* (props: { count: number }) { + * const value = yield* someEffect + * return
{value}
+ * }) {} + * ``` + * + * @example Without name + * ```tsx + * class MyComponent extends Component.makeUntraced(function* (props: { count: number }) { + * const value = yield* someEffect + * return
{value}
+ * }) {} + * ``` + * + * @example Using pipeline + * ```tsx + * class MyComponent extends Component.makeUntraced("MyComponent")( + * (props: { count: number }) => someEffect, + * Effect.map(value =>
{value}
), + * ) {} + * ``` + */ +export const makeUntraced: ( + & make.Gen + & make.NonGen + & ((name: string) => make.Gen & make.NonGen) +) = (spanNameOrBody: Function | string, ...pipeables: any[]): any => ( + typeof spanNameOrBody !== "string" + ? Object.setPrototypeOf( + Object.assign(function() {}, defaultOptions, { + body: Effect.fnUntraced(spanNameOrBody as any, ...pipeables as []), + }), + ComponentPrototype, + ) + : (body: any, ...pipeables: any[]) => Object.setPrototypeOf( + Object.assign(function() {}, defaultOptions, { + body: Effect.fnUntraced(body, ...pipeables as []), + displayName: spanNameOrBody, + }), + ComponentPrototype, + ) ) -export const make: Make = ((nameOrBody: string | Function, ...args: readonly unknown[]) => { - if (typeof nameOrBody === "string") { - return (body: Function, ...pipeables: readonly Function[]) => component(body, nameOrBody, true, pipeables) - } - return component(nameOrBody, undefined, true, args as readonly Function[]) -}) as Make - -export const makeUntraced: Make = ((nameOrBody: string | Function, ...args: readonly unknown[]) => { - if (typeof nameOrBody === "string") { - return (body: Function, ...pipeables: readonly Function[]) => component(body, nameOrBody, false, pipeables) - } - return component(nameOrBody, undefined, false, args as readonly Function[]) -}) as Make - export declare namespace withSignature { export type Result = ( & Omit> @@ -166,113 +528,266 @@ export declare namespace withSignature { } export const withSignature: { - (): (self: T) => withSignature.Result - (self: T): withSignature.Result -} = (self?: Component.Any): any => self === undefined ? identity : self + (): ( + self: T + ) => withSignature.Result + ( + self: T + ): withSignature.Result +} = (self?: Component.Any): any => self === undefined + ? identity + : self +/** + * Creates a new component with modified configuration options while preserving all original behavior. + * + * This function allows you to customize component-level options such as finalizer execution strategy + * and debounce timing. + * + * @example + * ```tsx + * const MyComponentWithCustomOptions = MyComponent.pipe( + * Component.withOptions({ + * finalizerExecutionStrategy: ExecutionStrategy.parallel, + * finalizerExecutionDebounce: "50 millis", + * }) + * ) + * ``` + */ export const withOptions: { - (options: Partial): (self: T) => T - (self: T, options: Partial): T -} = Function.dual(2, (self: T, options: Partial): T => Object.setPrototypeOf( + ( + options: Partial + ): (self: T) => T + ( + self: T, + options: Partial, + ): T +} = Function.dual(2, ( + self: T, + options: Partial, +): T => Object.setPrototypeOf( Object.assign(function() {}, self, options), Object.getPrototypeOf(self), )) +/** + * Wraps an Effect-FC Component and converts it into a standard React function component, + * serving as an **entrypoint** into an Effect-FC component hierarchy. + * + * This is how Effect-FC components are integrated with the broader React ecosystem, + * particularly when: + * - Using client-side routers (TanStack Router, React Router, etc.) + * - Implementing lazy-loaded or code-split routes + * - Connecting to third-party libraries expecting standard React components + * - Creating component boundaries between Effect-FC and non-Effect-FC code + * + * The Effect runtime is obtained from the provided React Context. + * + * @param self - The Effect-FC Component to be rendered as a standard React component + * @param context - React Context providing the Effect Runtime for this component tree. + * Create this using the `ReactRuntime` module. + * + * @example Integration with TanStack Router + * ```tsx + * // Application root + * export const runtime = ReactRuntime.make(Layer.empty) + * + * function App() { + * return ( + * + * + * + * ) + * } + * + * // Route definition + * export const Route = createFileRoute("/")({ + * component: Component.withRuntime(HomePage, runtime.context) + * }) + * ``` + * + */ export const withRuntime: {

( - context: React.Context>, + context: React.Context>, ): (self: Component, F>) => F

( self: Component, F>, - context: React.Context>, + context: React.Context>, ): F } = Function.dual(2,

( - self: Component, - context: React.Context>, + self: Component, + context: React.Context>, ) => function WithRuntime(props: P) { return React.createElement( - Effect.runSyncWith(React.useContext(context))(self.use) as React.FC

, + Runtime.runSync(React.useContext(context))(self.use) as React.FC

, props, ) }) + +/** + * Internal Effect service that maintains a registry of scopes associated with React component instances. + * + * This service is used internally by the `useScope` hook to manage the lifecycle of component scopes, + * including tracking active scopes and coordinating their cleanup when components unmount or dependencies change. + */ +export class ScopeMap extends Context.Service> +}>()( + "@effect-fc/Component/ScopeMap" +) { + static readonly layer = Layer.effect(ScopeMap, Effect.map( + Ref.make(HashMap.empty()), + ref => ({ ref }), + )) +} + +export declare namespace ScopeMap { + export interface Entry { + readonly scope: Scope.Closeable + readonly closeFiber: Option.Option> + } +} + + export declare namespace useScope { export interface Options { - readonly finalizerExecutionStrategy?: "sequential" | "parallel" + readonly finalizerExecutionStrategy?: ExecutionStrategy.ExecutionStrategy readonly finalizerExecutionDebounce?: Duration.Input } } -export const useScope = Effect.fnUntraced(function* ( +/** + * Effect hook that creates and manages a `Scope` for the current component instance. + * + * This hook establishes a new scope that is automatically closed when: + * - The component unmounts + * - The dependency array `deps` changes + * + * The scope provides a resource management boundary for any Effects executed within the component, + * ensuring proper cleanup of resources and execution of finalizers. + * + * @param deps - Dependency array following React.useEffect semantics. The scope is recreated + * whenever any dependency changes. + * @param options - Configuration for finalizer execution behavior, including execution strategy + * and debounce timing. + * + * @returns An Effect that produces a `Scope` for resource management +*/ +export const useScope = Effect.fnUntraced(function*( deps: React.DependencyList, options?: useScope.Options, ): Effect.fn.Return { - const context = yield* Effect.context() - const contextRef = React.useRef(context) - contextRef.current = context - const scope = React.useMemo( - () => Scope.makeUnsafe(options?.finalizerExecutionStrategy ?? defaultOptions.finalizerExecutionStrategy), - // biome-ignore lint/correctness/useExhaustiveDependencies: caller controls scope lifetime - deps, - ) + // biome-ignore lint/style/noNonNullAssertion: context initialization + const contextRef = React.useRef>(null!) + contextRef.current = yield* Effect.context() - React.useEffect(() => { - const pending = scopeCleanupTimers.get(scope) - if (pending !== undefined) clearTimeout(pending) - return () => { - const timer = setTimeout(() => { - Effect.runSyncWith(contextRef.current)(Scope.close(scope, Exit.succeed(undefined))) - scopeCleanupTimers.delete(scope) - }, durationMillis(options?.finalizerExecutionDebounce ?? defaultOptions.finalizerExecutionDebounce)) - scopeCleanupTimers.set(scope, timer) - } - }, [scope, options?.finalizerExecutionDebounce]) + const { key, scope } = React.useMemo(() => Effect.runSyncWith(contextRef.current)(Effect.Do.pipe( + Effect.bind("scopeMapRef", () => Effect.map( + ScopeMap as unknown as Effect.Effect, + scopeMap => scopeMap.ref, + )), + Effect.let("key", () => ({})), + Effect.bind("scope", () => Scope.make(options?.finalizerExecutionStrategy ?? defaultOptions.finalizerExecutionStrategy)), + Effect.tap(({ scopeMapRef, key, scope }) => + Ref.update(scopeMapRef, HashMap.set(key, { + scope, + closeFiber: Option.none(), + })) + ), + // biome-ignore lint/correctness/useExhaustiveDependencies: use of React.DependencyList + )), deps) + + // biome-ignore lint/correctness/useExhaustiveDependencies: only reactive on "key" + React.useEffect(() => Effect.runSyncWith(contextRef.current)( + (ScopeMap as unknown as Effect.Effect).pipe( + Effect.map(scopeMap => scopeMap.ref), + Effect.tap(ref => Ref.get(ref).pipe( + Effect.flatMap(map => Effect.fromOption(HashMap.get(map, key))), + Effect.flatMap(entry => Option.match(entry.closeFiber, { + onSome: fiber => Effect.forkDetach(Fiber.interrupt(fiber)), + onNone: () => Effect.void, + })), + )), + Effect.map(ref => + () => Effect.runSyncWith(contextRef.current)(Effect.flatMap( + Effect.sleep(options?.finalizerExecutionDebounce ?? defaultOptions.finalizerExecutionDebounce).pipe( + Effect.andThen(Scope.close(scope, Exit.void)), + Effect.onExit(() => Ref.update(ref, HashMap.remove(key))), + Effect.forkDetach, + ), + fiber => Ref.update(ref, HashMap.set(key, { + scope, + closeFiber: Option.some(fiber), + })), + )) + ), + ) + ), [key]) return scope }) -const scopeCleanupTimers = new WeakMap>() - -const durationMillis = (input: Duration.Input): number => { - if (typeof input === "number") return input - return Number(Effect.runSync(Effect.map(Effect.succeed(input), value => { - const match = typeof value === "string" ? /([\d.]+)\s*(millis|seconds?)/.exec(value) : undefined - if (!match) return 0 - return Number(match[1]) * (match[2].startsWith("second") ? 1_000 : 1) - }))) -} - +/** + * Effect hook that executes an Effect once when the component mounts and caches the result. + * + * This hook is useful for one-time initialization logic that should not be re-executed + * when the component re-renders. The Effect is executed exactly once during the component's + * initial mount, and the cached result is returned on all subsequent renders. + * + * @param f - A function that returns the Effect to execute on mount + * + * @returns An Effect that produces the cached result of the Effect + * + * @example + * ```tsx + * const MyComponent = Component.make(function*() { + * const initialData = yield* Component.useOnMount(() => getData) + * return

{initialData}
+ * }) + * ``` + */ export const useOnMount = Effect.fnUntraced(function* ( - f: () => Effect.Effect, + f: () => Effect.Effect ): Effect.fn.Return { const context = yield* Effect.context() - const id = React.useId() - let cached = mountCache.get(id) - if (cached === undefined) { - cached = { - effect: Effect.runSyncWith(context)(Effect.cached(f())), - } - mountCache.set(id, cached) - } - React.useEffect(() => { - if (cached?.cleanup !== undefined) clearTimeout(cached.cleanup) - const entry = cached - return () => { - entry.cleanup = setTimeout(() => mountCache.delete(id), 0) - } - }, [id, cached]) - return yield* cached.effect as Effect.Effect + return yield* React.useState(() => Effect.runSyncWith(context)(Effect.cached(f())))[0] }) -const mountCache = new Map - cleanup?: ReturnType -}>() - export declare namespace useOnChange { export interface Options extends useScope.Options {} } +/** + * Effect hook that executes an Effect whenever dependencies change and caches the result. + * + * This hook combines the dependency-tracking behavior of React.useEffect with Effect caching. + * The Effect is re-executed whenever any dependency in the `deps` array changes, and the result + * is cached until the next dependency change. + * + * A dedicated scope is created for each dependency change, ensuring proper resource cleanup: + * - The scope closes when dependencies change + * - The scope closes when the component unmounts + * - All finalizers are executed according to the configured execution strategy + * + * @param f - A function that returns the Effect to execute + * @param deps - Dependency array following React.useEffect semantics + * @param options - Configuration for scope and finalizer behavior + * + * @returns An Effect that produces the cached result of the Effect + * + * @example + * ```tsx + * const MyComponent = Component.make(function* (props: { userId: string }) { + * const userData = yield* Component.useOnChange( + * getUser(props.userId), + * [props.userId], + * ) + * return
{userData.name}
+ * }) + * ``` + */ export const useOnChange = Effect.fnUntraced(function* ( f: () => Effect.Effect, deps: React.DependencyList, @@ -280,91 +795,264 @@ export const useOnChange = Effect.fnUntraced(function* ( ): Effect.fn.Return> { const context = yield* Effect.context>() const scope = yield* useScope(deps, options) - const cached = - // biome-ignore lint/correctness/useExhaustiveDependencies: scope tracks the caller-provided dependency list - React.useMemo( - () => Effect.runSyncWith(context)(Effect.cached(Effect.provideService(f(), Scope.Scope, scope))), - [scope], - ) - return yield* cached + + // biome-ignore lint/correctness/useExhaustiveDependencies: only reactive on "scope" + return yield* React.useMemo(() => Effect.runSyncWith(context)( + Effect.cached(Effect.provideService(f(), Scope.Scope, scope)) + ), [scope]) }) export declare namespace useReactEffect { export interface Options { readonly finalizerExecutionMode?: "sync" | "fork" - readonly finalizerExecutionStrategy?: "sequential" | "parallel" - } -} - -const runReactEffect = ( - context: Context.Context>, - f: () => Effect.Effect, - options?: useReactEffect.Options, -): (() => void) => { - const scope = Scope.makeUnsafe(options?.finalizerExecutionStrategy ?? defaultOptions.finalizerExecutionStrategy) - Effect.runSyncWith(context)(Effect.exit(Effect.provideService(f(), Scope.Scope, scope))) - return () => { - const close = Scope.close(scope, Exit.succeed(undefined)) - if ((options?.finalizerExecutionMode ?? "fork") === "sync") Effect.runSyncWith(context)(close) - else Effect.runForkWith(context)(close) + readonly finalizerExecutionStrategy?: ExecutionStrategy.ExecutionStrategy } } +/** + * Effect hook that provides Effect-based semantics for React.useEffect. + * + * This hook bridges React's useEffect with the Effect system, allowing you to use Effects + * for React side effects while maintaining React's dependency tracking and lifecycle semantics. + * + * Unlike React.useEffect which uses imperative cleanup functions, this hook leverages the + * Effect Scope API for resource management. Cleanup logic is expressed declaratively through + * finalizers registered with the scope, providing better composability and error handling. + * + * @param f - A function that returns an Effect to execute as a side effect + * @param deps - Optional dependency array following React.useEffect semantics. + * If omitted, the effect runs after every render. + * @param options - Configuration for finalizer execution mode (sync or fork) and strategy + * + * @returns An Effect that produces void + * + * @example + * ```tsx + * const MyComponent = Component.make(function* (props: { id: string }) { + * yield* Component.useReactEffect( + * () => getNotificationStreamForUser(props.id).pipe( + * Stream.unwrap, + * Stream.runForEach(notification => Console.log(`Notification received: ${ notification }`), + * Effect.forkScoped, + * ), + * [props.id], + * ) + * return
Subscribed to notifications for {props.id}
+ * }) + * ``` + */ export const useReactEffect = Effect.fnUntraced(function* ( f: () => Effect.Effect, deps?: React.DependencyList, options?: useReactEffect.Options, ): Effect.fn.Return> { const context = yield* Effect.context>() - // biome-ignore lint/correctness/useExhaustiveDependencies: caller supplies React dependencies + // biome-ignore lint/correctness/useExhaustiveDependencies: use of React.DependencyList React.useEffect(() => runReactEffect(context, f, options), deps) }) +const runReactEffect = ( + context: Context.Context>, + f: () => Effect.Effect, + options?: useReactEffect.Options, +) => Effect.Do.pipe( + Effect.bind("scope", () => Scope.make(options?.finalizerExecutionStrategy ?? defaultOptions.finalizerExecutionStrategy)), + Effect.bind("exit", ({ scope }) => Effect.exit(Effect.provideService(f(), Scope.Scope, scope))), + Effect.map(({ scope }) => + () => { + switch (options?.finalizerExecutionMode ?? "fork") { + case "sync": + Effect.runSyncWith(context)(Scope.close(scope, Exit.void)) + break + case "fork": + Effect.runForkWith(context)(Scope.close(scope, Exit.void)) + break + } + } + ), + Effect.runSyncWith(context), +) + export declare namespace useReactLayoutEffect { export interface Options extends useReactEffect.Options {} } +/** + * Effect hook that provides Effect-based semantics for React.useLayoutEffect. + * + * This hook is identical to `useReactEffect` but executes synchronously after DOM mutations + * but before the browser paints, following React.useLayoutEffect semantics. + * + * Use this hook when you need to: + * - Measure DOM elements (e.g., for layout calculations) + * - Synchronously update state based on DOM measurements + * - Avoid visual flicker from asynchronous updates + * + * Like `useReactEffect`, cleanup logic is handled through the Effect Scope API rather than + * imperative cleanup functions, providing declarative and composable resource management. + * + * @param f - A function that returns an Effect to execute as a layout side effect + * @param deps - Optional dependency array following React.useLayoutEffect semantics. + * If omitted, the effect runs after every render. + * @param options - Configuration for finalizer execution mode (sync or fork) and strategy + * + * @returns An Effect that produces void + * + * @example + * ```tsx + * const MyComponent = Component.make(function*() { + * const ref = React.useRef(null) + * yield* Component.useReactLayoutEffect( + * () => Effect.gen(function* () { + * const element = ref.current + * if (element) { + * const rect = element.getBoundingClientRect() + * yield* Console.log(`Element dimensions: ${ rect.width }x${ rect.height }`) + * } + * }), + * [], + * ) + * return
Content
+ * }) + * ``` + */ export const useReactLayoutEffect = Effect.fnUntraced(function* ( f: () => Effect.Effect, deps?: React.DependencyList, options?: useReactLayoutEffect.Options, ): Effect.fn.Return> { const context = yield* Effect.context>() - // biome-ignore lint/correctness/useExhaustiveDependencies: caller supplies React dependencies + // biome-ignore lint/correctness/useExhaustiveDependencies: use of React.DependencyList React.useLayoutEffect(() => runReactEffect(context, f, options), deps) }) +/** + * Effect hook that provides a synchronous function to execute Effects within the current runtime context. + * + * This hook returns a function that can execute Effects synchronously, blocking until completion. + * Use this when you need to run Effects from non-Effect code (e.g., event handlers, callbacks) + * within a component. + * + * @returns An Effect that produces a function capable of synchronously executing Effects + * + * @example + * ```tsx + * const MyComponent = Component.make(function*() { + * const runSync = yield* Component.useRunSync() // Specify required services + * const runSync = yield* Component.useRunSync() // Or no service requirements + * + * return + * }) + * ``` + */ export const useRunSync = (): Effect.Effect< - (effect: Effect.Effect) => A, + (effect: Effect.Effect) => A, never, - R -> => Effect.map(Effect.context(), Effect.runSyncWith) + Scope.Scope | R +> => Effect.map(Effect.context(), Effect.runSyncWith) +/** + * Effect hook that provides an asynchronous function to execute Effects within the current runtime context. + * + * This hook returns a function that executes Effects asynchronously, returning a Promise that resolves + * with the Effect's result. Use this when you need to run Effects from non-Effect code (e.g., event handlers, + * async callbacks) and want to handle the result asynchronously. + * + * @returns An Effect that produces a function capable of asynchronously executing Effects + * + * @example + * ```tsx + * const MyComponent = Component.make(function*() { + * const runPromise = yield* Component.useRunPromise() // Specify required services + * const runPromise = yield* Component.useRunPromise() // Or no service requirements + * + * return + * }) + * ``` + */ export const useRunPromise = (): Effect.Effect< - (effect: Effect.Effect) => Promise
, + (effect: Effect.Effect) => Promise, never, - R -> => Effect.map(Effect.context(), Effect.runPromiseWith) + Scope.Scope | R +> => Effect.map(Effect.context(), Effect.runPromiseWith) +/** + * Effect hook that memoizes a function that returns an Effect, providing synchronous execution. + * + * This hook wraps a function that returns an Effect and returns a memoized version that: + * - Executes the Effect synchronously when called + * - Is memoized based on the provided dependency array + * - Maintains referential equality across renders when dependencies don't change + * + * Use this to create stable callback references for event handlers and other scenarios + * where you need to execute Effects synchronously from non-Effect code. + * + * @param f - A function that accepts arguments and returns an Effect + * @param deps - Dependency array. The memoized function is recreated when dependencies change. + * + * @returns An Effect that produces a memoized function with the same signature as `f` + * + * @example + * ```tsx + * const MyComponent = Component.make(function* (props: { onSave: (data: Data) => void }) { + * const handleSave = yield* Component.useCallbackSync( + * (data: Data) => Effect.sync(() => props.onSave(data)), + * [props.onSave], + * ) + * + * return + * }) + * ``` + */ export const useCallbackSync = Effect.fnUntraced(function* ( f: (...args: Args) => Effect.Effect, deps: React.DependencyList, ): Effect.fn.Return<(...args: Args) => A, never, R> { - const context = yield* Effect.context() - const contextRef = React.useRef(context) - contextRef.current = context - // biome-ignore lint/correctness/useExhaustiveDependencies: caller supplies React dependencies + // biome-ignore lint/style/noNonNullAssertion: context initialization + const contextRef = React.useRef>(null!) + contextRef.current = yield* Effect.context() + + // biome-ignore lint/correctness/useExhaustiveDependencies: use of React.DependencyList return React.useCallback((...args: Args) => Effect.runSyncWith(contextRef.current)(f(...args)), deps) }) +/** + * Effect hook that memoizes a function that returns an Effect, providing asynchronous execution. + * + * This hook wraps a function that returns an Effect and returns a memoized version that: + * - Executes the Effect asynchronously when called, returning a Promise + * - Is memoized based on the provided dependency array + * - Maintains referential equality across renders when dependencies don't change + * + * Use this to create stable callback references for async event handlers and other scenarios + * where you need to execute Effects asynchronously from non-Effect code. + * + * @param f - A function that accepts arguments and returns an Effect + * @param deps - Dependency array. The memoized function is recreated when dependencies change. + * + * @returns An Effect that produces a memoized function that returns a Promise + * + * @example + * ```tsx + * const MyComponent = Component.make(function* (props: { onSave: (data: Data) => void }) { + * const handleSave = yield* Component.useCallbackPromise( + * (data: Data) => Effect.promise(() => props.onSave(data)), + * [props.onSave], + * ) + * + * return + * }) + * ``` + */ export const useCallbackPromise = Effect.fnUntraced(function* ( f: (...args: Args) => Effect.Effect, deps: React.DependencyList, ): Effect.fn.Return<(...args: Args) => Promise, never, R> { - const context = yield* Effect.context() - const contextRef = React.useRef(context) - contextRef.current = context - // biome-ignore lint/correctness/useExhaustiveDependencies: caller supplies React dependencies + // biome-ignore lint/style/noNonNullAssertion: context initialization + const contextRef = React.useRef>(null!) + contextRef.current = yield* Effect.context() + + // biome-ignore lint/correctness/useExhaustiveDependencies: use of React.DependencyList return React.useCallback((...args: Args) => Effect.runPromiseWith(contextRef.current)(f(...args)), deps) }) @@ -372,14 +1060,71 @@ export declare namespace useContext { export interface Options extends useOnChange.Options {} } +/** + * Effect hook that constructs an Effect Layer and returns the resulting context. + * + * This hook creates a managed runtime from the provided layer and returns the context it produces. + * The layer is reconstructed whenever its value changes, so ensure the layer reference is stable + * (typically by memoizing it or defining it outside the component). + * + * The hook automatically manages the layer's lifecycle: + * - The layer is built when the component mounts or when the layer reference changes + * - Resources are properly released when the component unmounts or dependencies change + * - Finalizers are executed according to the configured execution strategy + * + * @param layer - The Effect Layer to construct. Should be a stable reference to avoid unnecessary + * reconstruction. Consider memoizing with React.useMemo if defined inline. + * @param options - Configuration for scope and finalizer behavior + * + * @returns An Effect that produces the context created by the layer + * + * @throws If the layer contains asynchronous effects, the component must be wrapped with `Async.async` + * + * @example + * ```tsx + * const MyLayer = Layer.succeed(MyService, new MyServiceImpl()) + * const MyComponent = Component.make(function*() { + * const context = yield* Component.useContextFromLayer(MyLayer) + * const Sub = yield* SubComponent.use.pipe( + * Effect.provide(context) + * ) + * + * return + * }) + * ``` + * + * @example With memoized layer + * ```tsx + * const MyComponent = Component.make(function*(props: { id: string })) { + * const context = yield* Component.useContextFromLayer( + * React.useMemo(() => Layer.succeed(MyService, new MyServiceImpl(props.id)), [props.id]) + * ) + * const Sub = yield* SubComponent.use.pipe( + * Effect.provide(context) + * ) + * + * return + * }) + * ``` + * + * @example With async layer + * ```tsx + * const MyAsyncLayer = Layer.effect(MyService, someAsyncEffect) + * const MyComponent = Component.make(function*() { + * const context = yield* Component.useContextFromLayer(MyAsyncLayer) + * const Sub = yield* SubComponent.use.pipe( + * Effect.provide(context) + * ) + * + * return + * }).pipe( + * Async.async // Required to handle async layer effects + * ) + */ export const useContextFromLayer = ( layer: Layer.Layer, options?: useContext.Options, -): Effect.Effect, E, RIn | Scope.Scope> => useOnChange( - () => Effect.flatMap( - Effect.context(), - context => Layer.build(Layer.provide(layer, Layer.succeedContext(context))), - ), - [layer], - options, -) +): Effect.Effect, E, RIn | Scope.Scope> => useOnChange(() => Effect.flatMap( + Effect.context(), + context => Layer.build(Layer.provide(layer, Layer.succeedContext(context))), +), [layer], options) -- 2.52.0 From c8df30480a1f19da5d82d500cbf38a0bb734a45d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 22 Jun 2026 06:44:31 +0200 Subject: [PATCH 039/133] Fix --- packages/effect-fc-next/src/Component.ts | 100 +++++++++++------------ 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/packages/effect-fc-next/src/Component.ts b/packages/effect-fc-next/src/Component.ts index ed24870..a651525 100644 --- a/packages/effect-fc-next/src/Component.ts +++ b/packages/effect-fc-next/src/Component.ts @@ -1,6 +1,6 @@ /** biome-ignore-all lint/complexity/noBannedTypes: {} is the default type for React props */ /** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */ -import { Context, type Duration, Effect, Equivalence, ExecutionStrategy, Exit, Fiber, Function, HashMap, identity, Layer, Option, Pipeable, Predicate, Ref, Runtime, Scope, Tracer, type Utils } from "effect" +import { Context, type Duration, Effect, Equivalence, ExecutionStrategy, Exit, Fiber, Function, HashMap, identity, Layer, Option, Pipeable, Predicate, Ref, Runtime, Scope, Tracer } from "effect" import * as React from "react" @@ -155,70 +155,70 @@ export const isComponent = (u: unknown): u is Component.Default<{}, React.ReactN export declare namespace make { export type Gen = { - >, A extends React.ReactNode, P extends {} = {}>( + , A extends React.ReactNode, P extends {} = {}>( body: (props: P) => Generator ): Component.Default< P, A, - [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, - [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never + [Eff] extends [never] ? never : [Eff] extends [Effect.Effect] ? E : never, + [Eff] extends [never] ? never : [Eff] extends [Effect.Effect] ? R : never > - >, A, B extends Effect.Effect, P extends {} = {}>( + , A, B extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( _: Effect.Effect< A, - [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, - [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never + [Eff] extends [never] ? never : [Eff] extends [Effect.Effect] ? E : never, + [Eff] extends [never] ? never : [Eff] extends [Effect.Effect] ? R : never >, props: NoInfer

, ) => B, - ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> - >, A, B, C extends Effect.Effect, P extends {} = {}>( + ): Component.Default, Effect.Error, Effect.Services> + , A, B, C extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( _: Effect.Effect< A, - [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, - [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never + [Eff] extends [never] ? never : [Eff] extends [Effect.Effect] ? E : never, + [Eff] extends [never] ? never : [Eff] extends [Effect.Effect] ? R : never >, props: NoInfer

, ) => B, b: (_: B, props: NoInfer

) => C, - ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> - >, A, B, C, D extends Effect.Effect, P extends {} = {}>( + ): Component.Default, Effect.Error, Effect.Services> + , A, B, C, D extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( _: Effect.Effect< A, - [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, - [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never + [Eff] extends [never] ? never : [Eff] extends [Effect.Effect] ? E : never, + [Eff] extends [never] ? never : [Eff] extends [Effect.Effect] ? R : never >, props: NoInfer

, ) => B, b: (_: B, props: NoInfer

) => C, c: (_: C, props: NoInfer

) => D, - ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> - >, A, B, C, D, E extends Effect.Effect, P extends {} = {}>( + ): Component.Default, Effect.Error, Effect.Services> + , A, B, C, D, E extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( _: Effect.Effect< A, - [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, - [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never + [Eff] extends [never] ? never : [Eff] extends [Effect.Effect] ? E : never, + [Eff] extends [never] ? never : [Eff] extends [Effect.Effect] ? R : never >, props: NoInfer

, ) => B, b: (_: B, props: NoInfer

) => C, c: (_: C, props: NoInfer

) => D, d: (_: D, props: NoInfer

) => E, - ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> - >, A, B, C, D, E, F extends Effect.Effect, P extends {} = {}>( + ): Component.Default, Effect.Error, Effect.Services> + , A, B, C, D, E, F extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( _: Effect.Effect< A, - [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, - [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never + [Eff] extends [never] ? never : [Eff] extends [Effect.Effect] ? E : never, + [Eff] extends [never] ? never : [Eff] extends [Effect.Effect] ? R : never >, props: NoInfer

, ) => B, @@ -226,14 +226,14 @@ export declare namespace make { c: (_: C, props: NoInfer

) => D, d: (_: D, props: NoInfer

) => E, e: (_: E, props: NoInfer

) => F, - ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> - >, A, B, C, D, E, F, G extends Effect.Effect, P extends {} = {}>( + ): Component.Default, Effect.Error, Effect.Services> + , A, B, C, D, E, F, G extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( _: Effect.Effect< A, - [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, - [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never + [Eff] extends [never] ? never : [Eff] extends [Effect.Effect] ? E : never, + [Eff] extends [never] ? never : [Eff] extends [Effect.Effect] ? R : never >, props: NoInfer

, ) => B, @@ -242,14 +242,14 @@ export declare namespace make { d: (_: D, props: NoInfer

) => E, e: (_: E, props: NoInfer

) => F, f: (_: F, props: NoInfer

) => G, - ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> - >, A, B, C, D, E, F, G, H extends Effect.Effect, P extends {} = {}>( + ): Component.Default, Effect.Error, Effect.Services> + , A, B, C, D, E, F, G, H extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( _: Effect.Effect< A, - [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, - [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never + [Eff] extends [never] ? never : [Eff] extends [Effect.Effect] ? E : never, + [Eff] extends [never] ? never : [Eff] extends [Effect.Effect] ? R : never >, props: NoInfer

, ) => B, @@ -259,14 +259,14 @@ export declare namespace make { e: (_: E, props: NoInfer

) => F, f: (_: F, props: NoInfer

) => G, g: (_: G, props: NoInfer

) => H, - ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> - >, A, B, C, D, E, F, G, H, I extends Effect.Effect, P extends {} = {}>( + ): Component.Default, Effect.Error, Effect.Services> + , A, B, C, D, E, F, G, H, I extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( _: Effect.Effect< A, - [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, - [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never + [Eff] extends [never] ? never : [Eff] extends [Effect.Effect] ? E : never, + [Eff] extends [never] ? never : [Eff] extends [Effect.Effect] ? R : never >, props: NoInfer

, ) => B, @@ -277,14 +277,14 @@ export declare namespace make { f: (_: F, props: NoInfer

) => G, g: (_: G, props: NoInfer

) => H, h: (_: H, props: NoInfer

) => I, - ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> - >, A, B, C, D, E, F, G, H, I, J extends Effect.Effect, P extends {} = {}>( + ): Component.Default, Effect.Error, Effect.Services> + , A, B, C, D, E, F, G, H, I, J extends Effect.Effect, P extends {} = {}>( body: (props: P) => Generator, a: ( _: Effect.Effect< A, - [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, - [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never + [Eff] extends [never] ? never : [Eff] extends [Effect.Effect] ? E : never, + [Eff] extends [never] ? never : [Eff] extends [Effect.Effect] ? R : never >, props: NoInfer

, ) => B, @@ -296,35 +296,35 @@ export declare namespace make { g: (_: G, props: NoInfer

) => H, h: (_: H, props: NoInfer

) => I, i: (_: I, props: NoInfer

) => J, - ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default, Effect.Error, Effect.Services> } export type NonGen = { , P extends {} = {}>( body: (props: P) => Eff - ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default, Effect.Error, Effect.Services> , A, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => Eff, - ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default, Effect.Error, Effect.Services> , A, B, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => B, b: (_: B, props: NoInfer

) => Eff, - ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default, Effect.Error, Effect.Services> , A, B, C, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => B, b: (_: B, props: NoInfer

) => C, c: (_: C, props: NoInfer

) => Eff, - ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default, Effect.Error, Effect.Services> , A, B, C, D, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => B, b: (_: B, props: NoInfer

) => C, c: (_: C, props: NoInfer

) => D, d: (_: D, props: NoInfer

) => Eff, - ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default, Effect.Error, Effect.Services> , A, B, C, D, E, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => B, @@ -332,7 +332,7 @@ export declare namespace make { c: (_: C, props: NoInfer

) => D, d: (_: D, props: NoInfer

) => E, e: (_: E, props: NoInfer

) => Eff, - ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default, Effect.Error, Effect.Services> , A, B, C, D, E, F, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => B, @@ -341,7 +341,7 @@ export declare namespace make { d: (_: D, props: NoInfer

) => E, e: (_: E, props: NoInfer

) => F, f: (_: F, props: NoInfer

) => Eff, - ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default, Effect.Error, Effect.Services> , A, B, C, D, E, F, G, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => B, @@ -351,7 +351,7 @@ export declare namespace make { e: (_: E, props: NoInfer

) => F, f: (_: F, props: NoInfer

) => G, g: (_: G, props: NoInfer

) => Eff, - ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default, Effect.Error, Effect.Services> , A, B, C, D, E, F, G, H, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => B, @@ -362,7 +362,7 @@ export declare namespace make { f: (_: F, props: NoInfer

) => G, g: (_: G, props: NoInfer

) => H, h: (_: H, props: NoInfer

) => Eff, - ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default, Effect.Error, Effect.Services> , A, B, C, D, E, F, G, H, I, P extends {} = {}>( body: (props: P) => A, a: (_: A, props: NoInfer

) => B, @@ -374,7 +374,7 @@ export declare namespace make { g: (_: G, props: NoInfer

) => H, h: (_: H, props: NoInfer

) => I, i: (_: I, props: NoInfer

) => Eff, - ): Component.Default>, Effect.Effect.Error, Effect.Effect.Context> + ): Component.Default, Effect.Error, Effect.Services> } } -- 2.52.0 From 6b5c501a98bb63ec56688525d90718577f361b52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 23 Jun 2026 03:25:58 +0200 Subject: [PATCH 040/133] Refactor --- packages/effect-fc-next/src/Component.ts | 10 ++-- packages/effect-fc-next/src/ReactRuntime.ts | 51 ++++++++++++--------- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/packages/effect-fc-next/src/Component.ts b/packages/effect-fc-next/src/Component.ts index a651525..12002ad 100644 --- a/packages/effect-fc-next/src/Component.ts +++ b/packages/effect-fc-next/src/Component.ts @@ -1,6 +1,6 @@ /** biome-ignore-all lint/complexity/noBannedTypes: {} is the default type for React props */ /** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */ -import { Context, type Duration, Effect, Equivalence, ExecutionStrategy, Exit, Fiber, Function, HashMap, identity, Layer, Option, Pipeable, Predicate, Ref, Runtime, Scope, Tracer } from "effect" +import { Context, type Duration, Effect, Equivalence, ExecutionStrategy, Exit, Fiber, Function, HashMap, identity, Layer, Option, Pipeable, Predicate, Ref, Scope, Tracer } from "effect" import * as React from "react" @@ -609,18 +609,18 @@ export const withOptions: { */ export const withRuntime: {

( - context: React.Context>, + context: React.Context>, ): (self: Component, F>) => F

( self: Component, F>, - context: React.Context>, + context: React.Context>, ): F } = Function.dual(2,

( self: Component, - context: React.Context>, + context: React.Context>, ) => function WithRuntime(props: P) { return React.createElement( - Runtime.runSync(React.useContext(context))(self.use) as React.FC

, + Effect.runSyncWith(React.useContext(context))(self.use) as React.FC

, props, ) }) diff --git a/packages/effect-fc-next/src/ReactRuntime.ts b/packages/effect-fc-next/src/ReactRuntime.ts index 92f9108..2c40a78 100644 --- a/packages/effect-fc-next/src/ReactRuntime.ts +++ b/packages/effect-fc-next/src/ReactRuntime.ts @@ -1,41 +1,52 @@ -/** biome-ignore-all lint/complexity/useArrowFunction: React component names are intentional */ -import { Context, Layer, ManagedRuntime, Predicate } from "effect" +/** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */ +import { type Context, Effect, Layer, ManagedRuntime, Predicate } from "effect" import * as React from "react" +import * as Component from "./Component.js" import * as ErrorObserver from "./ErrorObserver.js" import * as QueryClient from "./QueryClient.js" -export const TypeId: unique symbol = Symbol.for("@effect-fc/ReactRuntime/ReactRuntime") -export type TypeId = typeof TypeId +export const ReactRuntimeTypeId: unique symbol = Symbol.for("@effect-fc/ReactRuntime/ReactRuntime") +export type ReactRuntimeTypeId = typeof ReactRuntimeTypeId export interface ReactRuntime { new(_: never): Record - readonly [TypeId]: TypeId + readonly [ReactRuntimeTypeId]: ReactRuntimeTypeId readonly runtime: ManagedRuntime.ManagedRuntime readonly context: React.Context> } -const ReactRuntimeProto = Object.freeze({ [TypeId]: TypeId } as const) +const ReactRuntimeProto = Object.freeze({ [ReactRuntimeTypeId]: ReactRuntimeTypeId } as const) -export const Prelude: Layer.Layer = Layer.merge( +export const preludeLayer: Layer.Layer< + | Component.ScopeMap + | ErrorObserver.ErrorObserver + | QueryClient.QueryClient +> = Layer.mergeAll( + Component.ScopeMap.layer, ErrorObserver.layer, QueryClient.QueryClient.Default, ) + export const isReactRuntime = (u: unknown): u is ReactRuntime => Predicate.hasProperty(u, TypeId) export const make = ( layer: Layer.Layer, memoMap?: Layer.MemoMap, -): ReactRuntime => Object.setPrototypeOf( +): ReactRuntime | R, ER> => Object.setPrototypeOf( Object.assign(function() {}, { - runtime: ManagedRuntime.make(Layer.merge(layer, Prelude), { memoMap }), - // biome-ignore lint/style/noNonNullAssertion: initialized by Provider before consumers render - context: React.createContext>(null!), + runtime: ManagedRuntime.make( + Layer.merge(layer, preludeLayer), + memoMap, + ), + // biome-ignore lint/style/noNonNullAssertion: context initialization + context: React.createContext>(null!), }), ReactRuntimeProto, ) + export namespace Provider { export interface Props extends React.SuspenseProps { readonly runtime: ReactRuntime @@ -44,8 +55,13 @@ export namespace Provider { } export const Provider = ( - { runtime, children, ...suspenseProps }: Provider.Props, + { runtime, children, ...suspenseProps }: Provider.Props ): React.ReactNode => { + Effect.runSync(Component.useOnChange( + () => Effect.addFinalizer(() => runtime.runtime.disposeEffect), + [runtime], + )) + const promise = React.useMemo(() => runtime.runtime.context(), [runtime]) return React.createElement( @@ -60,12 +76,5 @@ const ProviderInner = ( readonly runtime: ReactRuntime readonly promise: Promise> readonly children?: React.ReactNode - }, -): React.ReactNode => { - const context = React.use(promise) - React.useEffect(() => () => { - void runtime.runtime.dispose() - }, [runtime]) - - return React.createElement(runtime.context, { value: context }, children) -} + } +): React.ReactNode => React.createElement(runtime.context, { value: React.use(promise) }, children) -- 2.52.0 From 24ce5430924e2279f5369e4c3982ac65026b9c9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 23 Jun 2026 03:32:13 +0200 Subject: [PATCH 041/133] Fix --- packages/effect-fc-next/src/Component.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/effect-fc-next/src/Component.ts b/packages/effect-fc-next/src/Component.ts index 12002ad..37b7cb8 100644 --- a/packages/effect-fc-next/src/Component.ts +++ b/packages/effect-fc-next/src/Component.ts @@ -1,6 +1,6 @@ /** biome-ignore-all lint/complexity/noBannedTypes: {} is the default type for React props */ /** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */ -import { Context, type Duration, Effect, Equivalence, ExecutionStrategy, Exit, Fiber, Function, HashMap, identity, Layer, Option, Pipeable, Predicate, Ref, Scope, Tracer } from "effect" +import { Context, type Duration, Effect, Equivalence, Exit, Fiber, Function, HashMap, identity, Layer, Option, Pipeable, Predicate, Ref, Scope, Tracer } from "effect" import * as React from "react" @@ -130,9 +130,9 @@ export interface ComponentOptions { * Specifies the execution strategy for finalizers when the component unmounts or its scope closes. * Determines whether finalizers execute sequentially or in parallel. * - * @default ExecutionStrategy.sequential + * @default "sequential" */ - readonly finalizerExecutionStrategy: ExecutionStrategy.ExecutionStrategy + readonly finalizerExecutionStrategy: "sequential" | "parallel" /** * Debounce duration before executing finalizers after component unmount. @@ -146,7 +146,7 @@ export interface ComponentOptions { export const defaultOptions: ComponentOptions = { nonReactiveTags: [Tracer.ParentSpan], - finalizerExecutionStrategy: ExecutionStrategy.sequential, + finalizerExecutionStrategy: "sequential", finalizerExecutionDebounce: "100 millis", } @@ -653,7 +653,7 @@ export declare namespace ScopeMap { export declare namespace useScope { export interface Options { - readonly finalizerExecutionStrategy?: ExecutionStrategy.ExecutionStrategy + readonly finalizerExecutionStrategy?: "sequential" | "parallel" readonly finalizerExecutionDebounce?: Duration.Input } } @@ -805,7 +805,7 @@ export const useOnChange = Effect.fnUntraced(function* ( export declare namespace useReactEffect { export interface Options { readonly finalizerExecutionMode?: "sync" | "fork" - readonly finalizerExecutionStrategy?: ExecutionStrategy.ExecutionStrategy + readonly finalizerExecutionStrategy?: "sequential" | "parallel" } } -- 2.52.0 From a86cb7e024de92dbbfaf5c8b1797417c71f0e0fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 23 Jun 2026 03:57:56 +0200 Subject: [PATCH 042/133] Refactor Component --- packages/effect-fc-next/src/Component.new.ts | 385 ------------------- packages/effect-fc-next/src/Component.ts | 28 +- 2 files changed, 19 insertions(+), 394 deletions(-) delete mode 100644 packages/effect-fc-next/src/Component.new.ts diff --git a/packages/effect-fc-next/src/Component.new.ts b/packages/effect-fc-next/src/Component.new.ts deleted file mode 100644 index a8f3d4a..0000000 --- a/packages/effect-fc-next/src/Component.new.ts +++ /dev/null @@ -1,385 +0,0 @@ -/** biome-ignore-all lint/complexity/noBannedTypes: {} is the default type for React props */ -import { - type Context, - type Duration, - Effect, - Exit, - Function, - identity, - Layer, - Pipeable, - Predicate, - Scope, - Tracer, -} from "effect" -import * as React from "react" - - -export const ComponentTypeId: unique symbol = Symbol.for("@effect-fc/Component/Component") -export type ComponentTypeId = typeof ComponentTypeId - -export interface Component

-extends ComponentPrototype, ComponentOptions { - new(_: never): Record - readonly [ComponentTypeId]: ComponentTypeId - readonly "~Props": P - readonly "~Success": A - readonly "~Error": E - readonly "~Context": R - readonly "~Function": F - readonly body: (props: P) => Effect.Effect -} - -export declare namespace Component { - export type Default

= Component> - export type Any = Component - export type Signature = (props: any) => React.ReactNode - export type DefaultSignature

= (props: P) => A - export type Props = T["~Props"] - export type Success = T["~Success"] - export type Error = T["~Error"] - export type Context = T["~Context"] - export type Function = T["~Function"] - export type AsComponent = Component, Success, Error, Context, Function> -} - -export interface ComponentOptions { - readonly displayName?: string - readonly nonReactiveTags: readonly Context.Key[] - readonly finalizerExecutionStrategy: "sequential" | "parallel" - readonly finalizerExecutionDebounce: Duration.Input -} - -export const defaultOptions: ComponentOptions = { - nonReactiveTags: [Tracer.ParentSpan], - finalizerExecutionStrategy: "sequential", - finalizerExecutionDebounce: "100 millis", -} - -export interface ComponentPrototype extends Pipeable.Pipeable { - readonly [ComponentTypeId]: ComponentTypeId - readonly use: Effect.Effect> -} - -type ComponentImpl = Component - -const makeFunctionComponent = ( - self: ComponentImpl, - contextRef: React.RefObject>, -): Component.Signature => { - if ("asFunctionComponent" in self && typeof self.asFunctionComponent === "function") { - return self.asFunctionComponent(contextRef) - } - const FunctionComponent = (props: {}) => Effect.runSyncWith(contextRef.current)( - Effect.flatMap( - useScope([], self), - scope => Effect.provideService(self.body(props), Scope.Scope, scope), - ), - ) - FunctionComponent.displayName = self.displayName ?? "Anonymous" - return "transformFunctionComponent" in self && typeof self.transformFunctionComponent === "function" - ? self.transformFunctionComponent(FunctionComponent) - : FunctionComponent -} - -const use = Effect.fnUntraced(function* (self: ComponentImpl) { - const context = yield* Effect.context() - const cached = componentCache.get(self) - if (cached !== undefined) { - cached.contextRef.current = context - return cached.component - } - const contextRef = { current: context } - const component = makeFunctionComponent(self, contextRef) - componentCache.set(self, { contextRef, component }) - return component -}) - -const componentCache = new WeakMap } - readonly component: Component.Signature -}>() - -export const ComponentPrototype = Object.freeze({ - [ComponentTypeId]: ComponentTypeId, - ...Pipeable.Prototype, - get use() { - return use(this as ComponentImpl) - }, -}) as unknown as ComponentPrototype - -export const isComponent = (u: unknown): u is Component.Any => Predicate.hasProperty(u, ComponentTypeId) - -type GeneratorBody

= ( - props: P, -) => Effect.fn.Return - -type EffectBody

= ( - props: P, -) => Effect.Effect - -export interface Make { -

( - body: GeneratorBody | EffectBody, - ...pipeables: readonly Function[] - ): Component.Default - (name: string, options?: Tracer.SpanOptionsNoTrace):

( - body: GeneratorBody | EffectBody, - ...pipeables: readonly Function[] - ) => Component.Default -} - -const component = ( - body: Function, - displayName: string | undefined, - traced: boolean, - pipeables: readonly Function[], -): Component.Any => Object.setPrototypeOf( - Object.assign(() => {}, defaultOptions, { - body: traced && displayName - ? Effect.fn(displayName)(body as never, ...pipeables as []) - : Effect.fnUntraced(body as never, ...pipeables as []), - displayName, - }), - ComponentPrototype, -) - -export const make: Make = ((nameOrBody: string | Function, ...args: readonly unknown[]) => { - if (typeof nameOrBody === "string") { - return (body: Function, ...pipeables: readonly Function[]) => component(body, nameOrBody, true, pipeables) - } - return component(nameOrBody, undefined, true, args as readonly Function[]) -}) as Make - -export const makeUntraced: Make = ((nameOrBody: string | Function, ...args: readonly unknown[]) => { - if (typeof nameOrBody === "string") { - return (body: Function, ...pipeables: readonly Function[]) => component(body, nameOrBody, false, pipeables) - } - return component(nameOrBody, undefined, false, args as readonly Function[]) -}) as Make - -export declare namespace withSignature { - export type Result = ( - & Omit> - & Component, Component.Success, Component.Error, Component.Context, F> - ) -} - -export const withSignature: { - (): (self: T) => withSignature.Result - (self: T): withSignature.Result -} = (self?: Component.Any): any => self === undefined ? identity : self - -export const withOptions: { - (options: Partial): (self: T) => T - (self: T, options: Partial): T -} = Function.dual(2, (self: T, options: Partial): T => Object.setPrototypeOf( - Object.assign(() => {}, self, options), - Object.getPrototypeOf(self), -)) - -export const withRuntime: { -

( - context: React.Context>, - ): (self: Component, F>) => F -

( - self: Component, F>, - context: React.Context>, - ): F -} = Function.dual(2,

( - self: Component, - context: React.Context>, -) => function WithRuntime(props: P) { - return React.createElement( - Effect.runSyncWith(React.useContext(context))(self.use) as React.FC

, - props, - ) -}) - -export declare namespace useScope { - export interface Options { - readonly finalizerExecutionStrategy?: "sequential" | "parallel" - readonly finalizerExecutionDebounce?: Duration.Input - } -} - -export const useScope = Effect.fnUntraced(function* ( - deps: React.DependencyList, - options?: useScope.Options, -): Effect.fn.Return { - const context = yield* Effect.context() - const contextRef = React.useRef(context) - contextRef.current = context - const scope = React.useMemo( - () => Scope.makeUnsafe(options?.finalizerExecutionStrategy ?? defaultOptions.finalizerExecutionStrategy), - // biome-ignore lint/correctness/useExhaustiveDependencies: caller controls scope lifetime - deps, - ) - - React.useEffect(() => { - const pending = scopeCleanupTimers.get(scope) - if (pending !== undefined) clearTimeout(pending) - return () => { - const timer = setTimeout(() => { - Effect.runSyncWith(contextRef.current)(Scope.close(scope, Exit.succeed(undefined))) - scopeCleanupTimers.delete(scope) - }, durationMillis(options?.finalizerExecutionDebounce ?? defaultOptions.finalizerExecutionDebounce)) - scopeCleanupTimers.set(scope, timer) - } - }, [scope, options?.finalizerExecutionDebounce]) - - return scope -}) - -const scopeCleanupTimers = new WeakMap>() - -const durationMillis = (input: Duration.Input): number => { - if (typeof input === "number") return input - return Number(Effect.runSync(Effect.map(Effect.succeed(input), value => { - const match = typeof value === "string" ? /([\d.]+)\s*(millis|seconds?)/.exec(value) : undefined - if (!match) return 0 - return Number(match[1]) * (match[2].startsWith("second") ? 1_000 : 1) - }))) -} - -export const useOnMount = Effect.fnUntraced(function* ( - f: () => Effect.Effect, -): Effect.fn.Return { - const context = yield* Effect.context() - const id = React.useId() - let cached = mountCache.get(id) - if (cached === undefined) { - cached = { - effect: Effect.runSyncWith(context)(Effect.cached(f())), - } - mountCache.set(id, cached) - } - React.useEffect(() => { - if (cached?.cleanup !== undefined) clearTimeout(cached.cleanup) - const entry = cached - return () => { - entry.cleanup = setTimeout(() => mountCache.delete(id), 0) - } - }, [id, cached]) - return yield* cached.effect as Effect.Effect -}) - -const mountCache = new Map - cleanup?: ReturnType -}>() - -export declare namespace useOnChange { - export interface Options extends useScope.Options {} -} - -export const useOnChange = Effect.fnUntraced(function* ( - f: () => Effect.Effect, - deps: React.DependencyList, - options?: useOnChange.Options, -): Effect.fn.Return> { - const context = yield* Effect.context>() - const scope = yield* useScope(deps, options) - const cached = - // biome-ignore lint/correctness/useExhaustiveDependencies: scope tracks the caller-provided dependency list - React.useMemo( - () => Effect.runSyncWith(context)(Effect.cached(Effect.provideService(f(), Scope.Scope, scope))), - [scope], - ) - return yield* cached -}) - -export declare namespace useReactEffect { - export interface Options { - readonly finalizerExecutionMode?: "sync" | "fork" - readonly finalizerExecutionStrategy?: "sequential" | "parallel" - } -} - -const runReactEffect = ( - context: Context.Context>, - f: () => Effect.Effect, - options?: useReactEffect.Options, -): (() => void) => { - const scope = Scope.makeUnsafe(options?.finalizerExecutionStrategy ?? defaultOptions.finalizerExecutionStrategy) - Effect.runSyncWith(context)(Effect.exit(Effect.provideService(f(), Scope.Scope, scope))) - return () => { - const close = Scope.close(scope, Exit.succeed(undefined)) - if ((options?.finalizerExecutionMode ?? "fork") === "sync") Effect.runSyncWith(context)(close) - else Effect.runForkWith(context)(close) - } -} - -export const useReactEffect = Effect.fnUntraced(function* ( - f: () => Effect.Effect, - deps?: React.DependencyList, - options?: useReactEffect.Options, -): Effect.fn.Return> { - const context = yield* Effect.context>() - // biome-ignore lint/correctness/useExhaustiveDependencies: caller supplies React dependencies - React.useEffect(() => runReactEffect(context, f, options), deps) -}) - -export declare namespace useReactLayoutEffect { - export interface Options extends useReactEffect.Options {} -} - -export const useReactLayoutEffect = Effect.fnUntraced(function* ( - f: () => Effect.Effect, - deps?: React.DependencyList, - options?: useReactLayoutEffect.Options, -): Effect.fn.Return> { - const context = yield* Effect.context>() - // biome-ignore lint/correctness/useExhaustiveDependencies: caller supplies React dependencies - React.useLayoutEffect(() => runReactEffect(context, f, options), deps) -}) - -export const useRunSync = (): Effect.Effect< - (effect: Effect.Effect) => A, - never, - R -> => Effect.map(Effect.context(), Effect.runSyncWith) - -export const useRunPromise = (): Effect.Effect< - (effect: Effect.Effect) => Promise, - never, - R -> => Effect.map(Effect.context(), Effect.runPromiseWith) - -export const useCallbackSync = Effect.fnUntraced(function* ( - f: (...args: Args) => Effect.Effect, - deps: React.DependencyList, -): Effect.fn.Return<(...args: Args) => A, never, R> { - const context = yield* Effect.context() - const contextRef = React.useRef(context) - contextRef.current = context - // biome-ignore lint/correctness/useExhaustiveDependencies: caller supplies React dependencies - return React.useCallback((...args: Args) => Effect.runSyncWith(contextRef.current)(f(...args)), deps) -}) - -export const useCallbackPromise = Effect.fnUntraced(function* ( - f: (...args: Args) => Effect.Effect, - deps: React.DependencyList, -): Effect.fn.Return<(...args: Args) => Promise, never, R> { - const context = yield* Effect.context() - const contextRef = React.useRef(context) - contextRef.current = context - // biome-ignore lint/correctness/useExhaustiveDependencies: caller supplies React dependencies - return React.useCallback((...args: Args) => Effect.runPromiseWith(contextRef.current)(f(...args)), deps) -}) - -export declare namespace useContext { - export interface Options extends useOnChange.Options {} -} - -export const useContextFromLayer = ( - layer: Layer.Layer, - options?: useContext.Options, -): Effect.Effect, E, RIn | Scope.Scope> => useOnChange( - () => Effect.flatMap( - Effect.context(), - context => Layer.build(Layer.provide(layer, Layer.succeedContext(context))), - ), - [layer], - options, -) diff --git a/packages/effect-fc-next/src/Component.ts b/packages/effect-fc-next/src/Component.ts index 37b7cb8..aadc0a9 100644 --- a/packages/effect-fc-next/src/Component.ts +++ b/packages/effect-fc-next/src/Component.ts @@ -83,16 +83,26 @@ const use = Effect.fnUntraced(function*

>>(null!) contextRef.current = yield* Effect.context>() - return yield* React.useState(() => Effect.runSyncWith(contextRef.current)(Effect.cachedFunction( - (_services: readonly any[]) => Effect.sync(() => { - const f = self.asFunctionComponent(contextRef) - self.setFunctionComponentName(f) - return self.transformFunctionComponent(f) - }), - Equivalence.array(Equivalence.strictEqual()), - )))[0](Array.from( + const componentRef = React.useRef(null) + const previousServicesRef = React.useRef(null) + + const services = Array.from( Context.omit(...self.nonReactiveTags)(contextRef.current).mapUnsafe.values() - )) + ) + + if ( + !componentRef.current || + !previousServicesRef.current || + services.length !== previousServicesRef.current.length || + !Equivalence.Array(Equivalence.strictEqual())(services, previousServicesRef.current) + ) { + previousServicesRef.current = services + const f = self.asFunctionComponent(contextRef) + self.setFunctionComponentName(f) + componentRef.current = self.transformFunctionComponent(f) + } + + return componentRef.current }) -- 2.52.0 From 65fcfb395eca66c28728080501dc544ded11ef55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 23 Jun 2026 04:08:49 +0200 Subject: [PATCH 043/133] Refactor --- packages/effect-fc-next/src/Async.ts | 4 ++-- packages/effect-fc-next/src/Component.ts | 2 +- packages/effect-fc-next/src/ReactRuntime.ts | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/effect-fc-next/src/Async.ts b/packages/effect-fc-next/src/Async.ts index 6495308..58b7f5a 100644 --- a/packages/effect-fc-next/src/Async.ts +++ b/packages/effect-fc-next/src/Async.ts @@ -1,5 +1,5 @@ /** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */ -import { Context, Effect, type Equivalence, Function, Predicate, Scope } from "effect" +import { type Context, Effect, type Equivalence, Function, Predicate, Scope } from "effect" import * as React from "react" import * as Component from "./Component.js" @@ -45,7 +45,7 @@ export const AsyncPrototype: AsyncPrototype = Object.freeze({ return ({ fallback, name, ...props }: AsyncProps) => { const promise = Effect.runPromiseWith(contextRef.current)( - Effect.andThen( + Effect.flatMap( Component.useScope([], this), scope => Effect.provideService(this.body(props as P), Scope.Scope, scope), ) diff --git a/packages/effect-fc-next/src/Component.ts b/packages/effect-fc-next/src/Component.ts index aadc0a9..bec0610 100644 --- a/packages/effect-fc-next/src/Component.ts +++ b/packages/effect-fc-next/src/Component.ts @@ -59,7 +59,7 @@ export const ComponentImplPrototype: ComponentImplPrototype = Object.f contextRef: React.RefObject>>, ) { return (props: P) => Effect.runSyncWith(contextRef.current)( - Effect.andThen( + Effect.flatMap( useScope([], this), scope => Effect.provideService(this.body(props), Scope.Scope, scope), ) diff --git a/packages/effect-fc-next/src/ReactRuntime.ts b/packages/effect-fc-next/src/ReactRuntime.ts index 2c40a78..4413e3e 100644 --- a/packages/effect-fc-next/src/ReactRuntime.ts +++ b/packages/effect-fc-next/src/ReactRuntime.ts @@ -29,16 +29,16 @@ export const preludeLayer: Layer.Layer< ) -export const isReactRuntime = (u: unknown): u is ReactRuntime => Predicate.hasProperty(u, TypeId) +export const isReactRuntime = (u: unknown): u is ReactRuntime => Predicate.hasProperty(u, ReactRuntimeTypeId) export const make = ( layer: Layer.Layer, memoMap?: Layer.MemoMap, -): ReactRuntime | R, ER> => Object.setPrototypeOf( +): ReactRuntime | R, ER> => Object.setPrototypeOf( Object.assign(function() {}, { runtime: ManagedRuntime.make( Layer.merge(layer, preludeLayer), - memoMap, + { memoMap }, ), // biome-ignore lint/style/noNonNullAssertion: context initialization context: React.createContext>(null!), -- 2.52.0 From 5945953555685d5288b2588b70303490282ea84e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 24 Jun 2026 00:54:10 +0200 Subject: [PATCH 044/133] Refactor QueryClient --- packages/effect-fc-next/src/QueryClient.ts | 167 ++++++++++----------- 1 file changed, 79 insertions(+), 88 deletions(-) diff --git a/packages/effect-fc-next/src/QueryClient.ts b/packages/effect-fc-next/src/QueryClient.ts index b811138..61dcf59 100644 --- a/packages/effect-fc-next/src/QueryClient.ts +++ b/packages/effect-fc-next/src/QueryClient.ts @@ -1,24 +1,8 @@ -import { - Context, - DateTime, - Duration, - Effect, - Equal, - Equivalence, - Hash, - HashMap, - Layer, - Option, - Pipeable, - Predicate, - Schedule, - Scope, - Semaphore, - SubscriptionRef, -} from "effect" -import { Subscribable } from "effect-lens" +import { type Cause, Context, DateTime, Duration, Effect, Equal, Equivalence, Hash, HashMap, type Option, Pipeable, Predicate, Schedule, type Scope, Semaphore, SubscriptionRef } from "effect" +import type { AsyncResult } from "effect/unstable/reactivity" +import * as Lens from "./Lens.js" import type * as Query from "./Query.js" -import type * as Result from "./Result.js" +import type * as Subscribable from "./Subscribable.js" export const QueryClientServiceTypeId: unique symbol = Symbol.for("@effect-fc/QueryClient/QueryClientService") @@ -26,89 +10,87 @@ export type QueryClientServiceTypeId = typeof QueryClientServiceTypeId export interface QueryClientService extends Pipeable.Pipeable { readonly [QueryClientServiceTypeId]: QueryClientServiceTypeId + readonly cache: Subscribable.Subscribable> - readonly cacheGcTime: Duration.Input - readonly defaultStaleTime: Duration.Input + readonly cacheGcTime: Duration.Duration + readonly defaultStaleTime: Duration.Duration readonly defaultRefreshOnWindowFocus: boolean - readonly run: Effect.Effect + + readonly run: Effect.Effect getCacheEntry(key: QueryClientCacheKey): Effect.Effect> - setCacheEntry(key: QueryClientCacheKey, result: Result.Success, staleTime: Duration.Input): Effect.Effect + setCacheEntry( + key: QueryClientCacheKey, + result: AsyncResult.Success, + staleTime: Duration.Duration, + ): Effect.Effect invalidateCacheEntries(f: (key: Query.Query.AnyKey) => Effect.Effect): Effect.Effect invalidateCacheEntry(key: QueryClientCacheKey): Effect.Effect } export class QueryClient extends Context.Service()( - "@effect-fc/QueryClient/QueryClient", -) { - static get Default(): Layer.Layer { - return Layer.effect(QueryClient)(service()) - } -} + "@effect-fc/QueryClient/QueryClient" +) {} -export class QueryClientServiceImpl extends Pipeable.Class implements QueryClientService { +export class QueryClientServiceImpl +extends Pipeable.Class +implements QueryClientService { readonly [QueryClientServiceTypeId]: QueryClientServiceTypeId = QueryClientServiceTypeId - readonly cache: Subscribable.Subscribable> constructor( - readonly cacheRef: SubscriptionRef.SubscriptionRef>, - readonly cacheGcTime: Duration.Input, - readonly defaultStaleTime: Duration.Input, + readonly cache: Lens.Lens>, + readonly cacheGcTime: Duration.Duration, + readonly defaultStaleTime: Duration.Duration, readonly defaultRefreshOnWindowFocus: boolean, readonly runSemaphore: Semaphore.Semaphore, ) { super() - this.cache = Subscribable.make({ - get: SubscriptionRef.get(cacheRef), - changes: SubscriptionRef.changes(cacheRef), - }) } - get run(): Effect.Effect { + get run(): Effect.Effect { return this.runSemaphore.withPermits(1)(Effect.repeat( - Effect.flatMap(DateTime.now, now => SubscriptionRef.update(this.cacheRef, HashMap.filter(entry => - Duration.isLessThan( - DateTime.distance(entry.lastAccessedAt, now), - Duration.sum(Duration.fromInputUnsafe(entry.staleTime), Duration.fromInputUnsafe(this.cacheGcTime)), - ) - ))), - Schedule.spaced("30 seconds"), + Effect.flatMap( + DateTime.now, + now => Lens.update(this.cache, HashMap.filter(entry => + Duration.isLessThan( + DateTime.distance(entry.lastAccessedAt, now), + Duration.sum(entry.staleTime, this.cacheGcTime), + ) + )), + ), + Schedule.spaced("30 second"), )) } getCacheEntry(key: QueryClientCacheKey): Effect.Effect> { - const self = this - return Effect.gen(function*() { - const entry = HashMap.get(yield* SubscriptionRef.get(self.cacheRef), key) - if (Option.isNone(entry)) return Option.none() - const now = yield* DateTime.now - const accessed = new QueryClientCacheEntry( - entry.value.result, - entry.value.staleTime, - entry.value.createdAt, - now, - ) - yield* SubscriptionRef.update(self.cacheRef, HashMap.set(key, accessed)) - return Option.some(accessed) - }) + return Effect.all([ + DateTime.now, + Effect.flatMap( + Effect.map(Lens.get(this.cache), HashMap.get(key)), + Effect.fromOption, + ), + ]).pipe( + Effect.map(([now, entry]) => new QueryClientCacheEntry(entry.result, entry.staleTime, entry.createdAt, now)), + Effect.tap(entry => Lens.update(this.cache, HashMap.set(key, entry))), + Effect.option, + ) } setCacheEntry( key: QueryClientCacheKey, - result: Result.Success, - staleTime: Duration.Input, + result: AsyncResult.Success, + staleTime: Duration.Duration, ): Effect.Effect { - return Effect.flatMap(DateTime.now, now => { - const entry = new QueryClientCacheEntry(result, staleTime, now, now) - return Effect.as(SubscriptionRef.update(this.cacheRef, HashMap.set(key, entry)), entry) - }) + return DateTime.now.pipe( + Effect.map(now => new QueryClientCacheEntry(result, staleTime, now, now)), + Effect.tap(entry => Lens.update(this.cache, HashMap.set(key, entry))), + ) } invalidateCacheEntries(f: (key: Query.Query.AnyKey) => Effect.Effect): Effect.Effect { - return SubscriptionRef.update(this.cacheRef, HashMap.filter((_, key) => !Equivalence.strictEqual()(key.f, f))) + return Lens.update(this.cache, HashMap.filter((_, key) => !Equivalence.strictEqual()(key.f, f))) } - invalidateCacheEntry(key: QueryClientCacheKey): Effect.Effect { - return SubscriptionRef.update(this.cacheRef, HashMap.remove(key)) + return Lens.update(this.cache, HashMap.remove(key)) } } @@ -122,11 +104,13 @@ export declare namespace make { } } -export const make = Effect.fnUntraced(function* (options: make.Options = {}): Effect.fn.Return { +export const make = Effect.fnUntraced(function* ( + options: make.Options = {} +): Effect.fn.Return { return new QueryClientServiceImpl( - yield* SubscriptionRef.make(HashMap.empty()), - options.cacheGcTime ?? "5 minutes", - options.defaultStaleTime ?? "0 minutes", + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(HashMap.empty())), + yield* Effect.fromOption(Duration.fromInput(options.cacheGcTime ?? "5 minutes")), + yield* Effect.fromOption(Duration.fromInput(options.defaultStaleTime ?? "0 minutes")), options.defaultRefreshOnWindowFocus ?? true, yield* Semaphore.make(1), ) @@ -136,15 +120,20 @@ export declare namespace service { export interface Options extends make.Options {} } -export const service = (options?: service.Options): Effect.Effect => Effect.tap( +export const service = ( + options?: service.Options +): Effect.Effect => Effect.tap( make(options), - client => Effect.asVoid(Effect.forkScoped(client.run)), + client => Effect.forkScoped(client.run), ) + export const QueryClientCacheKeyTypeId: unique symbol = Symbol.for("@effect-fc/QueryClient/QueryClientCacheKey") export type QueryClientCacheKeyTypeId = typeof QueryClientCacheKeyTypeId -export class QueryClientCacheKey extends Pipeable.Class implements Equal.Equal { +export class QueryClientCacheKey +extends Pipeable.Class +implements Pipeable.Pipeable, Equal.Equal { readonly [QueryClientCacheKeyTypeId]: QueryClientCacheKeyTypeId = QueryClientCacheKeyTypeId constructor( @@ -154,28 +143,28 @@ export class QueryClientCacheKey extends Pipeable.Class implements Equal.Equal { super() } - [Equal.symbol](that: Equal.Equal): boolean { - return isQueryClientCacheKey(that) - && Equivalence.Array(Equal.asEquivalence())(this.key, that.key) - && Equivalence.strictEqual()(this.f, that.f) + [Equal.symbol](that: Equal.Equal) { + return isQueryClientCacheKey(that) && Equivalence.Array(Equal.asEquivalence())(this.key, that.key) && Equivalence.strictEqual()(this.f, that.f) } - - [Hash.symbol](): number { + [Hash.symbol]() { return Hash.combine(Hash.hash(this.f))(Hash.array(this.key)) } } export const isQueryClientCacheKey = (u: unknown): u is QueryClientCacheKey => Predicate.hasProperty(u, QueryClientCacheKeyTypeId) + export const QueryClientCacheEntryTypeId: unique symbol = Symbol.for("@effect-fc/QueryClient/QueryClientCacheEntry") export type QueryClientCacheEntryTypeId = typeof QueryClientCacheEntryTypeId -export class QueryClientCacheEntry extends Pipeable.Class { +export class QueryClientCacheEntry +extends Pipeable.Class +implements Pipeable.Pipeable { readonly [QueryClientCacheEntryTypeId]: QueryClientCacheEntryTypeId = QueryClientCacheEntryTypeId constructor( - readonly result: Result.Success, - readonly staleTime: Duration.Input, + readonly result: AsyncResult.Success, + readonly staleTime: Duration.Duration, readonly createdAt: DateTime.DateTime, readonly lastAccessedAt: DateTime.DateTime, ) { @@ -185,7 +174,9 @@ export class QueryClientCacheEntry extends Pipeable.Class { export const isQueryClientCacheEntry = (u: unknown): u is QueryClientCacheEntry => Predicate.hasProperty(u, QueryClientCacheEntryTypeId) -export const isQueryClientCacheEntryStale = (self: QueryClientCacheEntry): Effect.Effect => Effect.map( +export const isQueryClientCacheEntryStale = ( + self: QueryClientCacheEntry +): Effect.Effect => Effect.map( DateTime.now, - now => Duration.isGreaterThanOrEqualTo(DateTime.distance(self.createdAt, now), Duration.fromInputUnsafe(self.staleTime)), + now => Duration.isGreaterThanOrEqualTo(DateTime.distance(self.createdAt, now), self.staleTime), ) -- 2.52.0 From 015aa0c8167a99662c947d1549b89bdc6c0a4cfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 25 Jun 2026 04:37:14 +0200 Subject: [PATCH 045/133] Refactor Query --- bun.lock | 3 + packages/effect-fc-next/package.json | 1 + packages/effect-fc-next/src/Query.ts | 387 +++++++++++++++------------ 3 files changed, 224 insertions(+), 167 deletions(-) diff --git a/bun.lock b/bun.lock index cf8ffc5..69dcdf1 100644 --- a/bun.lock +++ b/bun.lock @@ -61,6 +61,7 @@ "effect-lens": "2.0.0-beta.0", }, "devDependencies": { + "@effect/platform-browser": "^4.0.0-beta.85", "@testing-library/react": "^16.3.0", "jsdom": "^26.1.0", "vitest": "^3.2.4", @@ -3062,6 +3063,8 @@ "dot-prop/is-obj": ["is-obj@2.0.0", "", {}, "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="], + "effect-fc-next/@effect/platform-browser": ["@effect/platform-browser@4.0.0-beta.87", "", { "dependencies": { "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^4.0.0-beta.87" } }, "sha512-sBUUdRt5PjFPLI7+ayxEX/jgnjCbOuc3I//98CmqiwhfgWbCMZ/rofYYb8Zk5tLpNsmdHnWgHWTsBLvH2SFrCw=="], + "effect-fc-next/effect": ["effect@4.0.0-beta.85", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.8.0", "find-my-way-ts": "^0.1.6", "ini": "^7.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^2.0.1", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^14.0.0", "yaml": "^2.9.0" } }, "sha512-Cjv9YQyv4CiIccmRAQIWAoeESCpCpiuHYY8zb5vqiYs3Ac2yE5RQAnKq7z4Ir/3VJYZb8kxh6rS8czDDnpTdkQ=="], "effect-fc-next/effect-lens": ["effect-lens@2.0.0-beta.0", "", { "peerDependencies": { "effect": "4.0.0-beta.85" } }, "sha512-ntsRQSrzoX+GsNpkNVEGqebl/g1YOEDPIFRAHigf+4P6AtGnnOFcklqDHLrbm4zqnaxuWEgQvvn1J6S3eyC4yw=="], diff --git a/packages/effect-fc-next/package.json b/packages/effect-fc-next/package.json index f29a70b..fa29dbf 100644 --- a/packages/effect-fc-next/package.json +++ b/packages/effect-fc-next/package.json @@ -39,6 +39,7 @@ "clean:modules": "rm -rf node_modules" }, "devDependencies": { + "@effect/platform-browser": "^4.0.0-beta.85", "@testing-library/react": "^16.3.0", "jsdom": "^26.1.0", "vitest": "^3.2.4" diff --git a/packages/effect-fc-next/src/Query.ts b/packages/effect-fc-next/src/Query.ts index e9e317b..670f15d 100644 --- a/packages/effect-fc-next/src/Query.ts +++ b/packages/effect-fc-next/src/Query.ts @@ -1,44 +1,35 @@ -import { - type Cause, - type Context, - type Duration, - Effect, - Equal, - Fiber, - Option, - Pipeable, - Predicate, - type Scope, - Semaphore, - Stream, - SubscriptionRef, -} from "effect" -import { Subscribable } from "effect-lens" +import { type Cause, type Context, Duration, Effect, Equal, Exit, Fiber, Option, Pipeable, Predicate, type Scope, Semaphore, Stream, SubscriptionRef } from "effect" +import { AsyncResult } from "effect/unstable/reactivity" +import * as Lens from "./Lens.js" import * as QueryClient from "./QueryClient.js" -import * as Result from "./Result.js" +import * as Subscribable from "./Subscribable.js" export const QueryTypeId: unique symbol = Symbol.for("@effect-fc/Query/Query") export type QueryTypeId = typeof QueryTypeId -export interface Query +export interface Query extends Pipeable.Pipeable { readonly [QueryTypeId]: QueryTypeId + readonly context: Context.Context readonly key: Stream.Stream readonly f: (key: K) => Effect.Effect - readonly initialProgress: P - readonly staleTime: Duration.Input + + readonly staleTime: Duration.Duration readonly refreshOnWindowFocus: boolean + readonly latestKey: Subscribable.Subscribable> readonly fiber: Subscribable.Subscribable>> - readonly result: Subscribable.Subscribable> - readonly latestFinalResult: Subscribable.Subscribable>> + readonly result: Subscribable.Subscribable> + readonly latestFinalResult: Subscribable.Subscribable | AsyncResult.Failure>> + readonly run: Effect.Effect - fetch(key: K): Effect.Effect> - fetchSubscribable(key: K): Effect.Effect>> - readonly refresh: Effect.Effect, Cause.NoSuchElementError> - readonly refreshSubscribable: Effect.Effect>, Cause.NoSuchElementError> + fetch(key: K): Effect.Effect | AsyncResult.Failure, Cause.NoSuchElementError> + fetchSubscribable(key: K): Effect.Effect>, Cause.NoSuchElementError> + readonly refresh: Effect.Effect | AsyncResult.Failure, Cause.NoSuchElementError> + readonly refreshSubscribable: Effect.Effect>, Cause.NoSuchElementError> + readonly invalidateCache: Effect.Effect invalidateCacheEntry(key: K): Effect.Effect } @@ -47,42 +38,43 @@ export declare namespace Query { export type AnyKey = readonly any[] } -export class QueryImpl -extends Pipeable.Class implements Query { +export class QueryImpl +extends Pipeable.Class implements Query { readonly [QueryTypeId]: QueryTypeId = QueryTypeId - readonly latestKey: Subscribable.Subscribable> - readonly fiber: Subscribable.Subscribable>> - readonly result: Subscribable.Subscribable> - readonly latestFinalResult: Subscribable.Subscribable>> constructor( readonly context: Context.Context, readonly key: Stream.Stream, readonly f: (key: K) => Effect.Effect, - readonly initialProgress: P, - readonly staleTime: Duration.Input, + + readonly staleTime: Duration.Duration, readonly refreshOnWindowFocus: boolean, - readonly latestKeyRef: SubscriptionRef.SubscriptionRef>, - readonly fiberRef: SubscriptionRef.SubscriptionRef>>, - readonly resultRef: SubscriptionRef.SubscriptionRef>, - readonly latestFinalResultRef: SubscriptionRef.SubscriptionRef>>, + + readonly latestKey: Lens.Lens>, + readonly fiber: Lens.Lens>>, + readonly result: Lens.Lens>, + readonly latestFinalResult: Lens.Lens | AsyncResult.Failure>>, + readonly runSemaphore: Semaphore.Semaphore, ) { super() - this.latestKey = fromSubscriptionRef(latestKeyRef) - this.fiber = fromSubscriptionRef(fiberRef) - this.result = fromSubscriptionRef(resultRef) - this.latestFinalResult = fromSubscriptionRef(latestFinalResultRef) } get run(): Effect.Effect { - const focus = this.refreshOnWindowFocus && typeof window !== "undefined" - ? Stream.runForEach(Stream.fromEventListener(window, "focus"), () => this.refreshSubscribable) - : Effect.succeed(undefined) return Effect.all([ Stream.runForEach(this.key, key => this.fetchSubscribable(key)), - focus, - ], { concurrency: "unbounded", discard: true }).pipe( + + Effect.promise(() => import("@effect/platform-browser")).pipe( + Effect.flatMap(({ BrowserStream }) => this.refreshOnWindowFocus + ? Stream.runForEach( + BrowserStream.fromEventListenerWindow("focus"), + () => this.refreshSubscribable, + ) + : Effect.void + ), + Effect.catchDefect(() => Effect.void), + ), + ], { concurrency: "unbounded" }).pipe( Effect.ignore, this.runSemaphore.withPermits(1), Effect.provide(this.context), @@ -90,120 +82,159 @@ extends Pipeable.Class implements Query { } get interrupt(): Effect.Effect { - return Effect.flatMap(SubscriptionRef.get(this.fiberRef), Option.match({ + return Effect.flatMap(Lens.get(this.fiber), Option.match({ onSome: Fiber.interrupt, - onNone: () => Effect.succeed(undefined), + onNone: () => Effect.void, })) } - fetch(key: K): Effect.Effect> { - const self = this - return Effect.gen(function*() { - yield* self.interrupt - yield* SubscriptionRef.set(self.latestKeyRef, Option.some(key)) - const previous = yield* SubscriptionRef.get(self.latestFinalResultRef) - const sub = yield* self.startCached(key, Option.isSome(previous) - ? Result.willFetch(previous.value) as Result.Final - : Result.initial()) - return yield* self.watch(key, sub) - }).pipe(Effect.provide(this.context)) + fetch(key: K): Effect.Effect< + AsyncResult.Success | AsyncResult.Failure, + Cause.NoSuchElementError + > { + return this.interrupt.pipe( + Effect.andThen(Lens.set(this.latestKey, Option.some(key))), + Effect.andThen(this.startCached(key, AsyncResult.initial())), + Effect.flatMap(state => this.watch(key, state)), + Effect.provide(this.context), + ) } - fetchSubscribable(key: K): Effect.Effect>> { - const self = this - return Effect.gen(function*() { - yield* self.interrupt - yield* SubscriptionRef.set(self.latestKeyRef, Option.some(key)) - const previous = yield* SubscriptionRef.get(self.latestFinalResultRef) - const sub = yield* self.startCached(key, Option.isSome(previous) - ? Result.willFetch(previous.value) as Result.Final - : Result.initial()) - yield* Effect.forkScoped(self.watch(key, sub)) - return sub - }).pipe(Effect.provide(this.context)) + fetchSubscribable(key: K): Effect.Effect< + Subscribable.Subscribable>, + Cause.NoSuchElementError + > { + return this.interrupt.pipe( + Effect.andThen(Lens.set(this.latestKey, Option.some(key))), + Effect.andThen(this.startCached(key, AsyncResult.initial(false))), + Effect.tap(state => Effect.forkScoped(this.watch(key, state))), + Effect.provide(this.context), + ) } - get refresh(): Effect.Effect, Cause.NoSuchElementError> { - const self = this - return Effect.gen(function*() { - yield* self.interrupt - const key = yield* Effect.fromOption(yield* SubscriptionRef.get(self.latestKeyRef)) - const previous = yield* SubscriptionRef.get(self.latestFinalResultRef) - const sub = yield* self.startCached(key, Option.isSome(previous) - ? Result.willRefresh(previous.value) as Result.Final - : Result.initial()) - return yield* self.watch(key, sub) - }).pipe(Effect.provide(this.context)) + get refresh(): Effect.Effect | AsyncResult.Failure, Cause.NoSuchElementError> { + return this.interrupt.pipe( + Effect.andThen(Effect.Do), + Effect.bind("latestKey", () => Effect.flatMap(Lens.get(this.latestKey), Effect.fromOption)), + Effect.bind("latestFinalResult", () => Lens.get(this.latestFinalResult)), + Effect.bind("subscribable", ({ latestKey, latestFinalResult }) => + this.startCached(latestKey, Option.getOrElse(latestFinalResult, () => AsyncResult.initial(true))) + ), + Effect.flatMap(({ latestKey, subscribable }) => this.watch(latestKey, subscribable)), + Effect.provide(this.context), + ) } - get refreshSubscribable(): Effect.Effect>, Cause.NoSuchElementError> { - const self = this - return Effect.gen(function*() { - yield* self.interrupt - const key = yield* Effect.fromOption(yield* SubscriptionRef.get(self.latestKeyRef)) - const previous = yield* SubscriptionRef.get(self.latestFinalResultRef) - const sub = yield* self.startCached(key, Option.isSome(previous) - ? Result.willRefresh(previous.value) as Result.Final - : Result.initial()) - yield* Effect.forkScoped(self.watch(key, sub)) - return sub - }).pipe(Effect.provide(this.context)) + get refreshSubscribable(): Effect.Effect< + Subscribable.Subscribable>, + Cause.NoSuchElementError + > { + return this.interrupt.pipe( + Effect.andThen(Effect.Do), + Effect.bind("latestKey", () => Effect.flatMap(Lens.get(this.latestKey), Effect.fromOption)), + Effect.bind("latestFinalResult", () => Lens.get(this.latestFinalResult)), + Effect.bind("subscribable", ({ latestKey, latestFinalResult }) => + this.startCached(latestKey, Option.getOrElse(latestFinalResult, () => AsyncResult.initial(true))) + ), + Effect.tap(({ latestKey, subscribable }) => Effect.forkScoped(this.watch(latestKey, subscribable))), + Effect.map(({ subscribable }) => subscribable), + Effect.provide(this.context), + ) } startCached( key: K, - initial: Result.Initial | Result.Final, - ): Effect.Effect>, never, Scope.Scope | QueryClient.QueryClient | R> { + previous: AsyncResult.AsyncResult, + ): Effect.Effect< + Subscribable.Subscribable>, + Cause.NoSuchElementError, + Scope.Scope | QueryClient.QueryClient | R + > { return Effect.flatMap(this.getCacheEntry(key), Option.match({ onSome: entry => Effect.flatMap( QueryClient.isQueryClientCacheEntryStale(entry), isStale => isStale - ? this.start(key, Result.willRefresh(entry.result) as Result.Final) + ? this.start(key, entry.result as AsyncResult.AsyncResult) : Effect.succeed(Subscribable.make({ - get: Effect.succeed(entry.result as Result.Result), - changes: Stream.make(entry.result as Result.Result), + get: Effect.succeed(entry.result as AsyncResult.AsyncResult), + get changes() { return Stream.make(entry.result as AsyncResult.AsyncResult) }, })), ), - onNone: () => this.start(key, initial), + onNone: () => this.start(key, previous), })) } start( key: K, - initial: Result.Initial | Result.Final, - ): Effect.Effect>, never, Scope.Scope | R> { - const self = this - return Effect.gen(function*() { - const [sub, fiber] = yield* Result.unsafeForkEffect( - Effect.onExit(self.f(key), () => Effect.flatMap( - Effect.all([Effect.fiberId, SubscriptionRef.get(self.fiberRef)]), - ([currentFiberId, current]) => Option.match(current, { - onSome: value => Equal.equals(currentFiberId, value.id) - ? SubscriptionRef.set(self.fiberRef, Option.none()) - : Effect.succeed(undefined), - onNone: () => Effect.succeed(undefined), + previous: AsyncResult.AsyncResult, + ): Effect.Effect< + Subscribable.Subscribable>, + never, + Scope.Scope | R + > { + return Effect.gen({ self: this }, function*() { + const state = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(previous)) + + const fiber = yield* Effect.forkScoped(Effect.andThen( + Lens.update(state, AsyncResult.match({ + onInitial: () => AsyncResult.initial(true), + onSuccess: v => AsyncResult.success(v.value, { + waiting: true, }), + onFailure: v => AsyncResult.failure(v.cause, { + waiting: true, + previousSuccess: v.previousSuccess, + }) + })), + + Effect.onExit(this.f(key), exit => Lens.update( + state, + previous => Exit.match(exit, { + onSuccess: v => AsyncResult.success(v), + onFailure: c => AsyncResult.match(previous, { + onInitial: () => AsyncResult.failure(c), + onSuccess: v => AsyncResult.failure(c, { + previousSuccess: Option.some(v), + }), + onFailure: v => AsyncResult.failure(c, { + previousSuccess: v.previousSuccess, + }) + }), + }), + ).pipe( + Effect.andThen(Effect.all([ + Effect.fiberId, + Lens.get(this.fiber), + ])), + Effect.flatMap(([fiberId, fiber]) => Option.match(fiber, { + onSome: v => Equal.equals(fiberId, v.id) + ? Lens.set(this.fiber, Option.none()) + : Effect.void, + onNone: () => Effect.void, + })), )), - { initial, initialProgress: self.initialProgress }, - ) - yield* SubscriptionRef.set(self.fiberRef, Option.some(fiber)) - return sub + )) + + yield* Lens.set(this.fiber, Option.some(fiber)) + return state }) } watch( key: K, - sub: Subscribable.Subscribable>, - ): Effect.Effect, never, QueryClient.QueryClient> { - return Effect.flatMap(sub.get, initial => Stream.runFoldEffect( - Stream.takeUntil(sub.changes, result => Result.isFinal(result) && !Result.hasFlag(result)), - () => initial, - (_, result) => Effect.as(SubscriptionRef.set(this.resultRef, result), result), - ) as Effect.Effect>).pipe( - Effect.tap(result => SubscriptionRef.set(this.latestFinalResultRef, Option.some(result))), - Effect.tap(result => Result.isSuccess(result) - ? Effect.asVoid(this.setCacheEntry(key, result)) - : Effect.succeed(undefined)), + state: Subscribable.Subscribable> + ): Effect.Effect | AsyncResult.Failure, never, QueryClient.QueryClient> { + return state.get.pipe( + Effect.flatMap(initial => Stream.runFoldEffect( + state.changes, + () => initial, + (_, result) => Effect.as(Lens.set(this.result, result), result), + ) as Effect.Effect | AsyncResult.Failure>), + Effect.tap(result => Lens.set(this.latestFinalResult, Option.some(result))), + Effect.tap(result => AsyncResult.isSuccess(result) + ? this.setCacheEntry(key, result) + : Effect.void + ), ) } @@ -211,73 +242,95 @@ extends Pipeable.Class implements Query { return new QueryClient.QueryClientCacheKey(key, this.f as (key: Query.AnyKey) => Effect.Effect) } - getCacheEntry(key: K): Effect.Effect, never, QueryClient.QueryClient> { - return Effect.flatMap(QueryClient.QueryClient, client => client.getCacheEntry(this.makeCacheKey(key))) + getCacheEntry( + key: K + ): Effect.Effect, never, QueryClient.QueryClient> { + return Effect.andThen( + Effect.all([ + Effect.succeed(this.makeCacheKey(key)), + QueryClient.QueryClient, + ]), + ([key, client]) => client.getCacheEntry(key), + ) } - setCacheEntry(key: K, result: Result.Success): Effect.Effect { - return Effect.flatMap(QueryClient.QueryClient, client => client.setCacheEntry(this.makeCacheKey(key), result, this.staleTime)) + setCacheEntry( + key: K, + result: AsyncResult.Success, + ): Effect.Effect { + return Effect.flatMap( + Effect.all([ + Effect.succeed(this.makeCacheKey(key)), + QueryClient.QueryClient, + ]), + ([key, client]) => client.setCacheEntry(key, result, this.staleTime), + ) } get invalidateCache(): Effect.Effect { - return Effect.flatMap( - QueryClient.QueryClient, - client => client.invalidateCacheEntries(this.f as (key: Query.AnyKey) => Effect.Effect), - ).pipe(Effect.provide(this.context)) + return QueryClient.QueryClient.pipe( + Effect.andThen(client => client.invalidateCacheEntries(this.f as (key: Query.AnyKey) => Effect.Effect)), + Effect.provide(this.context), + ) } invalidateCacheEntry(key: K): Effect.Effect { - return Effect.flatMap( + return Effect.all([ + Effect.succeed(this.makeCacheKey(key)), QueryClient.QueryClient, - client => client.invalidateCacheEntry(this.makeCacheKey(key)), - ).pipe(Effect.provide(this.context)) + ]).pipe( + Effect.andThen(([key, client]) => client.invalidateCacheEntry(key)), + Effect.provide(this.context), + ) } } + export const isQuery = (u: unknown): u is Query => Predicate.hasProperty(u, QueryTypeId) + export declare namespace make { - export interface Options { + export interface Options { readonly key: Stream.Stream - readonly f: (key: NoInfer) => Effect.Effect>> - readonly initialProgress?: P + readonly f: (key: NoInfer) => Effect.Effect readonly staleTime?: Duration.Input readonly refreshOnWindowFocus?: boolean } } -export const make = Effect.fnUntraced(function* ( - options: make.Options, +export const make = Effect.fnUntraced(function* ( + options: make.Options ): Effect.fn.Return< - Query, P>, - never, - Scope.Scope | QueryClient.QueryClient | KR | Result.forkEffect.OutputContext + Query, + Cause.NoSuchElementError, + Scope.Scope | QueryClient.QueryClient | KR | R > { const client = yield* QueryClient.QueryClient - return new QueryImpl, P>( - yield* Effect.context>(), + + return new QueryImpl( + yield* Effect.context(), options.key, - options.f as any, - options.initialProgress as P, - options.staleTime ?? client.defaultStaleTime, + options.f, + + options.staleTime ? yield* Effect.fromOption(Duration.fromInput(options.staleTime)) : client.defaultStaleTime, options.refreshOnWindowFocus ?? client.defaultRefreshOnWindowFocus, - yield* SubscriptionRef.make(Option.none()), - yield* SubscriptionRef.make(Option.none>()), - yield* SubscriptionRef.make(Result.initial()), - yield* SubscriptionRef.make(Option.none>()), + + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none())), + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none>())), + Lens.fromSubscriptionRef(yield* SubscriptionRef.make>(AsyncResult.initial())), + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none | AsyncResult.Failure>())), + yield* Semaphore.make(1), ) }) -export const service = ( - options: make.Options, +export const service = ( + options: make.Options ): Effect.Effect< - Query, P>, - never, - Scope.Scope | QueryClient.QueryClient | KR | Result.forkEffect.OutputContext -> => Effect.tap(make(options), query => Effect.asVoid(Effect.forkScoped(query.run))) - -const fromSubscriptionRef = (ref: SubscriptionRef.SubscriptionRef): Subscribable.Subscribable => Subscribable.make({ - get: SubscriptionRef.get(ref), - changes: SubscriptionRef.changes(ref), -}) + Query, + Cause.NoSuchElementError, + Scope.Scope | QueryClient.QueryClient | KR | R +> => Effect.tap( + make(options), + query => Effect.forkScoped(query.run), +) -- 2.52.0 From e5ac120ceabce813c23c768d5dca8d4ea20a1f4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 26 Jun 2026 02:38:24 +0200 Subject: [PATCH 046/133] Refactor Mutation --- packages/effect-fc-next/src/Mutation.ts | 167 +++++++++++++----------- packages/effect-fc-next/src/Query.ts | 2 +- 2 files changed, 90 insertions(+), 79 deletions(-) diff --git a/packages/effect-fc-next/src/Mutation.ts b/packages/effect-fc-next/src/Mutation.ts index 0d873eb..5ae14d4 100644 --- a/packages/effect-fc-next/src/Mutation.ts +++ b/packages/effect-fc-next/src/Mutation.ts @@ -1,146 +1,157 @@ -import { type Context, Effect, Equal, type Fiber, Option, Pipeable, Predicate, type Scope, Stream, SubscriptionRef } from "effect" -import { Subscribable } from "effect-lens" -import * as Result from "./Result.js" +import { type Context, Effect, Equal, Exit, type Fiber, Option, Pipeable, Predicate, type Scope, Stream, SubscriptionRef } from "effect" +import { AsyncResult } from "effect/unstable/reactivity" +import * as Lens from "./Lens.js" +import type * as Subscribable from "./Subscribable.js" export const MutationTypeId: unique symbol = Symbol.for("@effect-fc/Mutation/Mutation") export type MutationTypeId = typeof MutationTypeId -export interface Mutation +export interface Mutation extends Pipeable.Pipeable { readonly [MutationTypeId]: MutationTypeId readonly context: Context.Context readonly f: (key: K) => Effect.Effect - readonly initialProgress: P readonly latestKey: Subscribable.Subscribable> readonly fiber: Subscribable.Subscribable>> - readonly result: Subscribable.Subscribable> - readonly latestFinalResult: Subscribable.Subscribable>> + readonly result: Subscribable.Subscribable> + readonly latestFinalResult: Subscribable.Subscribable | AsyncResult.Failure>> - mutate(key: K): Effect.Effect> - mutateSubscribable(key: K): Effect.Effect>> + mutate(key: K): Effect.Effect | AsyncResult.Failure> + mutateSubscribable(key: K): Effect.Effect>> } export declare namespace Mutation { export type AnyKey = readonly any[] } -export class MutationImpl -extends Pipeable.Class implements Mutation { +export class MutationImpl +extends Pipeable.Class implements Mutation { readonly [MutationTypeId]: MutationTypeId = MutationTypeId - readonly latestKey: Subscribable.Subscribable> - readonly fiber: Subscribable.Subscribable>> - readonly result: Subscribable.Subscribable> - readonly latestFinalResult: Subscribable.Subscribable>> constructor( readonly context: Context.Context, readonly f: (key: K) => Effect.Effect, - readonly initialProgress: P, - readonly latestKeyRef: SubscriptionRef.SubscriptionRef>, - readonly fiberRef: SubscriptionRef.SubscriptionRef>>, - readonly resultRef: SubscriptionRef.SubscriptionRef>, - readonly latestFinalResultRef: SubscriptionRef.SubscriptionRef>>, + readonly latestKey: Lens.Lens>, + readonly fiber: Lens.Lens>>, + readonly result: Lens.Lens>, + readonly latestFinalResult: Lens.Lens | AsyncResult.Failure>>, ) { super() - this.latestKey = fromSubscriptionRef(latestKeyRef) - this.fiber = fromSubscriptionRef(fiberRef) - this.result = fromSubscriptionRef(resultRef) - this.latestFinalResult = fromSubscriptionRef(latestFinalResultRef) } - mutate(key: K): Effect.Effect> { - return SubscriptionRef.set(this.latestKeyRef, Option.some(key)).pipe( + mutate(key: K): Effect.Effect | AsyncResult.Failure> { + return Lens.set(this.latestKey, Option.some(key)).pipe( Effect.andThen(this.start(key)), - Effect.andThen(sub => this.watch(sub)), + Effect.flatMap(state => this.watch(state)), Effect.provide(this.context), ) } - mutateSubscribable(key: K): Effect.Effect>> { - return SubscriptionRef.set(this.latestKeyRef, Option.some(key)).pipe( + mutateSubscribable(key: K): Effect.Effect>> { + return Lens.set(this.latestKey, Option.some(key)).pipe( Effect.andThen(this.start(key)), - Effect.tap(sub => Effect.forkScoped(this.watch(sub))), + Effect.tap(state => Effect.forkScoped(this.watch(state))), Effect.provide(this.context), ) } start(key: K): Effect.Effect< - Subscribable.Subscribable>, + Subscribable.Subscribable>, never, Scope.Scope | R > { - const self = this - return Effect.gen(function*() { - const initial = yield* SubscriptionRef.get(self.latestFinalResultRef) - const [sub, fiber] = yield* Result.unsafeForkEffect( - Effect.onExit(self.f(key), () => Effect.andThen( - Effect.all([Effect.fiberId, SubscriptionRef.get(self.fiberRef)]), - ([currentFiberId, fiber]) => Option.match(fiber, { - onSome: v => Equal.equals(currentFiberId, v.id) - ? SubscriptionRef.set(self.fiberRef, Option.none()) - : Effect.succeed(undefined), - onNone: () => Effect.succeed(undefined), - }), - )), + return Effect.gen({ self: this }, function*() { + const previous = yield* Lens.get(this.latestFinalResult) + const state = Lens.fromSubscriptionRef(yield* SubscriptionRef.make>( + Option.getOrElse(previous, () => AsyncResult.initial(false)) + )) - { - initial: Option.isSome(initial) ? Result.willFetch(initial.value) : Result.initial(), - initialProgress: self.initialProgress, - } as Result.unsafeForkEffect.Options, - ) - yield* SubscriptionRef.set(self.fiberRef, Option.some(fiber)) - return sub + const fiber = yield* Effect.forkScoped(Effect.andThen( + Lens.update(state, AsyncResult.match({ + onInitial: () => AsyncResult.initial(true), + onSuccess: v => AsyncResult.success(v.value, { + waiting: true, + }), + onFailure: v => AsyncResult.failure(v.cause, { + waiting: true, + previousSuccess: v.previousSuccess, + }) + })), + + Effect.onExit(this.f(key), exit => Lens.update( + state, + previous => Exit.match(exit, { + onSuccess: v => AsyncResult.success(v), + onFailure: c => AsyncResult.match(previous, { + onInitial: () => AsyncResult.failure(c), + onSuccess: v => AsyncResult.failure(c, { + previousSuccess: Option.some(v), + }), + onFailure: v => AsyncResult.failure(c, { + previousSuccess: v.previousSuccess, + }) + }), + }), + ).pipe( + Effect.andThen(Effect.all([ + Effect.fiberId, + Lens.get(this.fiber), + ])), + Effect.flatMap(([fiberId, fiber]) => Option.match(fiber, { + onSome: v => Equal.equals(fiberId, v.id) + ? Lens.set(this.fiber, Option.none()) + : Effect.void, + onNone: () => Effect.void, + })), + )), + )) + + yield* Lens.set(this.fiber, Option.some(fiber)) + return state }) } watch( - sub: Subscribable.Subscribable> - ): Effect.Effect> { - return sub.get.pipe( + state: Subscribable.Subscribable> + ): Effect.Effect | AsyncResult.Failure> { + return state.get.pipe( Effect.andThen(initial => Stream.runFoldEffect( - Stream.takeUntil(sub.changes, result => Result.isFinal(result) && !Result.hasFlag(result)), + state.changes, () => initial, - (_, result) => Effect.as(SubscriptionRef.set(this.resultRef, result), result), - ) as Effect.Effect>), - Effect.tap(result => SubscriptionRef.set(this.latestFinalResultRef, Option.some(result))), + (_, result) => Effect.as(Lens.set(this.result, result), result), + ) as Effect.Effect | AsyncResult.Failure>), + Effect.tap(result => Lens.set(this.latestFinalResult, Option.some(result))), ) } } -export const isMutation = (u: unknown): u is Mutation => Predicate.hasProperty(u, MutationTypeId) +export const isMutation = (u: unknown): u is Mutation => Predicate.hasProperty(u, MutationTypeId) export declare namespace make { - export interface Options { - readonly f: (key: K) => Effect.Effect>> - readonly initialProgress?: P + export interface Options { + readonly f: (key: K) => Effect.Effect } } -export const make = Effect.fnUntraced(function* ( - options: make.Options +export const make = Effect.fnUntraced(function* ( + options: make.Options ): Effect.fn.Return< - Mutation, P>, + Mutation, never, - Scope.Scope | Result.forkEffect.OutputContext + Scope.Scope | R > { return new MutationImpl( - yield* Effect.context>(), - options.f as any, - options.initialProgress as P, + yield* Effect.context(), + options.f, - yield* SubscriptionRef.make(Option.none()), - yield* SubscriptionRef.make(Option.none>()), - yield* SubscriptionRef.make(Result.initial()), - yield* SubscriptionRef.make(Option.none>()), + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none())), + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none>())), + Lens.fromSubscriptionRef(yield* SubscriptionRef.make>(AsyncResult.initial())), + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none | AsyncResult.Failure>())), ) }) - -const fromSubscriptionRef = (ref: SubscriptionRef.SubscriptionRef): Subscribable.Subscribable => Subscribable.make({ - get: SubscriptionRef.get(ref), - changes: SubscriptionRef.changes(ref), -}) diff --git a/packages/effect-fc-next/src/Query.ts b/packages/effect-fc-next/src/Query.ts index 670f15d..27a6531 100644 --- a/packages/effect-fc-next/src/Query.ts +++ b/packages/effect-fc-next/src/Query.ts @@ -286,7 +286,7 @@ extends Pipeable.Class implements Query { } -export const isQuery = (u: unknown): u is Query => Predicate.hasProperty(u, QueryTypeId) +export const isQuery = (u: unknown): u is Query => Predicate.hasProperty(u, QueryTypeId) export declare namespace make { -- 2.52.0 From c59e2ee03517c00910eb0d7751204faaf1f2a167 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 26 Jun 2026 02:54:40 +0200 Subject: [PATCH 047/133] Refactor Query/Mutation --- packages/effect-fc-next/src/Mutation.ts | 16 ++++++--------- packages/effect-fc-next/src/Query.ts | 23 +++++++++------------- packages/effect-fc-next/src/QueryClient.ts | 13 ++++++------ 3 files changed, 21 insertions(+), 31 deletions(-) diff --git a/packages/effect-fc-next/src/Mutation.ts b/packages/effect-fc-next/src/Mutation.ts index 5ae14d4..6ad8140 100644 --- a/packages/effect-fc-next/src/Mutation.ts +++ b/packages/effect-fc-next/src/Mutation.ts @@ -7,7 +7,7 @@ import type * as Subscribable from "./Subscribable.js" export const MutationTypeId: unique symbol = Symbol.for("@effect-fc/Mutation/Mutation") export type MutationTypeId = typeof MutationTypeId -export interface Mutation +export interface Mutation extends Pipeable.Pipeable { readonly [MutationTypeId]: MutationTypeId @@ -23,11 +23,10 @@ extends Pipeable.Pipeable { mutateSubscribable(key: K): Effect.Effect>> } -export declare namespace Mutation { - export type AnyKey = readonly any[] -} +export const isMutation = (u: unknown): u is Mutation => Predicate.hasProperty(u, MutationTypeId) -export class MutationImpl + +export class MutationImpl extends Pipeable.Class implements Mutation { readonly [MutationTypeId]: MutationTypeId = MutationTypeId @@ -129,16 +128,13 @@ extends Pipeable.Class implements Mutation { } -export const isMutation = (u: unknown): u is Mutation => Predicate.hasProperty(u, MutationTypeId) - - export declare namespace make { - export interface Options { + export interface Options { readonly f: (key: K) => Effect.Effect } } -export const make = Effect.fnUntraced(function* ( +export const make = Effect.fnUntraced(function* ( options: make.Options ): Effect.fn.Return< Mutation, diff --git a/packages/effect-fc-next/src/Query.ts b/packages/effect-fc-next/src/Query.ts index 27a6531..6e7d589 100644 --- a/packages/effect-fc-next/src/Query.ts +++ b/packages/effect-fc-next/src/Query.ts @@ -8,7 +8,7 @@ import * as Subscribable from "./Subscribable.js" export const QueryTypeId: unique symbol = Symbol.for("@effect-fc/Query/Query") export type QueryTypeId = typeof QueryTypeId -export interface Query +export interface Query extends Pipeable.Pipeable { readonly [QueryTypeId]: QueryTypeId @@ -34,11 +34,10 @@ extends Pipeable.Pipeable { invalidateCacheEntry(key: K): Effect.Effect } -export declare namespace Query { - export type AnyKey = readonly any[] -} +export const isQuery = (u: unknown): u is Query => Predicate.hasProperty(u, QueryTypeId) -export class QueryImpl + +export class QueryImpl extends Pipeable.Class implements Query { readonly [QueryTypeId]: QueryTypeId = QueryTypeId @@ -239,7 +238,7 @@ extends Pipeable.Class implements Query { } makeCacheKey(key: K): QueryClient.QueryClientCacheKey { - return new QueryClient.QueryClientCacheKey(key, this.f as (key: Query.AnyKey) => Effect.Effect) + return new QueryClient.QueryClientCacheKey(key, this.f as (key: unknown) => Effect.Effect) } getCacheEntry( @@ -269,7 +268,7 @@ extends Pipeable.Class implements Query { get invalidateCache(): Effect.Effect { return QueryClient.QueryClient.pipe( - Effect.andThen(client => client.invalidateCacheEntries(this.f as (key: Query.AnyKey) => Effect.Effect)), + Effect.andThen(client => client.invalidateCacheEntries(this.f as (key: unknown) => Effect.Effect)), Effect.provide(this.context), ) } @@ -285,12 +284,8 @@ extends Pipeable.Class implements Query { } } - -export const isQuery = (u: unknown): u is Query => Predicate.hasProperty(u, QueryTypeId) - - export declare namespace make { - export interface Options { + export interface Options { readonly key: Stream.Stream readonly f: (key: NoInfer) => Effect.Effect readonly staleTime?: Duration.Input @@ -298,7 +293,7 @@ export declare namespace make { } } -export const make = Effect.fnUntraced(function* ( +export const make = Effect.fnUntraced(function* ( options: make.Options ): Effect.fn.Return< Query, @@ -324,7 +319,7 @@ export const make = Effect.fnUntraced(function* ( +export const service = ( options: make.Options ): Effect.Effect< Query, diff --git a/packages/effect-fc-next/src/QueryClient.ts b/packages/effect-fc-next/src/QueryClient.ts index 61dcf59..2405cbd 100644 --- a/packages/effect-fc-next/src/QueryClient.ts +++ b/packages/effect-fc-next/src/QueryClient.ts @@ -1,7 +1,6 @@ import { type Cause, Context, DateTime, Duration, Effect, Equal, Equivalence, Hash, HashMap, type Option, Pipeable, Predicate, Schedule, type Scope, Semaphore, SubscriptionRef } from "effect" import type { AsyncResult } from "effect/unstable/reactivity" import * as Lens from "./Lens.js" -import type * as Query from "./Query.js" import type * as Subscribable from "./Subscribable.js" @@ -23,7 +22,7 @@ export interface QueryClientService extends Pipeable.Pipeable { result: AsyncResult.Success, staleTime: Duration.Duration, ): Effect.Effect - invalidateCacheEntries(f: (key: Query.Query.AnyKey) => Effect.Effect): Effect.Effect + invalidateCacheEntries(f: (key: unknown) => Effect.Effect): Effect.Effect invalidateCacheEntry(key: QueryClientCacheKey): Effect.Effect } @@ -86,7 +85,7 @@ implements QueryClientService { ) } - invalidateCacheEntries(f: (key: Query.Query.AnyKey) => Effect.Effect): Effect.Effect { + invalidateCacheEntries(f: (key: unknown) => Effect.Effect): Effect.Effect { return Lens.update(this.cache, HashMap.filter((_, key) => !Equivalence.strictEqual()(key.f, f))) } invalidateCacheEntry(key: QueryClientCacheKey): Effect.Effect { @@ -137,17 +136,17 @@ implements Pipeable.Pipeable, Equal.Equal { readonly [QueryClientCacheKeyTypeId]: QueryClientCacheKeyTypeId = QueryClientCacheKeyTypeId constructor( - readonly key: Query.Query.AnyKey, - readonly f: (key: Query.Query.AnyKey) => Effect.Effect, + readonly key: unknown, + readonly f: (key: unknown) => Effect.Effect, ) { super() } [Equal.symbol](that: Equal.Equal) { - return isQueryClientCacheKey(that) && Equivalence.Array(Equal.asEquivalence())(this.key, that.key) && Equivalence.strictEqual()(this.f, that.f) + return isQueryClientCacheKey(that) && Equal.equals(this.key, that.key) && Equivalence.strictEqual()(this.f, that.f) } [Hash.symbol]() { - return Hash.combine(Hash.hash(this.f))(Hash.array(this.key)) + return Hash.combine(Hash.hash(this.f))(Hash.hash(this.key)) } } -- 2.52.0 From e07ce1cd682f7ca99c3c48ba027e5e8c322e110d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 1 Jul 2026 21:12:05 +0200 Subject: [PATCH 048/133] Refactor Query --- packages/effect-fc-next/src/Query.ts | 251 ++++++++++++++++----------- 1 file changed, 147 insertions(+), 104 deletions(-) diff --git a/packages/effect-fc-next/src/Query.ts b/packages/effect-fc-next/src/Query.ts index 6e7d589..ca169be 100644 --- a/packages/effect-fc-next/src/Query.ts +++ b/packages/effect-fc-next/src/Query.ts @@ -1,4 +1,4 @@ -import { type Cause, type Context, Duration, Effect, Equal, Exit, Fiber, Option, Pipeable, Predicate, type Scope, Semaphore, Stream, SubscriptionRef } from "effect" +import { type Cause, type Context, Duration, Effect, Equal, type Equivalence, Exit, Fiber, Option, Pipeable, Predicate, type Scope, Semaphore, Stream, SubscriptionRef } from "effect" import { AsyncResult } from "effect/unstable/reactivity" import * as Lens from "./Lens.js" import * as QueryClient from "./QueryClient.js" @@ -8,51 +8,61 @@ import * as Subscribable from "./Subscribable.js" export const QueryTypeId: unique symbol = Symbol.for("@effect-fc/Query/Query") export type QueryTypeId = typeof QueryTypeId -export interface Query +export interface Query extends Pipeable.Pipeable { readonly [QueryTypeId]: QueryTypeId - readonly context: Context.Context - readonly key: Stream.Stream + readonly context: Context.Context readonly f: (key: K) => Effect.Effect + readonly keyEquivalence: Equivalence.Equivalence readonly staleTime: Duration.Duration readonly refreshOnWindowFocus: boolean readonly latestKey: Subscribable.Subscribable> readonly fiber: Subscribable.Subscribable>> - readonly result: Subscribable.Subscribable> - readonly latestFinalResult: Subscribable.Subscribable | AsyncResult.Failure>> + readonly state: Subscribable.Subscribable> + readonly latestFinalState: Subscribable.Subscribable>> readonly run: Effect.Effect - fetch(key: K): Effect.Effect | AsyncResult.Failure, Cause.NoSuchElementError> - fetchSubscribable(key: K): Effect.Effect>, Cause.NoSuchElementError> - readonly refresh: Effect.Effect | AsyncResult.Failure, Cause.NoSuchElementError> - readonly refreshSubscribable: Effect.Effect>, Cause.NoSuchElementError> + fetch(key: K): Effect.Effect, Cause.NoSuchElementError> + fetchSubscribable(key: K): Effect.Effect>, Cause.NoSuchElementError> + readonly refresh: Effect.Effect, Cause.NoSuchElementError> + readonly refreshSubscribable: Effect.Effect>, Cause.NoSuchElementError> readonly invalidateCache: Effect.Effect invalidateCacheEntry(key: K): Effect.Effect } -export const isQuery = (u: unknown): u is Query => Predicate.hasProperty(u, QueryTypeId) +export interface QueryState { + readonly key: K + readonly result: AsyncResult.AsyncResult +} + +export interface FinalQueryState { + readonly key: K + readonly result: AsyncResult.Success | AsyncResult.Failure +} + +export const isQuery = (u: unknown): u is Query => Predicate.hasProperty(u, QueryTypeId) -export class QueryImpl -extends Pipeable.Class implements Query { +export class QueryImpl +extends Pipeable.Class implements Query { readonly [QueryTypeId]: QueryTypeId = QueryTypeId constructor( - readonly context: Context.Context, - readonly key: Stream.Stream, + readonly context: Context.Context, readonly f: (key: K) => Effect.Effect, + readonly keyEquivalence: Equivalence.Equivalence, readonly staleTime: Duration.Duration, readonly refreshOnWindowFocus: boolean, readonly latestKey: Lens.Lens>, readonly fiber: Lens.Lens>>, - readonly result: Lens.Lens>, - readonly latestFinalResult: Lens.Lens | AsyncResult.Failure>>, + readonly state: Lens.Lens>, + readonly latestFinalState: Lens.Lens>>, readonly runSemaphore: Semaphore.Semaphore, ) { @@ -60,20 +70,15 @@ extends Pipeable.Class implements Query { } get run(): Effect.Effect { - return Effect.all([ - Stream.runForEach(this.key, key => this.fetchSubscribable(key)), - - Effect.promise(() => import("@effect/platform-browser")).pipe( - Effect.flatMap(({ BrowserStream }) => this.refreshOnWindowFocus - ? Stream.runForEach( - BrowserStream.fromEventListenerWindow("focus"), - () => this.refreshSubscribable, - ) - : Effect.void - ), - Effect.catchDefect(() => Effect.void), + return Effect.promise(() => import("@effect/platform-browser")).pipe( + Effect.flatMap(({ BrowserStream }) => this.refreshOnWindowFocus + ? Stream.runForEach( + BrowserStream.fromEventListenerWindow("focus"), + () => this.refreshSubscribable, + ) + : Effect.void ), - ], { concurrency: "unbounded" }).pipe( + Effect.catchDefect(() => Effect.void), Effect.ignore, this.runSemaphore.withPermits(1), Effect.provide(this.context), @@ -87,117 +92,156 @@ extends Pipeable.Class implements Query { })) } - fetch(key: K): Effect.Effect< - AsyncResult.Success | AsyncResult.Failure, - Cause.NoSuchElementError - > { + fetch(key: K): Effect.Effect, Cause.NoSuchElementError> { return this.interrupt.pipe( Effect.andThen(Lens.set(this.latestKey, Option.some(key))), - Effect.andThen(this.startCached(key, AsyncResult.initial())), - Effect.flatMap(state => this.watch(key, state)), + Effect.andThen(this.startCached({ + key, + result: AsyncResult.initial(false) + })), + Effect.flatMap(state => this.watch(state)), Effect.provide(this.context), ) } fetchSubscribable(key: K): Effect.Effect< - Subscribable.Subscribable>, + Subscribable.Subscribable>, Cause.NoSuchElementError > { return this.interrupt.pipe( Effect.andThen(Lens.set(this.latestKey, Option.some(key))), - Effect.andThen(this.startCached(key, AsyncResult.initial(false))), - Effect.tap(state => Effect.forkScoped(this.watch(key, state))), + Effect.andThen(this.startCached({ + key, + result: AsyncResult.initial(false) + })), + Effect.tap(state => Effect.forkScoped(this.watch(state))), Effect.provide(this.context), ) } - get refresh(): Effect.Effect | AsyncResult.Failure, Cause.NoSuchElementError> { + get refresh(): Effect.Effect, Cause.NoSuchElementError> { return this.interrupt.pipe( Effect.andThen(Effect.Do), Effect.bind("latestKey", () => Effect.flatMap(Lens.get(this.latestKey), Effect.fromOption)), - Effect.bind("latestFinalResult", () => Lens.get(this.latestFinalResult)), - Effect.bind("subscribable", ({ latestKey, latestFinalResult }) => - this.startCached(latestKey, Option.getOrElse(latestFinalResult, () => AsyncResult.initial(true))) + Effect.bind("latestFinalState", () => Lens.get(this.latestFinalState)), + Effect.bind("subscribable", ({ latestKey, latestFinalState }) => + this.startCached(Option.getOrElse(latestFinalState, () => ({ + key: latestKey, + result: AsyncResult.initial(true), + }))) ), - Effect.flatMap(({ latestKey, subscribable }) => this.watch(latestKey, subscribable)), + Effect.flatMap(({ subscribable }) => this.watch(subscribable)), Effect.provide(this.context), ) } get refreshSubscribable(): Effect.Effect< - Subscribable.Subscribable>, + Subscribable.Subscribable>, Cause.NoSuchElementError > { return this.interrupt.pipe( Effect.andThen(Effect.Do), Effect.bind("latestKey", () => Effect.flatMap(Lens.get(this.latestKey), Effect.fromOption)), - Effect.bind("latestFinalResult", () => Lens.get(this.latestFinalResult)), - Effect.bind("subscribable", ({ latestKey, latestFinalResult }) => - this.startCached(latestKey, Option.getOrElse(latestFinalResult, () => AsyncResult.initial(true))) + Effect.bind("latestFinalState", () => Lens.get(this.latestFinalState)), + Effect.bind("subscribable", ({ latestKey, latestFinalState }) => + this.startCached(Option.getOrElse(latestFinalState, () => ({ + key: latestKey, + result: AsyncResult.initial(true), + }))) ), - Effect.tap(({ latestKey, subscribable }) => Effect.forkScoped(this.watch(latestKey, subscribable))), + Effect.tap(({ subscribable }) => Effect.forkScoped(this.watch(subscribable))), Effect.map(({ subscribable }) => subscribable), Effect.provide(this.context), ) } startCached( - key: K, - previous: AsyncResult.AsyncResult, + previous: QueryState, ): Effect.Effect< - Subscribable.Subscribable>, + Subscribable.Subscribable>, Cause.NoSuchElementError, Scope.Scope | QueryClient.QueryClient | R > { - return Effect.flatMap(this.getCacheEntry(key), Option.match({ + return Effect.flatMap(this.getCacheEntry(previous.key), Option.match({ onSome: entry => Effect.flatMap( QueryClient.isQueryClientCacheEntryStale(entry), isStale => isStale - ? this.start(key, entry.result as AsyncResult.AsyncResult) + ? this.start({ + key: previous.key, + result: entry.result as AsyncResult.AsyncResult, + }) : Effect.succeed(Subscribable.make({ - get: Effect.succeed(entry.result as AsyncResult.AsyncResult), - get changes() { return Stream.make(entry.result as AsyncResult.AsyncResult) }, + get: Effect.succeed({ + key: previous.key, + result: entry.result as AsyncResult.AsyncResult, + }), + get changes() { + return Stream.make({ + key: previous.key, + result: entry.result as AsyncResult.AsyncResult, + }) + }, })), ), - onNone: () => this.start(key, previous), + onNone: () => this.start(previous), })) } start( - key: K, - previous: AsyncResult.AsyncResult, + previous: QueryState, ): Effect.Effect< - Subscribable.Subscribable>, + Subscribable.Subscribable>, never, Scope.Scope | R > { return Effect.gen({ self: this }, function*() { - const state = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(previous)) + const subscribable = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(previous)) const fiber = yield* Effect.forkScoped(Effect.andThen( - Lens.update(state, AsyncResult.match({ - onInitial: () => AsyncResult.initial(true), - onSuccess: v => AsyncResult.success(v.value, { - waiting: true, + Lens.update(subscribable, previous => AsyncResult.match(previous.result, { + onInitial: () => ({ + key: previous.key, + result: AsyncResult.initial(true), + }), + onSuccess: result => ({ + key: previous.key, + result: AsyncResult.success(result.value, { + waiting: true, + }), + }), + onFailure: result => ({ + key: previous.key, + result: AsyncResult.failure(result.cause, { + waiting: true, + previousSuccess: result.previousSuccess, + }), }), - onFailure: v => AsyncResult.failure(v.cause, { - waiting: true, - previousSuccess: v.previousSuccess, - }) })), - Effect.onExit(this.f(key), exit => Lens.update( - state, + Effect.onExit(this.f(previous.key), exit => Lens.update( + subscribable, previous => Exit.match(exit, { - onSuccess: v => AsyncResult.success(v), - onFailure: c => AsyncResult.match(previous, { - onInitial: () => AsyncResult.failure(c), - onSuccess: v => AsyncResult.failure(c, { - previousSuccess: Option.some(v), + onSuccess: v => ({ + key: previous.key, + result: AsyncResult.success(v), + }), + onFailure: c => AsyncResult.match(previous.result, { + onInitial: () => ({ + key: previous.key, + result: AsyncResult.failure(c), + }), + onSuccess: v => ({ + key: previous.key, + result: AsyncResult.failure(c, { + previousSuccess: Option.some(v), + }), + }), + onFailure: v => ({ + key: previous.key, + result: AsyncResult.failure(c, { + previousSuccess: v.previousSuccess, + }), }), - onFailure: v => AsyncResult.failure(c, { - previousSuccess: v.previousSuccess, - }) }), }), ).pipe( @@ -206,7 +250,7 @@ extends Pipeable.Class implements Query { Lens.get(this.fiber), ])), Effect.flatMap(([fiberId, fiber]) => Option.match(fiber, { - onSome: v => Equal.equals(fiberId, v.id) + onSome: v => fiberId === v.id ? Lens.set(this.fiber, Option.none()) : Effect.void, onNone: () => Effect.void, @@ -215,23 +259,22 @@ extends Pipeable.Class implements Query { )) yield* Lens.set(this.fiber, Option.some(fiber)) - return state + return subscribable }) } watch( - key: K, - state: Subscribable.Subscribable> - ): Effect.Effect | AsyncResult.Failure, never, QueryClient.QueryClient> { - return state.get.pipe( + subscribable: Subscribable.Subscribable> + ): Effect.Effect, never, QueryClient.QueryClient> { + return subscribable.get.pipe( Effect.flatMap(initial => Stream.runFoldEffect( - state.changes, + subscribable.changes, () => initial, - (_, result) => Effect.as(Lens.set(this.result, result), result), - ) as Effect.Effect | AsyncResult.Failure>), - Effect.tap(result => Lens.set(this.latestFinalResult, Option.some(result))), - Effect.tap(result => AsyncResult.isSuccess(result) - ? this.setCacheEntry(key, result) + (_, state) => Effect.as(Lens.set(this.state, state), state), + ) as Effect.Effect>), + Effect.tap(state => Lens.set(this.latestFinalState, Option.some(state))), + Effect.tap(state => AsyncResult.isSuccess(state.result) + ? this.setCacheEntry(state.key, state.result) : Effect.void ), ) @@ -285,27 +328,27 @@ extends Pipeable.Class implements Query { } export declare namespace make { - export interface Options { - readonly key: Stream.Stream - readonly f: (key: NoInfer) => Effect.Effect + export interface Options { + readonly f: (key: K) => Effect.Effect + readonly keyEquivalence?: Equivalence.Equivalence, readonly staleTime?: Duration.Input readonly refreshOnWindowFocus?: boolean } } -export const make = Effect.fnUntraced(function* ( - options: make.Options +export const make = Effect.fnUntraced(function* ( + options: make.Options ): Effect.fn.Return< - Query, + Query, Cause.NoSuchElementError, - Scope.Scope | QueryClient.QueryClient | KR | R + Scope.Scope | QueryClient.QueryClient | R > { const client = yield* QueryClient.QueryClient return new QueryImpl( - yield* Effect.context(), - options.key, + yield* Effect.context(), options.f, + options.keyEquivalence ?? Equal.asEquivalence(), options.staleTime ? yield* Effect.fromOption(Duration.fromInput(options.staleTime)) : client.defaultStaleTime, options.refreshOnWindowFocus ?? client.defaultRefreshOnWindowFocus, @@ -319,12 +362,12 @@ export const make = Effect.fnUntraced(function* ( - options: make.Options +export const service = ( + options: make.Options ): Effect.Effect< - Query, + Query, Cause.NoSuchElementError, - Scope.Scope | QueryClient.QueryClient | KR | R + Scope.Scope | QueryClient.QueryClient | R > => Effect.tap( make(options), query => Effect.forkScoped(query.run), -- 2.52.0 From 7a7783e7ded89409cc5db4ed89e78fc9e38f4f07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 7 Jul 2026 21:22:05 +0200 Subject: [PATCH 049/133] Refactor Query --- packages/effect-fc-next/src/Query.ts | 161 +++++++++++++++++---------- 1 file changed, 100 insertions(+), 61 deletions(-) diff --git a/packages/effect-fc-next/src/Query.ts b/packages/effect-fc-next/src/Query.ts index ca169be..9b5007b 100644 --- a/packages/effect-fc-next/src/Query.ts +++ b/packages/effect-fc-next/src/Query.ts @@ -13,13 +13,13 @@ extends Pipeable.Pipeable { readonly [QueryTypeId]: QueryTypeId readonly context: Context.Context - readonly f: (key: K) => Effect.Effect + readonly key: Subscribable.Subscribable readonly keyEquivalence: Equivalence.Equivalence + readonly f: (key: K) => Effect.Effect readonly staleTime: Duration.Duration readonly refreshOnWindowFocus: boolean - readonly latestKey: Subscribable.Subscribable> readonly fiber: Subscribable.Subscribable>> readonly state: Subscribable.Subscribable> readonly latestFinalState: Subscribable.Subscribable>> @@ -53,13 +53,13 @@ extends Pipeable.Class implements Query { constructor( readonly context: Context.Context, - readonly f: (key: K) => Effect.Effect, + readonly key: Lens.Lens, readonly keyEquivalence: Equivalence.Equivalence, + readonly f: (key: K) => Effect.Effect, readonly staleTime: Duration.Duration, readonly refreshOnWindowFocus: boolean, - readonly latestKey: Lens.Lens>, readonly fiber: Lens.Lens>>, readonly state: Lens.Lens>, readonly latestFinalState: Lens.Lens>>, @@ -70,15 +70,29 @@ extends Pipeable.Class implements Query { } get run(): Effect.Effect { - return Effect.promise(() => import("@effect/platform-browser")).pipe( - Effect.flatMap(({ BrowserStream }) => this.refreshOnWindowFocus - ? Stream.runForEach( - BrowserStream.fromEventListenerWindow("focus"), - () => this.refreshSubscribable, - ) - : Effect.void + return Effect.all([ + Stream.runFoldEffect( + this.key.changes, + () => Option.none(), + (previous, key) => Effect.as( + Option.isSome(previous) && this.keyEquivalence(key, previous.value) + ? this.refreshSubscribable + : this.fetchSubscribable(key), + Option.some(key), + ), ), - Effect.catchDefect(() => Effect.void), + + Effect.promise(() => import("@effect/platform-browser")).pipe( + Effect.flatMap(({ BrowserStream }) => this.refreshOnWindowFocus + ? Stream.runForEach( + BrowserStream.fromEventListenerWindow("focus"), + () => this.refreshSubscribable, + ) + : Effect.void + ), + Effect.catchDefect(() => Effect.void), + ), + ], { concurrency: "unbounded" }).pipe( Effect.ignore, this.runSemaphore.withPermits(1), Effect.provide(this.context), @@ -93,13 +107,16 @@ extends Pipeable.Class implements Query { } fetch(key: K): Effect.Effect, Cause.NoSuchElementError> { - return this.interrupt.pipe( - Effect.andThen(Lens.set(this.latestKey, Option.some(key))), - Effect.andThen(this.startCached({ + return Effect.gen({ self: this }, function*() { + yield* this.interrupt + yield* Lens.set(this.key, key) + + const state = yield* this.startCached({ key, - result: AsyncResult.initial(false) - })), - Effect.flatMap(state => this.watch(state)), + result: AsyncResult.initial(false), + }) + return yield* this.watch(state) + }).pipe( Effect.provide(this.context), ) } @@ -108,29 +125,39 @@ extends Pipeable.Class implements Query { Subscribable.Subscribable>, Cause.NoSuchElementError > { - return this.interrupt.pipe( - Effect.andThen(Lens.set(this.latestKey, Option.some(key))), - Effect.andThen(this.startCached({ + return Effect.gen({ self: this }, function*() { + yield* this.interrupt + yield* Lens.set(this.key, key) + + const state = yield* this.startCached({ key, - result: AsyncResult.initial(false) - })), - Effect.tap(state => Effect.forkScoped(this.watch(state))), + result: AsyncResult.initial(false), + }) + + yield* Effect.forkScoped(this.watch(state)) + return state + }).pipe( Effect.provide(this.context), ) } get refresh(): Effect.Effect, Cause.NoSuchElementError> { - return this.interrupt.pipe( - Effect.andThen(Effect.Do), - Effect.bind("latestKey", () => Effect.flatMap(Lens.get(this.latestKey), Effect.fromOption)), - Effect.bind("latestFinalState", () => Lens.get(this.latestFinalState)), - Effect.bind("subscribable", ({ latestKey, latestFinalState }) => - this.startCached(Option.getOrElse(latestFinalState, () => ({ - key: latestKey, - result: AsyncResult.initial(true), - }))) - ), - Effect.flatMap(({ subscribable }) => this.watch(subscribable)), + return Effect.gen({ self: this }, function*() { + yield* this.interrupt + const latestKey = yield* Lens.get(this.key) + const latestFinalState = yield* Lens.get(this.latestFinalState) + + const state = yield* this.startCached( + Option.isSome(latestFinalState) && this.keyEquivalence(latestKey, latestFinalState.value.key) + ? latestFinalState.value + : { + key: latestKey, + result: AsyncResult.initial(false), + } + ) + + return yield* this.watch(state) + }).pipe( Effect.provide(this.context), ) } @@ -139,18 +166,23 @@ extends Pipeable.Class implements Query { Subscribable.Subscribable>, Cause.NoSuchElementError > { - return this.interrupt.pipe( - Effect.andThen(Effect.Do), - Effect.bind("latestKey", () => Effect.flatMap(Lens.get(this.latestKey), Effect.fromOption)), - Effect.bind("latestFinalState", () => Lens.get(this.latestFinalState)), - Effect.bind("subscribable", ({ latestKey, latestFinalState }) => - this.startCached(Option.getOrElse(latestFinalState, () => ({ - key: latestKey, - result: AsyncResult.initial(true), - }))) - ), - Effect.tap(({ subscribable }) => Effect.forkScoped(this.watch(subscribable))), - Effect.map(({ subscribable }) => subscribable), + return Effect.gen({ self: this }, function*() { + yield* this.interrupt + const latestKey = yield* Lens.get(this.key) + const latestFinalState = yield* Lens.get(this.latestFinalState) + + const state = yield* this.startCached( + Option.isSome(latestFinalState) && this.keyEquivalence(latestKey, latestFinalState.value.key) + ? latestFinalState.value + : { + key: latestKey, + result: AsyncResult.initial(false), + } + ) + + yield* Effect.forkScoped(this.watch(state)) + return state + }).pipe( Effect.provide(this.context), ) } @@ -266,18 +298,20 @@ extends Pipeable.Class implements Query { watch( subscribable: Subscribable.Subscribable> ): Effect.Effect, never, QueryClient.QueryClient> { - return subscribable.get.pipe( - Effect.flatMap(initial => Stream.runFoldEffect( + return Effect.gen({ self: this }, function*() { + const initial = yield* subscribable.get + const final = yield* Stream.runFoldEffect( subscribable.changes, () => initial, (_, state) => Effect.as(Lens.set(this.state, state), state), - ) as Effect.Effect>), - Effect.tap(state => Lens.set(this.latestFinalState, Option.some(state))), - Effect.tap(state => AsyncResult.isSuccess(state.result) - ? this.setCacheEntry(state.key, state.result) - : Effect.void - ), - ) + ) as Effect.Effect> + + yield* Lens.set(this.latestFinalState, Option.some(final)) + if (AsyncResult.isSuccess(final.result)) + yield* this.setCacheEntry(final.key, final.result) + + return final + }) } makeCacheKey(key: K): QueryClient.QueryClientCacheKey { @@ -329,8 +363,10 @@ extends Pipeable.Class implements Query { export declare namespace make { export interface Options { - readonly f: (key: K) => Effect.Effect + readonly key: Lens.Lens, readonly keyEquivalence?: Equivalence.Equivalence, + readonly f: (key: K) => Effect.Effect + readonly staleTime?: Duration.Input readonly refreshOnWindowFocus?: boolean } @@ -347,16 +383,19 @@ export const make = Effect.fnUntraced(function* ( return new QueryImpl( yield* Effect.context(), - options.f, + options.key, options.keyEquivalence ?? Equal.asEquivalence(), + options.f, options.staleTime ? yield* Effect.fromOption(Duration.fromInput(options.staleTime)) : client.defaultStaleTime, options.refreshOnWindowFocus ?? client.defaultRefreshOnWindowFocus, - Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none())), Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none>())), - Lens.fromSubscriptionRef(yield* SubscriptionRef.make>(AsyncResult.initial())), - Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none | AsyncResult.Failure>())), + Lens.fromSubscriptionRef(yield* SubscriptionRef.make>({ + key: yield* Lens.get(options.key), + result: AsyncResult.initial(false), + })), + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none>())), yield* Semaphore.make(1), ) -- 2.52.0 From 23cdfb389488c5530e57934764f4829e1195bfb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 17 Jul 2026 01:15:53 +0200 Subject: [PATCH 050/133] Refactor Query --- packages/effect-fc-next/src/Query.ts | 46 +++++++++++++++------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/packages/effect-fc-next/src/Query.ts b/packages/effect-fc-next/src/Query.ts index 9b5007b..e9a155b 100644 --- a/packages/effect-fc-next/src/Query.ts +++ b/packages/effect-fc-next/src/Query.ts @@ -53,7 +53,7 @@ extends Pipeable.Class implements Query { constructor( readonly context: Context.Context, - readonly key: Lens.Lens, + readonly key: Subscribable.Subscribable, readonly keyEquivalence: Equivalence.Equivalence, readonly f: (key: K) => Effect.Effect, @@ -71,15 +71,23 @@ extends Pipeable.Class implements Query { get run(): Effect.Effect { return Effect.all([ - Stream.runFoldEffect( + Stream.runForEach( this.key.changes, - () => Option.none(), - (previous, key) => Effect.as( - Option.isSome(previous) && this.keyEquivalence(key, previous.value) - ? this.refreshSubscribable - : this.fetchSubscribable(key), - Option.some(key), - ), + key => Effect.gen({ self: this }, function*() { + yield* this.interrupt + const latestFinalState = yield* Lens.get(this.latestFinalState) + + const state = yield* this.startCached( + Option.isSome(latestFinalState) && this.keyEquivalence(key, latestFinalState.value.key) + ? latestFinalState.value + : { + key, + result: AsyncResult.initial(false), + } + ) + + yield* Effect.forkScoped(this.watch(state)) + }), ), Effect.promise(() => import("@effect/platform-browser")).pipe( @@ -109,8 +117,6 @@ extends Pipeable.Class implements Query { fetch(key: K): Effect.Effect, Cause.NoSuchElementError> { return Effect.gen({ self: this }, function*() { yield* this.interrupt - yield* Lens.set(this.key, key) - const state = yield* this.startCached({ key, result: AsyncResult.initial(false), @@ -127,8 +133,6 @@ extends Pipeable.Class implements Query { > { return Effect.gen({ self: this }, function*() { yield* this.interrupt - yield* Lens.set(this.key, key) - const state = yield* this.startCached({ key, result: AsyncResult.initial(false), @@ -144,14 +148,14 @@ extends Pipeable.Class implements Query { get refresh(): Effect.Effect, Cause.NoSuchElementError> { return Effect.gen({ self: this }, function*() { yield* this.interrupt - const latestKey = yield* Lens.get(this.key) + const latestState = yield* Lens.get(this.state) const latestFinalState = yield* Lens.get(this.latestFinalState) const state = yield* this.startCached( - Option.isSome(latestFinalState) && this.keyEquivalence(latestKey, latestFinalState.value.key) + Option.isSome(latestFinalState) && this.keyEquivalence(latestState.key, latestFinalState.value.key) ? latestFinalState.value : { - key: latestKey, + key: latestState.key, result: AsyncResult.initial(false), } ) @@ -168,14 +172,14 @@ extends Pipeable.Class implements Query { > { return Effect.gen({ self: this }, function*() { yield* this.interrupt - const latestKey = yield* Lens.get(this.key) + const latestState = yield* Lens.get(this.state) const latestFinalState = yield* Lens.get(this.latestFinalState) const state = yield* this.startCached( - Option.isSome(latestFinalState) && this.keyEquivalence(latestKey, latestFinalState.value.key) + Option.isSome(latestFinalState) && this.keyEquivalence(latestState.key, latestFinalState.value.key) ? latestFinalState.value : { - key: latestKey, + key: latestState.key, result: AsyncResult.initial(false), } ) @@ -363,7 +367,7 @@ extends Pipeable.Class implements Query { export declare namespace make { export interface Options { - readonly key: Lens.Lens, + readonly key: Subscribable.Subscribable, readonly keyEquivalence?: Equivalence.Equivalence, readonly f: (key: K) => Effect.Effect @@ -392,7 +396,7 @@ export const make = Effect.fnUntraced(function* ( Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none>())), Lens.fromSubscriptionRef(yield* SubscriptionRef.make>({ - key: yield* Lens.get(options.key), + key: yield* options.key.get, result: AsyncResult.initial(false), })), Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none>())), -- 2.52.0 From 3fc3904c6cfe336e5ea61ab88c25bf3dfa86eb09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 17 Jul 2026 21:25:07 +0200 Subject: [PATCH 051/133] Upgrade effect-lens --- bun.lock | 21 ++++--- packages/effect-fc-next/package.json | 7 ++- packages/effect-fc-next/src/Form.ts | 38 ++++++------- packages/effect-fc-next/src/Mutation.ts | 28 +++++----- packages/effect-fc-next/src/Query.ts | 56 +++++++++---------- packages/effect-fc-next/src/QueryClient.ts | 4 +- packages/effect-fc-next/src/Result.ts | 7 ++- .../effect-fc-next/src/SubmittableForm.ts | 20 +++---- packages/effect-fc-next/src/Subscribable.ts | 53 ------------------ .../effect-fc-next/src/SynchronizedForm.ts | 18 +++--- packages/effect-fc-next/src/View.ts | 40 +++++++++++++ packages/effect-fc-next/src/index.ts | 2 +- .../{Subscribable.test.tsx => View.test.tsx} | 49 +++------------- packages/example-next/src/main.tsx | 4 +- 14 files changed, 154 insertions(+), 193 deletions(-) delete mode 100644 packages/effect-fc-next/src/Subscribable.ts create mode 100644 packages/effect-fc-next/src/View.ts rename packages/effect-fc-next/test/{Subscribable.test.tsx => View.test.tsx} (60%) diff --git a/bun.lock b/bun.lock index 69dcdf1..91d7915 100644 --- a/bun.lock +++ b/bun.lock @@ -58,17 +58,18 @@ "name": "effect-fc-next", "version": "0.1.0-beta.0", "dependencies": { - "effect-lens": "2.0.0-beta.0", + "effect-lens": "^2.0.0-beta.1", }, "devDependencies": { - "@effect/platform-browser": "^4.0.0-beta.85", + "@effect/platform-browser": "4.0.0-beta.98", "@testing-library/react": "^16.3.0", + "effect": "4.0.0-beta.98", "jsdom": "^26.1.0", "vitest": "^3.2.4", }, "peerDependencies": { "@types/react": "^19.2.0", - "effect": "4.0.0-beta.85", + "effect": "4.0.0-beta.98", "react": "^19.2.0", }, }, @@ -3063,11 +3064,11 @@ "dot-prop/is-obj": ["is-obj@2.0.0", "", {}, "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="], - "effect-fc-next/@effect/platform-browser": ["@effect/platform-browser@4.0.0-beta.87", "", { "dependencies": { "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^4.0.0-beta.87" } }, "sha512-sBUUdRt5PjFPLI7+ayxEX/jgnjCbOuc3I//98CmqiwhfgWbCMZ/rofYYb8Zk5tLpNsmdHnWgHWTsBLvH2SFrCw=="], + "effect-fc-next/@effect/platform-browser": ["@effect/platform-browser@4.0.0-beta.98", "", { "dependencies": { "multipasta": "^0.2.8" }, "peerDependencies": { "effect": "^4.0.0-beta.98" } }, "sha512-Y0BEe0N8clKmJTHqmZpS85pLZbqAxP5uV3IYF0tDw/1dcs8AbTRHjbRmOLdyLtmU32NBVAiaUD3Vopb5Et5R/g=="], - "effect-fc-next/effect": ["effect@4.0.0-beta.85", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.8.0", "find-my-way-ts": "^0.1.6", "ini": "^7.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^2.0.1", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^14.0.0", "yaml": "^2.9.0" } }, "sha512-Cjv9YQyv4CiIccmRAQIWAoeESCpCpiuHYY8zb5vqiYs3Ac2yE5RQAnKq7z4Ir/3VJYZb8kxh6rS8czDDnpTdkQ=="], + "effect-fc-next/effect": ["effect@4.0.0-beta.98", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.9.0", "find-my-way-ts": "^0.1.6", "ini": "^7.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^2.0.4", "multipasta": "^0.2.8", "toml": "^4.1.2", "uuid": "^14.0.1", "yaml": "^2.9.0" } }, "sha512-oz+bsG5h+6RNrw4t5GMfQrk/xBS8ROoqkYsuvRhBr5O7mCOrpvH/hbw+QrDzvKIpX4HJClwm86F94c87W0sJxg=="], - "effect-fc-next/effect-lens": ["effect-lens@2.0.0-beta.0", "", { "peerDependencies": { "effect": "4.0.0-beta.85" } }, "sha512-ntsRQSrzoX+GsNpkNVEGqebl/g1YOEDPIFRAHigf+4P6AtGnnOFcklqDHLrbm4zqnaxuWEgQvvn1J6S3eyC4yw=="], + "effect-fc-next/effect-lens": ["effect-lens@2.0.0-beta.1", "", { "peerDependencies": { "effect": "4.0.0-beta.98" } }, "sha512-3MwsrezPczZHH4Tl2SvliZdiNRq9e/XbH+Vr1ckr/FPCEp6Wv8IYQL4Ie/hhI+XBiLlxbQERbgL8FlHW21avtw=="], "esrecurse/estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], @@ -3373,10 +3374,16 @@ "csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="], - "effect-fc-next/effect/fast-check": ["fast-check@4.8.0", "", { "dependencies": { "pure-rand": "^8.0.0" } }, "sha512-GOJ158CUMnN6cSahsv4+ExARvIDuzzinFjkp0E9WtiBa5zcVeLozVkWaE4IzFcc+Y48Wp1EDlUZsXRyAztQcSg=="], + "effect-fc-next/@effect/platform-browser/multipasta": ["multipasta@0.2.8", "", {}, "sha512-ZPWuMKyv0cSO29f7hozp+k6+crZbQijV8ipMvxNxRf2SwtYGTX1ZX89Kd20VV4H9Znonx+EQn+iy1wGQsJ+b+Q=="], + + "effect-fc-next/effect/fast-check": ["fast-check@4.9.0", "", { "dependencies": { "pure-rand": "^8.0.0" } }, "sha512-7ms6T7SybUev/PQITciI0yLM2pOSFy5zpG8Ty7tQofcVaQUvrMXp6CBwqF6fThLCLOrfBtuHAtwq6Yu4XPCllg=="], "effect-fc-next/effect/msgpackr": ["msgpackr@2.0.4", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.4" } }, "sha512-o1C5KRmuRt+apqMr1HuGSqWStZoRBUpEsCsl15uM9VdAF1qHLtvMOU2En747EnTyEl6c4pzPewRMFF31s1CNbA=="], + "effect-fc-next/effect/multipasta": ["multipasta@0.2.8", "", {}, "sha512-ZPWuMKyv0cSO29f7hozp+k6+crZbQijV8ipMvxNxRf2SwtYGTX1ZX89Kd20VV4H9Znonx+EQn+iy1wGQsJ+b+Q=="], + + "effect-fc-next/effect/toml": ["toml@4.3.0", "", {}, "sha512-lVb8X9BsPVuH0M4BKeS91tXAmJvCjQ5UIyAbQFaxkKGyUFK2RPkhwaFSQH8vbpl1d23eu/IBH+dwVMHWaq9A5A=="], + "express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "file-loader/schema-utils/ajv": ["ajv@6.15.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw=="], diff --git a/packages/effect-fc-next/package.json b/packages/effect-fc-next/package.json index fa29dbf..2375e0e 100644 --- a/packages/effect-fc-next/package.json +++ b/packages/effect-fc-next/package.json @@ -39,17 +39,18 @@ "clean:modules": "rm -rf node_modules" }, "devDependencies": { - "@effect/platform-browser": "^4.0.0-beta.85", + "@effect/platform-browser": "4.0.0-beta.98", "@testing-library/react": "^16.3.0", + "effect": "4.0.0-beta.98", "jsdom": "^26.1.0", "vitest": "^3.2.4" }, "peerDependencies": { "@types/react": "^19.2.0", - "effect": "4.0.0-beta.85", + "effect": "4.0.0-beta.98", "react": "^19.2.0" }, "dependencies": { - "effect-lens": "2.0.0-beta.0" + "effect-lens": "^2.0.0-beta.1" } } diff --git a/packages/effect-fc-next/src/Form.ts b/packages/effect-fc-next/src/Form.ts index 30039aa..e3ce5ea 100644 --- a/packages/effect-fc-next/src/Form.ts +++ b/packages/effect-fc-next/src/Form.ts @@ -2,7 +2,7 @@ import { Array, type Cause, Chunk, type Duration, Effect, Equal, Function, ident import type * as React from "react" import * as Component from "./Component.js" import * as Lens from "./Lens.js" -import * as Subscribable from "./Subscribable.js" +import * as View from "./View.js" export const FormTypeId: unique symbol = Symbol.for("@effect-fc/Form/Form") @@ -18,12 +18,12 @@ extends Pipeable.Pipeable { readonly [FormTypeId]: FormTypeId readonly path: P - readonly value: Subscribable.Subscribable, ER, never> + readonly value: View.View, ER, never> readonly encodedValue: Lens.Lens - readonly issues: Subscribable.Subscribable - readonly isValidating: Subscribable.Subscribable - readonly canCommit: Subscribable.Subscribable - readonly isCommitting: Subscribable.Subscribable + readonly issues: View.View + readonly isValidating: View.View + readonly canCommit: View.View + readonly isCommitting: View.View } export class FormImpl @@ -32,12 +32,12 @@ extends Pipeable.Class implements Form { constructor( readonly path: P, - readonly value: Subscribable.Subscribable, ER, never>, + readonly value: View.View, ER, never>, readonly encodedValue: Lens.Lens, - readonly issues: Subscribable.Subscribable, - readonly isValidating: Subscribable.Subscribable, - readonly canCommit: Subscribable.Subscribable, - readonly isCommitting: Subscribable.Subscribable, + readonly issues: View.View, + readonly isValidating: View.View, + readonly canCommit: View.View, + readonly isCommitting: View.View, ) { super() } @@ -71,9 +71,9 @@ export const focusObjectOn: { return new FormImpl( path, - Subscribable.mapOption(form.value, a => a[key]), + View.mapOption(form.value, a => a[key]), Lens.focusObjectOn(form.encodedValue, key), - Subscribable.map(form.issues, issues => filterIssuesByPath(issues, path)), + View.map(form.issues, issues => filterIssuesByPath(issues, path)), form.isValidating, form.canCommit, form.isCommitting, @@ -97,9 +97,9 @@ export const focusArrayAt: { return new FormImpl( path, - Subscribable.mapOptionEffect(form.value, values => Effect.fromOption(Array.get(values, index))), + View.mapOptionEffect(form.value, values => Effect.fromOption(Array.get(values, index))), Lens.focusArrayAt(form.encodedValue, index), - Subscribable.map(form.issues, issues => filterIssuesByPath(issues, path)), + View.map(form.issues, issues => filterIssuesByPath(issues, path)), form.isValidating, form.canCommit, form.isCommitting, @@ -123,9 +123,9 @@ export const focusTupleAt: { return new FormImpl( path, - Subscribable.mapOption(form.value, values => values[index]), + View.mapOption(form.value, values => values[index]), Lens.focusTupleAt(form.encodedValue, index), - Subscribable.map(form.issues, issues => filterIssuesByPath(issues, path)), + View.map(form.issues, issues => filterIssuesByPath(issues, path)), form.isValidating, form.canCommit, form.isCommitting, @@ -149,9 +149,9 @@ export const focusChunkAt: { return new FormImpl( path, - Subscribable.mapOptionEffect(form.value, values => Effect.fromOption(Chunk.get(values, index))), + View.mapOptionEffect(form.value, values => Effect.fromOption(Chunk.get(values, index))), Lens.focusChunkAt(form.encodedValue, index), - Subscribable.map(form.issues, issues => filterIssuesByPath(issues, path)), + View.map(form.issues, issues => filterIssuesByPath(issues, path)), form.isValidating, form.canCommit, form.isCommitting, diff --git a/packages/effect-fc-next/src/Mutation.ts b/packages/effect-fc-next/src/Mutation.ts index 6ad8140..3e13174 100644 --- a/packages/effect-fc-next/src/Mutation.ts +++ b/packages/effect-fc-next/src/Mutation.ts @@ -1,26 +1,26 @@ import { type Context, Effect, Equal, Exit, type Fiber, Option, Pipeable, Predicate, type Scope, Stream, SubscriptionRef } from "effect" import { AsyncResult } from "effect/unstable/reactivity" import * as Lens from "./Lens.js" -import type * as Subscribable from "./Subscribable.js" +import * as View from "./View.js" export const MutationTypeId: unique symbol = Symbol.for("@effect-fc/Mutation/Mutation") export type MutationTypeId = typeof MutationTypeId -export interface Mutation +export interface Mutation extends Pipeable.Pipeable { readonly [MutationTypeId]: MutationTypeId readonly context: Context.Context readonly f: (key: K) => Effect.Effect - readonly latestKey: Subscribable.Subscribable> - readonly fiber: Subscribable.Subscribable>> - readonly result: Subscribable.Subscribable> - readonly latestFinalResult: Subscribable.Subscribable | AsyncResult.Failure>> + readonly latestKey: View.View> + readonly fiber: View.View>> + readonly state: View.View> + readonly latestFinalResult: View.View | AsyncResult.Failure>> mutate(key: K): Effect.Effect | AsyncResult.Failure> - mutateSubscribable(key: K): Effect.Effect>> + mutateView(key: K): Effect.Effect>> } export const isMutation = (u: unknown): u is Mutation => Predicate.hasProperty(u, MutationTypeId) @@ -36,7 +36,7 @@ extends Pipeable.Class implements Mutation { readonly latestKey: Lens.Lens>, readonly fiber: Lens.Lens>>, - readonly result: Lens.Lens>, + readonly state: Lens.Lens>, readonly latestFinalResult: Lens.Lens | AsyncResult.Failure>>, ) { super() @@ -49,7 +49,7 @@ extends Pipeable.Class implements Mutation { Effect.provide(this.context), ) } - mutateSubscribable(key: K): Effect.Effect>> { + mutateView(key: K): Effect.Effect>> { return Lens.set(this.latestKey, Option.some(key)).pipe( Effect.andThen(this.start(key)), Effect.tap(state => Effect.forkScoped(this.watch(state))), @@ -58,7 +58,7 @@ extends Pipeable.Class implements Mutation { } start(key: K): Effect.Effect< - Subscribable.Subscribable>, + View.View>, never, Scope.Scope | R > { @@ -114,13 +114,13 @@ extends Pipeable.Class implements Mutation { } watch( - state: Subscribable.Subscribable> + state: View.View> ): Effect.Effect | AsyncResult.Failure> { - return state.get.pipe( + return View.get(state).pipe( Effect.andThen(initial => Stream.runFoldEffect( - state.changes, + View.changes(state), () => initial, - (_, result) => Effect.as(Lens.set(this.result, result), result), + (_, result) => Effect.as(Lens.set(this.state, result), result), ) as Effect.Effect | AsyncResult.Failure>), Effect.tap(result => Lens.set(this.latestFinalResult, Option.some(result))), ) diff --git a/packages/effect-fc-next/src/Query.ts b/packages/effect-fc-next/src/Query.ts index e9a155b..ff84519 100644 --- a/packages/effect-fc-next/src/Query.ts +++ b/packages/effect-fc-next/src/Query.ts @@ -2,44 +2,44 @@ import { type Cause, type Context, Duration, Effect, Equal, type Equivalence, Ex import { AsyncResult } from "effect/unstable/reactivity" import * as Lens from "./Lens.js" import * as QueryClient from "./QueryClient.js" -import * as Subscribable from "./Subscribable.js" +import * as View from "./View.js" export const QueryTypeId: unique symbol = Symbol.for("@effect-fc/Query/Query") export type QueryTypeId = typeof QueryTypeId -export interface Query +export interface Query extends Pipeable.Pipeable { readonly [QueryTypeId]: QueryTypeId readonly context: Context.Context - readonly key: Subscribable.Subscribable + readonly key: View.View readonly keyEquivalence: Equivalence.Equivalence readonly f: (key: K) => Effect.Effect readonly staleTime: Duration.Duration readonly refreshOnWindowFocus: boolean - readonly fiber: Subscribable.Subscribable>> - readonly state: Subscribable.Subscribable> - readonly latestFinalState: Subscribable.Subscribable>> + readonly fiber: View.View>> + readonly state: View.View> + readonly latestFinalState: View.View>> readonly run: Effect.Effect fetch(key: K): Effect.Effect, Cause.NoSuchElementError> - fetchSubscribable(key: K): Effect.Effect>, Cause.NoSuchElementError> + fetchView(key: K): Effect.Effect>, Cause.NoSuchElementError> readonly refresh: Effect.Effect, Cause.NoSuchElementError> - readonly refreshSubscribable: Effect.Effect>, Cause.NoSuchElementError> + readonly refreshView: Effect.Effect>, Cause.NoSuchElementError> readonly invalidateCache: Effect.Effect invalidateCacheEntry(key: K): Effect.Effect } -export interface QueryState { +export interface QueryState { readonly key: K readonly result: AsyncResult.AsyncResult } -export interface FinalQueryState { +export interface FinalQueryState { readonly key: K readonly result: AsyncResult.Success | AsyncResult.Failure } @@ -53,7 +53,7 @@ extends Pipeable.Class implements Query { constructor( readonly context: Context.Context, - readonly key: Subscribable.Subscribable, + readonly key: View.View, readonly keyEquivalence: Equivalence.Equivalence, readonly f: (key: K) => Effect.Effect, @@ -94,7 +94,7 @@ extends Pipeable.Class implements Query { Effect.flatMap(({ BrowserStream }) => this.refreshOnWindowFocus ? Stream.runForEach( BrowserStream.fromEventListenerWindow("focus"), - () => this.refreshSubscribable, + () => this.refreshView, ) : Effect.void ), @@ -127,8 +127,8 @@ extends Pipeable.Class implements Query { ) } - fetchSubscribable(key: K): Effect.Effect< - Subscribable.Subscribable>, + fetchView(key: K): Effect.Effect< + View.View>, Cause.NoSuchElementError > { return Effect.gen({ self: this }, function*() { @@ -166,8 +166,8 @@ extends Pipeable.Class implements Query { ) } - get refreshSubscribable(): Effect.Effect< - Subscribable.Subscribable>, + get refreshView(): Effect.Effect< + View.View>, Cause.NoSuchElementError > { return Effect.gen({ self: this }, function*() { @@ -194,7 +194,7 @@ extends Pipeable.Class implements Query { startCached( previous: QueryState, ): Effect.Effect< - Subscribable.Subscribable>, + View.View>, Cause.NoSuchElementError, Scope.Scope | QueryClient.QueryClient | R > { @@ -206,7 +206,7 @@ extends Pipeable.Class implements Query { key: previous.key, result: entry.result as AsyncResult.AsyncResult, }) - : Effect.succeed(Subscribable.make({ + : Effect.succeed(View.make({ get: Effect.succeed({ key: previous.key, result: entry.result as AsyncResult.AsyncResult, @@ -226,15 +226,15 @@ extends Pipeable.Class implements Query { start( previous: QueryState, ): Effect.Effect< - Subscribable.Subscribable>, + View.View>, never, Scope.Scope | R > { return Effect.gen({ self: this }, function*() { - const subscribable = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(previous)) + const state = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(previous)) const fiber = yield* Effect.forkScoped(Effect.andThen( - Lens.update(subscribable, previous => AsyncResult.match(previous.result, { + Lens.update(state, previous => AsyncResult.match(previous.result, { onInitial: () => ({ key: previous.key, result: AsyncResult.initial(true), @@ -255,7 +255,7 @@ extends Pipeable.Class implements Query { })), Effect.onExit(this.f(previous.key), exit => Lens.update( - subscribable, + state, previous => Exit.match(exit, { onSuccess: v => ({ key: previous.key, @@ -295,17 +295,17 @@ extends Pipeable.Class implements Query { )) yield* Lens.set(this.fiber, Option.some(fiber)) - return subscribable + return state }) } watch( - subscribable: Subscribable.Subscribable> + view: View.View> ): Effect.Effect, never, QueryClient.QueryClient> { return Effect.gen({ self: this }, function*() { - const initial = yield* subscribable.get + const initial = yield* View.get(view) const final = yield* Stream.runFoldEffect( - subscribable.changes, + View.changes(view), () => initial, (_, state) => Effect.as(Lens.set(this.state, state), state), ) as Effect.Effect> @@ -367,7 +367,7 @@ extends Pipeable.Class implements Query { export declare namespace make { export interface Options { - readonly key: Subscribable.Subscribable, + readonly key: View.View, readonly keyEquivalence?: Equivalence.Equivalence, readonly f: (key: K) => Effect.Effect @@ -396,7 +396,7 @@ export const make = Effect.fnUntraced(function* ( Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none>())), Lens.fromSubscriptionRef(yield* SubscriptionRef.make>({ - key: yield* options.key.get, + key: yield* View.get(options.key), result: AsyncResult.initial(false), })), Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none>())), diff --git a/packages/effect-fc-next/src/QueryClient.ts b/packages/effect-fc-next/src/QueryClient.ts index 2405cbd..946e45f 100644 --- a/packages/effect-fc-next/src/QueryClient.ts +++ b/packages/effect-fc-next/src/QueryClient.ts @@ -1,7 +1,7 @@ import { type Cause, Context, DateTime, Duration, Effect, Equal, Equivalence, Hash, HashMap, type Option, Pipeable, Predicate, Schedule, type Scope, Semaphore, SubscriptionRef } from "effect" import type { AsyncResult } from "effect/unstable/reactivity" import * as Lens from "./Lens.js" -import type * as Subscribable from "./Subscribable.js" +import type * as View from "./View.js" export const QueryClientServiceTypeId: unique symbol = Symbol.for("@effect-fc/QueryClient/QueryClientService") @@ -10,7 +10,7 @@ export type QueryClientServiceTypeId = typeof QueryClientServiceTypeId export interface QueryClientService extends Pipeable.Pipeable { readonly [QueryClientServiceTypeId]: QueryClientServiceTypeId - readonly cache: Subscribable.Subscribable> + readonly cache: View.View> readonly cacheGcTime: Duration.Duration readonly defaultStaleTime: Duration.Duration readonly defaultRefreshOnWindowFocus: boolean diff --git a/packages/effect-fc-next/src/Result.ts b/packages/effect-fc-next/src/Result.ts index 672ae18..1c92d99 100644 --- a/packages/effect-fc-next/src/Result.ts +++ b/packages/effect-fc-next/src/Result.ts @@ -1,5 +1,6 @@ import { Cause, Context, Data, Effect, Equal, Exit, type Fiber, Hash, Layer, Match, Pipeable, Predicate, pipe, type Scope, SubscriptionRef } from "effect" -import { Lens, Subscribable } from "effect-lens" +import { Lens } from "effect-lens" +import type { View } from "effect-lens/View" export const ResultTypeId: unique symbol = Symbol.for("@effect-fc/Result/Result") @@ -207,7 +208,7 @@ export const unsafeForkEffect = Effect.fnUntraced(function* effect: Effect.Effect, options?: unsafeForkEffect.Options, NoInfer, P>, ): Effect.fn.Return< - readonly [result: Subscribable.Subscribable, never, never>, fiber: Fiber.Fiber], + readonly [result: View, never, never>, fiber: Fiber.Fiber], never, Scope.Scope | unsafeForkEffect.OutputContext > { @@ -242,7 +243,7 @@ export const forkEffect: { effect: Effect.Effect>>, options?: forkEffect.Options, NoInfer, P>, ): Effect.Effect< - readonly [result: Subscribable.Subscribable, never, never>, fiber: Fiber.Fiber], + readonly [result: View, never, never>, fiber: Fiber.Fiber], never, Scope.Scope | forkEffect.OutputContext > diff --git a/packages/effect-fc-next/src/SubmittableForm.ts b/packages/effect-fc-next/src/SubmittableForm.ts index 79dfe46..a08b59c 100644 --- a/packages/effect-fc-next/src/SubmittableForm.ts +++ b/packages/effect-fc-next/src/SubmittableForm.ts @@ -18,7 +18,7 @@ import * as Form from "./Form.js" import * as Lens from "./Lens.js" import * as Mutation from "./Mutation.js" import * as Result from "./Result.js" -import * as Subscribable from "./Subscribable.js" +import * as View from "./View.js" type FormSchema = Schema.Top & { @@ -39,7 +39,7 @@ extends Form.Form { readonly [value: A, form: SubmittableForm], MA, ME, MR, MP > - readonly validationFiber: Subscribable.Subscribable>, never, never> + readonly validationFiber: View.View>, never, never> readonly run: Effect.Effect readonly submit: Effect.Effect>, Cause.NoSuchElementError> } @@ -50,9 +50,9 @@ extends Pipeable.Class implements SubmittableForm { readonly [SubmittableFormTypeId]: SubmittableFormTypeId = SubmittableFormTypeId readonly path = [] as const readonly encodedValue: Lens.Lens - readonly isValidating: Subscribable.Subscribable - readonly canCommit: Subscribable.Subscribable - readonly isCommitting: Subscribable.Subscribable + readonly isValidating: View.View + readonly canCommit: View.View + readonly isCommitting: View.View constructor( readonly schema: FormSchema, @@ -77,26 +77,26 @@ extends Pipeable.Class implements SubmittableForm { ), lock: Lens.asLensImpl(internalEncodedValue).lock, }) - this.isValidating = Subscribable.map(validationFiber, Option.isSome) - const commitState = Subscribable.zipLatestAll( + this.isValidating = View.map(validationFiber, Option.isSome) + const commitState = View.zipLatestAll( value as any, issues as any, validationFiber as any, mutation.result as any, - ) as unknown as Subscribable.Subscribable, readonly Form.FormIssue[], Option.Option>, Result.Result, ]> - this.canCommit = Subscribable.map( + this.canCommit = View.map( commitState, ([current, currentIssues, fiber, result]: readonly [Option.Option, readonly Form.FormIssue[], Option.Option>, Result.Result]) => Option.isSome(current) && currentIssues.length === 0 && Option.isNone(fiber) && !(Result.isRunning(result) || Result.hasRefreshingFlag(result)), ) - this.isCommitting = Subscribable.map( + this.isCommitting = View.map( mutation.result, result => Result.isRunning(result) || Result.hasRefreshingFlag(result), ) diff --git a/packages/effect-fc-next/src/Subscribable.ts b/packages/effect-fc-next/src/Subscribable.ts deleted file mode 100644 index e8fafe6..0000000 --- a/packages/effect-fc-next/src/Subscribable.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Effect, Equivalence, Stream } from "effect" -import { Subscribable } from "effect-lens" -import * as React from "react" -import * as Component from "./Component.js" - - -export * from "effect-lens/Subscribable" - -export const zipLatestAll = []>( - ...elements: T -): Subscribable.Subscribable< - [T[number]] extends [never] - ? never - : { [K in keyof T]: T[K] extends Subscribable.Subscribable ? A : never }, - [T[number]] extends [never] ? never : T[number] extends Subscribable.Subscribable ? E : never, - [T[number]] extends [never] ? never : T[number] extends Subscribable.Subscribable ? R : never -> => Subscribable.make({ - get: Effect.all(elements.map(v => v.get)), - changes: Stream.zipLatestAll(...elements.map(v => v.changes)), -}) as any - -export declare namespace useAll { - export type Success[]> = [T[number]] extends [never] - ? never - : { [K in keyof T]: T[K] extends Subscribable.Subscribable ? A : never } - - export interface Options { - readonly equivalence?: Equivalence.Equivalence - } -} - -export const useAll = Effect.fnUntraced(function* []>( - elements: T, - options?: useAll.Options>>, -): Effect.fn.Return< - useAll.Success, - [T[number]] extends [never] ? never : T[number] extends Subscribable.Subscribable ? E : never, - [T[number]] extends [never] ? never : T[number] extends Subscribable.Subscribable ? R : never -> { - const [reactStateValue, setReactStateValue] = React.useState( - yield* Component.useOnMount(() => Effect.all(elements.map(v => v.get))) - ) - - yield* Component.useReactEffect(() => Stream.zipLatestAll(...elements.map(ref => ref.changes)).pipe( - Stream.changesWith((options?.equivalence as Equivalence.Equivalence | undefined) ?? Equivalence.Array(Equivalence.strictEqual())), - Stream.runForEach(v => - Effect.sync(() => setReactStateValue(v)) - ), - Effect.forkScoped, - ), elements) - - return reactStateValue as any -}) diff --git a/packages/effect-fc-next/src/SynchronizedForm.ts b/packages/effect-fc-next/src/SynchronizedForm.ts index 330628a..96d6915 100644 --- a/packages/effect-fc-next/src/SynchronizedForm.ts +++ b/packages/effect-fc-next/src/SynchronizedForm.ts @@ -17,7 +17,7 @@ import { } from "effect" import * as Form from "./Form.js" import * as Lens from "./Lens.js" -import * as Subscribable from "./Subscribable.js" +import * as View from "./View.js" type FormSchema = Schema.Top & { @@ -43,7 +43,7 @@ export interface SynchronizedForm< readonly schema: FormSchema readonly context: Context.Context readonly target: Lens.Lens - readonly validationFiber: Subscribable.Subscribable>, never, never> + readonly validationFiber: View.View>, never, never> readonly run: Effect.Effect } @@ -59,10 +59,10 @@ export class SynchronizedFormImpl< readonly [Form.FormTypeId]: Form.FormTypeId = Form.FormTypeId readonly [SynchronizedFormTypeId]: SynchronizedFormTypeId = SynchronizedFormTypeId readonly path = [] as const - readonly value: Subscribable.Subscribable, TER, never> + readonly value: View.View, TER, never> readonly encodedValue: Lens.Lens - readonly isValidating: Subscribable.Subscribable - readonly canCommit: Subscribable.Subscribable + readonly isValidating: View.View + readonly canCommit: View.View constructor( readonly schema: FormSchema, @@ -75,7 +75,7 @@ export class SynchronizedFormImpl< readonly runSemaphore: Semaphore.Semaphore, ) { super() - this.value = Subscribable.make({ + this.value = View.make({ get: Effect.provide(Effect.map(target.get, Option.some), context), changes: Stream.provideContext( target.changes.pipe( @@ -94,13 +94,13 @@ export class SynchronizedFormImpl< ), lock: Lens.asLensImpl(internalEncodedValue).lock, }) as unknown as Lens.Lens - this.isValidating = Subscribable.map(validationFiber, Option.isSome) - const commitState = Subscribable.zipLatestAll(issues as any, validationFiber as any, isCommitting as any) as unknown as Subscribable.Subscribable>, boolean, ]> - this.canCommit = Subscribable.map( + this.canCommit = View.map( commitState, ([currentIssues, fiber, committing]) => currentIssues.length === 0 && Option.isNone(fiber) && !committing, ) diff --git a/packages/effect-fc-next/src/View.ts b/packages/effect-fc-next/src/View.ts new file mode 100644 index 0000000..04ab6f3 --- /dev/null +++ b/packages/effect-fc-next/src/View.ts @@ -0,0 +1,40 @@ +import { Effect, Equivalence, Stream } from "effect" +import { View } from "effect-lens" +import * as React from "react" +import * as Component from "./Component.js" + + +export * from "effect-lens/View" + +export declare namespace useAll { + export type Success[]> = [T[number]] extends [never] + ? never + : { [K in keyof T]: T[K] extends View.View ? A : never } + + export interface Options { + readonly equivalence?: Equivalence.Equivalence + } +} + +export const useAll = Effect.fnUntraced(function* []>( + elements: T, + options?: useAll.Options>>, +): Effect.fn.Return< + useAll.Success, + [T[number]] extends [never] ? never : T[number] extends View.View ? E : never, + [T[number]] extends [never] ? never : T[number] extends View.View ? R : never +> { + const [reactStateValue, setReactStateValue] = React.useState( + yield* Component.useOnMount(() => Effect.all(elements.map(View.get))) + ) + + yield* Component.useReactEffect(() => View.changes(View.zipLatestAll(...elements)).pipe( + Stream.changesWith((options?.equivalence as Equivalence.Equivalence | undefined) ?? Equivalence.Array(Equivalence.strictEqual())), + Stream.runForEach(v => + Effect.sync(() => setReactStateValue(v)) + ), + Effect.forkScoped, + ), elements) + + return reactStateValue as any +}) diff --git a/packages/effect-fc-next/src/index.ts b/packages/effect-fc-next/src/index.ts index 8829e81..ca7e03d 100644 --- a/packages/effect-fc-next/src/index.ts +++ b/packages/effect-fc-next/src/index.ts @@ -13,5 +13,5 @@ export * as Result from "./Result.js" export * as SetStateAction from "./SetStateAction.js" export * as Stream from "./Stream.js" export * as SubmittableForm from "./SubmittableForm.js" -export * as Subscribable from "./Subscribable.js" export * as SynchronizedForm from "./SynchronizedForm.js" +export * as View from "./View.js" diff --git a/packages/effect-fc-next/test/Subscribable.test.tsx b/packages/effect-fc-next/test/View.test.tsx similarity index 60% rename from packages/effect-fc-next/test/Subscribable.test.tsx rename to packages/effect-fc-next/test/View.test.tsx index 5641d1b..3065a8e 100644 --- a/packages/effect-fc-next/test/Subscribable.test.tsx +++ b/packages/effect-fc-next/test/View.test.tsx @@ -1,10 +1,10 @@ import { render, screen, waitFor } from "@testing-library/react" -import { Effect, Fiber, Layer, Stream, SubscriptionRef } from "effect" +import { Effect, Layer, SubscriptionRef } from "effect" import { Lens } from "effect-lens" import { describe, expect, it } from "vitest" import * as Component from "../src/Component.js" import * as ReactRuntime from "../src/ReactRuntime.js" -import * as Subscribable from "../src/Subscribable.js" +import * as View from "../src/View.js" const makeRuntime = async () => { @@ -18,42 +18,7 @@ const makeRuntime = async () => { } } -describe("Subscribable", () => { - it("zipLatestAll reads current values from all inputs", async () => { - const leftRef = await Effect.runPromise(SubscriptionRef.make(1)) - const rightRef = await Effect.runPromise(SubscriptionRef.make("a")) - const left = Lens.fromSubscriptionRef(leftRef) - const right = Lens.fromSubscriptionRef(rightRef) - - const zipped = Subscribable.zipLatestAll(left, right) - - expect(await Effect.runPromise(zipped.get)).toEqual([1, "a"]) - }) - - it("zipLatestAll emits updates when any input changes", async () => { - const leftRef = await Effect.runPromise(SubscriptionRef.make(1)) - const rightRef = await Effect.runPromise(SubscriptionRef.make("a")) - const left = Lens.fromSubscriptionRef(leftRef) - const right = Lens.fromSubscriptionRef(rightRef) - - const zipped = Subscribable.zipLatestAll(left, right) - const values: Array = [] - - const collector = Effect.runFork(Effect.scoped(zipped.changes.pipe( - Stream.runForEach(value => Effect.sync(() => { - values.push(value as readonly [number, string]) - })), - ))) - - await Effect.runPromise(Lens.set(left, 2)) - await waitFor(() => expect(values).toContainEqual([2, "a"])) - - await Effect.runPromise(Lens.set(right, "b")) - await waitFor(() => expect(values).toContainEqual([2, "b"])) - - await Effect.runPromise(Fiber.interrupt(collector)) - }) - +describe("View", () => { it("useAll returns the latest values and rerenders when any input changes", async () => { const { runtime, effectRuntime, dispose } = await makeRuntime() const countRef = await Effect.runPromise(SubscriptionRef.make(1)) @@ -61,8 +26,8 @@ describe("Subscribable", () => { const count = Lens.fromSubscriptionRef(countRef) const label = Lens.fromSubscriptionRef(labelRef) - const Probe = Component.makeUntraced("SubscribableUseAllProbe")(function*() { - const [currentCount, currentLabel] = yield* Subscribable.useAll([count, label]) + const Probe = Component.makeUntraced("ViewUseAllProbe")(function*() { + const [currentCount, currentLabel] = yield* View.useAll([count, label]) return

}).pipe( @@ -94,8 +59,8 @@ describe("Subscribable", () => { const item = Lens.fromSubscriptionRef(itemRef) const flag = Lens.fromSubscriptionRef(flagRef) - const Probe = Component.makeUntraced("SubscribableUseAllEquivalenceProbe")(function*() { - const [currentItem, currentFlag] = yield* Subscribable.useAll([item, flag], { + const Probe = Component.makeUntraced("ViewUseAllEquivalenceProbe")(function*() { + const [currentItem, currentFlag] = yield* View.useAll([item, flag], { equivalence: ([selfItem, selfFlag], [thatItem, thatFlag]) => selfItem.id === thatItem.id && selfFlag === thatFlag, }) diff --git a/packages/example-next/src/main.tsx b/packages/example-next/src/main.tsx index 125ca5b..3b4d312 100644 --- a/packages/example-next/src/main.tsx +++ b/packages/example-next/src/main.tsx @@ -1,5 +1,5 @@ import { Effect, Layer, SubscriptionRef } from "effect" -import { Component, Lens, ReactRuntime, Subscribable } from "effect-fc-next" +import { Component, Lens, ReactRuntime, View } from "effect-fc-next" import { StrictMode } from "react" import { createRoot } from "react-dom/client" import "./index.css" @@ -10,7 +10,7 @@ const Counter = Component.make("Counter")(function*() { SubscriptionRef.make(0), Lens.fromSubscriptionRef, )) - const [value] = yield* Subscribable.useAll([count]) + const [value] = yield* View.useAll([count]) const increment = yield* Component.useCallbackSync( () => Lens.update(count, n => n + 1), [count], -- 2.52.0 From cc64ae785d03e7f834d58d6c33dda759124b3c40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 19 Jul 2026 00:46:36 +0200 Subject: [PATCH 052/133] Refactor Form --- bun.lock | 637 +++++++++--------- packages/effect-fc-next/package.json | 1 + packages/effect-fc-next/src/Form.ts | 57 +- .../effect-fc-next/src/SubmittableForm.ts | 267 ++++---- 4 files changed, 478 insertions(+), 484 deletions(-) diff --git a/bun.lock b/bun.lock index 91d7915..9cee2d3 100644 --- a/bun.lock +++ b/bun.lock @@ -58,6 +58,7 @@ "name": "effect-fc-next", "version": "0.1.0-beta.0", "dependencies": { + "@standard-schema/spec": "^1.1.0", "effect-lens": "^2.0.0-beta.1", }, "devDependencies": { @@ -118,41 +119,41 @@ }, }, "packages": { - "@algolia/abtesting": ["@algolia/abtesting@1.19.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-Lhnez3hhXHk25lfxLAMxvkP4fmN3+1RgADhD2ssMDBYuAsDVReeyP+3SGRx+ntq8ijMrLqUyfvO72TB6jsTteQ=="], + "@algolia/abtesting": ["@algolia/abtesting@1.22.0", "", { "dependencies": { "@algolia/client-common": "5.56.0", "@algolia/requester-browser-xhr": "5.56.0", "@algolia/requester-fetch": "5.56.0", "@algolia/requester-node-http": "5.56.0" } }, "sha512-BFR6zNowNKcY7Ou7TaJc9QWexES4YKPbmf/OTFofpdsdhz4x6q0lbxp3duO0EHnyrN7rE4ba/TSXuY+BDGu4+g=="], - "@algolia/autocomplete-core": ["@algolia/autocomplete-core@1.19.8", "", { "dependencies": { "@algolia/autocomplete-plugin-algolia-insights": "1.19.8", "@algolia/autocomplete-shared": "1.19.8" } }, "sha512-3YEorYg44niXcm7gkft3nXYItHd44e8tmh4D33CTszPgP0QWkaLEaFywiNyJBo7UL/mqObA/G9RYuU7R8tN1IA=="], + "@algolia/autocomplete-core": ["@algolia/autocomplete-core@1.19.9", "", { "dependencies": { "@algolia/autocomplete-plugin-algolia-insights": "1.19.9", "@algolia/autocomplete-shared": "1.19.9" } }, "sha512-4U2JKLMWlDu0CotYyUkWakDxr8AIav3QtIUXXRpfavYN29aVWfzlwJp9T0rPKEf/dO2QCPAUc0Kq1Tj1GJxo2A=="], - "@algolia/autocomplete-plugin-algolia-insights": ["@algolia/autocomplete-plugin-algolia-insights@1.19.8", "", { "dependencies": { "@algolia/autocomplete-shared": "1.19.8" }, "peerDependencies": { "search-insights": ">= 1 < 3" } }, "sha512-ZvJWO8ZZJDpc1LNM2TTBdmQsZBLMR4rU5iNR2OYvEeFBiaf/0ESnRSSLQbryarJY4SVxtoz6A2ZtDMNM+iQEAA=="], + "@algolia/autocomplete-plugin-algolia-insights": ["@algolia/autocomplete-plugin-algolia-insights@1.19.9", "", { "dependencies": { "@algolia/autocomplete-shared": "1.19.9" }, "peerDependencies": { "search-insights": ">= 1 < 3" } }, "sha512-6mExC6X7762s2SV3eJy3QOkB8bdMmnUhQ2agvGVDuzwoGyr3PquGSY/0vPQXCfiAiCaXUz1rXn+lwghgSi0l0w=="], - "@algolia/autocomplete-shared": ["@algolia/autocomplete-shared@1.19.8", "", { "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, "sha512-h5hf2t8ejF6vlOgvLaZzQbWs5SyH2z4PAWygNAvvD/2RI29hdQ54ldUGwqVuj9Srs+n8XUKTPUqb7fvhBhQrnQ=="], + "@algolia/autocomplete-shared": ["@algolia/autocomplete-shared@1.19.9", "", { "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, "sha512-YosP9Uoek6y/Ur1r1qeogk4biMe/hzkyNcgMCciw0//3XpCM7VlYLSHnyt/vOnEOGhCCc0+3v+unEiH6zz+Z1A=="], - "@algolia/client-abtesting": ["@algolia/client-abtesting@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-0ZjA5Hcmaoz5Lj6OG0zhfIyeqzJZnLW2CRJA1W17UwMFGRtZAJ9yJKRvPEDA6gkpsIoQxORTSW6sWFiuYncPNQ=="], + "@algolia/client-abtesting": ["@algolia/client-abtesting@5.56.0", "", { "dependencies": { "@algolia/client-common": "5.56.0", "@algolia/requester-browser-xhr": "5.56.0", "@algolia/requester-fetch": "5.56.0", "@algolia/requester-node-http": "5.56.0" } }, "sha512-7r4Z3NC7yU1oAQVWJNA2HX7tX481F3pJvCGyLIXiTdBcthz4Q/o21jwcMYDFkuI92UWTNBQQmHYgwHo1zS5dzg=="], - "@algolia/client-analytics": ["@algolia/client-analytics@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-kWNodP75iiEaOtemC9F/hlxNBG5E2QUjN1BusnE6m2b4l7Qh/BUO3fGCVsmKJI65VO4VKGGmT43ICvHtTcJ2JQ=="], + "@algolia/client-analytics": ["@algolia/client-analytics@5.56.0", "", { "dependencies": { "@algolia/client-common": "5.56.0", "@algolia/requester-browser-xhr": "5.56.0", "@algolia/requester-fetch": "5.56.0", "@algolia/requester-node-http": "5.56.0" } }, "sha512-avmjXQSq+jadFO8Xl2em05/uQdQnEmHsJyOAdVbZkmVgpMfxL12aJwVVfGNwYr9nulcpuJN1X0lTaQ5wxuNGcA=="], - "@algolia/client-common": ["@algolia/client-common@5.53.0", "", {}, "sha512-YPN45TXD9Wrse185t/Ta7nktZsqpv97oOjCzp2sblHnCL6rBc9TDeJAg1IGl2UpdwnSD05Zu/5wLB4watOUMyg=="], + "@algolia/client-common": ["@algolia/client-common@5.56.0", "", {}, "sha512-v2TPStUhY//ripPjIVclZ8AWc7DEGooXULZGFlFu37zNatgHjw34oZZ+OSbbc/YHO+xZwPl62I1k8xH1m4S2eg=="], - "@algolia/client-insights": ["@algolia/client-insights@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-qAcYTDJE6m924FDDUQvdD6vh7DYaqOeSpFS74IP37/JRV0v4cGBauyxTF2WzDnokUylQDbqreoFIJZfg0Fitmw=="], + "@algolia/client-insights": ["@algolia/client-insights@5.56.0", "", { "dependencies": { "@algolia/client-common": "5.56.0", "@algolia/requester-browser-xhr": "5.56.0", "@algolia/requester-fetch": "5.56.0", "@algolia/requester-node-http": "5.56.0" } }, "sha512-P0ehROpM4Sem3Sqo5x2cKPgj67D3G3jy0rh1Amwkcvsfr6tkvIcdCmerieanqTF7NxUMPNFLkpIFeMO8Rpa50w=="], - "@algolia/client-personalization": ["@algolia/client-personalization@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-fQaY+DkSJOpuUVUe8MQTwrdiKAqkJGhpDarB08duBn/sUv7Bkib6MDRQauCcWTWTe4HIW+EbwQP9R4kci1V/Yw=="], + "@algolia/client-personalization": ["@algolia/client-personalization@5.56.0", "", { "dependencies": { "@algolia/client-common": "5.56.0", "@algolia/requester-browser-xhr": "5.56.0", "@algolia/requester-fetch": "5.56.0", "@algolia/requester-node-http": "5.56.0" } }, "sha512-SXK3Vn3WVxyzbm31oePZBJkp1wpOyuWdd4B/Pv7n0aXDxmeSWhC1R1FC1517mMrFAIaPH4Rt0x6RUe7ZNjz8FA=="], - "@algolia/client-query-suggestions": ["@algolia/client-query-suggestions@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-o72tsiEZGfeS/dxL9IADfzcZWGEwKDEe5CvtrBuT//3JR+SHuTtHRI2ZTf7D7bcKagcbojvO8hnkHdfoakSlYg=="], + "@algolia/client-query-suggestions": ["@algolia/client-query-suggestions@5.56.0", "", { "dependencies": { "@algolia/client-common": "5.56.0", "@algolia/requester-browser-xhr": "5.56.0", "@algolia/requester-fetch": "5.56.0", "@algolia/requester-node-http": "5.56.0" } }, "sha512-5+ZdX8garFnmycnZgKhtXHePEaLj5zqDxI/0lkhhluzCcvTn0/PvvTirTg8hHYetQHvn7GDyeAiqTAieMvMW4A=="], - "@algolia/client-search": ["@algolia/client-search@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-Ds16IyPm/dNJPCU8OzApo2gwGrgWT5BYHhE3NFwZbpCveqyvPDB9sZDDkJ5DsdOGT2aC+R3i0/M1OVXF2qdgPg=="], + "@algolia/client-search": ["@algolia/client-search@5.56.0", "", { "dependencies": { "@algolia/client-common": "5.56.0", "@algolia/requester-browser-xhr": "5.56.0", "@algolia/requester-fetch": "5.56.0", "@algolia/requester-node-http": "5.56.0" } }, "sha512-+mKUdYvqOi0BcvpAEyCEw49vSBptufIcfibtHz2bdr1pI789M46Yt0uQEk/sxtK3teh71OQvVFHaTDzShUWewQ=="], "@algolia/events": ["@algolia/events@4.0.1", "", {}, "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ=="], - "@algolia/ingestion": ["@algolia/ingestion@1.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-oNbT6z4NwD8Pou9VPINGlN/tlG1afESh2EbxqnP6rwl95xKVD/Zlciis1PpNeO/9U/rrajc1+7DcfKi03tX1KQ=="], + "@algolia/ingestion": ["@algolia/ingestion@1.56.0", "", { "dependencies": { "@algolia/client-common": "5.56.0", "@algolia/requester-browser-xhr": "5.56.0", "@algolia/requester-fetch": "5.56.0", "@algolia/requester-node-http": "5.56.0" } }, "sha512-9g/zj+AZx5moFcdFIrYQoVrueXivjUcc3MQHtCYT8WhIuk1lUh1AyEhvJCS0XBZld09cLvd1AZ3BvDBpVpX2UA=="], - "@algolia/monitoring": ["@algolia/monitoring@1.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-G+KZb/yd+qAOFn/cEvTGeLxQm8aP3a0od50l3z/ylccY+/o4YG3TNcjU1tFQHW4mXC137GPyR7W70R0kRQDLnA=="], + "@algolia/monitoring": ["@algolia/monitoring@1.56.0", "", { "dependencies": { "@algolia/client-common": "5.56.0", "@algolia/requester-browser-xhr": "5.56.0", "@algolia/requester-fetch": "5.56.0", "@algolia/requester-node-http": "5.56.0" } }, "sha512-Qf3Sr6f9A9uxCZUf3MXS0d2b877uYzEB5yxqpVGXAhcJnBCQjrRRon0KvefpGkxy+BshrIJs96OUoMtGqXTFDA=="], - "@algolia/recommend": ["@algolia/recommend@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-6aVfYd55Un6IUgPLbo84WfgFZlS3L0vA1ttzXL5vahHewUJ8jYgd89TzlWRTeej7w70mb9RWsVlFYGmJ/diQww=="], + "@algolia/recommend": ["@algolia/recommend@5.56.0", "", { "dependencies": { "@algolia/client-common": "5.56.0", "@algolia/requester-browser-xhr": "5.56.0", "@algolia/requester-fetch": "5.56.0", "@algolia/requester-node-http": "5.56.0" } }, "sha512-GXWG1rWc5wu8hY4N33Y3b6ernY6sAdAvmKWN/zHAiACOx40WnpG0TVX5YazCAr/9gOYGInSiM2A0y2jy2xbiDA=="], - "@algolia/requester-browser-xhr": ["@algolia/requester-browser-xhr@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0" } }, "sha512-ke27DqgzCOlt+RbeEdCxtXxMQOnAOi8ujr2wid0DmDKzR95Kw/f9sBsuhBxtjevCqJRJszfRTLY0B1pbO6IhkA=="], + "@algolia/requester-browser-xhr": ["@algolia/requester-browser-xhr@5.56.0", "", { "dependencies": { "@algolia/client-common": "5.56.0" } }, "sha512-7t24cBxaInS3mZb7ddEaZT/tp6q+/aR4YttsQVyP1/i+LmwPR34atO35KjaLFCcRVrlP7sYOAqkCfg6lIRB+ew=="], - "@algolia/requester-fetch": ["@algolia/requester-fetch@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0" } }, "sha512-GngiOqt2Gq4oLno6yXQVj9om+qSO9SWAoduoTOEg79dKZ62brB8OOIvSJG/vDNoanYi6a7Al9uDZwXvi+bcVTg=="], + "@algolia/requester-fetch": ["@algolia/requester-fetch@5.56.0", "", { "dependencies": { "@algolia/client-common": "5.56.0" } }, "sha512-R7ePHgVYmDFjZpvrsVAfbDz/d4RxKAYZ5/vgLfIsCVRZRryjWl/3INOxpOICzitehQ5FjNtNjcLQTrmHPTcHBQ=="], - "@algolia/requester-node-http": ["@algolia/requester-node-http@5.53.0", "", { "dependencies": { "@algolia/client-common": "5.53.0" } }, "sha512-6mF9LZMUk0QqWvrnxkxBqhswwz6Xfiwy6/gmTzL5HrlhdVG3ITAqGV2k3XmVThP1h0Ulc3VQwiNCD7/Nr4JNlQ=="], + "@algolia/requester-node-http": ["@algolia/requester-node-http@5.56.0", "", { "dependencies": { "@algolia/client-common": "5.56.0" } }, "sha512-PIOUXlSnrqM0S+WOgDRb4RzotydJH7ZoT6tOyL7tAO7qJOfvX5wsEW8Pe+PMKMwvuI4/gIyK9cg2H7lJXqnc4Q=="], "@asamuzakjp/css-color": ["@asamuzakjp/css-color@3.2.0", "", { "dependencies": { "@csstools/css-calc": "^2.1.3", "@csstools/css-color-parser": "^3.0.9", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "lru-cache": "^10.4.3" } }, "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw=="], @@ -362,23 +363,23 @@ "@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - "@biomejs/biome": ["@biomejs/biome@2.4.16", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.16", "@biomejs/cli-darwin-x64": "2.4.16", "@biomejs/cli-linux-arm64": "2.4.16", "@biomejs/cli-linux-arm64-musl": "2.4.16", "@biomejs/cli-linux-x64": "2.4.16", "@biomejs/cli-linux-x64-musl": "2.4.16", "@biomejs/cli-win32-arm64": "2.4.16", "@biomejs/cli-win32-x64": "2.4.16" }, "bin": { "biome": "bin/biome" } }, "sha512-x9ajFh1zChVybCiM3TN6OD4phAqLgtPZjFrZF+aTMYCPjwBO+k529TX7PPsAqtGNLeV4UgzwQnowEgS7bGmzcA=="], + "@biomejs/biome": ["@biomejs/biome@2.5.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.5.4", "@biomejs/cli-darwin-x64": "2.5.4", "@biomejs/cli-linux-arm64": "2.5.4", "@biomejs/cli-linux-arm64-musl": "2.5.4", "@biomejs/cli-linux-x64": "2.5.4", "@biomejs/cli-linux-x64-musl": "2.5.4", "@biomejs/cli-win32-arm64": "2.5.4", "@biomejs/cli-win32-x64": "2.5.4" }, "bin": { "biome": "bin/biome" } }, "sha512-xy5FNE5kQJKyK5MR1gJy6ztXYx4WBAbYGlK04lMEgmyPRWKybY9NFwiG9yo0XdzOU8Xvhj41u034J1ywfoWfMw=="], - "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.16", "", { "os": "darwin", "cpu": "arm64" }, "sha512-wxPvu4XOA85YJk9ixSWUmq/QBHbid85BISbOAqqBM/5xQpPk9ayjk5375tOlSC0BeCwNSbPFafQBm+vBumXq0A=="], + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.5.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4o3NFRobXHynkgcFVrlZsoDAFtF2ldlEGN8sORSws5ZQqyY4PXnPUIylu4ksfyHuwkfvDREuWh3JK+niRwGq3w=="], - "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.16", "", { "os": "darwin", "cpu": "x64" }, "sha512-xFCqGPwYusQJp4N4NJLi1XJiZqjwFdjhT+KqtNy+Ug3qgfczqnTa6MSDvxJF6TkuDLoYJItMapz6tAf7kCekFw=="], + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.5.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-D32P5HkU2Y6PySuC/WsVDTOgsDwVFmujzhhhOQjajtATpVWFDXuVd3oRbsWNSEA+aaFzyzZm22szsyydBYlSyQ=="], - "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-2kFb4//jxfZaP6D+Rj5VkHkxgyD9EoRAVBEQb8PKRv+s4NO2zYNJKXFaJmK1CmhufJOWEfpHKaRbOja7qjmdhQ=="], + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.5.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-pSEfW7B8kTsXUjUxC1xVVK+y85Ht3C5XxZ9gclmC7/3Ku9Vqz8jmI7k0p/BNIjQ6t4sFERI2sFeH73ybiZl6YQ=="], - "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-oYxnW0ARfJkr72ezzF2OR8N/rtkgLUQeYtF8cFhVswbknHxtTcmzSsanVJP8yQKnGpGpc2ck6c5zLvHahL6Cbg=="], + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.5.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-Rpm5/AT1m+DlJmUoYvS4/vXc+0tXJPJ2NQz25TGPyHVF5JrWy75PE0GH6kVxsKtQDuCH4OgzquZq0R4kj/wCVg=="], - "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.16", "", { "os": "linux", "cpu": "x64" }, "sha512-NbcBbi/nJqn5baae6wqRXdS7Gadf2uRpehSh6vMSYpG8OhkXl/Xg8aorWrJ+9VWqAT5ml90alLvorkpMW0nBwQ=="], + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.5.4", "", { "os": "linux", "cpu": "x64" }, "sha512-FNxojWJkL7EajAuzBgoLe0T2G0y112M4lBrDIFl/DomFTx8yqenYOIdsRLNXvOvBBofE8hJi85LjzLmBDpY7/Q=="], - "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.16", "", { "os": "linux", "cpu": "x64" }, "sha512-iHDS+MCM65DPqWGu+ECC3uoALyj2H7F4nVUPxIPjz/PIl94EUu+EDfGZDzFP+NY1EOPVt9NQvwFqq7HdMmowdg=="], + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.5.4", "", { "os": "linux", "cpu": "x64" }, "sha512-aby/PohmmgbShcHqFsZVzG8H6D98+P+A6xRWRrQcLW1pCjabcov5UUlke4UqNQBYTkDQav+jB4zyyDDeKB2GaA=="], - "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.16", "", { "os": "win32", "cpu": "arm64" }, "sha512-0rgImMsNb5v/chhkIFe3wu7PEFClS6RBAYUijGL9UsYN3PanSaoK24HSSuSJb1pYbYYVjzAyZTl3gtjJ84BM8A=="], + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.5.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-emoXexPZIPAZkz2RKmA95WJUqK3I5MJNYtwEbL5ESciRzhmFMMyekDhNG8hpeOaK+ZGRDxAU4wvGuA5IHQ0h0w=="], - "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.16", "", { "os": "win32", "cpu": "x64" }, "sha512-Kp85jgoBHa05gix6UIRjfCDiUV3w/8VIdZ247VyyO2gEjaw12WEVhdIjlxp/AMzXxqxQwbxNTDVZ3Mwd2RG5rw=="], + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.5.4", "", { "os": "win32", "cpu": "x64" }, "sha512-U1jaluLw1qQc2Tx7/CeSoL9N5XcqIH+GWjpUAy1ouB5nVjSCMNO+NNHdY3RAs8zxNurLWAdj6pehQdCA2zyU+Q=="], "@colors/colors": ["@colors/colors@1.5.0", "", {}, "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="], @@ -498,7 +499,7 @@ "@docusaurus/cssnano-preset": ["@docusaurus/cssnano-preset@3.10.1", "", { "dependencies": { "cssnano-preset-advanced": "^6.1.2", "postcss": "^8.5.4", "postcss-sort-media-queries": "^5.2.0", "tslib": "^2.6.0" } }, "sha512-eNfHGcTKCSq6xmcavAkX3RRclHaE2xRCMParlDXLdXVP01/a2e/jKXMj/0ULnLFQSNwwuI62L0Ge8J+nZsR7UQ=="], - "@docusaurus/faster": ["@docusaurus/faster@3.10.1", "", { "dependencies": { "@docusaurus/types": "3.10.1", "@rspack/core": "^1.7.10", "@swc/core": "^1.7.39", "@swc/html": "^1.13.5", "browserslist": "^4.24.2", "lightningcss": "^1.27.0", "semver": "^7.5.4", "swc-loader": "^0.2.6", "tslib": "^2.6.0", "webpack": "^5.95.0" } }, "sha512-XTZhE5C1gZ/DaYYMlSk02dwP5vhpQON5QHVz1s3892mSESAywgWanURpXEDAvt4GvGuq7s+XP8rTWHZvfaJmdQ=="], + "@docusaurus/faster": ["@docusaurus/faster@3.10.2", "", { "dependencies": { "@docusaurus/types": "3.10.2", "@rspack/core": "^1.7.10", "@swc/core": "^1.15.40", "@swc/html": "^1.15.40", "browserslist": "^4.24.2", "lightningcss": "^1.27.0", "semver": "^7.5.4", "swc-loader": "^0.2.6", "tslib": "^2.6.0", "webpack": "^5.95.0" } }, "sha512-p/5E5/RyHv+QWusJMPN5i3OMJTqTgkhuwzVbB1AReDWTUHXQCmf5mlTFzGiDrWeQWIDOKsuOPn1jJh0s9LUOHA=="], "@docusaurus/logger": ["@docusaurus/logger@3.10.1", "", { "dependencies": { "chalk": "^4.1.2", "tslib": "^2.6.0" } }, "sha512-oPjNFnfJsRCkePVjkGrxWGq4MvJKRQT0r9jOP0eRBTZ7Wr9FAbzdP/Gjs0I2Ss6YRkPoEgygKG112OkE6skvJw=="], @@ -550,77 +551,77 @@ "@effect-fc/example-next": ["@effect-fc/example-next@workspace:packages/example-next"], - "@effect/language-service": ["@effect/language-service@0.86.2", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-SaPln+8srOqDJDUwNTDmP5e+IYpEDr9+1epGznnsLqu8xvo6VnxyWARdeLpqvZJlb0Pgy9ca7ppqvvdWbHPXAg=="], + "@effect/language-service": ["@effect/language-service@0.86.6", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-uwXbp+lWzt60bZnm6lG6S5DWy0m6TthUzV2hcQoiS6NzieEDm07tuBakscxOXoO+AU9+yTH+0TSIa9mEDt9dFA=="], - "@effect/platform": ["@effect/platform@0.96.1", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.10", "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^3.21.2" } }, "sha512-cjB1QZZYEP8JXCFNGvBLVi0T6YUBQTmOVEUA3SDbiQ6RUO+p6CE3eyD2vMWmrz5nE8yY5QSAuOV9v0boEcUv+A=="], + "@effect/platform": ["@effect/platform@0.96.3", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.10", "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^3.21.5" } }, "sha512-LzvIj4HYE++TcTv/cVTCI/GRvQTV0ymwRmgjZMzNB5dU4OS05pTN36RKN0npiOm5orLJgw4yjIv1dNZoYGh/vg=="], "@effect/platform-browser": ["@effect/platform-browser@0.76.0", "", { "dependencies": { "multipasta": "^0.2.7" }, "peerDependencies": { "@effect/platform": "^0.96.0", "effect": "^3.21.0" } }, "sha512-cUyBpcLstrP/HiNsIePMBAI6R1+u6aRFlAUZb4wf08y1d1Vqf/Dmxsq14ZjBfnSYiqBPrCeYf1ZI+qMGQQL0RA=="], - "@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], + "@emnapi/core": ["@emnapi/core@1.11.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="], - "@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], + "@emnapi/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="], - "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], + "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="], - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.7", "", { "os": "aix", "cpu": "ppc64" }, "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ=="], - "@esbuild/android-arm": ["@esbuild/android-arm@0.27.7", "", { "os": "android", "cpu": "arm" }, "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ=="], + "@esbuild/android-arm": ["@esbuild/android-arm@0.28.1", "", { "os": "android", "cpu": "arm" }, "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ=="], - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.7", "", { "os": "android", "cpu": "arm64" }, "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ=="], + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.1", "", { "os": "android", "cpu": "arm64" }, "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg=="], - "@esbuild/android-x64": ["@esbuild/android-x64@0.27.7", "", { "os": "android", "cpu": "x64" }, "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg=="], + "@esbuild/android-x64": ["@esbuild/android-x64@0.28.1", "", { "os": "android", "cpu": "x64" }, "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng=="], - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw=="], + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q=="], - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ=="], + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ=="], - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.7", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w=="], + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw=="], - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.7", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ=="], + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ=="], - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.7", "", { "os": "linux", "cpu": "arm" }, "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA=="], + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.1", "", { "os": "linux", "cpu": "arm" }, "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ=="], - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A=="], + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g=="], - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.7", "", { "os": "linux", "cpu": "ia32" }, "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg=="], + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w=="], - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q=="], + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg=="], - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw=="], + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ=="], - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.7", "", { "os": "linux", "cpu": "ppc64" }, "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ=="], + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ=="], - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ=="], + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ=="], - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.7", "", { "os": "linux", "cpu": "s390x" }, "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw=="], + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag=="], - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.7", "", { "os": "linux", "cpu": "x64" }, "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA=="], + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.1", "", { "os": "linux", "cpu": "x64" }, "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA=="], - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w=="], + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw=="], - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.7", "", { "os": "none", "cpu": "x64" }, "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw=="], + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.1", "", { "os": "none", "cpu": "x64" }, "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg=="], - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.7", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A=="], + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q=="], - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.7", "", { "os": "openbsd", "cpu": "x64" }, "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg=="], + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw=="], - "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw=="], + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg=="], - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.7", "", { "os": "sunos", "cpu": "x64" }, "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA=="], + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ=="], - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA=="], + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA=="], - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.7", "", { "os": "win32", "cpu": "ia32" }, "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw=="], + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg=="], - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.7", "", { "os": "win32", "cpu": "x64" }, "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg=="], + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="], - "@floating-ui/core": ["@floating-ui/core@1.7.5", "", { "dependencies": { "@floating-ui/utils": "^0.2.11" } }, "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ=="], + "@floating-ui/core": ["@floating-ui/core@1.8.0", "", { "dependencies": { "@floating-ui/utils": "^0.2.12" } }, "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ=="], - "@floating-ui/dom": ["@floating-ui/dom@1.7.6", "", { "dependencies": { "@floating-ui/core": "^1.7.5", "@floating-ui/utils": "^0.2.11" } }, "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ=="], + "@floating-ui/dom": ["@floating-ui/dom@1.8.0", "", { "dependencies": { "@floating-ui/core": "^1.8.0", "@floating-ui/utils": "^0.2.12" } }, "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg=="], - "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.8", "", { "dependencies": { "@floating-ui/dom": "^1.7.6" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A=="], + "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.9", "", { "dependencies": { "@floating-ui/dom": "^1.8.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg=="], - "@floating-ui/utils": ["@floating-ui/utils@0.2.11", "", {}, "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg=="], + "@floating-ui/utils": ["@floating-ui/utils@0.2.12", "", {}, "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww=="], "@hapi/hoek": ["@hapi/hoek@9.3.0", "", {}, "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ=="], @@ -648,21 +649,21 @@ "@jsonjoy.com/codegen": ["@jsonjoy.com/codegen@1.0.0", "", { "peerDependencies": { "tslib": "2" } }, "sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g=="], - "@jsonjoy.com/fs-core": ["@jsonjoy.com/fs-core@4.57.6", "", { "dependencies": { "@jsonjoy.com/fs-node-builtins": "4.57.6", "@jsonjoy.com/fs-node-utils": "4.57.6", "thingies": "^2.5.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-uI++Wx6VkBJqVmkb4ZeExwAVpZiA2Do5NrEtXoDk0Pdvce3ytFXJoviT1sLOj16+qDIMnD5nWPfOhVpnDmRJKg=="], + "@jsonjoy.com/fs-core": ["@jsonjoy.com/fs-core@4.64.0", "", { "dependencies": { "@jsonjoy.com/fs-node-builtins": "4.64.0", "@jsonjoy.com/fs-node-utils": "4.64.0", "thingies": "^2.5.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-zs2TAq7Six5jgMuoMNjpspAvOP3mhtgq/k1UyQodEzCtQi/N83y2/y+zcvnZSGp/Rxq96DBN+bValOBQAyn/ew=="], - "@jsonjoy.com/fs-fsa": ["@jsonjoy.com/fs-fsa@4.57.6", "", { "dependencies": { "@jsonjoy.com/fs-core": "4.57.6", "@jsonjoy.com/fs-node-builtins": "4.57.6", "@jsonjoy.com/fs-node-utils": "4.57.6", "thingies": "^2.5.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-pKkw/yC5CzSZKhIIUIsH1przOa+K5jGmZIg1sWaSF24JojyrUFbjcQv7QrcGAudriei6HQ6R0BFj+V8NbQinJw=="], + "@jsonjoy.com/fs-fsa": ["@jsonjoy.com/fs-fsa@4.64.0", "", { "dependencies": { "@jsonjoy.com/fs-core": "4.64.0", "@jsonjoy.com/fs-node-builtins": "4.64.0", "@jsonjoy.com/fs-node-utils": "4.64.0", "thingies": "^2.5.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-nMWOVbkLFyEgmXZih3wyvxA9XpgyyqyfrINMHvEFqhi7uqfRl7c9ERJt6yX7vgMPrB9Uo+OJO+Spa0cFzPD01w=="], - "@jsonjoy.com/fs-node": ["@jsonjoy.com/fs-node@4.57.6", "", { "dependencies": { "@jsonjoy.com/fs-core": "4.57.6", "@jsonjoy.com/fs-node-builtins": "4.57.6", "@jsonjoy.com/fs-node-utils": "4.57.6", "@jsonjoy.com/fs-print": "4.57.6", "@jsonjoy.com/fs-snapshot": "4.57.6", "glob-to-regex.js": "^1.0.0", "thingies": "^2.5.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-Kbn1jdkvDN4F2+BhoB6mMu7NCbhP0bgA5NcI1aJj/Q5UcU+I1JLLW+dEQean33iV4tXv35AzBVKPICnDltBpxw=="], + "@jsonjoy.com/fs-node": ["@jsonjoy.com/fs-node@4.64.0", "", { "dependencies": { "@jsonjoy.com/fs-core": "4.64.0", "@jsonjoy.com/fs-node-builtins": "4.64.0", "@jsonjoy.com/fs-node-utils": "4.64.0", "@jsonjoy.com/fs-print": "4.64.0", "@jsonjoy.com/fs-snapshot": "4.64.0", "glob-to-regex.js": "^1.0.0", "thingies": "^2.5.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-dO+NNkODbUli4uV42bcNrrLvq5rE7SNpdZ5TNd0dtbLsAaNK3MDiIC9lUi+brboGoIjW6vd2fB1qao60nrk5xA=="], - "@jsonjoy.com/fs-node-builtins": ["@jsonjoy.com/fs-node-builtins@4.57.6", "", { "peerDependencies": { "tslib": "2" } }, "sha512-V4DgEFT3Cg5S9fCMOZSCVdTxdJWWLBO0WnAazV7hnCM96u5zXHyW/ubDAfcSVwqjkMJ50W1Y44IXtxRoIwaCVg=="], + "@jsonjoy.com/fs-node-builtins": ["@jsonjoy.com/fs-node-builtins@4.64.0", "", { "peerDependencies": { "tslib": "2" } }, "sha512-/o7WRFhUWaM/fOrslwLZGnzn4RmRILykn+lAL+mNObqqRNw+CQSiij6hpCeZ+C7buhdoVo7go/OYqzaSUfDYmA=="], - "@jsonjoy.com/fs-node-to-fsa": ["@jsonjoy.com/fs-node-to-fsa@4.57.6", "", { "dependencies": { "@jsonjoy.com/fs-fsa": "4.57.6", "@jsonjoy.com/fs-node-builtins": "4.57.6", "@jsonjoy.com/fs-node-utils": "4.57.6" }, "peerDependencies": { "tslib": "2" } }, "sha512-+JptNw3iifihxH2rEXrninDzX4FFVW8JD/wPR8GbJPAeL9CQUSblrlumOPB5gZuS7tYRX+PJPLtT7XzKoRhv/Q=="], + "@jsonjoy.com/fs-node-to-fsa": ["@jsonjoy.com/fs-node-to-fsa@4.64.0", "", { "dependencies": { "@jsonjoy.com/fs-fsa": "4.64.0", "@jsonjoy.com/fs-node-builtins": "4.64.0", "@jsonjoy.com/fs-node-utils": "4.64.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-WDD9WVs0hb7UAEKTgZW2f66WDrbj7gIIWwpP3spbLyXa0rghtUaFTB8L4gdR3ZCWwiKIsj38/CNijpVmpnuPUw=="], - "@jsonjoy.com/fs-node-utils": ["@jsonjoy.com/fs-node-utils@4.57.6", "", { "dependencies": { "@jsonjoy.com/fs-node-builtins": "4.57.6" }, "peerDependencies": { "tslib": "2" } }, "sha512-foyUrfS7WmYEUzqYXSNxmJBcSj04TABrkpFabwO9SCDCpVCfJ+qG+2sk5FjfiflG2n0SDFZDCJ6vYlJAEpxJFg=="], + "@jsonjoy.com/fs-node-utils": ["@jsonjoy.com/fs-node-utils@4.64.0", "", { "dependencies": { "@jsonjoy.com/fs-node-builtins": "4.64.0", "glob-to-regex.js": "^1.0.1" }, "peerDependencies": { "tslib": "2" } }, "sha512-k5Indsx9hWW9xSF7Y6oSKKwtCUNhzZxadub3owhIlitc+iMRVlPPdX2duTKQWBL3qNWpXya8jykgaaWpheeS4w=="], - "@jsonjoy.com/fs-print": ["@jsonjoy.com/fs-print@4.57.6", "", { "dependencies": { "@jsonjoy.com/fs-node-utils": "4.57.6", "tree-dump": "^1.1.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-96eAn4Dudtt67LTeuU47yUD+pg9/G/oKpI10zei9ljk3X3WK4lYKc+n3cpaPCAbKPzoyfxl0mXm8f8Y7BOSFXw=="], + "@jsonjoy.com/fs-print": ["@jsonjoy.com/fs-print@4.64.0", "", { "dependencies": { "@jsonjoy.com/fs-node-utils": "4.64.0", "tree-dump": "^1.1.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-PHZFccchvkhWrwPWHjmVAhbC3vSHCtyZvlZfJJ3ho2bnzl450hXri6/8e6pbkWdH+SkmLXNml0sV8e5HDAfxKw=="], - "@jsonjoy.com/fs-snapshot": ["@jsonjoy.com/fs-snapshot@4.57.6", "", { "dependencies": { "@jsonjoy.com/buffers": "^17.65.0", "@jsonjoy.com/fs-node-utils": "4.57.6", "@jsonjoy.com/json-pack": "^17.65.0", "@jsonjoy.com/util": "^17.65.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-V57CMzbOgTzUWGOWQ8GzHQdpJP6JnrYVNCtTBNxVYEnlVRvo4uEJqHhtAT8vhDFrIuJOXLrTL1Fki4h5oI7xxg=="], + "@jsonjoy.com/fs-snapshot": ["@jsonjoy.com/fs-snapshot@4.64.0", "", { "dependencies": { "@jsonjoy.com/buffers": "^17.65.0", "@jsonjoy.com/fs-node-utils": "4.64.0", "@jsonjoy.com/json-pack": "^17.65.0", "@jsonjoy.com/util": "^17.65.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-oM7UDeL83q6NBzzsfKAsYKXKVXlykKFqqOLh4xZZKAzzROTlInkPbc6LTDGThEOnPiFiUzA7tYziHG9xavd76Q=="], "@jsonjoy.com/json-pack": ["@jsonjoy.com/json-pack@1.21.0", "", { "dependencies": { "@jsonjoy.com/base64": "^1.1.2", "@jsonjoy.com/buffers": "^1.2.0", "@jsonjoy.com/codegen": "^1.0.0", "@jsonjoy.com/json-pointer": "^1.0.2", "@jsonjoy.com/util": "^1.9.0", "hyperdyperid": "^1.2.0", "thingies": "^2.5.0", "tree-dump": "^1.1.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg=="], @@ -700,7 +701,7 @@ "@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4", "", { "os": "win32", "cpu": "x64" }, "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ=="], - "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.6", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg=="], "@noble/hashes": ["@noble/hashes@1.4.0", "", {}, "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg=="], @@ -710,27 +711,27 @@ "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], - "@oxc-project/types": ["@oxc-project/types@0.133.0", "", {}, "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA=="], + "@oxc-project/types": ["@oxc-project/types@0.139.0", "", {}, "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw=="], - "@peculiar/asn1-cms": ["@peculiar/asn1-cms@2.7.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.7.0", "@peculiar/asn1-x509": "^2.7.0", "@peculiar/asn1-x509-attr": "^2.7.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-hew63shtzzvBcSHbhm+cyAmKe6AIfinT9hzEqSPjDC6opTTMKmTkQ0gHuN2KsWlvqiKw1S/fS94fhag/FJkioQ=="], + "@peculiar/asn1-cms": ["@peculiar/asn1-cms@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "@peculiar/asn1-x509-attr": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-NgekZOrSJFSBFLFoLfwePguAWAx7z1+f2TEsWFUMyiqqfntZ4+S/S5hzqME3q4pCA0iOsFKdwiQ35dwY24eVqA=="], - "@peculiar/asn1-csr": ["@peculiar/asn1-csr@2.7.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.7.0", "@peculiar/asn1-x509": "^2.7.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-VVsAyGqErT9D1SY4aEqozThXMVI+ssVRiv2DDeYuvpBKLIgZ3hYs3Ay3u/VSoKq6ESFi9cf6rf3IOOzfwh7oMA=="], + "@peculiar/asn1-csr": ["@peculiar/asn1-csr@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-akbF8+uvleHs8sejNPQxwmVFuInAg6FMNHOwMILXfP518YfFJwdR3jr6oNUPOaEJfuEhn/vkNOCIT6ASUd4mbg=="], - "@peculiar/asn1-ecc": ["@peculiar/asn1-ecc@2.7.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.7.0", "@peculiar/asn1-x509": "^2.7.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-n7KEs/Q/wrB415cxy4fHOBhegp4NdJ15fkJPwcB/3/8iNBQC2L/N7SChJPKDJPZGYH0jD4Tg4/0vnHmwghnbKw=="], + "@peculiar/asn1-ecc": ["@peculiar/asn1-ecc@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-ohwlk+u9Rv2NOAY1c6MfHj45ATVF8R1DUN/WCgABiRtLi2ZftlZWZX7KvpAbU8v9xPcmoILfELeEABj/rn18AQ=="], - "@peculiar/asn1-pfx": ["@peculiar/asn1-pfx@2.7.0", "", { "dependencies": { "@peculiar/asn1-cms": "^2.7.0", "@peculiar/asn1-pkcs8": "^2.7.0", "@peculiar/asn1-rsa": "^2.7.0", "@peculiar/asn1-schema": "^2.7.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-V/nrlQVmhg7lYAsM7E13UDL5erAwFv6kCIVFqNaMIHSVi7dngcT839JkRTkQBqznMG98l2XjxYk74ZztAohZzA=="], + "@peculiar/asn1-pfx": ["@peculiar/asn1-pfx@2.8.0", "", { "dependencies": { "@peculiar/asn1-cms": "^2.8.0", "@peculiar/asn1-pkcs8": "^2.8.0", "@peculiar/asn1-rsa": "^2.8.0", "@peculiar/asn1-schema": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-5yof1ytoB++RQtaFbqSUJ8pxDJtZT6vbVqZ8XoJ61ph7UjNVvfFwAilnCodqkNsAodpy13gDhoxZXw00pghnyg=="], - "@peculiar/asn1-pkcs8": ["@peculiar/asn1-pkcs8@2.7.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.7.0", "@peculiar/asn1-x509": "^2.7.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-9GTl1nE8Mx1kTZ+7QyYatDyKsm34QcWRBFkY1iPvWC3X4Dona5s/tlLiQsx5WzVdZqiMBZNYT0buyw4/vbhnjw=="], + "@peculiar/asn1-pkcs8": ["@peculiar/asn1-pkcs8@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-qAKXtLpBEw9LqhKpjw3ajZSXlBur+ipW+y2ivVBQAG6F6qRx94yO+1ZR4mvw+YaCfKSaOzLeYEzsPaBp4SJELA=="], - "@peculiar/asn1-pkcs9": ["@peculiar/asn1-pkcs9@2.7.0", "", { "dependencies": { "@peculiar/asn1-cms": "^2.7.0", "@peculiar/asn1-pfx": "^2.7.0", "@peculiar/asn1-pkcs8": "^2.7.0", "@peculiar/asn1-schema": "^2.7.0", "@peculiar/asn1-x509": "^2.7.0", "@peculiar/asn1-x509-attr": "^2.7.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-Bh7m+OuIaSEllPQcSd9OSp93F4ROWH7sbITWV8MI+8dwsjE5111/87VxiWVvYFKyww3vp39geLv9ENqhwWHcew=="], + "@peculiar/asn1-pkcs9": ["@peculiar/asn1-pkcs9@2.8.0", "", { "dependencies": { "@peculiar/asn1-cms": "^2.8.0", "@peculiar/asn1-pfx": "^2.8.0", "@peculiar/asn1-pkcs8": "^2.8.0", "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "@peculiar/asn1-x509-attr": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-b5nDWCnkV60+cQ141D6sVVwK9nz64R5n3zSVnklGd+ECdkW2Ol3U1a6yYFlalpSOaD557yuJB64A+q42jG7lUQ=="], - "@peculiar/asn1-rsa": ["@peculiar/asn1-rsa@2.7.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.7.0", "@peculiar/asn1-x509": "^2.7.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-/qvENQrXyTZURjMqSeofHul0JJt2sNSzSwk36pl2olkHbaioMQgrASDZAlHXl0xUlnVbHj0uGgOrBMTb5x2aJQ=="], + "@peculiar/asn1-rsa": ["@peculiar/asn1-rsa@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-zHEUlCqB2mk7x2lxDwHHJy7hWZOPdGHVlsmITWKB5/PbQo61atbu9PJ/0r9dQNMwFzbKPXZ8uK8/91eUhRznSg=="], - "@peculiar/asn1-schema": ["@peculiar/asn1-schema@2.7.0", "", { "dependencies": { "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-W8ZfWzLmQnrcky+eh3tni4IozMdqBDiHWU0N+vve/UGjMaUs8c0L7A2oEdkBXS8rTpWDpK/aoI3DG/L/hxmxPg=="], + "@peculiar/asn1-schema": ["@peculiar/asn1-schema@2.8.0", "", { "dependencies": { "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-7YT0U/ze0tF2QOBbE15gKZwy5tvgGyLRiRHLzhlbOpf7BT032oBSd0haZqXn5W6l26WLlu3dyxzjM+2638/z2Q=="], - "@peculiar/asn1-x509": ["@peculiar/asn1-x509@2.7.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.7.0", "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-mUn9RRrkGDnG4ALfunDmzyRW5dg+sWCj/pfnCCqEHYbkGxEpvUt6iVJv8Yw1cyp6SWZ26ZE5oSmI5SqEaen15g=="], + "@peculiar/asn1-x509": ["@peculiar/asn1-x509@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-N0CMuhWUzsWEVq6F1q9X6+VKUnWzSW+cSVg+aPaGGwDdbFoFWTYgin5MHwXgpWd6y9COMBxnfy/Qc+Xc7F0Zwg=="], - "@peculiar/asn1-x509-attr": ["@peculiar/asn1-x509-attr@2.7.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.7.0", "@peculiar/asn1-x509": "^2.7.0", "asn1js": "^3.0.6", "tslib": "^2.8.1" } }, "sha512-NS8e7SOgXipkzUPLF/sce7ukpMpWjhxYsH0n6Y+bHYo4TTxOb95Zv7hqwSuL212mj5YxovjdOKQOgH1As3E94w=="], + "@peculiar/asn1-x509-attr": ["@peculiar/asn1-x509-attr@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-tHjkfS/qhMnmrlB2J9NhflQlQ7In3khO3CfmVrriOlpTeErY9ZIKOso1hQ5JQiyrJ7ShvqVPk7E5fQmbclkSKA=="], "@peculiar/utils": ["@peculiar/utils@2.0.3", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ=="], @@ -740,7 +741,7 @@ "@pnpm/network.ca-file": ["@pnpm/network.ca-file@1.0.2", "", { "dependencies": { "graceful-fs": "4.2.10" } }, "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA=="], - "@pnpm/npm-conf": ["@pnpm/npm-conf@3.0.2", "", { "dependencies": { "@pnpm/config.env-replace": "^1.1.0", "@pnpm/network.ca-file": "^1.0.1", "config-chain": "^1.1.11" } }, "sha512-h104Kh26rR8tm+a3Qkc5S4VLYint3FE48as7+/5oCEcKR2idC/pF1G6AhIXKI+eHPJa/3J9i5z0Al47IeGHPkA=="], + "@pnpm/npm-conf": ["@pnpm/npm-conf@3.0.3", "", { "dependencies": { "@pnpm/config.env-replace": "^1.1.0", "@pnpm/network.ca-file": "^1.0.1", "config-chain": "^1.1.11" } }, "sha512-//0sR/cow/s4ICQaYoAobOl4aU8cjU6x/V24V7XkKotb9+O+3zySIYp146vpaobYHnxa4pZX8NkV54Z5AwbDKA=="], "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], @@ -748,101 +749,101 @@ "@radix-ui/number": ["@radix-ui/number@1.1.2", "", {}, "sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig=="], - "@radix-ui/primitive": ["@radix-ui/primitive@1.1.4", "", {}, "sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ=="], + "@radix-ui/primitive": ["@radix-ui/primitive@1.1.5", "", {}, "sha512-d86WIWFYNtGA0H/d8exstrTRTp7eWJYlYJbtNofxr/3ljupZYn6EFDG/Qgu/0Kc8v7yMUxySagqJsL1+PdYjWg=="], - "@radix-ui/react-accessible-icon": ["@radix-ui/react-accessible-icon@1.1.9", "", { "dependencies": { "@radix-ui/react-visually-hidden": "1.2.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-5W9KzJz/3DeYbGJHbZv8Q6AkxMOKUmALfc+PRg9dWwJZMk6zD37Sz8sZrF7UD6CBkiJvn7dNeRzn5G7XiCMyig=="], + "@radix-ui/react-accessible-icon": ["@radix-ui/react-accessible-icon@1.1.11", "", { "dependencies": { "@radix-ui/react-visually-hidden": "1.2.7" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-HQDOFTKwSnmUij6l54wYJJtxTAnxI71+YJLOrjm2ladFB8HAV5Jt7hwaZPhWTGBkYoW4+ZAOfNZrLDh/qvxSYA=="], - "@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.2.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-collapsible": "1.1.13", "@radix-ui/react-collection": "1.1.9", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-xITxBB2p5m5tAe7M0F95kb4uAh7jSIKGlExMEm93HlW+XxZHV2eXFbPWLktd4JhRiwcnXNbO7iekcrbZy6ZCvA=="], + "@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.2.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-collapsible": "1.1.16", "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-BpZJNmetujnGgUI6OX0jEhEmlA46WPqgub8Rv09Kyquwd0cc1ndMKpiPYCjmBU6KSSRPAMtgLpEoZSG/tdNIWQ=="], - "@radix-ui/react-alert-dialog": ["@radix-ui/react-alert-dialog@1.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-dialog": "1.1.16", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-slot": "1.2.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-vPaIgo0mxYlvcFaM9jB2Uot9TjGXMuAPEvrc6BOLeV+I5U8s1dkIoouYaa6lmSfc5SPMo5x5djOTOTvaigdGMQ=="], + "@radix-ui/react-alert-dialog": ["@radix-ui/react-alert-dialog@1.1.19", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-dialog": "1.1.19", "@radix-ui/react-primitive": "2.1.7" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-FA7n1f6D/DwGE0+AWxiY5LacNbbExQuEgMubeG06idEaH+mSLuf9dp/qBNqOnvbTQ+4gZ2ue1RATF1Ub91Mg5g=="], - "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-yqHW5WQ/cTpU/un7dqqIKNy2iRU8BC0JB78PEzTfCCYvZu1U6W9KwObAniMk9nhSfyotKPQTYaUD/HB0f5muig=="], + "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.11", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.7" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Kdil9BB1rIFC/khmf4hC35bn8701AJcizTU7G7cUbEbk5XqqbjDuHW60uUfKqO5WojjZcbAW51Q7P0hRmMLw8A=="], - "@radix-ui/react-aspect-ratio": ["@radix-ui/react-aspect-ratio@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Xy+Dpxt/5n9rVTdPrNFmf8GwG1NlT1pzCF/z1MgOGZMLZWdWl+km+ZRWGQAPEhbkzSwYEsfYmTca8NhUtVxqnw=="], + "@radix-ui/react-aspect-ratio": ["@radix-ui/react-aspect-ratio@1.1.11", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.7" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-IUAhIVpBUvP5NNICjlaB1OFmtRLGqQqTF3ZOSGPoq3XeLXRFtHiWTRxSVEULgOd9GQR2c7tsYqDnhUennapZnw=="], - "@radix-ui/react-avatar": ["@radix-ui/react-avatar@1.1.12", "", { "dependencies": { "@radix-ui/react-context": "1.1.4", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-is-hydrated": "0.1.1", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-NQCQyWC7QrDPhjMn8hUqFeU0lUrprIgm1AyMgLbzuQJibNnatdc3SSMo3/UGFu/eUkJUU1cEcKCnyhXTQzq6tA=="], + "@radix-ui/react-avatar": ["@radix-ui/react-avatar@1.2.2", "", { "dependencies": { "@radix-ui/react-context": "1.2.0", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-is-hydrated": "0.1.1", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-sST0qh8GzOB7besQ3tMLWLyngnRuSk0gc/Hm+667KYKQFCt6Y6ZXv25WlqM7dIDK54ULCh5+CHmk4LIolzfz+A=="], - "@radix-ui/react-checkbox": ["@radix-ui/react-checkbox@1.3.4", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-previous": "1.1.2", "@radix-ui/react-use-size": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m3JmIOAX5ZzZ6VPjxEU2dbTOhoHi0nT5riwcDwe8idocsWf4a5DXJLDtZ6LfJwMBx7W+A2b7kp2TgPEKtaiF6A=="], + "@radix-ui/react-checkbox": ["@radix-ui/react-checkbox@1.3.7", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-previous": "1.1.2", "@radix-ui/react-use-size": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-JroKHfQBfh+fDuzpPsBC+pESkhuq8ql4hljTguz8MWnS35cISr3d/Jhl9kYrB44FlDtxCArYdDvTx+BSsJ64rQ=="], - "@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F0s8+p2XNpfc3k02zBfB0jPWbkHVG162+p7BdUMyJ2308QMqZ+oaclX+FAzKFovgL5OqRU+Rvy6f/vbdlJVaqA=="], + "@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-opfXRe6nnzyGmCDPx+l1Aqo/RbqWtQal2FnsBqF9hhePp6j0LsRoBaRxcMOlTv+uYTJVtWYZKg9t9wTe+BA/ZA=="], - "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.9", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-slot": "1.2.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zuSVi7ziP7uQRqc+yGxsKJfNkdyHv3ZKDaHe0gzg4dRgws96TPKWIiz84tVHP4GEcEl8bC0mdt17NkcxaJHmaQ=="], + "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.12", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-slot": "1.3.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-nb67INpE0IahJKN7EYPp9m9YGwYeKlnzxT3MwXVkgCskaSJia97kG4T0ywpjNUSSnoJk/uvk12V8vbrEHEj+/Q=="], "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="], - "@radix-ui/react-context": ["@radix-ui/react-context@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg=="], + "@radix-ui/react-context": ["@radix-ui/react-context@1.2.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-fOE+JtN9rygNZkCnHRBEP0TAvLldlhyOxMsbwFvTP4nAs+nBmfnna+o/Zski2wkmY1YMrFC0aSzsHoLY47iLrg=="], - "@radix-ui/react-context-menu": ["@radix-ui/react-context-menu@2.3.0", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-menu": "2.1.17", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-d7CouXhAW+CGmFOqmB+IEvd3E9GcaqfgvfjCc3hfulp2pkaUCEVEGa0SN5nNWYA+IvQ6g1Pt+S5dpNn1AoY9hg=="], + "@radix-ui/react-context-menu": ["@radix-ui/react-context-menu@2.3.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-menu": "2.1.20", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-PS+gKE0z2prJ74Y0sM+brAGK4mYOHIR7TlcV5EJgUQ6E0xMvyswkK2X4yRqyganrzsRL+WCSKAPu0NQITICRWg=="], - "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-dismissable-layer": "1.1.12", "@radix-ui/react-focus-guards": "1.1.4", "@radix-ui/react-focus-scope": "1.1.9", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-portal": "1.1.11", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-slot": "1.2.5", "@radix-ui/react-use-controllable-state": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-l9ok83YBclEZhbjgzt76Hw733e6cvRKPNgO6GJ/IETlufXG9p+fRu2wlvpImQvR6xdJ8h7J8J2DBvsPEiEsKMw=="], + "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.19", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-dismissable-layer": "1.1.15", "@radix-ui/react-focus-guards": "1.1.4", "@radix-ui/react-focus-scope": "1.1.12", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-portal": "1.1.13", "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-slot": "1.3.0", "@radix-ui/react-use-controllable-state": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-+HhbN2+YtkRgVirjZ2afMeutQRuGOrdkWR5+EFC58SJojGmtyNQwYzgi6tHBpOxvFHefMtPeHdgtjz0BOGxFQg=="], "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA=="], - "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-escape-keydown": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-MhoruH6xEzsbvOmo4TNgMfmtvRGyDZw4MDSdf4ybMHfezjqwzv6hyd4lsMzBp8K9Sn6sGzCF62x1I7BYUECXOg=="], + "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-effect-event": "0.0.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-b0XaRlzn2QKuo10XyNgi2DAJDf5XC9d1nD3FJcuvCjbR7+4Ad28zmZsLsqx+hvDEzMnRuZaZxZm9gYObV6RmRA=="], - "@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.17", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-menu": "2.1.17", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-S6b3Jm57sY5EdDyOMLkacbB0qMnKhy1RCKZCt795ZkmtUOAvojYIZ5p7dXHIh5Cyr3jCLLI5/g64V3FKLudZmw=="], + "@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.20", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-menu": "2.1.20", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-slfm+rRaZRuQBvHq60lXvSVUPhid0IPtjSZzIuUlWZMUs01iYZNlGS3mJgRD3ChLQVBAYlKiL/tFyWGX+dz8Xw=="], "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q=="], - "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.9", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-callback-ref": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9Se8t+Zry+1rEOL7Y6l/4ANYU/TOtAtf8O2fKdwLltcaMcm6kOqYGbzO4tMFQ0bvzO920pRAoHpFZ4W85S3keQ=="], + "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.12", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-jjk/lqTeNL0azUx5ZYzVrl4NgaDIrdzTNE4mABV9yBFI7FQqN7pIgzV1bTleUezP2QiTGA1BFTqY8MegDgWX9A=="], - "@radix-ui/react-form": ["@radix-ui/react-form@0.1.9", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-label": "2.1.9", "@radix-ui/react-primitive": "2.1.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-eTPyThIKDacJ3mJDvYwf/PSmsEYlOyA2Qcb+aGyWwYv+P5w57VPUkMVA2XJ9z0Du2KBY1HoHQzhPV9iYL/r4hg=="], + "@radix-ui/react-form": ["@radix-ui/react-form@0.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-label": "2.1.11", "@radix-ui/react-primitive": "2.1.7" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-JTX94E4LDL91rzLg7X0mHPdxr0A8JEdVwZEmeOwZJSMDHCGW5DFtSlTSJozUyUs807IQmnvbfzKZFVCK5DmkqQ=="], - "@radix-ui/react-hover-card": ["@radix-ui/react-hover-card@1.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-dismissable-layer": "1.1.12", "@radix-ui/react-popper": "1.3.0", "@radix-ui/react-portal": "1.1.11", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-hAileDBtd6CX7nlZOarOnISQ6PP4q0e16BX51ulzdZ+7IzjL0sDTVpFdmSYrIjw6zVNsfQBao5gG6AWr3qwfvA=="], + "@radix-ui/react-hover-card": ["@radix-ui/react-hover-card@1.1.19", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-dismissable-layer": "1.1.15", "@radix-ui/react-popper": "1.3.3", "@radix-ui/react-portal": "1.1.13", "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-2KTgMLQtKvicznQgbindEI2RZ3QbDIwU5gabjUPwFJsormjGDz+rUvO4NANmYwzEEpTcTONUt33vBHIfTIVSfw=="], "@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="], - "@radix-ui/react-label": ["@radix-ui/react-label@2.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-rDoTeMbCwRVcnmo7NGT9IlPo1yXmEI+xc1URP3oeewwZEV4mdTp1dYUhYbQdo4D1q2SjKVvv4N1gNY77QAQtjA=="], + "@radix-ui/react-label": ["@radix-ui/react-label@2.1.11", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.7" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-3PKvDDxOn62k0oV1n4QtNtD2vpu+zYjXR7ojLBPaO6SPvhy53yg0vAmgNeBQeJW5rV3dffoRG+HYfLBZuzw0CQ=="], - "@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.17", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-collection": "1.1.9", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.12", "@radix-ui/react-focus-guards": "1.1.4", "@radix-ui/react-focus-scope": "1.1.9", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-popper": "1.3.0", "@radix-ui/react-portal": "1.1.11", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-roving-focus": "1.1.12", "@radix-ui/react-slot": "1.2.5", "@radix-ui/react-use-callback-ref": "1.1.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-fmbNnFyf+JYCN0DhhWnEdUTDnZD1mXaPQWivdsPIb8oOSbARfD3LIQJbLCG8a8QLCwoMxiJ7GVPIFcC8Dw8v2Q=="], + "@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.20", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.15", "@radix-ui/react-focus-guards": "1.1.4", "@radix-ui/react-focus-scope": "1.1.12", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-popper": "1.3.3", "@radix-ui/react-portal": "1.1.13", "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-roving-focus": "1.1.15", "@radix-ui/react-slot": "1.3.0", "@radix-ui/react-use-callback-ref": "1.1.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-VsUrXxFe9d2ScbZF0fR/oPR1+qjyeLs5p0jzG8h90puMoA9bq4SirYlXbE+USRg9Q2qTeJSFNqjw2nts8jJe4w=="], - "@radix-ui/react-menubar": ["@radix-ui/react-menubar@1.1.17", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-collection": "1.1.9", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-menu": "2.1.17", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-roving-focus": "1.1.12", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-AKtZ4O782yO7qwIyq73WpulYt1IHhQ0htDb6wNcxzxnSDCcSWMVBiU9ycpcA90XzQO4IVIxIErtak6Kg/Vt0rQ=="], + "@radix-ui/react-menubar": ["@radix-ui/react-menubar@1.1.20", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-menu": "2.1.20", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-roving-focus": "1.1.15", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-gzFZvybgmwYsFBWDqanycIoEYnhyk8MMnuLamdFVHUZYGp4COM+sqXiwbnn0VMWqGLeeU7GV7jm+dXRa+Wufag=="], - "@radix-ui/react-navigation-menu": ["@radix-ui/react-navigation-menu@1.2.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-collection": "1.1.9", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.12", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-use-previous": "1.1.2", "@radix-ui/react-visually-hidden": "1.2.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/fS8hKCcRt4DwCGa5QIB3juRXmfYSOk4a2AEe/BDIyy7Hm+eje2Y13oUx5zejl+wFt1owrM7E8NWlbaEl5EGpg=="], + "@radix-ui/react-navigation-menu": ["@radix-ui/react-navigation-menu@1.2.18", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.15", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-use-previous": "1.1.2", "@radix-ui/react-visually-hidden": "1.2.7" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-K9HiuxZ6xCwSaHcIuUpxyhy4w5gpwzWjh9dHTSbMN3Ix4qAyVObS9RlU3zMycb0PO3v9Tpk0BXMwWvXOUbVXew=="], - "@radix-ui/react-one-time-password-field": ["@radix-ui/react-one-time-password-field@0.1.9", "", { "dependencies": { "@radix-ui/number": "1.1.2", "@radix-ui/primitive": "1.1.4", "@radix-ui/react-collection": "1.1.9", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-roving-focus": "1.1.12", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-effect-event": "0.0.3", "@radix-ui/react-use-is-hydrated": "0.1.1", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-fvCzA9hm7yN5xxTPJIi4VhSmH5gv+76ILsxguBK3cm3icD5BR4vW7POQmu8Zio0yh91uuouG/Kang40IbMkaSQ=="], + "@radix-ui/react-one-time-password-field": ["@radix-ui/react-one-time-password-field@0.1.12", "", { "dependencies": { "@radix-ui/number": "1.1.2", "@radix-ui/primitive": "1.1.5", "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-roving-focus": "1.1.15", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-effect-event": "0.0.3", "@radix-ui/react-use-is-hydrated": "0.1.1", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-nQLu5OAcORDQp1EHAv6k3mJGV1hjMTw2NTGVAsGE1g/mWeNqAd1R5jyaAs3U+A8ZD/W8XNPY2yKT0ZdQnqo3NA=="], - "@radix-ui/react-password-toggle-field": ["@radix-ui/react-password-toggle-field@0.1.4", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-effect-event": "0.0.3", "@radix-ui/react-use-is-hydrated": "0.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-qoDSkObZ9faJlsjlwyBH6ia7kq9vaJ2QwWTowT3nQpzPvUTAKesmWuGJYpd91HIoJqS+5ZPXy5uFPp+HlwdaAg=="], + "@radix-ui/react-password-toggle-field": ["@radix-ui/react-password-toggle-field@0.1.7", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-effect-event": "0.0.3", "@radix-ui/react-use-is-hydrated": "0.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-gB1Mr8vzdv1XzDjrtJTXmL0JORRs1B4g7ngUs0F+H2VvMOwXTZMTmLCl0wZZ3m7ylX8TssI7NCvgiSHmLuTm/A=="], - "@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-dismissable-layer": "1.1.12", "@radix-ui/react-focus-guards": "1.1.4", "@radix-ui/react-focus-scope": "1.1.9", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-popper": "1.3.0", "@radix-ui/react-portal": "1.1.11", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-slot": "1.2.5", "@radix-ui/react-use-controllable-state": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-8brVpAU5Uq7Bh0c8EFc4ZTf2JJTYn0o+1L+CUJB3UYIOkTjKGMgoHvduylrahdmNlr3DfH0rFq2DrbNZXgaspw=="], + "@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.19", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-dismissable-layer": "1.1.15", "@radix-ui/react-focus-guards": "1.1.4", "@radix-ui/react-focus-scope": "1.1.12", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-popper": "1.3.3", "@radix-ui/react-portal": "1.1.13", "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-slot": "1.3.0", "@radix-ui/react-use-controllable-state": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-jkrTdQVxnIB8fpn0NyyxW9CTB5aCXZZelVz5z+Xmii6g5WxMqS3fInNslZ63puP39+Puu4jYohUK31y3dT87gQ=="], - "@radix-ui/react-popper": ["@radix-ui/react-popper@1.3.0", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.9", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-use-rect": "1.1.2", "@radix-ui/react-use-size": "1.1.2", "@radix-ui/rect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9PB589e1aWZbrlFUHdz6WiPCL+xLZHQFX7oibqG/6Q0SwOkxDyQX9W/cyPa+sAPPKuC8cpLCpRczE5a/1DiwVQ=="], + "@radix-ui/react-popper": ["@radix-ui/react-popper@1.3.3", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.11", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-use-rect": "1.1.2", "@radix-ui/react-use-size": "1.1.2", "@radix-ui/rect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-mS7dGpyjv6b+gsDjLF7e0ia1W4Im1B1hSCy2yuXlHuvnZxHKagfDaobt/KAKt27EpZMit2pss8eJBVyVjEWM+g=="], - "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.11", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-UEytdjgEh2tJGgD/gZK4FUx6t1rNIlM3U0DENhSrG7I75FGm1DnaDuVUWF1pWAWUwGmn1sCJ1VGHn8LhN1aTOw=="], + "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.13", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-z3oXfmaHLJTF1wktbjgD6cn9jiEbq3WSondB10LIuIt2m2Ym4iJlrW04/euMwENDdWDdE7z+OuY7Qyp1YpRSwA=="], - "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.6", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ=="], + "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.7", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zBZ4QM5XG3JRanDmqXYf3MD6th4AFXFmgU6KNMFzUaV6F3uw9I5/zjMUvFriSEn5ewo1nxuibvyxJdmLlDcslA=="], - "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.5", "", { "dependencies": { "@radix-ui/react-slot": "1.2.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zifXeB8Y88qCYx8PLZ5oQb32KwZub+s925mMoZsBBq9KUQqWKkREubTfs6ASjRPPBe7Jt9O8OHH89+95VG+grA=="], + "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.7", "", { "dependencies": { "@radix-ui/react-slot": "1.3.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bC3NiwsprbxKjuon9l7X6BUTw7FPVzEYaL92MPEY5SCd/9hUTPXVFtVwRix7778wtRsVao+zE062gL79FZleeQ=="], - "@radix-ui/react-progress": ["@radix-ui/react-progress@1.1.9", "", { "dependencies": { "@radix-ui/react-context": "1.1.4", "@radix-ui/react-primitive": "2.1.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-+EOkvg1Zn1vI1+fRDfRSAiJ7BWfcDAo5ASMmbqrcLZ4s4USk2FGkoHgeb2X+CkUgo2zJMiyObwf1k44CrRWsyw=="], + "@radix-ui/react-progress": ["@radix-ui/react-progress@1.1.12", "", { "dependencies": { "@radix-ui/react-context": "1.2.0", "@radix-ui/react-primitive": "2.1.7" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ZPHyI0JyzoH/rP0tq2uRaIZTj/4s8+kAbqPz+e2N8+ejHvwPJ889dHhqn+vh7PNvNeq+boAoH9yzqeoShzwF2w=="], - "@radix-ui/react-radio-group": ["@radix-ui/react-radio-group@1.4.0", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-roving-focus": "1.1.12", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-previous": "1.1.2", "@radix-ui/react-use-size": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-eHdV5bLx9sH+tBnbDjkIBdvQEH/c6MEtQYhTbxkaDK9qsIFFLtmJYEQFVdwhnruWotLfQmIuWEL/J+L3utE8rQ=="], + "@radix-ui/react-radio-group": ["@radix-ui/react-radio-group@1.4.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-roving-focus": "1.1.15", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-previous": "1.1.2", "@radix-ui/react-use-size": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-WwZFjWV4s3aC1QtR3k04R+oANHtX2q6fgKlc7MCEiDNlnTxCZ3H8k3mHtEgVlOejystwk1WQgarQhNOQZ2bK1g=="], - "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-collection": "1.1.9", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-FvgPt1bRmg8Xt2QpF7NUZW3dE0ZQHGm41dAdgT2J2GJPoIXz+9Em3NobAxf4fupcxhgHu03E5CRiU2MWvObXyg=="], + "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-is-hydrated": "0.1.1", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-40svmmugfM3mUN7VUDGVE1tQGOhyi8enlGD0CNJEcMM36C1f71PKM21DFgNHUfem0XnA+d8H8oN3Z9ZpJjSslg=="], - "@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.2.11", "", { "dependencies": { "@radix-ui/number": "1.1.2", "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-DS39ziOgea75U/TrXKU2/oKp0be2jrDHnzFLvahg/0iNAT1Zq16e4Uw0WXwyXvsK+mG3BRyMb7A3NRZMDuEXtQ=="], + "@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.2.14", "", { "dependencies": { "@radix-ui/number": "1.1.2", "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bBODCWZK7JTbQLHs0uIP4f73wIWatakK4OS33UzkR1x897wu0PuO658a3f+6P2GEGyDzGYMuHRatMVoAk9WZTw=="], - "@radix-ui/react-select": ["@radix-ui/react-select@2.3.0", "", { "dependencies": { "@radix-ui/number": "1.1.2", "@radix-ui/primitive": "1.1.4", "@radix-ui/react-collection": "1.1.9", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.12", "@radix-ui/react-focus-guards": "1.1.4", "@radix-ui/react-focus-scope": "1.1.9", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-popper": "1.3.0", "@radix-ui/react-portal": "1.1.11", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-slot": "1.2.5", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-use-previous": "1.1.2", "@radix-ui/react-visually-hidden": "1.2.5", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-mENc7WpJvJcW8hlMpzfFcHcEhTvYS5JMBmi9HVC1Q00uhBwML086MHYUV8QQdQv6lcu0Wg8dzd1RB8AFADcG/g=="], + "@radix-ui/react-select": ["@radix-ui/react-select@2.3.3", "", { "dependencies": { "@radix-ui/number": "1.1.2", "@radix-ui/primitive": "1.1.5", "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.15", "@radix-ui/react-focus-guards": "1.1.4", "@radix-ui/react-focus-scope": "1.1.12", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-popper": "1.3.3", "@radix-ui/react-portal": "1.1.13", "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-slot": "1.3.0", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-use-previous": "1.1.2", "@radix-ui/react-visually-hidden": "1.2.7", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-L5RQTXz6Anxsf9CCv+pTgiAsUpyVj7rJxsGtmhFaEOJ++cVfXucv4qWfsIO0AIB4NAhi3yovWGVMKKS1Xf1Wrg=="], - "@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-gvgW+JV/Mbjj6darztTetnmElpQEzZrXpJvfj+dOxNAxiyHEAyUvEjjl4zxblvmjmKmi3jfPoy7ZdxzCuUBJSA=="], + "@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.11", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.7" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-jRhe86+8PF7VZ1u14eOWVOuh2BuAhALg/FT1VcMC4OHedMTRUazDnDlKTt+yxo5cRNKHMfmvZ4sSQtWDeMV4CQ=="], - "@radix-ui/react-slider": ["@radix-ui/react-slider@1.4.0", "", { "dependencies": { "@radix-ui/number": "1.1.2", "@radix-ui/primitive": "1.1.4", "@radix-ui/react-collection": "1.1.9", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-use-previous": "1.1.2", "@radix-ui/react-use-size": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-RHcPlLOThRJM51DSIC33ZnpDEBYhyEFroVWkd2P54PGGjkmAt14RboYUU9E1MFst666zFHM0tGtWvMjSOtU1pw=="], + "@radix-ui/react-slider": ["@radix-ui/react-slider@1.4.3", "", { "dependencies": { "@radix-ui/number": "1.1.2", "@radix-ui/primitive": "1.1.5", "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-use-previous": "1.1.2", "@radix-ui/react-use-size": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-CWVVj+XaTom0SKCqw1EUgb0NuiLwS+N3OFG73mVEezKEjgNIvZiu0EevMelSSU+CbX3owbqJweG2gPU31WGC5A=="], - "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rCMO3QsIVKv5JTY5CVbo2MvO77SpEqqYc8AvRE7OWqRDOIqAKjsp+DrmnY9uc8NPdxB5E2z47HTYGeE2+NTptg=="], + "@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA=="], - "@radix-ui/react-switch": ["@radix-ui/react-switch@1.3.0", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-previous": "1.1.2", "@radix-ui/react-use-size": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-GP1EZwhoZO/GGnhM1P5/2Vpm8iN8EnngyU0oezn2l78kN8tj25pyrvjIaT7azBhK615KSt+P2w39y57YV5jVkA=="], + "@radix-ui/react-switch": ["@radix-ui/react-switch@1.3.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-previous": "1.1.2", "@radix-ui/react-use-size": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-1+mlB4/lxJfk5tgJ4g+R5mUCbRpPE1T9+UsEyeLYbGgMtwiMgmuTnfKz4Mw1nHALHjuwyxw4MLd4cSHn6pNSlQ=="], - "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.14", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-roving-focus": "1.1.12", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-D5jwp9JNuwDeCw3CYD2Fz+sSHo0droQjC8u75dJHe4aWr5q6yBiXZU+hurXnKudRgEpUkD5TsI6bjHPo5ThUxA=="], + "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.17", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-roving-focus": "1.1.15", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-nRyXnrAVCwjeXcHbvEbLS6ndbTeKHG1RqCP4A8Gw5L4cemDzPXdD8rAmr6wet0v57R69wGvuIIsFjHSVkZiMzQ=="], - "@radix-ui/react-toast": ["@radix-ui/react-toast@1.2.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-collection": "1.1.9", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-dismissable-layer": "1.1.12", "@radix-ui/react-portal": "1.1.11", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-visually-hidden": "1.2.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-WUymDDiN2DpoGudRN1aW4wF5O3BNQjZZO/5nngPoNiEVqjyOzirvZZNO0R6dC1ifucSINVaSv8JX1aq47VGgiA=="], + "@radix-ui/react-toast": ["@radix-ui/react-toast@1.2.19", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-dismissable-layer": "1.1.15", "@radix-ui/react-portal": "1.1.13", "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-visually-hidden": "1.2.7" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-SxfVZfVOibWKWdkf0Xx1awW2d09fQu4V4PXDY1j5hi4MVf7MWdJZqTBJMa1KWtOr1S6GGtCk02nniZ0Iia+dHw=="], - "@radix-ui/react-toggle": ["@radix-ui/react-toggle@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-FikrKJemoBGZQ6uRID0HJqSPBP6D7OppdD2OhLl0ZYLlAyPXI7MezoYGmumwNkrAoRm35xXkb4C8JPfJZZzcaw=="], + "@radix-ui/react-toggle": ["@radix-ui/react-toggle@1.1.14", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-QI/hB65XKWACA66P64A+aHxtLUgHJeJLkaQa+awUNXT6T3swndtY5DojeHA+vldrTspMTtFBd7HfZ9QGbM1Qrw=="], - "@radix-ui/react-toggle-group": ["@radix-ui/react-toggle-group@1.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-roving-focus": "1.1.12", "@radix-ui/react-toggle": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TEgECgJaWGAHJJZGzNNEYTNBdIXqX7LchANycpyP7DkfjmuiSN7ISt1k/ZRGVJgVJonsgP4vwaiKMn5utrcwWQ=="], + "@radix-ui/react-toggle-group": ["@radix-ui/react-toggle-group@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-roving-focus": "1.1.15", "@radix-ui/react-toggle": "1.1.14", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-gIC5Q+Xljg7lmUdzSuDoy0t97yZn1sZl00Ra37ZvKrYdWnQLU6sWLd09yG8cIB9jUAlQfHgJ2ACAG00MFwsqSQ=="], - "@radix-ui/react-toolbar": ["@radix-ui/react-toolbar@1.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-roving-focus": "1.1.12", "@radix-ui/react-separator": "1.1.9", "@radix-ui/react-toggle-group": "1.1.12" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-4wHtJVdIgqMmEwUvxA0BYg/2JMRbt0L3+8UD8Ml/nhKkfXtiZcM8u/S15gQ5xj9YEd/0qlrm5bE805LsjQ+J8A=="], + "@radix-ui/react-toolbar": ["@radix-ui/react-toolbar@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-roving-focus": "1.1.15", "@radix-ui/react-separator": "1.1.11", "@radix-ui/react-toggle-group": "1.1.15" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t/iEuVjUnXXtrsGK40AA43uIx37sn3AqZ7oAVnPICK6lFJP6dzMzWR3U9b6eCfFjb6wtSEqkJ9Rn9xDjiOx20g=="], - "@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.2.9", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-dismissable-layer": "1.1.12", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-popper": "1.3.0", "@radix-ui/react-portal": "1.1.11", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-slot": "1.2.5", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-visually-hidden": "1.2.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-u6F9MmTtBSLkiXNVDrtB/yPCZarM9smNswC24YYLV/M+bth6J3Gs3vlJezEoFwKZvPvxhCpUYdUnOsNG/0XOlA=="], + "@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.2.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-dismissable-layer": "1.1.15", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-popper": "1.3.3", "@radix-ui/react-portal": "1.1.13", "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-slot": "1.3.0", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-visually-hidden": "1.2.7" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-U3HoftgWnmla78vzQbLvKKb7bUYJxoiiqYFzp1wu/TBMyDqMZSuCl3aRICsD6EfVEwcJD2mumGDGUXLFVqQHKA=="], "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw=="], @@ -850,7 +851,7 @@ "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.3", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA=="], - "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.2", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2uVLvLjgO7NZCWw01/FdqRwmA42J0BcjPMUCA+koFEOAb+zjqIP7SiFz/7zWPrKnVmSqr76Omq2ALyCuX4dhLw=="], + "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.3", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-3wEkMiPHXha/2VadZ68rYBcmYnPINVGl4Y3gtcM7fKRjANk0OscK+cdqBgUWdozb7YJxsh0vefM7vgAMHXOjqg=="], "@radix-ui/react-use-is-hydrated": ["@radix-ui/react-use-is-hydrated@0.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-qwOiz4Tjo8CNnrOLAYUMXeZwDzXgXpvK4TKQPmWLECM9XoWvA6+0Z2/7Ag3A4ivjS4ovbLJPbskkxioFyBhr8A=="], @@ -862,117 +863,117 @@ "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w=="], - "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.5", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tPcHNI3FajdDBFpl/Ez1m2WL0ufJqBKyHxMDBvKitopamK36WwBGOMicuMEZKkM5Wce41QxUyv6BsiqfrWBiGg=="], + "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.7" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-1wNZBggTDK3GRuuQ6nP4k2yi7a6l7I5qbMPbZcRsrGsGVead/f/d5FhEzUvqFs0bcrDLx7n1zKQ3JvLR6whaaw=="], "@radix-ui/rect": ["@radix-ui/rect@1.1.2", "", {}, "sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA=="], "@radix-ui/themes": ["@radix-ui/themes@3.3.0", "", { "dependencies": { "@radix-ui/colors": "^3.0.0", "classnames": "^2.3.2", "radix-ui": "^1.1.3", "react-remove-scroll-bar": "^2.3.8" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-I0/h2CRNTpYNB7Mi3xFIvSsQq5a108d7kK8dTO5zp5b9HR5QJXKag6B8tjpz2ITkVYkFdkGk45doNkSr7OxwNw=="], - "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.3", "", { "os": "android", "cpu": "arm64" }, "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw=="], + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.1.5", "", { "os": "android", "cpu": "arm64" }, "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ=="], - "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA=="], + "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.1.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw=="], - "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg=="], + "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.1.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g=="], - "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g=="], + "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.1.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA=="], - "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.3", "", { "os": "linux", "cpu": "arm" }, "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw=="], + "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.1.5", "", { "os": "linux", "cpu": "arm" }, "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw=="], - "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw=="], + "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.1.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q=="], - "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q=="], + "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.1.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA=="], - "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg=="], + "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.1.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg=="], - "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg=="], + "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.1.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA=="], - "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg=="], + "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.1.5", "", { "os": "linux", "cpu": "x64" }, "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ=="], - "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow=="], + "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.1.5", "", { "os": "linux", "cpu": "x64" }, "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg=="], - "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.3", "", { "os": "none", "cpu": "arm64" }, "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg=="], + "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.1.5", "", { "os": "none", "cpu": "arm64" }, "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw=="], - "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.3", "", { "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", "@napi-rs/wasm-runtime": "^1.1.4" }, "cpu": "none" }, "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg=="], + "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.1.5", "", { "dependencies": { "@emnapi/core": "1.11.1", "@emnapi/runtime": "1.11.1", "@napi-rs/wasm-runtime": "^1.1.6" }, "cpu": "none" }, "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA=="], - "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g=="], + "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.1.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw=="], - "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA=="], + "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.1.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA=="], "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="], - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.61.1", "", { "os": "android", "cpu": "arm" }, "sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.62.2", "", { "os": "android", "cpu": "arm" }, "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg=="], - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.61.1", "", { "os": "android", "cpu": "arm64" }, "sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw=="], + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.62.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw=="], - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.61.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA=="], + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.62.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A=="], - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.61.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ=="], + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.62.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA=="], - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.61.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw=="], + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.62.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw=="], - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.61.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw=="], + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.62.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg=="], - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.61.1", "", { "os": "linux", "cpu": "arm" }, "sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA=="], + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.62.2", "", { "os": "linux", "cpu": "arm" }, "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg=="], - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.61.1", "", { "os": "linux", "cpu": "arm" }, "sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ=="], + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.62.2", "", { "os": "linux", "cpu": "arm" }, "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA=="], - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.61.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg=="], + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.62.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA=="], - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.61.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w=="], + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.62.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ=="], - "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.61.1", "", { "os": "linux", "cpu": "none" }, "sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ=="], + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.62.2", "", { "os": "linux", "cpu": "none" }, "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg=="], - "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.61.1", "", { "os": "linux", "cpu": "none" }, "sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ=="], + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.62.2", "", { "os": "linux", "cpu": "none" }, "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ=="], - "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.61.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g=="], + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.62.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A=="], - "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.61.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw=="], + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.62.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w=="], - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.61.1", "", { "os": "linux", "cpu": "none" }, "sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g=="], + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.62.2", "", { "os": "linux", "cpu": "none" }, "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg=="], - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.61.1", "", { "os": "linux", "cpu": "none" }, "sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ=="], + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.62.2", "", { "os": "linux", "cpu": "none" }, "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q=="], - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.61.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g=="], + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.62.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg=="], - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.61.1", "", { "os": "linux", "cpu": "x64" }, "sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q=="], + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.62.2", "", { "os": "linux", "cpu": "x64" }, "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A=="], - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.61.1", "", { "os": "linux", "cpu": "x64" }, "sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw=="], + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.62.2", "", { "os": "linux", "cpu": "x64" }, "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg=="], - "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.61.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg=="], + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.62.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg=="], - "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.61.1", "", { "os": "none", "cpu": "arm64" }, "sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA=="], + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.62.2", "", { "os": "none", "cpu": "arm64" }, "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA=="], - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.61.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g=="], + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.62.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg=="], - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.61.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ=="], + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.62.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q=="], - "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.61.1", "", { "os": "win32", "cpu": "x64" }, "sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ=="], + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.62.2", "", { "os": "win32", "cpu": "x64" }, "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg=="], - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.61.1", "", { "os": "win32", "cpu": "x64" }, "sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw=="], + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.62.2", "", { "os": "win32", "cpu": "x64" }, "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA=="], - "@rspack/binding": ["@rspack/binding@1.7.11", "", { "optionalDependencies": { "@rspack/binding-darwin-arm64": "1.7.11", "@rspack/binding-darwin-x64": "1.7.11", "@rspack/binding-linux-arm64-gnu": "1.7.11", "@rspack/binding-linux-arm64-musl": "1.7.11", "@rspack/binding-linux-x64-gnu": "1.7.11", "@rspack/binding-linux-x64-musl": "1.7.11", "@rspack/binding-wasm32-wasi": "1.7.11", "@rspack/binding-win32-arm64-msvc": "1.7.11", "@rspack/binding-win32-ia32-msvc": "1.7.11", "@rspack/binding-win32-x64-msvc": "1.7.11" } }, "sha512-2MGdy2s2HimsDT444Bp5XnALzNRxuBNc7y0JzyuqKbHBywd4x2NeXyhWXXoxufaCFu5PBc9Qq9jyfjW2Aeh06Q=="], + "@rspack/binding": ["@rspack/binding@1.7.12", "", { "optionalDependencies": { "@rspack/binding-darwin-arm64": "1.7.12", "@rspack/binding-darwin-x64": "1.7.12", "@rspack/binding-linux-arm64-gnu": "1.7.12", "@rspack/binding-linux-arm64-musl": "1.7.12", "@rspack/binding-linux-x64-gnu": "1.7.12", "@rspack/binding-linux-x64-musl": "1.7.12", "@rspack/binding-wasm32-wasi": "1.7.12", "@rspack/binding-win32-arm64-msvc": "1.7.12", "@rspack/binding-win32-ia32-msvc": "1.7.12", "@rspack/binding-win32-x64-msvc": "1.7.12" } }, "sha512-f4HHuLbvuld8Ba4iB/4ibse5XrKxFrgmM3S4P2AOKnPlekAFlBjmltCuaTL/W2ggYvILaVY+YcFXrEH1rrKeQA=="], - "@rspack/binding-darwin-arm64": ["@rspack/binding-darwin-arm64@1.7.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oduECiZVqbO5zlVw+q7Vy65sJFth99fWPTyucwvLJJtJkPL5n17Uiql2cYP6Ijn0pkqtf1SXgK8WjiKLG5bIig=="], + "@rspack/binding-darwin-arm64": ["@rspack/binding-darwin-arm64@1.7.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-rbFprJaJiqrmfy8SHth8EsoRS0wg4bXcucwj9NiMzpGFq14Opw8c04iQ6H9BECYzgmN0PKZ9rh41LdVvhdZe4A=="], - "@rspack/binding-darwin-x64": ["@rspack/binding-darwin-x64@1.7.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-a1+TtTE9ap6RalgFi7FGIgkJP6O4Vy6ctv+9WGJy53E4kuqHR0RygzaiVxCI/GMc/vBT9vY23hyrpWb3d1vtXA=="], + "@rspack/binding-darwin-x64": ["@rspack/binding-darwin-x64@1.7.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-jnOp+/UXOJa9xqUb8KXH03sysoO2e4Ij6tw6MqDdmdj8n/A8PQENRPUbW9AwXpPtVDJPus9r4fi7b3+6e4B8Hg=="], - "@rspack/binding-linux-arm64-gnu": ["@rspack/binding-linux-arm64-gnu@1.7.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-P0QrGRPbTWu6RKWfN0bDtbnEps3rXH0MWIMreZABoUrVmNQKtXR6e73J3ub6a+di5s2+K0M2LJ9Bh2/H4UsDUA=="], + "@rspack/binding-linux-arm64-gnu": ["@rspack/binding-linux-arm64-gnu@1.7.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-C8owWG+yvo7X0oVLIXetkoJhIFBP1LYNcAQqtgLmJnQLQDklGuP83dKC+zISGQWpjawHfZ1ER96vLgoTrxKZdw=="], - "@rspack/binding-linux-arm64-musl": ["@rspack/binding-linux-arm64-musl@1.7.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-6ky7R43VMjWwmx3Yx7Jl7faLBBMAgMDt+/bN35RgwjiPgsIByz65EwytUVuW9rikB43BGHvA/eqlnjLrUzNBqw=="], + "@rspack/binding-linux-arm64-musl": ["@rspack/binding-linux-arm64-musl@1.7.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-i51WWI64aRpsfSki6rN0aepPqXkVfS+vZM7+4bWDcmnhUmdMvhIPcYg0QRk3DtyJnu33jqNLM0WHY78k00NyfA=="], - "@rspack/binding-linux-x64-gnu": ["@rspack/binding-linux-x64-gnu@1.7.11", "", { "os": "linux", "cpu": "x64" }, "sha512-cuOJMfCOvb2Wgsry5enXJ3iT1FGUjdPqtGUBVupQlEG4ntSYsQ2PtF4wIDVasR3wdxC5nQbipOrDiN/u6fYsdQ=="], + "@rspack/binding-linux-x64-gnu": ["@rspack/binding-linux-x64-gnu@1.7.12", "", { "os": "linux", "cpu": "x64" }, "sha512-MSos0FuPEefqo9V92ULd5hggKG29EkSNg1zDcypy0OkpsKh5pfjVxTLYFXgTcVyFoUQQbdG8zFBzYbwmJ8V4ew=="], - "@rspack/binding-linux-x64-musl": ["@rspack/binding-linux-x64-musl@1.7.11", "", { "os": "linux", "cpu": "x64" }, "sha512-CoK37hva4AmHGh3VCsQXmGr40L36m1/AdnN5LEjUX6kx5rEH7/1nEBN6Ii72pejqDVvk9anEROmPDiPw10tpFg=="], + "@rspack/binding-linux-x64-musl": ["@rspack/binding-linux-x64-musl@1.7.12", "", { "os": "linux", "cpu": "x64" }, "sha512-JcAMVKXOnjfpC3coWjCFPWD3Yl8RBw6a+IXQQ8mfRlHaHMIiOv8IfZqx15XRxMUn49CtP7Z0Na8iiAg2aKrcfw=="], - "@rspack/binding-wasm32-wasi": ["@rspack/binding-wasm32-wasi@1.7.11", "", { "dependencies": { "@napi-rs/wasm-runtime": "1.0.7" }, "cpu": "none" }, "sha512-OtrmnPUVJMxjNa3eDMfHyPdtlLRmmp/aIm0fQHlAOATbZvlGm12q7rhPW5BXTu1yh+1rQ1/uqvz+SzKEZXuJaQ=="], + "@rspack/binding-wasm32-wasi": ["@rspack/binding-wasm32-wasi@1.7.12", "", { "dependencies": { "@napi-rs/wasm-runtime": "1.0.7" }, "cpu": "none" }, "sha512-n+ZqP6ZMc0nhOgvadg5VhEs9ojtbES80AcWeFnmGkbzIszvGSO63GKNiRkXtjJ9KFuRzytbbmsCqkUVH+Tywxg=="], - "@rspack/binding-win32-arm64-msvc": ["@rspack/binding-win32-arm64-msvc@1.7.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-lObFW6e5lCWNgTBNwT//yiEDbsxm9QG4BYUojqeXxothuzJ/L6ibXz6+gLMvbOvLGV3nKgkXmx8GvT9WDKR0mA=="], + "@rspack/binding-win32-arm64-msvc": ["@rspack/binding-win32-arm64-msvc@1.7.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-8+h5fYDXYdmugbdfZ+D1y8IQ3rv2EhSfyGP7vBe+bjNyaMa4jWrpucmZbtxojUL1AzaeuHbvMdj9UO/gelk/+g=="], - "@rspack/binding-win32-ia32-msvc": ["@rspack/binding-win32-ia32-msvc@1.7.11", "", { "os": "win32", "cpu": "ia32" }, "sha512-0pYGnZd8PPqNR68zQ8skamqNAXEA1sUfXuAdYcknIIRq2wsbiwFzIc0Pov1cIfHYab37G7sSIPBiOUdOWF5Ivw=="], + "@rspack/binding-win32-ia32-msvc": ["@rspack/binding-win32-ia32-msvc@1.7.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-cDMGwTRSa2p9fNBVe1wTRkF2AEXZ9ARWW36QeC5CkLaI0Ezz8lvhF2+CSOPnhaQ1O1qtn0L0SF+lFnrY+I7xGQ=="], - "@rspack/binding-win32-x64-msvc": ["@rspack/binding-win32-x64-msvc@1.7.11", "", { "os": "win32", "cpu": "x64" }, "sha512-EeQXayoQk/uBkI3pdoXfQBXNIUrADq56L3s/DFyM2pJeUDrWmhfIw2UFIGkYPTMSCo8F2JcdcGM32FGJrSnU0Q=="], + "@rspack/binding-win32-x64-msvc": ["@rspack/binding-win32-x64-msvc@1.7.12", "", { "os": "win32", "cpu": "x64" }, "sha512-wIqFvlgFqrgUyj/6S/FJcvShnkZOmIeXTfqvheLY67MGq8qd8jb1YimQVKAIrmWB3yuJKUFACI3Ag1UBtEedEA=="], - "@rspack/core": ["@rspack/core@1.7.11", "", { "dependencies": { "@module-federation/runtime-tools": "0.22.0", "@rspack/binding": "1.7.11", "@rspack/lite-tapable": "1.1.0" }, "peerDependencies": { "@swc/helpers": ">=0.5.1" }, "optionalPeers": ["@swc/helpers"] }, "sha512-rsD9b+Khmot5DwCMiB3cqTQo53ioPG3M/A7BySu8+0+RS7GCxKm+Z+mtsjtG/vsu4Tn2tcqCdZtA3pgLoJB+ew=="], + "@rspack/core": ["@rspack/core@1.7.12", "", { "dependencies": { "@module-federation/runtime-tools": "0.22.0", "@rspack/binding": "1.7.12", "@rspack/lite-tapable": "1.1.0" }, "peerDependencies": { "@swc/helpers": ">=0.5.1" }, "optionalPeers": ["@swc/helpers"] }, "sha512-6CwFIHlhRmXfZoMj3v9MZ1SMTPBn+cHVXeMIeaGp5sufqinKsISbsqHu6ZMJu2wDSmZLdmQJX6zLxkhcAUlhkQ=="], "@rspack/lite-tapable": ["@rspack/lite-tapable@1.1.0", "", {}, "sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw=="], @@ -982,7 +983,7 @@ "@sideway/pinpoint": ["@sideway/pinpoint@2.0.0", "", {}, "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ=="], - "@sinclair/typebox": ["@sinclair/typebox@0.27.10", "", {}, "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA=="], + "@sinclair/typebox": ["@sinclair/typebox@0.27.12", "", {}, "sha512-hhyNJ+nbR6ZR7pToHvllEFun9TL0sbL+tk/ON75lo+Xas054uez98qRbsuNt7MBCyZKK4+8Yli/OAGZhmfBZ/g=="], "@sindresorhus/is": ["@sindresorhus/is@4.6.0", "", {}, "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="], @@ -1018,79 +1019,79 @@ "@svgr/webpack": ["@svgr/webpack@8.1.0", "", { "dependencies": { "@babel/core": "^7.21.3", "@babel/plugin-transform-react-constant-elements": "^7.21.3", "@babel/preset-env": "^7.20.2", "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.21.0", "@svgr/core": "8.1.0", "@svgr/plugin-jsx": "8.1.0", "@svgr/plugin-svgo": "8.1.0" } }, "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA=="], - "@swc/core": ["@swc/core@1.15.40", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.26" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.15.40", "@swc/core-darwin-x64": "1.15.40", "@swc/core-linux-arm-gnueabihf": "1.15.40", "@swc/core-linux-arm64-gnu": "1.15.40", "@swc/core-linux-arm64-musl": "1.15.40", "@swc/core-linux-ppc64-gnu": "1.15.40", "@swc/core-linux-s390x-gnu": "1.15.40", "@swc/core-linux-x64-gnu": "1.15.40", "@swc/core-linux-x64-musl": "1.15.40", "@swc/core-win32-arm64-msvc": "1.15.40", "@swc/core-win32-ia32-msvc": "1.15.40", "@swc/core-win32-x64-msvc": "1.15.40" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-2kwzJikRvgtNAG7MwVZY2vEzZjTxKIq5jXOihuSV/8U+Hej8Va22t65aKnJZs3P+NwojZvR8Mf8kyM7O+V8sQg=="], + "@swc/core": ["@swc/core@1.15.43", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.27" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.15.43", "@swc/core-darwin-x64": "1.15.43", "@swc/core-linux-arm-gnueabihf": "1.15.43", "@swc/core-linux-arm64-gnu": "1.15.43", "@swc/core-linux-arm64-musl": "1.15.43", "@swc/core-linux-ppc64-gnu": "1.15.43", "@swc/core-linux-s390x-gnu": "1.15.43", "@swc/core-linux-x64-gnu": "1.15.43", "@swc/core-linux-x64-musl": "1.15.43", "@swc/core-win32-arm64-msvc": "1.15.43", "@swc/core-win32-ia32-msvc": "1.15.43", "@swc/core-win32-x64-msvc": "1.15.43" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-1CuKjFkPxIgGdeHVuNbkxmBxkcbdc08u0aiI43pFq6yY1tTVKmXT9hFEooyyKs/sJ3xf1GPHyEwTtk9Xl8dvQw=="], - "@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.15.40", "", { "os": "darwin", "cpu": "arm64" }, "sha512-PaYyclfmQ++77D8ityYvmmVzHv9aG8ROwt2GfG6/ccloy4Hgf80qtOnzb9VYvPsUT7Ty1uhuDRhv3XYpf62qhQ=="], + "@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.15.43", "", { "os": "darwin", "cpu": "arm64" }, "sha512-v1aVuvXdo/BHxJzco9V2xpHrvwWmhfS8t6gziY5wJxd+Z2h8AeJRnAwPD8itCDaGXVBwJ/CaKfxEzTkG0Va0OA=="], - "@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.15.40", "", { "os": "darwin", "cpu": "x64" }, "sha512-HbbPzvfLBUXjIB1Ezks+//lNUjmLjfyd63XSwprJgrZaXYdm70kohXPJUWdqKZozolFxbPaO+xtBaiUp6BoueA=="], + "@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.15.43", "", { "os": "darwin", "cpu": "x64" }, "sha512-lp3d4Lamc8dt5huYdGLSR+9hLxmfr1jb0l+4XXG2zPqZwYWRN9R0U2qYoTrggiU2RWW0oV9VbWM3kBnqIc2kdQ=="], - "@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.15.40", "", { "os": "linux", "cpu": "arm" }, "sha512-SlRZsCjOCPR2LvFs0Ri/Xrx/5o5TCt8vl4gW6mX1hEZOG0a625RxzRHpHdAQNGykmAN/7IeaFAJG+QnNmxlHcA=="], + "@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.15.43", "", { "os": "linux", "cpu": "arm" }, "sha512-JWTQQELtsG5GgphDrr/XqqmM2pDN3cZqbMS0Mrg+iTiXL3F74sn/S2IyYE/5u4h2KLkTf9qQ7dXyxsbx7YzkeA=="], - "@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.15.40", "", { "os": "linux", "cpu": "arm64" }, "sha512-Q8byxJt2fh8CR3EUX6snBpy47AoBVm+In/+Z3rjDHMjC38ZvR9/gtUUNCT0tfrn4EdVsO8/QPi59nxrxvqxvBQ=="], + "@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.15.43", "", { "os": "linux", "cpu": "arm64" }, "sha512-B4otJRdPWIsmiSBf0uG7Z/+vMWmkufjz5MmYxubwKuZazDW14Zd3symga1N62QR4RT+kEFeHEgsXfZGyn/w0hw=="], - "@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.15.40", "", { "os": "linux", "cpu": "arm64" }, "sha512-4z0MgHU+7M0pZDqBN1El7mFXDI1SBwinfcUkAyA4v8QrhOIUOZltySt2aStQLZGrdXVXM4Y4ylfiTC04ED+MoQ=="], + "@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.15.43", "", { "os": "linux", "cpu": "arm64" }, "sha512-6zB6OnpViBxYy4tgY3v2i6AZY9fwkcHZ032UOwtwUuW1d19sdT07qF0kZe6/3UR1tUaK6jjg2rmVcUIBCEYVjQ=="], - "@swc/core-linux-ppc64-gnu": ["@swc/core-linux-ppc64-gnu@1.15.40", "", { "os": "linux", "cpu": "ppc64" }, "sha512-fLI4iUgeSZu0eRWUXwe6YzPFx9gHbFiPkl8Rp3mJfP8OpNR3nTQCGPvHdDh9xniW7mVvgMY4ni7A4VzqI1KrpA=="], + "@swc/core-linux-ppc64-gnu": ["@swc/core-linux-ppc64-gnu@1.15.43", "", { "os": "linux", "cpu": "ppc64" }, "sha512-coxE1ZWdB3uSDVNoEtYNrRi/1epvckZx9cTJ8ICUxTMTxGk+yvQ/Twacp3ruZSaMPGCriUjP86C37VhaT6nyRg=="], - "@swc/core-linux-s390x-gnu": ["@swc/core-linux-s390x-gnu@1.15.40", "", { "os": "linux", "cpu": "s390x" }, "sha512-YqeKMAb7d4nQSGMJQ454IlaCENpzcDqhvBE9+CPfdnYpnUXxd+BSrB6Xk0YjW8UyoEhUj4p6quATCxbsp6J3jg=="], + "@swc/core-linux-s390x-gnu": ["@swc/core-linux-s390x-gnu@1.15.43", "", { "os": "linux", "cpu": "s390x" }, "sha512-lXfLhs+LpBsD5inuYx+YDH5WsPPBQ95KPUiy8P5wq9ob9xKDZFqwNfU2QW6bGO8NqRO/H9JQomTSt5Yyh+FGfA=="], - "@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.15.40", "", { "os": "linux", "cpu": "x64" }, "sha512-7HOuS1iGcme/j/TuL1TfmmLGiMQrjv/GmjyZeydl00FKPtpGXEldwqfI56xgd1YzrzoB2svWjxbGGyQ0TEASxg=="], + "@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.15.43", "", { "os": "linux", "cpu": "x64" }, "sha512-07XnKwTmKy8TGOZG3D9fRnLWGynxPjwQnZLVmBFbo6F+7vHYzBIOuwXEhemrChBWb6yDNZsVCcMWCPX6FDD2xg=="], - "@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.15.40", "", { "os": "linux", "cpu": "x64" }, "sha512-h4kZYHc7dpc9P9u4brRJaS8Pl7tPVHAeiLSzw7T5RfIJgAoSdaCMKzI/2Uay9gFhaw8uyCDl0L5q37r0EpAfIA=="], + "@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.15.43", "", { "os": "linux", "cpu": "x64" }, "sha512-TJc+bsSIaBh+hZvZ5GRtW/K1bw66TJ9vsUwvVIsZdiWxU5ObLwZvfcnZ3UpgVfMnFibRes9uriJrQNBHEEogRQ=="], - "@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.15.40", "", { "os": "win32", "cpu": "arm64" }, "sha512-+mQgKZXSj6mV38Zh05QaxSjUDmGP/R2JWlXZTDLSPkDzHU6p3GxN9eeSf5dfyDVU86946fmCvSzyl/ucImx8+A=="], + "@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.15.43", "", { "os": "win32", "cpu": "arm64" }, "sha512-jfd7s2/bUQYkOHLs+LWQNKZdmDa8+sufKLllhpWAhVQ2GDCwsHe3vR/j+OSiItZNtkzFuaawa3+SAKz9y5gYfw=="], - "@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.15.40", "", { "os": "win32", "cpu": "ia32" }, "sha512-yvwdPLGd25mcj/mNatjNQ0lZujtQD6psH3v9PNmMb+fSzjbNG8KIDxjFWrcV+fsFVLOkyOmdJsFmX7NAFjVyPw=="], + "@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.15.43", "", { "os": "win32", "cpu": "ia32" }, "sha512-rLAE8JvucqEW1ZGohxPQrQWPBQeJG4+ypKbWfdlU/qmKScvCkxf9/Jxnzki1dkUQCQ7P5Enp13RlvqOlvx/32g=="], - "@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.15.40", "", { "os": "win32", "cpu": "x64" }, "sha512-OXtKsLU1bVtInzzDEAY2sYiF/rl4tvAnLLLpuMp3HzAOQZ5A+i69AKDhA1YLQTaMAqO3vzyYNVAYVRMPtSYD4w=="], + "@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.15.43", "", { "os": "win32", "cpu": "x64" }, "sha512-h8MLDHZcfIukwQWj03rIJZx1I0E81AYj2X7J/nGErG4nz+QAv6G1Z+peotvinL3lqpbo32tLYSMFo32/ySzxKg=="], "@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="], - "@swc/html": ["@swc/html@1.15.40", "", { "dependencies": { "@swc/counter": "^0.1.3" }, "optionalDependencies": { "@swc/html-darwin-arm64": "1.15.40", "@swc/html-darwin-x64": "1.15.40", "@swc/html-linux-arm-gnueabihf": "1.15.40", "@swc/html-linux-arm64-gnu": "1.15.40", "@swc/html-linux-arm64-musl": "1.15.40", "@swc/html-linux-ppc64-gnu": "1.15.40", "@swc/html-linux-s390x-gnu": "1.15.40", "@swc/html-linux-x64-gnu": "1.15.40", "@swc/html-linux-x64-musl": "1.15.40", "@swc/html-win32-arm64-msvc": "1.15.40", "@swc/html-win32-ia32-msvc": "1.15.40", "@swc/html-win32-x64-msvc": "1.15.40" } }, "sha512-YVWNrh3wZWH2oXz7uL+s1WZbFIsnAp9cCl/b+C7ufDJqhsOvbr0un9IOYZ/CxRIRdnHGfhVD199n8baD/kRQew=="], + "@swc/html": ["@swc/html@1.15.43", "", { "dependencies": { "@swc/counter": "^0.1.3" }, "optionalDependencies": { "@swc/html-darwin-arm64": "1.15.43", "@swc/html-darwin-x64": "1.15.43", "@swc/html-linux-arm-gnueabihf": "1.15.43", "@swc/html-linux-arm64-gnu": "1.15.43", "@swc/html-linux-arm64-musl": "1.15.43", "@swc/html-linux-ppc64-gnu": "1.15.43", "@swc/html-linux-s390x-gnu": "1.15.43", "@swc/html-linux-x64-gnu": "1.15.43", "@swc/html-linux-x64-musl": "1.15.43", "@swc/html-win32-arm64-msvc": "1.15.43", "@swc/html-win32-ia32-msvc": "1.15.43", "@swc/html-win32-x64-msvc": "1.15.43" } }, "sha512-SKbkbdGi9SDO9cTdV+6H0/AYifnb2nDOlz5BlWxlWMXACV3kmX6WwZDo0bBdyGlO/G4jCVWdR5r84qfotU2now=="], - "@swc/html-darwin-arm64": ["@swc/html-darwin-arm64@1.15.40", "", { "os": "darwin", "cpu": "arm64" }, "sha512-A9oxSh60pMEsX/4VBpn3e6s8DfGWsHzAeG+Vd9mXgkTqt/ouX12owP6RyqfqK8v0WuEsBKkGY0+MbwLvlOG8NQ=="], + "@swc/html-darwin-arm64": ["@swc/html-darwin-arm64@1.15.43", "", { "os": "darwin", "cpu": "arm64" }, "sha512-+PFbHbeeN+zB0zfvR1V1NmvPriuWPI+sijQXpI+wq/nLIujxvtENWjOKVHgouC9TIN/uKmL2zu9HAq6L6YxnPA=="], - "@swc/html-darwin-x64": ["@swc/html-darwin-x64@1.15.40", "", { "os": "darwin", "cpu": "x64" }, "sha512-GZ5KxJ/1pqnpOAjaa9hnkSk3M1LHjc5ci5GCt5/llAgDKFLDHzydIflHXTOIaS4nRCtfVMmD0joKX+ZWruSkJw=="], + "@swc/html-darwin-x64": ["@swc/html-darwin-x64@1.15.43", "", { "os": "darwin", "cpu": "x64" }, "sha512-LQJ2U8Oxcx4T1rRF25y4h+/p05nn58FugTe/uGxC5OT3K83c2MftcSZLYaahOu4GVHRZeS1NI94CkSvQV++TVw=="], - "@swc/html-linux-arm-gnueabihf": ["@swc/html-linux-arm-gnueabihf@1.15.40", "", { "os": "linux", "cpu": "arm" }, "sha512-Bbwtl0G6vYcWlDQmjeuyFx8YIvqi1StlQWjRNB1ZtvrRNL3JadFARhijBONiUIyTK9MgUbWtTV3QU69qzjNENQ=="], + "@swc/html-linux-arm-gnueabihf": ["@swc/html-linux-arm-gnueabihf@1.15.43", "", { "os": "linux", "cpu": "arm" }, "sha512-DKIen6DuIRO7Xc5gAbgBT5QyRHJGEGXreIdM1VBosYWTGnnrQ//Hwd7bLD6UbT8X8eU1vqvpXwQ1E24QRqRaBQ=="], - "@swc/html-linux-arm64-gnu": ["@swc/html-linux-arm64-gnu@1.15.40", "", { "os": "linux", "cpu": "arm64" }, "sha512-buu4fGyCIhkwmCetI4CfWOPn7cphJHwQ9ksK685hlL0R0PUaDCNdIxo+JAz0mK2JgPhoGqnKsVEmV6gLIL5GBw=="], + "@swc/html-linux-arm64-gnu": ["@swc/html-linux-arm64-gnu@1.15.43", "", { "os": "linux", "cpu": "arm64" }, "sha512-0AuHiyfcE86CZ/CajFIszLzZVzbM2wn5p01oet8Q9RikflCGwyH79Nv9TrAKD1Cx7juUrONzDk+f2b/x73wLTg=="], - "@swc/html-linux-arm64-musl": ["@swc/html-linux-arm64-musl@1.15.40", "", { "os": "linux", "cpu": "arm64" }, "sha512-BwWtpAGEHIP1QwKExWqBIjGpAHMahjT26ORZv3+TbwW4IuEl2QP5FrcYc30O8abCG5J1lWRl1vXKYDpzbIDz1A=="], + "@swc/html-linux-arm64-musl": ["@swc/html-linux-arm64-musl@1.15.43", "", { "os": "linux", "cpu": "arm64" }, "sha512-TweIdl/g9ugkoiYvcL/qbu+gbglDY3TqNxfXH84WXc4rSqEP20owVlxLya2NjVct8LIP2wDrtutpOwAXWC+Eew=="], - "@swc/html-linux-ppc64-gnu": ["@swc/html-linux-ppc64-gnu@1.15.40", "", { "os": "linux", "cpu": "ppc64" }, "sha512-wLtHgwmZu5K7PvuTROklgWJO6D+KwywpYR1geI8dSWX/AYnx/KCGVK9ncPzWVfnLXWhXbkgua3Ujdrfoxf+qSw=="], + "@swc/html-linux-ppc64-gnu": ["@swc/html-linux-ppc64-gnu@1.15.43", "", { "os": "linux", "cpu": "ppc64" }, "sha512-4oue1pB38/W6mbudp+w0q1jbwxuwdbdbaOj85ay0pisCs213WkgP+MPN8Zqa5VVPjQnVk2CTY9kmEc74XQI/sA=="], - "@swc/html-linux-s390x-gnu": ["@swc/html-linux-s390x-gnu@1.15.40", "", { "os": "linux", "cpu": "s390x" }, "sha512-WbWzQBCmvECZG6ep9D5SMMZTFCbFmRSCc7lPD3YhhaK16BNqr1AybvybZYOgxX9Ot5tq7gTWCgjFZXLDOn6Y/g=="], + "@swc/html-linux-s390x-gnu": ["@swc/html-linux-s390x-gnu@1.15.43", "", { "os": "linux", "cpu": "s390x" }, "sha512-/tceMNvAxK70SKUZtcn3X+K0vcElMGk3i8Sz0CmPdtooso8MZ7WfAvVP1qi3TWgh1rpQ3cC+Al3433AHlET6+w=="], - "@swc/html-linux-x64-gnu": ["@swc/html-linux-x64-gnu@1.15.40", "", { "os": "linux", "cpu": "x64" }, "sha512-i+/JqL5j9mTGMcVshWLMzciqyE3Np+gN5+UqMyO1D5zSmniJD5MHRlGXCtLaWx/MIKX0Q4Xkre33o56fmxp+uA=="], + "@swc/html-linux-x64-gnu": ["@swc/html-linux-x64-gnu@1.15.43", "", { "os": "linux", "cpu": "x64" }, "sha512-YE7ltlTt5ZFl59GsoHTDrIHnCBY8EDBio66CVj4bqkElFXbE/28xmpVE5ksdGoI5c5aQ/8byUCfHxqzCzQQSVg=="], - "@swc/html-linux-x64-musl": ["@swc/html-linux-x64-musl@1.15.40", "", { "os": "linux", "cpu": "x64" }, "sha512-mroZnb5omFc4iE01UsJgta+Nm0sIFWpGfTPk+BHfH165MZAM4wNndeczX5RpGZbQSF1nNpCoNgqJyT7l3hITMg=="], + "@swc/html-linux-x64-musl": ["@swc/html-linux-x64-musl@1.15.43", "", { "os": "linux", "cpu": "x64" }, "sha512-nS20HmbOk+dEEzdosJqqxAeyjMIiS5yrCAti8LUf0+dgr4eRmjkH4MlkjfPjf49aayR8o+eMJ1jsDZ7whx4zog=="], - "@swc/html-win32-arm64-msvc": ["@swc/html-win32-arm64-msvc@1.15.40", "", { "os": "win32", "cpu": "arm64" }, "sha512-qg1LXRrsY9FXy0S7vouSF0kkn1vdkTD//FW66iuezH7HMm5/DJGwEj7oc1zTKPWU0HnwOINOUbl6rUHrRuXHAQ=="], + "@swc/html-win32-arm64-msvc": ["@swc/html-win32-arm64-msvc@1.15.43", "", { "os": "win32", "cpu": "arm64" }, "sha512-Yz7aQQhXT/Yc6QcuMDQDZP9jqf2phkVyU+qSu8ZRWEcJgIorrPL6q7YLqMk+MB5PpZyu5XJEODvc1/UVDE1Kyg=="], - "@swc/html-win32-ia32-msvc": ["@swc/html-win32-ia32-msvc@1.15.40", "", { "os": "win32", "cpu": "ia32" }, "sha512-5NwMmgt9A70LQzcYOl4OiIJk5AhsGP69fNRfkmXXop+iIcAGUmYy+d6F2/Gc0u05j9G0R04NPWcHBQrHQhr0Gw=="], + "@swc/html-win32-ia32-msvc": ["@swc/html-win32-ia32-msvc@1.15.43", "", { "os": "win32", "cpu": "ia32" }, "sha512-muUgfsSQRZk6YBRuhaGKSLvXy0bV9BW6/mHLI0N/06btWuf0hekoHhIzR7dUmS98NXKCA7Hv+buBPE/0vXUwyA=="], - "@swc/html-win32-x64-msvc": ["@swc/html-win32-x64-msvc@1.15.40", "", { "os": "win32", "cpu": "x64" }, "sha512-WUNZqCZ+8WY1pr8t3X3fq4gI5+FL/tCCzlrJ2lLn0s+TyA2SLm/RPfXxQIr94EnjJWcIV21G//6/uXeBN8cPzw=="], + "@swc/html-win32-x64-msvc": ["@swc/html-win32-x64-msvc@1.15.43", "", { "os": "win32", "cpu": "x64" }, "sha512-tuLDy4MxPXsLi6jW+ozCdFWO61AoMMnlhePWJxMafefC2Ojm+iILxP2zI2Hgfu6F16y1q7ITdXdpEuqptu5fHw=="], - "@swc/types": ["@swc/types@0.1.26", "", { "dependencies": { "@swc/counter": "^0.1.3" } }, "sha512-lyMwd7WGgG79RS7EERZV3T8wMdmPq3xwyg+1nmAM64kIhx5yl+juO2PYIHb7vTiPgPCj8LYjsNV2T5wiQHUEaw=="], + "@swc/types": ["@swc/types@0.1.27", "", { "dependencies": { "@swc/counter": "^0.1.3" } }, "sha512-K6h3iUlqeM946U4sXFYeahefR1YBbXJvko+hv8WS8/0BNJ4OHiHRywMnQUJCqkR7Y9+hqQ1TvEpiKqUhz7NEFg=="], "@szmarczak/http-timer": ["@szmarczak/http-timer@5.0.1", "", { "dependencies": { "defer-to-connect": "^2.0.1" } }, "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw=="], "@tanstack/history": ["@tanstack/history@1.162.0", "", {}, "sha512-79pf/RkhteYZTRgcR4F9kbk84P2N8rugQJswxfIqovlbRiT3yI7eBE+5QorIrZaOKktsgzRlXh1l/du/xpl4iA=="], - "@tanstack/react-router": ["@tanstack/react-router@1.170.15", "", { "dependencies": { "@tanstack/history": "1.162.0", "@tanstack/react-store": "^0.9.3", "@tanstack/router-core": "1.171.13", "isbot": "^5.1.22" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-GawYz7HEjj8rTUUDoT/SemDEVm63pZUO+2mOcXHY9Jl3EwMS5gFBnPu/2UvcrwRm1jN1k79fokc0d4aFmrLatg=="], + "@tanstack/react-router": ["@tanstack/react-router@1.170.18", "", { "dependencies": { "@tanstack/history": "1.162.0", "@tanstack/react-store": "^0.9.3", "@tanstack/router-core": "1.171.15", "isbot": "^5.1.22" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-wpbGYZEp/fmz1q4bn7BD8VZ+/VZ7GBqSJv5V969pU+chP8y7dquWDmKTFMohvUegb9lg12m1uPVvD6kB2wORvQ=="], "@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.167.0", "", { "dependencies": { "@tanstack/router-devtools-core": "1.168.0" }, "peerDependencies": { "@tanstack/react-router": "^1.170.0", "@tanstack/router-core": "^1.170.0", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" }, "optionalPeers": ["@tanstack/router-core"] }, "sha512-nGw095EG7IHx0h5NtlEmzf6vcCTaFNPWdTSuDKazajhN0ct/v/TkekJ9J6KYUCeV1a8/2ZmToc58M+0rrOyn7w=="], "@tanstack/react-store": ["@tanstack/react-store@0.9.3", "", { "dependencies": { "@tanstack/store": "0.9.3", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg=="], - "@tanstack/router-core": ["@tanstack/router-core@1.171.13", "", { "dependencies": { "@tanstack/history": "1.162.0", "cookie-es": "^3.0.0", "seroval": "^1.5.4", "seroval-plugins": "^1.5.4" } }, "sha512-+NOwEj1kO/6IGmpHRIZHasYxYWpyBQGNIZAST9aNrk9Q3YlU9SgqVnl1pbLa9qAKfeNdXQIRve0RQb/0kyDeDA=="], + "@tanstack/router-core": ["@tanstack/router-core@1.171.15", "", { "dependencies": { "@tanstack/history": "1.162.0", "cookie-es": "^3.0.0", "seroval": "^1.5.4", "seroval-plugins": "^1.5.4" } }, "sha512-IILCDcLaItMZQ2jEmCABHY1Nhjjn5XUvwpQp3e4Nmu+vfg0BgYFuu/QASz2SwE2ZNbVMrvt8X/wxa+Gg5aErxA=="], "@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.168.0", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16" }, "peerDependencies": { "@tanstack/router-core": "^1.170.0", "csstype": "^3.0.10" }, "optionalPeers": ["csstype"] }, "sha512-wQoQhlBK7nlZgqzaqdYXKWNTpdHdsaREdaPhFZVH0/Ador+F+eM3/NF2i3f2LPeS0GgKraZUQXe1Q/1+KHyEYg=="], - "@tanstack/router-generator": ["@tanstack/router-generator@1.167.17", "", { "dependencies": { "@babel/types": "^7.28.5", "@tanstack/router-core": "1.171.13", "@tanstack/router-utils": "1.162.2", "@tanstack/virtual-file-routes": "1.162.0", "jiti": "^2.7.0", "magic-string": "^0.30.21", "prettier": "^3.5.0", "zod": "^4.4.3" } }, "sha512-xtB9tB2Ws0tWR6Pi7nc3Qk9IYgoh1mQCKWjHqIl9tf6BNUpKoqniJoPAQ4+LGrK8FeZYU0o0p/qlZEyj9FAulA=="], + "@tanstack/router-generator": ["@tanstack/router-generator@1.167.20", "", { "dependencies": { "@babel/types": "^7.28.5", "@tanstack/router-core": "1.171.15", "@tanstack/router-utils": "1.162.2", "@tanstack/virtual-file-routes": "1.162.0", "jiti": "^2.7.0", "magic-string": "^0.30.21", "prettier": "^3.5.0", "zod": "^4.4.3" } }, "sha512-SCLPDE1ne+l1pf30T2mMGKwG3Q5rffGAs/lDqEXmoj0PwYeMwpChs/pLzLN+KH/oDGzSmXKmiK9aGKZy3aSwXA=="], - "@tanstack/router-plugin": ["@tanstack/router-plugin@1.168.18", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "@tanstack/router-core": "1.171.13", "@tanstack/router-generator": "1.167.17", "@tanstack/router-utils": "1.162.2", "chokidar": "^5.0.0", "unplugin": "^3.0.0", "zod": "^4.4.3" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2 || ^2.0.0", "@tanstack/react-router": "^1.170.15", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0 || >=8.0.0", "vite-plugin-solid": "^2.11.10 || ^3.0.0-0", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-MofS28/axfnfnhOD2RSgJEaU882aX5RsAzhGz5Vc4XhAmvCjy919u9JrNs4QsTWFbTD1P7IJ8WFlFVsrg0pStg=="], + "@tanstack/router-plugin": ["@tanstack/router-plugin@1.168.21", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "@tanstack/router-core": "1.171.15", "@tanstack/router-generator": "1.167.20", "@tanstack/router-utils": "1.162.2", "chokidar": "^5.0.0", "unplugin": "^3.0.0", "zod": "^4.4.3" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2 || ^2.0.0", "@tanstack/react-router": "^1.170.18", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0 || >=8.0.0", "vite-plugin-solid": "^2.11.10 || ^3.0.0-0", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-76erhrm1jGJ4NvwQZdEZjgUfwaw9jJJjv+w45qx5TNZwemW0zeskYSNSDhxmfnW7ZnMTY7bFCy3zUUBoR6cF7Q=="], "@tanstack/router-utils": ["@tanstack/router-utils@1.162.2", "", { "dependencies": { "@babel/generator": "^7.28.5", "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "ansis": "^4.1.0", "babel-dead-code-elimination": "^1.0.12", "diff": "^8.0.2", "pathe": "^2.0.3", "tinyglobby": "^0.2.15" } }, "sha512-hTWqJtqIFFdvuCl8WXNyrodp2L9zo2G37xKRrcVmVRWpAB2h+U1LuRAfS4tsFTiWOIoE/B+WDVFB8JpoEdw6jQ=="], @@ -1102,19 +1103,19 @@ "@testing-library/react": ["@testing-library/react@16.3.2", "", { "dependencies": { "@babel/runtime": "^7.12.5" }, "peerDependencies": { "@testing-library/dom": "^10.0.0", "@types/react": "^18.0.0 || ^19.0.0", "@types/react-dom": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g=="], - "@turbo/darwin-64": ["@turbo/darwin-64@2.9.16", "", { "os": "darwin", "cpu": "x64" }, "sha512-jLjApWTSNd7JZ5JaLYfelW1ytnGQOvB7ivl+2RD1xQvJTbi8I9gBjzcga7tDZVPyaxpl10YTfJt3BrYXR18KDw=="], + "@turbo/darwin-64": ["@turbo/darwin-64@2.10.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-ENvPwy3x5yS7MwNYHeWjqOBXkwIMp39Pd+/zXC6PoiNzF8EIvvLZOZZ+ny6L9x4WgS5vxUii2LM5gM+zjPdnWw=="], - "@turbo/darwin-arm64": ["@turbo/darwin-arm64@2.9.16", "", { "os": "darwin", "cpu": "arm64" }, "sha512-YPgrn+5HIGzrx0O2a631SV4MBQUe4W/DafMFUuBVgaU32PW9/OTT0ehviF0QSxTXuRJlHvW2eUTemddF5/spmw=="], + "@turbo/darwin-arm64": ["@turbo/darwin-arm64@2.10.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-rqROo9zsF/P9RqsdtbLD1nFJicjSrYyvQ9kNJC38AbxA3pAs6VAlATvtvOFx7bqOv6vicf20SP9kF33avJjy2w=="], - "@turbo/linux-64": ["@turbo/linux-64@2.9.16", "", { "os": "linux", "cpu": "x64" }, "sha512-vAEf1H6l26lTpl9FJ/peQo1NUB8RC0sbEJJz5mPcUhHA2bPDup2x3CZPgo/bH8S4cUcBLm4FN3UHd5iUO2RAew=="], + "@turbo/linux-64": ["@turbo/linux-64@2.10.5", "", { "os": "linux", "cpu": "x64" }, "sha512-RoSSiNFUxi27zLJuM9F6GyWWjHgLch9t6nwD6K0FkXRirZkTLlzIj6IhFnK8H9++nefLtdFqylE4vGjZAv6AAA=="], - "@turbo/linux-arm64": ["@turbo/linux-arm64@2.9.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-xDBLR2PZg4BrQOchfG6svgpv5FCNJ2TOtT2psLdEJcdKo1BH+pnPs9Xj6pvUjgfkHbuvBOfeE4R6tvxMoQKDHQ=="], + "@turbo/linux-arm64": ["@turbo/linux-arm64@2.10.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-4ZComcpzmHGmVynQqvvi+iZOSq/tBvY1SltXB8g4NZRsrA01W8E+yRL8RNM+PLoyWsrCnJa8xa+DkWkv+xg4iQ=="], - "@turbo/windows-64": ["@turbo/windows-64@2.9.16", "", { "os": "win32", "cpu": "x64" }, "sha512-NBAJnaUiGdgkSzQwUIdOvkCkcpTSu58G/sBGa0mvBtzfvFOOgrQwepKOOQ8cp6sWM6OcKDNFj2p1dsZA1OWjPg=="], + "@turbo/windows-64": ["@turbo/windows-64@2.10.5", "", { "os": "win32", "cpu": "x64" }, "sha512-eL2Iyj4DbMINq1Sr1w0iAi6nAiZOF16KSlRGwCJpVh+IWZeY33MAsLHVOBMj1xoFtncVJXclCVpTPL2nBoYkFg=="], - "@turbo/windows-arm64": ["@turbo/windows-arm64@2.9.16", "", { "os": "win32", "cpu": "arm64" }, "sha512-Y7SJppD0Z8wjO3Ec0ZGd9KQ4Yv0BMnA8CIowj5Vp+OEVsosXDG2weK6/t1RRLfJmc2Ozrnd6y4DOgQys+mn3WQ=="], + "@turbo/windows-arm64": ["@turbo/windows-arm64@2.10.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-sog+wP+8YSJrdWZ/rUJg8xghVTrwoG+BrSlDQpnK5fzSgJHn1INRWXbVWRH0d3vX8dBI01E3yxXRre9Dn+OXQA=="], - "@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], + "@tybys/wasm-util": ["@tybys/wasm-util@0.10.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg=="], "@typed/id": ["@typed/id@0.17.2", "", { "peerDependencies": { "effect": "^3.14.7" } }, "sha512-z/Z14/moeu9x45IpkGaRwuvb+CQ3s3UCc/agcpZibTz1yPb3RgSDXx4rOHIuyb6hG6oNzqe9yY4GbbMq3Hb5Ug=="], @@ -1142,11 +1143,11 @@ "@types/express": ["@types/express@4.17.25", "", { "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", "@types/serve-static": "^1" } }, "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw=="], - "@types/express-serve-static-core": ["@types/express-serve-static-core@4.19.8", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA=="], + "@types/express-serve-static-core": ["@types/express-serve-static-core@4.19.9", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-QP2ESEe/ImWY0HDwNAnK9PvEffUyhLTnWkk7KXzHfyeWAnlrDe1fN77bXl6ia8KT3wPlmA7t9/VPRpnf4Ex9sg=="], "@types/gtag.js": ["@types/gtag.js@0.0.20", "", {}, "sha512-wwAbk3SA2QeU67unN7zPxjEHmPmlXwZXZvQEpbEUQuMCRGgKyE1m6XDuTUA9b6pCGb/GqJmdfMOY5LuDjJSbbg=="], - "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], + "@types/hast": ["@types/hast@3.0.5", "", { "dependencies": { "@types/unist": "*" } }, "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g=="], "@types/history": ["@types/history@4.7.11", "", {}, "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA=="], @@ -1174,7 +1175,7 @@ "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], - "@types/node": ["@types/node@25.9.2", "", { "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } }, "sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw=="], + "@types/node": ["@types/node@26.1.1", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw=="], "@types/prismjs": ["@types/prismjs@1.26.6", "", {}, "sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw=="], @@ -1196,7 +1197,7 @@ "@types/sax": ["@types/sax@1.2.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A=="], - "@types/send": ["@types/send@1.2.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ=="], + "@types/send": ["@types/send@0.17.6", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og=="], "@types/serve-index": ["@types/serve-index@1.9.4", "", { "dependencies": { "@types/express": "*" } }, "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug=="], @@ -1212,23 +1213,23 @@ "@types/yargs-parser": ["@types/yargs-parser@21.0.3", "", {}, "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ=="], - "@ungap/structured-clone": ["@ungap/structured-clone@1.3.1", "", {}, "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ=="], + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.3", "", {}, "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg=="], - "@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.2", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.0" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler"] }, "sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg=="], + "@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.3", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.1" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler"] }, "sha512-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg=="], - "@vitest/expect": ["@vitest/expect@3.2.6", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.6", "@vitest/utils": "3.2.6", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-1+7q9BtaKzEmO+fmNT3kYvoNn5Y71XWAx2Q5HRim4tTVRQVRv4uJFAQ5FbK0OPUeNP/WmVCpxYxoJdvuHVjzBQ=="], + "@vitest/expect": ["@vitest/expect@3.2.7", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.7", "@vitest/utils": "3.2.7", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-E8eBXaKibuvH2pSZErOjdVb5vF4PbKYcrnluBTYxEk1l/VhhwZg1kZQsdtjq+CsF5CFydf2Rdkz7jDHKSisi3w=="], - "@vitest/mocker": ["@vitest/mocker@3.2.6", "", { "dependencies": { "@vitest/spy": "3.2.6", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-EZOrpDbkKotFAP7wPAQV1UIyoGOk4oX7ynWhBhLB7v+meMHbQhU16oPpIYGTTe4oFlhpryGpgpcZP/sin3hYuw=="], + "@vitest/mocker": ["@vitest/mocker@3.2.7", "", { "dependencies": { "@vitest/spy": "3.2.7", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA=="], - "@vitest/pretty-format": ["@vitest/pretty-format@3.2.6", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-lb7XXXzmm2h2ASzFnRvQpDo6onT1NmMJA3tkGTWiBFtRJ9lxGY3d3mm/Apt36gej2bkkOVLL/yTOtufDaFa/jA=="], + "@vitest/pretty-format": ["@vitest/pretty-format@3.2.7", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA=="], - "@vitest/runner": ["@vitest/runner@3.2.6", "", { "dependencies": { "@vitest/utils": "3.2.6", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-HYcoSj1w5tcgUnzoF0HcyaAQjpA1gj9ftUJ7iSJSuipc02jW9gKkigwZbjFldAfYHA1fa8UZVRftdMY5msWM9Q=="], + "@vitest/runner": ["@vitest/runner@3.2.7", "", { "dependencies": { "@vitest/utils": "3.2.7", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-sB9y4ovltoQP+WaUPwmSxO9WIg9Ig694Di5PalVPsYHklAdE027mehpWF2SQSVq+k6sFgaivbTjTJwZLSHbedA=="], - "@vitest/snapshot": ["@vitest/snapshot@3.2.6", "", { "dependencies": { "@vitest/pretty-format": "3.2.6", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-H+ZjNTWGpObenh0YnlBctAPnJSI20P81PL8BPzWpx54YXLLTm8hEsWawtcYLMrwvpK48hGxLLbCS+1KRXhsKhw=="], + "@vitest/snapshot": ["@vitest/snapshot@3.2.7", "", { "dependencies": { "@vitest/pretty-format": "3.2.7", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-7C+MwShwtBSI5Buwoyg3s/iY1eHL9PKAf+O1wVh/TdnjXUtkoL/9YQtre90i4MtNXM6edP1wJ2zOBpfCyhIS7g=="], - "@vitest/spy": ["@vitest/spy@3.2.6", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-oq6BbH68WzcWmwtBrU9nqLeaXTR4XwJF7FSLkKEZo4i6eoXcrxjcwSuTvWBIRUTC6VC72nXYunzqgZA+IKdtxg=="], + "@vitest/spy": ["@vitest/spy@3.2.7", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-Q2eQGI6d2L/hBtZ0qNuKcAGid68XK6cv1xsoaIma6PaJhHPoqcEJhYpXZ/5myCMqkNgtP6UKuBhbc0nHKnrkuQ=="], - "@vitest/utils": ["@vitest/utils@3.2.6", "", { "dependencies": { "@vitest/pretty-format": "3.2.6", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-lI23nIs4bnT3T8NIoh+vFaz5s2/DdP0Jgt2jxwgWljvwn82cLJtyi/If+fjFyoLMGIOz0U/fKvWE0d4jsNQEfg=="], + "@vitest/utils": ["@vitest/utils@3.2.7", "", { "dependencies": { "@vitest/pretty-format": "3.2.7", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-x6BDOd7dyo3PFLY3I9/HJ25X/6OurhGXk2/B9gOZNPF7XDVjeBK4k01lQE5uvDpbuheErh91qYuE1E2OEjK3Rw=="], "@webassemblyjs/ast": ["@webassemblyjs/ast@1.14.1", "", { "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ=="], @@ -1266,7 +1267,7 @@ "accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="], - "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], + "acorn": ["acorn@8.17.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg=="], "acorn-import-phases": ["acorn-import-phases@1.0.4", "", { "peerDependencies": { "acorn": "^8.14.0" } }, "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ=="], @@ -1286,9 +1287,9 @@ "ajv-keywords": ["ajv-keywords@5.1.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3" }, "peerDependencies": { "ajv": "^8.8.2" } }, "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw=="], - "algoliasearch": ["algoliasearch@5.53.0", "", { "dependencies": { "@algolia/abtesting": "1.19.0", "@algolia/client-abtesting": "5.53.0", "@algolia/client-analytics": "5.53.0", "@algolia/client-common": "5.53.0", "@algolia/client-insights": "5.53.0", "@algolia/client-personalization": "5.53.0", "@algolia/client-query-suggestions": "5.53.0", "@algolia/client-search": "5.53.0", "@algolia/ingestion": "1.53.0", "@algolia/monitoring": "1.53.0", "@algolia/recommend": "5.53.0", "@algolia/requester-browser-xhr": "5.53.0", "@algolia/requester-fetch": "5.53.0", "@algolia/requester-node-http": "5.53.0" } }, "sha512-OGW1q6b91CRSSeiOnM8LxuR5NYJ2esvw66jUZ4IIvdv+ItNkx3pwLuyR+jaCdbGee4ov5WgUnyPryyh11xvByQ=="], + "algoliasearch": ["algoliasearch@5.56.0", "", { "dependencies": { "@algolia/abtesting": "1.22.0", "@algolia/client-abtesting": "5.56.0", "@algolia/client-analytics": "5.56.0", "@algolia/client-common": "5.56.0", "@algolia/client-insights": "5.56.0", "@algolia/client-personalization": "5.56.0", "@algolia/client-query-suggestions": "5.56.0", "@algolia/client-search": "5.56.0", "@algolia/ingestion": "1.56.0", "@algolia/monitoring": "1.56.0", "@algolia/recommend": "5.56.0", "@algolia/requester-browser-xhr": "5.56.0", "@algolia/requester-fetch": "5.56.0", "@algolia/requester-node-http": "5.56.0" } }, "sha512-PrqppUmhT4ENdas2pH9caE7efUcxy6EcSFhWzosiVuQBzu2tQ5yLTI6jwomT/1cuBnivzGfxiJCqDNN9FRRh+Q=="], - "algoliasearch-helper": ["algoliasearch-helper@3.29.1", "", { "dependencies": { "@algolia/events": "^4.0.1" }, "peerDependencies": { "algoliasearch": ">= 3.1 < 6" } }, "sha512-6ck2YFudF2Pje7szQoPBiRFTGfd+1I+0I/WfLPGn0bj1kvrFoOQmNyedNiDxTk3/r4IfSLDYk+RA4G7u8H6+yA=="], + "algoliasearch-helper": ["algoliasearch-helper@3.29.2", "", { "dependencies": { "@algolia/events": "^4.0.1" }, "peerDependencies": { "algoliasearch": ">= 3.1 < 6" } }, "sha512-SaV+rZM3drExb0punEYYjT+sNcH74YFwN8ocjya7IDOyQvKWeQpEaSMVG3+IGTVos+feuatj7ljQ4BXlXdUp3w=="], "ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="], @@ -1320,7 +1321,7 @@ "astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="], - "autoprefixer": ["autoprefixer@10.5.0", "", { "dependencies": { "browserslist": "^4.28.2", "caniuse-lite": "^1.0.30001787", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong=="], + "autoprefixer": ["autoprefixer@10.5.4", "", { "dependencies": { "browserslist": "^4.28.6", "caniuse-lite": "^1.0.30001806", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-MaU0U/za7N3r6brxD4YB/l4NSrFzLPlANv6wEuQVaIPlD3L4W9rFcQPbL/EilY9BHhHvhfcz3gInDLrEtWT4EA=="], "babel-dead-code-elimination": ["babel-dead-code-elimination@1.0.12", "", { "dependencies": { "@babel/core": "^7.23.7", "@babel/parser": "^7.23.6", "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6" } }, "sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig=="], @@ -1338,7 +1339,7 @@ "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - "baseline-browser-mapping": ["baseline-browser-mapping@2.10.34", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-IMDedajPifLnHNY0X9n8hKxRTQ6/eTHwr5bDo04WnuqxyKw6LYtQywCuuqPZwhl3aBXMvQpJov42GLCwRRdQzw=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.10.43", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ=="], "batch": ["batch@0.6.1", "", {}, "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw=="], @@ -1346,19 +1347,19 @@ "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], - "body-parser": ["body-parser@1.20.5", "", { "dependencies": { "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "~1.2.0", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", "qs": "~6.15.1", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" } }, "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA=="], + "body-parser": ["body-parser@1.20.6", "", { "dependencies": { "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "~1.2.0", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", "qs": "~6.15.1", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" } }, "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g=="], - "bonjour-service": ["bonjour-service@1.4.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "multicast-dns": "^7.2.5" } }, "sha512-fGQtj1qdR9vIKjFiWPQd52qIqwjaYqhcI40JEiDuvlZ86E7ZBPBwY9fPgHy9r2rYGIjiRfctNPYz6OQU73ww2w=="], + "bonjour-service": ["bonjour-service@1.4.3", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "multicast-dns": "^7.2.5" } }, "sha512-2Kd5UYlFUVgAKMTyuBLl6w49wqfOnbxHqmuH0oCl/n7TfAikR0zoowNOP5BU4dfXmm+Vr9JyEN370auSMx+CNg=="], "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], "boxen": ["boxen@6.2.1", "", { "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^6.2.0", "chalk": "^4.1.2", "cli-boxes": "^3.0.0", "string-width": "^5.0.1", "type-fest": "^2.5.0", "widest-line": "^4.0.1", "wrap-ansi": "^8.0.1" } }, "sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw=="], - "brace-expansion": ["brace-expansion@1.1.15", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg=="], + "brace-expansion": ["brace-expansion@1.1.16", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], - "browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="], + "browserslist": ["browserslist@4.28.6", "", { "dependencies": { "baseline-browser-mapping": "^2.10.42", "caniuse-lite": "^1.0.30001803", "electron-to-chromium": "^1.5.389", "node-releases": "^2.0.51", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw=="], "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], @@ -1390,7 +1391,7 @@ "caniuse-api": ["caniuse-api@3.0.0", "", { "dependencies": { "browserslist": "^4.0.0", "caniuse-lite": "^1.0.0", "lodash.memoize": "^4.1.2", "lodash.uniq": "^4.5.0" } }, "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw=="], - "caniuse-lite": ["caniuse-lite@1.0.30001797", "", {}, "sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w=="], + "caniuse-lite": ["caniuse-lite@1.0.30001806", "", {}, "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw=="], "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], @@ -1508,7 +1509,7 @@ "css-select": ["css-select@4.3.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.0.1", "domhandler": "^4.3.1", "domutils": "^2.8.0", "nth-check": "^2.0.1" } }, "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ=="], - "css-tree": ["css-tree@2.3.1", "", { "dependencies": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" } }, "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw=="], + "css-tree": ["css-tree@2.2.1", "", { "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" } }, "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA=="], "css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="], @@ -1608,15 +1609,15 @@ "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], - "effect": ["effect@3.21.3", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-RqwU7WnJ6CqYhyjpOVJA5vh1Sgkn6eVECO6mnD0EjlbWcC2M3LJaPglXXr13Rdo/Y+B+wTEPzGRYFNL2xKxNeQ=="], + "effect": ["effect@3.22.0", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-jhYFe0zTlIRqYFrKTS+6luhmS/Tm0f+JLo0K9KUxvtFab1SUGEszQi2ehOP6QzAZvy831lDmTwwzvVDZSPNz3g=="], "effect-fc": ["effect-fc@workspace:packages/effect-fc"], "effect-fc-next": ["effect-fc-next@workspace:packages/effect-fc-next"], - "effect-lens": ["effect-lens@0.2.0", "", { "peerDependencies": { "effect": "^3.21.0" } }, "sha512-Cfyps811WQVSxnbxm6xqJldL08YXUU1jAzWtdKXSKWZfF6LAEqm1l8swnw4ltVky1TSfi2vvS+I/tFPqwObrMg=="], + "effect-lens": ["effect-lens@0.2.2", "", { "peerDependencies": { "effect": "^3.21.0" } }, "sha512-kt+aLq9djFFAH3sj+/YapcIzx/tqmkTO+ofU2HiEIm34zB/zx+0PvWLXqhK1EuCEjl9Ueififg711J1FvvzIqw=="], - "electron-to-chromium": ["electron-to-chromium@1.5.368", "", {}, "sha512-7RckJJK4uESJF9PxvfMWd3TGqIiieUTG4HxnKaKuIpGbcr+r2ZEB3g2gAhCP3Fqm42vJSzLfgab9eva/C4/XVw=="], + "electron-to-chromium": ["electron-to-chromium@1.5.393", "", {}, "sha512-kiDJdIUawuEIcp9XoICKp1iTYDEbgguIPq526N1Q7jIQDeQ3CqoMx71025PI/7E48Ddtw2HuWsVjY7afEgNxmg=="], "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], @@ -1628,7 +1629,7 @@ "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], - "enhanced-resolve": ["enhanced-resolve@5.23.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-yJN/BOOLxcOW2aQgeif9mSnaUB8KtvmMMp56oA1kx1CRfBKbhZm2pJ+NBY+3eOboHxix8lfjWpHE0Ei5U8RbSA=="], + "enhanced-resolve": ["enhanced-resolve@5.24.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-rpsZEGT1jFuve6QlpyRp9ckQ+kN61hvF9BzCPyMdaKTm8UJce96KBn3sorXOFXlzjPrs3Vc4T1NsSroZ3PxlFw=="], "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], @@ -1638,7 +1639,7 @@ "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], - "es-module-lexer": ["es-module-lexer@2.1.0", "", {}, "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ=="], + "es-module-lexer": ["es-module-lexer@2.3.1", "", {}, "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA=="], "es-object-atoms": ["es-object-atoms@1.1.2", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="], @@ -1646,7 +1647,7 @@ "esast-util-from-js": ["esast-util-from-js@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "acorn": "^8.0.0", "esast-util-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw=="], - "esbuild": ["esbuild@0.27.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.7", "@esbuild/android-arm": "0.27.7", "@esbuild/android-arm64": "0.27.7", "@esbuild/android-x64": "0.27.7", "@esbuild/darwin-arm64": "0.27.7", "@esbuild/darwin-x64": "0.27.7", "@esbuild/freebsd-arm64": "0.27.7", "@esbuild/freebsd-x64": "0.27.7", "@esbuild/linux-arm": "0.27.7", "@esbuild/linux-arm64": "0.27.7", "@esbuild/linux-ia32": "0.27.7", "@esbuild/linux-loong64": "0.27.7", "@esbuild/linux-mips64el": "0.27.7", "@esbuild/linux-ppc64": "0.27.7", "@esbuild/linux-riscv64": "0.27.7", "@esbuild/linux-s390x": "0.27.7", "@esbuild/linux-x64": "0.27.7", "@esbuild/netbsd-arm64": "0.27.7", "@esbuild/netbsd-x64": "0.27.7", "@esbuild/openbsd-arm64": "0.27.7", "@esbuild/openbsd-x64": "0.27.7", "@esbuild/openharmony-arm64": "0.27.7", "@esbuild/sunos-x64": "0.27.7", "@esbuild/win32-arm64": "0.27.7", "@esbuild/win32-ia32": "0.27.7", "@esbuild/win32-x64": "0.27.7" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w=="], + "esbuild": ["esbuild@0.28.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.1", "@esbuild/android-arm": "0.28.1", "@esbuild/android-arm64": "0.28.1", "@esbuild/android-x64": "0.28.1", "@esbuild/darwin-arm64": "0.28.1", "@esbuild/darwin-x64": "0.28.1", "@esbuild/freebsd-arm64": "0.28.1", "@esbuild/freebsd-x64": "0.28.1", "@esbuild/linux-arm": "0.28.1", "@esbuild/linux-arm64": "0.28.1", "@esbuild/linux-ia32": "0.28.1", "@esbuild/linux-loong64": "0.28.1", "@esbuild/linux-mips64el": "0.28.1", "@esbuild/linux-ppc64": "0.28.1", "@esbuild/linux-riscv64": "0.28.1", "@esbuild/linux-s390x": "0.28.1", "@esbuild/linux-x64": "0.28.1", "@esbuild/netbsd-arm64": "0.28.1", "@esbuild/netbsd-x64": "0.28.1", "@esbuild/openbsd-arm64": "0.28.1", "@esbuild/openbsd-x64": "0.28.1", "@esbuild/openharmony-arm64": "0.28.1", "@esbuild/sunos-x64": "0.28.1", "@esbuild/win32-arm64": "0.28.1", "@esbuild/win32-ia32": "0.28.1", "@esbuild/win32-x64": "0.28.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw=="], "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], @@ -1694,7 +1695,7 @@ "execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], - "expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="], + "expect-type": ["expect-type@1.4.0", "", {}, "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA=="], "express": ["express@4.22.2", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "~1.20.5", "content-disposition": "~0.5.4", "content-type": "~1.0.4", "cookie": "~0.7.1", "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "~1.3.1", "fresh": "~0.5.2", "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", "qs": "~6.15.1", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", "serve-static": "~1.16.2", "setprototypeof": "1.2.0", "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q=="], @@ -1710,7 +1711,7 @@ "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], - "fast-uri": ["fast-uri@3.1.2", "", {}, "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ=="], + "fast-uri": ["fast-uri@3.1.3", "", {}, "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg=="], "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], @@ -1748,7 +1749,7 @@ "fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="], - "fs-extra": ["fs-extra@11.3.5", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg=="], + "fs-extra": ["fs-extra@11.3.6", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-w8ZNZr2mKIc7qeNaQ9AVPT1+iFaI+Avd4xudVOvdDJ8VytREi1Ft5Ih7hd9jjehod8vAM5GMsfQ/TpPf4EyoEA=="], "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], @@ -1772,11 +1773,9 @@ "glob-to-regex.js": ["glob-to-regex.js@1.2.0", "", { "peerDependencies": { "tslib": "2" } }, "sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ=="], - "glob-to-regexp": ["glob-to-regexp@0.4.1", "", {}, "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="], - "global-dirs": ["global-dirs@3.0.1", "", { "dependencies": { "ini": "2.0.0" } }, "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA=="], - "globals": ["globals@17.6.0", "", {}, "sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA=="], + "globals": ["globals@17.7.0", "", {}, "sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg=="], "globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="], @@ -1854,7 +1853,7 @@ "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], - "http-proxy-middleware": ["http-proxy-middleware@2.0.9", "", { "dependencies": { "@types/http-proxy": "^1.17.8", "http-proxy": "^1.18.1", "is-glob": "^4.0.1", "is-plain-obj": "^3.0.0", "micromatch": "^4.0.2" }, "peerDependencies": { "@types/express": "^4.17.13" }, "optionalPeers": ["@types/express"] }, "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q=="], + "http-proxy-middleware": ["http-proxy-middleware@2.0.10", "", { "dependencies": { "@types/http-proxy": "^1.17.8", "http-proxy": "^1.18.1", "is-glob": "^4.0.1", "is-plain-obj": "^3.0.0", "micromatch": "^4.0.2" }, "peerDependencies": { "@types/express": "^4.17.13" }, "optionalPeers": ["@types/express"] }, "sha512-RKzRWNPxUZqbuk3BC5mGVJbBnWgr+diEnjJexIOytFbBzDy88Fbh/YvBr3DsNrl1jYAfjWfpATEv0NO35FDuPQ=="], "http2-wrapper": ["http2-wrapper@2.2.1", "", { "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.2.0" } }, "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ=="], @@ -1950,7 +1949,7 @@ "isarray": ["isarray@0.0.1", "", {}, "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="], - "isbot": ["isbot@5.1.41", "", {}, "sha512-9WFV/Vhh0FEj6CQ7MoHweEL9/vLKPjeoD2I2htbAjX7kbW7VJs3OCpWOVyd+JraNTWVU6/DRx2MZy2KaUNXHcg=="], + "isbot": ["isbot@5.2.1", "", {}, "sha512-dJ+LpKyClQZ7NG+j3OensC/mAZkGpukE9YUrgPYvAZj2doVL0edfDgywTUh5CXa0o+nW9a1V9e5+CJTX8+SxRw=="], "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], @@ -1962,11 +1961,11 @@ "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], - "joi": ["joi@17.13.3", "", { "dependencies": { "@hapi/hoek": "^9.3.0", "@hapi/topo": "^5.1.0", "@sideway/address": "^4.1.5", "@sideway/formula": "^3.0.1", "@sideway/pinpoint": "^2.0.0" } }, "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA=="], + "joi": ["joi@17.13.4", "", { "dependencies": { "@hapi/hoek": "^9.3.0", "@hapi/topo": "^5.1.0", "@sideway/address": "^4.1.5", "@sideway/formula": "^3.0.1", "@sideway/pinpoint": "^2.0.0" } }, "sha512-1RuuER6kmt8K8I3nIWvPZKi5RQCb568ZPyY4Pwjlua+yo+63ZTmIwxLZH0heBmiKN4uxjvCiarDrjaeH84xicQ=="], "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - "js-yaml": ["js-yaml@4.2.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw=="], + "js-yaml": ["js-yaml@4.3.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q=="], "jsdom": ["jsdom@26.1.0", "", { "dependencies": { "cssstyle": "^4.2.1", "data-urls": "^5.0.0", "decimal.js": "^10.5.0", "html-encoding-sniffer": "^4.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", "nwsapi": "^2.2.16", "parse5": "^7.2.1", "rrweb-cssom": "^0.8.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^5.1.1", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^7.0.0", "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.1.1", "ws": "^8.18.0", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.0.0" }, "optionalPeers": ["canvas"] }, "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg=="], @@ -2096,11 +2095,11 @@ "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="], - "mdn-data": ["mdn-data@2.0.30", "", {}, "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA=="], + "mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="], "media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="], - "memfs": ["memfs@4.57.6", "", { "dependencies": { "@jsonjoy.com/fs-core": "4.57.6", "@jsonjoy.com/fs-fsa": "4.57.6", "@jsonjoy.com/fs-node": "4.57.6", "@jsonjoy.com/fs-node-builtins": "4.57.6", "@jsonjoy.com/fs-node-to-fsa": "4.57.6", "@jsonjoy.com/fs-node-utils": "4.57.6", "@jsonjoy.com/fs-print": "4.57.6", "@jsonjoy.com/fs-snapshot": "4.57.6", "@jsonjoy.com/json-pack": "^1.11.0", "@jsonjoy.com/util": "^1.9.0", "glob-to-regex.js": "^1.0.1", "thingies": "^2.5.0", "tree-dump": "^1.0.3", "tslib": "^2.0.0" } }, "sha512-WQK+DGjKCnPdpSyJUXphz+COF2uEhhsxQ3VIWBSbzpbbXuch3h4FePMqXrXGdLjsTgo4JFzBFsP6AWd9pVazGw=="], + "memfs": ["memfs@4.64.0", "", { "dependencies": { "@jsonjoy.com/fs-core": "4.64.0", "@jsonjoy.com/fs-fsa": "4.64.0", "@jsonjoy.com/fs-node": "4.64.0", "@jsonjoy.com/fs-node-builtins": "4.64.0", "@jsonjoy.com/fs-node-to-fsa": "4.64.0", "@jsonjoy.com/fs-node-utils": "4.64.0", "@jsonjoy.com/fs-print": "4.64.0", "@jsonjoy.com/fs-snapshot": "4.64.0", "@jsonjoy.com/json-pack": "^1.11.0", "@jsonjoy.com/util": "^1.9.0", "glob-to-regex.js": "^1.0.1", "thingies": "^2.5.0", "tree-dump": "^1.0.3", "tslib": "^2.0.0" } }, "sha512-Kw72fgY7Wn+sD8KmtNWSafl1dz0UvAsE/PHs3YVfLiaZuA3HxNm9sRLqAu0ATiBGJvME1PxZXbBZPv5GycDeAw=="], "merge-descriptors": ["merge-descriptors@1.0.3", "", {}, "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ=="], @@ -2204,19 +2203,21 @@ "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + "minimizer-webpack-plugin": ["minimizer-webpack-plugin@5.6.1", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", "schema-utils": "^4.3.0", "terser": "^5.31.1" }, "peerDependencies": { "@minify-html/node": "*", "@swc/core": "*", "@swc/css": "*", "@swc/html": "*", "clean-css": "*", "cssnano": "*", "csso": "*", "esbuild": "*", "html-minifier-terser": "*", "lightningcss": "*", "postcss": "*", "uglify-js": "*", "webpack": "^5.1.0" }, "optionalPeers": ["@minify-html/node", "@swc/core", "@swc/css", "@swc/html", "clean-css", "cssnano", "csso", "esbuild", "html-minifier-terser", "lightningcss", "postcss", "uglify-js"] }, "sha512-DoeAZz8Q1C1znwsUzej1fdoi4jCf7/+Em27ouLqfK/+3m8G+D7yDhUwrc3CNhjSzGUN1kn7Iv4sWmjflQHenpw=="], + "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - "msgpackr": ["msgpackr@1.11.14", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-suPZQcjFtPGp0cksn70ICfLuxsO9F2/sRrbJzeNepojZ+OPwGzA0lNdLyU4SJUKAd5ZgvUWWPojzzdlVuOYcrQ=="], + "msgpackr": ["msgpackr@1.12.1", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-4EUH9tQHnMmEgzW/MdAP0KIfa1T9AF+htl0ffe2n5vb2EKn9y2co8ccpgWko6S52Jy1PQZKwRnx5/KkYjtd9MQ=="], "msgpackr-extract": ["msgpackr-extract@3.0.4", "", { "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, "optionalDependencies": { "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.4", "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.4", "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.4", "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.4", "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.4", "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4" }, "bin": { "download-msgpackr-prebuilds": "bin/download-prebuilds.js" } }, "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw=="], "multicast-dns": ["multicast-dns@7.2.5", "", { "dependencies": { "dns-packet": "^5.2.2", "thunky": "^1.0.2" }, "bin": { "multicast-dns": "cli.js" } }, "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg=="], - "multipasta": ["multipasta@0.2.7", "", {}, "sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA=="], + "multipasta": ["multipasta@0.2.8", "", {}, "sha512-ZPWuMKyv0cSO29f7hozp+k6+crZbQijV8ipMvxNxRf2SwtYGTX1ZX89Kd20VV4H9Znonx+EQn+iy1wGQsJ+b+Q=="], - "nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], + "nanoid": ["nanoid@3.3.16", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q=="], "negotiator": ["negotiator@0.6.4", "", {}, "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w=="], @@ -2228,13 +2229,13 @@ "node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="], - "node-releases": ["node-releases@2.0.47", "", {}, "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og=="], + "node-releases": ["node-releases@2.0.51", "", {}, "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ=="], "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], "normalize-url": ["normalize-url@8.1.1", "", {}, "sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ=="], - "npm-check-updates": ["npm-check-updates@22.2.3", "", { "bin": { "npm-check-updates": "build/cli.js", "ncu": "build/cli.js" } }, "sha512-n8HZ0ywZugYRgEqIm67hbZC5L/9S/gLX+MkGchJB43pP2xib1+u76OpVEJRZ4QP2BksPRi5M1CCStJVfc16+/A=="], + "npm-check-updates": ["npm-check-updates@22.2.9", "", { "bin": { "npm-check-updates": "build/cli.js", "ncu": "build/cli.js" } }, "sha512-DVeZ0KirHfliSsHuR2o7cHE+tW439sVHfJjF6cGWeDiY0Wyl3BI/jS4zV0eixtcMOquFbcF1Su/FsxOvk5MoYA=="], "npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], @@ -2322,13 +2323,13 @@ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - "picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], + "picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="], "pkg-dir": ["pkg-dir@7.0.0", "", { "dependencies": { "find-up": "^6.3.0" } }, "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA=="], "pkijs": ["pkijs@3.4.0", "", { "dependencies": { "@noble/hashes": "1.4.0", "asn1js": "^3.0.6", "bytestreamjs": "^2.0.1", "pvtsutils": "^1.3.6", "pvutils": "^1.1.3", "tslib": "^2.8.1" } }, "sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw=="], - "postcss": ["postcss@8.5.15", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A=="], + "postcss": ["postcss@8.5.19", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ=="], "postcss-attribute-case-insensitive": ["postcss-attribute-case-insensitive@7.0.1", "", { "dependencies": { "postcss-selector-parser": "^7.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-Uai+SupNSqzlschRyNx3kbCTWgY/2hcwtHEI/ej2LJWc9JJ77qKgGptd8DHwY1mXtZ7Aoh4z4yxfwMBue9eNgw=="], @@ -2448,7 +2449,7 @@ "postcss-selector-not": ["postcss-selector-not@8.0.1", "", { "dependencies": { "postcss-selector-parser": "^7.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-kmVy/5PYVb2UOhy0+LqUYAhKj7DUGDpSWa5LZqlkWJaaAV+dxxsOG3+St0yNLu6vsKD7Dmqx+nWQt0iil89+WA=="], - "postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], + "postcss-selector-parser": ["postcss-selector-parser@7.1.4", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg=="], "postcss-sort-media-queries": ["postcss-sort-media-queries@5.2.0", "", { "dependencies": { "sort-css-media-queries": "2.2.0" }, "peerDependencies": { "postcss": "^8.4.23" } }, "sha512-AZ5fDMLD8SldlAYlvi8NIqo0+Z8xnXU2ia0jxmuhxAU+Lqt9K+AlmLNJ/zWEnE9x+Zx3qL3+1K20ATgNOr3fAA=="], @@ -2460,7 +2461,7 @@ "postcss-zindex": ["postcss-zindex@6.0.2", "", { "peerDependencies": { "postcss": "^8.4.31" } }, "sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg=="], - "prettier": ["prettier@3.8.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw=="], + "prettier": ["prettier@3.9.5", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-/FVl766LpUfB5vXgCYOYa0MeV/441Ia99AeICQIQFTY/Nw0roZwULcXpku5i1/m5kt/baz+s4Zogspd839HSMg=="], "pretty-error": ["pretty-error@4.0.0", "", { "dependencies": { "lodash": "^4.17.20", "renderkid": "^3.0.0" } }, "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw=="], @@ -2494,13 +2495,13 @@ "pvutils": ["pvutils@1.1.5", "", {}, "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA=="], - "qs": ["qs@6.15.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw=="], + "qs": ["qs@6.15.3", "", { "dependencies": { "es-define-property": "^1.0.1", "side-channel": "^1.1.1" } }, "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A=="], "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], "quick-lru": ["quick-lru@5.1.1", "", {}, "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="], - "radix-ui": ["radix-ui@1.5.0", "", { "dependencies": { "@radix-ui/primitive": "1.1.4", "@radix-ui/react-accessible-icon": "1.1.9", "@radix-ui/react-accordion": "1.2.13", "@radix-ui/react-alert-dialog": "1.1.16", "@radix-ui/react-arrow": "1.1.9", "@radix-ui/react-aspect-ratio": "1.1.9", "@radix-ui/react-avatar": "1.1.12", "@radix-ui/react-checkbox": "1.3.4", "@radix-ui/react-collapsible": "1.1.13", "@radix-ui/react-collection": "1.1.9", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.1.4", "@radix-ui/react-context-menu": "2.3.0", "@radix-ui/react-dialog": "1.1.16", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.12", "@radix-ui/react-dropdown-menu": "2.1.17", "@radix-ui/react-focus-guards": "1.1.4", "@radix-ui/react-focus-scope": "1.1.9", "@radix-ui/react-form": "0.1.9", "@radix-ui/react-hover-card": "1.1.16", "@radix-ui/react-label": "2.1.9", "@radix-ui/react-menu": "2.1.17", "@radix-ui/react-menubar": "1.1.17", "@radix-ui/react-navigation-menu": "1.2.15", "@radix-ui/react-one-time-password-field": "0.1.9", "@radix-ui/react-password-toggle-field": "0.1.4", "@radix-ui/react-popover": "1.1.16", "@radix-ui/react-popper": "1.3.0", "@radix-ui/react-portal": "1.1.11", "@radix-ui/react-presence": "1.1.6", "@radix-ui/react-primitive": "2.1.5", "@radix-ui/react-progress": "1.1.9", "@radix-ui/react-radio-group": "1.4.0", "@radix-ui/react-roving-focus": "1.1.12", "@radix-ui/react-scroll-area": "1.2.11", "@radix-ui/react-select": "2.3.0", "@radix-ui/react-separator": "1.1.9", "@radix-ui/react-slider": "1.4.0", "@radix-ui/react-slot": "1.2.5", "@radix-ui/react-switch": "1.3.0", "@radix-ui/react-tabs": "1.1.14", "@radix-ui/react-toast": "1.2.16", "@radix-ui/react-toggle": "1.1.11", "@radix-ui/react-toggle-group": "1.1.12", "@radix-ui/react-toolbar": "1.1.12", "@radix-ui/react-tooltip": "1.2.9", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-effect-event": "0.0.3", "@radix-ui/react-use-escape-keydown": "1.1.2", "@radix-ui/react-use-is-hydrated": "0.1.1", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-use-size": "1.1.2", "@radix-ui/react-visually-hidden": "1.2.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nzh2HNpClgB31FBHRqt2xG8XNUfVfQRpf34hACC5PNrXTd5JdXdqOXwLs3BL+D8CNYiNQiJiT8QGr5Q4vq+00w=="], + "radix-ui": ["radix-ui@1.6.2", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-accessible-icon": "1.1.11", "@radix-ui/react-accordion": "1.2.16", "@radix-ui/react-alert-dialog": "1.1.19", "@radix-ui/react-arrow": "1.1.11", "@radix-ui/react-aspect-ratio": "1.1.11", "@radix-ui/react-avatar": "1.2.2", "@radix-ui/react-checkbox": "1.3.7", "@radix-ui/react-collapsible": "1.1.16", "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-context-menu": "2.3.3", "@radix-ui/react-dialog": "1.1.19", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.15", "@radix-ui/react-dropdown-menu": "2.1.20", "@radix-ui/react-focus-guards": "1.1.4", "@radix-ui/react-focus-scope": "1.1.12", "@radix-ui/react-form": "0.1.12", "@radix-ui/react-hover-card": "1.1.19", "@radix-ui/react-label": "2.1.11", "@radix-ui/react-menu": "2.1.20", "@radix-ui/react-menubar": "1.1.20", "@radix-ui/react-navigation-menu": "1.2.18", "@radix-ui/react-one-time-password-field": "0.1.12", "@radix-ui/react-password-toggle-field": "0.1.7", "@radix-ui/react-popover": "1.1.19", "@radix-ui/react-popper": "1.3.3", "@radix-ui/react-portal": "1.1.13", "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-progress": "1.1.12", "@radix-ui/react-radio-group": "1.4.3", "@radix-ui/react-roving-focus": "1.1.15", "@radix-ui/react-scroll-area": "1.2.14", "@radix-ui/react-select": "2.3.3", "@radix-ui/react-separator": "1.1.11", "@radix-ui/react-slider": "1.4.3", "@radix-ui/react-slot": "1.3.0", "@radix-ui/react-switch": "1.3.3", "@radix-ui/react-tabs": "1.1.17", "@radix-ui/react-toast": "1.2.19", "@radix-ui/react-toggle": "1.1.14", "@radix-ui/react-toggle-group": "1.1.15", "@radix-ui/react-toolbar": "1.1.15", "@radix-ui/react-tooltip": "1.2.12", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-effect-event": "0.0.3", "@radix-ui/react-use-escape-keydown": "1.1.3", "@radix-ui/react-use-is-hydrated": "0.1.1", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-use-size": "1.1.2", "@radix-ui/react-visually-hidden": "1.2.7" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-OwYUjzMwiInCUxgAWpPsavXC3Kh4iyi/49uU1/qZTG3RQDlvegyk1GOMiGvSkjua1RDb3JD3fo3eroL9FV4GQw=="], "randombytes": ["randombytes@2.1.0", "", { "dependencies": { "safe-buffer": "^5.1.0" } }, "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ=="], @@ -2518,7 +2519,7 @@ "react-helmet-async": ["@slorber/react-helmet-async@1.3.0", "", { "dependencies": { "@babel/runtime": "^7.12.5", "invariant": "^2.2.4", "prop-types": "^15.7.2", "react-fast-compare": "^3.2.0", "shallowequal": "^1.1.0" }, "peerDependencies": { "react": "^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-e9/OK8VhwUSc67diWI8Rb3I0YgI9/SBQtnhe9aEuK6MhZm7ntZZimXgwXnd8W96YTmSOb9M4d8LwhRZyhWr/1A=="], - "react-icons": ["react-icons@5.6.0", "", { "peerDependencies": { "react": "*" } }, "sha512-RH93p5ki6LfOiIt0UtDyNg/cee+HLVR6cHHtW3wALfo+eOHTp8RnU2kRkI6E+H19zMIs03DyxUG/GfZMOGvmiA=="], + "react-icons": ["react-icons@5.7.0", "", { "peerDependencies": { "react": "*" } }, "sha512-LBLy340Rzqy6+/yVhZKT3B/QpP1BZaesGqasf09HPOBzRarcDIFH0WwXlXQfE7q7ipxK4MSiC5DIBWURCny6fw=="], "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], @@ -2566,7 +2567,7 @@ "regjsgen": ["regjsgen@0.8.0", "", {}, "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q=="], - "regjsparser": ["regjsparser@0.13.1", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw=="], + "regjsparser": ["regjsparser@0.13.2", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ=="], "rehype-raw": ["rehype-raw@7.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-raw": "^9.0.0", "vfile": "^6.0.0" } }, "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww=="], @@ -2612,9 +2613,9 @@ "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], - "rolldown": ["rolldown@1.0.3", "", { "dependencies": { "@oxc-project/types": "=0.133.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.3", "@rolldown/binding-darwin-arm64": "1.0.3", "@rolldown/binding-darwin-x64": "1.0.3", "@rolldown/binding-freebsd-x64": "1.0.3", "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", "@rolldown/binding-linux-arm64-gnu": "1.0.3", "@rolldown/binding-linux-arm64-musl": "1.0.3", "@rolldown/binding-linux-ppc64-gnu": "1.0.3", "@rolldown/binding-linux-s390x-gnu": "1.0.3", "@rolldown/binding-linux-x64-gnu": "1.0.3", "@rolldown/binding-linux-x64-musl": "1.0.3", "@rolldown/binding-openharmony-arm64": "1.0.3", "@rolldown/binding-wasm32-wasi": "1.0.3", "@rolldown/binding-win32-arm64-msvc": "1.0.3", "@rolldown/binding-win32-x64-msvc": "1.0.3" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g=="], + "rolldown": ["rolldown@1.1.5", "", { "dependencies": { "@oxc-project/types": "=0.139.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.1.5", "@rolldown/binding-darwin-arm64": "1.1.5", "@rolldown/binding-darwin-x64": "1.1.5", "@rolldown/binding-freebsd-x64": "1.1.5", "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", "@rolldown/binding-linux-arm64-gnu": "1.1.5", "@rolldown/binding-linux-arm64-musl": "1.1.5", "@rolldown/binding-linux-ppc64-gnu": "1.1.5", "@rolldown/binding-linux-s390x-gnu": "1.1.5", "@rolldown/binding-linux-x64-gnu": "1.1.5", "@rolldown/binding-linux-x64-musl": "1.1.5", "@rolldown/binding-openharmony-arm64": "1.1.5", "@rolldown/binding-wasm32-wasi": "1.1.5", "@rolldown/binding-win32-arm64-msvc": "1.1.5", "@rolldown/binding-win32-x64-msvc": "1.1.5" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA=="], - "rollup": ["rollup@4.61.1", "", { "dependencies": { "@types/estree": "1.0.9" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.61.1", "@rollup/rollup-android-arm64": "4.61.1", "@rollup/rollup-darwin-arm64": "4.61.1", "@rollup/rollup-darwin-x64": "4.61.1", "@rollup/rollup-freebsd-arm64": "4.61.1", "@rollup/rollup-freebsd-x64": "4.61.1", "@rollup/rollup-linux-arm-gnueabihf": "4.61.1", "@rollup/rollup-linux-arm-musleabihf": "4.61.1", "@rollup/rollup-linux-arm64-gnu": "4.61.1", "@rollup/rollup-linux-arm64-musl": "4.61.1", "@rollup/rollup-linux-loong64-gnu": "4.61.1", "@rollup/rollup-linux-loong64-musl": "4.61.1", "@rollup/rollup-linux-ppc64-gnu": "4.61.1", "@rollup/rollup-linux-ppc64-musl": "4.61.1", "@rollup/rollup-linux-riscv64-gnu": "4.61.1", "@rollup/rollup-linux-riscv64-musl": "4.61.1", "@rollup/rollup-linux-s390x-gnu": "4.61.1", "@rollup/rollup-linux-x64-gnu": "4.61.1", "@rollup/rollup-linux-x64-musl": "4.61.1", "@rollup/rollup-openbsd-x64": "4.61.1", "@rollup/rollup-openharmony-arm64": "4.61.1", "@rollup/rollup-win32-arm64-msvc": "4.61.1", "@rollup/rollup-win32-ia32-msvc": "4.61.1", "@rollup/rollup-win32-x64-gnu": "4.61.1", "@rollup/rollup-win32-x64-msvc": "4.61.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA=="], + "rollup": ["rollup@4.62.2", "", { "dependencies": { "@types/estree": "1.0.9" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.62.2", "@rollup/rollup-android-arm64": "4.62.2", "@rollup/rollup-darwin-arm64": "4.62.2", "@rollup/rollup-darwin-x64": "4.62.2", "@rollup/rollup-freebsd-arm64": "4.62.2", "@rollup/rollup-freebsd-x64": "4.62.2", "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", "@rollup/rollup-linux-arm-musleabihf": "4.62.2", "@rollup/rollup-linux-arm64-gnu": "4.62.2", "@rollup/rollup-linux-arm64-musl": "4.62.2", "@rollup/rollup-linux-loong64-gnu": "4.62.2", "@rollup/rollup-linux-loong64-musl": "4.62.2", "@rollup/rollup-linux-ppc64-gnu": "4.62.2", "@rollup/rollup-linux-ppc64-musl": "4.62.2", "@rollup/rollup-linux-riscv64-gnu": "4.62.2", "@rollup/rollup-linux-riscv64-musl": "4.62.2", "@rollup/rollup-linux-s390x-gnu": "4.62.2", "@rollup/rollup-linux-x64-gnu": "4.62.2", "@rollup/rollup-linux-x64-musl": "4.62.2", "@rollup/rollup-openbsd-x64": "4.62.2", "@rollup/rollup-openharmony-arm64": "4.62.2", "@rollup/rollup-win32-arm64-msvc": "4.62.2", "@rollup/rollup-win32-ia32-msvc": "4.62.2", "@rollup/rollup-win32-x64-gnu": "4.62.2", "@rollup/rollup-win32-x64-msvc": "4.62.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA=="], "rrweb-cssom": ["rrweb-cssom@0.8.0", "", {}, "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw=="], @@ -2646,7 +2647,7 @@ "selfsigned": ["selfsigned@5.5.0", "", { "dependencies": { "@peculiar/x509": "^1.14.2", "pkijs": "^3.3.3" } }, "sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew=="], - "semver": ["semver@7.8.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ=="], + "semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], "semver-diff": ["semver-diff@4.0.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA=="], @@ -2654,9 +2655,9 @@ "serialize-javascript": ["serialize-javascript@6.0.2", "", { "dependencies": { "randombytes": "^2.1.0" } }, "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g=="], - "seroval": ["seroval@1.5.4", "", {}, "sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw=="], + "seroval": ["seroval@1.5.5", "", {}, "sha512-bSjOuPcwPKLSJNhr9+bZxA20nQxVle5J5MNsYRVE6cIg7KpRLXGupymePavu0jrxlPiPsr4xGZSB8yUY2sH2sw=="], - "seroval-plugins": ["seroval-plugins@1.5.4", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw=="], + "seroval-plugins": ["seroval-plugins@1.5.5", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-+BDhqYM6CEn3x09v44dpa9p6974FuUB2dxk+Ctn04k0cO1Zt6QODTXfmEZK0eBaTe/fJBvP4NMGuNJ+R8T+QMg=="], "serve-handler": ["serve-handler@6.1.7", "", { "dependencies": { "bytes": "3.0.0", "content-disposition": "0.5.2", "mime-types": "2.1.18", "minimatch": "3.1.5", "path-is-inside": "1.0.2", "path-to-regexp": "3.3.0", "range-parser": "1.2.0" } }, "sha512-CinAq1xWb0vR3twAv9evEU8cNWkXCb9kd5ePAHUKJBkOsUpR1wt/CvGdeca7vqumL1U5cSaeVQ6zZMxiJ3yWsg=="], @@ -2676,9 +2677,9 @@ "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - "shell-quote": ["shell-quote@1.8.4", "", {}, "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ=="], + "shell-quote": ["shell-quote@1.10.0", "", {}, "sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA=="], - "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": ["side-channel@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4", "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ=="], "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=="], @@ -2758,7 +2759,7 @@ "svg-parser": ["svg-parser@2.0.4", "", {}, "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ=="], - "svgo": ["svgo@3.3.3", "", { "dependencies": { "commander": "^7.2.0", "css-select": "^5.1.0", "css-tree": "^2.3.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.0.0", "sax": "^1.5.0" }, "bin": "./bin/svgo" }, "sha512-+wn7I4p7YgJhHs38k2TNjy1vCfPIfLIJWR5MnCStsN8WuuTcBnRKcMHQLMM2ijxGZmDoZwNv8ipl5aTTen62ng=="], + "svgo": ["svgo@3.3.4", "", { "dependencies": { "commander": "^7.2.0", "css-select": "^5.1.0", "css-tree": "^2.3.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.0.0", "sax": "^1.5.0" }, "bin": "./bin/svgo" }, "sha512-GsNRis4e8jxn2Y9ENz/8lbJ93CstG8svtMnuRaHbiF2LTJ5tK0/q3t/URPq9Zc7zVWBJnNnJMIp6bevK7bSmNg=="], "swc-loader": ["swc-loader@0.2.7", "", { "dependencies": { "@swc/counter": "^0.1.3" }, "peerDependencies": { "@swc/core": "^1.2.147", "webpack": ">=2" } }, "sha512-nwYWw3Fh9ame3Rtm7StS9SBLpHRRnYcK7bnpF3UKZmesAK0gw2/ADvlURFAINmPvKtDLzp+GBiP9yLoEjg6S9w=="], @@ -2768,7 +2769,7 @@ "tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="], - "terser": ["terser@5.48.0", "", { "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-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q=="], + "terser": ["terser@5.49.0", "", { "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-SNiDnXyHSrxVcIOtVbULzcTmniUiwcV7Nwdyj1twVubeTmbjoa8p69KKDpfkdoOavuM4/GRm1+ykI8qqnavHoA=="], "terser-webpack-plugin": ["terser-webpack-plugin@5.6.1", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", "schema-utils": "^4.3.0", "terser": "^5.31.1" }, "peerDependencies": { "webpack": "^5.1.0" } }, "sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ=="], @@ -2800,7 +2801,7 @@ "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], - "toml": ["toml@4.1.1", "", {}, "sha512-EBJnVBr3dTXdA89WVFoAIPUqkBjxPMwRqsfuo1r240tKFHXv3zgca4+NJib/h6TyvGF7vOawz0jGuryJCdNHrw=="], + "toml": ["toml@4.3.0", "", {}, "sha512-lVb8X9BsPVuH0M4BKeS91tXAmJvCjQ5UIyAbQFaxkKGyUFK2RPkhwaFSQH8vbpl1d23eu/IBH+dwVMHWaq9A5A=="], "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], @@ -2818,9 +2819,9 @@ "tsyringe": ["tsyringe@4.10.0", "", { "dependencies": { "tslib": "^1.9.3" } }, "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw=="], - "turbo": ["turbo@2.9.16", "", { "optionalDependencies": { "@turbo/darwin-64": "2.9.16", "@turbo/darwin-arm64": "2.9.16", "@turbo/linux-64": "2.9.16", "@turbo/linux-arm64": "2.9.16", "@turbo/windows-64": "2.9.16", "@turbo/windows-arm64": "2.9.16" }, "bin": { "turbo": "bin/turbo" } }, "sha512-NqgRQy6j6dPYcdSdv0q1g9QsZg7SWg87RERM8otw/1AtKU2yTFVClOM7cbwKzOonZr/Ek1blTBucw64L9H0Bwg=="], + "turbo": ["turbo@2.10.5", "", { "optionalDependencies": { "@turbo/darwin-64": "2.10.5", "@turbo/darwin-arm64": "2.10.5", "@turbo/linux-64": "2.10.5", "@turbo/linux-arm64": "2.10.5", "@turbo/windows-64": "2.10.5", "@turbo/windows-arm64": "2.10.5" }, "bin": { "turbo": "bin/turbo" } }, "sha512-07Y/C7OUp23l4P92PJoYtFNbHjLhftrZH5Ce7dbczS4kX2Re+wtbXvZLoxn/pUtzgsQaRCBaRuZPJp4zmAn0WQ=="], - "type-fest": ["type-fest@5.7.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg=="], + "type-fest": ["type-fest@5.8.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-YGYEVz3Fm5iy/AybuA0oyNFq7H4CgQNfRp/qfe8nurE1kuCeNm3/vfm9X4Mtl+qLyaKJUh5xrFZwogr41SMjYA=="], "type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="], @@ -2828,7 +2829,7 @@ "typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], - "undici-types": ["undici-types@7.24.6", "", {}, "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg=="], + "undici-types": ["undici-types@8.3.0", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="], "unicode-canonical-property-names-ecmascript": ["unicode-canonical-property-names-ecmascript@2.0.1", "", {}, "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg=="], @@ -2860,7 +2861,7 @@ "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], - "unplugin": ["unplugin@3.0.0", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg=="], + "unplugin": ["unplugin@3.3.0", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "picomatch": "^4.0.4", "webpack-virtual-modules": "^0.6.2" }, "peerDependencies": { "@farmfe/core": "*", "@rspack/core": "*", "bun-types-no-globals": "*", "esbuild": "*", "rolldown": "*", "rollup": "*", "unloader": "*", "vite": "*", "webpack": "*" }, "optionalPeers": ["@farmfe/core", "@rspack/core", "bun-types-no-globals", "esbuild", "rolldown", "rollup", "unloader", "vite", "webpack"] }, "sha512-qa66K+crbfyE6JK10GjvbJeRrOsuC/JpbnHctfyp/i4oBTxWOzJfRZyDiOk1PtErMFRu8JhsU/wPvOdBNWe5Rg=="], "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], @@ -2896,15 +2897,15 @@ "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], - "vite": ["vite@8.0.16", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.15", "rolldown": "1.0.3", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.18", "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-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw=="], + "vite": ["vite@8.1.5", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.5", "postcss": "^8.5.17", "rolldown": "~1.1.5", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.3.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-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw=="], "vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], - "vitest": ["vitest@3.2.6", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.6", "@vitest/mocker": "3.2.6", "@vitest/pretty-format": "^3.2.6", "@vitest/runner": "3.2.6", "@vitest/snapshot": "3.2.6", "@vitest/spy": "3.2.6", "@vitest/utils": "3.2.6", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.6", "@vitest/ui": "3.2.6", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-xejya+bT/j/+R/AGa1XOfRxLmNUlLtlwjRsFUILF+xHfzElmGcmFydy2gqqIrd62ptIEfwVMofd19uNWD9L7Nw=="], + "vitest": ["vitest@3.2.7", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.7", "@vitest/mocker": "3.2.7", "@vitest/pretty-format": "^3.2.7", "@vitest/runner": "3.2.7", "@vitest/snapshot": "3.2.7", "@vitest/spy": "3.2.7", "@vitest/utils": "3.2.7", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.7", "@vitest/ui": "3.2.7", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "./vitest.mjs" } }, "sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg=="], "w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="], - "watchpack": ["watchpack@2.5.1", "", { "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" } }, "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg=="], + "watchpack": ["watchpack@2.5.2", "", { "dependencies": { "graceful-fs": "^4.1.2" } }, "sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg=="], "wbuf": ["wbuf@1.7.3", "", { "dependencies": { "minimalistic-assert": "^1.0.0" } }, "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA=="], @@ -2912,17 +2913,17 @@ "webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="], - "webpack": ["webpack@5.107.2", "", { "dependencies": { "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.16.0", "acorn-import-phases": "^1.0.3", "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.22.0", "es-module-lexer": "^2.1.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "loader-runner": "^4.3.2", "mime-db": "^1.54.0", "neo-async": "^2.6.2", "schema-utils": "^4.3.3", "tapable": "^2.3.0", "terser-webpack-plugin": "^5.5.0", "watchpack": "^2.5.1", "webpack-sources": "^3.5.0" }, "bin": { "webpack": "bin/webpack.js" } }, "sha512-v7RhXaJbpMlV0D7hC7lb2EbnxkoeUqf9qhKr6lozx3Q48pmFrqqNRmZFUEGmi7pSwm6fCQ2H1IjvCkHqdpVdjQ=="], + "webpack": ["webpack@5.108.4", "", { "dependencies": { "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.16.0", "acorn-import-phases": "^1.0.3", "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.22.2", "es-module-lexer": "^2.1.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "graceful-fs": "^4.2.11", "loader-runner": "^4.3.2", "mime-db": "^1.54.0", "minimizer-webpack-plugin": "^5.6.1", "neo-async": "^2.6.2", "schema-utils": "^4.3.3", "tapable": "^2.3.0", "watchpack": "^2.5.2", "webpack-sources": "^3.5.0" }, "peerDependencies": { "webpack-cli": "*" }, "optionalPeers": ["webpack-cli"], "bin": { "webpack": "bin/webpack.js" } }, "sha512-yur8LyJoeiWh47dErD+Ok7vlbmDsJ3UbbRPAoxbGJ54WpE2y5yVo5G/inUzujnYgw3tPmBRdn+G7PoxXaYC33w=="], "webpack-bundle-analyzer": ["webpack-bundle-analyzer@4.10.2", "", { "dependencies": { "@discoveryjs/json-ext": "0.5.7", "acorn": "^8.0.4", "acorn-walk": "^8.0.0", "commander": "^7.2.0", "debounce": "^1.2.1", "escape-string-regexp": "^4.0.0", "gzip-size": "^6.0.0", "html-escaper": "^2.0.2", "opener": "^1.5.2", "picocolors": "^1.0.0", "sirv": "^2.0.3", "ws": "^7.3.1" }, "bin": { "webpack-bundle-analyzer": "lib/bin/analyzer.js" } }, "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw=="], "webpack-dev-middleware": ["webpack-dev-middleware@7.4.5", "", { "dependencies": { "colorette": "^2.0.10", "memfs": "^4.43.1", "mime-types": "^3.0.1", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "schema-utils": "^4.0.0" }, "peerDependencies": { "webpack": "^5.0.0" }, "optionalPeers": ["webpack"] }, "sha512-uxQ6YqGdE4hgDKNf7hUiPXOdtkXvBJXrfEGYSx7P7LC8hnUYGK70X6xQXUvXeNyBDDcsiQXpG2m3G9vxowaEuA=="], - "webpack-dev-server": ["webpack-dev-server@5.2.4", "", { "dependencies": { "@types/bonjour": "^3.5.13", "@types/connect-history-api-fallback": "^1.5.4", "@types/express": "^4.17.25", "@types/express-serve-static-core": "^4.17.21", "@types/serve-index": "^1.9.4", "@types/serve-static": "^1.15.5", "@types/sockjs": "^0.3.36", "@types/ws": "^8.5.10", "ansi-html-community": "^0.0.8", "bonjour-service": "^1.2.1", "chokidar": "^3.6.0", "colorette": "^2.0.10", "compression": "^1.8.1", "connect-history-api-fallback": "^2.0.0", "express": "^4.22.1", "graceful-fs": "^4.2.6", "http-proxy-middleware": "^2.0.9", "ipaddr.js": "^2.1.0", "launch-editor": "^2.6.1", "open": "^10.0.3", "p-retry": "^6.2.0", "schema-utils": "^4.2.0", "selfsigned": "^5.5.0", "serve-index": "^1.9.1", "sockjs": "^0.3.24", "spdy": "^4.0.2", "webpack-dev-middleware": "^7.4.2", "ws": "^8.18.0" }, "peerDependencies": { "webpack": "^5.0.0" }, "optionalPeers": ["webpack"], "bin": { "webpack-dev-server": "bin/webpack-dev-server.js" } }, "sha512-GqDPGZN9bRqKBTkp4aWkobDDHMsrXKoGSdOH56smIri8qR0JG8gfL8/v/f/OZR3/OKXjG8uwJbFVhKm/FNU/UA=="], + "webpack-dev-server": ["webpack-dev-server@5.2.6", "", { "dependencies": { "@types/bonjour": "^3.5.13", "@types/connect-history-api-fallback": "^1.5.4", "@types/express": "^4.17.25", "@types/express-serve-static-core": "^4.17.21", "@types/serve-index": "^1.9.4", "@types/serve-static": "^1.15.5", "@types/sockjs": "^0.3.36", "@types/ws": "^8.5.10", "ansi-html-community": "^0.0.8", "bonjour-service": "^1.2.1", "chokidar": "^3.6.0", "colorette": "^2.0.10", "compression": "^1.8.1", "connect-history-api-fallback": "^2.0.0", "express": "^4.22.1", "graceful-fs": "^4.2.6", "http-proxy-middleware": "^2.0.9", "ipaddr.js": "^2.1.0", "launch-editor": "^2.14.1", "open": "^10.0.3", "p-retry": "^6.2.0", "schema-utils": "^4.2.0", "selfsigned": "^5.5.0", "serve-index": "^1.9.1", "sockjs": "^0.3.24", "spdy": "^4.0.2", "webpack-dev-middleware": "^7.4.2", "ws": "^8.18.0" }, "peerDependencies": { "webpack": "^5.0.0", "webpack-cli": "*" }, "optionalPeers": ["webpack", "webpack-cli"], "bin": { "webpack-dev-server": "bin/webpack-dev-server.js" } }, "sha512-HNLRmamRvVavZQ+avceZifmv8hmdUjg43t6MI4SqJDwFdW7RPQwH5vzGhDRZSX59SgfbeHhLnq3g+uooWo7pVw=="], "webpack-merge": ["webpack-merge@5.10.0", "", { "dependencies": { "clone-deep": "^4.0.1", "flat": "^5.0.2", "wildcard": "^2.0.0" } }, "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA=="], - "webpack-sources": ["webpack-sources@3.5.0", "", {}, "sha512-HPuy+uuoTCaaoEoI1LQ3JN9+vrPBvEesnnX1jADHy728cHSMlq4wUc4afYqahq2B1mhQVZxCXOkNTnXltr+2vQ=="], + "webpack-sources": ["webpack-sources@3.5.1", "", {}, "sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw=="], "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], @@ -2950,7 +2951,7 @@ "write-file-atomic": ["write-file-atomic@3.0.3", "", { "dependencies": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", "signal-exit": "^3.0.2", "typedarray-to-buffer": "^3.1.5" } }, "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q=="], - "ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], + "ws": ["ws@8.21.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw=="], "wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="], @@ -2994,6 +2995,8 @@ "@docusaurus/core/webpack-merge": ["webpack-merge@6.0.1", "", { "dependencies": { "clone-deep": "^4.0.1", "flat": "^5.0.2", "wildcard": "^2.0.1" } }, "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg=="], + "@docusaurus/faster/@docusaurus/types": ["@docusaurus/types@3.10.2", "", { "dependencies": { "@mdx-js/mdx": "^3.0.0", "@types/history": "^4.7.11", "@types/mdast": "^4.0.2", "@types/react": "*", "commander": "^5.1.0", "joi": "^17.9.2", "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0", "utility-types": "^3.10.0", "webpack": "^5.95.0", "webpack-merge": "^5.9.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-B6rvfwIFSapUqUJjMriZswX13K8l5Z7AcmVE6uTEJpYddQieSTR12DsGaFtcZAIDsQd4p+0WTl0Vc6jmZK0Trw=="], + "@docusaurus/utils/jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], "@effect-fc/example-next/effect": ["effect@4.0.0-beta.85", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.8.0", "find-my-way-ts": "^0.1.6", "ini": "^7.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^2.0.1", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^14.0.0", "yaml": "^2.9.0" } }, "sha512-Cjv9YQyv4CiIccmRAQIWAoeESCpCpiuHYY8zb5vqiYs3Ac2yE5RQAnKq7z4Ir/3VJYZb8kxh6rS8czDDnpTdkQ=="], @@ -3012,8 +3015,6 @@ "@svgr/hast-util-to-babel-ast/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], - "@types/serve-static/@types/send": ["@types/send@0.17.6", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og=="], - "accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], "accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], @@ -3056,8 +3057,6 @@ "css-select/domutils": ["domutils@2.8.0", "", { "dependencies": { "dom-serializer": "^1.0.1", "domelementtype": "^2.2.0", "domhandler": "^4.2.0" } }, "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A=="], - "csso/css-tree": ["css-tree@2.2.1", "", { "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" } }, "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA=="], - "decompress-response/mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], "dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], @@ -3088,7 +3087,7 @@ "got/@sindresorhus/is": ["@sindresorhus/is@5.6.0", "", {}, "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g=="], - "gray-matter/js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="], + "gray-matter/js-yaml": ["js-yaml@3.15.0", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog=="], "hpack.js/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], @@ -3238,17 +3237,17 @@ "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], - "postcss-calc/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], + "postcss-calc/postcss-selector-parser": ["postcss-selector-parser@6.1.4", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ=="], - "postcss-discard-unused/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], + "postcss-discard-unused/postcss-selector-parser": ["postcss-selector-parser@6.1.4", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ=="], "postcss-loader/jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], - "postcss-merge-rules/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], + "postcss-merge-rules/postcss-selector-parser": ["postcss-selector-parser@6.1.4", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ=="], - "postcss-minify-selectors/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], + "postcss-minify-selectors/postcss-selector-parser": ["postcss-selector-parser@6.1.4", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ=="], - "postcss-unique-selectors/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], + "postcss-unique-selectors/postcss-selector-parser": ["postcss-selector-parser@6.1.4", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ=="], "pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], @@ -3290,12 +3289,14 @@ "strip-literal/js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], - "stylehacks/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], + "stylehacks/postcss-selector-parser": ["postcss-selector-parser@6.1.4", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ=="], "svgo/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], "svgo/css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="], + "svgo/css-tree": ["css-tree@2.3.1", "", { "dependencies": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" } }, "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw=="], + "terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], "tsyringe/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], @@ -3312,13 +3313,13 @@ "vite-node/es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], - "vite-node/vite": ["vite@7.3.5", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.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", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww=="], + "vite-node/vite": ["vite@7.3.6", "", { "dependencies": { "esbuild": "^0.27.0 || ^0.28.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.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", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg=="], - "vitest/vite": ["vite@7.3.5", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.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", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww=="], + "vitest/vite": ["vite@7.3.6", "", { "dependencies": { "esbuild": "^0.27.0 || ^0.28.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.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", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg=="], "webpack-bundle-analyzer/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], - "webpack-bundle-analyzer/ws": ["ws@7.5.11", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA=="], + "webpack-bundle-analyzer/ws": ["ws@7.5.13", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA=="], "webpack-dev-middleware/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], @@ -3340,7 +3341,7 @@ "@docusaurus/core/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], - "@effect-fc/example-next/effect/fast-check": ["fast-check@4.8.0", "", { "dependencies": { "pure-rand": "^8.0.0" } }, "sha512-GOJ158CUMnN6cSahsv4+ExARvIDuzzinFjkp0E9WtiBa5zcVeLozVkWaE4IzFcc+Y48Wp1EDlUZsXRyAztQcSg=="], + "@effect-fc/example-next/effect/fast-check": ["fast-check@4.9.0", "", { "dependencies": { "pure-rand": "^8.0.0" } }, "sha512-7ms6T7SybUev/PQITciI0yLM2pOSFy5zpG8Ty7tQofcVaQUvrMXp6CBwqF6fThLCLOrfBtuHAtwq6Yu4XPCllg=="], "@effect-fc/example-next/effect/msgpackr": ["msgpackr@2.0.4", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.4" } }, "sha512-o1C5KRmuRt+apqMr1HuGSqWStZoRBUpEsCsl15uM9VdAF1qHLtvMOU2En747EnTyEl6c4pzPewRMFF31s1CNbA=="], @@ -3372,18 +3373,10 @@ "css-select/domutils/dom-serializer": ["dom-serializer@1.4.1", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", "entities": "^2.0.0" } }, "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag=="], - "csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="], - - "effect-fc-next/@effect/platform-browser/multipasta": ["multipasta@0.2.8", "", {}, "sha512-ZPWuMKyv0cSO29f7hozp+k6+crZbQijV8ipMvxNxRf2SwtYGTX1ZX89Kd20VV4H9Znonx+EQn+iy1wGQsJ+b+Q=="], - "effect-fc-next/effect/fast-check": ["fast-check@4.9.0", "", { "dependencies": { "pure-rand": "^8.0.0" } }, "sha512-7ms6T7SybUev/PQITciI0yLM2pOSFy5zpG8Ty7tQofcVaQUvrMXp6CBwqF6fThLCLOrfBtuHAtwq6Yu4XPCllg=="], "effect-fc-next/effect/msgpackr": ["msgpackr@2.0.4", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.4" } }, "sha512-o1C5KRmuRt+apqMr1HuGSqWStZoRBUpEsCsl15uM9VdAF1qHLtvMOU2En747EnTyEl6c4pzPewRMFF31s1CNbA=="], - "effect-fc-next/effect/multipasta": ["multipasta@0.2.8", "", {}, "sha512-ZPWuMKyv0cSO29f7hozp+k6+crZbQijV8ipMvxNxRf2SwtYGTX1ZX89Kd20VV4H9Znonx+EQn+iy1wGQsJ+b+Q=="], - - "effect-fc-next/effect/toml": ["toml@4.3.0", "", {}, "sha512-lVb8X9BsPVuH0M4BKeS91tXAmJvCjQ5UIyAbQFaxkKGyUFK2RPkhwaFSQH8vbpl1d23eu/IBH+dwVMHWaq9A5A=="], - "express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "file-loader/schema-utils/ajv": ["ajv@6.15.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw=="], @@ -3424,6 +3417,8 @@ "serve-index/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + "svgo/css-tree/mdn-data": ["mdn-data@2.0.30", "", {}, "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA=="], + "type-is/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], "update-notifier/boxen/camelcase": ["camelcase@7.0.1", "", {}, "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw=="], @@ -3442,11 +3437,11 @@ "@docusaurus/core/chokidar/readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], - "@effect-fc/example-next/effect/fast-check/pure-rand": ["pure-rand@8.4.0", "", {}, "sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A=="], + "@effect-fc/example-next/effect/fast-check/pure-rand": ["pure-rand@8.4.2", "", {}, "sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng=="], "css-select/domutils/dom-serializer/entities": ["entities@2.2.0", "", {}, "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="], - "effect-fc-next/effect/fast-check/pure-rand": ["pure-rand@8.4.0", "", {}, "sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A=="], + "effect-fc-next/effect/fast-check/pure-rand": ["pure-rand@8.4.2", "", {}, "sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng=="], "file-loader/schema-utils/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], diff --git a/packages/effect-fc-next/package.json b/packages/effect-fc-next/package.json index 2375e0e..91b4e19 100644 --- a/packages/effect-fc-next/package.json +++ b/packages/effect-fc-next/package.json @@ -51,6 +51,7 @@ "react": "^19.2.0" }, "dependencies": { + "@standard-schema/spec": "^1.1.0", "effect-lens": "^2.0.0-beta.1" } } diff --git a/packages/effect-fc-next/src/Form.ts b/packages/effect-fc-next/src/Form.ts index e3ce5ea..2ff3fba 100644 --- a/packages/effect-fc-next/src/Form.ts +++ b/packages/effect-fc-next/src/Form.ts @@ -1,3 +1,4 @@ +import type { StandardSchemaV1 } from "@standard-schema/spec" import { Array, type Cause, Chunk, type Duration, Effect, Equal, Function, identity, Option, Pipeable, Predicate, type Scope, Stream, SubscriptionRef } from "effect" import type * as React from "react" import * as Component from "./Component.js" @@ -8,11 +9,6 @@ import * as View from "./View.js" export const FormTypeId: unique symbol = Symbol.for("@effect-fc/Form/Form") export type FormTypeId = typeof FormTypeId -export interface FormIssue { - readonly path: readonly PropertyKey[] - readonly message: string -} - export interface Form extends Pipeable.Pipeable { readonly [FormTypeId]: FormTypeId @@ -20,8 +16,8 @@ extends Pipeable.Pipeable { readonly path: P readonly value: View.View, ER, never> readonly encodedValue: Lens.Lens - readonly issues: View.View - readonly isValidating: View.View + readonly issues: View.View + readonly isValidating: View.View readonly canCommit: View.View readonly isCommitting: View.View } @@ -34,7 +30,7 @@ extends Pipeable.Class implements Form { readonly path: P, readonly value: View.View, ER, never>, readonly encodedValue: Lens.Lens, - readonly issues: View.View, + readonly issues: View.View, readonly isValidating: View.View, readonly canCommit: View.View, readonly isCommitting: View.View, @@ -43,16 +39,17 @@ extends Pipeable.Class implements Form { } } - export const isForm = (u: unknown): u is Form => Predicate.hasProperty(u, FormTypeId) const filterIssuesByPath = ( - issues: readonly FormIssue[], + issues: readonly StandardSchemaV1.Issue[], path: readonly PropertyKey[], -): readonly FormIssue[] => Array.filter(issues, issue => - issue.path.length >= path.length && Array.every(path, (p, i) => p === issue.path[i]) -) +): readonly StandardSchemaV1.Issue[] => Array.filter(issues, issue => { + const issuePath = issue.path + if (!issuePath) return false + return issuePath.length >= path.length && Array.every(path, (p, i) => p === issuePath[i]) +}) export const focusObjectOn: {

( @@ -66,7 +63,7 @@ export const focusObjectOn: { self: Form, key: K, ): Form => { - const form = self as unknown as FormImpl + const form = self as FormImpl const path = [...form.path, key] as const return new FormImpl( @@ -92,12 +89,12 @@ export const focusArrayAt: { self: Form, index: number, ): Form => { - const form = self as unknown as FormImpl + const form = self as FormImpl const path = [...form.path, index] as const return new FormImpl( path, - View.mapOptionEffect(form.value, values => Effect.fromOption(Array.get(values, index))), + View.mapOptionEffect(form.value, value => Effect.fromOption(Array.get(value, index))), Lens.focusArrayAt(form.encodedValue, index), View.map(form.issues, issues => filterIssuesByPath(issues, path)), form.isValidating, @@ -118,12 +115,12 @@ export const focusTupleAt: { self: Form, index: K, ): Form => { - const form = self as unknown as FormImpl + const form = self as FormImpl const path = [...form.path, index] as const return new FormImpl( path, - View.mapOption(form.value, values => values[index]), + View.mapOption(form.value, Array.getUnsafe(index)), Lens.focusTupleAt(form.encodedValue, index), View.map(form.issues, issues => filterIssuesByPath(issues, path)), form.isValidating, @@ -144,12 +141,12 @@ export const focusChunkAt: { self: Form, Chunk.Chunk, ER, EW>, index: number, ): Form => { - const form = self as unknown as FormImpl, Chunk.Chunk, ER, EW> + const form = self as FormImpl, Chunk.Chunk, ER, EW> const path = [...form.path, index] as const return new FormImpl( path, - View.mapOptionEffect(form.value, values => Effect.fromOption(Chunk.get(values, index))), + View.mapOptionEffect(form.value, value => Effect.fromOption(Chunk.get(value, index))), Lens.focusChunkAt(form.encodedValue, index), View.map(form.issues, issues => filterIssuesByPath(issues, path)), form.isValidating, @@ -183,11 +180,9 @@ export const useInput = Effect.fnUntraced(function*

Effect.flatMap( - Lens.get(internalValueLens), - internalValue => !Equal.equals(upstreamEncodedValue, internalValue) - ? Lens.set(internalValueLens, upstreamEncodedValue) - : Effect.succeed(undefined), + upstreamEncodedValue => Effect.when( + Lens.set(internalValueLens, upstreamEncodedValue), + Effect.map(Lens.get(internalValueLens), internalValue => !Equal.equals(upstreamEncodedValue, internalValue)), ), ), @@ -242,11 +237,8 @@ export const useOptionalInput = Effect.fnUntraced(function*

Effect.flatMap( - Effect.all([Lens.get(enabledLens), Lens.get(internalValueLens)]), - ([enabled, internalValue]) => Equal.equals(upstreamEncodedValue, enabled ? Option.some(internalValue) : Option.none()) - ? Effect.succeed(undefined) - : Option.match(upstreamEncodedValue, { + upstreamEncodedValue => Effect.when( + Option.match(upstreamEncodedValue, { onSome: v => Effect.andThen( Lens.set(enabledLens, true), Lens.set(internalValueLens, v), @@ -256,6 +248,11 @@ export const useOptionalInput = Effect.fnUntraced(function*

!Equal.equals(upstreamEncodedValue, enabled ? Option.some(internalValue) : Option.none()), + ), ), ), diff --git a/packages/effect-fc-next/src/SubmittableForm.ts b/packages/effect-fc-next/src/SubmittableForm.ts index a08b59c..122c9a3 100644 --- a/packages/effect-fc-next/src/SubmittableForm.ts +++ b/packages/effect-fc-next/src/SubmittableForm.ts @@ -1,212 +1,213 @@ -import { - Array, - Cause, - type Context, - Effect, - Fiber, - Option, - Pipeable, - Predicate, - Schema, - SchemaIssue, - SchemaParser, - type Scope, - Semaphore, - SubscriptionRef, -} from "effect" +import type { StandardSchemaV1 } from "@standard-schema/spec" +import { Array, Cause, type Context, Effect, Fiber, Option, Pipeable, Predicate, Schema, SchemaError, SchemaIssue, type Scope, Semaphore, SubscriptionRef } from "effect" +import { AsyncResult } from "effect/unstable/reactivity" import * as Form from "./Form.js" import * as Lens from "./Lens.js" import * as Mutation from "./Mutation.js" -import * as Result from "./Result.js" import * as View from "./View.js" -type FormSchema = Schema.Top & { - readonly Type: A - readonly Encoded: I - readonly DecodingServices: R -} - export const SubmittableFormTypeId: unique symbol = Symbol.for("@effect-fc/Form/SubmittableForm") export type SubmittableFormTypeId = typeof SubmittableFormTypeId -export interface SubmittableForm +export interface SubmittableForm extends Form.Form { readonly [SubmittableFormTypeId]: SubmittableFormTypeId - readonly schema: FormSchema + + readonly schema: Schema.ConstraintCodec readonly context: Context.Context readonly mutation: Mutation.Mutation< readonly [value: A, form: SubmittableForm], - MA, ME, MR, MP + MA, ME, MR > - readonly validationFiber: View.View>, never, never> + readonly validationFiber: View.View>, never, never> + readonly run: Effect.Effect - readonly submit: Effect.Effect>, Cause.NoSuchElementError> + readonly submit: Effect.Effect | AsyncResult.Failure>, Cause.NoSuchElementError> } -export class SubmittableFormImpl -extends Pipeable.Class implements SubmittableForm { +export class SubmittableFormImpl +extends Pipeable.Class implements SubmittableForm { readonly [Form.FormTypeId]: Form.FormTypeId = Form.FormTypeId readonly [SubmittableFormTypeId]: SubmittableFormTypeId = SubmittableFormTypeId + readonly path = [] as const + readonly encodedValue: Lens.Lens readonly isValidating: View.View readonly canCommit: View.View readonly isCommitting: View.View constructor( - readonly schema: FormSchema, + readonly schema: Schema.ConstraintCodec, readonly context: Context.Context, readonly mutation: Mutation.Mutation< readonly [value: A, form: SubmittableForm], - MA, ME, MR, MP + MA, ME, MR >, readonly value: Lens.Lens, never, never, never, never>, readonly internalEncodedValue: Lens.Lens, - readonly issues: Lens.Lens, - readonly validationFiber: Lens.Lens>, never, never, never, never>, + readonly issues: Lens.Lens, + readonly validationFiber: Lens.Lens>, never, never, never, never>, + readonly runSemaphore: Semaphore.Semaphore, ) { super() - this.encodedValue = Lens.make({ - get: Lens.get(internalEncodedValue), - changes: internalEncodedValue.changes, - commit: encoded => Effect.andThen( - Lens.set(internalEncodedValue, encoded), - this.synchronizeEncodedValue(encoded), - ), - lock: Lens.asLensImpl(internalEncodedValue).lock, - }) - this.isValidating = View.map(validationFiber, Option.isSome) - const commitState = View.zipLatestAll( - value as any, - issues as any, - validationFiber as any, - mutation.result as any, - ) as unknown as View.View, - readonly Form.FormIssue[], - Option.Option>, - Result.Result, - ]> - this.canCommit = View.map( - commitState, - ([current, currentIssues, fiber, result]: readonly [Option.Option, readonly Form.FormIssue[], Option.Option>, Result.Result]) => Option.isSome(current) - && currentIssues.length === 0 - && Option.isNone(fiber) - && !(Result.isRunning(result) || Result.hasRefreshingFlag(result)), - ) - this.isCommitting = View.map( - mutation.result, - result => Result.isRunning(result) || Result.hasRefreshingFlag(result), - ) - } - synchronizeEncodedValue(encodedValue: I): Effect.Effect { - const self = this - return Effect.gen(function*() { - const current = yield* Lens.get(self.validationFiber) - if (Option.isSome(current)) yield* Fiber.interrupt(current.value) - const fiber = yield* Effect.forkScoped( - Effect.ensuring( - SchemaParser.decodeEffect(self.schema)(encodedValue), - Lens.set(self.validationFiber, Option.none()), + this.encodedValue = Effect.all([ + Effect.succeed(this), + Effect.succeed(Lens.asLensImpl(this.internalEncodedValue)), + ]).pipe( + Effect.map(([self, parent]) => Lens.make({ + get: parent.get, + get changes() { return parent.changes }, + commit: a => Effect.andThen( + Effect.flatMap( + parent.resolve, + resolved => resolved.commit(Effect.succeed(a)), + ), + self.synchronizeEncodedValue(a), ), - ) - yield* Lens.set(self.validationFiber, Option.some(fiber)) - const decoded = yield* Fiber.join(fiber).pipe( - Effect.tap(value => Effect.andThen( - Lens.set(self.issues, Array.empty()), - Lens.set(self.value, Option.some(value)), - )), - Effect.catchIf(SchemaIssue.isIssue, issue => Lens.set(self.issues, formatIssue(issue))), - ) - void decoded - }).pipe(Effect.provide(this.context)) as Effect.Effect + lock: parent.lock, + })), + Lens.unwrap, + ) + this.isValidating = Effect.succeed(this).pipe( + Effect.map(self => View.map(self.validationFiber, Option.isSome)), + View.unwrap, + ) + this.canCommit = Effect.succeed(this).pipe( + Effect.map(self => View.map( + View.zipLatestAll(self.value, self.issues, self.validationFiber, self.mutation.state), + ([value, issues, validationFiber, result]) => ( + Option.isSome(value) && + Array.isReadonlyArrayEmpty(issues) && + Option.isNone(validationFiber) && + !AsyncResult.isWaiting(result) + ), + )), + View.unwrap, + ) + this.isCommitting = Effect.succeed(this).pipe( + Effect.map(self => View.map(self.mutation.state, AsyncResult.isWaiting)), + View.unwrap, + ) } - get run(): Effect.Effect { - return Effect.flatMap( - Lens.get(this.encodedValue), - SchemaParser.decodeEffect(this.schema), - ).pipe( + synchronizeEncodedValue(encodedValue: I): Effect.Effect { + return Lens.get(this.validationFiber).pipe( + Effect.andThen(Option.match({ + onSome: Fiber.interrupt, + onNone: () => Effect.void, + })), + Effect.andThen(Effect.forkScoped( + Effect.ensuring( + Schema.decodeEffect(this.schema, { errors: "all" })(encodedValue), + Lens.set(this.validationFiber, Option.none()), + ) + )), + Effect.tap(fiber => Lens.set(this.validationFiber, Option.some(fiber))), + Effect.flatMap(Fiber.join), + + Effect.tap(() => Lens.set(this.issues, Array.empty())), + Effect.flatMap(value => Lens.set(this.value, Option.some(value))), + Effect.catchIf( + SchemaError.isSchemaError, + error => Lens.set(this.issues, SchemaIssue.makeFormatterStandardSchemaV1()(error.issue).issues), + ), + + Effect.provide(this.context), + ) + } + + get run(): Effect.Effect { + return Lens.get(this.encodedValue).pipe( + Effect.flatMap(v => Schema.decodeEffect(this.schema)(v)), Effect.option, - Effect.flatMap(value => Lens.set(this.value, value)), + Effect.flatMap(v => Lens.set(this.value, v)), Effect.provide(this.context), this.runSemaphore.withPermits(1), ) } - get submit(): Effect.Effect>, Cause.NoSuchElementError> { - return Effect.flatMap(Lens.get(this.value), value => Effect.flatMap(Effect.fromOption(value), decoded => this.submitValue(decoded))) + get submit(): Effect.Effect | AsyncResult.Failure>, Cause.NoSuchElementError, never> { + return Lens.get(this.value).pipe( + Effect.flatMap(Effect.fromOption), + Effect.flatMap(value => this.submitValue(value)), + ) } - submitValue(value: A): Effect.Effect>> { - return Effect.flatMap(this.canCommit.get, canCommit => { - if (!canCommit) return Effect.succeed(Option.none()) - return Effect.map( - Effect.tap(this.mutation.mutate([value, this as any]), result => { - if (!Result.isFailure(result)) return Effect.succeed(undefined) - const issue = Cause.findErrorOption(result.cause) - return Option.isSome(issue) && SchemaIssue.isIssue(issue.value) - ? Lens.set(this.issues, formatIssue(issue.value)) - : Effect.succeed(undefined) - }), - Option.some, - ) - }) + submitValue(value: A): Effect.Effect | AsyncResult.Failure>, never, never> { + return Effect.when( + Effect.tap( + this.mutation.mutate([value, this as any]), + result => AsyncResult.isFailure(result) + ? Option.match( + Array.findFirst( + result.cause.reasons, + reason => Cause.isFailReason(reason) && SchemaError.isSchemaError(reason.error) + ? Option.some(reason.error) + : Option.none(), + ), + { + onSome: error => Lens.set(this.issues, SchemaIssue.makeFormatterStandardSchemaV1()(error.issue).issues), + onNone: () => Effect.void, + }, + ) + : Effect.void, + ), + View.get(this.canCommit), + ) } } -const formatIssue = (issue: SchemaIssue.Issue): readonly Form.FormIssue[] => { - const formatted = SchemaIssue.makeFormatterStandardSchemaV1()(issue) - return formatted.issues.map(item => ({ - path: (item.path ?? []) as readonly PropertyKey[], - message: item.message, - })) -} +export const isSubmittableForm = (u: unknown): u is SubmittableForm => Predicate.hasProperty(u, SubmittableFormTypeId) -export const isSubmittableForm = (u: unknown): u is SubmittableForm => Predicate.hasProperty(u, SubmittableFormTypeId) export declare namespace make { - export interface Options + export interface Options extends Mutation.make.Options< readonly [value: NoInfer, form: SubmittableForm, NoInfer, NoInfer, unknown, unknown, unknown>], - MA, ME, MR, MP + MA, ME, MR > { - readonly schema: FormSchema + readonly schema: Schema.ConstraintCodec readonly initialEncodedValue: NoInfer } } -export const make = Effect.fnUntraced(function* ( - options: make.Options, +export const make = Effect.fnUntraced(function* ( + options: make.Options ): Effect.fn.Return< - SubmittableForm, MP>, + SubmittableForm, never, - Scope.Scope | R | Result.forkEffect.OutputContext + Scope.Scope | R | MR > { return new SubmittableFormImpl( options.schema, - yield* Effect.context(), + yield* Effect.context(), yield* Mutation.make(options), + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none())), Lens.fromSubscriptionRef(yield* SubscriptionRef.make(options.initialEncodedValue)), - Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Array.empty())), - Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none>())), + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Array.empty())), + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none>())), + yield* Semaphore.make(1), ) }) export declare namespace service { - export interface Options - extends make.Options {} + export interface Options + extends make.Options {} } export const service = ( - options: service.Options, + options: service.Options ): Effect.Effect< - SubmittableForm, MP>, + SubmittableForm, never, - Scope.Scope | R | Result.forkEffect.OutputContext -> => Effect.tap(make(options), form => Effect.asVoid(Effect.forkScoped(form.run))) + Scope.Scope | R | MR +> => Effect.tap( + make(options), + form => Effect.forkScoped(form.run), +) -- 2.52.0 From feced4732f49d1f15b8ec77e5c853b0231f86949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 19 Jul 2026 02:43:48 +0200 Subject: [PATCH 053/133] Refactor Form subtypes --- .../effect-fc-next/src/SubmittableForm.ts | 46 +-- .../effect-fc-next/src/SynchronizedForm.ts | 272 +++++++++--------- 2 files changed, 160 insertions(+), 158 deletions(-) diff --git a/packages/effect-fc-next/src/SubmittableForm.ts b/packages/effect-fc-next/src/SubmittableForm.ts index 122c9a3..0ba9a56 100644 --- a/packages/effect-fc-next/src/SubmittableForm.ts +++ b/packages/effect-fc-next/src/SubmittableForm.ts @@ -10,14 +10,14 @@ import * as View from "./View.js" export const SubmittableFormTypeId: unique symbol = Symbol.for("@effect-fc/Form/SubmittableForm") export type SubmittableFormTypeId = typeof SubmittableFormTypeId -export interface SubmittableForm +export interface SubmittableForm extends Form.Form { readonly [SubmittableFormTypeId]: SubmittableFormTypeId - readonly schema: Schema.ConstraintCodec - readonly context: Context.Context + readonly schema: Schema.ConstraintCodec + readonly context: Context.Context readonly mutation: Mutation.Mutation< - readonly [value: A, form: SubmittableForm], + readonly [value: A, form: SubmittableForm], MA, ME, MR > readonly validationFiber: View.View>, never, never> @@ -26,8 +26,8 @@ extends Form.Form { readonly submit: Effect.Effect | AsyncResult.Failure>, Cause.NoSuchElementError> } -export class SubmittableFormImpl -extends Pipeable.Class implements SubmittableForm { +export class SubmittableFormImpl +extends Pipeable.Class implements SubmittableForm { readonly [Form.FormTypeId]: Form.FormTypeId = Form.FormTypeId readonly [SubmittableFormTypeId]: SubmittableFormTypeId = SubmittableFormTypeId @@ -39,10 +39,10 @@ extends Pipeable.Class implements SubmittableForm { readonly isCommitting: View.View constructor( - readonly schema: Schema.ConstraintCodec, - readonly context: Context.Context, + readonly schema: Schema.ConstraintCodec, + readonly context: Context.Context, readonly mutation: Mutation.Mutation< - readonly [value: A, form: SubmittableForm], + readonly [value: A, form: SubmittableForm], MA, ME, MR >, readonly value: Lens.Lens, never, never, never, never>, @@ -165,26 +165,26 @@ export const isSubmittableForm = (u: unknown): u is SubmittableForm + export interface Options extends Mutation.make.Options< - readonly [value: NoInfer, form: SubmittableForm, NoInfer, NoInfer, unknown, unknown, unknown>], + readonly [value: NoInfer, form: SubmittableForm, NoInfer, NoInfer, NoInfer, unknown, unknown, unknown>], MA, ME, MR > { - readonly schema: Schema.ConstraintCodec + readonly schema: Schema.ConstraintCodec readonly initialEncodedValue: NoInfer } } -export const make = Effect.fnUntraced(function* ( - options: make.Options +export const make = Effect.fnUntraced(function* ( + options: make.Options ): Effect.fn.Return< - SubmittableForm, + SubmittableForm, never, - Scope.Scope | R | MR + Scope.Scope | RD | RE | MR > { return new SubmittableFormImpl( options.schema, - yield* Effect.context(), + yield* Effect.context(), yield* Mutation.make(options), Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none())), @@ -197,16 +197,16 @@ export const make = Effect.fnUntraced(function* - extends make.Options {} + export interface Options + extends make.Options {} } -export const service = ( - options: service.Options +export const service = ( + options: service.Options ): Effect.Effect< - SubmittableForm, + SubmittableForm, never, - Scope.Scope | R | MR + Scope.Scope | RD | RE | MR > => Effect.tap( make(options), form => Effect.forkScoped(form.run), diff --git a/packages/effect-fc-next/src/SynchronizedForm.ts b/packages/effect-fc-next/src/SynchronizedForm.ts index 96d6915..4cccb9f 100644 --- a/packages/effect-fc-next/src/SynchronizedForm.ts +++ b/packages/effect-fc-next/src/SynchronizedForm.ts @@ -1,204 +1,206 @@ -import { - Array, - type Context, - Effect, - Equal, - Fiber, - Option, - Pipeable, - Predicate, - Schema, - SchemaIssue, - SchemaParser, - type Scope, - Semaphore, - Stream, - SubscriptionRef, -} from "effect" +import type { StandardSchemaV1 } from "@standard-schema/spec" +import { Array, type Context, Effect, Equal, Fiber, Option, Pipeable, Predicate, Schema, SchemaIssue, type Scope, Semaphore, Stream, SubscriptionRef } from "effect" import * as Form from "./Form.js" import * as Lens from "./Lens.js" import * as View from "./View.js" -type FormSchema = Schema.Top & { - readonly Type: A - readonly Encoded: I - readonly DecodingServices: R - readonly EncodingServices: R -} - export const SynchronizedFormTypeId: unique symbol = Symbol.for("@effect-fc/Form/SynchronizedForm") export type SynchronizedFormTypeId = typeof SynchronizedFormTypeId -export interface SynchronizedForm< - in out A, - in out I = A, - in out R = never, - in out TER = never, - in out TEW = never, - in out TRR = never, - in out TRW = never, -> extends Form.Form { +export interface SynchronizedForm +extends Form.Form { readonly [SynchronizedFormTypeId]: SynchronizedFormTypeId - readonly schema: FormSchema - readonly context: Context.Context + + readonly schema: Schema.ConstraintCodec + readonly context: Context.Context readonly target: Lens.Lens - readonly validationFiber: View.View>, never, never> + readonly validationFiber: View.View>, never, never> + readonly run: Effect.Effect } -export class SynchronizedFormImpl< - in out A, - in out I = A, - in out R = never, - in out TER = never, - in out TEW = never, - in out TRR = never, - in out TRW = never, -> extends Pipeable.Class implements SynchronizedForm { +export class SynchronizedFormImpl +extends Pipeable.Class implements SynchronizedForm { readonly [Form.FormTypeId]: Form.FormTypeId = Form.FormTypeId readonly [SynchronizedFormTypeId]: SynchronizedFormTypeId = SynchronizedFormTypeId + readonly path = [] as const - readonly value: View.View, TER, never> + + readonly value: View.View, never, never> readonly encodedValue: Lens.Lens readonly isValidating: View.View readonly canCommit: View.View constructor( - readonly schema: FormSchema, - readonly context: Context.Context, + readonly schema: Schema.ConstraintCodec, + readonly context: Context.Context, readonly target: Lens.Lens, + readonly internalEncodedValue: Lens.Lens, - readonly issues: Lens.Lens, - readonly validationFiber: Lens.Lens>, never, never, never, never>, - readonly isCommitting: Lens.Lens, + readonly issues: Lens.Lens, + readonly validationFiber: Lens.Lens>, never, never, never, never>, + readonly isCommitting: Lens.Lens, + readonly runSemaphore: Semaphore.Semaphore, ) { super() - this.value = View.make({ - get: Effect.provide(Effect.map(target.get, Option.some), context), - changes: Stream.provideContext( - target.changes.pipe( - Stream.map(Option.some), - Stream.catchCause(() => Stream.make(Option.none())), + + this.value = Effect.succeed(this).pipe( + Effect.map(self => View.make({ + get: Effect.provide(Effect.option(self.target.get), self.context), + get changes() { + return Stream.provideContext( + self.target.changes.pipe( + Stream.map(Option.some), + Stream.catch(() => Stream.make(Option.none())), + ), + self.context, + ) + }, + })), + View.unwrap, + ) + this.encodedValue = Effect.all([ + Effect.succeed(this), + Effect.succeed(Lens.asLensImpl(this.internalEncodedValue)), + ]).pipe( + Effect.map(([self, parent]) => Lens.make({ + get: parent.get, + get changes() { return parent.changes }, + commit: a => Effect.andThen( + Effect.flatMap( + parent.resolve, + resolved => resolved.commit(Effect.succeed(a)), + ), + self.synchronizeEncodedValue(a), ), - context, - ), - }) - this.encodedValue = Lens.make({ - get: Lens.get(internalEncodedValue), - changes: internalEncodedValue.changes, - commit: encoded => Effect.andThen( - Lens.set(internalEncodedValue, encoded), - this.synchronizeEncodedValue(encoded), - ), - lock: Lens.asLensImpl(internalEncodedValue).lock, - }) as unknown as Lens.Lens - this.isValidating = View.map(validationFiber, Option.isSome) - const commitState = View.zipLatestAll(issues as any, validationFiber as any, isCommitting as any) as unknown as View.View>, - boolean, - ]> - this.canCommit = View.map( - commitState, - ([currentIssues, fiber, committing]) => currentIssues.length === 0 && Option.isNone(fiber) && !committing, + lock: parent.lock, + })), + Lens.unwrap, + ) + this.isValidating = Effect.succeed(this).pipe( + Effect.map(self => View.map(self.validationFiber, Option.isSome)), + View.unwrap, + ) + this.canCommit = Effect.succeed(this).pipe( + Effect.map(self => View.map( + View.zipLatestAll(self.issues, self.validationFiber, self.isCommitting), + ([issues, validationFiber, isCommitting]) => ( + Array.isReadonlyArrayEmpty(issues) && + Option.isNone(validationFiber) && + !isCommitting + ), + )), + View.unwrap, ) } - synchronizeEncodedValue(encodedValue: I): Effect.Effect { - const self = this - return Effect.gen(function*() { - const current = yield* Lens.get(self.validationFiber) - if (Option.isSome(current)) yield* Fiber.interrupt(current.value) - const fiber = yield* Effect.forkScoped( + synchronizeEncodedValue(encodedValue: I): Effect.Effect { + return Lens.get(this.validationFiber).pipe( + Effect.andThen(Option.match({ + onSome: Fiber.interrupt, + onNone: () => Effect.void, + })), + Effect.andThen(Effect.forkScoped( Effect.ensuring( - SchemaParser.decodeEffect(self.schema)(encodedValue), - Lens.set(self.validationFiber, Option.none()), + Schema.decodeEffect(this.schema, { errors: "all" })(encodedValue), + Lens.set(this.validationFiber, Option.none()), + ) + )), + Effect.tap(fiber => Lens.set(this.validationFiber, Option.some(fiber))), + Effect.flatMap(Fiber.join), + + Effect.flatMap(value => Effect.ensuring( + Lens.set(this.isCommitting, true).pipe( + Effect.andThen(Lens.set(this.issues, Array.empty())), + Effect.andThen(Lens.set(this.target, value)), ), - ) - yield* Lens.set(self.validationFiber, Option.some(fiber)) - yield* Fiber.join(fiber).pipe( - Effect.flatMap(value => Effect.ensuring( - Effect.andThen( - Lens.set(self.isCommitting, true), - Effect.andThen(Lens.set(self.issues, Array.empty()), Lens.set(self.target, value)), - ), - Lens.set(self.isCommitting, false), - )), - Effect.catchIf(SchemaIssue.isIssue, issue => Lens.set(self.issues, formatIssue(issue))), - ) - }).pipe(Effect.provide(this.context)) as Effect.Effect + Lens.set(this.isCommitting, false), + )), + Effect.catchIf( + Schema.isSchemaError, + error => Lens.set(this.issues, SchemaIssue.makeFormatterStandardSchemaV1()(error.issue).issues), + ), + + Effect.provide(this.context), + ) } - get run(): Effect.Effect { + get run(): Effect.Effect { return this.runSemaphore.withPermits(1)(Effect.provide( - Stream.runForEach(Stream.drop(this.target.changes, 1), targetValue => Effect.ignore( - Effect.flatMap(SchemaParser.encodeEffect(this.schema)(targetValue), encodedValue => Effect.flatMap( - Lens.get(this.internalEncodedValue), - current => Equal.equals(encodedValue, current) - ? Effect.succeed(undefined) - : Effect.andThen( + Stream.runForEach( + Stream.drop(Lens.changes(this.target), 1), + targetValue => Schema.encodeEffect(this.schema, { errors: "all" })(targetValue).pipe( + Effect.flatMap(encodedValue => Effect.when( + Effect.andThen( Lens.set(this.issues, Array.empty()), Lens.set(this.internalEncodedValue, encodedValue), ), - )), - )), + Effect.map( + Lens.get(this.internalEncodedValue), + currentEncodedValue => !Equal.equals(encodedValue, currentEncodedValue), + ), + )), + Effect.ignore, + ), + ), this.context, )) } } -const formatIssue = (issue: SchemaIssue.Issue): readonly Form.FormIssue[] => { - const formatted = SchemaIssue.makeFormatterStandardSchemaV1()(issue) - return formatted.issues.map(item => ({ - path: (item.path ?? []) as readonly PropertyKey[], - message: item.message, - })) -} - export const isSynchronizedForm = (u: unknown): u is SynchronizedForm => Predicate.hasProperty(u, SynchronizedFormTypeId) + export declare namespace make { - export interface Options { - readonly schema: FormSchema + export interface Options { + readonly schema: Schema.ConstraintCodec readonly target: Lens.Lens readonly initialEncodedValue?: NoInfer } } -export const make = Effect.fnUntraced(function* ( - options: make.Options, +export const make = Effect.fnUntraced(function* ( + options: make.Options ): Effect.fn.Return< - SynchronizedForm, - SchemaIssue.Issue | TER, - Scope.Scope | R | TRR | TRW + SynchronizedForm, + Schema.SchemaError | TER, + Scope.Scope | RD | RE | TRR | TRW > { const initialEncodedValue = options.initialEncodedValue !== undefined ? options.initialEncodedValue - : yield* Effect.flatMap(Lens.get(options.target), SchemaParser.encodeEffect(options.schema)) + : yield* Effect.flatMap( + Lens.get(options.target), + Schema.encodeEffect(options.schema), + ) return new SynchronizedFormImpl( options.schema, - yield* Effect.context(), + yield* Effect.context(), options.target, + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(initialEncodedValue)), - Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Array.empty())), - Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none>())), + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Array.empty())), + Lens.fromSubscriptionRef(yield* SubscriptionRef.make(Option.none>())), Lens.fromSubscriptionRef(yield* SubscriptionRef.make(false)), + yield* Semaphore.make(1), ) }) export declare namespace service { - export interface Options - extends make.Options {} + export interface Options + extends make.Options {} } -export const service = ( - options: service.Options, +export const service = ( + options: service.Options ): Effect.Effect< - SynchronizedForm, - SchemaIssue.Issue | TER, - Scope.Scope | R | TRR | TRW -> => Effect.tap(make(options), form => Effect.asVoid(Effect.forkScoped(form.run))) + SynchronizedForm, + Schema.SchemaError | TER, + Scope.Scope | RD | RE | TRR | TRW +> => Effect.tap( + make(options), + form => Effect.forkScoped(form.run), +) -- 2.52.0 From 985556b4303001516ac3710ff19e0ecb793c2d76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 19 Jul 2026 02:50:37 +0200 Subject: [PATCH 054/133] Fix --- packages/effect-fc-next/src/Form.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/effect-fc-next/src/Form.ts b/packages/effect-fc-next/src/Form.ts index 2ff3fba..c76f3b8 100644 --- a/packages/effect-fc-next/src/Form.ts +++ b/packages/effect-fc-next/src/Form.ts @@ -179,7 +179,7 @@ export const useInput = Effect.fnUntraced(function*

Effect.when( Lens.set(internalValueLens, upstreamEncodedValue), Effect.map(Lens.get(internalValueLens), internalValue => !Equal.equals(upstreamEncodedValue, internalValue)), @@ -187,7 +187,7 @@ export const useInput = Effect.fnUntraced(function*

Effect.when( Option.match(upstreamEncodedValue, { @@ -257,7 +257,7 @@ export const useOptionalInput = Effect.fnUntraced(function*

Date: Sun, 19 Jul 2026 02:56:32 +0200 Subject: [PATCH 055/133] Fix --- packages/effect-fc-next/src/Form.ts | 6 +++--- packages/effect-fc-next/src/SubmittableForm.ts | 10 +++++----- packages/effect-fc-next/src/SynchronizedForm.ts | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/effect-fc-next/src/Form.ts b/packages/effect-fc-next/src/Form.ts index c76f3b8..ce404eb 100644 --- a/packages/effect-fc-next/src/Form.ts +++ b/packages/effect-fc-next/src/Form.ts @@ -9,7 +9,7 @@ import * as View from "./View.js" export const FormTypeId: unique symbol = Symbol.for("@effect-fc/Form/Form") export type FormTypeId = typeof FormTypeId -export interface Form +export interface Form extends Pipeable.Pipeable { readonly [FormTypeId]: FormTypeId @@ -22,7 +22,7 @@ extends Pipeable.Pipeable { readonly isCommitting: View.View } -export class FormImpl +export class FormImpl extends Pipeable.Class implements Form { readonly [FormTypeId]: FormTypeId = FormTypeId @@ -39,7 +39,7 @@ extends Pipeable.Class implements Form { } } -export const isForm = (u: unknown): u is Form => Predicate.hasProperty(u, FormTypeId) +export const isForm = (u: unknown): u is Form => Predicate.hasProperty(u, FormTypeId) const filterIssuesByPath = ( diff --git a/packages/effect-fc-next/src/SubmittableForm.ts b/packages/effect-fc-next/src/SubmittableForm.ts index 0ba9a56..68604d7 100644 --- a/packages/effect-fc-next/src/SubmittableForm.ts +++ b/packages/effect-fc-next/src/SubmittableForm.ts @@ -10,7 +10,7 @@ import * as View from "./View.js" export const SubmittableFormTypeId: unique symbol = Symbol.for("@effect-fc/Form/SubmittableForm") export type SubmittableFormTypeId = typeof SubmittableFormTypeId -export interface SubmittableForm +export interface SubmittableForm extends Form.Form { readonly [SubmittableFormTypeId]: SubmittableFormTypeId @@ -26,7 +26,7 @@ extends Form.Form { readonly submit: Effect.Effect | AsyncResult.Failure>, Cause.NoSuchElementError> } -export class SubmittableFormImpl +export class SubmittableFormImpl extends Pipeable.Class implements SubmittableForm { readonly [Form.FormTypeId]: Form.FormTypeId = Form.FormTypeId readonly [SubmittableFormTypeId]: SubmittableFormTypeId = SubmittableFormTypeId @@ -161,11 +161,11 @@ extends Pipeable.Class implements SubmittableForm { } } -export const isSubmittableForm = (u: unknown): u is SubmittableForm => Predicate.hasProperty(u, SubmittableFormTypeId) +export const isSubmittableForm = (u: unknown): u is SubmittableForm => Predicate.hasProperty(u, SubmittableFormTypeId) export declare namespace make { - export interface Options + export interface Options extends Mutation.make.Options< readonly [value: NoInfer, form: SubmittableForm, NoInfer, NoInfer, NoInfer, unknown, unknown, unknown>], MA, ME, MR @@ -197,7 +197,7 @@ export const make = Effect.fnUntraced(function* + export interface Options extends make.Options {} } diff --git a/packages/effect-fc-next/src/SynchronizedForm.ts b/packages/effect-fc-next/src/SynchronizedForm.ts index 4cccb9f..6af4f58 100644 --- a/packages/effect-fc-next/src/SynchronizedForm.ts +++ b/packages/effect-fc-next/src/SynchronizedForm.ts @@ -8,7 +8,7 @@ import * as View from "./View.js" export const SynchronizedFormTypeId: unique symbol = Symbol.for("@effect-fc/Form/SynchronizedForm") export type SynchronizedFormTypeId = typeof SynchronizedFormTypeId -export interface SynchronizedForm +export interface SynchronizedForm extends Form.Form { readonly [SynchronizedFormTypeId]: SynchronizedFormTypeId @@ -20,7 +20,7 @@ extends Form.Form { readonly run: Effect.Effect } -export class SynchronizedFormImpl +export class SynchronizedFormImpl extends Pipeable.Class implements SynchronizedForm { readonly [Form.FormTypeId]: Form.FormTypeId = Form.FormTypeId readonly [SynchronizedFormTypeId]: SynchronizedFormTypeId = SynchronizedFormTypeId @@ -150,11 +150,11 @@ extends Pipeable.Class implements SynchronizedForm => Predicate.hasProperty(u, SynchronizedFormTypeId) +export const isSynchronizedForm = (u: unknown): u is SynchronizedForm => Predicate.hasProperty(u, SynchronizedFormTypeId) export declare namespace make { - export interface Options { + export interface Options { readonly schema: Schema.ConstraintCodec readonly target: Lens.Lens readonly initialEncodedValue?: NoInfer @@ -190,7 +190,7 @@ export const make = Effect.fnUntraced(function* + export interface Options extends make.Options {} } -- 2.52.0 From be1038fc8d5534761de151408fa3317bdbd8f7d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 19 Jul 2026 03:01:38 +0200 Subject: [PATCH 056/133] Rename Form subtypes --- .../src/{SynchronizedForm.ts => LensForm.ts} | 22 +++++++-------- .../{SubmittableForm.ts => MutationForm.ts} | 28 +++++++++---------- packages/effect-fc-next/src/index.ts | 4 +-- 3 files changed, 27 insertions(+), 27 deletions(-) rename packages/effect-fc-next/src/{SynchronizedForm.ts => LensForm.ts} (87%) rename packages/effect-fc-next/src/{SubmittableForm.ts => MutationForm.ts} (85%) diff --git a/packages/effect-fc-next/src/SynchronizedForm.ts b/packages/effect-fc-next/src/LensForm.ts similarity index 87% rename from packages/effect-fc-next/src/SynchronizedForm.ts rename to packages/effect-fc-next/src/LensForm.ts index 6af4f58..576b486 100644 --- a/packages/effect-fc-next/src/SynchronizedForm.ts +++ b/packages/effect-fc-next/src/LensForm.ts @@ -5,12 +5,12 @@ import * as Lens from "./Lens.js" import * as View from "./View.js" -export const SynchronizedFormTypeId: unique symbol = Symbol.for("@effect-fc/Form/SynchronizedForm") -export type SynchronizedFormTypeId = typeof SynchronizedFormTypeId +export const LensFormTypeId: unique symbol = Symbol.for("@effect-fc/Form/LensForm") +export type LensFormTypeId = typeof LensFormTypeId -export interface SynchronizedForm +export interface LensForm extends Form.Form { - readonly [SynchronizedFormTypeId]: SynchronizedFormTypeId + readonly [LensFormTypeId]: LensFormTypeId readonly schema: Schema.ConstraintCodec readonly context: Context.Context @@ -20,10 +20,10 @@ extends Form.Form { readonly run: Effect.Effect } -export class SynchronizedFormImpl -extends Pipeable.Class implements SynchronizedForm { +export class LensFormImpl +extends Pipeable.Class implements LensForm { readonly [Form.FormTypeId]: Form.FormTypeId = Form.FormTypeId - readonly [SynchronizedFormTypeId]: SynchronizedFormTypeId = SynchronizedFormTypeId + readonly [LensFormTypeId]: LensFormTypeId = LensFormTypeId readonly path = [] as const @@ -150,7 +150,7 @@ extends Pipeable.Class implements SynchronizedForm => Predicate.hasProperty(u, SynchronizedFormTypeId) +export const isLensForm = (u: unknown): u is LensForm => Predicate.hasProperty(u, LensFormTypeId) export declare namespace make { @@ -164,7 +164,7 @@ export declare namespace make { export const make = Effect.fnUntraced(function* ( options: make.Options ): Effect.fn.Return< - SynchronizedForm, + LensForm, Schema.SchemaError | TER, Scope.Scope | RD | RE | TRR | TRW > { @@ -175,7 +175,7 @@ export const make = Effect.fnUntraced(function* (), options.target, @@ -197,7 +197,7 @@ export declare namespace service { export const service = ( options: service.Options ): Effect.Effect< - SynchronizedForm, + LensForm, Schema.SchemaError | TER, Scope.Scope | RD | RE | TRR | TRW > => Effect.tap( diff --git a/packages/effect-fc-next/src/SubmittableForm.ts b/packages/effect-fc-next/src/MutationForm.ts similarity index 85% rename from packages/effect-fc-next/src/SubmittableForm.ts rename to packages/effect-fc-next/src/MutationForm.ts index 68604d7..55d5ebc 100644 --- a/packages/effect-fc-next/src/SubmittableForm.ts +++ b/packages/effect-fc-next/src/MutationForm.ts @@ -7,17 +7,17 @@ import * as Mutation from "./Mutation.js" import * as View from "./View.js" -export const SubmittableFormTypeId: unique symbol = Symbol.for("@effect-fc/Form/SubmittableForm") -export type SubmittableFormTypeId = typeof SubmittableFormTypeId +export const MutationFormTypeId: unique symbol = Symbol.for("@effect-fc/Form/MutationForm") +export type MutationFormTypeId = typeof MutationFormTypeId -export interface SubmittableForm +export interface MutationForm extends Form.Form { - readonly [SubmittableFormTypeId]: SubmittableFormTypeId + readonly [MutationFormTypeId]: MutationFormTypeId readonly schema: Schema.ConstraintCodec readonly context: Context.Context readonly mutation: Mutation.Mutation< - readonly [value: A, form: SubmittableForm], + readonly [value: A, form: MutationForm], MA, ME, MR > readonly validationFiber: View.View>, never, never> @@ -26,10 +26,10 @@ extends Form.Form { readonly submit: Effect.Effect | AsyncResult.Failure>, Cause.NoSuchElementError> } -export class SubmittableFormImpl -extends Pipeable.Class implements SubmittableForm { +export class MutationFormImpl +extends Pipeable.Class implements MutationForm { readonly [Form.FormTypeId]: Form.FormTypeId = Form.FormTypeId - readonly [SubmittableFormTypeId]: SubmittableFormTypeId = SubmittableFormTypeId + readonly [MutationFormTypeId]: MutationFormTypeId = MutationFormTypeId readonly path = [] as const @@ -42,7 +42,7 @@ extends Pipeable.Class implements SubmittableForm { readonly schema: Schema.ConstraintCodec, readonly context: Context.Context, readonly mutation: Mutation.Mutation< - readonly [value: A, form: SubmittableForm], + readonly [value: A, form: MutationForm], MA, ME, MR >, readonly value: Lens.Lens, never, never, never, never>, @@ -161,13 +161,13 @@ extends Pipeable.Class implements SubmittableForm { } } -export const isSubmittableForm = (u: unknown): u is SubmittableForm => Predicate.hasProperty(u, SubmittableFormTypeId) +export const isMutationForm = (u: unknown): u is MutationForm => Predicate.hasProperty(u, MutationFormTypeId) export declare namespace make { export interface Options extends Mutation.make.Options< - readonly [value: NoInfer, form: SubmittableForm, NoInfer, NoInfer, NoInfer, unknown, unknown, unknown>], + readonly [value: NoInfer, form: MutationForm, NoInfer, NoInfer, NoInfer, unknown, unknown, unknown>], MA, ME, MR > { readonly schema: Schema.ConstraintCodec @@ -178,11 +178,11 @@ export declare namespace make { export const make = Effect.fnUntraced(function* ( options: make.Options ): Effect.fn.Return< - SubmittableForm, + MutationForm, never, Scope.Scope | RD | RE | MR > { - return new SubmittableFormImpl( + return new MutationFormImpl( options.schema, yield* Effect.context(), yield* Mutation.make(options), @@ -204,7 +204,7 @@ export declare namespace service { export const service = ( options: service.Options ): Effect.Effect< - SubmittableForm, + MutationForm, never, Scope.Scope | RD | RE | MR > => Effect.tap( diff --git a/packages/effect-fc-next/src/index.ts b/packages/effect-fc-next/src/index.ts index ca7e03d..b114c1c 100644 --- a/packages/effect-fc-next/src/index.ts +++ b/packages/effect-fc-next/src/index.ts @@ -3,8 +3,10 @@ export * as Component from "./Component.js" export * as ErrorObserver from "./ErrorObserver.js" export * as Form from "./Form.js" export * as Lens from "./Lens.js" +export * as LensForm from "./LensForm.js" export * as Memoized from "./Memoized.js" export * as Mutation from "./Mutation.js" +export * as MutationForm from "./MutationForm.js" export * as PubSub from "./PubSub.js" export * as Query from "./Query.js" export * as QueryClient from "./QueryClient.js" @@ -12,6 +14,4 @@ export * as ReactRuntime from "./ReactRuntime.js" export * as Result from "./Result.js" export * as SetStateAction from "./SetStateAction.js" export * as Stream from "./Stream.js" -export * as SubmittableForm from "./SubmittableForm.js" -export * as SynchronizedForm from "./SynchronizedForm.js" export * as View from "./View.js" -- 2.52.0 From 9b25ef4b57e7b882976bc8d6e4821b25e8d58cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 19 Jul 2026 03:15:55 +0200 Subject: [PATCH 057/133] Fix --- packages/effect-fc-next/src/PubSub.ts | 4 +- packages/effect-fc-next/src/QueryClient.ts | 4 +- packages/effect-fc-next/src/ReactRuntime.ts | 17 +- packages/effect-fc-next/src/Result.ts | 250 -------------------- packages/effect-fc-next/src/Stream.ts | 4 +- packages/effect-fc-next/src/index.ts | 1 - packages/effect-fc-next/src/utils.ts | 3 - 7 files changed, 10 insertions(+), 273 deletions(-) delete mode 100644 packages/effect-fc-next/src/Result.ts delete mode 100644 packages/effect-fc-next/src/utils.ts diff --git a/packages/effect-fc-next/src/PubSub.ts b/packages/effect-fc-next/src/PubSub.ts index 27401bc..17c44bc 100644 --- a/packages/effect-fc-next/src/PubSub.ts +++ b/packages/effect-fc-next/src/PubSub.ts @@ -3,6 +3,8 @@ import type * as React from "react" import * as Component from "./Component.js" +export * from "effect/PubSub" + export const useFromReactiveValues = Effect.fnUntraced(function* ( values: A ): Effect.fn.Return, never, Scope.Scope> { @@ -13,5 +15,3 @@ export const useFromReactiveValues = Effect.fnUntraced(function* Effect.forkScoped(client.run), ) +export const layer = (options?: service.Options) => Layer.effect(QueryClient, service(options)) + export const QueryClientCacheKeyTypeId: unique symbol = Symbol.for("@effect-fc/QueryClient/QueryClientCacheKey") export type QueryClientCacheKeyTypeId = typeof QueryClientCacheKeyTypeId diff --git a/packages/effect-fc-next/src/ReactRuntime.ts b/packages/effect-fc-next/src/ReactRuntime.ts index 4413e3e..c883af7 100644 --- a/packages/effect-fc-next/src/ReactRuntime.ts +++ b/packages/effect-fc-next/src/ReactRuntime.ts @@ -2,8 +2,6 @@ import { type Context, Effect, Layer, ManagedRuntime, Predicate } from "effect" import * as React from "react" import * as Component from "./Component.js" -import * as ErrorObserver from "./ErrorObserver.js" -import * as QueryClient from "./QueryClient.js" export const ReactRuntimeTypeId: unique symbol = Symbol.for("@effect-fc/ReactRuntime/ReactRuntime") @@ -16,18 +14,9 @@ export interface ReactRuntime { readonly context: React.Context> } -const ReactRuntimeProto = Object.freeze({ [ReactRuntimeTypeId]: ReactRuntimeTypeId } as const) - -export const preludeLayer: Layer.Layer< - | Component.ScopeMap - | ErrorObserver.ErrorObserver - | QueryClient.QueryClient -> = Layer.mergeAll( - Component.ScopeMap.layer, - ErrorObserver.layer, - QueryClient.QueryClient.Default, -) +const ReactRuntimePrototype = Object.freeze({ [ReactRuntimeTypeId]: ReactRuntimeTypeId } as const) +export const preludeLayer: Layer.Layer = Layer.mergeAll(Component.ScopeMap.layer) export const isReactRuntime = (u: unknown): u is ReactRuntime => Predicate.hasProperty(u, ReactRuntimeTypeId) @@ -43,7 +32,7 @@ export const make = ( // biome-ignore lint/style/noNonNullAssertion: context initialization context: React.createContext>(null!), }), - ReactRuntimeProto, + ReactRuntimePrototype, ) diff --git a/packages/effect-fc-next/src/Result.ts b/packages/effect-fc-next/src/Result.ts deleted file mode 100644 index 1c92d99..0000000 --- a/packages/effect-fc-next/src/Result.ts +++ /dev/null @@ -1,250 +0,0 @@ -import { Cause, Context, Data, Effect, Equal, Exit, type Fiber, Hash, Layer, Match, Pipeable, Predicate, pipe, type Scope, SubscriptionRef } from "effect" -import { Lens } from "effect-lens" -import type { View } from "effect-lens/View" - - -export const ResultTypeId: unique symbol = Symbol.for("@effect-fc/Result/Result") -export type ResultTypeId = typeof ResultTypeId - -export type Result = ( - | Initial - | Running

- | Final -) - -// biome-ignore lint/complexity/noBannedTypes: "{}" is relevant here -export type Final = (Success | Failure) & ({} | Flags

) -export type Flags

= WillFetch | WillRefresh | Refreshing

- -export declare namespace Result { - export type Success> = [R] extends [Result] ? A : never - export type Failure> = [R] extends [Result] ? E : never - export type Progress> = [R] extends [Result] ? P : never -} - -export declare namespace Flags { - export type Keys = keyof WillFetch & WillRefresh & Refreshing -} - -export interface Initial extends ResultPrototype { - readonly _tag: "Initial" -} - -export interface Running

extends ResultPrototype { - readonly _tag: "Running" - readonly progress: P -} - -export interface Success extends ResultPrototype { - readonly _tag: "Success" - readonly value: A -} - -export interface Failure extends ResultPrototype { - readonly _tag: "Failure" - readonly cause: Cause.Cause -} - -export interface WillFetch { - readonly _flag: "WillFetch" -} - -export interface WillRefresh { - readonly _flag: "WillRefresh" -} - -export interface Refreshing

{ - readonly _flag: "Refreshing" - readonly progress: P -} - - -export interface ResultPrototype extends Pipeable.Pipeable, Equal.Equal { - readonly [ResultTypeId]: ResultTypeId -} - -export const ResultPrototype: ResultPrototype = Object.freeze({ - ...Pipeable.Prototype, - [ResultTypeId]: ResultTypeId, - - [Equal.symbol](this: Result, that: Result): boolean { - if (this._tag !== that._tag || (this as Flags)._flag !== (that as Flags)._flag) - return false - if (hasRefreshingFlag(this) && !Equal.equals(this.progress, (that as Refreshing).progress)) - return false - return Match.value(this).pipe( - Match.tag("Initial", () => true), - Match.tag("Running", self => Equal.equals(self.progress, (that as Running).progress)), - Match.tag("Success", self => Equal.equals(self.value, (that as Success).value)), - Match.tag("Failure", self => Equal.equals(self.cause, (that as Failure).cause)), - Match.exhaustive, - ) - }, - - [Hash.symbol](this: Result): number { - return pipe(Hash.string(this._tag), - tagHash => Match.value(this).pipe( - Match.tag("Initial", () => tagHash), - Match.tag("Running", self => Hash.combine(Hash.hash(self.progress))(tagHash)), - Match.tag("Success", self => Hash.combine(Hash.hash(self.value))(tagHash)), - Match.tag("Failure", self => Hash.combine(Hash.hash(self.cause))(tagHash)), - Match.exhaustive, - ), - Hash.combine(Hash.hash((this as Flags)._flag)), - hash => hasRefreshingFlag(this) - ? Hash.combine(Hash.hash(this.progress))(hash) - : hash, - ) - }, -} as const) - - -export const isResult = (u: unknown): u is Result => Predicate.hasProperty(u, ResultTypeId) -export const isFinal = (u: unknown): u is Final => isResult(u) && (isSuccess(u) || isFailure(u)) -export const isInitial = (u: unknown): u is Initial => isResult(u) && u._tag === "Initial" -export const isRunning = (u: unknown): u is Running => isResult(u) && u._tag === "Running" -export const isSuccess = (u: unknown): u is Success => isResult(u) && u._tag === "Success" -export const isFailure = (u: unknown): u is Failure => isResult(u) && u._tag === "Failure" -export const hasFlag = (u: unknown): u is Flags => isResult(u) && Predicate.hasProperty(u, "_flag") -export const hasWillFetchFlag = (u: unknown): u is WillFetch => isResult(u) && Predicate.hasProperty(u, "_flag") && u._flag === "WillFetch" -export const hasWillRefreshFlag = (u: unknown): u is WillRefresh => isResult(u) && Predicate.hasProperty(u, "_flag") && u._flag === "WillRefresh" -export const hasRefreshingFlag = (u: unknown): u is Refreshing => isResult(u) && Predicate.hasProperty(u, "_flag") && u._flag === "Refreshing" - -export const initial: { - (): Initial - (): Result -} = (): Initial => Object.setPrototypeOf({ _tag: "Initial" }, ResultPrototype) -export const running =

(progress?: P): Running

=> Object.setPrototypeOf({ _tag: "Running", progress }, ResultPrototype) -export const succeed = (value: A): Success => Object.setPrototypeOf({ _tag: "Success", value }, ResultPrototype) -export const fail = (cause: Cause.Cause ): Failure => Object.setPrototypeOf({ _tag: "Failure", cause }, ResultPrototype) - -export const willFetch = >( - result: R -): Omit & WillFetch => Object.setPrototypeOf( - Object.assign({}, result, { _flag: "WillFetch" }), - Object.getPrototypeOf(result), -) - -export const willRefresh = >( - result: R -): Omit & WillRefresh => Object.setPrototypeOf( - Object.assign({}, result, { _flag: "WillRefresh" }), - Object.getPrototypeOf(result), -) - -export const refreshing = , P = never>( - result: R, - progress?: P, -): Omit & Refreshing

=> Object.setPrototypeOf( - Object.assign({}, result, { _flag: "Refreshing", progress }), - Object.getPrototypeOf(result), -) - -export const fromExit: { - (exit: Exit.Success): Success - (exit: Exit.Failure): Failure - (exit: Exit.Exit): Success | Failure -} = exit => (exit._tag === "Success" ? succeed(exit.value) : fail(exit.cause)) as any - -export const toExit: { - (self: Success): Exit.Success - (self: Failure): Exit.Failure - (self: Final): Exit.Exit - (self: Result): Exit.Exit -} = (self: Result): any => { - switch (self._tag) { - case "Success": - return Exit.succeed(self.value) - case "Failure": - return Exit.failCause(self.cause) - default: - return Exit.fail(new Cause.NoSuchElementError()) - } -} - - -export interface Progress

{ - readonly progress: Lens.Lens -} -export const Progress =

() => Context.Service>("@effect-fc/Result/Progress") - -export class PreviousResultNotRunningNorRefreshing extends Data.TaggedError("@effect-fc/Result/PreviousResultNotRunningNorRefreshing")<{ - readonly previous: Result -}> {} - -export const makeProgressLayer = ( - state: Lens.Lens, never, never, never, never> -): Layer.Layer | Progress, never, never> => Layer.succeed( - Progress

() as Context.Service | Progress, Progress

| Progress>, - { - progress: state.pipe( - Lens.mapEffect( - a => (isRunning(a) || hasRefreshingFlag(a)) - ? Effect.succeed(a) - : Effect.fail(new PreviousResultNotRunningNorRefreshing({ previous: a })), - (_, b) => Effect.succeed(b), - ), - Lens.map( - a => a.progress, - (a, b) => isRunning(a) - ? running(b) - : refreshing(a, b) as Final & Refreshing

, - ), - ) - }, -) - - -export namespace unsafeForkEffect { - export type OutputContext = Exclude | Progress> - - export interface Options { - readonly initial?: Initial | Final - readonly initialProgress?: P - } -} - -export const unsafeForkEffect = Effect.fnUntraced(function* ( - effect: Effect.Effect, - options?: unsafeForkEffect.Options, NoInfer, P>, -): Effect.fn.Return< - readonly [result: View, never, never>, fiber: Fiber.Fiber], - never, - Scope.Scope | unsafeForkEffect.OutputContext -> { - const state = Lens.fromSubscriptionRef(yield* SubscriptionRef.make>( - options?.initial ?? initial(), - )) - - const fiber = yield* Effect.gen(function*() { - yield* Lens.set( - state, - (isFinal(options?.initial) && hasWillRefreshFlag(options?.initial)) - ? refreshing(options.initial, options?.initialProgress) as Result - : running(options?.initialProgress), - ) - return yield* Effect.onExit(effect, exit => Lens.set(state, fromExit(exit))) - }).pipe( - Effect.forkScoped, - Effect.provide(makeProgressLayer(state)), - ) - - return [state, fiber] as const -}) - -export namespace forkEffect { - export type InputContext = R extends Progress ? [X] extends [P] ? R : never : R - export type OutputContext = unsafeForkEffect.OutputContext - export interface Options extends unsafeForkEffect.Options {} -} - -export const forkEffect: { - ( - effect: Effect.Effect>>, - options?: forkEffect.Options, NoInfer, P>, - ): Effect.Effect< - readonly [result: View, never, never>, fiber: Fiber.Fiber], - never, - Scope.Scope | forkEffect.OutputContext - > -} = unsafeForkEffect diff --git a/packages/effect-fc-next/src/Stream.ts b/packages/effect-fc-next/src/Stream.ts index 67bf82e..35af3f4 100644 --- a/packages/effect-fc-next/src/Stream.ts +++ b/packages/effect-fc-next/src/Stream.ts @@ -3,6 +3,8 @@ import * as React from "react" import * as Component from "./Component.js" +export * from "effect/Stream" + export const use: { ( stream: Stream.Stream @@ -29,5 +31,3 @@ export const use: { return reactStateValue as Option.Some }) - -export * from "effect/Stream" diff --git a/packages/effect-fc-next/src/index.ts b/packages/effect-fc-next/src/index.ts index b114c1c..33039ed 100644 --- a/packages/effect-fc-next/src/index.ts +++ b/packages/effect-fc-next/src/index.ts @@ -11,7 +11,6 @@ export * as PubSub from "./PubSub.js" export * as Query from "./Query.js" export * as QueryClient from "./QueryClient.js" export * as ReactRuntime from "./ReactRuntime.js" -export * as Result from "./Result.js" export * as SetStateAction from "./SetStateAction.js" export * as Stream from "./Stream.js" export * as View from "./View.js" diff --git a/packages/effect-fc-next/src/utils.ts b/packages/effect-fc-next/src/utils.ts deleted file mode 100644 index 44ed408..0000000 --- a/packages/effect-fc-next/src/utils.ts +++ /dev/null @@ -1,3 +0,0 @@ -export type ExcludeKeys = K extends keyof T ? ( - { [P in K]?: never } & Omit -) : T -- 2.52.0 From cbb050673be5ab86b8b8dc0bf5a35132fdebd0e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 19 Jul 2026 03:20:24 +0200 Subject: [PATCH 058/133] Fix --- bun.lock | 4 ++-- packages/effect-fc-next/src/ReactRuntime.ts | 6 +++--- packages/example-next/package.json | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/bun.lock b/bun.lock index 9cee2d3..99fe735 100644 --- a/bun.lock +++ b/bun.lock @@ -104,7 +104,7 @@ "name": "@effect-fc/example-next", "version": "0.0.0", "dependencies": { - "effect": "4.0.0-beta.85", + "effect": "4.0.0-beta.98", "effect-fc-next": "workspace:*", }, "devDependencies": { @@ -2999,7 +2999,7 @@ "@docusaurus/utils/jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], - "@effect-fc/example-next/effect": ["effect@4.0.0-beta.85", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.8.0", "find-my-way-ts": "^0.1.6", "ini": "^7.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^2.0.1", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^14.0.0", "yaml": "^2.9.0" } }, "sha512-Cjv9YQyv4CiIccmRAQIWAoeESCpCpiuHYY8zb5vqiYs3Ac2yE5RQAnKq7z4Ir/3VJYZb8kxh6rS8czDDnpTdkQ=="], + "@effect-fc/example-next/effect": ["effect@4.0.0-beta.98", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.9.0", "find-my-way-ts": "^0.1.6", "ini": "^7.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^2.0.4", "multipasta": "^0.2.8", "toml": "^4.1.2", "uuid": "^14.0.1", "yaml": "^2.9.0" } }, "sha512-oz+bsG5h+6RNrw4t5GMfQrk/xBS8ROoqkYsuvRhBr5O7mCOrpvH/hbw+QrDzvKIpX4HJClwm86F94c87W0sJxg=="], "@jsonjoy.com/fs-snapshot/@jsonjoy.com/json-pack": ["@jsonjoy.com/json-pack@17.67.0", "", { "dependencies": { "@jsonjoy.com/base64": "17.67.0", "@jsonjoy.com/buffers": "17.67.0", "@jsonjoy.com/codegen": "17.67.0", "@jsonjoy.com/json-pointer": "17.67.0", "@jsonjoy.com/util": "17.67.0", "hyperdyperid": "^1.2.0", "thingies": "^2.5.0", "tree-dump": "^1.1.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w=="], diff --git a/packages/effect-fc-next/src/ReactRuntime.ts b/packages/effect-fc-next/src/ReactRuntime.ts index c883af7..29c738a 100644 --- a/packages/effect-fc-next/src/ReactRuntime.ts +++ b/packages/effect-fc-next/src/ReactRuntime.ts @@ -16,7 +16,7 @@ export interface ReactRuntime { const ReactRuntimePrototype = Object.freeze({ [ReactRuntimeTypeId]: ReactRuntimeTypeId } as const) -export const preludeLayer: Layer.Layer = Layer.mergeAll(Component.ScopeMap.layer) +export const preludeLayer: Layer.Layer = Component.ScopeMap.layer export const isReactRuntime = (u: unknown): u is ReactRuntime => Predicate.hasProperty(u, ReactRuntimeTypeId) @@ -26,11 +26,11 @@ export const make = ( ): ReactRuntime | R, ER> => Object.setPrototypeOf( Object.assign(function() {}, { runtime: ManagedRuntime.make( - Layer.merge(layer, preludeLayer), + Layer.merge(preludeLayer, layer), { memoMap }, ), // biome-ignore lint/style/noNonNullAssertion: context initialization - context: React.createContext>(null!), + context: React.createContext | R>>(null!), }), ReactRuntimePrototype, ) diff --git a/packages/example-next/package.json b/packages/example-next/package.json index a37ebbd..fb45b01 100644 --- a/packages/example-next/package.json +++ b/packages/example-next/package.json @@ -23,12 +23,12 @@ "vite": "^8.0.16" }, "dependencies": { - "effect": "4.0.0-beta.85", + "effect": "4.0.0-beta.98", "effect-fc-next": "workspace:*" }, "overrides": { "@types/react": "^19.2.15", - "effect": "4.0.0-beta.85", + "effect": "4.0.0-beta.98", "react": "^19.2.6" } } -- 2.52.0 From 885cff74b3927aef932598002591446989237114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 19 Jul 2026 03:27:41 +0200 Subject: [PATCH 059/133] Example fix --- bun.lock | 8 + packages/effect-fc-next/src/ReactRuntime.ts | 2 +- packages/example-next/index.html | 2 +- packages/example-next/package.json | 8 +- packages/example-next/src/domain/Todo.ts | 20 ++ packages/example-next/src/domain/index.ts | 1 + packages/example-next/src/index.css | 28 --- .../src/lib/form/TextFieldFormInputView.tsx | 56 +++++ .../form/TextFieldOptionalFormInputView.tsx | 64 ++++++ .../example-next/src/lib/schema/datetime.ts | 38 ++++ packages/example-next/src/lib/schema/index.ts | 2 + packages/example-next/src/lib/schema/json.ts | 6 + packages/example-next/src/main.tsx | 109 +++++++-- packages/example-next/src/routeTree.gen.ts | 210 ++++++++++++++++++ packages/example-next/src/routes/__root.tsx | 28 +++ packages/example-next/src/routes/async.tsx | 71 ++++++ packages/example-next/src/routes/blank.tsx | 10 + .../example-next/src/routes/dev/context.tsx | 42 ++++ packages/example-next/src/routes/dev/memo.tsx | 37 +++ packages/example-next/src/routes/form.tsx | 143 ++++++++++++ packages/example-next/src/routes/index.tsx | 24 ++ packages/example-next/src/routes/query.tsx | 117 ++++++++++ packages/example-next/src/routes/result.tsx | 60 +++++ packages/example-next/src/runtime.ts | 15 ++ .../example-next/src/todo/EditTodoView.tsx | 88 ++++++++ .../example-next/src/todo/NewTodoView.tsx | 78 +++++++ .../example-next/src/todo/TodoFormSchema.ts | 9 + packages/example-next/src/todo/TodosState.ts | 96 ++++++++ packages/example-next/src/todo/TodosView.tsx | 34 +++ packages/example-next/tsconfig.app.json | 4 + packages/example-next/vite.config.ts | 12 + 31 files changed, 1368 insertions(+), 54 deletions(-) create mode 100644 packages/example-next/src/domain/Todo.ts create mode 100644 packages/example-next/src/domain/index.ts create mode 100644 packages/example-next/src/lib/form/TextFieldFormInputView.tsx create mode 100644 packages/example-next/src/lib/form/TextFieldOptionalFormInputView.tsx create mode 100644 packages/example-next/src/lib/schema/datetime.ts create mode 100644 packages/example-next/src/lib/schema/index.ts create mode 100644 packages/example-next/src/lib/schema/json.ts create mode 100644 packages/example-next/src/routeTree.gen.ts create mode 100644 packages/example-next/src/routes/__root.tsx create mode 100644 packages/example-next/src/routes/async.tsx create mode 100644 packages/example-next/src/routes/blank.tsx create mode 100644 packages/example-next/src/routes/dev/context.tsx create mode 100644 packages/example-next/src/routes/dev/memo.tsx create mode 100644 packages/example-next/src/routes/form.tsx create mode 100644 packages/example-next/src/routes/index.tsx create mode 100644 packages/example-next/src/routes/query.tsx create mode 100644 packages/example-next/src/routes/result.tsx create mode 100644 packages/example-next/src/runtime.ts create mode 100644 packages/example-next/src/todo/EditTodoView.tsx create mode 100644 packages/example-next/src/todo/NewTodoView.tsx create mode 100644 packages/example-next/src/todo/TodoFormSchema.ts create mode 100644 packages/example-next/src/todo/TodosState.ts create mode 100644 packages/example-next/src/todo/TodosView.tsx diff --git a/bun.lock b/bun.lock index 99fe735..2a9a52f 100644 --- a/bun.lock +++ b/bun.lock @@ -104,10 +104,16 @@ "name": "@effect-fc/example-next", "version": "0.0.0", "dependencies": { + "@effect/platform-browser": "4.0.0-beta.98", + "@radix-ui/themes": "^3.3.0", "effect": "4.0.0-beta.98", "effect-fc-next": "workspace:*", + "react-icons": "^5.6.0", }, "devDependencies": { + "@tanstack/react-router": "^1.170.10", + "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/router-plugin": "^1.168.13", "@types/react": "^19.2.15", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.2", @@ -2999,6 +3005,8 @@ "@docusaurus/utils/jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], + "@effect-fc/example-next/@effect/platform-browser": ["@effect/platform-browser@4.0.0-beta.98", "", { "dependencies": { "multipasta": "^0.2.8" }, "peerDependencies": { "effect": "^4.0.0-beta.98" } }, "sha512-Y0BEe0N8clKmJTHqmZpS85pLZbqAxP5uV3IYF0tDw/1dcs8AbTRHjbRmOLdyLtmU32NBVAiaUD3Vopb5Et5R/g=="], + "@effect-fc/example-next/effect": ["effect@4.0.0-beta.98", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.9.0", "find-my-way-ts": "^0.1.6", "ini": "^7.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^2.0.4", "multipasta": "^0.2.8", "toml": "^4.1.2", "uuid": "^14.0.1", "yaml": "^2.9.0" } }, "sha512-oz+bsG5h+6RNrw4t5GMfQrk/xBS8ROoqkYsuvRhBr5O7mCOrpvH/hbw+QrDzvKIpX4HJClwm86F94c87W0sJxg=="], "@jsonjoy.com/fs-snapshot/@jsonjoy.com/json-pack": ["@jsonjoy.com/json-pack@17.67.0", "", { "dependencies": { "@jsonjoy.com/base64": "17.67.0", "@jsonjoy.com/buffers": "17.67.0", "@jsonjoy.com/codegen": "17.67.0", "@jsonjoy.com/json-pointer": "17.67.0", "@jsonjoy.com/util": "17.67.0", "hyperdyperid": "^1.2.0", "thingies": "^2.5.0", "tree-dump": "^1.1.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w=="], diff --git a/packages/effect-fc-next/src/ReactRuntime.ts b/packages/effect-fc-next/src/ReactRuntime.ts index 29c738a..87023f3 100644 --- a/packages/effect-fc-next/src/ReactRuntime.ts +++ b/packages/effect-fc-next/src/ReactRuntime.ts @@ -26,7 +26,7 @@ export const make = ( ): ReactRuntime | R, ER> => Object.setPrototypeOf( Object.assign(function() {}, { runtime: ManagedRuntime.make( - Layer.merge(preludeLayer, layer), + Layer.mergeAll(preludeLayer, layer), { memoMap }, ), // biome-ignore lint/style/noNonNullAssertion: context initialization diff --git a/packages/example-next/index.html b/packages/example-next/index.html index e7e38b3..e4b78ea 100644 --- a/packages/example-next/index.html +++ b/packages/example-next/index.html @@ -4,7 +4,7 @@ - Effect FC Next Example + Vite + React + TS

diff --git a/packages/example-next/package.json b/packages/example-next/package.json index fb45b01..78995c3 100644 --- a/packages/example-next/package.json +++ b/packages/example-next/package.json @@ -14,6 +14,9 @@ "clean:modules": "rm -rf node_modules" }, "devDependencies": { + "@tanstack/react-router": "^1.170.10", + "@tanstack/react-router-devtools": "^1.167.0", + "@tanstack/router-plugin": "^1.168.13", "@types/react": "^19.2.15", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.2", @@ -23,8 +26,11 @@ "vite": "^8.0.16" }, "dependencies": { + "@effect/platform-browser": "4.0.0-beta.98", + "@radix-ui/themes": "^3.3.0", "effect": "4.0.0-beta.98", - "effect-fc-next": "workspace:*" + "effect-fc-next": "workspace:*", + "react-icons": "^5.6.0" }, "overrides": { "@types/react": "^19.2.15", diff --git a/packages/example-next/src/domain/Todo.ts b/packages/example-next/src/domain/Todo.ts new file mode 100644 index 0000000..500faab --- /dev/null +++ b/packages/example-next/src/domain/Todo.ts @@ -0,0 +1,20 @@ +import { Schema } from "effect" +import { assertEncodedJsonifiable } from "@/lib/schema" + + +export class Todo extends Schema.Class("Todo")({ + _tag: Schema.tag("Todo"), + id: Schema.String, + content: Schema.String, + completedAt: Schema.OptionFromSelf(Schema.DateTimeUtcFromSelf), +}) {} + + +export const TodoFromJsonStruct = Schema.Struct({ + ...Todo.fields, + completedAt: Schema.Option(Schema.DateTimeUtc), +}).pipe( + assertEncodedJsonifiable +) + +export const TodoFromJson = Schema.compose(TodoFromJsonStruct, Todo) diff --git a/packages/example-next/src/domain/index.ts b/packages/example-next/src/domain/index.ts new file mode 100644 index 0000000..8d70c13 --- /dev/null +++ b/packages/example-next/src/domain/index.ts @@ -0,0 +1 @@ +export * as Todo from "./Todo" diff --git a/packages/example-next/src/index.css b/packages/example-next/src/index.css index 8f84e7d..e69de29 100644 --- a/packages/example-next/src/index.css +++ b/packages/example-next/src/index.css @@ -1,28 +0,0 @@ -:root { - color: #17202a; - font-family: system-ui, sans-serif; - background: #f4f6f7; -} - -body { - display: grid; - min-width: 320px; - min-height: 100vh; - margin: 0; - place-items: center; -} - -main { - padding: 2rem; - text-align: center; -} - -button { - padding: 0.65rem 1rem; - border: 0; - border-radius: 0.5rem; - color: white; - font: inherit; - background: #7d3c98; - cursor: pointer; -} diff --git a/packages/example-next/src/lib/form/TextFieldFormInputView.tsx b/packages/example-next/src/lib/form/TextFieldFormInputView.tsx new file mode 100644 index 0000000..ca4f843 --- /dev/null +++ b/packages/example-next/src/lib/form/TextFieldFormInputView.tsx @@ -0,0 +1,56 @@ +import { Callout, Flex, Spinner, TextField } from "@radix-ui/themes" +import { Array, Option, Struct } from "effect" +import { Component, Form, Subscribable } from "effect-fc" +import type * as React from "react" + + +export declare namespace TextFieldFormInputView { + export interface Props + extends Omit, Form.useInput.Options { + readonly form: Form.Form + } + + export type Signature =

(props: Props) => React.ReactNode +} + +export const TextFieldFormInputView = Component.make("TextFieldFormInputView")(function*( + props: TextFieldFormInputView.Props +) { + const input = yield* Form.useInput(props.form, props) + const [issues, isValidating, isCommitting] = yield* Subscribable.useAll([ + props.form.issues, + props.form.isValidating, + props.form.isCommitting, + ]) + + return ( + + input.setValue(e.target.value)} + disabled={isCommitting} + {...Struct.omit(props, "form")} + > + {isValidating && + + + + } + + {props.children} + + + {Option.match(Array.head(issues), { + onSome: issue => ( + + {issue.message} + + ), + + onNone: () => <>, + })} + + ) +}).pipe( + Component.withSignature() +) diff --git a/packages/example-next/src/lib/form/TextFieldOptionalFormInputView.tsx b/packages/example-next/src/lib/form/TextFieldOptionalFormInputView.tsx new file mode 100644 index 0000000..68693ea --- /dev/null +++ b/packages/example-next/src/lib/form/TextFieldOptionalFormInputView.tsx @@ -0,0 +1,64 @@ +import { Callout, Flex, Spinner, Switch, TextField } from "@radix-ui/themes" +import { Array, Option, Struct } from "effect" +import { Component, Form, Subscribable } from "effect-fc" +import type * as React from "react" + + +export declare namespace TextFieldOptionalFormInputView { + export interface Props + extends Omit, Form.useOptionalInput.Options { + readonly form: Form.Form, ER, EW> + } + + export type Signature =

(props: Props) => React.ReactNode +} + +export const TextFieldOptionalFormInputView = Component.make("TextFieldOptionalFormInputView")(function*( + props: TextFieldOptionalFormInputView.Props +) { + const input = yield* Form.useOptionalInput(props.form, props) + const [issues, isValidating, isCommitting] = yield* Subscribable.useAll([ + props.form.issues, + props.form.isValidating, + props.form.isCommitting, + ]) + + return ( + + input.setValue(e.target.value)} + disabled={!input.enabled || isCommitting} + {...Struct.omit(props, "form", "defaultValue")} + > + + + + + {isValidating && + + + + } + + {props.children} + + + {Option.match(Array.head(issues), { + onSome: issue => ( + + {issue.message} + + ), + + onNone: () => <>, + })} + + ) +}).pipe( + Component.withSignature() +) diff --git a/packages/example-next/src/lib/schema/datetime.ts b/packages/example-next/src/lib/schema/datetime.ts new file mode 100644 index 0000000..ce85d3a --- /dev/null +++ b/packages/example-next/src/lib/schema/datetime.ts @@ -0,0 +1,38 @@ +import { DateTime, Effect, Option, ParseResult, Schema } from "effect" + + +export class DateTimeUtcFromZoned extends Schema.transformOrFail( + Schema.DateTimeZonedFromSelf, + Schema.DateTimeUtcFromSelf, + { + strict: true, + encode: DateTime.setZoneCurrent, + decode: i => ParseResult.succeed(DateTime.toUtc(i)), + }, +) {} + +export class DateTimeZonedFromUtc extends Schema.transformOrFail( + Schema.DateTimeUtcFromSelf, + Schema.DateTimeZonedFromSelf, + { + strict: true, + encode: a => ParseResult.succeed(DateTime.toUtc(a)), + decode: DateTime.setZoneCurrent, + }, +) {} + +export class DateTimeUtcFromZonedInput extends Schema.transformOrFail( + Schema.String, + DateTimeUtcFromZoned, + { + strict: true, + encode: a => ParseResult.succeed(DateTime.formatIsoZoned(a).slice(0, 16)), + decode: (i, _, ast) => Effect.flatMap( + DateTime.CurrentTimeZone, + timeZone => Option.match(DateTime.makeZoned(i, { timeZone, adjustForTimeZone: true }), { + onSome: ParseResult.succeed, + onNone: () => ParseResult.fail(new ParseResult.Type(ast, i, `Unable to decode ${JSON.stringify(i)} into a DateTime.Zoned`)), + }), + ), + }, +) {} diff --git a/packages/example-next/src/lib/schema/index.ts b/packages/example-next/src/lib/schema/index.ts new file mode 100644 index 0000000..260895c --- /dev/null +++ b/packages/example-next/src/lib/schema/index.ts @@ -0,0 +1,2 @@ +export * from "./datetime" +export * from "./json" diff --git a/packages/example-next/src/lib/schema/json.ts b/packages/example-next/src/lib/schema/json.ts new file mode 100644 index 0000000..5b69cc4 --- /dev/null +++ b/packages/example-next/src/lib/schema/json.ts @@ -0,0 +1,6 @@ +import type { Schema } from "effect" +import type { JsonValue } from "type-fest" + + +export const assertEncodedJsonifiable = , A, I extends JsonValue, R>(schema: S & Schema.Schema): S => schema +export const assertTypeJsonifiable = , A extends JsonValue, I, R>(schema: S & Schema.Schema): S => schema diff --git a/packages/example-next/src/main.tsx b/packages/example-next/src/main.tsx index 3b4d312..59d0c0c 100644 --- a/packages/example-next/src/main.tsx +++ b/packages/example-next/src/main.tsx @@ -1,40 +1,103 @@ -import { Effect, Layer, SubscriptionRef } from "effect" -import { Component, Lens, ReactRuntime, View } from "effect-fc-next" +import { Effect, Layer, Schema, SubscriptionRef } from "effect" +import { + Component, + Form, + Lens, + LensForm, + MutationForm, + ReactRuntime, + View, +} from "effect-fc-next" import { StrictMode } from "react" import { createRoot } from "react-dom/client" import "./index.css" -const Counter = Component.make("Counter")(function*() { - const count = yield* Component.useOnMount(() => Effect.map( - SubscriptionRef.make(0), - Lens.fromSubscriptionRef, - )) - const [value] = yield* View.useAll([count]) - const increment = yield* Component.useCallbackSync( - () => Lens.update(count, n => n + 1), - [count], - ) +const runtime = ReactRuntime.make(Layer.empty) + +const MutationFormExample = Component.make("MutationFormExample")(function*() { + const form = yield* Component.useOnMount(() => MutationForm.service({ + schema: Schema.String, + initialEncodedValue: "", + f: ([value]) => Effect.log(`Submitted: ${value}`), + })) + const input = yield* Form.useInput(form) + const [canCommit, isCommitting, issues] = yield* View.useAll([ + form.canCommit, + form.isCommitting, + form.issues, + ]) + const runPromise = yield* Component.useRunPromise() return ( -

-

Effect FC Next

-

Running on Effect V4.

- -
+
+

MutationForm

+

Owns a draft and commits it explicitly through a Mutation.

+
{ + event.preventDefault() + void runPromise(form.submit) + }}> + input.setValue(event.currentTarget.value)} + disabled={isCommitting} + placeholder="Write something to submit" + /> + +
+ {issues.map((issue) =>

{issue.message}

)} +
) -}) +}).pipe(Component.withRuntime(runtime.context)) -const runtime = ReactRuntime.make(Layer.empty) -const CounterApp = Counter.pipe(Component.withRuntime(runtime.context)) +const LensFormExample = Component.make("LensFormExample")(function*() { + const target = yield* Component.useOnMount(() => Effect.map( + SubscriptionRef.make("A value owned by the application"), + Lens.fromSubscriptionRef, + )) + const form = yield* Component.useOnMount(() => LensForm.service({ + schema: Schema.String, + target, + })) + const input = yield* Form.useInput(form) + const [value, canCommit, isCommitting, issues] = yield* View.useAll([ + target, + form.canCommit, + form.isCommitting, + form.issues, + ]) + + return ( +
+

LensForm

+

Synchronizes valid input directly with a target Lens.

+ input.setValue(event.currentTarget.value)} + disabled={!canCommit || isCommitting} + /> +

Target value: {value}

+ {issues.map((issue) =>

{issue.message}

)} +
+ ) +}).pipe(Component.withRuntime(runtime.context)) + +const App = () => ( +
+

Effect FC Next

+

Effect v4 examples for the finalized form API.

+ + +
+) // biome-ignore lint/style/noNonNullAssertion: the Vite template provides this element createRoot(document.getElementById("root")!).render( - + , ) diff --git a/packages/example-next/src/routeTree.gen.ts b/packages/example-next/src/routeTree.gen.ts new file mode 100644 index 0000000..3ad431c --- /dev/null +++ b/packages/example-next/src/routeTree.gen.ts @@ -0,0 +1,210 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. + +import { Route as rootRouteImport } from './routes/__root' +import { Route as IndexRouteImport } from './routes/index' +import { Route as AsyncRouteImport } from './routes/async' +import { Route as BlankRouteImport } from './routes/blank' +import { Route as FormRouteImport } from './routes/form' +import { Route as QueryRouteImport } from './routes/query' +import { Route as ResultRouteImport } from './routes/result' +import { Route as DevContextRouteImport } from './routes/dev/context' +import { Route as DevMemoRouteImport } from './routes/dev/memo' + +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) +const AsyncRoute = AsyncRouteImport.update({ + id: '/async', + path: '/async', + getParentRoute: () => rootRouteImport, +} as any) +const BlankRoute = BlankRouteImport.update({ + id: '/blank', + path: '/blank', + getParentRoute: () => rootRouteImport, +} as any) +const FormRoute = FormRouteImport.update({ + id: '/form', + path: '/form', + getParentRoute: () => rootRouteImport, +} as any) +const QueryRoute = QueryRouteImport.update({ + id: '/query', + path: '/query', + getParentRoute: () => rootRouteImport, +} as any) +const ResultRoute = ResultRouteImport.update({ + id: '/result', + path: '/result', + getParentRoute: () => rootRouteImport, +} as any) +const DevContextRoute = DevContextRouteImport.update({ + id: '/dev/context', + path: '/dev/context', + getParentRoute: () => rootRouteImport, +} as any) +const DevMemoRoute = DevMemoRouteImport.update({ + id: '/dev/memo', + path: '/dev/memo', + getParentRoute: () => rootRouteImport, +} as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/async': typeof AsyncRoute + '/blank': typeof BlankRoute + '/form': typeof FormRoute + '/query': typeof QueryRoute + '/result': typeof ResultRoute + '/dev/context': typeof DevContextRoute + '/dev/memo': typeof DevMemoRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/async': typeof AsyncRoute + '/blank': typeof BlankRoute + '/form': typeof FormRoute + '/query': typeof QueryRoute + '/result': typeof ResultRoute + '/dev/context': typeof DevContextRoute + '/dev/memo': typeof DevMemoRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute + '/async': typeof AsyncRoute + '/blank': typeof BlankRoute + '/form': typeof FormRoute + '/query': typeof QueryRoute + '/result': typeof ResultRoute + '/dev/context': typeof DevContextRoute + '/dev/memo': typeof DevMemoRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: + | '/' + | '/async' + | '/blank' + | '/form' + | '/query' + | '/result' + | '/dev/context' + | '/dev/memo' + fileRoutesByTo: FileRoutesByTo + to: + | '/' + | '/async' + | '/blank' + | '/form' + | '/query' + | '/result' + | '/dev/context' + | '/dev/memo' + id: + | '__root__' + | '/' + | '/async' + | '/blank' + | '/form' + | '/query' + | '/result' + | '/dev/context' + | '/dev/memo' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + AsyncRoute: typeof AsyncRoute + BlankRoute: typeof BlankRoute + FormRoute: typeof FormRoute + QueryRoute: typeof QueryRoute + ResultRoute: typeof ResultRoute + DevContextRoute: typeof DevContextRoute + DevMemoRoute: typeof DevMemoRoute +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + '/async': { + id: '/async' + path: '/async' + fullPath: '/async' + preLoaderRoute: typeof AsyncRouteImport + parentRoute: typeof rootRouteImport + } + '/blank': { + id: '/blank' + path: '/blank' + fullPath: '/blank' + preLoaderRoute: typeof BlankRouteImport + parentRoute: typeof rootRouteImport + } + '/form': { + id: '/form' + path: '/form' + fullPath: '/form' + preLoaderRoute: typeof FormRouteImport + parentRoute: typeof rootRouteImport + } + '/query': { + id: '/query' + path: '/query' + fullPath: '/query' + preLoaderRoute: typeof QueryRouteImport + parentRoute: typeof rootRouteImport + } + '/result': { + id: '/result' + path: '/result' + fullPath: '/result' + preLoaderRoute: typeof ResultRouteImport + parentRoute: typeof rootRouteImport + } + '/dev/context': { + id: '/dev/context' + path: '/dev/context' + fullPath: '/dev/context' + preLoaderRoute: typeof DevContextRouteImport + parentRoute: typeof rootRouteImport + } + '/dev/memo': { + id: '/dev/memo' + path: '/dev/memo' + fullPath: '/dev/memo' + preLoaderRoute: typeof DevMemoRouteImport + parentRoute: typeof rootRouteImport + } + } +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + AsyncRoute: AsyncRoute, + BlankRoute: BlankRoute, + FormRoute: FormRoute, + QueryRoute: QueryRoute, + ResultRoute: ResultRoute, + DevContextRoute: DevContextRoute, + DevMemoRoute: DevMemoRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() diff --git a/packages/example-next/src/routes/__root.tsx b/packages/example-next/src/routes/__root.tsx new file mode 100644 index 0000000..67b0910 --- /dev/null +++ b/packages/example-next/src/routes/__root.tsx @@ -0,0 +1,28 @@ +import { Container, Flex, Theme } from "@radix-ui/themes" +import { createRootRoute, Link, Outlet } from "@tanstack/react-router" +import { TanStackRouterDevtools } from "@tanstack/react-router-devtools" + +import "@radix-ui/themes/styles.css" +import "../index.css" + + +export const Route = createRootRoute({ + component: Root +}) + +function Root() { + return ( + + + + Index + Blank + + + + + + + + ) +} diff --git a/packages/example-next/src/routes/async.tsx b/packages/example-next/src/routes/async.tsx new file mode 100644 index 0000000..62e0234 --- /dev/null +++ b/packages/example-next/src/routes/async.tsx @@ -0,0 +1,71 @@ +import { HttpClient } from "@effect/platform" +import { Container, Flex, Heading, Slider, Text, TextField } from "@radix-ui/themes" +import { createFileRoute } from "@tanstack/react-router" +import { Array, Effect, flow, Option, Schema } from "effect" +import { Async, Component, Memoized } from "effect-fc" +import * as React from "react" +import { runtime } from "@/runtime" + + +const Post = Schema.Struct({ + userId: Schema.Int, + id: Schema.Int, + title: Schema.String, + body: Schema.String, +}) + +interface AsyncFetchPostViewProps { + readonly id: number +} + +class AsyncFetchPostView extends Component.make("AsyncFetchPostView")(function*(props: AsyncFetchPostViewProps) { + const post = yield* Component.useOnChange(() => HttpClient.HttpClient.pipe( + Effect.tap(Effect.sleep("500 millis")), + Effect.andThen(client => client.get(`https://jsonplaceholder.typicode.com/posts/${ props.id }`)), + Effect.andThen(response => response.json), + Effect.andThen(Schema.decodeUnknown(Post)), + ), [props.id]) + + return ( +
+ {post.title} + {post.body} +
+ ) +}).pipe( + Async.async, + Async.withOptions({ defaultFallback: Default fallback }), + Memoized.memoized, +) {} + + +const AsyncRouteComponent = Component.make("AsyncRouteView")(function*() { + const [text, setText] = React.useState("Typing here should not trigger a refetch of the post") + const [id, setId] = React.useState(1) + + const AsyncFetchPost = yield* AsyncFetchPostView.use + + return ( + + + setText(e.currentTarget.value)} + /> + + + + Loading post...} /> + + + ) +}).pipe( + Component.withRuntime(runtime.context) +) + +export const Route = createFileRoute("/async")({ + component: AsyncRouteComponent, +}) diff --git a/packages/example-next/src/routes/blank.tsx b/packages/example-next/src/routes/blank.tsx new file mode 100644 index 0000000..4f3c7df --- /dev/null +++ b/packages/example-next/src/routes/blank.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from "@tanstack/react-router" + + +export const Route = createFileRoute("/blank")({ + component: RouteComponent +}) + +function RouteComponent() { + return
Hello "/blank"!
+} diff --git a/packages/example-next/src/routes/dev/context.tsx b/packages/example-next/src/routes/dev/context.tsx new file mode 100644 index 0000000..26048a9 --- /dev/null +++ b/packages/example-next/src/routes/dev/context.tsx @@ -0,0 +1,42 @@ +import { Container, Flex, Text, TextField } from "@radix-ui/themes" +import { createFileRoute } from "@tanstack/react-router" +import { Console, Effect } from "effect" +import { Component } from "effect-fc" +import * as React from "react" +import { runtime } from "@/runtime" + + +class SubService extends Effect.Service()("SubService", { + effect: (value: string) => Effect.succeed({ value }) +}) {} + +const SubComponent = Component.makeUntraced("SubComponent")(function*() { + const service = yield* SubService + yield* Component.useOnMount(() => Effect.gen(function*() { + yield* Effect.addFinalizer(() => Console.log("SubComponent unmounted")) + yield* Console.log("SubComponent mounted") + })) + + return {service.value} +}) + +const ContextView = Component.makeUntraced("ContextView")(function*() { + const [serviceValue, setServiceValue] = React.useState("test") + const SubServiceLayer = React.useMemo(() => SubService.Default(serviceValue), [serviceValue]) + const SubComponentFC = yield* Effect.provide(SubComponent.use, yield* Component.useContextFromLayer(SubServiceLayer)) + + return ( + + + setServiceValue(e.target.value)} /> + + + + ) +}).pipe( + Component.withRuntime(runtime.context) +) + +export const Route = createFileRoute("/dev/context")({ + component: ContextView +}) diff --git a/packages/example-next/src/routes/dev/memo.tsx b/packages/example-next/src/routes/dev/memo.tsx new file mode 100644 index 0000000..ae672bd --- /dev/null +++ b/packages/example-next/src/routes/dev/memo.tsx @@ -0,0 +1,37 @@ +import { Flex, Text, TextField } from "@radix-ui/themes" +import { createFileRoute } from "@tanstack/react-router" +import { GetRandomValues, makeUuid4 } from "@typed/id" +import { Effect } from "effect" +import { Component, Memoized } from "effect-fc" +import * as React from "react" +import { runtime } from "@/runtime" + + +const RouteComponent = Component.makeUntraced("RouteComponent")(function*() { + const [value, setValue] = React.useState("") + + return ( + + setValue(e.target.value)} + /> + + {yield* Effect.map(SubComponent.use, FC => )} + {yield* Effect.map(MemoizedSubComponent.use, FC => )} + + ) +}).pipe( + Component.withRuntime(runtime.context) +) + +class SubComponent extends Component.makeUntraced("SubComponent")(function*() { + const id = yield* makeUuid4.pipe(Effect.provide(GetRandomValues.CryptoRandom)) + return {id} +}) {} + +class MemoizedSubComponent extends Memoized.memoized(SubComponent) {} + +export const Route = createFileRoute("/dev/memo")({ + component: RouteComponent, +}) diff --git a/packages/example-next/src/routes/form.tsx b/packages/example-next/src/routes/form.tsx new file mode 100644 index 0000000..9ce3fae --- /dev/null +++ b/packages/example-next/src/routes/form.tsx @@ -0,0 +1,143 @@ +import { Button, Container, Flex, Text } from "@radix-ui/themes" +import { createFileRoute } from "@tanstack/react-router" +import { Console, Effect, Match, Option, ParseResult, Schema } from "effect" +import { Component, Form, SubmittableForm, Subscribable } from "effect-fc" +import { TextFieldFormInputView } from "@/lib/form/TextFieldFormInputView" +import { TextFieldOptionalFormInputView } from "@/lib/form/TextFieldOptionalFormInputView" +import { DateTimeUtcFromZonedInput } from "@/lib/schema" +import { runtime } from "@/runtime" + + +const email = Schema.pattern( + /^(?!\.)(?!.*\.\.)([A-Z0-9_+-.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9-]*\.)+[A-Z]{2,}$/i, + + { + identifier: "email", + title: "email", + message: () => "Not an email address", + }, +) + +const RegisterFormSchema = Schema.Struct({ + email: Schema.String.pipe(email), + password: Schema.String.pipe(Schema.minLength(3)), + birth: Schema.OptionFromSelf(DateTimeUtcFromZonedInput), +}) + +const RegisterFormSubmitSchema = Schema.Struct({ + email: Schema.transformOrFail( + Schema.String, + Schema.String, + { + decode: (input, _options, ast) => input !== "admin@admin.com" + ? ParseResult.succeed(input) + : ParseResult.fail(new ParseResult.Type(ast, input, "This email is already in use.")), + encode: ParseResult.succeed, + }, + ), + password: Schema.String, + birth: Schema.OptionFromSelf(Schema.DateTimeUtcFromSelf), +}) + +class RegisterFormService extends Effect.Service()("RegisterFormService", { + scoped: Effect.gen(function*() { + const form = yield* SubmittableForm.service({ + schema: RegisterFormSchema.pipe( + Schema.compose( + Schema.transformOrFail( + Schema.typeSchema(RegisterFormSchema), + Schema.typeSchema(RegisterFormSchema), + { + decode: v => Effect.andThen(Effect.sleep("500 millis"), ParseResult.succeed(v)), + encode: ParseResult.succeed, + }, + ), + ), + ), + + initialEncodedValue: { email: "", password: "", birth: Option.none() }, + f: Effect.fnUntraced(function*([value]) { + yield* Effect.sleep("500 millis") + return yield* Schema.decode(RegisterFormSubmitSchema)(value) + }), + }) + + return { + form, + emailField: Form.focusObjectOn(form, "email"), + passwordField: Form.focusObjectOn(form, "password"), + birthField: Form.focusObjectOn(form, "birth"), + } as const + }) +}) {} + +class RegisterFormView extends Component.make("RegisterFormView")(function*() { + const form = yield* RegisterFormService + const [canCommit, submitResult] = yield* Subscribable.useAll([ + form.form.canCommit, + form.form.mutation.result, + ]) + + const runPromise = yield* Component.useRunPromise() + const TextFieldFormInput = yield* TextFieldFormInputView.use + const TextFieldOptionalFormInput = yield* TextFieldOptionalFormInputView.use + + yield* Component.useOnMount(() => Effect.gen(function*() { + yield* Effect.addFinalizer(() => Console.log("RegisterFormView unmounted")) + yield* Console.log("RegisterFormView mounted") + })) + + + return ( + +
{ + e.preventDefault() + void runPromise(form.form.submit) + }}> + + + + + + + + + +
+ + {Match.value(submitResult).pipe( + Match.tag("Initial", () => <>), + Match.tag("Running", () => Submitting...), + Match.tag("Success", () => Submitted successfully!), + Match.tag("Failure", e => Error: {e.cause.toString()}), + Match.exhaustive, + )} +
+ ) +}) {} + +const RegisterPage = Component.make("RegisterPageView")(function*() { + const RegisterForm = yield* Effect.provide( + RegisterFormView.use, + yield* Component.useContextFromLayer(RegisterFormService.Default), + ) + + return +}).pipe( + Component.withRuntime(runtime.context) +) + + +export const Route = createFileRoute("/form")({ + component: RegisterPage +}) diff --git a/packages/example-next/src/routes/index.tsx b/packages/example-next/src/routes/index.tsx new file mode 100644 index 0000000..d747b39 --- /dev/null +++ b/packages/example-next/src/routes/index.tsx @@ -0,0 +1,24 @@ +import { createFileRoute } from "@tanstack/react-router" +import { Effect } from "effect" +import { Component } from "effect-fc" +import { runtime } from "@/runtime" +import { TodosState } from "@/todo/TodosState" +import { TodosView } from "@/todo/TodosView" + + +const TodosStateLive = TodosState.Default("todos") + +const Index = Component.make("IndexView")(function*() { + const Todos = yield* Effect.provide( + TodosView.use, + yield* Component.useContextFromLayer(TodosStateLive), + ) + + return +}).pipe( + Component.withRuntime(runtime.context) +) + +export const Route = createFileRoute("/")({ + component: Index +}) diff --git a/packages/example-next/src/routes/query.tsx b/packages/example-next/src/routes/query.tsx new file mode 100644 index 0000000..047560e --- /dev/null +++ b/packages/example-next/src/routes/query.tsx @@ -0,0 +1,117 @@ +import { HttpClient, type HttpClientError } from "@effect/platform" +import { Button, Container, Flex, Heading, Slider, Text } from "@radix-ui/themes" +import { createFileRoute } from "@tanstack/react-router" +import { Array, Cause, Chunk, Console, Effect, flow, Match, Option, Schema, Stream, SubscriptionRef } from "effect" +import { Component, ErrorObserver, Lens, Mutation, Query, Result, Subscribable } from "effect-fc" +import { runtime } from "@/runtime" + + +const Post = Schema.Struct({ + userId: Schema.Int, + id: Schema.Int, + title: Schema.String, + body: Schema.String, +}) + +const ResultView = Component.make("ResultView")(function*() { + const runPromise = yield* Component.useRunPromise() + + const [idLens, query, mutation] = yield* Component.useOnMount(() => Effect.gen(function*() { + const idLens = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(1)) + const key = Stream.map(idLens.changes, id => [id] as const) + + const query = yield* Query.service({ + key, + f: ([id]) => HttpClient.HttpClient.pipe( + Effect.tap(Effect.sleep("500 millis")), + Effect.andThen(client => client.get(`https://jsonplaceholder.typicode.com/posts/${ id }`)), + Effect.andThen(response => response.json), + Effect.andThen(Schema.decodeUnknown(Post)), + ), + staleTime: "10 seconds", + }) + + const mutation = yield* Mutation.make({ + f: ([id]: readonly [id: number]) => HttpClient.HttpClient.pipe( + Effect.tap(Effect.sleep("500 millis")), + Effect.andThen(client => client.get(`https://jsonplaceholder.typicode.com/posts/${ id }`)), + Effect.andThen(response => response.json), + Effect.andThen(Schema.decodeUnknown(Post)), + ), + }) + + return [idLens, query, mutation] as const + })) + + const [id, setId] = yield* Lens.useState(idLens) + const [queryResult, mutationResult] = yield* Subscribable.useAll([query.result, mutation.result]) + + yield* Component.useOnMount(() => ErrorObserver.ErrorObserver().pipe( + Effect.andThen(observer => observer.subscribe), + Effect.andThen(Stream.fromQueue), + Stream.unwrapScoped, + Stream.runForEach(flow( + Cause.failures, + Chunk.findFirst(e => e._tag === "RequestError" || e._tag === "ResponseError"), + Option.match({ + onSome: e => Console.log("ResultView HttpClient error", e), + onNone: () => Effect.void, + }), + )), + Effect.forkScoped, + )) + + return ( + + + + +
+ {Match.value(queryResult).pipe( + Match.tag("Running", () => Loading...), + Match.tag("Success", result => <> + {result.value.title} + {result.value.body} + {Result.hasRefreshingFlag(result) && Refreshing...} + ), + Match.tag("Failure", result => + An error has occured: {result.cause.toString()} + ), + Match.orElse(() => <>), + )} +
+ + + + + + +
+ {Match.value(mutationResult).pipe( + Match.tag("Running", () => Loading...), + Match.tag("Success", result => <> + {result.value.title} + {result.value.body} + {Result.hasRefreshingFlag(result) && Refreshing...} + ), + Match.tag("Failure", result => + An error has occured: {result.cause.toString()} + ), + Match.orElse(() => <>), + )} +
+ + + + +
+
+ ) +}) + +export const Route = createFileRoute("/query")({ + component: Component.withRuntime(ResultView, runtime.context) +}) diff --git a/packages/example-next/src/routes/result.tsx b/packages/example-next/src/routes/result.tsx new file mode 100644 index 0000000..0828f37 --- /dev/null +++ b/packages/example-next/src/routes/result.tsx @@ -0,0 +1,60 @@ +import { HttpClient, type HttpClientError } from "@effect/platform" +import { Container, Heading, Text } from "@radix-ui/themes" +import { createFileRoute } from "@tanstack/react-router" +import { Cause, Chunk, Console, Effect, flow, Match, Option, Schema, Stream } from "effect" +import { Component, ErrorObserver, Result, Subscribable } from "effect-fc" +import { runtime } from "@/runtime" + + +const Post = Schema.Struct({ + userId: Schema.Int, + id: Schema.Int, + title: Schema.String, + body: Schema.String, +}) + +const ResultView = Component.makeUntraced("Result")(function*() { + const [resultSubscribable] = yield* Component.useOnMount(() => HttpClient.HttpClient.pipe( + Effect.andThen(client => client.get("https://jsonplaceholder.typicode.com/posts/1")), + Effect.andThen(response => response.json), + Effect.andThen(Schema.decodeUnknown(Post)), + Effect.tap(Effect.sleep("250 millis")), + Result.forkEffect, + )) + const [result] = yield* Subscribable.useAll([resultSubscribable]) + + yield* Component.useOnMount(() => ErrorObserver.ErrorObserver().pipe( + Effect.andThen(observer => observer.subscribe), + Effect.andThen(Stream.fromQueue), + Stream.unwrapScoped, + Stream.runForEach(flow( + Cause.failures, + Chunk.findFirst(e => e._tag === "RequestError" || e._tag === "ResponseError"), + Option.match({ + onSome: e => Console.log("ResultView HttpClient error", e), + onNone: () => Effect.void, + }), + )), + Effect.forkScoped, + )) + + return ( + + {Match.value(result).pipe( + Match.tag("Running", () => Loading...), + Match.tag("Success", result => <> + {result.value.title} + {result.value.body} + ), + Match.tag("Failure", result => + An error has occured: {result.cause.toString()} + ), + Match.orElse(() => <>), + )} + + ) +}) + +export const Route = createFileRoute("/result")({ + component: Component.withRuntime(ResultView, runtime.context) +}) diff --git a/packages/example-next/src/runtime.ts b/packages/example-next/src/runtime.ts new file mode 100644 index 0000000..3467529 --- /dev/null +++ b/packages/example-next/src/runtime.ts @@ -0,0 +1,15 @@ +import { FetchHttpClient } from "@effect/platform" +import { Clipboard, Geolocation, Permissions } from "@effect/platform-browser" +import { DateTime, Layer } from "effect" +import { ReactRuntime } from "effect-fc" + + +export const AppLive = Layer.empty.pipe( + Layer.provideMerge(DateTime.layerCurrentZoneLocal), + Layer.provideMerge(Clipboard.layer), + Layer.provideMerge(Geolocation.layer), + Layer.provideMerge(Permissions.layer), + Layer.provideMerge(FetchHttpClient.layer), +) + +export const runtime = ReactRuntime.make(AppLive) diff --git a/packages/example-next/src/todo/EditTodoView.tsx b/packages/example-next/src/todo/EditTodoView.tsx new file mode 100644 index 0000000..69a9016 --- /dev/null +++ b/packages/example-next/src/todo/EditTodoView.tsx @@ -0,0 +1,88 @@ +import { Box, Flex, IconButton } from "@radix-ui/themes" +import { Effect } from "effect" +import { Component, Form, Subscribable, SynchronizedForm } from "effect-fc" +import { FaArrowDown, FaArrowUp } from "react-icons/fa" +import { FaDeleteLeft } from "react-icons/fa6" +import { TextFieldFormInputView } from "@/lib/form/TextFieldFormInputView" +import { TextFieldOptionalFormInputView } from "@/lib/form/TextFieldOptionalFormInputView" +import { TodoFormSchema } from "./TodoFormSchema" +import { TodosState } from "./TodosState" + + +export interface EditTodoViewProps { + readonly id: string +} + +export class EditTodoView extends Component.make("TodoView")(function*(props: EditTodoViewProps) { + const state = yield* TodosState + + const [ + indexSubscribable, + contentField, + completedAtField, + ] = yield* Component.useOnChange(() => Effect.gen(function*() { + const indexSubscribable = state.getIndexSubscribable(props.id) + + const form = yield* SynchronizedForm.service({ + schema: TodoFormSchema, + target: state.getElementLens(props.id), + }) + + return [ + indexSubscribable, + Form.focusObjectOn(form, "content"), + Form.focusObjectOn(form, "completedAt"), + ] as const + }), [props.id]) + + const [index, size] = yield* Subscribable.useAll([ + indexSubscribable, + state.sizeSubscribable, + ]) + + const runSync = yield* Component.useRunSync() + const TextFieldFormInput = yield* TextFieldFormInputView.use + const TextFieldOptionalFormInput = yield* TextFieldOptionalFormInputView.use + + + return ( + + + + + + + + + + + + + runSync(state.moveLeft(props.id))} + > + + + + = size - 1} + onClick={() => runSync(state.moveRight(props.id))} + > + + + + runSync(state.remove(props.id))}> + + + + + ) +}) {} diff --git a/packages/example-next/src/todo/NewTodoView.tsx b/packages/example-next/src/todo/NewTodoView.tsx new file mode 100644 index 0000000..e382778 --- /dev/null +++ b/packages/example-next/src/todo/NewTodoView.tsx @@ -0,0 +1,78 @@ +import { Box, Button, Flex } from "@radix-ui/themes" +import { GetRandomValues, makeUuid4 } from "@typed/id" +import { Chunk, type DateTime, Effect, Option, Schema } from "effect" +import { Component, Form, Lens, SubmittableForm, Subscribable } from "effect-fc" +import * as Domain from "@/domain" +import { TextFieldFormInputView } from "@/lib/form/TextFieldFormInputView" +import { TextFieldOptionalFormInputView } from "@/lib/form/TextFieldOptionalFormInputView" +import { TodoFormSchema } from "./TodoFormSchema" +import { TodosState } from "./TodosState" + + +const makeTodo = makeUuid4.pipe( + Effect.map(id => Domain.Todo.Todo.make({ + id, + content: "", + completedAt: Option.none(), + })), + Effect.provide(GetRandomValues.CryptoRandom), +) + + +export class NewTodoView extends Component.make("NewTodoView")(function*() { + const state = yield* TodosState + + const [ + form, + contentField, + completedAtField, + ] = yield* Component.useOnMount(() => Effect.gen(function*() { + const form = yield* SubmittableForm.service({ + schema: TodoFormSchema, + initialEncodedValue: yield* Schema.encode(TodoFormSchema)(yield* makeTodo), + f: ([todo, form]) => Lens.update(state.lens, Chunk.prepend(todo)).pipe( + Effect.andThen(makeTodo), + Effect.andThen(Schema.encode(TodoFormSchema)), + Effect.andThen(v => Lens.set(form.encodedValue, v)), + ), + }) + + return [ + form, + Form.focusObjectOn(form, "content"), + Form.focusObjectOn(form, "completedAt"), + ] as const + })) + + const [canCommit] = yield* Subscribable.useAll([form.canCommit]) + + const runPromise = yield* Component.useRunPromise() + const TextFieldFormInput = yield* TextFieldFormInputView.use + const TextFieldOptionalFormInput = yield* TextFieldOptionalFormInputView.use + + + return ( + + + + + + + + + + + + + + ) +}) {} diff --git a/packages/example-next/src/todo/TodoFormSchema.ts b/packages/example-next/src/todo/TodoFormSchema.ts new file mode 100644 index 0000000..b2d4153 --- /dev/null +++ b/packages/example-next/src/todo/TodoFormSchema.ts @@ -0,0 +1,9 @@ +import { Schema } from "effect" +import * as Domain from "@/domain" +import { DateTimeUtcFromZonedInput } from "@/lib/schema" + + +export const TodoFormSchema = Schema.compose(Schema.Struct({ + ...Domain.Todo.Todo.fields, + completedAt: Schema.OptionFromSelf(DateTimeUtcFromZonedInput), +}), Domain.Todo.Todo) diff --git a/packages/example-next/src/todo/TodosState.ts b/packages/example-next/src/todo/TodosState.ts new file mode 100644 index 0000000..d77041d --- /dev/null +++ b/packages/example-next/src/todo/TodosState.ts @@ -0,0 +1,96 @@ +import { KeyValueStore } from "@effect/platform" +import { BrowserKeyValueStore } from "@effect/platform-browser" +import { Chunk, Console, Effect, Option, Schema, Stream, SubscriptionRef } from "effect" +import { Lens, Subscribable } from "effect-fc" +import { Todo } from "@/domain" + + +export class TodosState extends Effect.Service()("TodosState", { + scoped: Effect.fnUntraced(function*(key: string) { + const kv = yield* KeyValueStore.KeyValueStore + + const readFromLocalStorage = Console.log("Reading todos from local storage...").pipe( + Effect.andThen(kv.get(key)), + Effect.andThen(Option.match({ + onSome: Schema.decode( + Schema.parseJson(Schema.Chunk(Todo.TodoFromJson)) + ), + onNone: () => Effect.succeed(Chunk.empty()), + })) + ) + const saveToLocalStorage = (todos: Chunk.Chunk) => Effect.andThen( + Console.log("Saving todos to local storage..."), + Chunk.isNonEmpty(todos) + ? Effect.andThen( + Schema.encode( + Schema.parseJson(Schema.Chunk(Todo.TodoFromJson)) + )(todos), + v => kv.set(key, v), + ) + : kv.remove(key) + ) + + const lens = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(yield* readFromLocalStorage)) + yield* Effect.forkScoped(lens.changes.pipe( + Stream.debounce("500 millis"), + Stream.runForEach(saveToLocalStorage), + )) + yield* Effect.addFinalizer(() => Lens.get(lens).pipe( + Effect.andThen(saveToLocalStorage), + Effect.ignore, + )) + + const sizeSubscribable = Subscribable.map(lens, Chunk.size) + + const getElementLens = (id: string) => Lens.mapEffect( + lens, + Chunk.findFirst(v => v.id === id), + (a, b) => Effect.flatMap( + Chunk.findFirstIndex(a, v => v.id === id), + i => Chunk.replaceOption(a, i, b), + ) + ) + const getIndexSubscribable = (id: string) => Subscribable.mapEffect(lens, Chunk.findFirstIndex(v => v.id === id)) + + const moveLeft = (id: string) => Lens.updateEffect(lens, todos => Effect.Do.pipe( + Effect.bind("index", () => Chunk.findFirstIndex(todos, v => v.id === id)), + Effect.bind("todo", ({ index }) => Chunk.get(todos, index)), + Effect.bind("previous", ({ index }) => Chunk.get(todos, index - 1)), + Effect.andThen(({ todo, index, previous }) => index > 0 + ? todos.pipe( + Chunk.replace(index, previous), + Chunk.replace(index - 1, todo), + ) + : todos + ), + )) + const moveRight = (id: string) => Lens.updateEffect(lens, todos => Effect.Do.pipe( + Effect.bind("index", () => Chunk.findFirstIndex(todos, v => v.id === id)), + Effect.bind("todo", ({ index }) => Chunk.get(todos, index)), + Effect.bind("next", ({ index }) => Chunk.get(todos, index + 1)), + Effect.andThen(({ todo, index, next }) => index < Chunk.size(todos) - 1 + ? todos.pipe( + Chunk.replace(index, next), + Chunk.replace(index + 1, todo), + ) + : todos + ), + )) + const remove = (id: string) => Lens.updateEffect(lens, todos => Effect.andThen( + Chunk.findFirstIndex(todos, v => v.id === id), + index => Chunk.remove(todos, index), + )) + + return { + lens, + sizeSubscribable, + getElementLens, + getIndexSubscribable, + moveLeft, + moveRight, + remove, + } as const + }), + + dependencies: [BrowserKeyValueStore.layerLocalStorage], +}) {} diff --git a/packages/example-next/src/todo/TodosView.tsx b/packages/example-next/src/todo/TodosView.tsx new file mode 100644 index 0000000..b935681 --- /dev/null +++ b/packages/example-next/src/todo/TodosView.tsx @@ -0,0 +1,34 @@ +import { Container, Flex, Heading } from "@radix-ui/themes" +import { Chunk, Console, Effect } from "effect" +import { Component, Subscribable } from "effect-fc" +import { EditTodoView } from "./EditTodoView" +import { NewTodoView } from "./NewTodoView" +import { TodosState } from "./TodosState" + + +export class TodosView extends Component.make("TodosView")(function*() { + const state = yield* TodosState + const [todos] = yield* Subscribable.useAll([state.lens]) + + yield* Component.useOnMount(() => Effect.andThen( + Console.log("Todos mounted"), + Effect.addFinalizer(() => Console.log("Todos unmounted")), + )) + + const NewTodo = yield* NewTodoView.use + const EditTodo = yield* EditTodoView.use + + return ( + + Todos + + + + + {Chunk.map(todos, todo => + + )} + + + ) +}) {} diff --git a/packages/example-next/tsconfig.app.json b/packages/example-next/tsconfig.app.json index f95336a..b823bd2 100644 --- a/packages/example-next/tsconfig.app.json +++ b/packages/example-next/tsconfig.app.json @@ -22,6 +22,10 @@ "noFallthroughCasesInSwitch": true, "noUncheckedSideEffectImports": true, + "paths": { + "@/*": ["./src/*"] + }, + "plugins": [ { "name": "@effect/language-service" } ] diff --git a/packages/example-next/vite.config.ts b/packages/example-next/vite.config.ts index 5c94b15..79367b3 100644 --- a/packages/example-next/vite.config.ts +++ b/packages/example-next/vite.config.ts @@ -1,10 +1,22 @@ +import { tanstackRouter } from "@tanstack/router-plugin/vite" import react from "@vitejs/plugin-react" +import path from "node:path" import { defineConfig } from "vite" // https://vite.dev/config/ export default defineConfig({ plugins: [ + tanstackRouter({ + target: "react", + autoCodeSplitting: true, + }), react(), ], + + resolve: { + alias: { + "@": path.resolve(__dirname, "./src"), + }, + }, }) -- 2.52.0 From 31d87ffcabdd3651c5eb861d6043e0deefe8febb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 19 Jul 2026 04:00:46 +0200 Subject: [PATCH 060/133] Fix --- packages/example-next/src/domain/Todo.ts | 20 --- packages/example-next/src/domain/index.ts | 1 - .../src/lib/form/TextFieldFormInputView.tsx | 6 +- .../form/TextFieldOptionalFormInputView.tsx | 6 +- .../example-next/src/lib/schema/datetime.ts | 38 ----- packages/example-next/src/lib/schema/index.ts | 2 - packages/example-next/src/lib/schema/json.ts | 6 - packages/example-next/src/main.tsx | 100 +---------- packages/example-next/src/post.ts | 15 ++ packages/example-next/src/routeTree.gen.ts | 67 +------- packages/example-next/src/routes/__root.tsx | 6 +- packages/example-next/src/routes/async.tsx | 39 ++--- .../example-next/src/routes/dev/context.tsx | 42 ----- packages/example-next/src/routes/dev/memo.tsx | 37 ---- packages/example-next/src/routes/form.tsx | 158 +++++------------- packages/example-next/src/routes/index.tsx | 56 +++++-- packages/example-next/src/routes/query.tsx | 128 ++++++-------- packages/example-next/src/routes/result.tsx | 79 ++++----- packages/example-next/src/runtime.ts | 15 +- .../example-next/src/todo/EditTodoView.tsx | 88 ---------- .../example-next/src/todo/NewTodoView.tsx | 78 --------- .../example-next/src/todo/TodoFormSchema.ts | 9 - packages/example-next/src/todo/TodosState.ts | 96 ----------- packages/example-next/src/todo/TodosView.tsx | 34 ---- 24 files changed, 219 insertions(+), 907 deletions(-) delete mode 100644 packages/example-next/src/domain/Todo.ts delete mode 100644 packages/example-next/src/domain/index.ts delete mode 100644 packages/example-next/src/lib/schema/datetime.ts delete mode 100644 packages/example-next/src/lib/schema/index.ts delete mode 100644 packages/example-next/src/lib/schema/json.ts create mode 100644 packages/example-next/src/post.ts delete mode 100644 packages/example-next/src/routes/dev/context.tsx delete mode 100644 packages/example-next/src/routes/dev/memo.tsx delete mode 100644 packages/example-next/src/todo/EditTodoView.tsx delete mode 100644 packages/example-next/src/todo/NewTodoView.tsx delete mode 100644 packages/example-next/src/todo/TodoFormSchema.ts delete mode 100644 packages/example-next/src/todo/TodosState.ts delete mode 100644 packages/example-next/src/todo/TodosView.tsx diff --git a/packages/example-next/src/domain/Todo.ts b/packages/example-next/src/domain/Todo.ts deleted file mode 100644 index 500faab..0000000 --- a/packages/example-next/src/domain/Todo.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Schema } from "effect" -import { assertEncodedJsonifiable } from "@/lib/schema" - - -export class Todo extends Schema.Class("Todo")({ - _tag: Schema.tag("Todo"), - id: Schema.String, - content: Schema.String, - completedAt: Schema.OptionFromSelf(Schema.DateTimeUtcFromSelf), -}) {} - - -export const TodoFromJsonStruct = Schema.Struct({ - ...Todo.fields, - completedAt: Schema.Option(Schema.DateTimeUtc), -}).pipe( - assertEncodedJsonifiable -) - -export const TodoFromJson = Schema.compose(TodoFromJsonStruct, Todo) diff --git a/packages/example-next/src/domain/index.ts b/packages/example-next/src/domain/index.ts deleted file mode 100644 index 8d70c13..0000000 --- a/packages/example-next/src/domain/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * as Todo from "./Todo" diff --git a/packages/example-next/src/lib/form/TextFieldFormInputView.tsx b/packages/example-next/src/lib/form/TextFieldFormInputView.tsx index ca4f843..48241a9 100644 --- a/packages/example-next/src/lib/form/TextFieldFormInputView.tsx +++ b/packages/example-next/src/lib/form/TextFieldFormInputView.tsx @@ -1,6 +1,6 @@ import { Callout, Flex, Spinner, TextField } from "@radix-ui/themes" import { Array, Option, Struct } from "effect" -import { Component, Form, Subscribable } from "effect-fc" +import { Component, Form, View } from "effect-fc-next" import type * as React from "react" @@ -17,7 +17,7 @@ export const TextFieldFormInputView = Component.make("TextFieldFormInputView")(f props: TextFieldFormInputView.Props ) { const input = yield* Form.useInput(props.form, props) - const [issues, isValidating, isCommitting] = yield* Subscribable.useAll([ + const [issues, isValidating, isCommitting] = yield* View.useAll([ props.form.issues, props.form.isValidating, props.form.isCommitting, @@ -29,7 +29,7 @@ export const TextFieldFormInputView = Component.make("TextFieldFormInputView")(f value={input.value} onChange={e => input.setValue(e.target.value)} disabled={isCommitting} - {...Struct.omit(props, "form")} + {...Struct.omit(props, ["form"])} > {isValidating && diff --git a/packages/example-next/src/lib/form/TextFieldOptionalFormInputView.tsx b/packages/example-next/src/lib/form/TextFieldOptionalFormInputView.tsx index 68693ea..cd2bea4 100644 --- a/packages/example-next/src/lib/form/TextFieldOptionalFormInputView.tsx +++ b/packages/example-next/src/lib/form/TextFieldOptionalFormInputView.tsx @@ -1,6 +1,6 @@ import { Callout, Flex, Spinner, Switch, TextField } from "@radix-ui/themes" import { Array, Option, Struct } from "effect" -import { Component, Form, Subscribable } from "effect-fc" +import { Component, Form, View } from "effect-fc-next" import type * as React from "react" @@ -17,7 +17,7 @@ export const TextFieldOptionalFormInputView = Component.make("TextFieldOptionalF props: TextFieldOptionalFormInputView.Props ) { const input = yield* Form.useOptionalInput(props.form, props) - const [issues, isValidating, isCommitting] = yield* Subscribable.useAll([ + const [issues, isValidating, isCommitting] = yield* View.useAll([ props.form.issues, props.form.isValidating, props.form.isCommitting, @@ -29,7 +29,7 @@ export const TextFieldOptionalFormInputView = Component.make("TextFieldOptionalF value={input.value} onChange={e => input.setValue(e.target.value)} disabled={!input.enabled || isCommitting} - {...Struct.omit(props, "form", "defaultValue")} + {...Struct.omit(props, ["form", "defaultValue"])} > ParseResult.succeed(DateTime.toUtc(i)), - }, -) {} - -export class DateTimeZonedFromUtc extends Schema.transformOrFail( - Schema.DateTimeUtcFromSelf, - Schema.DateTimeZonedFromSelf, - { - strict: true, - encode: a => ParseResult.succeed(DateTime.toUtc(a)), - decode: DateTime.setZoneCurrent, - }, -) {} - -export class DateTimeUtcFromZonedInput extends Schema.transformOrFail( - Schema.String, - DateTimeUtcFromZoned, - { - strict: true, - encode: a => ParseResult.succeed(DateTime.formatIsoZoned(a).slice(0, 16)), - decode: (i, _, ast) => Effect.flatMap( - DateTime.CurrentTimeZone, - timeZone => Option.match(DateTime.makeZoned(i, { timeZone, adjustForTimeZone: true }), { - onSome: ParseResult.succeed, - onNone: () => ParseResult.fail(new ParseResult.Type(ast, i, `Unable to decode ${JSON.stringify(i)} into a DateTime.Zoned`)), - }), - ), - }, -) {} diff --git a/packages/example-next/src/lib/schema/index.ts b/packages/example-next/src/lib/schema/index.ts deleted file mode 100644 index 260895c..0000000 --- a/packages/example-next/src/lib/schema/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./datetime" -export * from "./json" diff --git a/packages/example-next/src/lib/schema/json.ts b/packages/example-next/src/lib/schema/json.ts deleted file mode 100644 index 5b69cc4..0000000 --- a/packages/example-next/src/lib/schema/json.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { Schema } from "effect" -import type { JsonValue } from "type-fest" - - -export const assertEncodedJsonifiable = , A, I extends JsonValue, R>(schema: S & Schema.Schema): S => schema -export const assertTypeJsonifiable = , A extends JsonValue, I, R>(schema: S & Schema.Schema): S => schema diff --git a/packages/example-next/src/main.tsx b/packages/example-next/src/main.tsx index 59d0c0c..1ed395f 100644 --- a/packages/example-next/src/main.tsx +++ b/packages/example-next/src/main.tsx @@ -1,103 +1,21 @@ -import { Effect, Layer, Schema, SubscriptionRef } from "effect" -import { - Component, - Form, - Lens, - LensForm, - MutationForm, - ReactRuntime, - View, -} from "effect-fc-next" +import { createRouter, RouterProvider } from "@tanstack/react-router" +import { ReactRuntime } from "effect-fc-next" import { StrictMode } from "react" import { createRoot } from "react-dom/client" -import "./index.css" +import { routeTree } from "./routeTree.gen" +import { runtime } from "./runtime" +const router = createRouter({ routeTree }) -const runtime = ReactRuntime.make(Layer.empty) - -const MutationFormExample = Component.make("MutationFormExample")(function*() { - const form = yield* Component.useOnMount(() => MutationForm.service({ - schema: Schema.String, - initialEncodedValue: "", - f: ([value]) => Effect.log(`Submitted: ${value}`), - })) - const input = yield* Form.useInput(form) - const [canCommit, isCommitting, issues] = yield* View.useAll([ - form.canCommit, - form.isCommitting, - form.issues, - ]) - const runPromise = yield* Component.useRunPromise() - - return ( -
-

MutationForm

-

Owns a draft and commits it explicitly through a Mutation.

-
{ - event.preventDefault() - void runPromise(form.submit) - }}> - input.setValue(event.currentTarget.value)} - disabled={isCommitting} - placeholder="Write something to submit" - /> - -
- {issues.map((issue) =>

{issue.message}

)} -
- ) -}).pipe(Component.withRuntime(runtime.context)) - -const LensFormExample = Component.make("LensFormExample")(function*() { - const target = yield* Component.useOnMount(() => Effect.map( - SubscriptionRef.make("A value owned by the application"), - Lens.fromSubscriptionRef, - )) - const form = yield* Component.useOnMount(() => LensForm.service({ - schema: Schema.String, - target, - })) - const input = yield* Form.useInput(form) - const [value, canCommit, isCommitting, issues] = yield* View.useAll([ - target, - form.canCommit, - form.isCommitting, - form.issues, - ]) - - return ( -
-

LensForm

-

Synchronizes valid input directly with a target Lens.

- input.setValue(event.currentTarget.value)} - disabled={!canCommit || isCommitting} - /> -

Target value: {value}

- {issues.map((issue) =>

{issue.message}

)} -
- ) -}).pipe(Component.withRuntime(runtime.context)) - -const App = () => ( -
-

Effect FC Next

-

Effect v4 examples for the finalized form API.

- - -
-) +declare module "@tanstack/react-router" { + interface Register { router: typeof router } +} // biome-ignore lint/style/noNonNullAssertion: the Vite template provides this element createRoot(document.getElementById("root")!).render( - + , ) diff --git a/packages/example-next/src/post.ts b/packages/example-next/src/post.ts new file mode 100644 index 0000000..2bbe6e6 --- /dev/null +++ b/packages/example-next/src/post.ts @@ -0,0 +1,15 @@ +import { Effect } from "effect" + + +export interface Post { + readonly title: string + readonly body: string +} + +export const fetchPost = (id: number) => Effect.tryPromise({ + try: () => + fetch(`https://jsonplaceholder.typicode.com/posts/${id}`) + .then(response => response.json() as Promise), + + catch: () => new Error("Unable to fetch post"), +}) diff --git a/packages/example-next/src/routeTree.gen.ts b/packages/example-next/src/routeTree.gen.ts index 3ad431c..5c09612 100644 --- a/packages/example-next/src/routeTree.gen.ts +++ b/packages/example-next/src/routeTree.gen.ts @@ -15,8 +15,6 @@ import { Route as BlankRouteImport } from './routes/blank' import { Route as FormRouteImport } from './routes/form' import { Route as QueryRouteImport } from './routes/query' import { Route as ResultRouteImport } from './routes/result' -import { Route as DevContextRouteImport } from './routes/dev/context' -import { Route as DevMemoRouteImport } from './routes/dev/memo' const IndexRoute = IndexRouteImport.update({ id: '/', @@ -48,16 +46,6 @@ const ResultRoute = ResultRouteImport.update({ path: '/result', getParentRoute: () => rootRouteImport, } as any) -const DevContextRoute = DevContextRouteImport.update({ - id: '/dev/context', - path: '/dev/context', - getParentRoute: () => rootRouteImport, -} as any) -const DevMemoRoute = DevMemoRouteImport.update({ - id: '/dev/memo', - path: '/dev/memo', - getParentRoute: () => rootRouteImport, -} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -66,8 +54,6 @@ export interface FileRoutesByFullPath { '/form': typeof FormRoute '/query': typeof QueryRoute '/result': typeof ResultRoute - '/dev/context': typeof DevContextRoute - '/dev/memo': typeof DevMemoRoute } export interface FileRoutesByTo { '/': typeof IndexRoute @@ -76,8 +62,6 @@ export interface FileRoutesByTo { '/form': typeof FormRoute '/query': typeof QueryRoute '/result': typeof ResultRoute - '/dev/context': typeof DevContextRoute - '/dev/memo': typeof DevMemoRoute } export interface FileRoutesById { __root__: typeof rootRouteImport @@ -87,40 +71,13 @@ export interface FileRoutesById { '/form': typeof FormRoute '/query': typeof QueryRoute '/result': typeof ResultRoute - '/dev/context': typeof DevContextRoute - '/dev/memo': typeof DevMemoRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: - | '/' - | '/async' - | '/blank' - | '/form' - | '/query' - | '/result' - | '/dev/context' - | '/dev/memo' + fullPaths: '/' | '/async' | '/blank' | '/form' | '/query' | '/result' fileRoutesByTo: FileRoutesByTo - to: - | '/' - | '/async' - | '/blank' - | '/form' - | '/query' - | '/result' - | '/dev/context' - | '/dev/memo' - id: - | '__root__' - | '/' - | '/async' - | '/blank' - | '/form' - | '/query' - | '/result' - | '/dev/context' - | '/dev/memo' + to: '/' | '/async' | '/blank' | '/form' | '/query' | '/result' + id: '__root__' | '/' | '/async' | '/blank' | '/form' | '/query' | '/result' fileRoutesById: FileRoutesById } export interface RootRouteChildren { @@ -130,8 +87,6 @@ export interface RootRouteChildren { FormRoute: typeof FormRoute QueryRoute: typeof QueryRoute ResultRoute: typeof ResultRoute - DevContextRoute: typeof DevContextRoute - DevMemoRoute: typeof DevMemoRoute } declare module '@tanstack/react-router' { @@ -178,20 +133,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ResultRouteImport parentRoute: typeof rootRouteImport } - '/dev/context': { - id: '/dev/context' - path: '/dev/context' - fullPath: '/dev/context' - preLoaderRoute: typeof DevContextRouteImport - parentRoute: typeof rootRouteImport - } - '/dev/memo': { - id: '/dev/memo' - path: '/dev/memo' - fullPath: '/dev/memo' - preLoaderRoute: typeof DevMemoRouteImport - parentRoute: typeof rootRouteImport - } } } @@ -202,8 +143,6 @@ const rootRouteChildren: RootRouteChildren = { FormRoute: FormRoute, QueryRoute: QueryRoute, ResultRoute: ResultRoute, - DevContextRoute: DevContextRoute, - DevMemoRoute: DevMemoRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) diff --git a/packages/example-next/src/routes/__root.tsx b/packages/example-next/src/routes/__root.tsx index 67b0910..c9826b6 100644 --- a/packages/example-next/src/routes/__root.tsx +++ b/packages/example-next/src/routes/__root.tsx @@ -7,7 +7,7 @@ import "../index.css" export const Route = createRootRoute({ - component: Root + component: Root, }) function Root() { @@ -17,6 +17,10 @@ function Root() { Index Blank + Async + Query + Result + Form diff --git a/packages/example-next/src/routes/async.tsx b/packages/example-next/src/routes/async.tsx index 62e0234..4be061b 100644 --- a/packages/example-next/src/routes/async.tsx +++ b/packages/example-next/src/routes/async.tsx @@ -1,30 +1,22 @@ -import { HttpClient } from "@effect/platform" import { Container, Flex, Heading, Slider, Text, TextField } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" -import { Array, Effect, flow, Option, Schema } from "effect" -import { Async, Component, Memoized } from "effect-fc" +import { Async, Component, Memoized } from "effect-fc-next" import * as React from "react" +import { fetchPost } from "@/post" import { runtime } from "@/runtime" -const Post = Schema.Struct({ - userId: Schema.Int, - id: Schema.Int, - title: Schema.String, - body: Schema.String, -}) - interface AsyncFetchPostViewProps { readonly id: number } -class AsyncFetchPostView extends Component.make("AsyncFetchPostView")(function*(props: AsyncFetchPostViewProps) { - const post = yield* Component.useOnChange(() => HttpClient.HttpClient.pipe( - Effect.tap(Effect.sleep("500 millis")), - Effect.andThen(client => client.get(`https://jsonplaceholder.typicode.com/posts/${ props.id }`)), - Effect.andThen(response => response.json), - Effect.andThen(Schema.decodeUnknown(Post)), - ), [props.id]) +const AsyncFetchPostView = Component.make("AsyncFetchPostView")(function*( + props: AsyncFetchPostViewProps, +) { + const post = yield* Component.useOnChange( + () => fetchPost(props.id), + [props.id], + ) return (
@@ -34,10 +26,9 @@ class AsyncFetchPostView extends Component.make("AsyncFetchPostView")(function*( ) }).pipe( Async.async, - Async.withOptions({ defaultFallback: Default fallback }), + Async.withOptions({ defaultFallback: Loading post... }), Memoized.memoized, -) {} - +) const AsyncRouteComponent = Component.make("AsyncRouteView")(function*() { const [text, setText] = React.useState("Typing here should not trigger a refetch of the post") @@ -50,12 +41,14 @@ const AsyncRouteComponent = Component.make("AsyncRouteView")(function*() { setText(e.currentTarget.value)} + onChange={event => setText(event.currentTarget.value)} /> setId(value ?? 1)} /> Loading post...} /> @@ -63,7 +56,7 @@ const AsyncRouteComponent = Component.make("AsyncRouteView")(function*() { ) }).pipe( - Component.withRuntime(runtime.context) + Component.withRuntime(runtime.context), ) export const Route = createFileRoute("/async")({ diff --git a/packages/example-next/src/routes/dev/context.tsx b/packages/example-next/src/routes/dev/context.tsx deleted file mode 100644 index 26048a9..0000000 --- a/packages/example-next/src/routes/dev/context.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { Container, Flex, Text, TextField } from "@radix-ui/themes" -import { createFileRoute } from "@tanstack/react-router" -import { Console, Effect } from "effect" -import { Component } from "effect-fc" -import * as React from "react" -import { runtime } from "@/runtime" - - -class SubService extends Effect.Service()("SubService", { - effect: (value: string) => Effect.succeed({ value }) -}) {} - -const SubComponent = Component.makeUntraced("SubComponent")(function*() { - const service = yield* SubService - yield* Component.useOnMount(() => Effect.gen(function*() { - yield* Effect.addFinalizer(() => Console.log("SubComponent unmounted")) - yield* Console.log("SubComponent mounted") - })) - - return {service.value} -}) - -const ContextView = Component.makeUntraced("ContextView")(function*() { - const [serviceValue, setServiceValue] = React.useState("test") - const SubServiceLayer = React.useMemo(() => SubService.Default(serviceValue), [serviceValue]) - const SubComponentFC = yield* Effect.provide(SubComponent.use, yield* Component.useContextFromLayer(SubServiceLayer)) - - return ( - - - setServiceValue(e.target.value)} /> - - - - ) -}).pipe( - Component.withRuntime(runtime.context) -) - -export const Route = createFileRoute("/dev/context")({ - component: ContextView -}) diff --git a/packages/example-next/src/routes/dev/memo.tsx b/packages/example-next/src/routes/dev/memo.tsx deleted file mode 100644 index ae672bd..0000000 --- a/packages/example-next/src/routes/dev/memo.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { Flex, Text, TextField } from "@radix-ui/themes" -import { createFileRoute } from "@tanstack/react-router" -import { GetRandomValues, makeUuid4 } from "@typed/id" -import { Effect } from "effect" -import { Component, Memoized } from "effect-fc" -import * as React from "react" -import { runtime } from "@/runtime" - - -const RouteComponent = Component.makeUntraced("RouteComponent")(function*() { - const [value, setValue] = React.useState("") - - return ( - - setValue(e.target.value)} - /> - - {yield* Effect.map(SubComponent.use, FC => )} - {yield* Effect.map(MemoizedSubComponent.use, FC => )} - - ) -}).pipe( - Component.withRuntime(runtime.context) -) - -class SubComponent extends Component.makeUntraced("SubComponent")(function*() { - const id = yield* makeUuid4.pipe(Effect.provide(GetRandomValues.CryptoRandom)) - return {id} -}) {} - -class MemoizedSubComponent extends Memoized.memoized(SubComponent) {} - -export const Route = createFileRoute("/dev/memo")({ - component: RouteComponent, -}) diff --git a/packages/example-next/src/routes/form.tsx b/packages/example-next/src/routes/form.tsx index 9ce3fae..88885a4 100644 --- a/packages/example-next/src/routes/form.tsx +++ b/packages/example-next/src/routes/form.tsx @@ -1,143 +1,63 @@ -import { Button, Container, Flex, Text } from "@radix-ui/themes" +import { Button, Container, Flex, Text, TextField } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" -import { Console, Effect, Match, Option, ParseResult, Schema } from "effect" -import { Component, Form, SubmittableForm, Subscribable } from "effect-fc" -import { TextFieldFormInputView } from "@/lib/form/TextFieldFormInputView" -import { TextFieldOptionalFormInputView } from "@/lib/form/TextFieldOptionalFormInputView" -import { DateTimeUtcFromZonedInput } from "@/lib/schema" +import { Effect, Schema } from "effect" +import { Component, Form, MutationForm, View } from "effect-fc-next" import { runtime } from "@/runtime" -const email = Schema.pattern( - /^(?!\.)(?!.*\.\.)([A-Z0-9_+-.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9-]*\.)+[A-Z]{2,}$/i, - - { - identifier: "email", - title: "email", - message: () => "Not an email address", - }, -) - -const RegisterFormSchema = Schema.Struct({ - email: Schema.String.pipe(email), - password: Schema.String.pipe(Schema.minLength(3)), - birth: Schema.OptionFromSelf(DateTimeUtcFromZonedInput), -}) - -const RegisterFormSubmitSchema = Schema.Struct({ - email: Schema.transformOrFail( - Schema.String, - Schema.String, - { - decode: (input, _options, ast) => input !== "admin@admin.com" - ? ParseResult.succeed(input) - : ParseResult.fail(new ParseResult.Type(ast, input, "This email is already in use.")), - encode: ParseResult.succeed, - }, - ), +const RegisterSchema = Schema.Struct({ + email: Schema.String, password: Schema.String, - birth: Schema.OptionFromSelf(Schema.DateTimeUtcFromSelf), }) -class RegisterFormService extends Effect.Service()("RegisterFormService", { - scoped: Effect.gen(function*() { - const form = yield* SubmittableForm.service({ - schema: RegisterFormSchema.pipe( - Schema.compose( - Schema.transformOrFail( - Schema.typeSchema(RegisterFormSchema), - Schema.typeSchema(RegisterFormSchema), - { - decode: v => Effect.andThen(Effect.sleep("500 millis"), ParseResult.succeed(v)), - encode: ParseResult.succeed, - }, - ), - ), - ), - - initialEncodedValue: { email: "", password: "", birth: Option.none() }, - f: Effect.fnUntraced(function*([value]) { - yield* Effect.sleep("500 millis") - return yield* Schema.decode(RegisterFormSubmitSchema)(value) - }), - }) - - return { - form, - emailField: Form.focusObjectOn(form, "email"), - passwordField: Form.focusObjectOn(form, "password"), - birthField: Form.focusObjectOn(form, "birth"), - } as const - }) -}) {} - -class RegisterFormView extends Component.make("RegisterFormView")(function*() { - const form = yield* RegisterFormService - const [canCommit, submitResult] = yield* Subscribable.useAll([ - form.form.canCommit, - form.form.mutation.result, - ]) - - const runPromise = yield* Component.useRunPromise() - const TextFieldFormInput = yield* TextFieldFormInputView.use - const TextFieldOptionalFormInput = yield* TextFieldOptionalFormInputView.use - - yield* Component.useOnMount(() => Effect.gen(function*() { - yield* Effect.addFinalizer(() => Console.log("RegisterFormView unmounted")) - yield* Console.log("RegisterFormView mounted") +const RegisterRouteComponent = Component.make("RegisterRouteView")(function*() { + const form = yield* Component.useOnMount(() => MutationForm.service({ + schema: RegisterSchema, + initialEncodedValue: { email: "", password: "" }, + f: ([value]) => Effect.log(`Registered ${value.email}`), })) - + const emailField = yield* Form.useInput( + Form.focusObjectOn(form, "email"), + ) + const passwordField = yield* Form.useInput( + Form.focusObjectOn(form, "password"), + ) + const [canCommit, isCommitting] = yield* View.useAll([ + form.canCommit, + form.isCommitting, + ]) + const runPromise = yield* Component.useRunPromise() return ( -
{ - e.preventDefault() - void runPromise(form.form.submit) + { + event.preventDefault() + void runPromise(form.submit) }}> - emailField.setValue(event.currentTarget.value)} + placeholder="Email" /> - - passwordField.setValue(event.currentTarget.value)} + placeholder="Password" + type="password" /> - - - - +
- - {Match.value(submitResult).pipe( - Match.tag("Initial", () => <>), - Match.tag("Running", () => Submitting...), - Match.tag("Success", () => Submitted successfully!), - Match.tag("Failure", e => Error: {e.cause.toString()}), - Match.exhaustive, - )} + A MutationForm validates local input, then submits it.
) -}) {} - -const RegisterPage = Component.make("RegisterPageView")(function*() { - const RegisterForm = yield* Effect.provide( - RegisterFormView.use, - yield* Component.useContextFromLayer(RegisterFormService.Default), - ) - - return }).pipe( - Component.withRuntime(runtime.context) + Component.withRuntime(runtime.context), ) - export const Route = createFileRoute("/form")({ - component: RegisterPage + component: RegisterRouteComponent, }) diff --git a/packages/example-next/src/routes/index.tsx b/packages/example-next/src/routes/index.tsx index d747b39..0b4d271 100644 --- a/packages/example-next/src/routes/index.tsx +++ b/packages/example-next/src/routes/index.tsx @@ -1,24 +1,56 @@ +import { Button, Container, Flex, Text, TextField } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" -import { Effect } from "effect" -import { Component } from "effect-fc" +import { Effect, SubscriptionRef } from "effect" +import { Component, Lens, View } from "effect-fc-next" import { runtime } from "@/runtime" -import { TodosState } from "@/todo/TodosState" -import { TodosView } from "@/todo/TodosView" -const TodosStateLive = TodosState.Default("todos") +const TodoRouteComponent = Component.make("TodoRouteView")(function*() { + const todosLens = yield* Component.useOnMount(() => Effect.map( + SubscriptionRef.make([]), + Lens.fromSubscriptionRef, + )) + const draftLens = yield* Component.useOnMount(() => Effect.map( + SubscriptionRef.make(""), + Lens.fromSubscriptionRef, + )) -const Index = Component.make("IndexView")(function*() { - const Todos = yield* Effect.provide( - TodosView.use, - yield* Component.useContextFromLayer(TodosStateLive), + const [todos] = yield* View.useAll([todosLens]) + const [draft, setDraft] = yield* Lens.useState(draftLens) + const runPromise = yield* Component.useRunPromise() + + const addTodo = Lens.update(todosLens, todos => + draft.trim() === "" + ? todos + : [...todos, draft.trim()], + ).pipe( + Effect.andThen(Lens.set(draftLens, "")), ) - return + return ( + + + A small Effect v4 todo state example backed by a Lens. + + + setDraft(event.currentTarget.value)} + /> + + + + + {todos.map(todo => • {todo})} + + + ) }).pipe( - Component.withRuntime(runtime.context) + Component.withRuntime(runtime.context), ) export const Route = createFileRoute("/")({ - component: Index + component: TodoRouteComponent, }) diff --git a/packages/example-next/src/routes/query.tsx b/packages/example-next/src/routes/query.tsx index 047560e..a97a8bf 100644 --- a/packages/example-next/src/routes/query.tsx +++ b/packages/example-next/src/routes/query.tsx @@ -1,117 +1,85 @@ -import { HttpClient, type HttpClientError } from "@effect/platform" import { Button, Container, Flex, Heading, Slider, Text } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" -import { Array, Cause, Chunk, Console, Effect, flow, Match, Option, Schema, Stream, SubscriptionRef } from "effect" -import { Component, ErrorObserver, Lens, Mutation, Query, Result, Subscribable } from "effect-fc" +import { Effect, SubscriptionRef } from "effect" +import { AsyncResult } from "effect/unstable/reactivity" +import { Component, Lens, Mutation, Query, View } from "effect-fc-next" +import { fetchPost, type Post } from "@/post" import { runtime } from "@/runtime" -const Post = Schema.Struct({ - userId: Schema.Int, - id: Schema.Int, - title: Schema.String, - body: Schema.String, -}) +interface PostResultViewProps { + readonly result: AsyncResult.AsyncResult +} -const ResultView = Component.make("ResultView")(function*() { - const runPromise = yield* Component.useRunPromise() +const PostResultView = (props: PostResultViewProps) => + AsyncResult.match(props.result, { + onInitial: () => Loading..., + onFailure: () => Request failed., + onSuccess: result => ( + <> + {result.value.title} + {result.value.body} + + ), + }) +const QueryRouteComponent = Component.make("QueryRouteView")(function*() { const [idLens, query, mutation] = yield* Component.useOnMount(() => Effect.gen(function*() { const idLens = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(1)) - const key = Stream.map(idLens.changes, id => [id] as const) - const query = yield* Query.service({ - key, - f: ([id]) => HttpClient.HttpClient.pipe( - Effect.tap(Effect.sleep("500 millis")), - Effect.andThen(client => client.get(`https://jsonplaceholder.typicode.com/posts/${ id }`)), - Effect.andThen(response => response.json), - Effect.andThen(Schema.decodeUnknown(Post)), - ), + key: idLens, + f: fetchPost, staleTime: "10 seconds", }) - const mutation = yield* Mutation.make({ - f: ([id]: readonly [id: number]) => HttpClient.HttpClient.pipe( - Effect.tap(Effect.sleep("500 millis")), - Effect.andThen(client => client.get(`https://jsonplaceholder.typicode.com/posts/${ id }`)), - Effect.andThen(response => response.json), - Effect.andThen(Schema.decodeUnknown(Post)), - ), + f: fetchPost, }) return [idLens, query, mutation] as const })) - const [id, setId] = yield* Lens.useState(idLens) - const [queryResult, mutationResult] = yield* Subscribable.useAll([query.result, mutation.result]) - - yield* Component.useOnMount(() => ErrorObserver.ErrorObserver().pipe( - Effect.andThen(observer => observer.subscribe), - Effect.andThen(Stream.fromQueue), - Stream.unwrapScoped, - Stream.runForEach(flow( - Cause.failures, - Chunk.findFirst(e => e._tag === "RequestError" || e._tag === "ResponseError"), - Option.match({ - onSome: e => Console.log("ResultView HttpClient error", e), - onNone: () => Effect.void, - }), - )), - Effect.forkScoped, - )) + const [id] = yield* View.useAll([idLens]) + const [queryState, mutationResult] = yield* View.useAll([ + query.state, + mutation.state, + ]) + const runPromise = yield* Component.useRunPromise() return ( + void runPromise(Lens.set(idLens, value ?? 1)) + } /> -
- {Match.value(queryResult).pipe( - Match.tag("Running", () => Loading...), - Match.tag("Success", result => <> - {result.value.title} - {result.value.body} - {Result.hasRefreshingFlag(result) && Refreshing...} - ), - Match.tag("Failure", result => - An error has occured: {result.cause.toString()} - ), - Match.orElse(() => <>), - )} -
+ - - + + -
- {Match.value(mutationResult).pipe( - Match.tag("Running", () => Loading...), - Match.tag("Success", result => <> - {result.value.title} - {result.value.body} - {Result.hasRefreshingFlag(result) && Refreshing...} - ), - Match.tag("Failure", result => - An error has occured: {result.cause.toString()} - ), - Match.orElse(() => <>), - )} -
+ - - - +
) -}) +}).pipe( + Component.withRuntime(runtime.context), +) export const Route = createFileRoute("/query")({ - component: Component.withRuntime(ResultView, runtime.context) + component: QueryRouteComponent, }) diff --git a/packages/example-next/src/routes/result.tsx b/packages/example-next/src/routes/result.tsx index 0828f37..bfcdb0c 100644 --- a/packages/example-next/src/routes/result.tsx +++ b/packages/example-next/src/routes/result.tsx @@ -1,60 +1,45 @@ -import { HttpClient, type HttpClientError } from "@effect/platform" -import { Container, Heading, Text } from "@radix-ui/themes" +import { Button, Container, Flex, Heading, Text } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" -import { Cause, Chunk, Console, Effect, flow, Match, Option, Schema, Stream } from "effect" -import { Component, ErrorObserver, Result, Subscribable } from "effect-fc" +import { AsyncResult } from "effect/unstable/reactivity" +import { Component, Mutation, View } from "effect-fc-next" +import { fetchPost, type Post } from "@/post" import { runtime } from "@/runtime" -const Post = Schema.Struct({ - userId: Schema.Int, - id: Schema.Int, - title: Schema.String, - body: Schema.String, -}) +const PostResultView = (props: { readonly result: AsyncResult.AsyncResult }) => + AsyncResult.match(props.result, { + onInitial: () => Ready to load., + onFailure: () => Request failed., + onSuccess: result => ( + <> + {result.value.title} + {result.value.body} + + ), + }) -const ResultView = Component.makeUntraced("Result")(function*() { - const [resultSubscribable] = yield* Component.useOnMount(() => HttpClient.HttpClient.pipe( - Effect.andThen(client => client.get("https://jsonplaceholder.typicode.com/posts/1")), - Effect.andThen(response => response.json), - Effect.andThen(Schema.decodeUnknown(Post)), - Effect.tap(Effect.sleep("250 millis")), - Result.forkEffect, - )) - const [result] = yield* Subscribable.useAll([resultSubscribable]) - - yield* Component.useOnMount(() => ErrorObserver.ErrorObserver().pipe( - Effect.andThen(observer => observer.subscribe), - Effect.andThen(Stream.fromQueue), - Stream.unwrapScoped, - Stream.runForEach(flow( - Cause.failures, - Chunk.findFirst(e => e._tag === "RequestError" || e._tag === "ResponseError"), - Option.match({ - onSome: e => Console.log("ResultView HttpClient error", e), - onNone: () => Effect.void, - }), - )), - Effect.forkScoped, - )) +const ResultRouteComponent = Component.make("ResultRouteView")(function*() { + const mutation = yield* Component.useOnMount(() => Mutation.make({ + f: (_: undefined) => fetchPost(1), + })) + const [result] = yield* View.useAll([mutation.state]) + const runPromise = yield* Component.useRunPromise() return ( - {Match.value(result).pipe( - Match.tag("Running", () => Loading...), - Match.tag("Success", result => <> - {result.value.title} - {result.value.body} - ), - Match.tag("Failure", result => - An error has occured: {result.cause.toString()} - ), - Match.orElse(() => <>), - )} + + + + + ) -}) +}).pipe( + Component.withRuntime(runtime.context), +) export const Route = createFileRoute("/result")({ - component: Component.withRuntime(ResultView, runtime.context) + component: ResultRouteComponent, }) diff --git a/packages/example-next/src/runtime.ts b/packages/example-next/src/runtime.ts index 3467529..e09447f 100644 --- a/packages/example-next/src/runtime.ts +++ b/packages/example-next/src/runtime.ts @@ -1,15 +1,4 @@ -import { FetchHttpClient } from "@effect/platform" -import { Clipboard, Geolocation, Permissions } from "@effect/platform-browser" -import { DateTime, Layer } from "effect" -import { ReactRuntime } from "effect-fc" +import { QueryClient, ReactRuntime } from "effect-fc-next" -export const AppLive = Layer.empty.pipe( - Layer.provideMerge(DateTime.layerCurrentZoneLocal), - Layer.provideMerge(Clipboard.layer), - Layer.provideMerge(Geolocation.layer), - Layer.provideMerge(Permissions.layer), - Layer.provideMerge(FetchHttpClient.layer), -) - -export const runtime = ReactRuntime.make(AppLive) +export const runtime = ReactRuntime.make(QueryClient.layer()) diff --git a/packages/example-next/src/todo/EditTodoView.tsx b/packages/example-next/src/todo/EditTodoView.tsx deleted file mode 100644 index 69a9016..0000000 --- a/packages/example-next/src/todo/EditTodoView.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { Box, Flex, IconButton } from "@radix-ui/themes" -import { Effect } from "effect" -import { Component, Form, Subscribable, SynchronizedForm } from "effect-fc" -import { FaArrowDown, FaArrowUp } from "react-icons/fa" -import { FaDeleteLeft } from "react-icons/fa6" -import { TextFieldFormInputView } from "@/lib/form/TextFieldFormInputView" -import { TextFieldOptionalFormInputView } from "@/lib/form/TextFieldOptionalFormInputView" -import { TodoFormSchema } from "./TodoFormSchema" -import { TodosState } from "./TodosState" - - -export interface EditTodoViewProps { - readonly id: string -} - -export class EditTodoView extends Component.make("TodoView")(function*(props: EditTodoViewProps) { - const state = yield* TodosState - - const [ - indexSubscribable, - contentField, - completedAtField, - ] = yield* Component.useOnChange(() => Effect.gen(function*() { - const indexSubscribable = state.getIndexSubscribable(props.id) - - const form = yield* SynchronizedForm.service({ - schema: TodoFormSchema, - target: state.getElementLens(props.id), - }) - - return [ - indexSubscribable, - Form.focusObjectOn(form, "content"), - Form.focusObjectOn(form, "completedAt"), - ] as const - }), [props.id]) - - const [index, size] = yield* Subscribable.useAll([ - indexSubscribable, - state.sizeSubscribable, - ]) - - const runSync = yield* Component.useRunSync() - const TextFieldFormInput = yield* TextFieldFormInputView.use - const TextFieldOptionalFormInput = yield* TextFieldOptionalFormInputView.use - - - return ( - - - - - - - - - - - - - runSync(state.moveLeft(props.id))} - > - - - - = size - 1} - onClick={() => runSync(state.moveRight(props.id))} - > - - - - runSync(state.remove(props.id))}> - - - - - ) -}) {} diff --git a/packages/example-next/src/todo/NewTodoView.tsx b/packages/example-next/src/todo/NewTodoView.tsx deleted file mode 100644 index e382778..0000000 --- a/packages/example-next/src/todo/NewTodoView.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { Box, Button, Flex } from "@radix-ui/themes" -import { GetRandomValues, makeUuid4 } from "@typed/id" -import { Chunk, type DateTime, Effect, Option, Schema } from "effect" -import { Component, Form, Lens, SubmittableForm, Subscribable } from "effect-fc" -import * as Domain from "@/domain" -import { TextFieldFormInputView } from "@/lib/form/TextFieldFormInputView" -import { TextFieldOptionalFormInputView } from "@/lib/form/TextFieldOptionalFormInputView" -import { TodoFormSchema } from "./TodoFormSchema" -import { TodosState } from "./TodosState" - - -const makeTodo = makeUuid4.pipe( - Effect.map(id => Domain.Todo.Todo.make({ - id, - content: "", - completedAt: Option.none(), - })), - Effect.provide(GetRandomValues.CryptoRandom), -) - - -export class NewTodoView extends Component.make("NewTodoView")(function*() { - const state = yield* TodosState - - const [ - form, - contentField, - completedAtField, - ] = yield* Component.useOnMount(() => Effect.gen(function*() { - const form = yield* SubmittableForm.service({ - schema: TodoFormSchema, - initialEncodedValue: yield* Schema.encode(TodoFormSchema)(yield* makeTodo), - f: ([todo, form]) => Lens.update(state.lens, Chunk.prepend(todo)).pipe( - Effect.andThen(makeTodo), - Effect.andThen(Schema.encode(TodoFormSchema)), - Effect.andThen(v => Lens.set(form.encodedValue, v)), - ), - }) - - return [ - form, - Form.focusObjectOn(form, "content"), - Form.focusObjectOn(form, "completedAt"), - ] as const - })) - - const [canCommit] = yield* Subscribable.useAll([form.canCommit]) - - const runPromise = yield* Component.useRunPromise() - const TextFieldFormInput = yield* TextFieldFormInputView.use - const TextFieldOptionalFormInput = yield* TextFieldOptionalFormInputView.use - - - return ( - - - - - - - - - - - - - - ) -}) {} diff --git a/packages/example-next/src/todo/TodoFormSchema.ts b/packages/example-next/src/todo/TodoFormSchema.ts deleted file mode 100644 index b2d4153..0000000 --- a/packages/example-next/src/todo/TodoFormSchema.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Schema } from "effect" -import * as Domain from "@/domain" -import { DateTimeUtcFromZonedInput } from "@/lib/schema" - - -export const TodoFormSchema = Schema.compose(Schema.Struct({ - ...Domain.Todo.Todo.fields, - completedAt: Schema.OptionFromSelf(DateTimeUtcFromZonedInput), -}), Domain.Todo.Todo) diff --git a/packages/example-next/src/todo/TodosState.ts b/packages/example-next/src/todo/TodosState.ts deleted file mode 100644 index d77041d..0000000 --- a/packages/example-next/src/todo/TodosState.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { KeyValueStore } from "@effect/platform" -import { BrowserKeyValueStore } from "@effect/platform-browser" -import { Chunk, Console, Effect, Option, Schema, Stream, SubscriptionRef } from "effect" -import { Lens, Subscribable } from "effect-fc" -import { Todo } from "@/domain" - - -export class TodosState extends Effect.Service()("TodosState", { - scoped: Effect.fnUntraced(function*(key: string) { - const kv = yield* KeyValueStore.KeyValueStore - - const readFromLocalStorage = Console.log("Reading todos from local storage...").pipe( - Effect.andThen(kv.get(key)), - Effect.andThen(Option.match({ - onSome: Schema.decode( - Schema.parseJson(Schema.Chunk(Todo.TodoFromJson)) - ), - onNone: () => Effect.succeed(Chunk.empty()), - })) - ) - const saveToLocalStorage = (todos: Chunk.Chunk) => Effect.andThen( - Console.log("Saving todos to local storage..."), - Chunk.isNonEmpty(todos) - ? Effect.andThen( - Schema.encode( - Schema.parseJson(Schema.Chunk(Todo.TodoFromJson)) - )(todos), - v => kv.set(key, v), - ) - : kv.remove(key) - ) - - const lens = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(yield* readFromLocalStorage)) - yield* Effect.forkScoped(lens.changes.pipe( - Stream.debounce("500 millis"), - Stream.runForEach(saveToLocalStorage), - )) - yield* Effect.addFinalizer(() => Lens.get(lens).pipe( - Effect.andThen(saveToLocalStorage), - Effect.ignore, - )) - - const sizeSubscribable = Subscribable.map(lens, Chunk.size) - - const getElementLens = (id: string) => Lens.mapEffect( - lens, - Chunk.findFirst(v => v.id === id), - (a, b) => Effect.flatMap( - Chunk.findFirstIndex(a, v => v.id === id), - i => Chunk.replaceOption(a, i, b), - ) - ) - const getIndexSubscribable = (id: string) => Subscribable.mapEffect(lens, Chunk.findFirstIndex(v => v.id === id)) - - const moveLeft = (id: string) => Lens.updateEffect(lens, todos => Effect.Do.pipe( - Effect.bind("index", () => Chunk.findFirstIndex(todos, v => v.id === id)), - Effect.bind("todo", ({ index }) => Chunk.get(todos, index)), - Effect.bind("previous", ({ index }) => Chunk.get(todos, index - 1)), - Effect.andThen(({ todo, index, previous }) => index > 0 - ? todos.pipe( - Chunk.replace(index, previous), - Chunk.replace(index - 1, todo), - ) - : todos - ), - )) - const moveRight = (id: string) => Lens.updateEffect(lens, todos => Effect.Do.pipe( - Effect.bind("index", () => Chunk.findFirstIndex(todos, v => v.id === id)), - Effect.bind("todo", ({ index }) => Chunk.get(todos, index)), - Effect.bind("next", ({ index }) => Chunk.get(todos, index + 1)), - Effect.andThen(({ todo, index, next }) => index < Chunk.size(todos) - 1 - ? todos.pipe( - Chunk.replace(index, next), - Chunk.replace(index + 1, todo), - ) - : todos - ), - )) - const remove = (id: string) => Lens.updateEffect(lens, todos => Effect.andThen( - Chunk.findFirstIndex(todos, v => v.id === id), - index => Chunk.remove(todos, index), - )) - - return { - lens, - sizeSubscribable, - getElementLens, - getIndexSubscribable, - moveLeft, - moveRight, - remove, - } as const - }), - - dependencies: [BrowserKeyValueStore.layerLocalStorage], -}) {} diff --git a/packages/example-next/src/todo/TodosView.tsx b/packages/example-next/src/todo/TodosView.tsx deleted file mode 100644 index b935681..0000000 --- a/packages/example-next/src/todo/TodosView.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { Container, Flex, Heading } from "@radix-ui/themes" -import { Chunk, Console, Effect } from "effect" -import { Component, Subscribable } from "effect-fc" -import { EditTodoView } from "./EditTodoView" -import { NewTodoView } from "./NewTodoView" -import { TodosState } from "./TodosState" - - -export class TodosView extends Component.make("TodosView")(function*() { - const state = yield* TodosState - const [todos] = yield* Subscribable.useAll([state.lens]) - - yield* Component.useOnMount(() => Effect.andThen( - Console.log("Todos mounted"), - Effect.addFinalizer(() => Console.log("Todos unmounted")), - )) - - const NewTodo = yield* NewTodoView.use - const EditTodo = yield* EditTodoView.use - - return ( - - Todos - - - - - {Chunk.map(todos, todo => - - )} - - - ) -}) {} -- 2.52.0 From f22743eefee5a128377d8dd71ae470b6cbce9543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 19 Jul 2026 18:20:22 +0200 Subject: [PATCH 061/133] Fix --- packages/effect-fc-next/src/ReactRuntime.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/effect-fc-next/src/ReactRuntime.ts b/packages/effect-fc-next/src/ReactRuntime.ts index 87023f3..29c738a 100644 --- a/packages/effect-fc-next/src/ReactRuntime.ts +++ b/packages/effect-fc-next/src/ReactRuntime.ts @@ -26,7 +26,7 @@ export const make = ( ): ReactRuntime | R, ER> => Object.setPrototypeOf( Object.assign(function() {}, { runtime: ManagedRuntime.make( - Layer.mergeAll(preludeLayer, layer), + Layer.merge(preludeLayer, layer), { memoMap }, ), // biome-ignore lint/style/noNonNullAssertion: context initialization -- 2.52.0 From 1dc83687500a430ea3226de312147f04ad3ed690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 19 Jul 2026 18:44:01 +0200 Subject: [PATCH 062/133] Rename withRuntime to withContext --- packages/effect-fc-next/README.md | 2 +- packages/effect-fc-next/src/Component.ts | 8 ++++---- packages/effect-fc-next/test/Component.test.tsx | 14 +++++++------- packages/effect-fc-next/test/Lens.test.tsx | 8 ++++---- packages/effect-fc-next/test/View.test.tsx | 4 ++-- packages/example-next/src/routes/async.tsx | 2 +- packages/example-next/src/routes/form.tsx | 2 +- packages/example-next/src/routes/index.tsx | 2 +- packages/example-next/src/routes/query.tsx | 2 +- packages/example-next/src/routes/result.tsx | 2 +- 10 files changed, 23 insertions(+), 23 deletions(-) diff --git a/packages/effect-fc-next/README.md b/packages/effect-fc-next/README.md index 5c164f8..2ce8f10 100644 --- a/packages/effect-fc-next/README.md +++ b/packages/effect-fc-next/README.md @@ -47,7 +47,7 @@ const Index = Component.make("IndexView")(function*() { return }).pipe( - Component.withRuntime(runtime.context) + Component.withContext(runtime.context) ) export const Route = createFileRoute("/")({ diff --git a/packages/effect-fc-next/src/Component.ts b/packages/effect-fc-next/src/Component.ts index bec0610..d457468 100644 --- a/packages/effect-fc-next/src/Component.ts +++ b/packages/effect-fc-next/src/Component.ts @@ -612,12 +612,12 @@ export const withOptions: { * * // Route definition * export const Route = createFileRoute("/")({ - * component: Component.withRuntime(HomePage, runtime.context) + * component: Component.withContext(HomePage, runtime.context) * }) * ``` * */ -export const withRuntime: { +export const withContext: {

( context: React.Context>, ): (self: Component, F>) => F @@ -628,9 +628,9 @@ export const withRuntime: { } = Function.dual(2,

( self: Component, context: React.Context>, -) => function WithRuntime(props: P) { +) => function WithContext(props: P) { return React.createElement( - Effect.runSyncWith(React.useContext(context))(self.use) as React.FC

, + Effect.runSyncWith(React.useContext(context))(self.use), props, ) }) diff --git a/packages/effect-fc-next/test/Component.test.tsx b/packages/effect-fc-next/test/Component.test.tsx index 9401608..0abd55e 100644 --- a/packages/effect-fc-next/test/Component.test.tsx +++ b/packages/effect-fc-next/test/Component.test.tsx @@ -22,7 +22,7 @@ describe("Component", () => { const value = yield* Component.useOnMount(onMount) return

{value}
}).pipe( - Component.withRuntime(runtime.context) + Component.withContext(runtime.context) ) const view = render( @@ -58,7 +58,7 @@ describe("Component", () => { return
{result}
}).pipe( - Component.withRuntime(runtime.context) + Component.withContext(runtime.context) ) const view = render( @@ -109,7 +109,7 @@ describe("Component", () => { return
{result}
}).pipe( - Component.withRuntime(runtime.context) + Component.withContext(runtime.context) ) const view = render( @@ -153,7 +153,7 @@ describe("Component", () => { return
{props.value}
}).pipe( - Component.withRuntime(runtime.context) + Component.withContext(runtime.context) ) const view = render( @@ -208,7 +208,7 @@ describe("Component", () => { return
{callback(1)}
}).pipe( - Component.withRuntime(runtime.context) + Component.withContext(runtime.context) ) const view = render( @@ -263,7 +263,7 @@ describe("Component", () => { return
{result}
}).pipe( - Component.withRuntime(runtime.context) + Component.withContext(runtime.context) ) const view = render( @@ -324,7 +324,7 @@ describe("Component", () => { return }).pipe( - Component.withRuntime(runtime.context) + Component.withContext(runtime.context) ) const view = render( diff --git a/packages/effect-fc-next/test/Lens.test.tsx b/packages/effect-fc-next/test/Lens.test.tsx index 3407f14..093b7bf 100644 --- a/packages/effect-fc-next/test/Lens.test.tsx +++ b/packages/effect-fc-next/test/Lens.test.tsx @@ -34,7 +34,7 @@ describe("Lens", () => { ) }).pipe( - Component.withRuntime(runtime.context) + Component.withContext(runtime.context) ) const view = render( @@ -68,7 +68,7 @@ describe("Lens", () => { return
{value.label}
}).pipe( - Component.withRuntime(runtime.context) + Component.withContext(runtime.context) ) const view = render( @@ -104,7 +104,7 @@ describe("Lens", () => { return }).pipe( - Component.withRuntime(runtime.context) + Component.withContext(runtime.context) ) const view = render( @@ -140,7 +140,7 @@ describe("Lens", () => { return
{value.label}
}).pipe( - Component.withRuntime(runtime.context) + Component.withContext(runtime.context) ) const view = render( diff --git a/packages/effect-fc-next/test/View.test.tsx b/packages/effect-fc-next/test/View.test.tsx index 3065a8e..301b6de 100644 --- a/packages/effect-fc-next/test/View.test.tsx +++ b/packages/effect-fc-next/test/View.test.tsx @@ -31,7 +31,7 @@ describe("View", () => { return
{`${currentCount}:${currentLabel}`}
}).pipe( - Component.withRuntime(runtime.context) + Component.withContext(runtime.context) ) const view = render( @@ -67,7 +67,7 @@ describe("View", () => { return
{`${currentItem.label}:${currentFlag ? "on" : "off"}`}
}).pipe( - Component.withRuntime(runtime.context) + Component.withContext(runtime.context) ) const view = render( diff --git a/packages/example-next/src/routes/async.tsx b/packages/example-next/src/routes/async.tsx index 4be061b..44c41a9 100644 --- a/packages/example-next/src/routes/async.tsx +++ b/packages/example-next/src/routes/async.tsx @@ -56,7 +56,7 @@ const AsyncRouteComponent = Component.make("AsyncRouteView")(function*() { ) }).pipe( - Component.withRuntime(runtime.context), + Component.withContext(runtime.context), ) export const Route = createFileRoute("/async")({ diff --git a/packages/example-next/src/routes/form.tsx b/packages/example-next/src/routes/form.tsx index 88885a4..9763951 100644 --- a/packages/example-next/src/routes/form.tsx +++ b/packages/example-next/src/routes/form.tsx @@ -55,7 +55,7 @@ const RegisterRouteComponent = Component.make("RegisterRouteView")(function*() { ) }).pipe( - Component.withRuntime(runtime.context), + Component.withContext(runtime.context), ) export const Route = createFileRoute("/form")({ diff --git a/packages/example-next/src/routes/index.tsx b/packages/example-next/src/routes/index.tsx index 0b4d271..7f98e30 100644 --- a/packages/example-next/src/routes/index.tsx +++ b/packages/example-next/src/routes/index.tsx @@ -48,7 +48,7 @@ const TodoRouteComponent = Component.make("TodoRouteView")(function*() { ) }).pipe( - Component.withRuntime(runtime.context), + Component.withContext(runtime.context), ) export const Route = createFileRoute("/")({ diff --git a/packages/example-next/src/routes/query.tsx b/packages/example-next/src/routes/query.tsx index a97a8bf..313bbff 100644 --- a/packages/example-next/src/routes/query.tsx +++ b/packages/example-next/src/routes/query.tsx @@ -77,7 +77,7 @@ const QueryRouteComponent = Component.make("QueryRouteView")(function*() { ) }).pipe( - Component.withRuntime(runtime.context), + Component.withContext(runtime.context), ) export const Route = createFileRoute("/query")({ diff --git a/packages/example-next/src/routes/result.tsx b/packages/example-next/src/routes/result.tsx index bfcdb0c..3ca96cb 100644 --- a/packages/example-next/src/routes/result.tsx +++ b/packages/example-next/src/routes/result.tsx @@ -37,7 +37,7 @@ const ResultRouteComponent = Component.make("ResultRouteView")(function*() { ) }).pipe( - Component.withRuntime(runtime.context), + Component.withContext(runtime.context), ) export const Route = createFileRoute("/result")({ -- 2.52.0 From cc01e46997cbb4ab8bec5bde00239c52552408fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 19 Jul 2026 18:55:01 +0200 Subject: [PATCH 063/133] Fix runtime --- packages/effect-fc-next/src/ReactRuntime.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/effect-fc-next/src/ReactRuntime.ts b/packages/effect-fc-next/src/ReactRuntime.ts index 29c738a..0cee1e0 100644 --- a/packages/effect-fc-next/src/ReactRuntime.ts +++ b/packages/effect-fc-next/src/ReactRuntime.ts @@ -46,11 +46,6 @@ export namespace Provider { export const Provider = ( { runtime, children, ...suspenseProps }: Provider.Props ): React.ReactNode => { - Effect.runSync(Component.useOnChange( - () => Effect.addFinalizer(() => runtime.runtime.disposeEffect), - [runtime], - )) - const promise = React.useMemo(() => runtime.runtime.context(), [runtime]) return React.createElement( @@ -66,4 +61,12 @@ const ProviderInner = ( readonly promise: Promise> readonly children?: React.ReactNode } -): React.ReactNode => React.createElement(runtime.context, { value: React.use(promise) }, children) +): React.ReactNode => { + const context = React.use(promise) + Effect.runSyncWith(context)(Component.useOnChange( + () => Effect.addFinalizer(() => runtime.runtime.disposeEffect), + [runtime], + )) + + return React.createElement(runtime.context, { value: context }, children) +} -- 2.52.0 From 6551969bb1f8c66f842d969b6b6d34980ca90b5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sun, 19 Jul 2026 20:41:59 +0200 Subject: [PATCH 064/133] Fix Query lifecycle --- packages/effect-fc-next/src/Query.ts | 63 ++++++++--------- packages/example-next/src/post.ts | 15 ---- packages/example-next/src/routes/query.tsx | 79 +++++++++++++--------- packages/example-next/src/runtime.ts | 14 +++- 4 files changed, 93 insertions(+), 78 deletions(-) delete mode 100644 packages/example-next/src/post.ts diff --git a/packages/effect-fc-next/src/Query.ts b/packages/effect-fc-next/src/Query.ts index ff84519..4d7c3e7 100644 --- a/packages/effect-fc-next/src/Query.ts +++ b/packages/effect-fc-next/src/Query.ts @@ -254,43 +254,44 @@ extends Pipeable.Class implements Query { }), })), - Effect.onExit(this.f(previous.key), exit => Lens.update( - state, - previous => Exit.match(exit, { - onSuccess: v => ({ - key: previous.key, - result: AsyncResult.success(v), - }), - onFailure: c => AsyncResult.match(previous.result, { - onInitial: () => ({ - key: previous.key, - result: AsyncResult.failure(c), - }), - onSuccess: v => ({ - key: previous.key, - result: AsyncResult.failure(c, { - previousSuccess: Option.some(v), - }), - }), - onFailure: v => ({ - key: previous.key, - result: AsyncResult.failure(c, { - previousSuccess: v.previousSuccess, - }), - }), - }), - }), - ).pipe( - Effect.andThen(Effect.all([ - Effect.fiberId, - Lens.get(this.fiber), - ])), + Effect.onExit(this.f(previous.key), exit => Effect.all([ + Effect.fiberId, + Lens.get(this.fiber), + ]).pipe( Effect.flatMap(([fiberId, fiber]) => Option.match(fiber, { onSome: v => fiberId === v.id ? Lens.set(this.fiber, Option.none()) : Effect.void, onNone: () => Effect.void, })), + Effect.andThen(Lens.updateAndGet( + state, + previous => Exit.match(exit, { + onSuccess: v => ({ + key: previous.key, + result: AsyncResult.success(v), + }), + onFailure: c => AsyncResult.match(previous.result, { + onInitial: () => ({ + key: previous.key, + result: AsyncResult.failure(c), + }), + onSuccess: v => ({ + key: previous.key, + result: AsyncResult.failure(c, { + previousSuccess: Option.some(v), + }), + }), + onFailure: v => ({ + key: previous.key, + result: AsyncResult.failure(c, { + previousSuccess: v.previousSuccess, + }), + }), + }), + }), + )), + Effect.flatMap(finalState => Lens.set(this.latestFinalState, Option.some(finalState as FinalQueryState))), )), )) diff --git a/packages/example-next/src/post.ts b/packages/example-next/src/post.ts deleted file mode 100644 index 2bbe6e6..0000000 --- a/packages/example-next/src/post.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Effect } from "effect" - - -export interface Post { - readonly title: string - readonly body: string -} - -export const fetchPost = (id: number) => Effect.tryPromise({ - try: () => - fetch(`https://jsonplaceholder.typicode.com/posts/${id}`) - .then(response => response.json() as Promise), - - catch: () => new Error("Unable to fetch post"), -}) diff --git a/packages/example-next/src/routes/query.tsx b/packages/example-next/src/routes/query.tsx index 313bbff..fd41ad8 100644 --- a/packages/example-next/src/routes/query.tsx +++ b/packages/example-next/src/routes/query.tsx @@ -1,49 +1,68 @@ import { Button, Container, Flex, Heading, Slider, Text } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" -import { Effect, SubscriptionRef } from "effect" +import { Effect, Schema, SubscriptionRef } from "effect" +import { HttpClient } from "effect/unstable/http" import { AsyncResult } from "effect/unstable/reactivity" import { Component, Lens, Mutation, Query, View } from "effect-fc-next" -import { fetchPost, type Post } from "@/post" import { runtime } from "@/runtime" +const Post = Schema.Struct({ + userId: Schema.Int, + id: Schema.Int, + title: Schema.String, + body: Schema.String, +}) + + interface PostResultViewProps { - readonly result: AsyncResult.AsyncResult + readonly result: AsyncResult.AsyncResult } -const PostResultView = (props: PostResultViewProps) => - AsyncResult.match(props.result, { - onInitial: () => Loading..., - onFailure: () => Request failed., - onSuccess: result => ( - <> - {result.value.title} - {result.value.body} - - ), - }) +const PostResultView = (props: PostResultViewProps) => AsyncResult.match(props.result, { + onInitial: result => result.waiting + ? Loading... + : No data., + onFailure: result => Request failed: { result.cause.toString() }, + onSuccess: result => <> + {result.waiting && Refreshing...} + {result.value.title} + {result.value.body} + , +}) const QueryRouteComponent = Component.make("QueryRouteView")(function*() { const [idLens, query, mutation] = yield* Component.useOnMount(() => Effect.gen(function*() { - const idLens = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(1)) + const keyLens = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(["post", 1 as number] as const)) + const idLens = Lens.focusTupleAt(keyLens, 1) + const query = yield* Query.service({ - key: idLens, - f: fetchPost, + key: keyLens, + f: ([, id]) => HttpClient.HttpClient.pipe( + Effect.tap(Effect.sleep("1 second")), + Effect.andThen(client => client.get(`https://jsonplaceholder.typicode.com/posts/${ id }`)), + Effect.andThen(response => response.json), + Effect.andThen(Schema.decodeUnknownEffect(Post)), + ), staleTime: "10 seconds", }) + const mutation = yield* Mutation.make({ - f: fetchPost, + f: ([id]: [id: number]) => HttpClient.HttpClient.pipe( + Effect.tap(Effect.sleep("1 second")), + Effect.andThen(client => client.get(`https://jsonplaceholder.typicode.com/posts/${ id }`)), + Effect.andThen(response => response.json), + Effect.andThen(Schema.decodeUnknownEffect(Post)), + ), }) return [idLens, query, mutation] as const })) - const [id] = yield* View.useAll([idLens]) - const [queryState, mutationResult] = yield* View.useAll([ - query.state, - mutation.state, - ]) - const runPromise = yield* Component.useRunPromise() + const [id, setId] = yield* Lens.useState(idLens) + const [queryState, mutationState] = yield* View.useAll([query.state, mutation.state]) + + const runSync = yield* Component.useRunSync() return ( @@ -52,25 +71,23 @@ const QueryRouteComponent = Component.make("QueryRouteView")(function*() { value={[id]} min={1} max={10} - onValueChange={([value]) => - void runPromise(Lens.set(idLens, value ?? 1)) - } + onValueChange={([value]) => setId(value ?? 1)} /> - - - + -
diff --git a/packages/example-next/src/runtime.ts b/packages/example-next/src/runtime.ts index e09447f..c50daee 100644 --- a/packages/example-next/src/runtime.ts +++ b/packages/example-next/src/runtime.ts @@ -1,4 +1,16 @@ +import { Clipboard, Geolocation, Permissions } from "@effect/platform-browser" +import { DateTime, Layer } from "effect" +import { FetchHttpClient } from "effect/unstable/http" import { QueryClient, ReactRuntime } from "effect-fc-next" -export const runtime = ReactRuntime.make(QueryClient.layer()) +export const layer = Layer.empty.pipe( + Layer.provideMerge(QueryClient.layer()), + Layer.provideMerge(DateTime.layerCurrentZoneLocal), + Layer.provideMerge(Clipboard.layer), + Layer.provideMerge(Geolocation.layer), + Layer.provideMerge(Permissions.layer), + Layer.provideMerge(FetchHttpClient.layer), +) + +export const runtime = ReactRuntime.make(layer) -- 2.52.0 From d21ff53c26bba953cdbec11d5152154ab089bd36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 20 Jul 2026 00:57:25 +0200 Subject: [PATCH 065/133] Fix Query state --- packages/effect-fc-next/src/Query.ts | 109 +++++++++++++++++++-------- 1 file changed, 76 insertions(+), 33 deletions(-) diff --git a/packages/effect-fc-next/src/Query.ts b/packages/effect-fc-next/src/Query.ts index 4d7c3e7..c1eb7eb 100644 --- a/packages/effect-fc-next/src/Query.ts +++ b/packages/effect-fc-next/src/Query.ts @@ -1,4 +1,4 @@ -import { type Cause, type Context, Duration, Effect, Equal, type Equivalence, Exit, Fiber, Option, Pipeable, Predicate, type Scope, Semaphore, Stream, SubscriptionRef } from "effect" +import { type Cause, type Context, Duration, Effect, Equal, type Equivalence, Exit, Fiber, Option, Pipeable, Predicate, PubSub, Ref, type Scope, Semaphore, Stream, SubscriptionRef } from "effect" import { AsyncResult } from "effect/unstable/reactivity" import * as Lens from "./Lens.js" import * as QueryClient from "./QueryClient.js" @@ -231,40 +231,40 @@ extends Pipeable.Class implements Query { Scope.Scope | R > { return Effect.gen({ self: this }, function*() { - const state = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(previous)) + const state = yield* makeQueryStateLens(previous) const fiber = yield* Effect.forkScoped(Effect.andThen( - Lens.update(state, previous => AsyncResult.match(previous.result, { - onInitial: () => ({ - key: previous.key, - result: AsyncResult.initial(true), - }), - onSuccess: result => ({ - key: previous.key, - result: AsyncResult.success(result.value, { - waiting: true, + Lens.update, never, never, never, never>( + state, + previous => AsyncResult.match(previous.result, { + onInitial: () => ({ + key: previous.key, + result: AsyncResult.initial(true), }), - }), - onFailure: result => ({ - key: previous.key, - result: AsyncResult.failure(result.cause, { - waiting: true, - previousSuccess: result.previousSuccess, + onSuccess: result => ({ + key: previous.key, + result: AsyncResult.success(result.value, { + waiting: true, + }), }), - }), - })), + onFailure: result => ({ + key: previous.key, + result: AsyncResult.failure(result.cause, { + waiting: true, + previousSuccess: result.previousSuccess, + }), + }), + } + )), - Effect.onExit(this.f(previous.key), exit => Effect.all([ - Effect.fiberId, - Lens.get(this.fiber), - ]).pipe( - Effect.flatMap(([fiberId, fiber]) => Option.match(fiber, { - onSome: v => fiberId === v.id - ? Lens.set(this.fiber, Option.none()) - : Effect.void, - onNone: () => Effect.void, - })), - Effect.andThen(Lens.updateAndGet( + Effect.onExit(this.f(previous.key), exit => Effect.gen({ self: this }, function*() { + const fiberId = yield* Effect.fiberId + const fiber = yield* Lens.get(this.fiber) + + if (Option.isSome(fiber) && fiberId === fiber.value.id) + yield* Lens.set(this.fiber, Option.none()) + + const finalState = (yield* Lens.updateAndGet, never, never, never, never>( state, previous => Exit.match(exit, { onSuccess: v => ({ @@ -290,9 +290,11 @@ extends Pipeable.Class implements Query { }), }), }), - )), - Effect.flatMap(finalState => Lens.set(this.latestFinalState, Option.some(finalState as FinalQueryState))), - )), + )) as FinalQueryState + + yield* Lens.set(this.latestFinalState, Option.some(finalState)) + yield* PubSub.shutdown(state.pubsub) + })) )) yield* Lens.set(this.fiber, Option.some(fiber)) @@ -416,3 +418,44 @@ export const service = ( make(options), query => Effect.forkScoped(query.run), ) + + +export class QueryStateLens +extends Lens.LensImpl, never, never, never, never> { + constructor( + readonly ref: Ref.Ref>, + readonly pubsub: PubSub.PubSub>, + readonly semaphore: Semaphore.Semaphore, + ) { + super() + } + + get resolve(): Effect.Effect>, never, never> { + return Effect.map( + Ref.get(this.ref), + value => ({ + value, + commit: next => Effect.flatMap( + next, + value => Effect.andThen( + Ref.set(this.ref, value), + PubSub.publish(this.pubsub, value), + ), + ), + }), + ) + } + get changes() { return Stream.fromPubSub(this.pubsub) } + get lock() { return Effect.succeed(this.semaphore.withPermit) } +} + +export const makeQueryStateLens = ( + initial: QueryState, +) => Effect.all([ + Ref.make(initial), + PubSub.unbounded>({ replay: 1 }), + Semaphore.make(1), +]).pipe( + Effect.tap(([, pubsub]) => PubSub.publish(pubsub, initial)), + Effect.map(([ref, pubsub, semaphore]) => new QueryStateLens(ref, pubsub, semaphore)), +) -- 2.52.0 From 89c8ac59bc62f4b186b931a0d0c4a0c6b742d8f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 20 Jul 2026 01:33:45 +0200 Subject: [PATCH 066/133] Fix --- packages/example-next/src/routes/form.tsx | 32 +++++++++++++---------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/packages/example-next/src/routes/form.tsx b/packages/example-next/src/routes/form.tsx index 9763951..da3d021 100644 --- a/packages/example-next/src/routes/form.tsx +++ b/packages/example-next/src/routes/form.tsx @@ -11,17 +11,21 @@ const RegisterSchema = Schema.Struct({ }) const RegisterRouteComponent = Component.make("RegisterRouteView")(function*() { - const form = yield* Component.useOnMount(() => MutationForm.service({ - schema: RegisterSchema, - initialEncodedValue: { email: "", password: "" }, - f: ([value]) => Effect.log(`Registered ${value.email}`), + const [form, emailField, passwordField] = yield* Component.useOnMount(() => Effect.gen(function*() { + const form = yield* MutationForm.service({ + schema: RegisterSchema, + initialEncodedValue: { email: "", password: "" }, + f: ([value]) => Effect.log(`Registered ${value.email}`), + }) + + const emailField = Form.focusObjectOn(form, "email") + const passwordField = Form.focusObjectOn(form, "password") + + return [form, emailField, passwordField] as const })) - const emailField = yield* Form.useInput( - Form.focusObjectOn(form, "email"), - ) - const passwordField = yield* Form.useInput( - Form.focusObjectOn(form, "password"), - ) + + const emailInput = yield* Form.useInput(emailField) + const passwordInput = yield* Form.useInput(passwordField) const [canCommit, isCommitting] = yield* View.useAll([ form.canCommit, form.isCommitting, @@ -36,13 +40,13 @@ const RegisterRouteComponent = Component.make("RegisterRouteView")(function*() { }}> emailField.setValue(event.currentTarget.value)} + value={emailInput.value} + onChange={event => emailInput.setValue(event.currentTarget.value)} placeholder="Email" /> passwordField.setValue(event.currentTarget.value)} + value={passwordInput.value} + onChange={event => passwordInput.setValue(event.currentTarget.value)} placeholder="Password" type="password" /> -- 2.52.0 From 4ba7943bef0adf42e8c4c98848b5b3d5e6374063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 20 Jul 2026 01:59:29 +0200 Subject: [PATCH 067/133] Test --- packages/example-next/src/routes/form.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/example-next/src/routes/form.tsx b/packages/example-next/src/routes/form.tsx index da3d021..f64f803 100644 --- a/packages/example-next/src/routes/form.tsx +++ b/packages/example-next/src/routes/form.tsx @@ -1,6 +1,6 @@ import { Button, Container, Flex, Text, TextField } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" -import { Effect, Schema } from "effect" +import { Console, Effect, Schema } from "effect" import { Component, Form, MutationForm, View } from "effect-fc-next" import { runtime } from "@/runtime" @@ -11,6 +11,11 @@ const RegisterSchema = Schema.Struct({ }) const RegisterRouteComponent = Component.make("RegisterRouteView")(function*() { + yield* Component.useOnMount(() => Effect.gen(function*() { + yield* Effect.addFinalizer(() => Console.log("Form route unmounted")) + yield* Console.log("Form route mounted") + })) + const [form, emailField, passwordField] = yield* Component.useOnMount(() => Effect.gen(function*() { const form = yield* MutationForm.service({ schema: RegisterSchema, -- 2.52.0 From 8bbeed9b8d9409ff3e819fad332a14893534e108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 20 Jul 2026 02:15:39 +0200 Subject: [PATCH 068/133] Fix scope registry race condition --- packages/effect-fc-next/src/Component.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/effect-fc-next/src/Component.ts b/packages/effect-fc-next/src/Component.ts index d457468..8aaa622 100644 --- a/packages/effect-fc-next/src/Component.ts +++ b/packages/effect-fc-next/src/Component.ts @@ -1,6 +1,6 @@ /** biome-ignore-all lint/complexity/noBannedTypes: {} is the default type for React props */ /** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */ -import { Context, type Duration, Effect, Equivalence, Exit, Fiber, Function, HashMap, identity, Layer, Option, Pipeable, Predicate, Ref, Scope, Tracer } from "effect" +import { Cause, Context, type Duration, Effect, Equal, Equivalence, Exit, Fiber, Function, HashMap, identity, Layer, Option, Pipeable, Predicate, Ref, Scope, Tracer } from "effect" import * as React from "react" @@ -698,13 +698,13 @@ export const useScope = Effect.fnUntraced(function*( ScopeMap as unknown as Effect.Effect, scopeMap => scopeMap.ref, )), - Effect.let("key", () => ({})), + Effect.let("key", () => Equal.byReference({})), Effect.bind("scope", () => Scope.make(options?.finalizerExecutionStrategy ?? defaultOptions.finalizerExecutionStrategy)), Effect.tap(({ scopeMapRef, key, scope }) => - Ref.update(scopeMapRef, HashMap.set(key, { + Ref.update(scopeMapRef, HashMap.set(key, Equal.byReference({ scope, closeFiber: Option.none(), - })) + }))) ), // biome-ignore lint/correctness/useExhaustiveDependencies: use of React.DependencyList )), deps) @@ -724,13 +724,18 @@ export const useScope = Effect.fnUntraced(function*( () => Effect.runSyncWith(contextRef.current)(Effect.flatMap( Effect.sleep(options?.finalizerExecutionDebounce ?? defaultOptions.finalizerExecutionDebounce).pipe( Effect.andThen(Scope.close(scope, Exit.void)), - Effect.onExit(() => Ref.update(ref, HashMap.remove(key))), + Effect.onExit(exit => Exit.match(exit, { + onSuccess: () => Ref.update(ref, HashMap.remove(key)), + onFailure: cause => Cause.hasInterruptsOnly(cause) + ? Effect.void + : Ref.update(ref, HashMap.remove(key)), + })), Effect.forkDetach, ), - fiber => Ref.update(ref, HashMap.set(key, { + fiber => Ref.update(ref, HashMap.set(key, Equal.byReference({ scope, closeFiber: Option.some(fiber), - })), + }))), )) ), ) -- 2.52.0 From efdf490bc01583462c7c2d1e49c27ac63668edc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 20 Jul 2026 02:43:38 +0200 Subject: [PATCH 069/133] Fix form example --- packages/example-next/package.json | 2 +- packages/example-next/src/routes/form.tsx | 34 +++++++++++++++-------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/packages/example-next/package.json b/packages/example-next/package.json index 78995c3..26a9e01 100644 --- a/packages/example-next/package.json +++ b/packages/example-next/package.json @@ -6,7 +6,7 @@ "scripts": { "build": "tsc -b && vite build", "dev": "vite", - "lint:tsc": "tsc --noEmit", + "lint:tsc": "tsc -b --noEmit", "lint:biome": "biome lint", "preview": "vite preview", "clean:cache": "rm -rf .turbo node_modules/.tmp node_modules/.vite* .tanstack", diff --git a/packages/example-next/src/routes/form.tsx b/packages/example-next/src/routes/form.tsx index f64f803..38c9109 100644 --- a/packages/example-next/src/routes/form.tsx +++ b/packages/example-next/src/routes/form.tsx @@ -1,13 +1,22 @@ -import { Button, Container, Flex, Text, TextField } from "@radix-ui/themes" +import { Button, Container, Flex, Text } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" import { Console, Effect, Schema } from "effect" import { Component, Form, MutationForm, View } from "effect-fc-next" +import { TextFieldFormInputView } from "@/lib/form/TextFieldFormInputView" import { runtime } from "@/runtime" const RegisterSchema = Schema.Struct({ - email: Schema.String, - password: Schema.String, + email: Schema.String.check( + Schema.isPattern(/^[^\s@]+@[^\s@]+\.[^\s@]+$/, { + message: "Enter a valid email address", + }), + ), + password: Schema.String.check( + Schema.isMinLength(5, { + message: "Password must be at least 5 characters long", + }), + ), }) const RegisterRouteComponent = Component.make("RegisterRouteView")(function*() { @@ -29,31 +38,32 @@ const RegisterRouteComponent = Component.make("RegisterRouteView")(function*() { return [form, emailField, passwordField] as const })) - const emailInput = yield* Form.useInput(emailField) - const passwordInput = yield* Form.useInput(passwordField) const [canCommit, isCommitting] = yield* View.useAll([ form.canCommit, form.isCommitting, ]) + + const TextFieldFormInput = yield* TextFieldFormInputView.use const runPromise = yield* Component.useRunPromise() + return ( -
{ + { event.preventDefault() void runPromise(form.submit) }}> - emailInput.setValue(event.currentTarget.value)} + - passwordInput.setValue(event.currentTarget.value)} + + + ) + }, +) +``` + +`SubmittableForm.service` starts validation in the form's scope. The form keeps +its encoded input state, decoded value, validation issues, submit mutation, and +commit flags available as Lenses/Subscribables. + +## Synchronized Forms + +Use `SynchronizedForm` when a form should edit an existing target `Lens`. +Instead of submitting a final value, valid encoded changes are synchronized back +to the target Lens. + +This is useful for edit screens where the form is a validated view over existing +state. Use `SubmittableForm` for "submit this draft" flows, and +`SynchronizedForm` for "keep this target state synchronized when valid" flows. + +## Focusing Fields + +Forms can be focused just like Lenses: + +```tsx +const emailField = Form.focusObjectOn(form, "email") +const firstItemField = Form.focusArrayAt(form, 0) +``` + +Focused fields keep the parent form's validation and commit state, but narrow +`encodedValue`, `value`, and `issues` to the field path. This lets each input +component receive only the field it needs. diff --git a/packages/docs/versioned_docs/version-3/getting-started.md b/packages/docs/versioned_docs/version-3/getting-started.md new file mode 100644 index 0000000..2c4141c --- /dev/null +++ b/packages/docs/versioned_docs/version-3/getting-started.md @@ -0,0 +1,415 @@ +--- +sidebar_position: 1 +title: Getting Started +--- + +# Getting Started + +`effect-fc` lets React components be written as Effect programs. Inside a +component body you can yield services, run Effects, subscribe to Effect-powered +state, and still export a normal React function component at the edge of your +app. + +## Install + +Install `effect-fc` alongside `effect` and React 19.2 or newer: + +```bash npm2yarn +npm install effect-fc effect react +``` +```bash npm2yarn +npm install --save-dev @types/react +``` + +`effect-fc` is not opinionated about the React platform. Use it with web, +native, custom renderers, or any environment where React components can run. +Then install the platform-specific React packages for your target. + +For web apps, install React DOM: + +```bash npm2yarn +npm install react-dom +``` +```bash npm2yarn +npm install --save-dev @types/react-dom +``` + +## Create A Runtime + +An Effect-FC app needs an Effect runtime. Build one from the services your UI +needs, then share it with React through `ReactRuntime.Provider`. + +For an empty app, `Layer.empty` is enough: + +```tsx title="src/runtime.ts" +import { Layer } from "effect" +import { ReactRuntime } from "effect-fc" + +export const runtime = ReactRuntime.make(Layer.empty) +``` + +As your app grows, add services to the layer: + +```tsx title="src/runtime.ts" +import { FetchHttpClient } from "@effect/platform" +import { Layer } from "effect" +import { ReactRuntime } from "effect-fc" + +const AppLive = Layer.empty.pipe( + Layer.provideMerge(FetchHttpClient.layer), +) + +export const runtime = ReactRuntime.make(AppLive) +``` + +## Provide The Runtime + +At the React root, wrap your app with `ReactRuntime.Provider`: + +```tsx title="src/main.tsx" +import { StrictMode } from "react" +import { createRoot } from "react-dom/client" +import { ReactRuntime } from "effect-fc" +import { App } from "./App" +import { runtime } from "./runtime" + +createRoot(document.getElementById("root")!).render( + + + + + , +) +``` + +`ReactRuntime.Provider` also works with routers. Keep it above your router +provider so route components can be converted with the same runtime context. + +## Write Your First Component + +Use `Component.make` when you want automatic tracing spans, or +`Component.makeUntraced` when you only want the component behavior. This creates +an Effect-FC component, not a plain React component yet. + +The Effect run during render must be synchronous. Effect-FC follows React's +render model, so component bodies should produce JSX without waiting on async +work. Use lifecycle hooks, callbacks, queries, or other Effect-FC helpers for +async work that happens outside render. + +```tsx title="src/HelloView.tsx" +import { Effect } from "effect" +import { Component } from "effect-fc" + +export const HelloView = Component.make("HelloView")(function* (props: { + readonly name: string +}) { + const message = yield* Effect.succeed(`Hello, ${props.name}`) + + return

{message}

+}) +``` + +At this point `HelloView` can yield Effects, services, and scoped lifecycle +work, but React cannot render it directly. + +## Apply A Runtime + +Use `Component.withRuntime` at the boundary where an Effect-FC component needs +to become a regular React function component. + +```tsx title="src/Hello.tsx" +import { Component } from "effect-fc" +import { HelloView } from "./HelloView" +import { runtime } from "./runtime" + +export const Hello = HelloView.pipe( + Component.withRuntime(runtime.context), +) +``` + +`Hello` is now a normal React component: + +```tsx title="src/App.tsx" +import { Hello } from "./Hello" + +export function App() { + return +} +``` + +## Use Effect-FC Components Together + +Inside an Effect-FC component, other Effect-FC components are available through +their `.use` effect. Yield `.use` to get a React component for the current +runtime context, then render it like JSX. + +```tsx title="src/GreetingCardView.tsx" +import { Component } from "effect-fc" +import { HelloView } from "./HelloView" + +export const GreetingCardView = Component.make("GreetingCard")( + function* () { + const Hello = yield* HelloView.use + + return ( +
+ +

This component is still running inside Effect-FC.

+
+ ) + }, +) +``` + +Use `Component.withRuntime` only when you leave the Effect-FC tree and need a +normal React component again: + +```tsx title="src/GreetingCard.tsx" +import { Component } from "effect-fc" +import { GreetingCardView } from "./GreetingCardView" +import { runtime } from "./runtime" + +export const GreetingCard = GreetingCardView.pipe( + Component.withRuntime(runtime.context), +) +``` + +## Component Lifecycle + +Every Effect-FC component instance exposes an Effect `Scope`. Effects that need +`Scope.Scope` can use that component scope to register finalizers, fork scoped +work, or acquire scoped resources. + +The component scope is created when React mounts the component and closes when +React unmounts it. When the scope closes, Effect runs the finalizers registered +inside that scope. + +Finalizers are forked when the scope closes, so cleanup logic can run +asynchronous Effects even though the component body itself must stay +synchronous. + +```tsx title="src/MountedMessageView.tsx" +import { Console, Effect } from "effect" +import { Component } from "effect-fc" + +export const MountedMessageView = Component.make("MountedMessage")( + function* () { + const message = yield* Component.useOnMount(() => + Effect.gen(function* () { + yield* Console.log("MountedMessage mounted") + yield* Effect.addFinalizer(() => + Console.log("MountedMessage unmounted"), + ) + + return "This value was loaded on mount." + }), + ) + + return

{message}

+ }, +) +``` + +Use `Component.useOnMount` when the component needs a value produced by an +Effect during its first render. The value is then cached for the component +instance. You can also use it to set up scoped component logic, subscriptions, +or resources that should live for the lifetime of that component instance. + +Use `Component.useOnChange` when render needs the value computed by +scoped work that depends on changing inputs. When a dependency changes, React +re-renders the component and the hook computes the next value during that +render. + +```tsx +import { Console, Effect } from "effect" +import { Component } from "effect-fc" + +const UserPanelView = Component.make("UserPanel")( + function* (props: { readonly userId: string }) { + const label = yield* Component.useOnChange( + () => + Effect.gen(function* () { + yield* Console.log(`Preparing view for ${props.userId}`) + yield* Effect.addFinalizer(() => + Console.log(`Cleaning up ${props.userId}`), + ) + + return `Viewing user ${props.userId}` + }), + [props.userId], + ) + + return

{label}

+ }, +) +``` + +In this example, each `userId` gets its own scope. When `userId` changes, +Effect-FC closes the previous scope, runs its finalizers, and creates a new +scope for the next load. Unlike `useOnMount`, `useOnChange` does not expose the +component's root scope directly. It creates and provides its own scope for that +dependency window. Some other Effect-FC hooks follow the same pattern when they +need a lifecycle that is narrower than the whole component instance. + +## Useful Effect-FC Hooks + +Unlike plain React hooks, Effect-FC hooks return Effects. Use `yield*` to run +them inside the component body. + +The most common Effect-FC hooks are: + +- `Component.useOnMount`: run an Effect once during the component's first render + after mount, and return its value. The Effect must be synchronous. + +```tsx +const initialData = yield* Component.useOnMount(() => loadInitialData) +``` + +- `Component.useOnChange`: when a dependency changes, React re-renders the + component and the Effect is run again inside the component body. It returns + the latest value, so the Effect must be synchronous too. + +```tsx +const label = yield* Component.useOnChange(() => formatUserLabel(id), [id]) +``` + +- `Component.useReactEffect`: Effect-powered `React.useEffect` for side effects + that do not need to compute render output or trigger a re-render. The Effect + must be synchronous and can register scoped finalizers. + +```tsx +yield* Component.useReactEffect( + () => Effect.forkScoped(subscribeToUser(id)), + [id], +) +``` + +- `Component.useReactLayoutEffect`: Effect-powered `React.useLayoutEffect` with scoped + finalizers. + +```tsx +yield* Component.useReactLayoutEffect(() => measure(ref), []) +``` + +- `Component.useRunSync` and `Component.useRunPromise`: run Effects from React + event handlers. + +```tsx +const runPromise = yield* Component.useRunPromise() + +return ( + +) +``` + +- `Component.useCallbackSync` and `Component.useCallbackPromise`: create stable + React callbacks. + +```tsx +const save = yield* Component.useCallbackPromise( + (user: User) => saveUser(user), + [], +) + +return +``` + +## Use React Normally + +An Effect-FC component is still a React function component. Anything you can do +in a regular React component can also be done in an Effect-FC component, +including React hooks, refs, event handlers, context, and JSX composition. + +```tsx +import { Component } from "effect-fc" +import * as React from "react" + +const CounterView = Component.make("Counter")(function* () { + const [count, setCount] = React.useState(0) + const buttonRef = React.useRef(null) + + return ( + + ) +}) +``` + +Use regular React hooks for local UI concerns, and reach for Effect-FC helpers +when the component needs Effect services, scopes, resources, or Effect-powered +callbacks. + +## Use Tags + +Components can yield Effect tags directly in the component body. There is no +need to memoize the service yourself; just yield the tag wherever the component +needs it. + +```tsx title="src/Greeting.tsx" +const GreetingView = Component.make("Greeting")(function* (props: { + readonly name: string +}) { + const greeting = yield* GreetingService + + return

{greeting.greet(props.name)}

+}) +``` + +Service values in the runtime context are reactive by default. If a provided +service instance changes, Effect-FC unmounts and mounts again the components +that depend on that context, so they read the new service and restart their +scoped lifecycle with the new environment. For services that should not trigger +that behavior, pass their tags with `Component.withOptions({ nonReactiveTags: +[...] })`. + +## Provide Services To A Subtree + +Use `Component.useContextFromLayer` when an Effect-FC component should provide +extra services to another Effect-FC component. It turns a `Layer` into a runtime +context that can be provided to `.use`. + +```tsx title="src/GreetingPageView.tsx" +import { Effect } from "effect" +import { Component } from "effect-fc" +import { GreetingView } from "./Greeting" +import { GreetingService } from "./services" + +const GreetingLive = GreetingService.Default({ + greet: (name: string) => `Welcome, ${name}`, +}) + +export const GreetingPageView = Component.make("GreetingPage")(function* () { + const context = yield* Component.useContextFromLayer(GreetingLive) + const Greeting = yield* Effect.provide(GreetingView.use, context) + + return +}) +``` + +The layer passed to `useContextFromLayer` should be stable. If a new layer value +is created on every render, Effect-FC has to build a new context, which can +cause unnecessary re-renders and scoped lifecycle restarts. Define static layers +outside the component, or memoize layers that depend on React props or state. + +Layers built with `useContextFromLayer` are scoped to the component that builds +them. That means service construction can register scoped logic, acquire +resources, fork scoped fibers, and add finalizers that are tied to that +component's lifecycle. + +## Where To Go Next + +Once the runtime and component boundary are in place, the rest of the library +builds on the same idea: + +- `Subscribable.useAll` reads Effect subscribables and rerenders when they + change. +- `Lens` connects React state and Effect `SubscriptionRef` values. +- `Query` and `Mutation` model async data and user-triggered operations. +- `Form`, `SubmittableForm`, and `SynchronizedForm` help build Effect-backed + forms. + +The important pattern is small and repeatable: write Effect-FC components inside +the runtime, then use `Component.withRuntime` at React boundaries. diff --git a/packages/docs/versioned_docs/version-3/state-management.md b/packages/docs/versioned_docs/version-3/state-management.md new file mode 100644 index 0000000..160bf96 --- /dev/null +++ b/packages/docs/versioned_docs/version-3/state-management.md @@ -0,0 +1,245 @@ +--- +sidebar_position: 2 +title: State Management +--- + +# State Management + +`Lens` is the main type used for state management in `effect-fc`. + +A Lens is an effectful handle to a piece of state. It can read the current value, +subscribe to changes, and write updates back to the underlying source. A Lens +can point at a whole state object or focus on one nested field inside it. + +The usual pattern is to create state with Effect primitives such as +`SubscriptionRef`, turn that primitive into a Lens with a matching constructor +such as `Lens.fromSubscriptionRef`, and bind Lens values into components with +`Subscribable.useAll`. + +`Subscribable` is the read-only side of this model. Every Lens is also a +Subscribable. + +`effect-fc` re-exports the `Lens` and `Subscribable` modules from +[`effect-lens`](https://github.com/Thiladev/effect-lens) for convenience. The +core data model and transformation APIs belong to `effect-lens`, so check the +[`effect-lens` documentation](https://github.com/Thiladev/effect-lens/tree/master/packages/effect-lens) for the full Lens/Subscribable API. + +## Where To Store State + +State can live pretty much anywhere as a `Lens` or `Subscribable`: in a +service, in a layer, in a component scope, or alongside plain React state. Pick +the owner based on who needs the state. Once you have a Lens/Subscribable +handle, pass it around however you like, including through React props. + +If state is shared by multiple components or belongs to application logic, store +it in an Effect service: + +```tsx +import { Effect, SubscriptionRef } from "effect" +import { Component, Lens, Subscribable } from "effect-fc" + +class CounterState extends Effect.Service()("CounterState", { + effect: Effect.gen(function* () { + const count = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(0)) + + return { count } as const + }), +}) {} + +const CounterValueView = Component.make("CounterValue")(function* () { + const state = yield* CounterState + const [count] = yield* Subscribable.useAll([state.count]) + + return

Count: {count}

+}) +``` + +If state belongs to a single Effect-FC component instance, or to a shallow +hierarchy of subcomponents that receive it through props, create it with +`Component.useOnMount`: + +```tsx +import { Effect, SubscriptionRef } from "effect" +import { Component, Lens, Subscribable } from "effect-fc" + +const LocalCounterView = Component.make("LocalCounter")(function* () { + const state = yield* Component.useOnMount(() => + Effect.gen(function* () { + const count = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(0)) + + return { count } as const + }), + ) + const [count] = yield* Subscribable.useAll([state.count]) + + return

Count: {count}

+}) +``` + +For simple UI state that is not shared and does not need Effect integration, +prefer regular React state. A local "show details" toggle is usually better as +`React.useState(false)` than as a Lens. + +## Subscribable.useAll + +A `Subscribable
` is reactive state with a current value and a stream of +changes. Use `Subscribable.useAll` whenever a component needs to bind +subscribable values into render output. + +`Lens` is a `Subscribable`, so this is also the default way to read Lens values +from a component. + +```tsx +import { Effect, SubscriptionRef } from "effect" +import { Component, Lens, Subscribable } from "effect-fc" + +class CounterState extends Effect.Service()("CounterState", { + effect: Effect.gen(function* () { + const count = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(0)) + const doubled = Subscribable.map(count, (n) => n * 2) + + return { count, doubled } as const + }), +}) {} + +const CounterReadOnlyView = Component.make("CounterReadOnly")( + function* () { + const state = yield* CounterState + const [count, doubled] = yield* Subscribable.useAll([ + state.count, + state.doubled, + ]) + + return

Count: {count}, doubled: {doubled}

+ }, +) +``` + +`Subscribable.useAll` reads the current values during render and uses scoped subscriptions to update React state when changes arrive. + +When you need to modify state, write to the Lens with `Lens.set` or `Lens.update`: + +```tsx +const CounterControlsView = Component.make("CounterControls")( + function* () { + const state = yield* CounterState + const [count] = yield* Subscribable.useAll([state.count]) + + const increment = yield* Component.useCallbackSync( + () => Lens.update(state.count, (n) => n + 1), + [], + ) + const reset = yield* Component.useCallbackSync( + () => Lens.set(state.count, 0), + [], + ) + + return ( +
+

Count: {count}

+ + +
+ ) + }, +) +``` + +## Lens.useState + +`Lens.useState` is useful when React needs the familiar `[value, setValue]` +tuple, backed by a Lens. Reach for it when the JSX API expects a synchronous +setter, especially controlled inputs such as text fields, checkboxes, selects, +or third-party components with `value` / `onChange` props. + +If a component only needs to display the value, prefer `Subscribable.useAll`. +`Lens.useState` is for places where reading and writing need to be wired +together in React's local-state shape. + +```tsx +import { Effect, SubscriptionRef } from "effect" +import { Component, Lens } from "effect-fc" + +class FormState extends Effect.Service()("FormState", { + effect: Effect.gen(function* () { + const name = Lens.fromSubscriptionRef(yield* SubscriptionRef.make("")) + + return { name } as const + }), +}) {} + +const NameInputView = Component.make("NameInput")(function* () { + const state = yield* FormState + const [name, setName] = yield* Lens.useState(state.name) + + return ( + setName(event.currentTarget.value)} + /> + ) +}) +``` + +`Lens.useState` returns the current value and a React-compatible setter. Calling +the setter writes through the Lens, so every other component subscribed to the +same Lens sees the update. + +## Focused Lenses + +Use focused Lenses when a component should work with one part of a larger state +object. A focused Lens is still a Lens, so it can be read with +`Subscribable.useAll` or used with `Lens.useState` when React needs a +read/write tuple. + +```tsx +import { Effect, SubscriptionRef } from "effect" +import { Component, Lens, Subscribable } from "effect-fc" + +interface UserProfile { + readonly name: string + readonly email: string + readonly role: string +} + +class ProfileState extends Effect.Service()("ProfileState", { + effect: Effect.gen(function* () { + const profile = Lens.fromSubscriptionRef( + yield* SubscriptionRef.make({ + name: "", + email: "", + role: "reader", + }), + ) + const name = Lens.focusObjectOn(profile, "name") + const role = Lens.focusObjectOn(profile, "role") + + return { profile, name, role } as const + }), +}) {} + +const ProfileNameView = Component.make("ProfileName")(function* () { + const state = yield* ProfileState + const [name, setName] = yield* Lens.useState(state.name) + const [role] = yield* Subscribable.useAll([state.role]) + + return ( + + ) +}) +``` + +Updating the focused `name` Lens through `Lens.useState` updates the parent +`profile` Lens. The focused `role` Lens is only read, so it stays on the simpler +`Subscribable.useAll` path. + +For focusing into nested state, deriving lenses, custom write behavior, and the +complete API, refer to the +[`effect-lens` documentation](https://github.com/Thiladev/effect-lens/tree/master/packages/effect-lens). diff --git a/packages/docs/versioned_sidebars/version-3-sidebars.json b/packages/docs/versioned_sidebars/version-3-sidebars.json new file mode 100644 index 0000000..068a564 --- /dev/null +++ b/packages/docs/versioned_sidebars/version-3-sidebars.json @@ -0,0 +1,7 @@ +{ + "docsSidebar": [ + "getting-started", + "state-management", + "forms" + ] +} diff --git a/packages/docs/versions.json b/packages/docs/versions.json new file mode 100644 index 0000000..fb052c8 --- /dev/null +++ b/packages/docs/versions.json @@ -0,0 +1,3 @@ +[ + "3" +] -- 2.52.0 From 5017ccf5ba3986d1d84b451c05380c00a0f4ec97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 20 Jul 2026 07:26:17 +0200 Subject: [PATCH 075/133] Docs work --- packages/docs/docusaurus.config.ts | 7 ++++--- .../{version-3 => version-effect-fc-1.0}/forms.md | 0 .../getting-started.md | 0 .../state-management.md | 0 ...3-sidebars.json => version-effect-fc-1.0-sidebars.json} | 0 packages/docs/versions.json | 2 +- 6 files changed, 5 insertions(+), 4 deletions(-) rename packages/docs/versioned_docs/{version-3 => version-effect-fc-1.0}/forms.md (100%) rename packages/docs/versioned_docs/{version-3 => version-effect-fc-1.0}/getting-started.md (100%) rename packages/docs/versioned_docs/{version-3 => version-effect-fc-1.0}/state-management.md (100%) rename packages/docs/versioned_sidebars/{version-3-sidebars.json => version-effect-fc-1.0-sidebars.json} (100%) diff --git a/packages/docs/docusaurus.config.ts b/packages/docs/docusaurus.config.ts index 618f110..45c8b00 100644 --- a/packages/docs/docusaurus.config.ts +++ b/packages/docs/docusaurus.config.ts @@ -46,10 +46,11 @@ const config: Config = { lastVersion: "current", versions: { current: { - label: "v4", + label: "effect-view v1", }, - "3": { - label: "v3", + "effect-fc-1.0": { + label: "effect-fc 1.0", + path: "effect-fc/1.0", }, }, }, diff --git a/packages/docs/versioned_docs/version-3/forms.md b/packages/docs/versioned_docs/version-effect-fc-1.0/forms.md similarity index 100% rename from packages/docs/versioned_docs/version-3/forms.md rename to packages/docs/versioned_docs/version-effect-fc-1.0/forms.md diff --git a/packages/docs/versioned_docs/version-3/getting-started.md b/packages/docs/versioned_docs/version-effect-fc-1.0/getting-started.md similarity index 100% rename from packages/docs/versioned_docs/version-3/getting-started.md rename to packages/docs/versioned_docs/version-effect-fc-1.0/getting-started.md diff --git a/packages/docs/versioned_docs/version-3/state-management.md b/packages/docs/versioned_docs/version-effect-fc-1.0/state-management.md similarity index 100% rename from packages/docs/versioned_docs/version-3/state-management.md rename to packages/docs/versioned_docs/version-effect-fc-1.0/state-management.md diff --git a/packages/docs/versioned_sidebars/version-3-sidebars.json b/packages/docs/versioned_sidebars/version-effect-fc-1.0-sidebars.json similarity index 100% rename from packages/docs/versioned_sidebars/version-3-sidebars.json rename to packages/docs/versioned_sidebars/version-effect-fc-1.0-sidebars.json diff --git a/packages/docs/versions.json b/packages/docs/versions.json index fb052c8..775c1db 100644 --- a/packages/docs/versions.json +++ b/packages/docs/versions.json @@ -1,3 +1,3 @@ [ - "3" + "effect-fc-1.0" ] -- 2.52.0 From 19d5aba009b33d8ae7220c0ddaba4f7300fb0796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Mon, 20 Jul 2026 07:35:44 +0200 Subject: [PATCH 076/133] Docs work --- packages/docs/docs/forms.md | 96 +++++++-------- packages/docs/docs/getting-started.md | 103 ++++++++-------- packages/docs/docs/state-management.md | 159 +++++++++++++++---------- 3 files changed, 190 insertions(+), 168 deletions(-) diff --git a/packages/docs/docs/forms.md b/packages/docs/docs/forms.md index 944234f..47d5dad 100644 --- a/packages/docs/docs/forms.md +++ b/packages/docs/docs/forms.md @@ -7,30 +7,30 @@ title: Forms The form modules provide an Effect-first model for editable, schema-validated state. They are not a visual component library. Instead, they give you -Lenses/Subscribables for form values, validation issues, and commit state, so +Lenses/Views for form values, validation issues, and commit state, so you can build the UI with whatever components your app already uses. The base `Form` type represents a field. A field tracks both the raw encoded value used by inputs and the decoded value produced by an Effect `Schema`: - `encodedValue`: a `Lens` containing the raw input value. -- `value`: a `Subscribable` containing the decoded value as an `Option`. -- `issues`: a `Subscribable` containing schema validation issues for that field. -- `canCommit`, `isValidating`, and `isCommitting`: `Subscribable` flags for UI +- `value`: a `View` containing the decoded value as an `Option`. +- `issues`: a `View` containing schema validation issues for that field. +- `canCommit`, `isValidating`, and `isCommitting`: `View` flags for UI state. There are two root form flavors: -- `SubmittableForm`: owns local encoded form state, validates it, and submits a +- `MutationForm`: owns local encoded form state, validates it, and submits a decoded value through a mutation. -- `SynchronizedForm`: wraps an existing target `Lens` and writes valid changes +- `LensForm`: wraps an existing target `Lens` and writes valid changes back to that target. Most apps create one of those root forms, then focus it into fields with `Form.focusObjectOn`, `Form.focusArrayAt`, or the other focusing helpers. The core form model is Effect code and can technically be used outside -Effect-FC. Effect-FC adds the React-facing hooks, such as `Form.useInput`, that +Effect View. Effect View adds the React-facing hooks, such as `Form.useInput`, that make those forms convenient to bind to components. ## Field Inputs @@ -40,7 +40,7 @@ a React-style `{ value, setValue }` pair and writes changes back to the field's `encodedValue` Lens. ```tsx -import { Component, Form, Subscribable } from "effect-fc" +import { Component, Form, View } from "effect-view" const TextInputView = Component.make("TextInput")( function* (props: { @@ -50,7 +50,7 @@ const TextInputView = Component.make("TextInput")( const input = yield* Form.useInput(props.form, { debounce: "250 millis", }) - const [issues] = yield* Subscribable.useAll([props.form.issues]) + const [issues] = yield* View.useAll([props.form.issues]) return (
` is reactive state with a current value and a stream of -changes. Use `Subscribable.useAll` whenever a component needs to bind -subscribable values into render output. +A `View` is reactive state with a current value and a stream of changes. Use +`View.useAll` whenever a component needs to bind View values into render output. -`Lens` is a `Subscribable`, so this is also the default way to read Lens values +`Lens` is a `View`, so this is also the default way to read Lens values from a component. ```tsx -import { Effect, SubscriptionRef } from "effect" -import { Component, Lens, Subscribable } from "effect-fc" +import { Context, Effect, Layer, SubscriptionRef } from "effect" +import { Component, Lens, View } from "effect-view" -class CounterState extends Effect.Service()("CounterState", { - effect: Effect.gen(function* () { - const count = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(0)) - const doubled = Subscribable.map(count, (n) => n * 2) +class CounterState extends Context.Service< + CounterState, + { + readonly count: Lens.Lens + readonly doubled: View.View + } +>()("CounterState") { + static readonly layer = Layer.effect( + CounterState, + Effect.gen(function* () { + const count = Lens.fromSubscriptionRef(yield* SubscriptionRef.make(0)) + const doubled = View.map(count, (n) => n * 2) - return { count, doubled } as const - }), -}) {} + return { count, doubled } as const + }), + ) +} const CounterReadOnlyView = Component.make("CounterReadOnly")( function* () { const state = yield* CounterState - const [count, doubled] = yield* Subscribable.useAll([ + const [count, doubled] = yield* View.useAll([ state.count, state.doubled, ]) @@ -115,7 +128,7 @@ const CounterReadOnlyView = Component.make("CounterReadOnly")( ) ``` -`Subscribable.useAll` reads the current values during render and uses scoped subscriptions to update React state when changes arrive. +`View.useAll` reads the current values during render and uses scoped subscriptions to update React state when changes arrive. When you need to modify state, write to the Lens with `Lens.set` or `Lens.update`: @@ -123,7 +136,7 @@ When you need to modify state, write to the Lens with `Lens.set` or `Lens.update const CounterControlsView = Component.make("CounterControls")( function* () { const state = yield* CounterState - const [count] = yield* Subscribable.useAll([state.count]) + const [count] = yield* View.useAll([state.count]) const increment = yield* Component.useCallbackSync( () => Lens.update(state.count, (n) => n + 1), @@ -152,21 +165,27 @@ tuple, backed by a Lens. Reach for it when the JSX API expects a synchronous setter, especially controlled inputs such as text fields, checkboxes, selects, or third-party components with `value` / `onChange` props. -If a component only needs to display the value, prefer `Subscribable.useAll`. +If a component only needs to display the value, prefer `View.useAll`. `Lens.useState` is for places where reading and writing need to be wired together in React's local-state shape. ```tsx -import { Effect, SubscriptionRef } from "effect" -import { Component, Lens } from "effect-fc" +import { Context, Effect, Layer, SubscriptionRef } from "effect" +import { Component, Lens } from "effect-view" -class FormState extends Effect.Service()("FormState", { - effect: Effect.gen(function* () { - const name = Lens.fromSubscriptionRef(yield* SubscriptionRef.make("")) +class FormState extends Context.Service< + FormState, + { readonly name: Lens.Lens } +>()("FormState") { + static readonly layer = Layer.effect( + FormState, + Effect.gen(function* () { + const name = Lens.fromSubscriptionRef(yield* SubscriptionRef.make("")) - return { name } as const - }), -}) {} + return { name } as const + }), + ) +} const NameInputView = Component.make("NameInput")(function* () { const state = yield* FormState @@ -189,12 +208,12 @@ same Lens sees the update. Use focused Lenses when a component should work with one part of a larger state object. A focused Lens is still a Lens, so it can be read with -`Subscribable.useAll` or used with `Lens.useState` when React needs a +`View.useAll` or used with `Lens.useState` when React needs a read/write tuple. ```tsx -import { Effect, SubscriptionRef } from "effect" -import { Component, Lens, Subscribable } from "effect-fc" +import { Context, Effect, Layer, SubscriptionRef } from "effect" +import { Component, Lens, View } from "effect-view" interface UserProfile { readonly name: string @@ -202,26 +221,36 @@ interface UserProfile { readonly role: string } -class ProfileState extends Effect.Service()("ProfileState", { - effect: Effect.gen(function* () { - const profile = Lens.fromSubscriptionRef( - yield* SubscriptionRef.make({ - name: "", - email: "", - role: "reader", - }), - ) - const name = Lens.focusObjectOn(profile, "name") - const role = Lens.focusObjectOn(profile, "role") +class ProfileState extends Context.Service< + ProfileState, + { + readonly profile: Lens.Lens + readonly name: Lens.Lens + readonly role: Lens.Lens + } +>()("ProfileState") { + static readonly layer = Layer.effect( + ProfileState, + Effect.gen(function* () { + const profile = Lens.fromSubscriptionRef( + yield* SubscriptionRef.make({ + name: "", + email: "", + role: "reader", + }), + ) + const name = Lens.focusObjectOn(profile, "name") + const role = Lens.focusObjectOn(profile, "role") - return { profile, name, role } as const - }), -}) {} + return { profile, name, role } as const + }), + ) +} const ProfileNameView = Component.make("ProfileName")(function* () { const state = yield* ProfileState const [name, setName] = yield* Lens.useState(state.name) - const [role] = yield* Subscribable.useAll([state.role]) + const [role] = yield* View.useAll([state.role]) return (
+ +
+
+
+

Ready when React is

+

Start with one Effect component.

+

+ Add a runtime, cross the React boundary once, and grow into the + rest of the toolkit only when you need it. +

+
+ + Read the guide + +