darabos commited on
Commit
a180fd2
·
1 Parent(s): 29d6ac1

Start switching to CRDT. The env setting works!!!

Browse files
requirements.txt CHANGED
@@ -5,6 +5,8 @@ numpy
5
  pandas
6
  scipy
7
  uvicorn[standard]
 
 
8
  # For llm_ops
9
  chromadb
10
  Jinja2
 
5
  pandas
6
  scipy
7
  uvicorn[standard]
8
+ pycrdt
9
+ pycrdt-websocket
10
  # For llm_ops
11
  chromadb
12
  Jinja2
server/crdt.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ '''CRDT is used to synchronize workspace state for backend and frontend(s).'''
2
+ import contextlib
3
+ import fastapi
4
+ import os.path
5
+ import pycrdt
6
+ import pycrdt_websocket
7
+
8
+ import pycrdt_websocket.ystore
9
+
10
+ router = fastapi.APIRouter()
11
+
12
+ def ws_exception_handler(exception, log):
13
+ log.exception(exception)
14
+ return True
15
+
16
+ class WebsocketServer(pycrdt_websocket.WebsocketServer):
17
+ async def init_room(self, name):
18
+ ystore = pycrdt_websocket.ystore.FileYStore(f'crdt_data/{name}.crdt')
19
+ ydoc = pycrdt.Doc()
20
+ ydoc['workspace'] = ws = pycrdt.Map()
21
+ ws['nodes'] = []
22
+ ws['edges'] = []
23
+ ws['env'] = 'unset'
24
+ # Replay updates from the store.
25
+ try:
26
+ for update, timestamp in [(item[0], item[-1]) async for item in ystore.read()]:
27
+ ydoc.apply_update(update)
28
+ except pycrdt_websocket.ystore.YDocNotFound:
29
+ pass
30
+ print('init_room', name, ws)
31
+ _subscription_id = ws.observe_deep(handle_deep_changes)
32
+ return pycrdt_websocket.YRoom(ystore=ystore, ydoc=ydoc)
33
+
34
+ async def get_room(self, name: str) -> pycrdt_websocket.YRoom:
35
+ print('get_room', name, self.rooms)
36
+ if name not in self.rooms:
37
+ self.rooms[name] = await self.init_room(name)
38
+ print('get_room2', name, self.rooms)
39
+ room = self.rooms[name]
40
+ await self.start_room(room)
41
+ return room
42
+
43
+ print('new WebsocketServer')
44
+ websocket_server = WebsocketServer(exception_handler=ws_exception_handler, auto_clean_rooms=False)
45
+ asgi_server = pycrdt_websocket.ASGIServer(websocket_server)
46
+
47
+ def handle_deep_changes(events):
48
+ print('events', events)
49
+
50
+ @contextlib.asynccontextmanager
51
+ async def lifespan(app):
52
+ async with websocket_server:
53
+ yield
54
+
55
+ def sanitize_path(path):
56
+ return os.path.relpath(os.path.normpath(os.path.join("/", path)), "/")
57
+
58
+ @router.websocket("/ws/crdt/{room_name}")
59
+ async def crdt_websocket(websocket: fastapi.WebSocket, room_name: str):
60
+ room_name = sanitize_path(room_name)
61
+ print('room_name', room_name)
62
+ await asgi_server({'path': room_name}, websocket._receive, websocket._send)
server/main.py CHANGED
@@ -2,6 +2,7 @@ import dataclasses
2
  import fastapi
3
  import pathlib
4
  import pkgutil
 
5
  from . import ops
6
  from . import workspace
7
 
@@ -11,8 +12,8 @@ for _, name, _ in pkgutil.iter_modules([str(here)]):
11
  print(f'Importing {name}')
12
  __import__(f'server.{name}')
13
 
14
- app = fastapi.FastAPI()
15
-
16
 
17
  @app.get("/api/catalog")
18
  def get_catalog():
 
2
  import fastapi
3
  import pathlib
4
  import pkgutil
5
+ from . import crdt
6
  from . import ops
7
  from . import workspace
8
 
 
12
  print(f'Importing {name}')
13
  __import__(f'server.{name}')
14
 
15
+ app = fastapi.FastAPI(lifespan=crdt.lifespan)
16
+ app.include_router(crdt.router)
17
 
18
  @app.get("/api/catalog")
19
  def get_catalog():
web/package-lock.json CHANGED
@@ -21,6 +21,8 @@
21
  },
22
  "devDependencies": {
23
  "@sveltejs/vite-plugin-svelte": "^3.0.2",
 
 
24
  "@tsconfig/svelte": "^5.0.4",
25
  "sass": "^1.77.2",
26
  "svelte": "^4.2.12",
@@ -28,7 +30,8 @@
28
  "tslib": "^2.6.2",
29
  "typescript": "^5.4.4",
30
  "unplugin-icons": "^0.18.5",
31
- "vite": "^5.2.8"
 
32
  }
33
  },
34
  "node_modules/@ampproject/remapping": {
@@ -575,6 +578,12 @@
575
  "url": "https://opencollective.com/popperjs"
576
  }
577
  },
 
 
 
 
 
 
578
  "node_modules/@rollup/rollup-android-arm-eabi": {
579
  "version": "4.18.0",
580
  "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz",
@@ -843,6 +852,39 @@
843
  }
844
  }
845
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
846
  "node_modules/@tsconfig/svelte": {
847
  "version": "5.0.4",
848
  "resolved": "https://registry.npmjs.org/@tsconfig/svelte/-/svelte-5.0.4.tgz",
@@ -1081,6 +1123,17 @@
1081
  "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz",
1082
  "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg=="
1083
  },
 
 
 
 
 
 
 
 
 
 
 
1084
  "node_modules/@types/pug": {
1085
  "version": "2.0.10",
1086
  "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz",
@@ -1114,6 +1167,23 @@
1114
  "d3-zoom": "^3.0.0"
1115
  }
1116
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1117
  "node_modules/acorn": {
1118
  "version": "8.11.3",
1119
  "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
@@ -1146,6 +1216,13 @@
1146
  "dequal": "^2.0.3"
1147
  }
1148
  },
 
 
 
 
 
 
 
