avallef commited on
Commit
e3cb794
·
1 Parent(s): a74716b

Added test to most things

Browse files
server/.prettierrc CHANGED
@@ -1,5 +1,6 @@
1
  {
2
  "trailingComma": "es5",
3
  "tabWidth": 4,
4
- "singleQuote": false
 
5
  }
 
1
  {
2
  "trailingComma": "es5",
3
  "tabWidth": 4,
4
+ "singleQuote": false,
5
+ "semi": true
6
  }
server/package-lock.json CHANGED
@@ -16,8 +16,12 @@
16
  "zod": "^3.22.4"
17
  },
18
  "devDependencies": {
 
19
  "@types/express": "^4.17.21",
 
20
  "@types/node": "^20.9.1",
 
 
21
  "nodemon": "^3.0.1",
22
  "prettier": "^3.1.0",
23
  "ts-node": "^10.9.1",
@@ -95,6 +99,12 @@
95
  "@types/node": "*"
96
  }
97
  },
 
 
 
 
 
 
98
  "node_modules/@types/connect": {
99
  "version": "3.4.38",
100
  "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
@@ -140,6 +150,12 @@
140
  "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
141
  "dev": true
142
  },
 
 
 
 
 
 
143
  "node_modules/@types/node": {
144
  "version": "20.9.1",
145
  "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.1.tgz",
@@ -239,6 +255,39 @@
239
  "node": ">=0.4.0"
240
  }
241
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
  "node_modules/anymatch": {
243
  "version": "3.1.3",
244
  "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
@@ -258,11 +307,26 @@
258
  "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
259
  "dev": true
260
  },
 
 
 
 
 
 
261
  "node_modules/array-flatten": {
262
  "version": "1.1.1",
263
  "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
264
  "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
265
  },
 
 
 
 
 
 
 
 
 
266
  "node_modules/atomic-sleep": {
267
  "version": "1.0.0",
268
  "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
@@ -363,6 +427,12 @@
363
  "node": ">=8"
364
  }
365
  },
 
 
 
 
 
 
366
  "node_modules/buffer": {
367
  "version": "6.0.3",
368
  "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
@@ -407,6 +477,85 @@
407
  "url": "https://github.com/sponsors/ljharb"
408
  }
409
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
410
  "node_modules/chokidar": {
411
  "version": "3.5.3",
412
  "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
@@ -434,6 +583,35 @@
434
  "fsevents": "~2.3.2"
435
  }
436
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
437
  "node_modules/concat-map": {
438
  "version": "0.0.1",
439
  "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -487,6 +665,30 @@
487
  "ms": "^2.1.1"
488
  }
489
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
490
  "node_modules/define-data-property": {
491
  "version": "1.1.1",
492
  "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
@@ -531,6 +733,12 @@
531
  "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
532
  "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
533
  },
 
 
 
 
 
 
534
  "node_modules/encodeurl": {
535
  "version": "1.0.2",
536
  "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
@@ -539,11 +747,32 @@
539
  "node": ">= 0.8"
540
  }
541
  },
 
 
 
 
 
 
 
 
 
542
  "node_modules/escape-html": {
543
  "version": "1.0.3",
544
  "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
545
  "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
546
  },
 
 
 
 
 
 
 
 
 
 
 
 
547
  "node_modules/etag": {
548
  "version": "1.8.1",
549
  "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
@@ -672,6 +901,31 @@
672
  "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
673
  "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
674
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
675
  "node_modules/forwarded": {
676
  "version": "0.2.0",
677
  "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -688,6 +942,12 @@
688
  "node": ">= 0.6"
689
  }
690
  },
 
 
 
 
 
 
691
  "node_modules/fsevents": {
692
  "version": "2.3.3",
693
  "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -710,6 +970,24 @@
710
  "url": "https://github.com/sponsors/ljharb"
711
  }
712
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
713
  "node_modules/get-intrinsic": {
714
  "version": "1.2.2",
715
  "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
@@ -724,6 +1002,26 @@
724
  "url": "https://github.com/sponsors/ljharb"
725
  }
726
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
727
  "node_modules/glob-parent": {
728
  "version": "5.1.2",
729
  "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
@@ -800,6 +1098,15 @@
800
  "node": ">= 0.4"
801
  }
802
  },
 
 
 
 
 
 
 
 
 
803
  "node_modules/http-errors": {
804
  "version": "2.0.0",
805
  "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
@@ -851,6 +1158,16 @@
851
  "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
852
  "dev": true
853
  },
 
 
 
 
 
 
 
 
 
 
854
  "node_modules/inherits": {
855
  "version": "2.0.4",
856
  "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
@@ -885,6 +1202,15 @@
885
  "node": ">=0.10.0"
886
  }
887
  },
 
 
 
 
 
 
 
 
 
888
  "node_modules/is-glob": {
889
  "version": "4.0.3",
890
  "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -906,6 +1232,79 @@
906
  "node": ">=0.12.0"
907
  }
908
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
909
  "node_modules/lru-cache": {
910
  "version": "6.0.0",
911
  "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@@ -987,11 +1386,140 @@
987
  "node": "*"
988
  }
989
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
990
  "node_modules/ms": {
991
  "version": "2.1.3",
992
  "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
993
  "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
994
  },
 
 
 
 
 
 
 
 
 
 
 
 
995
  "node_modules/negotiator": {
996
  "version": "0.6.3",
997
  "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@@ -1079,6 +1607,45 @@
1079
  "node": ">= 0.8"
1080
  }
1081
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1082
  "node_modules/parseurl": {
1083
  "version": "1.3.3",
1084
  "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -1087,11 +1654,38 @@
1087
  "node": ">= 0.8"
1088
  }
1089
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1090
  "node_modules/path-to-regexp": {
1091
  "version": "0.1.7",
1092
  "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
1093
  "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
1094
  },
 
 
 
 
 
 
 
 
 
1095
  "node_modules/picomatch": {
1096
  "version": "2.3.1",
1097
  "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
@@ -1204,6 +1798,15 @@
1204
  "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
1205
  "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="
1206
  },
 
 
 
 
 
 
 
 
 
1207
  "node_modules/range-parser": {
1208
  "version": "1.2.1",
1209
  "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@@ -1261,6 +1864,15 @@
1261
  "node": ">= 12.13.0"
1262
  }
1263
  },
 
 
 
 
 
 
 
 
 
1264
  "node_modules/safe-buffer": {
1265
  "version": "5.2.1",
1266
  "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -1344,6 +1956,15 @@
1344
  "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1345
  "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
1346
  },
 
 
 
 
 
 
 
 
 
1347
  "node_modules/serve-static": {
1348
  "version": "1.15.0",
1349
  "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
@@ -1434,6 +2055,44 @@
1434
  "safe-buffer": "~5.2.0"
1435
  }
1436
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1437
  "node_modules/supports-color": {
1438
  "version": "5.5.0",
1439
  "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
@@ -1529,6 +2188,15 @@
1529
  }
1530
  }
1531
  },
 
 
 
 
 
 
 
 
 
1532
  "node_modules/type-is": {
1533
  "version": "1.6.18",
1534
  "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
@@ -1595,6 +2263,35 @@
1595
  "node": ">= 0.8"
1596
  }
