Adrien Denat coyotte508 HF staff commited on
Commit
d9a878f
1 Parent(s): 0c599d2

✅ Add tests setup + test sign in flow

Browse files

* replace EthicsModal by LoginModal and start binding auth logic

* start implementing login modal + give a try to

@auth
/sveltekit

using Github oauth as a test

* start custom auth implementation instead of auth.js for consistency with moon-landing

* fetch user data from provider

* add migration from anonymous to user + bind frontend

* add missing auth secret + only migrate conversations for pre-existing users

* remove email scope as it's not needed

Co-authored-by: Eliott C. <coyotte508@gmail.com>

* no need to define .well-known path

Co-authored-by: Eliott C. <coyotte508@gmail.com>

* use env var for hf hub website url

* move sessionId to signature on csrf token exchange

* remove ethic modal check and set default settings on new users

* anonymous users can read only + modal on write tries

* refresh session cookie when existing user signin again rather than use the old one

* allow users to keep using the app without loging-in if env var is not present

* typo

* handle denied login

* do not use a form action for login as there is nothing post-ed

* use requiresUser instead of env var to define login modal or not

* move back to a form action for login so user can't be linked to /login directly

* show login modal even for pre-existing users

* fix logic of account creation/updates

* settings insertOne instead of updateOne

* fix missing userId to settings creation

* fix missing updatedAt when updating settings of pre-existing users

* show login modal for everyone + add comments

* fix login modal condition for both required/not required login

* 🔨

* bring back missing form values in login modal

* refactor default settings spread around to a constant

* missing default settings

* typo

* rename a bunch of things to remove SSO references

* always migrate conversations

* remove unneeded sha256 Node specific function, replace with browser crypto API

* fix typings

* Update src/lib/components/LoginModal.svelte

* use authCondition() in callback

* add logout

* 🐛 Fix signout

cc @Grsmto, because "path" of the cookie should be "/"

* fixup! 🐛 Fix signout

* add basic test setup with Vitest + tests of login DB updates

* add test for cookie

* try to run tests on CI

* try to fix docker compose not able to ignore .env file

* oopsy

* moved back .env before running tests

* add comment about docker compose .env workaround

* try to run mongo directly from github actions instead of docker-compose

* oopsy

* do we need to wait for mongo?

* move tested code back to relevant place + rename

* fix linting

* revert unnecessary change

---------

Co-authored-by: Eliott C. <coyotte508@gmail.com>

.github/workflows/lint-and-test.yml CHANGED
@@ -25,3 +25,25 @@ jobs:
25
  - name: "Checking type errors"
26
  run: |
27
  npm run check
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  - name: "Checking type errors"
26
  run: |
27
  npm run check
28
+ test:
29
+ runs-on: ubuntu-latest
30
+ timeout-minutes: 10
31
+
32
+ services:
33
+ mongodb:
34
+ image: mongo:6.0.5
35
+ ports:
36
+ - 27017:27017
37
+
38
+ steps:
39
+ - uses: actions/checkout@v3
40
+
41
+ - uses: actions/setup-node@v3
42
+ with:
43
+ node-version: "18"
44
+ cache: "npm"
45
+ - run: |
46
+ npm ci
47
+ - name: "Tests"
48
+ run: |
49
+ npm run test
package-lock.json CHANGED
@@ -44,7 +44,8 @@
44
  "tslib": "^2.4.1",
45
  "typescript": "^4.9.3",
46
  "unplugin-icons": "^0.16.1",
47
- "vite": "^4.0.0"
 
48
  }
49
  },
50
  "node_modules/@antfu/install-pkg": {
@@ -879,6 +880,21 @@
879
  "node": ">=4"
880
  }
881
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
882
  "node_modules/@types/cookie": {
883
  "version": "0.5.1",
884
  "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.5.1.tgz",
@@ -1146,6 +1162,102 @@
1146
  "url": "https://opencollective.com/typescript-eslint"
1147
  }
1148
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1149
  "node_modules/acorn": {
1150
  "version": "8.8.2",
1151
  "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
@@ -1167,6 +1279,15 @@
1167
  "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
1168
  }
1169
  },
 
 
 
 
 
 
 
 
 
1170
  "node_modules/ajv": {
1171
  "version": "6.12.6",
1172
  "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -1244,6 +1365,15 @@
1244
  "node": ">=8"
1245
  }
1246
  },
 
 
 
 
 
 
 
 
 
1247
  "node_modules/autoprefixer": {
1248
  "version": "10.4.14",
1249
  "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz",
@@ -1314,6 +1444,12 @@
1314
  "integrity": "sha512-u4cBQNepWxYA55FunZSM7wMi55yQaN0otnhhilNoWHq0MfOfJeQx0v0mRRpolGOExPjZcl6FtB0BB8Xkb88F0g==",
1315
  "optional": true
1316
  },
 
 
 
 
 
 
1317
  "node_modules/brace-expansion": {
1318
  "version": "1.1.11",
1319
  "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -1410,6 +1546,15 @@
1410
  "node": ">=10.16.0"
1411
  }
1412
  },
 
 
 
 
 
 
 
 
 
1413
  "node_modules/callsites": {
1414
  "version": "3.1.0",
1415
  "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -1446,6 +1591,24 @@
1446
  }
1447
  ]
1448
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1449
  "node_modules/chalk": {
1450
  "version": "4.1.2",
1451
  "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -1462,6 +1625,15 @@
1462
  "url": "https://github.com/chalk/chalk?sponsor=1"
1463
  }
1464
  },
 
 
 
 
 
 
 
 
 
1465
  "node_modules/chokidar": {
1466
  "version": "3.5.3",
1467
  "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
@@ -1535,6 +1707,25 @@
1535
  "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
1536
  "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
1537
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1538
  "node_modules/cookie": {
1539
  "version": "0.5.0",
1540
  "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
@@ -1581,6 +1772,18 @@
1581
  "url": "https://opencollective.com/date-fns"
1582
  }
1583
  },
 
 
 
 
 
 
 
 
 
 
 
 
1584
  "node_modules/debug": {
1585
  "version": "4.3.4",
1586
  "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -1598,6 +1801,18 @@
1598
  }
1599
  }
1600
  },
 
 
 
 
 
 
 
 
 
 
 
 
1601
  "node_modules/deep-is": {
1602
  "version": "0.1.4",
1603
  "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -2031,6 +2246,12 @@
2031
  "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
2032
  "dev": true
2033
  },
 
 
 
 
 
 
2034
  "node_modules/fast-glob": {
2035
  "version": "3.2.12",
2036
  "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
@@ -2170,6 +2391,15 @@
2170
  "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
2171
  "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
2172
  },
 
 
 
 
 
 
 
 
 