1149
  "node_modules/axobject-query": {
1150
  "version": "4.0.0",
1151
  "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz",
@@ -1159,6 +1236,27 @@
1159
  "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
1160
  "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
1161
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1162
  "node_modules/binary-extensions": {
1163
  "version": "2.3.0",
1164
  "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
@@ -1210,6 +1308,31 @@
1210
  "node": ">=8"
1211
  }
1212
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1213
  "node_modules/buffer-crc32": {
1214
  "version": "0.2.13",
1215
  "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
@@ -1438,6 +1561,20 @@
1438
  "node": ">=0.10.0"
1439
  }
1440
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1441
  "node_modules/dequal": {
1442
  "version": "2.0.3",
1443
  "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
@@ -1468,6 +1605,35 @@
1468
  "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
1469
  "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
1470
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1471
  "node_modules/es6-promise": {
1472
  "version": "3.3.1",
1473
  "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
@@ -1680,6 +1846,34 @@
1680
  "node": ">=10.17.0"
1681
  }
1682
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1683
  "node_modules/immutable": {
1684
  "version": "4.3.6",
1685
  "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz",
@@ -1785,6 +1979,16 @@
1785
  "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
1786
  "dev": true
1787
  },
 
 
 
 
 
 
 
 
 
 
1788
  "node_modules/kleur": {
1789
  "version": "4.1.5",
1790
  "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
@@ -1800,6 +2004,170 @@
1800
  "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==",
1801
  "dev": true
1802
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1803
  "node_modules/local-pkg": {
1804
  "version": "0.5.0",
1805
  "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz",
@@ -1836,6 +2204,19 @@
1836
  "url": "https://github.com/sponsors/sindresorhus"
1837
  }
1838
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
1839
  "node_modules/magic-string": {
1840
  "version": "0.30.10",
1841
  "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz",
@@ -1969,6 +2350,25 @@
1969
  "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
1970
  }
1971
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1972
  "node_modules/normalize-path": {
1973
  "version": "3.0.0",
1974
  "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -2154,6 +2554,13 @@
2154
  "node": "^10 || ^12 || >=14"
2155
  }
2156
  },
 
 
 
 
 
 
 
2157
  "node_modules/queue-microtask": {
2158
  "version": "1.2.3",
2159
  "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -2174,6 +2581,21 @@
2174
  }
2175
  ]
2176
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2177
  "node_modules/readdirp": {
2178
  "version": "3.6.0",
2179
  "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@@ -2287,6 +2709,27 @@
2287
  "node": ">=6"
2288
  }
2289
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2290
  "node_modules/sander": {
2291
  "version": "0.5.1",
2292
  "resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz",
@@ -2364,6 +2807,16 @@
2364
  "node": ">=0.10.0"
2365
  }
2366
  },
 
 
 
 
 
 
 
 
 
 
2367
  "node_modules/string-argv": {
2368
  "version": "0.3.2",
2369
  "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
@@ -2572,6 +3025,14 @@
2572
  "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==",
2573
  "dev": true
2574
  },
 
 
 
 
 
 
 
 
2575
  "node_modules/unplugin": {
2576
  "version": "1.10.1",
2577
  "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.10.1.tgz",
@@ -2629,6 +3090,13 @@
2629
  }
2630
  }
2631
  },
 
 
 
 
 
 
 
2632
  "node_modules/vite": {
2633
  "version": "5.2.11",
2634
  "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz",
@@ -2733,6 +3201,112 @@
2733
  "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
2734
  "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
2735
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2736
  "node_modules/yocto-queue": {
2737
  "version": "0.1.0",
2738
  "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
 
21
  },
22
  "devDependencies": {
23
  "@sveltejs/vite-plugin-svelte": "^3.0.2",
24
+ "@syncedstore/core": "^0.6.0",
25
+ "@syncedstore/svelte": "^0.6.0",
26
  "@tsconfig/svelte": "^5.0.4",
27
  "sass": "^1.77.2",
28
  "svelte": "^4.2.12",
 
30
  "tslib": "^2.6.2",
31
  "typescript": "^5.4.4",
32
  "unplugin-icons": "^0.18.5",
33
+ "vite": "^5.2.8",
34
+ "y-websocket": "^2.0.4"
35
  }
36
  },
37
  "node_modules/@ampproject/remapping": {
 
578
  "url": "https://opencollective.com/popperjs"
579
  }
580
  },
581
+ "node_modules/@reactivedata/reactive": {
582
+ "version": "0.2.2",
583
+ "resolved": "https://registry.npmjs.org/@reactivedata/reactive/-/reactive-0.2.2.tgz",
584
+ "integrity": "sha512-KnINM/Sng25QAv6sHkJO9q/XyslLegCF5jTsTSVu+AouY3uZDVf4Am99xNCqsfqFZFvnTBBDvCsHNdvTVGvPEA==",
585
+ "dev": true
586
+ },
587
  "node_modules/@rollup/rollup-android-arm-eabi": {
588
  "version": "4.18.0",
589
  "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz",
 
852
  }
853
  }
854
  },
855
+ "node_modules/@syncedstore/core": {
856
+ "version": "0.6.0",
857
+ "resolved": "https://registry.npmjs.org/@syncedstore/core/-/core-0.6.0.tgz",
858
+ "integrity": "sha512-6TtjEoYJsceYi8u1oRecXwbbLmjHaU0S7HvVfOaEdDfphZLGm/faVuA2fpazqc28F0yIFGvYzvPEBUJn9vqRNw==",
859
+ "dev": true,
860
+ "dependencies": {
861
+ "@reactivedata/reactive": "^0.2.0",
862
+ "@syncedstore/yjs-reactive-bindings": "^0.6.0"
863
+ },
864
+ "peerDependencies": {
865
+ "yjs": "^13.5.13"
866
+ }
867
+ },
868
+ "node_modules/@syncedstore/svelte": {
869
+ "version": "0.6.0",
870
+ "resolved": "https://registry.npmjs.org/@syncedstore/svelte/-/svelte-0.6.0.tgz",
871
+ "integrity": "sha512-MZcA1v4Qcwkix4P3/aax8azXFV1bqwyNnUZOT3XFCgpGgbRTq0NqNfKVDbDFRerpkZdrYqyWYxpRus2KtqPSjQ==",
872
+ "dev": true,
873
+ "peerDependencies": {
874
+ "@reactivedata/reactive": "*",
875
+ "@syncedstore/core": "*",
876
+ "svelte": "*"
877
+ }
878
+ },
879
+ "node_modules/@syncedstore/yjs-reactive-bindings": {
880
+ "version": "0.6.0",
881
+ "resolved": "https://registry.npmjs.org/@syncedstore/yjs-reactive-bindings/-/yjs-reactive-bindings-0.6.0.tgz",
882
+ "integrity": "sha512-VF78h0J4iOt79YU9d6j5E6bFKu7WXYuiI2ue9ZnA+T4SNVn8viRvg0AHm3NqHzudZZUgYT3dpnbv1/ZmU7yPZQ==",
883
+ "dev": true,
884
+ "peerDependencies": {
885
+ "yjs": "^13.5.13"
886
+ }
887
+ },
888
  "node_modules/@tsconfig/svelte": {
889
  "version": "5.0.4",
890
  "resolved": "https://registry.npmjs.org/@tsconfig/svelte/-/svelte-5.0.4.tgz",
 
1123
  "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz",
1124
  "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg=="
1125
  },
