├── .gitignore
├── Cargo.lock
├── Cargo.toml
├── example.mp4
├── front
├── .gitignore
├── index.html
├── package.json
├── src
│ ├── App.scss
│ ├── App.tsx
│ ├── Todo.ts
│ ├── Websocket.ts
│ ├── favicon.svg
│ ├── index.css
│ ├── main.tsx
│ └── vite-env.d.ts
├── tsconfig.json
├── vite.config.ts
└── yarn.lock
└── src
├── main.rs
├── todo.rs
└── websocket.rs
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 |
--------------------------------------------------------------------------------
/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "async-trait"
7 | version = "0.1.63"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "eff18d764974428cf3a9328e23fc5c986f5fbed46e6cd4cdf42544df5d297ec1"
10 | dependencies = [
11 | "proc-macro2",
12 | "quote",
13 | "syn",
14 | ]
15 |
16 | [[package]]
17 | name = "autocfg"
18 | version = "1.1.0"
19 | source = "registry+https://github.com/rust-lang/crates.io-index"
20 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
21 |
22 | [[package]]
23 | name = "axum"
24 | version = "0.6.4"
25 | source = "registry+https://github.com/rust-lang/crates.io-index"
26 | checksum = "e5694b64066a2459918d8074c2ce0d5a88f409431994c2356617c8ae0c4721fc"
27 | dependencies = [
28 | "async-trait",
29 | "axum-core",
30 | "base64 0.20.0",
31 | "bitflags",
32 | "bytes",
33 | "futures-util",
34 | "http",
35 | "http-body",
36 | "hyper",
37 | "itoa",
38 | "matchit",
39 | "memchr",
40 | "mime",
41 | "percent-encoding",
42 | "pin-project-lite",
43 | "rustversion",
44 | "serde",
45 | "serde_json",
46 | "serde_path_to_error",
47 | "serde_urlencoded",
48 | "sha1",
49 | "sync_wrapper",
50 | "tokio",
51 | "tokio-tungstenite",
52 | "tower",
53 | "tower-http",
54 | "tower-layer",
55 | "tower-service",
56 | ]
57 |
58 | [[package]]
59 | name = "axum-core"
60 | version = "0.3.2"
61 | source = "registry+https://github.com/rust-lang/crates.io-index"
62 | checksum = "1cae3e661676ffbacb30f1a824089a8c9150e71017f7e1e38f2aa32009188d34"
63 | dependencies = [
64 | "async-trait",
65 | "bytes",
66 | "futures-util",
67 | "http",
68 | "http-body",
69 | "mime",
70 | "rustversion",
71 | "tower-layer",
72 | "tower-service",
73 | ]
74 |
75 | [[package]]
76 | name = "base64"
77 | version = "0.13.1"
78 | source = "registry+https://github.com/rust-lang/crates.io-index"
79 | checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
80 |
81 | [[package]]
82 | name = "base64"
83 | version = "0.20.0"
84 | source = "registry+https://github.com/rust-lang/crates.io-index"
85 | checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5"
86 |
87 | [[package]]
88 | name = "bitflags"
89 | version = "1.3.2"
90 | source = "registry+https://github.com/rust-lang/crates.io-index"
91 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
92 |
93 | [[package]]
94 | name = "block-buffer"
95 | version = "0.10.3"
96 | source = "registry+https://github.com/rust-lang/crates.io-index"
97 | checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
98 | dependencies = [
99 | "generic-array",
100 | ]
101 |
102 | [[package]]
103 | name = "byteorder"
104 | version = "1.4.3"
105 | source = "registry+https://github.com/rust-lang/crates.io-index"
106 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
107 |
108 | [[package]]
109 | name = "bytes"
110 | version = "1.3.0"
111 | source = "registry+https://github.com/rust-lang/crates.io-index"
112 | checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
113 |
114 | [[package]]
115 | name = "cfg-if"
116 | version = "1.0.0"
117 | source = "registry+https://github.com/rust-lang/crates.io-index"
118 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
119 |
120 | [[package]]
121 | name = "cpufeatures"
122 | version = "0.2.5"
123 | source = "registry+https://github.com/rust-lang/crates.io-index"
124 | checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
125 | dependencies = [
126 | "libc",
127 | ]
128 |
129 | [[package]]
130 | name = "crypto-common"
131 | version = "0.1.6"
132 | source = "registry+https://github.com/rust-lang/crates.io-index"
133 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
134 | dependencies = [
135 | "generic-array",
136 | "typenum",
137 | ]
138 |
139 | [[package]]
140 | name = "digest"
141 | version = "0.10.6"
142 | source = "registry+https://github.com/rust-lang/crates.io-index"
143 | checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
144 | dependencies = [
145 | "block-buffer",
146 | "crypto-common",
147 | ]
148 |
149 | [[package]]
150 | name = "fnv"
151 | version = "1.0.7"
152 | source = "registry+https://github.com/rust-lang/crates.io-index"
153 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
154 |
155 | [[package]]
156 | name = "form_urlencoded"
157 | version = "1.1.0"
158 | source = "registry+https://github.com/rust-lang/crates.io-index"
159 | checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
160 | dependencies = [
161 | "percent-encoding",
162 | ]
163 |
164 | [[package]]
165 | name = "futures"
166 | version = "0.3.25"
167 | source = "registry+https://github.com/rust-lang/crates.io-index"
168 | checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
169 | dependencies = [
170 | "futures-channel",
171 | "futures-core",
172 | "futures-executor",
173 | "futures-io",
174 | "futures-sink",
175 | "futures-task",
176 | "futures-util",
177 | ]
178 |
179 | [[package]]
180 | name = "futures-channel"
181 | version = "0.3.25"
182 | source = "registry+https://github.com/rust-lang/crates.io-index"
183 | checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
184 | dependencies = [
185 | "futures-core",
186 | "futures-sink",
187 | ]
188 |
189 | [[package]]
190 | name = "futures-core"
191 | version = "0.3.25"
192 | source = "registry+https://github.com/rust-lang/crates.io-index"
193 | checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
194 |
195 | [[package]]
196 | name = "futures-executor"
197 | version = "0.3.25"
198 | source = "registry+https://github.com/rust-lang/crates.io-index"
199 | checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
200 | dependencies = [
201 | "futures-core",
202 | "futures-task",
203 | "futures-util",
204 | ]
205 |
206 | [[package]]
207 | name = "futures-io"
208 | version = "0.3.25"
209 | source = "registry+https://github.com/rust-lang/crates.io-index"
210 | checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
211 |
212 | [[package]]
213 | name = "futures-macro"
214 | version = "0.3.25"
215 | source = "registry+https://github.com/rust-lang/crates.io-index"
216 | checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
217 | dependencies = [
218 | "proc-macro2",
219 | "quote",
220 | "syn",
221 | ]
222 |
223 | [[package]]
224 | name = "futures-sink"
225 | version = "0.3.25"
226 | source = "registry+https://github.com/rust-lang/crates.io-index"
227 | checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
228 |
229 | [[package]]
230 | name = "futures-task"
231 | version = "0.3.25"
232 | source = "registry+https://github.com/rust-lang/crates.io-index"
233 | checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
234 |
235 | [[package]]
236 | name = "futures-util"
237 | version = "0.3.25"
238 | source = "registry+https://github.com/rust-lang/crates.io-index"
239 | checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
240 | dependencies = [
241 | "futures-channel",
242 | "futures-core",
243 | "futures-io",
244 | "futures-macro",
245 | "futures-sink",
246 | "futures-task",
247 | "memchr",
248 | "pin-project-lite",
249 | "pin-utils",
250 | "slab",
251 | ]
252 |
253 | [[package]]
254 | name = "generic-array"
255 | version = "0.14.6"
256 | source = "registry+https://github.com/rust-lang/crates.io-index"
257 | checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
258 | dependencies = [
259 | "typenum",
260 | "version_check",
261 | ]
262 |
263 | [[package]]
264 | name = "getrandom"
265 | version = "0.2.8"
266 | source = "registry+https://github.com/rust-lang/crates.io-index"
267 | checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
268 | dependencies = [
269 | "cfg-if",
270 | "libc",
271 | "wasi",
272 | ]
273 |
274 | [[package]]
275 | name = "hermit-abi"
276 | version = "0.2.6"
277 | source = "registry+https://github.com/rust-lang/crates.io-index"
278 | checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
279 | dependencies = [
280 | "libc",
281 | ]
282 |
283 | [[package]]
284 | name = "http"
285 | version = "0.2.8"
286 | source = "registry+https://github.com/rust-lang/crates.io-index"
287 | checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
288 | dependencies = [
289 | "bytes",
290 | "fnv",
291 | "itoa",
292 | ]
293 |
294 | [[package]]
295 | name = "http-body"
296 | version = "0.4.5"
297 | source = "registry+https://github.com/rust-lang/crates.io-index"
298 | checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
299 | dependencies = [
300 | "bytes",
301 | "http",
302 | "pin-project-lite",
303 | ]
304 |
305 | [[package]]
306 | name = "http-range-header"
307 | version = "0.3.0"
308 | source = "registry+https://github.com/rust-lang/crates.io-index"
309 | checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29"
310 |
311 | [[package]]
312 | name = "httparse"
313 | version = "1.8.0"
314 | source = "registry+https://github.com/rust-lang/crates.io-index"
315 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
316 |
317 | [[package]]
318 | name = "httpdate"
319 | version = "1.0.2"
320 | source = "registry+https://github.com/rust-lang/crates.io-index"
321 | checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
322 |
323 | [[package]]
324 | name = "hyper"
325 | version = "0.14.23"
326 | source = "registry+https://github.com/rust-lang/crates.io-index"
327 | checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c"
328 | dependencies = [
329 | "bytes",
330 | "futures-channel",
331 | "futures-core",
332 | "futures-util",
333 | "http",
334 | "http-body",
335 | "httparse",
336 | "httpdate",
337 | "itoa",
338 | "pin-project-lite",
339 | "socket2",
340 | "tokio",
341 | "tower-service",
342 | "tracing",
343 | "want",
344 | ]
345 |
346 | [[package]]
347 | name = "idna"
348 | version = "0.3.0"
349 | source = "registry+https://github.com/rust-lang/crates.io-index"
350 | checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
351 | dependencies = [
352 | "unicode-bidi",
353 | "unicode-normalization",
354 | ]
355 |
356 | [[package]]
357 | name = "itoa"
358 | version = "1.0.5"
359 | source = "registry+https://github.com/rust-lang/crates.io-index"
360 | checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
361 |
362 | [[package]]
363 | name = "json-patch"
364 | version = "0.3.0"
365 | source = "registry+https://github.com/rust-lang/crates.io-index"
366 | checksum = "e712e62827c382a77b87f590532febb1f8b2fdbc3eefa1ee37fe7281687075ef"
367 | dependencies = [
368 | "serde",
369 | "serde_json",
370 | "thiserror",
371 | "treediff",
372 | ]
373 |
374 | [[package]]
375 | name = "lazy_static"
376 | version = "1.4.0"
377 | source = "registry+https://github.com/rust-lang/crates.io-index"
378 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
379 |
380 | [[package]]
381 | name = "libc"
382 | version = "0.2.139"
383 | source = "registry+https://github.com/rust-lang/crates.io-index"
384 | checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
385 |
386 | [[package]]
387 | name = "lock_api"
388 | version = "0.4.9"
389 | source = "registry+https://github.com/rust-lang/crates.io-index"
390 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
391 | dependencies = [
392 | "autocfg",
393 | "scopeguard",
394 | ]
395 |
396 | [[package]]
397 | name = "log"
398 | version = "0.4.17"
399 | source = "registry+https://github.com/rust-lang/crates.io-index"
400 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
401 | dependencies = [
402 | "cfg-if",
403 | ]
404 |
405 | [[package]]
406 | name = "matchit"
407 | version = "0.7.0"
408 | source = "registry+https://github.com/rust-lang/crates.io-index"
409 | checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40"
410 |
411 | [[package]]
412 | name = "memchr"
413 | version = "2.5.0"
414 | source = "registry+https://github.com/rust-lang/crates.io-index"
415 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
416 |
417 | [[package]]
418 | name = "mime"
419 | version = "0.3.16"
420 | source = "registry+https://github.com/rust-lang/crates.io-index"
421 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
422 |
423 | [[package]]
424 | name = "mio"
425 | version = "0.8.5"
426 | source = "registry+https://github.com/rust-lang/crates.io-index"
427 | checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
428 | dependencies = [
429 | "libc",
430 | "log",
431 | "wasi",
432 | "windows-sys",
433 | ]
434 |
435 | [[package]]
436 | name = "nu-ansi-term"
437 | version = "0.46.0"
438 | source = "registry+https://github.com/rust-lang/crates.io-index"
439 | checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
440 | dependencies = [
441 | "overload",
442 | "winapi",
443 | ]
444 |
445 | [[package]]
446 | name = "num_cpus"
447 | version = "1.15.0"
448 | source = "registry+https://github.com/rust-lang/crates.io-index"
449 | checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
450 | dependencies = [
451 | "hermit-abi",
452 | "libc",
453 | ]
454 |
455 | [[package]]
456 | name = "once_cell"
457 | version = "1.17.0"
458 | source = "registry+https://github.com/rust-lang/crates.io-index"
459 | checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
460 |
461 | [[package]]
462 | name = "overload"
463 | version = "0.1.1"
464 | source = "registry+https://github.com/rust-lang/crates.io-index"
465 | checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
466 |
467 | [[package]]
468 | name = "parking_lot"
469 | version = "0.12.1"
470 | source = "registry+https://github.com/rust-lang/crates.io-index"
471 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
472 | dependencies = [
473 | "lock_api",
474 | "parking_lot_core",
475 | ]
476 |
477 | [[package]]
478 | name = "parking_lot_core"
479 | version = "0.9.6"
480 | source = "registry+https://github.com/rust-lang/crates.io-index"
481 | checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf"
482 | dependencies = [
483 | "cfg-if",
484 | "libc",
485 | "redox_syscall",
486 | "smallvec",
487 | "windows-sys",
488 | ]
489 |
490 | [[package]]
491 | name = "percent-encoding"
492 | version = "2.2.0"
493 | source = "registry+https://github.com/rust-lang/crates.io-index"
494 | checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
495 |
496 | [[package]]
497 | name = "pin-project"
498 | version = "1.0.12"
499 | source = "registry+https://github.com/rust-lang/crates.io-index"
500 | checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc"
501 | dependencies = [
502 | "pin-project-internal",
503 | ]
504 |
505 | [[package]]
506 | name = "pin-project-internal"
507 | version = "1.0.12"
508 | source = "registry+https://github.com/rust-lang/crates.io-index"
509 | checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55"
510 | dependencies = [
511 | "proc-macro2",
512 | "quote",
513 | "syn",
514 | ]
515 |
516 | [[package]]
517 | name = "pin-project-lite"
518 | version = "0.2.9"
519 | source = "registry+https://github.com/rust-lang/crates.io-index"
520 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
521 |
522 | [[package]]
523 | name = "pin-utils"
524 | version = "0.1.0"
525 | source = "registry+https://github.com/rust-lang/crates.io-index"
526 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
527 |
528 | [[package]]
529 | name = "ppv-lite86"
530 | version = "0.2.17"
531 | source = "registry+https://github.com/rust-lang/crates.io-index"
532 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
533 |
534 | [[package]]
535 | name = "proc-macro2"
536 | version = "1.0.50"
537 | source = "registry+https://github.com/rust-lang/crates.io-index"
538 | checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
539 | dependencies = [
540 | "unicode-ident",
541 | ]
542 |
543 | [[package]]
544 | name = "quote"
545 | version = "1.0.23"
546 | source = "registry+https://github.com/rust-lang/crates.io-index"
547 | checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
548 | dependencies = [
549 | "proc-macro2",
550 | ]
551 |
552 | [[package]]
553 | name = "rand"
554 | version = "0.8.5"
555 | source = "registry+https://github.com/rust-lang/crates.io-index"
556 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
557 | dependencies = [
558 | "libc",
559 | "rand_chacha",
560 | "rand_core",
561 | ]
562 |
563 | [[package]]
564 | name = "rand_chacha"
565 | version = "0.3.1"
566 | source = "registry+https://github.com/rust-lang/crates.io-index"
567 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
568 | dependencies = [
569 | "ppv-lite86",
570 | "rand_core",
571 | ]
572 |
573 | [[package]]
574 | name = "rand_core"
575 | version = "0.6.4"
576 | source = "registry+https://github.com/rust-lang/crates.io-index"
577 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
578 | dependencies = [
579 | "getrandom",
580 | ]
581 |
582 | [[package]]
583 | name = "redox_syscall"
584 | version = "0.2.16"
585 | source = "registry+https://github.com/rust-lang/crates.io-index"
586 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
587 | dependencies = [
588 | "bitflags",
589 | ]
590 |
591 | [[package]]
592 | name = "rustversion"
593 | version = "1.0.11"
594 | source = "registry+https://github.com/rust-lang/crates.io-index"
595 | checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70"
596 |
597 | [[package]]
598 | name = "ryu"
599 | version = "1.0.12"
600 | source = "registry+https://github.com/rust-lang/crates.io-index"
601 | checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
602 |
603 | [[package]]
604 | name = "scopeguard"
605 | version = "1.1.0"
606 | source = "registry+https://github.com/rust-lang/crates.io-index"
607 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
608 |
609 | [[package]]
610 | name = "serde"
611 | version = "1.0.152"
612 | source = "registry+https://github.com/rust-lang/crates.io-index"
613 | checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
614 | dependencies = [
615 | "serde_derive",
616 | ]
617 |
618 | [[package]]
619 | name = "serde_derive"
620 | version = "1.0.152"
621 | source = "registry+https://github.com/rust-lang/crates.io-index"
622 | checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
623 | dependencies = [
624 | "proc-macro2",
625 | "quote",
626 | "syn",
627 | ]
628 |
629 | [[package]]
630 | name = "serde_json"
631 | version = "1.0.91"
632 | source = "registry+https://github.com/rust-lang/crates.io-index"
633 | checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883"
634 | dependencies = [
635 | "itoa",
636 | "ryu",
637 | "serde",
638 | ]
639 |
640 | [[package]]
641 | name = "serde_path_to_error"
642 | version = "0.1.9"
643 | source = "registry+https://github.com/rust-lang/crates.io-index"
644 | checksum = "26b04f22b563c91331a10074bda3dd5492e3cc39d56bd557e91c0af42b6c7341"
645 | dependencies = [
646 | "serde",
647 | ]
648 |
649 | [[package]]
650 | name = "serde_urlencoded"
651 | version = "0.7.1"
652 | source = "registry+https://github.com/rust-lang/crates.io-index"
653 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
654 | dependencies = [
655 | "form_urlencoded",
656 | "itoa",
657 | "ryu",
658 | "serde",
659 | ]
660 |
661 | [[package]]
662 | name = "sha1"
663 | version = "0.10.5"
664 | source = "registry+https://github.com/rust-lang/crates.io-index"
665 | checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
666 | dependencies = [
667 | "cfg-if",
668 | "cpufeatures",
669 | "digest",
670 | ]
671 |
672 | [[package]]
673 | name = "sharded-slab"
674 | version = "0.1.4"
675 | source = "registry+https://github.com/rust-lang/crates.io-index"
676 | checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
677 | dependencies = [
678 | "lazy_static",
679 | ]
680 |
681 | [[package]]
682 | name = "signal-hook-registry"
683 | version = "1.4.0"
684 | source = "registry+https://github.com/rust-lang/crates.io-index"
685 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
686 | dependencies = [
687 | "libc",
688 | ]
689 |
690 | [[package]]
691 | name = "slab"
692 | version = "0.4.7"
693 | source = "registry+https://github.com/rust-lang/crates.io-index"
694 | checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
695 | dependencies = [
696 | "autocfg",
697 | ]
698 |
699 | [[package]]
700 | name = "smallvec"
701 | version = "1.10.0"
702 | source = "registry+https://github.com/rust-lang/crates.io-index"
703 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
704 |
705 | [[package]]
706 | name = "socket2"
707 | version = "0.4.7"
708 | source = "registry+https://github.com/rust-lang/crates.io-index"
709 | checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
710 | dependencies = [
711 | "libc",
712 | "winapi",
713 | ]
714 |
715 | [[package]]
716 | name = "syn"
717 | version = "1.0.107"
718 | source = "registry+https://github.com/rust-lang/crates.io-index"
719 | checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
720 | dependencies = [
721 | "proc-macro2",
722 | "quote",
723 | "unicode-ident",
724 | ]
725 |
726 | [[package]]
727 | name = "sync_wrapper"
728 | version = "0.1.1"
729 | source = "registry+https://github.com/rust-lang/crates.io-index"
730 | checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8"
731 |
732 | [[package]]
733 | name = "thiserror"
734 | version = "1.0.38"
735 | source = "registry+https://github.com/rust-lang/crates.io-index"
736 | checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
737 | dependencies = [
738 | "thiserror-impl",
739 | ]
740 |
741 | [[package]]
742 | name = "thiserror-impl"
743 | version = "1.0.38"
744 | source = "registry+https://github.com/rust-lang/crates.io-index"
745 | checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
746 | dependencies = [
747 | "proc-macro2",
748 | "quote",
749 | "syn",
750 | ]
751 |
752 | [[package]]
753 | name = "thread_local"
754 | version = "1.1.4"
755 | source = "registry+https://github.com/rust-lang/crates.io-index"
756 | checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
757 | dependencies = [
758 | "once_cell",
759 | ]
760 |
761 | [[package]]
762 | name = "tinyvec"
763 | version = "1.6.0"
764 | source = "registry+https://github.com/rust-lang/crates.io-index"
765 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
766 | dependencies = [
767 | "tinyvec_macros",
768 | ]
769 |
770 | [[package]]
771 | name = "tinyvec_macros"
772 | version = "0.1.0"
773 | source = "registry+https://github.com/rust-lang/crates.io-index"
774 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
775 |
776 | [[package]]
777 | name = "tokio"
778 | version = "1.24.2"
779 | source = "registry+https://github.com/rust-lang/crates.io-index"
780 | checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb"
781 | dependencies = [
782 | "autocfg",
783 | "bytes",
784 | "libc",
785 | "memchr",
786 | "mio",
787 | "num_cpus",
788 | "parking_lot",
789 | "pin-project-lite",
790 | "signal-hook-registry",
791 | "socket2",
792 | "tokio-macros",
793 | "windows-sys",
794 | ]
795 |
796 | [[package]]
797 | name = "tokio-macros"
798 | version = "1.8.2"
799 | source = "registry+https://github.com/rust-lang/crates.io-index"
800 | checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
801 | dependencies = [
802 | "proc-macro2",
803 | "quote",
804 | "syn",
805 | ]
806 |
807 | [[package]]
808 | name = "tokio-tungstenite"
809 | version = "0.18.0"
810 | source = "registry+https://github.com/rust-lang/crates.io-index"
811 | checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd"
812 | dependencies = [
813 | "futures-util",
814 | "log",
815 | "tokio",
816 | "tungstenite",
817 | ]
818 |
819 | [[package]]
820 | name = "tower"
821 | version = "0.4.13"
822 | source = "registry+https://github.com/rust-lang/crates.io-index"
823 | checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
824 | dependencies = [
825 | "futures-core",
826 | "futures-util",
827 | "pin-project",
828 | "pin-project-lite",
829 | "tokio",
830 | "tower-layer",
831 | "tower-service",
832 | "tracing",
833 | ]
834 |
835 | [[package]]
836 | name = "tower-http"
837 | version = "0.3.5"
838 | source = "registry+https://github.com/rust-lang/crates.io-index"
839 | checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858"
840 | dependencies = [
841 | "bitflags",
842 | "bytes",
843 | "futures-core",
844 | "futures-util",
845 | "http",
846 | "http-body",
847 | "http-range-header",
848 | "pin-project-lite",
849 | "tower",
850 | "tower-layer",
851 | "tower-service",
852 | ]
853 |
854 | [[package]]
855 | name = "tower-layer"
856 | version = "0.3.2"
857 | source = "registry+https://github.com/rust-lang/crates.io-index"
858 | checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
859 |
860 | [[package]]
861 | name = "tower-service"
862 | version = "0.3.2"
863 | source = "registry+https://github.com/rust-lang/crates.io-index"
864 | checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
865 |
866 | [[package]]
867 | name = "tracing"
868 | version = "0.1.37"
869 | source = "registry+https://github.com/rust-lang/crates.io-index"
870 | checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
871 | dependencies = [
872 | "cfg-if",
873 | "log",
874 | "pin-project-lite",
875 | "tracing-attributes",
876 | "tracing-core",
877 | ]
878 |
879 | [[package]]
880 | name = "tracing-attributes"
881 | version = "0.1.23"
882 | source = "registry+https://github.com/rust-lang/crates.io-index"
883 | checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
884 | dependencies = [
885 | "proc-macro2",
886 | "quote",
887 | "syn",
888 | ]
889 |
890 | [[package]]
891 | name = "tracing-core"
892 | version = "0.1.30"
893 | source = "registry+https://github.com/rust-lang/crates.io-index"
894 | checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
895 | dependencies = [
896 | "once_cell",
897 | "valuable",
898 | ]
899 |
900 | [[package]]
901 | name = "tracing-log"
902 | version = "0.1.3"
903 | source = "registry+https://github.com/rust-lang/crates.io-index"
904 | checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
905 | dependencies = [
906 | "lazy_static",
907 | "log",
908 | "tracing-core",
909 | ]
910 |
911 | [[package]]
912 | name = "tracing-subscriber"
913 | version = "0.3.16"
914 | source = "registry+https://github.com/rust-lang/crates.io-index"
915 | checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70"
916 | dependencies = [
917 | "nu-ansi-term",
918 | "sharded-slab",
919 | "smallvec",
920 | "thread_local",
921 | "tracing-core",
922 | "tracing-log",
923 | ]
924 |
925 | [[package]]
926 | name = "treediff"
927 | version = "4.0.2"
928 | source = "registry+https://github.com/rust-lang/crates.io-index"
929 | checksum = "52984d277bdf2a751072b5df30ec0377febdb02f7696d64c2d7d54630bac4303"
930 | dependencies = [
931 | "serde_json",
932 | ]
933 |
934 | [[package]]
935 | name = "try-lock"
936 | version = "0.2.4"
937 | source = "registry+https://github.com/rust-lang/crates.io-index"
938 | checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
939 |
940 | [[package]]
941 | name = "tungstenite"
942 | version = "0.18.0"
943 | source = "registry+https://github.com/rust-lang/crates.io-index"
944 | checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788"
945 | dependencies = [
946 | "base64 0.13.1",
947 | "byteorder",
948 | "bytes",
949 | "http",
950 | "httparse",
951 | "log",
952 | "rand",
953 | "sha1",
954 | "thiserror",
955 | "url",
956 | "utf-8",
957 | ]
958 |
959 | [[package]]
960 | name = "typenum"
961 | version = "1.16.0"
962 | source = "registry+https://github.com/rust-lang/crates.io-index"
963 | checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
964 |
965 | [[package]]
966 | name = "unicode-bidi"
967 | version = "0.3.10"
968 | source = "registry+https://github.com/rust-lang/crates.io-index"
969 | checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
970 |
971 | [[package]]
972 | name = "unicode-ident"
973 | version = "1.0.6"
974 | source = "registry+https://github.com/rust-lang/crates.io-index"
975 | checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
976 |
977 | [[package]]
978 | name = "unicode-normalization"
979 | version = "0.1.22"
980 | source = "registry+https://github.com/rust-lang/crates.io-index"
981 | checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
982 | dependencies = [
983 | "tinyvec",
984 | ]
985 |
986 | [[package]]
987 | name = "url"
988 | version = "2.3.1"
989 | source = "registry+https://github.com/rust-lang/crates.io-index"
990 | checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
991 | dependencies = [
992 | "form_urlencoded",
993 | "idna",
994 | "percent-encoding",
995 | ]
996 |
997 | [[package]]
998 | name = "utf-8"
999 | version = "0.7.6"
1000 | source = "registry+https://github.com/rust-lang/crates.io-index"
1001 | checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
1002 |
1003 | [[package]]
1004 | name = "valuable"
1005 | version = "0.1.0"
1006 | source = "registry+https://github.com/rust-lang/crates.io-index"
1007 | checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
1008 |
1009 | [[package]]
1010 | name = "version_check"
1011 | version = "0.9.4"
1012 | source = "registry+https://github.com/rust-lang/crates.io-index"
1013 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
1014 |
1015 | [[package]]
1016 | name = "want"
1017 | version = "0.3.0"
1018 | source = "registry+https://github.com/rust-lang/crates.io-index"
1019 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
1020 | dependencies = [
1021 | "log",
1022 | "try-lock",
1023 | ]
1024 |
1025 | [[package]]
1026 | name = "wasi"
1027 | version = "0.11.0+wasi-snapshot-preview1"
1028 | source = "registry+https://github.com/rust-lang/crates.io-index"
1029 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
1030 |
1031 | [[package]]
1032 | name = "websocket_jsonpatch"
1033 | version = "0.1.0"
1034 | dependencies = [
1035 | "axum",
1036 | "futures",
1037 | "json-patch",
1038 | "serde",
1039 | "serde_json",
1040 | "tokio",
1041 | "tracing",
1042 | "tracing-subscriber",
1043 | ]
1044 |
1045 | [[package]]
1046 | name = "winapi"
1047 | version = "0.3.9"
1048 | source = "registry+https://github.com/rust-lang/crates.io-index"
1049 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
1050 | dependencies = [
1051 | "winapi-i686-pc-windows-gnu",
1052 | "winapi-x86_64-pc-windows-gnu",
1053 | ]
1054 |
1055 | [[package]]
1056 | name = "winapi-i686-pc-windows-gnu"
1057 | version = "0.4.0"
1058 | source = "registry+https://github.com/rust-lang/crates.io-index"
1059 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
1060 |
1061 | [[package]]
1062 | name = "winapi-x86_64-pc-windows-gnu"
1063 | version = "0.4.0"
1064 | source = "registry+https://github.com/rust-lang/crates.io-index"
1065 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
1066 |
1067 | [[package]]
1068 | name = "windows-sys"
1069 | version = "0.42.0"
1070 | source = "registry+https://github.com/rust-lang/crates.io-index"
1071 | checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
1072 | dependencies = [
1073 | "windows_aarch64_gnullvm",
1074 | "windows_aarch64_msvc",
1075 | "windows_i686_gnu",
1076 | "windows_i686_msvc",
1077 | "windows_x86_64_gnu",
1078 | "windows_x86_64_gnullvm",
1079 | "windows_x86_64_msvc",
1080 | ]
1081 |
1082 | [[package]]
1083 | name = "windows_aarch64_gnullvm"
1084 | version = "0.42.1"
1085 | source = "registry+https://github.com/rust-lang/crates.io-index"
1086 | checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
1087 |
1088 | [[package]]
1089 | name = "windows_aarch64_msvc"
1090 | version = "0.42.1"
1091 | source = "registry+https://github.com/rust-lang/crates.io-index"
1092 | checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
1093 |
1094 | [[package]]
1095 | name = "windows_i686_gnu"
1096 | version = "0.42.1"
1097 | source = "registry+https://github.com/rust-lang/crates.io-index"
1098 | checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
1099 |
1100 | [[package]]
1101 | name = "windows_i686_msvc"
1102 | version = "0.42.1"
1103 | source = "registry+https://github.com/rust-lang/crates.io-index"
1104 | checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
1105 |
1106 | [[package]]
1107 | name = "windows_x86_64_gnu"
1108 | version = "0.42.1"
1109 | source = "registry+https://github.com/rust-lang/crates.io-index"
1110 | checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
1111 |
1112 | [[package]]
1113 | name = "windows_x86_64_gnullvm"
1114 | version = "0.42.1"
1115 | source = "registry+https://github.com/rust-lang/crates.io-index"
1116 | checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
1117 |
1118 | [[package]]
1119 | name = "windows_x86_64_msvc"
1120 | version = "0.42.1"
1121 | source = "registry+https://github.com/rust-lang/crates.io-index"
1122 | checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
1123 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "websocket_jsonpatch"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [dependencies]
7 | axum = { version = "0.6.4", features = ["ws"]}
8 | tokio = { version = "1", features = ["full"] }
9 |
10 | tracing = "0.1.37"
11 | tracing-subscriber = "0.3.16"
12 |
13 | serde = { version = "1.0", features = ["derive"] }
14 | serde_json = "1.0"
15 | futures = "0.3"
16 | json-patch = "0.3.0"
17 |
--------------------------------------------------------------------------------
/example.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cetra3/websocket_jsonpatch/3153c0bc0533c681d4492f57ac811b076249600b/example.mp4
--------------------------------------------------------------------------------
/front/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | dist-ssr
5 | *.local
6 |
--------------------------------------------------------------------------------
/front/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/front/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "websocket_jsonpatch",
3 | "version": "0.1.0",
4 | "license": "GPL-3.0-or-later",
5 | "scripts": {
6 | "dev": "vite",
7 | "build": "tsc && vite build",
8 | "serve": "vite preview",
9 | "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md,css}\" "
10 | },
11 | "dependencies": {
12 | "fast-json-patch": "^3.1.0",
13 | "react": "^17.0.0",
14 | "react-dom": "^17.0.0",
15 | "spectre.css": "^0.5.9"
16 | },
17 | "devDependencies": {
18 | "@types/react": "^17.0.0",
19 | "@types/react-dom": "^17.0.0",
20 | "@vitejs/plugin-react": "^1.0.0",
21 | "prettier": "^2.4.1",
22 | "sass": "=1.32.8",
23 | "typescript": "^4.3.2",
24 | "vite": "^2.6.4"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/front/src/App.scss:
--------------------------------------------------------------------------------
1 |
2 | @import "spectre.css/src/spectre";
3 | @import "spectre.css/src/spectre-icons";
4 |
5 |
6 | .todo-title {
7 | margin-top: 2rem;
8 | }
9 |
10 | .todo-row {
11 | margin-top: 0.5rem;
12 | }
13 |
14 | .todo-buttons {
15 | margin-top: 0.5rem;
16 | display: flex;
17 | flex-direction: row-reverse;
18 | }
19 |
20 | .completed {
21 | text-decoration: line-through;
22 | }
--------------------------------------------------------------------------------
/front/src/App.tsx:
--------------------------------------------------------------------------------
1 | import "./App.scss";
2 |
3 | import { Todo, TodoRow } from "./Todo";
4 | import { sendAction, useWebsocket } from "./Websocket";
5 |
6 | function App() {
7 | const todo = useWebsocket();
8 |
9 | return (
10 |
11 |
12 |
13 |
Todo App Example
14 |
15 |
16 | {todo === undefined &&
}
17 | {todo &&
}
18 |
19 | );
20 | }
21 |
22 | function TodoComponent({ todo }: { todo: Todo }) {
23 | return (
24 | <>
25 |
26 |
27 |
28 |
31 |
32 |
33 | {
40 | sendAction({
41 | type: "ChangeName",
42 | name: ev.currentTarget.value,
43 | });
44 | }}
45 | />
46 |
47 |
48 |
49 | {Object.entries(todo.todos).map(([index, row]) => (
50 |
51 | ))}
52 |
53 |
54 |
55 | {Object.entries(todo.todos).some(([, val]) => val.completed) && (
56 |
66 | )}
67 |
81 |
82 |
83 |
84 | >
85 | );
86 | }
87 |
88 | function TodoRowComponent({ row, index }: { row: TodoRow; index: number }) {
89 | return (
90 |
91 |
92 |
93 |
110 |
116 | sendAction({
117 | type: "Update",
118 | row: {
119 | ...row,
120 | name: ev.currentTarget.value,
121 | },
122 | index,
123 | })
124 | }
125 | />
126 |
127 |
138 |
139 |
140 |
141 | );
142 | }
143 |
144 | export default App;
145 |
--------------------------------------------------------------------------------
/front/src/Todo.ts:
--------------------------------------------------------------------------------
1 | export interface Todo {
2 | name: string;
3 | todos: { [index: number]: TodoRow };
4 | }
5 |
6 | export interface TodoRow {
7 | name: string;
8 | completed: boolean;
9 | }
10 |
11 | export interface Add {
12 | type: "Add";
13 | row: TodoRow;
14 | }
15 |
16 | export interface ChangeName {
17 | type: "ChangeName";
18 | name: string;
19 | }
20 |
21 | export interface Update {
22 | type: "Update";
23 | row: TodoRow;
24 | index: number;
25 | }
26 |
27 | export interface Remove {
28 | type: "Remove";
29 | index: number;
30 | }
31 |
32 | export interface RemoveCompleted {
33 | type: "RemoveCompleted";
34 | }
35 |
36 | export type TodoAction = Add | ChangeName | Update | Remove | RemoveCompleted;
37 |
--------------------------------------------------------------------------------
/front/src/Websocket.ts:
--------------------------------------------------------------------------------
1 | import { applyPatch, Operation } from "fast-json-patch";
2 | import { useEffect, useState } from "react";
3 | import { Todo, TodoAction } from "./Todo";
4 |
5 | interface Patch {
6 | type: "Patch";
7 | ops: Operation[];
8 | }
9 |
10 | interface Full {
11 | type: "Full";
12 | todo: Todo;
13 | }
14 |
15 | type ServerMessage = Patch | Full;
16 |
17 | let websocket: WebSocket | undefined;
18 | let todo: Todo | undefined;
19 |
20 | const setupWebsocket = (onTodoUpdate: (todo: Todo) => void) => {
21 | const loc = window.location;
22 | const uri = `${loc.protocol === "https:" ? "wss:" : "ws:"}//${loc.host}/ws`;
23 | console.log(`Connecting websocket: ${uri}`);
24 |
25 | const connection = new WebSocket(uri);
26 |
27 | connection.onopen = () => {
28 | console.log("Websocket Connected");
29 | websocket = connection;
30 | };
31 |
32 | // If we receive a close event the backend has gone away, we try reconnecting in a bit of time
33 | connection.onclose = (reason) => {
34 | websocket = undefined;
35 |
36 | // https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
37 | if (reason.code !== 1000 && reason.code !== 1001) {
38 | console.error("Websocket connection closed", reason);
39 |
40 | setTimeout(() => {
41 | setupWebsocket(onTodoUpdate);
42 | }, 500);
43 | }
44 | };
45 |
46 | connection.onerror = (error) => {
47 | console.error("Error with websocket", error);
48 | connection.close();
49 | };
50 |
51 | connection.onmessage = (message) => {
52 | const msg = JSON.parse(message.data) as ServerMessage;
53 |
54 | switch (msg.type) {
55 | case "Patch": {
56 | if (todo !== undefined) {
57 | let { newDocument: newTodo } = applyPatch(
58 | todo,
59 | msg.ops,
60 | false,
61 | false
62 | );
63 |
64 | onTodoUpdate(newTodo);
65 | todo = newTodo;
66 | }
67 | break;
68 | }
69 | case "Full": {
70 | onTodoUpdate(msg.todo);
71 | todo = msg.todo;
72 | break;
73 | }
74 | }
75 | };
76 | };
77 |
78 | export const useWebsocket = () => {
79 | // Keep our local state of the todo app to trigger a render on change
80 | let [todo, updateTodo] = useState();
81 |
82 | useEffect(() => {
83 | // Update our app state when changes are received
84 | setupWebsocket((msg) => {
85 | updateTodo(msg);
86 | });
87 | // If the destructor runs, clean up the websocket
88 | return () => {
89 | if (websocket) {
90 | websocket.close(1000);
91 | }
92 | };
93 | // The empty `[]` dependency list makes this `useEffect` callback execute only once on construction
94 | }, []);
95 |
96 | return todo;
97 | };
98 |
99 | export const sendAction = (action: TodoAction): void => {
100 | if (websocket) {
101 | websocket.send(JSON.stringify(action));
102 | }
103 | };
104 |
--------------------------------------------------------------------------------
/front/src/favicon.svg:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/front/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/front/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./index.css";
4 | import App from "./App";
5 |
6 | ReactDOM.render(
7 |
8 |
9 | ,
10 | document.getElementById("root")
11 | );
12 |
--------------------------------------------------------------------------------
/front/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/front/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": false,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx"
18 | },
19 | "include": ["./src"]
20 | }
21 |
--------------------------------------------------------------------------------
/front/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import react from "@vitejs/plugin-react";
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | server: {
8 | proxy: {
9 | "/ws": {
10 | target: "http://127.0.0.1:3333",
11 | ws: true,
12 | },
13 | },
14 | },
15 | });
16 |
--------------------------------------------------------------------------------
/front/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@babel/code-frame@^7.14.5", "@babel/code-frame@^7.15.8":
6 | version "7.15.8"
7 | resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.15.8.tgz#45990c47adadb00c03677baa89221f7cc23d2503"
8 | integrity sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==
9 | dependencies:
10 | "@babel/highlight" "^7.14.5"
11 |
12 | "@babel/compat-data@^7.15.0":
13 | version "7.15.0"
14 | resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.15.0.tgz#2dbaf8b85334796cafbb0f5793a90a2fc010b176"
15 | integrity sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA==
16 |
17 | "@babel/core@^7.15.5":
18 | version "7.15.8"
19 | resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.15.8.tgz#195b9f2bffe995d2c6c159e72fe525b4114e8c10"
20 | integrity sha512-3UG9dsxvYBMYwRv+gS41WKHno4K60/9GPy1CJaH6xy3Elq8CTtvtjT5R5jmNhXfCYLX2mTw+7/aq5ak/gOE0og==
21 | dependencies:
22 | "@babel/code-frame" "^7.15.8"
23 | "@babel/generator" "^7.15.8"
24 | "@babel/helper-compilation-targets" "^7.15.4"
25 | "@babel/helper-module-transforms" "^7.15.8"
26 | "@babel/helpers" "^7.15.4"
27 | "@babel/parser" "^7.15.8"
28 | "@babel/template" "^7.15.4"
29 | "@babel/traverse" "^7.15.4"
30 | "@babel/types" "^7.15.6"
31 | convert-source-map "^1.7.0"
32 | debug "^4.1.0"
33 | gensync "^1.0.0-beta.2"
34 | json5 "^2.1.2"
35 | semver "^6.3.0"
36 | source-map "^0.5.0"
37 |
38 | "@babel/generator@^7.15.4", "@babel/generator@^7.15.8":
39 | version "7.15.8"
40 | resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.15.8.tgz#fa56be6b596952ceb231048cf84ee499a19c0cd1"
41 | integrity sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g==
42 | dependencies:
43 | "@babel/types" "^7.15.6"
44 | jsesc "^2.5.1"
45 | source-map "^0.5.0"
46 |
47 | "@babel/helper-annotate-as-pure@^7.14.5":
48 | version "7.15.4"
49 | resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.15.4.tgz#3d0e43b00c5e49fdb6c57e421601a7a658d5f835"
50 | integrity sha512-QwrtdNvUNsPCj2lfNQacsGSQvGX8ee1ttrBrcozUP2Sv/jylewBP/8QFe6ZkBsC8T/GYWonNAWJV4aRR9AL2DA==
51 | dependencies:
52 | "@babel/types" "^7.15.4"
53 |
54 | "@babel/helper-compilation-targets@^7.15.4":
55 | version "7.15.4"
56 | resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz#cf6d94f30fbefc139123e27dd6b02f65aeedb7b9"
57 | integrity sha512-rMWPCirulnPSe4d+gwdWXLfAXTTBj8M3guAf5xFQJ0nvFY7tfNAFnWdqaHegHlgDZOCT4qvhF3BYlSJag8yhqQ==
58 | dependencies:
59 | "@babel/compat-data" "^7.15.0"
60 | "@babel/helper-validator-option" "^7.14.5"
61 | browserslist "^4.16.6"
62 | semver "^6.3.0"
63 |
64 | "@babel/helper-function-name@^7.15.4":
65 | version "7.15.4"
66 | resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz#845744dafc4381a4a5fb6afa6c3d36f98a787ebc"
67 | integrity sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==
68 | dependencies:
69 | "@babel/helper-get-function-arity" "^7.15.4"
70 | "@babel/template" "^7.15.4"
71 | "@babel/types" "^7.15.4"
72 |
73 | "@babel/helper-get-function-arity@^7.15.4":
74 | version "7.15.4"
75 | resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz#098818934a137fce78b536a3e015864be1e2879b"
76 | integrity sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==
77 | dependencies:
78 | "@babel/types" "^7.15.4"
79 |
80 | "@babel/helper-hoist-variables@^7.15.4":
81 | version "7.15.4"
82 | resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz#09993a3259c0e918f99d104261dfdfc033f178df"
83 | integrity sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA==
84 | dependencies:
85 | "@babel/types" "^7.15.4"
86 |
87 | "@babel/helper-member-expression-to-functions@^7.15.4":
88 | version "7.15.4"
89 | resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz#bfd34dc9bba9824a4658b0317ec2fd571a51e6ef"
90 | integrity sha512-cokOMkxC/BTyNP1AlY25HuBWM32iCEsLPI4BHDpJCHHm1FU2E7dKWWIXJgQgSFiu4lp8q3bL1BIKwqkSUviqtA==
91 | dependencies:
92 | "@babel/types" "^7.15.4"
93 |
94 | "@babel/helper-module-imports@^7.14.5", "@babel/helper-module-imports@^7.15.4":
95 | version "7.15.4"
96 | resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz#e18007d230632dea19b47853b984476e7b4e103f"
97 | integrity sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA==
98 | dependencies:
99 | "@babel/types" "^7.15.4"
100 |
101 | "@babel/helper-module-transforms@^7.15.8":
102 | version "7.15.8"
103 | resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.15.8.tgz#d8c0e75a87a52e374a8f25f855174786a09498b2"
104 | integrity sha512-DfAfA6PfpG8t4S6npwzLvTUpp0sS7JrcuaMiy1Y5645laRJIp/LiLGIBbQKaXSInK8tiGNI7FL7L8UvB8gdUZg==
105 | dependencies:
106 | "@babel/helper-module-imports" "^7.15.4"
107 | "@babel/helper-replace-supers" "^7.15.4"
108 | "@babel/helper-simple-access" "^7.15.4"
109 | "@babel/helper-split-export-declaration" "^7.15.4"
110 | "@babel/helper-validator-identifier" "^7.15.7"
111 | "@babel/template" "^7.15.4"
112 | "@babel/traverse" "^7.15.4"
113 | "@babel/types" "^7.15.6"
114 |
115 | "@babel/helper-optimise-call-expression@^7.15.4":
116 | version "7.15.4"
117 | resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz#f310a5121a3b9cc52d9ab19122bd729822dee171"
118 | integrity sha512-E/z9rfbAOt1vDW1DR7k4SzhzotVV5+qMciWV6LaG1g4jeFrkDlJedjtV4h0i4Q/ITnUu+Pk08M7fczsB9GXBDw==
119 | dependencies:
120 | "@babel/types" "^7.15.4"
121 |
122 | "@babel/helper-plugin-utils@^7.14.5":
123 | version "7.14.5"
124 | resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9"
125 | integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==
126 |
127 | "@babel/helper-replace-supers@^7.15.4":
128 | version "7.15.4"
129 | resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz#52a8ab26ba918c7f6dee28628b07071ac7b7347a"
130 | integrity sha512-/ztT6khaXF37MS47fufrKvIsiQkx1LBRvSJNzRqmbyeZnTwU9qBxXYLaaT/6KaxfKhjs2Wy8kG8ZdsFUuWBjzw==
131 | dependencies:
132 | "@babel/helper-member-expression-to-functions" "^7.15.4"
133 | "@babel/helper-optimise-call-expression" "^7.15.4"
134 | "@babel/traverse" "^7.15.4"
135 | "@babel/types" "^7.15.4"
136 |
137 | "@babel/helper-simple-access@^7.15.4":
138 | version "7.15.4"
139 | resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz#ac368905abf1de8e9781434b635d8f8674bcc13b"
140 | integrity sha512-UzazrDoIVOZZcTeHHEPYrr1MvTR/K+wgLg6MY6e1CJyaRhbibftF6fR2KU2sFRtI/nERUZR9fBd6aKgBlIBaPg==
141 | dependencies:
142 | "@babel/types" "^7.15.4"
143 |
144 | "@babel/helper-split-export-declaration@^7.15.4":
145 | version "7.15.4"
146 | resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz#aecab92dcdbef6a10aa3b62ab204b085f776e257"
147 | integrity sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==
148 | dependencies:
149 | "@babel/types" "^7.15.4"
150 |
151 | "@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.9", "@babel/helper-validator-identifier@^7.15.7":
152 | version "7.15.7"
153 | resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389"
154 | integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==
155 |
156 | "@babel/helper-validator-option@^7.14.5":
157 | version "7.14.5"
158 | resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3"
159 | integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==
160 |
161 | "@babel/helpers@^7.15.4":
162 | version "7.15.4"
163 | resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.15.4.tgz#5f40f02050a3027121a3cf48d497c05c555eaf43"
164 | integrity sha512-V45u6dqEJ3w2rlryYYXf6i9rQ5YMNu4FLS6ngs8ikblhu2VdR1AqAd6aJjBzmf2Qzh6KOLqKHxEN9+TFbAkAVQ==
165 | dependencies:
166 | "@babel/template" "^7.15.4"
167 | "@babel/traverse" "^7.15.4"
168 | "@babel/types" "^7.15.4"
169 |
170 | "@babel/highlight@^7.14.5":
171 | version "7.14.5"
172 | resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9"
173 | integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==
174 | dependencies:
175 | "@babel/helper-validator-identifier" "^7.14.5"
176 | chalk "^2.0.0"
177 | js-tokens "^4.0.0"
178 |
179 | "@babel/parser@^7.15.4", "@babel/parser@^7.15.8":
180 | version "7.15.8"
181 | resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.8.tgz#7bacdcbe71bdc3ff936d510c15dcea7cf0b99016"
182 | integrity sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==
183 |
184 | "@babel/plugin-syntax-jsx@^7.14.5":
185 | version "7.14.5"
186 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.14.5.tgz#000e2e25d8673cce49300517a3eda44c263e4201"
187 | integrity sha512-ohuFIsOMXJnbOMRfX7/w7LocdR6R7whhuRD4ax8IipLcLPlZGJKkBxgHp++U4N/vKyU16/YDQr2f5seajD3jIw==
188 | dependencies:
189 | "@babel/helper-plugin-utils" "^7.14.5"
190 |
191 | "@babel/plugin-transform-react-jsx-development@^7.14.5":
192 | version "7.14.5"
193 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.14.5.tgz#1a6c73e2f7ed2c42eebc3d2ad60b0c7494fcb9af"
194 | integrity sha512-rdwG/9jC6QybWxVe2UVOa7q6cnTpw8JRRHOxntG/h6g/guAOe6AhtQHJuJh5FwmnXIT1bdm5vC2/5huV8ZOorQ==
195 | dependencies:
196 | "@babel/plugin-transform-react-jsx" "^7.14.5"
197 |
198 | "@babel/plugin-transform-react-jsx-self@^7.14.9":
199 | version "7.14.9"
200 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.14.9.tgz#33041e665453391eb6ee54a2ecf3ba1d46bd30f4"
201 | integrity sha512-Fqqu0f8zv9W+RyOnx29BX/RlEsBRANbOf5xs5oxb2aHP4FKbLXxIaVPUiCti56LAR1IixMH4EyaixhUsKqoBHw==
202 | dependencies:
203 | "@babel/helper-plugin-utils" "^7.14.5"
204 |
205 | "@babel/plugin-transform-react-jsx-source@^7.14.5":
206 | version "7.14.5"
207 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.14.5.tgz#79f728e60e6dbd31a2b860b0bf6c9765918acf1d"
208 | integrity sha512-1TpSDnD9XR/rQ2tzunBVPThF5poaYT9GqP+of8fAtguYuI/dm2RkrMBDemsxtY0XBzvW7nXjYM0hRyKX9QYj7Q==
209 | dependencies:
210 | "@babel/helper-plugin-utils" "^7.14.5"
211 |
212 | "@babel/plugin-transform-react-jsx@^7.14.5", "@babel/plugin-transform-react-jsx@^7.14.9":
213 | version "7.14.9"
214 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.14.9.tgz#3314b2163033abac5200a869c4de242cd50a914c"
215 | integrity sha512-30PeETvS+AeD1f58i1OVyoDlVYQhap/K20ZrMjLmmzmC2AYR/G43D4sdJAaDAqCD3MYpSWbmrz3kES158QSLjw==
216 | dependencies:
217 | "@babel/helper-annotate-as-pure" "^7.14.5"
218 | "@babel/helper-module-imports" "^7.14.5"
219 | "@babel/helper-plugin-utils" "^7.14.5"
220 | "@babel/plugin-syntax-jsx" "^7.14.5"
221 | "@babel/types" "^7.14.9"
222 |
223 | "@babel/template@^7.15.4":
224 | version "7.15.4"
225 | resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.15.4.tgz#51898d35dcf3faa670c4ee6afcfd517ee139f194"
226 | integrity sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==
227 | dependencies:
228 | "@babel/code-frame" "^7.14.5"
229 | "@babel/parser" "^7.15.4"
230 | "@babel/types" "^7.15.4"
231 |
232 | "@babel/traverse@^7.15.4":
233 | version "7.15.4"
234 | resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.15.4.tgz#ff8510367a144bfbff552d9e18e28f3e2889c22d"
235 | integrity sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==
236 | dependencies:
237 | "@babel/code-frame" "^7.14.5"
238 | "@babel/generator" "^7.15.4"
239 | "@babel/helper-function-name" "^7.15.4"
240 | "@babel/helper-hoist-variables" "^7.15.4"
241 | "@babel/helper-split-export-declaration" "^7.15.4"
242 | "@babel/parser" "^7.15.4"
243 | "@babel/types" "^7.15.4"
244 | debug "^4.1.0"
245 | globals "^11.1.0"
246 |
247 | "@babel/types@^7.14.9", "@babel/types@^7.15.4", "@babel/types@^7.15.6":
248 | version "7.15.6"
249 | resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.15.6.tgz#99abdc48218b2881c058dd0a7ab05b99c9be758f"
250 | integrity sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==
251 | dependencies:
252 | "@babel/helper-validator-identifier" "^7.14.9"
253 | to-fast-properties "^2.0.0"
254 |
255 | "@rollup/pluginutils@^4.1.1":
256 | version "4.1.1"
257 | resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.1.tgz#1d4da86dd4eded15656a57d933fda2b9a08d47ec"
258 | integrity sha512-clDjivHqWGXi7u+0d2r2sBi4Ie6VLEAzWMIkvJLnDmxoOhBYOTfzGbOQBA32THHm11/LiJbd01tJUpJsbshSWQ==
259 | dependencies:
260 | estree-walker "^2.0.1"
261 | picomatch "^2.2.2"
262 |
263 | "@types/prop-types@*":
264 | version "15.7.4"
265 | resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11"
266 | integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==
267 |
268 | "@types/react-dom@^17.0.0":
269 | version "17.0.9"
270 | resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.9.tgz#441a981da9d7be117042e1a6fd3dac4b30f55add"
271 | integrity sha512-wIvGxLfgpVDSAMH5utdL9Ngm5Owu0VsGmldro3ORLXV8CShrL8awVj06NuEXFQ5xyaYfdca7Sgbk/50Ri1GdPg==
272 | dependencies:
273 | "@types/react" "*"
274 |
275 | "@types/react@*", "@types/react@^17.0.0":
276 | version "17.0.30"
277 | resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.30.tgz#2f8e6f5ab6415c091cc5e571942ee9064b17609e"
278 | integrity sha512-3Dt/A8gd3TCXi2aRe84y7cK1K8G+N9CZRDG8kDGguOKa0kf/ZkSwTmVIDPsm/KbQOVMaDJXwhBtuOXxqwdpWVg==
279 | dependencies:
280 | "@types/prop-types" "*"
281 | "@types/scheduler" "*"
282 | csstype "^3.0.2"
283 |
284 | "@types/scheduler@*":
285 | version "0.16.2"
286 | resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
287 | integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
288 |
289 | "@vitejs/plugin-react@^1.0.0":
290 | version "1.0.4"
291 | resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-1.0.4.tgz#b4e934915205f2b05998019549c853bb1bb1f6c1"
292 | integrity sha512-38/w1q2FON4e/es8WnAW0ZOa/RIOoOrpeWNUkgY6+u+M1eQZjyWWI0piLRM6fbDnm8Lm8Qtged8A7OZ/YnkNtw==
293 | dependencies:
294 | "@babel/core" "^7.15.5"
295 | "@babel/plugin-transform-react-jsx" "^7.14.9"
296 | "@babel/plugin-transform-react-jsx-development" "^7.14.5"
297 | "@babel/plugin-transform-react-jsx-self" "^7.14.9"
298 | "@babel/plugin-transform-react-jsx-source" "^7.14.5"
299 | "@rollup/pluginutils" "^4.1.1"
300 | react-refresh "^0.10.0"
301 | resolve "^1.20.0"
302 |
303 | ansi-styles@^3.2.1:
304 | version "3.2.1"
305 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
306 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
307 | dependencies:
308 | color-convert "^1.9.0"
309 |
310 | anymatch@~3.1.2:
311 | version "3.1.2"
312 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
313 | integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
314 | dependencies:
315 | normalize-path "^3.0.0"
316 | picomatch "^2.0.4"
317 |
318 | binary-extensions@^2.0.0:
319 | version "2.2.0"
320 | resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
321 | integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
322 |
323 | braces@~3.0.2:
324 | version "3.0.2"
325 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
326 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
327 | dependencies:
328 | fill-range "^7.0.1"
329 |
330 | browserslist@^4.16.6:
331 | version "4.17.4"
332 | resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.4.tgz#72e2508af2a403aec0a49847ef31bd823c57ead4"
333 | integrity sha512-Zg7RpbZpIJRW3am9Lyckue7PLytvVxxhJj1CaJVlCWENsGEAOlnlt8X0ZxGRPp7Bt9o8tIRM5SEXy4BCPMJjLQ==
334 | dependencies:
335 | caniuse-lite "^1.0.30001265"
336 | electron-to-chromium "^1.3.867"
337 | escalade "^3.1.1"
338 | node-releases "^2.0.0"
339 | picocolors "^1.0.0"
340 |
341 | caniuse-lite@^1.0.30001265:
342 | version "1.0.30001269"
343 | resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001269.tgz#3a71bee03df627364418f9fd31adfc7aa1cc2d56"
344 | integrity sha512-UOy8okEVs48MyHYgV+RdW1Oiudl1H6KolybD6ZquD0VcrPSgj25omXO1S7rDydjpqaISCwA8Pyx+jUQKZwWO5w==
345 |
346 | chalk@^2.0.0:
347 | version "2.4.2"
348 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
349 | integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
350 | dependencies:
351 | ansi-styles "^3.2.1"
352 | escape-string-regexp "^1.0.5"
353 | supports-color "^5.3.0"
354 |
355 | "chokidar@>=2.0.0 <4.0.0":
356 | version "3.5.2"
357 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75"
358 | integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==
359 | dependencies:
360 | anymatch "~3.1.2"
361 | braces "~3.0.2"
362 | glob-parent "~5.1.2"
363 | is-binary-path "~2.1.0"
364 | is-glob "~4.0.1"
365 | normalize-path "~3.0.0"
366 | readdirp "~3.6.0"
367 | optionalDependencies:
368 | fsevents "~2.3.2"
369 |
370 | color-convert@^1.9.0:
371 | version "1.9.3"
372 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
373 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
374 | dependencies:
375 | color-name "1.1.3"
376 |
377 | color-name@1.1.3:
378 | version "1.1.3"
379 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
380 | integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
381 |
382 | convert-source-map@^1.7.0:
383 | version "1.8.0"
384 | resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"
385 | integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==
386 | dependencies:
387 | safe-buffer "~5.1.1"
388 |
389 | csstype@^3.0.2:
390 | version "3.0.9"
391 | resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.9.tgz#6410af31b26bd0520933d02cbc64fce9ce3fbf0b"
392 | integrity sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw==
393 |
394 | debug@^4.1.0:
395 | version "4.3.2"
396 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
397 | integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
398 | dependencies:
399 | ms "2.1.2"
400 |
401 | electron-to-chromium@^1.3.867:
402 | version "1.3.871"
403 | resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.871.tgz#6e87365fd72037a6c898fb46050ad4be3ac9ef62"
404 | integrity sha512-qcLvDUPf8DSIMWarHT2ptgcqrYg62n3vPA7vhrOF24d8UNzbUBaHu2CySiENR3nEDzYgaN60071t0F6KLYMQ7Q==
405 |
406 | esbuild-android-arm64@0.13.8:
407 | version "0.13.8"
408 | resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.13.8.tgz#c20e875c3c98164b1ffba9b28637bdf96f5e9e7c"
409 | integrity sha512-AilbChndywpk7CdKkNSZ9klxl+9MboLctXd9LwLo3b0dawmOF/i/t2U5d8LM6SbT1Xw36F8yngSUPrd8yPs2RA==
410 |
411 | esbuild-darwin-64@0.13.8:
412 | version "0.13.8"
413 | resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.13.8.tgz#f46e6b471ddbf62265234808a6a1aa91df18a417"
414 | integrity sha512-b6sdiT84zV5LVaoF+UoMVGJzR/iE2vNUfUDfFQGrm4LBwM/PWXweKpuu6RD9mcyCq18cLxkP6w/LD/w9DtX3ng==
415 |
416 | esbuild-darwin-arm64@0.13.8:
417 | version "0.13.8"
418 | resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.8.tgz#a991157a6013facd4f2e14159b7da52626c90154"
419 | integrity sha512-R8YuPiiJayuJJRUBG4H0VwkEKo6AvhJs2m7Tl0JaIer3u1FHHXwGhMxjJDmK+kXwTFPriSysPvcobXC/UrrZCQ==
420 |
421 | esbuild-freebsd-64@0.13.8:
422 | version "0.13.8"
423 | resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.8.tgz#301601d2e443ad458960e359b402a17d9500be9d"
424 | integrity sha512-zBn6urrn8FnKC+YSgDxdof9jhPCeU8kR/qaamlV4gI8R3KUaUK162WYM7UyFVAlj9N0MyD3AtB+hltzu4cysTw==
425 |
426 | esbuild-freebsd-arm64@0.13.8:
427 | version "0.13.8"
428 | resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.8.tgz#039a63acc12ec0892006c147ea221e55f9125a9f"
429 | integrity sha512-pWW2slN7lGlkx0MOEBoUGwRX5UgSCLq3dy2c8RIOpiHtA87xAUpDBvZK10MykbT+aMfXc0NI2lu1X+6kI34xng==
430 |
431 | esbuild-linux-32@0.13.8:
432 | version "0.13.8"
433 | resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.13.8.tgz#c537b67d7e694b60bfa2786581412838c6ba0284"
434 | integrity sha512-T0I0ueeKVO/Is0CAeSEOG9s2jeNNb8jrrMwG9QBIm3UU18MRB60ERgkS2uV3fZ1vP2F8i3Z2e3Zju4lg9dhVmw==
435 |
436 | esbuild-linux-64@0.13.8:
437 | version "0.13.8"
438 | resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.13.8.tgz#0092fc8a064001a777bfa0e3b425bb8be8f96e6a"
439 | integrity sha512-Bm8SYmFtvfDCIu9sjKppFXzRXn2BVpuCinU1ChTuMtdKI/7aPpXIrkqBNOgPTOQO9AylJJc1Zw6EvtKORhn64w==
440 |
441 | esbuild-linux-arm64@0.13.8:
442 | version "0.13.8"
443 | resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.8.tgz#5cd3f2bb924212971482e8dbc25c4afd09b28110"
444 | integrity sha512-X4pWZ+SL+FJ09chWFgRNO3F+YtvAQRcWh0uxKqZSWKiWodAB20flsW/OWFYLXBKiVCTeoGMvENZS/GeVac7+tQ==
445 |
446 | esbuild-linux-arm@0.13.8:
447 | version "0.13.8"
448 | resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.13.8.tgz#ad634f96bf2975536907aeb9fdb75a3194f4ddce"
449 | integrity sha512-4/HfcC40LJ4GPyboHA+db0jpFarTB628D1ifU+/5bunIgY+t6mHkJWyxWxAAE8wl/ZIuRYB9RJFdYpu1AXGPdg==
450 |
451 | esbuild-linux-mips64le@0.13.8:
452 | version "0.13.8"
453 | resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.8.tgz#57857edfebf9bf65766dc8be1637f2179c990572"
454 | integrity sha512-o7e0D+sqHKT31v+mwFircJFjwSKVd2nbkHEn4l9xQ1hLR+Bv8rnt3HqlblY3+sBdlrOTGSwz0ReROlKUMJyldA==
455 |
456 | esbuild-linux-ppc64le@0.13.8:
457 | version "0.13.8"
458 | resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.8.tgz#fdb82a059a5b86bb10fb42091b4ebcf488b9cd46"
459 | integrity sha512-eZSQ0ERsWkukJp2px/UWJHVNuy0lMoz/HZcRWAbB6reoaBw7S9vMzYNUnflfL3XA6WDs+dZn3ekHE4Y2uWLGig==
460 |
461 | esbuild-netbsd-64@0.13.8:
462 | version "0.13.8"
463 | resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.8.tgz#d7879e7123d3b2c04754ece8bd061aa6866deeff"
464 | integrity sha512-gZX4kP7gVvOrvX0ZwgHmbuHczQUwqYppxqtoyC7VNd80t5nBHOFXVhWo2Ad/Lms0E8b+wwgI/WjZFTCpUHOg9Q==
465 |
466 | esbuild-openbsd-64@0.13.8:
467 | version "0.13.8"
468 | resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.8.tgz#88b280b6cb0a3f6adb60abf27fc506c506a35cf0"
469 | integrity sha512-afzza308X4WmcebexbTzAgfEWt9MUkdTvwIa8xOu4CM2qGbl2LanqEl8/LUs8jh6Gqw6WsicEK52GPrS9wvkcw==
470 |
471 | esbuild-sunos-64@0.13.8:
472 | version "0.13.8"
473 | resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.13.8.tgz#229ae7c7703196a58acd0f0291ad9bebda815d63"
474 | integrity sha512-mWPZibmBbuMKD+LDN23LGcOZ2EawMYBONMXXHmbuxeT0XxCNwadbCVwUQ/2p5Dp5Kvf6mhrlIffcnWOiCBpiVw==
475 |
476 | esbuild-windows-32@0.13.8:
477 | version "0.13.8"
478 | resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.13.8.tgz#892d093e32a21c0c9135e5a0ffdc380aeb70e763"
479 | integrity sha512-QsZ1HnWIcnIEApETZWw8HlOhDSWqdZX2SylU7IzGxOYyVcX7QI06ety/aDcn437mwyO7Ph4RrbhB+2ntM8kX8A==
480 |
481 | esbuild-windows-64@0.13.8:
482 | version "0.13.8"
483 | resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.13.8.tgz#7defd8d79ae3bb7e6f53b65a7190be7daf901686"
484 | integrity sha512-76Fb57B9eE/JmJi1QmUW0tRLQZfGo0it+JeYoCDTSlbTn7LV44ecOHIMJSSgZADUtRMWT9z0Kz186bnaB3amSg==
485 |
486 | esbuild-windows-arm64@0.13.8:
487 | version "0.13.8"
488 | resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.8.tgz#e59ae004496fd8a5ab67bfc7945a2e47480d6fb9"
489 | integrity sha512-HW6Mtq5eTudllxY2YgT62MrVcn7oq2o8TAoAvDUhyiEmRmDY8tPwAhb1vxw5/cdkbukM3KdMYtksnUhF/ekWeg==
490 |
491 | esbuild@^0.13.2:
492 | version "0.13.8"
493 | resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.13.8.tgz#bd7cc51b881ab067789f88e17baca74724c1ec4f"
494 | integrity sha512-A4af7G7YZLfG5OnARJRMtlpEsCkq/zHZQXewgPA864l9D6VjjbH1SuFYK/OSV6BtHwDGkdwyRrX0qQFLnMfUcw==
495 | optionalDependencies:
496 | esbuild-android-arm64 "0.13.8"
497 | esbuild-darwin-64 "0.13.8"
498 | esbuild-darwin-arm64 "0.13.8"
499 | esbuild-freebsd-64 "0.13.8"
500 | esbuild-freebsd-arm64 "0.13.8"
501 | esbuild-linux-32 "0.13.8"
502 | esbuild-linux-64 "0.13.8"
503 | esbuild-linux-arm "0.13.8"
504 | esbuild-linux-arm64 "0.13.8"
505 | esbuild-linux-mips64le "0.13.8"
506 | esbuild-linux-ppc64le "0.13.8"
507 | esbuild-netbsd-64 "0.13.8"
508 | esbuild-openbsd-64 "0.13.8"
509 | esbuild-sunos-64 "0.13.8"
510 | esbuild-windows-32 "0.13.8"
511 | esbuild-windows-64 "0.13.8"
512 | esbuild-windows-arm64 "0.13.8"
513 |
514 | escalade@^3.1.1:
515 | version "3.1.1"
516 | resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
517 | integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
518 |
519 | escape-string-regexp@^1.0.5:
520 | version "1.0.5"
521 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
522 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
523 |
524 | estree-walker@^2.0.1:
525 | version "2.0.2"
526 | resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
527 | integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
528 |
529 | fast-json-patch@^3.1.0:
530 | version "3.1.0"
531 | resolved "https://registry.yarnpkg.com/fast-json-patch/-/fast-json-patch-3.1.0.tgz#ec8cd9b9c4c564250ec8b9140ef7a55f70acaee6"
532 | integrity sha512-IhpytlsVTRndz0hU5t0/MGzS/etxLlfrpG5V5M9mVbuj9TrJLWaMfsox9REM5rkuGX0T+5qjpe8XA1o0gZ42nA==
533 |
534 | fill-range@^7.0.1:
535 | version "7.0.1"
536 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
537 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
538 | dependencies:
539 | to-regex-range "^5.0.1"
540 |
541 | fsevents@~2.3.2:
542 | version "2.3.2"
543 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
544 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
545 |
546 | function-bind@^1.1.1:
547 | version "1.1.1"
548 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
549 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
550 |
551 | gensync@^1.0.0-beta.2:
552 | version "1.0.0-beta.2"
553 | resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
554 | integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
555 |
556 | glob-parent@~5.1.2:
557 | version "5.1.2"
558 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
559 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
560 | dependencies:
561 | is-glob "^4.0.1"
562 |
563 | globals@^11.1.0:
564 | version "11.12.0"
565 | resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
566 | integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
567 |
568 | has-flag@^3.0.0:
569 | version "3.0.0"
570 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
571 | integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
572 |
573 | has@^1.0.3:
574 | version "1.0.3"
575 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
576 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
577 | dependencies:
578 | function-bind "^1.1.1"
579 |
580 | is-binary-path@~2.1.0:
581 | version "2.1.0"
582 | resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
583 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
584 | dependencies:
585 | binary-extensions "^2.0.0"
586 |
587 | is-core-module@^2.2.0:
588 | version "2.8.0"
589 | resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.0.tgz#0321336c3d0925e497fd97f5d95cb114a5ccd548"
590 | integrity sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==
591 | dependencies:
592 | has "^1.0.3"
593 |
594 | is-extglob@^2.1.1:
595 | version "2.1.1"
596 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
597 | integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
598 |
599 | is-glob@^4.0.1, is-glob@~4.0.1:
600 | version "4.0.3"
601 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
602 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
603 | dependencies:
604 | is-extglob "^2.1.1"
605 |
606 | is-number@^7.0.0:
607 | version "7.0.0"
608 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
609 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
610 |
611 | "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
612 | version "4.0.0"
613 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
614 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
615 |
616 | jsesc@^2.5.1:
617 | version "2.5.2"
618 | resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
619 | integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
620 |
621 | json5@^2.1.2:
622 | version "2.2.0"
623 | resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3"
624 | integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==
625 | dependencies:
626 | minimist "^1.2.5"
627 |
628 | loose-envify@^1.1.0:
629 | version "1.4.0"
630 | resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
631 | integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
632 | dependencies:
633 | js-tokens "^3.0.0 || ^4.0.0"
634 |
635 | minimist@^1.2.5:
636 | version "1.2.5"
637 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
638 | integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
639 |
640 | ms@2.1.2:
641 | version "2.1.2"
642 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
643 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
644 |
645 | nanoid@^3.1.28:
646 | version "3.1.30"
647 | resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.30.tgz#63f93cc548d2a113dc5dfbc63bfa09e2b9b64362"
648 | integrity sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==
649 |
650 | node-releases@^2.0.0:
651 | version "2.0.0"
652 | resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.0.tgz#67dc74903100a7deb044037b8a2e5f453bb05400"
653 | integrity sha512-aA87l0flFYMzCHpTM3DERFSYxc6lv/BltdbRTOMZuxZ0cwZCD3mejE5n9vLhSJCN++/eOqr77G1IO5uXxlQYWA==
654 |
655 | normalize-path@^3.0.0, normalize-path@~3.0.0:
656 | version "3.0.0"
657 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
658 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
659 |
660 | object-assign@^4.1.1:
661 | version "4.1.1"
662 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
663 | integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
664 |
665 | path-parse@^1.0.6:
666 | version "1.0.7"
667 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
668 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
669 |
670 | picocolors@^0.2.1:
671 | version "0.2.1"
672 | resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f"
673 | integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==
674 |
675 | picocolors@^1.0.0:
676 | version "1.0.0"
677 | resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
678 | integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
679 |
680 | picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2:
681 | version "2.3.0"
682 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
683 | integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==
684 |
685 | postcss@^8.3.8:
686 | version "8.3.9"
687 | resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.9.tgz#98754caa06c4ee9eb59cc48bd073bb6bd3437c31"
688 | integrity sha512-f/ZFyAKh9Dnqytx5X62jgjhhzttjZS7hMsohcI7HEI5tjELX/HxCy3EFhsRxyzGvrzFF+82XPvCS8T9TFleVJw==
689 | dependencies:
690 | nanoid "^3.1.28"
691 | picocolors "^0.2.1"
692 | source-map-js "^0.6.2"
693 |
694 | prettier@^2.4.1:
695 | version "2.4.1"
696 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.4.1.tgz#671e11c89c14a4cfc876ce564106c4a6726c9f5c"
697 | integrity sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==
698 |
699 | react-dom@^17.0.0:
700 | version "17.0.2"
701 | resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
702 | integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
703 | dependencies:
704 | loose-envify "^1.1.0"
705 | object-assign "^4.1.1"
706 | scheduler "^0.20.2"
707 |
708 | react-refresh@^0.10.0:
709 | version "0.10.0"
710 | resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.10.0.tgz#2f536c9660c0b9b1d500684d9e52a65e7404f7e3"
711 | integrity sha512-PgidR3wST3dDYKr6b4pJoqQFpPGNKDSCDx4cZoshjXipw3LzO7mG1My2pwEzz2JVkF+inx3xRpDeQLFQGH/hsQ==
712 |
713 | react@^17.0.0:
714 | version "17.0.2"
715 | resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
716 | integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
717 | dependencies:
718 | loose-envify "^1.1.0"
719 | object-assign "^4.1.1"
720 |
721 | readdirp@~3.6.0:
722 | version "3.6.0"
723 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
724 | integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
725 | dependencies:
726 | picomatch "^2.2.1"
727 |
728 | resolve@^1.20.0:
729 | version "1.20.0"
730 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
731 | integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
732 | dependencies:
733 | is-core-module "^2.2.0"
734 | path-parse "^1.0.6"
735 |
736 | rollup@^2.57.0:
737 | version "2.58.0"
738 | resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.58.0.tgz#a643983365e7bf7f5b7c62a8331b983b7c4c67fb"
739 | integrity sha512-NOXpusKnaRpbS7ZVSzcEXqxcLDOagN6iFS8p45RkoiMqPHDLwJm758UF05KlMoCRbLBTZsPOIa887gZJ1AiXvw==
740 | optionalDependencies:
741 | fsevents "~2.3.2"
742 |
743 | safe-buffer@~5.1.1:
744 | version "5.1.2"
745 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
746 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
747 |
748 | sass@=1.32.8:
749 | version "1.32.8"
750 | resolved "https://registry.yarnpkg.com/sass/-/sass-1.32.8.tgz#f16a9abd8dc530add8834e506878a2808c037bdc"
751 | integrity sha512-Sl6mIeGpzjIUZqvKnKETfMf0iDAswD9TNlv13A7aAF3XZlRPMq4VvJWBC2N2DXbp94MQVdNSFG6LfF/iOXrPHQ==
752 | dependencies:
753 | chokidar ">=2.0.0 <4.0.0"
754 |
755 | scheduler@^0.20.2:
756 | version "0.20.2"
757 | resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
758 | integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
759 | dependencies:
760 | loose-envify "^1.1.0"
761 | object-assign "^4.1.1"
762 |
763 | semver@^6.3.0:
764 | version "6.3.0"
765 | resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
766 | integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
767 |
768 | source-map-js@^0.6.2:
769 | version "0.6.2"
770 | resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e"
771 | integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==
772 |
773 | source-map@^0.5.0:
774 | version "0.5.7"
775 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
776 | integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
777 |
778 | spectre.css@^0.5.9:
779 | version "0.5.9"
780 | resolved "https://registry.yarnpkg.com/spectre.css/-/spectre.css-0.5.9.tgz#86c732d093036d9fdc0a2ba570f005e4023ae6ca"
781 | integrity sha512-9jUqwZmCnvflrxFGcK+ize43TvjwDjqMwZPVubEtSIHzvinH0TBUESm1LcOJx3Ur7bdPaeOHQIjOqBl1Y5kLFw==
782 |
783 | supports-color@^5.3.0:
784 | version "5.5.0"
785 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
786 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
787 | dependencies:
788 | has-flag "^3.0.0"
789 |
790 | to-fast-properties@^2.0.0:
791 | version "2.0.0"
792 | resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
793 | integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
794 |
795 | to-regex-range@^5.0.1:
796 | version "5.0.1"
797 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
798 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
799 | dependencies:
800 | is-number "^7.0.0"
801 |
802 | typescript@^4.3.2:
803 | version "4.4.4"
804 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c"
805 | integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==
806 |
807 | vite@^2.6.4:
808 | version "2.6.7"
809 | resolved "https://registry.yarnpkg.com/vite/-/vite-2.6.7.tgz#e15c1d8327950720b5d7c4ec3fb36a5a58ccf7cb"
810 | integrity sha512-ewk//jve9k6vlU8PfJmWUHN8k0YYdw4VaKOMvoQ3nT2Pb6k5OSMKQi4jPOzVH/TlUqMsCrq7IJ80xcuDDVyigg==
811 | dependencies:
812 | esbuild "^0.13.2"
813 | postcss "^8.3.8"
814 | resolve "^1.20.0"
815 | rollup "^2.57.0"
816 | optionalDependencies:
817 | fsevents "~2.3.2"
818 |
--------------------------------------------------------------------------------
/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::{net::SocketAddr, sync::Arc};
2 |
3 | use axum::{
4 | extract::{ws::WebSocketUpgrade, Extension},
5 | response::IntoResponse,
6 | routing::get,
7 | Router,
8 | };
9 | use tracing::*;
10 |
11 | mod todo;
12 | mod websocket;
13 |
14 | use websocket::{handle_socket, WsState};
15 |
16 | #[tokio::main]
17 | async fn main() {
18 | // Set a sensible default for logging to ensure we see something
19 | if std::env::var_os("RUST_LOG").is_none() {
20 | std::env::set_var("RUST_LOG", "websocket_jsonpatch=debug")
21 | }
22 |
23 | // Initialise the `fmt` subscriber which will print logs to stderr
24 | tracing_subscriber::fmt::init();
25 |
26 | // Add in our application with the websocket handler
27 | let app = Router::new()
28 | .route("/ws", get(ws_handler))
29 | // Add in a shared websocket state struct `WsState`
30 | .layer(Extension(Arc::new(WsState::new())));
31 |
32 | // Listen on port 3333 for connections
33 | let addr: SocketAddr = "127.0.0.1:3333".parse().unwrap();
34 |
35 | debug!("Listening for requests on {}", addr);
36 |
37 | // Start up the server
38 | axum::Server::bind(&addr)
39 | .serve(app.into_make_service())
40 | .await
41 | .unwrap();
42 | }
43 |
44 | async fn ws_handler(
45 | ws: WebSocketUpgrade,
46 | Extension(state): Extension>,
47 | ) -> impl IntoResponse {
48 | debug!("New Websocket Connection");
49 | ws.on_upgrade(|socket| handle_socket(socket, state))
50 | }
51 |
--------------------------------------------------------------------------------
/src/todo.rs:
--------------------------------------------------------------------------------
1 | use std::collections::BTreeMap;
2 |
3 | use serde::{Deserialize, Serialize};
4 |
5 | #[derive(Serialize, Deserialize, Default, Debug, Clone)]
6 | pub struct Todo {
7 | // The main name of the todo list
8 | name: String,
9 | // The list of todos
10 | todos: BTreeMap,
11 | }
12 |
13 | #[derive(Serialize, Deserialize, Default, Debug, Clone)]
14 | pub struct TodoRow {
15 | // The name of the specific todo
16 | name: String,
17 | // Whether this todo is completed or not
18 | completed: bool,
19 | }
20 |
21 | #[derive(Deserialize)]
22 | #[serde(tag = "type")]
23 | pub enum TodoAction {
24 | Add { row: TodoRow },
25 | ChangeName { name: String },
26 | Update { index: u32, row: TodoRow },
27 | Remove { index: u32 },
28 | RemoveCompleted,
29 | }
30 |
31 | impl Todo {
32 | pub fn apply(&mut self, action: TodoAction) {
33 | match action {
34 | TodoAction::Add { row } => {
35 | // Find the next available index
36 | let index = self.todos.keys().max().copied().unwrap_or_default() + 1;
37 |
38 | // Insert this into our map
39 | self.todos.insert(index, row);
40 | }
41 | TodoAction::Update { row, index } => {
42 | self.todos.insert(index, row);
43 | }
44 | // Change the name of the todo list
45 | TodoAction::ChangeName { name } => self.name = name,
46 | TodoAction::Remove { index } => {
47 | self.todos.remove(&index);
48 | }
49 | // Filter and remove all completed todo rows
50 | TodoAction::RemoveCompleted => self.todos.retain(|_, val| !val.completed),
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/websocket.rs:
--------------------------------------------------------------------------------
1 | use std::mem;
2 | use std::sync::Arc;
3 |
4 | use axum::extract::ws::{Message, WebSocket};
5 | use futures::{stream::SplitSink, SinkExt, StreamExt};
6 | use json_patch::{diff, PatchOperation};
7 | use serde::Serialize;
8 | use serde_json::Error;
9 | use tokio::sync::Mutex;
10 | use tracing::*;
11 |
12 | use crate::todo::{Todo, TodoAction};
13 |
14 | pub struct WsState {
15 | // Our main state, behind a tokio Mutex
16 | todo: Mutex,
17 | // A list of sessions we will send changes to
18 | txs: Mutex>>,
19 | }
20 |
21 | impl WsState {
22 | pub fn new() -> Self {
23 | WsState {
24 | todo: Mutex::new(Todo::default()),
25 | txs: Mutex::new(Vec::default()),
26 | }
27 | }
28 |
29 | async fn add_session(&self, mut tx: SplitSink) {
30 | let mut txs = self.txs.lock().await;
31 |
32 | if let Err(err) = tx
33 | .send(Message::Text(
34 | // This method will not fail in "normal" operations so an `expect()` is OK here
35 | serde_json::to_string(&ServerMessage::Full {
36 | todo: &*self.todo.lock().await,
37 | })
38 | .expect("Serialize Error"),
39 | ))
40 | .await
41 | {
42 | warn!("Could not send initial state update: {}", err);
43 | return;
44 | }
45 |
46 | // Add session to our list of sessions
47 | txs.push(tx);
48 | }
49 |
50 | async fn apply(&self, action: TodoAction) -> Result<(), Error> {
51 | // Grab a mutable reference
52 | let mut state = self.todo.lock().await;
53 |
54 | // Serialize out the existing JSON for diffing later on
55 | let existing_json = serde_json::to_value(&*state)?;
56 |
57 | // Apply the action to our todo list. This mutates it in place
58 | state.apply(action);
59 |
60 | // Serialize out the new JSON for diffing
61 | let new_json = serde_json::to_value(&*state)?;
62 |
63 | // Get the changes using the `diff` method from `json_patch`
64 | let ops = diff(&existing_json, &new_json).0;
65 |
66 | debug!("New Patches:{:?}", ops);
67 |
68 | // If there are no changes, don't bother broadcasting
69 | if !ops.is_empty() {
70 | let message = serde_json::to_string(&ServerMessage::Patch { ops })?;
71 |
72 | let mut txs = self.txs.lock().await;
73 |
74 | // We take all the txs to iterate, and replace with an empty `Vec`
75 | for mut tx in mem::take(&mut *txs) {
76 | // If there is an issue sending a message we will warn about it
77 | if let Err(err) = tx.send(Message::Text(message.clone())).await {
78 | warn!("Client disconnected: {}", err);
79 | // If there is no issue sending, then we add it back to our `Vec`
80 | } else {
81 | txs.push(tx)
82 | }
83 | }
84 | }
85 |
86 | Ok(())
87 | }
88 | }
89 |
90 | #[derive(Serialize, Debug)]
91 | #[serde(tag = "type")]
92 | enum ServerMessage<'a> {
93 | Patch { ops: Vec },
94 | Full { todo: &'a Todo },
95 | }
96 |
97 | pub async fn handle_socket(socket: WebSocket, state: Arc) {
98 | let (tx, mut rx) = socket.split();
99 |
100 | // Add tx to our list of sessions for broadcasting later
101 | state.add_session(tx).await;
102 |
103 | // Loop until there are no messages or an error
104 | while let Some(Ok(msg)) = rx.next().await {
105 | if let Message::Text(text) = msg {
106 | // Decode our message and warn if it's something we don't know about
107 | if let Ok(action) = serde_json::from_str::(&text) {
108 | // Apply the state, which will broadcast out changes as a JSON patch
109 | if let Err(err) = state.apply(action).await {
110 | warn!("Error applying state:{}", err);
111 | }
112 | } else {
113 | warn!("Unknown action received:{}", text);
114 | }
115 | }
116 | }
117 | }
118 |
--------------------------------------------------------------------------------