1597
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1598
  "node_modules/ws": {
1599
  "version": "8.14.2",
1600
  "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
@@ -1615,12 +2312,63 @@
1615
  }
1616
  }
1617
  },
 
 
 
 
 
 
 
 
 
1618
  "node_modules/yallist": {
1619
  "version": "4.0.0",
1620
  "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
1621
  "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
1622
  "dev": true
1623
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1624
  "node_modules/yn": {
1625
  "version": "3.1.1",
1626
  "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
@@ -1630,6 +2378,18 @@
1630
  "node": ">=6"
1631
  }
1632
  },
 
 
 
 
 
 
 
 
 
 
 
 
1633
  "node_modules/zod": {
1634
  "version": "3.22.4",
1635
  "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz",
 
16
  "zod": "^3.22.4"
17
  },
18
  "devDependencies": {
19
+ "@types/chai": "^4.3.10",
20
  "@types/express": "^4.17.21",
21
+ "@types/mocha": "^10.0.4",
22
  "@types/node": "^20.9.1",
23
+ "chai": "^4.3.10",
24
+ "mocha": "^10.2.0",
25
  "nodemon": "^3.0.1",
26
  "prettier": "^3.1.0",
27
  "ts-node": "^10.9.1",
 
99
  "@types/node": "*"
100
  }
101
  },
102
+ "node_modules/@types/chai": {
103
+ "version": "4.3.10",
104
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.10.tgz",
105
+ "integrity": "sha512-of+ICnbqjmFCiixUnqRulbylyXQrPqIGf/B3Jax1wIF3DvSheysQxAWvqHhZiW3IQrycvokcLcFQlveGp+vyNg==",
106
+ "dev": true
107
+ },
108
  "node_modules/@types/connect": {
109
  "version": "3.4.38",
110
  "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
 
150
  "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
151
  "dev": true
152
  },
153
+ "node_modules/@types/mocha": {
154
+ "version": "10.0.4",
155
+ "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.4.tgz",
156
+ "integrity": "sha512-xKU7bUjiFTIttpWaIZ9qvgg+22O1nmbA+HRxdlR+u6TWsGfmFdXrheJoK4fFxrHNVIOBDvDNKZG+LYBpMHpX3w==",
157
+ "dev": true
158
+ },
159
  "node_modules/@types/node": {
160
  "version": "20.9.1",
161
  "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.1.tgz",
 
255
  "node": ">=0.4.0"
256
  }
257
  },
258
+ "node_modules/ansi-colors": {
259
+ "version": "4.1.1",
260
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
261
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
262
+ "dev": true,
263
+ "engines": {
264
+ "node": ">=6"
265
+ }
266
+ },
267
+ "node_modules/ansi-regex": {
268
+ "version": "5.0.1",
269
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
270
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
271
+ "dev": true,
272
+ "engines": {
273
+ "node": ">=8"
274
+ }
275
+ },
276
+ "node_modules/ansi-styles": {
277
+ "version": "4.3.0",
278
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
279
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
280
+ "dev": true,
281
+ "dependencies": {
282
+ "color-convert": "^2.0.1"
283
+ },
284
+ "engines": {
285
+ "node": ">=8"
286
+ },
287
+ "funding": {
288
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
289
+ }
290
+ },
291
  "node_modules/anymatch": {
292
  "version": "3.1.3",
293
  "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
 
307
  "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
308
  "dev": true
309
  },
310
+ "node_modules/argparse": {
311
+ "version": "2.0.1",
312
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
313
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
314
+ "dev": true
315
+ },
316
  "node_modules/array-flatten": {
317
  "version": "1.1.1",
318
  "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
319
  "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
320
  },
321
+ "node_modules/assertion-error": {
322
+ "version": "1.1.0",
323
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
324
+ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
325
+ "dev": true,
326
+ "engines": {
327
+ "node": "*"
328
+ }
329
+ },
330
  "node_modules/atomic-sleep": {
331
  "version": "1.0.0",
332
  "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
 
427
  "node": ">=8"
428
  }
429
  },
430
+ "node_modules/browser-stdout": {
431
+ "version": "1.3.1",
432
+ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
433
+ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
434
+ "dev": true
435
+ },
436
  "node_modules/buffer": {
437
  "version": "6.0.3",
438
  "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
 
477
  "url": "https://github.com/sponsors/ljharb"
478
  }
479
  },
480
+ "node_modules/camelcase": {
481
+ "version": "6.3.0",
482
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
483
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
484
+ "dev": true,
485
+ "engines": {
486
+ "node": ">=10"
487
+ },
488
+ "funding": {
489
+ "url": "https://github.com/sponsors/sindresorhus"
490
+ }
491
+ },
492
+ "node_modules/chai": {
493
+ "version": "4.3.10",
494
+ "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz",
495
+ "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==",
496
+ "dev": true,
497
+ "dependencies": {
498
+ "assertion-error": "^1.1.0",
499
+ "check-error": "^1.0.3",
500
+ "deep-eql": "^4.1.3",
501
+ "get-func-name": "^2.0.2",
502
+ "loupe": "^2.3.6",
503
+ "pathval": "^1.1.1",
504
+ "type-detect": "^4.0.8"
505
+ },
506
+ "engines": {
507
+ "node": ">=4"
508
+ }
509
+ },
510
+ "node_modules/chalk": {
511
+ "version": "4.1.2",
512
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
513
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
514
+ "dev": true,
515
+ "dependencies": {
516
+ "ansi-styles": "^4.1.0",
517
+ "supports-color": "^7.1.0"
518
+ },
519
+ "engines": {
520
+ "node": ">=10"
521
+ },
522
+ "funding": {
523
+ "url": "https://github.com/chalk/chalk?sponsor=1"
524
+ }
525
+ },
526
+ "node_modules/chalk/node_modules/has-flag": {
527
+ "version": "4.0.0",
528
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
529
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
530
+ "dev": true,
531
+ "engines": {
532
+ "node": ">=8"
533
+ }
534
+ },
535
+ "node_modules/chalk/node_modules/supports-color": {
536
+ "version": "7.2.0",
537
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
538
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
539
+ "dev": true,
540
+ "dependencies": {
541
+ "has-flag": "^4.0.0"
542
+ },
543
+ "engines": {
544
+ "node": ">=8"
545
+ }
546
+ },
547
+ "node_modules/check-error": {
548
+ "version": "1.0.3",
549
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz",
550
+ "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==",
551
+ "dev": true,
552
+ "dependencies": {
553
+ "get-func-name": "^2.0.2"
554
+ },
555
+ "engines": {
556
+ "node": "*"
557
+ }
558
+ },
559
  "node_modules/chokidar": {
560
  "version": "3.5.3",
561
  "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
 
583
  "fsevents": "~2.3.2"
584
  }
585
  },
586
+ "node_modules/cliui": {
587
+ "version": "7.0.4",
588
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
589
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
590
+ "dev": true,
591
+ "dependencies": {
592
+ "string-width": "^4.2.0",
593
+ "strip-ansi": "^6.0.0",
594
+ "wrap-ansi": "^7.0.0"
595
+ }
596
+ },
597
+ "node_modules/color-convert": {
598
+ "version": "2.0.1",
599
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
600
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
601
+ "dev": true,
602
+ "dependencies": {
603
+ "color-name": "~1.1.4"
604
+ },
605
+ "engines": {
606
+ "node": ">=7.0.0"
607
+ }
608
+ },
609
+ "node_modules/color-name": {
610
+ "version": "1.1.4",
611
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
612
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
613
+ "dev": true
614
+ },
615
  "node_modules/concat-map": {
616
  "version": "0.0.1",
617
  "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
 
665
  "ms": "^2.1.1"
666
  }