1126
+ "node_modules/@types/node": {
1127
+ "version": "22.5.5",
1128
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.5.tgz",
1129
+ "integrity": "sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==",
1130
+ "dev": true,
1131
+ "optional": true,
1132
+ "peer": true,
1133
+ "dependencies": {
1134
+ "undici-types": "~6.19.2"
1135
+ }
1136
+ },
1137
  "node_modules/@types/pug": {
1138
  "version": "2.0.10",
1139
  "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz",
 
1167
  "d3-zoom": "^3.0.0"
1168
  }
1169
  },
1170
+ "node_modules/abstract-leveldown": {
1171
+ "version": "6.2.3",
1172
+ "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz",
1173
+ "integrity": "sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ==",
1174
+ "dev": true,
1175
+ "optional": true,
1176
+ "dependencies": {
1177
+ "buffer": "^5.5.0",
1178
+ "immediate": "^3.2.3",
1179
+ "level-concat-iterator": "~2.0.0",
1180
+ "level-supports": "~1.0.0",
1181
+ "xtend": "~4.0.0"
1182
+ },
1183
+ "engines": {
1184
+ "node": ">=6"
1185
+ }
1186
+ },
1187
  "node_modules/acorn": {
1188
  "version": "8.11.3",
1189
  "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
 
1216
  "dequal": "^2.0.3"
1217
  }
1218
  },
1219
+ "node_modules/async-limiter": {
1220
+ "version": "1.0.1",
1221
+ "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
1222
+ "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
1223
+ "dev": true,
1224
+ "optional": true
1225
+ },
1226
  "node_modules/axobject-query": {
1227
  "version": "4.0.0",
1228
  "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz",
 
1236
  "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
1237
  "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
1238
  },
1239
+ "node_modules/base64-js": {
1240
+ "version": "1.5.1",
1241
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
1242
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
1243
+ "dev": true,
1244
+ "funding": [
1245
+ {
1246
+ "type": "github",
1247
+ "url": "https://github.com/sponsors/feross"
1248
+ },
1249
+ {
1250
+ "type": "patreon",
1251
+ "url": "https://www.patreon.com/feross"
1252
+ },
1253
+ {
1254
+ "type": "consulting",
1255
+ "url": "https://feross.org/support"
1256
+ }
1257
+ ],
1258
+ "optional": true
1259
+ },
1260
  "node_modules/binary-extensions": {
1261
  "version": "2.3.0",
1262
  "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
 
1308
  "node": ">=8"
1309
  }
1310
  },
1311
+ "node_modules/buffer": {
1312
+ "version": "5.7.1",
1313
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
1314
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
1315
+ "dev": true,
1316
+ "funding": [
1317
+ {
1318
+ "type": "github",
1319
+ "url": "https://github.com/sponsors/feross"
1320
+ },
1321
+ {
1322
+ "type": "patreon",
1323
+ "url": "https://www.patreon.com/feross"
1324
+ },
1325
+ {
1326
+ "type": "consulting",
1327
+ "url": "https://feross.org/support"
1328
+ }
1329
+ ],
1330
+ "optional": true,
1331
+ "dependencies": {
1332
+ "base64-js": "^1.3.1",
1333
+ "ieee754": "^1.1.13"
1334
+ }
1335
+ },
1336
  "node_modules/buffer-crc32": {
1337
  "version": "0.2.13",
1338
  "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
 
1561
  "node": ">=0.10.0"
1562
  }
1563
  },
1564
+ "node_modules/deferred-leveldown": {
1565
+ "version": "5.3.0",
1566
+ "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz",
1567
+ "integrity": "sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw==",
1568
+ "dev": true,
1569
+ "optional": true,
1570
+ "dependencies": {
1571
+ "abstract-leveldown": "~6.2.1",
1572
+ "inherits": "^2.0.3"
1573
+ },
1574
+ "engines": {
1575
+ "node": ">=6"
1576
+ }
1577
+ },
1578
  "node_modules/dequal": {
1579
  "version": "2.0.3",
1580
  "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
 
1605
  "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
1606
  "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
1607
  },
1608
+ "node_modules/encoding-down": {
1609
+ "version": "6.3.0",
1610
+ "resolved": "https://registry.npmjs.org/encoding-down/-/encoding-down-6.3.0.tgz",
1611
+ "integrity": "sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw==",
1612
+ "dev": true,
1613
+ "optional": true,
1614
+ "dependencies": {
1615
+ "abstract-leveldown": "^6.2.1",
1616
+ "inherits": "^2.0.3",
1617
+ "level-codec": "^9.0.0",
1618
+ "level-errors": "^2.0.0"
1619
+ },
1620
+ "engines": {
1621
+ "node": ">=6"
1622
+ }
1623
+ },
1624
+ "node_modules/errno": {
1625
+ "version": "0.1.8",
1626
+ "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
1627
+ "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
1628
+ "dev": true,
1629
+ "optional": true,
1630
+ "dependencies": {
1631
+ "prr": "~1.0.1"
1632
+ },
1633
+ "bin": {
1634
+ "errno": "cli.js"
1635
+ }
1636
+ },
1637
  "node_modules/es6-promise": {
1638
  "version": "3.3.1",
1639
  "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
 
1846
  "node": ">=10.17.0"
1847
  }
1848
  },
1849
+ "node_modules/ieee754": {
1850
+ "version": "1.2.1",
1851
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
1852
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
1853
+ "dev": true,
1854
+ "funding": [
1855
+ {
1856
+ "type": "github",
1857
+ "url": "https://github.com/sponsors/feross"
1858
+ },
1859
+ {
1860
+ "type": "patreon",
1861
+ "url": "https://www.patreon.com/feross"
1862
+ },
1863
+ {
1864
+ "type": "consulting",
1865
+ "url": "https://feross.org/support"
1866
+ }
1867
+ ],
1868
+ "optional": true
1869
+ },
1870
+ "node_modules/immediate": {
1871
+ "version": "3.3.0",
1872
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz",
1873
+ "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==",
1874
+ "dev": true,
1875
+ "optional": true
1876
+ },
1877
  "node_modules/immutable": {
1878
  "version": "4.3.6",
1879
  "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz",
 
1979
  "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
1980
  "dev": true
1981
  },