2173
  "node_modules/get-stream": {
2174
  "version": "6.0.1",
2175
  "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
@@ -2504,6 +2734,15 @@
2504
  "url": "https://opencollective.com/js-sdsl"
2505
  }
2506
  },
 
 
 
 
 
 
 
 
 
2507
  "node_modules/js-yaml": {
2508
  "version": "4.1.0",
2509
  "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
@@ -2528,6 +2767,12 @@
2528
  "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
2529
  "dev": true
2530
  },
 
 
 
 
 
 
2531
  "node_modules/kleur": {
2532
  "version": "4.1.5",
2533
  "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
@@ -2602,6 +2847,12 @@
2602
  "url": "https://github.com/sponsors/sindresorhus"
2603
  }
2604
  },
 
 
 
 
 
 
2605
  "node_modules/lodash.castarray": {
2606
  "version": "4.4.0",
2607
  "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
@@ -2620,6 +2871,15 @@
2620
  "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
2621
  "dev": true
2622
  },
 
 
 
 
 
 
 
 
 
2623
  "node_modules/lru-cache": {
2624
  "version": "6.0.0",
2625
  "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@@ -2664,6 +2924,18 @@
2664
  "node": ">= 12"
2665
  }
2666
  },
 
 
 
 
 
 
 
 
 
 
 
 
2667
  "node_modules/memory-pager": {
2668
  "version": "1.5.0",
2669
  "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
@@ -2758,6 +3030,18 @@
2758
  "mkdirp": "bin/cmd.js"
2759
  }
2760
  },
 
 
 
 
 
 
 
 
 
 
 
 
2761
  "node_modules/mongodb": {
2762
  "version": "5.3.0",
2763
  "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.3.0.tgz",
@@ -3104,6 +3388,21 @@
3104
  "node": ">=8"
3105
  }
3106
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3107
  "node_modules/picocolors": {
3108
  "version": "1.0.0",
3109
  "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -3136,6 +3435,17 @@
3136
  "node": ">= 6"
3137
  }
3138
  },
 
 
 
 
 
 
 
 
 
 
 
3139
  "node_modules/postcss": {
3140
  "version": "8.4.23",
3141
  "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz",
@@ -3401,6 +3711,32 @@
3401
  }
3402
  }
3403
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3404
  "node_modules/punycode": {
3405
  "version": "2.3.0",
3406
  "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
@@ -3448,6 +3784,12 @@
3448
  "url": "https://github.com/sponsors/sindresorhus"
3449
  }
3450
  },
 
 
 
 
 
 
3451
  "node_modules/read-cache": {
3452
  "version": "1.0.0",
3453
  "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -3656,6 +3998,12 @@
3656
  "node": ">=8"
3657
  }
3658
  },
 
 
 
 
 
 
3659
  "node_modules/signal-exit": {
3660
  "version": "3.0.7",
3661
  "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
@@ -3744,6 +4092,18 @@
3744
  "memory-pager": "^1.0.2"
3745
  }
3746
  },
 
 
 
 
 
 
 
 
 
 
 
 
3747
  "node_modules/streamsearch": {
3748
  "version": "1.1.0",
3749
  "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
@@ -3798,6 +4158,18 @@
3798
  "url": "https://github.com/sponsors/sindresorhus"
3799
  }
3800
  },
 
 
 
 
 
 
 
 
 
 
 
 
3801
  "node_modules/sucrase": {
3802
  "version": "3.32.0",
3803
  "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz",
@@ -4131,6 +4503,15 @@
4131
  "node": ">= 4.1.0"
4132
  }
4133
  },
 
 
 
 
 
 
 
 
 
4134
  "node_modules/tiny-glob": {
4135
  "version": "0.2.9",
4136
  "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz",
@@ -4141,6 +4522,30 @@
4141
  "globrex": "^0.1.2"
4142
  }
4143
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4144
  "node_modules/to-regex-range": {
4145
  "version": "5.0.1",
4146
  "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -4216,6 +4621,15 @@
4216
  "node": ">= 0.8.0"
4217
  }
4218
  },
 
 
 
 
 
 
 
 
 
4219
  "node_modules/type-fest": {
4220
  "version": "0.20.2",
4221
  "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
@@ -4241,6 +4655,12 @@
4241
  "node": ">=4.2.0"
4242
  }
4243
  },
 
 
 
 
 
 
4244
  "node_modules/undici": {
4245
  "version": "5.22.0",
4246
  "resolved": "https://registry.npmjs.org/undici/-/undici-5.22.0.tgz",
@@ -4395,6 +4815,29 @@
4395
  }
4396
  }
4397
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4398
  "node_modules/vitefu": {
4399
  "version": "0.2.4",
4400
  "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.4.tgz",
@@ -4409,6 +4852,84 @@
4409
  }
4410
  }
4411
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4412
  "node_modules/webidl-conversions": {
4413
  "version": "7.0.0",
4414
  "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
@@ -4432,6 +4953,15 @@
4432
  "integrity": "sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==",
4433
  "dev": true
4434
  },
 
 
 
 
 
 
 
 
 
4435
  "node_modules/whatwg-url": {
4436
  "version": "11.0.0",
4437
  "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
@@ -4459,6 +4989,22 @@
4459
  "node": ">= 8"
4460
  }
4461
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4462
  "node_modules/word-wrap": {
4463
  "version": "1.2.3",
4464
  "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
 
44
  "tslib": "^2.4.1",
45
  "typescript": "^4.9.3",
46
  "unplugin-icons": "^0.16.1",
47
+ "vite": "^4.0.0",
48
+ "vitest": "^0.31.0"
49
  }
50
  },
51
  "node_modules/@antfu/install-pkg": {
 
880
  "node": ">=4"
881
  }
882
  },
883
+ "node_modules/@types/chai": {
884
+ "version": "4.3.5",
885
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz",
886
+ "integrity": "sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==",
887
+ "dev": true
888
+ },
889
+ "node_modules/@types/chai-subset": {
890
+ "version": "1.3.3",
891
+ "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.3.tgz",
892
+ "integrity": "sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==",
893
+ "dev": true,
894
+ "dependencies": {
895
+ "@types/chai": "*"
896
+ }
897
+ },
898
  "node_modules/@types/cookie": {
899
  "version": "0.5.1",
900
  "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.5.1.tgz",
 
1162
  "url": "https://opencollective.com/typescript-eslint"
1163
  }
1164
  },
