mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-14 18:19:42 +03:00
feat: remove mongo and all of its deps (#1525)
* feat: remove mongo and all of its deps * feat: remove all mongodb logic+thin out some unused deps
This commit is contained in:
@@ -1,144 +0,0 @@
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"iidx-sp","userID":1,"sessionID":"Q578fa997a13b92203dbc59082a452636631ff691","type":"Created","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":0},"timestamp":1778174638000,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qc252f411274450ce5698af2907997cbab60105d2","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":0},"timestamp":1778174660089,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q5d5e46543de37de7144aabde86aaeaf00498195a","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":1},"timestamp":1778174660094,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qc37df566339572b6fee3ca5edd4b3bf0314e29c5","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":2},"timestamp":1778174660099,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q8cbcd83ef2ee60cecb791c09e573cacb82614819","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":3},"timestamp":1778174660105,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q189cf02b87c556a9b3f871e303758dd88d16c96a","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":4},"timestamp":1778174660108,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qe40bb6742d397db1bfedc9aa2da386994054cb8e","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":5},"timestamp":1778174660112,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q7d9e5a5aa58b424f33253e67721fb416d43c0a8b","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":6},"timestamp":1778174660115,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qcafb063529a7746b14be61b5f8b8f785b4c0bb9d","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":7},"timestamp":1778174660121,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qdde44db5089244cc55f662c6a41ffb2fb714a8b8","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":8},"timestamp":1778174660131,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q6e8d8cbad5078faf2b592504808d09e213ed0129","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":9},"timestamp":1778174660136,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q8559d0c86bf3b2f8b15801d36f27fb876ea552b3","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":10},"timestamp":1778174660142,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qe37e28c4561537d764d26d3084f544d4f07fc794","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":11},"timestamp":1778174660147,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q6657d8805d106093b0dddb14cb504b937f132760","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":12},"timestamp":1778174660155,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q48f3114dd0c29c80252fd65a670d97fa4a600f82","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":13},"timestamp":1778174660162,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q990c934c1383480e3d37baf91bd6a765cf8b3a2a","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":14},"timestamp":1778174660165,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q7a417c5a2159bf29e7a01e5250a0711528697f8f","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":15},"timestamp":1778174660170,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q7e1c4f468c77e2fefc8f7c6883e2de759bdd25b9","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":16},"timestamp":1778174660174,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qee252e93a7d251e87e9a7fe6c17139b2d2f99e7a","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":17},"timestamp":1778174660178,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qa105eb34d8423b192e43c238f8ff91083e1bffc9","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":18},"timestamp":1778174660181,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q27daaebb4468a50aae51882418658cc1aa106a34","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":19},"timestamp":1778174660184,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q8c121fa8b73857c8ee117e700842b866e3baf6a7","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":20},"timestamp":1778174660199,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q7210852aa9d6b82fe8d99da03f02d86782285985","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":21},"timestamp":1778174660205,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qcfb1824558a1a99b1538dbd7432289a8be57b3e6","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":22},"timestamp":1778174660209,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q49efff3408ecd0c5b7e41548875f8e443d0dd7f9","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":23},"timestamp":1778174660217,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qc2618e82e8d9911531d1020c4d467859c9214b36","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":24},"timestamp":1778174660223,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q0f170b6b312d00be302e5cc0d4285da541524fbe","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":25},"timestamp":1778174660227,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qdb008e8c2209f91ad53ec0cdee933f67f6e5331e","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":26},"timestamp":1778174660233,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q045d94351296c45a1eaabae9a30029dc68318e19","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":27},"timestamp":1778174660236,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q39b379df7a10f4a72574f3680b52a7ef69751af4","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":28},"timestamp":1778174660245,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q811a4764a3d2534fb4b1da6cbaea1d407a60d0b5","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":29},"timestamp":1778174660248,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qb79f15ac66e1965c35be2004006e577e071b87b7","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":30},"timestamp":1778174660252,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q0f4611dfe6752e90e0ea7ad0a339aed51391d94a","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":31},"timestamp":1778174660257,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q9f0a302cbb6160468da3399669832c52ae7c5910","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":32},"timestamp":1778174660260,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q9418f31ea6a52ffaca93f0657abab15f648d3f24","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":33},"timestamp":1778174660263,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qb4db5061c31ee7743fd4c592a3136cee6c53225d","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":34},"timestamp":1778174660267,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qa7a6babb5c27931e45e0396d81c95272beee00f8","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":35},"timestamp":1778174660270,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q927681faa73a41ffbcc2b2e51bb3220656f7a27c","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":36},"timestamp":1778174660278,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q1edafb103d7f842bd3164bf2d77cca323ddc24d5","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":37},"timestamp":1778174660287,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qc93f8402727171169df28aa12c1852e59a00b0b4","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":38},"timestamp":1778174660294,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qae0736c538299e163942c0fd4fd5c34bcd8ab7c5","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":39},"timestamp":1778174660299,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qa8723107d169ea4e765d28d9dec764e119d4879e","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":40},"timestamp":1778174660303,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qe8e555ad077e1897eab4ece65580c27f1fd8b0bb","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":41},"timestamp":1778174660309,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qfef6ca0ce28485e788d6562db2527b7a8ddce498","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":42},"timestamp":1778174660314,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q5c68d2fe24dc80dfd015b2abcb89e4eba774feaf","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":43},"timestamp":1778174660317,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q4d88a6581e28b7024f020673feb45b75c1d101b3","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":44},"timestamp":1778174660321,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q7d8887d5d3fd265b73b1b29363d305ce13360c94","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":45},"timestamp":1778174660327,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q6f0fb12cb6e8aaf252a1d6040e00d391ea8464d4","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":46},"timestamp":1778174660345,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qd4ec361100e6590f313350b9e3e9150d6387e2ba","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":47},"timestamp":1778174660348,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qe0381db5230e8b7dd310bf2d23d8427dbbb84822","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":48},"timestamp":1778174660350,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q31e15ba0f00783be89720b1aec46550b58976d3b","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":49},"timestamp":1778174660354,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qba5cc5abba6d6f03e6868628b3a4de1461abbc4c","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":50},"timestamp":1778174660360,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qc9d38a91243fdd685b83f61292103dc53da0d4cf","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":51},"timestamp":1778174660365,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q8dabfc9ce6038eb05610efabd1bbe169fe060c46","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":52},"timestamp":1778174660369,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q44c21d76c1f0aceca460bbd6dd9f32e3ba5c7732","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":53},"timestamp":1778174660372,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qba93137b42cdac7a6a33e6b0b8936879e526089c","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":54},"timestamp":1778174660378,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qac474a57764cbaeb6b055f0c98d12bd1dfc75497","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":55},"timestamp":1778174660383,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q54aed62223d5f4a774bbf7283e1d71bffa6a8877","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":56},"timestamp":1778174660389,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qed26c346b3a65815a31b5228e4e632f42f62fa39","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":57},"timestamp":1778174660401,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qaf7a4851b6f7ddf6e9c186c8418b6130bad70798","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":58},"timestamp":1778174660405,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q801affd326b159befcc5ec230f6eac3149603622","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":59},"timestamp":1778174660415,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qc69f6c9c7ab8ecdbcad074aab2f796f052f5d0f8","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":60},"timestamp":1778174660430,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q8b639b6300480f0ecfd5db79b36d2e3c132a54ac","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":61},"timestamp":1778174660438,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qf85158125f1aeaea09a2827da2b3d7d45aba72bc","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":62},"timestamp":1778174660443,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qd9ea6889198ee5e8a099baa6c91bcffa7eca95c3","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":63},"timestamp":1778174660449,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q0585eab487fb017863aaac9f8768e9d1b72cc123","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":64},"timestamp":1778174660453,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q567de464814f23d575e9004f505e1a322be5de31","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":65},"timestamp":1778174660456,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q0b7df3b5afa5e0b50e42c67639b43f175072ce22","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":66},"timestamp":1778174660462,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qdc9e3f944ec4759285e25a25c7a372303c7e18b9","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":67},"timestamp":1778174660465,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q38e82752ad57d923af66a12471ade3d9b883f958","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":68},"timestamp":1778174660469,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q452773b20751ea073659360a00cec1b3861b85ef","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":69},"timestamp":1778174660474,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qd55da19de534bd8038ed3e52b82a890567b6ad19","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":70},"timestamp":1778174660482,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q314a9e746a8b962a00703e9d84a93190c0e915f3","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":71},"timestamp":1778174660487,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qfa374369968d49856bca05e82f36b4d7145ed1ec","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":72},"timestamp":1778174660490,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q76b0603b5a47f4a0236c9087dfbbfeba1fb3bc7b","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":73},"timestamp":1778174660506,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q203f0fe1b402e9a0448ac39e6cd1db01d511a50a","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":74},"timestamp":1778174660514,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q8c27ac1b60300274df3d669847921d35d01289ae","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":75},"timestamp":1778174660520,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q25b244ba922b0d4f1092bf419ea9788b66708f7c","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":76},"timestamp":1778174660524,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q70d5b9a4d9b79fff4f66b8ae300cf64fc488f02a","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":77},"timestamp":1778174660531,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q20e1e28b721c3b0733cd924e720704962e54e3e8","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":78},"timestamp":1778174660553,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qadc97ca77718ccfb8bde7ac9593edaac4bb87a6f","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":79},"timestamp":1778174660569,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q4b2feb98be77e6882f84d6a9cbeca42887abdbf3","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":80},"timestamp":1778174660573,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q77b97d7a7b1bfda84da3f472cc32bacec267219f","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":81},"timestamp":1778174660581,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q424e90d547303ea4923e0258cfc2d8d392679cae","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":82},"timestamp":1778174660586,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q97f2bc192eeb70752c9e30cfe7dcde99bc47e690","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":83},"timestamp":1778174660590,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q59001766f9b8a52c8dcc97bf1d042b454708eae4","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":84},"timestamp":1778174660593,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q00e706d002c2e9ea6086371b935a50b282706d39","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":85},"timestamp":1778174660598,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q0086ff797f2b95359b9472c107f20ae075898b21","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":86},"timestamp":1778174660601,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qe989edaca5a2f24b8d604bf98ad03a48baf0f010","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":87},"timestamp":1778174660606,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q12ef1db3064ea85095e2ff9d74a45a21d7ccc9b5","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":88},"timestamp":1778174660622,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q660bd3bb616c87eeaa3df3b65f25f1a98c57af10","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":89},"timestamp":1778174660626,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q1d038349e88718833f4acd084ca6940a66deb9c4","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":90},"timestamp":1778174660636,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q11339defac1468d048e2b57b1b4d2cc67751ac86","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":91},"timestamp":1778174660639,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q44a76aab9debe86da79c4b870f83d88f76dc4118","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":92},"timestamp":1778174660642,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qe1adc2f7ec768f394813223ed21bd2b571969a4a","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":93},"timestamp":1778174660652,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q489661edfc15cfc92c3834eceb309f1e2d4bb893","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":94},"timestamp":1778174660660,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q489661edfc15cfc92c3834eceb309f1e2d4bb893","type":"Appended","repeatSessionInThisLoad":true,"willPush":false,"runId":"post-fix","priorReturnCount":95},"timestamp":1778174660671,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q06014b95faecab25d0f47b051f1e38d96af5ea7d","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":95},"timestamp":1778174660680,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qdcf58e13a815753fe75b38a564c8e581fef42d17","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":96},"timestamp":1778174660684,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q016e78c91f15ef13016dedc24a16c91284de26ba","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":97},"timestamp":1778174660688,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q12b6a4887a80ecc13a74079376f1259c5cc40e86","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":98},"timestamp":1778174660690,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q577397051d7966d96c1fb5cf649654f3b8dd6edb","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":99},"timestamp":1778174660699,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qa0b0c7a4bb6862f33febafdc80de2efe2d2adb2d","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":100},"timestamp":1778174660702,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qa17b785caf6e37dc996afe0f218baa008a63fb1d","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":101},"timestamp":1778174660705,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qa5bac01b00d12082158013a264ae8c2b10c837c6","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":102},"timestamp":1778174660708,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q6c6e8a526936a57adab446b28d870ae68bee3910","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":103},"timestamp":1778174660715,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q190d862f7853a94510ae603cefa8fec2d0010c17","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":104},"timestamp":1778174660719,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q68b4212ad3a72c9029272f3c8dba2909f6c717d5","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":105},"timestamp":1778174660724,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q61e5881c2e33e7b9c47afc832e54fa23c07657a3","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":106},"timestamp":1778174660729,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q151c94c4fb26eab70ff8db30652dac361d09d11b","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":107},"timestamp":1778174660736,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qb19bcd6ae2f4c2b1bee880fd6021214bfbf05604","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":108},"timestamp":1778174660741,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q952a8c756b74a7c556f19edb77628dbb04c0800b","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":109},"timestamp":1778174660744,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q673e224863b4bd6073c46591576644da16c4324f","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":110},"timestamp":1778174660750,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qd814902d29a9e34622ddc9dfa5089dc8c57f80bc","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":111},"timestamp":1778174660754,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q3ebba12414b002bbde0e9f5ca58134ca37071867","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":112},"timestamp":1778174660758,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qd073fc8da813e7fefa8fe1d6352714f48dd0ff7a","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":113},"timestamp":1778174660761,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q401bdb009b493618518bd7c4a066bd54acdabc10","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":114},"timestamp":1778174660768,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q2ce7b164c51b3bf0e4dcfd4b9b2228892815b856","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":115},"timestamp":1778174660772,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q0f405f56021d7115617fde39cadcaf6b89866705","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":116},"timestamp":1778174660778,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q9b7187fe54b150ef7e249c75257cd2b252084e85","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":117},"timestamp":1778174660797,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q70dc4b3da3675781ee314b6f44f95187cba7d3c1","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":118},"timestamp":1778174660809,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q60b1a04f2e28aab22c166378e89871125de6d563","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":119},"timestamp":1778174660813,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q7fc3d41514a6629a92e0c89674848e4224fed28d","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":120},"timestamp":1778174660818,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q4d84680f2603292d7f650921e4fdea73b0a6c6ca","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":121},"timestamp":1778174660822,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q3d517c038e36209ee4256306c34690fd53a8306e","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":122},"timestamp":1778174660826,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qaa50b6fadf96a3e12d60cca87f87cb5f7f462c69","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":123},"timestamp":1778174660843,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qc769036b5a38cb57c5ee1bf6f17360ed47f5ea54","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":124},"timestamp":1778174660858,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q84b63c6eefe648254d00a5ec71dc3808339dcce2","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":125},"timestamp":1778174660862,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q7a1ed0017220e8b96e861fa6289129b7e7f0cb33","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":126},"timestamp":1778174660867,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q86e26421f03118913a2f7a9adf630a3b10c879f9","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":127},"timestamp":1778174660873,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q7554af752b6d25d731396bdb31432e41e77ac694","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":128},"timestamp":1778174660877,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q100d03555b2f9ffbc2a9da5bb2e973b75356f9e7","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":129},"timestamp":1778174660883,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q9a42fb36c8479ca5c84e5d7c9e9d5103ae61e8fb","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":130},"timestamp":1778174660888,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q935ab1b2ff13b6a00f83bff9dc92e1b816eb8c98","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":131},"timestamp":1778174660893,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q97c8f2cf1053a36e4966d82265ecea04d1051f1e","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":132},"timestamp":1778174660904,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q5ded6f7f8d7f5d37223f280f2d900da118f7a193","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":133},"timestamp":1778174660908,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q7d5db734e4edef4477ef4cab6b6427fb9f45e4a8","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":134},"timestamp":1778174660911,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q948fd05d2c9ed5d49aa75daace5658c6a64aa74c","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":135},"timestamp":1778174660919,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q10363461fbf6a6be6dd31c1f1e978b0c0a9c08a9","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":136},"timestamp":1778174660923,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Q55b15f2d894f8e3bcd768ed1510da91c2158992f","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":137},"timestamp":1778174660928,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qb87640a3371880e254627383c3b23cf5230527c9","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":138},"timestamp":1778174660936,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"sessions.ts:LoadScoresIntoSessions:beforePush","message":"sessionInfoReturn push","data":{"game":"jubeat","userID":1,"sessionID":"Qf199ca3f596016185da0299200f1c4322656cce1","type":"Appended","repeatSessionInThisLoad":false,"willPush":true,"runId":"post-fix","priorReturnCount":139},"timestamp":1778174660941,"hypothesisId":"H1"}
|
||||
{"sessionId":"7b27d8","location":"finalize-import-pg.ts:finalizeImportToPostgres:entry","message":"finalizeImportToPostgres called","data":{"importID":"4584b4feb70692dba1707a42c243a56a4b3be56b","runId":"post-fix"},"timestamp":1778174661244,"hypothesisId":"H2"}
|
||||
{"sessionId":"7b27d8","location":"finalize-import-pg.ts:import_session:preInsert","message":"import_session insert payload stats","data":{"importID":"4584b4feb70692dba1707a42c243a56a4b3be56b","createdLen":140,"uniqueSessionCount":140,"dupSessionIds":[],"runId":"post-fix"},"timestamp":1778174661246,"hypothesisId":"H1"}
|
||||
@@ -1,80 +0,0 @@
|
||||
/**
|
||||
* Seed / upsert users into MongoDB (standalone; not wired to this repo's app).
|
||||
*
|
||||
* Usage:
|
||||
* mongosh "mongodb://127.0.0.1:27017/YOUR_DB_NAME" --file scripts/mongo-insert-users.mongosh.js
|
||||
*
|
||||
* Override IDs (comma- or space-separated):
|
||||
* USER_IDS="1001,1002" mongosh "mongodb://127.0.0.1:27017/YOUR_DB" --file scripts/mongo-insert-users.mongosh.js
|
||||
*
|
||||
* If USER_IDS is unset, defaults to 1, 2, 3 (edit DEFAULT_USER_IDS below).
|
||||
*/
|
||||
|
||||
const DEFAULT_USER_IDS = [1, 2, 3];
|
||||
|
||||
const USER_IDS =
|
||||
typeof process !== "undefined" && process.env.USER_IDS
|
||||
? process.env.USER_IDS.split(/[\s,]+/)
|
||||
.map((s) => parseInt(s, 10))
|
||||
.filter((n) => !Number.isNaN(n))
|
||||
: DEFAULT_USER_IDS;
|
||||
|
||||
const PASSWORD_HASH = "$2b$12$XXXXXXXjo9lrnLGCree4e.tm7PSWwA5N.Lb8yLDzP8ytDt6cZAtuy";
|
||||
|
||||
for (const userId of USER_IDS) {
|
||||
const username = `mysteryman${userId}`;
|
||||
const email = `${username}@example.com`;
|
||||
|
||||
db.getCollection("users").updateOne(
|
||||
{ id: userId },
|
||||
{
|
||||
$set: {
|
||||
id: userId,
|
||||
username,
|
||||
usernameLowercase: username,
|
||||
about: "I'm a fairly nondescript person.",
|
||||
socialMedia: {},
|
||||
status: null,
|
||||
customBannerLocation: null,
|
||||
customPfpLocation: null,
|
||||
joinDate: 1638903236112,
|
||||
lastSeen: 1772301833519,
|
||||
authLevel: 1,
|
||||
badges: [],
|
||||
},
|
||||
},
|
||||
{ upsert: true },
|
||||
);
|
||||
|
||||
db.getCollection("user-private-information").updateOne(
|
||||
{ userID: userId },
|
||||
{
|
||||
$set: {
|
||||
userID: userId,
|
||||
password: PASSWORD_HASH,
|
||||
email,
|
||||
},
|
||||
},
|
||||
{ upsert: true },
|
||||
);
|
||||
|
||||
db.getCollection("user-settings").updateOne(
|
||||
{ userID: userId },
|
||||
{
|
||||
$set: {
|
||||
userID: userId,
|
||||
preferences: {
|
||||
developerMode: false,
|
||||
advancedMode: false,
|
||||
invisible: false,
|
||||
contentiousContent: false,
|
||||
deletableScores: false,
|
||||
},
|
||||
following: [],
|
||||
},
|
||||
},
|
||||
{ upsert: true },
|
||||
);
|
||||
}
|
||||
|
||||
print(`Upserted ${USER_IDS.length} user(s): ${USER_IDS.join(", ")}`);
|
||||
@@ -1,52 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# restore-mongo.sh
|
||||
# Runs mongorestore (via a throwaway Docker container) against MongoDB
|
||||
# already running on localhost:27017. Nothing is installed to the system.
|
||||
#
|
||||
# Usage:
|
||||
# ./restore-mongo.sh <dump.gz> [target-db-name]
|
||||
#
|
||||
# Env overrides:
|
||||
# MONGO_VERSION (default: 7)
|
||||
# MONGO_PORT (default: 27017)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
DUMP_FILE="${1:-dump.gz}"
|
||||
MONGO_VERSION="${MONGO_VERSION:-7}"
|
||||
MONGO_PORT="${MONGO_PORT:-27017}"
|
||||
DB_NAME="${2:-}"
|
||||
|
||||
if ! command -v docker &>/dev/null; then
|
||||
echo "ERROR: docker is not in PATH." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$DUMP_FILE" ]]; then
|
||||
echo "ERROR: dump file not found: $DUMP_FILE" >&2
|
||||
echo "Usage: $0 <path/to/dump.gz> [db-name-override]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DUMP_FILE="$(realpath "$DUMP_FILE")"
|
||||
DUMP_DIR="$(dirname "$DUMP_FILE")"
|
||||
DUMP_BASENAME="$(basename "$DUMP_FILE")"
|
||||
|
||||
RESTORE_ARGS=(--archive="/dump/${DUMP_BASENAME}" --gzip --verbose)
|
||||
if [[ -n "$DB_NAME" ]]; then
|
||||
RESTORE_ARGS+=(--db "$DB_NAME")
|
||||
fi
|
||||
|
||||
echo "Restoring $DUMP_BASENAME -> localhost:$MONGO_PORT ..."
|
||||
|
||||
docker run --rm \
|
||||
--network host \
|
||||
-v "${DUMP_DIR}:/dump:ro" \
|
||||
"mongo:$MONGO_VERSION" \
|
||||
mongorestore \
|
||||
--host "localhost:${MONGO_PORT}" \
|
||||
"${RESTORE_ARGS[@]}"
|
||||
|
||||
echo "Done."
|
||||
@@ -1,22 +1,44 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
/**
|
||||
* ts_autoinherit: Move pinned dependency versions from workspace package.json files
|
||||
* into the root catalog so every package uses `catalog:` references.
|
||||
*
|
||||
* Usage:
|
||||
* bun .scripts/ts_autoinherit.js # apply changes in place
|
||||
* bun .scripts/ts_autoinherit.js --check # report violations, exit 1 if any
|
||||
*/
|
||||
|
||||
import path from "node:path";
|
||||
import fs from "node:fs";
|
||||
import { Glob } from "bun";
|
||||
import { execSync } from "node:child_process";
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const check = args.includes("--check");
|
||||
|
||||
// ANSI colours
|
||||
const RED = "\x1b[31m";
|
||||
const GREEN = "\x1b[32m";
|
||||
const YELLOW = "\x1b[33m";
|
||||
const BOLD = "\x1b[1m";
|
||||
const DIM = "\x1b[2m";
|
||||
const RESET = "\x1b[0m";
|
||||
|
||||
const cwd = path.resolve(__dirname, "..");
|
||||
process.chdir(cwd);
|
||||
|
||||
const rootPkg = JSON.parse(fs.readFileSync(path.join(cwd, "package.json"), "utf8"));
|
||||
|
||||
/** @type {Array<{pkgPath: string, key: string, dependency: string, version: string}>} */
|
||||
const violations = [];
|
||||
|
||||
for (const workspacePattern of rootPkg.workspaces) {
|
||||
// "typescript/*" + "/package.json" => "typescript/*/package.json" (one workspace dir only).
|
||||
// "typescript/*" + "*/package.json" would merge *+* into ** and match nested paths / node_modules.
|
||||
let glob = new Glob(workspacePattern + "/package.json");
|
||||
|
||||
for (const pkgPath of glob.scanSync(".")) {
|
||||
console.log(pkgPath);
|
||||
|
||||
const pkg = JSON.parse(fs.readFileSync(path.join(cwd, pkgPath), "utf8"));
|
||||
|
||||
for (const key of ["devDependencies", "dependencies"]) {
|
||||
@@ -29,15 +51,45 @@ for (const workspacePattern of rootPkg.workspaces) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pkg[key][dependency] = "catalog:";
|
||||
rootPkg.catalog[dependency] = version;
|
||||
violations.push({ pkgPath, key, dependency, version });
|
||||
|
||||
if (!check) {
|
||||
pkg[key][dependency] = "catalog:";
|
||||
rootPkg.catalog[dependency] = version;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, "\t"));
|
||||
if (!check) {
|
||||
console.log(pkgPath);
|
||||
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, "\t"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync("package.json", JSON.stringify(rootPkg, null, "\t"));
|
||||
|
||||
execSync("just fmt", { stdio: "inherit" });
|
||||
if (check) {
|
||||
if (violations.length === 0) {
|
||||
console.log(`${GREEN}${BOLD}✓ All dependencies use catalog: references.${RESET}`);
|
||||
} else {
|
||||
const byFile = /** @type {Map<string, typeof violations>} */ (new Map());
|
||||
for (const v of violations) {
|
||||
const list = byFile.get(v.pkgPath) ?? [];
|
||||
list.push(v);
|
||||
byFile.set(v.pkgPath, list);
|
||||
}
|
||||
for (const [pkgPath, vs] of byFile) {
|
||||
console.log(`\n${BOLD}${pkgPath}${RESET}`);
|
||||
for (const { key, dependency, version } of vs) {
|
||||
console.log(` ${RED}✗${RESET} ${DIM}${key}:${RESET} ${dependency}@${version}`);
|
||||
}
|
||||
}
|
||||
console.log(
|
||||
`\n${YELLOW}${BOLD}⚠ ${violations.length} dep(s) not using catalog: references.${RESET}`,
|
||||
);
|
||||
console.log(`${DIM} Run \`.scripts/ts_autoinherit.js\` to fix automatically.${RESET}`);
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
fs.writeFileSync("package.json", JSON.stringify(rootPkg, null, "\t"));
|
||||
execSync("just fmt", { stdio: "inherit" });
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import "Justfile-java"
|
||||
import "Justfile-db"
|
||||
import "Justfile-backup"
|
||||
import "Justfile-misc"
|
||||
import "Justfile-migrate"
|
||||
import "Justfile-observability"
|
||||
import "Justfile-test"
|
||||
import "Justfile-bench"
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
# Justfile for the v3 migration. To be deleted once everything has moved over.
|
||||
|
||||
# do the full migration suite.
|
||||
migrate-v3:
|
||||
just gen
|
||||
just seeds-v3-migrate
|
||||
just mongo-to-pg kamai kamai
|
||||
just mongo-to-pg boku boku
|
||||
|
||||
# Migrate all MongoDB data into Postgres (run once, requires both DBs to be reachable).
|
||||
mongo-to-pg POSTGRES_DB_NAME MONGO_DB_NAME:
|
||||
just db-cheap-reset "{{ POSTGRES_DB_NAME }}"
|
||||
|
||||
cd typescript/server && VERSION=$(jq -r .version package.json) COMMIT_HASH=$(git rev-parse --short HEAD) POSTGRES_URL="{{POSTGRES_URL}}/{{POSTGRES_DB_NAME}}" MONGO_URL="mongodb://tachi-mongo/{{MONGO_DB_NAME}}" \
|
||||
bun run src/scripts/migrate-to-postgres.ts
|
||||
|
||||
# Rollback the seeds to the state stored somewhere (v2 keeps seeds in seeds/collections, v3 in db/seeds).
|
||||
seeds-rollback ROLLBACK_DIR="/host-pc/Documents/Repos/Tachi/seeds/collections":
|
||||
rsync -avz --delete "{{ROLLBACK_DIR}}/" db/seeds
|
||||
|
||||
seeds-test:
|
||||
cd "typescript/seeds-scripts" && ./run-tests.sh
|
||||
|
||||
seeds-v3-migrate: seeds-rollback
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "typescript/seeds-scripts"
|
||||
|
||||
for script in $(ls -1 rerunners/v3/*.ts); do
|
||||
bun run "./$script"
|
||||
done
|
||||
just seeds-test
|
||||
+4
-6
@@ -7,6 +7,8 @@ check:
|
||||
evaluate "just fmt-check"
|
||||
evaluate "bun run --filter '*' lint"
|
||||
evaluate "just typecheck"
|
||||
evaluate "bun .scripts/ts_machete.js"
|
||||
evaluate "bun .scripts/ts_autoinherit.js --check"
|
||||
|
||||
post_evaluate
|
||||
|
||||
@@ -66,12 +68,8 @@ test-typescript FILTER="*":
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# `tachi-postgres-test` (tmpfs + fsync=off, see docker-compose-dev.yml) is the
|
||||
# fast path; falls back to the dev PG if the dedicated service isn't running.
|
||||
export POSTGRES_TEST_HOST="${POSTGRES_TEST_HOST:-tachi-postgres-test}"
|
||||
# Keep POSTGRES_TEST_URL in sync so the `*-db-test-template-reset` recipes
|
||||
# (called from vitest.globalSetup.ts) hit the same host the workers will.
|
||||
export POSTGRES_TEST_URL="${POSTGRES_TEST_URL:-postgresql://tachi:tachi@${POSTGRES_TEST_HOST}}"
|
||||
export POSTGRES_TEST_HOST="tachi-postgres-test"
|
||||
export POSTGRES_TEST_URL="postgresql://tachi:tachi@${POSTGRES_TEST_HOST}"
|
||||
bun run --elide-lines=0 --sequential --filter '{{FILTER}}' test -- --reporter=default --reporter=junit --outputFile.junit=test-results/junit.xml
|
||||
|
||||
# Summarize Vitest v8 coverage across workspaces (see typescript/coverage-tools).
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"eslint": "^9.20.1",
|
||||
"kanel": "^3.16.1",
|
||||
"kanel-kysely": "^0.8.0",
|
||||
"ts-node": "10.9.1",
|
||||
"typescript": "5.9.3",
|
||||
},
|
||||
},
|
||||
@@ -56,8 +55,6 @@
|
||||
"prudence": "catalog:",
|
||||
"tachi-common": "workspace:*",
|
||||
"tachi-db": "workspace:*",
|
||||
"ts-node": "catalog:",
|
||||
"tsconfig-paths": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -135,7 +132,6 @@
|
||||
"name": "tachi-common",
|
||||
"version": "2.4.0-dev",
|
||||
"dependencies": {
|
||||
"@types/mongodb": "catalog:",
|
||||
"prudence": "catalog:",
|
||||
"zod": "catalog:",
|
||||
},
|
||||
@@ -207,7 +203,6 @@
|
||||
"@types/pg": "catalog:",
|
||||
"@typescript/native-preview": "catalog:",
|
||||
"eslint-config-tachi": "workspace:*",
|
||||
"ts-node": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
},
|
||||
},
|
||||
@@ -306,7 +301,6 @@
|
||||
"devDependencies": {
|
||||
"@typescript/native-preview": "catalog:",
|
||||
"eslint-config-tachi": "workspace:*",
|
||||
"tsconfig-paths": "catalog:",
|
||||
},
|
||||
},
|
||||
"typescript/seeds-webui": {
|
||||
@@ -320,10 +314,7 @@
|
||||
"codemirror": "catalog:",
|
||||
"comlink": "catalog:",
|
||||
"fast-json-patch": "catalog:",
|
||||
"nanoid": "catalog:",
|
||||
"natural-compare": "catalog:",
|
||||
"react": "catalog:",
|
||||
"react-bootstrap": "catalog:",
|
||||
"react-dom": "catalog:",
|
||||
"react-query": "catalog:",
|
||||
"react-router-dom": "catalog:",
|
||||
@@ -331,18 +322,14 @@
|
||||
"zod": "catalog:",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/natural-compare": "catalog:",
|
||||
"@types/react": "catalog:",
|
||||
"@types/react-dom": "catalog:",
|
||||
"@types/react-router-dom": "catalog:",
|
||||
"@typescript/native-preview": "catalog:",
|
||||
"@vitejs/plugin-react": "catalog:",
|
||||
"bootstrap-scss": "catalog:",
|
||||
"eslint-config-tachi": "workspace:*",
|
||||
"sass": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vite-plugin-html": "catalog:",
|
||||
},
|
||||
},
|
||||
"typescript/server": {
|
||||
@@ -352,26 +339,17 @@
|
||||
"@aws-sdk/client-s3": "catalog:",
|
||||
"@octokit/app": "catalog:",
|
||||
"@types/bcryptjs": "catalog:",
|
||||
"@types/bunyan": "catalog:",
|
||||
"@types/connect-redis": "catalog:",
|
||||
"@types/express": "catalog:",
|
||||
"@types/express-rate-limit": "catalog:",
|
||||
"@types/express-serve-static-core": "catalog:",
|
||||
"@types/express-session": "catalog:",
|
||||
"@types/mongodb": "catalog:",
|
||||
"@types/multer": "catalog:",
|
||||
"@types/node": "catalog:",
|
||||
"@types/node-fetch": "catalog:",
|
||||
"@types/nodemailer": "catalog:",
|
||||
"@types/rate-limit-redis": "catalog:",
|
||||
"@types/redis": "catalog:",
|
||||
"bcryptjs": "catalog:",
|
||||
"bliss": "workspace:*",
|
||||
"bms-table-loader": "catalog:",
|
||||
"bullmq": "catalog:",
|
||||
"bunyan": "catalog:",
|
||||
"commander": "catalog:",
|
||||
"connect-redis": "catalog:",
|
||||
"cron-parser": "catalog:",
|
||||
"csv-parse": "catalog:",
|
||||
"deepmerge": "catalog:",
|
||||
@@ -379,7 +357,6 @@
|
||||
"express": "catalog:",
|
||||
"express-async-errors": "catalog:",
|
||||
"express-prom-bundle": "catalog:",
|
||||
"express-rate-limit": "catalog:",
|
||||
"express-session": "catalog:",
|
||||
"fast-json-stable-hash": "catalog:",
|
||||
"fast-xml-parser": "catalog:",
|
||||
@@ -387,8 +364,6 @@
|
||||
"html-entities": "catalog:",
|
||||
"kysely": "catalog:",
|
||||
"lodash": "catalog:",
|
||||
"mongodb": "catalog:",
|
||||
"monk": "catalog:",
|
||||
"multer": "catalog:",
|
||||
"node-cache": "catalog:",
|
||||
"node-fetch": "catalog:",
|
||||
@@ -397,17 +372,15 @@
|
||||
"pg": "catalog:",
|
||||
"prom-client": "catalog:",
|
||||
"prudence": "catalog:",
|
||||
"rate-limit-redis": "catalog:",
|
||||
"redis": "catalog:",
|
||||
"rg-stats": "catalog:",
|
||||
"tachi-common": "workspace:*",
|
||||
"tachi-db": "workspace:*",
|
||||
"tachi-db-migration-engine": "workspace:*",
|
||||
"ts-node": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"zod": "catalog:",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "catalog:",
|
||||
"@types/pg": "catalog:",
|
||||
"@types/supertest": "catalog:",
|
||||
"@typescript/native-preview": "catalog:",
|
||||
@@ -415,7 +388,6 @@
|
||||
"eslint-config-tachi": "workspace:*",
|
||||
"node-mocks-http": "catalog:",
|
||||
"supertest": "catalog:",
|
||||
"tsconfig-paths": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
},
|
||||
},
|
||||
@@ -438,18 +410,10 @@
|
||||
},
|
||||
},
|
||||
},
|
||||
"patchedDependencies": {
|
||||
"monk@7.3.4": "patches/monk@7.3.4.patch",
|
||||
"@types/mongodb@3.6.20": "patches/@types__mongodb@3.6.20.patch",
|
||||
},
|
||||
"overrides": {
|
||||
"@types/bson": "1.0.11",
|
||||
"@types/express": "4.17.13",
|
||||
"@types/express-serve-static-core": "4.17.29",
|
||||
"@types/mongodb": "3.6.20",
|
||||
"@types/node": "20.19.37",
|
||||
"mongodb": "3.6.3",
|
||||
"ts-node": "10.9.1",
|
||||
"typescript": "5.9.3",
|
||||
},
|
||||
"catalog": {
|
||||
@@ -473,11 +437,9 @@
|
||||
"@octokit/webhooks-types": "7.5.1",
|
||||
"@sqlite.org/sqlite-wasm": "^3.51.2-build9",
|
||||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/bunyan": "^1.8.8",
|
||||
"@types/connect-redis": "0.0.18",
|
||||
"@types/bun": "1.3.14",
|
||||
"@types/eslint__js": "8.42.3",
|
||||
"@types/express": "4.17.13",
|
||||
"@types/express-rate-limit": "5.1.3",
|
||||
"@types/express-serve-static-core": "4.17.29",
|
||||
"@types/express-session": "1.17.4",
|
||||
"@types/history": "4.7.11",
|
||||
@@ -486,20 +448,17 @@
|
||||
"@types/lodash": "4.14.172",
|
||||
"@types/lodash.get": "^4.4.7",
|
||||
"@types/luxon": "^2.3.1",
|
||||
"@types/mongodb": "3.6.20",
|
||||
"@types/multer": "1.4.7",
|
||||
"@types/natural-compare": "^1.4.1",
|
||||
"@types/node": "*",
|
||||
"@types/node-fetch": "2.5.12",
|
||||
"@types/nodemailer": "^6.4.4",
|
||||
"@types/pg": "^8.11.11",
|
||||
"@types/rate-limit-redis": "1.7.4",
|
||||
"@types/react": "17.0.13",
|
||||
"@types/react-dom": "17.0.8",
|
||||
"@types/react-google-recaptcha": "2.1.2",
|
||||
"@types/react-router-bootstrap": "0.26.1",
|
||||
"@types/react-router-dom": "5.1.7",
|
||||
"@types/redis": "2.8.32",
|
||||
"@types/supertest": "2.0.11",
|
||||
"@types/tap": "^15.0.5",
|
||||
"@typescript-eslint/eslint-plugin": "^4.27.0",
|
||||
@@ -512,14 +471,11 @@
|
||||
"binary-parser": "^2.3.0",
|
||||
"bms-table-loader": "2.0.0",
|
||||
"bootstrap-scss": "^5.3.0",
|
||||
"bullmq": "1.68.4",
|
||||
"bunyan": "1.8.15",
|
||||
"chalk": "4",
|
||||
"cheerio": "^1.0.0-rc.12",
|
||||
"codemirror": "^6.0.2",
|
||||
"comlink": "^4.4.2",
|
||||
"commander": "^8.2.0",
|
||||
"connect-redis": "6.1.1",
|
||||
"cron-parser": "5.0.2",
|
||||
"csv-parse": "^5.0.3",
|
||||
"deepmerge": "^4.2.2",
|
||||
@@ -539,7 +495,6 @@
|
||||
"express": "4.20.0",
|
||||
"express-async-errors": "3.1.1",
|
||||
"express-prom-bundle": "^7.0.0",
|
||||
"express-rate-limit": "5.5.1",
|
||||
"express-session": "1.17.2",
|
||||
"fast-json-patch": "^3.1.1",
|
||||
"fast-json-stable-hash": "^1.0.3",
|
||||
@@ -555,8 +510,6 @@
|
||||
"lodash": "^4.17.23",
|
||||
"lodash.get": "^4.4.2",
|
||||
"luxon": "^2.3.1",
|
||||
"mongodb": "3.6.3",
|
||||
"monk": "^7.3.4",
|
||||
"multer": "2.0.1",
|
||||
"nanoid": "3.1.23",
|
||||
"natural-compare": "^1.4.0",
|
||||
@@ -573,7 +526,6 @@
|
||||
"pretty-bytes": "5.6.0",
|
||||
"prom-client": "15.1.3",
|
||||
"prudence": "0.10.0",
|
||||
"rate-limit-redis": "2.1.0",
|
||||
"react": "17.0.2",
|
||||
"react-async": "10.0.1",
|
||||
"react-bootstrap": "2.9.0-beta.0",
|
||||
@@ -585,14 +537,11 @@
|
||||
"react-router-bootstrap": "^0.25.0",
|
||||
"react-router-dom": "5.1.2",
|
||||
"react-select": "^5.6.1",
|
||||
"redis": "3.1.2",
|
||||
"rg-stats": "workspace:*",
|
||||
"sass": "^1.77.0",
|
||||
"supertest": "6.2.2",
|
||||
"sync-fetch": "^0.3.1",
|
||||
"tap": "^15.1.6",
|
||||
"ts-node": "^8.5.2",
|
||||
"tsconfig-paths": "3.12.0",
|
||||
"typescript": "^4.3.4",
|
||||
"typescript-eslint": "^8.24.1",
|
||||
"vite": "^8.0.0",
|
||||
@@ -849,8 +798,6 @@
|
||||
|
||||
"@connectrpc/protoc-gen-connect-es": ["@connectrpc/protoc-gen-connect-es@1.7.0", "", { "dependencies": { "@bufbuild/protobuf": "^1.10.0", "@bufbuild/protoplugin": "^1.10.0" }, "peerDependencies": { "@bufbuild/protoc-gen-es": "^1.10.0", "@connectrpc/connect": "1.7.0" }, "optionalPeers": ["@bufbuild/protoc-gen-es", "@connectrpc/connect"], "bin": { "protoc-gen-connect-es": "bin/protoc-gen-connect-es" } }, "sha512-g2rE799dxGgXtwSTBOJoSlzCy3HN0IX/Es8uKsCgXRmco8o277/bb5nz1X8TmvBooCBGNdtEdUDG50olcvS9jQ=="],
|
||||
|
||||
"@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="],
|
||||
|
||||
"@discordjs/builders": ["@discordjs/builders@0.5.0", "", { "dependencies": { "@sindresorhus/is": "^4.0.1", "discord-api-types": "^0.22.0", "ow": "^0.27.0", "ts-mixer": "^6.0.0", "tslib": "^2.3.0" } }, "sha512-HP5y4Rqw68o61Qv4qM5tVmDbWi4mdTFftqIOGRo33SNPpLJ1Ga3KEIR2ibKofkmsoQhEpLmopD1AZDs3cKpHuw=="],
|
||||
|
||||
"@discordjs/collection": ["@discordjs/collection@0.1.6", "", {}, "sha512-utRNxnd9kSS2qhyivo9lMlt5qgAUasH2gb7BEOn6p0efFh24gjGomHzWKMAPn2hEReOPQZCJaRKoURwRotKucQ=="],
|
||||
@@ -981,8 +928,6 @@
|
||||
|
||||
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
|
||||
|
||||
"@ioredis/commands": ["@ioredis/commands@1.5.1", "", {}, "sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw=="],
|
||||
|
||||
"@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
|
||||
|
||||
"@istanbuljs/schema": ["@istanbuljs/schema@0.1.3", "", {}, "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA=="],
|
||||
@@ -995,7 +940,7 @@
|
||||
|
||||
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
||||
|
||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="],
|
||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||
|
||||
"@kristiandupont/recase": ["@kristiandupont/recase@1.4.1", "", { "dependencies": { "ramda": "^0.30.0" } }, "sha512-e5t4YqhnRGbS9sU4N52cQgTn37qKwTsxDDcIuIkgPvX0UmnL+7eoOR6oFXeCib5zYuw03vYKpR55NBq+W43j1A=="],
|
||||
|
||||
@@ -1007,18 +952,6 @@
|
||||
|
||||
"@marijn/find-cluster-break": ["@marijn/find-cluster-break@1.0.2", "", {}, "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g=="],
|
||||
|
||||
"@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="],
|
||||
|
||||
"@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw=="],
|
||||
|
||||
"@msgpackr-extract/msgpackr-extract-linux-arm": ["@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3", "", { "os": "linux", "cpu": "arm" }, "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw=="],
|
||||
|
||||
"@msgpackr-extract/msgpackr-extract-linux-arm64": ["@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg=="],
|
||||
|
||||
"@msgpackr-extract/msgpackr-extract-linux-x64": ["@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg=="],
|
||||
|
||||
"@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ=="],
|
||||
|
||||
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="],
|
||||
|
||||
"@nivo/annotations": ["@nivo/annotations@0.79.1", "", { "dependencies": { "@nivo/colors": "0.79.1", "@react-spring/web": "9.3.1", "lodash": "^4.17.21" }, "peerDependencies": { "@nivo/core": "0.79.0", "react": ">= 16.14.0 < 18.0.0" } }, "sha512-lYso9Luu0maSDtIufwvyVt2+Wue7R9Fh3CIjuRDmNR72UjAgAVEcCar27Fy865UXGsj2hRJZ7KY/1s6kT3gu/w=="],
|
||||
@@ -1257,14 +1190,6 @@
|
||||
|
||||
"@swc/helpers": ["@swc/helpers@0.5.19", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-QamiFeIK3txNjgUTNppE6MiG3p7TdninpZu0E0PbqVh1a9FNLT2FRhisaa4NcaX52XVhA5l7Pk58Ft7Sqi/2sA=="],
|
||||
|
||||
"@tsconfig/node10": ["@tsconfig/node10@1.0.12", "", {}, "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ=="],
|
||||
|
||||
"@tsconfig/node12": ["@tsconfig/node12@1.0.11", "", {}, "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag=="],
|
||||
|
||||
"@tsconfig/node14": ["@tsconfig/node14@1.0.3", "", {}, "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow=="],
|
||||
|
||||
"@tsconfig/node16": ["@tsconfig/node16@1.0.4", "", {}, "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA=="],
|
||||
|
||||
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
|
||||
|
||||
"@types/aws-lambda": ["@types/aws-lambda@8.10.161", "", {}, "sha512-rUYdp+MQwSFocxIOcSsYSF3YYYC/uUpMbCY/mbO21vGqfrEYvNSoPyKYDj6RhXXpPfS0KstW9RwG3qXh9sL7FQ=="],
|
||||
@@ -1273,16 +1198,12 @@
|
||||
|
||||
"@types/body-parser": ["@types/body-parser@1.19.6", "", { "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g=="],
|
||||
|
||||
"@types/bson": ["@types/bson@1.0.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-j+UcCWI+FsbI5/FQP/Kj2CXyplWAz39ktHFkXk84h7dNblKRSoNJs95PZFRd96NQGqsPEPgeclqnznWZr14ZDA=="],
|
||||
|
||||
"@types/bunyan": ["@types/bunyan@1.8.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-758fRH7umIMk5qt5ELmRMff4mLDlN+xyYzC+dkPTdKwbSkJFvz6xwyScrytPU0QIBbRRwbiE8/BIg8bpajerNQ=="],
|
||||
"@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="],
|
||||
|
||||
"@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="],
|
||||
|
||||
"@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="],
|
||||
|
||||
"@types/connect-redis": ["@types/connect-redis@0.0.18", "", { "dependencies": { "@types/express": "*", "@types/express-session": "*", "@types/ioredis": "*", "@types/redis": "^2.8.0" } }, "sha512-iGygGbXgPIr94DEAuoluWhzre3c2/ew5NPlbW9IWvwCTXMM1YCmc7M9wpXMkYqt6kB9aO1sjZnmDzyugUu+2vQ=="],
|
||||
|
||||
"@types/cookiejar": ["@types/cookiejar@2.1.5", "", {}, "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q=="],
|
||||
|
||||
"@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
|
||||
@@ -1297,8 +1218,6 @@
|
||||
|
||||
"@types/express": ["@types/express@4.17.13", "", { "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.18", "@types/qs": "*", "@types/serve-static": "*" } }, "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA=="],
|
||||
|
||||
"@types/express-rate-limit": ["@types/express-rate-limit@5.1.3", "", { "dependencies": { "@types/express": "*" } }, "sha512-H+TYy3K53uPU2TqPGFYaiWc2xJV6+bIFkDd/Ma2/h67Pa6ARk9kWE0p/K9OH1Okm0et9Sfm66fmXoAxsH2PHXg=="],
|
||||
|
||||
"@types/express-serve-static-core": ["@types/express-serve-static-core@4.17.29", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*" } }, "sha512-uMd++6dMKS32EOuw1Uli3e3BPgdLIXmezcfHv7N4c1s3gkhikBplORPpMq3fuWkxncZN1reb16d5n8yhQ80x7Q=="],
|
||||
|
||||
"@types/express-session": ["@types/express-session@1.17.4", "", { "dependencies": { "@types/express": "*" } }, "sha512-7cNlSI8+oOBUHTfPXMwDxF/Lchx5aJ3ho7+p9jJZYVg9dVDJFh3qdMXmJtRsysnvS+C6x46k9DRYmrmCkE+MVg=="],
|
||||
@@ -1311,8 +1230,6 @@
|
||||
|
||||
"@types/humanize-duration": ["@types/humanize-duration@3.27.4", "", {}, "sha512-yaf7kan2Sq0goxpbcwTQ+8E9RP6HutFBPv74T/IA/ojcHKhuKVlk2YFYyHhWZeLvZPzzLE3aatuQB4h0iqyyUA=="],
|
||||
|
||||
"@types/ioredis": ["@types/ioredis@5.0.0", "", { "dependencies": { "ioredis": "*" } }, "sha512-zJbJ3FVE17CNl5KXzdeSPtdltc4tMT3TzC6fxQS0sQngkbFZ6h+0uTafsRqu+eSLIugf6Yb0Ea0SUuRr42Nk9g=="],
|
||||
|
||||
"@types/istanbul-lib-coverage": ["@types/istanbul-lib-coverage@2.0.6", "", {}, "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w=="],
|
||||
|
||||
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
||||
@@ -1331,8 +1248,6 @@
|
||||
|
||||
"@types/methods": ["@types/methods@1.1.4", "", {}, "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ=="],
|
||||
|
||||
"@types/mongodb": ["@types/mongodb@3.6.20", "", { "dependencies": { "@types/bson": "*", "@types/node": "*" } }, "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ=="],
|
||||
|
||||
"@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
|
||||
|
||||
"@types/multer": ["@types/multer@1.4.7", "", { "dependencies": { "@types/express": "*" } }, "sha512-/SNsDidUFCvqqcWDwxv2feww/yqhNeTRL5CVoL3jU4Goc4kKEL10T7Eye65ZqPNi4HRx8sAEX59pV1aEH7drNA=="],
|
||||
@@ -1355,8 +1270,6 @@
|
||||
|
||||
"@types/range-parser": ["@types/range-parser@1.2.7", "", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="],
|
||||
|
||||
"@types/rate-limit-redis": ["@types/rate-limit-redis@1.7.4", "", { "dependencies": { "@types/express-rate-limit": "3", "@types/ioredis": "*", "@types/redis": "^2.8.0" } }, "sha512-TDsh9CJHHhx+sGHlqF6MI+PgEv91o/wQ67e2IR02ebYPzWQ8Rk0pcVY+WBy2pjxa+cAkPwjcOFDd21VToKaOzQ=="],
|
||||
|
||||
"@types/react": ["@types/react@17.0.13", "", { "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", "csstype": "^3.0.2" } }, "sha512-D/G3PiuqTfE3IMNjLn/DCp6umjVCSvtZTPdtAFy5+Ved6CsdRvivfKeCzw79W4AatShtU4nGqgvOv5Gro534vQ=="],
|
||||
|
||||
"@types/react-dom": ["@types/react-dom@17.0.8", "", { "dependencies": { "@types/react": "*" } }, "sha512-0ohAiJAx1DAUEcY9UopnfwCE9sSMDGnY/oXjWMax6g3RpzmTt2GMyMVAXcbn0mo8XAff0SbQJl2/SBU+hjSZ1A=="],
|
||||
@@ -1371,8 +1284,6 @@
|
||||
|
||||
"@types/react-transition-group": ["@types/react-transition-group@4.4.12", "", { "peerDependencies": { "@types/react": "*" } }, "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w=="],
|
||||
|
||||
"@types/redis": ["@types/redis@2.8.32", "", { "dependencies": { "@types/node": "*" } }, "sha512-7jkMKxcGq9p242exlbsVzuJb57KqHRhNl4dHoQu2Y5v9bCAbtIXXH0R3HleSQW4CTOqpHIYUW3t6tpUj4BVQ+w=="],
|
||||
|
||||
"@types/scheduler": ["@types/scheduler@0.26.0", "", {}, "sha512-WFHp9YUJQ6CKshqoC37iOlHnQSmxNc795UhB26CyBBttrN9svdIrUjl/NjnNmfcwtncN0h/0PPAFWv9ovP8mLA=="],
|
||||
|
||||
"@types/serve-static": ["@types/serve-static@2.2.0", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*" } }, "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ=="],
|
||||
@@ -1491,8 +1402,6 @@
|
||||
|
||||
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
||||
|
||||
"acorn-walk": ["acorn-walk@8.3.5", "", { "dependencies": { "acorn": "^8.11.0" } }, "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw=="],
|
||||
|
||||
"ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="],
|
||||
|
||||
"ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
@@ -1501,8 +1410,6 @@
|
||||
|
||||
"append-field": ["append-field@1.0.0", "", {}, "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="],
|
||||
|
||||
"arg": ["arg@4.1.3", "", {}, "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="],
|
||||
|
||||
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||
|
||||
"aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
|
||||
@@ -1571,7 +1478,7 @@
|
||||
|
||||
"bintrees": ["bintrees@1.0.2", "", {}, "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw=="],
|
||||
|
||||
"bl": ["bl@2.2.1", "", { "dependencies": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" } }, "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g=="],
|
||||
"bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
|
||||
|
||||
"bliss": ["bliss@workspace:typescript/bliss"],
|
||||
|
||||
@@ -1591,15 +1498,11 @@
|
||||
|
||||
"broadcast-channel": ["broadcast-channel@3.7.0", "", { "dependencies": { "@babel/runtime": "^7.7.2", "detect-node": "^2.1.0", "js-sha3": "0.8.0", "microseconds": "0.2.0", "nano-time": "1.0.0", "oblivious-set": "1.0.0", "rimraf": "3.0.2", "unload": "2.2.0" } }, "sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg=="],
|
||||
|
||||
"bson": ["bson@1.1.6", "", {}, "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg=="],
|
||||
|
||||
"buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
|
||||
|
||||
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
|
||||
|
||||
"bullmq": ["bullmq@1.68.4", "", { "dependencies": { "cron-parser": "^2.18.0", "get-port": "^5.1.1", "glob": "^7.2.0", "ioredis": "^4.28.2", "lodash": "^4.17.21", "msgpackr": "^1.4.6", "semver": "^6.3.0", "tslib": "^1.14.1", "uuid": "^8.3.2" } }, "sha512-I0BYxhRocWHcNpzdXX5jR8DwKG1PkGJ8KUt9SJhWCCWd4sYvsMl6KV6J35ZfigGydXuAja19tbEefTUxdVv/sw=="],
|
||||
|
||||
"bunyan": ["bunyan@1.8.15", "", { "optionalDependencies": { "dtrace-provider": "~0.8", "moment": "^2.19.3", "mv": "~2", "safe-json-stringify": "~1" }, "bin": { "bunyan": "bin/bunyan" } }, "sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig=="],
|
||||
"bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
|
||||
|
||||
"busboy": ["busboy@1.6.0", "", { "dependencies": { "streamsearch": "^1.1.0" } }, "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA=="],
|
||||
|
||||
@@ -1639,8 +1542,6 @@
|
||||
|
||||
"clone": ["clone@2.1.2", "", {}, "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="],
|
||||
|
||||
"cluster-key-slot": ["cluster-key-slot@1.1.2", "", {}, "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="],
|
||||
|
||||
"codemirror": ["codemirror@6.0.2", "", { "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/commands": "^6.0.0", "@codemirror/language": "^6.0.0", "@codemirror/lint": "^6.0.0", "@codemirror/search": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0" } }, "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw=="],
|
||||
|
||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||
@@ -1669,8 +1570,6 @@
|
||||
|
||||
"connect-history-api-fallback": ["connect-history-api-fallback@1.6.0", "", {}, "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg=="],
|
||||
|
||||
"connect-redis": ["connect-redis@6.1.1", "", {}, "sha512-4on27NmUz1hVCVsrUFUOe06YkgCx88ZZ29Wi7D5mXg22alN/Xx+Y+iPrJ1Cc7coUKAe5SP3W2LF/zo6WXSJFFg=="],
|
||||
|
||||
"consola": ["consola@2.15.3", "", {}, "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw=="],
|
||||
|
||||
"content-disposition": ["content-disposition@0.5.4", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ=="],
|
||||
@@ -1689,8 +1588,6 @@
|
||||
|
||||
"cosmiconfig": ["cosmiconfig@7.1.0", "", { "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", "parse-json": "^5.0.0", "path-type": "^4.0.0", "yaml": "^1.10.0" } }, "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA=="],
|
||||
|
||||
"create-require": ["create-require@1.1.1", "", {}, "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="],
|
||||
|
||||
"crelt": ["crelt@1.0.6", "", {}, "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="],
|
||||
|
||||
"cron-parser": ["cron-parser@5.0.2", "", { "dependencies": { "luxon": "^3.5.0" } }, "sha512-RXXr5WuvLInay/DstwRD087/DpLOm24Y3mrkRmOdrMjbdGfQyPiMsx1Ad+SaJF2zN0tN78ckBjcEkIHVd+MX7Q=="],
|
||||
@@ -1749,8 +1646,6 @@
|
||||
|
||||
"deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
|
||||
|
||||
"defaults": ["defaults@1.0.4", "", { "dependencies": { "clone": "^1.0.2" } }, "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A=="],
|
||||
|
||||
"define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="],
|
||||
|
||||
"define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
|
||||
@@ -1759,8 +1654,6 @@
|
||||
|
||||
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
|
||||
|
||||
"denque": ["denque@1.5.1", "", {}, "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw=="],
|
||||
|
||||
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
|
||||
|
||||
"dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
|
||||
@@ -1773,7 +1666,7 @@
|
||||
|
||||
"dezalgo": ["dezalgo@1.0.4", "", { "dependencies": { "asap": "^2.0.0", "wrappy": "1" } }, "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig=="],
|
||||
|
||||
"diff": ["diff@4.0.4", "", {}, "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ=="],
|
||||
"diff": ["diff@5.2.2", "", {}, "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A=="],
|
||||
|
||||
"dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="],
|
||||
|
||||
@@ -1801,8 +1694,6 @@
|
||||
|
||||
"dotenv-expand": ["dotenv-expand@8.0.3", "", {}, "sha512-SErOMvge0ZUyWd5B0NXMQlDkN+8r+HhVUsxgOO7IoPDOdDRD2JjExpN6y3KnFR66jsJMwSn1pqIivhU5rcJiNg=="],
|
||||
|
||||
"dtrace-provider": ["dtrace-provider@0.8.8", "", { "dependencies": { "nan": "^2.14.0" } }, "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg=="],
|
||||
|
||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||
|
||||
"duplexify": ["duplexify@3.7.1", "", { "dependencies": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", "readable-stream": "^2.0.0", "stream-shift": "^1.0.0" } }, "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g=="],
|
||||
@@ -1957,8 +1848,6 @@
|
||||
|
||||
"express-prom-bundle": ["express-prom-bundle@7.0.2", "", { "dependencies": { "@types/express": "^4.17.21", "express": "^4.18.2", "on-finished": "^2.3.0", "url-value-parser": "^2.0.0" }, "peerDependencies": { "prom-client": ">=15.0.0" } }, "sha512-ffFV4HGHvCKnkNJFqm42sYztRJE5mLgOj8MpGey1HOatuFhtcwXoBD2m5gca7ZbcyjkIf7lOH5ZdrhlrBf0sGw=="],
|
||||
|
||||
"express-rate-limit": ["express-rate-limit@5.5.1", "", {}, "sha512-MTjE2eIbHv5DyfuFz4zLYWxpqVhEhkTiwFGuB74Q9CSou2WHO52nlE5y3Zlg6SIsiYUIPj6ifFxnkPz6O3sIUg=="],
|
||||
|
||||
"express-session": ["express-session@1.17.2", "", { "dependencies": { "cookie": "0.4.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "~2.0.0", "on-headers": "~1.0.2", "parseurl": "~1.3.3", "safe-buffer": "5.2.1", "uid-safe": "~2.1.5" } }, "sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ=="],
|
||||
|
||||
"extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
|
||||
@@ -2041,8 +1930,6 @@
|
||||
|
||||
"get-package-type": ["get-package-type@0.1.0", "", {}, "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q=="],
|
||||
|
||||
"get-port": ["get-port@5.1.1", "", {}, "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ=="],
|
||||
|
||||
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
||||
|
||||
"get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="],
|
||||
@@ -2051,7 +1938,7 @@
|
||||
|
||||
"getopts": ["getopts@2.3.0", "", {}, "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA=="],
|
||||
|
||||
"glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
|
||||
"glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="],
|
||||
|
||||
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
||||
|
||||
@@ -2129,8 +2016,6 @@
|
||||
|
||||
"invariant": ["invariant@2.2.4", "", { "dependencies": { "loose-envify": "^1.0.0" } }, "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA=="],
|
||||
|
||||
"ioredis": ["ioredis@4.31.0", "", { "dependencies": { "@ioredis/commands": "^1.0.2", "cluster-key-slot": "^1.1.0", "debug": "^4.3.1", "denque": "^1.1.0", "lodash.defaults": "^4.2.0", "lodash.flatten": "^4.4.0", "lodash.isarguments": "^3.1.0", "p-map": "^2.1.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" } }, "sha512-tVrCrc4LWJwX82GD79dZ0teZQGq+5KJEGpXJRgzHOrhHtLgF9ME6rTwDV5+HN5bjnvmtrnS8ioXhflY16sy2HQ=="],
|
||||
|
||||
"ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
|
||||
|
||||
"is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="],
|
||||
@@ -2167,8 +2052,6 @@
|
||||
|
||||
"is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="],
|
||||
|
||||
"is-nan": ["is-nan@1.3.2", "", { "dependencies": { "call-bind": "^1.0.0", "define-properties": "^1.1.3" } }, "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w=="],
|
||||
|
||||
"is-negative-zero": ["is-negative-zero@2.0.3", "", {}, "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw=="],
|
||||
|
||||
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
|
||||
@@ -2299,14 +2182,8 @@
|
||||
|
||||
"lodash-es": ["lodash-es@4.17.23", "", {}, "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg=="],
|
||||
|
||||
"lodash.defaults": ["lodash.defaults@4.2.0", "", {}, "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="],
|
||||
|
||||
"lodash.flatten": ["lodash.flatten@4.4.0", "", {}, "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g=="],
|
||||
|
||||
"lodash.get": ["lodash.get@4.4.2", "", {}, "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ=="],
|
||||
|
||||
"lodash.isarguments": ["lodash.isarguments@3.1.0", "", {}, "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg=="],
|
||||
|
||||
"lodash.isequal": ["lodash.isequal@4.5.0", "", {}, "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="],
|
||||
|
||||
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
|
||||
@@ -2327,8 +2204,6 @@
|
||||
|
||||
"make-dir": ["make-dir@4.0.0", "", { "dependencies": { "semver": "^7.5.3" } }, "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw=="],
|
||||
|
||||
"make-error": ["make-error@1.3.6", "", {}, "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="],
|
||||
|
||||
"match-sorter": ["match-sorter@6.4.0", "", { "dependencies": { "@babel/runtime": "^7.23.8", "remove-accents": "0.5.0" } }, "sha512-d4664ahzdL1QTTvmK1iI0JsrxWeJ6gn33qkYtnPg3mcn+naBLtXSgSPOe+X2vUgtgGwaAk3eiaj7gwKjjMAq+Q=="],
|
||||
|
||||
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
||||
@@ -2347,8 +2222,6 @@
|
||||
|
||||
"memoize-one": ["memoize-one@6.0.0", "", {}, "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="],
|
||||
|
||||
"memory-pager": ["memory-pager@1.5.0", "", {}, "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg=="],
|
||||
|
||||
"merge-descriptors": ["merge-descriptors@1.0.3", "", {}, "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ=="],
|
||||
|
||||
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
|
||||
@@ -2417,26 +2290,6 @@
|
||||
|
||||
"mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="],
|
||||
|
||||
"moment": ["moment@2.30.1", "", {}, "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how=="],
|
||||
|
||||
"moment-timezone": ["moment-timezone@0.5.48", "", { "dependencies": { "moment": "^2.29.4" } }, "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw=="],
|
||||
|
||||
"mongodb": ["mongodb@3.6.3", "", { "dependencies": { "bl": "^2.2.1", "bson": "^1.1.4", "denque": "^1.4.1", "require_optional": "^1.0.1", "safe-buffer": "^5.1.2" }, "optionalDependencies": { "saslprep": "^1.0.0" } }, "sha512-rOZuR0QkodZiM+UbQE5kDsJykBqWi0CL4Ec2i1nrGrUI3KO11r6Fbxskqmq3JK2NH7aW4dcccBuUujAP0ERl5w=="],
|
||||
|
||||
"monk": ["monk@7.3.4", "", { "dependencies": { "@types/mongodb": "^3.5.25", "debug": "*", "mongodb": "^3.2.3", "monk-middleware-cast-ids": "^0.2.1", "monk-middleware-fields": "^0.2.0", "monk-middleware-handle-callback": "^0.2.0", "monk-middleware-options": "^0.2.1", "monk-middleware-query": "^0.2.0", "monk-middleware-wait-for-connection": "^0.2.0", "object-assign": "^4.1.1" } }, "sha512-PkPNiElwroVyKQj01usyziOvwiKYBUVSq7YU1FB4KFr0J3v0GeXW0TebYsLR4u33WB8JGqPiAcuzDspfdujqQg=="],
|
||||
|
||||
"monk-middleware-cast-ids": ["monk-middleware-cast-ids@0.2.1", "", {}, "sha512-Hp9gwjXo+inSPhXUlsV1u3DyZh2cXT3Osij/1gSNCJQCgacAF1lfmnTj6bKmlf1Et4dvLhwUL0PquxdZRIofkA=="],
|
||||
|
||||
"monk-middleware-fields": ["monk-middleware-fields@0.2.0", "", {}, "sha512-pp+k0JeBX+HkIKnCisrtsW/QM2WGkOVG1Bd1qKLSanuBCf1l5khpdHrEullfko0HO6tCjCJMp//MsR16gd5tAw=="],
|
||||
|
||||
"monk-middleware-handle-callback": ["monk-middleware-handle-callback@0.2.2", "", {}, "sha512-5hBynb7asZ2uw9XVze7C3XH0zXT51yFDvYydk/5HnWWzh2NLglDSiKDcX0yLKPHzFgiq+5Z4Laq5fFVnFsmm8w=="],
|
||||
|
||||
"monk-middleware-options": ["monk-middleware-options@0.2.1", "", {}, "sha512-ypE0wGC8n5ARvOq2tlERr1uyu1edckeDRkheQLaXPRm9LoW7kr9P7xMGEjW2vmBh7IxnkpONVc7555X8x+34hQ=="],
|
||||
|
||||
"monk-middleware-query": ["monk-middleware-query@0.2.0", "", {}, "sha512-BApd5HBiczJl1eVy65c9/NBVLtygBePFxuoHgar/LeMWCjWP9odSJk3vsnbxTnsOT1CMJ+hFysbaosLzDsxvtA=="],
|
||||
|
||||
"monk-middleware-wait-for-connection": ["monk-middleware-wait-for-connection@0.2.0", "", {}, "sha512-jSTz73B/+pGTTvhu5Ym8xsG6+QqaWab53UXnXdNNlTijTdLvcHABCLJXudQiJxob5N1Mzr5EOSx5ziwn2sihPQ=="],
|
||||
|
||||
"mqtt": ["mqtt@4.3.8", "", { "dependencies": { "commist": "^1.0.0", "concat-stream": "^2.0.0", "debug": "^4.1.1", "duplexify": "^4.1.1", "help-me": "^3.0.0", "inherits": "^2.0.3", "lru-cache": "^6.0.0", "minimist": "^1.2.5", "mqtt-packet": "^6.8.0", "number-allocator": "^1.0.9", "pump": "^3.0.0", "readable-stream": "^3.6.0", "reinterval": "^1.1.0", "rfdc": "^1.3.0", "split2": "^3.1.0", "ws": "^7.5.5", "xtend": "^4.0.2" }, "bin": { "mqtt": "bin/mqtt.js", "mqtt_pub": "bin/pub.js", "mqtt_sub": "bin/sub.js" } }, "sha512-2xT75uYa0kiPEF/PE0VPdavmEkoBzMT/UL9moid0rAvlCtV48qBwxD62m7Ld/4j8tSkIO1E/iqRl/S72SEOhOw=="],
|
||||
|
||||
"mqtt-packet": ["mqtt-packet@6.10.0", "", { "dependencies": { "bl": "^4.0.2", "debug": "^4.1.1", "process-nextick-args": "^2.0.1" } }, "sha512-ja8+mFKIHdB1Tpl6vac+sktqy3gA8t9Mduom1BA75cI+R9AHnZOiaBQwpGiWnaVJLDGRdNhQmFaAqd7tkKSMGA=="],
|
||||
@@ -2445,16 +2298,8 @@
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"msgpackr": ["msgpackr@1.11.8", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-bC4UGzHhVvgDNS7kn9tV8fAucIYUBuGojcaLiz7v+P63Lmtm0Xeji8B/8tYKddALXxJLpwIeBmUN3u64C4YkRA=="],
|
||||
|
||||
"msgpackr-extract": ["msgpackr-extract@3.0.3", "", { "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, "optionalDependencies": { "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" }, "bin": { "download-msgpackr-prebuilds": "bin/download-prebuilds.js" } }, "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA=="],
|
||||
|
||||
"multer": ["multer@2.0.1", "", { "dependencies": { "append-field": "^1.0.0", "busboy": "^1.6.0", "concat-stream": "^2.0.0", "mkdirp": "^0.5.6", "object-assign": "^4.1.1", "type-is": "^1.6.18", "xtend": "^4.0.2" } }, "sha512-Ug8bXeTIUlxurg8xLTEskKShvcKDZALo1THEX5E41pYCD2sCVub5/kIRIGqWNoqV6szyLyQKV6mD4QUrWE5GCQ=="],
|
||||
|
||||
"mv": ["mv@2.1.1", "", { "dependencies": { "mkdirp": "~0.5.1", "ncp": "~2.0.0", "rimraf": "~2.4.0" } }, "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg=="],
|
||||
|
||||
"nan": ["nan@2.25.0", "", {}, "sha512-0M90Ag7Xn5KMLLZ7zliPWP3rT90P6PN+IzVFS0VqmnPktBk3700xUVv8Ikm9EUaUE5SDWdp/BIxdENzVznpm1g=="],
|
||||
|
||||
"nano-time": ["nano-time@1.0.0", "", { "dependencies": { "big-integer": "^1.6.16" } }, "sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA=="],
|
||||
|
||||
"nanoid": ["nanoid@3.1.23", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw=="],
|
||||
@@ -2465,8 +2310,6 @@
|
||||
|
||||
"natural-compare-lite": ["natural-compare-lite@1.4.0", "", {}, "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g=="],
|
||||
|
||||
"ncp": ["ncp@2.0.0", "", { "bin": { "ncp": "./bin/ncp" } }, "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA=="],
|
||||
|
||||
"negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
||||
|
||||
"no-case": ["no-case@3.0.4", "", { "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" } }, "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg=="],
|
||||
@@ -2479,8 +2322,6 @@
|
||||
|
||||
"node-fetch": ["node-fetch@2.6.7", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ=="],
|
||||
|
||||
"node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="],
|
||||
|
||||
"node-html-parser": ["node-html-parser@5.4.2", "", { "dependencies": { "css-select": "^4.2.1", "he": "1.2.0" } }, "sha512-RaBPP3+51hPne/OolXxcz89iYvQvKOydaqoePpOgXcrOKZhjVIzmpKZz+Hd/RBO2/zN2q6CNJhQzucVz+u3Jyw=="],
|
||||
|
||||
"node-mocks-http": ["node-mocks-http@1.11.0", "", { "dependencies": { "accepts": "^1.3.7", "content-disposition": "^0.5.3", "depd": "^1.1.0", "fresh": "^0.5.2", "merge-descriptors": "^1.0.1", "methods": "^1.1.2", "mime": "^1.3.4", "parseurl": "^1.3.3", "range-parser": "^1.2.0", "type-is": "^1.6.18" } }, "sha512-jS/WzSOcKbOeGrcgKbenZeNhxUNnP36Yw11+hL4TTxQXErGfqYZ+MaYNNvhaTiGIJlzNSqgQkk9j8dSu1YWSuw=="],
|
||||
@@ -2527,8 +2368,6 @@
|
||||
|
||||
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
|
||||
|
||||
"p-map": ["p-map@2.1.0", "", {}, "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw=="],
|
||||
|
||||
"package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="],
|
||||
|
||||
"param-case": ["param-case@3.0.4", "", { "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A=="],
|
||||
@@ -2651,8 +2490,6 @@
|
||||
|
||||
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
|
||||
|
||||
"rate-limit-redis": ["rate-limit-redis@2.1.0", "", { "dependencies": { "defaults": "^1.0.3", "redis": "^3.0.2" } }, "sha512-6SAsTCzY0v6UCIKLOLLYqR2XzFmgdtF7jWXlSPq2FrNIZk8tZ7xwBvyGW7GFMCe5I4S9lYNdrSJ9E84rz3/CpA=="],
|
||||
|
||||
"raw-body": ["raw-body@2.5.2", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } }, "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA=="],
|
||||
|
||||
"react": ["react@17.0.2", "", { "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" } }, "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA=="],
|
||||
@@ -2697,14 +2534,6 @@
|
||||
|
||||
"rechoir": ["rechoir@0.8.0", "", { "dependencies": { "resolve": "^1.20.0" } }, "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ=="],
|
||||
|
||||
"redis": ["redis@3.1.2", "", { "dependencies": { "denque": "^1.5.0", "redis-commands": "^1.7.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0" } }, "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw=="],
|
||||
|
||||
"redis-commands": ["redis-commands@1.7.0", "", {}, "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ=="],
|
||||
|
||||
"redis-errors": ["redis-errors@1.2.0", "", {}, "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w=="],
|
||||
|
||||
"redis-parser": ["redis-parser@3.0.0", "", { "dependencies": { "redis-errors": "^1.0.0" } }, "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A=="],
|
||||
|
||||
"reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="],
|
||||
|
||||
"regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="],
|
||||
@@ -2721,11 +2550,9 @@
|
||||
|
||||
"remove-accents": ["remove-accents@0.5.0", "", {}, "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A=="],
|
||||
|
||||
"require_optional": ["require_optional@1.0.1", "", { "dependencies": { "resolve-from": "^2.0.0", "semver": "^5.1.0" } }, "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g=="],
|
||||
|
||||
"resolve": ["resolve@2.0.0-next.6", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "node-exports-info": "^1.6.0", "object-keys": "^1.1.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA=="],
|
||||
|
||||
"resolve-from": ["resolve-from@2.0.0", "", {}, "sha512-qpFcKaXsq8+oRoLilkwyc7zHGF5i9Q2/25NIgLQQ/+VVv9rU4qvr6nXVAw1DsnXJyQkZsR4Ytfbtg5ehfcUssQ=="],
|
||||
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
||||
|
||||
"resolve-pathname": ["resolve-pathname@3.0.0", "", {}, "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng=="],
|
||||
|
||||
@@ -2751,8 +2578,6 @@
|
||||
|
||||
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
||||
|
||||
"safe-json-stringify": ["safe-json-stringify@1.2.0", "", {}, "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg=="],
|
||||
|
||||
"safe-push-apply": ["safe-push-apply@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" } }, "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA=="],
|
||||
|
||||
"safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="],
|
||||
@@ -2761,8 +2586,6 @@
|
||||
|
||||
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
||||
|
||||
"saslprep": ["saslprep@1.0.3", "", { "dependencies": { "sparse-bitfield": "^3.0.3" } }, "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag=="],
|
||||
|
||||
"sass": ["sass@1.98.0", "", { "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.1.5", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": { "sass": "sass.js" } }, "sha512-+4N/u9dZ4PrgzGgPlKnaaRQx64RO0JBKs9sDhQ2pLgN6JQZ25uPQZKQYaBJU48Kd5BxgXoJ4e09Dq7nMcOUW3A=="],
|
||||
|
||||
"scheduler": ["scheduler@0.20.2", "", { "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" } }, "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ=="],
|
||||
@@ -2811,8 +2634,6 @@
|
||||
|
||||
"space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="],
|
||||
|
||||
"sparse-bitfield": ["sparse-bitfield@3.0.3", "", { "dependencies": { "memory-pager": "^1.0.2" } }, "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ=="],
|
||||
|
||||
"split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="],
|
||||
|
||||
"stable-hash": ["stable-hash@0.0.5", "", {}, "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA=="],
|
||||
@@ -2821,8 +2642,6 @@
|
||||
|
||||
"stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="],
|
||||
|
||||
"standard-as-callback": ["standard-as-callback@2.1.0", "", {}, "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="],
|
||||
|
||||
"statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="],
|
||||
|
||||
"std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="],
|
||||
@@ -2949,9 +2768,7 @@
|
||||
|
||||
"ts-mixer": ["ts-mixer@6.0.4", "", {}, "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA=="],
|
||||
|
||||
"ts-node": ["ts-node@10.9.1", "", { "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", "@tsconfig/node14": "^1.0.0", "@tsconfig/node16": "^1.0.2", "acorn": "^8.4.1", "acorn-walk": "^8.1.1", "arg": "^4.1.0", "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" }, "peerDependencies": { "@swc/core": ">=1.2.50", "@swc/wasm": ">=1.2.50", "@types/node": "*", "typescript": ">=2.7" }, "optionalPeers": ["@swc/core", "@swc/wasm"], "bin": { "ts-node": "dist/bin.js", "ts-script": "dist/bin-script-deprecated.js", "ts-node-cwd": "dist/bin-cwd.js", "ts-node-esm": "dist/bin-esm.js", "ts-node-script": "dist/bin-script.js", "ts-node-transpile-only": "dist/bin-transpile.js" } }, "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw=="],
|
||||
|
||||
"tsconfig-paths": ["tsconfig-paths@3.12.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.1", "minimist": "^1.2.0", "strip-bom": "^3.0.0" } }, "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg=="],
|
||||
"tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="],
|
||||
|
||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
@@ -3029,8 +2846,6 @@
|
||||
|
||||
"uvu": ["uvu@0.5.6", "", { "dependencies": { "dequal": "^2.0.0", "diff": "^5.0.0", "kleur": "^4.0.3", "sade": "^1.7.3" }, "bin": { "uvu": "bin.js" } }, "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA=="],
|
||||
|
||||
"v8-compile-cache-lib": ["v8-compile-cache-lib@3.0.1", "", {}, "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg=="],
|
||||
|
||||
"vali-date": ["vali-date@1.0.0", "", {}, "sha512-sgECfZthyaCKW10N0fm27cg8HYTFK5qMWgypqkXMQ4Wbl/zZKx7xZICgcoxIIE+WFAP/MBL2EFwC/YvLxw3Zeg=="],
|
||||
|
||||
"value-equal": ["value-equal@1.0.1", "", {}, "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="],
|
||||
@@ -3089,14 +2904,10 @@
|
||||
|
||||
"yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="],
|
||||
|
||||
"yn": ["yn@3.1.1", "", {}, "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="],
|
||||
|
||||
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
||||
|
||||
"zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
|
||||
|
||||
"@ampproject/remapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||
|
||||
"@aws-crypto/crc32/@aws-sdk/types": ["@aws-sdk/types@3.973.5", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-hl7BGwDCWsjH8NkZfx+HgS7H2LyM2lTMAI7ba9c8O0KqdBLTdNJivsHpqjg9rNlAlPyREb6DeDRXUl0s8uFdmQ=="],
|
||||
|
||||
"@aws-crypto/crc32/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="],
|
||||
@@ -3121,8 +2932,6 @@
|
||||
|
||||
"@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||
|
||||
"@babel/generator/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||
|
||||
"@connectrpc/protoc-gen-connect-es/@bufbuild/protobuf": ["@bufbuild/protobuf@1.10.1", "", {}, "sha512-wJ8ReQbHxsAfXhrf9ixl0aYbZorRuOWpBNzm8pL8ftmSxQx/wnJD5Eg861NwJU/czy2VXFIebCeZnZrI9rktIQ=="],
|
||||
|
||||
"@connectrpc/protoc-gen-connect-es/@bufbuild/protoplugin": ["@bufbuild/protoplugin@1.10.1", "", { "dependencies": { "@bufbuild/protobuf": "1.10.1", "@typescript/vfs": "^1.4.0", "typescript": "4.5.2" } }, "sha512-LaSbfwabAFIvbVnbn8jWwElRoffCIxhVraO8arliVwWupWezHLXgqPHEYLXZY/SsAR+/YsFBQJa8tAGtNPJyaQ=="],
|
||||
@@ -3145,10 +2954,6 @@
|
||||
|
||||
"@isaacs/cliui/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
|
||||
|
||||
"@jridgewell/gen-mapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||
|
||||
"@jridgewell/source-map/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||
|
||||
"@kristiandupont/recase/ramda": ["ramda@0.30.1", "", {}, "sha512-tEF5I22zJnuclswcZMc8bDIrwRHRzf+NqVEmqg50ShAZMP7MWeR/RGDthfM/p+BlqvF2fXAzpn8i+SJcYD3alw=="],
|
||||
|
||||
"@octokit/request/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="],
|
||||
@@ -3163,8 +2968,6 @@
|
||||
|
||||
"@rollup/pluginutils/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
|
||||
"@types/rate-limit-redis/@types/express-rate-limit": ["@types/express-rate-limit@3.3.5", "", { "dependencies": { "@types/express": "*" } }, "sha512-v6M28DcbdATYvzTN5lFJhN2EP6BgY8MbTskcKJ/m5bmGuzFKHfbEVXF3po8U8ivOyyeT5o6HV0janPlFBBN5yg=="],
|
||||
|
||||
"@types/superagent/form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="],
|
||||
|
||||
"@typescript-eslint/experimental-utils/eslint-scope": ["eslint-scope@5.1.1", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw=="],
|
||||
@@ -3185,8 +2988,6 @@
|
||||
|
||||
"@vitest/mocker/vite": ["vite@3.2.11", "", { "dependencies": { "esbuild": "^0.15.9", "postcss": "^8.4.18", "resolve": "^1.22.1", "rollup": "^2.79.1" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@types/node": ">= 14", "less": "*", "sass": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "sass", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-K/jGKL/PgbIgKCiJo5QbASQhFiV02X9Jh+Qq0AKCRCRKZtOTVi4t6wh75FDpGf2N9rYOnzH87OEFQNaFy6pdxQ=="],
|
||||
|
||||
"ast-v8-to-istanbul/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||
|
||||
"aws-crt/@aws-sdk/util-utf8-browser": ["@aws-sdk/util-utf8-browser@3.259.0", "", { "dependencies": { "tslib": "^2.3.1" } }, "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw=="],
|
||||
|
||||
"aws-crt/buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
|
||||
@@ -3195,8 +2996,6 @@
|
||||
|
||||
"babel-plugin-macros/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="],
|
||||
|
||||
"bl/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
|
||||
|
||||
"bms-table-loader/node-fetch": ["node-fetch@2.6.2", "", {}, "sha512-aLoxToI6RfZ+0NOjmWAgn9+LEd30YCkJKFSyWacNZdEKTit/ZMcKjGkTRo8uWEsnIb/hfKecNPEbln02PdWbcA=="],
|
||||
|
||||
"bms-table-loader/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||
@@ -3209,12 +3008,6 @@
|
||||
|
||||
"broadcast-channel/rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="],
|
||||
|
||||
"bullmq/cron-parser": ["cron-parser@2.18.0", "", { "dependencies": { "is-nan": "^1.3.0", "moment-timezone": "^0.5.31" } }, "sha512-s4odpheTyydAbTBQepsqd2rNWGa2iV3cyo8g7zbI2QQYGLVsfbhmwukayS1XHppe02Oy1fg7mg6xoaraVJeEcg=="],
|
||||
|
||||
"bullmq/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"bullmq/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="],
|
||||
|
||||
"cheerio-select/css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="],
|
||||
|
||||
"cron-parser/luxon": ["luxon@3.7.2", "", {}, "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew=="],
|
||||
@@ -3227,8 +3020,6 @@
|
||||
|
||||
"d3-time-format/d3-time": ["d3-time@2.1.1", "", { "dependencies": { "d3-array": "2" } }, "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ=="],
|
||||
|
||||
"defaults/clone": ["clone@1.0.4", "", {}, "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg=="],
|
||||
|
||||
"discord.js/@discordjs/builders": ["@discordjs/builders@0.11.0", "", { "dependencies": { "@sindresorhus/is": "^4.2.0", "discord-api-types": "^0.26.0", "ts-mixer": "^6.0.0", "tslib": "^2.3.1", "zod": "^3.11.6" } }, "sha512-ZTB8yJdJKrKlq44dpWkNUrAtEJEq0gqpb7ASdv4vmq6/mZal5kOv312hQ56I/vxwMre+VIkoHquNUAfnTbiYtg=="],
|
||||
|
||||
"discord.js/@discordjs/collection": ["@discordjs/collection@0.4.0", "", {}, "sha512-zmjq+l/rV35kE6zRrwe8BHqV78JvIh2ybJeZavBi5NySjWXqN3hmmAKg7kYMMXSeiWtSsMoZ/+MQi0DiQWy2lw=="],
|
||||
@@ -3251,8 +3042,6 @@
|
||||
|
||||
"eslint-plugin-import/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"eslint-plugin-import/tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="],
|
||||
|
||||
"eslint-plugin-import-x/@typescript-eslint/types": ["@typescript-eslint/types@8.56.1", "", {}, "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw=="],
|
||||
|
||||
"eslint-plugin-import-x/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
|
||||
@@ -3289,14 +3078,12 @@
|
||||
|
||||
"formik/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="],
|
||||
|
||||
"glob/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
|
||||
|
||||
"hoist-non-react-statics/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||
|
||||
"htmlparser2/entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="],
|
||||
|
||||
"import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
||||
|
||||
"istanbul-lib-source-maps/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||
|
||||
"knex/colorette": ["colorette@2.0.19", "", {}, "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ=="],
|
||||
|
||||
"knex/commander": ["commander@10.0.1", "", {}, "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="],
|
||||
@@ -3321,10 +3108,6 @@
|
||||
|
||||
"mqtt/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="],
|
||||
|
||||
"mqtt-packet/bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
|
||||
|
||||
"mv/rimraf": ["rimraf@2.4.5", "", { "dependencies": { "glob": "^6.0.1" }, "bin": { "rimraf": "./bin.js" } }, "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ=="],
|
||||
|
||||
"node-exports-info/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"node-mocks-http/depd": ["depd@1.1.2", "", {}, "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="],
|
||||
@@ -3345,10 +3128,6 @@
|
||||
|
||||
"rechoir/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="],
|
||||
|
||||
"require_optional/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="],
|
||||
|
||||
"rimraf/glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="],
|
||||
|
||||
"rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.9", "", {}, "sha512-w6oiRWgEBl04QkFZgmW+jnU1EC9b57Oihi2ot3HNWIQRqgHp5PnYDia5iZ5FF7rpa4EQdiqMDXjlqKGXBhsoXw=="],
|
||||
|
||||
"send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||
@@ -3385,8 +3164,6 @@
|
||||
|
||||
"typescript-eslint/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.56.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.56.1", "@typescript-eslint/tsconfig-utils": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg=="],
|
||||
|
||||
"uvu/diff": ["diff@5.2.2", "", {}, "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A=="],
|
||||
|
||||
"vite-node/vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="],
|
||||
|
||||
"vite-plugin-html/pathe": ["pathe@0.2.0", "", {}, "sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw=="],
|
||||
@@ -3431,14 +3208,10 @@
|
||||
|
||||
"@vitest/mocker/vite/rollup": ["rollup@2.80.0", "", { "optionalDependencies": { "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ=="],
|
||||
|
||||
"bl/readable-stream/isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
|
||||
|
||||
"bl/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
|
||||
|
||||
"bl/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
|
||||
|
||||
"body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||
|
||||
"broadcast-channel/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
|
||||
|
||||
"css-select/domutils/dom-serializer": ["dom-serializer@1.4.1", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", "entities": "^2.0.0" } }, "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag=="],
|
||||
|
||||
"discord.js/@discordjs/builders/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||
@@ -3461,14 +3234,14 @@
|
||||
|
||||
"finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||
|
||||
"glob/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
|
||||
|
||||
"knex/debug/ms": ["ms@2.1.2", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="],
|
||||
|
||||
"mv/rimraf/glob": ["glob@6.0.4", "", { "dependencies": { "inflight": "^1.0.4", "inherits": "2", "minimatch": "2 || 3", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A=="],
|
||||
"mqtt/help-me/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
|
||||
|
||||
"react-router/path-to-regexp/isarray": ["isarray@0.0.1", "", {}, "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="],
|
||||
|
||||
"rimraf/glob/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
|
||||
|
||||
"send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||
|
||||
"serve-static/send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||
@@ -3515,7 +3288,7 @@
|
||||
|
||||
"eslint-plugin-import-x/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
|
||||
|
||||
"rimraf/glob/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="],
|
||||
"glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
|
||||
|
||||
"serve-static/send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||
|
||||
@@ -3541,8 +3314,6 @@
|
||||
|
||||
"@typescript-eslint/utils/@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
|
||||
|
||||
"rimraf/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
|
||||
|
||||
"typescript-eslint/@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
storage:
|
||||
wiredTiger:
|
||||
engineConfig:
|
||||
cacheSizeGB: 2
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/bin/fish
|
||||
|
||||
# add a local bin for ts-node
|
||||
fish_add_path /tachi/node_modules/.bin
|
||||
|
||||
source ./dev/fish-plugins/fisher/functions/fisher.fish
|
||||
|
||||
+1
-13
@@ -1,15 +1,4 @@
|
||||
services:
|
||||
tachi-mongo:
|
||||
container_name: tachi-mongo
|
||||
command: "--config /etc/mongod.conf"
|
||||
image: mongo:5
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "27017:27017"
|
||||
volumes:
|
||||
- tachi-volume:/data/db
|
||||
- "./dev/mongod.conf:/etc/mongod.conf"
|
||||
|
||||
tachi-redis:
|
||||
container_name: tachi-redis
|
||||
command: redis-server --save 60 1 --loglevel warning
|
||||
@@ -55,7 +44,7 @@ services:
|
||||
# Dedicated test Postgres. Lives on tmpfs with durability disabled - every
|
||||
# `CREATE DATABASE ... TEMPLATE`, every `TRUNCATE`, every commit is ~5-20x
|
||||
# faster than the dev DB. Wiped on container restart, which is exactly what
|
||||
# we want for tests. Use POSTGRES_TEST_HOST in vitest setup to point at it.
|
||||
# we want for tests.
|
||||
tachi-postgres-test:
|
||||
container_name: tachi-postgres-test
|
||||
image: postgres:18
|
||||
@@ -216,7 +205,6 @@ services:
|
||||
- ./observability/alloy-config.alloy:/etc/alloy/config.alloy
|
||||
|
||||
volumes:
|
||||
tachi-volume:
|
||||
tachi-redis:
|
||||
tachi-logs:
|
||||
tachi-postgres:
|
||||
|
||||
+5
-42
@@ -9,7 +9,9 @@
|
||||
],
|
||||
"tsMachete": {
|
||||
"excludeGlobal": [
|
||||
"@types/bun",
|
||||
"@typescript/native-preview",
|
||||
"@vitest/coverage-v8",
|
||||
"typescript"
|
||||
],
|
||||
"exclude": {
|
||||
@@ -18,33 +20,16 @@
|
||||
"bootstrap-scss",
|
||||
"sass"
|
||||
],
|
||||
"tachi-bot": [
|
||||
"ts-node",
|
||||
"tsconfig-paths",
|
||||
"@vitest/coverage-v8"
|
||||
],
|
||||
"tachi-db": [
|
||||
"@types/express",
|
||||
"@types/node",
|
||||
"@types/node-fetch",
|
||||
"@vitest/coverage-v8"
|
||||
"@types/node-fetch"
|
||||
],
|
||||
"eslint-config-tachi": [
|
||||
"@typescript-eslint/eslint-plugin",
|
||||
"@typescript-eslint/parser",
|
||||
"eslint-import-resolver-typescript",
|
||||
"@types/eslint__js"
|
||||
],
|
||||
"tachi-seeds-scripts": [
|
||||
"tsconfig-paths"
|
||||
],
|
||||
"tachi-server": [
|
||||
"ts-node",
|
||||
"tsconfig-paths",
|
||||
"@vitest/coverage-v8"
|
||||
],
|
||||
"tachi-db-migration-engine": [
|
||||
"ts-node"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -58,7 +43,6 @@
|
||||
"eslint": "^9.20.1",
|
||||
"kanel": "^3.16.1",
|
||||
"kanel-kysely": "^0.8.0",
|
||||
"ts-node": "10.9.1",
|
||||
"typescript": "5.9.3"
|
||||
},
|
||||
"catalog": {
|
||||
@@ -78,11 +62,9 @@
|
||||
"@octokit/app": "^16.1.2",
|
||||
"@octokit/webhooks-types": "7.5.1",
|
||||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/bunyan": "^1.8.8",
|
||||
"@types/connect-redis": "0.0.18",
|
||||
"@types/bun": "1.3.14",
|
||||
"@types/eslint__js": "8.42.3",
|
||||
"@types/express": "4.17.13",
|
||||
"@types/express-rate-limit": "5.1.3",
|
||||
"@types/express-serve-static-core": "4.17.29",
|
||||
"@types/express-session": "1.17.4",
|
||||
"@types/history": "4.7.11",
|
||||
@@ -90,20 +72,17 @@
|
||||
"@types/lodash": "4.14.172",
|
||||
"@types/lodash.get": "^4.4.7",
|
||||
"@types/luxon": "^2.3.1",
|
||||
"@types/mongodb": "3.6.20",
|
||||
"@types/multer": "1.4.7",
|
||||
"@types/natural-compare": "^1.4.1",
|
||||
"@types/node": "*",
|
||||
"@types/node-fetch": "2.5.12",
|
||||
"@types/nodemailer": "^6.4.4",
|
||||
"@types/pg": "^8.11.11",
|
||||
"@types/rate-limit-redis": "1.7.4",
|
||||
"@types/react": "17.0.13",
|
||||
"@types/react-dom": "17.0.8",
|
||||
"@types/react-google-recaptcha": "2.1.2",
|
||||
"@types/react-router-bootstrap": "0.26.1",
|
||||
"@types/react-router-dom": "5.1.7",
|
||||
"@types/redis": "2.8.32",
|
||||
"@types/supertest": "2.0.11",
|
||||
"@typescript-eslint/eslint-plugin": "^4.27.0",
|
||||
"@typescript-eslint/parser": "^4.27.0",
|
||||
@@ -115,12 +94,9 @@
|
||||
"binary-parser": "^2.3.0",
|
||||
"bms-table-loader": "2.0.0",
|
||||
"bootstrap-scss": "^5.3.0",
|
||||
"bullmq": "1.68.4",
|
||||
"bunyan": "1.8.15",
|
||||
"chalk": "4",
|
||||
"cheerio": "^1.0.0-rc.12",
|
||||
"commander": "^8.2.0",
|
||||
"connect-redis": "6.1.1",
|
||||
"csv-parse": "^5.0.3",
|
||||
"deepmerge": "^4.2.2",
|
||||
"discord-api-types": "0.22.0",
|
||||
@@ -139,7 +115,6 @@
|
||||
"express": "4.20.0",
|
||||
"express-async-errors": "3.1.1",
|
||||
"express-prom-bundle": "^7.0.0",
|
||||
"express-rate-limit": "5.5.1",
|
||||
"express-session": "1.17.2",
|
||||
"fast-json-stable-hash": "^1.0.3",
|
||||
"fast-xml-parser": "^4.2.5",
|
||||
@@ -153,8 +128,6 @@
|
||||
"lodash": "^4.17.23",
|
||||
"lodash.get": "^4.4.2",
|
||||
"luxon": "^2.3.1",
|
||||
"mongodb": "3.6.3",
|
||||
"monk": "^7.3.4",
|
||||
"multer": "2.0.1",
|
||||
"nanoid": "3.1.23",
|
||||
"natural-compare": "^1.4.0",
|
||||
@@ -171,7 +144,6 @@
|
||||
"pretty-bytes": "5.6.0",
|
||||
"prom-client": "15.1.3",
|
||||
"prudence": "0.10.0",
|
||||
"rate-limit-redis": "2.1.0",
|
||||
"react": "17.0.2",
|
||||
"react-async": "10.0.1",
|
||||
"react-bootstrap": "2.9.0-beta.0",
|
||||
@@ -183,13 +155,10 @@
|
||||
"react-router-bootstrap": "^0.25.0",
|
||||
"react-router-dom": "5.1.2",
|
||||
"react-select": "^5.6.1",
|
||||
"redis": "3.1.2",
|
||||
"rg-stats": "workspace:*",
|
||||
"sass": "^1.77.0",
|
||||
"supertest": "6.2.2",
|
||||
"sync-fetch": "^0.3.1",
|
||||
"ts-node": "^8.5.2",
|
||||
"tsconfig-paths": "3.12.0",
|
||||
"typescript": "^4.3.4",
|
||||
"typescript-eslint": "^8.24.1",
|
||||
"vite": "^8.0.0",
|
||||
@@ -210,19 +179,13 @@
|
||||
"cron-parser": "5.0.2"
|
||||
},
|
||||
"overrides": {
|
||||
"@types/mongodb": "3.6.20",
|
||||
"@types/bson": "1.0.11",
|
||||
"@types/node": "20.19.37",
|
||||
"@types/express-serve-static-core": "4.17.29",
|
||||
"@types/express": "4.17.13",
|
||||
"ts-node": "10.9.1",
|
||||
"mongodb": "3.6.3",
|
||||
"typescript": "5.9.3"
|
||||
},
|
||||
"patchedDependencies": {
|
||||
"@types/mongodb@3.6.20": "patches/@types__mongodb@3.6.20.patch",
|
||||
"browserslist@4.21.4": "patches/browserslist@4.21.4.patch",
|
||||
"monk@7.3.4": "patches/monk@7.3.4.patch"
|
||||
"browserslist@4.21.4": "patches/browserslist@4.21.4.patch"
|
||||
},
|
||||
"engines": {
|
||||
"bun": ">=1.2.0"
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
diff --git a/index.d.ts b/index.d.ts
|
||||
index d37f518dd645945193de473874cd1fecc4f68d11..ad40dcf6b2ca944043572ea63b4b7a9c46ca79cb 100755
|
||||
--- a/index.d.ts
|
||||
+++ b/index.d.ts
|
||||
@@ -1193,11 +1193,11 @@ export class Db extends EventEmitter {
|
||||
* @returns Promise if no callback is passed
|
||||
* @see https://mongodb.github.io/node-mongodb-native/3.6/api/Db.html#collection
|
||||
*/
|
||||
- collection<TSchema = DefaultSchema>(
|
||||
+ collection<TSchema extends { [key: string]: any } = DefaultSchema>(
|
||||
name: string,
|
||||
callback?: MongoCallback<Collection<TSchema>>,
|
||||
): Collection<TSchema>;
|
||||
- collection<TSchema = DefaultSchema>(
|
||||
+ collection<TSchema extends { [key: string]: any } = DefaultSchema>(
|
||||
name: string,
|
||||
options: DbCollectionOptions,
|
||||
callback?: MongoCallback<Collection<TSchema>>,
|
||||
@@ -1241,12 +1241,12 @@ export class Db extends EventEmitter {
|
||||
* @returns Promise if no callback is passed
|
||||
* @see https://mongodb.github.io/node-mongodb-native/3.6/api/Db.html#createCollection
|
||||
*/
|
||||
- createCollection<TSchema = DefaultSchema>(name: string, callback: MongoCallback<Collection<TSchema>>): void;
|
||||
- createCollection<TSchema = DefaultSchema>(
|
||||
+ createCollection<TSchema extends { [key: string]: any } = DefaultSchema>(name: string, callback: MongoCallback<Collection<TSchema>>): void;
|
||||
+ createCollection<TSchema extends { [key: string]: any } = DefaultSchema>(
|
||||
name: string,
|
||||
options?: CollectionCreateOptions,
|
||||
): Promise<Collection<TSchema>>;
|
||||
- createCollection<TSchema = DefaultSchema>(
|
||||
+ createCollection<TSchema extends { [key: string]: any } = DefaultSchema>(
|
||||
name: string,
|
||||
options: CollectionCreateOptions,
|
||||
callback: MongoCallback<Collection<TSchema>>,
|
||||
@@ -1378,17 +1378,17 @@ export class Db extends EventEmitter {
|
||||
* @returns Promise if no callback is passed
|
||||
* @see https://mongodb.github.io/node-mongodb-native/3.6/api/Db.html#renameCollection
|
||||
*/
|
||||
- renameCollection<TSchema = DefaultSchema>(
|
||||
+ renameCollection<TSchema extends { [key: string]: any } = DefaultSchema>(
|
||||
fromCollection: string,
|
||||
toCollection: string,
|
||||
callback: MongoCallback<Collection<TSchema>>,
|
||||
): void;
|
||||
- renameCollection<TSchema = DefaultSchema>(
|
||||
+ renameCollection<TSchema extends { [key: string]: any } = DefaultSchema>(
|
||||
fromCollection: string,
|
||||
toCollection: string,
|
||||
options?: { dropTarget?: boolean | undefined },
|
||||
): Promise<Collection<TSchema>>;
|
||||
- renameCollection<TSchema = DefaultSchema>(
|
||||
+ renameCollection<TSchema extends { [key: string]: any } = DefaultSchema>(
|
||||
fromCollection: string,
|
||||
toCollection: string,
|
||||
options: { dropTarget?: boolean | undefined },
|
||||
@@ -2553,11 +2553,11 @@ export interface Collection<TSchema extends { [key: string]: any } = DefaultSche
|
||||
* @param options Optional settings
|
||||
* @see https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#watch
|
||||
*/
|
||||
- watch<T = TSchema>(
|
||||
+ watch<T extends { [key: string]: any } = TSchema>(
|
||||
pipeline?: object[],
|
||||
options?: ChangeStreamOptions & { session?: ClientSession | undefined },
|
||||
): ChangeStream<T>;
|
||||
- watch<T = TSchema>(options?: ChangeStreamOptions & { session?: ClientSession | undefined }): ChangeStream<T>;
|
||||
+ watch<T extends { [key: string]: any } = TSchema>(options?: ChangeStreamOptions & { session?: ClientSession | undefined }): ChangeStream<T>;
|
||||
}
|
||||
|
||||
/** Update Query */
|
||||
@@ -2906,7 +2906,7 @@ export type FilterQuery<T> = {
|
||||
RootQuerySelector<T>;
|
||||
|
||||
/** @see https://docs.mongodb.com/v3.6/reference/method/db.collection.bulkWrite/#insertone */
|
||||
-export type BulkWriteInsertOneOperation<TSchema> = {
|
||||
+export type BulkWriteInsertOneOperation<TSchema extends { [key: string]: any }> = {
|
||||
insertOne: {
|
||||
document: OptionalId<TSchema>;
|
||||
};
|
||||
@@ -2982,7 +2982,7 @@ export type BulkWriteDeleteManyOperation<TSchema> = {
|
||||
*
|
||||
* @see https://mongodb.github.io/node-mongodb-native/3.6/api/Collection.html#bulkWrite
|
||||
*/
|
||||
-export type BulkWriteOperation<TSchema> =
|
||||
+export type BulkWriteOperation<TSchema extends { [key: string]: any }> =
|
||||
| BulkWriteInsertOneOperation<TSchema>
|
||||
| BulkWriteUpdateOneOperation<TSchema>
|
||||
| BulkWriteUpdateManyOperation<TSchema>
|
||||
@@ -1,15 +0,0 @@
|
||||
diff --git a/index.d.ts b/index.d.ts
|
||||
index e5c4acb9f8defb48045a54119a6500ff1a81d4b7..a3c9deedb1d9929085302e69bd0505ec1fd97788 100644
|
||||
--- a/index.d.ts
|
||||
+++ b/index.d.ts
|
||||
@@ -101,8 +101,8 @@ declare module "monk" {
|
||||
readonly close: () => Promise<void>;
|
||||
readonly listCollections: (query?: Object) => Promise<Array<ICollection>>;
|
||||
|
||||
- get<T = any>(name: string, options?: CollectionOptions): ICollection<T>;
|
||||
- create<T = any>(
|
||||
+ get<T extends { [key: string]: any } = any>(name: string, options?: CollectionOptions): ICollection<T>;
|
||||
+ create<T extends { [key: string]: any } = any>(
|
||||
name: string,
|
||||
creationOption?: CollectionCreateOptions,
|
||||
options?: CollectionOptions
|
||||
@@ -42,8 +42,6 @@
|
||||
"prudence": "catalog:",
|
||||
"tachi-common": "workspace:*",
|
||||
"tachi-db": "workspace:*",
|
||||
"ts-node": "catalog:",
|
||||
"tsconfig-paths": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -19,7 +19,6 @@ import {
|
||||
type FolderDocument,
|
||||
FormatDifficultySearch,
|
||||
GameToGameGroup,
|
||||
GetGameConfig,
|
||||
GetGameGroupConfig,
|
||||
type SongDocument,
|
||||
type V3Game,
|
||||
@@ -49,8 +48,6 @@ export default function ChartInfoFormat({
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
const versions = Object.keys(GetGameConfig(game).versions);
|
||||
|
||||
return (
|
||||
<Row
|
||||
className="text-center align-items-center"
|
||||
@@ -64,12 +61,6 @@ export default function ChartInfoFormat({
|
||||
{data.length !== 0 ? (
|
||||
data
|
||||
.sort((a, b) => a.title.localeCompare(b.title))
|
||||
.sort((a, b) =>
|
||||
"versions" in a.data && "versions" in b.data
|
||||
? versions.indexOf(a.data.versions) -
|
||||
versions.indexOf(b.data.versions)
|
||||
: 0,
|
||||
)
|
||||
.map((e) => (
|
||||
<li key={e.slug}>
|
||||
{user && ugs ? (
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import Divider from "#components/util/Divider";
|
||||
import Muted from "#components/util/Muted";
|
||||
import { type CellsRenderFN } from "#types/seeds";
|
||||
import { FlattenValue, StringifyKeyChain } from "#util/misc";
|
||||
import { StrSOV } from "#util/sorts";
|
||||
import { type SearchFunctions } from "#util/ztable/search";
|
||||
import React from "react";
|
||||
@@ -44,13 +43,7 @@ export const SeedsFolderSearchFns: SearchFunctions<FolderDocument> = {
|
||||
query: (x) => {
|
||||
const w = (x as { where?: string }).where;
|
||||
|
||||
if (typeof w === "string" && w.length > 0) {
|
||||
return w;
|
||||
}
|
||||
|
||||
return FlattenValue(x.data)
|
||||
.map((e) => `${StringifyKeyChain(e.keychain)} ${e.value}`)
|
||||
.join("\n");
|
||||
return typeof w === "string" ? w : "";
|
||||
},
|
||||
};
|
||||
|
||||
@@ -87,16 +80,7 @@ export const SeedsFolderCells: CellsRenderFN<FolderDocument> = ({
|
||||
TYPE: <b>{folderSeedKindLabel(data)}</b>
|
||||
<Divider />
|
||||
<div className="text-start">
|
||||
{showWhere ? (
|
||||
<code className="text-break">{whereSql}</code>
|
||||
) : (
|
||||
FlattenValue(data.data).map((e, i) => (
|
||||
<React.Fragment key={i}>
|
||||
{StringifyKeyChain(e.keychain)} = {String(e.value)}
|
||||
<br />
|
||||
</React.Fragment>
|
||||
))
|
||||
)}
|
||||
{showWhere && <code className="text-break">{whereSql}</code>}
|
||||
</div>
|
||||
</td>
|
||||
</>
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
"author": "zk",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mongodb": "catalog:",
|
||||
"prudence": "catalog:",
|
||||
"zod": "catalog:"
|
||||
},
|
||||
@@ -57,8 +56,7 @@
|
||||
"src/*/!(test-utils)/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"src/**/*.test.ts",
|
||||
"src/external/mongo/schemas.ts"
|
||||
"src/**/*.test.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,23 +347,9 @@ const PRE_SCHEMAS = {
|
||||
folders: prSchemaFnWrap({
|
||||
title: "string",
|
||||
game: p.isIn(games),
|
||||
playtype: isValidPlaytype,
|
||||
folderID: "string",
|
||||
inactive: "boolean",
|
||||
searchTerms: ["string"],
|
||||
type: p.isIn("songs", "charts", "static"),
|
||||
data: (self, parent) => {
|
||||
if (parent.type === "songs") {
|
||||
return true; // temp. should be a song.
|
||||
} else if (parent.type === "charts") {
|
||||
return true; // temp. should be a chart.
|
||||
}
|
||||
|
||||
return (
|
||||
(Array.isArray(self) && self.every((r) => typeof r === "string")) ||
|
||||
"Expected an array of only strings."
|
||||
);
|
||||
},
|
||||
}),
|
||||
tables: prSchemaFnWrap({
|
||||
tableID: "string",
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import type { FilterQuery } from "mongodb";
|
||||
|
||||
import type {
|
||||
AnyClasses,
|
||||
ChartDocumentData,
|
||||
@@ -397,22 +395,7 @@ export interface BaseFolderDocument {
|
||||
searchTerms: Array<string>;
|
||||
}
|
||||
|
||||
export interface FolderSongsDocument extends BaseFolderDocument {
|
||||
type: "songs";
|
||||
data: FilterQuery<SongDocument>;
|
||||
}
|
||||
|
||||
export interface FolderChartsDocument extends BaseFolderDocument {
|
||||
type: "charts";
|
||||
data: FilterQuery<ChartDocument>;
|
||||
}
|
||||
|
||||
export interface FolderStaticDocument extends BaseFolderDocument {
|
||||
type: "static";
|
||||
data: Array<string>;
|
||||
}
|
||||
|
||||
export type FolderDocument = FolderChartsDocument | FolderSongsDocument | FolderStaticDocument;
|
||||
export type FolderDocument = BaseFolderDocument;
|
||||
|
||||
export interface FolderChartLookup {
|
||||
chartID: string;
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
"@types/node": "catalog:",
|
||||
"@types/pg": "catalog:",
|
||||
"eslint-config-tachi": "workspace:*",
|
||||
"ts-node": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
// THIS SCRIPT IS NOT FOR IMPORTING TO AN INSTANCE OF TACHI.
|
||||
// THAT PROCESS IS *SIGNIFICANTLY* MORE COMPLEX, AND IS PART
|
||||
// OF TACHI-SERVER.
|
||||
|
||||
// This script is for importing the collections *immediately* into
|
||||
// any old mongo instance.
|
||||
|
||||
import { execSync } from "child_process";
|
||||
import { Command } from "commander";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
import { log as logger } from "./log.ts";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program.option("-d, --db <Database Name>");
|
||||
|
||||
program.parse(process.argv);
|
||||
const options = program.opts();
|
||||
|
||||
if (!options.db) {
|
||||
throw new Error(`Must provide database name with -d or --db.`);
|
||||
}
|
||||
|
||||
const base = path.join(__dirname, "../../db/seeds");
|
||||
|
||||
for (const file of fs.readdirSync(base)) {
|
||||
const filename = path.parse(file).name;
|
||||
const fileLoc = path.join(base, file);
|
||||
|
||||
const command = `mongoimport -c=${filename} --jsonArray ${fileLoc} --db=${options.db}`;
|
||||
|
||||
const data = fs.readFileSync(fileLoc, "utf-8");
|
||||
|
||||
if (data === "[]") {
|
||||
logger.info(`Skipping collection ${file} as the data is empty.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
logger.info(`Running ${command}.`);
|
||||
|
||||
try {
|
||||
execSync(command, { stdio: "inherit" });
|
||||
} catch (err) {
|
||||
logger.error(`Failed to import ${file}.`, { command, err });
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,7 @@
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@typescript/native-preview": "catalog:",
|
||||
"eslint-config-tachi": "workspace:*",
|
||||
"tsconfig-paths": "catalog:"
|
||||
"eslint-config-tachi": "workspace:*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/lodash.get": "catalog:",
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { ALL_GAMES, GAME_GROUP_CONFIGS } from "tachi-common";
|
||||
|
||||
import { ReadCollection, WRITE_COLLECTION_SKIP_BIOME, WriteCollection } from "../../util";
|
||||
|
||||
const COLLECTIONS_DIR = path.join(__dirname, "../../../../db/seeds");
|
||||
|
||||
// Games where the Postgres game name equals the game group name (single playtype).
|
||||
// These files are left as-is.
|
||||
const SINGLE_PT_GAMES = new Set([
|
||||
"arcaea",
|
||||
"chunithm",
|
||||
"jubeat",
|
||||
"maimai",
|
||||
"maimaidx",
|
||||
"museca",
|
||||
"ongeki",
|
||||
"popn",
|
||||
"sdvx",
|
||||
"wacca",
|
||||
]);
|
||||
|
||||
for (const game of Object.keys(GAME_GROUP_CONFIGS)) {
|
||||
if (!fs.existsSync(path.join(COLLECTIONS_DIR, `charts-${game}.json`))) {
|
||||
console.log(`charts-${game}.json: does not exist, skipping`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (SINGLE_PT_GAMES.has(game)) {
|
||||
console.log(`charts-${game}: single playtype, skipping`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const data = ReadCollection(`charts-${game}.json`);
|
||||
|
||||
const byPlaytype = new Map();
|
||||
|
||||
for (const entry of data) {
|
||||
const pt = entry.playtype.toLowerCase();
|
||||
|
||||
if (!byPlaytype.has(pt)) {
|
||||
byPlaytype.set(pt, []);
|
||||
}
|
||||
|
||||
byPlaytype.get(pt).push(entry);
|
||||
}
|
||||
|
||||
for (const [pt, charts] of byPlaytype) {
|
||||
const newName = `charts-${game}-${pt}.json`;
|
||||
|
||||
WriteCollection(newName, charts, WRITE_COLLECTION_SKIP_BIOME);
|
||||
console.log(`${newName}: wrote ${charts.length} charts`);
|
||||
}
|
||||
|
||||
fs.unlinkSync(path.join(COLLECTIONS_DIR, `charts-${game}.json`));
|
||||
console.log(`charts-${game}.json: deleted`);
|
||||
}
|
||||
|
||||
// now, for each collection remove the playtype field.
|
||||
for (const nugame of ALL_GAMES) {
|
||||
const data = ReadCollection(`charts-${nugame}.json`);
|
||||
for (const entry of data) {
|
||||
delete entry.playtype;
|
||||
}
|
||||
WriteCollection(`charts-${nugame}.json`, data, WRITE_COLLECTION_SKIP_BIOME);
|
||||
console.log(`charts-${nugame}.json: removed playtype field`);
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { ALL_GAMES, GAME_GROUP_CONFIGS, GameToGameGroup } from "tachi-common";
|
||||
|
||||
import {
|
||||
CreateChartID,
|
||||
CreateSongID,
|
||||
ReadCollection,
|
||||
WRITE_COLLECTION_SKIP_BIOME,
|
||||
WriteCollection,
|
||||
} from "../../util";
|
||||
|
||||
// ── Stability map ─────────────────────────────────────────────────────────────
|
||||
// Persists legacy→new ID mappings so re-runs produce the same IDs.
|
||||
|
||||
const STABILITY_MAP_PATH = path.join(__dirname, "stability-map.json");
|
||||
|
||||
interface StabilityMap {
|
||||
songs: Record<string, string>;
|
||||
charts: Record<string, string>;
|
||||
folders: Record<string, string>;
|
||||
tables: Record<string, string>;
|
||||
goals: Record<string, string>;
|
||||
}
|
||||
|
||||
function readStabilityMap(): StabilityMap {
|
||||
if (fs.existsSync(STABILITY_MAP_PATH)) {
|
||||
const raw = JSON.parse(
|
||||
fs.readFileSync(STABILITY_MAP_PATH, "utf-8"),
|
||||
) as Partial<StabilityMap>;
|
||||
|
||||
return {
|
||||
songs: raw.songs ?? {},
|
||||
charts: raw.charts ?? {},
|
||||
folders: raw.folders ?? {},
|
||||
tables: raw.tables ?? {},
|
||||
goals: raw.goals ?? {},
|
||||
};
|
||||
}
|
||||
|
||||
return { songs: {}, charts: {}, folders: {}, tables: {}, goals: {} };
|
||||
}
|
||||
|
||||
function writeStabilityMap(map: StabilityMap) {
|
||||
fs.writeFileSync(STABILITY_MAP_PATH, JSON.stringify(map, null, "\t"));
|
||||
}
|
||||
|
||||
const stabilityMap = readStabilityMap();
|
||||
|
||||
/**
|
||||
* Charts whose pre-remap mongo integer `songID` falls here reference songs absent from rollback
|
||||
* (BMS)—TODO remove band once upstream collections include them.
|
||||
*
|
||||
* Canonical list supplied to this repo was every id inclusive from 97678 through 97928.
|
||||
*/
|
||||
const SKIP_CHART_ORPHAN_SONG_MONGO_MIN = 97678;
|
||||
const SKIP_CHART_ORPHAN_SONG_MONGO_MAX = 97928;
|
||||
|
||||
function chartSkippedForOrphanRollbackSongMongoId(songFk: unknown): boolean {
|
||||
return (
|
||||
typeof songFk === "number" &&
|
||||
songFk >= SKIP_CHART_ORPHAN_SONG_MONGO_MIN &&
|
||||
songFk <= SKIP_CHART_ORPHAN_SONG_MONGO_MAX
|
||||
);
|
||||
}
|
||||
// ── Pass 1: songs ─────────────────────────────────────────────────────────────
|
||||
//
|
||||
// Writes each song row's final `id` (hex song id).
|
||||
//
|
||||
const songIdMap = new Map<string, string>(); // "game:<mongoSongInt>" → hex song id (from id or legacySongID)
|
||||
const songLegacyIdMap = new Map(); // "game:hexSongId" → integer mongo song id
|
||||
|
||||
for (const gameGroup of Object.keys(GAME_GROUP_CONFIGS)) {
|
||||
const collection = `songs-${gameGroup}.json`;
|
||||
const data = ReadCollection(collection);
|
||||
let modified = 0;
|
||||
|
||||
for (const entry of data) {
|
||||
const stabilityKey = `${gameGroup}:${entry.id}`;
|
||||
const savedId = stabilityMap.songs[stabilityKey];
|
||||
|
||||
const nextSongHexId =
|
||||
savedId ?? CreateSongID(Date.now() - 1000000 + (entry.legacySongID ?? entry.id));
|
||||
|
||||
if (entry.id !== undefined && typeof entry.id === "number") {
|
||||
entry.legacySongID = entry.id;
|
||||
songIdMap.set(stabilityKey, nextSongHexId);
|
||||
songLegacyIdMap.set(`${gameGroup}:${nextSongHexId}`, entry.id);
|
||||
stabilityMap.songs[stabilityKey] = nextSongHexId;
|
||||
}
|
||||
|
||||
entry.id = nextSongHexId;
|
||||
delete entry.sid;
|
||||
|
||||
if (typeof entry.legacySongID === "number") {
|
||||
songIdMap.set(`${gameGroup}:${entry.legacySongID}`, entry.id);
|
||||
}
|
||||
|
||||
modified++;
|
||||
}
|
||||
|
||||
if (modified > 0) {
|
||||
WriteCollection(collection, data, WRITE_COLLECTION_SKIP_BIOME);
|
||||
console.log(`${collection}: migrated ${modified} entries`);
|
||||
} else {
|
||||
console.log(`${collection}: already migrated, skipped`);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Pass 2: charts ────────────────────────────────────────────────────────────
|
||||
//
|
||||
// Each chart row gains `id` (hex chart id) and retains `songID` as FK → parent song.
|
||||
// Charts have many‑to‑one songs: `songID` is normalized from mongo ints to hex song ids.
|
||||
//
|
||||
for (const game of ALL_GAMES) {
|
||||
const gameGroup = GameToGameGroup(game);
|
||||
const collection = `charts-${game}.json`;
|
||||
const rawCharts = ReadCollection(collection);
|
||||
let droppedHardcoded = 0;
|
||||
const data = rawCharts.filter((entry: Record<string, unknown>) => {
|
||||
const songFk = entry.songID;
|
||||
if (chartSkippedForOrphanRollbackSongMongoId(songFk)) {
|
||||
droppedHardcoded++;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (droppedHardcoded > 0) {
|
||||
console.warn(
|
||||
`${collection}: omitted ${String(droppedHardcoded)} chart(s) (mongo songID in [${String(SKIP_CHART_ORPHAN_SONG_MONGO_MIN)}, ${String(SKIP_CHART_ORPHAN_SONG_MONGO_MAX)}] — rollback gap)`,
|
||||
);
|
||||
}
|
||||
let modified = 0;
|
||||
|
||||
for (const entry of data) {
|
||||
// Stability map keyed by mongo chart hash; after migrate it mirrors legacyChartID.
|
||||
const mongoChartStableKey =
|
||||
typeof entry.chartID === "string" && entry.chartID !== ""
|
||||
? entry.chartID
|
||||
: typeof entry.legacyChartID === "string" && entry.legacyChartID !== ""
|
||||
? entry.legacyChartID
|
||||
: undefined;
|
||||
|
||||
const savedHexChartId =
|
||||
mongoChartStableKey !== undefined
|
||||
? stabilityMap.charts[mongoChartStableKey]
|
||||
: undefined;
|
||||
|
||||
const retainedHexChartId =
|
||||
mongoChartStableKey === undefined && typeof entry.id === "string" && entry.id !== ""
|
||||
? entry.id
|
||||
: undefined;
|
||||
|
||||
const nextChartHexId =
|
||||
retainedHexChartId ??
|
||||
savedHexChartId ??
|
||||
CreateChartID(Date.now() - 1000000 + (entry.songID ?? entry.legacySongID));
|
||||
|
||||
if (mongoChartStableKey !== undefined) {
|
||||
stabilityMap.charts[mongoChartStableKey] = nextChartHexId;
|
||||
}
|
||||
|
||||
if (typeof entry.chartID === "string" && entry.chartID !== "") {
|
||||
entry.legacyChartID = entry.chartID;
|
||||
delete entry.chartID;
|
||||
}
|
||||
|
||||
const newSongFkHexId = songIdMap.get(`${gameGroup}:${entry.songID}`);
|
||||
if (newSongFkHexId === undefined) {
|
||||
throw new Error(
|
||||
`${collection}: chart (mongo chart hash=${mongoChartStableKey ?? "none"}, chart hex provisional=${nextChartHexId}) ` +
|
||||
`— parent song FK songID=${JSON.stringify(entry.songID)} → no song with that mongo integer id under songs-${gameGroup}.json ` +
|
||||
`(add the song to the collections mirror, fix songID on the chart, or remove the orphaned chart row).`,
|
||||
);
|
||||
}
|
||||
|
||||
entry.songID = newSongFkHexId;
|
||||
entry.id = nextChartHexId;
|
||||
delete entry.sid;
|
||||
modified++;
|
||||
}
|
||||
|
||||
if (modified > 0 || droppedHardcoded > 0) {
|
||||
WriteCollection(collection, data, WRITE_COLLECTION_SKIP_BIOME);
|
||||
console.log(`${collection}: migrated ${modified} entries`);
|
||||
} else {
|
||||
console.log(`${collection}: already migrated, skipped`);
|
||||
}
|
||||
}
|
||||
|
||||
writeStabilityMap(stabilityMap);
|
||||
console.log(
|
||||
`stability-map.json: saved ${Object.keys(stabilityMap.songs).length} songs, ${Object.keys(stabilityMap.charts).length} charts`,
|
||||
);
|
||||
@@ -1,128 +0,0 @@
|
||||
/**
|
||||
* Legacy Mongo → v3 hex ids for folders/tables. Also run `3-migrate-goals-quests-questlines-game.ts`
|
||||
* in the same pass for goal/quest/questline `game` strings. After this, run `4-tablefolders-to-object.ts`,
|
||||
* `5-folders-to-sql-queries.ts`, then `6-tables-folder-refs-to-slugs.ts` so `tables.json`
|
||||
* references folders by slug.
|
||||
*/
|
||||
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import {
|
||||
type GameGroup,
|
||||
LEGACY_GetGPTString,
|
||||
LEGACY_GPTStringToGame,
|
||||
type LEGACY_Playtype,
|
||||
} from "tachi-common";
|
||||
|
||||
import {
|
||||
CreateFolderID,
|
||||
CreateTableID,
|
||||
ReadCollection,
|
||||
WRITE_COLLECTION_SKIP_BIOME,
|
||||
WriteCollection,
|
||||
} from "../../util";
|
||||
|
||||
// ── Stability map ─────────────────────────────────────────────────────────────
|
||||
|
||||
const STABILITY_MAP_PATH = path.join(__dirname, "stability-map.json");
|
||||
|
||||
interface StabilityMap {
|
||||
songs: Record<string, string>;
|
||||
charts: Record<string, string>;
|
||||
folders: Record<string, string>;
|
||||
tables: Record<string, string>;
|
||||
goals: Record<string, string>;
|
||||
}
|
||||
|
||||
function readStabilityMap(): StabilityMap {
|
||||
if (fs.existsSync(STABILITY_MAP_PATH)) {
|
||||
const raw = JSON.parse(
|
||||
fs.readFileSync(STABILITY_MAP_PATH, "utf-8"),
|
||||
) as Partial<StabilityMap>;
|
||||
|
||||
return {
|
||||
songs: raw.songs ?? {},
|
||||
charts: raw.charts ?? {},
|
||||
folders: raw.folders ?? {},
|
||||
tables: raw.tables ?? {},
|
||||
goals: raw.goals ?? {},
|
||||
};
|
||||
}
|
||||
|
||||
return { songs: {}, charts: {}, folders: {}, tables: {}, goals: {} };
|
||||
}
|
||||
|
||||
function writeStabilityMap(map: StabilityMap): void {
|
||||
fs.writeFileSync(STABILITY_MAP_PATH, JSON.stringify(map, null, "\t"));
|
||||
}
|
||||
|
||||
const stabilityMap = readStabilityMap();
|
||||
|
||||
// ── Pass 1: folders ───────────────────────────────────────────────────────────
|
||||
// Build a map of old folderID → new hex id so tables can update their refs.
|
||||
const folderIdMap = new Map<string, string>(); // oldFolderID → new hex id
|
||||
|
||||
const folders = ReadCollection("folders.json");
|
||||
|
||||
for (const entry of folders) {
|
||||
if (!entry.id) {
|
||||
entry.id = stabilityMap.folders[entry.folderID] ?? CreateFolderID();
|
||||
}
|
||||
|
||||
if (entry.folderID) {
|
||||
entry.legacyFolderID = entry.folderID;
|
||||
folderIdMap.set(entry.folderID, entry.id);
|
||||
stabilityMap.folders[entry.folderID] = entry.id;
|
||||
delete entry.folderID;
|
||||
}
|
||||
|
||||
entry.game = LEGACY_GPTStringToGame(
|
||||
LEGACY_GetGPTString(entry.game as GameGroup, entry.playtype as LEGACY_Playtype),
|
||||
);
|
||||
delete entry.playtype;
|
||||
}
|
||||
|
||||
WriteCollection("folders.json", folders, WRITE_COLLECTION_SKIP_BIOME);
|
||||
console.log(`folders.json: migrated ${folders.length} entries`);
|
||||
|
||||
// ── Pass 2: tables ────────────────────────────────────────────────────────────
|
||||
|
||||
const tables = ReadCollection("tables.json");
|
||||
|
||||
for (const entry of tables) {
|
||||
if (!entry.id) {
|
||||
entry.id = stabilityMap.tables[entry.tableID] ?? CreateTableID();
|
||||
}
|
||||
|
||||
if (entry.tableID) {
|
||||
entry.legacyTableID = entry.tableID;
|
||||
stabilityMap.tables[entry.tableID] = entry.id;
|
||||
delete entry.tableID;
|
||||
}
|
||||
|
||||
entry.game = LEGACY_GPTStringToGame(
|
||||
LEGACY_GetGPTString(entry.game as GameGroup, entry.playtype as LEGACY_Playtype),
|
||||
);
|
||||
delete entry.playtype;
|
||||
|
||||
// Rewrite folder references to new hex ids.
|
||||
entry.folders = entry.folders.map((oldId: string) => {
|
||||
const newId = folderIdMap.get(oldId);
|
||||
|
||||
if (newId === undefined) {
|
||||
throw new Error(`Table "${entry.title}": unknown folder reference ${oldId}`);
|
||||
}
|
||||
|
||||
return newId;
|
||||
});
|
||||
}
|
||||
|
||||
WriteCollection("tables.json", tables, WRITE_COLLECTION_SKIP_BIOME);
|
||||
console.log(`tables.json: migrated ${tables.length} entries`);
|
||||
|
||||
writeStabilityMap(stabilityMap);
|
||||
console.log(
|
||||
`stability-map.json: saved ${Object.keys(stabilityMap.folders).length} folders, ${
|
||||
Object.keys(stabilityMap.tables).length
|
||||
} tables`,
|
||||
);
|
||||
@@ -1,110 +0,0 @@
|
||||
/**
|
||||
* Normalise `game` on `goals.json`, `quests.json`, and `questlines.json` to canonical {@link V3Game}
|
||||
* strings (aligned with Folder/Table migration in `3-migrate-folders-tables.ts`).
|
||||
*
|
||||
* Accepted shapes:
|
||||
* - Mongo-era **game group** + **playtype** → {@link LEGACY_GetGPTString} + {@link LEGACY_GPTStringToGame}.
|
||||
* - Already a **V3Game** string (passthrough).
|
||||
* - Colon **GPT strings** (`sdvx:Single`, …) → {@link LEGACY_GPTStringToGame}.
|
||||
*
|
||||
* Strips stale `playtype` when consumed or when `game` is already a V3 string. Runs before
|
||||
* `7-remap-goals-folder-and-chart-ids.ts` so goalID hashes use the corrected game.
|
||||
*/
|
||||
|
||||
import {
|
||||
ALL_GAMES,
|
||||
type GameGroup,
|
||||
LEGACY_GetGPTString,
|
||||
type LEGACY_GPTString,
|
||||
LEGACY_GPTStringToGame,
|
||||
type LEGACY_Playtype,
|
||||
type V3Game,
|
||||
} from "tachi-common";
|
||||
|
||||
import { ReadCollection, WRITE_COLLECTION_SKIP_BIOME, WriteCollection } from "../../util";
|
||||
|
||||
function isV3GameString(s: string): s is V3Game {
|
||||
return (ALL_GAMES as readonly string[]).includes(s);
|
||||
}
|
||||
|
||||
function migrateSeedDocGame(entry: Record<string, unknown>, label: string): boolean {
|
||||
const rawGame = entry.game;
|
||||
|
||||
if (typeof rawGame !== "string") {
|
||||
throw new Error(`${label}: expected string game, got ${JSON.stringify(rawGame)}`);
|
||||
}
|
||||
|
||||
let changed = false;
|
||||
const hadPlaytypeKey = Object.hasOwn(entry, "playtype");
|
||||
|
||||
const ptRaw = entry.playtype;
|
||||
const nonemptyPt =
|
||||
ptRaw !== undefined && ptRaw !== null && !(typeof ptRaw === "string" && ptRaw === "");
|
||||
|
||||
let v3: V3Game;
|
||||
|
||||
if (nonemptyPt) {
|
||||
v3 = LEGACY_GPTStringToGame(
|
||||
LEGACY_GetGPTString(rawGame as GameGroup, String(ptRaw) as LEGACY_Playtype),
|
||||
);
|
||||
delete entry.playtype;
|
||||
changed = rawGame !== v3 || hadPlaytypeKey;
|
||||
} else if (isV3GameString(rawGame)) {
|
||||
v3 = rawGame;
|
||||
if (hadPlaytypeKey) {
|
||||
delete entry.playtype;
|
||||
changed = true;
|
||||
}
|
||||
} else {
|
||||
const fromGptColon = LEGACY_GPTStringToGame(rawGame as LEGACY_GPTString);
|
||||
|
||||
if (fromGptColon !== undefined && isV3GameString(fromGptColon)) {
|
||||
v3 = fromGptColon;
|
||||
changed = rawGame !== v3 || hadPlaytypeKey;
|
||||
} else {
|
||||
throw new Error(
|
||||
`${label}: could not migrate game ${JSON.stringify(rawGame)} to a V3Game ` +
|
||||
`(expected V3 string, GPT string such as 'sdvx:Single', or game group plus playtype)`,
|
||||
);
|
||||
}
|
||||
|
||||
if (hadPlaytypeKey) {
|
||||
delete entry.playtype;
|
||||
}
|
||||
}
|
||||
|
||||
entry.game = v3;
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
function migrateCollection(name: string, rowLabelPrefix: string): number {
|
||||
let changedRows = 0;
|
||||
const rows = ReadCollection(name) as Array<Record<string, unknown>>;
|
||||
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const row = rows[i]!;
|
||||
const human =
|
||||
rowLabelPrefix +
|
||||
String(row.name ?? row.questID ?? row.questlineID ?? `index ${String(i)}`);
|
||||
|
||||
if (migrateSeedDocGame(row, human)) {
|
||||
changedRows++;
|
||||
}
|
||||
}
|
||||
|
||||
WriteCollection(name, rows, WRITE_COLLECTION_SKIP_BIOME);
|
||||
|
||||
return changedRows;
|
||||
}
|
||||
|
||||
const goalsChanged = migrateCollection("goals.json", "goals.json ");
|
||||
const questsChanged = migrateCollection("quests.json", "quests.json ");
|
||||
const questlinesChanged = migrateCollection("questlines.json", "questlines.json ");
|
||||
|
||||
console.log(`goals.json: normalised game on ${String(goalsChanged)} goals`);
|
||||
console.log(`quests.json: normalised game on ${String(questsChanged)} quests`);
|
||||
console.log(`questlines.json: normalised game on ${String(questlinesChanged)} questlines`);
|
||||
console.log(
|
||||
`done: ${String(goalsChanged + questsChanged + questlinesChanged)} total entries updated`,
|
||||
);
|
||||
@@ -1,179 +0,0 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
import { ReadCollection, WRITE_COLLECTION_SKIP_BIOME, WriteCollection } from "../../util";
|
||||
|
||||
/**
|
||||
* BMS / PMS / USC chart `data.tableFolders`: migrate from Mongo-style
|
||||
* `[{ table, level }, …]` to `{ [table]: level }` so folder SQL can use
|
||||
* `chart.data->'tableFolders'->>'…'` (see `5-folders-to-sql-queries.ts`).
|
||||
*
|
||||
* Must run **before** `5-folders-to-sql-queries.ts`.
|
||||
*/
|
||||
const TABLEFOLDERS_OBJECT_CHART_COLLECTIONS = [
|
||||
"charts-bms-7k.json",
|
||||
"charts-bms-14k.json",
|
||||
"charts-pms-controller.json",
|
||||
"charts-pms-keyboard.json",
|
||||
"charts-usc-controller.json",
|
||||
"charts-usc-keyboard.json",
|
||||
] as const;
|
||||
|
||||
/** Folder `game` values that use object-shaped `chart.data.tableFolders` after this script. */
|
||||
const TABLEFOLDERS_OBJECT_GAMES = new Set([
|
||||
"bms-7k",
|
||||
"bms-14k",
|
||||
"pms-controller",
|
||||
"pms-keyboard",
|
||||
"usc-controller",
|
||||
"usc-keyboard",
|
||||
]);
|
||||
|
||||
function sqlStr(value: string): string {
|
||||
return `'${value.replace(/'/gu, "''")}'`;
|
||||
}
|
||||
|
||||
/**
|
||||
* `jsonb_array_elements` does not work on JSON objects - rewrite legacy elemMatch SQL
|
||||
* to key lookup (must match `5-folders-to-sql-queries.ts`).
|
||||
*/
|
||||
function rewriteFolderWhereTableFoldersElemExists(where: string): string | null {
|
||||
const pattern =
|
||||
/^EXISTS \(SELECT 1 FROM jsonb_array_elements\(chart\.data->'tableFolders'\) AS elem WHERE elem->>'level' = '((?:''|[^'])*)' AND elem->>'table' = '((?:''|[^'])*)'\)$/u;
|
||||
|
||||
const m = where.match(pattern);
|
||||
|
||||
if (!m) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const level = m[1].replace(/''/gu, "'");
|
||||
const table = m[2].replace(/''/gu, "'");
|
||||
|
||||
return `(chart.data->'tableFolders'->>${sqlStr(table)}) = ${sqlStr(level)}`;
|
||||
}
|
||||
|
||||
function tableFoldersArrayToObject(arr: unknown, chartId: string): Record<string, string> {
|
||||
if (!Array.isArray(arr)) {
|
||||
throw new Error(`chart ${chartId}: expected tableFolders array, got ${typeof arr}`);
|
||||
}
|
||||
|
||||
const out: Record<string, string> = {};
|
||||
|
||||
for (const row of arr) {
|
||||
if (row === null || typeof row !== "object") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const r = row as Record<string, unknown>;
|
||||
|
||||
if (!("table" in r) || !("level" in r)) {
|
||||
throw new Error(
|
||||
`chart ${chartId}: tableFolders row must have table and level: ${JSON.stringify(row)}`,
|
||||
);
|
||||
}
|
||||
|
||||
const table = String(r.table);
|
||||
const level = r.level;
|
||||
const levelStr =
|
||||
typeof level === "string" || typeof level === "number" || typeof level === "boolean"
|
||||
? String(level)
|
||||
: JSON.stringify(level);
|
||||
|
||||
if (Object.hasOwn(out, table) && out[table] !== levelStr) {
|
||||
throw new Error(
|
||||
`chart ${chartId}: duplicate tableFolders table ${JSON.stringify(table)} with conflicting levels (${JSON.stringify(out[table])} vs ${JSON.stringify(levelStr)})`,
|
||||
);
|
||||
}
|
||||
|
||||
out[table] = levelStr;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
function transformTableFolders(data: Record<string, unknown>, chartId: string): boolean {
|
||||
const tf = data.tableFolders;
|
||||
|
||||
if (tf === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tf !== null && typeof tf === "object" && !Array.isArray(tf)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Array.isArray(tf)) {
|
||||
data.tableFolders = tableFoldersArrayToObject(tf, chartId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new Error(`chart ${chartId}: expected tableFolders array or object, got ${typeof tf}`);
|
||||
}
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
for (const name of TABLEFOLDERS_OBJECT_CHART_COLLECTIONS) {
|
||||
const data = ReadCollection(name) as Array<Record<string, unknown>>;
|
||||
let modified = 0;
|
||||
|
||||
for (const entry of data) {
|
||||
const chartId = String(entry.id ?? "?");
|
||||
const dataObj = entry.data as Record<string, unknown> | undefined;
|
||||
|
||||
if (!dataObj || typeof dataObj !== "object") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (transformTableFolders(dataObj, chartId)) {
|
||||
modified++;
|
||||
}
|
||||
}
|
||||
|
||||
if (modified > 0) {
|
||||
WriteCollection(name, data, WRITE_COLLECTION_SKIP_BIOME);
|
||||
console.log(`${name}: converted tableFolders on ${modified} charts`);
|
||||
} else {
|
||||
console.log(`${name}: no tableFolders arrays to convert`);
|
||||
}
|
||||
}
|
||||
|
||||
// ── folders.json: WHERE clauses that still use jsonb_array_elements on tableFolders ──
|
||||
|
||||
const FOLDERS_JSON = path.join(__dirname, "../../../../db/seeds/folders.json");
|
||||
const folders = JSON.parse(fs.readFileSync(FOLDERS_JSON, "utf-8")) as Array<
|
||||
Record<string, unknown>
|
||||
>;
|
||||
let foldersRewritten = 0;
|
||||
|
||||
for (const entry of folders) {
|
||||
const game = entry.game;
|
||||
|
||||
if (typeof game !== "string" || !TABLEFOLDERS_OBJECT_GAMES.has(game)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const w = entry.where;
|
||||
|
||||
if (typeof w !== "string") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const next = rewriteFolderWhereTableFoldersElemExists(w);
|
||||
|
||||
if (next !== null) {
|
||||
entry.where = next;
|
||||
foldersRewritten++;
|
||||
}
|
||||
}
|
||||
|
||||
if (foldersRewritten > 0) {
|
||||
fs.writeFileSync(FOLDERS_JSON, `${JSON.stringify(folders, null, "\t")}\n`);
|
||||
console.log(
|
||||
`folders.json: rewrote ${foldersRewritten} tableFolders WHERE clauses (array elem → object key lookup)`,
|
||||
);
|
||||
} else {
|
||||
console.log(`folders.json: no tableFolders array-style WHERE clauses to rewrite`);
|
||||
}
|
||||
@@ -1,454 +0,0 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { computeFolderSlug, type SeedFolderRow } from "tachi-common";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
/**
|
||||
* Converts legacy Mongo-style folder `data` into SQL `where` strings, then assigns
|
||||
* `slug` per {@link computeFolderSlug} (required for Postgres `folder.slug`).
|
||||
*
|
||||
* After this, run `6-tables-folder-refs-to-slugs.ts` so `tables.json` lists folder
|
||||
* slugs instead of ids (see `importSeeds` in the server).
|
||||
*/
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────────
|
||||
|
||||
function sqlStr(value: string): string {
|
||||
// Escape single quotes for SQL string literals.
|
||||
return `'${value.replace(/'/gu, "''")}'`;
|
||||
}
|
||||
|
||||
const RANGE_OPS: Record<string, string> = {
|
||||
"~gte": ">=",
|
||||
"~gt": ">",
|
||||
"~lt": "<",
|
||||
"~lte": "<=",
|
||||
};
|
||||
|
||||
/**
|
||||
* Range / inequality on `chart.data->>'field'` or `song.data->>'field'`.
|
||||
* `->>` returns text; compare to numbers with `::numeric` or Postgres errors (e.g. text >= int).
|
||||
*/
|
||||
function rangeConditions(expr: string, ops: Record<string, unknown>): Array<string> {
|
||||
const conds: Array<string> = [];
|
||||
|
||||
for (const [op, val] of Object.entries(ops)) {
|
||||
if (op === "~in") {
|
||||
if (!Array.isArray(val)) {
|
||||
throw new Error(`~in value must be an array, got: ${JSON.stringify(val)}`);
|
||||
}
|
||||
|
||||
const list = val.map((v: unknown) => sqlStr(String(v))).join(", ");
|
||||
|
||||
conds.push(`${expr} IN (${list})`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (op === "~not") {
|
||||
if (typeof val !== "object" || val === null) {
|
||||
throw new Error(
|
||||
`~not value must be an operator object, got: ${JSON.stringify(val)}`,
|
||||
);
|
||||
}
|
||||
|
||||
const inner = rangeConditions(expr, val as Record<string, unknown>);
|
||||
|
||||
conds.push(`NOT (${inner.join(" AND ")})`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const sqlOp = RANGE_OPS[op];
|
||||
|
||||
if (!sqlOp) {
|
||||
throw new Error(`Unknown operator: ${op}`);
|
||||
}
|
||||
|
||||
if (typeof val === "number") {
|
||||
conds.push(`${expr} ${sqlOp} ${val}`);
|
||||
} else {
|
||||
conds.push(`${expr} ${sqlOp} ${sqlStr(String(val))}`);
|
||||
}
|
||||
}
|
||||
|
||||
return conds;
|
||||
}
|
||||
|
||||
function isOperatorObject(v: unknown): v is Record<string, unknown> {
|
||||
return (
|
||||
typeof v === "object" &&
|
||||
v !== null &&
|
||||
!Array.isArray(v) &&
|
||||
Object.keys(v as object).some((k) => k.startsWith("~"))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSONB text extraction `expr` / numeric `numExpr` (same path, `::numeric`).
|
||||
* Used for `data->>'field'`, `elem->>'k'`, and `tableFolders` key lookups.
|
||||
*/
|
||||
function rangeConditionsJsonFieldExpr(
|
||||
textExpr: string,
|
||||
numExpr: string,
|
||||
ops: Record<string, unknown>,
|
||||
): Array<string> {
|
||||
const conds: Array<string> = [];
|
||||
|
||||
for (const [op, val] of Object.entries(ops)) {
|
||||
if (op === "~in") {
|
||||
if (!Array.isArray(val)) {
|
||||
throw new Error(`~in value must be an array, got: ${JSON.stringify(val)}`);
|
||||
}
|
||||
|
||||
const list = val.map((v: unknown) => sqlStr(String(v))).join(", ");
|
||||
|
||||
conds.push(`${textExpr} IN (${list})`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (op === "~not") {
|
||||
if (typeof val !== "object" || val === null) {
|
||||
throw new Error(
|
||||
`~not value must be an operator object, got: ${JSON.stringify(val)}`,
|
||||
);
|
||||
}
|
||||
|
||||
const inner = rangeConditionsJsonFieldExpr(
|
||||
textExpr,
|
||||
numExpr,
|
||||
val as Record<string, unknown>,
|
||||
);
|
||||
|
||||
conds.push(`NOT (${inner.join(" AND ")})`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const sqlOp = RANGE_OPS[op];
|
||||
|
||||
if (!sqlOp) {
|
||||
throw new Error(`Unknown operator: ${op}`);
|
||||
}
|
||||
|
||||
if (typeof val === "number") {
|
||||
conds.push(`${numExpr} ${sqlOp} ${val}`);
|
||||
} else {
|
||||
conds.push(`${numExpr} ${sqlOp} ${sqlStr(String(val))}`);
|
||||
}
|
||||
}
|
||||
|
||||
return conds;
|
||||
}
|
||||
|
||||
/**
|
||||
* JSONB `data->>'field'` is text. For numeric comparisons, use `::numeric` on the LHS.
|
||||
* `~in` stays as text equality to string literals.
|
||||
*/
|
||||
function rangeConditionsJsonField(
|
||||
tableAlias: "chart" | "song",
|
||||
field: string,
|
||||
ops: Record<string, unknown>,
|
||||
): Array<string> {
|
||||
const alias = tableAlias === "chart" ? "chart" : "s";
|
||||
const textExpr = `${alias}.data->>'${field}'`;
|
||||
const numExpr = `(${alias}.data->>'${field}')::numeric`;
|
||||
|
||||
return rangeConditionsJsonFieldExpr(textExpr, numExpr, ops);
|
||||
}
|
||||
|
||||
/** `elem->>'field'` conditions; supports `~in` / range ops on a match field (e.g. USC `level`). */
|
||||
function elemMatchFieldSql(field: string, v: unknown): Array<string> {
|
||||
const expr = `elem->>'${field}'`;
|
||||
const numExpr = `(elem->>'${field}')::numeric`;
|
||||
|
||||
if (isOperatorObject(v)) {
|
||||
return rangeConditionsJsonFieldExpr(expr, numExpr, v);
|
||||
}
|
||||
|
||||
return [`${expr} = ${sqlStr(String(v))}`];
|
||||
}
|
||||
|
||||
// ── Chart filter ──────────────────────────────────────────────────────────────
|
||||
|
||||
const CHART_FIELD_MAP: Record<string, string> = {
|
||||
level: "chart.level",
|
||||
levelNum: "chart.level_num",
|
||||
difficulty: "chart.difficulty",
|
||||
isPrimary: "chart.is_primary",
|
||||
};
|
||||
|
||||
/** Chart `data.tableFolders` is object-shaped for these games after `4-tablefolders-to-object.ts`. */
|
||||
const TABLEFOLDERS_OBJECT_GAMES = new Set([
|
||||
"bms-7k",
|
||||
"bms-14k",
|
||||
"pms-controller",
|
||||
"pms-keyboard",
|
||||
"usc-controller",
|
||||
"usc-keyboard",
|
||||
]);
|
||||
|
||||
interface ChartFilterResult {
|
||||
where: string;
|
||||
versionFilter: Array<string> | undefined;
|
||||
}
|
||||
|
||||
function convertChartFilter(data: Record<string, unknown>, folderGame: string): ChartFilterResult {
|
||||
const conds: Array<string> = [];
|
||||
let versionFilter: Array<string> | undefined;
|
||||
|
||||
for (const [key, val] of Object.entries(data)) {
|
||||
// ── versions → extracted to versionFilter, not inlined into where ────
|
||||
if (key === "versions") {
|
||||
if (typeof val === "string") {
|
||||
versionFilter = [val];
|
||||
} else if (Array.isArray(val)) {
|
||||
versionFilter = val.map(String);
|
||||
} else {
|
||||
throw new Error(`Unexpected value for "versions": ${JSON.stringify(val)}`);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// ── data¬field (path into JSONB data column) ──────────────────────────
|
||||
if (key.includes("\u00ac")) {
|
||||
const [, field] = key.split("\u00ac");
|
||||
|
||||
if (!field) {
|
||||
throw new Error(`Malformed data¬ key: ${key}`);
|
||||
}
|
||||
|
||||
// elemMatch → object key lookup for tableFolders (see 4-tablefolders-to-object.ts),
|
||||
// else EXISTS over jsonb_array_elements.
|
||||
if (typeof val === "object" && val !== null && "~elemMatch" in val) {
|
||||
const match = (val as Record<string, unknown>)["~elemMatch"];
|
||||
|
||||
if (typeof match !== "object" || match === null) {
|
||||
throw new Error(
|
||||
`~elemMatch value must be an object, got: ${JSON.stringify(match)}`,
|
||||
);
|
||||
}
|
||||
|
||||
const m = match as Record<string, unknown>;
|
||||
|
||||
if (
|
||||
field === "tableFolders" &&
|
||||
TABLEFOLDERS_OBJECT_GAMES.has(folderGame) &&
|
||||
"table" in m &&
|
||||
"level" in m
|
||||
) {
|
||||
const tableKey =
|
||||
typeof m.table === "string" ||
|
||||
typeof m.table === "number" ||
|
||||
typeof m.table === "boolean"
|
||||
? String(m.table)
|
||||
: JSON.stringify(m.table);
|
||||
const textExpr = `(chart.data->'tableFolders'->>${sqlStr(tableKey)})`;
|
||||
const numExpr = `(${textExpr})::numeric`;
|
||||
const lvl = m.level;
|
||||
|
||||
if (isOperatorObject(lvl)) {
|
||||
conds.push(
|
||||
...rangeConditionsJsonFieldExpr(
|
||||
textExpr,
|
||||
numExpr,
|
||||
lvl as Record<string, unknown>,
|
||||
),
|
||||
);
|
||||
} else if (typeof lvl === "object" && lvl !== null) {
|
||||
throw new Error(
|
||||
`tableFolders ~elemMatch level must be a scalar or operator object (~in, …), got: ${JSON.stringify(lvl)}`,
|
||||
);
|
||||
} else {
|
||||
conds.push(`${textExpr} = ${sqlStr(String(lvl))}`);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
const elemConds = Object.entries(m).flatMap(([k, v]) => elemMatchFieldSql(k, v));
|
||||
|
||||
conds.push(
|
||||
`EXISTS (SELECT 1 FROM jsonb_array_elements(chart.data->'${field}') AS elem WHERE ${elemConds.join(
|
||||
" AND ",
|
||||
)})`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Meta-table folder: scalar means "this table key exists" on object-shaped tableFolders.
|
||||
// (`->>'tableFolders'` would compare the whole JSON text, never a single table name.)
|
||||
if (
|
||||
field === "tableFolders" &&
|
||||
TABLEFOLDERS_OBJECT_GAMES.has(folderGame) &&
|
||||
(typeof val === "string" || typeof val === "number" || typeof val === "boolean")
|
||||
) {
|
||||
conds.push(`(chart.data->'tableFolders') ? ${sqlStr(String(val))}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Range / equality on a JSONB scalar (text extract → cast for numeric compares)
|
||||
if (typeof val === "object" && val !== null) {
|
||||
conds.push(
|
||||
...rangeConditionsJsonField("chart", field, val as Record<string, unknown>),
|
||||
);
|
||||
} else if (typeof val === "number") {
|
||||
conds.push(`(chart.data->>'${field}')::numeric = ${val}`);
|
||||
} else {
|
||||
conds.push(`chart.data->>'${field}' = ${sqlStr(String(val))}`);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// ── mapped top-level chart columns ────────────────────────────────────
|
||||
const col = CHART_FIELD_MAP[key];
|
||||
|
||||
if (col) {
|
||||
if (typeof val === "object" && val !== null) {
|
||||
conds.push(...rangeConditions(col, val as Record<string, unknown>));
|
||||
} else if (typeof val === "boolean") {
|
||||
conds.push(`${col} = ${val}`);
|
||||
} else if (typeof val === "number") {
|
||||
conds.push(`${col} = ${val}`);
|
||||
} else {
|
||||
conds.push(`${col} = ${sqlStr(String(val))}`);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new Error(`Unknown chart filter key: ${key}`);
|
||||
}
|
||||
|
||||
return { where: conds.join(" AND "), versionFilter };
|
||||
}
|
||||
|
||||
// ── Song filter ───────────────────────────────────────────────────────────────
|
||||
|
||||
function convertSongFilter(data: Record<string, unknown>): string {
|
||||
const conds: Array<string> = [];
|
||||
|
||||
for (const [key, val] of Object.entries(data)) {
|
||||
// The one observed pattern: { "data": { "genre": "LUNATIC" } }
|
||||
// "data" refers to the song's JSONB data column.
|
||||
if (key === "data") {
|
||||
if (typeof val !== "object" || val === null) {
|
||||
throw new Error(
|
||||
`Expected object for song "data" filter, got: ${JSON.stringify(val)}`,
|
||||
);
|
||||
}
|
||||
|
||||
for (const [field, fieldVal] of Object.entries(val as Record<string, unknown>)) {
|
||||
if (typeof fieldVal === "object" && fieldVal !== null) {
|
||||
conds.push(
|
||||
...rangeConditionsJsonField(
|
||||
"song",
|
||||
field,
|
||||
fieldVal as Record<string, unknown>,
|
||||
),
|
||||
);
|
||||
} else if (typeof fieldVal === "number") {
|
||||
conds.push(`(song.data->>'${field}')::numeric = ${fieldVal}`);
|
||||
} else {
|
||||
conds.push(`song.data->>'${field}' = ${sqlStr(String(fieldVal))}`);
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new Error(`Unknown song filter key: ${key}`);
|
||||
}
|
||||
|
||||
return conds.join(" AND ");
|
||||
}
|
||||
|
||||
/** When `type` is omitted, song filters are the only shape whose sole top-level key is `data`. */
|
||||
function inferFolderKindFromFilterData(data: Record<string, unknown>): "charts" | "songs" {
|
||||
const keys = Object.keys(data);
|
||||
|
||||
if (keys.length === 1 && keys[0] === "data") {
|
||||
return "songs";
|
||||
}
|
||||
|
||||
return "charts";
|
||||
}
|
||||
|
||||
// ── Main ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const FOLDERS_JSON = path.join(__dirname, "../../../../db/seeds/folders.json");
|
||||
|
||||
const folders = JSON.parse(fs.readFileSync(FOLDERS_JSON, "utf-8")) as Array<
|
||||
Record<string, unknown>
|
||||
>;
|
||||
|
||||
for (const entry of folders) {
|
||||
const filterData = entry.data as Record<string, unknown> | undefined;
|
||||
|
||||
if (filterData === undefined) {
|
||||
if (typeof entry.where !== "string" || entry.where.trim() === "") {
|
||||
throw new Error(
|
||||
`Folder ${String(entry.id ?? "?")} has no "data" and no non-empty "where".`,
|
||||
);
|
||||
}
|
||||
|
||||
delete entry.type;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Array.isArray(filterData)) {
|
||||
throw new Error("Static folders are no longer used.");
|
||||
}
|
||||
|
||||
const dataObj = filterData as Record<string, unknown>;
|
||||
const explicitType = entry.type as string | undefined;
|
||||
|
||||
let kind: "charts" | "songs";
|
||||
|
||||
if (explicitType === "charts" || explicitType === "songs") {
|
||||
kind = explicitType;
|
||||
} else if (explicitType === "static") {
|
||||
throw new Error("Static folders are no longer used.");
|
||||
} else if (explicitType === undefined) {
|
||||
kind = inferFolderKindFromFilterData(dataObj);
|
||||
} else {
|
||||
throw new Error(`Unknown folder type: ${String(explicitType)}`);
|
||||
}
|
||||
|
||||
const folderGame = String(entry.game ?? "");
|
||||
|
||||
if (kind === "charts") {
|
||||
const { where, versionFilter } = convertChartFilter(dataObj, folderGame);
|
||||
|
||||
entry.where = where;
|
||||
|
||||
if (versionFilter !== undefined) {
|
||||
entry.versionFilter = versionFilter;
|
||||
}
|
||||
} else {
|
||||
entry.where = convertSongFilter(dataObj);
|
||||
}
|
||||
|
||||
delete entry.data;
|
||||
delete entry.type;
|
||||
}
|
||||
|
||||
const slugKeys = new Map<string, string>();
|
||||
|
||||
for (const entry of folders) {
|
||||
const slug = computeFolderSlug(entry as SeedFolderRow);
|
||||
const game = String(entry.game);
|
||||
const key = `${game}\0${slug}`;
|
||||
|
||||
if (slugKeys.has(key)) {
|
||||
throw new Error(
|
||||
`Duplicate slug "${slug}" for game ${game}: "${slugKeys.get(key)}" vs "${String(entry.title)}"`,
|
||||
);
|
||||
}
|
||||
|
||||
slugKeys.set(key, String(entry.title));
|
||||
entry.slug = slug;
|
||||
}
|
||||
|
||||
fs.writeFileSync(FOLDERS_JSON, `${JSON.stringify(folders, null, "\t")}\n`);
|
||||
console.log(`Wrote ${FOLDERS_JSON} (${folders.length} folders, with slugs).`);
|
||||
@@ -1,21 +0,0 @@
|
||||
import { ReadCollection, WRITE_COLLECTION_SKIP_BIOME, WriteCollection } from "../../util";
|
||||
|
||||
const courses = ReadCollection("bms-course-lookup.json");
|
||||
let modified = 0;
|
||||
|
||||
for (const entry of courses) {
|
||||
if (entry.game !== undefined && entry.playtype === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry.playtype === undefined) {
|
||||
throw new Error(`Entry missing both playtype and game: ${JSON.stringify(entry)}`);
|
||||
}
|
||||
|
||||
entry.game = entry.playtype === "7K" ? "bms-7k" : "bms-14k";
|
||||
delete entry.playtype;
|
||||
modified++;
|
||||
}
|
||||
|
||||
WriteCollection("bms-course-lookup.json", courses, WRITE_COLLECTION_SKIP_BIOME);
|
||||
console.log(`bms-course-lookup.json: migrated ${modified} entries`);
|
||||
@@ -1,96 +0,0 @@
|
||||
/**
|
||||
* Rewrites `tables.json` so each `folders[]` entry is a folder **slug** (per game),
|
||||
* not a hex folder id. Run after `5-folders-to-sql-queries.ts` (folders must have
|
||||
* `where` and `slug`).
|
||||
*
|
||||
* Idempotent: if an entry is already a slug for that game, it is left unchanged.
|
||||
*/
|
||||
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { computeFolderSlug, type SeedFolderRow } from "tachi-common";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const SEEDS = path.join(__dirname, "../../../../db/seeds");
|
||||
|
||||
function resolvedFolderSlug(f: {
|
||||
game: string;
|
||||
id: string;
|
||||
slug?: string;
|
||||
title: string;
|
||||
versionFilter?: Array<string>;
|
||||
where: string;
|
||||
}): string {
|
||||
if (f.slug !== undefined && f.slug !== "") {
|
||||
return f.slug;
|
||||
}
|
||||
|
||||
return computeFolderSlug({
|
||||
game: f.game,
|
||||
id: f.id,
|
||||
title: f.title,
|
||||
where: f.where,
|
||||
versionFilter: f.versionFilter,
|
||||
} as SeedFolderRow);
|
||||
}
|
||||
|
||||
const foldersPath = path.join(SEEDS, "folders.json");
|
||||
const tablesPath = path.join(SEEDS, "tables.json");
|
||||
|
||||
const folders = JSON.parse(fs.readFileSync(foldersPath, "utf-8")) as Array<{
|
||||
game: string;
|
||||
id: string;
|
||||
slug?: string;
|
||||
title: string;
|
||||
versionFilter?: Array<string>;
|
||||
where: string;
|
||||
}>;
|
||||
|
||||
const idToSlug = new Map<string, string>();
|
||||
const slugByGame = new Map<string, Set<string>>();
|
||||
|
||||
for (const f of folders) {
|
||||
const slug = resolvedFolderSlug(f);
|
||||
|
||||
idToSlug.set(f.id, slug);
|
||||
|
||||
const set = slugByGame.get(f.game) ?? new Set<string>();
|
||||
|
||||
set.add(slug);
|
||||
slugByGame.set(f.game, set);
|
||||
}
|
||||
|
||||
const tables = JSON.parse(fs.readFileSync(tablesPath, "utf-8")) as Array<{
|
||||
folders: Array<string>;
|
||||
game: string;
|
||||
id: string;
|
||||
title: string;
|
||||
}>;
|
||||
|
||||
for (const t of tables) {
|
||||
const validSlugs = slugByGame.get(t.game);
|
||||
|
||||
if (validSlugs === undefined) {
|
||||
throw new Error(`Table "${t.title}": no folders for game ${JSON.stringify(t.game)}`);
|
||||
}
|
||||
|
||||
t.folders = t.folders.map((ref, i) => {
|
||||
const asSlug = idToSlug.get(ref);
|
||||
|
||||
if (asSlug !== undefined) {
|
||||
return asSlug;
|
||||
}
|
||||
|
||||
if (validSlugs.has(ref)) {
|
||||
return ref;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Table "${t.title}" (${t.id}): folders[${i}] = ${JSON.stringify(ref)} is neither a known folder id nor a slug for game ${JSON.stringify(t.game)}`,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fs.writeFileSync(tablesPath, `${JSON.stringify(tables, null, "\t")}\n`);
|
||||
console.log(`Wrote ${tablesPath} (${tables.length} tables; folder refs are slugs).`);
|
||||
@@ -1,331 +0,0 @@
|
||||
/**
|
||||
* After charts + folders gained Postgres-style ids (`id` plus `legacyChartID` /
|
||||
* `legacyFolderID`), `goals.json` may still reference legacy chart hashes and legacy
|
||||
* folder ids inside `charts.data`. Remap those references, recalculate `goalID` hashes
|
||||
* (same `{ charts, criteria, game }` convention as ../../util CreateGoalID), rewrite
|
||||
* `quests.json` goal refs when ids change, and update `stability-map.json` `goals` so
|
||||
* mongo→postgres can translate subscriptions.
|
||||
*
|
||||
* Expects each goal row’s {@link GoalSeed.game} to already be a canonical V3 string (run
|
||||
* `3-migrate-goals-quests-questlines-game.ts` earlier in `just seeds-v3-migrate`).
|
||||
*/
|
||||
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { spawnSync } from "child_process";
|
||||
|
||||
import { CreateGoalID } from "../../util";
|
||||
|
||||
type GoalCharts =
|
||||
| { data: Array<string>; type: "multi" }
|
||||
| { data: string; folderSlug?: string; type: "folder" }
|
||||
| { data: string; type: "single" };
|
||||
|
||||
interface GoalSeed {
|
||||
charts: GoalCharts;
|
||||
criteria:
|
||||
| {
|
||||
countNum: number;
|
||||
key: string;
|
||||
mode: "absolute" | "proportion";
|
||||
value: number;
|
||||
}
|
||||
| {
|
||||
key: string;
|
||||
mode: "single";
|
||||
value: number;
|
||||
};
|
||||
game: string;
|
||||
goalID: string;
|
||||
name: string;
|
||||
/** @deprecated Legacy field; omit on v3 seeds. */
|
||||
playtype?: string;
|
||||
}
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const SEEDS_DIR = path.join(__dirname, "../../../../db/seeds");
|
||||
const STABILITY_MAP_PATH = path.join(__dirname, "stability-map.json");
|
||||
|
||||
interface StabilityMap {
|
||||
songs: Record<string, string>;
|
||||
charts: Record<string, string>;
|
||||
folders: Record<string, string>;
|
||||
tables: Record<string, string>;
|
||||
goals: Record<string, string>;
|
||||
}
|
||||
|
||||
function readStabilityMap(): StabilityMap {
|
||||
if (fs.existsSync(STABILITY_MAP_PATH)) {
|
||||
const raw = JSON.parse(
|
||||
fs.readFileSync(STABILITY_MAP_PATH, "utf-8"),
|
||||
) as Partial<StabilityMap>;
|
||||
|
||||
return {
|
||||
songs: raw.songs ?? {},
|
||||
charts: raw.charts ?? {},
|
||||
folders: raw.folders ?? {},
|
||||
tables: raw.tables ?? {},
|
||||
goals: raw.goals ?? {},
|
||||
};
|
||||
}
|
||||
|
||||
return { songs: {}, charts: {}, folders: {}, tables: {}, goals: {} };
|
||||
}
|
||||
|
||||
function writeStabilityMap(map: StabilityMap): void {
|
||||
fs.writeFileSync(STABILITY_MAP_PATH, `${JSON.stringify(map, null, "\t")}\n`);
|
||||
}
|
||||
|
||||
const LEGACY_CHART_HEX_RE = /^[0-9a-f]{40}$/u;
|
||||
|
||||
function readJsonArray<T>(filePath: string): Array<T> {
|
||||
return JSON.parse(fs.readFileSync(filePath, "utf8")) as Array<T>;
|
||||
}
|
||||
|
||||
function buildChartLegacyToIdMaps(): {
|
||||
canonicalChartIds: Set<string>;
|
||||
legacyToSeedId: Map<string, string>;
|
||||
} {
|
||||
const legacyToSeedId = new Map<string, string>();
|
||||
const canonicalChartIds = new Set<string>();
|
||||
|
||||
for (const name of fs.readdirSync(SEEDS_DIR)) {
|
||||
if (!/^charts-/u.test(name) || !name.endsWith(".json")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const charts = readJsonArray<{ id: string; legacyChartID?: string }>(
|
||||
path.join(SEEDS_DIR, name),
|
||||
);
|
||||
|
||||
for (const c of charts) {
|
||||
canonicalChartIds.add(c.id);
|
||||
const leg = c.legacyChartID;
|
||||
|
||||
if (leg) {
|
||||
legacyToSeedId.set(leg.toLowerCase(), c.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { canonicalChartIds, legacyToSeedId };
|
||||
}
|
||||
|
||||
function buildFolderLegacyToIdMaps(): {
|
||||
canonicalFolderIds: Set<string>;
|
||||
legacyToSeedId: Map<string, string>;
|
||||
} {
|
||||
const legacyToSeedId = new Map<string, string>();
|
||||
const canonicalFolderIds = new Set<string>();
|
||||
const folders = readJsonArray<{
|
||||
id: string;
|
||||
legacyFolderID: string;
|
||||
}>(path.join(SEEDS_DIR, "folders.json"));
|
||||
|
||||
for (const f of folders) {
|
||||
canonicalFolderIds.add(f.id);
|
||||
|
||||
const leg = f.legacyFolderID;
|
||||
|
||||
if (leg !== undefined && leg !== "") {
|
||||
legacyToSeedId.set(leg.trim(), f.id);
|
||||
}
|
||||
}
|
||||
|
||||
return { canonicalFolderIds, legacyToSeedId };
|
||||
}
|
||||
|
||||
function resolveChartGoalRef(
|
||||
candidate: string,
|
||||
legacyToSeedId: Map<string, string>,
|
||||
canonicalChartIds: Set<string>,
|
||||
context: string,
|
||||
legacyHexRe: RegExp,
|
||||
): string {
|
||||
const t = candidate.trim();
|
||||
const lowered = t.toLowerCase();
|
||||
|
||||
if (canonicalChartIds.has(t)) {
|
||||
return t;
|
||||
}
|
||||
|
||||
const migrated = legacyToSeedId.get(lowered);
|
||||
|
||||
if (migrated !== undefined) {
|
||||
return migrated;
|
||||
}
|
||||
|
||||
if (!legacyHexRe.test(t)) {
|
||||
console.warn(
|
||||
`${context}: chart ref ${JSON.stringify(t)} not in canonical set and not a known legacy SHA — leaving as-is`,
|
||||
);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
throw new Error(`${context}: unknown legacy chart SHA ${JSON.stringify(t)}`);
|
||||
}
|
||||
|
||||
function resolveFolderGoalRef(
|
||||
candidate: string,
|
||||
legacyToSeedId: Map<string, string>,
|
||||
canonicalFolderIds: Set<string>,
|
||||
context: string,
|
||||
): string {
|
||||
const t = candidate.trim();
|
||||
|
||||
if (canonicalFolderIds.has(t)) {
|
||||
return t;
|
||||
}
|
||||
|
||||
const migrated = legacyToSeedId.get(t);
|
||||
|
||||
if (migrated !== undefined) {
|
||||
return migrated;
|
||||
}
|
||||
|
||||
throw new Error(`${context}: unknown folder reference ${JSON.stringify(t)}`);
|
||||
}
|
||||
|
||||
function remapChartsBlock(
|
||||
charts: GoalCharts,
|
||||
context: string,
|
||||
legacyHexRe: RegExp,
|
||||
legacyToSeedChart: Map<string, string>,
|
||||
canonicalChartIds: Set<string>,
|
||||
legacyToSeedFolder: Map<string, string>,
|
||||
canonicalFolderIds: Set<string>,
|
||||
): GoalCharts {
|
||||
switch (charts.type) {
|
||||
case "single":
|
||||
return {
|
||||
...charts,
|
||||
data: resolveChartGoalRef(
|
||||
charts.data,
|
||||
legacyToSeedChart,
|
||||
canonicalChartIds,
|
||||
context,
|
||||
legacyHexRe,
|
||||
),
|
||||
};
|
||||
case "multi":
|
||||
return {
|
||||
...charts,
|
||||
data: charts.data.map((cid, i) =>
|
||||
resolveChartGoalRef(
|
||||
cid,
|
||||
legacyToSeedChart,
|
||||
canonicalChartIds,
|
||||
`${context} [multi ${i}]`,
|
||||
legacyHexRe,
|
||||
),
|
||||
),
|
||||
};
|
||||
case "folder":
|
||||
return {
|
||||
...charts,
|
||||
data: resolveFolderGoalRef(
|
||||
charts.data,
|
||||
legacyToSeedFolder,
|
||||
canonicalFolderIds,
|
||||
context,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const translateMap = new Map<string, string>();
|
||||
|
||||
const goalsPath = path.join(SEEDS_DIR, "goals.json");
|
||||
const goals = readJsonArray<GoalSeed>(goalsPath);
|
||||
|
||||
const { canonicalChartIds, legacyToSeedId: chartLegacy } = buildChartLegacyToIdMaps();
|
||||
const { canonicalFolderIds, legacyToSeedId: folderLegacy } = buildFolderLegacyToIdMaps();
|
||||
|
||||
for (const goal of goals) {
|
||||
const oldId = goal.goalID;
|
||||
|
||||
goal.charts = remapChartsBlock(
|
||||
goal.charts,
|
||||
`goal ${JSON.stringify(goal.name)} (${oldId})`,
|
||||
LEGACY_CHART_HEX_RE,
|
||||
chartLegacy,
|
||||
canonicalChartIds,
|
||||
folderLegacy,
|
||||
canonicalFolderIds,
|
||||
);
|
||||
|
||||
const newId = CreateGoalID(goal.charts, goal.criteria, goal.game);
|
||||
|
||||
goal.goalID = newId;
|
||||
|
||||
if (newId !== oldId) {
|
||||
translateMap.set(oldId, newId);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`goals.json: remapped chart/folder refs; ${translateMap.size} goal IDs will change`);
|
||||
|
||||
fs.writeFileSync(goalsPath, `${JSON.stringify(goals, null, "\t")}\n`);
|
||||
|
||||
type QuestSeed = {
|
||||
desc: string;
|
||||
game: string;
|
||||
name: string;
|
||||
playtype: string;
|
||||
questData: Array<{
|
||||
desc?: string;
|
||||
goals: Array<{ goalID: string; note?: string }>;
|
||||
title: string;
|
||||
}>;
|
||||
questID: string;
|
||||
};
|
||||
|
||||
const questsPath = path.join(SEEDS_DIR, "quests.json");
|
||||
const quests = readJsonArray<QuestSeed>(questsPath);
|
||||
|
||||
let questsUpdated = 0;
|
||||
|
||||
for (const quest of quests) {
|
||||
let touched = false;
|
||||
|
||||
for (const qd of quest.questData) {
|
||||
for (let i = 0; i < qd.goals.length; i++) {
|
||||
const gRef = qd.goals[i];
|
||||
const nextId = translateMap.get(gRef.goalID);
|
||||
|
||||
if (nextId !== undefined && nextId !== gRef.goalID) {
|
||||
qd.goals[i] = { ...gRef, goalID: nextId };
|
||||
touched = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (touched) {
|
||||
questsUpdated++;
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync(questsPath, `${JSON.stringify(quests, null, "\t")}\n`);
|
||||
console.log(`quests.json: updated goal refs on ${questsUpdated} quests`);
|
||||
|
||||
const stabilityMap = readStabilityMap();
|
||||
stabilityMap.goals = Object.fromEntries(translateMap);
|
||||
writeStabilityMap(stabilityMap);
|
||||
console.log(`stability-map.json: wrote ${translateMap.size} goal id translations`);
|
||||
|
||||
const repoRoot = path.resolve(SEEDS_DIR, "..", "..");
|
||||
const biome = path.join(repoRoot, "node_modules", ".bin", "biome");
|
||||
const biomeResult = spawnSync(
|
||||
biome,
|
||||
["format", "--write", goalsPath, questsPath, STABILITY_MAP_PATH],
|
||||
{
|
||||
cwd: repoRoot,
|
||||
stdio: "inherit",
|
||||
},
|
||||
);
|
||||
|
||||
if (biomeResult.status !== 0) {
|
||||
throw new Error(`biome format exited ${biomeResult.status}`);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,10 +22,7 @@
|
||||
"codemirror": "catalog:",
|
||||
"comlink": "catalog:",
|
||||
"fast-json-patch": "catalog:",
|
||||
"nanoid": "catalog:",
|
||||
"natural-compare": "catalog:",
|
||||
"react": "catalog:",
|
||||
"react-bootstrap": "catalog:",
|
||||
"react-dom": "catalog:",
|
||||
"react-query": "catalog:",
|
||||
"react-router-dom": "catalog:",
|
||||
@@ -33,18 +30,14 @@
|
||||
"zod": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/natural-compare": "catalog:",
|
||||
"@types/react": "catalog:",
|
||||
"@types/react-dom": "catalog:",
|
||||
"@types/react-router-dom": "catalog:",
|
||||
"@typescript/native-preview": "catalog:",
|
||||
"@vitejs/plugin-react": "catalog:",
|
||||
"bootstrap-scss": "catalog:",
|
||||
"eslint-config-tachi": "workspace:*",
|
||||
"sass": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vite-plugin-html": "catalog:"
|
||||
"vite": "catalog:"
|
||||
},
|
||||
"engines": {
|
||||
"bun": ">=1.2.0"
|
||||
|
||||
@@ -11,7 +11,6 @@ COMMIT_HASH=
|
||||
LOG_LEVEL=
|
||||
|
||||
# Tachi server config (required)
|
||||
TACHI_MONGO_DATABASE_NAME=
|
||||
TACHI_CAPTCHA_SECRET_KEY=
|
||||
TACHI_SESSION_SECRET=
|
||||
TACHI_OUR_URL=
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"author": "zk",
|
||||
"license": "AGPL3",
|
||||
"devDependencies": {
|
||||
"@types/bun": "catalog:",
|
||||
"@types/pg": "catalog:",
|
||||
"@types/supertest": "catalog:",
|
||||
"@typescript/native-preview": "catalog:",
|
||||
@@ -27,33 +28,23 @@
|
||||
"eslint-config-tachi": "workspace:*",
|
||||
"node-mocks-http": "catalog:",
|
||||
"supertest": "catalog:",
|
||||
"tsconfig-paths": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "catalog:",
|
||||
"@octokit/app": "catalog:",
|
||||
"@types/bcryptjs": "catalog:",
|
||||
"@types/bunyan": "catalog:",
|
||||
"@types/connect-redis": "catalog:",
|
||||
"@types/express": "catalog:",
|
||||
"@types/express-rate-limit": "catalog:",
|
||||
"@types/express-serve-static-core": "catalog:",
|
||||
"@types/express-session": "catalog:",
|
||||
"@types/mongodb": "catalog:",
|
||||
"@types/multer": "catalog:",
|
||||
"@types/node": "catalog:",
|
||||
"@types/node-fetch": "catalog:",
|
||||
"@types/nodemailer": "catalog:",
|
||||
"@types/rate-limit-redis": "catalog:",
|
||||
"@types/redis": "catalog:",
|
||||
"bcryptjs": "catalog:",
|
||||
"bliss": "workspace:*",
|
||||
"bms-table-loader": "catalog:",
|
||||
"bullmq": "catalog:",
|
||||
"bunyan": "catalog:",
|
||||
"commander": "catalog:",
|
||||
"connect-redis": "catalog:",
|
||||
"cron-parser": "catalog:",
|
||||
"csv-parse": "catalog:",
|
||||
"deepmerge": "catalog:",
|
||||
@@ -61,7 +52,6 @@
|
||||
"express": "catalog:",
|
||||
"express-async-errors": "catalog:",
|
||||
"express-prom-bundle": "catalog:",
|
||||
"express-rate-limit": "catalog:",
|
||||
"express-session": "catalog:",
|
||||
"fast-json-stable-hash": "catalog:",
|
||||
"fast-xml-parser": "catalog:",
|
||||
@@ -69,8 +59,6 @@
|
||||
"html-entities": "catalog:",
|
||||
"kysely": "catalog:",
|
||||
"lodash": "catalog:",
|
||||
"mongodb": "catalog:",
|
||||
"monk": "catalog:",
|
||||
"multer": "catalog:",
|
||||
"node-cache": "catalog:",
|
||||
"node-fetch": "catalog:",
|
||||
@@ -79,13 +67,10 @@
|
||||
"pg": "catalog:",
|
||||
"prom-client": "catalog:",
|
||||
"prudence": "catalog:",
|
||||
"rate-limit-redis": "catalog:",
|
||||
"redis": "catalog:",
|
||||
"rg-stats": "catalog:",
|
||||
"tachi-common": "workspace:*",
|
||||
"tachi-db": "workspace:*",
|
||||
"tachi-db-migration-engine": "workspace:*",
|
||||
"ts-node": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"zod": "catalog:"
|
||||
},
|
||||
@@ -98,9 +83,7 @@
|
||||
"include": [
|
||||
"src/*/!(test-utils)/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"src/external/mongo/schemas.ts"
|
||||
]
|
||||
"exclude": []
|
||||
},
|
||||
"engines": {
|
||||
"bun": ">=1.2.0"
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
declare module "mongodb" {
|
||||
interface FindOneOptions<T> {
|
||||
projectID?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
@@ -18,7 +18,6 @@ import { log } from "#lib/log/log";
|
||||
import { maybeStartWorkerMetricsServer } from "#lib/metrics/worker-metrics";
|
||||
import ScoreImportFatalError from "#lib/score-import/framework/score-importing/score-import-error";
|
||||
import { MarkImportAsFailed } from "#lib/score-import/framework/status-tracking/import-status-tracking";
|
||||
import { CloseScoreImportQueue } from "#lib/score-import/worker/queue";
|
||||
import { processScoreImportJobFromPayload } from "#lib/score-import/worker/score-import-job-processor";
|
||||
import { Env } from "#lib/setup/config";
|
||||
import { ClosePgConnection } from "#services/pg/db";
|
||||
@@ -183,7 +182,6 @@ async function bootstrap() {
|
||||
clearInterval(heartbeatInterval);
|
||||
metrics?.close();
|
||||
log.info("Job worker loops stopped, closing resources.");
|
||||
await CloseScoreImportQueue();
|
||||
await CloseRedisConnection();
|
||||
await ClosePgConnection();
|
||||
process.exit(0);
|
||||
|
||||
@@ -20,15 +20,10 @@ export function ToFolderDocument(
|
||||
return {
|
||||
folderID: row.id,
|
||||
slug: row.slug,
|
||||
data: {
|
||||
$comment:
|
||||
"This data does not exist anymore, and you never should've been depending on it, anyway. This field is only here for backwards compatibility.",
|
||||
},
|
||||
game: row.game,
|
||||
inactive: row.inactive,
|
||||
searchTerms: row.search_terms ?? [],
|
||||
title: row.title,
|
||||
type: "charts",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { log } from "#lib/log/log";
|
||||
import { Env, ServerConfig } from "#lib/setup/config";
|
||||
import bunyan from "bunyan";
|
||||
import nodemailer, { type SentMessageInfo, type Transporter } from "nodemailer";
|
||||
|
||||
let transporter: Transporter | undefined;
|
||||
@@ -12,9 +11,7 @@ try {
|
||||
transporter = nodemailer.createTransport({
|
||||
newline: "unix",
|
||||
|
||||
logger: emailConf.TRANSPORT_OPS?.debug
|
||||
? bunyan.createLogger({ name: "Email Logger" })
|
||||
: undefined,
|
||||
logger: emailConf.TRANSPORT_OPS?.debug ? log : undefined,
|
||||
...emailConf.TRANSPORT_OPS,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type http from "http";
|
||||
|
||||
import { log } from "#lib/log/log";
|
||||
import { CloseScoreImportQueue } from "#lib/score-import/worker/queue";
|
||||
import { ClosePgConnection } from "#services/pg/db";
|
||||
import { CloseRedisConnection } from "#services/redis/redis";
|
||||
|
||||
@@ -34,9 +33,6 @@ async function CloseEverythingElse() {
|
||||
log.info({ shutdownInfo: true }, "Closing Redis Connection.");
|
||||
await CloseRedisConnection();
|
||||
|
||||
log.info({ shutdownInfo: true }, "Closing Score Import Queue.");
|
||||
await CloseScoreImportQueue();
|
||||
|
||||
log.info(
|
||||
{
|
||||
shutdownInfo: true,
|
||||
|
||||
+2
-4
@@ -444,8 +444,6 @@ function ParseImportInfo(importInfo: Array<ImportProcessingInfo>) {
|
||||
return { scoreIDs, errors, scoreGameMap: scoreGameMap, chartIDs };
|
||||
}
|
||||
|
||||
function SetJobProgress(job: ScoreImportJob | undefined, description: string) {
|
||||
if (job) {
|
||||
return job.updateProgress({ description });
|
||||
}
|
||||
function SetJobProgress(_job: ScoreImportJob | undefined, _description: string) {
|
||||
// TODO(zk): impl
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* BullMQ score import queue (legacy). Replaced by Postgres `job_queue`.
|
||||
* The API process no longer instantiates a Redis queue; keep a no-op close for shutdown paths.
|
||||
*/
|
||||
export default null;
|
||||
|
||||
export const ScoreImportQueueEvents = null;
|
||||
|
||||
export async function CloseScoreImportQueue(): Promise<void> {
|
||||
// no-op (Postgres `job_queue` is used for score imports)
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { Job } from "bullmq";
|
||||
import type { ImportDocument, ImportTypes, integer } from "tachi-common";
|
||||
|
||||
import type { Parsers } from "../import-types/parsers";
|
||||
@@ -37,12 +36,15 @@ export interface ScoreImportJobData<I extends ImportTypes> {
|
||||
parserArguments: ParserArguments<I>;
|
||||
}
|
||||
|
||||
export type ScoreImportJob<I extends ImportTypes = ImportTypes> = Job<ScoreImportJobData<I>>;
|
||||
|
||||
export interface ScoreImportProgress {
|
||||
description: string;
|
||||
}
|
||||
|
||||
/** Minimal interface for reporting import progress to a job runner. */
|
||||
export interface ScoreImportJob {
|
||||
updateProgress(progress: ScoreImportProgress): Promise<void> | void;
|
||||
}
|
||||
|
||||
export type ScoreImportWorkerReturns =
|
||||
| {
|
||||
description: string;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { ICollection } from "monk";
|
||||
import type { GameGroup } from "tachi-common";
|
||||
|
||||
import fs from "fs/promises";
|
||||
@@ -290,22 +289,3 @@ export async function PullDatabaseSeeds() {
|
||||
throw new Error(`SEEDS_CONFIG was not defined. You cannot pull a seeds repo.`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function BacksyncCollection(
|
||||
collectionName: SeedsCollections,
|
||||
collection: ICollection,
|
||||
commitMessage: string,
|
||||
) {
|
||||
const repo = await PullDatabaseSeeds();
|
||||
|
||||
let charts = await collection.find({});
|
||||
|
||||
await repo.WriteCollection(collectionName, charts);
|
||||
|
||||
// @ts-expect-error Force node to free the memory.
|
||||
charts = null;
|
||||
|
||||
await repo.CommitChangesBack(`${commitMessage} ${new Date().toISOString()}`);
|
||||
|
||||
await repo.Destroy();
|
||||
}
|
||||
|
||||
@@ -1,405 +0,0 @@
|
||||
/**
|
||||
* MongoDB document shapes for {@link ./migrate-to-postgres.ts}.
|
||||
*
|
||||
* These intentionally do **not** match normalized API / `tachi-common` document
|
||||
* types: legacy rows use `game` as a **game group** plus `playtype`, and many
|
||||
* fields are optional or nullable compared to current validation.
|
||||
*
|
||||
* Primary spec reference: `PRE_SCHEMAS` in `typescript/common/src/lib/schemas.ts`
|
||||
* (collection keys match Mongo collection names where applicable).
|
||||
*/
|
||||
|
||||
import type { ScoreData, V3Game } from "tachi-common";
|
||||
import type { GameGroup } from "tachi-db";
|
||||
|
||||
// ─── api-clients ─────────────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoApiClientsCollectionDocument {
|
||||
clientID: string;
|
||||
clientSecret: string;
|
||||
name: string;
|
||||
author: number;
|
||||
requestedPermissions: Array<string>;
|
||||
apiKeyTemplate: string | null;
|
||||
apiKeyFilename: string | null;
|
||||
}
|
||||
|
||||
// ─── api-tokens ──────────────────────────────────────────────────────────────
|
||||
|
||||
/** Stored field names follow server usage (`fromAPIClient`); see PRE_SCHEMAS for idealized names. */
|
||||
export interface MongoApiTokensCollectionDocument {
|
||||
fromAPIClient: string | null;
|
||||
identifier: string;
|
||||
permissions: Record<string, boolean>;
|
||||
token: string | null;
|
||||
userID: number | null;
|
||||
}
|
||||
|
||||
// ─── cg-card-info ────────────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoCgCardInfoCollectionDocument {
|
||||
userID: number;
|
||||
service: string;
|
||||
cardID: string;
|
||||
pin: string;
|
||||
}
|
||||
|
||||
// ─── class-achievements ──────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoClassAchievementsCollectionDocument {
|
||||
userID: number;
|
||||
/** Game group (not V3 slug) in legacy Mongo. */
|
||||
game: string;
|
||||
playtype: string;
|
||||
classSet: string;
|
||||
classOldValue: number | null;
|
||||
classValue: number;
|
||||
timeAchieved: number;
|
||||
}
|
||||
|
||||
// ─── fer-settings ────────────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoFerSettingsCollectionDocument {
|
||||
userID: number;
|
||||
cards?: Array<string> | null;
|
||||
forceStaticImport: boolean;
|
||||
}
|
||||
|
||||
// ─── game-stats ───────────────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoGameStatsCollectionDocument {
|
||||
userID: number;
|
||||
game: string;
|
||||
playtype?: string;
|
||||
ratings: Record<string, number | null>;
|
||||
classes: Record<string, number | null | undefined>;
|
||||
}
|
||||
|
||||
// ─── game-stats-snapshots ────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoGameStatsSnapshotsCollectionDocument {
|
||||
userID: number;
|
||||
game: string;
|
||||
playtype?: string;
|
||||
timestamp: number;
|
||||
playcount: number;
|
||||
ratings: Record<string, number | null>;
|
||||
classes: Record<string, number | null | undefined>;
|
||||
rankings: Record<string, { outOf: number; ranking: number }>;
|
||||
}
|
||||
|
||||
// ─── game-settings (UGPT) ────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoGameSettingsCollectionDocument {
|
||||
userID: number;
|
||||
game: string;
|
||||
playtype?: string;
|
||||
rivals: Array<number>;
|
||||
preferences: {
|
||||
defaultTable: string | null;
|
||||
gameSpecific: unknown;
|
||||
preferredDefaultEnum: "grade" | "lamp" | null;
|
||||
preferredProfileAlg: string | null;
|
||||
preferredRanking: "global" | "rival" | null;
|
||||
preferredScoreAlg: string | null;
|
||||
preferredSessionAlg: string | null;
|
||||
stats: unknown;
|
||||
};
|
||||
}
|
||||
|
||||
// ─── goal-subs ───────────────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoGoalSubsCollectionDocument {
|
||||
goalID: string;
|
||||
userID: number;
|
||||
game: string;
|
||||
playtype: string;
|
||||
achieved: boolean;
|
||||
timeAchieved: number | null;
|
||||
lastInteraction: number | null;
|
||||
progress: number | null;
|
||||
progressHuman: string;
|
||||
outOf: number;
|
||||
outOfHuman: string;
|
||||
wasInstantlyAchieved: boolean;
|
||||
wasAssignedStandalone: boolean;
|
||||
}
|
||||
|
||||
// ─── import-locks ────────────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoImportLocksCollectionDocument {
|
||||
userID: number;
|
||||
locked: boolean;
|
||||
lockedAt: number | null;
|
||||
}
|
||||
|
||||
// ─── import-timings ───────────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoImportTimingsCollectionDocument {
|
||||
importID: string;
|
||||
timestamp: number;
|
||||
total: number;
|
||||
rel: {
|
||||
import: number;
|
||||
importParse: number;
|
||||
pb: number;
|
||||
session: number;
|
||||
};
|
||||
abs: {
|
||||
goal?: number;
|
||||
import?: number;
|
||||
importParse?: number;
|
||||
parse?: number;
|
||||
pb?: number;
|
||||
quest?: number;
|
||||
session?: number;
|
||||
ugs?: number;
|
||||
};
|
||||
}
|
||||
|
||||
// ─── imports ─────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoImportsCollectionDocument {
|
||||
importID: string;
|
||||
userID: number;
|
||||
timeStarted: number;
|
||||
timeFinished: number;
|
||||
scoreIDs?: Array<string>;
|
||||
|
||||
/** Legacy field name — game group, not V3 slug. */
|
||||
game?: GameGroup;
|
||||
gameGroup?: GameGroup;
|
||||
|
||||
/** Oldest-era composite keys; see LEGACY_GPTStringToGame. */
|
||||
gptStrings?: Array<string>;
|
||||
|
||||
/** Legacy list of playtypes for `game` / `gameGroup`. */
|
||||
playtypes?: Array<string>;
|
||||
|
||||
/** Some eras store one playtype string instead of a `playtypes` array. */
|
||||
playtype?: string;
|
||||
|
||||
/** New-era V3 game slugs (API ImportDocument.games). */
|
||||
games?: Array<string>;
|
||||
|
||||
errors?: Array<{ message: string; type: string }>;
|
||||
classDeltas?: Array<{
|
||||
game: string;
|
||||
new: number | string;
|
||||
old?: number | string | null;
|
||||
playtype?: string;
|
||||
set: string;
|
||||
}>;
|
||||
createdSessions?: Array<{ sessionID: string; type: string }>;
|
||||
importType?: string | null;
|
||||
userIntent?: boolean;
|
||||
}
|
||||
|
||||
// ─── invite-locks ────────────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoInviteLocksCollectionDocument {
|
||||
userID: number;
|
||||
locked: boolean;
|
||||
}
|
||||
|
||||
// ─── invites ─────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoInvitesCollectionDocument {
|
||||
code: string;
|
||||
createdBy: number;
|
||||
createdAt: number;
|
||||
consumed: boolean;
|
||||
consumedBy: number | null;
|
||||
consumedAt: number | null;
|
||||
}
|
||||
|
||||
// ─── kai-auth-tokens ───────────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoKaiAuthTokensCollectionDocument {
|
||||
userID: number;
|
||||
service: string;
|
||||
token: string;
|
||||
refreshToken: string;
|
||||
}
|
||||
|
||||
// ─── kshook-sv6c-settings ────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoKshookSv6cSettingsCollectionDocument {
|
||||
userID: number;
|
||||
forceStaticImport: boolean;
|
||||
}
|
||||
|
||||
// ─── myt-card-info ─────────────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoMytCardInfoCollectionDocument {
|
||||
userID: number;
|
||||
cardAccessCode: string;
|
||||
}
|
||||
|
||||
// ─── notifications ─────────────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoNotificationsCollectionDocument {
|
||||
title: string;
|
||||
sentTo: number;
|
||||
sentAt: number;
|
||||
read: boolean;
|
||||
body: { [key: string]: unknown; type: string };
|
||||
}
|
||||
|
||||
// ─── orphan-chart-queue ──────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoOrphanChartQueueCollectionDocument {
|
||||
/** `game:playtype` composite; current schema name in PRE_SCHEMAS. */
|
||||
gptString?: string;
|
||||
/** Same semantics as {@link gptString} on older Mongo rows (e.g. USC orphans). */
|
||||
idString?: string;
|
||||
/** V3 slug or denormalized game field when present on the envelope. */
|
||||
game?: string;
|
||||
/** Embedding often matches {@link ChartDocument}; `game` lives here even when absent at root. */
|
||||
chartDoc: { chartID: string; game?: string } & Record<string, unknown>;
|
||||
songDoc: unknown;
|
||||
userIDs: Array<number>;
|
||||
}
|
||||
|
||||
// ─── orphan-scores ─────────────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoOrphanScoresCollectionDocument {
|
||||
orphanID: string;
|
||||
importType: string;
|
||||
game: GameGroup;
|
||||
userID: number;
|
||||
timeInserted: number;
|
||||
errMsg?: string | null;
|
||||
data: unknown;
|
||||
context: unknown;
|
||||
}
|
||||
|
||||
// ─── quest-subs ──────────────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoQuestSubsCollectionDocument {
|
||||
questID: string;
|
||||
userID: number;
|
||||
game: string;
|
||||
playtype: string;
|
||||
achieved: boolean;
|
||||
timeAchieved: number | null;
|
||||
progress: number;
|
||||
lastInteraction: number | null;
|
||||
wasInstantlyAchieved: boolean;
|
||||
}
|
||||
|
||||
// ─── recent-folder-views ─────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoRecentFolderViewsCollectionDocument {
|
||||
userID: number;
|
||||
folderID: string;
|
||||
lastViewed: number;
|
||||
game?: string;
|
||||
playtype?: string;
|
||||
}
|
||||
|
||||
// ─── score-blacklist ─────────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoScoreBlacklistCollectionDocument {
|
||||
scoreID: string;
|
||||
userID: number;
|
||||
}
|
||||
|
||||
// ─── scores ──────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoScoresCollectionDocument {
|
||||
scoreID: string;
|
||||
service: string;
|
||||
/** Game group or V3 slug depending on era. */
|
||||
game: string;
|
||||
playtype?: string;
|
||||
userID: number;
|
||||
songID: number;
|
||||
chartID: string;
|
||||
isPrimary: boolean;
|
||||
highlight: boolean;
|
||||
comment: string | null;
|
||||
timeAdded: number;
|
||||
importType: string | null;
|
||||
timeAchieved: number | null;
|
||||
scoreData: ScoreData<V3Game>;
|
||||
scoreMeta: Record<string, unknown>;
|
||||
calculatedData: Partial<Record<string, number | null>>;
|
||||
}
|
||||
|
||||
// ─── sessions ────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoSessionsCollectionDocument {
|
||||
sessionID: string;
|
||||
userID: number;
|
||||
name: string;
|
||||
desc: string | null;
|
||||
game: string;
|
||||
playtype?: string;
|
||||
timeInserted: number;
|
||||
timeEnded: number;
|
||||
timeStarted: number;
|
||||
highlight: boolean;
|
||||
calculatedData: Record<string, number | null | undefined>;
|
||||
scoreIDs: Array<string>;
|
||||
}
|
||||
|
||||
/** Projection used when resolving score → session from Mongo. */
|
||||
export type MongoSessionsScoreLookupProjection = Pick<
|
||||
MongoSessionsCollectionDocument,
|
||||
"scoreIDs" | "sessionID"
|
||||
>;
|
||||
|
||||
// ─── user-name-changes ────────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoUserNameChangesCollectionDocument {
|
||||
userID: number;
|
||||
username: string;
|
||||
previousUsername: string;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
// ─── user-private-information ────────────────────────────────────────────────
|
||||
|
||||
export interface MongoUserPrivateInformationCollectionDocument {
|
||||
userID: number;
|
||||
password: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
// ─── user-settings ───────────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoUserSettingsCollectionDocument {
|
||||
userID: number;
|
||||
following: Array<number>;
|
||||
preferences: {
|
||||
advancedMode: boolean;
|
||||
contentiousContent: boolean;
|
||||
deletableScores: boolean;
|
||||
developerMode: boolean;
|
||||
invisible: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
// ─── users ───────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface MongoUsersCollectionDocument {
|
||||
id: number;
|
||||
username: string;
|
||||
socialMedia: {
|
||||
discord: string | null;
|
||||
github: string | null;
|
||||
steam: string | null;
|
||||
twitch: string | null;
|
||||
twitter: string | null;
|
||||
youtube: string | null;
|
||||
};
|
||||
joinDate: number;
|
||||
about: string;
|
||||
status: string | null;
|
||||
customPfpLocation: string | null;
|
||||
customBannerLocation: string | null;
|
||||
lastSeen: number;
|
||||
badges: Array<string>;
|
||||
authLevel: number;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,257 +0,0 @@
|
||||
/**
|
||||
* Re-import Mongo `api-clients` → Postgres `priv_api_client` and Mongo `api-tokens` → `priv_api_token`.
|
||||
*
|
||||
* Use after correcting permission-field mapping bugs — upserts Mongo rows into Postgres (overwrite on PK conflict).
|
||||
*
|
||||
* Postgres dependency order:
|
||||
* If you truncate tokens only: `TRUNCATE priv_api_token;`
|
||||
* If you redo clients too (recommended when fixing client permission columns):
|
||||
* `TRUNCATE priv_api_token, priv_api_client;`
|
||||
* (ordering does not matter in one statement; tokens must be cleared if you truncate clients.)
|
||||
*
|
||||
* Heads-up: `priv_discord_user_map` FKs into `priv_api_token` — resetting tokens may unlink Discord API mapping rows until you reconcile.
|
||||
*
|
||||
* Env:
|
||||
* MONGO_URL — MongoDB URL (default: mongodb://mongo/tachi)
|
||||
* POSTGRES_URL — required
|
||||
*
|
||||
* From `typescript/server`:
|
||||
* MONGO_URL=... POSTGRES_URL=... bun run src/scripts/reimport-priv-api-from-mongo.ts
|
||||
*/
|
||||
|
||||
import type { Database, NewPrivApiClient, NewPrivApiToken } from "tachi-db";
|
||||
|
||||
import { type Insertable, Kysely, PostgresDialect, type RawBuilder, sql } from "kysely";
|
||||
import monk from "monk";
|
||||
import { Pool } from "pg";
|
||||
|
||||
import type {
|
||||
MongoApiClientsCollectionDocument,
|
||||
MongoApiTokensCollectionDocument,
|
||||
} from "./migrate-to-postgres.mongo-docs";
|
||||
|
||||
const MONGO_URL = process.env.MONGO_URL ?? "mongodb://mongo/tachi";
|
||||
const POSTGRES_URL = process.env.POSTGRES_URL;
|
||||
|
||||
if (!POSTGRES_URL) {
|
||||
console.error("[reimport-priv-api] POSTGRES_URL is not set.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const mongoDB = monk(MONGO_URL);
|
||||
|
||||
const pg = new Kysely<Database>({
|
||||
dialect: new PostgresDialect({
|
||||
pool: new Pool({ connectionString: POSTGRES_URL }),
|
||||
}),
|
||||
});
|
||||
|
||||
// ── Same permission normalization as migrate-to-postgres.ts ─────────────────
|
||||
|
||||
function permissionKeyDashVariant(canonicalUnderscoreKey: string): string {
|
||||
return canonicalUnderscoreKey.replaceAll("_", "-");
|
||||
}
|
||||
|
||||
/** requestedPermissions entries may use underscore (`customise_profile`) or dashed legacy strings. */
|
||||
function perm(permissions: Array<string>, canonicalUnderscoreKey: string): boolean | null {
|
||||
const dashed = permissionKeyDashVariant(canonicalUnderscoreKey);
|
||||
|
||||
return permissions.includes(canonicalUnderscoreKey) || permissions.includes(dashed)
|
||||
? true
|
||||
: null;
|
||||
}
|
||||
|
||||
function permRec(
|
||||
permissions: Record<string, boolean>,
|
||||
canonicalUnderscoreKey: string,
|
||||
): boolean | null {
|
||||
const dashed = permissionKeyDashVariant(canonicalUnderscoreKey);
|
||||
|
||||
if (Object.hasOwn(permissions, canonicalUnderscoreKey)) {
|
||||
return permissions[canonicalUnderscoreKey]!;
|
||||
}
|
||||
|
||||
if (Object.hasOwn(permissions, dashed)) {
|
||||
return permissions[dashed]!;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const INSERT_CHUNK = 500;
|
||||
|
||||
/** ON CONFLICT DO UPDATE — set each column from the proposed row (`excluded`). */
|
||||
function onConflictExcludedAll(
|
||||
exampleRow: Record<string, unknown>,
|
||||
): Record<string, RawBuilder<unknown>> {
|
||||
const toSet: Record<string, RawBuilder<unknown>> = {};
|
||||
|
||||
for (const key of Object.keys(exampleRow)) {
|
||||
toSet[key] = sql`excluded.${sql.ref(key)}`;
|
||||
}
|
||||
|
||||
return toSet;
|
||||
}
|
||||
|
||||
async function batchUpsertPrivApiClient(
|
||||
rows: ReadonlyArray<Insertable<Database["priv_api_client"]>>,
|
||||
): Promise<void> {
|
||||
if (rows.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < rows.length; i = i + INSERT_CHUNK) {
|
||||
const chunk = rows.slice(i, i + INSERT_CHUNK);
|
||||
// Sequential chunks — avoid oversized multi-row INSERT payloads.
|
||||
// eslint-disable-next-line no-await-in-loop -- bounded batch upserts
|
||||
await pg
|
||||
.insertInto("priv_api_client")
|
||||
.values(chunk as never)
|
||||
.onConflict((oc) =>
|
||||
oc
|
||||
.column("client_id")
|
||||
.doUpdateSet(onConflictExcludedAll(chunk[0] as Record<string, unknown>)),
|
||||
)
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
|
||||
async function batchUpsertPrivApiToken(
|
||||
rows: ReadonlyArray<Insertable<Database["priv_api_token"]>>,
|
||||
): Promise<void> {
|
||||
if (rows.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < rows.length; i = i + INSERT_CHUNK) {
|
||||
const chunk = rows.slice(i, i + INSERT_CHUNK);
|
||||
// eslint-disable-next-line no-await-in-loop -- bounded batch upserts
|
||||
await pg
|
||||
.insertInto("priv_api_token")
|
||||
.values(chunk as never)
|
||||
.onConflict((oc) =>
|
||||
oc
|
||||
.column("token")
|
||||
.doUpdateSet(onConflictExcludedAll(chunk[0] as Record<string, unknown>)),
|
||||
)
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
|
||||
function getFromApiClient(doc: MongoApiTokensCollectionDocument): string | null {
|
||||
const d = doc as {
|
||||
fromOAuth2Client?: string | null;
|
||||
} & MongoApiTokensCollectionDocument;
|
||||
if (typeof d.fromOAuth2Client === "string") {
|
||||
return d.fromOAuth2Client;
|
||||
}
|
||||
return d.fromAPIClient;
|
||||
}
|
||||
|
||||
function mapApiClientRow(c: MongoApiClientsCollectionDocument): NewPrivApiClient {
|
||||
const extended = c as {
|
||||
redirectURI?: string | null;
|
||||
redirectUri?: string | null;
|
||||
webhookURI?: string | null;
|
||||
webhookUri?: string | null;
|
||||
} & MongoApiClientsCollectionDocument;
|
||||
|
||||
const p = c.requestedPermissions as unknown as Array<string>;
|
||||
|
||||
return {
|
||||
client_id: c.clientID,
|
||||
client_secret: c.clientSecret,
|
||||
name: c.name,
|
||||
author: c.author,
|
||||
pm_customise_profile: perm(p, "customise_profile"),
|
||||
pm_customise_score: perm(p, "customise_score"),
|
||||
pm_customise_session: perm(p, "customise_session"),
|
||||
pm_delete_score: perm(p, "delete_score"),
|
||||
pm_manage_rivals: perm(p, "manage_rivals"),
|
||||
pm_manage_targets: perm(p, "manage_targets"),
|
||||
pm_submit_score: perm(p, "submit_score"),
|
||||
pm_manage_challenges: perm(p, "manage_challenges"),
|
||||
api_key_template: c.apiKeyTemplate,
|
||||
api_key_filename: c.apiKeyFilename,
|
||||
redirect_uri: extended.redirectUri ?? extended.redirectURI ?? null,
|
||||
webhook_uri: extended.webhookUri ?? extended.webhookURI ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
console.log("[reimport-priv-api] fetching api-clients from Mongo...");
|
||||
const clients = await mongoDB.get<MongoApiClientsCollectionDocument>("api-clients").find({});
|
||||
|
||||
const clientRows: Array<NewPrivApiClient> = clients.map((c) => mapApiClientRow(c));
|
||||
|
||||
console.log(
|
||||
`[reimport-priv-api] upserting ${clientRows.length} row(s) into priv_api_client (conflict target: client_id)...`,
|
||||
);
|
||||
|
||||
await batchUpsertPrivApiClient(clientRows);
|
||||
|
||||
console.log("[reimport-priv-api] fetching api-tokens from Mongo...");
|
||||
const apiTokens = await mongoDB.get<MongoApiTokensCollectionDocument>("api-tokens").find({});
|
||||
|
||||
const existingClientIds = new Set(
|
||||
(await pg.selectFrom("priv_api_client").select("client_id").execute()).map(
|
||||
(r) => r.client_id,
|
||||
),
|
||||
);
|
||||
|
||||
const validTokens = apiTokens.filter((t) => {
|
||||
const fromClient = getFromApiClient(t);
|
||||
return (
|
||||
t.token !== null &&
|
||||
t.userID !== null &&
|
||||
(fromClient === null || fromClient === undefined || existingClientIds.has(fromClient))
|
||||
);
|
||||
});
|
||||
|
||||
if (validTokens.length !== apiTokens.length) {
|
||||
console.warn(
|
||||
`[reimport-priv-api] Skipping ${apiTokens.length - validTokens.length} token row(s): null token/userID or dangling oauth client`,
|
||||
);
|
||||
}
|
||||
|
||||
const tokenRows: Array<NewPrivApiToken> = validTokens.map((t) => {
|
||||
const fromClient = getFromApiClient(t);
|
||||
|
||||
const p =
|
||||
t.permissions && typeof t.permissions === "object"
|
||||
? t.permissions
|
||||
: ({} as Record<string, boolean>);
|
||||
|
||||
return {
|
||||
token: t.token!,
|
||||
user_id: t.userID!,
|
||||
identifier: t.identifier,
|
||||
pm_customise_profile: permRec(p, "customise_profile"),
|
||||
pm_customise_score: permRec(p, "customise_score"),
|
||||
pm_customise_session: permRec(p, "customise_session"),
|
||||
pm_delete_score: permRec(p, "delete_score"),
|
||||
pm_manage_rivals: permRec(p, "manage_rivals"),
|
||||
pm_manage_targets: permRec(p, "manage_targets"),
|
||||
pm_submit_score: permRec(p, "submit_score"),
|
||||
pm_manage_challenges: permRec(p, "manage_challenges"),
|
||||
from_oauth2_client: fromClient ?? null,
|
||||
};
|
||||
});
|
||||
|
||||
console.log(
|
||||
`[reimport-priv-api] upserting ${tokenRows.length} row(s) into priv_api_token (conflict target: token)...`,
|
||||
);
|
||||
|
||||
await batchUpsertPrivApiToken(tokenRows);
|
||||
|
||||
console.log("[reimport-priv-api] done.");
|
||||
|
||||
await mongoDB.close();
|
||||
await pg.destroy();
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error("[reimport-priv-api]", err);
|
||||
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -0,0 +1,123 @@
|
||||
import express from "express";
|
||||
import supertest from "supertest";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { createRateLimiter } from "./rate-limiter";
|
||||
|
||||
function makeApp(max: number, windowMs: number) {
|
||||
const limiter = createRateLimiter(max, windowMs);
|
||||
const app = express();
|
||||
|
||||
app.set("trust proxy", true);
|
||||
app.use(limiter);
|
||||
app.get("/", (_req, res) => res.status(200).json({ ok: true }));
|
||||
|
||||
return { app, limiter };
|
||||
}
|
||||
|
||||
describe("createRateLimiter", () => {
|
||||
describe("under the limit", () => {
|
||||
it("allows requests below max", async () => {
|
||||
const { app } = makeApp(3, 60_000);
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const res = await supertest(app).get("/");
|
||||
expect(res.status).toBe(200);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("over the limit", () => {
|
||||
it("returns 429 once max is exceeded", async () => {
|
||||
const { app } = makeApp(2, 60_000);
|
||||
|
||||
await supertest(app).get("/");
|
||||
await supertest(app).get("/");
|
||||
|
||||
const res = await supertest(app).get("/");
|
||||
expect(res.status).toBe(429);
|
||||
expect(res.body.success).toBe(false);
|
||||
expect(res.body.status).toBe(429);
|
||||
});
|
||||
|
||||
it("includes the window duration in the description", async () => {
|
||||
const { app } = makeApp(1, 30_000);
|
||||
|
||||
await supertest(app).get("/");
|
||||
const res = await supertest(app).get("/");
|
||||
|
||||
expect(res.body.description).toContain("30 seconds");
|
||||
});
|
||||
});
|
||||
|
||||
describe("window reset", () => {
|
||||
it("resets the counter after the window expires", async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const { app } = makeApp(1, 1_000);
|
||||
|
||||
await supertest(app).get("/");
|
||||
expect((await supertest(app).get("/")).status).toBe(429);
|
||||
|
||||
// Advance past the window
|
||||
vi.advanceTimersByTime(1_001);
|
||||
|
||||
expect((await supertest(app).get("/")).status).toBe(200);
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
});
|
||||
|
||||
describe("key isolation", () => {
|
||||
it("tracks different IPs independently", async () => {
|
||||
const { app } = makeApp(1, 60_000);
|
||||
|
||||
// First IP exhausts its quota
|
||||
await supertest(app).get("/").set("X-Forwarded-For", "1.2.3.4");
|
||||
expect((await supertest(app).get("/").set("X-Forwarded-For", "1.2.3.4")).status).toBe(
|
||||
429,
|
||||
);
|
||||
|
||||
// Second IP still has quota
|
||||
expect((await supertest(app).get("/").set("X-Forwarded-For", "5.6.7.8")).status).toBe(
|
||||
200,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("resetKey", () => {
|
||||
it("clears the counter for a key so requests are allowed again", async () => {
|
||||
const { app, limiter } = makeApp(1, 60_000);
|
||||
|
||||
await supertest(app).get("/");
|
||||
expect((await supertest(app).get("/")).status).toBe(429);
|
||||
|
||||
limiter.resetKey("::ffff:127.0.0.1");
|
||||
|
||||
expect((await supertest(app).get("/")).status).toBe(200);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Infinity max", () => {
|
||||
it("always allows requests through", async () => {
|
||||
const { app } = makeApp(Infinity, 1);
|
||||
|
||||
for (let i = 0; i < 100; i++) {
|
||||
expect((await supertest(app).get("/")).status).toBe(200);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("exact boundary", () => {
|
||||
it("allows the max-th request and blocks the (max+1)-th", async () => {
|
||||
const max = 5;
|
||||
const { app } = makeApp(max, 60_000);
|
||||
|
||||
for (let i = 0; i < max; i++) {
|
||||
expect((await supertest(app).get("/")).status).toBe(200);
|
||||
}
|
||||
|
||||
expect((await supertest(app).get("/")).status).toBe(429);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,28 +1,72 @@
|
||||
import type { Request } from "express";
|
||||
import type { NextFunction, Request, Response } from "express";
|
||||
import type { integer } from "tachi-common";
|
||||
|
||||
import { ONE_MINUTE } from "#lib/constants/time";
|
||||
import { log } from "#lib/log/log";
|
||||
import { Env, ServerConfig, TachiConfig } from "#lib/setup/config";
|
||||
import { RedisClient } from "#services/redis/redis";
|
||||
import { OmitUndefinedKeys } from "#utils/misc";
|
||||
import rateLimit, { type Options } from "express-rate-limit";
|
||||
import RateLimitRedis from "rate-limit-redis";
|
||||
import { Env, ServerConfig } from "#lib/setup/config";
|
||||
|
||||
if (process.env.NODE_ENV === "test") {
|
||||
// Signal to vitest.setup.ts that this worker has loaded the rate limiter,
|
||||
// so its beforeEach should clear the in-memory cache. Pure-unit test files
|
||||
// that never load any router skip the cache-clear (and the import chain)
|
||||
// entirely.
|
||||
(globalThis as { __tachi_rate_limiter_loaded?: boolean }).__tachi_rate_limiter_loaded = true;
|
||||
interface RateLimiterEntry {
|
||||
count: integer;
|
||||
resetAt: number;
|
||||
}
|
||||
|
||||
function CreateStore(name: string) {
|
||||
// undefined forces a default to an in-memory store
|
||||
// So we use that when in testing or localdev.
|
||||
return Env.NODE_ENV === "production" || Env.NODE_ENV === "staging"
|
||||
? new RateLimitRedis({ prefix: `${TachiConfig.NAME}-RL:${name}`, client: RedisClient })
|
||||
: undefined;
|
||||
export interface RateLimitMiddleware {
|
||||
(req: Request, res: Response, next: NextFunction): void;
|
||||
resetKey(key: string): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixed-window in-memory rate limiter. Each unique `req.ip` gets its own counter
|
||||
* that resets after `windowMs`. Pass `Infinity` for `max` to create a no-op limiter.
|
||||
*/
|
||||
export function createRateLimiter(
|
||||
max: integer | typeof Infinity,
|
||||
windowMs: number,
|
||||
): RateLimitMiddleware {
|
||||
const store = new Map<string, RateLimiterEntry>();
|
||||
|
||||
function middleware(req: Request, res: Response, next: NextFunction): void {
|
||||
if (max === Infinity) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
const key = req.ip ?? "unknown";
|
||||
const now = Date.now();
|
||||
|
||||
let entry = store.get(key);
|
||||
|
||||
if (!entry || now >= entry.resetAt) {
|
||||
entry = { count: 0, resetAt: now + windowMs };
|
||||
store.set(key, entry);
|
||||
}
|
||||
|
||||
entry.count++;
|
||||
|
||||
if (entry.count <= max) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
log.warn({ url: req.url, method: req.method }, `User ${key} hit rate limit.`);
|
||||
|
||||
res.status(429).json({
|
||||
success: false,
|
||||
description: `You have exceeded ${max} requests per ${windowMs / 1000} seconds. Please wait.`,
|
||||
status: 429,
|
||||
message: "You're being rate limited.",
|
||||
});
|
||||
}
|
||||
|
||||
middleware.resetKey = (key: string): void => {
|
||||
store.delete(key);
|
||||
};
|
||||
|
||||
return middleware;
|
||||
}
|
||||
|
||||
if (Env.NODE_ENV === "test") {
|
||||
(globalThis as { __tachi_rate_limiter_loaded?: boolean }).__tachi_rate_limiter_loaded = true;
|
||||
}
|
||||
|
||||
/** Express / supertest may use any of these as `req.ip` for loopback. */
|
||||
@@ -36,50 +80,17 @@ export function ClearTestingRateLimitCache() {
|
||||
}
|
||||
}
|
||||
|
||||
const CreateRateLimitOptions = (max: integer, name: string, windowMs?: number): Partial<Options> =>
|
||||
OmitUndefinedKeys({
|
||||
max,
|
||||
onLimitReached: (req: Request) => {
|
||||
log.warn(
|
||||
{
|
||||
url: req.url,
|
||||
method: req.method,
|
||||
hideFromConsole: ["req"],
|
||||
},
|
||||
`User ${req.ip} hit rate limit.`,
|
||||
);
|
||||
},
|
||||
store: CreateStore(name),
|
||||
message: {
|
||||
success: false,
|
||||
description: `You have exceeded ${max} requests per ${
|
||||
(windowMs ?? 60_000) / 1000
|
||||
} seconds. Please wait.`,
|
||||
status: 429,
|
||||
message: "You're being rate limited.",
|
||||
},
|
||||
windowMs,
|
||||
});
|
||||
// 100 requests / minute
|
||||
export const NormalRateLimitMiddleware = createRateLimiter(ServerConfig.RATE_LIMIT, ONE_MINUTE);
|
||||
|
||||
// 100 requests / minute is the current cap
|
||||
export const NormalRateLimitMiddleware = rateLimit(
|
||||
CreateRateLimitOptions(ServerConfig.RATE_LIMIT, "Normal"),
|
||||
);
|
||||
// 15 requests every 10 minutes
|
||||
export const AggressiveRateLimitMiddleware = createRateLimiter(15, ONE_MINUTE * 10);
|
||||
|
||||
// 15 requests every 10 minutes.
|
||||
export const AggressiveRateLimitMiddleware = rateLimit(
|
||||
CreateRateLimitOptions(15, "Aggressive", ONE_MINUTE * 10),
|
||||
);
|
||||
// 2 requests every 5 minutes
|
||||
export const HyperAggressiveRateLimitMiddleware = createRateLimiter(2, ONE_MINUTE * 5);
|
||||
|
||||
// 2 requests every 5 minutes.
|
||||
export const HyperAggressiveRateLimitMiddleware = rateLimit(
|
||||
CreateRateLimitOptions(2, "HyAgressive", ONE_MINUTE * 5),
|
||||
);
|
||||
|
||||
// 5 requests every minute. This one has a tighter window, so it is less
|
||||
// vulnerable to bursting down the server.
|
||||
// Vitest: unlimited. Optional: TACHI_DISABLE_SCORE_IMPORT_RATE_LIMIT for local stress runs.
|
||||
// 5 requests per minute; unlimited in test or when explicitly disabled for load testing
|
||||
export const ScoreImportRateLimiter =
|
||||
Env.NODE_ENV === "test" || ServerConfig.DISABLE_SCORE_IMPORT_RATE_LIMIT
|
||||
? rateLimit(CreateRateLimitOptions(Infinity, "ScImport", ONE_MINUTE))
|
||||
: rateLimit(CreateRateLimitOptions(5, "ScImport", ONE_MINUTE));
|
||||
? createRateLimiter(Infinity, ONE_MINUTE)
|
||||
: createRateLimiter(5, ONE_MINUTE);
|
||||
|
||||
@@ -678,9 +678,7 @@ API_V1_ROUTER.add("GET /games/:game/targets/recently-raised", withGame, async ({
|
||||
API_V1_ROUTER.add("GET /games/:game/targets/goals/popular", withGame, async ({ ctx }) => {
|
||||
const { gameGroup, playtype } = LEGACY_GameToGameGroupPT(ctx.game);
|
||||
|
||||
// GetMostSubscribedGoals internally converts GameGroup+playtype to V3Game for filtering
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const goals = await GetMostSubscribedGoals({ game: gameGroup as any, playtype });
|
||||
const goals = await GetMostSubscribedGoals({ game: gameGroup, playtype });
|
||||
|
||||
return success(`Returned ${goals.length} goals.`, goals);
|
||||
});
|
||||
|
||||
+2
-6
@@ -97,9 +97,7 @@ describe("GET /api/v1/users/:userID/games/:game/custom-tables/:tableUrlName", ()
|
||||
it("returns HTML with a bmstable meta header for user-specific tables", async () => {
|
||||
const { id } = await seedUser({ username: "bms_custom_tbl" });
|
||||
|
||||
const res = await mockApi.get(
|
||||
`/api/v1/users/${id}/games/bms-7k/custom-tables/rival-info`,
|
||||
);
|
||||
const res = await mockApi.get(`/api/v1/users/${id}/games/bms-7k/custom-tables/rival-info`);
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.text).toContain('meta name="bmstable"');
|
||||
@@ -111,9 +109,7 @@ describe("GET /api/v1/users/:userID/games/:game/custom-tables/:tableUrlName", ()
|
||||
it("returns 404 when fetching a public table from the user route", async () => {
|
||||
const { id } = await seedUser({ username: "bms_custom_tbl_pub" });
|
||||
|
||||
const res = await mockApi.get(
|
||||
`/api/v1/users/${id}/games/bms-7k/custom-tables/sieglindeEC`,
|
||||
);
|
||||
const res = await mockApi.get(`/api/v1/users/${id}/games/bms-7k/custom-tables/sieglindeEC`);
|
||||
|
||||
expect(res.status).toBe(404);
|
||||
expect(res.body.description).toContain("user specific");
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { integer } from "tachi-common";
|
||||
|
||||
import "express-async-errors";
|
||||
import connectRedis from "connect-redis";
|
||||
import express, { type Express } from "express";
|
||||
// THIS IMPORT **MUST** GO HERE. DO NOT MOVE IT. IT MUST OCCUR BEFORE ANYTHING HAPPENS WITH EXPRESS
|
||||
// BUT AFTER EXPRESS IS IMPORTED.
|
||||
@@ -9,7 +8,6 @@ import express, { type Express } from "express";
|
||||
import { SYMBOL_TACHI_API_AUTH } from "#lib/constants/tachi";
|
||||
import { log } from "#lib/log/log";
|
||||
import { Env, ServerConfig, TachiConfig } from "#lib/setup/config";
|
||||
import { RedisClient } from "#services/redis/redis";
|
||||
import { IsNonEmptyString, IsRecord } from "#utils/misc";
|
||||
import { ExpectedErr } from "bliss";
|
||||
import expressSession from "express-session";
|
||||
@@ -23,14 +21,14 @@ let store;
|
||||
|
||||
if (Env.NODE_ENV !== "test") {
|
||||
log.info({ bootInfo: true }, "Connecting ExpressSession to Redis.");
|
||||
const RedisStore = connectRedis(expressSession);
|
||||
|
||||
store = new RedisStore({
|
||||
host: "localhost",
|
||||
port: 6379,
|
||||
client: RedisClient,
|
||||
prefix: TachiConfig.NAME,
|
||||
});
|
||||
// n.b. load bearing prefix here - do not remove the prefix for any reason.
|
||||
// Dynamic import keeps redis.ts (which uses Bun.RedisClient) out of the
|
||||
// module graph when running under Vitest's Node.js worker threads in tests.
|
||||
const [{ RedisClient }, { BunRedisSessionStore }] = await Promise.all([
|
||||
import("#services/redis/redis"),
|
||||
import("#services/redis/session-store"),
|
||||
]);
|
||||
store = new BunRedisSessionStore(RedisClient, { prefix: TachiConfig.NAME });
|
||||
}
|
||||
|
||||
const userSessionMiddleware = expressSession({
|
||||
|
||||
@@ -1,54 +1,20 @@
|
||||
import { ONE_MINUTE, ONE_SECOND } from "#lib/constants/time";
|
||||
import { log } from "#lib/log/log";
|
||||
import { Env } from "#lib/setup/config";
|
||||
import { GetMillisecondsSince } from "#utils/misc";
|
||||
import redis from "redis";
|
||||
|
||||
log.debug({ bootInfo: true }, "Instantiated Redis Store");
|
||||
|
||||
export const RedisClient = redis.createClient({
|
||||
url: `redis://${Env.REDIS_URL}`,
|
||||
});
|
||||
|
||||
const startConnect = process.hrtime.bigint();
|
||||
|
||||
log.debug({ bootInfo: true }, "Instantiated Redis Client");
|
||||
|
||||
function EmitCritical() {
|
||||
/* istanbul ignore next */
|
||||
if (!RedisClient.connected) {
|
||||
log.fatal(`Could not connect to redis in time. No more information is available.`);
|
||||
export const RedisClient = new Bun.RedisClient(`redis://${Env.REDIS_URL}`);
|
||||
|
||||
// can't connect. kill self after 1 second.
|
||||
setTimeout(() => {
|
||||
process.exit(1);
|
||||
}, ONE_SECOND);
|
||||
}
|
||||
}
|
||||
|
||||
// awful performance on windows and in test runners mean that connecting to redis can be a
|
||||
// nearly FIVE minute endeavour!
|
||||
const ref = setTimeout(EmitCritical, ONE_MINUTE * 5);
|
||||
|
||||
RedisClient.on("connect", () => {
|
||||
RedisClient.onconnect = () => {
|
||||
log.info(
|
||||
{
|
||||
bootInfo: true,
|
||||
},
|
||||
{ bootInfo: true },
|
||||
`Connected to Redis. Took ${GetMillisecondsSince(startConnect)}ms`,
|
||||
);
|
||||
};
|
||||
|
||||
clearTimeout(ref);
|
||||
});
|
||||
|
||||
export function CloseRedisConnection() {
|
||||
return new Promise((resolve, reject) => {
|
||||
RedisClient.quit((err, reply) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(reply);
|
||||
}
|
||||
});
|
||||
});
|
||||
export function CloseRedisConnection(): void {
|
||||
RedisClient.close();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import { type SessionData, Store } from "express-session";
|
||||
|
||||
/**
|
||||
* express-session store backed by Bun's built-in Redis client.
|
||||
*/
|
||||
export class BunRedisSessionStore extends Store {
|
||||
private readonly client: InstanceType<typeof Bun.RedisClient>;
|
||||
private readonly defaultTtl: number;
|
||||
private readonly prefix: string;
|
||||
|
||||
constructor(
|
||||
client: InstanceType<typeof Bun.RedisClient>,
|
||||
options: { prefix?: string; ttl?: number } = {},
|
||||
) {
|
||||
super();
|
||||
this.client = client;
|
||||
this.prefix = options.prefix ?? "sess:";
|
||||
this.defaultTtl = options.ttl ?? 86_400;
|
||||
}
|
||||
|
||||
private key(sid: string): string {
|
||||
return this.prefix + sid;
|
||||
}
|
||||
|
||||
private ttl(session: SessionData): number {
|
||||
if (session.cookie?.expires) {
|
||||
const ms = Number(new Date(session.cookie.expires)) - Date.now();
|
||||
return Math.max(1, Math.ceil(ms / 1000));
|
||||
}
|
||||
|
||||
return this.defaultTtl;
|
||||
}
|
||||
|
||||
destroy(sid: string, callback?: (err?: unknown) => void): void {
|
||||
this.client
|
||||
.del(this.key(sid))
|
||||
.then(() => callback?.())
|
||||
.catch((err) => callback?.(err));
|
||||
}
|
||||
|
||||
get(sid: string, callback: (err: unknown, session?: SessionData | null) => void): void {
|
||||
this.client
|
||||
.get(this.key(sid))
|
||||
.then((data) => {
|
||||
if (!data) {
|
||||
return callback(null, null);
|
||||
}
|
||||
try {
|
||||
callback(null, JSON.parse(data) as SessionData);
|
||||
} catch (e) {
|
||||
callback(e);
|
||||
}
|
||||
})
|
||||
.catch((err) => callback(err));
|
||||
}
|
||||
|
||||
set(sid: string, session: SessionData, callback?: (err?: unknown) => void): void {
|
||||
this.client
|
||||
.setex(this.key(sid), this.ttl(session), JSON.stringify(session))
|
||||
.then(() => callback?.())
|
||||
.catch((err) => callback?.(err));
|
||||
}
|
||||
|
||||
touch(sid: string, session: SessionData, callback?: () => void): void {
|
||||
this.client
|
||||
.expire(this.key(sid), this.ttl(session))
|
||||
.then(() => callback?.())
|
||||
.catch(() => callback?.());
|
||||
}
|
||||
}
|
||||
@@ -926,12 +926,8 @@ export const HC511UserGoal: GoalSubscriptionDocument = {
|
||||
export const TestingIIDXFolderSP10: FolderDocument = {
|
||||
title: "Level 10",
|
||||
game: "iidx-sp",
|
||||
type: "charts",
|
||||
folderID: "ed9d8c734447ce67d7135c0067441a98cc81aeaf",
|
||||
slug: "level-10",
|
||||
data: {
|
||||
level: "10",
|
||||
},
|
||||
searchTerms: [],
|
||||
inactive: false,
|
||||
};
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { FilterQuery } from "mongodb";
|
||||
import type { Game } from "tachi-db";
|
||||
|
||||
import { GetChartsByIds, SELECT_CHART, ToChartDocument } from "#lib/db-formats/chart";
|
||||
@@ -21,14 +20,12 @@ import {
|
||||
FormatChart,
|
||||
type GameGroup,
|
||||
type GoalDocument,
|
||||
type GoalSubscriptionDocument,
|
||||
type integer,
|
||||
LEGACY_GameGroupPTToGame,
|
||||
type LEGACY_Playtype,
|
||||
type PBScoreDocument,
|
||||
type QuestDocument,
|
||||
type QuestlineDocument,
|
||||
type QuestSubscriptionDocument,
|
||||
type ScoreDocument,
|
||||
} from "tachi-common";
|
||||
|
||||
@@ -36,6 +33,16 @@ type MsTimeRange =
|
||||
| number
|
||||
| { $gt?: number; $gte?: number; $lt?: number; $lte?: number; $ne?: null };
|
||||
|
||||
type UserIDFilter = integer | { $in: Array<integer> };
|
||||
|
||||
interface TargetSubFilter {
|
||||
game?: GameGroup;
|
||||
playtype?: LEGACY_Playtype;
|
||||
userID?: UserIDFilter;
|
||||
timeAchieved?: MsTimeRange;
|
||||
lastInteraction?: MsTimeRange;
|
||||
}
|
||||
|
||||
function applyMsTimeOnColumn(
|
||||
column:
|
||||
| "goal_sub.last_interaction"
|
||||
@@ -92,7 +99,7 @@ function applyMsTimeOnColumn(
|
||||
};
|
||||
}
|
||||
|
||||
function whereUserIdOnGoalSub(userID: unknown) {
|
||||
function whereUserIdOnGoalSub(userID: UserIDFilter | undefined) {
|
||||
if (userID === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -101,25 +108,16 @@ function whereUserIdOnGoalSub(userID: unknown) {
|
||||
return (eb: any) => eb("goal_sub.user_id", "=", userID);
|
||||
}
|
||||
|
||||
if (
|
||||
userID &&
|
||||
typeof userID === "object" &&
|
||||
"$in" in userID &&
|
||||
Array.isArray((userID as { $in: Array<integer> }).$in)
|
||||
) {
|
||||
const ids = (userID as { $in: Array<integer> }).$in;
|
||||
const ids = userID.$in;
|
||||
|
||||
if (ids.length === 0) {
|
||||
return () => sql`false`;
|
||||
}
|
||||
|
||||
return (eb: any) => eb("goal_sub.user_id", "in", ids);
|
||||
if (ids.length === 0) {
|
||||
return () => sql`false`;
|
||||
}
|
||||
|
||||
throw new Error("Unsupported userID filter for goal_sub.");
|
||||
return (eb: any) => eb("goal_sub.user_id", "in", ids);
|
||||
}
|
||||
|
||||
function whereUserIdOnQuestSub(userID: unknown) {
|
||||
function whereUserIdOnQuestSub(userID: UserIDFilter | undefined) {
|
||||
if (userID === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -128,22 +126,13 @@ function whereUserIdOnQuestSub(userID: unknown) {
|
||||
return (eb: any) => eb("quest_sub.user_id", "=", userID);
|
||||
}
|
||||
|
||||
if (
|
||||
userID &&
|
||||
typeof userID === "object" &&
|
||||
"$in" in userID &&
|
||||
Array.isArray((userID as { $in: Array<integer> }).$in)
|
||||
) {
|
||||
const ids = (userID as { $in: Array<integer> }).$in;
|
||||
const ids = userID.$in;
|
||||
|
||||
if (ids.length === 0) {
|
||||
return () => sql`false`;
|
||||
}
|
||||
|
||||
return (eb: any) => eb("quest_sub.user_id", "in", ids);
|
||||
if (ids.length === 0) {
|
||||
return () => sql`false`;
|
||||
}
|
||||
|
||||
throw new Error("Unsupported userID filter for quest_sub.");
|
||||
return (eb: any) => eb("quest_sub.user_id", "in", ids);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -266,49 +255,36 @@ export async function HumaniseChartID(chartID: string) {
|
||||
* Get recently achieved goals for this query.
|
||||
*
|
||||
* @param baseQuery - A base query, used to limit results on GPTs or UGPTs.
|
||||
* @param limit - How many recently achieved goals to search for. MongoDB: `0` means no limit.
|
||||
* @param limit - How many recently achieved goals to search for. `0` means no limit.
|
||||
* @returns - The goals and their subs.
|
||||
*/
|
||||
export async function GetRecentlyAchievedGoals(
|
||||
baseQuery: Omit<FilterQuery<GoalSubscriptionDocument>, "achieved">,
|
||||
{ game, playtype, userID, timeAchieved, lastInteraction }: TargetSubFilter,
|
||||
limit = 100,
|
||||
) {
|
||||
const b = baseQuery as Record<string, unknown>;
|
||||
|
||||
let q = DB.selectFrom("goal_sub")
|
||||
.innerJoin("goal", "goal.id", "goal_sub.goal_id")
|
||||
.select(SELECT_GOAL_SUB_WITH_GOAL_GAME)
|
||||
.where("goal_sub.was_instantly_achieved", "=", false)
|
||||
.where("goal_sub.achieved", "=", true);
|
||||
|
||||
const uidFn = whereUserIdOnGoalSub(b.userID);
|
||||
const uidFn = whereUserIdOnGoalSub(userID);
|
||||
|
||||
if (uidFn) {
|
||||
q = q.where(uidFn);
|
||||
}
|
||||
|
||||
if (b.game !== undefined && b.playtype !== undefined) {
|
||||
const v3 = LEGACY_GameGroupPTToGame(
|
||||
b.game as GameGroup,
|
||||
b.playtype as LEGACY_Playtype,
|
||||
) as Game;
|
||||
|
||||
q = q.where("goal.game", "=", v3);
|
||||
if (game !== undefined && playtype !== undefined) {
|
||||
q = q.where("goal.game", "=", LEGACY_GameGroupPTToGame(game, playtype) as Game);
|
||||
}
|
||||
|
||||
const taFn = applyMsTimeOnColumn(
|
||||
"goal_sub.time_achieved",
|
||||
b.timeAchieved as MsTimeRange | undefined,
|
||||
);
|
||||
const taFn = applyMsTimeOnColumn("goal_sub.time_achieved", timeAchieved);
|
||||
|
||||
if (taFn) {
|
||||
q = q.where(taFn);
|
||||
}
|
||||
|
||||
const liFn = applyMsTimeOnColumn(
|
||||
"goal_sub.last_interaction",
|
||||
b.lastInteraction as MsTimeRange | undefined,
|
||||
);
|
||||
const liFn = applyMsTimeOnColumn("goal_sub.last_interaction", lastInteraction);
|
||||
|
||||
if (liFn) {
|
||||
q = q.where(liFn);
|
||||
@@ -341,11 +317,9 @@ export async function GetRecentlyAchievedGoals(
|
||||
}
|
||||
|
||||
export async function GetRecentlyInteractedGoals(
|
||||
baseQuery: Omit<FilterQuery<GoalSubscriptionDocument>, "achieved">,
|
||||
{ game, playtype, userID, timeAchieved, lastInteraction }: TargetSubFilter,
|
||||
limit = 100,
|
||||
) {
|
||||
const b = baseQuery as Record<string, unknown>;
|
||||
|
||||
let q = DB.selectFrom("goal_sub")
|
||||
.innerJoin("goal", "goal.id", "goal_sub.goal_id")
|
||||
.select(SELECT_GOAL_SUB_WITH_GOAL_GAME)
|
||||
@@ -353,34 +327,23 @@ export async function GetRecentlyInteractedGoals(
|
||||
.where("goal_sub.achieved", "=", false)
|
||||
.where("goal_sub.last_interaction", "is not", null);
|
||||
|
||||
const uidFn = whereUserIdOnGoalSub(b.userID);
|
||||
const uidFn = whereUserIdOnGoalSub(userID);
|
||||
|
||||
if (uidFn) {
|
||||
q = q.where(uidFn);
|
||||
}
|
||||
|
||||
if (b.game !== undefined && b.playtype !== undefined) {
|
||||
const v3 = LEGACY_GameGroupPTToGame(
|
||||
b.game as GameGroup,
|
||||
b.playtype as LEGACY_Playtype,
|
||||
) as Game;
|
||||
|
||||
q = q.where("goal.game", "=", v3);
|
||||
if (game !== undefined && playtype !== undefined) {
|
||||
q = q.where("goal.game", "=", LEGACY_GameGroupPTToGame(game, playtype) as Game);
|
||||
}
|
||||
|
||||
const taFn = applyMsTimeOnColumn(
|
||||
"goal_sub.time_achieved",
|
||||
b.timeAchieved as MsTimeRange | undefined,
|
||||
);
|
||||
const taFn = applyMsTimeOnColumn("goal_sub.time_achieved", timeAchieved);
|
||||
|
||||
if (taFn) {
|
||||
q = q.where(taFn);
|
||||
}
|
||||
|
||||
const liFn = applyMsTimeOnColumn(
|
||||
"goal_sub.last_interaction",
|
||||
b.lastInteraction as MsTimeRange | undefined,
|
||||
);
|
||||
const liFn = applyMsTimeOnColumn("goal_sub.last_interaction", lastInteraction);
|
||||
|
||||
if (liFn) {
|
||||
q = q.where(liFn);
|
||||
@@ -413,45 +376,32 @@ export async function GetRecentlyInteractedGoals(
|
||||
}
|
||||
|
||||
export async function GetRecentlyAchievedQuests(
|
||||
baseQuery: Omit<FilterQuery<QuestSubscriptionDocument>, "achieved">,
|
||||
{ game, playtype, userID, timeAchieved, lastInteraction }: TargetSubFilter,
|
||||
limit = 100,
|
||||
) {
|
||||
const b = baseQuery as Record<string, unknown>;
|
||||
|
||||
let q = DB.selectFrom("quest_sub")
|
||||
.innerJoin("quest", "quest.id", "quest_sub.quest_id")
|
||||
.select(SELECT_QUEST_SUB_WITH_QUEST_GAME)
|
||||
.where("quest_sub.was_instantly_achieved", "=", false)
|
||||
.where("quest_sub.achieved", "=", true);
|
||||
|
||||
const uidFn = whereUserIdOnQuestSub(b.userID);
|
||||
const uidFn = whereUserIdOnQuestSub(userID);
|
||||
|
||||
if (uidFn) {
|
||||
q = q.where(uidFn);
|
||||
}
|
||||
|
||||
if (b.game !== undefined && b.playtype !== undefined) {
|
||||
const v3 = LEGACY_GameGroupPTToGame(
|
||||
b.game as GameGroup,
|
||||
b.playtype as LEGACY_Playtype,
|
||||
) as Game;
|
||||
|
||||
q = q.where("quest.game", "=", v3);
|
||||
if (game !== undefined && playtype !== undefined) {
|
||||
q = q.where("quest.game", "=", LEGACY_GameGroupPTToGame(game, playtype) as Game);
|
||||
}
|
||||
|
||||
const taFn = applyMsTimeOnColumn(
|
||||
"quest_sub.time_achieved",
|
||||
b.timeAchieved as MsTimeRange | undefined,
|
||||
);
|
||||
const taFn = applyMsTimeOnColumn("quest_sub.time_achieved", timeAchieved);
|
||||
|
||||
if (taFn) {
|
||||
q = q.where(taFn);
|
||||
}
|
||||
|
||||
const liFn = applyMsTimeOnColumn(
|
||||
"quest_sub.last_interaction",
|
||||
b.lastInteraction as MsTimeRange | undefined,
|
||||
);
|
||||
const liFn = applyMsTimeOnColumn("quest_sub.last_interaction", lastInteraction);
|
||||
|
||||
if (liFn) {
|
||||
q = q.where(liFn);
|
||||
@@ -483,11 +433,9 @@ export async function GetRecentlyAchievedQuests(
|
||||
}
|
||||
|
||||
export async function GetRecentlyInteractedQuests(
|
||||
baseQuery: Omit<FilterQuery<QuestSubscriptionDocument>, "achieved">,
|
||||
{ game, playtype, userID, timeAchieved, lastInteraction }: TargetSubFilter,
|
||||
limit = 100,
|
||||
) {
|
||||
const b = baseQuery as Record<string, unknown>;
|
||||
|
||||
let q = DB.selectFrom("quest_sub")
|
||||
.innerJoin("quest", "quest.id", "quest_sub.quest_id")
|
||||
.select(SELECT_QUEST_SUB_WITH_QUEST_GAME)
|
||||
@@ -495,34 +443,23 @@ export async function GetRecentlyInteractedQuests(
|
||||
.where("quest_sub.achieved", "=", false)
|
||||
.where("quest_sub.was_instantly_achieved", "=", false);
|
||||
|
||||
const uidFn = whereUserIdOnQuestSub(b.userID);
|
||||
const uidFn = whereUserIdOnQuestSub(userID);
|
||||
|
||||
if (uidFn) {
|
||||
q = q.where(uidFn);
|
||||
}
|
||||
|
||||
if (b.game !== undefined && b.playtype !== undefined) {
|
||||
const v3 = LEGACY_GameGroupPTToGame(
|
||||
b.game as GameGroup,
|
||||
b.playtype as LEGACY_Playtype,
|
||||
) as Game;
|
||||
|
||||
q = q.where("quest.game", "=", v3);
|
||||
if (game !== undefined && playtype !== undefined) {
|
||||
q = q.where("quest.game", "=", LEGACY_GameGroupPTToGame(game, playtype) as Game);
|
||||
}
|
||||
|
||||
const taFn = applyMsTimeOnColumn(
|
||||
"quest_sub.time_achieved",
|
||||
b.timeAchieved as MsTimeRange | undefined,
|
||||
);
|
||||
const taFn = applyMsTimeOnColumn("quest_sub.time_achieved", timeAchieved);
|
||||
|
||||
if (taFn) {
|
||||
q = q.where(taFn);
|
||||
}
|
||||
|
||||
const liFn = applyMsTimeOnColumn(
|
||||
"quest_sub.last_interaction",
|
||||
b.lastInteraction as MsTimeRange | undefined,
|
||||
);
|
||||
const liFn = applyMsTimeOnColumn("quest_sub.last_interaction", lastInteraction);
|
||||
|
||||
if (liFn) {
|
||||
q = q.where(liFn);
|
||||
@@ -553,7 +490,7 @@ export async function GetRecentlyInteractedQuests(
|
||||
return { quests, questSubs };
|
||||
}
|
||||
|
||||
function whereUserIdOnGoalSubForAggregate(userID: unknown) {
|
||||
function whereUserIdOnGoalSubForAggregate(userID: UserIDFilter | undefined) {
|
||||
if (userID === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -562,49 +499,33 @@ function whereUserIdOnGoalSubForAggregate(userID: unknown) {
|
||||
return (eb: any) => eb("goal_sub.user_id", "=", userID);
|
||||
}
|
||||
|
||||
if (
|
||||
userID &&
|
||||
typeof userID === "object" &&
|
||||
"$in" in userID &&
|
||||
Array.isArray((userID as { $in: Array<integer> }).$in)
|
||||
) {
|
||||
const ids = (userID as { $in: Array<integer> }).$in;
|
||||
const ids = userID.$in;
|
||||
|
||||
if (ids.length === 0) {
|
||||
return () => sql`false`;
|
||||
}
|
||||
|
||||
return (eb: any) => eb("goal_sub.user_id", "in", ids);
|
||||
if (ids.length === 0) {
|
||||
return () => sql`false`;
|
||||
}
|
||||
|
||||
throw new Error("Unsupported userID filter for goal_sub aggregate.");
|
||||
return (eb: any) => eb("goal_sub.user_id", "in", ids);
|
||||
}
|
||||
|
||||
export async function GetMostSubscribedGoals(
|
||||
query: FilterQuery<GoalSubscriptionDocument>,
|
||||
{ game, playtype, userID }: TargetSubFilter,
|
||||
limit = 100,
|
||||
): Promise<Array<{ __subscriptions: integer } & GoalDocument>> {
|
||||
const b = query as Record<string, unknown>;
|
||||
|
||||
let q = DB.selectFrom("goal_sub")
|
||||
.innerJoin("goal", "goal.id", "goal_sub.goal_id")
|
||||
.select("goal_sub.goal_id")
|
||||
.select(sql<number>`count(*)::int`.as("subscriptions"))
|
||||
.groupBy("goal_sub.goal_id");
|
||||
|
||||
const uidFn = whereUserIdOnGoalSubForAggregate(b.userID);
|
||||
const uidFn = whereUserIdOnGoalSubForAggregate(userID);
|
||||
|
||||
if (uidFn) {
|
||||
q = q.where(uidFn);
|
||||
}
|
||||
|
||||
if (b.game !== undefined && b.playtype !== undefined) {
|
||||
const v3 = LEGACY_GameGroupPTToGame(
|
||||
b.game as GameGroup,
|
||||
b.playtype as LEGACY_Playtype,
|
||||
) as Game;
|
||||
|
||||
q = q.where("goal.game", "=", v3);
|
||||
if (game !== undefined && playtype !== undefined) {
|
||||
q = q.where("goal.game", "=", LEGACY_GameGroupPTToGame(game, playtype) as Game);
|
||||
}
|
||||
|
||||
const ranked = await q.orderBy("subscriptions", "desc").limit(limit).execute();
|
||||
@@ -639,24 +560,17 @@ export async function GetMostSubscribedGoals(
|
||||
}
|
||||
|
||||
export async function GetMostSubscribedQuests(
|
||||
query: FilterQuery<QuestSubscriptionDocument>,
|
||||
{ game, playtype }: TargetSubFilter,
|
||||
limit = 100,
|
||||
): Promise<Array<{ __subscriptions: integer } & QuestDocument>> {
|
||||
const b = query as Record<string, unknown>;
|
||||
|
||||
let q = DB.selectFrom("quest")
|
||||
.innerJoin("quest_sub", "quest_sub.quest_id", "quest.id")
|
||||
.select("quest.id")
|
||||
.select(sql<number>`count(*)::int`.as("subscriptions"))
|
||||
.groupBy("quest.id");
|
||||
|
||||
if (b.game !== undefined && b.playtype !== undefined) {
|
||||
const v3 = LEGACY_GameGroupPTToGame(
|
||||
b.game as GameGroup,
|
||||
b.playtype as LEGACY_Playtype,
|
||||
) as Game;
|
||||
|
||||
q = q.where("quest.game", "=", v3);
|
||||
if (game !== undefined && playtype !== undefined) {
|
||||
q = q.where("quest.game", "=", LEGACY_GameGroupPTToGame(game, playtype) as Game);
|
||||
}
|
||||
|
||||
const ranked = await q.orderBy("subscriptions", "desc").limit(limit).execute();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"module": "Preserve",
|
||||
"moduleResolution": "bundler",
|
||||
"lib": ["ES2022"],
|
||||
"types": ["bun"],
|
||||
"paths": {
|
||||
"#*": ["./src/*"]
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@ import pg from "pg";
|
||||
|
||||
import { ensureTestCdnBucket } from "./src/test-utils/ensure-test-cdn-bucket";
|
||||
|
||||
// See note in vitest.setup.ts about POSTGRES_TEST_HOST + tachi-postgres-test.
|
||||
const POSTGRES_HOST = process.env.POSTGRES_TEST_HOST ?? "tachi-postgres";
|
||||
const POSTGRES_HOST = process.env.POSTGRES_TEST_HOST ?? "tachi-postgres-test";
|
||||
const POSTGRES_USER = "tachi";
|
||||
const POSTGRES_PASS = "tachi";
|
||||
const TEMPLATE_DB = "tachi_server_test_template";
|
||||
|
||||
@@ -28,7 +28,7 @@ const WORKER_DB_NAME = `tachi_server_test_${WORKER_ID}`;
|
||||
// `tachi-postgres-test` (tmpfs + fsync=off, see docker-compose-dev.yml) is the
|
||||
// preferred backend for tests. Fall back to the dev Postgres if the dedicated
|
||||
// service isn't running so a freshly-cloned repo still passes tests.
|
||||
const POSTGRES_HOST = process.env.POSTGRES_TEST_HOST ?? "tachi-postgres";
|
||||
const POSTGRES_HOST = process.env.POSTGRES_TEST_HOST ?? "tachi-postgres-test";
|
||||
const POSTGRES_USER = "tachi";
|
||||
const POSTGRES_PASS = "tachi";
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "tsgo -b",
|
||||
"calc-v1": "ts-node src/main.ts $@",
|
||||
"calc-v1": "bun run src/main.ts $@",
|
||||
"lint": "eslint .",
|
||||
"lint-fix": "eslint . --fix",
|
||||
"typecheck": "tsgo --noEmit",
|
||||
|
||||
Reference in New Issue
Block a user