1982
+ "node_modules/isomorphic.js": {
1983
+ "version": "0.2.5",
1984
+ "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz",
1985
+ "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==",
1986
+ "dev": true,
1987
+ "funding": {
1988
+ "type": "GitHub Sponsors ❤",
1989
+ "url": "https://github.com/sponsors/dmonad"
1990
+ }
1991
+ },
1992
  "node_modules/kleur": {
1993
  "version": "4.1.5",
1994
  "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
 
2004
  "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==",
2005
  "dev": true
2006
  },
2007
+ "node_modules/level": {
2008
+ "version": "6.0.1",
2009
+ "resolved": "https://registry.npmjs.org/level/-/level-6.0.1.tgz",
2010
+ "integrity": "sha512-psRSqJZCsC/irNhfHzrVZbmPYXDcEYhA5TVNwr+V92jF44rbf86hqGp8fiT702FyiArScYIlPSBTDUASCVNSpw==",
2011
+ "dev": true,
2012
+ "optional": true,
2013
+ "dependencies": {
2014
+ "level-js": "^5.0.0",
2015
+ "level-packager": "^5.1.0",
2016
+ "leveldown": "^5.4.0"
2017
+ },
2018
+ "engines": {
2019
+ "node": ">=8.6.0"
2020
+ },
2021
+ "funding": {
2022
+ "type": "opencollective",
2023
+ "url": "https://opencollective.com/level"
2024
+ }
2025
+ },
2026
+ "node_modules/level-codec": {
2027
+ "version": "9.0.2",
2028
+ "resolved": "https://registry.npmjs.org/level-codec/-/level-codec-9.0.2.tgz",
2029
+ "integrity": "sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ==",
2030
+ "dev": true,
2031
+ "optional": true,
2032
+ "dependencies": {
2033
+ "buffer": "^5.6.0"
2034
+ },
2035
+ "engines": {
2036
+ "node": ">=6"
2037
+ }
2038
+ },
2039
+ "node_modules/level-concat-iterator": {
2040
+ "version": "2.0.1",
2041
+ "resolved": "https://registry.npmjs.org/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz",
2042
+ "integrity": "sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw==",
2043
+ "dev": true,
2044
+ "optional": true,
2045
+ "engines": {
2046
+ "node": ">=6"
2047
+ }
2048
+ },
2049
+ "node_modules/level-errors": {
2050
+ "version": "2.0.1",
2051
+ "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-2.0.1.tgz",
2052
+ "integrity": "sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw==",
2053
+ "dev": true,
2054
+ "optional": true,
2055
+ "dependencies": {
2056
+ "errno": "~0.1.1"
2057
+ },
2058
+ "engines": {
2059
+ "node": ">=6"
2060
+ }
2061
+ },
2062
+ "node_modules/level-iterator-stream": {
2063
+ "version": "4.0.2",
2064
+ "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz",
2065
+ "integrity": "sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q==",
2066
+ "dev": true,
2067
+ "optional": true,
2068
+ "dependencies": {
2069
+ "inherits": "^2.0.4",
2070
+ "readable-stream": "^3.4.0",
2071
+ "xtend": "^4.0.2"
2072
+ },
2073
+ "engines": {
2074
+ "node": ">=6"
2075
+ }
2076
+ },
2077
+ "node_modules/level-js": {
2078
+ "version": "5.0.2",
2079
+ "resolved": "https://registry.npmjs.org/level-js/-/level-js-5.0.2.tgz",
2080
+ "integrity": "sha512-SnBIDo2pdO5VXh02ZmtAyPP6/+6YTJg2ibLtl9C34pWvmtMEmRTWpra+qO/hifkUtBTOtfx6S9vLDjBsBK4gRg==",
2081
+ "dev": true,
2082
+ "optional": true,
2083
+ "dependencies": {
2084
+ "abstract-leveldown": "~6.2.3",
2085
+ "buffer": "^5.5.0",
2086
+ "inherits": "^2.0.3",
2087
+ "ltgt": "^2.1.2"
2088
+ }
2089
+ },
2090
+ "node_modules/level-packager": {
2091
+ "version": "5.1.1",
2092
+ "resolved": "https://registry.npmjs.org/level-packager/-/level-packager-5.1.1.tgz",
2093
+ "integrity": "sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ==",
2094
+ "dev": true,
2095
+ "optional": true,
2096
+ "dependencies": {
2097
+ "encoding-down": "^6.3.0",
2098
+ "levelup": "^4.3.2"
2099
+ },
2100
+ "engines": {
2101
+ "node": ">=6"
2102
+ }
2103
+ },
2104
+ "node_modules/level-supports": {
2105
+ "version": "1.0.1",
2106
+ "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-1.0.1.tgz",
2107
+ "integrity": "sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg==",
2108
+ "dev": true,
2109
+ "optional": true,
2110
+ "dependencies": {
2111
+ "xtend": "^4.0.2"
2112
+ },
2113
+ "engines": {
2114
+ "node": ">=6"
2115
+ }
2116
+ },
2117
+ "node_modules/leveldown": {
2118
+ "version": "5.6.0",
2119
+ "resolved": "https://registry.npmjs.org/leveldown/-/leveldown-5.6.0.tgz",
2120
+ "integrity": "sha512-iB8O/7Db9lPaITU1aA2txU/cBEXAt4vWwKQRrrWuS6XDgbP4QZGj9BL2aNbwb002atoQ/lIotJkfyzz+ygQnUQ==",
2121
+ "dev": true,
2122
+ "hasInstallScript": true,
2123
+ "optional": true,
2124
+ "dependencies": {
2125
+ "abstract-leveldown": "~6.2.1",
2126
+ "napi-macros": "~2.0.0",
2127
+ "node-gyp-build": "~4.1.0"
2128
+ },
2129
+ "engines": {
2130
+ "node": ">=8.6.0"
2131
+ }
2132
+ },
2133
+ "node_modules/levelup": {
2134
+ "version": "4.4.0",
2135
+ "resolved": "https://registry.npmjs.org/levelup/-/levelup-4.4.0.tgz",
2136
+ "integrity": "sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ==",
2137
+ "dev": true,
2138
+ "optional": true,
2139
+ "dependencies": {
2140
+ "deferred-leveldown": "~5.3.0",
2141
+ "level-errors": "~2.0.0",
2142
+ "level-iterator-stream": "~4.0.0",
2143
+ "level-supports": "~1.0.0",
2144
+ "xtend": "~4.0.0"
2145
+ },
2146
+ "engines": {
2147
+ "node": ">=6"
2148
+ }
2149
+ },
2150
+ "node_modules/lib0": {
2151
+ "version": "0.2.98",
2152
+ "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.98.tgz",
2153
+ "integrity": "sha512-XteTiNO0qEXqqweWx+b21p/fBnNHUA1NwAtJNJek1oPrewEZs2uiT4gWivHKr9GqCjDPAhchz0UQO8NwU3bBNA==",
2154
+ "dev": true,
2155
+ "dependencies": {
2156
+ "isomorphic.js": "^0.2.4"
2157
+ },
2158
+ "bin": {
2159
+ "0ecdsa-generate-keypair": "bin/0ecdsa-generate-keypair.js",
2160
+ "0gentesthtml": "bin/gentesthtml.js",
2161
+ "0serve": "bin/0serve.js"
2162
+ },
2163
+ "engines": {
2164
+ "node": ">=16"
2165
+ },
2166
+ "funding": {
2167
+ "type": "GitHub Sponsors ❤",
2168
+ "url": "https://github.com/sponsors/dmonad"
2169
+ }
2170
+ },
2171
  "node_modules/local-pkg": {
2172
  "version": "0.5.0",
2173
  "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz",
 
2204
  "url": "https://github.com/sponsors/sindresorhus"
2205
  }