1165
+ "node_modules/@vitest/expect": {
1166
+ "version": "0.31.0",
1167
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.31.0.tgz",
1168
+ "integrity": "sha512-Jlm8ZTyp6vMY9iz9Ny9a0BHnCG4fqBa8neCF6Pk/c/6vkUk49Ls6UBlgGAU82QnzzoaUs9E/mUhq/eq9uMOv/g==",
1169
+ "dev": true,
1170
+ "dependencies": {
1171
+ "@vitest/spy": "0.31.0",
1172
+ "@vitest/utils": "0.31.0",
1173
+ "chai": "^4.3.7"
1174
+ },
1175
+ "funding": {
1176
+ "url": "https://opencollective.com/vitest"
1177
+ }
1178
+ },
1179
+ "node_modules/@vitest/runner": {
1180
+ "version": "0.31.0",
1181
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.31.0.tgz",
1182
+ "integrity": "sha512-H1OE+Ly7JFeBwnpHTrKyCNm/oZgr+16N4qIlzzqSG/YRQDATBYmJb/KUn3GrZaiQQyL7GwpNHVZxSQd6juLCgw==",
1183
+ "dev": true,
1184
+ "dependencies": {
1185
+ "@vitest/utils": "0.31.0",
1186
+ "concordance": "^5.0.4",
1187
+ "p-limit": "^4.0.0",
1188
+ "pathe": "^1.1.0"
1189
+ },
1190
+ "funding": {
1191
+ "url": "https://opencollective.com/vitest"
1192
+ }
1193
+ },
1194
+ "node_modules/@vitest/runner/node_modules/p-limit": {
1195
+ "version": "4.0.0",
1196
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz",
1197
+ "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==",
1198
+ "dev": true,
1199
+ "dependencies": {
1200
+ "yocto-queue": "^1.0.0"
1201
+ },
1202
+ "engines": {
1203
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
1204
+ },
1205
+ "funding": {
1206
+ "url": "https://github.com/sponsors/sindresorhus"
1207
+ }
1208
+ },
1209
+ "node_modules/@vitest/runner/node_modules/yocto-queue": {
1210
+ "version": "1.0.0",
1211
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz",
1212
+ "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==",
1213
+ "dev": true,
1214
+ "engines": {
1215
+ "node": ">=12.20"
1216
+ },
1217
+ "funding": {
1218
+ "url": "https://github.com/sponsors/sindresorhus"
1219
+ }
1220
+ },
1221
+ "node_modules/@vitest/snapshot": {
1222
+ "version": "0.31.0",
1223
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.31.0.tgz",
1224
+ "integrity": "sha512-5dTXhbHnyUMTMOujZPB0wjFjQ6q5x9c8TvAsSPUNKjp1tVU7i9pbqcKPqntyu2oXtmVxKbuHCqrOd+Ft60r4tg==",
1225
+ "dev": true,
1226
+ "dependencies": {
1227
+ "magic-string": "^0.30.0",
1228
+ "pathe": "^1.1.0",
1229
+ "pretty-format": "^27.5.1"
1230
+ },
1231
+ "funding": {
1232
+ "url": "https://opencollective.com/vitest"
1233
+ }
1234
+ },
1235
+ "node_modules/@vitest/spy": {
1236
+ "version": "0.31.0",
1237
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.31.0.tgz",
1238
+ "integrity": "sha512-IzCEQ85RN26GqjQNkYahgVLLkULOxOm5H/t364LG0JYb3Apg0PsYCHLBYGA006+SVRMWhQvHlBBCyuByAMFmkg==",
1239
+ "dev": true,
1240
+ "dependencies": {
1241
+ "tinyspy": "^2.1.0"
1242
+ },
1243
+ "funding": {
1244
+ "url": "https://opencollective.com/vitest"
1245
+ }
1246
+ },
1247
+ "node_modules/@vitest/utils": {
1248
+ "version": "0.31.0",
1249
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.31.0.tgz",
1250
+ "integrity": "sha512-kahaRyLX7GS1urekRXN2752X4gIgOGVX4Wo8eDUGUkTWlGpXzf5ZS6N9RUUS+Re3XEE8nVGqNyxkSxF5HXlGhQ==",
1251
+ "dev": true,
1252
+ "dependencies": {
1253
+ "concordance": "^5.0.4",
1254
+ "loupe": "^2.3.6",
1255
+ "pretty-format": "^27.5.1"
1256
+ },
1257
+ "funding": {
1258
+ "url": "https://opencollective.com/vitest"
1259
+ }
1260
+ },
1261
  "node_modules/acorn": {
1262
  "version": "8.8.2",
1263
  "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
 
1279
  "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
1280
  }
1281
  },
1282
+ "node_modules/acorn-walk": {
1283
+ "version": "8.2.0",
1284
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
1285
+ "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
1286
+ "dev": true,
1287
+ "engines": {
1288
+ "node": ">=0.4.0"
1289
+ }
1290
+ },
1291
  "node_modules/ajv": {
1292
  "version": "6.12.6",
1293
  "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
 
1365
  "node": ">=8"
1366
  }
1367
  },
1368
+ "node_modules/assertion-error": {
1369
+ "version": "1.1.0",
1370
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
1371
+ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
1372
+ "dev": true,
1373
+ "engines": {
1374
+ "node": "*"
1375
+ }
1376
+ },
1377
  "node_modules/autoprefixer": {
1378
  "version": "10.4.14",
1379
  "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz",
 
1444
  "integrity": "sha512-u4cBQNepWxYA55FunZSM7wMi55yQaN0otnhhilNoWHq0MfOfJeQx0v0mRRpolGOExPjZcl6FtB0BB8Xkb88F0g==",
1445
  "optional": true
1446
  },
1447
+ "node_modules/blueimp-md5": {
1448
+ "version": "2.19.0",
1449
+ "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz",
1450
+ "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==",
1451
+ "dev": true
1452
+ },
1453
  "node_modules/brace-expansion": {
1454
  "version": "1.1.11",
1455
  "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
 
1546
  "node": ">=10.16.0"
1547
  }
1548
  },
1549
+ "node_modules/cac": {
1550
+ "version": "6.7.14",
1551
+ "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
1552
+ "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
1553
+ "dev": true,
1554
+ "engines": {
1555
+ "node": ">=8"
1556
+ }
1557
+ },
1558
  "node_modules/callsites": {
1559
  "version": "3.1.0",
1560
  "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
 
1591
  }
1592
  ]
1593
  },
