Compare commits
68 Commits
renovate/v
...
ca448c6bae
| Author | SHA1 | Date | |
|---|---|---|---|
| ca448c6bae | |||
|
|
f537490f40 | ||
|
|
2348ea9bc1 | ||
|
|
0619af6524 | ||
|
|
993e97676f | ||
|
|
95f53b8a00 | ||
|
|
8b948b2251 | ||
|
|
626a9292d5 | ||
| cb40ecff06 | |||
| b9b9f37859 | |||
|
|
363c7d24f4 | ||
|
|
d57654d872 | ||
|
|
0b7d9383ec | ||
|
|
c380fe9d08 | ||
|
|
92722444cf | ||
|
|
882054b53d | ||
|
|
1c0519cfaf | ||
|
|
f69125012e | ||
|
|
8c8560b63c | ||
|
|
86e8a7bd92 | ||
|
|
12878cd76b | ||
|
|
308025d662 | ||
|
|
2094f254b3 | ||
|
|
8ce4a959a6 | ||
| 3708059da4 | |||
|
|
cd8b5e6364 | ||
|
|
a48b623822 | ||
|
|
499e1e174b | ||
|
|
6b9c177ae7 | ||
|
|
b73b053cc8 | ||
|
|
bbad86bf97 | ||
|
|
6ae311cdfd | ||
|
|
03eca8a1af | ||
|
|
c03d697361 | ||
|
|
3847686d54 | ||
|
|
9801444c0a | ||
|
|
68d8c9fa84 | ||
|
|
cba42bfa52 | ||
|
|
874da0b963 | ||
|
|
bb0579408d | ||
|
|
b39c5946f9 | ||
|
|
aaf494e27a | ||
|
|
dbc5694b6d | ||
|
|
86582de0c5 | ||
|
|
72495bb9b5 | ||
|
|
312c103e71 | ||
|
|
a252cfec27 | ||
|
|
4a5f4c329d | ||
|
|
6f96608f64 | ||
| 0bc29b2cb9 | |||
|
|
8642619a6a | ||
|
|
e8b8df9449 | ||
|
|
3695128923 | ||
|
|
1f14e8be6b | ||
|
|
adc8835304 | ||
|
|
8b06c56ec0 | ||
|
|
003d2f19a2 | ||
|
|
15f6d695f8 | ||
|
|
64583601dc | ||
|
|
cf4ba5805f | ||
|
|
90db94e905 | ||
|
|
336ea67ea2 | ||
|
|
1af839f036 | ||
|
|
6bdf2a4d87 | ||
| 8d55a67e75 | |||
|
|
a1ec5c4781 | ||
| 756b652861 | |||
| 59f9358b9a |
@@ -9,7 +9,7 @@ jobs:
|
|||||||
- name: Setup Bun
|
- name: Setup Bun
|
||||||
uses: oven-sh/setup-bun@v2
|
uses: oven-sh/setup-bun@v2
|
||||||
- name: Clone repo
|
- name: Clone repo
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v5
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: bun install --frozen-lockfile
|
run: bun install --frozen-lockfile
|
||||||
- name: Lint TypeScript
|
- name: Lint TypeScript
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
- name: Setup Bun
|
- name: Setup Bun
|
||||||
uses: oven-sh/setup-bun@v2
|
uses: oven-sh/setup-bun@v2
|
||||||
- name: Clone repo
|
- name: Clone repo
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v5
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: bun install --frozen-lockfile
|
run: bun install --frozen-lockfile
|
||||||
- name: Lint TypeScript
|
- name: Lint TypeScript
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: "24"
|
node-version: "24"
|
||||||
- name: Clone repo
|
- name: Clone repo
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v5
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: bun install --frozen-lockfile
|
run: bun install --frozen-lockfile
|
||||||
- name: Lint TypeScript
|
- name: Lint TypeScript
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Effect FC Monorepo
|
# Effect FC Monorepo
|
||||||
|
|
||||||
[Effect-TS](https://effect.website/) integration for React 19.2+ that allows you to write function components using Effect generators.
|
[Effect-TS](https://effect.website/) integration for React 19+ that allows you to write function components using Effect generators.
|
||||||
|
|
||||||
This monorepo contains:
|
This monorepo contains:
|
||||||
- [The `effect-fc` library](packages/effect-fc)
|
- [The `effect-fc` library](packages/effect-fc)
|
||||||
|
|||||||
364
bun.lock
364
bun.lock
@@ -1,178 +1,192 @@
|
|||||||
{
|
{
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"configVersion": 1,
|
|
||||||
"workspaces": {
|
"workspaces": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@effect-fc/monorepo",
|
"name": "@effect-fc/monorepo",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^2.3.11",
|
"@biomejs/biome": "^2.2.5",
|
||||||
"@effect/language-service": "^0.75.0",
|
"@effect/language-service": "^0.52.0",
|
||||||
"@types/bun": "^1.3.6",
|
"@types/bun": "^1.2.23",
|
||||||
"npm-check-updates": "^19.3.1",
|
"npm-check-updates": "^19.0.0",
|
||||||
"npm-sort": "^0.0.4",
|
"npm-sort": "^0.0.4",
|
||||||
"turbo": "^2.7.5",
|
"turbo": "^2.5.8",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"packages/effect-fc": {
|
"packages/effect-fc": {
|
||||||
"name": "effect-fc",
|
"name": "effect-fc",
|
||||||
"version": "0.2.3",
|
"version": "0.2.0",
|
||||||
"devDependencies": {
|
|
||||||
"@effect/platform-browser": "^0.74.0",
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^19.2.0",
|
"@types/react": "^19.0.0",
|
||||||
"effect": "^3.19.0",
|
"effect": "^3.15.0",
|
||||||
"react": "^19.2.0",
|
"react": "^19.0.0",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"packages/example": {
|
"packages/example": {
|
||||||
"name": "@effect-fc/example",
|
"name": "@effect-fc/example",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@effect/platform": "^0.94.2",
|
"@effect/platform": "^0.92.1",
|
||||||
"@effect/platform-browser": "^0.74.0",
|
"@effect/platform-browser": "^0.72.0",
|
||||||
"@radix-ui/themes": "^3.2.1",
|
"@radix-ui/themes": "^3.2.1",
|
||||||
"@typed/id": "^0.17.2",
|
"@typed/id": "^0.17.2",
|
||||||
"effect": "^3.19.15",
|
"effect": "^3.18.1",
|
||||||
"effect-fc": "workspace:*",
|
"effect-fc": "workspace:*",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tanstack/react-router": "^1.154.12",
|
"@tanstack/react-router": "^1.132.31",
|
||||||
"@tanstack/react-router-devtools": "^1.154.12",
|
"@tanstack/react-router-devtools": "^1.132.31",
|
||||||
"@tanstack/router-plugin": "^1.154.12",
|
"@tanstack/router-plugin": "^1.132.31",
|
||||||
"@types/react": "^19.2.9",
|
"@types/react": "^19.2.0",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.0",
|
||||||
"@vitejs/plugin-react": "^5.1.2",
|
"@vitejs/plugin-react": "^5.0.4",
|
||||||
"globals": "^17.0.0",
|
"globals": "^16.4.0",
|
||||||
"react": "^19.2.3",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.3",
|
"react-dom": "^19.2.0",
|
||||||
"type-fest": "^5.4.1",
|
"type-fest": "^5.0.1",
|
||||||
"vite": "^7.3.1",
|
"vite": "^7.1.8",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@babel/code-frame": ["@babel/code-frame@7.28.6", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q=="],
|
"@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
|
||||||
|
|
||||||
"@babel/compat-data": ["@babel/compat-data@7.28.6", "", {}, "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg=="],
|
"@babel/compat-data": ["@babel/compat-data@7.28.4", "", {}, "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw=="],
|
||||||
|
|
||||||
"@babel/core": ["@babel/core@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/template": "^7.28.6", "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6", "@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-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw=="],
|
"@babel/core": ["@babel/core@7.28.4", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.4", "@babel/types": "^7.28.4", "@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-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA=="],
|
||||||
|
|
||||||
"@babel/generator": ["@babel/generator@7.28.6", "", { "dependencies": { "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw=="],
|
"@babel/generator": ["@babel/generator@7.28.3", "", { "dependencies": { "@babel/parser": "^7.28.3", "@babel/types": "^7.28.2", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw=="],
|
||||||
|
|
||||||
"@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-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="],
|
||||||
|
|
||||||
|
"@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
|
||||||
|
|
||||||
|
"@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.3", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.3", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg=="],
|
||||||
|
|
||||||
"@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="],
|
"@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="],
|
||||||
|
|
||||||
"@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-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA=="],
|
||||||
|
|
||||||
"@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-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
|
||||||
|
|
||||||
"@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.28.6", "", {}, "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug=="],
|
"@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="],
|
||||||
|
|
||||||
|
"@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-plugin-utils": ["@babel/helper-plugin-utils@7.27.1", "", {}, "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw=="],
|
||||||
|
|
||||||
|
"@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="],
|
||||||
|
|
||||||
|
"@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-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
|
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
|
||||||
|
|
||||||
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
|
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="],
|
||||||
|
|
||||||
"@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="],
|
"@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="],
|
||||||
|
|
||||||
"@babel/helpers": ["@babel/helpers@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="],
|
"@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="],
|
||||||
|
|
||||||
"@babel/parser": ["@babel/parser@7.28.6", "", { "dependencies": { "@babel/types": "^7.28.6" }, "bin": "./bin/babel-parser.js" }, "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ=="],
|
"@babel/parser": ["@babel/parser@7.28.4", "", { "dependencies": { "@babel/types": "^7.28.4" }, "bin": "./bin/babel-parser.js" }, "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg=="],
|
||||||
|
|
||||||
"@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.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w=="],
|
||||||
|
|
||||||
"@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.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ=="],
|
||||||
|
|
||||||
|
"@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@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-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw=="],
|
||||||
|
|
||||||
"@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="],
|
"@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="],
|
||||||
|
|
||||||
"@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="],
|
"@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="],
|
||||||
|
|
||||||
"@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/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.28.0", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg=="],
|
||||||
|
|
||||||
"@babel/traverse": ["@babel/traverse@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.6", "@babel/template": "^7.28.6", "@babel/types": "^7.28.6", "debug": "^4.3.1" } }, "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg=="],
|
"@babel/preset-typescript": ["@babel/preset-typescript@7.27.1", "", { "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.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ=="],
|
||||||
|
|
||||||
"@babel/types": ["@babel/types@7.28.6", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg=="],
|
"@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
|
||||||
|
|
||||||
"@biomejs/biome": ["@biomejs/biome@2.3.11", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.11", "@biomejs/cli-darwin-x64": "2.3.11", "@biomejs/cli-linux-arm64": "2.3.11", "@biomejs/cli-linux-arm64-musl": "2.3.11", "@biomejs/cli-linux-x64": "2.3.11", "@biomejs/cli-linux-x64-musl": "2.3.11", "@biomejs/cli-win32-arm64": "2.3.11", "@biomejs/cli-win32-x64": "2.3.11" }, "bin": { "biome": "bin/biome" } }, "sha512-/zt+6qazBWguPG6+eWmiELqO+9jRsMZ/DBU3lfuU2ngtIQYzymocHhKiZRyrbra4aCOoyTg/BmY+6WH5mv9xmQ=="],
|
"@babel/traverse": ["@babel/traverse@7.28.4", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", "@babel/types": "^7.28.4", "debug": "^4.3.1" } }, "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ=="],
|
||||||
|
|
||||||
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-/uXXkBcPKVQY7rc9Ys2CrlirBJYbpESEDme7RKiBD6MmqR2w3j0+ZZXRIL2xiaNPsIMMNhP1YnA+jRRxoOAFrA=="],
|
"@babel/types": ["@babel/types@7.28.4", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q=="],
|
||||||
|
|
||||||
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-fh7nnvbweDPm2xEmFjfmq7zSUiox88plgdHF9OIW4i99WnXrAC3o2P3ag9judoUMv8FCSUnlwJCM1B64nO5Fbg=="],
|
"@biomejs/biome": ["@biomejs/biome@2.2.5", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.2.5", "@biomejs/cli-darwin-x64": "2.2.5", "@biomejs/cli-linux-arm64": "2.2.5", "@biomejs/cli-linux-arm64-musl": "2.2.5", "@biomejs/cli-linux-x64": "2.2.5", "@biomejs/cli-linux-x64-musl": "2.2.5", "@biomejs/cli-win32-arm64": "2.2.5", "@biomejs/cli-win32-x64": "2.2.5" }, "bin": { "biome": "bin/biome" } }, "sha512-zcIi+163Rc3HtyHbEO7CjeHq8DjQRs40HsGbW6vx2WI0tg8mYQOPouhvHSyEnCBAorfYNnKdR64/IxO7xQ5faw=="],
|
||||||
|
|
||||||
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-l4xkGa9E7Uc0/05qU2lMYfN1H+fzzkHgaJoy98wO+b/7Gl78srbCRRgwYSW+BTLixTBrM6Ede5NSBwt7rd/i6g=="],
|
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.2.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-MYT+nZ38wEIWVcL5xLyOhYQQ7nlWD0b/4mgATW2c8dvq7R4OQjt/XGXFkXrmtWmQofaIM14L7V8qIz/M+bx5QQ=="],
|
||||||
|
|
||||||
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-XPSQ+XIPZMLaZ6zveQdwNjbX+QdROEd1zPgMwD47zvHV+tCGB88VH+aynyGxAHdzL+Tm/+DtKST5SECs4iwCLg=="],
|
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.2.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-FLIEl73fv0R7dI10EnEiZLw+IMz3mWLnF95ASDI0kbx6DDLJjWxE5JxxBfmG+udz1hIDd3fr5wsuP7nwuTRdAg=="],
|
||||||
|
|
||||||
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.11", "", { "os": "linux", "cpu": "x64" }, "sha512-/1s9V/H3cSe0r0Mv/Z8JryF5x9ywRxywomqZVLHAoa/uN0eY7F8gEngWKNS5vbbN/BsfpCG5yeBT5ENh50Frxg=="],
|
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.2.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-5DjiiDfHqGgR2MS9D+AZ8kOfrzTGqLKywn8hoXpXXlJXIECGQ32t+gt/uiS2XyGBM2XQhR6ztUvbjZWeccFMoQ=="],
|
||||||
|
|
||||||
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.11", "", { "os": "linux", "cpu": "x64" }, "sha512-vU7a8wLs5C9yJ4CB8a44r12aXYb8yYgBn+WeyzbMjaCMklzCv1oXr8x+VEyWodgJt9bDmhiaW/I0RHbn7rsNmw=="],
|
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.2.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Ov2wgAFwqDvQiESnu7b9ufD1faRa+40uwrohgBopeY84El2TnBDoMNXx6iuQdreoFGjwW8vH6k68G21EpNERw=="],
|
||||||
|
|
||||||
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-PZQ6ElCOnkYapSsysiTy0+fYX+agXPlWugh6+eQ6uPKI3vKAqNp6TnMhoM3oY2NltSB89hz59o8xIfOdyhi9Iw=="],
|
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.2.5", "", { "os": "linux", "cpu": "x64" }, "sha512-fq9meKm1AEXeAWan3uCg6XSP5ObA6F/Ovm89TwaMiy1DNIwdgxPkNwxlXJX8iM6oRbFysYeGnT0OG8diCWb9ew=="],
|
||||||
|
|
||||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.11", "", { "os": "win32", "cpu": "x64" }, "sha512-43VrG813EW+b5+YbDbz31uUsheX+qFKCpXeY9kfdAx+ww3naKxeVkTD9zLIWxUPfJquANMHrmW3wbe/037G0Qg=="],
|
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.2.5", "", { "os": "linux", "cpu": "x64" }, "sha512-AVqLCDb/6K7aPNIcxHaTQj01sl1m989CJIQFQEaiQkGr2EQwyOpaATJ473h+nXDUuAcREhccfRpe/tu+0wu0eQ=="],
|
||||||
|
|
||||||
|
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.2.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-xaOIad4wBambwJa6mdp1FigYSIF9i7PCqRbvBqtIi9y29QtPVQ13sDGtUnsRoe6SjL10auMzQ6YAe+B3RpZXVg=="],
|
||||||
|
|
||||||
|
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.2.5", "", { "os": "win32", "cpu": "x64" }, "sha512-F/jhuXCssPFAuciMhHKk00xnCAxJRS/pUzVfXYmOMUp//XW7mO6QeCjsjvnm8L4AO/dG2VOB0O+fJPiJ2uXtIw=="],
|
||||||
|
|
||||||
"@effect-fc/example": ["@effect-fc/example@workspace:packages/example"],
|
"@effect-fc/example": ["@effect-fc/example@workspace:packages/example"],
|
||||||
|
|
||||||
"@effect/language-service": ["@effect/language-service@0.75.0", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-DxRN8+b5IEQ/x8hukpV39kJe7fs6er7LDWp1PvKjOxPkN5UJ8VJovUVzoHtOX6XWzMmJBRCN9/j0s8jujXTduw=="],
|
"@effect/language-service": ["@effect/language-service@0.52.1", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-1azwmMvg5UhJ2HgJ7iNhdbrbCvXgPNvszjOKBZmxEWLUSlvzki/e0JX33nz6pW15GTO2ZkuCf2ExwnsFX9atnQ=="],
|
||||||
|
|
||||||
"@effect/platform": ["@effect/platform@0.94.2", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.4", "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^3.19.15" } }, "sha512-85vdwpnK4oH/rJ3EuX/Gi2Hkt+K4HvXWr9bxCuqvty9hxyEcRxkJcqTesYrcVoQB6aULb1Za2B0MKoTbvffB3Q=="],
|
"@effect/platform": ["@effect/platform@0.92.1", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.4", "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^3.18.1" } }, "sha512-XXWCBVwyhaKZISN7aM1fv/3fWDGyxr84ObywnUrL8aHvJLoIeskWFAP/fqw3c5MFCrJ3ZV97RWLbv6JiBQugdg=="],
|
||||||
|
|
||||||
"@effect/platform-browser": ["@effect/platform-browser@0.74.0", "", { "dependencies": { "multipasta": "^0.2.7" }, "peerDependencies": { "@effect/platform": "^0.94.0", "effect": "^3.19.13" } }, "sha512-PAgkg5L5cASQpScA0SZTSy543MVA4A9kmpVCjo2fCINLRpTeuCFAOQHgPmw8dKHnYS0yGs2TYn7AlrhhqQ5o3g=="],
|
"@effect/platform-browser": ["@effect/platform-browser@0.72.0", "", { "dependencies": { "multipasta": "^0.2.7" }, "peerDependencies": { "@effect/platform": "^0.92.0", "effect": "^3.18.0" } }, "sha512-xLlhR2S5yGo7//i8rTOiu1wCyrmrotXk+lK7Y257odxmQ2+HhV4wA2E+xFa0bFbHnqFCE3Yza9r0BkA3y1tgag=="],
|
||||||
|
|
||||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw=="],
|
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.10", "", { "os": "aix", "cpu": "ppc64" }, "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw=="],
|
||||||
|
|
||||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.27.2", "", { "os": "android", "cpu": "arm" }, "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA=="],
|
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.10", "", { "os": "android", "cpu": "arm" }, "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w=="],
|
||||||
|
|
||||||
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.2", "", { "os": "android", "cpu": "arm64" }, "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA=="],
|
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.10", "", { "os": "android", "cpu": "arm64" }, "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg=="],
|
||||||
|
|
||||||
"@esbuild/android-x64": ["@esbuild/android-x64@0.27.2", "", { "os": "android", "cpu": "x64" }, "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A=="],
|
"@esbuild/android-x64": ["@esbuild/android-x64@0.25.10", "", { "os": "android", "cpu": "x64" }, "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg=="],
|
||||||
|
|
||||||
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg=="],
|
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA=="],
|
||||||
|
|
||||||
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA=="],
|
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg=="],
|
||||||
|
|
||||||
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g=="],
|
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.10", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg=="],
|
||||||
|
|
||||||
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA=="],
|
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.10", "", { "os": "freebsd", "cpu": "x64" }, "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA=="],
|
||||||
|
|
||||||
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.2", "", { "os": "linux", "cpu": "arm" }, "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw=="],
|
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.10", "", { "os": "linux", "cpu": "arm" }, "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg=="],
|
||||||
|
|
||||||
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw=="],
|
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ=="],
|
||||||
|
|
||||||
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w=="],
|
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.10", "", { "os": "linux", "cpu": "ia32" }, "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ=="],
|
||||||
|
|
||||||
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg=="],
|
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg=="],
|
||||||
|
|
||||||
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw=="],
|
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA=="],
|
||||||
|
|
||||||
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ=="],
|
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.10", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA=="],
|
||||||
|
|
||||||
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA=="],
|
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA=="],
|
||||||
|
|
||||||
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w=="],
|
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.10", "", { "os": "linux", "cpu": "s390x" }, "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew=="],
|
||||||
|
|
||||||
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.2", "", { "os": "linux", "cpu": "x64" }, "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA=="],
|
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.10", "", { "os": "linux", "cpu": "x64" }, "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA=="],
|
||||||
|
|
||||||
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.2", "", { "os": "none", "cpu": "arm64" }, "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw=="],
|
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.10", "", { "os": "none", "cpu": "arm64" }, "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A=="],
|
||||||
|
|
||||||
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.2", "", { "os": "none", "cpu": "x64" }, "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA=="],
|
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.10", "", { "os": "none", "cpu": "x64" }, "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig=="],
|
||||||
|
|
||||||
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA=="],
|
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.10", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw=="],
|
||||||
|
|
||||||
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg=="],
|
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.10", "", { "os": "openbsd", "cpu": "x64" }, "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw=="],
|
||||||
|
|
||||||
"@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.2", "", { "os": "none", "cpu": "arm64" }, "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag=="],
|
"@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.10", "", { "os": "none", "cpu": "arm64" }, "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag=="],
|
||||||
|
|
||||||
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg=="],
|
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.10", "", { "os": "sunos", "cpu": "x64" }, "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ=="],
|
||||||
|
|
||||||
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg=="],
|
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw=="],
|
||||||
|
|
||||||
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ=="],
|
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.10", "", { "os": "win32", "cpu": "ia32" }, "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw=="],
|
||||||
|
|
||||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.2", "", { "os": "win32", "cpu": "x64" }, "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ=="],
|
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.10", "", { "os": "win32", "cpu": "x64" }, "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw=="],
|
||||||
|
|
||||||
"@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="],
|
"@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="],
|
||||||
|
|
||||||
@@ -204,6 +218,12 @@
|
|||||||
|
|
||||||
"@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.3", "", { "os": "win32", "cpu": "x64" }, "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ=="],
|
||||||
|
|
||||||
|
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
||||||
|
|
||||||
|
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
||||||
|
|
||||||
|
"@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=="],
|
||||||
|
|
||||||
"@radix-ui/colors": ["@radix-ui/colors@3.0.0", "", {}, "sha512-FUOsGBkHrYJwCSEtWRCIfQbZG7q1e6DgxCIOe1SUQzDe/7rXXeA47s8yCn6fuTNQAj1Zq4oTFi9Yjp3wzElcxg=="],
|
"@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.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="],
|
||||||
@@ -328,81 +348,75 @@
|
|||||||
|
|
||||||
"@radix-ui/themes": ["@radix-ui/themes@3.2.1", "", { "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-WJL2YKAGItkunwm3O4cLTFKCGJTfAfF6Hmq7f5bCo1ggqC9qJQ/wfg/25AAN72aoEM1yqXZQ+pslsw48AFR0Xg=="],
|
"@radix-ui/themes": ["@radix-ui/themes@3.2.1", "", { "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-WJL2YKAGItkunwm3O4cLTFKCGJTfAfF6Hmq7f5bCo1ggqC9qJQ/wfg/25AAN72aoEM1yqXZQ+pslsw48AFR0Xg=="],
|
||||||
|
|
||||||
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.53", "", {}, "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ=="],
|
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.38", "", {}, "sha512-N/ICGKleNhA5nc9XXQG/kkKHJ7S55u0x0XUJbbkmdCnFuoRkM1Il12q9q0eX19+M7KKUEPw/daUPIRnxhcxAIw=="],
|
||||||
|
|
||||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.55.1", "", { "os": "android", "cpu": "arm" }, "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg=="],
|
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.3", "", { "os": "android", "cpu": "arm" }, "sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw=="],
|
||||||
|
|
||||||
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.55.1", "", { "os": "android", "cpu": "arm64" }, "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg=="],
|
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.3", "", { "os": "android", "cpu": "arm64" }, "sha512-wd+u7SLT/u6knklV/ifG7gr5Qy4GUbH2hMWcDauPFJzmCZUAJ8L2bTkVXC2niOIxp8lk3iH/QX8kSrUxVZrOVw=="],
|
||||||
|
|
||||||
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.55.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg=="],
|
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.52.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-lj9ViATR1SsqycwFkJCtYfQTheBdvlWJqzqxwc9f2qrcVrQaF/gCuBRTiTolkRWS6KvNxSk4KHZWG7tDktLgjg=="],
|
||||||
|
|
||||||
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.55.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ=="],
|
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.52.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-+Dyo7O1KUmIsbzx1l+4V4tvEVnVQqMOIYtrxK7ncLSknl1xnMHLgn7gddJVrYPNZfEB8CIi3hK8gq8bDhb3h5A=="],
|
||||||
|
|
||||||
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.55.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg=="],
|
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.52.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-u9Xg2FavYbD30g3DSfNhxgNrxhi6xVG4Y6i9Ur1C7xUuGDW3banRbXj+qgnIrwRN4KeJ396jchwy9bCIzbyBEQ=="],
|
||||||
|
|
||||||
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.55.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw=="],
|
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.52.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-5M8kyi/OX96wtD5qJR89a/3x5x8x5inXBZO04JWhkQb2JWavOWfjgkdvUqibGJeNNaz1/Z1PPza5/tAPXICI6A=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.55.1", "", { "os": "linux", "cpu": "arm" }, "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ=="],
|
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.52.3", "", { "os": "linux", "cpu": "arm" }, "sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.55.1", "", { "os": "linux", "cpu": "arm" }, "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg=="],
|
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.52.3", "", { "os": "linux", "cpu": "arm" }, "sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.55.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ=="],
|
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.52.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.55.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA=="],
|
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.52.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g=="],
|
"@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.52.3", "", { "os": "linux", "cpu": "none" }, "sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw=="],
|
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.52.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.55.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw=="],
|
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.52.3", "", { "os": "linux", "cpu": "none" }, "sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.55.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw=="],
|
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.52.3", "", { "os": "linux", "cpu": "none" }, "sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw=="],
|
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.52.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg=="],
|
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.52.3", "", { "os": "linux", "cpu": "x64" }, "sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.55.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg=="],
|
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.52.3", "", { "os": "linux", "cpu": "x64" }, "sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.55.1", "", { "os": "linux", "cpu": "x64" }, "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg=="],
|
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.52.3", "", { "os": "none", "cpu": "arm64" }, "sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA=="],
|
||||||
|
|
||||||
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.55.1", "", { "os": "linux", "cpu": "x64" }, "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w=="],
|
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.52.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-+zteHZdoUYLkyYKObGHieibUFLbttX2r+58l27XZauq0tcWYYuKUwY2wjeCN9oK1Um2YgH2ibd6cnX/wFD7DuA=="],
|
||||||
|
|
||||||
"@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.55.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg=="],
|
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.52.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-of1iHkTQSo3kr6dTIRX6t81uj/c/b15HXVsPcEElN5sS859qHrOepM5p9G41Hah+CTqSh2r8Bm56dL2z9UQQ7g=="],
|
||||||
|
|
||||||
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.55.1", "", { "os": "none", "cpu": "arm64" }, "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw=="],
|
"@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.3", "", { "os": "win32", "cpu": "x64" }, "sha512-s0hybmlHb56mWVZQj8ra9048/WZTPLILKxcvcq+8awSZmyiSUZjjem1AhU3Tf4ZKpYhK4mg36HtHDOe8QJS5PQ=="],
|
||||||
|
|
||||||
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.55.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g=="],
|
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.3", "", { "os": "win32", "cpu": "x64" }, "sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA=="],
|
||||||
|
|
||||||
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.55.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA=="],
|
"@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="],
|
||||||
|
|
||||||
"@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.55.1", "", { "os": "win32", "cpu": "x64" }, "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg=="],
|
"@tanstack/history": ["@tanstack/history@1.132.31", "", {}, "sha512-UCHM2uS0t/uSszqPEo+SBSSoQVeQ+LlOWAVBl5SA7+AedeAbKafIPjFn8huZCXNLAYb0WKV2+wETr7lDK9uz7g=="],
|
||||||
|
|
||||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.55.1", "", { "os": "win32", "cpu": "x64" }, "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw=="],
|
"@tanstack/react-router": ["@tanstack/react-router@1.132.31", "", { "dependencies": { "@tanstack/history": "1.132.31", "@tanstack/react-store": "^0.7.0", "@tanstack/router-core": "1.132.31", "isbot": "^5.1.22", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-bgYgffI9TQhi8Zc/I5DMQEO4WOcDNtSll66Eb3/+k3iuI59ovVB/CiVCGjqdT8+2YBBj2x0saRDjsF00vj5+Yg=="],
|
||||||
|
|
||||||
"@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
"@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.132.31", "", { "dependencies": { "@tanstack/router-devtools-core": "1.132.31", "vite": "^7.1.7" }, "peerDependencies": { "@tanstack/react-router": "^1.132.31", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-EiO+t6s1K8igqqtxCO0GLG6KoJgaIsv9JAZMcJV+z/BspElGQwGDBzTtWYcHd9NOP2Yw7OCkAhM8ihwMbzWJNQ=="],
|
||||||
|
|
||||||
"@tanstack/history": ["@tanstack/history@1.154.7", "", {}, "sha512-YBgwS9qG4rs1ZY/ZrhQtjOH8BG9Qa2wf2AsxT/SnZ4HZJ1DcCEqkoiHH0yH6CYvdDit31X5HokOqQrRSsZEwGA=="],
|
"@tanstack/react-store": ["@tanstack/react-store@0.7.7", "", { "dependencies": { "@tanstack/store": "0.7.7", "use-sync-external-store": "^1.5.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-qqT0ufegFRDGSof9D/VqaZgjNgp4tRPHZIJq2+QIHkMUtHjaJ0lYrrXjeIUJvjnTbgPfSD1XgOMEt0lmANn6Zg=="],
|
||||||
|
|
||||||
"@tanstack/react-router": ["@tanstack/react-router@1.154.12", "", { "dependencies": { "@tanstack/history": "1.154.7", "@tanstack/react-store": "^0.8.0", "@tanstack/router-core": "1.154.12", "isbot": "^5.1.22", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-WiYfC6IYC2HwjkATouJCQlAM5RJ8MViefslfUcZpsbCb+WGQpdpvUY7GPJLEeessSpqgiC2EabRYC2kYVNyMPg=="],
|
"@tanstack/router-core": ["@tanstack/router-core@1.132.31", "", { "dependencies": { "@tanstack/history": "1.132.31", "@tanstack/store": "^0.7.0", "cookie-es": "^2.0.0", "seroval": "^1.3.2", "seroval-plugins": "^1.3.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-74W+J5N1NuPcuWDwsBAjCgK4ahtIRaB51KdegYrD1AeSNqiV4u8KzOzHKAAZD01UipQApUbpJbzFrHq0XQ9BHw=="],
|
||||||
|
|
||||||
"@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.154.12", "", { "dependencies": { "@tanstack/router-devtools-core": "1.154.12" }, "peerDependencies": { "@tanstack/react-router": "^1.154.12", "@tanstack/router-core": "^1.154.12", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" }, "optionalPeers": ["@tanstack/router-core"] }, "sha512-TcGe7pmeVjk1zD58eMR87GG9OXMx6LDGz5QopmJS4LafvK2hvuaht+eKBnZlCvKLPlXu5juwHT4u+2bYdn6sqQ=="],
|
"@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.132.31", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16", "solid-js": "^1.9.5", "vite": "^7.1.7" }, "peerDependencies": { "@tanstack/router-core": "^1.132.31", "csstype": "^3.0.10", "tiny-invariant": "^1.3.3" }, "optionalPeers": ["csstype"] }, "sha512-GwymJRm21hkluQMjOkXn+mBNPMyWlpzQut8mqEObh1cnF3zUsYT5YkCFV8ePA0jb/YVdjK/AfCAgSlhyIa09IA=="],
|
||||||
|
|
||||||
"@tanstack/react-store": ["@tanstack/react-store@0.8.0", "", { "dependencies": { "@tanstack/store": "0.8.0", "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-1vG9beLIuB7q69skxK9r5xiLN3ztzIPfSQSs0GfeqWGO2tGIyInZx0x1COhpx97RKaONSoAb8C3dxacWksm1ow=="],
|
"@tanstack/router-generator": ["@tanstack/router-generator@1.132.31", "", { "dependencies": { "@tanstack/router-core": "1.132.31", "@tanstack/router-utils": "1.132.31", "@tanstack/virtual-file-routes": "1.132.31", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-6Ys47sBR3jxet3CaqnF/ykV44R8HLQoT5ZbDqi6f2At6TXYe/+VELRSApC+cq1yjVJwp6Ot5Hm6mYWewh69bdQ=="],
|
||||||
|
|
||||||
"@tanstack/router-core": ["@tanstack/router-core@1.154.12", "", { "dependencies": { "@tanstack/history": "1.154.7", "@tanstack/store": "^0.8.0", "cookie-es": "^2.0.0", "seroval": "^1.4.2", "seroval-plugins": "^1.4.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-p+TKxkXcLGtCwwW237D8pV4f6ea2K1pzc/e65ljugoTawsA/YR2/gmTSBDTUsSYy6Tmu4mMJmZ0Q4zNkcfCS3g=="],
|
"@tanstack/router-plugin": ["@tanstack/router-plugin@1.132.31", "", { "dependencies": { "@babel/core": "^7.27.7", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/template": "^7.27.2", "@babel/traverse": "^7.27.7", "@babel/types": "^7.27.7", "@tanstack/router-core": "1.132.31", "@tanstack/router-generator": "1.132.31", "@tanstack/router-utils": "1.132.31", "@tanstack/virtual-file-routes": "1.132.31", "babel-dead-code-elimination": "^1.0.10", "chokidar": "^3.6.0", "unplugin": "^2.1.2", "zod": "^3.24.2" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", "@tanstack/react-router": "^1.132.31", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0", "vite-plugin-solid": "^2.11.8", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-5/n6VxA6tFLFyewjl1+Av0Qsxmr/WpnAR2UlccS7ZaYli3bvNPJSZd3dy9EphEAXeSbqvFT29nQ/ox8EmGTonQ=="],
|
||||||
|
|
||||||
"@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.154.12", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16", "tiny-invariant": "^1.3.3" }, "peerDependencies": { "@tanstack/router-core": "^1.154.12", "csstype": "^3.0.10" }, "optionalPeers": ["csstype"] }, "sha512-lvnP9cqknvSSkUjqQRVn61TcBhq72hCFFOzMwdFdFPTO8nMEXvYE6ZZJiXtivwcvsKmO6XVFLMXuJr/928gNkw=="],
|
"@tanstack/router-utils": ["@tanstack/router-utils@1.132.31", "", { "dependencies": { "@babel/core": "^7.27.4", "@babel/generator": "^7.27.5", "@babel/parser": "^7.27.5", "@babel/preset-typescript": "^7.27.1", "ansis": "^4.1.0", "diff": "^8.0.2", "fast-glob": "^3.3.3", "pathe": "^2.0.3" } }, "sha512-uf8mQ3wV58K8TL5XXBoWhkYxmCV7LLWbbf6AvcxdhnCnBNmXBGlY+T8RdsRnXyI2Iyp2HfHaVZ+8H3CEQedXfw=="],
|
||||||
|
|
||||||
"@tanstack/router-generator": ["@tanstack/router-generator@1.154.12", "", { "dependencies": { "@tanstack/router-core": "1.154.12", "@tanstack/router-utils": "1.154.7", "@tanstack/virtual-file-routes": "1.154.7", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-cjr3KS3Esnyh05CWl78KgK2Z9kTjeFasZXcSUrh//TzzU72eXQ+dzKppD3kMsjuyRfUxAfdufsR9GDNMMuLk9w=="],
|
"@tanstack/store": ["@tanstack/store@0.7.7", "", {}, "sha512-xa6pTan1bcaqYDS9BDpSiS63qa6EoDkPN9RsRaxHuDdVDNntzq3xNwR5YKTU/V3SkSyC9T4YVOPh2zRQN0nhIQ=="],
|
||||||
|
|
||||||
"@tanstack/router-plugin": ["@tanstack/router-plugin@1.154.12", "", { "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.154.12", "@tanstack/router-generator": "1.154.12", "@tanstack/router-utils": "1.154.7", "@tanstack/virtual-file-routes": "1.154.7", "babel-dead-code-elimination": "^1.0.11", "chokidar": "^3.6.0", "unplugin": "^2.1.2", "zod": "^3.24.2" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", "@tanstack/react-router": "^1.154.12", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0", "vite-plugin-solid": "^2.11.10", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-YlFjrL5j7RbYT/B3RZZedbXOHXfqRV7b/qIGyojBaHsrIgKFGo4AHg/FyS50HJaHGQ27vvgWNSy/4Orrozbm0Q=="],
|
"@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.132.31", "", {}, "sha512-rxS8Cm2nIXroLqkm9pE/8X2lFNuvcTIIiFi5VH4PwzvKscAuaW3YRMN1WmaGDI2mVEn+GLaoY6Kc3jOczL5i4w=="],
|
||||||
|
|
||||||
"@tanstack/router-utils": ["@tanstack/router-utils@1.154.7", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/generator": "^7.28.5", "@babel/parser": "^7.28.5", "ansis": "^4.1.0", "diff": "^8.0.2", "pathe": "^2.0.3", "tinyglobby": "^0.2.15" } }, "sha512-61bGx32tMKuEpVRseu2sh1KQe8CfB7793Mch/kyQt0EP3tD7X0sXmimCl3truRiDGUtI0CaSoQV1NPjAII1RBA=="],
|
|
||||||
|
|
||||||
"@tanstack/store": ["@tanstack/store@0.8.0", "", {}, "sha512-Om+BO0YfMZe//X2z0uLF2j+75nQga6TpTJgLJQBiq85aOyZNIhkCgleNcud2KQg4k4v9Y9l+Uhru3qWMPGTOzQ=="],
|
|
||||||
|
|
||||||
"@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.154.7", "", {}, "sha512-cHHDnewHozgjpI+MIVp9tcib6lYEQK5MyUr0ChHpHFGBl8Xei55rohFK0I0ve/GKoHeioaK42Smd8OixPp6CTg=="],
|
|
||||||
|
|
||||||
"@typed/id": ["@typed/id@0.17.2", "", { "peerDependencies": { "effect": "^3.14.7" } }, "sha512-z/Z14/moeu9x45IpkGaRwuvb+CQ3s3UCc/agcpZibTz1yPb3RgSDXx4rOHIuyb6hG6oNzqe9yY4GbbMq3Hb5Ug=="],
|
"@typed/id": ["@typed/id@0.17.2", "", { "peerDependencies": { "effect": "^3.14.7" } }, "sha512-z/Z14/moeu9x45IpkGaRwuvb+CQ3s3UCc/agcpZibTz1yPb3RgSDXx4rOHIuyb6hG6oNzqe9yY4GbbMq3Hb5Ug=="],
|
||||||
|
|
||||||
@@ -414,17 +428,17 @@
|
|||||||
|
|
||||||
"@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="],
|
"@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="],
|
||||||
|
|
||||||
"@types/bun": ["@types/bun@1.3.6", "", { "dependencies": { "bun-types": "1.3.6" } }, "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA=="],
|
"@types/bun": ["@types/bun@1.2.23", "", { "dependencies": { "bun-types": "1.2.23" } }, "sha512-le8ueOY5b6VKYf19xT3McVbXqLqmxzPXHsQT/q9JHgikJ2X22wyTW3g3ohz2ZMnp7dod6aduIiq8A14Xyimm0A=="],
|
||||||
|
|
||||||
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||||
|
|
||||||
"@types/node": ["@types/node@25.0.9", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-/rpCXHlCWeqClNBwUhDcusJxXYDjZTyE8v5oTO7WbL8eij2nKhUeU89/6xgjU7N4/Vh3He0BtyhJdQbDyhiXAw=="],
|
"@types/node": ["@types/node@24.6.2", "", { "dependencies": { "undici-types": "~7.13.0" } }, "sha512-d2L25Y4j+W3ZlNAeMKcy7yDsK425ibcAOO2t7aPTz6gNMH0z2GThtwENCDc0d/Pw9wgyRqE5Px1wkV7naz8ang=="],
|
||||||
|
|
||||||
"@types/react": ["@types/react@19.2.9", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA=="],
|
"@types/react": ["@types/react@19.2.0", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-1LOH8xovvsKsCBq1wnT4ntDUdCJKmnEakhsuoUSy6ExlHCkGP2hqnatagYTgFk6oeL0VU31u7SNjunPN+GchtA=="],
|
||||||
|
|
||||||
"@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
|
"@types/react-dom": ["@types/react-dom@19.2.0", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-brtBs0MnE9SMx7px208g39lRmC5uHZs96caOJfTjFcYSLHNamvaSMfJNagChVNkup2SdtOxKX1FDBkRSJe1ZAg=="],
|
||||||
|
|
||||||
"@vitejs/plugin-react": ["@vitejs/plugin-react@5.1.2", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.53", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ=="],
|
"@vitejs/plugin-react": ["@vitejs/plugin-react@5.0.4", "", { "dependencies": { "@babel/core": "^7.28.4", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.38", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-La0KD0vGkVkSk6K+piWDKRUyg8Rl5iAIKRMH0vMJI0Eg47bq1eOxmoObAaQG37WMW9MSyk7Cs8EIWwJC1PtzKA=="],
|
||||||
|
|
||||||
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
||||||
|
|
||||||
@@ -436,19 +450,19 @@
|
|||||||
|
|
||||||
"ast-types": ["ast-types@0.16.1", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg=="],
|
"ast-types": ["ast-types@0.16.1", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg=="],
|
||||||
|
|
||||||
"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=="],
|
"babel-dead-code-elimination": ["babel-dead-code-elimination@1.0.10", "", { "dependencies": { "@babel/core": "^7.23.7", "@babel/parser": "^7.23.6", "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6" } }, "sha512-DV5bdJZTzZ0zn0DC24v3jD7Mnidh6xhKa4GfKCbq3sfW8kaWhDdZjP3i81geA8T33tdYqWKw4D3fVv0CwEgKVA=="],
|
||||||
|
|
||||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.9.14", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg=="],
|
"baseline-browser-mapping": ["baseline-browser-mapping@2.8.10", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-uLfgBi+7IBNay8ECBO2mVMGZAc1VgZWEChxm4lv+TobGdG82LnXMjuNGo/BSSZZL4UmkWhxEHP2f5ziLNwGWMA=="],
|
||||||
|
|
||||||
"binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
|
"binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
|
||||||
|
|
||||||
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
||||||
|
|
||||||
"browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="],
|
"browserslist": ["browserslist@4.26.3", "", { "dependencies": { "baseline-browser-mapping": "^2.8.9", "caniuse-lite": "^1.0.30001746", "electron-to-chromium": "^1.5.227", "node-releases": "^2.0.21", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w=="],
|
||||||
|
|
||||||
"bun-types": ["bun-types@1.3.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ=="],
|
"bun-types": ["bun-types@1.2.23", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-R9f0hKAZXgFU3mlrA0YpE/fiDvwV0FT9rORApt2aQVWSuJDzZOyB5QLc0N/4HF57CS8IXJ6+L5E4W1bW6NS2Aw=="],
|
||||||
|
|
||||||
"caniuse-lite": ["caniuse-lite@1.0.30001764", "", {}, "sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g=="],
|
"caniuse-lite": ["caniuse-lite@1.0.30001746", "", {}, "sha512-eA7Ys/DGw+pnkWWSE/id29f2IcPHVoE8wxtvE5JdvD2V28VTDPy1yEeo11Guz0sJ4ZeGRcm3uaTcAqK1LXaphA=="],
|
||||||
|
|
||||||
"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=="],
|
"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=="],
|
||||||
|
|
||||||
@@ -460,23 +474,23 @@
|
|||||||
|
|
||||||
"cookie-es": ["cookie-es@2.0.0", "", {}, "sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg=="],
|
"cookie-es": ["cookie-es@2.0.0", "", {}, "sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg=="],
|
||||||
|
|
||||||
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||||
|
|
||||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||||
|
|
||||||
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
"detect-libc": ["detect-libc@2.1.1", "", {}, "sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw=="],
|
||||||
|
|
||||||
"detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="],
|
"detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="],
|
||||||
|
|
||||||
"diff": ["diff@8.0.3", "", {}, "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ=="],
|
"diff": ["diff@8.0.2", "", {}, "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg=="],
|
||||||
|
|
||||||
"effect": ["effect@3.19.15", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-vzMmgfZKLcojmUjBdlQx+uaKryO7yULlRxjpDnHdnvcp1NPHxJyoM6IOXBLlzz2I/uPtZpGKavt5hBv7IvGZkA=="],
|
"effect": ["effect@3.18.1", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-5aJ7yRlvvkBplMSnhPyol7WYvPenvau12asO3HJhG/126SySWV9D8bscGTbV52XxtC5bwO/VUd5ffjE6uep/1A=="],
|
||||||
|
|
||||||
"effect-fc": ["effect-fc@workspace:packages/effect-fc"],
|
"effect-fc": ["effect-fc@workspace:packages/effect-fc"],
|
||||||
|
|
||||||
"electron-to-chromium": ["electron-to-chromium@1.5.267", "", {}, "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw=="],
|
"electron-to-chromium": ["electron-to-chromium@1.5.228", "", {}, "sha512-nxkiyuqAn4MJ1QbobwqJILiDtu/jk14hEAWaMiJmNPh1Z+jqoFlBFZjdXwLWGeVSeu9hGLg6+2G9yJaW8rBIFA=="],
|
||||||
|
|
||||||
"esbuild": ["esbuild@0.27.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.2", "@esbuild/android-arm": "0.27.2", "@esbuild/android-arm64": "0.27.2", "@esbuild/android-x64": "0.27.2", "@esbuild/darwin-arm64": "0.27.2", "@esbuild/darwin-x64": "0.27.2", "@esbuild/freebsd-arm64": "0.27.2", "@esbuild/freebsd-x64": "0.27.2", "@esbuild/linux-arm": "0.27.2", "@esbuild/linux-arm64": "0.27.2", "@esbuild/linux-ia32": "0.27.2", "@esbuild/linux-loong64": "0.27.2", "@esbuild/linux-mips64el": "0.27.2", "@esbuild/linux-ppc64": "0.27.2", "@esbuild/linux-riscv64": "0.27.2", "@esbuild/linux-s390x": "0.27.2", "@esbuild/linux-x64": "0.27.2", "@esbuild/netbsd-arm64": "0.27.2", "@esbuild/netbsd-x64": "0.27.2", "@esbuild/openbsd-arm64": "0.27.2", "@esbuild/openbsd-x64": "0.27.2", "@esbuild/openharmony-arm64": "0.27.2", "@esbuild/sunos-x64": "0.27.2", "@esbuild/win32-arm64": "0.27.2", "@esbuild/win32-ia32": "0.27.2", "@esbuild/win32-x64": "0.27.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw=="],
|
"esbuild": ["esbuild@0.25.10", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.10", "@esbuild/android-arm": "0.25.10", "@esbuild/android-arm64": "0.25.10", "@esbuild/android-x64": "0.25.10", "@esbuild/darwin-arm64": "0.25.10", "@esbuild/darwin-x64": "0.25.10", "@esbuild/freebsd-arm64": "0.25.10", "@esbuild/freebsd-x64": "0.25.10", "@esbuild/linux-arm": "0.25.10", "@esbuild/linux-arm64": "0.25.10", "@esbuild/linux-ia32": "0.25.10", "@esbuild/linux-loong64": "0.25.10", "@esbuild/linux-mips64el": "0.25.10", "@esbuild/linux-ppc64": "0.25.10", "@esbuild/linux-riscv64": "0.25.10", "@esbuild/linux-s390x": "0.25.10", "@esbuild/linux-x64": "0.25.10", "@esbuild/netbsd-arm64": "0.25.10", "@esbuild/netbsd-x64": "0.25.10", "@esbuild/openbsd-arm64": "0.25.10", "@esbuild/openbsd-x64": "0.25.10", "@esbuild/openharmony-arm64": "0.25.10", "@esbuild/sunos-x64": "0.25.10", "@esbuild/win32-arm64": "0.25.10", "@esbuild/win32-ia32": "0.25.10", "@esbuild/win32-x64": "0.25.10" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ=="],
|
||||||
|
|
||||||
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
||||||
|
|
||||||
@@ -484,6 +498,10 @@
|
|||||||
|
|
||||||
"fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="],
|
"fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="],
|
||||||
|
|
||||||
|
"fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
|
||||||
|
|
||||||
|
"fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
|
||||||
|
|
||||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||||
|
|
||||||
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
||||||
@@ -496,13 +514,13 @@
|
|||||||
|
|
||||||
"get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
|
"get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
|
||||||
|
|
||||||
"get-tsconfig": ["get-tsconfig@4.13.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ=="],
|
"get-tsconfig": ["get-tsconfig@4.10.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="],
|
||||||
|
|
||||||
"glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
"glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||||
|
|
||||||
"globals": ["globals@17.0.0", "", {}, "sha512-gv5BeD2EssA793rlFWVPMMCqefTlpusw6/2TbAVMy0FzcG8wKJn4O+NqJ4+XWmmwrayJgw5TzrmWjFgmz1XPqw=="],
|
"globals": ["globals@16.4.0", "", {}, "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw=="],
|
||||||
|
|
||||||
"goober": ["goober@2.1.18", "", { "peerDependencies": { "csstype": "^3.0.10" } }, "sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw=="],
|
"goober": ["goober@2.1.16", "", { "peerDependencies": { "csstype": "^3.0.10" } }, "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g=="],
|
||||||
|
|
||||||
"is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="],
|
"is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="],
|
||||||
|
|
||||||
@@ -512,7 +530,7 @@
|
|||||||
|
|
||||||
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
|
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
|
||||||
|
|
||||||
"isbot": ["isbot@5.1.32", "", {}, "sha512-VNfjM73zz2IBZmdShMfAUg10prm6t7HFUQmNAEOAVS4YH92ZrZcvkMcGX6cIgBJAzWDzPent/EeAtYEHNPNPBQ=="],
|
"isbot": ["isbot@5.1.31", "", {}, "sha512-DPgQshehErHAqSCKDb3rNW03pa2wS/v5evvUqtxt6TTnHRqAG8FdzcSSJs9656pK6Y+NT7K9R4acEYXLHYfpUQ=="],
|
||||||
|
|
||||||
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||||
|
|
||||||
@@ -522,9 +540,13 @@
|
|||||||
|
|
||||||
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||||
|
|
||||||
|
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
|
||||||
|
|
||||||
|
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
|
||||||
|
|
||||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||||
|
|
||||||
"msgpackr": ["msgpackr@1.11.8", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-bC4UGzHhVvgDNS7kn9tV8fAucIYUBuGojcaLiz7v+P63Lmtm0Xeji8B/8tYKddALXxJLpwIeBmUN3u64C4YkRA=="],
|
"msgpackr": ["msgpackr@1.11.5", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA=="],
|
||||||
|
|
||||||
"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.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=="],
|
||||||
|
|
||||||
@@ -534,11 +556,11 @@
|
|||||||
|
|
||||||
"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-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.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="],
|
"node-releases": ["node-releases@2.0.21", "", {}, "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw=="],
|
||||||
|
|
||||||
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
|
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
|
||||||
|
|
||||||
"npm-check-updates": ["npm-check-updates@19.3.1", "", { "bin": { "npm-check-updates": "build/cli.js", "ncu": "build/cli.js" } }, "sha512-v92fHH8fmf9VVmQwwL5JWpX8GDEe8BDyrz4w3GF6D6JBUZKpQNcTfBBgxVkCcAPzVUjCHSZEXYmZAAKfLTsDBA=="],
|
"npm-check-updates": ["npm-check-updates@19.0.0", "", { "bin": { "npm-check-updates": "build/cli.js", "ncu": "build/cli.js" } }, "sha512-qcfjZEv6xB+WvW24S8wU1MKISPPiTREraBg62XDo/7zmOLXH3Zj7ti2v/LRfks0qITU8SDZLTWwgIitflvursw=="],
|
||||||
|
|
||||||
"npm-sort": ["npm-sort@0.0.4", "", { "bin": { "npm-sort": "./index.js" } }, "sha512-S5Id/3Jvr7Cf/QnWjRteprngERCBhhEFOM+wMhUrAYP060/HUBC1aL5GoXS3xITlgacJCWaSmP4HQaAt91nNYQ=="],
|
"npm-sort": ["npm-sort@0.0.4", "", { "bin": { "npm-sort": "./index.js" } }, "sha512-S5Id/3Jvr7Cf/QnWjRteprngERCBhhEFOM+wMhUrAYP060/HUBC1aL5GoXS3xITlgacJCWaSmP4HQaAt91nNYQ=="],
|
||||||
|
|
||||||
@@ -550,21 +572,23 @@
|
|||||||
|
|
||||||
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
||||||
|
|
||||||
"prettier": ["prettier@3.8.0", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-yEPsovQfpxYfgWNhCfECjG5AQaO+K3dp6XERmOepyPDVqcJm+bjyCVO3pmU+nAPe0N5dDvekfGezt/EIiRe1TA=="],
|
"prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="],
|
||||||
|
|
||||||
"pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="],
|
"pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="],
|
||||||
|
|
||||||
|
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
||||||
|
|
||||||
"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.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=="],
|
||||||
|
|
||||||
"react": ["react@19.2.3", "", {}, "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA=="],
|
"react": ["react@19.2.0", "", {}, "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ=="],
|
||||||
|
|
||||||
"react-dom": ["react-dom@19.2.3", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.3" } }, "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg=="],
|
"react-dom": ["react-dom@19.2.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ=="],
|
||||||
|
|
||||||
"react-icons": ["react-icons@5.5.0", "", { "peerDependencies": { "react": "*" } }, "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw=="],
|
"react-icons": ["react-icons@5.5.0", "", { "peerDependencies": { "react": "*" } }, "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw=="],
|
||||||
|
|
||||||
"react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="],
|
"react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="],
|
||||||
|
|
||||||
"react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="],
|
"react-remove-scroll": ["react-remove-scroll@2.7.1", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA=="],
|
||||||
|
|
||||||
"react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="],
|
"react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="],
|
||||||
|
|
||||||
@@ -576,15 +600,21 @@
|
|||||||
|
|
||||||
"resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
|
"resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
|
||||||
|
|
||||||
"rollup": ["rollup@4.55.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.55.1", "@rollup/rollup-android-arm64": "4.55.1", "@rollup/rollup-darwin-arm64": "4.55.1", "@rollup/rollup-darwin-x64": "4.55.1", "@rollup/rollup-freebsd-arm64": "4.55.1", "@rollup/rollup-freebsd-x64": "4.55.1", "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", "@rollup/rollup-linux-arm-musleabihf": "4.55.1", "@rollup/rollup-linux-arm64-gnu": "4.55.1", "@rollup/rollup-linux-arm64-musl": "4.55.1", "@rollup/rollup-linux-loong64-gnu": "4.55.1", "@rollup/rollup-linux-loong64-musl": "4.55.1", "@rollup/rollup-linux-ppc64-gnu": "4.55.1", "@rollup/rollup-linux-ppc64-musl": "4.55.1", "@rollup/rollup-linux-riscv64-gnu": "4.55.1", "@rollup/rollup-linux-riscv64-musl": "4.55.1", "@rollup/rollup-linux-s390x-gnu": "4.55.1", "@rollup/rollup-linux-x64-gnu": "4.55.1", "@rollup/rollup-linux-x64-musl": "4.55.1", "@rollup/rollup-openbsd-x64": "4.55.1", "@rollup/rollup-openharmony-arm64": "4.55.1", "@rollup/rollup-win32-arm64-msvc": "4.55.1", "@rollup/rollup-win32-ia32-msvc": "4.55.1", "@rollup/rollup-win32-x64-gnu": "4.55.1", "@rollup/rollup-win32-x64-msvc": "4.55.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A=="],
|
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
||||||
|
|
||||||
|
"rollup": ["rollup@4.52.3", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.3", "@rollup/rollup-android-arm64": "4.52.3", "@rollup/rollup-darwin-arm64": "4.52.3", "@rollup/rollup-darwin-x64": "4.52.3", "@rollup/rollup-freebsd-arm64": "4.52.3", "@rollup/rollup-freebsd-x64": "4.52.3", "@rollup/rollup-linux-arm-gnueabihf": "4.52.3", "@rollup/rollup-linux-arm-musleabihf": "4.52.3", "@rollup/rollup-linux-arm64-gnu": "4.52.3", "@rollup/rollup-linux-arm64-musl": "4.52.3", "@rollup/rollup-linux-loong64-gnu": "4.52.3", "@rollup/rollup-linux-ppc64-gnu": "4.52.3", "@rollup/rollup-linux-riscv64-gnu": "4.52.3", "@rollup/rollup-linux-riscv64-musl": "4.52.3", "@rollup/rollup-linux-s390x-gnu": "4.52.3", "@rollup/rollup-linux-x64-gnu": "4.52.3", "@rollup/rollup-linux-x64-musl": "4.52.3", "@rollup/rollup-openharmony-arm64": "4.52.3", "@rollup/rollup-win32-arm64-msvc": "4.52.3", "@rollup/rollup-win32-ia32-msvc": "4.52.3", "@rollup/rollup-win32-x64-gnu": "4.52.3", "@rollup/rollup-win32-x64-msvc": "4.52.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A=="],
|
||||||
|
|
||||||
|
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
|
||||||
|
|
||||||
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
||||||
|
|
||||||
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||||
|
|
||||||
"seroval": ["seroval@1.4.2", "", {}, "sha512-N3HEHRCZYn3cQbsC4B5ldj9j+tHdf4JZoYPlcI4rRYu0Xy4qN8MQf1Z08EibzB0WpgRG5BGK08FTrmM66eSzKQ=="],
|
"seroval": ["seroval@1.3.2", "", {}, "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ=="],
|
||||||
|
|
||||||
"seroval-plugins": ["seroval-plugins@1.4.2", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-X7p4MEDTi+60o2sXZ4bnDBhgsUYDSkQEvzYZuJyFqWg9jcoPsHts5nrg5O956py2wyt28lUrBxk0M0/wU8URpA=="],
|
"seroval-plugins": ["seroval-plugins@1.3.3", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w=="],
|
||||||
|
|
||||||
|
"solid-js": ["solid-js@1.9.9", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.3.0", "seroval-plugins": "~1.3.0" } }, "sha512-A0ZBPJQldAeGCTW0YRYJmt7RCeh5rbFfPZ2aOttgYnctHE7HgKeHCBB/PVc2P7eOfmNXqMFFFoYYdm3S4dcbkA=="],
|
||||||
|
|
||||||
"source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
|
"source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
|
||||||
|
|
||||||
@@ -602,31 +632,31 @@
|
|||||||
|
|
||||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||||
|
|
||||||
"tsx": ["tsx@4.21.0", "", { "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw=="],
|
"tsx": ["tsx@4.20.6", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg=="],
|
||||||
|
|
||||||
"turbo": ["turbo@2.7.5", "", { "optionalDependencies": { "turbo-darwin-64": "2.7.5", "turbo-darwin-arm64": "2.7.5", "turbo-linux-64": "2.7.5", "turbo-linux-arm64": "2.7.5", "turbo-windows-64": "2.7.5", "turbo-windows-arm64": "2.7.5" }, "bin": { "turbo": "bin/turbo" } }, "sha512-7Imdmg37joOloTnj+DPrab9hIaQcDdJ5RwSzcauo/wMOSAgO+A/I/8b3hsGGs6PWQz70m/jkPgdqWsfNKtwwDQ=="],
|
"turbo": ["turbo@2.5.8", "", { "optionalDependencies": { "turbo-darwin-64": "2.5.8", "turbo-darwin-arm64": "2.5.8", "turbo-linux-64": "2.5.8", "turbo-linux-arm64": "2.5.8", "turbo-windows-64": "2.5.8", "turbo-windows-arm64": "2.5.8" }, "bin": { "turbo": "bin/turbo" } }, "sha512-5c9Fdsr9qfpT3hA0EyYSFRZj1dVVsb6KIWubA9JBYZ/9ZEAijgUEae0BBR/Xl/wekt4w65/lYLTFaP3JmwSO8w=="],
|
||||||
|
|
||||||
"turbo-darwin-64": ["turbo-darwin-64@2.7.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-nN3wfLLj4OES/7awYyyM7fkU8U8sAFxsXau2bYJwAWi6T09jd87DgHD8N31zXaJ7LcpyppHWPRI2Ov9MuZEwnQ=="],
|
"turbo-darwin-64": ["turbo-darwin-64@2.5.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-Dh5bCACiHO8rUXZLpKw+m3FiHtAp2CkanSyJre+SInEvEr5kIxjGvCK/8MFX8SFRjQuhjtvpIvYYZJB4AGCxNQ=="],
|
||||||
|
|
||||||
"turbo-darwin-arm64": ["turbo-darwin-arm64@2.7.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-wCoDHMiTf3FgLAbZHDDx/unNNonSGhsF5AbbYODbxnpYyoKDpEYacUEPjZD895vDhNvYCH0Nnk24YsP4n/cD6g=="],
|
"turbo-darwin-arm64": ["turbo-darwin-arm64@2.5.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-f1H/tQC9px7+hmXn6Kx/w8Jd/FneIUnvLlcI/7RGHunxfOkKJKvsoiNzySkoHQ8uq1pJnhJ0xNGTlYM48ZaJOQ=="],
|
||||||
|
|
||||||
"turbo-linux-64": ["turbo-linux-64@2.7.5", "", { "os": "linux", "cpu": "x64" }, "sha512-KKPvhOmJMmzWj/yjeO4LywkQ85vOJyhru7AZk/+c4B6OUh/odQ++SiIJBSbTG2lm1CuV5gV5vXZnf/2AMlu3Zg=="],
|
"turbo-linux-64": ["turbo-linux-64@2.5.8", "", { "os": "linux", "cpu": "x64" }, "sha512-hMyvc7w7yadBlZBGl/bnR6O+dJTx3XkTeyTTH4zEjERO6ChEs0SrN8jTFj1lueNXKIHh1SnALmy6VctKMGnWfw=="],
|
||||||
|
|
||||||
"turbo-linux-arm64": ["turbo-linux-arm64@2.7.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-8PIva4L6BQhiPikUTds9lSFSHXVDAsEvV6QUlgwPsXrtXVQMVi6Sv9p+IxtlWQFvGkdYJUgX9GnK2rC030Xcmw=="],
|
"turbo-linux-arm64": ["turbo-linux-arm64@2.5.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-LQELGa7bAqV2f+3rTMRPnj5G/OHAe2U+0N9BwsZvfMvHSUbsQ3bBMWdSQaYNicok7wOZcHjz2TkESn1hYK6xIQ=="],
|
||||||
|
|
||||||
"turbo-windows-64": ["turbo-windows-64@2.7.5", "", { "os": "win32", "cpu": "x64" }, "sha512-rupskv/mkIUgQXzX/wUiK00mKMorQcK8yzhGFha/D5lm05FEnLx8dsip6rWzMcVpvh+4GUMA56PgtnOgpel2AA=="],
|
"turbo-windows-64": ["turbo-windows-64@2.5.8", "", { "os": "win32", "cpu": "x64" }, "sha512-3YdcaW34TrN1AWwqgYL9gUqmZsMT4T7g8Y5Azz+uwwEJW+4sgcJkIi9pYFyU4ZBSjBvkfuPZkGgfStir5BBDJQ=="],
|
||||||
|
|
||||||
"turbo-windows-arm64": ["turbo-windows-arm64@2.7.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-G377Gxn6P42RnCzfMyDvsqQV7j69kVHKlhz9J4RhtJOB5+DyY4yYh/w0oTIxZQ4JRMmhjwLu3w9zncMoQ6nNDw=="],
|
"turbo-windows-arm64": ["turbo-windows-arm64@2.5.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-eFC5XzLmgXJfnAK3UMTmVECCwuBcORrWdewoiXBnUm934DY6QN8YowC/srhNnROMpaKaqNeRpoB5FxCww3eteQ=="],
|
||||||
|
|
||||||
"type-fest": ["type-fest@5.4.1", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-xygQcmneDyzsEuKZrFbRMne5HDqMs++aFzefrJTgEIKjQ3rekM+RPfFCVq2Gp1VIDqddoYeppCj4Pcb+RZW0GQ=="],
|
"type-fest": ["type-fest@5.0.1", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-9MpwAI52m8H6ssA542UxSLnSiSD2dsC3/L85g6hVubLSXd82wdI80eZwTWhdOfN67NlA+D+oipAs1MlcTcu3KA=="],
|
||||||
|
|
||||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||||
|
|
||||||
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
"undici-types": ["undici-types@7.13.0", "", {}, "sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ=="],
|
||||||
|
|
||||||
"unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="],
|
"unplugin": ["unplugin@2.3.10", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw=="],
|
||||||
|
|
||||||
"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=="],
|
"update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="],
|
||||||
|
|
||||||
"use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="],
|
"use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="],
|
||||||
|
|
||||||
@@ -634,7 +664,7 @@
|
|||||||
|
|
||||||
"use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="],
|
"use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="],
|
||||||
|
|
||||||
"vite": ["vite@7.3.1", "", { "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-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="],
|
"vite": ["vite@7.1.8", "", { "dependencies": { "esbuild": "^0.25.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-oBXvfSHEOL8jF+R9Am7h59Up07kVVGH1NrFGFoEG6bPDZP3tGpQhvkBpy5x7U6+E6wZCu9OihsWgJqDbQIm8LQ=="],
|
||||||
|
|
||||||
"webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="],
|
"webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="],
|
||||||
|
|
||||||
@@ -644,6 +674,8 @@
|
|||||||
|
|
||||||
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
|
|
||||||
|
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
|
|
||||||
"readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
"readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
|
|
||||||
"recast/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
"recast/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||||
|
|||||||
12
package.json
12
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@effect-fc/monorepo",
|
"name": "@effect-fc/monorepo",
|
||||||
"packageManager": "bun@1.3.6",
|
"packageManager": "bun@1.2.23",
|
||||||
"private": true,
|
"private": true,
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"./packages/*"
|
"./packages/*"
|
||||||
@@ -15,12 +15,12 @@
|
|||||||
"clean:modules": "turbo clean:modules && rm -rf node_modules"
|
"clean:modules": "turbo clean:modules && rm -rf node_modules"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^2.3.11",
|
"@biomejs/biome": "^2.2.5",
|
||||||
"@effect/language-service": "^0.75.0",
|
"@effect/language-service": "^0.52.0",
|
||||||
"@types/bun": "^1.3.6",
|
"@types/bun": "^1.2.23",
|
||||||
"npm-check-updates": "^19.3.1",
|
"npm-check-updates": "^19.0.0",
|
||||||
"npm-sort": "^0.0.4",
|
"npm-sort": "^0.0.4",
|
||||||
"turbo": "^2.7.5",
|
"turbo": "^2.5.8",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,31 @@
|
|||||||
# Effect FC
|
# Effect FC
|
||||||
|
|
||||||
[Effect-TS](https://effect.website/) integration for React 19.2+ that allows you to write function components using Effect generators.
|
[Effect-TS](https://effect.website/) integration for React 19+ 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.
|
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.
|
Documentation is currently being written. In the meantime, you can take a look at the `packages/example` directory.
|
||||||
|
|
||||||
## Peer dependencies
|
## Peer dependencies
|
||||||
- `effect` 3.19+
|
- `effect` 3.15+
|
||||||
- `react` & `@types/react` 19.2+
|
- `react` & `@types/react` 19+
|
||||||
|
|
||||||
## Known issues
|
## Known issues
|
||||||
- React Refresh doesn't work for Effect FC's yet. Page reload is required to view changes. Regular React components are unaffected.
|
- 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
|
## What writing components looks like
|
||||||
```typescript
|
```typescript
|
||||||
export class Todos extends Component.make("Todos")(function*() {
|
import { Component } from "effect-fc"
|
||||||
|
import { useOnce, useSubscribables } from "effect-fc/Hooks"
|
||||||
|
import { Todo } from "./Todo"
|
||||||
|
import { TodosState } from "./TodosState.service"
|
||||||
|
|
||||||
|
|
||||||
|
export class Todos extends Component.makeUntraced("Todos")(function*() {
|
||||||
const state = yield* TodosState
|
const state = yield* TodosState
|
||||||
const [todos] = yield* useSubscribables(state.ref)
|
const [todos] = yield* useSubscribables(state.ref)
|
||||||
|
|
||||||
yield* useOnMount(() => Effect.andThen(
|
yield* useOnce(() => Effect.andThen(
|
||||||
Console.log("Todos mounted"),
|
Console.log("Todos mounted"),
|
||||||
Effect.addFinalizer(() => Console.log("Todos unmounted")),
|
Effect.addFinalizer(() => Console.log("Todos unmounted")),
|
||||||
))
|
))
|
||||||
@@ -43,8 +49,8 @@ export class Todos extends Component.make("Todos")(function*() {
|
|||||||
|
|
||||||
const TodosStateLive = TodosState.Default("todos")
|
const TodosStateLive = TodosState.Default("todos")
|
||||||
|
|
||||||
const Index = Component.make("Index")(function*() {
|
const Index = Component.makeUntraced("Index")(function*() {
|
||||||
const context = yield* useContext(TodosStateLive)
|
const context = yield* useContext(TodosStateLive, { finalizerExecutionMode: "fork" })
|
||||||
const TodosFC = yield* Effect.provide(Todos, context)
|
const TodosFC = yield* Effect.provide(Todos, context)
|
||||||
|
|
||||||
return <TodosFC />
|
return <TodosFC />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "effect-fc",
|
"name": "effect-fc",
|
||||||
"description": "Write React function components with Effect",
|
"description": "Write React function components with Effect",
|
||||||
"version": "0.2.3",
|
"version": "0.2.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"files": [
|
"files": [
|
||||||
"./README.md",
|
"./README.md",
|
||||||
@@ -37,12 +37,9 @@
|
|||||||
"clean:dist": "rm -rf dist",
|
"clean:dist": "rm -rf dist",
|
||||||
"clean:modules": "rm -rf node_modules"
|
"clean:modules": "rm -rf node_modules"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
|
||||||
"@effect/platform-browser": "^0.74.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^19.2.0",
|
"@types/react": "^19.0.0",
|
||||||
"effect": "^3.19.0",
|
"effect": "^3.15.0",
|
||||||
"react": "^19.2.0"
|
"react": "^19.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +1,35 @@
|
|||||||
/** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */
|
/** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */
|
||||||
import { Effect, type Equivalence, Function, Predicate, Runtime, Scope } from "effect"
|
import { Effect, Function, Predicate, Runtime, Scope } from "effect"
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as Component from "./Component.js"
|
import * as Component from "./Component.js"
|
||||||
|
|
||||||
|
|
||||||
export const AsyncTypeId: unique symbol = Symbol.for("@effect-fc/Async/Async")
|
export const TypeId: unique symbol = Symbol.for("@effect-fc/Async/Async")
|
||||||
export type AsyncTypeId = typeof AsyncTypeId
|
export type TypeId = typeof TypeId
|
||||||
|
|
||||||
|
export interface Async extends Async.Options {
|
||||||
/**
|
readonly [TypeId]: TypeId
|
||||||
* A trait for `Component`'s that allows them running asynchronous effects.
|
|
||||||
*/
|
|
||||||
export interface Async extends AsyncPrototype, AsyncOptions {}
|
|
||||||
|
|
||||||
export interface AsyncPrototype {
|
|
||||||
readonly [AsyncTypeId]: AsyncTypeId
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export namespace Async {
|
||||||
* Configuration options for `Async` components.
|
export interface Options {
|
||||||
*/
|
readonly defaultFallback?: React.ReactNode
|
||||||
export interface AsyncOptions {
|
}
|
||||||
/**
|
|
||||||
* The default fallback React node to display while the async operation is pending.
|
export type Props = Omit<React.SuspenseProps, "children">
|
||||||
* Used if no fallback is provided to the component when rendering.
|
|
||||||
*/
|
|
||||||
readonly defaultFallback?: React.ReactNode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Props for `Async` components.
|
|
||||||
*/
|
|
||||||
export type AsyncProps = Omit<React.SuspenseProps, "children">
|
|
||||||
|
|
||||||
|
const SuspenseProto = Object.freeze({
|
||||||
|
[TypeId]: TypeId,
|
||||||
|
|
||||||
export const AsyncPrototype: AsyncPrototype = Object.freeze({
|
makeFunctionComponent<P extends {}, A extends React.ReactNode, E, R>(
|
||||||
[AsyncTypeId]: AsyncTypeId,
|
|
||||||
|
|
||||||
asFunctionComponent<P extends {}, A extends React.ReactNode, E, R>(
|
|
||||||
this: Component.Component<P, A, E, R> & Async,
|
this: Component.Component<P, A, E, R> & Async,
|
||||||
runtimeRef: React.RefObject<Runtime.Runtime<Exclude<R, Scope.Scope>>>,
|
runtimeRef: React.RefObject<Runtime.Runtime<Exclude<R, Scope.Scope>>>,
|
||||||
) {
|
) {
|
||||||
const Inner = (props: { readonly promise: Promise<React.ReactNode> }) => React.use(props.promise)
|
const SuspenseInner = (props: { readonly promise: Promise<React.ReactNode> }) => React.use(props.promise)
|
||||||
|
|
||||||
return ({ fallback, name, ...props }: AsyncProps) => {
|
return ({ fallback, name, ...props }: Async.Props) => {
|
||||||
const promise = Runtime.runPromise(runtimeRef.current)(
|
const promise = Runtime.runPromise(runtimeRef.current)(
|
||||||
Effect.andThen(
|
Effect.andThen(
|
||||||
Component.useScope([], this),
|
Component.useScope([], this),
|
||||||
@@ -54,116 +40,45 @@ export const AsyncPrototype: AsyncPrototype = Object.freeze({
|
|||||||
return React.createElement(
|
return React.createElement(
|
||||||
React.Suspense,
|
React.Suspense,
|
||||||
{ fallback: fallback ?? this.defaultFallback, name },
|
{ fallback: fallback ?? this.defaultFallback, name },
|
||||||
React.createElement(Inner, { promise }),
|
React.createElement(SuspenseInner, { promise }),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
} as const)
|
} 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<AsyncProps> = (
|
|
||||||
self: Record<string, unknown>,
|
|
||||||
that: Record<string, unknown>,
|
|
||||||
) => {
|
|
||||||
if (self === that)
|
|
||||||
return true
|
|
||||||
|
|
||||||
for (const key in self) {
|
export const isAsync = (u: unknown): u is Async => Predicate.hasProperty(u, TypeId)
|
||||||
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 = <T extends Component.Component<any, any, any, any>>(
|
export const async = <T extends Component.Component<any, any, any, any>>(
|
||||||
self: T & (
|
self: T
|
||||||
"promise" extends keyof Component.Component.Props<T>
|
|
||||||
? "The 'promise' prop name is restricted for Async components. Please rename the 'promise' prop to something else."
|
|
||||||
: T
|
|
||||||
)
|
|
||||||
): (
|
): (
|
||||||
& Omit<T, keyof Component.Component.AsComponent<T>>
|
& Omit<T, keyof Component.Component.AsComponent<T>>
|
||||||
& Component.Component<
|
& Component.Component<
|
||||||
Component.Component.Props<T> & AsyncProps,
|
Component.Component.Props<T> & Async.Props,
|
||||||
Component.Component.Success<T>,
|
Component.Component.Success<T>,
|
||||||
Component.Component.Error<T>,
|
Component.Component.Error<T>,
|
||||||
Component.Component.Context<T>
|
Component.Component.Context<T>
|
||||||
>
|
>
|
||||||
& Async
|
& Async
|
||||||
) => Object.setPrototypeOf(
|
) => Object.setPrototypeOf(
|
||||||
Object.assign(function() {}, self, { propsEquivalence: defaultPropsEquivalence }),
|
Object.assign(function() {}, self),
|
||||||
Object.freeze(Object.setPrototypeOf(
|
Object.freeze(Object.setPrototypeOf(
|
||||||
Object.assign({}, AsyncPrototype),
|
Object.assign({}, SuspenseProto),
|
||||||
Object.getPrototypeOf(self),
|
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: <p>Loading...</p> }),
|
|
||||||
* )
|
|
||||||
*
|
|
||||||
* // Uncurried
|
|
||||||
* const MyAsyncComponent = Async.withOptions(
|
|
||||||
* Async.async(MyComponent),
|
|
||||||
* { defaultFallback: <p>Loading...</p> },
|
|
||||||
* )
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
export const withOptions: {
|
export const withOptions: {
|
||||||
<T extends Component.Component<any, any, any, any> & Async>(
|
<T extends Component.Component<any, any, any, any> & Async>(
|
||||||
options: Partial<AsyncOptions>
|
options: Partial<Async.Options>
|
||||||
): (self: T) => T
|
): (self: T) => T
|
||||||
<T extends Component.Component<any, any, any, any> & Async>(
|
<T extends Component.Component<any, any, any, any> & Async>(
|
||||||
self: T,
|
self: T,
|
||||||
options: Partial<AsyncOptions>,
|
options: Partial<Async.Options>,
|
||||||
): T
|
): T
|
||||||
} = Function.dual(2, <T extends Component.Component<any, any, any, any> & Async>(
|
} = Function.dual(2, <T extends Component.Component<any, any, any, any> & Async>(
|
||||||
self: T,
|
self: T,
|
||||||
options: Partial<AsyncOptions>,
|
options: Partial<Async.Options>,
|
||||||
): T => Object.setPrototypeOf(
|
): T => Object.setPrototypeOf(
|
||||||
Object.assign(function() {}, self, options),
|
Object.assign(function() {}, self, options),
|
||||||
Object.getPrototypeOf(self),
|
Object.getPrototypeOf(self),
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,62 +0,0 @@
|
|||||||
import { type Cause, Context, Effect, Exit, Layer, Option, Pipeable, Predicate, PubSub, type Queue, type Scope, Supervisor } from "effect"
|
|
||||||
|
|
||||||
|
|
||||||
export const ErrorObserverTypeId: unique symbol = Symbol.for("@effect-fc/ErrorObserver/ErrorObserver")
|
|
||||||
export type ErrorObserverTypeId = typeof ErrorObserverTypeId
|
|
||||||
|
|
||||||
export interface ErrorObserver<in out E = never> extends Pipeable.Pipeable {
|
|
||||||
readonly [ErrorObserverTypeId]: ErrorObserverTypeId
|
|
||||||
handle<A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
|
|
||||||
readonly subscribe: Effect.Effect<Queue.Dequeue<Cause.Cause<E>>, never, Scope.Scope>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ErrorObserver = <E = never>(): Context.Tag<ErrorObserver, ErrorObserver<E>> => Context.GenericTag("@effect-fc/ErrorObserver/ErrorObserver")
|
|
||||||
|
|
||||||
export class ErrorObserverImpl<in out E = never>
|
|
||||||
extends Pipeable.Class() implements ErrorObserver<E> {
|
|
||||||
readonly [ErrorObserverTypeId]: ErrorObserverTypeId = ErrorObserverTypeId
|
|
||||||
readonly subscribe: Effect.Effect<Queue.Dequeue<Cause.Cause<E>>, never, Scope.Scope>
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
readonly pubsub: PubSub.PubSub<Cause.Cause<E>>
|
|
||||||
) {
|
|
||||||
super()
|
|
||||||
this.subscribe = pubsub.subscribe
|
|
||||||
}
|
|
||||||
|
|
||||||
handle<A, EffE, R>(effect: Effect.Effect<A, EffE, R>): Effect.Effect<A, EffE, R> {
|
|
||||||
return Effect.tapErrorCause(effect, cause => PubSub.publish(this.pubsub, cause as Cause.Cause<E>))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ErrorObserverSupervisorImpl extends Supervisor.AbstractSupervisor<void> {
|
|
||||||
readonly value = Effect.void
|
|
||||||
constructor(readonly pubsub: PubSub.PubSub<Cause.Cause<never>>) {
|
|
||||||
super()
|
|
||||||
}
|
|
||||||
|
|
||||||
onEnd<A, E>(_value: Exit.Exit<A, E>): void {
|
|
||||||
if (Exit.isFailure(_value)) {
|
|
||||||
Effect.runSync(PubSub.publish(this.pubsub, _value.cause as Cause.Cause<never>))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export const isErrorObserver = (u: unknown): u is ErrorObserver<unknown> => Predicate.hasProperty(u, ErrorObserverTypeId)
|
|
||||||
|
|
||||||
export const layer: Layer.Layer<ErrorObserver> = Layer.unwrapEffect(Effect.map(
|
|
||||||
PubSub.unbounded<Cause.Cause<never>>(),
|
|
||||||
pubsub => Layer.merge(
|
|
||||||
Supervisor.addSupervisor(new ErrorObserverSupervisorImpl(pubsub)),
|
|
||||||
Layer.succeed(ErrorObserver(), new ErrorObserverImpl(pubsub)),
|
|
||||||
),
|
|
||||||
))
|
|
||||||
|
|
||||||
export const handle = <A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> => Effect.andThen(
|
|
||||||
Effect.serviceOption(ErrorObserver()),
|
|
||||||
Option.match({
|
|
||||||
onSome: observer => observer.handle(effect),
|
|
||||||
onNone: () => effect,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Array, Cause, Chunk, type Context, type Duration, Effect, Equal, Exit, Fiber, flow, Hash, HashMap, identity, Option, ParseResult, Pipeable, Predicate, Ref, Schema, type Scope, Stream } from "effect"
|
import { Array, Cause, Chunk, type Duration, Effect, Equal, Exit, Fiber, flow, identity, Option, ParseResult, Pipeable, Predicate, Ref, Schema, type Scope, Stream } from "effect"
|
||||||
import type * as React from "react"
|
import type { NoSuchElementException } from "effect/Cause"
|
||||||
|
import * as React from "react"
|
||||||
import * as Component from "./Component.js"
|
import * as Component from "./Component.js"
|
||||||
import * as Mutation from "./Mutation.js"
|
|
||||||
import * as PropertyPath from "./PropertyPath.js"
|
import * as PropertyPath from "./PropertyPath.js"
|
||||||
import * as Result from "./Result.js"
|
import * as Result from "./Result.js"
|
||||||
import * as Subscribable from "./Subscribable.js"
|
import * as Subscribable from "./Subscribable.js"
|
||||||
@@ -12,211 +12,191 @@ import * as SubscriptionSubRef from "./SubscriptionSubRef.js"
|
|||||||
export const FormTypeId: unique symbol = Symbol.for("@effect-fc/Form/Form")
|
export const FormTypeId: unique symbol = Symbol.for("@effect-fc/Form/Form")
|
||||||
export type FormTypeId = typeof FormTypeId
|
export type FormTypeId = typeof FormTypeId
|
||||||
|
|
||||||
export interface Form<in out A, in out I = A, in out R = never, in out MA = void, in out ME = never, in out MR = never, in out MP = never>
|
export interface Form<in out A, in out I = A, out R = never, in out SA = void, in out SE = A, out SR = never>
|
||||||
extends Pipeable.Pipeable {
|
extends Pipeable.Pipeable {
|
||||||
readonly [FormTypeId]: FormTypeId
|
readonly [FormTypeId]: FormTypeId
|
||||||
|
|
||||||
readonly schema: Schema.Schema<A, I, R>
|
readonly schema: Schema.Schema<A, I, R>
|
||||||
readonly context: Context.Context<Scope.Scope | R>
|
readonly onSubmit: (value: NoInfer<A>) => Effect.Effect<SA, SE, SR>
|
||||||
readonly mutation: Mutation.Mutation<
|
|
||||||
readonly [value: A, form: Form<A, I, R, unknown, unknown, unknown>],
|
|
||||||
MA, ME, MR, MP
|
|
||||||
>
|
|
||||||
readonly autosubmit: boolean
|
readonly autosubmit: boolean
|
||||||
readonly debounce: Option.Option<Duration.DurationInput>
|
readonly debounce: Option.Option<Duration.DurationInput>
|
||||||
|
|
||||||
readonly value: Subscribable.Subscribable<Option.Option<A>>
|
readonly valueRef: SubscriptionRef.SubscriptionRef<Option.Option<A>>
|
||||||
readonly encodedValue: SubscriptionRef.SubscriptionRef<I>
|
readonly encodedValueRef: SubscriptionRef.SubscriptionRef<I>
|
||||||
readonly error: Subscribable.Subscribable<Option.Option<ParseResult.ParseError>>
|
readonly errorRef: SubscriptionRef.SubscriptionRef<Option.Option<ParseResult.ParseError>>
|
||||||
readonly validationFiber: Subscribable.Subscribable<Option.Option<Fiber.Fiber<A, ParseResult.ParseError>>>
|
readonly validationFiberRef: SubscriptionRef.SubscriptionRef<Option.Option<Fiber.Fiber<void, never>>>
|
||||||
|
readonly submitResultRef: SubscriptionRef.SubscriptionRef<Result.Result<SA, SE>>
|
||||||
|
|
||||||
readonly canSubmit: Subscribable.Subscribable<boolean>
|
readonly canSubmitSubscribable: Subscribable.Subscribable<boolean>
|
||||||
|
|
||||||
field<const P extends PropertyPath.Paths<I>>(
|
|
||||||
path: P
|
|
||||||
): Effect.Effect<FormField<PropertyPath.ValueFromPath<A, P>, PropertyPath.ValueFromPath<I, P>>>
|
|
||||||
|
|
||||||
readonly run: Effect.Effect<void>
|
|
||||||
readonly submit: Effect.Effect<Option.Option<Result.Final<MA, ME, MP>>, Cause.NoSuchElementException>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FormImpl<in out A, in out I = A, in out R = never, in out MA = void, in out ME = never, in out MR = never, in out MP = never>
|
class FormImpl<in out A, in out I = A, out R = never, in out SA = void, in out SE = A, out SR = never>
|
||||||
extends Pipeable.Class() implements Form<A, I, R, MA, ME, MR, MP> {
|
extends Pipeable.Class() implements Form<A, I, R, SA, SE, SR> {
|
||||||
readonly [FormTypeId]: FormTypeId = FormTypeId
|
readonly [FormTypeId]: FormTypeId = FormTypeId
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly schema: Schema.Schema<A, I, R>,
|
readonly schema: Schema.Schema<A, I, R>,
|
||||||
readonly context: Context.Context<Scope.Scope | R>,
|
readonly onSubmit: (value: NoInfer<A>) => Effect.Effect<SA, SE, SR>,
|
||||||
readonly mutation: Mutation.Mutation<
|
|
||||||
readonly [value: A, form: Form<A, I, R, unknown, unknown, unknown>],
|
|
||||||
MA, ME, MR, MP
|
|
||||||
>,
|
|
||||||
readonly autosubmit: boolean,
|
readonly autosubmit: boolean,
|
||||||
readonly debounce: Option.Option<Duration.DurationInput>,
|
readonly debounce: Option.Option<Duration.DurationInput>,
|
||||||
|
|
||||||
readonly value: SubscriptionRef.SubscriptionRef<Option.Option<A>>,
|
readonly valueRef: SubscriptionRef.SubscriptionRef<Option.Option<A>>,
|
||||||
readonly encodedValue: SubscriptionRef.SubscriptionRef<I>,
|
readonly encodedValueRef: SubscriptionRef.SubscriptionRef<I>,
|
||||||
readonly error: SubscriptionRef.SubscriptionRef<Option.Option<ParseResult.ParseError>>,
|
readonly errorRef: SubscriptionRef.SubscriptionRef<Option.Option<ParseResult.ParseError>>,
|
||||||
readonly validationFiber: SubscriptionRef.SubscriptionRef<Option.Option<Fiber.Fiber<A, ParseResult.ParseError>>>,
|
readonly validationFiberRef: SubscriptionRef.SubscriptionRef<Option.Option<Fiber.Fiber<void, never>>>,
|
||||||
|
readonly submitResultRef: SubscriptionRef.SubscriptionRef<Result.Result<SA, SE>>,
|
||||||
|
|
||||||
readonly runSemaphore: Effect.Semaphore,
|
readonly canSubmitSubscribable: Subscribable.Subscribable<boolean>,
|
||||||
readonly fieldCache: Ref.Ref<HashMap.HashMap<FormFieldKey, FormField<unknown, unknown>>>,
|
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
this.canSubmit = Subscribable.map(
|
|
||||||
Subscribable.zipLatestAll(this.value, this.error, this.validationFiber, this.mutation.result),
|
|
||||||
([value, error, validationFiber, result]) => (
|
|
||||||
Option.isSome(value) &&
|
|
||||||
Option.isNone(error) &&
|
|
||||||
Option.isNone(validationFiber) &&
|
|
||||||
!(Result.isRunning(result) || Result.hasRefreshingFlag(result))
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
field<const P extends PropertyPath.Paths<I>>(
|
|
||||||
path: P
|
|
||||||
): Effect.Effect<FormField<PropertyPath.ValueFromPath<A, P>, PropertyPath.ValueFromPath<I, P>>> {
|
|
||||||
const key = new FormFieldKey(path)
|
|
||||||
return this.fieldCache.pipe(
|
|
||||||
Effect.map(HashMap.get(key)),
|
|
||||||
Effect.flatMap(Option.match({
|
|
||||||
onSome: v => Effect.succeed(v as FormField<PropertyPath.ValueFromPath<A, P>, PropertyPath.ValueFromPath<I, P>>),
|
|
||||||
onNone: () => Effect.tap(
|
|
||||||
Effect.succeed(makeFormField(this as Form<A, I, R, MA, ME, MR, MP>, path)),
|
|
||||||
v => Ref.update(this.fieldCache, HashMap.set(key, v as FormField<unknown, unknown>)),
|
|
||||||
),
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly canSubmit: Subscribable.Subscribable<boolean>
|
|
||||||
|
|
||||||
get run(): Effect.Effect<void> {
|
|
||||||
return this.runSemaphore.withPermits(1)(Stream.runForEach(
|
|
||||||
this.encodedValue.changes.pipe(
|
|
||||||
Option.isSome(this.debounce) ? Stream.debounce(this.debounce.value) : identity
|
|
||||||
),
|
|
||||||
|
|
||||||
encodedValue => this.validationFiber.pipe(
|
|
||||||
Effect.andThen(Option.match({
|
|
||||||
onSome: Fiber.interrupt,
|
|
||||||
onNone: () => Effect.void,
|
|
||||||
})),
|
|
||||||
Effect.andThen(
|
|
||||||
Effect.forkScoped(Effect.onExit(
|
|
||||||
Schema.decode(this.schema, { errors: "all" })(encodedValue),
|
|
||||||
exit => Effect.andThen(
|
|
||||||
Exit.matchEffect(exit, {
|
|
||||||
onSuccess: v => Effect.andThen(
|
|
||||||
Ref.set(this.value, Option.some(v)),
|
|
||||||
Ref.set(this.error, Option.none()),
|
|
||||||
),
|
|
||||||
onFailure: c => Option.match(Chunk.findFirst(Cause.failures(c), e => e._tag === "ParseError"), {
|
|
||||||
onSome: e => Ref.set(this.error, Option.some(e)),
|
|
||||||
onNone: () => Effect.void,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
Ref.set(this.validationFiber, Option.none()),
|
|
||||||
),
|
|
||||||
)).pipe(
|
|
||||||
Effect.tap(fiber => Ref.set(this.validationFiber, Option.some(fiber))),
|
|
||||||
Effect.andThen(Fiber.join),
|
|
||||||
Effect.andThen(value => this.autosubmit
|
|
||||||
? Effect.asVoid(Effect.forkScoped(this.submitValue(value)))
|
|
||||||
: Effect.void
|
|
||||||
),
|
|
||||||
Effect.forkScoped,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
Effect.provide(this.context),
|
|
||||||
),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
get submit(): Effect.Effect<Option.Option<Result.Final<MA, ME, MP>>, Cause.NoSuchElementException> {
|
|
||||||
return this.value.pipe(
|
|
||||||
Effect.andThen(identity),
|
|
||||||
Effect.andThen(value => this.submitValue(value)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
submitValue(value: A): Effect.Effect<Option.Option<Result.Final<MA, ME, MP>>> {
|
|
||||||
return Effect.whenEffect(
|
|
||||||
Effect.tap(
|
|
||||||
this.mutation.mutate([value, this as any]),
|
|
||||||
result => Result.isFailure(result)
|
|
||||||
? Option.match(
|
|
||||||
Chunk.findFirst(
|
|
||||||
Cause.failures(result.cause as Cause.Cause<ParseResult.ParseError>),
|
|
||||||
e => e._tag === "ParseError",
|
|
||||||
),
|
|
||||||
{
|
|
||||||
onSome: e => Ref.set(this.error, Option.some(e)),
|
|
||||||
onNone: () => Effect.void,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: Effect.void
|
|
||||||
),
|
|
||||||
this.canSubmit.get,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isForm = (u: unknown): u is Form<unknown, unknown, unknown, unknown, unknown, unknown> => Predicate.hasProperty(u, FormTypeId)
|
export const isForm = (u: unknown): u is Form<unknown, unknown, unknown, unknown, unknown, unknown> => Predicate.hasProperty(u, FormTypeId)
|
||||||
|
|
||||||
export declare namespace make {
|
export namespace make {
|
||||||
export interface Options<in out A, in out I = A, in out R = never, in out MA = void, in out ME = never, in out MR = never, in out MP = never>
|
export interface Options<in out A, in out I, in out R, in out SA = void, in out SE = A, out SR = never> {
|
||||||
extends Mutation.make.Options<
|
|
||||||
readonly [value: NoInfer<A>, form: Form<NoInfer<A>, NoInfer<I>, NoInfer<R>, unknown, unknown, unknown>],
|
|
||||||
MA, ME, MR, MP
|
|
||||||
> {
|
|
||||||
readonly schema: Schema.Schema<A, I, R>
|
readonly schema: Schema.Schema<A, I, R>
|
||||||
readonly initialEncodedValue: NoInfer<I>
|
readonly initialEncodedValue: NoInfer<I>
|
||||||
|
readonly onSubmit: (
|
||||||
|
this: Form<NoInfer<A>, NoInfer<I>, NoInfer<R>, unknown, unknown, unknown>,
|
||||||
|
value: NoInfer<A>,
|
||||||
|
) => Effect.Effect<SA, SE, SR>
|
||||||
readonly autosubmit?: boolean
|
readonly autosubmit?: boolean
|
||||||
readonly debounce?: Duration.DurationInput
|
readonly debounce?: Duration.DurationInput
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const make = Effect.fnUntraced(function* <A, I = A, R = never, MA = void, ME = never, MR = never, MP = never>(
|
export const make: {
|
||||||
options: make.Options<A, I, R, MA, ME, MR, MP>
|
<A, I = A, R = never, SA = void, SE = A, SR = never>(
|
||||||
): Effect.fn.Return<
|
options: make.Options<A, I, R, SA, SE, SR>
|
||||||
Form<A, I, R, MA, ME, Result.forkEffect.OutputContext<MA, ME, MR, MP>, MP>,
|
): Effect.Effect<Form<A, I, R, SA, SE, SR>>
|
||||||
never,
|
} = Effect.fnUntraced(function* <A, I = A, R = never, SA = void, SE = A, SR = never>(
|
||||||
Scope.Scope | R | Result.forkEffect.OutputContext<MA, ME, MR, MP>
|
options: make.Options<A, I, R, SA, SE, SR>
|
||||||
> {
|
) {
|
||||||
|
const valueRef = yield* SubscriptionRef.make(Option.none<A>())
|
||||||
|
const errorRef = yield* SubscriptionRef.make(Option.none<ParseResult.ParseError>())
|
||||||
|
const validationFiberRef = yield* SubscriptionRef.make(Option.none<Fiber.Fiber<void, never>>())
|
||||||
|
const submitResultRef = yield* SubscriptionRef.make<Result.Result<SA, SE>>(Result.initial())
|
||||||
|
|
||||||
return new FormImpl(
|
return new FormImpl(
|
||||||
options.schema,
|
options.schema,
|
||||||
yield* Effect.context<Scope.Scope | R>(),
|
options.onSubmit,
|
||||||
yield* Mutation.make(options),
|
|
||||||
options.autosubmit ?? false,
|
options.autosubmit ?? false,
|
||||||
Option.fromNullable(options.debounce),
|
Option.fromNullable(options.debounce),
|
||||||
|
|
||||||
yield* SubscriptionRef.make(Option.none<A>()),
|
valueRef,
|
||||||
yield* SubscriptionRef.make(options.initialEncodedValue),
|
yield* SubscriptionRef.make(options.initialEncodedValue),
|
||||||
yield* SubscriptionRef.make(Option.none<ParseResult.ParseError>()),
|
errorRef,
|
||||||
yield* SubscriptionRef.make(Option.none<Fiber.Fiber<A, ParseResult.ParseError>>()),
|
validationFiberRef,
|
||||||
|
submitResultRef,
|
||||||
|
|
||||||
yield* Effect.makeSemaphore(1),
|
Subscribable.map(
|
||||||
yield* Ref.make(HashMap.empty<FormFieldKey, FormField<unknown, unknown>>()),
|
Subscribable.zipLatestAll(valueRef, errorRef, validationFiberRef, submitResultRef),
|
||||||
|
([value, error, validationFiber, submitResult]) => (
|
||||||
|
Option.isSome(value) &&
|
||||||
|
Option.isNone(error) &&
|
||||||
|
Option.isNone(validationFiber) &&
|
||||||
|
!(Result.isRunning(submitResult) || Result.isRefreshing(submitResult))
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
export declare namespace service {
|
export const run = <A, I, R, SA, SE, SR>(
|
||||||
export interface Options<in out A, in out I = A, in out R = never, in out MA = void, in out ME = never, in out MR = never, in out MP = never>
|
self: Form<A, I, R, SA, SE, SR>
|
||||||
extends make.Options<A, I, R, MA, ME, MR, MP> {}
|
): Effect.Effect<void, never, Scope.Scope | R | SR> => Stream.runForEach(
|
||||||
|
self.encodedValueRef.changes.pipe(
|
||||||
|
Option.isSome(self.debounce) ? Stream.debounce(self.debounce.value) : identity
|
||||||
|
),
|
||||||
|
|
||||||
|
encodedValue => self.validationFiberRef.pipe(
|
||||||
|
Effect.andThen(Option.match({
|
||||||
|
onSome: Fiber.interrupt,
|
||||||
|
onNone: () => Effect.void,
|
||||||
|
})),
|
||||||
|
Effect.andThen(
|
||||||
|
Effect.addFinalizer(() => Ref.set(self.validationFiberRef, Option.none())).pipe(
|
||||||
|
Effect.andThen(Schema.decode(self.schema, { errors: "all" })(encodedValue)),
|
||||||
|
Effect.exit,
|
||||||
|
Effect.andThen(flow(
|
||||||
|
Exit.matchEffect({
|
||||||
|
onSuccess: v => Ref.set(self.valueRef, Option.some(v)).pipe(
|
||||||
|
Effect.andThen(Ref.set(self.errorRef, Option.none())),
|
||||||
|
Effect.as(Option.some(v)),
|
||||||
|
),
|
||||||
|
onFailure: c => Chunk.findFirst(Cause.failures(c), e => e._tag === "ParseError").pipe(
|
||||||
|
Option.match({
|
||||||
|
onSome: e => Ref.set(self.errorRef, Option.some(e)),
|
||||||
|
onNone: () => Effect.void,
|
||||||
|
}),
|
||||||
|
Effect.as(Option.none<A>()),
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
Effect.uninterruptible,
|
||||||
|
)),
|
||||||
|
Effect.scoped,
|
||||||
|
|
||||||
|
Effect.andThen(value => Option.isSome(value) && self.autosubmit
|
||||||
|
? Effect.asVoid(Effect.forkScoped(submit(self)))
|
||||||
|
: Effect.void
|
||||||
|
),
|
||||||
|
Effect.forkScoped,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Effect.andThen(fiber => Ref.set(self.validationFiberRef, Option.some(fiber)))
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
export const submit = <A, I, R, SA, SE, SR>(
|
||||||
|
self: Form<A, I, R, SA, SE, SR>
|
||||||
|
): Effect.Effect<Option.Option<Result.Result<SA, SE>>, NoSuchElementException, Scope.Scope | SR> => Effect.whenEffect(
|
||||||
|
self.valueRef.pipe(
|
||||||
|
Effect.andThen(identity),
|
||||||
|
Effect.andThen(value => Result.forkEffectScoped(
|
||||||
|
self.onSubmit(value) as Effect.Effect<SA, SE, Result.forkEffectScoped.InputContext<SR, never>>)
|
||||||
|
),
|
||||||
|
Effect.andThen(Stream.fromQueue),
|
||||||
|
Stream.unwrap,
|
||||||
|
Stream.runFoldEffect(
|
||||||
|
Result.initial() as Result.Result<SA, SE>,
|
||||||
|
(_, result) => Effect.as(Ref.set(self.submitResultRef, result), result),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
self.canSubmitSubscribable.get,
|
||||||
|
)
|
||||||
|
|
||||||
|
export namespace service {
|
||||||
|
export interface Options<in out A, in out I, in out R, in out SA = void, in out SE = A, out SR = never>
|
||||||
|
extends make.Options<A, I, R, SA, SE, SR> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const service = <A, I = A, R = never, MA = void, ME = never, MR = never, MP = never>(
|
export const service = <A, I = A, R = never, SA = void, SE = A, SR = never>(
|
||||||
options: service.Options<A, I, R, MA, ME, MR, MP>
|
options: service.Options<A, I, R, SA, SE, SR>
|
||||||
): Effect.Effect<
|
): Effect.Effect<Form<A, I, R, SA, SE, SR>, never, Scope.Scope | R | SR> => Effect.tap(
|
||||||
Form<A, I, R, MA, ME, Result.forkEffect.OutputContext<MA, ME, MR, MP>, MP>,
|
|
||||||
never,
|
|
||||||
Scope.Scope | R | Result.forkEffect.OutputContext<MA, ME, MR, MP>
|
|
||||||
> => Effect.tap(
|
|
||||||
make(options),
|
make(options),
|
||||||
form => Effect.forkScoped(form.run),
|
form => Effect.forkScoped(run(form)),
|
||||||
|
)
|
||||||
|
|
||||||
|
export const field = <A, I, R, SA, SE, SR, const P extends PropertyPath.Paths<NoInfer<I>>>(
|
||||||
|
self: Form<A, I, R, SA, SE, SR>,
|
||||||
|
path: P,
|
||||||
|
): FormField<PropertyPath.ValueFromPath<A, P>, PropertyPath.ValueFromPath<I, P>> => new FormFieldImpl(
|
||||||
|
Subscribable.mapEffect(self.valueRef, Option.match({
|
||||||
|
onSome: v => Option.map(PropertyPath.get(v, path), Option.some),
|
||||||
|
onNone: () => Option.some(Option.none()),
|
||||||
|
})),
|
||||||
|
SubscriptionSubRef.makeFromPath(self.encodedValueRef, path),
|
||||||
|
Subscribable.mapEffect(self.errorRef, Option.match({
|
||||||
|
onSome: flow(
|
||||||
|
ParseResult.ArrayFormatter.formatError,
|
||||||
|
Effect.map(Array.filter(issue => PropertyPath.equivalence(issue.path, path))),
|
||||||
|
),
|
||||||
|
onNone: () => Effect.succeed([]),
|
||||||
|
})),
|
||||||
|
Subscribable.map(self.validationFiberRef, Option.isSome),
|
||||||
|
Subscribable.map(self.submitResultRef, result => Result.isRunning(result) || Result.isRefreshing(result)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -227,11 +207,11 @@ export interface FormField<in out A, in out I = A>
|
|||||||
extends Pipeable.Pipeable {
|
extends Pipeable.Pipeable {
|
||||||
readonly [FormFieldTypeId]: FormFieldTypeId
|
readonly [FormFieldTypeId]: FormFieldTypeId
|
||||||
|
|
||||||
readonly value: Subscribable.Subscribable<Option.Option<A>, Cause.NoSuchElementException>
|
readonly valueSubscribable: Subscribable.Subscribable<Option.Option<A>, NoSuchElementException>
|
||||||
readonly encodedValue: SubscriptionRef.SubscriptionRef<I>
|
readonly encodedValueRef: SubscriptionRef.SubscriptionRef<I>
|
||||||
readonly issues: Subscribable.Subscribable<readonly ParseResult.ArrayFormatterIssue[]>
|
readonly issuesSubscribable: Subscribable.Subscribable<readonly ParseResult.ArrayFormatterIssue[]>
|
||||||
readonly isValidating: Subscribable.Subscribable<boolean>
|
readonly isValidatingSubscribable: Subscribable.Subscribable<boolean>
|
||||||
readonly isSubmitting: Subscribable.Subscribable<boolean>
|
readonly isSubmittingSubscribable: Subscribable.Subscribable<boolean>
|
||||||
}
|
}
|
||||||
|
|
||||||
class FormFieldImpl<in out A, in out I = A>
|
class FormFieldImpl<in out A, in out I = A>
|
||||||
@@ -239,77 +219,61 @@ extends Pipeable.Class() implements FormField<A, I> {
|
|||||||
readonly [FormFieldTypeId]: FormFieldTypeId = FormFieldTypeId
|
readonly [FormFieldTypeId]: FormFieldTypeId = FormFieldTypeId
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly value: Subscribable.Subscribable<Option.Option<A>, Cause.NoSuchElementException>,
|
readonly valueSubscribable: Subscribable.Subscribable<Option.Option<A>, NoSuchElementException>,
|
||||||
readonly encodedValue: SubscriptionRef.SubscriptionRef<I>,
|
readonly encodedValueRef: SubscriptionRef.SubscriptionRef<I>,
|
||||||
readonly issues: Subscribable.Subscribable<readonly ParseResult.ArrayFormatterIssue[]>,
|
readonly issuesSubscribable: Subscribable.Subscribable<readonly ParseResult.ArrayFormatterIssue[]>,
|
||||||
readonly isValidating: Subscribable.Subscribable<boolean>,
|
readonly isValidatingSubscribable: Subscribable.Subscribable<boolean>,
|
||||||
readonly isSubmitting: Subscribable.Subscribable<boolean>,
|
readonly isSubmittingSubscribable: Subscribable.Subscribable<boolean>,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const FormFieldKeyTypeId: unique symbol = Symbol.for("@effect-fc/Form/FormFieldKey")
|
|
||||||
type FormFieldKeyTypeId = typeof FormFieldKeyTypeId
|
|
||||||
|
|
||||||
class FormFieldKey implements Equal.Equal {
|
|
||||||
readonly [FormFieldKeyTypeId]: FormFieldKeyTypeId = FormFieldKeyTypeId
|
|
||||||
constructor(readonly path: PropertyPath.PropertyPath) {}
|
|
||||||
|
|
||||||
[Equal.symbol](that: Equal.Equal) {
|
|
||||||
return isFormFieldKey(that) && PropertyPath.equivalence(this.path, that.path)
|
|
||||||
}
|
|
||||||
[Hash.symbol]() {
|
|
||||||
return Hash.array(this.path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const isFormField = (u: unknown): u is FormField<unknown, unknown> => Predicate.hasProperty(u, FormFieldTypeId)
|
export const isFormField = (u: unknown): u is FormField<unknown, unknown> => Predicate.hasProperty(u, FormFieldTypeId)
|
||||||
const isFormFieldKey = (u: unknown): u is FormFieldKey => Predicate.hasProperty(u, FormFieldKeyTypeId)
|
|
||||||
|
|
||||||
export const makeFormField = <A, I, R, MA, ME, MR, MP, const P extends PropertyPath.Paths<NoInfer<I>>>(
|
|
||||||
self: Form<A, I, R, MA, ME, MR, MP>,
|
export const useSubmit = <A, I, R, SA, SE, SR>(
|
||||||
|
self: Form<A, I, R, SA, SE, SR>
|
||||||
|
): Effect.Effect<
|
||||||
|
() => Promise<Option.Option<Result.Result<SA, SE>>>,
|
||||||
|
never,
|
||||||
|
Scope.Scope | SR
|
||||||
|
> => Component.useCallbackPromise(() => submit(self), [self])
|
||||||
|
|
||||||
|
export const useField = <A, I, R, SA, SE, SR, const P extends PropertyPath.Paths<NoInfer<I>>>(
|
||||||
|
self: Form<A, I, R, SA, SE, SR>,
|
||||||
path: P,
|
path: P,
|
||||||
): FormField<PropertyPath.ValueFromPath<A, P>, PropertyPath.ValueFromPath<I, P>> => {
|
): FormField<
|
||||||
return new FormFieldImpl(
|
PropertyPath.ValueFromPath<A, P>,
|
||||||
Subscribable.mapEffect(self.value, Option.match({
|
PropertyPath.ValueFromPath<I, P>
|
||||||
onSome: v => Option.map(PropertyPath.get(v, path), Option.some),
|
// biome-ignore lint/correctness/useExhaustiveDependencies: individual path components need to be compared
|
||||||
onNone: () => Option.some(Option.none()),
|
> => React.useMemo(() => field(self, path), [self, ...path])
|
||||||
})),
|
|
||||||
SubscriptionSubRef.makeFromPath(self.encodedValue, path),
|
|
||||||
Subscribable.mapEffect(self.error, Option.match({
|
|
||||||
onSome: flow(
|
|
||||||
ParseResult.ArrayFormatter.formatError,
|
|
||||||
Effect.map(Array.filter(issue => PropertyPath.equivalence(issue.path, path))),
|
|
||||||
),
|
|
||||||
onNone: () => Effect.succeed([]),
|
|
||||||
})),
|
|
||||||
Subscribable.map(self.validationFiber, Option.isSome),
|
|
||||||
Subscribable.map(self.mutation.result, result => Result.isRunning(result) || Result.hasRefreshingFlag(result)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export namespace useInput {
|
export namespace useInput {
|
||||||
export interface Options {
|
export interface Options {
|
||||||
readonly debounce?: Duration.DurationInput
|
readonly debounce?: Duration.DurationInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Success<T> {
|
export interface Result<T> {
|
||||||
readonly value: T
|
readonly value: T
|
||||||
readonly setValue: React.Dispatch<React.SetStateAction<T>>
|
readonly setValue: React.Dispatch<React.SetStateAction<T>>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useInput = Effect.fnUntraced(function* <A, I>(
|
export const useInput: {
|
||||||
|
<A, I>(
|
||||||
|
field: FormField<A, I>,
|
||||||
|
options?: useInput.Options,
|
||||||
|
): Effect.Effect<useInput.Result<I>, NoSuchElementException, Scope.Scope>
|
||||||
|
} = Effect.fnUntraced(function* <A, I>(
|
||||||
field: FormField<A, I>,
|
field: FormField<A, I>,
|
||||||
options?: useInput.Options,
|
options?: useInput.Options,
|
||||||
): Effect.fn.Return<useInput.Success<I>, Cause.NoSuchElementException, Scope.Scope> {
|
) {
|
||||||
const internalValueRef = yield* Component.useOnChange(() => Effect.tap(
|
const internalValueRef = yield* Component.useOnChange(() => Effect.tap(
|
||||||
Effect.andThen(field.encodedValue, SubscriptionRef.make),
|
Effect.andThen(field.encodedValueRef, SubscriptionRef.make),
|
||||||
internalValueRef => Effect.forkScoped(Effect.all([
|
internalValueRef => Effect.forkScoped(Effect.all([
|
||||||
Stream.runForEach(
|
Stream.runForEach(
|
||||||
Stream.drop(field.encodedValue, 1),
|
Stream.drop(field.encodedValueRef, 1),
|
||||||
upstreamEncodedValue => Effect.whenEffect(
|
upstreamEncodedValue => Effect.whenEffect(
|
||||||
Ref.set(internalValueRef, upstreamEncodedValue),
|
Ref.set(internalValueRef, upstreamEncodedValue),
|
||||||
Effect.andThen(internalValueRef, internalValue => !Equal.equals(upstreamEncodedValue, internalValue)),
|
Effect.andThen(internalValueRef, internalValue => !Equal.equals(upstreamEncodedValue, internalValue)),
|
||||||
@@ -322,7 +286,7 @@ export const useInput = Effect.fnUntraced(function* <A, I>(
|
|||||||
Stream.changesWith(Equal.equivalence()),
|
Stream.changesWith(Equal.equivalence()),
|
||||||
options?.debounce ? Stream.debounce(options.debounce) : identity,
|
options?.debounce ? Stream.debounce(options.debounce) : identity,
|
||||||
),
|
),
|
||||||
internalValue => Ref.set(field.encodedValue, internalValue),
|
internalValue => Ref.set(field.encodedValueRef, internalValue),
|
||||||
),
|
),
|
||||||
], { concurrency: "unbounded" })),
|
], { concurrency: "unbounded" })),
|
||||||
), [field, options?.debounce])
|
), [field, options?.debounce])
|
||||||
@@ -336,19 +300,24 @@ export namespace useOptionalInput {
|
|||||||
readonly defaultValue: T
|
readonly defaultValue: T
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Success<T> extends useInput.Success<T> {
|
export interface Result<T> extends useInput.Result<T> {
|
||||||
readonly enabled: boolean
|
readonly enabled: boolean
|
||||||
readonly setEnabled: React.Dispatch<React.SetStateAction<boolean>>
|
readonly setEnabled: React.Dispatch<React.SetStateAction<boolean>>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useOptionalInput = Effect.fnUntraced(function* <A, I>(
|
export const useOptionalInput: {
|
||||||
|
<A, I>(
|
||||||
|
field: FormField<A, Option.Option<I>>,
|
||||||
|
options: useOptionalInput.Options<I>,
|
||||||
|
): Effect.Effect<useOptionalInput.Result<I>, NoSuchElementException, Scope.Scope>
|
||||||
|
} = Effect.fnUntraced(function* <A, I>(
|
||||||
field: FormField<A, Option.Option<I>>,
|
field: FormField<A, Option.Option<I>>,
|
||||||
options: useOptionalInput.Options<I>,
|
options: useOptionalInput.Options<I>,
|
||||||
): Effect.fn.Return<useOptionalInput.Success<I>, Cause.NoSuchElementException, Scope.Scope> {
|
) {
|
||||||
const [enabledRef, internalValueRef] = yield* Component.useOnChange(() => Effect.tap(
|
const [enabledRef, internalValueRef] = yield* Component.useOnChange(() => Effect.tap(
|
||||||
Effect.andThen(
|
Effect.andThen(
|
||||||
field.encodedValue,
|
field.encodedValueRef,
|
||||||
Option.match({
|
Option.match({
|
||||||
onSome: v => Effect.all([SubscriptionRef.make(true), SubscriptionRef.make(v)]),
|
onSome: v => Effect.all([SubscriptionRef.make(true), SubscriptionRef.make(v)]),
|
||||||
onNone: () => Effect.all([SubscriptionRef.make(false), SubscriptionRef.make(options.defaultValue)]),
|
onNone: () => Effect.all([SubscriptionRef.make(false), SubscriptionRef.make(options.defaultValue)]),
|
||||||
@@ -357,7 +326,7 @@ export const useOptionalInput = Effect.fnUntraced(function* <A, I>(
|
|||||||
|
|
||||||
([enabledRef, internalValueRef]) => Effect.forkScoped(Effect.all([
|
([enabledRef, internalValueRef]) => Effect.forkScoped(Effect.all([
|
||||||
Stream.runForEach(
|
Stream.runForEach(
|
||||||
Stream.drop(field.encodedValue, 1),
|
Stream.drop(field.encodedValueRef, 1),
|
||||||
|
|
||||||
upstreamEncodedValue => Effect.whenEffect(
|
upstreamEncodedValue => Effect.whenEffect(
|
||||||
Option.match(upstreamEncodedValue, {
|
Option.match(upstreamEncodedValue, {
|
||||||
@@ -385,7 +354,7 @@ export const useOptionalInput = Effect.fnUntraced(function* <A, I>(
|
|||||||
Stream.changesWith(Equal.equivalence()),
|
Stream.changesWith(Equal.equivalence()),
|
||||||
options?.debounce ? Stream.debounce(options.debounce) : identity,
|
options?.debounce ? Stream.debounce(options.debounce) : identity,
|
||||||
),
|
),
|
||||||
([enabled, internalValue]) => Ref.set(field.encodedValue, enabled ? Option.some(internalValue) : Option.none()),
|
([enabled, internalValue]) => Ref.set(field.encodedValueRef, enabled ? Option.some(internalValue) : Option.none()),
|
||||||
),
|
),
|
||||||
], { concurrency: "unbounded" })),
|
], { concurrency: "unbounded" })),
|
||||||
), [field, options.debounce])
|
), [field, options.debounce])
|
||||||
|
|||||||
@@ -1,111 +1,50 @@
|
|||||||
/** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */
|
/** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */
|
||||||
import { type Equivalence, Function, Predicate } from "effect"
|
import { type Equivalence, Function, Predicate } from "effect"
|
||||||
import * as React from "react"
|
|
||||||
import type * as Component from "./Component.js"
|
import type * as Component from "./Component.js"
|
||||||
|
|
||||||
|
|
||||||
export const MemoizedTypeId: unique symbol = Symbol.for("@effect-fc/Memoized/Memoized")
|
export const TypeId: unique symbol = Symbol.for("@effect-fc/Memoized/Memoized")
|
||||||
export type MemoizedTypeId = typeof MemoizedTypeId
|
export type TypeId = typeof TypeId
|
||||||
|
|
||||||
|
export interface Memoized<P> extends Memoized.Options<P> {
|
||||||
/**
|
readonly [TypeId]: TypeId
|
||||||
* 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<P> extends MemoizedPrototype, MemoizedOptions<P> {}
|
|
||||||
|
|
||||||
export interface MemoizedPrototype {
|
|
||||||
readonly [MemoizedTypeId]: MemoizedTypeId
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export namespace Memoized {
|
||||||
* Configuration options for Memoized components.
|
export interface Options<P> {
|
||||||
*
|
readonly propsAreEqual?: Equivalence.Equivalence<P>
|
||||||
* @template P The props type of the component
|
}
|
||||||
*/
|
|
||||||
export interface MemoizedOptions<P> {
|
|
||||||
/**
|
|
||||||
* 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<P>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const MemoizedPrototype: MemoizedPrototype = Object.freeze({
|
const MemoizedProto = Object.freeze({
|
||||||
[MemoizedTypeId]: MemoizedTypeId,
|
[TypeId]: TypeId
|
||||||
|
|
||||||
transformFunctionComponent<P extends {}>(
|
|
||||||
this: Memoized<P>,
|
|
||||||
f: React.FC<P>,
|
|
||||||
) {
|
|
||||||
return React.memo(f, this.propsEquivalence)
|
|
||||||
},
|
|
||||||
} as const)
|
} as const)
|
||||||
|
|
||||||
|
|
||||||
export const isMemoized = (u: unknown): u is Memoized<unknown> => Predicate.hasProperty(u, MemoizedTypeId)
|
export const isMemoized = (u: unknown): u is Memoized<unknown> => Predicate.hasProperty(u, TypeId)
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 = <T extends Component.Component<any, any, any, any>>(
|
export const memoized = <T extends Component.Component<any, any, any, any>>(
|
||||||
self: T
|
self: T
|
||||||
): T & Memoized<Component.Component.Props<T>> => Object.setPrototypeOf(
|
): T & Memoized<Component.Component.Props<T>> => Object.setPrototypeOf(
|
||||||
Object.assign(function() {}, self),
|
Object.assign(function() {}, self),
|
||||||
Object.freeze(Object.setPrototypeOf(
|
Object.freeze(Object.setPrototypeOf(
|
||||||
Object.assign({}, MemoizedPrototype),
|
Object.assign({}, MemoizedProto),
|
||||||
Object.getPrototypeOf(self),
|
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: {
|
export const withOptions: {
|
||||||
<T extends Component.Component<any, any, any, any> & Memoized<any>>(
|
<T extends Component.Component<any, any, any, any> & Memoized<any>>(
|
||||||
options: Partial<MemoizedOptions<Component.Component.Props<T>>>
|
options: Partial<Memoized.Options<Component.Component.Props<T>>>
|
||||||
): (self: T) => T
|
): (self: T) => T
|
||||||
<T extends Component.Component<any, any, any, any> & Memoized<any>>(
|
<T extends Component.Component<any, any, any, any> & Memoized<any>>(
|
||||||
self: T,
|
self: T,
|
||||||
options: Partial<MemoizedOptions<Component.Component.Props<T>>>,
|
options: Partial<Memoized.Options<Component.Component.Props<T>>>,
|
||||||
): T
|
): T
|
||||||
} = Function.dual(2, <T extends Component.Component<any, any, any, any> & Memoized<any>>(
|
} = Function.dual(2, <T extends Component.Component<any, any, any, any> & Memoized<any>>(
|
||||||
self: T,
|
self: T,
|
||||||
options: Partial<MemoizedOptions<Component.Component.Props<T>>>,
|
options: Partial<Memoized.Options<Component.Component.Props<T>>>,
|
||||||
): T => Object.setPrototypeOf(
|
): T => Object.setPrototypeOf(
|
||||||
Object.assign(function() {}, self, options),
|
Object.assign(function() {}, self, options),
|
||||||
Object.getPrototypeOf(self),
|
Object.getPrototypeOf(self),
|
||||||
|
|||||||
@@ -1,128 +0,0 @@
|
|||||||
import { type Context, Effect, Equal, type Fiber, Option, Pipeable, Predicate, type Scope, Stream, type Subscribable, SubscriptionRef } from "effect"
|
|
||||||
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<in out K extends Mutation.AnyKey, in out A, in out E = never, in out R = never, in out P = never>
|
|
||||||
extends Pipeable.Pipeable {
|
|
||||||
readonly [MutationTypeId]: MutationTypeId
|
|
||||||
|
|
||||||
readonly context: Context.Context<Scope.Scope | R>
|
|
||||||
readonly f: (key: K) => Effect.Effect<A, E, R>
|
|
||||||
readonly initialProgress: P
|
|
||||||
|
|
||||||
readonly latestKey: Subscribable.Subscribable<Option.Option<K>>
|
|
||||||
readonly fiber: Subscribable.Subscribable<Option.Option<Fiber.Fiber<A, E>>>
|
|
||||||
readonly result: Subscribable.Subscribable<Result.Result<A, E, P>>
|
|
||||||
readonly latestFinalResult: Subscribable.Subscribable<Option.Option<Result.Final<A, E, P>>>
|
|
||||||
|
|
||||||
mutate(key: K): Effect.Effect<Result.Final<A, E, P>>
|
|
||||||
mutateSubscribable(key: K): Effect.Effect<Subscribable.Subscribable<Result.Result<A, E, P>>>
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare namespace Mutation {
|
|
||||||
export type AnyKey = readonly any[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MutationImpl<in out K extends Mutation.AnyKey, in out A, in out E = never, in out R = never, in out P = never>
|
|
||||||
extends Pipeable.Class() implements Mutation<K, A, E, R, P> {
|
|
||||||
readonly [MutationTypeId]: MutationTypeId = MutationTypeId
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
readonly context: Context.Context<Scope.Scope | R>,
|
|
||||||
readonly f: (key: K) => Effect.Effect<A, E, R>,
|
|
||||||
readonly initialProgress: P,
|
|
||||||
|
|
||||||
readonly latestKey: SubscriptionRef.SubscriptionRef<Option.Option<K>>,
|
|
||||||
readonly fiber: SubscriptionRef.SubscriptionRef<Option.Option<Fiber.Fiber<A, E>>>,
|
|
||||||
readonly result: SubscriptionRef.SubscriptionRef<Result.Result<A, E, P>>,
|
|
||||||
readonly latestFinalResult: SubscriptionRef.SubscriptionRef<Option.Option<Result.Final<A, E, P>>>,
|
|
||||||
) {
|
|
||||||
super()
|
|
||||||
}
|
|
||||||
|
|
||||||
mutate(key: K): Effect.Effect<Result.Final<A, E, P>> {
|
|
||||||
return SubscriptionRef.set(this.latestKey, Option.some(key)).pipe(
|
|
||||||
Effect.andThen(this.start(key)),
|
|
||||||
Effect.andThen(sub => this.watch(sub)),
|
|
||||||
Effect.provide(this.context),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
mutateSubscribable(key: K): Effect.Effect<Subscribable.Subscribable<Result.Result<A, E, P>>> {
|
|
||||||
return SubscriptionRef.set(this.latestKey, 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<Result.Result<A, E, P>>,
|
|
||||||
never,
|
|
||||||
Scope.Scope | R
|
|
||||||
> {
|
|
||||||
return this.latestFinalResult.pipe(
|
|
||||||
Effect.andThen(initial => Result.unsafeForkEffect(
|
|
||||||
Effect.onExit(this.f(key), () => Effect.andThen(
|
|
||||||
Effect.all([Effect.fiberId, this.fiber]),
|
|
||||||
([currentFiberId, fiber]) => Option.match(fiber, {
|
|
||||||
onSome: v => Equal.equals(currentFiberId, v.id())
|
|
||||||
? SubscriptionRef.set(this.fiber, Option.none())
|
|
||||||
: Effect.void,
|
|
||||||
onNone: () => Effect.void,
|
|
||||||
}),
|
|
||||||
)),
|
|
||||||
|
|
||||||
{
|
|
||||||
initial: Option.isSome(initial) ? Result.willFetch(initial.value) : Result.initial(),
|
|
||||||
initialProgress: this.initialProgress,
|
|
||||||
} as Result.unsafeForkEffect.Options<A, E, P>,
|
|
||||||
)),
|
|
||||||
Effect.tap(([, fiber]) => SubscriptionRef.set(this.fiber, Option.some(fiber))),
|
|
||||||
Effect.map(([sub]) => sub),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
|
||||||
sub: Subscribable.Subscribable<Result.Result<A, E, P>>
|
|
||||||
): Effect.Effect<Result.Final<A, E, P>> {
|
|
||||||
return sub.get.pipe(
|
|
||||||
Effect.andThen(initial => Stream.runFoldEffect(
|
|
||||||
sub.changes,
|
|
||||||
initial,
|
|
||||||
(_, result) => Effect.as(SubscriptionRef.set(this.result, result), result),
|
|
||||||
) as Effect.Effect<Result.Final<A, E, P>>),
|
|
||||||
Effect.tap(result => SubscriptionRef.set(this.latestFinalResult, Option.some(result))),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const isMutation = (u: unknown): u is Mutation<readonly unknown[], unknown, unknown, unknown, unknown> => Predicate.hasProperty(u, MutationTypeId)
|
|
||||||
|
|
||||||
export declare namespace make {
|
|
||||||
export interface Options<K extends Mutation.AnyKey = never, A = void, E = never, R = never, P = never> {
|
|
||||||
readonly f: (key: K) => Effect.Effect<A, E, Result.forkEffect.InputContext<R, NoInfer<P>>>
|
|
||||||
readonly initialProgress?: P
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const make = Effect.fnUntraced(function* <const K extends Mutation.AnyKey = never, A = void, E = never, R = never, P = never>(
|
|
||||||
options: make.Options<K, A, E, R, P>
|
|
||||||
): Effect.fn.Return<
|
|
||||||
Mutation<K, A, E, Result.forkEffect.OutputContext<A, E, R, P>, P>,
|
|
||||||
never,
|
|
||||||
Scope.Scope | Result.forkEffect.OutputContext<A, E, R, P>
|
|
||||||
> {
|
|
||||||
return new MutationImpl(
|
|
||||||
yield* Effect.context<Scope.Scope | Result.forkEffect.OutputContext<A, E, R, P>>(),
|
|
||||||
options.f as any,
|
|
||||||
options.initialProgress as P,
|
|
||||||
|
|
||||||
yield* SubscriptionRef.make(Option.none<K>()),
|
|
||||||
yield* SubscriptionRef.make(Option.none<Fiber.Fiber<A, E>>()),
|
|
||||||
yield* SubscriptionRef.make(Result.initial<A, E, P>()),
|
|
||||||
yield* SubscriptionRef.make(Option.none<Result.Final<A, E, P>>()),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { Effect, PubSub, type Scope } from "effect"
|
|
||||||
import type * as React from "react"
|
|
||||||
import * as Component from "./Component.js"
|
|
||||||
|
|
||||||
|
|
||||||
export const usePubSubFromReactiveValues = Effect.fnUntraced(function* <const A extends React.DependencyList>(
|
|
||||||
values: A
|
|
||||||
): Effect.fn.Return<PubSub.PubSub<A>, never, Scope.Scope> {
|
|
||||||
const pubsub = yield* Component.useOnMount(() => Effect.acquireRelease(PubSub.unbounded<A>(), PubSub.shutdown))
|
|
||||||
yield* Component.useReactEffect(() => Effect.unlessEffect(PubSub.publish(pubsub, values), PubSub.isShutdown(pubsub)), values)
|
|
||||||
return pubsub
|
|
||||||
})
|
|
||||||
|
|
||||||
export * from "effect/PubSub"
|
|
||||||
@@ -1,317 +0,0 @@
|
|||||||
import { type Cause, type Context, type Duration, Effect, Equal, Fiber, identity, Option, Pipeable, Predicate, type Scope, Stream, Subscribable, SubscriptionRef } from "effect"
|
|
||||||
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<in out K extends Query.AnyKey, in out A, in out KE = never, in out KR = never, in out E = never, in out R = never, in out P = never>
|
|
||||||
extends Pipeable.Pipeable {
|
|
||||||
readonly [QueryTypeId]: QueryTypeId
|
|
||||||
|
|
||||||
readonly context: Context.Context<Scope.Scope | QueryClient.QueryClient | KR | R>
|
|
||||||
readonly key: Stream.Stream<K, KE, KR>
|
|
||||||
readonly f: (key: K) => Effect.Effect<A, E, R>
|
|
||||||
readonly initialProgress: P
|
|
||||||
|
|
||||||
readonly staleTime: Duration.DurationInput
|
|
||||||
readonly refreshOnWindowFocus: boolean
|
|
||||||
|
|
||||||
readonly latestKey: Subscribable.Subscribable<Option.Option<K>>
|
|
||||||
readonly fiber: Subscribable.Subscribable<Option.Option<Fiber.Fiber<A, E>>>
|
|
||||||
readonly result: Subscribable.Subscribable<Result.Result<A, E, P>>
|
|
||||||
readonly latestFinalResult: Subscribable.Subscribable<Option.Option<Result.Final<A, E, P>>>
|
|
||||||
|
|
||||||
readonly run: Effect.Effect<void>
|
|
||||||
fetch(key: K): Effect.Effect<Result.Final<A, E, P>>
|
|
||||||
fetchSubscribable(key: K): Effect.Effect<Subscribable.Subscribable<Result.Result<A, E, P>>>
|
|
||||||
readonly refresh: Effect.Effect<Result.Final<A, E, P>, Cause.NoSuchElementException>
|
|
||||||
readonly refreshSubscribable: Effect.Effect<Subscribable.Subscribable<Result.Result<A, E, P>>, Cause.NoSuchElementException>
|
|
||||||
|
|
||||||
readonly invalidateCache: Effect.Effect<void>
|
|
||||||
invalidateCacheEntry(key: K): Effect.Effect<void>
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare namespace Query {
|
|
||||||
export type AnyKey = readonly any[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export class QueryImpl<in out K extends Query.AnyKey, in out A, in out KE = never, in out KR = never, in out E = never, in out R = never, in out P = never>
|
|
||||||
extends Pipeable.Class() implements Query<K, A, KE, KR, E, R, P> {
|
|
||||||
readonly [QueryTypeId]: QueryTypeId = QueryTypeId
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
readonly context: Context.Context<Scope.Scope | QueryClient.QueryClient | KR | R>,
|
|
||||||
readonly key: Stream.Stream<K, KE, KR>,
|
|
||||||
readonly f: (key: K) => Effect.Effect<A, E, R>,
|
|
||||||
readonly initialProgress: P,
|
|
||||||
|
|
||||||
readonly staleTime: Duration.DurationInput,
|
|
||||||
readonly refreshOnWindowFocus: boolean,
|
|
||||||
|
|
||||||
readonly latestKey: SubscriptionRef.SubscriptionRef<Option.Option<K>>,
|
|
||||||
readonly fiber: SubscriptionRef.SubscriptionRef<Option.Option<Fiber.Fiber<A, E>>>,
|
|
||||||
readonly result: SubscriptionRef.SubscriptionRef<Result.Result<A, E, P>>,
|
|
||||||
readonly latestFinalResult: SubscriptionRef.SubscriptionRef<Option.Option<Result.Final<A, E, P>>>,
|
|
||||||
|
|
||||||
readonly runSemaphore: Effect.Semaphore,
|
|
||||||
) {
|
|
||||||
super()
|
|
||||||
}
|
|
||||||
|
|
||||||
get run(): Effect.Effect<void> {
|
|
||||||
return Effect.all([
|
|
||||||
Stream.runForEach(this.key, key => this.fetchSubscribable(key)),
|
|
||||||
|
|
||||||
Effect.promise(() => import("@effect/platform-browser")).pipe(
|
|
||||||
Effect.andThen(({ BrowserStream }) => this.refreshOnWindowFocus
|
|
||||||
? Stream.runForEach(
|
|
||||||
BrowserStream.fromEventListenerWindow("focus"),
|
|
||||||
() => this.refreshSubscribable,
|
|
||||||
)
|
|
||||||
: Effect.void
|
|
||||||
),
|
|
||||||
Effect.catchAllDefect(() => Effect.void),
|
|
||||||
),
|
|
||||||
], { concurrency: "unbounded" }).pipe(
|
|
||||||
Effect.ignore,
|
|
||||||
this.runSemaphore.withPermits(1),
|
|
||||||
Effect.provide(this.context),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
get interrupt(): Effect.Effect<void> {
|
|
||||||
return Effect.andThen(this.fiber, Option.match({
|
|
||||||
onSome: Fiber.interrupt,
|
|
||||||
onNone: () => Effect.void,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch(key: K): Effect.Effect<Result.Final<A, E, P>> {
|
|
||||||
return this.interrupt.pipe(
|
|
||||||
Effect.andThen(SubscriptionRef.set(this.latestKey, Option.some(key))),
|
|
||||||
Effect.andThen(this.latestFinalResult),
|
|
||||||
Effect.andThen(previous => this.startCached(key, Option.isSome(previous)
|
|
||||||
? Result.willFetch(previous.value) as Result.Final<A, E, P>
|
|
||||||
: Result.initial()
|
|
||||||
)),
|
|
||||||
Effect.andThen(sub => this.watch(key, sub)),
|
|
||||||
Effect.provide(this.context),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchSubscribable(key: K): Effect.Effect<Subscribable.Subscribable<Result.Result<A, E, P>>> {
|
|
||||||
return this.interrupt.pipe(
|
|
||||||
Effect.andThen(SubscriptionRef.set(this.latestKey, Option.some(key))),
|
|
||||||
Effect.andThen(this.latestFinalResult),
|
|
||||||
Effect.andThen(previous => this.startCached(key, Option.isSome(previous)
|
|
||||||
? Result.willFetch(previous.value) as Result.Final<A, E, P>
|
|
||||||
: Result.initial()
|
|
||||||
)),
|
|
||||||
Effect.tap(sub => Effect.forkScoped(this.watch(key, sub))),
|
|
||||||
Effect.provide(this.context),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
get refresh(): Effect.Effect<Result.Final<A, E, P>, Cause.NoSuchElementException> {
|
|
||||||
return this.interrupt.pipe(
|
|
||||||
Effect.andThen(Effect.Do),
|
|
||||||
Effect.bind("latestKey", () => Effect.andThen(this.latestKey, identity)),
|
|
||||||
Effect.bind("latestFinalResult", () => this.latestFinalResult),
|
|
||||||
Effect.bind("subscribable", ({ latestKey, latestFinalResult }) =>
|
|
||||||
this.startCached(latestKey, Option.isSome(latestFinalResult)
|
|
||||||
? Result.willRefresh(latestFinalResult.value) as Result.Final<A, E, P>
|
|
||||||
: Result.initial()
|
|
||||||
)
|
|
||||||
),
|
|
||||||
Effect.andThen(({ latestKey, subscribable }) => this.watch(latestKey, subscribable)),
|
|
||||||
Effect.provide(this.context),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
get refreshSubscribable(): Effect.Effect<
|
|
||||||
Subscribable.Subscribable<Result.Result<A, E, P>>,
|
|
||||||
Cause.NoSuchElementException
|
|
||||||
> {
|
|
||||||
return this.interrupt.pipe(
|
|
||||||
Effect.andThen(Effect.Do),
|
|
||||||
Effect.bind("latestKey", () => Effect.andThen(this.latestKey, identity)),
|
|
||||||
Effect.bind("latestFinalResult", () => this.latestFinalResult),
|
|
||||||
Effect.bind("subscribable", ({ latestKey, latestFinalResult }) =>
|
|
||||||
this.startCached(latestKey, Option.isSome(latestFinalResult)
|
|
||||||
? Result.willRefresh(latestFinalResult.value) as Result.Final<A, E, P>
|
|
||||||
: Result.initial()
|
|
||||||
)
|
|
||||||
),
|
|
||||||
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<A, E, P>,
|
|
||||||
): Effect.Effect<
|
|
||||||
Subscribable.Subscribable<Result.Result<A, E, P>>,
|
|
||||||
never,
|
|
||||||
Scope.Scope | QueryClient.QueryClient | R
|
|
||||||
> {
|
|
||||||
return Effect.andThen(this.getCacheEntry(key), Option.match({
|
|
||||||
onSome: entry => Effect.andThen(
|
|
||||||
QueryClient.isQueryClientCacheEntryStale(entry),
|
|
||||||
isStale => isStale
|
|
||||||
? this.start(key, Result.willRefresh(entry.result) as Result.Final<A, E, P>)
|
|
||||||
: Effect.succeed(Subscribable.make({
|
|
||||||
get: Effect.succeed(entry.result as Result.Result<A, E, P>),
|
|
||||||
get changes() { return Stream.make(entry.result as Result.Result<A, E, P>) },
|
|
||||||
})),
|
|
||||||
),
|
|
||||||
onNone: () => this.start(key, initial),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
start(
|
|
||||||
key: K,
|
|
||||||
initial: Result.Initial | Result.Final<A, E, P>,
|
|
||||||
): Effect.Effect<
|
|
||||||
Subscribable.Subscribable<Result.Result<A, E, P>>,
|
|
||||||
never,
|
|
||||||
Scope.Scope | R
|
|
||||||
> {
|
|
||||||
return Result.unsafeForkEffect(
|
|
||||||
Effect.onExit(this.f(key), () => Effect.andThen(
|
|
||||||
Effect.all([Effect.fiberId, this.fiber]),
|
|
||||||
([currentFiberId, fiber]) => Option.match(fiber, {
|
|
||||||
onSome: v => Equal.equals(currentFiberId, v.id())
|
|
||||||
? SubscriptionRef.set(this.fiber, Option.none())
|
|
||||||
: Effect.void,
|
|
||||||
onNone: () => Effect.void,
|
|
||||||
}),
|
|
||||||
)),
|
|
||||||
|
|
||||||
{
|
|
||||||
initial,
|
|
||||||
initialProgress: this.initialProgress,
|
|
||||||
} as Result.unsafeForkEffect.Options<A, E, P>,
|
|
||||||
).pipe(
|
|
||||||
Effect.tap(([, fiber]) => SubscriptionRef.set(this.fiber, Option.some(fiber))),
|
|
||||||
Effect.map(([sub]) => sub),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
|
||||||
key: K,
|
|
||||||
sub: Subscribable.Subscribable<Result.Result<A, E, P>>
|
|
||||||
): Effect.Effect<Result.Final<A, E, P>, never, QueryClient.QueryClient> {
|
|
||||||
return sub.get.pipe(
|
|
||||||
Effect.andThen(initial => Stream.runFoldEffect(
|
|
||||||
sub.changes,
|
|
||||||
initial,
|
|
||||||
(_, result) => Effect.as(SubscriptionRef.set(this.result, result), result),
|
|
||||||
) as Effect.Effect<Result.Final<A, E, P>>),
|
|
||||||
Effect.tap(result => SubscriptionRef.set(this.latestFinalResult, Option.some(result))),
|
|
||||||
Effect.tap(result => Result.isSuccess(result)
|
|
||||||
? this.setCacheEntry(key, result)
|
|
||||||
: Effect.void
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
makeCacheKey(key: K): QueryClient.QueryClientCacheKey {
|
|
||||||
return new QueryClient.QueryClientCacheKey(key, this.f as (key: Query.AnyKey) => Effect.Effect<unknown, unknown, unknown>)
|
|
||||||
}
|
|
||||||
|
|
||||||
getCacheEntry(
|
|
||||||
key: K
|
|
||||||
): Effect.Effect<Option.Option<QueryClient.QueryClientCacheEntry>, 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<A>,
|
|
||||||
): Effect.Effect<QueryClient.QueryClientCacheEntry, never, QueryClient.QueryClient> {
|
|
||||||
return Effect.andThen(
|
|
||||||
Effect.all([
|
|
||||||
Effect.succeed(this.makeCacheKey(key)),
|
|
||||||
QueryClient.QueryClient,
|
|
||||||
]),
|
|
||||||
([key, client]) => client.setCacheEntry(key, result, this.staleTime),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
get invalidateCache(): Effect.Effect<void> {
|
|
||||||
return QueryClient.QueryClient.pipe(
|
|
||||||
Effect.andThen(client => client.invalidateCacheEntries(this.f as (key: Query.AnyKey) => Effect.Effect<unknown, unknown, unknown>)),
|
|
||||||
Effect.provide(this.context),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
invalidateCacheEntry(key: K): Effect.Effect<void> {
|
|
||||||
return Effect.all([
|
|
||||||
Effect.succeed(this.makeCacheKey(key)),
|
|
||||||
QueryClient.QueryClient,
|
|
||||||
]).pipe(
|
|
||||||
Effect.andThen(([key, client]) => client.invalidateCacheEntry(key)),
|
|
||||||
Effect.provide(this.context),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const isQuery = (u: unknown): u is Query<readonly unknown[], unknown> => Predicate.hasProperty(u, QueryTypeId)
|
|
||||||
|
|
||||||
export declare namespace make {
|
|
||||||
export interface Options<K extends Query.AnyKey, A, KE = never, KR = never, E = never, R = never, P = never> {
|
|
||||||
readonly key: Stream.Stream<K, KE, KR>
|
|
||||||
readonly f: (key: NoInfer<K>) => Effect.Effect<A, E, Result.forkEffect.InputContext<R, NoInfer<P>>>
|
|
||||||
readonly initialProgress?: P
|
|
||||||
readonly staleTime?: Duration.DurationInput
|
|
||||||
readonly refreshOnWindowFocus?: boolean
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const make = Effect.fnUntraced(function* <K extends Query.AnyKey, A, KE = never, KR = never, E = never, R = never, P = never>(
|
|
||||||
options: make.Options<K, A, KE, KR, E, R, P>
|
|
||||||
): Effect.fn.Return<
|
|
||||||
Query<K, A, KE, KR, E, Result.forkEffect.OutputContext<A, E, R, P>, P>,
|
|
||||||
never,
|
|
||||||
Scope.Scope | QueryClient.QueryClient | KR | Result.forkEffect.OutputContext<A, E, R, P>
|
|
||||||
> {
|
|
||||||
const client = yield* QueryClient.QueryClient
|
|
||||||
|
|
||||||
return new QueryImpl<K, A, KE, KR, E, Result.forkEffect.OutputContext<A, E, R, P>, P>(
|
|
||||||
yield* Effect.context<Scope.Scope | QueryClient.QueryClient | KR | Result.forkEffect.OutputContext<A, E, R, P>>(),
|
|
||||||
options.key,
|
|
||||||
options.f as any,
|
|
||||||
options.initialProgress as P,
|
|
||||||
|
|
||||||
options.staleTime ?? client.defaultStaleTime,
|
|
||||||
options.refreshOnWindowFocus ?? client.defaultRefreshOnWindowFocus,
|
|
||||||
|
|
||||||
yield* SubscriptionRef.make(Option.none<K>()),
|
|
||||||
yield* SubscriptionRef.make(Option.none<Fiber.Fiber<A, E>>()),
|
|
||||||
yield* SubscriptionRef.make(Result.initial<A, E, P>()),
|
|
||||||
yield* SubscriptionRef.make(Option.none<Result.Final<A, E, P>>()),
|
|
||||||
|
|
||||||
yield* Effect.makeSemaphore(1),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
export const service = <K extends Query.AnyKey, A, KE = never, KR = never, E = never, R = never, P = never>(
|
|
||||||
options: make.Options<K, A, KE, KR, E, R, P>
|
|
||||||
): Effect.Effect<
|
|
||||||
Query<K, A, KE, KR, E, Result.forkEffect.OutputContext<A, E, R, P>, P>,
|
|
||||||
never,
|
|
||||||
Scope.Scope | QueryClient.QueryClient | KR | Result.forkEffect.OutputContext<A, E, R, P>
|
|
||||||
> => Effect.tap(
|
|
||||||
make(options),
|
|
||||||
query => Effect.forkScoped(query.run),
|
|
||||||
)
|
|
||||||
@@ -1,173 +0,0 @@
|
|||||||
import { DateTime, Duration, Effect, Equal, Equivalence, Hash, HashMap, type Option, Pipeable, Predicate, Schedule, type Scope, type Subscribable, SubscriptionRef } from "effect"
|
|
||||||
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<HashMap.HashMap<QueryClientCacheKey, QueryClientCacheEntry>>
|
|
||||||
readonly cacheGcTime: Duration.DurationInput
|
|
||||||
readonly defaultStaleTime: Duration.DurationInput
|
|
||||||
readonly defaultRefreshOnWindowFocus: boolean
|
|
||||||
|
|
||||||
readonly run: Effect.Effect<void>
|
|
||||||
getCacheEntry(key: QueryClientCacheKey): Effect.Effect<Option.Option<QueryClientCacheEntry>>
|
|
||||||
setCacheEntry(
|
|
||||||
key: QueryClientCacheKey,
|
|
||||||
result: Result.Success<unknown>,
|
|
||||||
staleTime: Duration.DurationInput,
|
|
||||||
): Effect.Effect<QueryClientCacheEntry>
|
|
||||||
invalidateCacheEntries(f: (key: Query.Query.AnyKey) => Effect.Effect<unknown, unknown, unknown>): Effect.Effect<void>
|
|
||||||
invalidateCacheEntry(key: QueryClientCacheKey): Effect.Effect<void>
|
|
||||||
}
|
|
||||||
|
|
||||||
export class QueryClient extends Effect.Service<QueryClient>()("@effect-fc/QueryClient/QueryClient", {
|
|
||||||
scoped: Effect.suspend(() => service())
|
|
||||||
}) {}
|
|
||||||
|
|
||||||
export class QueryClientServiceImpl
|
|
||||||
extends Pipeable.Class()
|
|
||||||
implements QueryClientService {
|
|
||||||
readonly [QueryClientServiceTypeId]: QueryClientServiceTypeId = QueryClientServiceTypeId
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
readonly cache: SubscriptionRef.SubscriptionRef<HashMap.HashMap<QueryClientCacheKey, QueryClientCacheEntry>>,
|
|
||||||
readonly cacheGcTime: Duration.DurationInput,
|
|
||||||
readonly defaultStaleTime: Duration.DurationInput,
|
|
||||||
readonly defaultRefreshOnWindowFocus: boolean,
|
|
||||||
readonly runSemaphore: Effect.Semaphore,
|
|
||||||
) {
|
|
||||||
super()
|
|
||||||
}
|
|
||||||
|
|
||||||
get run(): Effect.Effect<void> {
|
|
||||||
return this.runSemaphore.withPermits(1)(Effect.repeat(
|
|
||||||
Effect.andThen(
|
|
||||||
DateTime.now,
|
|
||||||
now => SubscriptionRef.update(this.cache, HashMap.filter(entry =>
|
|
||||||
Duration.lessThan(
|
|
||||||
DateTime.distanceDuration(entry.lastAccessedAt, now),
|
|
||||||
Duration.sum(entry.staleTime, this.cacheGcTime),
|
|
||||||
)
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
Schedule.spaced("30 second"),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
getCacheEntry(key: QueryClientCacheKey): Effect.Effect<Option.Option<QueryClientCacheEntry>> {
|
|
||||||
return Effect.all([
|
|
||||||
Effect.andThen(this.cache, HashMap.get(key)),
|
|
||||||
DateTime.now,
|
|
||||||
]).pipe(
|
|
||||||
Effect.map(([entry, now]) => new QueryClientCacheEntry(entry.result, entry.staleTime, entry.createdAt, now)),
|
|
||||||
Effect.tap(entry => SubscriptionRef.update(this.cache, HashMap.set(key, entry))),
|
|
||||||
Effect.option,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
setCacheEntry(
|
|
||||||
key: QueryClientCacheKey,
|
|
||||||
result: Result.Success<unknown>,
|
|
||||||
staleTime: Duration.DurationInput,
|
|
||||||
): Effect.Effect<QueryClientCacheEntry> {
|
|
||||||
return DateTime.now.pipe(
|
|
||||||
Effect.map(now => new QueryClientCacheEntry(result, staleTime, now, now)),
|
|
||||||
Effect.tap(entry => SubscriptionRef.update(this.cache, HashMap.set(key, entry))),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
invalidateCacheEntries(f: (key: Query.Query.AnyKey) => Effect.Effect<unknown, unknown, unknown>): Effect.Effect<void> {
|
|
||||||
return SubscriptionRef.update(this.cache, HashMap.filter((_, key) => !Equivalence.strict()(key.f, f)))
|
|
||||||
}
|
|
||||||
invalidateCacheEntry(key: QueryClientCacheKey): Effect.Effect<void> {
|
|
||||||
return SubscriptionRef.update(this.cache, 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.DurationInput
|
|
||||||
readonly defaultStaleTime?: Duration.DurationInput
|
|
||||||
readonly defaultRefreshOnWindowFocus?: boolean
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const make = Effect.fnUntraced(function* (options: make.Options = {}): Effect.fn.Return<QueryClientService> {
|
|
||||||
return new QueryClientServiceImpl(
|
|
||||||
yield* SubscriptionRef.make(HashMap.empty<QueryClientCacheKey, QueryClientCacheEntry>()),
|
|
||||||
options.cacheGcTime ?? "5 minutes",
|
|
||||||
options.defaultStaleTime ?? "0 minutes",
|
|
||||||
options.defaultRefreshOnWindowFocus ?? true,
|
|
||||||
yield* Effect.makeSemaphore(1),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
export declare namespace service {
|
|
||||||
export interface Options extends make.Options {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const service = (options?: service.Options): Effect.Effect<QueryClientService, never, Scope.Scope> => Effect.tap(
|
|
||||||
make(options),
|
|
||||||
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 Pipeable.Pipeable, Equal.Equal {
|
|
||||||
readonly [QueryClientCacheKeyTypeId]: QueryClientCacheKeyTypeId = QueryClientCacheKeyTypeId
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
readonly key: Query.Query.AnyKey,
|
|
||||||
readonly f: (key: Query.Query.AnyKey) => Effect.Effect<unknown, unknown, unknown>,
|
|
||||||
) {
|
|
||||||
super()
|
|
||||||
}
|
|
||||||
|
|
||||||
[Equal.symbol](that: Equal.Equal) {
|
|
||||||
return isQueryClientCacheKey(that) && Equivalence.array(Equal.equivalence())(this.key, that.key) && Equivalence.strict()(this.f, that.f)
|
|
||||||
}
|
|
||||||
[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()
|
|
||||||
implements Pipeable.Pipeable {
|
|
||||||
readonly [QueryClientCacheEntryTypeId]: QueryClientCacheEntryTypeId = QueryClientCacheEntryTypeId
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
readonly result: Result.Success<unknown>,
|
|
||||||
readonly staleTime: Duration.DurationInput,
|
|
||||||
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<boolean> => Effect.andThen(
|
|
||||||
DateTime.now,
|
|
||||||
now => Duration.greaterThanOrEqualTo(DateTime.distanceDuration(self.createdAt, now), self.staleTime),
|
|
||||||
)
|
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
/** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */
|
/** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */
|
||||||
import { Effect, Layer, ManagedRuntime, Predicate, Runtime, Scope } from "effect"
|
import { Effect, Layer, ManagedRuntime, Predicate, type Runtime } from "effect"
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as Component from "./Component.js"
|
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 const TypeId: unique symbol = Symbol.for("@effect-fc/ReactRuntime/ReactRuntime")
|
||||||
@@ -18,26 +16,16 @@ export interface ReactRuntime<R, ER> {
|
|||||||
|
|
||||||
const ReactRuntimeProto = Object.freeze({ [TypeId]: TypeId } as const)
|
const ReactRuntimeProto = Object.freeze({ [TypeId]: TypeId } as const)
|
||||||
|
|
||||||
export const Prelude: Layer.Layer<
|
|
||||||
| Component.ScopeMap
|
|
||||||
| ErrorObserver.ErrorObserver
|
|
||||||
| QueryClient.QueryClient
|
|
||||||
> = Layer.mergeAll(
|
|
||||||
Component.ScopeMap.Default,
|
|
||||||
ErrorObserver.layer,
|
|
||||||
QueryClient.QueryClient.Default,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
export const isReactRuntime = (u: unknown): u is ReactRuntime<unknown, unknown> => Predicate.hasProperty(u, TypeId)
|
export const isReactRuntime = (u: unknown): u is ReactRuntime<unknown, unknown> => Predicate.hasProperty(u, TypeId)
|
||||||
|
|
||||||
export const make = <R, ER>(
|
export const make = <R, ER>(
|
||||||
layer: Layer.Layer<R, ER>,
|
layer: Layer.Layer<R, ER>,
|
||||||
memoMap?: Layer.MemoMap,
|
memoMap?: Layer.MemoMap,
|
||||||
): ReactRuntime<Layer.Layer.Success<typeof Prelude> | R, ER> => Object.setPrototypeOf(
|
): ReactRuntime<R | Component.ScopeMap, ER> => Object.setPrototypeOf(
|
||||||
Object.assign(function() {}, {
|
Object.assign(function() {}, {
|
||||||
runtime: ManagedRuntime.make(
|
runtime: ManagedRuntime.make(
|
||||||
Layer.merge(layer, Prelude),
|
Layer.merge(layer, Component.ScopeMap.Default),
|
||||||
memoMap,
|
memoMap,
|
||||||
),
|
),
|
||||||
// biome-ignore lint/style/noNonNullAssertion: context initialization
|
// biome-ignore lint/style/noNonNullAssertion: context initialization
|
||||||
@@ -66,20 +54,16 @@ export const Provider = <R, ER>(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProviderInner = <R, ER>(
|
interface ProviderInnerProps<R, ER> {
|
||||||
{ runtime, promise, children }: {
|
readonly runtime: ReactRuntime<R, ER>
|
||||||
readonly runtime: ReactRuntime<R, ER>
|
readonly promise: Promise<Runtime.Runtime<R>>
|
||||||
readonly promise: Promise<Runtime.Runtime<R>>
|
readonly children?: React.ReactNode
|
||||||
readonly children?: React.ReactNode
|
|
||||||
}
|
|
||||||
): React.ReactNode => {
|
|
||||||
const effectRuntime = React.use(promise)
|
|
||||||
const scope = Runtime.runSync(effectRuntime)(Component.useScope([effectRuntime]))
|
|
||||||
Runtime.runSync(effectRuntime)(Effect.provideService(
|
|
||||||
Component.useOnChange(() => Effect.addFinalizer(() => runtime.runtime.disposeEffect), [scope]),
|
|
||||||
Scope.Scope,
|
|
||||||
scope,
|
|
||||||
))
|
|
||||||
|
|
||||||
return React.createElement(runtime.context, { value: effectRuntime }, children)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ProviderInner = <R, ER>(
|
||||||
|
{ runtime, promise, children }: ProviderInnerProps<R, ER>
|
||||||
|
): React.ReactNode => React.createElement(
|
||||||
|
runtime.context,
|
||||||
|
{ value: React.use(promise) },
|
||||||
|
children,
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Cause, Context, Data, Effect, Equal, Exit, type Fiber, Hash, Layer, Match, Pipeable, Predicate, PubSub, pipe, Ref, type Scope, Stream, Subscribable } from "effect"
|
import { Cause, Context, Data, Effect, Equal, Exit, Hash, Layer, Match, Option, Pipeable, Predicate, pipe, Queue, Ref, Scope } from "effect"
|
||||||
|
|
||||||
|
|
||||||
export const ResultTypeId: unique symbol = Symbol.for("@effect-fc/Result/Result")
|
export const ResultTypeId: unique symbol = Symbol.for("@effect-fc/Result/Result")
|
||||||
@@ -7,14 +7,13 @@ export type ResultTypeId = typeof ResultTypeId
|
|||||||
export type Result<A, E = never, P = never> = (
|
export type Result<A, E = never, P = never> = (
|
||||||
| Initial
|
| Initial
|
||||||
| Running<P>
|
| Running<P>
|
||||||
| Final<A, E, P>
|
| Success<A>
|
||||||
|
| (Success<A> & Refreshing<P>)
|
||||||
|
| Failure<A, E>
|
||||||
|
| (Failure<A, E> & Refreshing<P>)
|
||||||
)
|
)
|
||||||
|
|
||||||
// biome-ignore lint/complexity/noBannedTypes: "{}" is relevant here
|
export namespace Result {
|
||||||
export type Final<A, E = never, P = never> = (Success<A> | Failure<E>) & ({} | Flags<P>)
|
|
||||||
export type Flags<P = never> = WillFetch | WillRefresh | Refreshing<P>
|
|
||||||
|
|
||||||
export declare namespace Result {
|
|
||||||
export interface Prototype extends Pipeable.Pipeable, Equal.Equal {
|
export interface Prototype extends Pipeable.Pipeable, Equal.Equal {
|
||||||
readonly [ResultTypeId]: ResultTypeId
|
readonly [ResultTypeId]: ResultTypeId
|
||||||
}
|
}
|
||||||
@@ -24,10 +23,6 @@ export declare namespace Result {
|
|||||||
export type Progress<R extends Result<any, any, any>> = [R] extends [Result<infer _A, infer _E, infer P>] ? P : never
|
export type Progress<R extends Result<any, any, any>> = [R] extends [Result<infer _A, infer _E, infer P>] ? P : never
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare namespace Flags {
|
|
||||||
export type Keys = keyof WillFetch & WillRefresh & Refreshing<any>
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Initial extends Result.Prototype {
|
export interface Initial extends Result.Prototype {
|
||||||
readonly _tag: "Initial"
|
readonly _tag: "Initial"
|
||||||
}
|
}
|
||||||
@@ -42,21 +37,14 @@ export interface Success<A> extends Result.Prototype {
|
|||||||
readonly value: A
|
readonly value: A
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Failure<E = never> extends Result.Prototype {
|
export interface Failure<A, E = never> extends Result.Prototype {
|
||||||
readonly _tag: "Failure"
|
readonly _tag: "Failure"
|
||||||
readonly cause: Cause.Cause<E>
|
readonly cause: Cause.Cause<E>
|
||||||
}
|
readonly previousSuccess: Option.Option<Success<A>>
|
||||||
|
|
||||||
export interface WillFetch {
|
|
||||||
readonly _flag: "WillFetch"
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WillRefresh {
|
|
||||||
readonly _flag: "WillRefresh"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Refreshing<P = never> {
|
export interface Refreshing<P = never> {
|
||||||
readonly _flag: "Refreshing"
|
readonly refreshing: true
|
||||||
readonly progress: P
|
readonly progress: P
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,32 +54,41 @@ const ResultPrototype = Object.freeze({
|
|||||||
[ResultTypeId]: ResultTypeId,
|
[ResultTypeId]: ResultTypeId,
|
||||||
|
|
||||||
[Equal.symbol](this: Result<any, any, any>, that: Result<any, any, any>): boolean {
|
[Equal.symbol](this: Result<any, any, any>, that: Result<any, any, any>): boolean {
|
||||||
if (this._tag !== that._tag || (this as Flags)._flag !== (that as Flags)._flag)
|
if (this._tag !== that._tag)
|
||||||
return false
|
|
||||||
if (hasRefreshingFlag(this) && !Equal.equals(this.progress, (that as Refreshing<any>).progress))
|
|
||||||
return false
|
return false
|
||||||
|
|
||||||
return Match.value(this).pipe(
|
return Match.value(this).pipe(
|
||||||
Match.tag("Initial", () => true),
|
Match.tag("Initial", () => true),
|
||||||
Match.tag("Running", self => Equal.equals(self.progress, (that as Running<any>).progress)),
|
Match.tag("Running", self => Equal.equals(self.progress, (that as Running<any>).progress)),
|
||||||
Match.tag("Success", self => Equal.equals(self.value, (that as Success<any>).value)),
|
Match.tag("Success", self =>
|
||||||
Match.tag("Failure", self => Equal.equals(self.cause, (that as Failure<any>).cause)),
|
Equal.equals(self.value, (that as Success<any>).value) &&
|
||||||
|
(isRefreshing(self) ? self.refreshing : false) === (isRefreshing(that) ? that.refreshing : false) &&
|
||||||
|
Equal.equals(isRefreshing(self) ? self.progress : undefined, isRefreshing(that) ? that.progress : undefined)
|
||||||
|
),
|
||||||
|
Match.tag("Failure", self =>
|
||||||
|
Equal.equals(self.cause, (that as Failure<any, any>).cause) &&
|
||||||
|
(isRefreshing(self) ? self.refreshing : false) === (isRefreshing(that) ? that.refreshing : false) &&
|
||||||
|
Equal.equals(isRefreshing(self) ? self.progress : undefined, isRefreshing(that) ? that.progress : undefined)
|
||||||
|
),
|
||||||
Match.exhaustive,
|
Match.exhaustive,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
[Hash.symbol](this: Result<any, any, any>): number {
|
[Hash.symbol](this: Result<any, any, any>): number {
|
||||||
return pipe(Hash.string(this._tag),
|
const tagHash = Hash.string(this._tag)
|
||||||
tagHash => Match.value(this).pipe(
|
|
||||||
Match.tag("Initial", () => tagHash),
|
return Match.value(this).pipe(
|
||||||
Match.tag("Running", self => Hash.combine(Hash.hash(self.progress))(tagHash)),
|
Match.tag("Initial", () => tagHash),
|
||||||
Match.tag("Success", self => Hash.combine(Hash.hash(self.value))(tagHash)),
|
Match.tag("Running", self => Hash.combine(Hash.hash(self.progress))(tagHash)),
|
||||||
Match.tag("Failure", self => Hash.combine(Hash.hash(self.cause))(tagHash)),
|
Match.tag("Success", self => pipe(tagHash,
|
||||||
Match.exhaustive,
|
Hash.combine(Hash.hash(self.value)),
|
||||||
),
|
Hash.combine(Hash.hash(isRefreshing(self) ? self.progress : undefined)),
|
||||||
Hash.combine(Hash.hash((this as Flags)._flag)),
|
)),
|
||||||
hash => hasRefreshingFlag(this)
|
Match.tag("Failure", self => pipe(tagHash,
|
||||||
? Hash.combine(Hash.hash(this.progress))(hash)
|
Hash.combine(Hash.hash(self.cause)),
|
||||||
: hash,
|
Hash.combine(Hash.hash(isRefreshing(self) ? self.progress : undefined)),
|
||||||
|
)),
|
||||||
|
Match.exhaustive,
|
||||||
Hash.cached(this),
|
Hash.cached(this),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -99,58 +96,41 @@ const ResultPrototype = Object.freeze({
|
|||||||
|
|
||||||
|
|
||||||
export const isResult = (u: unknown): u is Result<unknown, unknown, unknown> => Predicate.hasProperty(u, ResultTypeId)
|
export const isResult = (u: unknown): u is Result<unknown, unknown, unknown> => Predicate.hasProperty(u, ResultTypeId)
|
||||||
export const isFinal = (u: unknown): u is Final<unknown, unknown, unknown> => isResult(u) && (isSuccess(u) || isFailure(u))
|
|
||||||
export const isInitial = (u: unknown): u is Initial => isResult(u) && u._tag === "Initial"
|
export const isInitial = (u: unknown): u is Initial => isResult(u) && u._tag === "Initial"
|
||||||
export const isRunning = (u: unknown): u is Running<unknown> => isResult(u) && u._tag === "Running"
|
export const isRunning = (u: unknown): u is Running<unknown> => isResult(u) && u._tag === "Running"
|
||||||
export const isSuccess = (u: unknown): u is Success<unknown> => isResult(u) && u._tag === "Success"
|
export const isSuccess = (u: unknown): u is Success<unknown> => isResult(u) && u._tag === "Success"
|
||||||
export const isFailure = (u: unknown): u is Failure<unknown> => isResult(u) && u._tag === "Failure"
|
export const isFailure = (u: unknown): u is Failure<unknown, unknown> => isResult(u) && u._tag === "Failure"
|
||||||
export const hasFlag = (u: unknown): u is Flags => isResult(u) && Predicate.hasProperty(u, "_flag")
|
export const isRefreshing = (u: unknown): u is Refreshing<unknown> => isResult(u) && Predicate.hasProperty(u, "refreshing") && u.refreshing
|
||||||
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<unknown> => isResult(u) && Predicate.hasProperty(u, "_flag") && u._flag === "Refreshing"
|
|
||||||
|
|
||||||
export const initial: {
|
export const initial = (): Initial => Object.setPrototypeOf({ _tag: "Initial" }, ResultPrototype)
|
||||||
(): Initial
|
|
||||||
<A, E = never, P = never>(): Result<A, E, P>
|
|
||||||
} = (): Initial => Object.setPrototypeOf({ _tag: "Initial" }, ResultPrototype)
|
|
||||||
export const running = <P = never>(progress?: P): Running<P> => Object.setPrototypeOf({ _tag: "Running", progress }, ResultPrototype)
|
export const running = <P = never>(progress?: P): Running<P> => Object.setPrototypeOf({ _tag: "Running", progress }, ResultPrototype)
|
||||||
export const succeed = <A>(value: A): Success<A> => Object.setPrototypeOf({ _tag: "Success", value }, ResultPrototype)
|
export const succeed = <A>(value: A): Success<A> => Object.setPrototypeOf({ _tag: "Success", value }, ResultPrototype)
|
||||||
export const fail = <E>(cause: Cause.Cause<E> ): Failure<E> => Object.setPrototypeOf({ _tag: "Failure", cause }, ResultPrototype)
|
|
||||||
|
|
||||||
export const willFetch = <R extends Final<any, any, any>>(
|
export const fail = <E, A = never>(
|
||||||
result: R
|
cause: Cause.Cause<E>,
|
||||||
): Omit<R, keyof Flags.Keys> & WillFetch => Object.setPrototypeOf(
|
previousSuccess?: Success<A>,
|
||||||
Object.assign({}, result, { _flag: "WillFetch" }),
|
): Failure<A, E> => Object.setPrototypeOf({
|
||||||
Object.getPrototypeOf(result),
|
_tag: "Failure",
|
||||||
)
|
cause,
|
||||||
|
previousSuccess: Option.fromNullable(previousSuccess),
|
||||||
export const willRefresh = <R extends Final<any, any, any>>(
|
}, ResultPrototype)
|
||||||
result: R
|
export const refreshing = <R extends Success<any> | Failure<any, any>, P = never>(
|
||||||
): Omit<R, keyof Flags.Keys> & WillRefresh => Object.setPrototypeOf(
|
|
||||||
Object.assign({}, result, { _flag: "WillRefresh" }),
|
|
||||||
Object.getPrototypeOf(result),
|
|
||||||
)
|
|
||||||
|
|
||||||
export const refreshing = <R extends Final<any, any, any>, P = never>(
|
|
||||||
result: R,
|
result: R,
|
||||||
progress?: P,
|
progress?: P,
|
||||||
): Omit<R, keyof Flags.Keys> & Refreshing<P> => Object.setPrototypeOf(
|
): Omit<R, keyof Refreshing<Result.Progress<R>>> & Refreshing<P> => Object.setPrototypeOf(
|
||||||
Object.assign({}, result, { _flag: "Refreshing", progress }),
|
Object.assign({}, result, { progress }),
|
||||||
Object.getPrototypeOf(result),
|
Object.getPrototypeOf(result),
|
||||||
)
|
)
|
||||||
|
|
||||||
export const fromExit: {
|
export const fromExit = <A, E>(
|
||||||
<A, E>(exit: Exit.Success<A, E>): Success<A>
|
exit: Exit.Exit<A, E>
|
||||||
<A, E>(exit: Exit.Failure<A, E>): Failure<E>
|
): Success<A> | Failure<A, E> => exit._tag === "Success"
|
||||||
<A, E>(exit: Exit.Exit<A, E>): Success<A> | Failure<E>
|
? succeed(exit.value)
|
||||||
} = exit => (exit._tag === "Success" ? succeed(exit.value) : fail(exit.cause)) as any
|
: fail(exit.cause)
|
||||||
|
|
||||||
export const toExit: {
|
export const toExit = <A, E, P>(
|
||||||
<A>(self: Success<A>): Exit.Success<A, never>
|
self: Result<A, E, P>
|
||||||
<E>(self: Failure<E>): Exit.Failure<never, E>
|
): Exit.Exit<A, E | Cause.NoSuchElementException> => {
|
||||||
<A, E, P>(self: Final<A, E, P>): Exit.Exit<A, E>
|
|
||||||
<A, E, P>(self: Result<A, E, P>): Exit.Exit<A, E | Cause.NoSuchElementException>
|
|
||||||
} = <A, E, P>(self: Result<A, E, P>): any => {
|
|
||||||
switch (self._tag) {
|
switch (self._tag) {
|
||||||
case "Success":
|
case "Success":
|
||||||
return Exit.succeed(self.value)
|
return Exit.succeed(self.value)
|
||||||
@@ -162,118 +142,123 @@ export const toExit: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface State<A, E = never, P = never> {
|
|
||||||
readonly get: Effect.Effect<Result<A, E, P>>
|
|
||||||
readonly set: (v: Result<A, E, P>) => Effect.Effect<void>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const State = <A, E = never, P = never>(): Context.Tag<State<A, E, P>, State<A, E, P>> => Context.GenericTag("@effect-fc/Result/State")
|
|
||||||
|
|
||||||
export interface Progress<P = never> {
|
export interface Progress<P = never> {
|
||||||
readonly update: <E, R>(
|
readonly update: <E, R>(
|
||||||
f: (previous: P) => Effect.Effect<P, E, R>
|
f: (previous: P) => Effect.Effect<P, E, R>
|
||||||
) => Effect.Effect<void, PreviousResultNotRunningNorRefreshing | E, R>
|
) => Effect.Effect<void, PreviousResultNotRunningOrRefreshing | E, R>
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PreviousResultNotRunningNorRefreshing extends Data.TaggedError("@effect-fc/Result/PreviousResultNotRunningNorRefreshing")<{
|
export class PreviousResultNotRunningOrRefreshing extends Data.TaggedError("@effect-fc/Result/PreviousResultNotRunningOrRefreshing")<{
|
||||||
readonly previous: Result<unknown, unknown, unknown>
|
readonly previous: Result<unknown, unknown, unknown>
|
||||||
}> {}
|
}> {}
|
||||||
|
|
||||||
export const Progress = <P = never>(): Context.Tag<Progress<P>, Progress<P>> => Context.GenericTag("@effect-fc/Result/Progress")
|
export const Progress = <P = never>(): Context.Tag<Progress<P>, Progress<P>> => Context.GenericTag("@effect-fc/Result/Progress")
|
||||||
|
|
||||||
export const makeProgressLayer = <A, E, P = never>(): Layer.Layer<
|
export const makeProgressLayer = <A, E, P = never>(
|
||||||
Progress<P>,
|
queue: Queue.Enqueue<Result<A, E, P>>,
|
||||||
never,
|
ref: Ref.Ref<Result<A, E, P>>,
|
||||||
State<A, E, P>
|
): Layer.Layer<Progress<P>> => Layer.sync(Progress<P>(), () => ({
|
||||||
> => Layer.effect(Progress<P>(), Effect.gen(function*() {
|
update: <E, R>(f: (previous: P) => Effect.Effect<P, E, R>) => Effect.Do.pipe(
|
||||||
const state = yield* State<A, E, P>()
|
Effect.bind("previous", () => Effect.andThen(
|
||||||
|
ref,
|
||||||
return {
|
previous => isRunning(previous) || isRefreshing(previous)
|
||||||
update: <FE, FR>(f: (previous: P) => Effect.Effect<P, FE, FR>) => Effect.Do.pipe(
|
? Effect.succeed(previous)
|
||||||
Effect.bind("previous", () => Effect.andThen(state.get, previous =>
|
: Effect.fail(new PreviousResultNotRunningOrRefreshing({ previous })),
|
||||||
(isRunning(previous) || hasRefreshingFlag(previous))
|
)),
|
||||||
? Effect.succeed(previous)
|
Effect.bind("progress", ({ previous }) => f(previous.progress)),
|
||||||
: Effect.fail(new PreviousResultNotRunningNorRefreshing({ previous })),
|
Effect.let("next", ({ previous, progress }) => Object.setPrototypeOf(
|
||||||
)),
|
Object.assign({}, previous, { progress }),
|
||||||
Effect.bind("progress", ({ previous }) => f(previous.progress)),
|
Object.getPrototypeOf(previous),
|
||||||
Effect.let("next", ({ previous, progress }) => isRunning(previous)
|
)),
|
||||||
? running(progress)
|
Effect.tap(({ next }) => Ref.set(ref, next)),
|
||||||
: refreshing(previous, progress) as Final<A, E, P> & Refreshing<P>
|
Effect.tap(({ next }) => Queue.offer(queue, next)),
|
||||||
),
|
Effect.asVoid,
|
||||||
Effect.andThen(({ next }) => state.set(next)),
|
),
|
||||||
),
|
|
||||||
}
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
||||||
export namespace unsafeForkEffect {
|
export namespace forkEffect {
|
||||||
export type OutputContext<A, E, R, P> = Exclude<R, State<A, E, P> | Progress<P> | Progress<never>>
|
export type InputContext<R, P> = R extends Progress<infer X> ? [X] extends [P] ? R : never : R
|
||||||
|
export type OutputContext<R> = Scope.Scope | Exclude<R, Progress<any> | Progress<never>>
|
||||||
|
|
||||||
export interface Options<A, E, P> {
|
export interface Options<P> {
|
||||||
readonly initial?: Initial | Final<A, E, P>
|
|
||||||
readonly initialProgress?: P
|
readonly initialProgress?: P
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const unsafeForkEffect = <A, E, R, P = never>(
|
export const forkEffect = <A, E, R, P = never>(
|
||||||
effect: Effect.Effect<A, E, R>,
|
effect: Effect.Effect<A, E, forkEffect.InputContext<R, NoInfer<P>>>,
|
||||||
options?: unsafeForkEffect.Options<NoInfer<A>, NoInfer<E>, P>,
|
options?: forkEffect.Options<P>,
|
||||||
): Effect.Effect<
|
): Effect.Effect<
|
||||||
readonly [result: Subscribable.Subscribable<Result<A, E, P>, never, never>, fiber: Fiber.Fiber<A, E>],
|
Queue.Dequeue<Result<A, E, P>>,
|
||||||
never,
|
never,
|
||||||
Scope.Scope | unsafeForkEffect.OutputContext<A, E, R, P>
|
forkEffect.OutputContext<R>
|
||||||
> => Effect.Do.pipe(
|
> => Effect.Do.pipe(
|
||||||
Effect.bind("ref", () => Ref.make(options?.initial ?? initial<A, E, P>())),
|
Effect.bind("scope", () => Scope.Scope),
|
||||||
Effect.bind("pubsub", () => PubSub.unbounded<Result<A, E, P>>()),
|
Effect.bind("queue", () => Queue.unbounded<Result<A, E, P>>()),
|
||||||
Effect.bind("fiber", ({ ref, pubsub }) => Effect.forkScoped(State<A, E, P>().pipe(
|
Effect.bind("ref", () => Ref.make<Result<A, E, P>>(initial())),
|
||||||
Effect.andThen(state => state.set(
|
Effect.tap(({ queue, ref }) => Effect.andThen(ref, v => Queue.offer(queue, v))),
|
||||||
(isFinal(options?.initial) && hasWillRefreshFlag(options?.initial))
|
Effect.tap(({ scope, queue, ref }) => Effect.forkScoped(
|
||||||
? refreshing(options.initial, options?.initialProgress) as Result<A, E, P>
|
Effect.addFinalizer(() => Queue.shutdown(queue)).pipe(
|
||||||
: running(options?.initialProgress)
|
Effect.andThen(Effect.succeed(running(options?.initialProgress)).pipe(
|
||||||
).pipe(
|
Effect.tap(v => Ref.set(ref, v)),
|
||||||
Effect.andThen(effect),
|
Effect.tap(v => Queue.offer(queue, v)),
|
||||||
Effect.onExit(exit => Effect.andThen(
|
|
||||||
state.set(fromExit(exit)),
|
|
||||||
Effect.forkScoped(PubSub.shutdown(pubsub)),
|
|
||||||
)),
|
)),
|
||||||
)),
|
Effect.andThen(Effect.provideService(effect, Scope.Scope, scope)),
|
||||||
Effect.provide(Layer.empty.pipe(
|
Effect.exit,
|
||||||
Layer.provideMerge(makeProgressLayer<A, E, P>()),
|
Effect.andThen(exit => Effect.succeed(fromExit(exit)).pipe(
|
||||||
Layer.provideMerge(Layer.succeed(State<A, E, P>(), {
|
Effect.tap(v => Ref.set(ref, v)),
|
||||||
get: Ref.get(ref),
|
Effect.tap(v => Queue.offer(queue, v)),
|
||||||
set: v => Effect.andThen(Ref.set(ref, v), PubSub.publish(pubsub, v))
|
|
||||||
})),
|
|
||||||
)),
|
|
||||||
))),
|
|
||||||
Effect.map(({ ref, pubsub, fiber }) => [
|
|
||||||
Subscribable.make({
|
|
||||||
get: Ref.get(ref),
|
|
||||||
changes: Stream.unwrapScoped(Effect.map(
|
|
||||||
Effect.all([Ref.get(ref), Stream.fromPubSub(pubsub, { scoped: true })]),
|
|
||||||
([latest, stream]) => Stream.concat(Stream.make(latest), stream),
|
|
||||||
)),
|
)),
|
||||||
}),
|
Effect.scoped,
|
||||||
fiber,
|
Effect.provide(makeProgressLayer(queue, ref)),
|
||||||
]),
|
)
|
||||||
) as Effect.Effect<
|
)),
|
||||||
readonly [result: Subscribable.Subscribable<Result<A, E, P>, never, never>, fiber: Fiber.Fiber<A, E>],
|
Effect.map(({ queue }) => queue),
|
||||||
never,
|
) as Effect.Effect<Queue.Queue<Result<A, E, P>>, never, Scope.Scope>
|
||||||
Scope.Scope | unsafeForkEffect.OutputContext<A, E, R, P>
|
|
||||||
>
|
|
||||||
|
|
||||||
export namespace forkEffect {
|
export namespace forkEffectScoped {
|
||||||
export type InputContext<R, P> = R extends Progress<infer X> ? [X] extends [P] ? R : never : R
|
export type InputContext<R, P> = (R extends Progress<infer X>
|
||||||
export type OutputContext<A, E, R, P> = unsafeForkEffect.OutputContext<A, E, R, P>
|
? [X] extends [P]
|
||||||
export interface Options<A, E, P> extends unsafeForkEffect.Options<A, E, P> {}
|
? R
|
||||||
|
: never
|
||||||
|
: R
|
||||||
|
)
|
||||||
|
|
||||||
|
export interface Options<P> {
|
||||||
|
readonly initialProgress?: P
|
||||||
|
}
|
||||||
|
|
||||||
|
export type OutputContext<R> = Scope.Scope | Exclude<R, Progress<any> | Progress<never>>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const forkEffect: {
|
export const forkEffectScoped = <A, E, R, P = never>(
|
||||||
<A, E, R, P = never>(
|
effect: Effect.Effect<A, E, forkEffectScoped.InputContext<R, NoInfer<P>>>,
|
||||||
effect: Effect.Effect<A, E, forkEffect.InputContext<R, NoInfer<P>>>,
|
options?: forkEffectScoped.Options<P>,
|
||||||
options?: forkEffect.Options<NoInfer<A>, NoInfer<E>, P>,
|
): Effect.Effect<
|
||||||
): Effect.Effect<
|
Queue.Dequeue<Result<A, E, P>>,
|
||||||
readonly [result: Subscribable.Subscribable<Result<A, E, P>, never, never>, fiber: Fiber.Fiber<A, E>],
|
never,
|
||||||
never,
|
forkEffectScoped.OutputContext<R>
|
||||||
Scope.Scope | forkEffect.OutputContext<A, E, R, P>
|
> => Effect.Do.pipe(
|
||||||
>
|
Effect.bind("scope", () => Scope.Scope),
|
||||||
} = unsafeForkEffect
|
Effect.bind("queue", () => Queue.unbounded<Result<A, E, P>>()),
|
||||||
|
Effect.bind("ref", () => Ref.make<Result<A, E, P>>(initial())),
|
||||||
|
Effect.tap(({ queue, ref }) => Effect.andThen(ref, v => Queue.offer(queue, v))),
|
||||||
|
Effect.tap(({ scope, queue, ref }) => Effect.forkScoped(
|
||||||
|
Effect.addFinalizer(() => Queue.shutdown(queue)).pipe(
|
||||||
|
Effect.andThen(Effect.succeed(running(options?.initialProgress)).pipe(
|
||||||
|
Effect.tap(v => Ref.set(ref, v)),
|
||||||
|
Effect.tap(v => Queue.offer(queue, v)),
|
||||||
|
)),
|
||||||
|
Effect.andThen(Effect.provideService(effect, Scope.Scope, scope)),
|
||||||
|
Effect.exit,
|
||||||
|
Effect.andThen(exit => Effect.succeed(fromExit(exit)).pipe(
|
||||||
|
Effect.tap(v => Ref.set(ref, v)),
|
||||||
|
Effect.tap(v => Queue.offer(queue, v)),
|
||||||
|
)),
|
||||||
|
Effect.scoped,
|
||||||
|
Effect.provide(makeProgressLayer(queue, ref)),
|
||||||
|
)
|
||||||
|
)),
|
||||||
|
Effect.map(({ queue }) => queue),
|
||||||
|
) as Effect.Effect<Queue.Queue<Result<A, E, P>>, never, Scope.Scope>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Effect, Equivalence, Option, Stream } from "effect"
|
import { Effect, Equivalence, Option, PubSub, Ref, type Scope, Stream } from "effect"
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as Component from "./Component.js"
|
import * as Component from "./Component.js"
|
||||||
|
|
||||||
@@ -30,4 +30,29 @@ export const useStream: {
|
|||||||
return reactStateValue as Option.Some<A>
|
return reactStateValue as Option.Some<A>
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const useStreamFromReactiveValues: {
|
||||||
|
<const A extends React.DependencyList>(
|
||||||
|
values: A
|
||||||
|
): Effect.Effect<Stream.Stream<A>, never, Scope.Scope>
|
||||||
|
} = Effect.fnUntraced(function* <const A extends React.DependencyList>(values: A) {
|
||||||
|
const { latest, pubsub, stream } = yield* Component.useOnMount(() => Effect.Do.pipe(
|
||||||
|
Effect.bind("latest", () => Ref.make(values)),
|
||||||
|
Effect.bind("pubsub", () => Effect.acquireRelease(PubSub.unbounded<A>(), PubSub.shutdown)),
|
||||||
|
Effect.let("stream", ({ latest, pubsub }) => latest.pipe(
|
||||||
|
Effect.flatMap(a => Effect.map(
|
||||||
|
Stream.fromPubSub(pubsub, { scoped: true }),
|
||||||
|
s => Stream.concat(Stream.make(a), s),
|
||||||
|
)),
|
||||||
|
Stream.unwrapScoped,
|
||||||
|
)),
|
||||||
|
))
|
||||||
|
|
||||||
|
yield* Component.useReactEffect(() => Ref.set(latest, values).pipe(
|
||||||
|
Effect.andThen(PubSub.publish(pubsub, values)),
|
||||||
|
Effect.unlessEffect(PubSub.isShutdown(pubsub)),
|
||||||
|
), values)
|
||||||
|
|
||||||
|
return stream
|
||||||
|
})
|
||||||
|
|
||||||
export * from "effect/Stream"
|
export * from "effect/Stream"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Effect, Equivalence, Stream, Subscribable } from "effect"
|
import { Effect, Equivalence, pipe, type Scope, Stream, Subscribable } from "effect"
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as Component from "./Component.js"
|
import * as Component from "./Component.js"
|
||||||
|
|
||||||
@@ -16,35 +16,30 @@ export const zipLatestAll = <const T extends readonly Subscribable.Subscribable<
|
|||||||
changes: Stream.zipLatestAll(...elements.map(v => v.changes)),
|
changes: Stream.zipLatestAll(...elements.map(v => v.changes)),
|
||||||
}) as any
|
}) as any
|
||||||
|
|
||||||
export declare namespace useSubscribables {
|
export const useSubscribables: {
|
||||||
export type Success<T extends readonly Subscribable.Subscribable<any, any, any>[]> = [T[number]] extends [never]
|
<const T extends readonly Subscribable.Subscribable<any, any, any>[]>(
|
||||||
? never
|
...elements: T
|
||||||
: { [K in keyof T]: T[K] extends Subscribable.Subscribable<infer A, infer _E, infer _R> ? A : never }
|
): Effect.Effect<
|
||||||
|
[T[number]] extends [never]
|
||||||
export interface Options<A> {
|
? never
|
||||||
readonly equivalence?: Equivalence.Equivalence<A>
|
: { [K in keyof T]: T[K] extends Subscribable.Subscribable<infer A, infer _E, infer _R> ? A : never },
|
||||||
}
|
[T[number]] extends [never] ? never : T[number] extends Subscribable.Subscribable<infer _A, infer E, infer _R> ? E : never,
|
||||||
}
|
([T[number]] extends [never] ? never : T[number] extends Subscribable.Subscribable<infer _A, infer _E, infer R> ? R : never) | Scope.Scope
|
||||||
|
>
|
||||||
export const useSubscribables = Effect.fnUntraced(function* <const T extends readonly Subscribable.Subscribable<any, any, any>[]>(
|
} = Effect.fnUntraced(function* <const T extends readonly Subscribable.Subscribable<any, any, any>[]>(
|
||||||
elements: T,
|
...elements: T
|
||||||
options?: useSubscribables.Options<useSubscribables.Success<NoInfer<T>>>,
|
) {
|
||||||
): Effect.fn.Return<
|
|
||||||
useSubscribables.Success<T>,
|
|
||||||
[T[number]] extends [never] ? never : T[number] extends Subscribable.Subscribable<infer _A, infer E, infer _R> ? E : never,
|
|
||||||
[T[number]] extends [never] ? never : T[number] extends Subscribable.Subscribable<infer _A, infer _E, infer R> ? R : never
|
|
||||||
> {
|
|
||||||
const [reactStateValue, setReactStateValue] = React.useState(
|
const [reactStateValue, setReactStateValue] = React.useState(
|
||||||
yield* Component.useOnMount(() => Effect.all(elements.map(v => v.get)))
|
yield* Component.useOnMount(() => Effect.all(elements.map(v => v.get)))
|
||||||
)
|
)
|
||||||
|
|
||||||
yield* Component.useReactEffect(() => Stream.zipLatestAll(...elements.map(ref => ref.changes)).pipe(
|
yield* Component.useReactEffect(() => Effect.forkScoped(pipe(
|
||||||
Stream.changesWith((options?.equivalence as Equivalence.Equivalence<any[]> | undefined) ?? Equivalence.array(Equivalence.strict())),
|
elements.map(ref => Stream.changesWith(ref.changes, Equivalence.strict())),
|
||||||
|
streams => Stream.zipLatestAll(...streams),
|
||||||
Stream.runForEach(v =>
|
Stream.runForEach(v =>
|
||||||
Effect.sync(() => setReactStateValue(v))
|
Effect.sync(() => setReactStateValue(v))
|
||||||
),
|
),
|
||||||
Effect.forkScoped,
|
)), elements)
|
||||||
), elements)
|
|
||||||
|
|
||||||
return reactStateValue as any
|
return reactStateValue as any
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,19 +1,24 @@
|
|||||||
import { Effect, Equivalence, Ref, Stream, SubscriptionRef } from "effect"
|
import { Effect, Equivalence, Ref, type Scope, Stream, SubscriptionRef } from "effect"
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as Component from "./Component.js"
|
import * as Component from "./Component.js"
|
||||||
import * as SetStateAction from "./SetStateAction.js"
|
import * as SetStateAction from "./SetStateAction.js"
|
||||||
|
|
||||||
|
|
||||||
export declare namespace useSubscriptionRefState {
|
export namespace useSubscriptionRefState {
|
||||||
export interface Options<A> {
|
export interface Options<A> {
|
||||||
readonly equivalence?: Equivalence.Equivalence<A>
|
readonly equivalence?: Equivalence.Equivalence<A>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useSubscriptionRefState = Effect.fnUntraced(function* <A>(
|
export const useSubscriptionRefState: {
|
||||||
|
<A>(
|
||||||
|
ref: SubscriptionRef.SubscriptionRef<A>,
|
||||||
|
options?: useSubscriptionRefState.Options<NoInfer<A>>,
|
||||||
|
): Effect.Effect<readonly [A, React.Dispatch<React.SetStateAction<A>>], never, Scope.Scope>
|
||||||
|
} = Effect.fnUntraced(function* <A>(
|
||||||
ref: SubscriptionRef.SubscriptionRef<A>,
|
ref: SubscriptionRef.SubscriptionRef<A>,
|
||||||
options?: useSubscriptionRefState.Options<NoInfer<A>>,
|
options?: useSubscriptionRefState.Options<NoInfer<A>>,
|
||||||
): Effect.fn.Return<readonly [A, React.Dispatch<React.SetStateAction<A>>]> {
|
) {
|
||||||
const [reactStateValue, setReactStateValue] = React.useState(yield* Component.useOnMount(() => ref))
|
const [reactStateValue, setReactStateValue] = React.useState(yield* Component.useOnMount(() => ref))
|
||||||
|
|
||||||
yield* Component.useReactEffect(() => Effect.forkScoped(
|
yield* Component.useReactEffect(() => Effect.forkScoped(
|
||||||
@@ -23,27 +28,28 @@ export const useSubscriptionRefState = Effect.fnUntraced(function* <A>(
|
|||||||
)
|
)
|
||||||
), [ref])
|
), [ref])
|
||||||
|
|
||||||
const setValue = yield* Component.useCallbackSync(
|
const setValue = yield* Component.useCallbackSync((setStateAction: React.SetStateAction<A>) =>
|
||||||
(setStateAction: React.SetStateAction<A>) => Effect.andThen(
|
Effect.andThen(
|
||||||
Ref.updateAndGet(ref, prevState => SetStateAction.value(setStateAction, prevState)),
|
Ref.updateAndGet(ref, prevState => SetStateAction.value(setStateAction, prevState)),
|
||||||
v => setReactStateValue(v),
|
v => setReactStateValue(v),
|
||||||
),
|
),
|
||||||
[ref],
|
[ref])
|
||||||
)
|
|
||||||
|
|
||||||
return [reactStateValue, setValue]
|
return [reactStateValue, setValue]
|
||||||
})
|
})
|
||||||
|
|
||||||
export declare namespace useSubscriptionRefFromState {
|
export namespace useSubscriptionRefFromState {
|
||||||
export interface Options<A> {
|
export interface Options<A> {
|
||||||
readonly equivalence?: Equivalence.Equivalence<A>
|
readonly equivalence?: Equivalence.Equivalence<A>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useSubscriptionRefFromState = Effect.fnUntraced(function* <A>(
|
export const useSubscriptionRefFromState: {
|
||||||
[value, setValue]: readonly [A, React.Dispatch<React.SetStateAction<A>>],
|
<A>(
|
||||||
options?: useSubscriptionRefFromState.Options<NoInfer<A>>,
|
state: readonly [A, React.Dispatch<React.SetStateAction<A>>],
|
||||||
): Effect.fn.Return<SubscriptionRef.SubscriptionRef<A>> {
|
options?: useSubscriptionRefFromState.Options<NoInfer<A>>,
|
||||||
|
): Effect.Effect<SubscriptionRef.SubscriptionRef<A>, never, Scope.Scope>
|
||||||
|
} = Effect.fnUntraced(function*([value, setValue], options) {
|
||||||
const ref = yield* Component.useOnChange(() => Effect.tap(
|
const ref = yield* Component.useOnChange(() => Effect.tap(
|
||||||
SubscriptionRef.make(value),
|
SubscriptionRef.make(value),
|
||||||
ref => Effect.forkScoped(
|
ref => Effect.forkScoped(
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
export * as Async from "./Async.js"
|
export * as Async from "./Async.js"
|
||||||
export * as Component from "./Component.js"
|
export * as Component from "./Component.js"
|
||||||
export * as ErrorObserver from "./ErrorObserver.js"
|
|
||||||
export * as Form from "./Form.js"
|
export * as Form from "./Form.js"
|
||||||
export * as Memoized from "./Memoized.js"
|
export * as Memoized from "./Memoized.js"
|
||||||
export * as Mutation from "./Mutation.js"
|
|
||||||
export * as PropertyPath from "./PropertyPath.js"
|
export * as PropertyPath from "./PropertyPath.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 ReactRuntime from "./ReactRuntime.js"
|
||||||
export * as Result from "./Result.js"
|
export * as Result from "./Result.js"
|
||||||
export * as SetStateAction from "./SetStateAction.js"
|
export * as SetStateAction from "./SetStateAction.js"
|
||||||
|
|||||||
@@ -13,30 +13,30 @@
|
|||||||
"clean:modules": "rm -rf node_modules"
|
"clean:modules": "rm -rf node_modules"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tanstack/react-router": "^1.154.12",
|
"@tanstack/react-router": "^1.132.31",
|
||||||
"@tanstack/react-router-devtools": "^1.154.12",
|
"@tanstack/react-router-devtools": "^1.132.31",
|
||||||
"@tanstack/router-plugin": "^1.154.12",
|
"@tanstack/router-plugin": "^1.132.31",
|
||||||
"@types/react": "^19.2.9",
|
"@types/react": "^19.2.0",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.0",
|
||||||
"@vitejs/plugin-react": "^6.0.0",
|
"@vitejs/plugin-react": "^5.0.4",
|
||||||
"globals": "^17.0.0",
|
"globals": "^16.4.0",
|
||||||
"react": "^19.2.3",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.3",
|
"react-dom": "^19.2.0",
|
||||||
"type-fest": "^5.4.1",
|
"type-fest": "^5.0.1",
|
||||||
"vite": "^7.3.1"
|
"vite": "^7.1.8"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@effect/platform": "^0.94.2",
|
"@effect/platform": "^0.92.1",
|
||||||
"@effect/platform-browser": "^0.74.0",
|
"@effect/platform-browser": "^0.72.0",
|
||||||
"@radix-ui/themes": "^3.2.1",
|
"@radix-ui/themes": "^3.2.1",
|
||||||
"@typed/id": "^0.17.2",
|
"@typed/id": "^0.17.2",
|
||||||
"effect": "^3.19.15",
|
"effect": "^3.18.1",
|
||||||
"effect-fc": "workspace:*",
|
"effect-fc": "workspace:*",
|
||||||
"react-icons": "^5.5.0"
|
"react-icons": "^5.5.0"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"@types/react": "^19.2.9",
|
"@types/react": "^19.2.0",
|
||||||
"effect": "^3.19.15",
|
"effect": "^3.18.1",
|
||||||
"react": "^19.2.3"
|
"react": "^19.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,21 +18,21 @@ extends Omit<TextField.RootProps, "optional" | "defaultValue">, Form.useOptional
|
|||||||
export type TextFieldFormInputProps = Props | OptionalProps
|
export type TextFieldFormInputProps = Props | OptionalProps
|
||||||
|
|
||||||
|
|
||||||
export class TextFieldFormInputView extends Component.make("TextFieldFormInputView")(function*(props: TextFieldFormInputProps) {
|
export class TextFieldFormInput extends Component.makeUntraced("TextFieldFormInput")(function*(props: TextFieldFormInputProps) {
|
||||||
const input: (
|
const input: (
|
||||||
| { readonly optional: true } & Form.useOptionalInput.Success<string>
|
| { readonly optional: true } & Form.useOptionalInput.Result<string>
|
||||||
| { readonly optional: false } & Form.useInput.Success<string>
|
| { readonly optional: false } & Form.useInput.Result<string>
|
||||||
) = props.optional
|
) = props.optional
|
||||||
// biome-ignore lint/correctness/useHookAtTopLevel: "optional" reactivity not supported
|
// biome-ignore lint/correctness/useHookAtTopLevel: "optional" reactivity not supported
|
||||||
? { optional: true, ...yield* Form.useOptionalInput(props.field, props) }
|
? { optional: true, ...yield* Form.useOptionalInput(props.field, props) }
|
||||||
// biome-ignore lint/correctness/useHookAtTopLevel: "optional" reactivity not supported
|
// biome-ignore lint/correctness/useHookAtTopLevel: "optional" reactivity not supported
|
||||||
: { optional: false, ...yield* Form.useInput(props.field, props) }
|
: { optional: false, ...yield* Form.useInput(props.field, props) }
|
||||||
|
|
||||||
const [issues, isValidating, isSubmitting] = yield* Subscribable.useSubscribables([
|
const [issues, isValidating, isSubmitting] = yield* Subscribable.useSubscribables(
|
||||||
props.field.issues,
|
props.field.issuesSubscribable,
|
||||||
props.field.isValidating,
|
props.field.isValidatingSubscribable,
|
||||||
props.field.isSubmitting,
|
props.field.isSubmittingSubscribable,
|
||||||
])
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction="column" gap="1">
|
<Flex direction="column" gap="1">
|
||||||
@@ -10,24 +10,18 @@
|
|||||||
|
|
||||||
import { Route as rootRouteImport } from './routes/__root'
|
import { Route as rootRouteImport } from './routes/__root'
|
||||||
import { Route as ResultRouteImport } from './routes/result'
|
import { Route as ResultRouteImport } from './routes/result'
|
||||||
import { Route as QueryRouteImport } from './routes/query'
|
|
||||||
import { Route as FormRouteImport } from './routes/form'
|
import { Route as FormRouteImport } from './routes/form'
|
||||||
import { Route as BlankRouteImport } from './routes/blank'
|
import { Route as BlankRouteImport } from './routes/blank'
|
||||||
import { Route as AsyncRouteImport } from './routes/async'
|
|
||||||
import { Route as IndexRouteImport } from './routes/index'
|
import { Route as IndexRouteImport } from './routes/index'
|
||||||
import { Route as DevMemoRouteImport } from './routes/dev/memo'
|
import { Route as DevMemoRouteImport } from './routes/dev/memo'
|
||||||
import { Route as DevContextRouteImport } from './routes/dev/context'
|
import { Route as DevContextRouteImport } from './routes/dev/context'
|
||||||
|
import { Route as DevAsyncRenderingRouteImport } from './routes/dev/async-rendering'
|
||||||
|
|
||||||
const ResultRoute = ResultRouteImport.update({
|
const ResultRoute = ResultRouteImport.update({
|
||||||
id: '/result',
|
id: '/result',
|
||||||
path: '/result',
|
path: '/result',
|
||||||
getParentRoute: () => rootRouteImport,
|
getParentRoute: () => rootRouteImport,
|
||||||
} as any)
|
} as any)
|
||||||
const QueryRoute = QueryRouteImport.update({
|
|
||||||
id: '/query',
|
|
||||||
path: '/query',
|
|
||||||
getParentRoute: () => rootRouteImport,
|
|
||||||
} as any)
|
|
||||||
const FormRoute = FormRouteImport.update({
|
const FormRoute = FormRouteImport.update({
|
||||||
id: '/form',
|
id: '/form',
|
||||||
path: '/form',
|
path: '/form',
|
||||||
@@ -38,11 +32,6 @@ const BlankRoute = BlankRouteImport.update({
|
|||||||
path: '/blank',
|
path: '/blank',
|
||||||
getParentRoute: () => rootRouteImport,
|
getParentRoute: () => rootRouteImport,
|
||||||
} as any)
|
} as any)
|
||||||
const AsyncRoute = AsyncRouteImport.update({
|
|
||||||
id: '/async',
|
|
||||||
path: '/async',
|
|
||||||
getParentRoute: () => rootRouteImport,
|
|
||||||
} as any)
|
|
||||||
const IndexRoute = IndexRouteImport.update({
|
const IndexRoute = IndexRouteImport.update({
|
||||||
id: '/',
|
id: '/',
|
||||||
path: '/',
|
path: '/',
|
||||||
@@ -58,35 +47,37 @@ const DevContextRoute = DevContextRouteImport.update({
|
|||||||
path: '/dev/context',
|
path: '/dev/context',
|
||||||
getParentRoute: () => rootRouteImport,
|
getParentRoute: () => rootRouteImport,
|
||||||
} as any)
|
} as any)
|
||||||
|
const DevAsyncRenderingRoute = DevAsyncRenderingRouteImport.update({
|
||||||
|
id: '/dev/async-rendering',
|
||||||
|
path: '/dev/async-rendering',
|
||||||
|
getParentRoute: () => rootRouteImport,
|
||||||
|
} as any)
|
||||||
|
|
||||||
export interface FileRoutesByFullPath {
|
export interface FileRoutesByFullPath {
|
||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
'/async': typeof AsyncRoute
|
|
||||||
'/blank': typeof BlankRoute
|
'/blank': typeof BlankRoute
|
||||||
'/form': typeof FormRoute
|
'/form': typeof FormRoute
|
||||||
'/query': typeof QueryRoute
|
|
||||||
'/result': typeof ResultRoute
|
'/result': typeof ResultRoute
|
||||||
|
'/dev/async-rendering': typeof DevAsyncRenderingRoute
|
||||||
'/dev/context': typeof DevContextRoute
|
'/dev/context': typeof DevContextRoute
|
||||||
'/dev/memo': typeof DevMemoRoute
|
'/dev/memo': typeof DevMemoRoute
|
||||||
}
|
}
|
||||||
export interface FileRoutesByTo {
|
export interface FileRoutesByTo {
|
||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
'/async': typeof AsyncRoute
|
|
||||||
'/blank': typeof BlankRoute
|
'/blank': typeof BlankRoute
|
||||||
'/form': typeof FormRoute
|
'/form': typeof FormRoute
|
||||||
'/query': typeof QueryRoute
|
|
||||||
'/result': typeof ResultRoute
|
'/result': typeof ResultRoute
|
||||||
|
'/dev/async-rendering': typeof DevAsyncRenderingRoute
|
||||||
'/dev/context': typeof DevContextRoute
|
'/dev/context': typeof DevContextRoute
|
||||||
'/dev/memo': typeof DevMemoRoute
|
'/dev/memo': typeof DevMemoRoute
|
||||||
}
|
}
|
||||||
export interface FileRoutesById {
|
export interface FileRoutesById {
|
||||||
__root__: typeof rootRouteImport
|
__root__: typeof rootRouteImport
|
||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
'/async': typeof AsyncRoute
|
|
||||||
'/blank': typeof BlankRoute
|
'/blank': typeof BlankRoute
|
||||||
'/form': typeof FormRoute
|
'/form': typeof FormRoute
|
||||||
'/query': typeof QueryRoute
|
|
||||||
'/result': typeof ResultRoute
|
'/result': typeof ResultRoute
|
||||||
|
'/dev/async-rendering': typeof DevAsyncRenderingRoute
|
||||||
'/dev/context': typeof DevContextRoute
|
'/dev/context': typeof DevContextRoute
|
||||||
'/dev/memo': typeof DevMemoRoute
|
'/dev/memo': typeof DevMemoRoute
|
||||||
}
|
}
|
||||||
@@ -94,42 +85,38 @@ export interface FileRouteTypes {
|
|||||||
fileRoutesByFullPath: FileRoutesByFullPath
|
fileRoutesByFullPath: FileRoutesByFullPath
|
||||||
fullPaths:
|
fullPaths:
|
||||||
| '/'
|
| '/'
|
||||||
| '/async'
|
|
||||||
| '/blank'
|
| '/blank'
|
||||||
| '/form'
|
| '/form'
|
||||||
| '/query'
|
|
||||||
| '/result'
|
| '/result'
|
||||||
|
| '/dev/async-rendering'
|
||||||
| '/dev/context'
|
| '/dev/context'
|
||||||
| '/dev/memo'
|
| '/dev/memo'
|
||||||
fileRoutesByTo: FileRoutesByTo
|
fileRoutesByTo: FileRoutesByTo
|
||||||
to:
|
to:
|
||||||
| '/'
|
| '/'
|
||||||
| '/async'
|
|
||||||
| '/blank'
|
| '/blank'
|
||||||
| '/form'
|
| '/form'
|
||||||
| '/query'
|
|
||||||
| '/result'
|
| '/result'
|
||||||
|
| '/dev/async-rendering'
|
||||||
| '/dev/context'
|
| '/dev/context'
|
||||||
| '/dev/memo'
|
| '/dev/memo'
|
||||||
id:
|
id:
|
||||||
| '__root__'
|
| '__root__'
|
||||||
| '/'
|
| '/'
|
||||||
| '/async'
|
|
||||||
| '/blank'
|
| '/blank'
|
||||||
| '/form'
|
| '/form'
|
||||||
| '/query'
|
|
||||||
| '/result'
|
| '/result'
|
||||||
|
| '/dev/async-rendering'
|
||||||
| '/dev/context'
|
| '/dev/context'
|
||||||
| '/dev/memo'
|
| '/dev/memo'
|
||||||
fileRoutesById: FileRoutesById
|
fileRoutesById: FileRoutesById
|
||||||
}
|
}
|
||||||
export interface RootRouteChildren {
|
export interface RootRouteChildren {
|
||||||
IndexRoute: typeof IndexRoute
|
IndexRoute: typeof IndexRoute
|
||||||
AsyncRoute: typeof AsyncRoute
|
|
||||||
BlankRoute: typeof BlankRoute
|
BlankRoute: typeof BlankRoute
|
||||||
FormRoute: typeof FormRoute
|
FormRoute: typeof FormRoute
|
||||||
QueryRoute: typeof QueryRoute
|
|
||||||
ResultRoute: typeof ResultRoute
|
ResultRoute: typeof ResultRoute
|
||||||
|
DevAsyncRenderingRoute: typeof DevAsyncRenderingRoute
|
||||||
DevContextRoute: typeof DevContextRoute
|
DevContextRoute: typeof DevContextRoute
|
||||||
DevMemoRoute: typeof DevMemoRoute
|
DevMemoRoute: typeof DevMemoRoute
|
||||||
}
|
}
|
||||||
@@ -143,13 +130,6 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof ResultRouteImport
|
preLoaderRoute: typeof ResultRouteImport
|
||||||
parentRoute: typeof rootRouteImport
|
parentRoute: typeof rootRouteImport
|
||||||
}
|
}
|
||||||
'/query': {
|
|
||||||
id: '/query'
|
|
||||||
path: '/query'
|
|
||||||
fullPath: '/query'
|
|
||||||
preLoaderRoute: typeof QueryRouteImport
|
|
||||||
parentRoute: typeof rootRouteImport
|
|
||||||
}
|
|
||||||
'/form': {
|
'/form': {
|
||||||
id: '/form'
|
id: '/form'
|
||||||
path: '/form'
|
path: '/form'
|
||||||
@@ -164,13 +144,6 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof BlankRouteImport
|
preLoaderRoute: typeof BlankRouteImport
|
||||||
parentRoute: typeof rootRouteImport
|
parentRoute: typeof rootRouteImport
|
||||||
}
|
}
|
||||||
'/async': {
|
|
||||||
id: '/async'
|
|
||||||
path: '/async'
|
|
||||||
fullPath: '/async'
|
|
||||||
preLoaderRoute: typeof AsyncRouteImport
|
|
||||||
parentRoute: typeof rootRouteImport
|
|
||||||
}
|
|
||||||
'/': {
|
'/': {
|
||||||
id: '/'
|
id: '/'
|
||||||
path: '/'
|
path: '/'
|
||||||
@@ -192,16 +165,22 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof DevContextRouteImport
|
preLoaderRoute: typeof DevContextRouteImport
|
||||||
parentRoute: typeof rootRouteImport
|
parentRoute: typeof rootRouteImport
|
||||||
}
|
}
|
||||||
|
'/dev/async-rendering': {
|
||||||
|
id: '/dev/async-rendering'
|
||||||
|
path: '/dev/async-rendering'
|
||||||
|
fullPath: '/dev/async-rendering'
|
||||||
|
preLoaderRoute: typeof DevAsyncRenderingRouteImport
|
||||||
|
parentRoute: typeof rootRouteImport
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const rootRouteChildren: RootRouteChildren = {
|
const rootRouteChildren: RootRouteChildren = {
|
||||||
IndexRoute: IndexRoute,
|
IndexRoute: IndexRoute,
|
||||||
AsyncRoute: AsyncRoute,
|
|
||||||
BlankRoute: BlankRoute,
|
BlankRoute: BlankRoute,
|
||||||
FormRoute: FormRoute,
|
FormRoute: FormRoute,
|
||||||
QueryRoute: QueryRoute,
|
|
||||||
ResultRoute: ResultRoute,
|
ResultRoute: ResultRoute,
|
||||||
|
DevAsyncRenderingRoute: DevAsyncRenderingRoute,
|
||||||
DevContextRoute: DevContextRoute,
|
DevContextRoute: DevContextRoute,
|
||||||
DevMemoRoute: DevMemoRoute,
|
DevMemoRoute: DevMemoRoute,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
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 (
|
|
||||||
<div>
|
|
||||||
<Heading>{post.title}</Heading>
|
|
||||||
<Text>{post.body}</Text>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}).pipe(
|
|
||||||
Async.async,
|
|
||||||
Async.withOptions({ defaultFallback: <Text>Default fallback</Text> }),
|
|
||||||
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 (
|
|
||||||
<Container>
|
|
||||||
<Flex direction="column" align="stretch" gap="2">
|
|
||||||
<TextField.Root
|
|
||||||
value={text}
|
|
||||||
onChange={e => setText(e.currentTarget.value)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Slider
|
|
||||||
value={[id]}
|
|
||||||
onValueChange={flow(Array.head, Option.getOrThrow, setId)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<AsyncFetchPost id={id} fallback={<Text>Loading post...</Text>} />
|
|
||||||
</Flex>
|
|
||||||
</Container>
|
|
||||||
)
|
|
||||||
}).pipe(
|
|
||||||
Component.withRuntime(runtime.context)
|
|
||||||
)
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/async")({
|
|
||||||
component: AsyncRouteComponent,
|
|
||||||
})
|
|
||||||
78
packages/example/src/routes/dev/async-rendering.tsx
Normal file
78
packages/example/src/routes/dev/async-rendering.tsx
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
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 { Async, Component, Memoized } from "effect-fc"
|
||||||
|
import * as React from "react"
|
||||||
|
import { runtime } from "@/runtime"
|
||||||
|
|
||||||
|
|
||||||
|
// Generator version
|
||||||
|
const RouteComponent = Component.makeUntraced(function* AsyncRendering() {
|
||||||
|
const MemoizedAsyncComponentFC = yield* MemoizedAsyncComponent
|
||||||
|
const AsyncComponentFC = yield* AsyncComponent
|
||||||
|
const [input, setInput] = React.useState("")
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex direction="column" align="stretch" gap="2">
|
||||||
|
<TextField.Root
|
||||||
|
value={input}
|
||||||
|
onChange={e => setInput(e.target.value)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<MemoizedAsyncComponentFC fallback={React.useMemo(() => <p>Loading memoized...</p>, [])} />
|
||||||
|
<AsyncComponentFC />
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}).pipe(
|
||||||
|
Component.withRuntime(runtime.context)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Pipeline version
|
||||||
|
// const RouteComponent = Component.make("RouteComponent")(() => Effect.Do,
|
||||||
|
// Effect.bind("VMemoizedAsyncComponent", () => Component.useFC(MemoizedAsyncComponent)),
|
||||||
|
// Effect.bind("VAsyncComponent", () => Component.useFC(AsyncComponent)),
|
||||||
|
// Effect.let("input", () => React.useState("")),
|
||||||
|
|
||||||
|
// Effect.map(({ input: [input, setInput], VAsyncComponent, VMemoizedAsyncComponent }) =>
|
||||||
|
// <Flex direction="column" align="stretch" gap="2">
|
||||||
|
// <TextField.Root
|
||||||
|
// value={input}
|
||||||
|
// onChange={e => setInput(e.target.value)}
|
||||||
|
// />
|
||||||
|
|
||||||
|
// <VMemoizedAsyncComponent />
|
||||||
|
// <VAsyncComponent />
|
||||||
|
// </Flex>
|
||||||
|
// ),
|
||||||
|
// ).pipe(
|
||||||
|
// Component.withRuntime(runtime.context)
|
||||||
|
// )
|
||||||
|
|
||||||
|
|
||||||
|
class AsyncComponent extends Component.makeUntraced("AsyncComponent")(function*() {
|
||||||
|
const SubComponentFC = yield* SubComponent
|
||||||
|
|
||||||
|
yield* Effect.sleep("500 millis") // Async operation
|
||||||
|
// Cannot use React hooks after the async operation
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex direction="column" align="stretch">
|
||||||
|
<Text>Rendered!</Text>
|
||||||
|
<SubComponentFC />
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}).pipe(
|
||||||
|
Async.async,
|
||||||
|
Async.withOptions({ defaultFallback: <p>Loading...</p> }),
|
||||||
|
) {}
|
||||||
|
class MemoizedAsyncComponent extends Memoized.memoized(AsyncComponent) {}
|
||||||
|
|
||||||
|
class SubComponent extends Component.makeUntraced("SubComponent")(function*() {
|
||||||
|
const [state] = React.useState(yield* Component.useOnMount(() => Effect.provide(makeUuid4, GetRandomValues.CryptoRandom)))
|
||||||
|
return <Text>{state}</Text>
|
||||||
|
}) {}
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/dev/async-rendering")({
|
||||||
|
component: RouteComponent
|
||||||
|
})
|
||||||
@@ -23,7 +23,7 @@ const SubComponent = Component.makeUntraced("SubComponent")(function*() {
|
|||||||
const ContextView = Component.makeUntraced("ContextView")(function*() {
|
const ContextView = Component.makeUntraced("ContextView")(function*() {
|
||||||
const [serviceValue, setServiceValue] = React.useState("test")
|
const [serviceValue, setServiceValue] = React.useState("test")
|
||||||
const SubServiceLayer = React.useMemo(() => SubService.Default(serviceValue), [serviceValue])
|
const SubServiceLayer = React.useMemo(() => SubService.Default(serviceValue), [serviceValue])
|
||||||
const SubComponentFC = yield* Effect.provide(SubComponent.use, yield* Component.useContextFromLayer(SubServiceLayer))
|
const SubComponentFC = yield* Effect.provide(SubComponent, yield* Component.useContext(SubServiceLayer))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ const RouteComponent = Component.makeUntraced("RouteComponent")(function*() {
|
|||||||
onChange={e => setValue(e.target.value)}
|
onChange={e => setValue(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{yield* Effect.map(SubComponent.use, FC => <FC />)}
|
{yield* Effect.map(SubComponent, FC => <FC />)}
|
||||||
{yield* Effect.map(MemoizedSubComponent.use, FC => <FC />)}
|
{yield* Effect.map(MemoizedSubComponent, FC => <FC />)}
|
||||||
</Flex>
|
</Flex>
|
||||||
)
|
)
|
||||||
}).pipe(
|
}).pipe(
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Button, Container, Flex, Text } from "@radix-ui/themes"
|
|||||||
import { createFileRoute } from "@tanstack/react-router"
|
import { createFileRoute } from "@tanstack/react-router"
|
||||||
import { Console, Effect, Match, Option, ParseResult, Schema } from "effect"
|
import { Console, Effect, Match, Option, ParseResult, Schema } from "effect"
|
||||||
import { Component, Form, Subscribable } from "effect-fc"
|
import { Component, Form, Subscribable } from "effect-fc"
|
||||||
import { TextFieldFormInputView } from "@/lib/form/TextFieldFormInputView"
|
import { TextFieldFormInput } from "@/lib/form/TextFieldFormInput"
|
||||||
import { DateTimeUtcFromZonedInput } from "@/lib/schema"
|
import { DateTimeUtcFromZonedInput } from "@/lib/schema"
|
||||||
import { runtime } from "@/runtime"
|
import { runtime } from "@/runtime"
|
||||||
|
|
||||||
@@ -23,22 +23,7 @@ const RegisterFormSchema = Schema.Struct({
|
|||||||
birth: Schema.OptionFromSelf(DateTimeUtcFromZonedInput),
|
birth: Schema.OptionFromSelf(DateTimeUtcFromZonedInput),
|
||||||
})
|
})
|
||||||
|
|
||||||
const RegisterFormSubmitSchema = Schema.Struct({
|
class RegisterForm extends Effect.Service<RegisterForm>()("RegisterForm", {
|
||||||
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>()("RegisterFormService", {
|
|
||||||
scoped: Form.service({
|
scoped: Form.service({
|
||||||
schema: RegisterFormSchema.pipe(
|
schema: RegisterFormSchema.pipe(
|
||||||
Schema.compose(
|
Schema.compose(
|
||||||
@@ -54,23 +39,23 @@ class RegisterFormService extends Effect.Service<RegisterFormService>()("Registe
|
|||||||
),
|
),
|
||||||
|
|
||||||
initialEncodedValue: { email: "", password: "", birth: Option.none() },
|
initialEncodedValue: { email: "", password: "", birth: Option.none() },
|
||||||
f: Effect.fnUntraced(function*([value]) {
|
onSubmit: v => Effect.sleep("500 millis").pipe(
|
||||||
yield* Effect.sleep("500 millis")
|
Effect.andThen(Console.log(v)),
|
||||||
return yield* Schema.decode(RegisterFormSubmitSchema)(value)
|
Effect.andThen(Effect.sync(() => alert("Done!"))),
|
||||||
}),
|
),
|
||||||
debounce: "500 millis",
|
debounce: "500 millis",
|
||||||
})
|
})
|
||||||
}) {}
|
}) {}
|
||||||
|
|
||||||
class RegisterFormView extends Component.make("RegisterFormView")(function*() {
|
class RegisterFormView extends Component.makeUntraced("RegisterFormView")(function*() {
|
||||||
const form = yield* RegisterFormService
|
const form = yield* RegisterForm
|
||||||
const [canSubmit, submitResult] = yield* Subscribable.useSubscribables([
|
const submit = yield* Form.useSubmit(form)
|
||||||
form.canSubmit,
|
const [canSubmit, submitResult] = yield* Subscribable.useSubscribables(
|
||||||
form.mutation.result,
|
form.canSubmitSubscribable,
|
||||||
])
|
form.submitResultRef,
|
||||||
|
)
|
||||||
|
|
||||||
const runPromise = yield* Component.useRunPromise()
|
const TextFieldFormInputFC = yield* TextFieldFormInput
|
||||||
const TextFieldFormInput = yield* TextFieldFormInputView.use
|
|
||||||
|
|
||||||
yield* Component.useOnMount(() => Effect.gen(function*() {
|
yield* Component.useOnMount(() => Effect.gen(function*() {
|
||||||
yield* Effect.addFinalizer(() => Console.log("RegisterFormView unmounted"))
|
yield* Effect.addFinalizer(() => Console.log("RegisterFormView unmounted"))
|
||||||
@@ -82,21 +67,21 @@ class RegisterFormView extends Component.make("RegisterFormView")(function*() {
|
|||||||
<Container width="300">
|
<Container width="300">
|
||||||
<form onSubmit={e => {
|
<form onSubmit={e => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
void runPromise(form.submit)
|
void submit()
|
||||||
}}>
|
}}>
|
||||||
<Flex direction="column" gap="2">
|
<Flex direction="column" gap="2">
|
||||||
<TextFieldFormInput
|
<TextFieldFormInputFC
|
||||||
field={yield* form.field(["email"])}
|
field={Form.useField(form, ["email"])}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextFieldFormInput
|
<TextFieldFormInputFC
|
||||||
field={yield* form.field(["password"])}
|
field={Form.useField(form, ["password"])}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextFieldFormInput
|
<TextFieldFormInputFC
|
||||||
optional
|
optional
|
||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
field={yield* form.field(["birth"])}
|
field={Form.useField(form, ["birth"])}
|
||||||
defaultValue=""
|
defaultValue=""
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -108,20 +93,20 @@ class RegisterFormView extends Component.make("RegisterFormView")(function*() {
|
|||||||
Match.tag("Initial", () => <></>),
|
Match.tag("Initial", () => <></>),
|
||||||
Match.tag("Running", () => <Text>Submitting...</Text>),
|
Match.tag("Running", () => <Text>Submitting...</Text>),
|
||||||
Match.tag("Success", () => <Text>Submitted successfully!</Text>),
|
Match.tag("Success", () => <Text>Submitted successfully!</Text>),
|
||||||
Match.tag("Failure", e => <Text>Error: {e.cause.toString()}</Text>),
|
Match.tag("Failure", v => <Text>Error: {v.cause.toString()}</Text>),
|
||||||
Match.exhaustive,
|
Match.exhaustive,
|
||||||
)}
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}) {}
|
}) {}
|
||||||
|
|
||||||
const RegisterPage = Component.make("RegisterPageView")(function*() {
|
const RegisterPage = Component.makeUntraced("RegisterPage")(function*() {
|
||||||
const RegisterForm = yield* Effect.provide(
|
const RegisterFormViewFC = yield* Effect.provide(
|
||||||
RegisterFormView.use,
|
RegisterFormView,
|
||||||
yield* Component.useContextFromLayer(RegisterFormService.Default),
|
yield* Component.useContext(RegisterForm.Default),
|
||||||
)
|
)
|
||||||
|
|
||||||
return <RegisterForm />
|
return <RegisterFormViewFC />
|
||||||
}).pipe(
|
}).pipe(
|
||||||
Component.withRuntime(runtime.context)
|
Component.withRuntime(runtime.context)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,19 +2,19 @@ import { createFileRoute } from "@tanstack/react-router"
|
|||||||
import { Effect } from "effect"
|
import { Effect } from "effect"
|
||||||
import { Component } from "effect-fc"
|
import { Component } from "effect-fc"
|
||||||
import { runtime } from "@/runtime"
|
import { runtime } from "@/runtime"
|
||||||
import { TodosState } from "@/todo/TodosState"
|
import { Todos } from "@/todo/Todos"
|
||||||
import { TodosView } from "@/todo/TodosView"
|
import { TodosState } from "@/todo/TodosState.service"
|
||||||
|
|
||||||
|
|
||||||
const TodosStateLive = TodosState.Default("todos")
|
const TodosStateLive = TodosState.Default("todos")
|
||||||
|
|
||||||
const Index = Component.make("IndexView")(function*() {
|
const Index = Component.makeUntraced("Index")(function*() {
|
||||||
const Todos = yield* Effect.provide(
|
const TodosFC = yield* Effect.provide(
|
||||||
TodosView.use,
|
Todos,
|
||||||
yield* Component.useContextFromLayer(TodosStateLive),
|
yield* Component.useContext(TodosStateLive),
|
||||||
)
|
)
|
||||||
|
|
||||||
return <Todos />
|
return <TodosFC />
|
||||||
}).pipe(
|
}).pipe(
|
||||||
Component.withRuntime(runtime.context)
|
Component.withRuntime(runtime.context)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,117 +0,0 @@
|
|||||||
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 } from "effect"
|
|
||||||
import { Component, ErrorObserver, Mutation, Query, Result, Subscribable, SubscriptionRef } 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 [idRef, query, mutation] = yield* Component.useOnMount(() => Effect.gen(function*() {
|
|
||||||
const idRef = yield* SubscriptionRef.make(1)
|
|
||||||
const key = Stream.map(idRef.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 [idRef, query, mutation] as const
|
|
||||||
}))
|
|
||||||
|
|
||||||
const [id, setId] = yield* SubscriptionRef.useSubscriptionRefState(idRef)
|
|
||||||
const [queryResult, mutationResult] = yield* Subscribable.useSubscribables([query.result, mutation.result])
|
|
||||||
|
|
||||||
yield* Component.useOnMount(() => ErrorObserver.ErrorObserver<HttpClientError.HttpClientError>().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 (
|
|
||||||
<Container>
|
|
||||||
<Flex direction="column" align="center" gap="2">
|
|
||||||
<Slider
|
|
||||||
value={[id]}
|
|
||||||
onValueChange={flow(Array.head, Option.getOrThrow, setId)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
{Match.value(queryResult).pipe(
|
|
||||||
Match.tag("Running", () => <Text>Loading...</Text>),
|
|
||||||
Match.tag("Success", result => <>
|
|
||||||
<Heading>{result.value.title}</Heading>
|
|
||||||
<Text>{result.value.body}</Text>
|
|
||||||
{Result.hasRefreshingFlag(result) && <Text>Refreshing...</Text>}
|
|
||||||
</>),
|
|
||||||
Match.tag("Failure", result =>
|
|
||||||
<Text>An error has occured: {result.cause.toString()}</Text>
|
|
||||||
),
|
|
||||||
Match.orElse(() => <></>),
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Flex direction="row" justify="center" align="center" gap="1">
|
|
||||||
<Button onClick={() => runPromise(query.refresh)}>Refresh</Button>
|
|
||||||
<Button onClick={() => runPromise(query.invalidateCache)}>Invalidate cache</Button>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
{Match.value(mutationResult).pipe(
|
|
||||||
Match.tag("Running", () => <Text>Loading...</Text>),
|
|
||||||
Match.tag("Success", result => <>
|
|
||||||
<Heading>{result.value.title}</Heading>
|
|
||||||
<Text>{result.value.body}</Text>
|
|
||||||
{Result.hasRefreshingFlag(result) && <Text>Refreshing...</Text>}
|
|
||||||
</>),
|
|
||||||
Match.tag("Failure", result =>
|
|
||||||
<Text>An error has occured: {result.cause.toString()}</Text>
|
|
||||||
),
|
|
||||||
Match.orElse(() => <></>),
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Flex direction="row" justify="center" align="center" gap="1">
|
|
||||||
<Button onClick={() => runPromise(Effect.andThen(idRef, id => mutation.mutate([id])))}>Mutate</Button>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Container>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/query")({
|
|
||||||
component: Component.withRuntime(ResultView, runtime.context)
|
|
||||||
})
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { HttpClient, type HttpClientError } from "@effect/platform"
|
import { HttpClient } from "@effect/platform"
|
||||||
import { Container, Heading, Text } from "@radix-ui/themes"
|
import { Container, Heading, Text } from "@radix-ui/themes"
|
||||||
import { createFileRoute } from "@tanstack/react-router"
|
import { createFileRoute } from "@tanstack/react-router"
|
||||||
import { Cause, Chunk, Console, Effect, flow, Match, Option, Schema, Stream } from "effect"
|
import { Effect, Match, Schema } from "effect"
|
||||||
import { Component, ErrorObserver, Result, Subscribable } from "effect-fc"
|
import { Component } from "effect-fc"
|
||||||
import { runtime } from "@/runtime"
|
import { runtime } from "@/runtime"
|
||||||
|
|
||||||
|
|
||||||
@@ -13,29 +13,11 @@ const Post = Schema.Struct({
|
|||||||
body: Schema.String,
|
body: Schema.String,
|
||||||
})
|
})
|
||||||
|
|
||||||
const ResultView = Component.makeUntraced("Result")(function*() {
|
const Result = Component.makeUntraced("Result")(function*() {
|
||||||
const [resultSubscribable] = yield* Component.useOnMount(() => HttpClient.HttpClient.pipe(
|
const result = yield* Component.useOnMountResult(() => HttpClient.HttpClient.pipe(
|
||||||
Effect.andThen(client => client.get("https://jsonplaceholder.typicode.com/posts/1")),
|
Effect.andThen(client => client.get("https://jsonplaceholder.typicode.com/posts/1")),
|
||||||
Effect.andThen(response => response.json),
|
|
||||||
Effect.andThen(Schema.decodeUnknown(Post)),
|
Effect.andThen(Schema.decodeUnknown(Post)),
|
||||||
Effect.tap(Effect.sleep("250 millis")),
|
Effect.tap(Effect.sleep("250 millis")),
|
||||||
Result.forkEffect,
|
|
||||||
))
|
|
||||||
const [result] = yield* Subscribable.useSubscribables([resultSubscribable])
|
|
||||||
|
|
||||||
yield* Component.useOnMount(() => ErrorObserver.ErrorObserver<HttpClientError.HttpClientError>().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 (
|
return (
|
||||||
@@ -53,8 +35,10 @@ const ResultView = Component.makeUntraced("Result")(function*() {
|
|||||||
)}
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
})
|
}).pipe(
|
||||||
|
Component.withRuntime(runtime.context)
|
||||||
|
)
|
||||||
|
|
||||||
export const Route = createFileRoute("/result")({
|
export const Route = createFileRoute("/result")({
|
||||||
component: Component.withRuntime(ResultView, runtime.context)
|
component: Result
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { Box, Button, Flex, IconButton } from "@radix-ui/themes"
|
import { Box, Button, Flex, IconButton } from "@radix-ui/themes"
|
||||||
import { GetRandomValues, makeUuid4 } from "@typed/id"
|
import { GetRandomValues, makeUuid4 } from "@typed/id"
|
||||||
import { Chunk, type DateTime, Effect, Match, Option, Ref, Schema, Stream } from "effect"
|
import { Chunk, Effect, Match, Option, Ref, Runtime, Schema, Stream } from "effect"
|
||||||
import { Component, Form, Subscribable } from "effect-fc"
|
import { Component, Form, Subscribable } from "effect-fc"
|
||||||
import { FaArrowDown, FaArrowUp } from "react-icons/fa"
|
import { FaArrowDown, FaArrowUp } from "react-icons/fa"
|
||||||
import { FaDeleteLeft } from "react-icons/fa6"
|
import { FaDeleteLeft } from "react-icons/fa6"
|
||||||
import * as Domain from "@/domain"
|
import * as Domain from "@/domain"
|
||||||
import { TextFieldFormInputView } from "@/lib/form/TextFieldFormInputView"
|
import { TextFieldFormInput } from "@/lib/form/TextFieldFormInput"
|
||||||
import { DateTimeUtcFromZonedInput } from "@/lib/schema"
|
import { DateTimeUtcFromZonedInput } from "@/lib/schema"
|
||||||
import { TodosState } from "./TodosState"
|
import { TodosState } from "./TodosState.service"
|
||||||
|
|
||||||
|
|
||||||
const TodoFormSchema = Schema.compose(Schema.Struct({
|
const TodoFormSchema = Schema.compose(Schema.Struct({
|
||||||
@@ -30,7 +30,8 @@ export type TodoProps = (
|
|||||||
| { readonly _tag: "edit", readonly id: string }
|
| { readonly _tag: "edit", readonly id: string }
|
||||||
)
|
)
|
||||||
|
|
||||||
export class TodoView extends Component.make("TodoView")(function*(props: TodoProps) {
|
export class Todo extends Component.makeUntraced("Todo")(function*(props: TodoProps) {
|
||||||
|
const runtime = yield* Effect.runtime()
|
||||||
const state = yield* TodosState
|
const state = yield* TodosState
|
||||||
|
|
||||||
const [
|
const [
|
||||||
@@ -54,15 +55,17 @@ export class TodoView extends Component.make("TodoView")(function*(props: TodoPr
|
|||||||
Match.exhaustive,
|
Match.exhaustive,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
f: ([todo, form]) => Match.value(props).pipe(
|
onSubmit: function(todo) {
|
||||||
Match.tag("new", () => Ref.update(state.ref, Chunk.prepend(todo)).pipe(
|
return Match.value(props).pipe(
|
||||||
Effect.andThen(makeTodo),
|
Match.tag("new", () => Ref.update(state.ref, Chunk.prepend(todo)).pipe(
|
||||||
Effect.andThen(Schema.encode(TodoFormSchema)),
|
Effect.andThen(makeTodo),
|
||||||
Effect.andThen(v => Ref.set(form.encodedValue, v)),
|
Effect.andThen(Schema.encode(TodoFormSchema)),
|
||||||
)),
|
Effect.andThen(v => Ref.set(this.encodedValueRef, v)),
|
||||||
Match.tag("edit", ({ id }) => Ref.set(state.getElementRef(id), todo)),
|
)),
|
||||||
Match.exhaustive,
|
Match.tag("edit", ({ id }) => Ref.set(state.getElementRef(id), todo)),
|
||||||
),
|
Match.exhaustive,
|
||||||
|
)
|
||||||
|
},
|
||||||
autosubmit: props._tag === "edit",
|
autosubmit: props._tag === "edit",
|
||||||
debounce: "250 millis",
|
debounce: "250 millis",
|
||||||
})
|
})
|
||||||
@@ -70,30 +73,28 @@ export class TodoView extends Component.make("TodoView")(function*(props: TodoPr
|
|||||||
return [
|
return [
|
||||||
indexRef,
|
indexRef,
|
||||||
form,
|
form,
|
||||||
yield* form.field(["content"]),
|
Form.field(form, ["content"]),
|
||||||
yield* form.field(["completedAt"]),
|
Form.field(form, ["completedAt"]),
|
||||||
] as const
|
] as const
|
||||||
}), [props._tag, props._tag === "edit" ? props.id : undefined])
|
}), [props._tag, props._tag === "edit" ? props.id : undefined])
|
||||||
|
|
||||||
const [index, size, canSubmit] = yield* Subscribable.useSubscribables([
|
const [index, size, canSubmit] = yield* Subscribable.useSubscribables(
|
||||||
indexRef,
|
indexRef,
|
||||||
state.sizeSubscribable,
|
state.sizeSubscribable,
|
||||||
form.canSubmit,
|
form.canSubmitSubscribable,
|
||||||
])
|
)
|
||||||
|
const submit = yield* Form.useSubmit(form)
|
||||||
const runSync = yield* Component.useRunSync()
|
const TextFieldFormInputFC = yield* TextFieldFormInput
|
||||||
const runPromise = yield* Component.useRunPromise<DateTime.CurrentTimeZone>()
|
|
||||||
const TextFieldFormInput = yield* TextFieldFormInputView.use
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction="row" align="center" gap="2">
|
<Flex direction="row" align="center" gap="2">
|
||||||
<Box flexGrow="1">
|
<Box flexGrow="1">
|
||||||
<Flex direction="column" align="stretch" gap="2">
|
<Flex direction="column" align="stretch" gap="2">
|
||||||
<TextFieldFormInput field={contentField} />
|
<TextFieldFormInputFC field={contentField} />
|
||||||
|
|
||||||
<Flex direction="row" justify="center" align="center" gap="2">
|
<Flex direction="row" justify="center" align="center" gap="2">
|
||||||
<TextFieldFormInput
|
<TextFieldFormInputFC
|
||||||
optional
|
optional
|
||||||
field={completedAtField}
|
field={completedAtField}
|
||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
@@ -101,7 +102,7 @@ export class TodoView extends Component.make("TodoView")(function*(props: TodoPr
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{props._tag === "new" &&
|
{props._tag === "new" &&
|
||||||
<Button disabled={!canSubmit} onClick={() => void runPromise(form.submit)}>
|
<Button disabled={!canSubmit} onClick={() => submit()}>
|
||||||
Add
|
Add
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
@@ -113,19 +114,19 @@ export class TodoView extends Component.make("TodoView")(function*(props: TodoPr
|
|||||||
<Flex direction="column" justify="center" align="center" gap="1">
|
<Flex direction="column" justify="center" align="center" gap="1">
|
||||||
<IconButton
|
<IconButton
|
||||||
disabled={index <= 0}
|
disabled={index <= 0}
|
||||||
onClick={() => runSync(state.moveLeft(props.id))}
|
onClick={() => Runtime.runSync(runtime)(state.moveLeft(props.id))}
|
||||||
>
|
>
|
||||||
<FaArrowUp />
|
<FaArrowUp />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|
||||||
<IconButton
|
<IconButton
|
||||||
disabled={index >= size - 1}
|
disabled={index >= size - 1}
|
||||||
onClick={() => runSync(state.moveRight(props.id))}
|
onClick={() => Runtime.runSync(runtime)(state.moveRight(props.id))}
|
||||||
>
|
>
|
||||||
<FaArrowDown />
|
<FaArrowDown />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|
||||||
<IconButton onClick={() => runSync(state.remove(props.id))}>
|
<IconButton onClick={() => Runtime.runSync(runtime)(state.remove(props.id))}>
|
||||||
<FaDeleteLeft />
|
<FaDeleteLeft />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Flex>
|
</Flex>
|
||||||
@@ -1,30 +1,30 @@
|
|||||||
import { Container, Flex, Heading } from "@radix-ui/themes"
|
import { Container, Flex, Heading } from "@radix-ui/themes"
|
||||||
import { Chunk, Console, Effect } from "effect"
|
import { Chunk, Console, Effect } from "effect"
|
||||||
import { Component, Subscribable } from "effect-fc"
|
import { Component, Subscribable } from "effect-fc"
|
||||||
import { TodosState } from "./TodosState"
|
import { Todo } from "./Todo"
|
||||||
import { TodoView } from "./TodoView"
|
import { TodosState } from "./TodosState.service"
|
||||||
|
|
||||||
|
|
||||||
export class TodosView extends Component.make("TodosView")(function*() {
|
export class Todos extends Component.makeUntraced("Todos")(function*() {
|
||||||
const state = yield* TodosState
|
const state = yield* TodosState
|
||||||
const [todos] = yield* Subscribable.useSubscribables([state.ref])
|
const [todos] = yield* Subscribable.useSubscribables(state.ref)
|
||||||
|
|
||||||
yield* Component.useOnMount(() => Effect.andThen(
|
yield* Component.useOnMount(() => Effect.andThen(
|
||||||
Console.log("Todos mounted"),
|
Console.log("Todos mounted"),
|
||||||
Effect.addFinalizer(() => Console.log("Todos unmounted")),
|
Effect.addFinalizer(() => Console.log("Todos unmounted")),
|
||||||
))
|
))
|
||||||
|
|
||||||
const Todo = yield* TodoView.use
|
const TodoFC = yield* Todo
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Heading align="center">Todos</Heading>
|
<Heading align="center">Todos</Heading>
|
||||||
|
|
||||||
<Flex direction="column" align="stretch" gap="2" mt="2">
|
<Flex direction="column" align="stretch" gap="2" mt="2">
|
||||||
<Todo _tag="new" />
|
<TodoFC _tag="new" />
|
||||||
|
|
||||||
{Chunk.map(todos, todo =>
|
{Chunk.map(todos, todo =>
|
||||||
<Todo key={todo.id} _tag="edit" id={todo.id} />
|
<TodoFC key={todo.id} _tag="edit" id={todo.id} />
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Container>
|
</Container>
|
||||||
Reference in New Issue
Block a user