2206
  },
2207
+ "node_modules/lodash.debounce": {
2208
+ "version": "4.0.8",
2209
+ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
2210
+ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
2211
+ "dev": true
2212
+ },
2213
+ "node_modules/ltgt": {
2214
+ "version": "2.2.1",
2215
+ "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz",
2216
+ "integrity": "sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA==",
2217
+ "dev": true,
2218
+ "optional": true
2219
+ },
2220
  "node_modules/magic-string": {
2221
  "version": "0.30.10",
2222
  "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz",
 
2350
  "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
2351
  }
2352
  },
2353
+ "node_modules/napi-macros": {
2354
+ "version": "2.0.0",
2355
+ "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz",
2356
+ "integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==",
2357
+ "dev": true,
2358
+ "optional": true
2359
+ },
2360
+ "node_modules/node-gyp-build": {
2361
+ "version": "4.1.1",
2362
+ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.1.1.tgz",
2363
+ "integrity": "sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ==",
2364
+ "dev": true,
2365
+ "optional": true,
2366
+ "bin": {
2367
+ "node-gyp-build": "bin.js",
2368
+ "node-gyp-build-optional": "optional.js",
2369
+ "node-gyp-build-test": "build-test.js"
2370
+ }
2371
+ },
2372
  "node_modules/normalize-path": {
2373
  "version": "3.0.0",
2374
  "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
 
2554
  "node": "^10 || ^12 || >=14"
2555
  }
2556
  },
2557
+ "node_modules/prr": {
2558
+ "version": "1.0.1",
2559
+ "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
2560
+ "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
2561
+ "dev": true,
2562
+ "optional": true
2563
+ },
2564
  "node_modules/queue-microtask": {
2565
  "version": "1.2.3",
2566
  "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
 
2581
  }
2582
  ]
2583
  },
2584
+ "node_modules/readable-stream": {
2585
+ "version": "3.6.2",
2586
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
2587
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
2588
+ "dev": true,
2589
+ "optional": true,
2590
+ "dependencies": {
2591
+ "inherits": "^2.0.3",
2592
+ "string_decoder": "^1.1.1",
2593
+ "util-deprecate": "^1.0.1"
2594
+ },
2595
+ "engines": {
2596
+ "node": ">= 6"
2597
+ }
2598
+ },
2599
  "node_modules/readdirp": {
2600
  "version": "3.6.0",
2601
  "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
 
2709
  "node": ">=6"
2710
  }
2711
  },
2712
+ "node_modules/safe-buffer": {
2713
+ "version": "5.2.1",
2714
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
2715
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
2716
+ "dev": true,
2717
+ "funding": [
2718
+ {
2719
+ "type": "github",
2720
+ "url": "https://github.com/sponsors/feross"
2721
+ },
2722
+ {
2723
+ "type": "patreon",
2724
+ "url": "https://www.patreon.com/feross"
2725
+ },
2726
+ {
2727
+ "type": "consulting",
2728
+ "url": "https://feross.org/support"
2729
+ }
2730
+ ],
2731
+ "optional": true
2732
+ },
2733
  "node_modules/sander": {
2734
  "version": "0.5.1",
2735
  "resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz",
 
2807
  "node": ">=0.10.0"
2808
  }
2809
  },
2810
+ "node_modules/string_decoder": {
2811
+ "version": "1.3.0",
2812
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
2813
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
2814
+ "dev": true,
2815
+ "optional": true,
2816
+ "dependencies": {
2817
+ "safe-buffer": "~5.2.0"
2818
+ }
2819
+ },
2820
  "node_modules/string-argv": {
2821
  "version": "0.3.2",
2822
  "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
 
3025
  "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==",
3026
  "dev": true
3027
  },
3028
+ "node_modules/undici-types": {
3029
+ "version": "6.19.8",
3030
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
3031
+ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
3032
+ "dev": true,
3033
+ "optional": true,
3034
+ "peer": true
3035
+ },
3036
  "node_modules/unplugin": {
3037
  "version": "1.10.1",
3038
  "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.10.1.tgz",
 
3090
  }
3091
  }
3092
  },
3093
+ "node_modules/util-deprecate": {
3094
+ "version": "1.0.2",
3095
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
3096
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
3097
+ "dev": true,
3098
+ "optional": true
3099
+ },
3100
  "node_modules/vite": {
3101
  "version": "5.2.11",
3102
  "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz",
 
3201
  "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
3202
  "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
3203
  },