1594
+ "node_modules/chai": {
1595
+ "version": "4.3.7",
1596
+ "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz",
1597
+ "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==",
1598
+ "dev": true,
1599
+ "dependencies": {
1600
+ "assertion-error": "^1.1.0",
1601
+ "check-error": "^1.0.2",
1602
+ "deep-eql": "^4.1.2",
1603
+ "get-func-name": "^2.0.0",
1604
+ "loupe": "^2.3.1",
1605
+ "pathval": "^1.1.1",
1606
+ "type-detect": "^4.0.5"
1607
+ },
1608
+ "engines": {
1609
+ "node": ">=4"
1610
+ }
1611
+ },
1612
  "node_modules/chalk": {
1613
  "version": "4.1.2",
1614
  "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
 
1625
  "url": "https://github.com/chalk/chalk?sponsor=1"
1626
  }
1627
  },
1628
+ "node_modules/check-error": {
1629
+ "version": "1.0.2",
1630
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
1631
+ "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==",
1632
+ "dev": true,
1633
+ "engines": {
1634
+ "node": "*"
1635
+ }
1636
+ },
1637
  "node_modules/chokidar": {
1638
  "version": "3.5.3",
1639
  "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
 
1707
  "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
1708
  "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
1709
  },
1710
+ "node_modules/concordance": {
1711
+ "version": "5.0.4",
1712
+ "resolved": "https://registry.npmjs.org/concordance/-/concordance-5.0.4.tgz",
1713
+ "integrity": "sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==",
1714
+ "dev": true,
1715
+ "dependencies": {
1716
+ "date-time": "^3.1.0",
1717
+ "esutils": "^2.0.3",
1718
+ "fast-diff": "^1.2.0",
1719
+ "js-string-escape": "^1.0.1",
1720
+ "lodash": "^4.17.15",
1721
+ "md5-hex": "^3.0.1",
1722
+ "semver": "^7.3.2",
1723
+ "well-known-symbols": "^2.0.0"
1724
+ },
1725
+ "engines": {
1726
+ "node": ">=10.18.0 <11 || >=12.14.0 <13 || >=14"
1727
+ }
1728
+ },
1729
  "node_modules/cookie": {
1730
  "version": "0.5.0",
1731
  "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
 
1772
  "url": "https://opencollective.com/date-fns"
1773
  }
1774
  },
1775
+ "node_modules/date-time": {
1776
+ "version": "3.1.0",
1777
+ "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz",
1778
+ "integrity": "sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==",
1779
+ "dev": true,
1780
+ "dependencies": {
1781
+ "time-zone": "^1.0.0"
1782
+ },
1783
+ "engines": {
1784
+ "node": ">=6"
1785
+ }
1786
+ },
1787
  "node_modules/debug": {
1788
  "version": "4.3.4",
1789
  "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
 
1801
  }
1802
  }
1803
  },
1804
+ "node_modules/deep-eql": {
1805
+ "version": "4.1.3",
1806
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz",
1807
+ "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==",
1808
+ "dev": true,
1809
+ "dependencies": {
1810
+ "type-detect": "^4.0.0"
1811
+ },
1812
+ "engines": {
1813
+ "node": ">=6"
1814
+ }
1815
+ },
1816
  "node_modules/deep-is": {
1817
  "version": "0.1.4",
1818
  "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
 
2246
  "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
2247
  "dev": true
2248
  },
2249
+ "node_modules/fast-diff": {
2250
+ "version": "1.2.0",
2251
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
2252
+ "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
2253
+ "dev": true
2254
+ },
2255
  "node_modules/fast-glob": {
2256
  "version": "3.2.12",
2257
  "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
 
2391
  "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
2392
  "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
2393
  },
2394
+ "node_modules/get-func-name": {
2395
+ "version": "2.0.0",
2396
+ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
2397
+ "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==",
2398
+ "dev": true,
2399
+ "engines": {
2400
+ "node": "*"
2401
+ }
2402
+ },
2403
  "node_modules/get-stream": {
2404
  "version": "6.0.1",
2405
  "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
 
2734
  "url": "https://opencollective.com/js-sdsl"
2735
  }
2736
  },
2737
+ "node_modules/js-string-escape": {
2738
+ "version": "1.0.1",
2739
+ "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz",
2740
+ "integrity": "sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==",
2741
+ "dev": true,
2742
+ "engines": {
2743
+ "node": ">= 0.8"
2744
+ }
2745
+ },
2746
  "node_modules/js-yaml": {
2747
  "version": "4.1.0",
2748
  "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
 
2767
  "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
2768
  "dev": true
2769
  },
2770
+ "node_modules/jsonc-parser": {
2771
+ "version": "3.2.0",
2772
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
2773
+ "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==",
2774
+ "dev": true
2775
+ },
2776
  "node_modules/kleur": {
2777
  "version": "4.1.5",
2778
  "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
 
2847
  "url": "https://github.com/sponsors/sindresorhus"
2848
  }
2849
  },
2850
+ "node_modules/lodash": {
2851
+ "version": "4.17.21",
2852
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
2853
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
2854
+ "dev": true
2855
+ },
2856
  "node_modules/lodash.castarray": {
2857
  "version": "4.4.0",
2858
  "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
 
2871
  "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
2872
  "dev": true
2873
  },
2874
+ "node_modules/loupe": {
2875
+ "version": "2.3.6",
2876
+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz",
2877
+ "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==",
2878
+ "dev": true,
2879
+ "dependencies": {
2880
+ "get-func-name": "^2.0.0"
2881
+ }
2882
+ },
2883
  "node_modules/lru-cache": {
2884
  "version": "6.0.0",
2885
  "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
 
2924
  "node": ">= 12"
2925
  }
2926
  },
2927
+ "node_modules/md5-hex": {
2928
+ "version": "3.0.1",
2929
+ "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-3.0.1.tgz",
2930
+ "integrity": "sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==",
2931
+ "dev": true,
2932
+ "dependencies": {
2933
+ "blueimp-md5": "^2.10.0"
2934
+ },
2935
+ "engines": {
2936
+ "node": ">=8"
2937
+ }
2938
+ },
2939
  "node_modules/memory-pager": {
2940
  "version": "1.5.0",
2941
  "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
 
3030
  "mkdirp": "bin/cmd.js"
3031
  }
3032
  },
3033
+ "node_modules/mlly": {
3034
+ "version": "1.2.1",
3035
+ "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.2.1.tgz",
3036
+ "integrity": "sha512-1aMEByaWgBPEbWV2BOPEMySRrzl7rIHXmQxam4DM8jVjalTQDjpN2ZKOLUrwyhfZQO7IXHml2StcHMhooDeEEQ==",
3037
+ "dev": true,
3038
+ "dependencies": {
3039
+ "acorn": "^8.8.2",
3040
+ "pathe": "^1.1.0",
3041
+ "pkg-types": "^1.0.3",
3042
+ "ufo": "^1.1.2"
3043
+ }
3044
+ },
3045
  "node_modules/mongodb": {
3046
  "version": "5.3.0",
3047
  "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.3.0.tgz",
 
3388
  "node": ">=8"
3389
  }
