authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-01-23 17:35:22-08:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-01-23 17:47:43-08:00
logaf2a34170fc51b625182c42987277eec81558c1e
tree20c42966ce5613ee6c35c1ac04a3c8399e77d72d
signaturelock-open Commit is signed but in an unrecognized format.

feat: initial commit with demo code


12 files changed, 2418 insertions(+), 0 deletions(-)

.gitignore created+2
...@@ -0,0 +1,2 @@
1node_modules
2coverage
package-lock.json created+1847
...@@ -0,0 +1,1847 @@
1{
2 "name": "react-mutation",
3 "version": "1.0.0",
4 "lockfileVersion": 3,
5 "requires": true,
6 "packages": {
7 "": {
8 "devDependencies": {
9 "@tanstack/react-query": "^5.90.20",
10 "@types/react": "^19.2.9",
11 "@vitest/coverage-v8": "^4.0.18",
12 "@vitest/ui": "^4.0.18",
13 "react": "^19.2.3",
14 "vitest": "^4.0.18"
15 }
16 },
17 "node_modules/@babel/helper-string-parser": {
18 "version": "7.27.1",
19 "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
20 "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
21 "dev": true,
22 "license": "MIT",
23 "engines": {
24 "node": ">=6.9.0"
25 }
26 },
27 "node_modules/@babel/helper-validator-identifier": {
28 "version": "7.28.5",
29 "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
30 "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
31 "dev": true,
32 "license": "MIT",
33 "engines": {
34 "node": ">=6.9.0"
35 }
36 },
37 "node_modules/@babel/parser": {
38 "version": "7.28.6",
39 "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz",
40 "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==",
41 "dev": true,
42 "license": "MIT",
43 "dependencies": {
44 "@babel/types": "^7.28.6"
45 },
46 "bin": {
47 "parser": "bin/babel-parser.js"
48 },
49 "engines": {
50 "node": ">=6.0.0"
51 }
52 },
53 "node_modules/@babel/types": {
54 "version": "7.28.6",
55 "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz",
56 "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==",
57 "dev": true,
58 "license": "MIT",
59 "dependencies": {
60 "@babel/helper-string-parser": "^7.27.1",
61 "@babel/helper-validator-identifier": "^7.28.5"
62 },
63 "engines": {
64 "node": ">=6.9.0"
65 }
66 },
67 "node_modules/@bcoe/v8-coverage": {
68 "version": "1.0.2",
69 "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz",
70 "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==",
71 "dev": true,
72 "license": "MIT",
73 "engines": {
74 "node": ">=18"
75 }
76 },
77 "node_modules/@esbuild/aix-ppc64": {
78 "version": "0.27.2",
79 "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz",
80 "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==",
81 "cpu": [
82 "ppc64"
83 ],
84 "dev": true,
85 "license": "MIT",
86 "optional": true,
87 "os": [
88 "aix"
89 ],
90 "engines": {
91 "node": ">=18"
92 }
93 },
94 "node_modules/@esbuild/android-arm": {
95 "version": "0.27.2",
96 "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz",
97 "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==",
98 "cpu": [
99 "arm"
100 ],
101 "dev": true,
102 "license": "MIT",
103 "optional": true,
104 "os": [
105 "android"
106 ],
107 "engines": {
108 "node": ">=18"
109 }
110 },
111 "node_modules/@esbuild/android-arm64": {
112 "version": "0.27.2",
113 "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz",
114 "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==",
115 "cpu": [
116 "arm64"
117 ],
118 "dev": true,
119 "license": "MIT",
120 "optional": true,
121 "os": [
122 "android"
123 ],
124 "engines": {
125 "node": ">=18"
126 }
127 },
128 "node_modules/@esbuild/android-x64": {
129 "version": "0.27.2",
130 "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz",
131 "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==",
132 "cpu": [
133 "x64"
134 ],
135 "dev": true,
136 "license": "MIT",
137 "optional": true,
138 "os": [
139 "android"
140 ],
141 "engines": {
142 "node": ">=18"
143 }
144 },
145 "node_modules/@esbuild/darwin-arm64": {
146 "version": "0.27.2",
147 "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz",
148 "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==",
149 "cpu": [
150 "arm64"
151 ],
152 "dev": true,
153 "license": "MIT",
154 "optional": true,
155 "os": [
156 "darwin"
157 ],
158 "engines": {
159 "node": ">=18"
160 }
161 },
162 "node_modules/@esbuild/darwin-x64": {
163 "version": "0.27.2",
164 "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz",
165 "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==",
166 "cpu": [
167 "x64"
168 ],
169 "dev": true,
170 "license": "MIT",
171 "optional": true,
172 "os": [
173 "darwin"
174 ],
175 "engines": {
176 "node": ">=18"
177 }
178 },
179 "node_modules/@esbuild/freebsd-arm64": {
180 "version": "0.27.2",
181 "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz",
182 "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==",
183 "cpu": [
184 "arm64"
185 ],
186 "dev": true,
187 "license": "MIT",
188 "optional": true,
189 "os": [
190 "freebsd"
191 ],
192 "engines": {
193 "node": ">=18"
194 }
195 },
196 "node_modules/@esbuild/freebsd-x64": {
197 "version": "0.27.2",
198 "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz",
199 "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==",
200 "cpu": [
201 "x64"
202 ],
203 "dev": true,
204 "license": "MIT",
205 "optional": true,
206 "os": [
207 "freebsd"
208 ],
209 "engines": {
210 "node": ">=18"
211 }
212 },
213 "node_modules/@esbuild/linux-arm": {
214 "version": "0.27.2",
215 "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz",
216 "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==",
217 "cpu": [
218 "arm"
219 ],
220 "dev": true,
221 "license": "MIT",
222 "optional": true,
223 "os": [
224 "linux"
225 ],
226 "engines": {
227 "node": ">=18"
228 }
229 },
230 "node_modules/@esbuild/linux-arm64": {
231 "version": "0.27.2",
232 "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz",
233 "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==",
234 "cpu": [
235 "arm64"
236 ],
237 "dev": true,
238 "license": "MIT",
239 "optional": true,
240 "os": [
241 "linux"
242 ],
243 "engines": {
244 "node": ">=18"
245 }
246 },
247 "node_modules/@esbuild/linux-ia32": {
248 "version": "0.27.2",
249 "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz",
250 "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==",
251 "cpu": [
252 "ia32"
253 ],
254 "dev": true,
255 "license": "MIT",
256 "optional": true,
257 "os": [
258 "linux"
259 ],
260 "engines": {
261 "node": ">=18"
262 }
263 },
264 "node_modules/@esbuild/linux-loong64": {
265 "version": "0.27.2",
266 "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz",
267 "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==",
268 "cpu": [
269 "loong64"
270 ],
271 "dev": true,
272 "license": "MIT",
273 "optional": true,
274 "os": [
275 "linux"
276 ],
277 "engines": {
278 "node": ">=18"
279 }
280 },
281 "node_modules/@esbuild/linux-mips64el": {
282 "version": "0.27.2",
283 "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz",
284 "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==",
285 "cpu": [
286 "mips64el"
287 ],
288 "dev": true,
289 "license": "MIT",
290 "optional": true,
291 "os": [
292 "linux"
293 ],
294 "engines": {
295 "node": ">=18"
296 }
297 },
298 "node_modules/@esbuild/linux-ppc64": {
299 "version": "0.27.2",
300 "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz",
301 "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==",
302 "cpu": [
303 "ppc64"
304 ],
305 "dev": true,
306 "license": "MIT",
307 "optional": true,
308 "os": [
309 "linux"
310 ],
311 "engines": {
312 "node": ">=18"
313 }
314 },
315 "node_modules/@esbuild/linux-riscv64": {
316 "version": "0.27.2",
317 "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz",
318 "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==",
319 "cpu": [
320 "riscv64"
321 ],
322 "dev": true,
323 "license": "MIT",
324 "optional": true,
325 "os": [
326 "linux"
327 ],
328 "engines": {
329 "node": ">=18"
330 }
331 },
332 "node_modules/@esbuild/linux-s390x": {
333 "version": "0.27.2",
334 "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz",
335 "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==",
336 "cpu": [
337 "s390x"
338 ],
339 "dev": true,
340 "license": "MIT",
341 "optional": true,
342 "os": [
343 "linux"
344 ],
345 "engines": {
346 "node": ">=18"
347 }
348 },
349 "node_modules/@esbuild/linux-x64": {
350 "version": "0.27.2",
351 "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz",
352 "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==",
353 "cpu": [
354 "x64"
355 ],
356 "dev": true,
357 "license": "MIT",
358 "optional": true,
359 "os": [
360 "linux"
361 ],
362 "engines": {
363 "node": ">=18"
364 }
365 },
366 "node_modules/@esbuild/netbsd-arm64": {
367 "version": "0.27.2",
368 "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz",
369 "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==",
370 "cpu": [
371 "arm64"
372 ],
373 "dev": true,
374 "license": "MIT",
375 "optional": true,
376 "os": [
377 "netbsd"
378 ],
379 "engines": {
380 "node": ">=18"
381 }
382 },
383 "node_modules/@esbuild/netbsd-x64": {
384 "version": "0.27.2",
385 "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz",
386 "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==",
387 "cpu": [
388 "x64"
389 ],
390 "dev": true,
391 "license": "MIT",
392 "optional": true,
393 "os": [
394 "netbsd"
395 ],
396 "engines": {
397 "node": ">=18"
398 }
399 },
400 "node_modules/@esbuild/openbsd-arm64": {
401 "version": "0.27.2",
402 "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz",
403 "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==",
404 "cpu": [
405 "arm64"
406 ],
407 "dev": true,
408 "license": "MIT",
409 "optional": true,
410 "os": [
411 "openbsd"
412 ],
413 "engines": {
414 "node": ">=18"
415 }
416 },
417 "node_modules/@esbuild/openbsd-x64": {
418 "version": "0.27.2",
419 "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz",
420 "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==",
421 "cpu": [
422 "x64"
423 ],
424 "dev": true,
425 "license": "MIT",
426 "optional": true,
427 "os": [
428 "openbsd"
429 ],
430 "engines": {
431 "node": ">=18"
432 }
433 },
434 "node_modules/@esbuild/openharmony-arm64": {
435 "version": "0.27.2",
436 "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz",
437 "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==",
438 "cpu": [
439 "arm64"
440 ],
441 "dev": true,
442 "license": "MIT",
443 "optional": true,
444 "os": [
445 "openharmony"
446 ],
447 "engines": {
448 "node": ">=18"
449 }
450 },
451 "node_modules/@esbuild/sunos-x64": {
452 "version": "0.27.2",
453 "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz",
454 "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==",
455 "cpu": [
456 "x64"
457 ],
458 "dev": true,
459 "license": "MIT",
460 "optional": true,
461 "os": [
462 "sunos"
463 ],
464 "engines": {
465 "node": ">=18"
466 }
467 },
468 "node_modules/@esbuild/win32-arm64": {
469 "version": "0.27.2",
470 "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz",
471 "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==",
472 "cpu": [
473 "arm64"
474 ],
475 "dev": true,
476 "license": "MIT",
477 "optional": true,
478 "os": [
479 "win32"
480 ],
481 "engines": {
482 "node": ">=18"
483 }
484 },
485 "node_modules/@esbuild/win32-ia32": {
486 "version": "0.27.2",
487 "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz",
488 "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==",
489 "cpu": [
490 "ia32"
491 ],
492 "dev": true,
493 "license": "MIT",
494 "optional": true,
495 "os": [
496 "win32"
497 ],
498 "engines": {
499 "node": ">=18"
500 }
501 },
502 "node_modules/@esbuild/win32-x64": {
503 "version": "0.27.2",
504 "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz",
505 "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==",
506 "cpu": [
507 "x64"
508 ],
509 "dev": true,
510 "license": "MIT",
511 "optional": true,
512 "os": [
513 "win32"
514 ],
515 "engines": {
516 "node": ">=18"
517 }
518 },
519 "node_modules/@jridgewell/resolve-uri": {
520 "version": "3.1.2",
521 "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
522 "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
523 "dev": true,
524 "license": "MIT",
525 "engines": {
526 "node": ">=6.0.0"
527 }
528 },
529 "node_modules/@jridgewell/sourcemap-codec": {
530 "version": "1.5.5",
531 "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
532 "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
533 "dev": true,
534 "license": "MIT"
535 },
536 "node_modules/@jridgewell/trace-mapping": {
537 "version": "0.3.31",
538 "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
539 "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
540 "dev": true,
541 "license": "MIT",
542 "dependencies": {
543 "@jridgewell/resolve-uri": "^3.1.0",
544 "@jridgewell/sourcemap-codec": "^1.4.14"
545 }
546 },
547 "node_modules/@polka/url": {
548 "version": "1.0.0-next.29",
549 "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz",
550 "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==",
551 "dev": true,
552 "license": "MIT"
553 },
554 "node_modules/@rollup/rollup-android-arm-eabi": {
555 "version": "4.56.0",
556 "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.56.0.tgz",
557 "integrity": "sha512-LNKIPA5k8PF1+jAFomGe3qN3bbIgJe/IlpDBwuVjrDKrJhVWywgnJvflMt/zkbVNLFtF1+94SljYQS6e99klnw==",
558 "cpu": [
559 "arm"
560 ],
561 "dev": true,
562 "license": "MIT",
563 "optional": true,
564 "os": [
565 "android"
566 ]
567 },
568 "node_modules/@rollup/rollup-android-arm64": {
569 "version": "4.56.0",
570 "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.56.0.tgz",
571 "integrity": "sha512-lfbVUbelYqXlYiU/HApNMJzT1E87UPGvzveGg2h0ktUNlOCxKlWuJ9jtfvs1sKHdwU4fzY7Pl8sAl49/XaEk6Q==",
572 "cpu": [
573 "arm64"
574 ],
575 "dev": true,
576 "license": "MIT",
577 "optional": true,
578 "os": [
579 "android"
580 ]
581 },
582 "node_modules/@rollup/rollup-darwin-arm64": {
583 "version": "4.56.0",
584 "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.56.0.tgz",
585 "integrity": "sha512-EgxD1ocWfhoD6xSOeEEwyE7tDvwTgZc8Bss7wCWe+uc7wO8G34HHCUH+Q6cHqJubxIAnQzAsyUsClt0yFLu06w==",
586 "cpu": [
587 "arm64"
588 ],
589 "dev": true,
590 "license": "MIT",
591 "optional": true,
592 "os": [
593 "darwin"
594 ]
595 },
596 "node_modules/@rollup/rollup-darwin-x64": {
597 "version": "4.56.0",
598 "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.56.0.tgz",
599 "integrity": "sha512-1vXe1vcMOssb/hOF8iv52A7feWW2xnu+c8BV4t1F//m9QVLTfNVpEdja5ia762j/UEJe2Z1jAmEqZAK42tVW3g==",
600 "cpu": [
601 "x64"
602 ],
603 "dev": true,
604 "license": "MIT",
605 "optional": true,
606 "os": [
607 "darwin"
608 ]
609 },
610 "node_modules/@rollup/rollup-freebsd-arm64": {
611 "version": "4.56.0",
612 "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.56.0.tgz",
613 "integrity": "sha512-bof7fbIlvqsyv/DtaXSck4VYQ9lPtoWNFCB/JY4snlFuJREXfZnm+Ej6yaCHfQvofJDXLDMTVxWscVSuQvVWUQ==",
614 "cpu": [
615 "arm64"
616 ],
617 "dev": true,
618 "license": "MIT",
619 "optional": true,
620 "os": [
621 "freebsd"
622 ]
623 },
624 "node_modules/@rollup/rollup-freebsd-x64": {
625 "version": "4.56.0",
626 "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.56.0.tgz",
627 "integrity": "sha512-KNa6lYHloW+7lTEkYGa37fpvPq+NKG/EHKM8+G/g9WDU7ls4sMqbVRV78J6LdNuVaeeK5WB9/9VAFbKxcbXKYg==",
628 "cpu": [
629 "x64"
630 ],
631 "dev": true,
632 "license": "MIT",
633 "optional": true,
634 "os": [
635 "freebsd"
636 ]
637 },
638 "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
639 "version": "4.56.0",
640 "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.56.0.tgz",
641 "integrity": "sha512-E8jKK87uOvLrrLN28jnAAAChNq5LeCd2mGgZF+fGF5D507WlG/Noct3lP/QzQ6MrqJ5BCKNwI9ipADB6jyiq2A==",
642 "cpu": [
643 "arm"
644 ],
645 "dev": true,
646 "license": "MIT",
647 "optional": true,
648 "os": [
649 "linux"
650 ]
651 },
652 "node_modules/@rollup/rollup-linux-arm-musleabihf": {
653 "version": "4.56.0",
654 "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.56.0.tgz",
655 "integrity": "sha512-jQosa5FMYF5Z6prEpTCCmzCXz6eKr/tCBssSmQGEeozA9tkRUty/5Vx06ibaOP9RCrW1Pvb8yp3gvZhHwTDsJw==",
656 "cpu": [
657 "arm"
658 ],
659 "dev": true,
660 "license": "MIT",
661 "optional": true,
662 "os": [
663 "linux"
664 ]
665 },
666 "node_modules/@rollup/rollup-linux-arm64-gnu": {
667 "version": "4.56.0",
668 "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.56.0.tgz",
669 "integrity": "sha512-uQVoKkrC1KGEV6udrdVahASIsaF8h7iLG0U0W+Xn14ucFwi6uS539PsAr24IEF9/FoDtzMeeJXJIBo5RkbNWvQ==",
670 "cpu": [
671 "arm64"
672 ],
673 "dev": true,
674 "license": "MIT",
675 "optional": true,
676 "os": [
677 "linux"
678 ]
679 },
680 "node_modules/@rollup/rollup-linux-arm64-musl": {
681 "version": "4.56.0",
682 "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.56.0.tgz",
683 "integrity": "sha512-vLZ1yJKLxhQLFKTs42RwTwa6zkGln+bnXc8ueFGMYmBTLfNu58sl5/eXyxRa2RarTkJbXl8TKPgfS6V5ijNqEA==",
684 "cpu": [
685 "arm64"
686 ],
687 "dev": true,
688 "license": "MIT",
689 "optional": true,
690 "os": [
691 "linux"
692 ]
693 },
694 "node_modules/@rollup/rollup-linux-loong64-gnu": {
695 "version": "4.56.0",
696 "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.56.0.tgz",
697 "integrity": "sha512-FWfHOCub564kSE3xJQLLIC/hbKqHSVxy8vY75/YHHzWvbJL7aYJkdgwD/xGfUlL5UV2SB7otapLrcCj2xnF1dg==",
698 "cpu": [
699 "loong64"
700 ],
701 "dev": true,
702 "license": "MIT",
703 "optional": true,
704 "os": [
705 "linux"
706 ]
707 },
708 "node_modules/@rollup/rollup-linux-loong64-musl": {
709 "version": "4.56.0",
710 "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.56.0.tgz",
711 "integrity": "sha512-z1EkujxIh7nbrKL1lmIpqFTc/sr0u8Uk0zK/qIEFldbt6EDKWFk/pxFq3gYj4Bjn3aa9eEhYRlL3H8ZbPT1xvA==",
712 "cpu": [
713 "loong64"
714 ],
715 "dev": true,
716 "license": "MIT",
717 "optional": true,
718 "os": [
719 "linux"
720 ]
721 },
722 "node_modules/@rollup/rollup-linux-ppc64-gnu": {
723 "version": "4.56.0",
724 "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.56.0.tgz",
725 "integrity": "sha512-iNFTluqgdoQC7AIE8Q34R3AuPrJGJirj5wMUErxj22deOcY7XwZRaqYmB6ZKFHoVGqRcRd0mqO+845jAibKCkw==",
726 "cpu": [
727 "ppc64"
728 ],
729 "dev": true,
730 "license": "MIT",
731 "optional": true,
732 "os": [
733 "linux"
734 ]
735 },
736 "node_modules/@rollup/rollup-linux-ppc64-musl": {
737 "version": "4.56.0",
738 "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.56.0.tgz",
739 "integrity": "sha512-MtMeFVlD2LIKjp2sE2xM2slq3Zxf9zwVuw0jemsxvh1QOpHSsSzfNOTH9uYW9i1MXFxUSMmLpeVeUzoNOKBaWg==",
740 "cpu": [
741 "ppc64"
742 ],
743 "dev": true,
744 "license": "MIT",
745 "optional": true,
746 "os": [
747 "linux"
748 ]
749 },
750 "node_modules/@rollup/rollup-linux-riscv64-gnu": {
751 "version": "4.56.0",
752 "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.56.0.tgz",
753 "integrity": "sha512-in+v6wiHdzzVhYKXIk5U74dEZHdKN9KH0Q4ANHOTvyXPG41bajYRsy7a8TPKbYPl34hU7PP7hMVHRvv/5aCSew==",
754 "cpu": [
755 "riscv64"
756 ],
757 "dev": true,
758 "license": "MIT",
759 "optional": true,
760 "os": [
761 "linux"
762 ]
763 },
764 "node_modules/@rollup/rollup-linux-riscv64-musl": {
765 "version": "4.56.0",
766 "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.56.0.tgz",
767 "integrity": "sha512-yni2raKHB8m9NQpI9fPVwN754mn6dHQSbDTwxdr9SE0ks38DTjLMMBjrwvB5+mXrX+C0npX0CVeCUcvvvD8CNQ==",
768 "cpu": [
769 "riscv64"
770 ],
771 "dev": true,
772 "license": "MIT",
773 "optional": true,
774 "os": [
775 "linux"
776 ]
777 },
778 "node_modules/@rollup/rollup-linux-s390x-gnu": {
779 "version": "4.56.0",
780 "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.56.0.tgz",
781 "integrity": "sha512-zhLLJx9nQPu7wezbxt2ut+CI4YlXi68ndEve16tPc/iwoylWS9B3FxpLS2PkmfYgDQtosah07Mj9E0khc3Y+vQ==",
782 "cpu": [
783 "s390x"
784 ],
785 "dev": true,
786 "license": "MIT",
787 "optional": true,
788 "os": [
789 "linux"
790 ]
791 },
792 "node_modules/@rollup/rollup-linux-x64-gnu": {
793 "version": "4.56.0",
794 "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.56.0.tgz",
795 "integrity": "sha512-MVC6UDp16ZSH7x4rtuJPAEoE1RwS8N4oK9DLHy3FTEdFoUTCFVzMfJl/BVJ330C+hx8FfprA5Wqx4FhZXkj2Kw==",
796 "cpu": [
797 "x64"
798 ],
799 "dev": true,
800 "license": "MIT",
801 "optional": true,
802 "os": [
803 "linux"
804 ]
805 },
806 "node_modules/@rollup/rollup-linux-x64-musl": {
807 "version": "4.56.0",
808 "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.56.0.tgz",
809 "integrity": "sha512-ZhGH1eA4Qv0lxaV00azCIS1ChedK0V32952Md3FtnxSqZTBTd6tgil4nZT5cU8B+SIw3PFYkvyR4FKo2oyZIHA==",
810 "cpu": [
811 "x64"
812 ],
813 "dev": true,
814 "license": "MIT",
815 "optional": true,
816 "os": [
817 "linux"
818 ]
819 },
820 "node_modules/@rollup/rollup-openbsd-x64": {
821 "version": "4.56.0",
822 "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.56.0.tgz",
823 "integrity": "sha512-O16XcmyDeFI9879pEcmtWvD/2nyxR9mF7Gs44lf1vGGx8Vg2DRNx11aVXBEqOQhWb92WN4z7fW/q4+2NYzCbBA==",
824 "cpu": [
825 "x64"
826 ],
827 "dev": true,
828 "license": "MIT",
829 "optional": true,
830 "os": [
831 "openbsd"
832 ]
833 },
834 "node_modules/@rollup/rollup-openharmony-arm64": {
835 "version": "4.56.0",
836 "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.56.0.tgz",
837 "integrity": "sha512-LhN/Reh+7F3RCgQIRbgw8ZMwUwyqJM+8pXNT6IIJAqm2IdKkzpCh/V9EdgOMBKuebIrzswqy4ATlrDgiOwbRcQ==",
838 "cpu": [
839 "arm64"
840 ],
841 "dev": true,
842 "license": "MIT",
843 "optional": true,
844 "os": [
845 "openharmony"
846 ]
847 },
848 "node_modules/@rollup/rollup-win32-arm64-msvc": {
849 "version": "4.56.0",
850 "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.56.0.tgz",
851 "integrity": "sha512-kbFsOObXp3LBULg1d3JIUQMa9Kv4UitDmpS+k0tinPBz3watcUiV2/LUDMMucA6pZO3WGE27P7DsfaN54l9ing==",
852 "cpu": [
853 "arm64"
854 ],
855 "dev": true,
856 "license": "MIT",
857 "optional": true,
858 "os": [
859 "win32"
860 ]
861 },
862 "node_modules/@rollup/rollup-win32-ia32-msvc": {
863 "version": "4.56.0",
864 "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.56.0.tgz",
865 "integrity": "sha512-vSSgny54D6P4vf2izbtFm/TcWYedw7f8eBrOiGGecyHyQB9q4Kqentjaj8hToe+995nob/Wv48pDqL5a62EWtg==",
866 "cpu": [
867 "ia32"
868 ],
869 "dev": true,
870 "license": "MIT",
871 "optional": true,
872 "os": [
873 "win32"
874 ]
875 },
876 "node_modules/@rollup/rollup-win32-x64-gnu": {
877 "version": "4.56.0",
878 "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.56.0.tgz",
879 "integrity": "sha512-FeCnkPCTHQJFbiGG49KjV5YGW/8b9rrXAM2Mz2kiIoktq2qsJxRD5giEMEOD2lPdgs72upzefaUvS+nc8E3UzQ==",
880 "cpu": [
881 "x64"
882 ],
883 "dev": true,
884 "license": "MIT",
885 "optional": true,
886 "os": [
887 "win32"
888 ]
889 },
890 "node_modules/@rollup/rollup-win32-x64-msvc": {
891 "version": "4.56.0",
892 "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.56.0.tgz",
893 "integrity": "sha512-H8AE9Ur/t0+1VXujj90w0HrSOuv0Nq9r1vSZF2t5km20NTfosQsGGUXDaKdQZzwuLts7IyL1fYT4hM95TI9c4g==",
894 "cpu": [
895 "x64"
896 ],
897 "dev": true,
898 "license": "MIT",
899 "optional": true,
900 "os": [
901 "win32"
902 ]
903 },
904 "node_modules/@standard-schema/spec": {
905 "version": "1.1.0",
906 "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
907 "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
908 "dev": true,
909 "license": "MIT"
910 },
911 "node_modules/@tanstack/query-core": {
912 "version": "5.90.20",
913 "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.20.tgz",
914 "integrity": "sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==",
915 "dev": true,
916 "license": "MIT",
917 "funding": {
918 "type": "github",
919 "url": "https://github.com/sponsors/tannerlinsley"
920 }
921 },
922 "node_modules/@tanstack/react-query": {
923 "version": "5.90.20",
924 "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.20.tgz",
925 "integrity": "sha512-vXBxa+qeyveVO7OA0jX1z+DeyCA4JKnThKv411jd5SORpBKgkcVnYKCiBgECvADvniBX7tobwBmg01qq9JmMJw==",
926 "dev": true,
927 "license": "MIT",
928 "dependencies": {
929 "@tanstack/query-core": "5.90.20"
930 },
931 "funding": {
932 "type": "github",
933 "url": "https://github.com/sponsors/tannerlinsley"
934 },
935 "peerDependencies": {
936 "react": "^18 || ^19"
937 }
938 },
939 "node_modules/@types/chai": {
940 "version": "5.2.3",
941 "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
942 "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
943 "dev": true,
944 "license": "MIT",
945 "dependencies": {
946 "@types/deep-eql": "*",
947 "assertion-error": "^2.0.1"
948 }
949 },
950 "node_modules/@types/deep-eql": {
951 "version": "4.0.2",
952 "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
953 "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
954 "dev": true,
955 "license": "MIT"
956 },
957 "node_modules/@types/estree": {
958 "version": "1.0.8",
959 "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
960 "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
961 "dev": true,
962 "license": "MIT"
963 },
964 "node_modules/@types/react": {
965 "version": "19.2.9",
966 "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.9.tgz",
967 "integrity": "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==",
968 "dev": true,
969 "license": "MIT",
970 "dependencies": {
971 "csstype": "^3.2.2"
972 }
973 },
974 "node_modules/@vitest/coverage-v8": {
975 "version": "4.0.18",
976 "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.0.18.tgz",
977 "integrity": "sha512-7i+N2i0+ME+2JFZhfuz7Tg/FqKtilHjGyGvoHYQ6iLV0zahbsJ9sljC9OcFcPDbhYKCet+sG8SsVqlyGvPflZg==",
978 "dev": true,
979 "license": "MIT",
980 "dependencies": {
981 "@bcoe/v8-coverage": "^1.0.2",
982 "@vitest/utils": "4.0.18",
983 "ast-v8-to-istanbul": "^0.3.10",
984 "istanbul-lib-coverage": "^3.2.2",
985 "istanbul-lib-report": "^3.0.1",
986 "istanbul-reports": "^3.2.0",
987 "magicast": "^0.5.1",
988 "obug": "^2.1.1",
989 "std-env": "^3.10.0",
990 "tinyrainbow": "^3.0.3"
991 },
992 "funding": {
993 "url": "https://opencollective.com/vitest"
994 },
995 "peerDependencies": {
996 "@vitest/browser": "4.0.18",
997 "vitest": "4.0.18"
998 },
999 "peerDependenciesMeta": {
1000 "@vitest/browser": {
1001 "optional": true
1002 }
1003 }
1004 },
1005 "node_modules/@vitest/expect": {
1006 "version": "4.0.18",
1007 "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz",
1008 "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==",
1009 "dev": true,
1010 "license": "MIT",
1011 "dependencies": {
1012 "@standard-schema/spec": "^1.0.0",
1013 "@types/chai": "^5.2.2",
1014 "@vitest/spy": "4.0.18",
1015 "@vitest/utils": "4.0.18",
1016 "chai": "^6.2.1",
1017 "tinyrainbow": "^3.0.3"
1018 },
1019 "funding": {
1020 "url": "https://opencollective.com/vitest"
1021 }
1022 },
1023 "node_modules/@vitest/mocker": {
1024 "version": "4.0.18",
1025 "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz",
1026 "integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==",
1027 "dev": true,
1028 "license": "MIT",
1029 "dependencies": {
1030 "@vitest/spy": "4.0.18",
1031 "estree-walker": "^3.0.3",
1032 "magic-string": "^0.30.21"
1033 },
1034 "funding": {
1035 "url": "https://opencollective.com/vitest"
1036 },
1037 "peerDependencies": {
1038 "msw": "^2.4.9",
1039 "vite": "^6.0.0 || ^7.0.0-0"
1040 },
1041 "peerDependenciesMeta": {
1042 "msw": {
1043 "optional": true
1044 },
1045 "vite": {
1046 "optional": true
1047 }
1048 }
1049 },
1050 "node_modules/@vitest/pretty-format": {
1051 "version": "4.0.18",
1052 "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz",
1053 "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==",
1054 "dev": true,
1055 "license": "MIT",
1056 "dependencies": {
1057 "tinyrainbow": "^3.0.3"
1058 },
1059 "funding": {
1060 "url": "https://opencollective.com/vitest"
1061 }
1062 },
1063 "node_modules/@vitest/runner": {
1064 "version": "4.0.18",
1065 "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz",
1066 "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==",
1067 "dev": true,
1068 "license": "MIT",
1069 "dependencies": {
1070 "@vitest/utils": "4.0.18",
1071 "pathe": "^2.0.3"
1072 },
1073 "funding": {
1074 "url": "https://opencollective.com/vitest"
1075 }
1076 },
1077 "node_modules/@vitest/snapshot": {
1078 "version": "4.0.18",
1079 "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz",
1080 "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==",
1081 "dev": true,
1082 "license": "MIT",
1083 "dependencies": {
1084 "@vitest/pretty-format": "4.0.18",
1085 "magic-string": "^0.30.21",
1086 "pathe": "^2.0.3"
1087 },
1088 "funding": {
1089 "url": "https://opencollective.com/vitest"
1090 }
1091 },
1092 "node_modules/@vitest/spy": {
1093 "version": "4.0.18",
1094 "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz",
1095 "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==",
1096 "dev": true,
1097 "license": "MIT",
1098 "funding": {
1099 "url": "https://opencollective.com/vitest"
1100 }
1101 },
1102 "node_modules/@vitest/ui": {
1103 "version": "4.0.18",
1104 "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.0.18.tgz",
1105 "integrity": "sha512-CGJ25bc8fRi8Lod/3GHSvXRKi7nBo3kxh0ApW4yCjmrWmRmlT53B5E08XRSZRliygG0aVNxLrBEqPYdz/KcCtQ==",
1106 "dev": true,
1107 "license": "MIT",
1108 "peer": true,
1109 "dependencies": {
1110 "@vitest/utils": "4.0.18",
1111 "fflate": "^0.8.2",
1112 "flatted": "^3.3.3",
1113 "pathe": "^2.0.3",
1114 "sirv": "^3.0.2",
1115 "tinyglobby": "^0.2.15",
1116 "tinyrainbow": "^3.0.3"
1117 },
1118 "funding": {
1119 "url": "https://opencollective.com/vitest"
1120 },
1121 "peerDependencies": {
1122 "vitest": "4.0.18"
1123 }
1124 },
1125 "node_modules/@vitest/utils": {
1126 "version": "4.0.18",
1127 "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz",
1128 "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==",
1129 "dev": true,
1130 "license": "MIT",
1131 "dependencies": {
1132 "@vitest/pretty-format": "4.0.18",
1133 "tinyrainbow": "^3.0.3"
1134 },
1135 "funding": {
1136 "url": "https://opencollective.com/vitest"
1137 }
1138 },
1139 "node_modules/assertion-error": {
1140 "version": "2.0.1",
1141 "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
1142 "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
1143 "dev": true,
1144 "license": "MIT",
1145 "engines": {
1146 "node": ">=12"
1147 }
1148 },
1149 "node_modules/ast-v8-to-istanbul": {
1150 "version": "0.3.10",
1151 "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.10.tgz",
1152 "integrity": "sha512-p4K7vMz2ZSk3wN8l5o3y2bJAoZXT3VuJI5OLTATY/01CYWumWvwkUw0SqDBnNq6IiTO3qDa1eSQDibAV8g7XOQ==",
1153 "dev": true,
1154 "license": "MIT",
1155 "dependencies": {
1156 "@jridgewell/trace-mapping": "^0.3.31",
1157 "estree-walker": "^3.0.3",
1158 "js-tokens": "^9.0.1"
1159 }
1160 },
1161 "node_modules/chai": {
1162 "version": "6.2.2",
1163 "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
1164 "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
1165 "dev": true,
1166 "license": "MIT",
1167 "engines": {
1168 "node": ">=18"
1169 }
1170 },
1171 "node_modules/csstype": {
1172 "version": "3.2.3",
1173 "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
1174 "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
1175 "dev": true,
1176 "license": "MIT"
1177 },
1178 "node_modules/es-module-lexer": {
1179 "version": "1.7.0",
1180 "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
1181 "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
1182 "dev": true,
1183 "license": "MIT"
1184 },
1185 "node_modules/esbuild": {
1186 "version": "0.27.2",
1187 "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz",
1188 "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==",
1189 "dev": true,
1190 "hasInstallScript": true,
1191 "license": "MIT",
1192 "bin": {
1193 "esbuild": "bin/esbuild"
1194 },
1195 "engines": {
1196 "node": ">=18"
1197 },
1198 "optionalDependencies": {
1199 "@esbuild/aix-ppc64": "0.27.2",
1200 "@esbuild/android-arm": "0.27.2",
1201 "@esbuild/android-arm64": "0.27.2",
1202 "@esbuild/android-x64": "0.27.2",
1203 "@esbuild/darwin-arm64": "0.27.2",
1204 "@esbuild/darwin-x64": "0.27.2",
1205 "@esbuild/freebsd-arm64": "0.27.2",
1206 "@esbuild/freebsd-x64": "0.27.2",
1207 "@esbuild/linux-arm": "0.27.2",
1208 "@esbuild/linux-arm64": "0.27.2",
1209 "@esbuild/linux-ia32": "0.27.2",
1210 "@esbuild/linux-loong64": "0.27.2",
1211 "@esbuild/linux-mips64el": "0.27.2",
1212 "@esbuild/linux-ppc64": "0.27.2",
1213 "@esbuild/linux-riscv64": "0.27.2",
1214 "@esbuild/linux-s390x": "0.27.2",
1215 "@esbuild/linux-x64": "0.27.2",
1216 "@esbuild/netbsd-arm64": "0.27.2",
1217 "@esbuild/netbsd-x64": "0.27.2",
1218 "@esbuild/openbsd-arm64": "0.27.2",
1219 "@esbuild/openbsd-x64": "0.27.2",
1220 "@esbuild/openharmony-arm64": "0.27.2",
1221 "@esbuild/sunos-x64": "0.27.2",
1222 "@esbuild/win32-arm64": "0.27.2",
1223 "@esbuild/win32-ia32": "0.27.2",
1224 "@esbuild/win32-x64": "0.27.2"
1225 }
1226 },
1227 "node_modules/estree-walker": {
1228 "version": "3.0.3",
1229 "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
1230 "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
1231 "dev": true,
1232 "license": "MIT",
1233 "dependencies": {
1234 "@types/estree": "^1.0.0"
1235 }
1236 },
1237 "node_modules/expect-type": {
1238 "version": "1.3.0",
1239 "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
1240 "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==",
1241 "dev": true,
1242 "license": "Apache-2.0",
1243 "engines": {
1244 "node": ">=12.0.0"
1245 }
1246 },
1247 "node_modules/fdir": {
1248 "version": "6.5.0",
1249 "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
1250 "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
1251 "dev": true,
1252 "license": "MIT",
1253 "engines": {
1254 "node": ">=12.0.0"
1255 },
1256 "peerDependencies": {
1257 "picomatch": "^3 || ^4"
1258 },
1259 "peerDependenciesMeta": {
1260 "picomatch": {
1261 "optional": true
1262 }
1263 }
1264 },
1265 "node_modules/fflate": {
1266 "version": "0.8.2",
1267 "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
1268 "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
1269 "dev": true,
1270 "license": "MIT"
1271 },
1272 "node_modules/flatted": {
1273 "version": "3.3.3",
1274 "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
1275 "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
1276 "dev": true,
1277 "license": "ISC"
1278 },
1279 "node_modules/fsevents": {
1280 "version": "2.3.3",
1281 "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
1282 "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
1283 "dev": true,
1284 "hasInstallScript": true,
1285 "license": "MIT",
1286 "optional": true,
1287 "os": [
1288 "darwin"
1289 ],
1290 "engines": {
1291 "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
1292 }
1293 },
1294 "node_modules/has-flag": {
1295 "version": "4.0.0",
1296 "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
1297 "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
1298 "dev": true,
1299 "license": "MIT",
1300 "engines": {
1301 "node": ">=8"
1302 }
1303 },
1304 "node_modules/html-escaper": {
1305 "version": "2.0.2",
1306 "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
1307 "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
1308 "dev": true,
1309 "license": "MIT"
1310 },
1311 "node_modules/istanbul-lib-coverage": {
1312 "version": "3.2.2",
1313 "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
1314 "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==",
1315 "dev": true,
1316 "license": "BSD-3-Clause",
1317 "engines": {
1318 "node": ">=8"
1319 }
1320 },
1321 "node_modules/istanbul-lib-report": {
1322 "version": "3.0.1",
1323 "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
1324 "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
1325 "dev": true,
1326 "license": "BSD-3-Clause",
1327 "dependencies": {
1328 "istanbul-lib-coverage": "^3.0.0",
1329 "make-dir": "^4.0.0",
1330 "supports-color": "^7.1.0"
1331 },
1332 "engines": {
1333 "node": ">=10"
1334 }
1335 },
1336 "node_modules/istanbul-reports": {
1337 "version": "3.2.0",
1338 "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz",
1339 "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==",
1340 "dev": true,
1341 "license": "BSD-3-Clause",
1342 "dependencies": {
1343 "html-escaper": "^2.0.0",
1344 "istanbul-lib-report": "^3.0.0"
1345 },
1346 "engines": {
1347 "node": ">=8"
1348 }
1349 },
1350 "node_modules/js-tokens": {
1351 "version": "9.0.1",
1352 "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz",
1353 "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==",
1354 "dev": true,
1355 "license": "MIT"
1356 },
1357 "node_modules/magic-string": {
1358 "version": "0.30.21",
1359 "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
1360 "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
1361 "dev": true,
1362 "license": "MIT",
1363 "dependencies": {
1364 "@jridgewell/sourcemap-codec": "^1.5.5"
1365 }
1366 },
1367 "node_modules/magicast": {
1368 "version": "0.5.1",
1369 "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.1.tgz",
1370 "integrity": "sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==",
1371 "dev": true,
1372 "license": "MIT",
1373 "dependencies": {
1374 "@babel/parser": "^7.28.5",
1375 "@babel/types": "^7.28.5",
1376 "source-map-js": "^1.2.1"
1377 }
1378 },
1379 "node_modules/make-dir": {
1380 "version": "4.0.0",
1381 "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
1382 "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
1383 "dev": true,
1384 "license": "MIT",
1385 "dependencies": {
1386 "semver": "^7.5.3"
1387 },
1388 "engines": {
1389 "node": ">=10"
1390 },
1391 "funding": {
1392 "url": "https://github.com/sponsors/sindresorhus"
1393 }
1394 },
1395 "node_modules/mrmime": {
1396 "version": "2.0.1",
1397 "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
1398 "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
1399 "dev": true,
1400 "license": "MIT",
1401 "engines": {
1402 "node": ">=10"
1403 }
1404 },
1405 "node_modules/nanoid": {
1406 "version": "3.3.11",
1407 "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
1408 "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
1409 "dev": true,
1410 "funding": [
1411 {
1412 "type": "github",
1413 "url": "https://github.com/sponsors/ai"
1414 }
1415 ],
1416 "license": "MIT",
1417 "bin": {
1418 "nanoid": "bin/nanoid.cjs"
1419 },
1420 "engines": {
1421 "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
1422 }
1423 },
1424 "node_modules/obug": {
1425 "version": "2.1.1",
1426 "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
1427 "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==",
1428 "dev": true,
1429 "funding": [
1430 "https://github.com/sponsors/sxzz",
1431 "https://opencollective.com/debug"
1432 ],
1433 "license": "MIT"
1434 },
1435 "node_modules/pathe": {
1436 "version": "2.0.3",
1437 "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
1438 "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
1439 "dev": true,
1440 "license": "MIT"
1441 },
1442 "node_modules/picocolors": {
1443 "version": "1.1.1",
1444 "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
1445 "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
1446 "dev": true,
1447 "license": "ISC"
1448 },
1449 "node_modules/picomatch": {
1450 "version": "4.0.3",
1451 "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
1452 "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
1453 "dev": true,
1454 "license": "MIT",
1455 "peer": true,
1456 "engines": {
1457 "node": ">=12"
1458 },
1459 "funding": {
1460 "url": "https://github.com/sponsors/jonschlinkert"
1461 }
1462 },
1463 "node_modules/postcss": {
1464 "version": "8.5.6",
1465 "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
1466 "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
1467 "dev": true,
1468 "funding": [
1469 {
1470 "type": "opencollective",
1471 "url": "https://opencollective.com/postcss/"
1472 },
1473 {
1474 "type": "tidelift",
1475 "url": "https://tidelift.com/funding/github/npm/postcss"
1476 },
1477 {
1478 "type": "github",
1479 "url": "https://github.com/sponsors/ai"
1480 }
1481 ],
1482 "license": "MIT",
1483 "dependencies": {
1484 "nanoid": "^3.3.11",
1485 "picocolors": "^1.1.1",
1486 "source-map-js": "^1.2.1"
1487 },
1488 "engines": {
1489 "node": "^10 || ^12 || >=14"
1490 }
1491 },
1492 "node_modules/react": {
1493 "version": "19.2.3",
1494 "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
1495 "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
1496 "dev": true,
1497 "license": "MIT",
1498 "peer": true,
1499 "engines": {
1500 "node": ">=0.10.0"
1501 }
1502 },
1503 "node_modules/rollup": {
1504 "version": "4.56.0",
1505 "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.56.0.tgz",
1506 "integrity": "sha512-9FwVqlgUHzbXtDg9RCMgodF3Ua4Na6Gau+Sdt9vyCN4RhHfVKX2DCHy3BjMLTDd47ITDhYAnTwGulWTblJSDLg==",
1507 "dev": true,
1508 "license": "MIT",
1509 "dependencies": {
1510 "@types/estree": "1.0.8"
1511 },
1512 "bin": {
1513 "rollup": "dist/bin/rollup"
1514 },
1515 "engines": {
1516 "node": ">=18.0.0",
1517 "npm": ">=8.0.0"
1518 },
1519 "optionalDependencies": {
1520 "@rollup/rollup-android-arm-eabi": "4.56.0",
1521 "@rollup/rollup-android-arm64": "4.56.0",
1522 "@rollup/rollup-darwin-arm64": "4.56.0",
1523 "@rollup/rollup-darwin-x64": "4.56.0",
1524 "@rollup/rollup-freebsd-arm64": "4.56.0",
1525 "@rollup/rollup-freebsd-x64": "4.56.0",
1526 "@rollup/rollup-linux-arm-gnueabihf": "4.56.0",
1527 "@rollup/rollup-linux-arm-musleabihf": "4.56.0",
1528 "@rollup/rollup-linux-arm64-gnu": "4.56.0",
1529 "@rollup/rollup-linux-arm64-musl": "4.56.0",
1530 "@rollup/rollup-linux-loong64-gnu": "4.56.0",
1531 "@rollup/rollup-linux-loong64-musl": "4.56.0",
1532 "@rollup/rollup-linux-ppc64-gnu": "4.56.0",
1533 "@rollup/rollup-linux-ppc64-musl": "4.56.0",
1534 "@rollup/rollup-linux-riscv64-gnu": "4.56.0",
1535 "@rollup/rollup-linux-riscv64-musl": "4.56.0",
1536 "@rollup/rollup-linux-s390x-gnu": "4.56.0",
1537 "@rollup/rollup-linux-x64-gnu": "4.56.0",
1538 "@rollup/rollup-linux-x64-musl": "4.56.0",
1539 "@rollup/rollup-openbsd-x64": "4.56.0",
1540 "@rollup/rollup-openharmony-arm64": "4.56.0",
1541 "@rollup/rollup-win32-arm64-msvc": "4.56.0",
1542 "@rollup/rollup-win32-ia32-msvc": "4.56.0",
1543 "@rollup/rollup-win32-x64-gnu": "4.56.0",
1544 "@rollup/rollup-win32-x64-msvc": "4.56.0",
1545 "fsevents": "~2.3.2"
1546 }
1547 },
1548 "node_modules/semver": {
1549 "version": "7.7.3",
1550 "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
1551 "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
1552 "dev": true,
1553 "license": "ISC",
1554 "bin": {
1555 "semver": "bin/semver.js"
1556 },
1557 "engines": {
1558 "node": ">=10"
1559 }
1560 },
1561 "node_modules/siginfo": {
1562 "version": "2.0.0",
1563 "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
1564 "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
1565 "dev": true,
1566 "license": "ISC"
1567 },
1568 "node_modules/sirv": {
1569 "version": "3.0.2",
1570 "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz",
1571 "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==",
1572 "dev": true,
1573 "license": "MIT",
1574 "dependencies": {
1575 "@polka/url": "^1.0.0-next.24",
1576 "mrmime": "^2.0.0",
1577 "totalist": "^3.0.0"
1578 },
1579 "engines": {
1580 "node": ">=18"
1581 }
1582 },
1583 "node_modules/source-map-js": {
1584 "version": "1.2.1",
1585 "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
1586 "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
1587 "dev": true,
1588 "license": "BSD-3-Clause",
1589 "engines": {
1590 "node": ">=0.10.0"
1591 }
1592 },
1593 "node_modules/stackback": {
1594 "version": "0.0.2",
1595 "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
1596 "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
1597 "dev": true,
1598 "license": "MIT"
1599 },
1600 "node_modules/std-env": {
1601 "version": "3.10.0",
1602 "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz",
1603 "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
1604 "dev": true,
1605 "license": "MIT"
1606 },
1607 "node_modules/supports-color": {
1608 "version": "7.2.0",
1609 "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
1610 "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
1611 "dev": true,
1612 "license": "MIT",
1613 "dependencies": {
1614 "has-flag": "^4.0.0"
1615 },
1616 "engines": {
1617 "node": ">=8"
1618 }
1619 },
1620 "node_modules/tinybench": {
1621 "version": "2.9.0",
1622 "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
1623 "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
1624 "dev": true,
1625 "license": "MIT"
1626 },
1627 "node_modules/tinyexec": {
1628 "version": "1.0.2",
1629 "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",
1630 "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==",
1631 "dev": true,
1632 "license": "MIT",
1633 "engines": {
1634 "node": ">=18"
1635 }
1636 },
1637 "node_modules/tinyglobby": {
1638 "version": "0.2.15",
1639 "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
1640 "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
1641 "dev": true,
1642 "license": "MIT",
1643 "dependencies": {
1644 "fdir": "^6.5.0",
1645 "picomatch": "^4.0.3"
1646 },
1647 "engines": {
1648 "node": ">=12.0.0"
1649 },
1650 "funding": {
1651 "url": "https://github.com/sponsors/SuperchupuDev"
1652 }
1653 },
1654 "node_modules/tinyrainbow": {
1655 "version": "3.0.3",
1656 "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz",
1657 "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==",
1658 "dev": true,
1659 "license": "MIT",
1660 "engines": {
1661 "node": ">=14.0.0"
1662 }
1663 },
1664 "node_modules/totalist": {
1665 "version": "3.0.1",
1666 "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
1667 "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
1668 "dev": true,
1669 "license": "MIT",
1670 "engines": {
1671 "node": ">=6"
1672 }
1673 },
1674 "node_modules/vite": {
1675 "version": "7.3.1",
1676 "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
1677 "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
1678 "dev": true,
1679 "license": "MIT",
1680 "peer": true,
1681 "dependencies": {
1682 "esbuild": "^0.27.0",
1683 "fdir": "^6.5.0",
1684 "picomatch": "^4.0.3",
1685 "postcss": "^8.5.6",
1686 "rollup": "^4.43.0",
1687 "tinyglobby": "^0.2.15"
1688 },
1689 "bin": {
1690 "vite": "bin/vite.js"
1691 },
1692 "engines": {
1693 "node": "^20.19.0 || >=22.12.0"
1694 },
1695 "funding": {
1696 "url": "https://github.com/vitejs/vite?sponsor=1"
1697 },
1698 "optionalDependencies": {
1699 "fsevents": "~2.3.3"
1700 },
1701 "peerDependencies": {
1702 "@types/node": "^20.19.0 || >=22.12.0",
1703 "jiti": ">=1.21.0",
1704 "less": "^4.0.0",
1705 "lightningcss": "^1.21.0",
1706 "sass": "^1.70.0",
1707 "sass-embedded": "^1.70.0",
1708 "stylus": ">=0.54.8",
1709 "sugarss": "^5.0.0",
1710 "terser": "^5.16.0",
1711 "tsx": "^4.8.1",
1712 "yaml": "^2.4.2"
1713 },
1714 "peerDependenciesMeta": {
1715 "@types/node": {
1716 "optional": true
1717 },
1718 "jiti": {
1719 "optional": true
1720 },
1721 "less": {
1722 "optional": true
1723 },
1724 "lightningcss": {
1725 "optional": true
1726 },
1727 "sass": {
1728 "optional": true
1729 },
1730 "sass-embedded": {
1731 "optional": true
1732 },
1733 "stylus": {
1734 "optional": true
1735 },
1736 "sugarss": {
1737 "optional": true
1738 },
1739 "terser": {
1740 "optional": true
1741 },
1742 "tsx": {
1743 "optional": true
1744 },
1745 "yaml": {
1746 "optional": true
1747 }
1748 }
1749 },
1750 "node_modules/vitest": {
1751 "version": "4.0.18",
1752 "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz",
1753 "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==",
1754 "dev": true,
1755 "license": "MIT",
1756 "peer": true,
1757 "dependencies": {
1758 "@vitest/expect": "4.0.18",
1759 "@vitest/mocker": "4.0.18",
1760 "@vitest/pretty-format": "4.0.18",
1761 "@vitest/runner": "4.0.18",
1762 "@vitest/snapshot": "4.0.18",
1763 "@vitest/spy": "4.0.18",
1764 "@vitest/utils": "4.0.18",
1765 "es-module-lexer": "^1.7.0",
1766 "expect-type": "^1.2.2",
1767 "magic-string": "^0.30.21",
1768 "obug": "^2.1.1",
1769 "pathe": "^2.0.3",
1770 "picomatch": "^4.0.3",
1771 "std-env": "^3.10.0",
1772 "tinybench": "^2.9.0",
1773 "tinyexec": "^1.0.2",
1774 "tinyglobby": "^0.2.15",
1775 "tinyrainbow": "^3.0.3",
1776 "vite": "^6.0.0 || ^7.0.0",
1777 "why-is-node-running": "^2.3.0"
1778 },
1779 "bin": {
1780 "vitest": "vitest.mjs"
1781 },
1782 "engines": {
1783 "node": "^20.0.0 || ^22.0.0 || >=24.0.0"
1784 },
1785 "funding": {
1786 "url": "https://opencollective.com/vitest"
1787 },
1788 "peerDependencies": {
1789 "@edge-runtime/vm": "*",
1790 "@opentelemetry/api": "^1.9.0",
1791 "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
1792 "@vitest/browser-playwright": "4.0.18",
1793 "@vitest/browser-preview": "4.0.18",
1794 "@vitest/browser-webdriverio": "4.0.18",
1795 "@vitest/ui": "4.0.18",
1796 "happy-dom": "*",
1797 "jsdom": "*"
1798 },
1799 "peerDependenciesMeta": {
1800 "@edge-runtime/vm": {
1801 "optional": true
1802 },
1803 "@opentelemetry/api": {
1804 "optional": true
1805 },
1806 "@types/node": {
1807 "optional": true
1808 },
1809 "@vitest/browser-playwright": {
1810 "optional": true
1811 },
1812 "@vitest/browser-preview": {
1813 "optional": true
1814 },
1815 "@vitest/browser-webdriverio": {
1816 "optional": true
1817 },
1818 "@vitest/ui": {
1819 "optional": true
1820 },
1821 "happy-dom": {
1822 "optional": true
1823 },
1824 "jsdom": {
1825 "optional": true
1826 }
1827 }
1828 },
1829 "node_modules/why-is-node-running": {
1830 "version": "2.3.0",
1831 "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
1832 "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
1833 "dev": true,
1834 "license": "MIT",
1835 "dependencies": {
1836 "siginfo": "^2.0.0",
1837 "stackback": "0.0.2"
1838 },
1839 "bin": {
1840 "why-is-node-running": "cli.js"
1841 },
1842 "engines": {
1843 "node": ">=8"
1844 }
1845 }
1846 }
1847}
package.json created+14
...@@ -0,0 +1,14 @@
1{
2 "type": "module",
3 "scripts": {
4 "test": "vitest run"
5 },
6 "devDependencies": {
7 "@tanstack/react-query": "^5.90.20",
8 "@types/react": "^19.2.9",
9 "@vitest/coverage-v8": "^4.0.18",
10 "@vitest/ui": "^4.0.18",
11 "react": "^19.2.3",
12 "vitest": "^4.0.18"
13 }
14}
src/batch.ts created+3
...@@ -0,0 +1,3 @@
1export function defineBatchMutation<Args extends unknown[], Result, Optimistic>(
2 options: BatchMutationOptions<Args, Result, Optimistic>,
3): Mutation<Args, Result> {}
src/client.ts created+56
...@@ -0,0 +1,56 @@
1import { type MutationOptions, QueuedMutation } from "./queued";
2import type { Mutation } from "./types";
3
4export interface MutationClientOptions<
5 Context extends object,
6 OptimisticHelpers extends object,
7> {
8 context: Context;
9 getOptimisticHelpers: (
10 onRestore: (cb: () => void) => void,
11 ) => OptimisticHelpers;
12}
13
14export class MutationClient<
15 Context extends object,
16 OptimisticHelpers extends object,
17> {
18 context: Context;
19 getOptimisticHelpers: (
20 onRestore: (cb: () => void) => void,
21 ) => OptimisticHelpers;
22
23 constructor(
24 { context, getOptimisticHelpers }: MutationClientOptions<
25 Context,
26 OptimisticHelpers
27 >,
28 ) {
29 this.context = context;
30 this.getOptimisticHelpers = getOptimisticHelpers;
31 }
32
33 reportError(error: unknown) {
34 //
35 }
36
37 defineMutation<Args extends unknown[], Result>(
38 options: MutationOptions<
39 Args,
40 Result,
41 { context: Context; optimisticHelpers: OptimisticHelpers }
42 >,
43 ): Mutation<Args, Result> {
44 return new QueuedMutation<
45 Args,
46 Result,
47 { context: Context; optimisticHelpers: OptimisticHelpers }
48 >(this, options);
49 }
50
51 // defineBatchMutation<Args extends unknown[], Result>(
52 // options: BatchMutationOptions<Args, Result>,
53 // ): Mutation<Args, Result> {
54 // return new BatchedMutation(this, options);
55 // }
56}
src/queued.ts created+157
...@@ -0,0 +1,157 @@
1import type { MutationClient } from "./client.ts";
2import type { GlobalMutationConfig, MutationClientConfig } from "./register.ts";
3import type { Mutation, MutationEvent } from "./types.ts";
4
5/**
6 * Argument to `defineMutation`.
7 * @template Args - the parameters to the mutation
8 * @template Result - the result of the API call
9 * @template Config - global values and helpers from `MutationContext`
10 */
11export interface MutationOptions<
12 Args extends unknown[],
13 Result,
14 Config extends MutationClientConfig = GlobalMutationConfig,
15> {
16 /**
17 * This function is only responsible for performing the underlying API call,
18 * syncronizing the optimistic state with reality. Throw on failure. A rest
19 * params type is used to allow type inference. Place this function first to
20 * ensure TypeScript correctly infers the argument type for the rest of the
21 * functions.
22 */
23 mutate: (...args: Args) => Promise<Result>;
24 /**
25 * Used in error messages and debug tools.
26 * Phrase it considering the template `Failed to ${describe(...)}`
27 */
28 describe: string | ((context: Config["context"] & { args: Args }) => string);
29 /**
30 * Specifying the optimistic strategy is required. To disable, pass an empty
31 * function with a comment to document why it isn't needed.
32 */
33 optimistic: (context: OptimisticContext<Args, Result, Config>) => void;
34 /**
35 * Refetch all of the data this mutation could have affected.
36 * This is called automatically on errors.
37 */
38 refetch: (context: Config["context"] & { args: Args }) => Promise<void>;
39 /**
40 * If the optimistic updator function is perfect, then this may be set to false.
41 * @default true
42 */
43 refetchOnSuccess?: boolean;
44 /**
45 * A key to associate related items. For example, returning a user ID. If
46 * specifying, then all mutations of the same key will evaluate in serial,
47 * but optimistic updates will apply instantly.
48 */
49 key?: (context: Config["context"] & { args: Args }) => string | string[];
50}
51
52export type OptimisticContext<
53 Args extends unknown[],
54 Result,
55 Config extends MutationClientConfig = GlobalMutationConfig,
56> = Config["context"] & {
57 helpers: Config["optimisticHelpers"];
58 args: Args;
59 /** Add an event listener to roll back the update */
60 onRestore: (cb: () => void) => void;
61 /** Add an event listener to apply `Result` to the store. */
62 onSuccess: (cb: (result: Result) => void) => void;
63};
64
65export class QueuedMutation<
66 Args extends unknown[],
67 Result,
68 Config extends MutationClientConfig,
69> implements Mutation<Args, Result> {
70 #options: MutationOptions<Args, Result, Config>;
71 #client: MutationClient<Config["context"], Config["optimisticHelpers"]>;
72 #queues: Map<string, {
73 listeners: Set<(update: MutationEvent<Result>) => void>;
74 rollbacks: Array<() => void>;
75 queue: Array<{
76 args: Args;
77 rollbacks: number;
78 }>;
79 }> = new Map();
80
81 constructor(
82 client: MutationClient<Config["context"], Config["optimisticHelpers"]>,
83 options: MutationOptions<Args, Result>,
84 ) {
85 this.#options = options;
86 this.#client = client;
87 }
88
89 #key(args: Args) {
90 const k = this.#options.key?.({ ...this.#client.context, args }) ??
91 "shared";
92 return JSON.stringify(k);
93 }
94
95 subscribe(
96 args: Args,
97 cb: (update: MutationEvent<Result>) => void,
98 ): () => void {
99 const k = this.#key(args);
100 let entry = this.#queues.get(k);
101 if (!entry) {
102 entry = { listeners: new Set(), rollbacks: [], queue: [] };
103 this.#queues.set(k, entry);
104 }
105 entry.listeners.add(cb);
106 return () => entry?.listeners.delete(cb);
107 }
108
109 describe(...args: Args): string {
110 const { describe } = this.#options;
111 return typeof describe === "function"
112 ? describe({ ...this.#client.context, args })
113 : describe;
114 }
115
116 /** Calling the mutation in a global scope. Errors are turned into UI toasts. */
117 run(...args: Args) {
118 this.runAndReturn(...args).catch((error) => {
119 this.#client.reportError(error);
120 });
121 }
122
123 /** Calls the mutation, treating the errors as promise rejection. */
124 runAndReturn(...args: Args): Promise<Result> {
125 // return this.#dedupe?.get(...args) ?? this.#execute(...args);
126 }
127
128 async #execute(...args: Args): Promise<Result> {
129 const restoreCallbacks: Array<() => void> = [];
130 const successCallbacks: Array<(result: Result) => void> = [];
131 const onRestore = (cb: () => void) => restoreCallbacks.push(cb);
132 const onSuccess = (cb: (result: Result) => void) =>
133 successCallbacks.push(cb);
134
135 try {
136 this.#options.optimistic({
137 ...this.#client.context,
138 args,
139 onRestore,
140 onSuccess,
141 });
142
143 const result = await this.#options.mutate(...args);
144 successCallbacks.forEach((cb) => cb(result));
145 if (this.#options.refetchOnSuccess !== false) {
146 this.#options.refetch({
147 ...this.#client.context,
148 args,
149 });
150 }
151 return result;
152 } catch (err) {
153 restoreCallbacks.forEach((cb) => cb());
154 throw err;
155 }
156 }
157}
src/react.tsx created+175
...@@ -0,0 +1,175 @@
1import {
2 defineMutation,
3 type GlobalMutationContext,
4 Mutation,
5 type OptimisticContext,
6} from "./types.ts";
7import {
8 type FC,
9 type MouseEvent,
10 type MouseEventHandler,
11 type ReactNode,
12 useCallback,
13 useEffect,
14 useRef,
15 useState,
16} from "react";
17
18export function useMutation<
19 Args extends unknown[],
20 Result,
21 M extends Mutation<Args, Result> | null,
22>(
23 mutation: M extends Mutation<Args, Result> ? M : never,
24) {
25 const ref = useRef<Observer | null>(null);
26 const [_, setRerender] = useState(0);
27 ref.current ??= new Observer(setRerender);
28 useEffect(() => {
29 ref.current ??= new Observer(setRerender);
30 return () => {
31 ref.current?.close();
32 ref.current = null;
33 };
34 }, []);
35 ref.current.mutation = mutation;
36 return ref.current.binding;
37}
38
39class Observer<A extends unknown[], R> {
40 setRerender: (n: number) => void;
41 mutation: Mutation<A, R> | null = null;
42
43 constructor(setRerender: (n: number) => void) {
44 this.setRerender = setRerender;
45 }
46
47 binding = ((self: this) => ({
48 mutate(...args: A) {
49 self.mutation?.runAndReturn(...args);
50 },
51 get isPending() {
52 return false;
53 },
54 get isMutating() {
55 return false;
56 },
57 get isUpToDate() {
58 return true;
59 },
60 get error() {
61 return null;
62 },
63 get isSuccess() {
64 return false;
65 },
66 get result() {
67 return null;
68 },
69 }))(this);
70
71 close() {}
72}
73
74interface BaseButtonProps {
75 onClick: MouseEventHandler<HTMLElement> | undefined;
76 isPending: boolean;
77}
78
79interface MutationButtonComponent<Props> {
80 <Args extends unknown[], Result>(
81 props:
82 & MutationButtonProps<Args, Result>
83 & Props,
84 ): ReactNode;
85 displayName?: string;
86}
87
88export interface MutationButtonProps<Args extends unknown[], Result> {
89 mutation: Mutation<Args, Result>;
90 /** Preventing default will interrupt the mutation */
91 args: Args | ((e: MouseEvent) => Args | null);
92 /** Preventing default will interrupt the mutation */
93 onClick?: (e: MouseEvent) => void;
94}
95
96/**
97 * Wraps a custom button component with logic to execute a mutation. The wrapped
98 * component must accept `onClick` and an `isPending` property. When the inner
99 * component emits `onClick`, that will begin the mutation.
100 */
101export function createMutationButton<Props>(
102 // Prevent calling this function if missing `onClick`
103 Component: Required<Props> extends BaseButtonProps ? FC<Props>
104 : "Missing required props",
105): MutationButtonComponent<Flatten<Omit<Props, keyof BaseButtonProps>>> {
106 // apply the generics at a type level to allow `.bind` to work
107 type BareProps = Omit<Props, keyof BaseButtonProps>;
108 const bound = (GenericMutationButton<Props, unknown[], unknown>)
109 // the `as` clause here converts the second and third generic parameter
110 // back into unspecified generics.
111 .bind(null, Component) as MutationButtonComponent<BareProps>;
112 // react devtools loves display names
113 bound.displayName = `MutationButton[${
114 Component.displayName ?? Component.name
115 }]`;
116
117 return bound;
118}
119
120type Identity<T> = T;
121type Flatten<T> = Identity<{ [K in keyof T]: T[K] }>;
122
123function GenericMutationButton<
124 Props,
125 Args extends unknown[],
126 Result,
127>(
128 Component: FC<Props>,
129 props: MutationButtonProps<Args, Result> & Props,
130) {
131 const { mutation, args, onClick, ...forwarded } = props;
132 forwarded satisfies Omit<Props, keyof MutationButtonProps<Args, Result>>;
133
134 const state = useMutation(mutation);
135
136 return (
137 <Component
138 {...forwarded}
139 />
140 ) // onClick={useCallback((e) => {
141 ; // onClick?.(e);
142 // if (e.defaultPrevented) return;
143 // const computedArgs = typeof args === "function" ? args(e) : args;
144 // if (e.defaultPrevented) return;
145 // state.run(computedArgs);
146 // }, [state])}
147 // isPending={state.isPending}
148}
149
150function BaseButton(
151 props: { a: boolean; isPending: boolean; onClick?: (e: MouseEvent) => void },
152) {
153 /** Preventing default will interrupt the mutation */
154 return <button type="button" />;
155}
156const DefaultMutationButton = createMutationButton(BaseButton);
157
158const def = defineMutation({
159 async mutate(meow: string) {
160 return true;
161 },
162 describe: "meow",
163 optimistic(context) {
164 },
165 async refetch(context) {
166 },
167});
168
169const y = (
170 <DefaultMutationButton
171 mutation={def}
172 args={["meow"]}
173 a={false}
174 />
175);
src/register.ts created+9
...@@ -0,0 +1,9 @@
1export interface MutationClientConfig {
2 context: {};
3 optimisticHelpers: {};
4}
5
6export interface GlobalMutationConfig {
7 context: {};
8 optimisticHelpers: {};
9}
src/types.ts created+20
...@@ -0,0 +1,20 @@
1export interface Mutation<Args extends unknown[], Result> {
2 /** Calling the mutation. Errors are turned into UI toasts. */
3 run(...args: Args): void;
4
5 /** Calls the mutation, treating the errors as promise rejection. */
6 runAndReturn(...args: Args): Promise<Result>;
7
8 subscribe(
9 args: Args,
10 cb: (update: MutationEvent<Result>) => void,
11 ): () => void;
12
13 describe(...args: Args): string;
14}
15
16export interface MutationEvent<Result> {
17 status: "waiting" | "mutating" | "refetching";
18 result: Result | null;
19 error: unknown;
20}
test/example.test.tsx created+96
...@@ -0,0 +1,96 @@
1import { QueryClient, type QueryOptions } from "@tanstack/react-query";
2import { MutationClient } from "../src/client.ts";
3import {
4 type ButtonHTMLAttributes,
5 type DetailedHTMLProps,
6 useState,
7} from "react";
8import { createMutationButton, useMutation } from "../src/react.tsx";
9
10const client = new QueryClient();
11const mutationClient = new MutationClient({
12 context: {
13 client,
14 },
15 getOptimisticHelpers(onRestore) {
16 return {
17 setProp<T extends object, K extends keyof T>(
18 queryKey: QueryOptions<T>,
19 key: K,
20 value: T[K],
21 ) {
22 const prev = client.getQueryData<T>(queryKey.queryKey!);
23 if (!prev) return;
24 client.setQueryData<T>(
25 queryKey.queryKey!,
26 (obj) => obj ? ({ ...obj, [key]: value }) : obj,
27 );
28 onRestore(() =>
29 client.setQueryData<T>(
30 queryKey.queryKey!,
31 (obj) => obj ? ({ ...obj, [key]: prev[key] }) : obj,
32 )
33 );
34 },
35 };
36 },
37});
38
39function queryChat(id: string): QueryOptions<{ title: string }> {
40 throw new Error();
41}
42
43const MutationButton = createMutationButton((
44 props:
45 & DetailedHTMLProps<
46 ButtonHTMLAttributes<HTMLButtonElement>,
47 HTMLButtonElement
48 >
49 & { isPending: true },
50) => <button {...props} disabled={props.disabled || props.isPending} />);
51
52const mutSetChatName = mutationClient.defineMutation({
53 async mutate(chatId: string, newName: string) {
54 const res = await fetch(`/api/chats/${chatId}`, {
55 method: "PATCH",
56 body: JSON.stringify({ name: newName }),
57 });
58 if (!res.ok) throw new Error(`HTTP ${res.status}`);
59 },
60
61 describe: "Rename Chat",
62
63 optimistic({ args: [chatId, newName], helpers }) {
64 helpers.setProp(queryChat(chatId), "title", newName);
65 },
66
67 async refetch({ client, args: [chatId] }) {
68 await client.invalidateQueries(queryChat(chatId));
69 },
70});
71
72function Form({ chatId }: { chatId: string }) {
73 const mutation = useMutation(mutSetChatName);
74 const [newName, setNewName] = useState("");
75 return (
76 <>
77 <input
78 onChange={(e) => setNewName(e.target.value)}
79 disabled={mutation.isPending}
80 />
81 {/* button that contains a loading state */}
82 <MutationButton
83 mutation={mutation}
84 args={() => [chatId, newName]}
85 >
86 Save
87 </MutationButton>
88 {/* trigger programatically */}
89 <button
90 onClick={() => {
91 mutation.run(chatId, newName);
92 }}
93 />
94 </>
95 );
96}
tsconfig.json created+23
...@@ -0,0 +1,23 @@
1{
2 "compilerOptions": {
3 "target": "ESNext",
4 "lib": ["DOM", "ESNext"],
5 "module": "NodeNext",
6 "moduleResolution": "NodeNext",
7 "skipLibCheck": true,
8 "verbatimModuleSyntax": true,
9 "resolveJsonModule": true,
10 "allowImportingTsExtensions": true,
11 "noEmit": true,
12 "jsx": "react-jsx",
13
14 // strictness
15 "strict": true,
16 "noUnusedLocals": true,
17 "noUnusedParameters": true,
18 "noFallthroughCasesInSwitch": true,
19 "noUncheckedIndexedAccess": true,
20 "allowUnreachableCode": false
21 },
22 "include": ["src", "test"]
23}
vitest.config.ts created+16
...@@ -0,0 +1,16 @@
1import { defineConfig } from "vitest/config";
2
3export default defineConfig({
4 test: {
5 coverage: {
6 enabled: true,
7 provider: "v8",
8 reporter: [
9 process.argv.includes("--ui") ? "html" : "html-spa",
10 "json-summary",
11 "json",
12 ],
13 reportsDirectory: "./coverage",
14 },
15 },
16});