Compare commits
305 Commits
master
...
27ca5e643a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27ca5e643a | ||
|
|
a616e84079 | ||
|
|
c832c3f79a | ||
|
|
6f65574ebd | ||
|
|
9ccabbb627 | ||
|
|
5f4087aa40 | ||
|
|
85b41bda9f | ||
|
|
e5a7fe8ad6 | ||
|
|
59b7115d19 | ||
|
|
08af31f0b9 | ||
|
|
44fc6bbbc4 | ||
|
|
904b725753 | ||
|
|
73dd7bc160 | ||
|
|
6bc07d5b2a | ||
|
|
70e9b9218d | ||
|
|
31b07f842b | ||
|
|
10f23d4cb4 | ||
|
|
39765102db | ||
|
|
04e78e1ea3 | ||
|
|
606dd2c00f | ||
|
|
c13a8d549f | ||
|
|
4b9bfd0637 | ||
|
|
53fc1ef505 | ||
|
|
c8b675d93e | ||
|
|
882ec9591c | ||
|
|
5b3637afd8 | ||
|
|
d6256a7cfd | ||
|
|
cf6c84ff8e | ||
|
|
198a7cee03 | ||
|
|
032f283ac8 | ||
|
|
c34629e20d | ||
|
|
284a080f19 | ||
|
|
87d27dd48d | ||
|
|
24853561f1 | ||
|
|
1902ad373f | ||
|
|
aa6c4a8008 | ||
|
|
d5ac84b2cc | ||
|
|
3c604abcef | ||
|
|
ba99309877 | ||
|
|
db3cd05851 | ||
|
|
dce81be269 | ||
|
|
3980c10747 | ||
|
|
43a3793dbf | ||
|
|
da7044ee9f | ||
|
|
ff5503cfd1 | ||
|
|
dc2cfb35e0 | ||
|
|
1228c51694 | ||
|
|
076007ec67 | ||
|
|
dd524e1aa5 | ||
|
|
1c7cef703b | ||
|
|
fa0f8c6b24 | ||
|
|
357e5aa56b | ||
|
|
ea374d7e0f | ||
|
|
148c98acbd | ||
|
|
39d2176c61 | ||
|
|
107ff1e794 | ||
|
|
a70ef27f75 | ||
|
|
04b2fad038 | ||
|
|
691b28427d | ||
|
|
1de976aaa8 | ||
|
|
df851cf9ee | ||
|
|
459f548c10 | ||
|
|
6156baec4d | ||
|
|
1163b83929 | ||
|
|
8917f84952 | ||
|
|
58752253b3 | ||
|
|
ba362baf04 | ||
|
|
33cf4fbcbd | ||
|
|
e8f92c88b8 | ||
|
|
6ae155de34 | ||
|
|
db783f174e | ||
|
|
2b48695e54 | ||
|
|
ab441fe982 | ||
|
|
eabcf9085b | ||
|
|
926482b154 | ||
|
|
110b0813f8 | ||
|
|
974af95a22 | ||
|
|
d6e1d445e8 | ||
|
|
d8d6e87a12 | ||
|
|
682e473bf7 | ||
|
|
31dd7b5fdb | ||
|
|
17686e68c3 | ||
|
|
49d4bd4d43 | ||
|
|
be88035936 | ||
|
|
3497d17046 | ||
|
|
1ca832e69d | ||
|
|
98bd72d1d7 | ||
|
|
f594f47793 | ||
|
|
4f9827720c | ||
|
|
0f761524fd | ||
|
|
574136e161 | ||
|
|
7a12abdbdf | ||
|
|
8fecb94292 | ||
|
|
26a2111705 | ||
|
|
1cb02407c8 | ||
|
|
6e8ce84851 | ||
|
|
570fb93876 | ||
|
|
821fd18f8f | ||
|
|
b7ef95341b | ||
|
|
5f5ef5614b | ||
|
|
cbd39f893e | ||
|
|
529e3d3f9d | ||
|
|
9d47418a69 | ||
|
|
c1b6e73231 | ||
|
|
d1ba4148f2 | ||
|
|
ef13e87d12 | ||
|
|
8b141b907f | ||
|
|
52a36cb882 | ||
|
|
3b844f071b | ||
|
|
4e422a1901 | ||
|
|
a5c6b34dfe | ||
|
|
ab1f851428 | ||
|
|
3f091d55c2 | ||
|
|
76a33fccca | ||
|
|
c75bb10e6b | ||
|
|
3da4b2a318 | ||
|
|
9a24ecaf84 | ||
|
|
7b20df6c71 | ||
|
|
f40dae90fb | ||
|
|
46211638f5 | ||
|
|
a28d6c3d30 | ||
|
|
6b74b9a3b2 | ||
|
|
e17f945666 | ||
|
|
aa46ecc82d | ||
|
|
8ea9146dd9 | ||
|
|
0a4bb2856d | ||
|
|
b4cd7daa81 | ||
|
|
b5712d5433 | ||
|
|
57b7eac05c | ||
|
|
9a9bd78ec6 | ||
|
|
ddcd681ca4 | ||
|
|
66de517ab5 | ||
|
|
b50255ded2 | ||
|
|
03f0b623ed | ||
|
|
fb6d803723 | ||
|
|
972986241c | ||
|
|
9eb0904600 | ||
|
|
fc86c818e0 | ||
|
|
5a12139602 | ||
|
|
a0928c718f | ||
|
|
49d9edd4b1 | ||
|
|
3552c25b5c | ||
|
|
516e0a465d | ||
|
|
7cf5367409 | ||
|
|
3b237c0588 | ||
|
|
d9aa42d23a | ||
|
|
fd3213c53f | ||
|
|
baa8c92221 | ||
|
|
d55b432846 | ||
|
|
6266c7506e | ||
|
|
043e966e45 | ||
|
|
88fab2c7d7 | ||
|
|
224ccd8e32 | ||
|
|
4cf70ada0b | ||
|
|
f9bd5d4d6b | ||
|
|
1ec1db0658 | ||
|
|
2d94e84941 | ||
|
|
aab83907ba | ||
|
|
8c0d6b4c8a | ||
|
|
d82d1d1c29 | ||
|
|
0f09573948 | ||
|
|
2b6b36713e | ||
|
|
5d0aecc9d5 | ||
|
|
f21d8b2d8a | ||
|
|
f85173fa68 | ||
|
|
65a124de1f | ||
|
|
16893761c6 | ||
|
|
3fdc2e31eb | ||
|
|
8636a28f2f | ||
|
|
d56578da8f | ||
|
|
299109d421 | ||
|
|
4995b2949f | ||
|
|
6e6e675709 | ||
|
|
b04860aa25 | ||
|
|
e9e17ac211 | ||
|
|
1f0ff725ff | ||
|
|
447d89982c | ||
|
|
778ee27795 | ||
|
|
077816efb6 | ||
|
|
e4bacd1ca7 | ||
|
|
0e2c0db28f | ||
|
|
c943d81702 | ||
|
|
c2bc406a5f | ||
|
|
4e778b6c95 | ||
|
|
0437fa5dcc | ||
|
|
5614b8df38 | ||
|
|
70b6c4434e | ||
|
|
2e8dfbc988 | ||
|
|
abc47c4647 | ||
|
|
eedd2a7f2a | ||
|
|
f4ab575a8d | ||
|
|
747e2c6056 | ||
|
|
68c68417d8 | ||
|
|
ed384a62a8 | ||
|
|
3a1748bb39 | ||
|
|
66b8fd2c2e | ||
|
|
bc81c443ab | ||
|
|
ee5dbe3766 | ||
|
|
825de84cef | ||
|
|
d6011f7897 | ||
|
|
8d4bce9e53 | ||
|
|
f7dd4e51f5 | ||
|
|
8772e25ff5 | ||
|
|
94a0864132 | ||
|
|
be8098fb7d | ||
|
|
7021e604ed | ||
|
|
1fd2a9ffbe | ||
|
|
1ed73dc3ac | ||
|
|
c689778cea | ||
|
|
da2a32001c | ||
|
|
5ac3a932d9 | ||
|
|
7935293bc3 | ||
|
|
cabceaffcd | ||
|
|
d239a11cdc | ||
|
|
fad61afce7 | ||
|
|
11fd4941c0 | ||
|
|
7bebc39a87 | ||
|
|
3bc0cc6586 | ||
|
|
f99d18b846 | ||
|
|
d61339ea6a | ||
|
|
3659d3f342 | ||
|
|
1e8a5d412f | ||
|
|
86539f33f0 | ||
|
|
8fa24b1791 | ||
|
|
adaadf13b2 | ||
|
|
3af7c3bf7a | ||
|
|
00b7228073 | ||
|
|
c2b2b1b96e | ||
|
|
74cf37e3a3 | ||
|
|
98091d4598 | ||
|
|
b2f1626268 | ||
|
|
40e8bf6a1f | ||
|
|
9c96741c8e | ||
|
|
3fa9b7d821 | ||
|
|
6b0f2f33cb | ||
|
|
2e00db5778 | ||
|
|
660f32a171 | ||
|
|
3f2639fda1 | ||
|
|
f76b3f333a | ||
|
|
3b407c6b4f | ||
|
|
b01b95a9d5 | ||
|
|
91b95ea6af | ||
|
|
7c99d1ff3d | ||
|
|
ae815553f2 | ||
|
|
86a96cbcce | ||
|
|
538b3a415d | ||
|
|
5b023678f4 | ||
|
|
9266697aa4 | ||
|
|
ad81bf9ed8 | ||
|
|
e92087e593 | ||
|
|
e182e6ab5c | ||
|
|
89175be558 | ||
|
|
4df90a0f1c | ||
|
|
693c7b2db8 | ||
|
|
5f60d03d83 | ||
|
|
ea768218a0 | ||
|
|
3b4eb750ed | ||
|
|
47aa130486 | ||
|
|
02da3df8eb | ||
|
|
8d276d2fbf | ||
|
|
af077d34aa | ||
|
|
618cee4028 | ||
|
|
8244c34d2a | ||
|
|
523d835d00 | ||
|
|
15e96b8fa9 | ||
|
|
44de864713 | ||
|
|
8e1f0a27cf | ||
|
|
8754020323 | ||
|
|
d9a01dae0f | ||
|
|
8873e81f7c | ||
|
|
38fcafb15c | ||
|
|
411397c7de | ||
|
|
85e7b54962 | ||
|
|
ce3989ab77 | ||
|
|
da0f6168f0 | ||
|
|
690dec1f1a | ||
|
|
60274266da | ||
|
|
28424b63cb | ||
|
|
e063eb06f7 | ||
|
|
fb5bb7fcef | ||
|
|
1f57f7d127 | ||
|
|
e8742e5aa6 | ||
|
|
be79d24d6e | ||
|
|
e1349e5e03 | ||
|
|
837dcbb1cb | ||
|
|
8252b6cbdf | ||
|
|
256638bc06 | ||
|
|
c0097bbe81 | ||
|
|
febeaa05d0 | ||
|
|
a71640d493 | ||
|
|
b636a709f3 | ||
|
|
fffbd01b5e | ||
|
|
36d5414d10 | ||
|
|
65810a6d79 | ||
|
|
9e7b30fbb4 | ||
|
|
6c843562ab | ||
|
|
809f512d11 | ||
|
|
e71239b903 | ||
|
|
bfcc097882 | ||
|
|
933b061b5d | ||
|
|
734c84824c | ||
|
|
e83e86f8f1 | ||
|
|
bebbc1d7de | ||
|
|
a7a0951b61 | ||
|
|
1b1a1961bc |
92
bun.lock
92
bun.lock
@@ -14,27 +14,27 @@
|
|||||||
"name": "@reffuse/example",
|
"name": "@reffuse/example",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@effect/platform": "^0.82.1",
|
"@effect/platform": "^0.80.21",
|
||||||
"@effect/platform-browser": "^0.62.1",
|
"@effect/platform-browser": "^0.60.12",
|
||||||
"@radix-ui/themes": "^3.2.1",
|
"@radix-ui/themes": "^3.2.1",
|
||||||
"@reffuse/extension-lazyref": "workspace:*",
|
"@reffuse/extension-lazyref": "workspace:*",
|
||||||
"@reffuse/extension-query": "workspace:*",
|
"@reffuse/extension-query": "workspace:*",
|
||||||
"@typed/async-data": "^0.13.1",
|
"@typed/async-data": "^0.13.1",
|
||||||
"@typed/id": "^0.17.2",
|
"@typed/id": "^0.17.2",
|
||||||
"@typed/lazy-ref": "^0.3.3",
|
"@typed/lazy-ref": "^0.3.3",
|
||||||
"effect": "^3.15.1",
|
"effect": "^3.14.21",
|
||||||
"lucide-react": "^0.510.0",
|
"lucide-react": "^0.508.0",
|
||||||
"mobx": "^6.13.7",
|
"mobx": "^6.13.7",
|
||||||
"reffuse": "workspace:*",
|
"reffuse": "workspace:*",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.26.0",
|
"@eslint/js": "^9.26.0",
|
||||||
"@tanstack/react-router": "^1.120.3",
|
"@tanstack/react-router": "^1.120.2",
|
||||||
"@tanstack/react-router-devtools": "^1.120.3",
|
"@tanstack/react-router-devtools": "^1.120.2",
|
||||||
"@tanstack/router-plugin": "^1.120.3",
|
"@tanstack/router-plugin": "^1.120.2",
|
||||||
"@thilawyn/thilaschema": "^0.1.4",
|
"@thilawyn/thilaschema": "^0.1.4",
|
||||||
"@types/react": "^19.1.4",
|
"@types/react": "^19.1.3",
|
||||||
"@types/react-dom": "^19.1.5",
|
"@types/react-dom": "^19.1.3",
|
||||||
"@vitejs/plugin-react": "^4.4.1",
|
"@vitejs/plugin-react": "^4.4.1",
|
||||||
"eslint": "^9.26.0",
|
"eslint": "^9.26.0",
|
||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
"globals": "^16.1.0",
|
"globals": "^16.1.0",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
"typescript-eslint": "^8.32.1",
|
"typescript-eslint": "^8.32.0",
|
||||||
"vite": "^6.3.5",
|
"vite": "^6.3.5",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@typed/lazy-ref": "^0.3.0",
|
"@typed/lazy-ref": "^0.3.0",
|
||||||
"@types/react": "^19.0.0",
|
"@types/react": "^19.0.0",
|
||||||
"effect": "^3.15.0",
|
"effect": "^3.13.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"reffuse": "^0.1.8",
|
"reffuse": "^0.1.8",
|
||||||
},
|
},
|
||||||
@@ -71,17 +71,17 @@
|
|||||||
"@effect/platform-browser": "^0.56.0",
|
"@effect/platform-browser": "^0.56.0",
|
||||||
"@typed/async-data": "^0.13.0",
|
"@typed/async-data": "^0.13.0",
|
||||||
"@types/react": "^19.0.0",
|
"@types/react": "^19.0.0",
|
||||||
"effect": "^3.15.0",
|
"effect": "^3.13.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"reffuse": "^0.1.6",
|
"reffuse": "^0.1.6",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"packages/reffuse": {
|
"packages/reffuse": {
|
||||||
"name": "reffuse",
|
"name": "reffuse",
|
||||||
"version": "0.1.11",
|
"version": "0.1.9",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^19.0.0",
|
"@types/react": "^19.0.0",
|
||||||
"effect": "^3.15.0",
|
"effect": "^3.13.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -129,9 +129,9 @@
|
|||||||
|
|
||||||
"@babel/types": ["@babel/types@7.27.1", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q=="],
|
"@babel/types": ["@babel/types@7.27.1", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q=="],
|
||||||
|
|
||||||
"@effect/platform": ["@effect/platform@0.82.1", "", { "dependencies": { "find-my-way-ts": "^0.1.5", "msgpackr": "^1.11.2", "multipasta": "^0.2.5" }, "peerDependencies": { "effect": "^3.15.1" } }, "sha512-fX5Lu//VkLXPegouxT1AdSyuRkxF55k70YaLV0vIzjgK97/u3Mow0ux8fYglm2dWDXWTLBkNprlhheGm/5/bvQ=="],
|
"@effect/platform": ["@effect/platform@0.80.21", "", { "dependencies": { "find-my-way-ts": "^0.1.5", "msgpackr": "^1.11.2", "multipasta": "^0.2.5" }, "peerDependencies": { "effect": "^3.14.21" } }, "sha512-c6pDar/1TYqeFc8Z6uxNC+065ElSJ4A0AuACZi0CT1riLAfGLCFIpejonkJ1tz6rsBTDyBm0JPdyb0aqyGAYAA=="],
|
||||||
|
|
||||||
"@effect/platform-browser": ["@effect/platform-browser@0.62.1", "", { "dependencies": { "multipasta": "^0.2.5" }, "peerDependencies": { "@effect/platform": "^0.82.1", "effect": "^3.15.1" } }, "sha512-+aioMY5OsD9SQc7S88yv6tlWpkKhbA5Dv3lDs4CXQbRL5TWuHjzzDGpFNRhCBdv5ouAjoBAzu2Zi4+HIaWYqHQ=="],
|
"@effect/platform-browser": ["@effect/platform-browser@0.60.12", "", { "dependencies": { "multipasta": "^0.2.5" }, "peerDependencies": { "@effect/platform": "^0.80.21", "effect": "^3.14.21" } }, "sha512-4E+6SvGwSa3kIwuQNwk44vg7vZoZz7Q9d8VFvldz8zzv3+XZYMAhy+qaFQWmrqMoRP4n10xBE7MJJ2rIEtZYPw=="],
|
||||||
|
|
||||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q=="],
|
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q=="],
|
||||||
|
|
||||||
@@ -227,7 +227,7 @@
|
|||||||
|
|
||||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
|
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
|
||||||
|
|
||||||
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.11.2", "", { "dependencies": { "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.3", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-H9vwztj5OAqHg9GockCQC06k1natgcxWQSRpQcPJf6i5+MWBzfKkRtxGbjQf0X2ihii0ffLZCRGbYV2f2bjNCQ=="],
|
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.11.1", "", { "dependencies": { "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.3", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-9LfmxKTb1v+vUS1/emSk1f5ePmTLkb9Le9AxOB5T0XM59EUumwcS45z05h7aiZx3GI0Bl7mjb3FMEglYj+acuQ=="],
|
||||||
|
|
||||||
"@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="],
|
"@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="],
|
||||||
|
|
||||||
@@ -421,19 +421,19 @@
|
|||||||
|
|
||||||
"@tanstack/history": ["@tanstack/history@1.115.0", "", {}, "sha512-K7JJNrRVvyjAVnbXOH2XLRhFXDkeP54Kt2P4FR1Kl2KDGlIbkua5VqZQD2rot3qaDrpufyUa63nuLai1kOLTsQ=="],
|
"@tanstack/history": ["@tanstack/history@1.115.0", "", {}, "sha512-K7JJNrRVvyjAVnbXOH2XLRhFXDkeP54Kt2P4FR1Kl2KDGlIbkua5VqZQD2rot3qaDrpufyUa63nuLai1kOLTsQ=="],
|
||||||
|
|
||||||
"@tanstack/react-router": ["@tanstack/react-router@1.120.3", "", { "dependencies": { "@tanstack/history": "1.115.0", "@tanstack/react-store": "^0.7.0", "@tanstack/router-core": "1.120.3", "jsesc": "^3.1.0", "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-+5Y5ORtjW/LJhIxOxxBrvhZzfMOP9B+LaJ1j1P5tM5YqpLYWHuImfzGNRpKtBgiRoSaJedPjwY7lj88EwNWVbg=="],
|
"@tanstack/react-router": ["@tanstack/react-router@1.120.2", "", { "dependencies": { "@tanstack/history": "1.115.0", "@tanstack/react-store": "^0.7.0", "@tanstack/router-core": "1.119.0", "jsesc": "^3.1.0", "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-CNduh/O3miW6A/WDMd2cfca8D8x+kVJTYwG5fMaBfcEF/bfjneDnEWXsmKLMdB2iLc6miaRQu66ryPSMdIBUAw=="],
|
||||||
|
|
||||||
"@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.120.3", "", { "dependencies": { "@tanstack/router-devtools-core": "^1.120.3", "solid-js": "^1.9.5" }, "peerDependencies": { "@tanstack/react-router": "^1.120.3", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-aeEodmbATZ81H1xJuiaWaadSW9iqG9YEvaBgmlS70bxepFNkeXONEXcw38IQMTsPNoEZqbtvqAjl2Pg08cZlxQ=="],
|
"@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.120.2", "", { "dependencies": { "@tanstack/router-devtools-core": "^1.119.0", "solid-js": "^1.9.5" }, "peerDependencies": { "@tanstack/react-router": "^1.120.2", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-89qY5pKdIN6r5G0pHP92mLvorzd7rUlHjvCkjNYOyYduxGQ8a703Y7Fp/RqXibwhjvZcet6BR2IrEhIqg9Yjhw=="],
|
||||||
|
|
||||||
"@tanstack/react-store": ["@tanstack/react-store@0.7.0", "", { "dependencies": { "@tanstack/store": "0.7.0", "use-sync-external-store": "^1.4.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-S/Rq17HaGOk+tQHV/yrePMnG1xbsKZIl/VsNWnNXt4XW+tTY8dTlvpJH2ZQ3GRALsusG5K6Q3unAGJ2pd9W/Ng=="],
|
"@tanstack/react-store": ["@tanstack/react-store@0.7.0", "", { "dependencies": { "@tanstack/store": "0.7.0", "use-sync-external-store": "^1.4.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-S/Rq17HaGOk+tQHV/yrePMnG1xbsKZIl/VsNWnNXt4XW+tTY8dTlvpJH2ZQ3GRALsusG5K6Q3unAGJ2pd9W/Ng=="],
|
||||||
|
|
||||||
"@tanstack/router-core": ["@tanstack/router-core@1.120.3", "", { "dependencies": { "@tanstack/history": "1.115.0", "@tanstack/store": "^0.7.0", "tiny-invariant": "^1.3.3" } }, "sha512-/16Pp7yxUUIGkc+oPVnlWqvlGtvLoQeKfJPpKc1vcPIBvHFO/o3yg/CEzP5raWDAjyq3b+BVkej3lSzkNxgBSg=="],
|
"@tanstack/router-core": ["@tanstack/router-core@1.119.0", "", { "dependencies": { "@tanstack/history": "1.115.0", "@tanstack/store": "^0.7.0", "tiny-invariant": "^1.3.3" } }, "sha512-3dZYP5cCq3jJYgnRDzKR3w4sYzrXP5sw1st303ye87VV26r31I8UaIuUEs7kiJaxgWBvqHglWCiygBWQODZXVw=="],
|
||||||
|
|
||||||
"@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.120.3", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16" }, "peerDependencies": { "@tanstack/router-core": "^1.120.3", "csstype": "^3.0.10", "solid-js": ">=1.9.5", "tiny-invariant": "^1.3.3" }, "optionalPeers": ["csstype"] }, "sha512-cUY1GFq8qNfIfivhozaG2NOt05Jran1yoZrBajVK0qLO0nIXJ673XeCPjzQkDctN5FU6xfmF6aXgO/xJd0igrA=="],
|
"@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.119.0", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16" }, "peerDependencies": { "@tanstack/router-core": "^1.119.0", "csstype": "^3.0.10", "solid-js": ">=1.9.5", "tiny-invariant": "^1.3.3" }, "optionalPeers": ["csstype"] }, "sha512-CH2Hx4J2UOigFtKR0anQfNiWQfidV2S7AZafkeo/S885IxwoFK7xXWzYxNbUhCDJC2tsBJ+XKjgxeBv5wGi62Q=="],
|
||||||
|
|
||||||
"@tanstack/router-generator": ["@tanstack/router-generator@1.120.3", "", { "dependencies": { "@tanstack/virtual-file-routes": "^1.115.0", "prettier": "^3.5.0", "tsx": "^4.19.2", "zod": "^3.24.2" }, "peerDependencies": { "@tanstack/react-router": "^1.120.3" }, "optionalPeers": ["@tanstack/react-router"] }, "sha512-Lz0nIwGNM+vlLGGiSBTQvcD2gW5WhoIeZN8IlTBssUb33m21QLpoj9ozpXFDrlzk36rTn5NcijHEStpYqrvQbA=="],
|
"@tanstack/router-generator": ["@tanstack/router-generator@1.120.2", "", { "dependencies": { "@tanstack/virtual-file-routes": "^1.115.0", "prettier": "^3.5.0", "tsx": "^4.19.2", "zod": "^3.24.2" }, "peerDependencies": { "@tanstack/react-router": "^1.120.2" }, "optionalPeers": ["@tanstack/react-router"] }, "sha512-rI+hQjUtsAZs5K2292zM6OE/fHAVRZxejAkrLlaQlunphqJYtHBizXk15SP9QsP3i+QvS1D8YnioMPvSlVPEOw=="],
|
||||||
|
|
||||||
"@tanstack/router-plugin": ["@tanstack/router-plugin@1.120.3", "", { "dependencies": { "@babel/core": "^7.26.8", "@babel/plugin-syntax-jsx": "^7.25.9", "@babel/plugin-syntax-typescript": "^7.25.9", "@babel/template": "^7.26.8", "@babel/traverse": "^7.26.8", "@babel/types": "^7.26.8", "@tanstack/router-core": "^1.120.3", "@tanstack/router-generator": "^1.120.3", "@tanstack/router-utils": "^1.115.0", "@tanstack/virtual-file-routes": "^1.115.0", "@types/babel__core": "^7.20.5", "@types/babel__template": "^7.4.4", "@types/babel__traverse": "^7.20.6", "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.120.3", "vite": ">=5.0.0 || >=6.0.0", "vite-plugin-solid": "^2.11.2", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-iTW402GLCxexMn42OSN8Md7A0vYm5q5+vBKDp3FcjnLgmD+31AI7H//RnGI6nxRWo/xMN8ZjESy/PVg1ouvDxA=="],
|
"@tanstack/router-plugin": ["@tanstack/router-plugin@1.120.2", "", { "dependencies": { "@babel/core": "^7.26.8", "@babel/plugin-syntax-jsx": "^7.25.9", "@babel/plugin-syntax-typescript": "^7.25.9", "@babel/template": "^7.26.8", "@babel/traverse": "^7.26.8", "@babel/types": "^7.26.8", "@tanstack/router-core": "^1.119.0", "@tanstack/router-generator": "^1.120.2", "@tanstack/router-utils": "^1.115.0", "@tanstack/virtual-file-routes": "^1.115.0", "@types/babel__core": "^7.20.5", "@types/babel__template": "^7.4.4", "@types/babel__traverse": "^7.20.6", "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.120.2", "vite": ">=5.0.0 || >=6.0.0", "vite-plugin-solid": "^2.11.2", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-LVwvd/QKFrxtsKfm1Oiv6+NzAB79hcuhlbu14NwmRfdexPYmfjvJJPK0E3IlLmh/mRlEHmfYajutwuqvBONM9w=="],
|
||||||
|
|
||||||
"@tanstack/router-utils": ["@tanstack/router-utils@1.115.0", "", { "dependencies": { "@babel/generator": "^7.26.8", "@babel/parser": "^7.26.8", "ansis": "^3.11.0", "diff": "^7.0.0" } }, "sha512-Dng4y+uLR9b5zPGg7dHReHOTHQa6x+G6nCoZshsDtWrYsrdCcJEtLyhwZ5wG8OyYS6dVr/Cn+E5Bd2b6BhJ89w=="],
|
"@tanstack/router-utils": ["@tanstack/router-utils@1.115.0", "", { "dependencies": { "@babel/generator": "^7.26.8", "@babel/parser": "^7.26.8", "ansis": "^3.11.0", "diff": "^7.0.0" } }, "sha512-Dng4y+uLR9b5zPGg7dHReHOTHQa6x+G6nCoZshsDtWrYsrdCcJEtLyhwZ5wG8OyYS6dVr/Cn+E5Bd2b6BhJ89w=="],
|
||||||
|
|
||||||
@@ -461,25 +461,25 @@
|
|||||||
|
|
||||||
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
||||||
|
|
||||||
"@types/react": ["@types/react@19.1.4", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-EB1yiiYdvySuIITtD5lhW4yPyJ31RkJkkDw794LaQYrxCSaQV/47y5o1FMC4zF9ZyjUjzJMZwbovEnT5yHTW6g=="],
|
"@types/react": ["@types/react@19.1.3", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-dLWQ+Z0CkIvK1J8+wrDPwGxEYFA4RAyHoZPxHVGspYmFVnwGSNT24cGIhFJrtfRnWVuW8X7NO52gCXmhkVUWGQ=="],
|
||||||
|
|
||||||
"@types/react-dom": ["@types/react-dom@19.1.5", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg=="],
|
"@types/react-dom": ["@types/react-dom@19.1.3", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-rJXC08OG0h3W6wDMFxQrZF00Kq6qQvw0djHRdzl3U5DnIERz0MRce3WVc7IS6JYBwtaP/DwYtRRjVlvivNveKg=="],
|
||||||
|
|
||||||
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.32.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.32.1", "@typescript-eslint/type-utils": "8.32.1", "@typescript-eslint/utils": "8.32.1", "@typescript-eslint/visitor-keys": "8.32.1", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg=="],
|
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.32.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.32.0", "@typescript-eslint/type-utils": "8.32.0", "@typescript-eslint/utils": "8.32.0", "@typescript-eslint/visitor-keys": "8.32.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-/jU9ettcntkBFmWUzzGgsClEi2ZFiikMX5eEQsmxIAWMOn4H3D4rvHssstmAHGVvrYnaMqdWWWg0b5M6IN/MTQ=="],
|
||||||
|
|
||||||
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.32.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.32.1", "@typescript-eslint/types": "8.32.1", "@typescript-eslint/typescript-estree": "8.32.1", "@typescript-eslint/visitor-keys": "8.32.1", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg=="],
|
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.32.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.32.0", "@typescript-eslint/types": "8.32.0", "@typescript-eslint/typescript-estree": "8.32.0", "@typescript-eslint/visitor-keys": "8.32.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-B2MdzyWxCE2+SqiZHAjPphft+/2x2FlO9YBx7eKE1BCb+rqBlQdhtAEhzIEdozHd55DXPmxBdpMygFJjfjjA9A=="],
|
||||||
|
|
||||||
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.32.1", "", { "dependencies": { "@typescript-eslint/types": "8.32.1", "@typescript-eslint/visitor-keys": "8.32.1" } }, "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA=="],
|
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.32.0", "", { "dependencies": { "@typescript-eslint/types": "8.32.0", "@typescript-eslint/visitor-keys": "8.32.0" } }, "sha512-jc/4IxGNedXkmG4mx4nJTILb6TMjL66D41vyeaPWvDUmeYQzF3lKtN15WsAeTr65ce4mPxwopPSo1yUUAWw0hQ=="],
|
||||||
|
|
||||||
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.32.1", "", { "dependencies": { "@typescript-eslint/typescript-estree": "8.32.1", "@typescript-eslint/utils": "8.32.1", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA=="],
|
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.32.0", "", { "dependencies": { "@typescript-eslint/typescript-estree": "8.32.0", "@typescript-eslint/utils": "8.32.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-t2vouuYQKEKSLtJaa5bB4jHeha2HJczQ6E5IXPDPgIty9EqcJxpr1QHQ86YyIPwDwxvUmLfP2YADQ5ZY4qddZg=="],
|
||||||
|
|
||||||
"@typescript-eslint/types": ["@typescript-eslint/types@8.32.1", "", {}, "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg=="],
|
"@typescript-eslint/types": ["@typescript-eslint/types@8.32.0", "", {}, "sha512-O5Id6tGadAZEMThM6L9HmVf5hQUXNSxLVKeGJYWNhhVseps/0LddMkp7//VDkzwJ69lPL0UmZdcZwggj9akJaA=="],
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.32.1", "", { "dependencies": { "@typescript-eslint/types": "8.32.1", "@typescript-eslint/visitor-keys": "8.32.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg=="],
|
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.32.0", "", { "dependencies": { "@typescript-eslint/types": "8.32.0", "@typescript-eslint/visitor-keys": "8.32.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-pU9VD7anSCOIoBFnhTGfOzlVFQIA1XXiQpH/CezqOBaDppRwTglJzCC6fUQGpfwey4T183NKhF1/mfatYmjRqQ=="],
|
||||||
|
|
||||||
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.32.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.32.1", "@typescript-eslint/types": "8.32.1", "@typescript-eslint/typescript-estree": "8.32.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA=="],
|
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.32.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.32.0", "@typescript-eslint/types": "8.32.0", "@typescript-eslint/typescript-estree": "8.32.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-8S9hXau6nQ/sYVtC3D6ISIDoJzS1NsCK+gluVhLN2YkBPX+/1wkwyUiDKnxRh15579WoOIyVWnoyIf3yGI9REw=="],
|
||||||
|
|
||||||
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.32.1", "", { "dependencies": { "@typescript-eslint/types": "8.32.1", "eslint-visitor-keys": "^4.2.0" } }, "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w=="],
|
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.32.0", "", { "dependencies": { "@typescript-eslint/types": "8.32.0", "eslint-visitor-keys": "^4.2.0" } }, "sha512-1rYQTCLFFzOI5Nl0c8LUpJT8HxpwVRn9E4CkMsYfuN6ctmQqExjSTzzSk0Tz2apmXy7WU6/6fyaZVVA/thPN+w=="],
|
||||||
|
|
||||||
"@vitejs/plugin-react": ["@vitejs/plugin-react@4.4.1", "", { "dependencies": { "@babel/core": "^7.26.10", "@babel/plugin-transform-react-jsx-self": "^7.25.9", "@babel/plugin-transform-react-jsx-source": "^7.25.9", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" } }, "sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w=="],
|
"@vitejs/plugin-react": ["@vitejs/plugin-react@4.4.1", "", { "dependencies": { "@babel/core": "^7.26.10", "@babel/plugin-transform-react-jsx-self": "^7.25.9", "@babel/plugin-transform-react-jsx-source": "^7.25.9", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" } }, "sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w=="],
|
||||||
|
|
||||||
@@ -523,7 +523,7 @@
|
|||||||
|
|
||||||
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
||||||
|
|
||||||
"caniuse-lite": ["caniuse-lite@1.0.30001718", "", {}, "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw=="],
|
"caniuse-lite": ["caniuse-lite@1.0.30001717", "", {}, "sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw=="],
|
||||||
|
|
||||||
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||||
|
|
||||||
@@ -555,7 +555,7 @@
|
|||||||
|
|
||||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||||
|
|
||||||
"debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
"debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
|
||||||
|
|
||||||
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
|
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
|
||||||
|
|
||||||
@@ -571,9 +571,9 @@
|
|||||||
|
|
||||||
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
|
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
|
||||||
|
|
||||||
"effect": ["effect@3.15.1", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-n3bDF6K3R+FSVuH+dSVU3ya2pI4Wt/tnKzum3DC/3b5e0E9HfhrhbkonOkYU3AVJJOzCA6zZE2/y6EUgQNAY4g=="],
|
"effect": ["effect@3.14.21", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-TKR7zfWcuZgEdWd+oIGA8LdREj/c+1Q0wz4pWqQtYT7VHnkW/QQEYCXgrDI5dT6lJgRTgyQAC1bAnpAf6MdjIA=="],
|
||||||
|
|
||||||
"electron-to-chromium": ["electron-to-chromium@1.5.152", "", {}, "sha512-xBOfg/EBaIlVsHipHl2VdTPJRSvErNUaqW8ejTq5OlOlIYx1wOllCHsAvAIrr55jD1IYEfdR86miUEt8H5IeJg=="],
|
"electron-to-chromium": ["electron-to-chromium@1.5.151", "", {}, "sha512-Rl6uugut2l9sLojjS4H4SAr3A4IgACMLgpuEMPYCVcKydzfyPrn5absNRju38IhQOf/NwjJY8OGWjlteqYeBCA=="],
|
||||||
|
|
||||||
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
||||||
|
|
||||||
@@ -613,7 +613,7 @@
|
|||||||
|
|
||||||
"etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
|
"etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
|
||||||
|
|
||||||
"eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="],
|
"eventsource": ["eventsource@3.0.6", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-l19WpE2m9hSuyP06+FbuUUf1G+R0SFLrtQfbRb9PRr+oimOfxQhgGCbVaXg5IvZyyTThJsxh6L/srkMiCeBPDA=="],
|
||||||
|
|
||||||
"eventsource-parser": ["eventsource-parser@3.0.1", "", {}, "sha512-VARTJ9CYeuQYb0pZEPbzi740OWFgpHe7AYJ2WFZVnUDUQp5Dk2yJUgF36YsZ81cOyxT0QxmXD2EQpapAouzWVA=="],
|
"eventsource-parser": ["eventsource-parser@3.0.1", "", {}, "sha512-VARTJ9CYeuQYb0pZEPbzi740OWFgpHe7AYJ2WFZVnUDUQp5Dk2yJUgF36YsZ81cOyxT0QxmXD2EQpapAouzWVA=="],
|
||||||
|
|
||||||
@@ -733,7 +733,7 @@
|
|||||||
|
|
||||||
"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=="],
|
||||||
|
|
||||||
"lucide-react": ["lucide-react@0.510.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-p8SQRAMVh7NhsAIETokSqDrc5CHnDLbV29mMnzaXx+Vc/hnqQzwI2r0FMWCcoTXnbw2KEjy48xwpGdEL+ck06Q=="],
|
"lucide-react": ["lucide-react@0.508.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-gcP16PnexqtOFrTtv98kVsGzTfnbPekzZiQfByi2S89xfk7E/4uKE1USZqccIp58v42LqkO7MuwpCqshwSrJCg=="],
|
||||||
|
|
||||||
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
||||||
|
|
||||||
@@ -845,7 +845,7 @@
|
|||||||
|
|
||||||
"reffuse": ["reffuse@workspace:packages/reffuse"],
|
"reffuse": ["reffuse@workspace:packages/reffuse"],
|
||||||
|
|
||||||
"remeda": ["remeda@2.21.5", "", { "dependencies": { "type-fest": "^4.40.1" } }, "sha512-kXzS4ITQBoLNoec4CMj4GThW7/TG94l186/FEMKPHKN3PeY42xluSLIZMEda3nkqolQJrJ0oCet1rp3UGLx1Yg=="],
|
"remeda": ["remeda@2.21.3", "", { "dependencies": { "type-fest": "^4.39.1" } }, "sha512-XXrZdLA10oEOQhLLzEJEiFFSKi21REGAkHdImIb4rt/XXy8ORGXh5HCcpUOsElfPNDb+X6TA/+wkh+p2KffYmg=="],
|
||||||
|
|
||||||
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
||||||
|
|
||||||
@@ -869,9 +869,9 @@
|
|||||||
|
|
||||||
"send": ["send@1.2.0", "", { "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.0", "mime-types": "^3.0.1", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.1" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="],
|
"send": ["send@1.2.0", "", { "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.0", "mime-types": "^3.0.1", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.1" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="],
|
||||||
|
|
||||||
"seroval": ["seroval@1.3.1", "", {}, "sha512-F+T9EQPdLzgdewgxnBh4mSc+vde+EOkU6dC9BDuu/bfGb+UyUlqM6t8znFCTPQSuai/ZcfFg0gu79h+bVW2O0w=="],
|
"seroval": ["seroval@1.3.0", "", {}, "sha512-4tYQDy3HVM0JjJ1CfDK3K8FhBKIDDri27oc2AyabuuHfQw6/yTDPp2Abt1h2cNtf1R0T+7AQYAzPhUgqXztaXw=="],
|
||||||
|
|
||||||
"seroval-plugins": ["seroval-plugins@1.3.1", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-dOlUoiI3fgZbQIcj6By+l865pzeWdP3XCSLdI3xlKnjCk5983yLWPsXytFOUI0BUZKG9qwqbj78n9yVcVwUqaQ=="],
|
"seroval-plugins": ["seroval-plugins@1.3.0", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-FFu/UE3uA8L1vj0CXXZo2Nlh10MtYoOs0G//ptwlQMjfPFSeIVYUNy0zewfV8iM0CrOebAfHEG6J3xA9c+lsaQ=="],
|
||||||
|
|
||||||
"serve-static": ["serve-static@2.2.0", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ=="],
|
"serve-static": ["serve-static@2.2.0", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ=="],
|
||||||
|
|
||||||
@@ -937,11 +937,11 @@
|
|||||||
|
|
||||||
"typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
|
"typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
|
||||||
|
|
||||||
"typescript-eslint": ["typescript-eslint@8.32.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.32.1", "@typescript-eslint/parser": "8.32.1", "@typescript-eslint/utils": "8.32.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg=="],
|
"typescript-eslint": ["typescript-eslint@8.32.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.32.0", "@typescript-eslint/parser": "8.32.0", "@typescript-eslint/utils": "8.32.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-UMq2kxdXCzinFFPsXc9o2ozIpYCCOiEC46MG3yEh5Vipq6BO27otTtEBZA1fQ66DulEUgE97ucQ/3YY66CPg0A=="],
|
||||||
|
|
||||||
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
|
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
|
||||||
|
|
||||||
"unplugin": ["unplugin@2.3.4", "", { "dependencies": { "acorn": "^8.14.1", "picomatch": "^4.0.2", "webpack-virtual-modules": "^0.6.2" } }, "sha512-m4PjxTurwpWfpMomp8AptjD5yj8qEZN5uQjjGM3TAs9MWWD2tXSSNNj6jGR2FoVGod4293ytyV6SwBbertfyJg=="],
|
"unplugin": ["unplugin@2.3.2", "", { "dependencies": { "acorn": "^8.14.1", "picomatch": "^4.0.2", "webpack-virtual-modules": "^0.6.2" } }, "sha512-3n7YA46rROb3zSj8fFxtxC/PqoyvYQ0llwz9wtUPUutr9ig09C8gGo5CWCwHrUzlqC1LLR43kxp5vEIyH1ac1w=="],
|
||||||
|
|
||||||
"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=="],
|
"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=="],
|
||||||
|
|
||||||
@@ -981,11 +981,9 @@
|
|||||||
|
|
||||||
"@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="],
|
"@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="],
|
||||||
|
|
||||||
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.4", "", {}, "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A=="],
|
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
"@typescript-eslint/typescript-estree/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
||||||
|
|
||||||
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@reffuse/monorepo",
|
"name": "@reffuse/monorepo",
|
||||||
"packageManager": "bun@1.2.13",
|
"packageManager": "bun@1.2.12",
|
||||||
"private": true,
|
"private": true,
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"./packages/*"
|
"./packages/*"
|
||||||
|
|||||||
@@ -12,12 +12,12 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.26.0",
|
"@eslint/js": "^9.26.0",
|
||||||
"@tanstack/react-router": "^1.120.3",
|
"@tanstack/react-router": "^1.120.2",
|
||||||
"@tanstack/react-router-devtools": "^1.120.3",
|
"@tanstack/react-router-devtools": "^1.120.2",
|
||||||
"@tanstack/router-plugin": "^1.120.3",
|
"@tanstack/router-plugin": "^1.120.2",
|
||||||
"@thilawyn/thilaschema": "^0.1.4",
|
"@thilawyn/thilaschema": "^0.1.4",
|
||||||
"@types/react": "^19.1.4",
|
"@types/react": "^19.1.3",
|
||||||
"@types/react-dom": "^19.1.5",
|
"@types/react-dom": "^19.1.3",
|
||||||
"@vitejs/plugin-react": "^4.4.1",
|
"@vitejs/plugin-react": "^4.4.1",
|
||||||
"eslint": "^9.26.0",
|
"eslint": "^9.26.0",
|
||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
@@ -25,27 +25,27 @@
|
|||||||
"globals": "^16.1.0",
|
"globals": "^16.1.0",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
"typescript-eslint": "^8.32.1",
|
"typescript-eslint": "^8.32.0",
|
||||||
"vite": "^6.3.5"
|
"vite": "^6.3.5"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@effect/platform": "^0.82.1",
|
"@effect/platform": "^0.80.21",
|
||||||
"@effect/platform-browser": "^0.62.1",
|
"@effect/platform-browser": "^0.60.12",
|
||||||
"@radix-ui/themes": "^3.2.1",
|
"@radix-ui/themes": "^3.2.1",
|
||||||
"@reffuse/extension-lazyref": "workspace:*",
|
"@reffuse/extension-lazyref": "workspace:*",
|
||||||
"@reffuse/extension-query": "workspace:*",
|
"@reffuse/extension-query": "workspace:*",
|
||||||
"@typed/async-data": "^0.13.1",
|
"@typed/async-data": "^0.13.1",
|
||||||
"@typed/id": "^0.17.2",
|
"@typed/id": "^0.17.2",
|
||||||
"@typed/lazy-ref": "^0.3.3",
|
"@typed/lazy-ref": "^0.3.3",
|
||||||
"effect": "^3.15.1",
|
"effect": "^3.14.21",
|
||||||
"lucide-react": "^0.510.0",
|
"lucide-react": "^0.508.0",
|
||||||
"mobx": "^6.13.7",
|
"mobx": "^6.13.7",
|
||||||
"reffuse": "workspace:*"
|
"reffuse": "workspace:*"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"effect": "^3.15.1",
|
"effect": "^3.14.21",
|
||||||
"@effect/platform": "^0.82.1",
|
"@effect/platform": "^0.80.21",
|
||||||
"@effect/platform-browser": "^0.62.1",
|
"@effect/platform-browser": "^0.60.12",
|
||||||
"@typed/lazy-ref": "^0.3.3",
|
"@typed/lazy-ref": "^0.3.3",
|
||||||
"@typed/async-data": "^0.13.1"
|
"@typed/async-data": "^0.13.1"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { AlertDialog, Button, Flex, Text } from "@radix-ui/themes"
|
import { AlertDialog, Button, Flex, Text } from "@radix-ui/themes"
|
||||||
import { Cause, Console, Effect, Either, flow, Match, Option, Stream } from "effect"
|
import { Cause, Console, Effect, Either, flow, Match, Option, Stream } from "effect"
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
|
import { AppQueryClient } from "./query"
|
||||||
import { R } from "./reffuse"
|
import { R } from "./reffuse"
|
||||||
import { AppQueryErrorHandler } from "./services"
|
|
||||||
|
|
||||||
|
|
||||||
export function VQueryErrorHandler() {
|
export function VQueryErrorHandler() {
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
|
|
||||||
const error = R.useSubscribeStream(
|
const error = R.useSubscribeStream(
|
||||||
R.useMemo(() => AppQueryErrorHandler.AppQueryErrorHandler.pipe(
|
R.useMemo(() => AppQueryClient.pipe(
|
||||||
Effect.map(handler => handler.errors.pipe(
|
Effect.map(client => client.errorHandler.errors.pipe(
|
||||||
Stream.changes,
|
Stream.changes,
|
||||||
Stream.tap(Console.error),
|
Stream.tap(Console.error),
|
||||||
Stream.tap(() => Effect.sync(() => setOpen(true))),
|
Stream.tap(() => Effect.sync(() => setOpen(true))),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { ThSchema } from "@thilawyn/thilaschema"
|
import { ThSchema } from "@thilawyn/thilaschema"
|
||||||
import { Schema } from "effect"
|
import { GetRandomValues, makeUuid4 } from "@typed/id"
|
||||||
|
import { Effect, Schema } from "effect"
|
||||||
|
|
||||||
|
|
||||||
export class Todo extends Schema.Class<Todo>("Todo")({
|
export class Todo extends Schema.Class<Todo>("Todo")({
|
||||||
@@ -17,4 +18,9 @@ export const TodoFromJsonStruct = Schema.Struct({
|
|||||||
ThSchema.assertEncodedJsonifiable
|
ThSchema.assertEncodedJsonifiable
|
||||||
)
|
)
|
||||||
|
|
||||||
export const TodoFromJson = Schema.compose(TodoFromJsonStruct, Todo)
|
export const TodoFromJson = TodoFromJsonStruct.pipe(Schema.compose(Todo))
|
||||||
|
|
||||||
|
|
||||||
|
export const generateUniqueID = makeUuid4.pipe(
|
||||||
|
Effect.provide(GetRandomValues.CryptoRandom)
|
||||||
|
)
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ import { Layer } from "effect"
|
|||||||
import { StrictMode } from "react"
|
import { StrictMode } from "react"
|
||||||
import { createRoot } from "react-dom/client"
|
import { createRoot } from "react-dom/client"
|
||||||
import { ReffuseRuntime } from "reffuse"
|
import { ReffuseRuntime } from "reffuse"
|
||||||
|
import { AppQueryClient, AppQueryErrorHandler } from "./query"
|
||||||
import { RootContext } from "./reffuse"
|
import { RootContext } from "./reffuse"
|
||||||
import { routeTree } from "./routeTree.gen"
|
import { routeTree } from "./routeTree.gen"
|
||||||
import { AppQueryClient, AppQueryErrorHandler } from "./services"
|
|
||||||
|
|
||||||
|
|
||||||
const layer = Layer.empty.pipe(
|
const layer = Layer.empty.pipe(
|
||||||
Layer.provideMerge(AppQueryClient.AppQueryClient.Default),
|
Layer.provideMerge(AppQueryClient.Live),
|
||||||
Layer.provideMerge(AppQueryErrorHandler.AppQueryErrorHandler.Default),
|
Layer.provideMerge(AppQueryErrorHandler.Live),
|
||||||
Layer.provideMerge(Clipboard.layer),
|
Layer.provideMerge(Clipboard.layer),
|
||||||
Layer.provideMerge(Geolocation.layer),
|
Layer.provideMerge(Geolocation.layer),
|
||||||
Layer.provideMerge(Permissions.layer),
|
Layer.provideMerge(Permissions.layer),
|
||||||
|
|||||||
21
packages/example/src/query.ts
Normal file
21
packages/example/src/query.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { HttpClientError } from "@effect/platform"
|
||||||
|
import { QueryClient, QueryErrorHandler } from "@reffuse/extension-query"
|
||||||
|
import { Effect } from "effect"
|
||||||
|
|
||||||
|
|
||||||
|
export class AppQueryErrorHandler extends QueryErrorHandler.Service<AppQueryErrorHandler,
|
||||||
|
HttpClientError.HttpClientError
|
||||||
|
>()(
|
||||||
|
"AppQueryErrorHandler",
|
||||||
|
|
||||||
|
(self, failure, defect) => self.pipe(
|
||||||
|
Effect.catchTags({
|
||||||
|
RequestError: failure,
|
||||||
|
ResponseError: failure,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Effect.catchAllDefect(defect),
|
||||||
|
),
|
||||||
|
) {}
|
||||||
|
|
||||||
|
export class AppQueryClient extends QueryClient.Service<AppQueryClient>()({ ErrorHandler: AppQueryErrorHandler }) {}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import { QueryRunner } from "@reffuse/extension-query"
|
import { QueryService } from "@reffuse/extension-query"
|
||||||
import { ParseResult, Schema } from "effect"
|
import { ParseResult, Schema } from "effect"
|
||||||
|
|
||||||
|
|
||||||
export const Result = Schema.Array(Schema.String)
|
export const Result = Schema.Array(Schema.String)
|
||||||
|
|
||||||
export class Uuid4Query extends QueryRunner.Tag("Uuid4Query")<Uuid4Query,
|
export class Uuid4Query extends QueryService.Tag("Uuid4Query")<Uuid4Query,
|
||||||
readonly ["uuid4", number],
|
readonly ["uuid4", number],
|
||||||
typeof Result.Type,
|
typeof Result.Type,
|
||||||
ParseResult.ParseError
|
ParseResult.ParseError
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export function Uuid4QueryService() {
|
|||||||
const runFork = R.useRunFork()
|
const runFork = R.useRunFork()
|
||||||
|
|
||||||
const query = R.useMemo(() => Uuid4Query.Uuid4Query, [])
|
const query = R.useMemo(() => Uuid4Query.Uuid4Query, [])
|
||||||
const [state] = R.useSubscribeRefs(query.stateRef)
|
const [state] = R.useRefState(query.state)
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -3,12 +3,11 @@ import { Clipboard, Geolocation, Permissions } from "@effect/platform-browser"
|
|||||||
import { LazyRefExtension } from "@reffuse/extension-lazyref"
|
import { LazyRefExtension } from "@reffuse/extension-lazyref"
|
||||||
import { QueryExtension } from "@reffuse/extension-query"
|
import { QueryExtension } from "@reffuse/extension-query"
|
||||||
import { Reffuse, ReffuseContext } from "reffuse"
|
import { Reffuse, ReffuseContext } from "reffuse"
|
||||||
import { AppQueryClient, AppQueryErrorHandler } from "./services"
|
import { AppQueryClient } from "./query"
|
||||||
|
|
||||||
|
|
||||||
export const RootContext = ReffuseContext.make<
|
export const RootContext = ReffuseContext.make<
|
||||||
| AppQueryClient.AppQueryClient
|
| AppQueryClient
|
||||||
| AppQueryErrorHandler.AppQueryErrorHandler
|
|
||||||
| Clipboard.Clipboard
|
| Clipboard.Clipboard
|
||||||
| Geolocation.Geolocation
|
| Geolocation.Geolocation
|
||||||
| Permissions.Permissions
|
| Permissions.Permissions
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Uuid4QueryService } from "@/query/views/Uuid4QueryService"
|
|||||||
import { R } from "@/reffuse"
|
import { R } from "@/reffuse"
|
||||||
import { HttpClient } from "@effect/platform"
|
import { HttpClient } from "@effect/platform"
|
||||||
import { createFileRoute } from "@tanstack/react-router"
|
import { createFileRoute } from "@tanstack/react-router"
|
||||||
import { Console, Effect, Layer, Schema } from "effect"
|
import { Console, Effect, Schema } from "effect"
|
||||||
import { useMemo } from "react"
|
import { useMemo } from "react"
|
||||||
|
|
||||||
|
|
||||||
@@ -17,18 +17,15 @@ function RouteComponent() {
|
|||||||
key: R.useStreamFromReactiveValues(["uuid4", 10 as number]),
|
key: R.useStreamFromReactiveValues(["uuid4", 10 as number]),
|
||||||
query: ([, count]) => Console.log(`Querying ${ count } IDs...`).pipe(
|
query: ([, count]) => Console.log(`Querying ${ count } IDs...`).pipe(
|
||||||
Effect.andThen(Effect.sleep("500 millis")),
|
Effect.andThen(Effect.sleep("500 millis")),
|
||||||
Effect.andThen(Effect.map(
|
Effect.andThen(HttpClient.get(`https://www.uuidtools.com/api/generate/v4/count/${ count }`)),
|
||||||
HttpClient.HttpClient,
|
HttpClient.withTracerPropagation(false),
|
||||||
HttpClient.withTracerPropagation(false),
|
|
||||||
)),
|
|
||||||
Effect.flatMap(client => client.get(`https://www.uuidtools.com/api/generate/v4/count/${ count }`)),
|
|
||||||
Effect.flatMap(res => res.json),
|
Effect.flatMap(res => res.json),
|
||||||
Effect.flatMap(Schema.decodeUnknown(Uuid4Query.Result)),
|
Effect.flatMap(Schema.decodeUnknown(Uuid4Query.Result)),
|
||||||
Effect.scoped,
|
Effect.scoped,
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
|
|
||||||
const layer = useMemo(() => Layer.succeed(Uuid4Query.Uuid4Query, query), [query])
|
const layer = useMemo(() => query.layer(Uuid4Query.Uuid4Query), [query])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<QueryContext.Provider layer={layer}>
|
<QueryContext.Provider layer={layer}>
|
||||||
|
|||||||
@@ -29,18 +29,15 @@ function RouteComponent() {
|
|||||||
Effect.tap(() => QueryProgress.QueryProgress.update(() =>
|
Effect.tap(() => QueryProgress.QueryProgress.update(() =>
|
||||||
AsyncData.Progress.make({ loaded: 50, total: Option.some(100) })
|
AsyncData.Progress.make({ loaded: 50, total: Option.some(100) })
|
||||||
)),
|
)),
|
||||||
Effect.andThen(Effect.map(
|
Effect.andThen(HttpClient.get(`https://www.uuidtools.com/api/generate/v4/count/${ count }`)),
|
||||||
HttpClient.HttpClient,
|
HttpClient.withTracerPropagation(false),
|
||||||
HttpClient.withTracerPropagation(false),
|
|
||||||
)),
|
|
||||||
Effect.flatMap(client => client.get(`https://www.uuidtools.com/api/generate/v4/count/${ count }`)),
|
|
||||||
Effect.flatMap(res => res.json),
|
Effect.flatMap(res => res.json),
|
||||||
Effect.flatMap(Schema.decodeUnknown(Result)),
|
Effect.flatMap(Schema.decodeUnknown(Result)),
|
||||||
Effect.scoped,
|
Effect.scoped,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const [state] = R.useSubscribeRefs(mutation.stateRef)
|
const [state] = R.useSubscribeRefs(mutation.state)
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -23,18 +23,15 @@ function RouteComponent() {
|
|||||||
key: R.useStreamFromReactiveValues(["uuid4", count]),
|
key: R.useStreamFromReactiveValues(["uuid4", count]),
|
||||||
query: ([, count]) => Console.log(`Querying ${ count } IDs...`).pipe(
|
query: ([, count]) => Console.log(`Querying ${ count } IDs...`).pipe(
|
||||||
Effect.andThen(Effect.sleep("500 millis")),
|
Effect.andThen(Effect.sleep("500 millis")),
|
||||||
Effect.andThen(Effect.map(
|
Effect.andThen(HttpClient.get(`https://www.uuidtools.com/api/generate/v4/count/${ count }`)),
|
||||||
HttpClient.HttpClient,
|
HttpClient.withTracerPropagation(false),
|
||||||
HttpClient.withTracerPropagation(false),
|
|
||||||
)),
|
|
||||||
Effect.flatMap(client => client.get(`https://www.uuidtools.com/api/generate/v4/count/${ count }`)),
|
|
||||||
Effect.flatMap(res => res.json),
|
Effect.flatMap(res => res.json),
|
||||||
Effect.flatMap(Schema.decodeUnknown(Result)),
|
Effect.flatMap(Schema.decodeUnknown(Result)),
|
||||||
Effect.scoped,
|
Effect.scoped,
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
|
|
||||||
const [state] = R.useSubscribeRefs(query.stateRef)
|
const [state] = R.useSubscribeRefs(query.state)
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -2,20 +2,10 @@ import { R } from "@/reffuse"
|
|||||||
import { Button, Flex, Text } from "@radix-ui/themes"
|
import { Button, Flex, Text } from "@radix-ui/themes"
|
||||||
import { createFileRoute } from "@tanstack/react-router"
|
import { createFileRoute } from "@tanstack/react-router"
|
||||||
import { GetRandomValues, makeUuid4 } from "@typed/id"
|
import { GetRandomValues, makeUuid4 } from "@typed/id"
|
||||||
import { Console, Effect, Option } from "effect"
|
import { Console, Effect, Option, Scope } from "effect"
|
||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
|
|
||||||
|
|
||||||
interface Node {
|
|
||||||
value: string
|
|
||||||
left?: Leaf
|
|
||||||
right?: Leaf
|
|
||||||
}
|
|
||||||
interface Leaf {
|
|
||||||
node: Node
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const makeUuid = Effect.provide(makeUuid4, GetRandomValues.CryptoRandom)
|
const makeUuid = Effect.provide(makeUuid4, GetRandomValues.CryptoRandom)
|
||||||
|
|
||||||
|
|
||||||
@@ -34,18 +24,13 @@ function RouteComponent() {
|
|||||||
const uuidStream = R.useStreamFromReactiveValues([uuid])
|
const uuidStream = R.useStreamFromReactiveValues([uuid])
|
||||||
const uuidStreamLatestValue = R.useSubscribeStream(uuidStream)
|
const uuidStreamLatestValue = R.useSubscribeStream(uuidStream)
|
||||||
|
|
||||||
const [, scopeLayer] = R.useScope([uuid])
|
const scope = R.useScope([uuid])
|
||||||
|
|
||||||
useEffect(() => Effect.addFinalizer(() => Console.log("Scope cleanup!")).pipe(
|
useEffect(() => Effect.addFinalizer(() => Console.log("Scope cleanup!")).pipe(
|
||||||
Effect.andThen(Console.log("Scope changed")),
|
Effect.andThen(Console.log("Scope changed")),
|
||||||
Effect.provide(scopeLayer),
|
Effect.provideService(Scope.Scope, scope),
|
||||||
runSync,
|
runSync,
|
||||||
), [scopeLayer, runSync])
|
), [scope, runSync])
|
||||||
|
|
||||||
|
|
||||||
const nodeRef = R.useRef(() => Effect.succeed<Node>({ value: "prout" }))
|
|
||||||
const nodeValueRef = R.useSubRefFromPath(nodeRef, ["value"])
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction="column" justify="center" align="center" gap="2">
|
<Flex direction="column" justify="center" align="center" gap="2">
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ function Todos() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<TodosContext.Provider layer={todosLayer} finalizerExecutionMode="fork">
|
<TodosContext.Provider layer={todosLayer}>
|
||||||
<VTodos />
|
<VTodos />
|
||||||
</TodosContext.Provider>
|
</TodosContext.Provider>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
import { QueryClient } from "@reffuse/extension-query"
|
|
||||||
import * as AppQueryErrorHandler from "./AppQueryErrorHandler"
|
|
||||||
|
|
||||||
|
|
||||||
export class AppQueryClient extends QueryClient.Service<AppQueryClient>()({
|
|
||||||
errorHandler: AppQueryErrorHandler.AppQueryErrorHandler
|
|
||||||
}) {}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { HttpClientError } from "@effect/platform"
|
|
||||||
import { QueryErrorHandler } from "@reffuse/extension-query"
|
|
||||||
import { Effect } from "effect"
|
|
||||||
|
|
||||||
|
|
||||||
export class AppQueryErrorHandler extends Effect.Service<AppQueryErrorHandler>()("AppQueryErrorHandler", {
|
|
||||||
effect: QueryErrorHandler.make<HttpClientError.HttpClientError>()(
|
|
||||||
(self, failure, defect) => self.pipe(
|
|
||||||
Effect.catchTag("RequestError", "ResponseError", failure),
|
|
||||||
Effect.catchAllDefect(defect),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}) {}
|
|
||||||
@@ -1,2 +1 @@
|
|||||||
export * as AppQueryClient from "./AppQueryClient"
|
export {}
|
||||||
export * as AppQueryErrorHandler from "./AppQueryErrorHandler"
|
|
||||||
|
|||||||
@@ -2,43 +2,68 @@ import { Todo } from "@/domain"
|
|||||||
import { KeyValueStore } from "@effect/platform"
|
import { KeyValueStore } from "@effect/platform"
|
||||||
import { BrowserKeyValueStore } from "@effect/platform-browser"
|
import { BrowserKeyValueStore } from "@effect/platform-browser"
|
||||||
import { PlatformError } from "@effect/platform/Error"
|
import { PlatformError } from "@effect/platform/Error"
|
||||||
import { Chunk, Context, Effect, identity, Layer, ParseResult, Ref, Schema, Stream, SubscriptionRef } from "effect"
|
import { Chunk, Context, Effect, identity, Layer, ParseResult, Ref, Schema, SubscriptionRef } from "effect"
|
||||||
|
|
||||||
|
|
||||||
export class TodosState extends Context.Tag("TodosState")<TodosState, {
|
export class TodosState extends Context.Tag("TodosState")<TodosState, {
|
||||||
readonly todos: SubscriptionRef.SubscriptionRef<Chunk.Chunk<Todo.Todo>>
|
readonly todos: SubscriptionRef.SubscriptionRef<Chunk.Chunk<Todo.Todo>>
|
||||||
readonly load: Effect.Effect<void, PlatformError | ParseResult.ParseError>
|
|
||||||
readonly save: Effect.Effect<void, PlatformError | ParseResult.ParseError>
|
readonly readFromLocalStorage: Effect.Effect<void, PlatformError | ParseResult.ParseError>
|
||||||
|
readonly saveToLocalStorage: Effect.Effect<void, PlatformError | ParseResult.ParseError>
|
||||||
|
|
||||||
|
readonly prepend: (todo: Todo.Todo) => Effect.Effect<void>
|
||||||
|
readonly replace: (index: number, todo: Todo.Todo) => Effect.Effect<void>
|
||||||
|
readonly remove: (index: number) => Effect.Effect<void>
|
||||||
|
// readonly moveUp: (index: number) => Effect.Effect<void, Cause.NoSuchElementException>
|
||||||
|
// readonly moveDown: (index: number) => Effect.Effect<void, Cause.NoSuchElementException>
|
||||||
}>() {}
|
}>() {}
|
||||||
|
|
||||||
|
|
||||||
export const make = (key: string) => Layer.effect(TodosState, Effect.gen(function*() {
|
export const make = (key: string) => Layer.effect(TodosState, Effect.gen(function*() {
|
||||||
|
const todos = yield* SubscriptionRef.make(Chunk.empty<Todo.Todo>())
|
||||||
|
|
||||||
const readFromLocalStorage = KeyValueStore.KeyValueStore.pipe(
|
const readFromLocalStorage = KeyValueStore.KeyValueStore.pipe(
|
||||||
Effect.flatMap(kv => kv.get(key)),
|
Effect.flatMap(kv => kv.get(key)),
|
||||||
Effect.flatMap(identity),
|
Effect.flatMap(identity),
|
||||||
Effect.flatMap(Schema.decode(
|
Effect.flatMap(Schema.parseJson().pipe(
|
||||||
Schema.compose(Schema.parseJson(), Schema.Chunk(Todo.TodoFromJson))
|
Schema.compose(Schema.Chunk(Todo.TodoFromJson)),
|
||||||
|
Schema.decode,
|
||||||
)),
|
)),
|
||||||
Effect.catchTag("NoSuchElementException", () => Effect.succeed(Chunk.empty<Todo.Todo>())),
|
Effect.flatMap(v => Ref.set(todos, v)),
|
||||||
|
|
||||||
|
Effect.catchTag("NoSuchElementException", () => Ref.set(todos, Chunk.empty())),
|
||||||
|
|
||||||
Effect.provide(BrowserKeyValueStore.layerLocalStorage),
|
Effect.provide(BrowserKeyValueStore.layerLocalStorage),
|
||||||
)
|
)
|
||||||
|
|
||||||
const writeToLocalStorage = (values: Chunk.Chunk<Todo.Todo>) => KeyValueStore.KeyValueStore.pipe(
|
const saveToLocalStorage = Effect.all([KeyValueStore.KeyValueStore, todos]).pipe(
|
||||||
Effect.flatMap(kv => values.pipe(
|
Effect.flatMap(([kv, values]) => values.pipe(
|
||||||
Schema.encode(
|
Schema.parseJson().pipe(
|
||||||
Schema.compose(Schema.parseJson(), Schema.Chunk(Todo.TodoFromJson))
|
Schema.compose(Schema.Chunk(Todo.TodoFromJson)),
|
||||||
|
Schema.encode,
|
||||||
),
|
),
|
||||||
Effect.flatMap(v => kv.set(key, v)),
|
Effect.flatMap(v => kv.set(key, v)),
|
||||||
)),
|
)),
|
||||||
|
|
||||||
Effect.provide(BrowserKeyValueStore.layerLocalStorage),
|
Effect.provide(BrowserKeyValueStore.layerLocalStorage),
|
||||||
)
|
)
|
||||||
|
|
||||||
const todos = yield* SubscriptionRef.make(yield* readFromLocalStorage)
|
const prepend = (todo: Todo.Todo) => Ref.update(todos, Chunk.prepend(todo))
|
||||||
const load = Effect.flatMap(readFromLocalStorage, v => Ref.set(todos, v))
|
const replace = (index: number, todo: Todo.Todo) => Ref.update(todos, Chunk.replace(index, todo))
|
||||||
const save = Effect.flatMap(todos, writeToLocalStorage)
|
const remove = (index: number) => Ref.update(todos, Chunk.remove(index))
|
||||||
|
|
||||||
// Sync changes with local storage
|
// const moveUp = (index: number) => Effect.gen(function*() {
|
||||||
yield* Effect.forkScoped(Stream.runForEach(todos.changes, writeToLocalStorage))
|
|
||||||
|
|
||||||
return { todos, load, save }
|
// })
|
||||||
|
|
||||||
|
yield* readFromLocalStorage
|
||||||
|
|
||||||
|
return {
|
||||||
|
todos,
|
||||||
|
readFromLocalStorage,
|
||||||
|
saveToLocalStorage,
|
||||||
|
prepend,
|
||||||
|
replace,
|
||||||
|
remove,
|
||||||
|
}
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -1,27 +1,25 @@
|
|||||||
import { Todo } from "@/domain"
|
import { Todo } from "@/domain"
|
||||||
import { Box, Button, Card, Flex, TextArea } from "@radix-ui/themes"
|
import { Box, Button, Card, Flex, TextArea } from "@radix-ui/themes"
|
||||||
import { GetRandomValues, makeUuid4 } from "@typed/id"
|
import { Effect, Option, SubscriptionRef } from "effect"
|
||||||
import { Chunk, Effect, Option, Ref } from "effect"
|
|
||||||
import { R } from "../reffuse"
|
import { R } from "../reffuse"
|
||||||
import { TodosState } from "../services"
|
import { TodosState } from "../services"
|
||||||
|
|
||||||
|
|
||||||
const createEmptyTodo = makeUuid4.pipe(
|
const createEmptyTodo = Todo.generateUniqueID.pipe(
|
||||||
Effect.map(id => Todo.Todo.make({ id, content: "", completedAt: Option.none()}, true)),
|
Effect.map(id => Todo.Todo.make({
|
||||||
Effect.provide(GetRandomValues.CryptoRandom),
|
id,
|
||||||
|
content: "",
|
||||||
|
completedAt: Option.none(),
|
||||||
|
}, true))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
export function VNewTodo() {
|
export function VNewTodo() {
|
||||||
|
|
||||||
const todoRef = R.useRef(() => createEmptyTodo)
|
const runSync = R.useRunSync()
|
||||||
const [content, setContent] = R.useRefState(R.useSubRefFromPath(todoRef, ["content"]))
|
|
||||||
|
|
||||||
const add = R.useCallbackSync(() => Effect.all([TodosState.TodosState, todoRef]).pipe(
|
const todoRef = R.useMemo(() => createEmptyTodo.pipe(Effect.flatMap(SubscriptionRef.make)), [])
|
||||||
Effect.flatMap(([state, todo]) => Ref.update(state.todos, Chunk.prepend(todo))),
|
const [todo, setTodo] = R.useRefState(todoRef)
|
||||||
Effect.andThen(createEmptyTodo),
|
|
||||||
Effect.flatMap(v => Ref.set(todoRef, v)),
|
|
||||||
), [todoRef])
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -29,12 +27,23 @@ export function VNewTodo() {
|
|||||||
<Card>
|
<Card>
|
||||||
<Flex direction="column" align="stretch" gap="2">
|
<Flex direction="column" align="stretch" gap="2">
|
||||||
<TextArea
|
<TextArea
|
||||||
value={content}
|
value={todo.content}
|
||||||
onChange={e => setContent(e.target.value)}
|
onChange={e => setTodo(prev =>
|
||||||
|
Todo.Todo.make({ ...prev, content: e.target.value }, true)
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Flex direction="row" justify="center" align="center">
|
<Flex direction="row" justify="center" align="center">
|
||||||
<Button onClick={add}>Add</Button>
|
<Button
|
||||||
|
onClick={() => TodosState.TodosState.pipe(
|
||||||
|
Effect.flatMap(state => state.prepend(todo)),
|
||||||
|
Effect.andThen(createEmptyTodo),
|
||||||
|
Effect.map(setTodo),
|
||||||
|
runSync,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
Add
|
||||||
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -1,28 +1,20 @@
|
|||||||
import { Todo } from "@/domain"
|
import { Todo } from "@/domain"
|
||||||
import { Box, Card, Flex, IconButton, TextArea } from "@radix-ui/themes"
|
import { Box, Card, Flex, IconButton, TextArea } from "@radix-ui/themes"
|
||||||
import { Effect, Ref, Stream, SubscriptionRef } from "effect"
|
import { Effect } from "effect"
|
||||||
import { Delete } from "lucide-react"
|
import { Delete } from "lucide-react"
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
import { R } from "../reffuse"
|
import { R } from "../reffuse"
|
||||||
|
import { TodosState } from "../services"
|
||||||
|
|
||||||
|
|
||||||
export interface VTodoProps {
|
export interface VTodoProps {
|
||||||
readonly todoRef: SubscriptionRef.SubscriptionRef<Todo.Todo>
|
readonly index: number
|
||||||
readonly remove: Effect.Effect<void>
|
readonly todo: Todo.Todo
|
||||||
}
|
}
|
||||||
|
|
||||||
export function VTodo({ todoRef, remove }: VTodoProps) {
|
export function VTodo({ index, todo }: VTodoProps) {
|
||||||
|
|
||||||
const runSync = R.useRunSync()
|
const runSync = R.useRunSync()
|
||||||
|
|
||||||
const localTodoRef = R.useRef(() => todoRef)
|
|
||||||
const [content, setContent] = R.useRefState(R.useSubRefFromPath(localTodoRef, ["content"]))
|
|
||||||
|
|
||||||
R.useFork(() => localTodoRef.changes.pipe(
|
|
||||||
Stream.debounce("250 millis"),
|
|
||||||
Stream.runForEach(v => Ref.set(todoRef, v)),
|
|
||||||
), [localTodoRef])
|
|
||||||
|
|
||||||
const editorMode = useState(false)
|
const editorMode = useState(false)
|
||||||
|
|
||||||
|
|
||||||
@@ -31,8 +23,14 @@ export function VTodo({ todoRef, remove }: VTodoProps) {
|
|||||||
<Card>
|
<Card>
|
||||||
<Flex direction="column" align="stretch" gap="1">
|
<Flex direction="column" align="stretch" gap="1">
|
||||||
<TextArea
|
<TextArea
|
||||||
value={content}
|
value={todo.content}
|
||||||
onChange={e => setContent(e.target.value)}
|
onChange={e => TodosState.TodosState.pipe(
|
||||||
|
Effect.flatMap(state => state.replace(
|
||||||
|
index,
|
||||||
|
Todo.Todo.make({ ...todo, content: e.target.value }, true),
|
||||||
|
)),
|
||||||
|
runSync,
|
||||||
|
)}
|
||||||
disabled={!editorMode}
|
disabled={!editorMode}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -40,7 +38,12 @@ export function VTodo({ todoRef, remove }: VTodoProps) {
|
|||||||
<Box></Box>
|
<Box></Box>
|
||||||
|
|
||||||
<Flex direction="row" align="center" gap="1">
|
<Flex direction="row" align="center" gap="1">
|
||||||
<IconButton onClick={() => runSync(remove)}>
|
<IconButton
|
||||||
|
onClick={() => TodosState.TodosState.pipe(
|
||||||
|
Effect.flatMap(state => state.remove(index)),
|
||||||
|
runSync,
|
||||||
|
)}
|
||||||
|
>
|
||||||
<Delete />
|
<Delete />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Box, Flex } from "@radix-ui/themes"
|
import { Box, Flex } from "@radix-ui/themes"
|
||||||
import { Chunk, Effect, Ref } from "effect"
|
import { Chunk, Effect, Stream } from "effect"
|
||||||
import { R } from "../reffuse"
|
import { R } from "../reffuse"
|
||||||
import { TodosState } from "../services"
|
import { TodosState } from "../services"
|
||||||
import { VNewTodo } from "./VNewTodo"
|
import { VNewTodo } from "./VNewTodo"
|
||||||
@@ -8,7 +8,14 @@ import { VTodo } from "./VTodo"
|
|||||||
|
|
||||||
export function VTodos() {
|
export function VTodos() {
|
||||||
|
|
||||||
const todosRef = R.useMemo(() => Effect.map(TodosState.TodosState, state => state.todos), [])
|
// Sync changes to the todos with the local storage
|
||||||
|
R.useFork(() => TodosState.TodosState.pipe(
|
||||||
|
Effect.flatMap(state =>
|
||||||
|
Stream.runForEach(state.todos.changes, () => state.saveToLocalStorage)
|
||||||
|
)
|
||||||
|
), [])
|
||||||
|
|
||||||
|
const todosRef = R.useMemo(() => TodosState.TodosState.pipe(Effect.map(state => state.todos)), [])
|
||||||
const [todos] = R.useSubscribeRefs(todosRef)
|
const [todos] = R.useSubscribeRefs(todosRef)
|
||||||
|
|
||||||
|
|
||||||
@@ -20,16 +27,7 @@ export function VTodos() {
|
|||||||
|
|
||||||
{Chunk.map(todos, (todo, index) => (
|
{Chunk.map(todos, (todo, index) => (
|
||||||
<Box key={todo.id} width="500px">
|
<Box key={todo.id} width="500px">
|
||||||
<R.SubRefFromGetSet
|
<VTodo index={index} todo={todo} />
|
||||||
parent={todosRef}
|
|
||||||
getter={parentValue => Chunk.unsafeGet(parentValue, index)}
|
|
||||||
setter={(parentValue, value) => Chunk.replace(parentValue, index, value)}
|
|
||||||
>
|
|
||||||
{ref => <VTodo
|
|
||||||
todoRef={ref}
|
|
||||||
remove={Ref.update(todosRef, Chunk.remove(index))}
|
|
||||||
/>}
|
|
||||||
</R.SubRefFromGetSet>
|
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@typed/lazy-ref": "^0.3.0",
|
"@typed/lazy-ref": "^0.3.0",
|
||||||
"@types/react": "^19.0.0",
|
"@types/react": "^19.0.0",
|
||||||
"effect": "^3.15.0",
|
"effect": "^3.13.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"reffuse": "^0.1.8"
|
"reffuse": "^0.1.8"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@reffuse/extension-query",
|
"name": "@reffuse/extension-query",
|
||||||
"version": "0.1.5",
|
"version": "0.1.3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"files": [
|
"files": [
|
||||||
"./README.md",
|
"./README.md",
|
||||||
@@ -37,8 +37,8 @@
|
|||||||
"@effect/platform-browser": "^0.56.0",
|
"@effect/platform-browser": "^0.56.0",
|
||||||
"@typed/async-data": "^0.13.0",
|
"@typed/async-data": "^0.13.0",
|
||||||
"@types/react": "^19.0.0",
|
"@types/react": "^19.0.0",
|
||||||
"effect": "^3.15.0",
|
"effect": "^3.13.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"reffuse": "^0.1.11"
|
"reffuse": "^0.1.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
packages/extension-query/src/MutationService.ts
Normal file
16
packages/extension-query/src/MutationService.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import type * as AsyncData from "@typed/async-data"
|
||||||
|
import { Effect, type Fiber, type Stream, type SubscriptionRef } from "effect"
|
||||||
|
|
||||||
|
|
||||||
|
export interface MutationService<K extends readonly unknown[], A, E> {
|
||||||
|
readonly state: SubscriptionRef.SubscriptionRef<AsyncData.AsyncData<A, E>>
|
||||||
|
readonly mutate: (...key: K) => Effect.Effect<AsyncData.Success<A> | AsyncData.Failure<E>>
|
||||||
|
readonly forkMutate: (...key: K) => Effect.Effect<readonly [
|
||||||
|
fiber: Fiber.RuntimeFiber<AsyncData.Success<A> | AsyncData.Failure<E>>,
|
||||||
|
state: Stream.Stream<AsyncData.AsyncData<A, E>>,
|
||||||
|
]>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Tag = <const Id extends string>(id: Id) => <
|
||||||
|
Self, K extends readonly unknown[], A, E = never,
|
||||||
|
>() => Effect.Tag(id)<Self, MutationService<K, A, E>>()
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Context, Effect, identity, Layer } from "effect"
|
import { Context, Effect, Layer } from "effect"
|
||||||
import type { Mutable } from "effect/Types"
|
import type { Mutable } from "effect/Types"
|
||||||
import * as QueryErrorHandler from "./QueryErrorHandler.js"
|
import * as QueryErrorHandler from "./QueryErrorHandler.js"
|
||||||
|
|
||||||
@@ -8,17 +8,6 @@ export interface QueryClient<FallbackA, HandledE> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface MakeProps<FallbackA, HandledE> {
|
|
||||||
readonly errorHandler: QueryErrorHandler.QueryErrorHandler<FallbackA, HandledE>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const make = <FallbackA, HandledE>(
|
|
||||||
{ errorHandler }: MakeProps<FallbackA, HandledE>
|
|
||||||
): Effect.Effect<QueryClient<FallbackA, HandledE>> => Effect.Do.pipe(
|
|
||||||
Effect.let("errorHandler", () => errorHandler)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
const id = "@reffuse/extension-query/QueryClient"
|
const id = "@reffuse/extension-query/QueryClient"
|
||||||
|
|
||||||
export type TagClassShape<FallbackA, HandledE> = Context.TagClassShape<typeof id, QueryClient<FallbackA, HandledE>>
|
export type TagClassShape<FallbackA, HandledE> = Context.TagClassShape<typeof id, QueryClient<FallbackA, HandledE>>
|
||||||
@@ -30,28 +19,46 @@ export type GenericTagClass<FallbackA, HandledE> = Context.TagClass<
|
|||||||
export const makeGenericTagClass = <FallbackA = never, HandledE = never>(): GenericTagClass<FallbackA, HandledE> => Context.Tag(id)()
|
export const makeGenericTagClass = <FallbackA = never, HandledE = never>(): GenericTagClass<FallbackA, HandledE> => Context.Tag(id)()
|
||||||
|
|
||||||
|
|
||||||
export interface ServiceProps<FallbackA = never, HandledE = never, E = never, R = never> {
|
export interface ServiceProps<EH, FallbackA, HandledE> {
|
||||||
readonly errorHandler?: Effect.Effect<QueryErrorHandler.QueryErrorHandler<FallbackA, HandledE>, E, R>
|
readonly ErrorHandler?: Context.Tag<EH, QueryErrorHandler.QueryErrorHandler<FallbackA, HandledE>>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ServiceResult<Self, FallbackA, HandledE, E, R> extends Context.TagClass<
|
export interface ServiceResult<Self, EH, FallbackA, HandledE> extends Context.TagClass<
|
||||||
Self,
|
Self,
|
||||||
typeof id,
|
typeof id,
|
||||||
QueryClient<FallbackA, HandledE>
|
QueryClient<FallbackA, HandledE>
|
||||||
> {
|
> {
|
||||||
readonly Default: Layer.Layer<Self, E, R>
|
readonly Live: Layer.Layer<
|
||||||
|
Self | (EH extends QueryErrorHandler.DefaultQueryErrorHandler ? EH : never),
|
||||||
|
never,
|
||||||
|
EH extends QueryErrorHandler.DefaultQueryErrorHandler ? never : EH
|
||||||
|
>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Service = <Self>() => (
|
export const Service = <Self>() => (
|
||||||
<FallbackA = never, HandledE = never, E = never, R = never>(
|
<
|
||||||
props?: ServiceProps<FallbackA, HandledE, E, R>
|
EH = QueryErrorHandler.DefaultQueryErrorHandler,
|
||||||
): ServiceResult<Self, FallbackA, HandledE, E, R> => {
|
FallbackA = QueryErrorHandler.Fallback<Context.Tag.Service<QueryErrorHandler.DefaultQueryErrorHandler>>,
|
||||||
const TagClass = Context.Tag(id)() as ServiceResult<Self, FallbackA, HandledE, E, R>
|
HandledE = QueryErrorHandler.Error<Context.Tag.Service<QueryErrorHandler.DefaultQueryErrorHandler>>,
|
||||||
|
>(
|
||||||
|
props?: ServiceProps<EH, FallbackA, HandledE>
|
||||||
|
): ServiceResult<Self, EH, FallbackA, HandledE> => {
|
||||||
|
const TagClass = Context.Tag(id)() as ServiceResult<Self, EH, FallbackA, HandledE>
|
||||||
|
|
||||||
(TagClass as Mutable<typeof TagClass>).Default = Layer.effect(TagClass, Effect.flatMap(
|
(TagClass as Mutable<typeof TagClass>).Live = Layer.effect(TagClass, Effect.Do.pipe(
|
||||||
props?.errorHandler ?? QueryErrorHandler.make<never>()(identity),
|
Effect.bind("errorHandler", () =>
|
||||||
errorHandler => make({ errorHandler }),
|
(props?.ErrorHandler ?? QueryErrorHandler.DefaultQueryErrorHandler) as Effect.Effect<
|
||||||
))
|
QueryErrorHandler.QueryErrorHandler<FallbackA, HandledE>,
|
||||||
|
never,
|
||||||
|
EH extends QueryErrorHandler.DefaultQueryErrorHandler ? never : EH
|
||||||
|
>
|
||||||
|
)
|
||||||
|
)).pipe(
|
||||||
|
Layer.provideMerge((props?.ErrorHandler
|
||||||
|
? Layer.empty
|
||||||
|
: QueryErrorHandler.DefaultQueryErrorHandler.Live
|
||||||
|
) as Layer.Layer<EH>)
|
||||||
|
)
|
||||||
|
|
||||||
return TagClass
|
return TagClass
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Cause, Effect, PubSub, Stream } from "effect"
|
import { Cause, Context, Effect, identity, Layer, PubSub, Stream } from "effect"
|
||||||
|
import type { Mutable } from "effect/Types"
|
||||||
|
|
||||||
|
|
||||||
export interface QueryErrorHandler<FallbackA, HandledE> {
|
export interface QueryErrorHandler<FallbackA, HandledE> {
|
||||||
@@ -10,31 +11,55 @@ export type Fallback<T> = T extends QueryErrorHandler<infer A, any> ? A : never
|
|||||||
export type Error<T> = T extends QueryErrorHandler<any, infer E> ? E : never
|
export type Error<T> = T extends QueryErrorHandler<any, infer E> ? E : never
|
||||||
|
|
||||||
|
|
||||||
export const make = <HandledE = never>() => (
|
export interface ServiceResult<
|
||||||
<FallbackA>(
|
Self,
|
||||||
|
Id extends string,
|
||||||
|
FallbackA,
|
||||||
|
HandledE,
|
||||||
|
> extends Context.TagClass<
|
||||||
|
Self,
|
||||||
|
Id,
|
||||||
|
QueryErrorHandler<FallbackA, HandledE>
|
||||||
|
> {
|
||||||
|
readonly Live: Layer.Layer<Self>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Service = <Self, HandledE = never>() => (
|
||||||
|
<const Id extends string, FallbackA>(
|
||||||
|
id: Id,
|
||||||
f: (
|
f: (
|
||||||
self: Effect.Effect<never, HandledE>,
|
self: Effect.Effect<never, HandledE>,
|
||||||
failure: (failure: HandledE) => Effect.Effect<never>,
|
failure: (failure: HandledE) => Effect.Effect<never>,
|
||||||
defect: (defect: unknown) => Effect.Effect<never>,
|
defect: (defect: unknown) => Effect.Effect<never>,
|
||||||
) => Effect.Effect<FallbackA>
|
) => Effect.Effect<FallbackA>,
|
||||||
): Effect.Effect<QueryErrorHandler<FallbackA, HandledE>> => Effect.gen(function*() {
|
): ServiceResult<Self, Id, FallbackA, HandledE> => {
|
||||||
const pubsub = yield* PubSub.unbounded<Cause.Cause<HandledE>>()
|
const TagClass = Context.Tag(id)() as ServiceResult<Self, Id, FallbackA, HandledE>
|
||||||
const errors = Stream.fromPubSub(pubsub)
|
|
||||||
|
|
||||||
const handle = <A, E, R>(
|
(TagClass as Mutable<typeof TagClass>).Live = Layer.effect(TagClass, Effect.gen(function*() {
|
||||||
self: Effect.Effect<A, E, R>
|
const pubsub = yield* PubSub.unbounded<Cause.Cause<HandledE>>()
|
||||||
): Effect.Effect<A | FallbackA, Exclude<E, HandledE>, R> => f(
|
const errors = Stream.fromPubSub(pubsub)
|
||||||
self as unknown as Effect.Effect<never, HandledE, never>,
|
|
||||||
(failure: HandledE) => Effect.andThen(
|
|
||||||
PubSub.publish(pubsub, Cause.fail(failure)),
|
|
||||||
Effect.failCause(Cause.empty),
|
|
||||||
),
|
|
||||||
(defect: unknown) => Effect.andThen(
|
|
||||||
PubSub.publish(pubsub, Cause.die(defect)),
|
|
||||||
Effect.failCause(Cause.empty),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
return { errors, handle }
|
const handle = <A, E, R>(
|
||||||
})
|
self: Effect.Effect<A, E, R>
|
||||||
|
): Effect.Effect<A | FallbackA, Exclude<E, HandledE>, R> => f(
|
||||||
|
self as unknown as Effect.Effect<never, HandledE, never>,
|
||||||
|
(failure: HandledE) => PubSub.publish(pubsub, Cause.fail(failure)).pipe(
|
||||||
|
Effect.andThen(Effect.failCause(Cause.empty))
|
||||||
|
),
|
||||||
|
(defect: unknown) => PubSub.publish(pubsub, Cause.die(defect)).pipe(
|
||||||
|
Effect.andThen(Effect.failCause(Cause.empty))
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return { errors, handle }
|
||||||
|
}))
|
||||||
|
|
||||||
|
return TagClass
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
export class DefaultQueryErrorHandler extends Service<DefaultQueryErrorHandler>()(
|
||||||
|
"@reffuse/extension-query/DefaultQueryErrorHandler",
|
||||||
|
identity,
|
||||||
|
) {}
|
||||||
|
|||||||
@@ -1,21 +1,53 @@
|
|||||||
import type { Effect, Stream } from "effect"
|
import type * as AsyncData from "@typed/async-data"
|
||||||
|
import { type Cause, type Context, Effect, type Fiber, Layer, type Option, type Stream, type SubscriptionRef } from "effect"
|
||||||
|
import * as React from "react"
|
||||||
import { ReffuseExtension, type ReffuseNamespace } from "reffuse"
|
import { ReffuseExtension, type ReffuseNamespace } from "reffuse"
|
||||||
import * as MutationRunner from "./MutationRunner.js"
|
import type * as MutationService from "./MutationService.js"
|
||||||
import * as QueryClient from "./QueryClient.js"
|
import * as QueryClient from "./QueryClient.js"
|
||||||
import type * as QueryProgress from "./QueryProgress.js"
|
import type * as QueryProgress from "./QueryProgress.js"
|
||||||
import * as QueryRunner from "./QueryRunner.js"
|
import type * as QueryService from "./QueryService.js"
|
||||||
|
import { MutationRunner, QueryRunner } from "./internal/index.js"
|
||||||
|
|
||||||
|
|
||||||
export interface UseQueryProps<K extends readonly unknown[], A, E, R> {
|
export interface UseQueryProps<K extends readonly unknown[], A, E, R> {
|
||||||
readonly key: Stream.Stream<K>
|
readonly key: Stream.Stream<K>
|
||||||
readonly query: (key: K) => Effect.Effect<A, E, R | QueryProgress.QueryProgress>
|
readonly query: (key: K) => Effect.Effect<A, E, R | QueryProgress.QueryProgress>
|
||||||
readonly options?: QueryRunner.RunOptions
|
readonly refreshOnWindowFocus?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UseQueryResult<K extends readonly unknown[], A, E> {
|
||||||
|
readonly latestKey: SubscriptionRef.SubscriptionRef<Option.Option<K>>
|
||||||
|
readonly state: SubscriptionRef.SubscriptionRef<AsyncData.AsyncData<A, E>>
|
||||||
|
|
||||||
|
readonly forkRefresh: Effect.Effect<readonly [
|
||||||
|
fiber: Fiber.RuntimeFiber<AsyncData.Success<A> | AsyncData.Failure<E>, Cause.NoSuchElementException>,
|
||||||
|
state: Stream.Stream<AsyncData.AsyncData<A, E>>,
|
||||||
|
]>
|
||||||
|
|
||||||
|
readonly layer: <Self, Id extends string>(
|
||||||
|
tag: Context.TagClass<Self, Id, QueryService.QueryService<K, A, E>>
|
||||||
|
) => Layer.Layer<Self>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface UseMutationProps<K extends readonly unknown[], A, E, R> {
|
export interface UseMutationProps<K extends readonly unknown[], A, E, R> {
|
||||||
readonly mutation: (key: K) => Effect.Effect<A, E, R | QueryProgress.QueryProgress>
|
readonly mutation: (key: K) => Effect.Effect<A, E, R | QueryProgress.QueryProgress>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UseMutationResult<K extends readonly unknown[], A, E> {
|
||||||
|
readonly state: SubscriptionRef.SubscriptionRef<AsyncData.AsyncData<A, E>>
|
||||||
|
|
||||||
|
readonly mutate: (...key: K) => Effect.Effect<AsyncData.Success<A> | AsyncData.Failure<E>>
|
||||||
|
readonly forkMutate: (...key: K) => Effect.Effect<readonly [
|
||||||
|
fiber: Fiber.RuntimeFiber<AsyncData.Success<A> | AsyncData.Failure<E>>,
|
||||||
|
state: Stream.Stream<AsyncData.AsyncData<A, E>>,
|
||||||
|
]>
|
||||||
|
|
||||||
|
readonly layer: <Self, Id extends string>(
|
||||||
|
tag: Context.TagClass<Self, Id, MutationService.MutationService<K, A, E>>
|
||||||
|
) => Layer.Layer<Self>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export const QueryExtension = ReffuseExtension.make(() => ({
|
export const QueryExtension = ReffuseExtension.make(() => ({
|
||||||
useQuery<
|
useQuery<
|
||||||
@@ -29,16 +61,32 @@ export const QueryExtension = ReffuseExtension.make(() => ({
|
|||||||
>(
|
>(
|
||||||
this: ReffuseNamespace.ReffuseNamespace<R | QueryClient.TagClassShape<FallbackA, HandledE>>,
|
this: ReffuseNamespace.ReffuseNamespace<R | QueryClient.TagClassShape<FallbackA, HandledE>>,
|
||||||
props: UseQueryProps<QK, QA, QE, QR>,
|
props: UseQueryProps<QK, QA, QE, QR>,
|
||||||
): QueryRunner.QueryRunner<QK, QA | FallbackA, Exclude<QE, HandledE>> {
|
): UseQueryResult<QK, QA | FallbackA, Exclude<QE, HandledE>> {
|
||||||
const runner = this.useMemo(() => QueryRunner.make({
|
const runner = this.useMemo(() => QueryRunner.make({
|
||||||
QueryClient: QueryClient.makeGenericTagClass<FallbackA, HandledE>(),
|
QueryClient: QueryClient.makeGenericTagClass<FallbackA, HandledE>(),
|
||||||
key: props.key,
|
key: props.key,
|
||||||
query: props.query,
|
query: props.query,
|
||||||
}), [props.key])
|
}), [props.key])
|
||||||
|
|
||||||
this.useFork(() => QueryRunner.run(runner, props.options), [runner])
|
this.useFork(() => runner.fetchOnKeyChange, [runner])
|
||||||
|
|
||||||
return runner
|
this.useFork(() => (props.refreshOnWindowFocus ?? true)
|
||||||
|
? runner.refreshOnWindowFocus
|
||||||
|
: Effect.void,
|
||||||
|
[props.refreshOnWindowFocus, runner])
|
||||||
|
|
||||||
|
return React.useMemo(() => ({
|
||||||
|
latestKey: runner.latestKeyRef,
|
||||||
|
state: runner.stateRef,
|
||||||
|
|
||||||
|
forkRefresh: runner.forkRefresh,
|
||||||
|
|
||||||
|
layer: tag => Layer.succeed(tag, {
|
||||||
|
latestKey: runner.latestKeyRef,
|
||||||
|
state: runner.stateRef,
|
||||||
|
forkRefresh: runner.forkRefresh,
|
||||||
|
}),
|
||||||
|
}), [runner])
|
||||||
},
|
},
|
||||||
|
|
||||||
useMutation<
|
useMutation<
|
||||||
@@ -52,10 +100,23 @@ export const QueryExtension = ReffuseExtension.make(() => ({
|
|||||||
>(
|
>(
|
||||||
this: ReffuseNamespace.ReffuseNamespace<R | QueryClient.TagClassShape<FallbackA, HandledE>>,
|
this: ReffuseNamespace.ReffuseNamespace<R | QueryClient.TagClassShape<FallbackA, HandledE>>,
|
||||||
props: UseMutationProps<QK, QA, QE, QR>,
|
props: UseMutationProps<QK, QA, QE, QR>,
|
||||||
): MutationRunner.MutationRunner<QK, QA | FallbackA, Exclude<QE, HandledE>> {
|
): UseMutationResult<QK, QA | FallbackA, Exclude<QE, HandledE>> {
|
||||||
return this.useMemo(() => MutationRunner.make({
|
const runner = this.useMemo(() => MutationRunner.make({
|
||||||
QueryClient: QueryClient.makeGenericTagClass<FallbackA, HandledE>(),
|
QueryClient: QueryClient.makeGenericTagClass<FallbackA, HandledE>(),
|
||||||
mutation: props.mutation,
|
mutation: props.mutation,
|
||||||
}), [])
|
}), [])
|
||||||
|
|
||||||
|
return React.useMemo(() => ({
|
||||||
|
state: runner.stateRef,
|
||||||
|
|
||||||
|
mutate: runner.mutate,
|
||||||
|
forkMutate: runner.forkMutate,
|
||||||
|
|
||||||
|
layer: tag => Layer.succeed(tag, {
|
||||||
|
state: runner.stateRef,
|
||||||
|
mutate: runner.mutate,
|
||||||
|
forkMutate: runner.forkMutate,
|
||||||
|
}),
|
||||||
|
}), [runner])
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export class QueryProgress extends Effect.Tag("@reffuse/extension-query/QueryPro
|
|||||||
f: (previous: Option.Option<AsyncData.Progress>) => AsyncData.Progress
|
f: (previous: Option.Option<AsyncData.Progress>) => AsyncData.Progress
|
||||||
) => Effect.Effect<void>
|
) => Effect.Effect<void>
|
||||||
}>() {
|
}>() {
|
||||||
static readonly Default: Layer.Layer<
|
static readonly Live: Layer.Layer<
|
||||||
QueryProgress,
|
QueryProgress,
|
||||||
never,
|
never,
|
||||||
QueryState.QueryState<any, any>
|
QueryState.QueryState<any, any>
|
||||||
|
|||||||
@@ -1,193 +0,0 @@
|
|||||||
import { BrowserStream } from "@effect/platform-browser"
|
|
||||||
import * as AsyncData from "@typed/async-data"
|
|
||||||
import { type Cause, Effect, Fiber, identity, Option, Queue, Ref, type Scope, Stream, SubscriptionRef } from "effect"
|
|
||||||
import type * as QueryClient from "./QueryClient.js"
|
|
||||||
import * as QueryProgress from "./QueryProgress.js"
|
|
||||||
import { QueryState } from "./internal/index.js"
|
|
||||||
|
|
||||||
|
|
||||||
export interface QueryRunner<K extends readonly unknown[], A, E> {
|
|
||||||
readonly queryKey: Stream.Stream<K>
|
|
||||||
readonly latestKeyValueRef: SubscriptionRef.SubscriptionRef<Option.Option<K>>
|
|
||||||
readonly stateRef: SubscriptionRef.SubscriptionRef<AsyncData.AsyncData<A, E>>
|
|
||||||
readonly fiberRef: SubscriptionRef.SubscriptionRef<Option.Option<Fiber.RuntimeFiber<
|
|
||||||
AsyncData.Success<A> | AsyncData.Failure<E>,
|
|
||||||
Cause.NoSuchElementException
|
|
||||||
>>>
|
|
||||||
|
|
||||||
readonly interrupt: Effect.Effect<void>
|
|
||||||
readonly forkInterrupt: Effect.Effect<Fiber.RuntimeFiber<void>>
|
|
||||||
readonly forkFetch: (keyValue: K) => Effect.Effect<readonly [
|
|
||||||
fiber: Fiber.RuntimeFiber<AsyncData.Success<A> | AsyncData.Failure<E>>,
|
|
||||||
state: Stream.Stream<AsyncData.AsyncData<A, E>>,
|
|
||||||
]>
|
|
||||||
readonly forkRefresh: Effect.Effect<readonly [
|
|
||||||
fiber: Fiber.RuntimeFiber<AsyncData.Success<A> | AsyncData.Failure<E>, Cause.NoSuchElementException>,
|
|
||||||
state: Stream.Stream<AsyncData.AsyncData<A, E>>,
|
|
||||||
]>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export const Tag = <const Id extends string>(id: Id) => <
|
|
||||||
Self, K extends readonly unknown[], A, E = never
|
|
||||||
>() => Effect.Tag(id)<Self, QueryRunner<K, A, E>>()
|
|
||||||
|
|
||||||
|
|
||||||
export interface MakeProps<K extends readonly unknown[], A, FallbackA, E, HandledE, R> {
|
|
||||||
readonly QueryClient: QueryClient.GenericTagClass<FallbackA, HandledE>
|
|
||||||
readonly key: Stream.Stream<K>
|
|
||||||
readonly query: (key: K) => Effect.Effect<A, E, R | QueryProgress.QueryProgress>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const make = <K extends readonly unknown[], A, FallbackA, E, HandledE, R>(
|
|
||||||
{
|
|
||||||
QueryClient,
|
|
||||||
key,
|
|
||||||
query,
|
|
||||||
}: MakeProps<K, A, FallbackA, E, HandledE, R>
|
|
||||||
): Effect.Effect<
|
|
||||||
QueryRunner<K, A | FallbackA, Exclude<E, HandledE>>,
|
|
||||||
never,
|
|
||||||
R | QueryClient.TagClassShape<FallbackA, HandledE>
|
|
||||||
> => Effect.gen(function*() {
|
|
||||||
const context = yield* Effect.context<R | QueryClient.TagClassShape<FallbackA, HandledE>>()
|
|
||||||
|
|
||||||
const latestKeyValueRef = yield* SubscriptionRef.make(Option.none<K>())
|
|
||||||
const stateRef = yield* SubscriptionRef.make(AsyncData.noData<A | FallbackA, Exclude<E, HandledE>>())
|
|
||||||
const fiberRef = yield* SubscriptionRef.make(Option.none<Fiber.RuntimeFiber<
|
|
||||||
AsyncData.Success<A | FallbackA> | AsyncData.Failure<Exclude<E, HandledE>>,
|
|
||||||
Cause.NoSuchElementException
|
|
||||||
>>())
|
|
||||||
|
|
||||||
const queryStateTag = QueryState.makeTag<A | FallbackA, Exclude<E, HandledE>>()
|
|
||||||
|
|
||||||
const interrupt = Effect.flatMap(fiberRef, Option.match({
|
|
||||||
onSome: fiber => Ref.set(fiberRef, Option.none()).pipe(
|
|
||||||
Effect.andThen(Fiber.interrupt(fiber))
|
|
||||||
),
|
|
||||||
onNone: () => Effect.void,
|
|
||||||
}))
|
|
||||||
|
|
||||||
const forkInterrupt = Effect.flatMap(fiberRef, Option.match({
|
|
||||||
onSome: fiber => Ref.set(fiberRef, Option.none()).pipe(
|
|
||||||
Effect.andThen(Fiber.interrupt(fiber).pipe(
|
|
||||||
Effect.asVoid,
|
|
||||||
Effect.forkDaemon,
|
|
||||||
))
|
|
||||||
),
|
|
||||||
onNone: () => Effect.forkDaemon(Effect.void),
|
|
||||||
}))
|
|
||||||
|
|
||||||
const run = (keyValue: K) => Effect.all([QueryClient, queryStateTag]).pipe(
|
|
||||||
Effect.flatMap(([client, state]) => Ref.set(latestKeyValueRef, Option.some(keyValue)).pipe(
|
|
||||||
Effect.andThen(query(keyValue)),
|
|
||||||
client.errorHandler.handle,
|
|
||||||
Effect.matchCauseEffect({
|
|
||||||
onSuccess: v => Effect.tap(Effect.succeed(AsyncData.success(v)), state.set),
|
|
||||||
onFailure: c => Effect.tap(Effect.succeed(AsyncData.failure(c)), state.set),
|
|
||||||
}),
|
|
||||||
)),
|
|
||||||
|
|
||||||
Effect.provide(context),
|
|
||||||
Effect.provide(QueryProgress.QueryProgress.Default),
|
|
||||||
)
|
|
||||||
|
|
||||||
const forkFetch = (keyValue: K) => Queue.unbounded<AsyncData.AsyncData<A | FallbackA, Exclude<E, HandledE>>>().pipe(
|
|
||||||
Effect.flatMap(stateQueue => queryStateTag.pipe(
|
|
||||||
Effect.flatMap(state => interrupt.pipe(
|
|
||||||
Effect.andThen(
|
|
||||||
Effect.addFinalizer(() => Effect.andThen(
|
|
||||||
Ref.set(fiberRef, Option.none()),
|
|
||||||
Queue.shutdown(stateQueue),
|
|
||||||
)).pipe(
|
|
||||||
Effect.andThen(state.set(AsyncData.loading())),
|
|
||||||
Effect.andThen(run(keyValue)),
|
|
||||||
Effect.scoped,
|
|
||||||
Effect.forkDaemon,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
|
|
||||||
Effect.tap(fiber => Ref.set(fiberRef, Option.some(fiber))),
|
|
||||||
Effect.map(fiber => [fiber, Stream.fromQueue(stateQueue)] as const),
|
|
||||||
)),
|
|
||||||
|
|
||||||
Effect.provide(QueryState.layer(
|
|
||||||
queryStateTag,
|
|
||||||
stateRef,
|
|
||||||
value => Effect.andThen(
|
|
||||||
Queue.offer(stateQueue, value),
|
|
||||||
Ref.set(stateRef, value),
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
))
|
|
||||||
)
|
|
||||||
|
|
||||||
const setInitialRefreshState = Effect.flatMap(queryStateTag, state => state.update(previous => {
|
|
||||||
if (AsyncData.isSuccess(previous) || AsyncData.isFailure(previous))
|
|
||||||
return AsyncData.refreshing(previous)
|
|
||||||
if (AsyncData.isRefreshing(previous))
|
|
||||||
return AsyncData.refreshing(previous.previous)
|
|
||||||
return AsyncData.loading()
|
|
||||||
}))
|
|
||||||
|
|
||||||
const forkRefresh = Queue.unbounded<AsyncData.AsyncData<A | FallbackA, Exclude<E, HandledE>>>().pipe(
|
|
||||||
Effect.flatMap(stateQueue => interrupt.pipe(
|
|
||||||
Effect.andThen(
|
|
||||||
Effect.addFinalizer(() => Effect.andThen(
|
|
||||||
Ref.set(fiberRef, Option.none()),
|
|
||||||
Queue.shutdown(stateQueue),
|
|
||||||
)).pipe(
|
|
||||||
Effect.andThen(setInitialRefreshState),
|
|
||||||
Effect.andThen(latestKeyValueRef.pipe(
|
|
||||||
Effect.flatMap(identity),
|
|
||||||
Effect.flatMap(run),
|
|
||||||
)),
|
|
||||||
Effect.scoped,
|
|
||||||
Effect.forkDaemon,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
|
|
||||||
Effect.tap(fiber => Ref.set(fiberRef, Option.some(fiber))),
|
|
||||||
Effect.map(fiber => [fiber, Stream.fromQueue(stateQueue)] as const),
|
|
||||||
|
|
||||||
Effect.provide(QueryState.layer(
|
|
||||||
queryStateTag,
|
|
||||||
stateRef,
|
|
||||||
value => Effect.andThen(
|
|
||||||
Queue.offer(stateQueue, value),
|
|
||||||
Ref.set(stateRef, value),
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
))
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
queryKey: key,
|
|
||||||
latestKeyValueRef,
|
|
||||||
stateRef,
|
|
||||||
fiberRef,
|
|
||||||
|
|
||||||
interrupt,
|
|
||||||
forkInterrupt,
|
|
||||||
forkFetch,
|
|
||||||
forkRefresh,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
export interface RunOptions {
|
|
||||||
readonly refreshOnWindowFocus?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export const run = <K extends readonly unknown[], A, E>(
|
|
||||||
self: QueryRunner<K, A, E>,
|
|
||||||
options?: RunOptions,
|
|
||||||
): Effect.Effect<void, never, Scope.Scope> => Effect.gen(function*() {
|
|
||||||
if (typeof window !== "undefined" && (options?.refreshOnWindowFocus ?? true))
|
|
||||||
yield* Effect.forkScoped(
|
|
||||||
Stream.runForEach(BrowserStream.fromEventListenerWindow("focus"), () => self.forkRefresh)
|
|
||||||
)
|
|
||||||
|
|
||||||
yield* Effect.addFinalizer(() => self.interrupt)
|
|
||||||
yield* Stream.runForEach(Stream.changes(self.queryKey), latestKey => self.forkFetch(latestKey))
|
|
||||||
})
|
|
||||||
16
packages/extension-query/src/QueryService.ts
Normal file
16
packages/extension-query/src/QueryService.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import type * as AsyncData from "@typed/async-data"
|
||||||
|
import { type Cause, Effect, type Fiber, type Option, type Stream, type SubscriptionRef } from "effect"
|
||||||
|
|
||||||
|
|
||||||
|
export interface QueryService<K extends readonly unknown[], A, E> {
|
||||||
|
readonly latestKey: SubscriptionRef.SubscriptionRef<Option.Option<K>>
|
||||||
|
readonly state: SubscriptionRef.SubscriptionRef<AsyncData.AsyncData<A, E>>
|
||||||
|
readonly forkRefresh: Effect.Effect<readonly [
|
||||||
|
fiber: Fiber.RuntimeFiber<AsyncData.Success<A> | AsyncData.Failure<E>, Cause.NoSuchElementException>,
|
||||||
|
state: Stream.Stream<AsyncData.AsyncData<A, E>>,
|
||||||
|
]>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Tag = <const Id extends string>(id: Id) => <
|
||||||
|
Self, K extends readonly unknown[], A, E = never,
|
||||||
|
>() => Effect.Tag(id)<Self, QueryService<K, A, E>>()
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
export * as MutationRunner from "./MutationRunner.js"
|
export * as MutationService from "./MutationService.js"
|
||||||
export * as QueryClient from "./QueryClient.js"
|
export * as QueryClient from "./QueryClient.js"
|
||||||
export * as QueryErrorHandler from "./QueryErrorHandler.js"
|
export * as QueryErrorHandler from "./QueryErrorHandler.js"
|
||||||
export * from "./QueryExtension.js"
|
export * from "./QueryExtension.js"
|
||||||
export * as QueryProgress from "./QueryProgress.js"
|
export * as QueryProgress from "./QueryProgress.js"
|
||||||
export * as QueryRunner from "./QueryRunner.js"
|
export * as QueryService from "./QueryService.js"
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import * as AsyncData from "@typed/async-data"
|
import * as AsyncData from "@typed/async-data"
|
||||||
import { Effect, type Fiber, Queue, Ref, Stream, SubscriptionRef } from "effect"
|
import { type Context, Effect, type Fiber, Queue, Ref, Stream, SubscriptionRef } from "effect"
|
||||||
import type * as QueryClient from "./QueryClient.js"
|
import type * as QueryClient from "../QueryClient.js"
|
||||||
import * as QueryProgress from "./QueryProgress.js"
|
import * as QueryProgress from "../QueryProgress.js"
|
||||||
import { QueryState } from "./internal/index.js"
|
import * as QueryState from "./QueryState.js"
|
||||||
|
|
||||||
|
|
||||||
export interface MutationRunner<K extends readonly unknown[], A, E> {
|
export interface MutationRunner<K extends readonly unknown[], A, E, R> {
|
||||||
|
readonly context: Context.Context<R>
|
||||||
readonly stateRef: SubscriptionRef.SubscriptionRef<AsyncData.AsyncData<A, E>>
|
readonly stateRef: SubscriptionRef.SubscriptionRef<AsyncData.AsyncData<A, E>>
|
||||||
|
|
||||||
readonly mutate: (...key: K) => Effect.Effect<AsyncData.Success<A> | AsyncData.Failure<E>>
|
readonly mutate: (...key: K) => Effect.Effect<AsyncData.Success<A> | AsyncData.Failure<E>>
|
||||||
@@ -16,11 +17,6 @@ export interface MutationRunner<K extends readonly unknown[], A, E> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const Tag = <const Id extends string>(id: Id) => <
|
|
||||||
Self, K extends readonly unknown[], A, E = never,
|
|
||||||
>() => Effect.Tag(id)<Self, MutationRunner<K, A, E>>()
|
|
||||||
|
|
||||||
|
|
||||||
export interface MakeProps<K extends readonly unknown[], A, FallbackA, E, HandledE, R> {
|
export interface MakeProps<K extends readonly unknown[], A, FallbackA, E, HandledE, R> {
|
||||||
readonly QueryClient: QueryClient.GenericTagClass<FallbackA, HandledE>
|
readonly QueryClient: QueryClient.GenericTagClass<FallbackA, HandledE>
|
||||||
readonly mutation: (key: K) => Effect.Effect<A, E, R | QueryProgress.QueryProgress>
|
readonly mutation: (key: K) => Effect.Effect<A, E, R | QueryProgress.QueryProgress>
|
||||||
@@ -32,7 +28,7 @@ export const make = <K extends readonly unknown[], A, FallbackA, E, HandledE, R>
|
|||||||
mutation,
|
mutation,
|
||||||
}: MakeProps<K, A, FallbackA, E, HandledE, R>
|
}: MakeProps<K, A, FallbackA, E, HandledE, R>
|
||||||
): Effect.Effect<
|
): Effect.Effect<
|
||||||
MutationRunner<K, A | FallbackA, Exclude<E, HandledE>>,
|
MutationRunner<K, A | FallbackA, Exclude<E, HandledE>, R>,
|
||||||
never,
|
never,
|
||||||
R | QueryClient.TagClassShape<FallbackA, HandledE>
|
R | QueryClient.TagClassShape<FallbackA, HandledE>
|
||||||
> => Effect.gen(function*() {
|
> => Effect.gen(function*() {
|
||||||
@@ -41,18 +37,25 @@ export const make = <K extends readonly unknown[], A, FallbackA, E, HandledE, R>
|
|||||||
|
|
||||||
const queryStateTag = QueryState.makeTag<A | FallbackA, Exclude<E, HandledE>>()
|
const queryStateTag = QueryState.makeTag<A | FallbackA, Exclude<E, HandledE>>()
|
||||||
|
|
||||||
const run = (key: K) => Effect.all([QueryClient, queryStateTag]).pipe(
|
const run = (key: K) => Effect.Do.pipe(
|
||||||
Effect.flatMap(([client, state]) => state.set(AsyncData.loading()).pipe(
|
Effect.bind("state", () => queryStateTag),
|
||||||
|
Effect.bind("client", () => QueryClient),
|
||||||
|
|
||||||
|
Effect.flatMap(({ state, client }) => state.set(AsyncData.loading()).pipe(
|
||||||
Effect.andThen(mutation(key)),
|
Effect.andThen(mutation(key)),
|
||||||
client.errorHandler.handle,
|
client.errorHandler.handle,
|
||||||
Effect.matchCauseEffect({
|
Effect.matchCauseEffect({
|
||||||
onSuccess: v => Effect.tap(Effect.succeed(AsyncData.success(v)), state.set),
|
onSuccess: v => Effect.succeed(AsyncData.success(v)).pipe(
|
||||||
onFailure: c => Effect.tap(Effect.succeed(AsyncData.failure(c)), state.set),
|
Effect.tap(state.set)
|
||||||
|
),
|
||||||
|
onFailure: c => Effect.succeed(AsyncData.failure(c)).pipe(
|
||||||
|
Effect.tap(state.set)
|
||||||
|
),
|
||||||
}),
|
}),
|
||||||
)),
|
)),
|
||||||
|
|
||||||
Effect.provide(context),
|
Effect.provide(context),
|
||||||
Effect.provide(QueryProgress.QueryProgress.Default),
|
Effect.provide(QueryProgress.QueryProgress.Live),
|
||||||
)
|
)
|
||||||
|
|
||||||
const mutate = (...key: K) => Effect.provide(run(key), QueryState.layer(
|
const mutate = (...key: K) => Effect.provide(run(key), QueryState.layer(
|
||||||
@@ -61,11 +64,11 @@ export const make = <K extends readonly unknown[], A, FallbackA, E, HandledE, R>
|
|||||||
value => Ref.set(globalStateRef, value),
|
value => Ref.set(globalStateRef, value),
|
||||||
))
|
))
|
||||||
|
|
||||||
const forkMutate = (...key: K) => Effect.all([
|
const forkMutate = (...key: K) => Effect.Do.pipe(
|
||||||
Ref.make(AsyncData.noData<A | FallbackA, Exclude<E, HandledE>>()),
|
Effect.bind("stateRef", () => Ref.make(AsyncData.noData<A | FallbackA, Exclude<E, HandledE>>())),
|
||||||
Queue.unbounded<AsyncData.AsyncData<A | FallbackA, Exclude<E, HandledE>>>(),
|
Effect.bind("stateQueue", () => Queue.unbounded<AsyncData.AsyncData<A | FallbackA, Exclude<E, HandledE>>>()),
|
||||||
]).pipe(
|
|
||||||
Effect.flatMap(([stateRef, stateQueue]) =>
|
Effect.flatMap(({ stateRef, stateQueue }) =>
|
||||||
Effect.addFinalizer(() => Queue.shutdown(stateQueue)).pipe(
|
Effect.addFinalizer(() => Queue.shutdown(stateQueue)).pipe(
|
||||||
Effect.andThen(run(key)),
|
Effect.andThen(run(key)),
|
||||||
Effect.scoped,
|
Effect.scoped,
|
||||||
191
packages/extension-query/src/internal/QueryRunner.ts
Normal file
191
packages/extension-query/src/internal/QueryRunner.ts
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
import { BrowserStream } from "@effect/platform-browser"
|
||||||
|
import * as AsyncData from "@typed/async-data"
|
||||||
|
import { type Cause, type Context, Effect, Fiber, identity, Option, Queue, Ref, type Scope, Stream, SubscriptionRef } from "effect"
|
||||||
|
import type * as QueryClient from "../QueryClient.js"
|
||||||
|
import * as QueryProgress from "../QueryProgress.js"
|
||||||
|
import * as QueryState from "./QueryState.js"
|
||||||
|
|
||||||
|
|
||||||
|
export interface QueryRunner<K extends readonly unknown[], A, E, R> {
|
||||||
|
readonly context: Context.Context<R>
|
||||||
|
|
||||||
|
readonly latestKeyRef: SubscriptionRef.SubscriptionRef<Option.Option<K>>
|
||||||
|
readonly stateRef: SubscriptionRef.SubscriptionRef<AsyncData.AsyncData<A, E>>
|
||||||
|
readonly fiberRef: SubscriptionRef.SubscriptionRef<Option.Option<Fiber.RuntimeFiber<
|
||||||
|
AsyncData.Success<A> | AsyncData.Failure<E>,
|
||||||
|
Cause.NoSuchElementException
|
||||||
|
>>>
|
||||||
|
|
||||||
|
readonly forkInterrupt: Effect.Effect<Fiber.RuntimeFiber<void, Cause.NoSuchElementException>>
|
||||||
|
readonly forkFetch: Effect.Effect<readonly [
|
||||||
|
fiber: Fiber.RuntimeFiber<AsyncData.Success<A> | AsyncData.Failure<E>, Cause.NoSuchElementException>,
|
||||||
|
state: Stream.Stream<AsyncData.AsyncData<A, E>>,
|
||||||
|
]>
|
||||||
|
readonly forkRefresh: Effect.Effect<readonly [
|
||||||
|
fiber: Fiber.RuntimeFiber<AsyncData.Success<A> | AsyncData.Failure<E>, Cause.NoSuchElementException>,
|
||||||
|
state: Stream.Stream<AsyncData.AsyncData<A, E>>,
|
||||||
|
]>
|
||||||
|
|
||||||
|
readonly fetchOnKeyChange: Effect.Effect<void, Cause.NoSuchElementException, Scope.Scope>
|
||||||
|
readonly refreshOnWindowFocus: Effect.Effect<void>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface MakeProps<K extends readonly unknown[], A, FallbackA, E, HandledE, R> {
|
||||||
|
readonly QueryClient: QueryClient.GenericTagClass<FallbackA, HandledE>
|
||||||
|
readonly key: Stream.Stream<K>
|
||||||
|
readonly query: (key: K) => Effect.Effect<A, E, R | QueryProgress.QueryProgress>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const make = <K extends readonly unknown[], A, FallbackA, E, HandledE, R>(
|
||||||
|
{
|
||||||
|
QueryClient,
|
||||||
|
key,
|
||||||
|
query,
|
||||||
|
}: MakeProps<K, A, FallbackA, E, HandledE, R>
|
||||||
|
): Effect.Effect<
|
||||||
|
QueryRunner<K, A | FallbackA, Exclude<E, HandledE>, R>,
|
||||||
|
never,
|
||||||
|
R | QueryClient.TagClassShape<FallbackA, HandledE>
|
||||||
|
> => Effect.gen(function*() {
|
||||||
|
const context = yield* Effect.context<R | QueryClient.TagClassShape<FallbackA, HandledE>>()
|
||||||
|
|
||||||
|
const latestKeyRef = yield* SubscriptionRef.make(Option.none<K>())
|
||||||
|
const stateRef = yield* SubscriptionRef.make(AsyncData.noData<A | FallbackA, Exclude<E, HandledE>>())
|
||||||
|
const fiberRef = yield* SubscriptionRef.make(Option.none<Fiber.RuntimeFiber<
|
||||||
|
AsyncData.Success<A | FallbackA> | AsyncData.Failure<Exclude<E, HandledE>>,
|
||||||
|
Cause.NoSuchElementException
|
||||||
|
>>())
|
||||||
|
|
||||||
|
const queryStateTag = QueryState.makeTag<A | FallbackA, Exclude<E, HandledE>>()
|
||||||
|
|
||||||
|
const interrupt = fiberRef.pipe(
|
||||||
|
Effect.flatMap(Option.match({
|
||||||
|
onSome: fiber => Ref.set(fiberRef, Option.none()).pipe(
|
||||||
|
Effect.andThen(Fiber.interrupt(fiber))
|
||||||
|
),
|
||||||
|
onNone: () => Effect.void,
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
|
const forkInterrupt = fiberRef.pipe(
|
||||||
|
Effect.flatMap(Option.match({
|
||||||
|
onSome: fiber => Ref.set(fiberRef, Option.none()).pipe(
|
||||||
|
Effect.andThen(Fiber.interrupt(fiber).pipe(
|
||||||
|
Effect.asVoid,
|
||||||
|
Effect.forkDaemon,
|
||||||
|
))
|
||||||
|
),
|
||||||
|
onNone: () => Effect.forkDaemon(Effect.void),
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
|
const run = Effect.Do.pipe(
|
||||||
|
Effect.bind("state", () => queryStateTag),
|
||||||
|
Effect.bind("client", () => QueryClient),
|
||||||
|
Effect.bind("latestKey", () => latestKeyRef.pipe(Effect.flatMap(identity))),
|
||||||
|
|
||||||
|
Effect.flatMap(({ state, client, latestKey }) => query(latestKey).pipe(
|
||||||
|
client.errorHandler.handle,
|
||||||
|
Effect.matchCauseEffect({
|
||||||
|
onSuccess: v => Effect.succeed(AsyncData.success(v)).pipe(
|
||||||
|
Effect.tap(state.set)
|
||||||
|
),
|
||||||
|
onFailure: c => Effect.succeed(AsyncData.failure(c)).pipe(
|
||||||
|
Effect.tap(state.set)
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
)),
|
||||||
|
|
||||||
|
Effect.provide(context),
|
||||||
|
Effect.provide(QueryProgress.QueryProgress.Live),
|
||||||
|
)
|
||||||
|
|
||||||
|
const forkFetch = Queue.unbounded<AsyncData.AsyncData<A | FallbackA, Exclude<E, HandledE>>>().pipe(
|
||||||
|
Effect.flatMap(stateQueue => queryStateTag.pipe(
|
||||||
|
Effect.flatMap(state => interrupt.pipe(
|
||||||
|
Effect.andThen(Effect.addFinalizer(() => Ref.set(fiberRef, Option.none()).pipe(
|
||||||
|
Effect.andThen(Queue.shutdown(stateQueue))
|
||||||
|
)).pipe(
|
||||||
|
Effect.andThen(state.set(AsyncData.loading())),
|
||||||
|
Effect.andThen(run),
|
||||||
|
Effect.scoped,
|
||||||
|
Effect.forkDaemon,
|
||||||
|
)),
|
||||||
|
|
||||||
|
Effect.tap(fiber => Ref.set(fiberRef, Option.some(fiber))),
|
||||||
|
Effect.map(fiber => [fiber, Stream.fromQueue(stateQueue)] as const),
|
||||||
|
)),
|
||||||
|
|
||||||
|
Effect.provide(QueryState.layer(
|
||||||
|
queryStateTag,
|
||||||
|
stateRef,
|
||||||
|
value => Queue.offer(stateQueue, value).pipe(
|
||||||
|
Effect.andThen(Ref.set(stateRef, value))
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
))
|
||||||
|
)
|
||||||
|
|
||||||
|
const setInitialRefreshState = queryStateTag.pipe(
|
||||||
|
Effect.flatMap(state => state.update(previous => {
|
||||||
|
if (AsyncData.isSuccess(previous) || AsyncData.isFailure(previous))
|
||||||
|
return AsyncData.refreshing(previous)
|
||||||
|
if (AsyncData.isRefreshing(previous))
|
||||||
|
return AsyncData.refreshing(previous.previous)
|
||||||
|
return AsyncData.loading()
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
|
const forkRefresh = Queue.unbounded<AsyncData.AsyncData<A | FallbackA, Exclude<E, HandledE>>>().pipe(
|
||||||
|
Effect.flatMap(stateQueue => interrupt.pipe(
|
||||||
|
Effect.andThen(Effect.addFinalizer(() => Ref.set(fiberRef, Option.none()).pipe(
|
||||||
|
Effect.andThen(Queue.shutdown(stateQueue))
|
||||||
|
)).pipe(
|
||||||
|
Effect.andThen(setInitialRefreshState),
|
||||||
|
Effect.andThen(run),
|
||||||
|
Effect.scoped,
|
||||||
|
Effect.forkDaemon,
|
||||||
|
)),
|
||||||
|
|
||||||
|
Effect.tap(fiber => Ref.set(fiberRef, Option.some(fiber))),
|
||||||
|
Effect.map(fiber => [fiber, Stream.fromQueue(stateQueue)] as const),
|
||||||
|
|
||||||
|
Effect.provide(QueryState.layer(
|
||||||
|
queryStateTag,
|
||||||
|
stateRef,
|
||||||
|
value => Queue.offer(stateQueue, value).pipe(
|
||||||
|
Effect.andThen(Ref.set(stateRef, value))
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
))
|
||||||
|
)
|
||||||
|
|
||||||
|
const fetchOnKeyChange = Effect.addFinalizer(() => interrupt).pipe(
|
||||||
|
Effect.andThen(Stream.runForEach(Stream.changes(key), latestKey =>
|
||||||
|
Ref.set(latestKeyRef, Option.some(latestKey)).pipe(
|
||||||
|
Effect.andThen(forkFetch)
|
||||||
|
)
|
||||||
|
))
|
||||||
|
)
|
||||||
|
|
||||||
|
const refreshOnWindowFocus = Stream.runForEach(
|
||||||
|
BrowserStream.fromEventListenerWindow("focus"),
|
||||||
|
() => forkRefresh,
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
context,
|
||||||
|
|
||||||
|
latestKeyRef,
|
||||||
|
stateRef,
|
||||||
|
fiberRef,
|
||||||
|
|
||||||
|
forkInterrupt,
|
||||||
|
forkFetch,
|
||||||
|
forkRefresh,
|
||||||
|
|
||||||
|
fetchOnKeyChange,
|
||||||
|
refreshOnWindowFocus,
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -1 +1,3 @@
|
|||||||
|
export * as MutationRunner from "./MutationRunner.js"
|
||||||
|
export * as QueryRunner from "./QueryRunner.js"
|
||||||
export * as QueryState from "./QueryState.js"
|
export * as QueryState from "./QueryState.js"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "reffuse",
|
"name": "reffuse",
|
||||||
"version": "0.1.13",
|
"version": "0.1.10",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"files": [
|
"files": [
|
||||||
"./README.md",
|
"./README.md",
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^19.0.0",
|
"@types/react": "^19.0.0",
|
||||||
"effect": "^3.15.0",
|
"effect": "^3.13.0",
|
||||||
"react": "^19.0.0"
|
"react": "^19.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Array, Context, Effect, ExecutionStrategy, Exit, Layer, Match, Ref, Runtime, Scope } from "effect"
|
import { Array, Context, Effect, ExecutionStrategy, Exit, Layer, Ref, Runtime, Scope } from "effect"
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as ReffuseRuntime from "./ReffuseRuntime.js"
|
import * as ReffuseRuntime from "./ReffuseRuntime.js"
|
||||||
|
|
||||||
@@ -25,8 +25,6 @@ export type R<T> = T extends ReffuseContext<infer R> ? R : never
|
|||||||
export type ReactProvider<R> = React.FC<{
|
export type ReactProvider<R> = React.FC<{
|
||||||
readonly layer: Layer.Layer<R, unknown, Scope.Scope>
|
readonly layer: Layer.Layer<R, unknown, Scope.Scope>
|
||||||
readonly scope?: Scope.Scope
|
readonly scope?: Scope.Scope
|
||||||
readonly finalizerExecutionStrategy?: ExecutionStrategy.ExecutionStrategy
|
|
||||||
readonly finalizerExecutionMode?: "sync" | "fork"
|
|
||||||
readonly children?: React.ReactNode
|
readonly children?: React.ReactNode
|
||||||
}>
|
}>
|
||||||
|
|
||||||
@@ -34,25 +32,16 @@ const makeProvider = <R>(Context: React.Context<Context.Context<R>>): ReactProvi
|
|||||||
return function ReffuseContextReactProvider(props) {
|
return function ReffuseContextReactProvider(props) {
|
||||||
const runtime = ReffuseRuntime.useRuntime()
|
const runtime = ReffuseRuntime.useRuntime()
|
||||||
const runSync = React.useMemo(() => Runtime.runSync(runtime), [runtime])
|
const runSync = React.useMemo(() => Runtime.runSync(runtime), [runtime])
|
||||||
const runFork = React.useMemo(() => Runtime.runFork(runtime), [runtime])
|
|
||||||
|
|
||||||
const makeScope = React.useMemo(() => props.scope
|
const makeScope = React.useMemo(() => props.scope
|
||||||
? Scope.fork(props.scope, props.finalizerExecutionStrategy ?? ExecutionStrategy.sequential)
|
? Scope.fork(props.scope, ExecutionStrategy.sequential)
|
||||||
: Scope.make(props.finalizerExecutionStrategy ?? ExecutionStrategy.sequential),
|
: Scope.make(),
|
||||||
[props.scope])
|
[props.scope])
|
||||||
|
|
||||||
const makeContext = (scope: Scope.CloseableScope) => Effect.context<R>().pipe(
|
const makeContext = React.useCallback((scope: Scope.CloseableScope) => Effect.context<R>().pipe(
|
||||||
Effect.provide(props.layer),
|
Effect.provide(props.layer),
|
||||||
Effect.provideService(Scope.Scope, scope),
|
Effect.provideService(Scope.Scope, scope),
|
||||||
)
|
), [props.layer])
|
||||||
|
|
||||||
const closeScope = (scope: Scope.CloseableScope) => Scope.close(scope, Exit.void).pipe(
|
|
||||||
effect => Match.value(props.finalizerExecutionMode ?? "sync").pipe(
|
|
||||||
Match.when("sync", () => { runSync(effect) }),
|
|
||||||
Match.when("fork", () => { runFork(effect) }),
|
|
||||||
Match.exhaustive,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
const [isInitialRun, initialScope, initialValue] = React.useMemo(() => Effect.Do.pipe(
|
const [isInitialRun, initialScope, initialValue] = React.useMemo(() => Effect.Do.pipe(
|
||||||
Effect.bind("isInitialRun", () => Ref.make(true)),
|
Effect.bind("isInitialRun", () => Ref.make(true)),
|
||||||
@@ -68,7 +57,7 @@ const makeProvider = <R>(Context: React.Context<Context.Context<R>>): ReactProvi
|
|||||||
Effect.if({
|
Effect.if({
|
||||||
onTrue: () => Ref.set(isInitialRun, false).pipe(
|
onTrue: () => Ref.set(isInitialRun, false).pipe(
|
||||||
Effect.map(() =>
|
Effect.map(() =>
|
||||||
() => closeScope(initialScope)
|
() => runSync(Scope.close(initialScope, Exit.void))
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -79,13 +68,13 @@ const makeProvider = <R>(Context: React.Context<Context.Context<R>>): ReactProvi
|
|||||||
Effect.sync(() => setValue(context))
|
Effect.sync(() => setValue(context))
|
||||||
),
|
),
|
||||||
Effect.map(({ scope }) =>
|
Effect.map(({ scope }) =>
|
||||||
() => closeScope(scope)
|
() => runSync(Scope.close(scope, Exit.void))
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
runSync,
|
runSync,
|
||||||
), [makeScope, runSync, runFork])
|
), [makeScope, makeContext, runSync])
|
||||||
|
|
||||||
return React.createElement(Context, { ...props, value })
|
return React.createElement(Context, { ...props, value })
|
||||||
}
|
}
|
||||||
@@ -95,7 +84,6 @@ export type AsyncReactProvider<R> = React.FC<{
|
|||||||
readonly layer: Layer.Layer<R, unknown, Scope.Scope>
|
readonly layer: Layer.Layer<R, unknown, Scope.Scope>
|
||||||
readonly scope?: Scope.Scope
|
readonly scope?: Scope.Scope
|
||||||
readonly finalizerExecutionStrategy?: ExecutionStrategy.ExecutionStrategy
|
readonly finalizerExecutionStrategy?: ExecutionStrategy.ExecutionStrategy
|
||||||
readonly finalizerExecutionMode?: "sync" | "fork"
|
|
||||||
readonly fallback?: React.ReactNode
|
readonly fallback?: React.ReactNode
|
||||||
readonly children?: React.ReactNode
|
readonly children?: React.ReactNode
|
||||||
}>
|
}>
|
||||||
@@ -124,7 +112,7 @@ const makeAsyncProvider = <R>(Context: React.Context<Context.Context<R>>): Async
|
|||||||
|
|
||||||
const scope = runSync(props.scope
|
const scope = runSync(props.scope
|
||||||
? Scope.fork(props.scope, props.finalizerExecutionStrategy ?? ExecutionStrategy.sequential)
|
? Scope.fork(props.scope, props.finalizerExecutionStrategy ?? ExecutionStrategy.sequential)
|
||||||
: Scope.make(props.finalizerExecutionStrategy ?? ExecutionStrategy.sequential)
|
: Scope.make(props.finalizerExecutionStrategy)
|
||||||
)
|
)
|
||||||
|
|
||||||
Effect.context<R>().pipe(
|
Effect.context<R>().pipe(
|
||||||
@@ -138,13 +126,7 @@ const makeAsyncProvider = <R>(Context: React.Context<Context.Context<R>>): Async
|
|||||||
effect => runFork(effect, { ...props, scope }),
|
effect => runFork(effect, { ...props, scope }),
|
||||||
)
|
)
|
||||||
|
|
||||||
return () => Scope.close(scope, Exit.void).pipe(
|
return () => { runFork(Scope.close(scope, Exit.void)) }
|
||||||
effect => Match.value(props.finalizerExecutionMode ?? "sync").pipe(
|
|
||||||
Match.when("sync", () => { runSync(effect) }),
|
|
||||||
Match.when("fork", () => { runFork(effect) }),
|
|
||||||
Match.exhaustive,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}, [props.layer, runSync, runFork])
|
}, [props.layer, runSync, runFork])
|
||||||
|
|
||||||
return React.createElement(React.Suspense, {
|
return React.createElement(React.Suspense, {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { type Context, Effect, ExecutionStrategy, Exit, type Fiber, Layer, Match, Option, pipe, Pipeable, PubSub, Ref, Runtime, Scope, Stream, SubscriptionRef } from "effect"
|
import { type Context, Effect, ExecutionStrategy, Exit, type Fiber, type Layer, Match, Option, pipe, Pipeable, PubSub, Ref, Runtime, Scope, Stream, SubscriptionRef } from "effect"
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as ReffuseContext from "./ReffuseContext.js"
|
import * as ReffuseContext from "./ReffuseContext.js"
|
||||||
import * as ReffuseRuntime from "./ReffuseRuntime.js"
|
import * as ReffuseRuntime from "./ReffuseRuntime.js"
|
||||||
@@ -15,7 +15,6 @@ export interface ScopeOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface UseScopeOptions extends RenderOptions, ScopeOptions {
|
export interface UseScopeOptions extends RenderOptions, ScopeOptions {
|
||||||
readonly scope?: Scope.Scope
|
|
||||||
readonly finalizerExecutionMode?: "sync" | "fork"
|
readonly finalizerExecutionMode?: "sync" | "fork"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,8 +27,7 @@ export abstract class ReffuseNamespace<R> {
|
|||||||
declare ["constructor"]: ReffuseNamespaceClass<R>
|
declare ["constructor"]: ReffuseNamespaceClass<R>
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.SubRefFromGetSet = this.SubRefFromGetSet.bind(this as any) as any
|
this.SubRef = this.SubRef.bind(this as any) as any
|
||||||
this.SubRefFromPath = this.SubRefFromPath.bind(this as any) as any
|
|
||||||
this.SubscribeRefs = this.SubscribeRefs.bind(this as any) as any
|
this.SubscribeRefs = this.SubscribeRefs.bind(this as any) as any
|
||||||
this.RefState = this.RefState.bind(this as any) as any
|
this.RefState = this.RefState.bind(this as any) as any
|
||||||
this.SubscribeStream = this.SubscribeStream.bind(this as any) as any
|
this.SubscribeStream = this.SubscribeStream.bind(this as any) as any
|
||||||
@@ -98,26 +96,14 @@ export abstract class ReffuseNamespace<R> {
|
|||||||
this: ReffuseNamespace<R>,
|
this: ReffuseNamespace<R>,
|
||||||
deps: React.DependencyList = [],
|
deps: React.DependencyList = [],
|
||||||
options?: UseScopeOptions,
|
options?: UseScopeOptions,
|
||||||
): readonly [scope: Scope.Scope, layer: Layer.Layer<Scope.Scope>] {
|
): Scope.Scope {
|
||||||
const runSync = this.useRunSync()
|
const runSync = this.useRunSync()
|
||||||
const runFork = this.useRunFork()
|
const runFork = this.useRunFork()
|
||||||
|
|
||||||
const makeScope = React.useMemo(() => options?.scope
|
const [isInitialRun, initialScope] = React.useMemo(() => runSync(Effect.all([
|
||||||
? Scope.fork(options.scope, options.finalizerExecutionStrategy ?? ExecutionStrategy.sequential)
|
Ref.make(true),
|
||||||
: Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential),
|
Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential),
|
||||||
[options?.scope])
|
])), [])
|
||||||
|
|
||||||
const closeScope = (scope: Scope.CloseableScope) => Scope.close(scope, Exit.void).pipe(
|
|
||||||
effect => Match.value(options?.finalizerExecutionMode ?? "sync").pipe(
|
|
||||||
Match.when("sync", () => { runSync(effect) }),
|
|
||||||
Match.when("fork", () => { runFork(effect) }),
|
|
||||||
Match.exhaustive,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
const [isInitialRun, initialScope] = React.useMemo(() => runSync(
|
|
||||||
Effect.all([Ref.make(true), makeScope])
|
|
||||||
), [makeScope])
|
|
||||||
|
|
||||||
const [scope, setScope] = React.useState(initialScope)
|
const [scope, setScope] = React.useState(initialScope)
|
||||||
|
|
||||||
@@ -125,23 +111,34 @@ export abstract class ReffuseNamespace<R> {
|
|||||||
Effect.if({
|
Effect.if({
|
||||||
onTrue: () => Effect.as(
|
onTrue: () => Effect.as(
|
||||||
Ref.set(isInitialRun, false),
|
Ref.set(isInitialRun, false),
|
||||||
() => closeScope(initialScope),
|
() => Scope.close(initialScope, Exit.void).pipe(
|
||||||
|
effect => Match.value(options?.finalizerExecutionMode ?? "sync").pipe(
|
||||||
|
Match.when("sync", () => { runSync(effect) }),
|
||||||
|
Match.when("fork", () => { runFork(effect) }),
|
||||||
|
Match.exhaustive,
|
||||||
|
)
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
onFalse: () => makeScope.pipe(
|
onFalse: () => Scope.make(options?.finalizerExecutionStrategy).pipe(
|
||||||
Effect.tap(v => Effect.sync(() => setScope(v))),
|
Effect.tap(v => Effect.sync(() => setScope(v))),
|
||||||
Effect.map(v => () => closeScope(v)),
|
Effect.map(v => () => Scope.close(v, Exit.void).pipe(
|
||||||
|
effect => Match.value(options?.finalizerExecutionMode ?? "sync").pipe(
|
||||||
|
Match.when("sync", () => { runSync(effect) }),
|
||||||
|
Match.when("fork", () => { runFork(effect) }),
|
||||||
|
Match.exhaustive,
|
||||||
|
)
|
||||||
|
)),
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
runSync,
|
runSync,
|
||||||
), [
|
), [
|
||||||
makeScope,
|
|
||||||
...options?.doNotReExecuteOnRuntimeOrContextChange ? [] : [runSync, runFork],
|
...options?.doNotReExecuteOnRuntimeOrContextChange ? [] : [runSync, runFork],
|
||||||
...deps,
|
...deps,
|
||||||
])
|
])
|
||||||
|
|
||||||
return React.useMemo(() => [scope, Layer.succeed(Scope.Scope, scope)] as const, [scope])
|
return scope
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -407,19 +404,7 @@ export abstract class ReffuseNamespace<R> {
|
|||||||
return ref
|
return ref
|
||||||
}
|
}
|
||||||
|
|
||||||
useSubRefFromGetSet<A, B, R>(
|
useSubRef<B, const P extends PropertyPath.Paths<B>, R>(
|
||||||
this: ReffuseNamespace<R>,
|
|
||||||
parent: SubscriptionRef.SubscriptionRef<B>,
|
|
||||||
getter: (parentValue: B) => A,
|
|
||||||
setter: (parentValue: B, value: A) => B,
|
|
||||||
): SubscriptionSubRef.SubscriptionSubRef<A, B> {
|
|
||||||
return React.useMemo(
|
|
||||||
() => SubscriptionSubRef.makeFromGetSet(parent, getter, setter),
|
|
||||||
[parent],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
useSubRefFromPath<B, const P extends PropertyPath.Paths<B>, R>(
|
|
||||||
this: ReffuseNamespace<R>,
|
this: ReffuseNamespace<R>,
|
||||||
parent: SubscriptionRef.SubscriptionRef<B>,
|
parent: SubscriptionRef.SubscriptionRef<B>,
|
||||||
path: P,
|
path: P,
|
||||||
@@ -489,7 +474,7 @@ export abstract class ReffuseNamespace<R> {
|
|||||||
this: ReffuseNamespace<R>,
|
this: ReffuseNamespace<R>,
|
||||||
values: A,
|
values: A,
|
||||||
): Stream.Stream<A> {
|
): Stream.Stream<A> {
|
||||||
const [, scopeLayer] = this.useScope([], { finalizerExecutionMode: "fork" })
|
const scope = this.useScope()
|
||||||
|
|
||||||
const { latest, pubsub, stream } = this.useMemo(() => Effect.Do.pipe(
|
const { latest, pubsub, stream } = this.useMemo(() => Effect.Do.pipe(
|
||||||
Effect.bind("latest", () => Ref.make(values)),
|
Effect.bind("latest", () => Ref.make(values)),
|
||||||
@@ -501,8 +486,8 @@ export abstract class ReffuseNamespace<R> {
|
|||||||
)),
|
)),
|
||||||
Stream.unwrapScoped,
|
Stream.unwrapScoped,
|
||||||
)),
|
)),
|
||||||
Effect.provide(scopeLayer),
|
Effect.provideService(Scope.Scope, scope),
|
||||||
), [scopeLayer], { doNotReExecuteOnRuntimeOrContextChange: true })
|
), [scope], { doNotReExecuteOnRuntimeOrContextChange: true })
|
||||||
|
|
||||||
this.useEffect(() => Ref.set(latest, values).pipe(
|
this.useEffect(() => Ref.set(latest, values).pipe(
|
||||||
Effect.andThen(PubSub.publish(pubsub, values)),
|
Effect.andThen(PubSub.publish(pubsub, values)),
|
||||||
@@ -543,19 +528,7 @@ export abstract class ReffuseNamespace<R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SubRefFromGetSet<A, B, R>(
|
SubRef<B, const P extends PropertyPath.Paths<B>, R>(
|
||||||
this: ReffuseNamespace<R>,
|
|
||||||
props: {
|
|
||||||
readonly parent: SubscriptionRef.SubscriptionRef<B>,
|
|
||||||
readonly getter: (parentValue: B) => A,
|
|
||||||
readonly setter: (parentValue: B, value: A) => B,
|
|
||||||
readonly children: (subRef: SubscriptionSubRef.SubscriptionSubRef<A, B>) => React.ReactNode
|
|
||||||
},
|
|
||||||
): React.ReactNode {
|
|
||||||
return props.children(this.useSubRefFromGetSet(props.parent, props.getter, props.setter))
|
|
||||||
}
|
|
||||||
|
|
||||||
SubRefFromPath<B, const P extends PropertyPath.Paths<B>, R>(
|
|
||||||
this: ReffuseNamespace<R>,
|
this: ReffuseNamespace<R>,
|
||||||
props: {
|
props: {
|
||||||
readonly parent: SubscriptionRef.SubscriptionRef<B>,
|
readonly parent: SubscriptionRef.SubscriptionRef<B>,
|
||||||
@@ -563,7 +536,7 @@ export abstract class ReffuseNamespace<R> {
|
|||||||
readonly children: (subRef: SubscriptionSubRef.SubscriptionSubRef<PropertyPath.ValueFromPath<B, P>, B>) => React.ReactNode
|
readonly children: (subRef: SubscriptionSubRef.SubscriptionSubRef<PropertyPath.ValueFromPath<B, P>, B>) => React.ReactNode
|
||||||
},
|
},
|
||||||
): React.ReactNode {
|
): React.ReactNode {
|
||||||
return props.children(this.useSubRefFromPath(props.parent, props.path))
|
return props.children(this.useSubRef(props.parent, props.path))
|
||||||
}
|
}
|
||||||
|
|
||||||
SubscribeRefs<
|
SubscribeRefs<
|
||||||
|
|||||||
@@ -1,29 +1,24 @@
|
|||||||
import { Array, Function, Option, Predicate } from "effect"
|
import { Array, Function, Option, Predicate } from "effect"
|
||||||
|
|
||||||
|
|
||||||
type Prev = readonly [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
export type Paths<T> = [] | (
|
||||||
|
T extends readonly any[] ? ArrayPaths<T> :
|
||||||
export type Paths<T, D extends number = 5, Seen = never> = [] | (
|
T extends object ? ObjectPaths<T> :
|
||||||
D extends never ? [] :
|
|
||||||
T extends Seen ? [] :
|
|
||||||
T extends readonly any[] ? ArrayPaths<T, D, Seen | T> :
|
|
||||||
T extends object ? ObjectPaths<T, D, Seen | T> :
|
|
||||||
never
|
never
|
||||||
)
|
)
|
||||||
|
|
||||||
export type ArrayPaths<T extends readonly any[], D extends number, Seen> = {
|
export type ArrayPaths<T extends readonly any[]> = {
|
||||||
[K in keyof T as K extends number ? K : never]:
|
[K in keyof T as K extends number ? K : never]:
|
||||||
| [K]
|
| [K]
|
||||||
| [K, ...Paths<T[K], Prev[D], Seen>]
|
| [K, ...Paths<T[K]>]
|
||||||
} extends infer O
|
} extends infer O
|
||||||
? O[keyof O]
|
? O[keyof O]
|
||||||
: never
|
: never
|
||||||
|
|
||||||
export type ObjectPaths<T extends object, D extends number, Seen> = {
|
export type ObjectPaths<T extends object> = {
|
||||||
[K in keyof T as K extends string | number | symbol ? K : never]-?:
|
[K in keyof T as K extends string | number | symbol ? K : never]:
|
||||||
NonNullable<T[K]> extends infer V
|
| [K]
|
||||||
? [K] | [K, ...Paths<V, Prev[D], Seen>]
|
| [K, ...Paths<T[K]>]
|
||||||
: never
|
|
||||||
} extends infer O
|
} extends infer O
|
||||||
? O[keyof O]
|
? O[keyof O]
|
||||||
: never
|
: never
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class SubscriptionSubRefImpl<in out A, in out B> extends Effectable.Class<A> imp
|
|||||||
readonly setter: (parentValue: B, value: A) => B,
|
readonly setter: (parentValue: B, value: A) => B,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
this.get = Effect.map(Ref.get(this.parent), this.getter)
|
this.get = Ref.get(this.parent).pipe(Effect.map(this.getter))
|
||||||
}
|
}
|
||||||
|
|
||||||
commit() {
|
commit() {
|
||||||
|
|||||||
Reference in New Issue
Block a user