3390
  },
3391
+ "node_modules/pathe": {
3392
+ "version": "1.1.0",
3393
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.0.tgz",
3394
+ "integrity": "sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==",
3395
+ "dev": true
3396
+ },
3397
+ "node_modules/pathval": {
3398
+ "version": "1.1.1",
3399
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
3400
+ "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
3401
+ "dev": true,
3402
+ "engines": {
3403
+ "node": "*"
3404
+ }
3405
+ },
3406
  "node_modules/picocolors": {
3407
  "version": "1.0.0",
3408
  "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
 
3435
  "node": ">= 6"
3436
  }
3437
  },
3438
+ "node_modules/pkg-types": {
3439
+ "version": "1.0.3",
3440
+ "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz",
3441
+ "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==",
3442
+ "dev": true,
3443
+ "dependencies": {
3444
+ "jsonc-parser": "^3.2.0",
3445
+ "mlly": "^1.2.0",
3446
+ "pathe": "^1.1.0"
3447
+ }
3448
+ },
3449
  "node_modules/postcss": {
3450
  "version": "8.4.23",
3451
  "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz",
 
3711
  }
3712
  }
3713
  },
3714
+ "node_modules/pretty-format": {
3715
+ "version": "27.5.1",
3716
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
3717
+ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
3718
+ "dev": true,
3719
+ "dependencies": {
3720
+ "ansi-regex": "^5.0.1",
3721
+ "ansi-styles": "^5.0.0",
3722
+ "react-is": "^17.0.1"
3723
+ },
3724
+ "engines": {
3725
+ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
3726
+ }
3727
+ },
3728
+ "node_modules/pretty-format/node_modules/ansi-styles": {
3729
+ "version": "5.2.0",
3730
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
3731
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
3732
+ "dev": true,
3733
+ "engines": {
3734
+ "node": ">=10"
3735
+ },
3736
+ "funding": {
3737
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
3738
+ }
3739
+ },
3740
  "node_modules/punycode": {
3741
  "version": "2.3.0",
3742
  "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
 
3784
  "url": "https://github.com/sponsors/sindresorhus"
3785
  }
3786
  },
3787
+ "node_modules/react-is": {
3788
+ "version": "17.0.2",
3789
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
3790
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
3791
+ "dev": true
3792
+ },
3793
  "node_modules/read-cache": {
3794
  "version": "1.0.0",
3795
  "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
 
3998
  "node": ">=8"
3999
  }
4000
  },
4001
+ "node_modules/siginfo": {
4002
+ "version": "2.0.0",
4003
+ "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
4004
+ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
4005
+ "dev": true
4006
+ },
4007
  "node_modules/signal-exit": {
4008
  "version": "3.0.7",
4009
  "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
 
4092
  "memory-pager": "^1.0.2"
4093
  }
4094
  },
4095
+ "node_modules/stackback": {
4096
+ "version": "0.0.2",
4097
+ "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
4098
+ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
4099
+ "dev": true
4100
+ },
4101
+ "node_modules/std-env": {
4102
+ "version": "3.3.3",
4103
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.3.3.tgz",
4104
+ "integrity": "sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==",
4105
+ "dev": true
4106
+ },
4107
  "node_modules/streamsearch": {
4108
  "version": "1.1.0",
4109
  "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
 
4158
  "url": "https://github.com/sponsors/sindresorhus"
4159
  }
4160
  },
4161
+ "node_modules/strip-literal": {
4162
+ "version": "1.0.1",
4163
+ "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.0.1.tgz",
4164
+ "integrity": "sha512-QZTsipNpa2Ppr6v1AmJHESqJ3Uz247MUS0OjrnnZjFAvEoWqxuyFuXn2xLgMtRnijJShAa1HL0gtJyUs7u7n3Q==",
4165
+ "dev": true,
4166
+ "dependencies": {
4167
+ "acorn": "^8.8.2"
4168
+ },
4169
+ "funding": {
4170
+ "url": "https://github.com/sponsors/antfu"
4171
+ }
4172
+ },
4173
  "node_modules/sucrase": {
4174
  "version": "3.32.0",
4175
  "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz",
 
4503
  "node": ">= 4.1.0"
4504
  }
4505
  },
4506
+ "node_modules/time-zone": {
4507
+ "version": "1.0.0",
4508
+ "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz",
4509
+ "integrity": "sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==",
4510
+ "dev": true,
4511
+ "engines": {
4512
+ "node": ">=4"
4513
+ }
4514
+ },
4515
  "node_modules/tiny-glob": {
4516
  "version": "0.2.9",
4517
  "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz",
 
4522
  "globrex": "^0.1.2"
4523
  }
4524
  },
4525
+ "node_modules/tinybench": {
4526
+ "version": "2.5.0",
4527
+ "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.5.0.tgz",
4528
+ "integrity": "sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==",
4529
+ "dev": true
4530
+ },
4531
+ "node_modules/tinypool": {
4532
+ "version": "0.5.0",
4533
+ "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.5.0.tgz",
4534
+ "integrity": "sha512-paHQtnrlS1QZYKF/GnLoOM/DN9fqaGOFbCbxzAhwniySnzl9Ebk8w73/dd34DAhe/obUbPAOldTyYXQZxnPBPQ==",
4535
+ "dev": true,
4536
+ "engines": {
4537
+ "node": ">=14.0.0"
4538
+ }
4539
+ },
4540
+ "node_modules/tinyspy": {
4541
+ "version": "2.1.0",
4542
+ "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.1.0.tgz",
4543
+ "integrity": "sha512-7eORpyqImoOvkQJCSkL0d0mB4NHHIFAy4b1u8PHdDa7SjGS2njzl6/lyGoZLm+eyYEtlUmFGE0rFj66SWxZgQQ==",
4544
+ "dev": true,
4545
+ "engines": {
4546
+ "node": ">=14.0.0"
4547
+ }
4548
+ },
4549
  "node_modules/to-regex-range": {
4550
  "version": "5.0.1",
4551
  "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
 
4621
  "node": ">= 0.8.0"
4622
  }
4623
  },
4624
+ "node_modules/type-detect": {
4625
+ "version": "4.0.8",
4626
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
4627
+ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
4628
+ "dev": true,
4629
+ "engines": {
4630
+ "node": ">=4"
4631
+ }
4632
+ },
4633
  "node_modules/type-fest": {
4634
  "version": "0.20.2",
4635
  "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
 
4655
  "node": ">=4.2.0"
4656
  }
4657
  },