667
  },
668
+ "node_modules/decamelize": {
669
+ "version": "4.0.0",
670
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
671
+ "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
672
+ "dev": true,
673
+ "engines": {
674
+ "node": ">=10"
675
+ },
676
+ "funding": {
677
+ "url": "https://github.com/sponsors/sindresorhus"
678
+ }
679
+ },
680
+ "node_modules/deep-eql": {
681
+ "version": "4.1.3",
682
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz",
683
+ "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==",
684
+ "dev": true,
685
+ "dependencies": {
686
+ "type-detect": "^4.0.0"
687
+ },
688
+ "engines": {
689
+ "node": ">=6"
690
+ }
691
+ },
692
  "node_modules/define-data-property": {
693
  "version": "1.1.1",
694
  "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
 
733
  "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
734
  "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
735
  },
736
+ "node_modules/emoji-regex": {
737
+ "version": "8.0.0",
738
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
739
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
740
+ "dev": true
741
+ },
742
  "node_modules/encodeurl": {
743
  "version": "1.0.2",
744
  "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
 
747
  "node": ">= 0.8"
748
  }
749
  },
750
+ "node_modules/escalade": {
751
+ "version": "3.1.1",
752
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
753
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
754
+ "dev": true,
755
+ "engines": {
756
+ "node": ">=6"
757
+ }
758
+ },
759
  "node_modules/escape-html": {
760
  "version": "1.0.3",
761
  "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
762
  "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
763
  },
764
+ "node_modules/escape-string-regexp": {
765
+ "version": "4.0.0",
766
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
767
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
768
+ "dev": true,
769
+ "engines": {
770
+ "node": ">=10"
771
+ },
772
+ "funding": {
773
+ "url": "https://github.com/sponsors/sindresorhus"
774
+ }
775
+ },
776
  "node_modules/etag": {
777
  "version": "1.8.1",
778
  "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
 
901
  "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
902
  "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
903
  },
904
+ "node_modules/find-up": {
905
+ "version": "5.0.0",
906
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
907
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
908
+ "dev": true,
909
+ "dependencies": {
910
+ "locate-path": "^6.0.0",
911
+ "path-exists": "^4.0.0"
912
+ },
913
+ "engines": {
914
+ "node": ">=10"
915
+ },
916
+ "funding": {
917
+ "url": "https://github.com/sponsors/sindresorhus"
918
+ }
919
+ },
920
+ "node_modules/flat": {
921
+ "version": "5.0.2",
922
+ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
923
+ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
924
+ "dev": true,
925
+ "bin": {
926
+ "flat": "cli.js"
927
+ }
928
+ },
929
  "node_modules/forwarded": {
930
  "version": "0.2.0",
931
  "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
 
942
  "node": ">= 0.6"
943
  }
944
  },
945
+ "node_modules/fs.realpath": {
946
+ "version": "1.0.0",
947
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
948
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
949
+ "dev": true
950
+ },
951
  "node_modules/fsevents": {
952
  "version": "2.3.3",
953
  "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
 
970
  "url": "https://github.com/sponsors/ljharb"
971
  }
972
  },
973
+ "node_modules/get-caller-file": {
974
+ "version": "2.0.5",
975
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
976
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
977
+ "dev": true,
978
+ "engines": {
979
+ "node": "6.* || 8.* || >= 10.*"
980
+ }
981
+ },
982
+ "node_modules/get-func-name": {
983
+ "version": "2.0.2",
984
+ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
985
+ "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==",
986
+ "dev": true,
987
+ "engines": {
988
+ "node": "*"
989
+ }
990
+ },
991
  "node_modules/get-intrinsic": {
992
  "version": "1.2.2",
993
  "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
 
1002
  "url": "https://github.com/sponsors/ljharb"
1003
  }
1004
  },
1005
+ "node_modules/glob": {
1006
+ "version": "7.2.0",
1007
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
1008
+ "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
1009
+ "dev": true,
1010
+ "dependencies": {
1011
+ "fs.realpath": "^1.0.0",
1012
+ "inflight": "^1.0.4",
1013
+ "inherits": "2",
1014
+ "minimatch": "^3.0.4",
1015
+ "once": "^1.3.0",
1016
+ "path-is-absolute": "^1.0.0"
1017
+ },
1018
+ "engines": {
1019
+ "node": "*"
1020
+ },
1021
+ "funding": {
1022
+ "url": "https://github.com/sponsors/isaacs"
1023
+ }
1024
+ },
1025
  "node_modules/glob-parent": {
1026
  "version": "5.1.2",
1027
  "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
 
1098
  "node": ">= 0.4"
1099
  }
1100
  },
1101
+ "node_modules/he": {
1102
+ "version": "1.2.0",
1103
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
1104
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
1105
+ "dev": true,
1106
+ "bin": {
1107
+ "he": "bin/he"
1108
+ }
1109
+ },
1110
  "node_modules/http-errors": {
1111
  "version": "2.0.0",
1112
  "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
 
1158
  "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
1159
  "dev": true
1160
  },
1161
+ "node_modules/inflight": {
1162
+ "version": "1.0.6",
1163
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
1164
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
1165
+ "dev": true,
1166
+ "dependencies": {
1167
+ "once": "^1.3.0",
1168
+ "wrappy": "1"
1169
+ }
1170
+ },
1171
  "node_modules/inherits": {
1172
  "version": "2.0.4",
1173
  "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
 
1202
  "node": ">=0.10.0"
1203
  }
1204
  },
1205
+ "node_modules/is-fullwidth-code-point": {
1206
+ "version": "3.0.0",
1207
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
1208
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
1209
+ "dev": true,
1210
+ "engines": {
1211
+ "node": ">=8"
1212
+ }
1213
+ },
1214
  "node_modules/is-glob": {
1215
  "version": "4.0.3",
1216
  "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
 
1232
  "node": ">=0.12.0"
1233
  }
1234
  },