3204
+ "node_modules/ws": {
3205
+ "version": "6.2.3",
3206
+ "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz",
3207
+ "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==",
3208
+ "dev": true,
3209
+ "optional": true,
3210
+ "dependencies": {
3211
+ "async-limiter": "~1.0.0"
3212
+ }
3213
+ },
3214
+ "node_modules/xtend": {
3215
+ "version": "4.0.2",
3216
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
3217
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
3218
+ "dev": true,
3219
+ "optional": true,
3220
+ "engines": {
3221
+ "node": ">=0.4"
3222
+ }
3223
+ },
3224
+ "node_modules/y-leveldb": {
3225
+ "version": "0.1.2",
3226
+ "resolved": "https://registry.npmjs.org/y-leveldb/-/y-leveldb-0.1.2.tgz",
3227
+ "integrity": "sha512-6ulEn5AXfXJYi89rXPEg2mMHAyyw8+ZfeMMdOtBbV8FJpQ1NOrcgi6DTAcXof0dap84NjHPT2+9d0rb6cFsjEg==",
3228
+ "dev": true,
3229
+ "optional": true,
3230
+ "dependencies": {
3231
+ "level": "^6.0.1",
3232
+ "lib0": "^0.2.31"
3233
+ },
3234
+ "funding": {
3235
+ "type": "GitHub Sponsors ❤",
3236
+ "url": "https://github.com/sponsors/dmonad"
3237
+ },
3238
+ "peerDependencies": {
3239
+ "yjs": "^13.0.0"
3240
+ }
3241
+ },
3242
+ "node_modules/y-protocols": {
3243
+ "version": "1.0.6",
3244
+ "resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.6.tgz",
3245
+ "integrity": "sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q==",
3246
+ "dev": true,
3247
+ "dependencies": {
3248
+ "lib0": "^0.2.85"
3249
+ },
3250
+ "engines": {
3251
+ "node": ">=16.0.0",
3252
+ "npm": ">=8.0.0"
3253
+ },
3254
+ "funding": {
3255
+ "type": "GitHub Sponsors ❤",
3256
+ "url": "https://github.com/sponsors/dmonad"
3257
+ },
3258
+ "peerDependencies": {
3259
+ "yjs": "^13.0.0"
3260
+ }
3261
+ },
3262
+ "node_modules/y-websocket": {
3263
+ "version": "2.0.4",
3264
+ "resolved": "https://registry.npmjs.org/y-websocket/-/y-websocket-2.0.4.tgz",
3265
+ "integrity": "sha512-UbrkOU4GPNFFTDlJYAxAmzZhia8EPxHkngZ6qjrxgIYCN3gI2l+zzLzA9p4LQJ0IswzpioeIgmzekWe7HoBBjg==",
3266
+ "dev": true,
3267
+ "dependencies": {
3268
+ "lib0": "^0.2.52",
3269
+ "lodash.debounce": "^4.0.8",
3270
+ "y-protocols": "^1.0.5"
3271
+ },
3272
+ "bin": {
3273
+ "y-websocket": "bin/server.cjs",
3274
+ "y-websocket-server": "bin/server.cjs"
3275
+ },
3276
+ "engines": {
3277
+ "node": ">=16.0.0",
3278
+ "npm": ">=8.0.0"
3279
+ },
3280
+ "funding": {
3281
+ "type": "GitHub Sponsors ❤",
3282
+ "url": "https://github.com/sponsors/dmonad"
3283
+ },
3284
+ "optionalDependencies": {
3285
+ "ws": "^6.2.1",
3286
+ "y-leveldb": "^0.1.0"
3287
+ },
3288
+ "peerDependencies": {
3289
+ "yjs": "^13.5.6"
3290
+ }
3291
+ },
3292
+ "node_modules/yjs": {
3293
+ "version": "13.6.19",
3294
+ "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.19.tgz",
3295
+ "integrity": "sha512-GNKw4mEUn5yWU2QPHRx8jppxmCm9KzbBhB4qJLUJFiiYD0g/tDVgXQ7aPkyh01YO28kbs2J/BEbWBagjuWyejw==",
3296
+ "dev": true,
3297
+ "peer": true,
3298
+ "dependencies": {
3299
+ "lib0": "^0.2.86"
3300
+ },
3301
+ "engines": {
3302
+ "node": ">=16.0.0",
3303
+ "npm": ">=8.0.0"
3304
+ },
3305
+ "funding": {
3306
+ "type": "GitHub Sponsors ❤",
3307
+ "url": "https://github.com/sponsors/dmonad"
3308
+ }
3309
+ },
3310
  "node_modules/yocto-queue": {
3311
  "version": "0.1.0",
3312
  "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
web/package.json CHANGED
@@ -11,6 +11,8 @@
11
  },
12
  "devDependencies": {
13
  "@sveltejs/vite-plugin-svelte": "^3.0.2",
 
 
14
  "@tsconfig/svelte": "^5.0.4",
15
  "sass": "^1.77.2",
16
  "svelte": "^4.2.12",
@@ -18,7 +20,8 @@
18
  "tslib": "^2.6.2",
19
  "typescript": "^5.4.4",
20
  "unplugin-icons": "^0.18.5",
21
- "vite": "^5.2.8"
 
22
  },
23
  "dependencies": {
24
  "@iconify-json/tabler": "^1.1.110",
 
11
  },
12
  "devDependencies": {
13
  "@sveltejs/vite-plugin-svelte": "^3.0.2",
14
+ "@syncedstore/core": "^0.6.0",
15
+ "@syncedstore/svelte": "^0.6.0",
16
  "@tsconfig/svelte": "^5.0.4",
17
  "sass": "^1.77.2",
18
  "svelte": "^4.2.12",
 
20
  "tslib": "^2.6.2",
21
  "typescript": "^5.4.4",
22
  "unplugin-icons": "^0.18.5",
23
+ "vite": "^5.2.8",
24
+ "y-websocket": "^2.0.4"
25
  },