4658
+ "node_modules/ufo": {
4659
+ "version": "1.1.2",
4660
+ "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.1.2.tgz",
4661
+ "integrity": "sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ==",
4662
+ "dev": true
4663
+ },
4664
  "node_modules/undici": {
4665
  "version": "5.22.0",
4666
  "resolved": "https://registry.npmjs.org/undici/-/undici-5.22.0.tgz",
 
4815
  }
4816
  }
4817
  },
4818
+ "node_modules/vite-node": {
4819
+ "version": "0.31.0",
4820
+ "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.31.0.tgz",
4821
+ "integrity": "sha512-8x1x1LNuPvE2vIvkSB7c1mApX5oqlgsxzHQesYF7l5n1gKrEmrClIiZuOFbFDQcjLsmcWSwwmrWrcGWm9Fxc/g==",
4822
+ "dev": true,
4823
+ "dependencies": {
4824
+ "cac": "^6.7.14",
4825
+ "debug": "^4.3.4",
4826
+ "mlly": "^1.2.0",
4827
+ "pathe": "^1.1.0",
4828
+ "picocolors": "^1.0.0",
4829
+ "vite": "^3.0.0 || ^4.0.0"
4830
+ },
4831
+ "bin": {
4832
+ "vite-node": "vite-node.mjs"
4833
+ },
4834
+ "engines": {
4835
+ "node": ">=v14.18.0"
4836
+ },
4837
+ "funding": {
4838
+ "url": "https://opencollective.com/vitest"
4839
+ }
4840
+ },
4841
  "node_modules/vitefu": {
4842
  "version": "0.2.4",
4843
  "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.4.tgz",
 
4852
  }
4853
  }
4854
  },
4855
+ "node_modules/vitest": {
4856
+ "version": "0.31.0",
4857
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.31.0.tgz",
4858
+ "integrity": "sha512-JwWJS9p3GU9GxkG7eBSmr4Q4x4bvVBSswaCFf1PBNHiPx00obfhHRJfgHcnI0ffn+NMlIh9QGvG75FlaIBdKGA==",
4859
+ "dev": true,
4860
+ "dependencies": {
4861
+ "@types/chai": "^4.3.4",
4862
+ "@types/chai-subset": "^1.3.3",
4863
+ "@types/node": "*",
4864
+ "@vitest/expect": "0.31.0",
4865
+ "@vitest/runner": "0.31.0",
4866
+ "@vitest/snapshot": "0.31.0",
4867
+ "@vitest/spy": "0.31.0",
4868
+ "@vitest/utils": "0.31.0",
4869
+ "acorn": "^8.8.2",
4870
+ "acorn-walk": "^8.2.0",
4871
+ "cac": "^6.7.14",
4872
+ "chai": "^4.3.7",
4873
+ "concordance": "^5.0.4",
4874
+ "debug": "^4.3.4",
4875
+ "local-pkg": "^0.4.3",
4876
+ "magic-string": "^0.30.0",
4877
+ "pathe": "^1.1.0",
4878
+ "picocolors": "^1.0.0",
4879
+ "std-env": "^3.3.2",
4880
+ "strip-literal": "^1.0.1",
4881
+ "tinybench": "^2.4.0",
4882
+ "tinypool": "^0.5.0",
4883
+ "vite": "^3.0.0 || ^4.0.0",
4884
+ "vite-node": "0.31.0",
4885
+ "why-is-node-running": "^2.2.2"
4886
+ },
4887
+ "bin": {
4888
+ "vitest": "vitest.mjs"
4889
+ },
4890
+ "engines": {
4891
+ "node": ">=v14.18.0"
4892
+ },
4893
+ "funding": {
4894
+ "url": "https://opencollective.com/vitest"
4895
+ },
4896
+ "peerDependencies": {
4897
+ "@edge-runtime/vm": "*",
4898
+ "@vitest/browser": "*",
4899
+ "@vitest/ui": "*",
4900
+ "happy-dom": "*",
4901
+ "jsdom": "*",
4902
+ "playwright": "*",
4903
+ "safaridriver": "*",
4904
+ "webdriverio": "*"
4905
+ },
4906
+ "peerDependenciesMeta": {
4907
+ "@edge-runtime/vm": {
4908
+ "optional": true
4909
+ },
4910
+ "@vitest/browser": {
4911
+ "optional": true
4912
+ },
4913
+ "@vitest/ui": {
4914
+ "optional": true
4915
+ },
4916
+ "happy-dom": {
4917
+ "optional": true
4918
+ },
4919
+ "jsdom": {
4920
+ "optional": true
4921
+ },
4922
+ "playwright": {
4923
+ "optional": true
4924
+ },
4925
+ "safaridriver": {
4926
+ "optional": true
4927
+ },
4928
+ "webdriverio": {
4929
+ "optional": true
4930
+ }
4931
+ }
4932
+ },
4933
  "node_modules/webidl-conversions": {
4934
  "version": "7.0.0",
4935
  "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
 
4953
  "integrity": "sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==",
4954
  "dev": true
4955
  },
4956
+ "node_modules/well-known-symbols": {
4957
+ "version": "2.0.0",
4958
+ "resolved": "https://registry.npmjs.org/well-known-symbols/-/well-known-symbols-2.0.0.tgz",
4959
+ "integrity": "sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==",
4960
+ "dev": true,
4961
+ "engines": {
4962
+ "node": ">=6"
4963
+ }
4964
+ },
4965
  "node_modules/whatwg-url": {
4966
  "version": "11.0.0",
4967
  "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
 
4989
  "node": ">= 8"
4990
  }
4991
  },
4992
+ "node_modules/why-is-node-running": {
4993
+ "version": "2.2.2",
4994
+ "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz",
4995
+ "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==",
4996
+ "dev": true,
4997
+ "dependencies": {
4998
+ "siginfo": "^2.0.0",
4999
+ "stackback": "0.0.2"
5000
+ },
5001
+ "bin": {
5002
+ "why-is-node-running": "cli.js"
5003
+ },
5004
+ "engines": {
5005
+ "node": ">=8"
5006
+ }
5007
+ },
5008
  "node_modules/word-wrap": {
5009
  "version": "1.2.3",
5010
  "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
package.json CHANGED
@@ -10,7 +10,8 @@
10
  "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
11
  "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
12
  "lint": "prettier --plugin-search-dir . --check . && eslint .",
13
- "format": "prettier --plugin-search-dir . --write ."
 
14
  },
15
  "devDependencies": {
16
  "@iconify-json/carbon": "^1.1.16",
@@ -32,7 +33,8 @@
32
  "tslib": "^2.4.1",
33
  "typescript": "^4.9.3",
34
  "unplugin-icons": "^0.16.1",
35
- "vite": "^4.0.0"
 
36
  },