1235
+ "node_modules/is-plain-obj": {
1236
+ "version": "2.1.0",
1237
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
1238
+ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
1239
+ "dev": true,
1240
+ "engines": {
1241
+ "node": ">=8"
1242
+ }
1243
+ },
1244
+ "node_modules/is-unicode-supported": {
1245
+ "version": "0.1.0",
1246
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
1247
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
1248
+ "dev": true,
1249
+ "engines": {
1250
+ "node": ">=10"
1251
+ },
1252
+ "funding": {
1253
+ "url": "https://github.com/sponsors/sindresorhus"
1254
+ }
1255
+ },
1256
+ "node_modules/js-yaml": {
1257
+ "version": "4.1.0",
1258
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
1259
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
1260
+ "dev": true,
1261
+ "dependencies": {
1262
+ "argparse": "^2.0.1"
1263
+ },
1264
+ "bin": {
1265
+ "js-yaml": "bin/js-yaml.js"
1266
+ }
1267
+ },
1268
+ "node_modules/locate-path": {
1269
+ "version": "6.0.0",
1270
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
1271
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
1272
+ "dev": true,
1273
+ "dependencies": {
1274
+ "p-locate": "^5.0.0"
1275
+ },
1276
+ "engines": {
1277
+ "node": ">=10"
1278
+ },
1279
+ "funding": {
1280
+ "url": "https://github.com/sponsors/sindresorhus"
1281
+ }
1282
+ },
1283
+ "node_modules/log-symbols": {
1284
+ "version": "4.1.0",
1285
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
1286
+ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
1287
+ "dev": true,
1288
+ "dependencies": {
1289
+ "chalk": "^4.1.0",
1290
+ "is-unicode-supported": "^0.1.0"
1291
+ },
1292
+ "engines": {
1293
+ "node": ">=10"
1294
+ },
1295
+ "funding": {
1296
+ "url": "https://github.com/sponsors/sindresorhus"
1297
+ }
1298
+ },
1299
+ "node_modules/loupe": {
1300
+ "version": "2.3.7",
1301
+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz",
1302
+ "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==",
1303
+ "dev": true,
1304
+ "dependencies": {
1305
+ "get-func-name": "^2.0.1"
1306
+ }
1307
+ },
1308
  "node_modules/lru-cache": {
1309
  "version": "6.0.0",
1310
  "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
 
1386
  "node": "*"
1387
  }
1388
  },
1389
+ "node_modules/mocha": {
1390
+ "version": "10.2.0",
1391
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz",
1392
+ "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==",
1393
+ "dev": true,
1394
+ "dependencies": {
1395
+ "ansi-colors": "4.1.1",
1396
+ "browser-stdout": "1.3.1",
1397
+ "chokidar": "3.5.3",
1398
+ "debug": "4.3.4",
1399
+ "diff": "5.0.0",
1400
+ "escape-string-regexp": "4.0.0",
1401
+ "find-up": "5.0.0",
1402
+ "glob": "7.2.0",
1403
+ "he": "1.2.0",
1404
+ "js-yaml": "4.1.0",
1405
+ "log-symbols": "4.1.0",
1406
+ "minimatch": "5.0.1",
1407
+ "ms": "2.1.3",
1408
+ "nanoid": "3.3.3",
1409
+ "serialize-javascript": "6.0.0",
1410
+ "strip-json-comments": "3.1.1",
1411
+ "supports-color": "8.1.1",
1412
+ "workerpool": "6.2.1",
1413
+ "yargs": "16.2.0",
1414
+ "yargs-parser": "20.2.4",
1415
+ "yargs-unparser": "2.0.0"
1416
+ },
1417
+ "bin": {
1418
+ "_mocha": "bin/_mocha",
1419
+ "mocha": "bin/mocha.js"
1420
+ },
1421
+ "engines": {
1422
+ "node": ">= 14.0.0"
1423
+ },
1424
+ "funding": {
1425
+ "type": "opencollective",
1426
+ "url": "https://opencollective.com/mochajs"
1427
+ }
1428
+ },
1429
+ "node_modules/mocha/node_modules/brace-expansion": {
1430
+ "version": "2.0.1",
1431
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
1432
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
1433
+ "dev": true,
1434
+ "dependencies": {
1435
+ "balanced-match": "^1.0.0"
1436
+ }
1437
+ },
1438
+ "node_modules/mocha/node_modules/debug": {
1439
+ "version": "4.3.4",
1440
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
1441
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
1442
+ "dev": true,
1443
+ "dependencies": {
1444
+ "ms": "2.1.2"
1445
+ },
1446
+ "engines": {
1447
+ "node": ">=6.0"
1448
+ },
1449
+ "peerDependenciesMeta": {
1450
+ "supports-color": {
1451
+ "optional": true
1452
+ }
1453
+ }
1454
+ },
1455
+ "node_modules/mocha/node_modules/debug/node_modules/ms": {
1456
+ "version": "2.1.2",
1457
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
1458
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
1459
+ "dev": true
1460
+ },
1461
+ "node_modules/mocha/node_modules/diff": {
1462
+ "version": "5.0.0",
1463
+ "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
1464
+ "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
1465
+ "dev": true,
1466
+ "engines": {
1467
+ "node": ">=0.3.1"
1468
+ }
1469
+ },
1470
+ "node_modules/mocha/node_modules/has-flag": {
1471
+ "version": "4.0.0",
1472
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
1473
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
1474
+ "dev": true,
1475
+ "engines": {
1476
+ "node": ">=8"
1477
+ }
1478
+ },
1479
+ "node_modules/mocha/node_modules/minimatch": {
1480
+ "version": "5.0.1",
1481
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
1482
+ "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
1483
+ "dev": true,
1484
+ "dependencies": {
1485
+ "brace-expansion": "^2.0.1"
1486
+ },
1487
+ "engines": {
1488
+ "node": ">=10"
1489
+ }
1490
+ },
1491
+ "node_modules/mocha/node_modules/supports-color": {
1492
+ "version": "8.1.1",
1493
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
1494
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
1495
+ "dev": true,
1496
+ "dependencies": {
1497
+ "has-flag": "^4.0.0"
1498
+ },
1499
+ "engines": {
1500
+ "node": ">=10"
1501
+ },
1502
+ "funding": {
1503
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
1504
+ }
1505
+ },
1506
  "node_modules/ms": {
1507
  "version": "2.1.3",
1508
  "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1509
  "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
1510
  },
1511
+ "node_modules/nanoid": {
1512
+ "version": "3.3.3",
1513
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz",
1514
+ "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==",
1515
+ "dev": true,
1516
+ "bin": {
1517
+ "nanoid": "bin/nanoid.cjs"
1518
+ },
1519
+ "engines": {
1520
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
1521
+ }
1522
+ },
1523
  "node_modules/negotiator": {
1524
  "version": "0.6.3",
1525
  "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
 
1607
  "node": ">= 0.8"
1608
  }
1609
  },
1610
+ "node_modules/once": {
1611
+ "version": "1.4.0",
1612
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
1613
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
1614
+ "dev": true,
1615
+ "dependencies": {
1616
+ "wrappy": "1"
1617
+ }
1618
+ },
1619
+ "node_modules/p-limit": {
1620
+ "version": "3.1.0",
1621
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
1622
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
1623
+ "dev": true,
1624
+ "dependencies": {
1625
+ "yocto-queue": "^0.1.0"
1626
+ },
1627
+ "engines": {
1628
+ "node": ">=10"
1629
+ },
1630
+ "funding": {
1631
+ "url": "https://github.com/sponsors/sindresorhus"
1632
+ }
1633
+ },
1634
+ "node_modules/p-locate": {
1635
+ "version": "5.0.0",
1636
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
1637
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
1638
+ "dev": true,
1639
+ "dependencies": {
1640
+ "p-limit": "^3.0.2"
1641
+ },
1642
+ "engines": {
1643
+ "node": ">=10"
1644
+ },
1645
+ "funding": {
1646
+ "url": "https://github.com/sponsors/sindresorhus"
1647
+ }
1648
+ },
1649
  "node_modules/parseurl": {
1650
  "version": "1.3.3",
1651
  "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
 
1654
  "node": ">= 0.8"
1655
  }
1656
  },