26
  "dependencies": {
27
  "@iconify-json/tabler": "^1.1.110",
web/src/LynxKiteFlow.svelte CHANGED
@@ -16,7 +16,7 @@
16
  import ArrowBack from 'virtual:icons/tabler/arrow-back'
17
  import Backspace from 'virtual:icons/tabler/backspace'
18
  import Atom from 'virtual:icons/tabler/Atom'
19
- import { useQuery, useMutation, useQueryClient } from '@sveltestack/svelte-query';
20
  import NodeWithParams from './NodeWithParams.svelte';
21
  import NodeWithVisualization from './NodeWithVisualization.svelte';
22
  import NodeWithImage from './NodeWithImage.svelte';
@@ -26,27 +26,28 @@
26
  import NodeSearch from './NodeSearch.svelte';
27
  import EnvironmentSelector from './EnvironmentSelector.svelte';
28
  import '@xyflow/svelte/dist/style.css';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
  export let path = '';
31
 
32
  const { screenToFlowPosition } = useSvelteFlow();
33
- const queryClient = useQueryClient();
34
- const backendWorkspace = useQuery(['workspace', path], async () => {
35
- const res = await fetch(`/api/load?path=${path}`);
36
- return res.json();
37
- }, {staleTime: 10000, retry: false});
38
- const mutation = useMutation(async(update) => {
39
- const res = await fetch('/api/save', {
40
- method: 'POST',
41
- headers: {
42
- 'Content-Type': 'application/json',
43
- },
44
- body: JSON.stringify(update),
45
- });
46
- return await res.json();
47
- }, {
48
- onSuccess: data => queryClient.setQueryData(['workspace', path], data),
49
- });
50
 
51
  const nodeTypes: NodeTypes = {
52
  basic: NodeWithParams,
@@ -57,16 +58,6 @@
57
  area: NodeWithArea,
58
  };
59
 
60
- const nodes = writable<Node[]>([]);
61
- const edges = writable<Edge[]>([]);
62
- let doNotSave = true;
63
- $: if ($backendWorkspace.isSuccess) {
64
- doNotSave = true; // Change is coming from the backend.
65
- nodes.set(JSON.parse(JSON.stringify($backendWorkspace.data?.nodes || [])));
66
- edges.set(JSON.parse(JSON.stringify($backendWorkspace.data?.edges || [])));
67
- doNotSave = false;
68
- }
69
-
70
  function closeNodeSearch() {
71
  nodeSearchSettings = undefined;
72
  }
@@ -78,12 +69,12 @@
78
  event.preventDefault();
79
  nodeSearchSettings = {
80
  pos: { x: event.clientX, y: event.clientY },
81
- boxes: $catalog.data[$backendWorkspace.data?.env],
82
  };
83
  }
84
  function addNode(e) {
85
  const meta = {...e.detail};
86
- nodes.update((n) => {
87
  const node = {
88
  type: meta.type,
89
  data: {
@@ -97,7 +88,7 @@
97
  const title = node.data.title;
98
  let i = 1;
99
  node.id = `${title} ${i}`;
100
- while (n.find((x) => x.id === node.id)) {
101
  i += 1;
102
  node.id = `${title} ${i}`;
103
  }
@@ -107,7 +98,7 @@
107
  const parent = n.find((x) => x.id === node.parentId);
108
  node.position = { x: node.position.x - parent.position.x, y: node.position.y - parent.position.y };
109
  }
110
- return [...n, node]
111
  });
112
  closeNodeSearch();
113
  }
@@ -122,46 +113,6 @@
122
  parentId: string,
123
  };
124
 
125
- const graph = derived([nodes, edges], ([nodes, edges]) => ({ nodes, edges }));
126
- // Like JSON.stringify, but with keys sorted.
127
- function orderedJSON(obj: any) {
128
- const allKeys = new Set();
129
- JSON.stringify(obj, (key, value) => (allKeys.add(key), value));
130
- return JSON.stringify(obj, Array.from(allKeys).sort());
131
- }
132
- graph.subscribe(async (g) => {
133
- if (doNotSave) return;
134
- const dragging = g.nodes.find((n) => n.dragging);
135
- if (dragging) return;
136
- const resizing = g.nodes.find((n) => n.data?.beingResized);
137
- if (resizing) return;
138
- scheduleSave(g);
139
- });
140
- let saveTimeout;
141
- function scheduleSave(g) {
142
- // A slight delay, so we don't send a million requests when a node is resized, for example.
143
- clearTimeout(saveTimeout);
144
- saveTimeout = setTimeout(() => save(g), 500);
145
- }
146
- function save(g) {
147
- g = JSON.parse(JSON.stringify(g));
148
- for (const node of g.nodes) {
149
- delete node.measured;
150
- delete node.selected;
151
- delete node.dragging;
152
- delete node.beingResized;
153
- }
154
- for (const node of g.edges) {
155
- delete node.markerEnd;
156
- delete node.selected;
157
- }
158
- g.env = $backendWorkspace.data?.env;
159
- const ws = orderedJSON(g);
160
- const bd = orderedJSON($backendWorkspace.data);
161
- if (ws === bd) return;
162
- console.log('changed', JSON.stringify(diff(g, $backendWorkspace.data), null, 2));
163
- $mutation.mutate({ path, ws: g });
164
- }
165
  function nodeClick(e) {
166
  const node = e.detail.node;
167
  const meta = node.data.meta;
@@ -181,6 +132,11 @@
181
  </script>
182
 
183
  <div class="page">
 
 
 
 
 
184
  <div class="top-bar">
185
  <div class="ws-name">
186
  <a href><img src="/favicon.ico"></a>
@@ -189,8 +145,11 @@
189
  <div class="tools">
190
  <EnvironmentSelector
191
  options={Object.keys($catalog.data || {})}
192
- value={$backendWorkspace.data?.env}
193
- onChange={(env) => $mutation.mutate({ path, ws: { ...$backendWorkspace.data, env } })}
 
 
 
194
  />
195
  <a href><Atom /></a>
196
  <a href><Backspace /></a>
@@ -198,7 +157,7 @@
198
  </div>
199
  </div>
200
  <div style:height="100%">
201
- <SvelteFlow {nodes} {edges} {nodeTypes} fitView
202
  on:paneclick={toggleNodeSearch}
203
  on:nodeclick={nodeClick}
204
  proOptions={{ hideAttribution: true }}
@@ -213,6 +172,7 @@
213
  {/if}
214
  </SvelteFlow>
215
  </div>
 
216
  </div>
217
 
218
  <style>
 
16
  import ArrowBack from 'virtual:icons/tabler/arrow-back'
17
  import Backspace from 'virtual:icons/tabler/backspace'
18
  import Atom from 'virtual:icons/tabler/Atom'
19
+ import { useQuery } from '@sveltestack/svelte-query';
20
  import NodeWithParams from './NodeWithParams.svelte';
21
  import NodeWithVisualization from './NodeWithVisualization.svelte';
22
  import NodeWithImage from './NodeWithImage.svelte';
 
26
  import NodeSearch from './NodeSearch.svelte';
27
  import EnvironmentSelector from './EnvironmentSelector.svelte';
28
  import '@xyflow/svelte/dist/style.css';
29
+ import { syncedStore, getYjsDoc } from "@syncedstore/core";
30
+ import { svelteSyncedStore } from "@syncedstore/svelte";
31
+ import { WebsocketProvider } from "y-websocket";
32
+
33
+ function getCRDTStore(path) {
34
+ const sstore = syncedStore({ workspace: {} });
35
+ console.log('ss', sstore.workspace);
36
+ const doc = getYjsDoc(sstore);
37
+ console.log('doc', doc.toJSON());
38
+ const wsProvider = new WebsocketProvider("ws://localhost:8000/ws/crdt", path, doc);
39
+ wsProvider.on('sync', function(isSynced: boolean) {
40
+ console.log('synced', isSynced, 'ydoc', doc.toJSON());
41
+ });
42
+ return {store: svelteSyncedStore(sstore), doc};
43
+ }
44
+ $: connection = getCRDTStore(path);
45
+ $: store = connection.store;
46
+ $: ws = connection.doc?.getMap('workspace');
47
 
48
  export let path = '';
49
 
50
  const { screenToFlowPosition } = useSvelteFlow();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
  const nodeTypes: NodeTypes = {
53
  basic: NodeWithParams,
 
58
  area: NodeWithArea,
59
  };
60
 
 
 
 
 
 
 
 
 
 
 
61
  function closeNodeSearch() {
62
  nodeSearchSettings = undefined;
63
  }
 
69
  event.preventDefault();
70
  nodeSearchSettings = {
71
  pos: { x: event.clientX, y: event.clientY },
72
+ boxes: $catalog.data[ws.env],
73
  };
74
  }