37
  "type": "module",
38
  "dependencies": {
 
10
  "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
11
  "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
12
  "lint": "prettier --plugin-search-dir . --check . && eslint .",
13
+ "format": "prettier --plugin-search-dir . --write .",
14
+ "test": "MONGODB_URL=mongodb://127.0.0.1:27017/ vitest"
15
  },
16
  "devDependencies": {
17
  "@iconify-json/carbon": "^1.1.16",
 
33
  "tslib": "^2.4.1",
34
  "typescript": "^4.9.3",
35
  "unplugin-icons": "^0.16.1",
36
+ "vite": "^4.0.0",
37
+ "vitest": "^0.31.0"
38
  },
39
  "type": "module",
40
  "dependencies": {
src/lib/server/database.ts CHANGED
@@ -18,7 +18,7 @@ const client = new MongoClient(MONGODB_URL, {
18
 
19
  export const connectPromise = client.connect().catch(console.error);
20
 
21
- const db = client.db(MONGODB_DB_NAME);
22
 
23
  const conversations = db.collection<Conversation>("conversations");
24
  const sharedConversations = db.collection<SharedConversation>("sharedConversations");
 
18
 
19
  export const connectPromise = client.connect().catch(console.error);
20
 
21
+ const db = client.db(MONGODB_DB_NAME + (import.meta.env.MODE === "test" ? "-test" : ""));
22
 
23
  const conversations = db.collection<Conversation>("conversations");
24
  const sharedConversations = db.collection<SharedConversation>("sharedConversations");
src/routes/login/callback/+server.ts CHANGED
@@ -1,16 +1,8 @@
1
  import { redirect, error } from "@sveltejs/kit";
2
- import {
3
- authCondition,
4
- getOIDCUserData,
5
- getRedirectURI,
6
- refreshSessionCookie,
7
- validateCsrfToken,
8
- } from "$lib/server/auth";
9
  import { z } from "zod";
10
- import { collections } from "$lib/server/database";
11
- import { ObjectId } from "mongodb";
12
  import { base } from "$app/paths";
13
- import { DEFAULT_SETTINGS } from "$lib/types/Settings";
14
 
15
  export async function GET({ url, locals, cookies }) {
16
  const { error: errorName } = z
@@ -41,69 +33,7 @@ export async function GET({ url, locals, cookies }) {
41
 
42
  const { userData } = await getOIDCUserData({ redirectURI: getRedirectURI(url) }, code);
43
 
44
- const {
45
- preferred_username: username,
46
- name,
47
- picture: avatarUrl,
48
- sub: hfUserId,
49
- } = z
50
- .object({
51
- preferred_username: z.string(),
52
- name: z.string(),
53
- picture: z.string(),
54
- sub: z.string(),
55
- })
56
- .parse(userData);
57
-
58
- const existingUser = await collections.users.findOne({ hfUserId });
59
- let userId = existingUser?._id;
60
-
61
- if (existingUser) {
62
- // update existing user if any
63
- await collections.users.updateOne(
64
- { _id: existingUser._id },
65
- { $set: { username, name, avatarUrl } }
66
- );
67
- // refresh session cookie
68
- refreshSessionCookie(cookies, existingUser.sessionId);
69
- } else {
70
- // user doesn't exist yet, create a new one
71
- const { insertedId } = await collections.users.insertOne({
72
- _id: new ObjectId(),
73
- createdAt: new Date(),
74
- updatedAt: new Date(),
75
- username,
76
- name,
77
- avatarUrl,
78
- hfUserId,
79
- sessionId: locals.sessionId,
80
- });
81
-
82
- userId = insertedId;
83
-
84
- // update pre-existing settings
85
- const { matchedCount } = await collections.settings.updateOne(authCondition(locals), {
86
- $set: { userId, updatedAt: new Date() },
87
- $unset: { sessionId: "" },
88
- });
89
-
90
- if (!matchedCount) {
91
- // update settings if existing or create new default ones
92
- await collections.settings.insertOne({
93
- userId,
94
- ethicsModalAcceptedAt: new Date(),
95
- updatedAt: new Date(),
96
- createdAt: new Date(),
97
- ...DEFAULT_SETTINGS,
98
- });
99
- }
100
- }
101
-
102
- // migrate pre-existing conversations
103
- await collections.conversations.updateMany(authCondition(locals), {
104
- $set: { userId },
105
- $unset: { sessionId: "" },
106
- });
107
 
108
  throw redirect(302, base || "/");
109
  }
 
1
  import { redirect, error } from "@sveltejs/kit";
2
+ import { getOIDCUserData, getRedirectURI, validateCsrfToken } from "$lib/server/auth";
 
 
 
 
 
 
3
  import { z } from "zod";
 
 
4
  import { base } from "$app/paths";
5
+ import { updateUser } from "./updateUser";
6
 
7
  export async function GET({ url, locals, cookies }) {
8
  const { error: errorName } = z
 
33
 
34
  const { userData } = await getOIDCUserData({ redirectURI: getRedirectURI(url) }, code);
35
 
36
+ await updateUser({ userData, locals, cookies });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
  throw redirect(302, base || "/");
39
  }
src/routes/login/callback/updateUser.spec.ts ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { assert, it, describe, afterEach, vi, expect } from "vitest";
2
+ import type { Cookies } from "@sveltejs/kit";
3
+ import { collections } from "$lib/server/database";
4
+ import { updateUser } from "./updateUser";
5
+ import { ObjectId } from "mongodb";
6
+ import { DEFAULT_SETTINGS } from "$lib/types/Settings";
7
+ import { defaultModel } from "$lib/server/models";
8
+
9
+ const userData = {
10
+ preferred_username: "new-username",
11
+ name: "name",
12
+ picture: "https://example.com/avatar.png",
13
+ sub: "1234567890",
14
+ };
15
+
16
+ const locals = {
17
+ userId: "1234567890",
18
+ sessionId: "1234567890",
19
+ };
20
+
21
+ // @ts-expect-error SvelteKit cookies dumb mock
22
+ const cookiesMock: Cookies = {
23
+ set: vi.fn(),
24
+ };
25
+
26
+ const insertRandomUser = async () => {
27
+ const res = await collections.users.insertOne({
28
+ _id: new ObjectId(),
29
+ createdAt: new Date(),
30
+ updatedAt: new Date(),
31
+ username: "base-username",
32
+ name: userData.name,
33
+ avatarUrl: userData.picture,
34
+ hfUserId: userData.sub,
35
+ sessionId: locals.sessionId,
36
+ });
37
+
38
+ return res.insertedId;
39
+ };
40
+
41
+ const insertRandomConversations = async (count: number) => {
42
+ const res = await collections.conversations.insertMany(
43
+ new Array(count).fill(0).map(() => ({
44
+ _id: new ObjectId(),
45
+ title: "random title",
46
+ messages: [],
47
+ model: defaultModel.id,
48
+ createdAt: new Date(),
49
+ updatedAt: new Date(),
50
+ sessionId: locals.sessionId,
51
+ }))
52
+ );
53
+
54
+ return res.insertedIds;
55
+ };
56
+
57
+ describe("login", () => {
58
+ it("should update user if existing", async () => {
59
+ await insertRandomUser();
60
+
61
+ await updateUser({ userData, locals, cookies: cookiesMock });
62
+
63
+ const existingUser = await collections.users.findOne({ hfUserId: userData.sub });
64
+
65
+ assert.equal(existingUser?.name, userData.name);
66
+
67
+ expect(cookiesMock.set).toBeCalledTimes(1);
68
+ });
69
+
70
+ it("should migrate pre-existing conversations for new user", async () => {
71
+ const insertedId = await insertRandomUser();
72
+
73
+ await insertRandomConversations(2);
74
+
75
+ await updateUser({ userData, locals, cookies: cookiesMock });
76
+
77
+ const conversationCount = await collections.conversations.countDocuments({
78
+ userId: insertedId,
79
+ sessionId: { $exists: false },
80
+ });
81
+
82
+ assert.equal(conversationCount, 2);
83
+
84
+ await collections.conversations.deleteMany({ userId: insertedId });
85
+ });
86
+
87
+ it("should create default settings for new user", async () => {
88
+ await updateUser({ userData, locals, cookies: cookiesMock });
89
+
90
+ const user = await collections.users.findOne({ sessionId: locals.sessionId });
91
+
92
+ assert.exists(user);
93
+
94
+ const settings = await collections.settings.findOne({ userId: user?._id });
95
+
96
+ expect(settings).toMatchObject({
97
+ userId: user?._id,
98
+ updatedAt: expect.any(Date),
99
+ createdAt: expect.any(Date),
100
+ ethicsModalAcceptedAt: expect.any(Date),
101
+ ...DEFAULT_SETTINGS,
102
+ });
103
+
104
+ await collections.settings.deleteOne({ userId: user?._id });
105
+ });
106
+
107
+ it("should migrate pre-existing settings for pre-existing user", async () => {
108
+ const { insertedId } = await collections.settings.insertOne({
109
+ sessionId: locals.sessionId,
110
+ ethicsModalAcceptedAt: new Date(),
111
+ updatedAt: new Date(),
112
+ createdAt: new Date(),
113
+ ...DEFAULT_SETTINGS,
114
+ shareConversationsWithModelAuthors: false,
115
+ });
116
+
117
+ await updateUser({ userData, locals, cookies: cookiesMock });
118
+
119
+ const settings = await collections.settings.findOne({
120
+ _id: insertedId,
121
+ sessionId: { $exists: false },
122
+ });
123
+
124
+ assert.exists(settings);
125
+
126
+ const user = await collections.users.findOne({ hfUserId: userData.sub });
127
+
128
+ expect(settings).toMatchObject({
129
+ userId: user?._id,
130
+ updatedAt: expect.any(Date),
131
+ createdAt: expect.any(Date),
132
+ ethicsModalAcceptedAt: expect.any(Date),
133
+ ...DEFAULT_SETTINGS,
134
+ shareConversationsWithModelAuthors: false,
135
+ });
136
+
137
+ await collections.settings.deleteOne({ userId: user?._id });
138
+ });
139
+ });
140
+
141
+ afterEach(async () => {
142
+ await collections.users.deleteMany({ hfUserId: userData.sub });
143
+ vi.clearAllMocks();
144
+ });
src/routes/login/callback/updateUser.ts ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { authCondition, refreshSessionCookie } from "$lib/server/auth";
2
+ import { collections } from "$lib/server/database";
3
+ import { ObjectId } from "mongodb";
4
+ import { DEFAULT_SETTINGS } from "$lib/types/Settings";
5
+ import { z } from "zod";
6
+ import type { UserinfoResponse } from "openid-client";
7
+ import type { Cookies } from "@sveltejs/kit";
8
+
9
+ export async function updateUser(params: {
10
+ userData: UserinfoResponse;
11
+ locals: App.Locals;
12
+ cookies: Cookies;
13
+ }) {
14
+ const { userData, locals, cookies } = params;
15
+
16
+ const {
17
+ preferred_username: username,
18
+ name,
19
+ picture: avatarUrl,
20
+ sub: hfUserId,
21
+ } = z
22
+ .object({
23
+ preferred_username: z.string(),
24
+ name: z.string(),
25
+ picture: z.string(),
26
+ sub: z.string(),
27
+ })
28
+ .parse(userData);
29
+
30
+ const existingUser = await collections.users.findOne({ hfUserId });
31
+ let userId = existingUser?._id;
32
+
33
+ if (existingUser) {
34
+ // update existing user if any
35
+ await collections.users.updateOne(
36
+ { _id: existingUser._id },
37
+ { $set: { username, name, avatarUrl } }
38
+ );
39
+ // refresh session cookie
40
+ refreshSessionCookie(cookies, existingUser.sessionId);
41
+ } else {
42
+ // user doesn't exist yet, create a new one
43
+ const { insertedId } = await collections.users.insertOne({
44
+ _id: new ObjectId(),
45
+ createdAt: new Date(),
46
+ updatedAt: new Date(),
47
+ username,
48
+ name,
49
+ avatarUrl,
50
+ hfUserId,
51
+ sessionId: locals.sessionId,
52
+ });
53
+
54
+ userId = insertedId;
55
+
56
+ // update pre-existing settings
57
+ const { matchedCount } = await collections.settings.updateOne(authCondition(locals), {
58
+ $set: { userId, updatedAt: new Date() },
59
+ $unset: { sessionId: "" },
60
+ });
61
+
62
+ if (!matchedCount) {
63
+ // create new default settings
64
+ await collections.settings.insertOne({
65
+ userId,
66
+ ethicsModalAcceptedAt: new Date(),
67
+ updatedAt: new Date(),
68
+ createdAt: new Date(),
69
+ ...DEFAULT_SETTINGS,
70
+ });
71
+ }
72
+ }
73
+
74
+ // migrate pre-existing conversations
75
+ await collections.conversations.updateMany(authCondition(locals), {
76
+ $set: { userId },
77
+ $unset: { sessionId: "" },
78
+ });
79
+ }