1657
+ "node_modules/path-exists": {
1658
+ "version": "4.0.0",
1659
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
1660
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
1661
+ "dev": true,
1662
+ "engines": {
1663
+ "node": ">=8"
1664
+ }
1665
+ },
1666
+ "node_modules/path-is-absolute": {
1667
+ "version": "1.0.1",
1668
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
1669
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
1670
+ "dev": true,
1671
+ "engines": {
1672
+ "node": ">=0.10.0"
1673
+ }
1674
+ },
1675
  "node_modules/path-to-regexp": {
1676
  "version": "0.1.7",
1677
  "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
1678
  "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
1679
  },
1680
+ "node_modules/pathval": {
1681
+ "version": "1.1.1",
1682
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
1683
+ "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
1684
+ "dev": true,
1685
+ "engines": {
1686
+ "node": "*"
1687
+ }
1688
+ },
1689
  "node_modules/picomatch": {
1690
  "version": "2.3.1",
1691
  "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
 
1798
  "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
1799
  "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="
1800
  },
1801
+ "node_modules/randombytes": {
1802
+ "version": "2.1.0",
1803
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
1804
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
1805
+ "dev": true,
1806
+ "dependencies": {
1807
+ "safe-buffer": "^5.1.0"
1808
+ }
1809
+ },
1810
  "node_modules/range-parser": {
1811
  "version": "1.2.1",
1812
  "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
 
1864
  "node": ">= 12.13.0"
1865
  }
1866
  },
1867
+ "node_modules/require-directory": {
1868
+ "version": "2.1.1",
1869
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
1870
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
1871
+ "dev": true,
1872
+ "engines": {
1873
+ "node": ">=0.10.0"
1874
+ }
1875
+ },
1876
  "node_modules/safe-buffer": {
1877
  "version": "5.2.1",
1878
  "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
 
1956
  "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1957
  "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
1958
  },
1959
+ "node_modules/serialize-javascript": {
1960
+ "version": "6.0.0",
1961
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
1962
+ "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
1963
+ "dev": true,
1964
+ "dependencies": {
1965
+ "randombytes": "^2.1.0"
1966
+ }
1967
+ },
1968
  "node_modules/serve-static": {
1969
  "version": "1.15.0",
1970
  "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
 
2055
  "safe-buffer": "~5.2.0"
2056
  }
2057
  },
2058
+ "node_modules/string-width": {
2059
+ "version": "4.2.3",
2060
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
2061
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
2062
+ "dev": true,
2063
+ "dependencies": {
2064
+ "emoji-regex": "^8.0.0",
2065
+ "is-fullwidth-code-point": "^3.0.0",
2066
+ "strip-ansi": "^6.0.1"
2067
+ },
2068
+ "engines": {
2069
+ "node": ">=8"
2070
+ }
2071
+ },
2072
+ "node_modules/strip-ansi": {
2073
+ "version": "6.0.1",
2074
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
2075
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
2076
+ "dev": true,
2077
+ "dependencies": {
2078
+ "ansi-regex": "^5.0.1"
2079
+ },
2080
+ "engines": {
2081
+ "node": ">=8"
2082
+ }
2083
+ },
2084
+ "node_modules/strip-json-comments": {
2085
+ "version": "3.1.1",
2086
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
2087
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
2088
+ "dev": true,
2089
+ "engines": {
2090
+ "node": ">=8"
2091
+ },
2092
+ "funding": {
2093
+ "url": "https://github.com/sponsors/sindresorhus"
2094
+ }
2095
+ },
2096
  "node_modules/supports-color": {
2097
  "version": "5.5.0",
2098
  "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
 
2188
  }
2189
  }
2190
  },
2191
+ "node_modules/type-detect": {
2192
+ "version": "4.0.8",
2193
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
2194
+ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
2195
+ "dev": true,
2196
+ "engines": {
2197
+ "node": ">=4"
2198
+ }
2199
+ },
2200
  "node_modules/type-is": {
2201
  "version": "1.6.18",
2202
  "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
 
2263
  "node": ">= 0.8"
2264
  }
2265
  },
2266
+ "node_modules/workerpool": {
2267
+ "version": "6.2.1",
2268
+ "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz",
2269
+ "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==",
2270
+ "dev": true
2271
+ },
2272
+ "node_modules/wrap-ansi": {
2273
+ "version": "7.0.0",
2274
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
2275
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
2276
+ "dev": true,
2277
+ "dependencies": {
2278
+ "ansi-styles": "^4.0.0",
2279
+ "string-width": "^4.1.0",
2280
+ "strip-ansi": "^6.0.0"
2281
+ },
2282
+ "engines": {
2283
+ "node": ">=10"
2284
+ },
2285
+ "funding": {
2286
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
2287
+ }
2288
+ },
2289
+ "node_modules/wrappy": {
2290
+ "version": "1.0.2",
2291
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
2292
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
2293
+ "dev": true
2294
+ },
2295
  "node_modules/ws": {
2296
  "version": "8.14.2",
2297
  "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
 
2312
  }
2313
  }
2314
  },
2315
+ "node_modules/y18n": {
2316
+ "version": "5.0.8",
2317
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
2318
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
2319
+ "dev": true,
2320
+ "engines": {
2321
+ "node": ">=10"
2322
+ }
2323
+ },
2324
  "node_modules/yallist": {
2325
  "version": "4.0.0",
2326
  "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
2327
  "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
2328
  "dev": true
2329
  },
2330
+ "node_modules/yargs": {
2331
+ "version": "16.2.0",
2332
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
2333
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
2334
+ "dev": true,
2335
+ "dependencies": {
2336
+ "cliui": "^7.0.2",
2337
+ "escalade": "^3.1.1",
2338
+ "get-caller-file": "^2.0.5",
2339
+ "require-directory": "^2.1.1",
2340
+ "string-width": "^4.2.0",
2341
+ "y18n": "^5.0.5",
2342
+ "yargs-parser": "^20.2.2"
2343
+ },
2344
+ "engines": {
2345
+ "node": ">=10"
2346
+ }
2347
+ },
2348
+ "node_modules/yargs-parser": {
2349
+ "version": "20.2.4",
2350
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
2351
+ "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==",
2352
+ "dev": true,
2353
+ "engines": {
2354
+ "node": ">=10"
2355
+ }
2356
+ },
2357
+ "node_modules/yargs-unparser": {
2358
+ "version": "2.0.0",
2359
+ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
2360
+ "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
2361
+ "dev": true,
2362
+ "dependencies": {
2363
+ "camelcase": "^6.0.0",
2364
+ "decamelize": "^4.0.0",
2365
+ "flat": "^5.0.2",
2366
+ "is-plain-obj": "^2.1.0"
2367
+ },
2368
+ "engines": {
2369
+ "node": ">=10"
2370
+ }
2371
+ },
2372
  "node_modules/yn": {
2373
  "version": "3.1.1",
2374
  "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
 
2378
  "node": ">=6"
2379
  }
2380
  },