75
  function addNode(e) {
76
  const meta = {...e.detail};
77
+ store.update((ws) => {
78
  const node = {
79
  type: meta.type,
80
  data: {
 
88
  const title = node.data.title;
89
  let i = 1;
90
  node.id = `${title} ${i}`;
91
+ while (ws.nodes.find((x) => x.id === node.id)) {
92
  i += 1;
93
  node.id = `${title} ${i}`;
94
  }
 
98
  const parent = n.find((x) => x.id === node.parentId);
99
  node.position = { x: node.position.x - parent.position.x, y: node.position.y - parent.position.y };
100
  }
101
+ return {...ws, nodes: [...n, node]};
102
  });
103
  closeNodeSearch();
104
  }
 
113
  parentId: string,
114
  };
115
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  function nodeClick(e) {
117
  const node = e.detail.node;
118
  const meta = node.data.meta;
 
132
  </script>
133
 
134
  <div class="page">
135
+ <br>doc: {JSON.stringify(connection.doc)}
136
+ <br>w2j: {JSON.parse(JSON.stringify(connection.doc)).workspace}
137
+ <br>ws: {connection.doc?.getMap("workspace")}
138
+ {#if ws !== undefined}
139
+ {{ws}}
140
  <div class="top-bar">
141
  <div class="ws-name">
142
  <a href><img src="/favicon.ico"></a>
 
145
  <div class="tools">
146
  <EnvironmentSelector
147
  options={Object.keys($catalog.data || {})}
148
+ value={$store.workspace.env}
149
+ onChange={(env) => {
150
+ console.log('env change', env);
151
+ $store.workspace.env = env;
152
+ }}
153
  />
154
  <a href><Atom /></a>
155
  <a href><Backspace /></a>
 
157
  </div>
158
  </div>
159
  <div style:height="100%">
160
+ <SvelteFlow nodes={ws.nodes} edges={ws.edges} {nodeTypes} fitView
161
  on:paneclick={toggleNodeSearch}
162
  on:nodeclick={nodeClick}
163
  proOptions={{ hideAttribution: true }}
 
172
  {/if}
173
  </SvelteFlow>
174
  </div>
175
+ {/if}
176
  </div>
177
 
178
  <style>
web/src/Workspace.svelte CHANGED
@@ -3,6 +3,29 @@
3
  import { QueryClient, QueryClientProvider } from '@sveltestack/svelte-query'
4
  import { SvelteFlowProvider } from '@xyflow/svelte';
5
  import LynxKiteFlow from './LynxKiteFlow.svelte';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  export let path = '';
7
  const queryClient = new QueryClient()
8
  </script>
 
3
  import { QueryClient, QueryClientProvider } from '@sveltestack/svelte-query'
4
  import { SvelteFlowProvider } from '@xyflow/svelte';
5
  import LynxKiteFlow from './LynxKiteFlow.svelte';
6
+ // import { syncedStore, getYjsDoc } from "@syncedstore/core";
7
+ // import { svelteSyncedStore } from "@syncedstore/svelte";
8
+ // import { WebsocketProvider } from "y-websocket";
9
+ // const todoStore = syncedStore({ todos: [] });
10
+ // const doc = getYjsDoc(todoStore);
11
+ // const wsProvider = new WebsocketProvider("ws://localhost:8000/ws/crdt", "my-roomname3", doc);
12
+ // wsProvider.on('sync', function(isSynced: boolean) {
13
+ // console.log('synced test', isSynced, 'ydoc', doc.toJSON());
14
+ // });
15
+
16
+ // console.log('ydoc', doc.toJSON());
17
+ // console.log('todoStore', todoStore);
18
+ // todoStore.todos.push('asdfx');
19
+ // console.log('ydoc', doc.toJSON());
20
+ // // console.log(doc.toJSON());
21
+ // // console.log(doc.getMap().toJSON());
22
+ // // console.log(doc.getMap().get('todos'));
23
+ // // doc.getMap().get('todos').observe(() => {
24
+ // // console.log('todos changed', todoStore.todos);
25
+ // // });
26
+ // // doc.getMap().get('todos').add('hello');
27
+
28
+ // export const store = svelteSyncedStore(todoStore);
29
  export let path = '';
30
  const queryClient = new QueryClient()
31
  </script>
web/vite.config.ts CHANGED
@@ -9,6 +9,13 @@ export default defineConfig({
9
  Icons({ compiler: 'svelte', defaultStyle: 'vertical-align: sub;' }),
10
  ],
11
  server: {
12
- proxy: { '/api': 'http://127.0.0.1:8000' },
 
 
 
 
 
 
 
13
  },
14
  })
 
9
  Icons({ compiler: 'svelte', defaultStyle: 'vertical-align: sub;' }),
10
  ],
11
  server: {
12
+ proxy: {
13
+ '/api': 'http://127.0.0.1:8000',
14
+ '/ws': {
15
+ target: 'ws://127.0.0.1:8000',
16
+ ws: true,
17
+ changeOrigin: true,
18
+ },
19
+ },
20
  },
21
  })