2381
+ "node_modules/yocto-queue": {
2382
+ "version": "0.1.0",
2383
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
2384
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
2385
+ "dev": true,
2386
+ "engines": {
2387
+ "node": ">=10"
2388
+ },
2389
+ "funding": {
2390
+ "url": "https://github.com/sponsors/sindresorhus"
2391
+ }
2392
+ },
2393
  "node_modules/zod": {
2394
  "version": "3.22.4",
2395
  "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz",
server/package.json CHANGED
@@ -4,7 +4,7 @@
4
  "description": "",
5
  "main": "build/index.js",
6
  "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1",
8
  "build": "tsc",
9
  "start": "node .",
10
  "dev": "nodemon src/index.ts"
@@ -13,8 +13,12 @@
13
  "author": "",
14
  "license": "ISC",
15
  "devDependencies": {
 
16
  "@types/express": "^4.17.21",
 
17
  "@types/node": "^20.9.1",
 
 
18
  "nodemon": "^3.0.1",
19
  "prettier": "^3.1.0",
20
  "ts-node": "^10.9.1",
 
4
  "description": "",
5
  "main": "build/index.js",
6
  "scripts": {
7
+ "test": "mocha -r ts-node/register 'test/**/*.spec.ts'",
8
  "build": "tsc",
9
  "start": "node .",
10
  "dev": "nodemon src/index.ts"
 
13
  "author": "",
14
  "license": "ISC",
15
  "devDependencies": {
16
+ "@types/chai": "^4.3.10",
17
  "@types/express": "^4.17.21",
18
+ "@types/mocha": "^10.0.4",
19
  "@types/node": "^20.9.1",
20
+ "chai": "^4.3.10",
21
+ "mocha": "^10.2.0",
22
  "nodemon": "^3.0.1",
23
  "prettier": "^3.1.0",
24
  "ts-node": "^10.9.1",
server/src/helpers.ts CHANGED
@@ -1,6 +1,7 @@
1
  import * as crypto from "crypto";
2
 
3
  export function generateSessionToken(length: number): string {
 
4
  return crypto.randomBytes(length).toString("hex");
5
  }
6
 
 
1
  import * as crypto from "crypto";
2
 
3
  export function generateSessionToken(length: number): string {
4
+ if (length < 1) throw new Error("invalid length. length must be greater than 0")
5
  return crypto.randomBytes(length).toString("hex");
6
  }
7
 
server/src/index.ts CHANGED
@@ -7,62 +7,66 @@ import WebSocketWrapper from "./socket/WebSocketAdapter";
7
  import pino from "pino";
8
 
9
  (() => {
10
- const args = process.argv;
11
- if (args.includes("--silent")) {
12
- return main(false);
13
- }
14
 
15
- return main(true);
16
  })();
17
 
18
  function main(logging: boolean = true) {
19
- const port: number = parseInt(process.env.PORT as string, 10) || 3000;
20
- const logger = pino({
21
- level: logging ? "info" : "silent",
22
- formatters: {
23
- bindings(bindings) {
24
- return { level: bindings.level, time: bindings.time, msg: bindings.msg }
25
- }
26
- }
27
- });
28
-
29
- const app: Application = express();
30
- const server = http.createServer(app);
31
- const wss = new WebSocket.Server({ server });
32
-
33
- app.get("/", (_req: Request, res: Response) => {
34
- res.send("Bonjour");
35
- });
36
 
37
- // Routes are in socket/routes/*.ts
38
- // registerRoutes imports and adds them all to router
39
- const hapticLink = new HapticLinkServer();
40
- registerRoutes(hapticLink);
41
 
42
- // When a user sends a message, the router checks if that route is available
43
- // and then calls the handler. It also generates a User object for them to store
44
- // data for later requests such as an ID
45
- wss.on("connection", (ws: WebSocket) => {
46
- logger.info("Client Connected")
47
- const wsw = new WebSocketWrapper(ws);
48
 
49
- ws.on("message", (message: string) => {
50
- logger.info(`Received Message: ${message}`);
51
- hapticLink.handleRoute(wsw, message);
52
- });
53
 
54
- // When a user disconnects, their account is removed, and they are removed from all groups
55
- ws.on("close", () => {
56
- hapticLink.removeUser(wsw);
57
- })
58
- ws.on("error", () => {
59
- hapticLink.removeUser(wsw);
60
- })
61
 
62
- ws.send("Welcome");
 
 
63
  });
64
 
65
- server.listen(port, () => {
66
- logger.info(`Server is running on port ${port}`);
 
67
  });
 
 
 
 
 
 
 
 
 
 
68
  }
 
7
  import pino from "pino";
8
 
9
  (() => {
10
+ const args = process.argv;
11
+ if (args.includes("--silent")) {
12
+ return main(false);
13
+ }
14
 
15
+ return main(true);
16
  })();
17
 
18
  function main(logging: boolean = true) {
19
+ const port: number = parseInt(process.env.PORT as string, 10) || 3000;
20
+ const logger = pino({
21
+ level: logging ? "info" : "silent",
22
+ formatters: {
23
+ bindings(bindings) {
24
+ return {
25
+ level: bindings.level,
26
+ time: bindings.time,
27
+ msg: bindings.msg,
28
+ };
29
+ },
30
+ },
31
+ });
 
 
 
 
32
 
33
+ const app: Application = express();
34
+ const server = http.createServer(app);
35
+ const wss = new WebSocket.Server({ server });
 
36
 
37
+ app.get("/", (_req: Request, res: Response) => {
38
+ res.send("Bonjour");
39
+ });
 
 
 
40
 
41
+ // Routes are in socket/routes/*.ts
42
+ // registerRoutes imports and adds them all to router
43
+ const hapticLink = new HapticLinkServer();
44
+ registerRoutes(hapticLink);
45
 
46
+ // When a user sends a message, the router checks if that route is available
47
+ // and then calls the handler. It also generates a User object for them to store
48
+ // data for later requests such as an ID
49
+ wss.on("connection", (ws: WebSocket) => {
50
+ logger.info("Client Connected");
51
+ const wsw = new WebSocketWrapper(ws);
 
52
 
53
+ ws.on("message", (message: string) => {
54
+ logger.info(`Received Message: ${message}`);
55
+ hapticLink.handleRoute(wsw, message);
56
  });
57
 
58
+ // When a user disconnects, their account is removed, and they are removed from all groups
59
+ ws.on("close", () => {
60
+ hapticLink.removeUser(wsw);
61
  });
62
+ ws.on("error", () => {
63
+ hapticLink.removeUser(wsw);
64
+ });
65
+
66
+ ws.send("Welcome");
67
+ });
68
+
69
+ server.listen(port, () => {
70
+ logger.info(`Server is running on port ${port}`);
71
+ });
72
  }
server/src/socket/WebSocketAdapter.ts CHANGED
@@ -1,6 +1,11 @@
1
  import { WebSocket } from "ws";
2
 
3
- export default class WebSocketWrapper {
 
 
 
 
 
4
  ws: WebSocket
5
 
6
  constructor(ws: WebSocket) {
@@ -11,15 +16,7 @@ export default class WebSocketWrapper {
11
  this.ws.send(data);
12
  }
13
 
14
- get OPEN() {
15
- return this.ws.OPEN;
16
- }
17
-
18
- get CLOSED() {
19
- return this.ws.CLOSED;
20
- }
21
-
22
- get CLOSING() {
23
- return this.ws.CLOSING;
24
  }
25
  }
 
1
  import { WebSocket } from "ws";
2
 
3
+ export interface WebSocketInterface {
4
+ send: (data: string | Buffer) => void;
5
+ readyState: () => 0 | 1 | 2 | 3;
6
+ }
7
+
8
+ export default class WebSocketWrapper implements WebSocketInterface {
9
  ws: WebSocket
10
 
11
  constructor(ws: WebSocket) {
 
16
  this.ws.send(data);
17
  }
18
 
19
+ readyState() {
20
+ return this.ws.readyState
 
 
 
 
 
 
 
 
21
  }
22
  }
server/src/socket/hapticLinkServer.ts CHANGED
@@ -1,7 +1,7 @@
1
  import { ZodSchema } from "zod";
2
  import { generateSessionToken } from "../helpers";
3
  import { Room } from "./room";
4
- import WebSocketWrapper from "./WebSocketAdapter";
5
 
6
  type RouteHandler<T> = (context: Context<T>) => void;
7
 
@@ -14,17 +14,17 @@ export interface Route<T> {
14
  export class User {
15
  id: string;
16
  username?: string;
17
- socket: WebSocketWrapper;
18
  currentRoom?: Room;
19
 
20
- constructor(socket: WebSocketWrapper) {
21
  this.id = generateSessionToken(16);
22
  this.socket = socket;
23
  }
24
  }
25
 
26
  export interface Context<T> {
27
- ws: WebSocketWrapper;
28
  user: User;
29
  payload: T;
30
  server: HapticLinkServer;
@@ -33,7 +33,7 @@ export interface Context<T> {
33
  export class HapticLinkServer {
34
  routes: { [key: string]: Route<any> };
35
  rooms: { [key: string]: Room };
36
- users: Map<WebSocketWrapper, User>;
37
 
38
  constructor() {
39
  this.routes = {};
@@ -41,7 +41,7 @@ export class HapticLinkServer {
41
  this.rooms = {};
42
  }
43
 
44
- removeUser(ws: WebSocketWrapper): boolean {
45
  const user = this.users.get(ws);
46
  if (!user) return false;
47
  if (user.currentRoom) {
@@ -68,7 +68,7 @@ export class HapticLinkServer {
68
  return true;
69
  }
70
 
71
- handleRoute(ws: WebSocketWrapper, message: string) {
72
  // Parse JSON
73
  let payload: any;
74
  try {
 
1
  import { ZodSchema } from "zod";
2
  import { generateSessionToken } from "../helpers";
3
  import { Room } from "./room";
4
+ import { WebSocketInterface } from "./WebSocketAdapter";
5
 
6
  type RouteHandler<T> = (context: Context<T>) => void;
7
 
 
14
  export class User {
15
  id: string;
16
  username?: string;
17
+ socket: WebSocketInterface;
18
  currentRoom?: Room;
19
 
20
+ constructor(socket: WebSocketInterface) {
21
  this.id = generateSessionToken(16);
22
  this.socket = socket;
23
  }
24
  }
25
 
26
  export interface Context<T> {
27
+ ws: WebSocketInterface;
28
  user: User;
29
  payload: T;
30
  server: HapticLinkServer;
 
33
  export class HapticLinkServer {
34
  routes: { [key: string]: Route<any> };
35
  rooms: { [key: string]: Room };
36
+ users: Map<WebSocketInterface, User>;
37
 
38
  constructor() {
39
  this.routes = {};
 
41
  this.rooms = {};
42
  }
43
 
44
+ removeUser(ws: WebSocketInterface): boolean {
45
  const user = this.users.get(ws);
46
  if (!user) return false;
47
  if (user.currentRoom) {
 
68
  return true;
69
  }
70
 
71
+ handleRoute(ws: WebSocketInterface, message: string) {
72
  // Parse JSON
73
  let payload: any;
74
  try {
server/src/socket/room.ts CHANGED
@@ -1,7 +1,7 @@
1
  import { generateSessionToken } from "../helpers";
2
  import { User } from "./hapticLinkServer";
3
 
4
- interface UserData {
5
  username: string;
6
  id: string;
7
  online: boolean;
@@ -18,6 +18,7 @@ export class Room {
18
 
19
  addUser(user: User) {
20
  // Generate random roomId if one wasn't created.
 
21
  this.users.push(user);
22
  this.updateRoom();
23
  }
 
1
  import { generateSessionToken } from "../helpers";
2
  import { User } from "./hapticLinkServer";
3
 
4
+ export interface UserData {
5
  username: string;
6
  id: string;
7
  online: boolean;
 
18
 
19
  addUser(user: User) {
20
  // Generate random roomId if one wasn't created.
21
+ if (this.users.includes(user)) return;
22
  this.users.push(user);
23
  this.updateRoom();
24
  }
server/test/helpers.spec.ts ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { expect } from 'chai';
2
+ import { generateSessionToken } from '../src/helpers';
3
+
4
+ describe('generateSessionToken', () => {
5
+ it('should generate a token of a correct length', () => {
6
+ const length = 16; // Specify the length of the token to generate
7
+ const token = generateSessionToken(length);
8
+
9
+ // Since the token is hexadecimal, its string length should be twice the byte length
10
+ expect(token).to.be.a('string');
11
+ expect(token.length).to.equal(length * 2);
12
+ });
13
+
14
+ it('should generate a unique token each time', () => {
15
+ const tokens = new Set<string>();
16
+ const tokenCount = 100; // Generate a number of tokens to test uniqueness
17
+ const length = 16;
18
+
19
+ for (let i = 0; i < tokenCount; i++) {
20
+ const token = generateSessionToken(length);
21
+ tokens.add(token);
22
+ }
23
+
24
+ // If all tokens are unique, the set's size should match the number of generated tokens
25
+ expect(tokens.size).to.equal(tokenCount);
26
+ });
27
+
28
+ it('should throw an exception if an invalid length is provided', () => {
29
+ const invalidLength = 0; // Zero or negative length or other invalid values could be tested
30
+ expect(() => generateSessionToken(invalidLength)).to.throw();
31
+ });
32
+ });
server/test/index.spec.ts ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { expect } from 'chai';
2
+ import WebSocket from 'ws';
3
+ import express, { Application, Request, Response } from "express";
4
+ import * as http from "http";
5
+ import { HapticLinkServer } from '../src/socket/hapticLinkServer';
6
+ import { registerRoutes } from '../src/socket/routes';
7
+ import WebSocketWrapper from '../src/socket/WebSocketAdapter';
8
+
9
+ describe('WebSocket Server', function() {
10
+ const port: number = parseInt(process.env.PORT as string, 10) || 3000;
11
+ let wsClient: WebSocket;
12
+ let server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>
13
+
14
+ before((done) => {
15
+ const app: Application = express();
16
+ server = http.createServer(app);
17
+ const wss = new WebSocket.Server({ server });
18
+
19
+ app.get("/", (_req: Request, res: Response) => {
20
+ res.send("Bonjour");
21
+ });
22
+
23
+ const hapticLink = new HapticLinkServer();
24
+ registerRoutes(hapticLink);
25
+
26
+ wss.on("connection", (ws: WebSocket) => {
27
+ const wsw = new WebSocketWrapper(ws);
28
+
29
+ ws.on("message", (message: string) => {
30
+ hapticLink.handleRoute(wsw, message);
31
+ });
32
+
33
+ ws.on("close", () => {
34
+ hapticLink.removeUser(wsw);
35
+ });
36
+ ws.on("error", () => {
37
+ hapticLink.removeUser(wsw);
38
+ });
39
+
40
+ ws.send("Welcome");
41
+ });
42
+
43
+ server.listen(port, () => {
44
+ wsClient = new WebSocket('ws://localhost:3000');
45
+ wsClient.on('open', done);
46
+ });
47
+
48
+ server.on("error", (err) => {
49
+ done(err);
50
+ })
51
+
52
+ });
53
+
54
+ after(() => {
55
+ // Close the WebSocket client
56
+ if (wsClient.readyState === WebSocket.OPEN) {
57
+ wsClient.close();
58
+ }
59
+
60
+ server.close();
61
+
62
+ });
63
+
64
+ it('server should start, listen, and respond to test_connection', (done) => {
65
+ // Listen for messages from the server
66
+ wsClient.on('message', (data: string) => {
67
+ expect(JSON.parse(data).message).to.equal("test_connection_response");
68
+ done();
69
+ });
70
+
71
+ // Send a ping message to the server
72
+ wsClient.send(JSON.stringify({ "route": "test_connection" }));
73
+ });
74
+ });
server/test/socket/hapticLinkServer.spec.ts ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { expect } from 'chai';
2
+ import { HapticLinkServer, User, Context } from '../../src/socket/hapticLinkServer';
3
+ import { WebSocketInterface } from '../../src/socket/WebSocketAdapter';
4
+ import { z } from 'zod';
5
+
6
+ export class WebSocketWrapper implements WebSocketInterface {
7
+ sendData: string | Buffer = ""
8
+ state: 0 | 1 | 2 | 3 = 1;
9
+
10
+ constructor() { }
11
+
12
+ send(data: string | Buffer) {
13
+ this.sendData = data
14
+ }
15
+
16
+ readyState() {
17
+ return this.state;
18
+ }
19
+
20
+ }
21
+
22
+ describe("Haptic Link Server", function() {
23
+ let server: HapticLinkServer = new HapticLinkServer();
24
+ const ws = new WebSocketWrapper();
25
+ const user: User = new User(ws);
26
+
27
+ before((done) => {
28
+ done();
29
+ })
30
+
31
+
32
+ after(() => {
33
+
34
+ })
35
+
36
+ it("hapticLinkServer should initialize", (done) => {
37
+ if (!server.users || server.users.size != 0) {
38
+ return done("users map not initialized correctly")
39
+ }
40
+
41
+ if (!server.routes) {
42
+ return done("routes map not initialized correctly")
43
+ }
44
+
45
+ if (!server.rooms) {
46
+ return done("rooms map not initialized correctly")
47
+ }
48
+
49
+ done();
50
+ })
51
+
52
+ it("should add user", (done) => {
53
+ server.users.set(ws, user);
54
+ server.users.set(ws, user);
55
+
56
+ expect(server.users.size).to.equal(1, "user not added");
57
+ done()
58
+ })
59
+
60
+ it("should remove user", (done) => {
61
+ let removed = server.users.delete(ws);
62
+
63
+ expect(server.users.size).to.equal(0, "user not removed");
64
+ expect(removed).to.equal(true, "user not removed");
65
+ done()
66
+ })
67
+
68
+ it("should add route", (done) => {
69
+
70
+ interface TestConnectionPayload { }
71
+ const TestConnnectionSchema = z.object({})
72
+
73
+ function TestConnectionHandler(_ctx: Context<TestConnectionPayload>) {
74
+ return;
75
+ }
76
+
77
+ server.addRoute("test_add_route", TestConnnectionSchema, TestConnectionHandler);
78
+
79
+ const r = server.routes["test_add_route"];
80
+ if (!r) {
81
+ done("failed to add route")
82
+ }
83
+
84
+ done()
85
+ })
86
+
87
+ it("should handle route", (done) => {
88
+ interface TestConnectionPayload { }
89
+ const TestConnnectionSchema = z.object({})
90
+
91
+ let executed = false;
92
+
93
+ function TestConnectionHandler(_ctx: Context<TestConnectionPayload>) {
94
+ executed = true;
95
+ return;
96
+ }
97
+
98
+ server.addRoute("test_route", TestConnnectionSchema, TestConnectionHandler);
99
+ server.handleRoute(ws, JSON.stringify({ route: "test_route" }))
100
+
101
+ expect(executed).to.equal(true, "route not handled");
102
+ done();
103
+ })
104
+ })
server/test/socket/room.spec.ts ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { expect } from "chai";
2
+ import { HapticLinkServer, User } from "../../src/socket/hapticLinkServer";
3
+ import { Room } from "../../src/socket/room";
4
+ import {WebSocketWrapper} from "./hapticLinkServer.spec";
5
+
6
+ describe("Rooms", function() {
7
+ let server: HapticLinkServer = new HapticLinkServer();
8
+ const ws1 = new WebSocketWrapper();
9
+ const ws2 = new WebSocketWrapper();
10
+ const user1: User = new User(ws1);
11
+ const user2: User = new User(ws2);
12
+ const room: Room = new Room()
13
+
14
+ before((done) => {
15
+ done();
16
+ })
17
+
18
+
19
+ after(() => {
20
+ })
21
+
22
+ it("should have a unique random ID", (done) => {
23
+ expect(room.id.length).to.gte(3, "ID not generated")
24
+ done();
25
+ })
26
+
27
+ it("should add/remove user", (done) => {
28
+ room.addUser(user1)
29
+ room.addUser(user1)
30
+ expect(room.users.length).to.equal(1, "user not added")
31
+ room.removeUserById(user1.id);
32
+ expect(room.users.length).to.equal(0, "user not removed")
33
+ done();
34
+ })
35
+
36
+ it("should check if user exists", (done) => {
37
+ room.addUser(user1)
38
+ expect(room.hasUser(user1.id)).to.equal(true, "room didn't find user")
39
+ done();
40
+ })
41
+
42
+ it("should broadcast to all users", (done) => {
43
+ room.addUser(user1)
44
+ room.addUser(user2)
45
+ room.broadcast("hello");
46
+ expect(ws1.sendData).to.equal("hello", "didn't broadcast")
47
+ expect(ws2.sendData).to.equal("hello", "didn't broadcast")
48
+ done();
49
+ })
50
+
51
+ it("should broadcast to all users, but filter a user", (done) => {
52
+ ws1.sendData = ""
53
+ ws2.sendData = ""
54
+ room.addUser(user1)
55
+ room.addUser(user2)
56
+ room.broadcast("hello", [user1.id]);
57
+ expect(ws1.sendData).to.equal("", "didn't filter")
58
+ expect(ws2.sendData).to.equal("hello", "didn't broadcast")
59
+ done();
60
+ })
61
+ });
server/tsconfig.json CHANGED
@@ -96,6 +96,9 @@
96
  // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
97
  /* Completeness */
98
  // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
99
- "skipLibCheck": true /* Skip type checking all .d.ts files. */
100
- }
 
 
 
101
  }
 
96
  // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
97
  /* Completeness */
98
  // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
99
+ "skipLibCheck": true, /* Skip type checking all .d.ts files. */
100
+ },
101
+ "exclude": [
102
+ "test/**/*"
103
+ ]
104
  }