@studious-lms/server 1.1.24 → 1.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (485) hide show
  1. package/.coderabbit.yaml +9 -0
  2. package/.env.example +53 -0
  3. package/.env.test.example +37 -0
  4. package/README.md +34 -7
  5. package/dist/exportType.d.ts.map +1 -1
  6. package/dist/exportType.js +4 -0
  7. package/dist/exportType.js.map +1 -0
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +212 -51
  11. package/dist/index.js.map +1 -0
  12. package/dist/instrument.d.ts +2 -0
  13. package/dist/instrument.d.ts.map +1 -0
  14. package/dist/instrument.js +18 -0
  15. package/dist/instrument.js.map +1 -0
  16. package/dist/lib/config/env.d.ts +190 -0
  17. package/dist/lib/config/env.d.ts.map +1 -0
  18. package/dist/lib/config/env.js +121 -0
  19. package/dist/lib/config/env.js.map +1 -0
  20. package/dist/lib/fileUpload.d.ts +2 -2
  21. package/dist/lib/fileUpload.d.ts.map +1 -1
  22. package/dist/lib/fileUpload.js +82 -15
  23. package/dist/lib/fileUpload.js.map +1 -0
  24. package/dist/lib/googleCloudStorage.d.ts +13 -0
  25. package/dist/lib/googleCloudStorage.d.ts.map +1 -1
  26. package/dist/lib/googleCloudStorage.js +45 -6
  27. package/dist/lib/googleCloudStorage.js.map +1 -0
  28. package/dist/lib/jsonConversion.d.ts.map +1 -1
  29. package/dist/lib/jsonConversion.js +16 -14
  30. package/dist/lib/jsonConversion.js.map +1 -0
  31. package/dist/lib/jsonStyles.d.ts.map +1 -1
  32. package/dist/lib/jsonStyles.js +4 -0
  33. package/dist/lib/jsonStyles.js.map +1 -0
  34. package/dist/lib/notificationHandler.d.ts +25 -0
  35. package/dist/lib/notificationHandler.d.ts.map +1 -0
  36. package/dist/lib/notificationHandler.js +32 -0
  37. package/dist/lib/notificationHandler.js.map +1 -0
  38. package/dist/lib/prisma.d.ts +2 -2
  39. package/dist/lib/prisma.d.ts.map +1 -1
  40. package/dist/lib/prisma.js +24 -1
  41. package/dist/lib/prisma.js.map +1 -0
  42. package/dist/lib/pusher.d.ts +4 -1
  43. package/dist/lib/pusher.d.ts.map +1 -1
  44. package/dist/lib/pusher.js +14 -6
  45. package/dist/lib/pusher.js.map +1 -0
  46. package/dist/lib/redis.d.ts +5 -0
  47. package/dist/lib/redis.d.ts.map +1 -0
  48. package/dist/lib/redis.js +53 -0
  49. package/dist/lib/redis.js.map +1 -0
  50. package/dist/lib/thumbnailGenerator.d.ts +0 -21
  51. package/dist/lib/thumbnailGenerator.d.ts.map +1 -1
  52. package/dist/lib/thumbnailGenerator.js +159 -158
  53. package/dist/lib/thumbnailGenerator.js.map +1 -0
  54. package/dist/middleware/auth.d.ts.map +1 -1
  55. package/dist/middleware/auth.js +41 -93
  56. package/dist/middleware/auth.js.map +1 -0
  57. package/dist/middleware/logging.d.ts.map +1 -1
  58. package/dist/middleware/logging.js +4 -0
  59. package/dist/middleware/logging.js.map +1 -0
  60. package/dist/middleware/security.d.ts +5 -0
  61. package/dist/middleware/security.d.ts.map +1 -0
  62. package/dist/middleware/security.js +77 -0
  63. package/dist/middleware/security.js.map +1 -0
  64. package/dist/models/agenda.d.ts +97 -0
  65. package/dist/models/agenda.d.ts.map +1 -0
  66. package/dist/models/agenda.js +40 -0
  67. package/dist/models/agenda.js.map +1 -0
  68. package/dist/models/announcement.d.ts +223 -0
  69. package/dist/models/announcement.d.ts.map +1 -0
  70. package/dist/models/announcement.js +120 -0
  71. package/dist/models/announcement.js.map +1 -0
  72. package/dist/models/assignment.d.ts +1292 -0
  73. package/dist/models/assignment.d.ts.map +1 -0
  74. package/dist/models/assignment.js +309 -0
  75. package/dist/models/assignment.js.map +1 -0
  76. package/dist/models/attendance.d.ts +180 -0
  77. package/dist/models/attendance.d.ts.map +1 -0
  78. package/dist/models/attendance.js +188 -0
  79. package/dist/models/attendance.js.map +1 -0
  80. package/dist/models/auth.d.ts +153 -0
  81. package/dist/models/auth.d.ts.map +1 -0
  82. package/dist/models/auth.js +217 -0
  83. package/dist/models/auth.js.map +1 -0
  84. package/dist/models/class.d.ts +439 -0
  85. package/dist/models/class.d.ts.map +1 -0
  86. package/dist/models/class.js +546 -0
  87. package/dist/models/class.js.map +1 -0
  88. package/dist/models/comment.d.ts +171 -0
  89. package/dist/models/comment.d.ts.map +1 -0
  90. package/dist/models/comment.js +138 -0
  91. package/dist/models/comment.js.map +1 -0
  92. package/dist/models/conversation.d.ts +164 -0
  93. package/dist/models/conversation.d.ts.map +1 -0
  94. package/dist/models/conversation.js +175 -0
  95. package/dist/models/conversation.js.map +1 -0
  96. package/dist/models/event.d.ts +295 -0
  97. package/dist/models/event.d.ts.map +1 -0
  98. package/dist/models/event.js +145 -0
  99. package/dist/models/event.js.map +1 -0
  100. package/dist/models/file.d.ts +536 -0
  101. package/dist/models/file.d.ts.map +1 -0
  102. package/dist/models/file.js +126 -0
  103. package/dist/models/file.js.map +1 -0
  104. package/dist/models/folder.d.ts +295 -0
  105. package/dist/models/folder.d.ts.map +1 -0
  106. package/dist/models/folder.js +202 -0
  107. package/dist/models/folder.js.map +1 -0
  108. package/dist/models/labChat.d.ts +243 -0
  109. package/dist/models/labChat.d.ts.map +1 -0
  110. package/dist/models/labChat.js +204 -0
  111. package/dist/models/labChat.js.map +1 -0
  112. package/dist/models/marketing.d.ts +72 -0
  113. package/dist/models/marketing.d.ts.map +1 -0
  114. package/dist/models/marketing.js +26 -0
  115. package/dist/models/marketing.js.map +1 -0
  116. package/dist/models/message.d.ts +100 -0
  117. package/dist/models/message.d.ts.map +1 -0
  118. package/dist/models/message.js +131 -0
  119. package/dist/models/message.js.map +1 -0
  120. package/dist/models/newtonChat.d.ts +72 -0
  121. package/dist/models/newtonChat.d.ts.map +1 -0
  122. package/dist/models/newtonChat.js +61 -0
  123. package/dist/models/newtonChat.js.map +1 -0
  124. package/dist/models/notification.d.ts +65 -0
  125. package/dist/models/notification.d.ts.map +1 -0
  126. package/dist/models/notification.js +46 -0
  127. package/dist/models/notification.js.map +1 -0
  128. package/dist/models/section.d.ts +102 -0
  129. package/dist/models/section.d.ts.map +1 -0
  130. package/dist/models/section.js +83 -0
  131. package/dist/models/section.js.map +1 -0
  132. package/dist/models/user.d.ts +39 -0
  133. package/dist/models/user.d.ts.map +1 -0
  134. package/dist/models/user.js +38 -0
  135. package/dist/models/user.js.map +1 -0
  136. package/dist/models/worksheet.d.ts +460 -0
  137. package/dist/models/worksheet.d.ts.map +1 -0
  138. package/dist/models/worksheet.js +200 -0
  139. package/dist/models/worksheet.js.map +1 -0
  140. package/dist/pipelines/aiLabChat.d.ts +21 -0
  141. package/dist/pipelines/aiLabChat.d.ts.map +1 -0
  142. package/dist/pipelines/aiLabChat.js +460 -0
  143. package/dist/pipelines/aiLabChat.js.map +1 -0
  144. package/dist/pipelines/aiNewtonChat.d.ts +30 -0
  145. package/dist/pipelines/aiNewtonChat.d.ts.map +1 -0
  146. package/dist/pipelines/aiNewtonChat.js +289 -0
  147. package/dist/pipelines/aiNewtonChat.js.map +1 -0
  148. package/dist/pipelines/gradeWorksheet.d.ts +30 -0
  149. package/dist/pipelines/gradeWorksheet.d.ts.map +1 -0
  150. package/dist/pipelines/gradeWorksheet.js +252 -0
  151. package/dist/pipelines/gradeWorksheet.js.map +1 -0
  152. package/dist/routers/_app.d.ts +6403 -3741
  153. package/dist/routers/_app.d.ts.map +1 -1
  154. package/dist/routers/_app.js +10 -0
  155. package/dist/routers/_app.js.map +1 -0
  156. package/dist/routers/agenda.d.ts +58 -6
  157. package/dist/routers/agenda.d.ts.map +1 -1
  158. package/dist/routers/agenda.js +6 -58
  159. package/dist/routers/agenda.js.map +1 -0
  160. package/dist/routers/announcement.d.ts +325 -6
  161. package/dist/routers/announcement.d.ts.map +1 -1
  162. package/dist/routers/announcement.js +547 -57
  163. package/dist/routers/announcement.js.map +1 -0
  164. package/dist/routers/assignment.d.ts +431 -318
  165. package/dist/routers/assignment.d.ts.map +1 -1
  166. package/dist/routers/assignment.js +104 -1559
  167. package/dist/routers/assignment.js.map +1 -0
  168. package/dist/routers/attendance.d.ts +20 -9
  169. package/dist/routers/attendance.d.ts.map +1 -1
  170. package/dist/routers/attendance.js +10 -263
  171. package/dist/routers/attendance.js.map +1 -0
  172. package/dist/routers/auth.d.ts +21 -1
  173. package/dist/routers/auth.d.ts.map +1 -1
  174. package/dist/routers/auth.js +37 -241
  175. package/dist/routers/auth.js.map +1 -0
  176. package/dist/routers/class.d.ts +198 -68
  177. package/dist/routers/class.d.ts.map +1 -1
  178. package/dist/routers/class.js +88 -909
  179. package/dist/routers/class.js.map +1 -0
  180. package/dist/routers/comment.d.ts +153 -0
  181. package/dist/routers/comment.d.ts.map +1 -0
  182. package/dist/routers/comment.js +58 -0
  183. package/dist/routers/comment.js.map +1 -0
  184. package/dist/routers/conversation.d.ts +73 -3
  185. package/dist/routers/conversation.d.ts.map +1 -1
  186. package/dist/routers/conversation.js +23 -265
  187. package/dist/routers/conversation.js.map +1 -0
  188. package/dist/routers/event.d.ts +46 -37
  189. package/dist/routers/event.d.ts.map +1 -1
  190. package/dist/routers/event.js +15 -431
  191. package/dist/routers/event.js.map +1 -0
  192. package/dist/routers/file.d.ts +4 -2
  193. package/dist/routers/file.d.ts.map +1 -1
  194. package/dist/routers/file.js +11 -295
  195. package/dist/routers/file.js.map +1 -0
  196. package/dist/routers/folder.d.ts +21 -14
  197. package/dist/routers/folder.d.ts.map +1 -1
  198. package/dist/routers/folder.js +36 -743
  199. package/dist/routers/folder.js.map +1 -0
  200. package/dist/routers/labChat.d.ts +12 -9
  201. package/dist/routers/labChat.d.ts.map +1 -1
  202. package/dist/routers/labChat.js +21 -877
  203. package/dist/routers/labChat.js.map +1 -0
  204. package/dist/routers/marketing.d.ts +2 -2
  205. package/dist/routers/marketing.d.ts.map +1 -1
  206. package/dist/routers/marketing.js +9 -54
  207. package/dist/routers/marketing.js.map +1 -0
  208. package/dist/routers/message.d.ts +2 -1
  209. package/dist/routers/message.d.ts.map +1 -1
  210. package/dist/routers/message.js +29 -519
  211. package/dist/routers/message.js.map +1 -0
  212. package/dist/routers/newtonChat.d.ts +55 -0
  213. package/dist/routers/newtonChat.d.ts.map +1 -0
  214. package/dist/routers/newtonChat.js +22 -0
  215. package/dist/routers/newtonChat.js.map +1 -0
  216. package/dist/routers/notifications.d.ts +8 -8
  217. package/dist/routers/notifications.d.ts.map +1 -1
  218. package/dist/routers/notifications.js +20 -81
  219. package/dist/routers/notifications.js.map +1 -0
  220. package/dist/routers/section.d.ts +37 -6
  221. package/dist/routers/section.d.ts.map +1 -1
  222. package/dist/routers/section.js +26 -167
  223. package/dist/routers/section.js.map +1 -0
  224. package/dist/routers/user.d.ts +1 -1
  225. package/dist/routers/user.d.ts.map +1 -1
  226. package/dist/routers/user.js +34 -204
  227. package/dist/routers/user.js.map +1 -0
  228. package/dist/routers/worksheet.d.ts +362 -0
  229. package/dist/routers/worksheet.d.ts.map +1 -0
  230. package/dist/routers/worksheet.js +153 -0
  231. package/dist/routers/worksheet.js.map +1 -0
  232. package/dist/seedDatabase.d.ts +2 -3
  233. package/dist/seedDatabase.d.ts.map +1 -1
  234. package/dist/seedDatabase.js +311 -289
  235. package/dist/seedDatabase.js.map +1 -0
  236. package/dist/server/pipelines/aiLabChat.d.ts +21 -0
  237. package/dist/server/pipelines/aiLabChat.d.ts.map +1 -0
  238. package/dist/server/pipelines/aiLabChat.js +456 -0
  239. package/dist/server/pipelines/aiLabChat.js.map +1 -0
  240. package/dist/server/pipelines/aiNewtonChat.d.ts +30 -0
  241. package/dist/server/pipelines/aiNewtonChat.d.ts.map +1 -0
  242. package/dist/server/pipelines/aiNewtonChat.js +285 -0
  243. package/dist/server/pipelines/aiNewtonChat.js.map +1 -0
  244. package/dist/server/pipelines/gradeWorksheet.d.ts +30 -0
  245. package/dist/server/pipelines/gradeWorksheet.d.ts.map +1 -0
  246. package/dist/server/pipelines/gradeWorksheet.js +248 -0
  247. package/dist/server/pipelines/gradeWorksheet.js.map +1 -0
  248. package/dist/services/agenda.d.ts +100 -0
  249. package/dist/services/agenda.d.ts.map +1 -0
  250. package/dist/services/agenda.js +21 -0
  251. package/dist/services/agenda.js.map +1 -0
  252. package/dist/services/announcement.d.ts +135 -0
  253. package/dist/services/announcement.d.ts.map +1 -0
  254. package/dist/services/announcement.js +223 -0
  255. package/dist/services/announcement.js.map +1 -0
  256. package/dist/services/assignment.d.ts +1462 -0
  257. package/dist/services/assignment.d.ts.map +1 -0
  258. package/dist/services/assignment.js +898 -0
  259. package/dist/services/assignment.js.map +1 -0
  260. package/dist/services/attendance.d.ts +93 -0
  261. package/dist/services/attendance.d.ts.map +1 -0
  262. package/dist/services/attendance.js +61 -0
  263. package/dist/services/attendance.js.map +1 -0
  264. package/dist/services/auth.d.ts +68 -0
  265. package/dist/services/auth.d.ts.map +1 -0
  266. package/dist/services/auth.js +218 -0
  267. package/dist/services/auth.js.map +1 -0
  268. package/dist/services/class.d.ts +621 -0
  269. package/dist/services/class.d.ts.map +1 -0
  270. package/dist/services/class.js +474 -0
  271. package/dist/services/class.js.map +1 -0
  272. package/dist/services/comment.d.ts +100 -0
  273. package/dist/services/comment.d.ts.map +1 -0
  274. package/dist/services/comment.js +83 -0
  275. package/dist/services/comment.js.map +1 -0
  276. package/dist/services/conversation.d.ts +159 -0
  277. package/dist/services/conversation.d.ts.map +1 -0
  278. package/dist/services/conversation.js +138 -0
  279. package/dist/services/conversation.js.map +1 -0
  280. package/dist/services/event.d.ts +216 -0
  281. package/dist/services/event.d.ts.map +1 -0
  282. package/dist/services/event.js +168 -0
  283. package/dist/services/event.js.map +1 -0
  284. package/dist/services/file.d.ts +74 -0
  285. package/dist/services/file.d.ts.map +1 -0
  286. package/dist/services/file.js +133 -0
  287. package/dist/services/file.js.map +1 -0
  288. package/dist/services/folder.d.ts +239 -0
  289. package/dist/services/folder.d.ts.map +1 -0
  290. package/dist/services/folder.js +248 -0
  291. package/dist/services/folder.js.map +1 -0
  292. package/dist/services/labChat.d.ts +165 -0
  293. package/dist/services/labChat.d.ts.map +1 -0
  294. package/dist/services/labChat.js +289 -0
  295. package/dist/services/labChat.js.map +1 -0
  296. package/dist/services/marketing.d.ts +50 -0
  297. package/dist/services/marketing.d.ts.map +1 -0
  298. package/dist/services/marketing.js +32 -0
  299. package/dist/services/marketing.js.map +1 -0
  300. package/dist/services/message.d.ts +95 -0
  301. package/dist/services/message.d.ts.map +1 -0
  302. package/dist/services/message.js +350 -0
  303. package/dist/services/message.js.map +1 -0
  304. package/dist/services/newtonChat.d.ts +22 -0
  305. package/dist/services/newtonChat.d.ts.map +1 -0
  306. package/dist/services/newtonChat.js +174 -0
  307. package/dist/services/newtonChat.js.map +1 -0
  308. package/dist/services/notification.d.ts +65 -0
  309. package/dist/services/notification.d.ts.map +1 -0
  310. package/dist/services/notification.js +33 -0
  311. package/dist/services/notification.js.map +1 -0
  312. package/dist/services/section.d.ts +53 -0
  313. package/dist/services/section.d.ts.map +1 -0
  314. package/dist/services/section.js +199 -0
  315. package/dist/services/section.js.map +1 -0
  316. package/dist/services/user.d.ts +48 -0
  317. package/dist/services/user.d.ts.map +1 -0
  318. package/dist/services/user.js +141 -0
  319. package/dist/services/user.js.map +1 -0
  320. package/dist/services/worksheet.d.ts +239 -0
  321. package/dist/services/worksheet.d.ts.map +1 -0
  322. package/dist/services/worksheet.js +235 -0
  323. package/dist/services/worksheet.js.map +1 -0
  324. package/dist/socket/handlers.d.ts.map +1 -1
  325. package/dist/socket/handlers.js +4 -0
  326. package/dist/socket/handlers.js.map +1 -0
  327. package/dist/trpc.d.ts.map +1 -1
  328. package/dist/trpc.js +4 -0
  329. package/dist/trpc.js.map +1 -0
  330. package/dist/types/trpc.d.ts.map +1 -1
  331. package/dist/types/trpc.js +4 -0
  332. package/dist/types/trpc.js.map +1 -0
  333. package/dist/utils/aiUser.d.ts +1 -3
  334. package/dist/utils/aiUser.d.ts.map +1 -1
  335. package/dist/utils/aiUser.js +8 -3
  336. package/dist/utils/aiUser.js.map +1 -0
  337. package/dist/utils/email.d.ts +12 -1
  338. package/dist/utils/email.d.ts.map +1 -1
  339. package/dist/utils/email.js +26 -4
  340. package/dist/utils/email.js.map +1 -0
  341. package/dist/utils/generateInviteCode.d.ts +1 -2
  342. package/dist/utils/generateInviteCode.d.ts.map +1 -1
  343. package/dist/utils/generateInviteCode.js +5 -2
  344. package/dist/utils/generateInviteCode.js.map +1 -0
  345. package/dist/utils/inference.d.ts +8 -0
  346. package/dist/utils/inference.d.ts.map +1 -1
  347. package/dist/utils/inference.js +78 -10
  348. package/dist/utils/inference.js.map +1 -0
  349. package/dist/utils/logger.d.ts +4 -0
  350. package/dist/utils/logger.d.ts.map +1 -1
  351. package/dist/utils/logger.js +35 -3
  352. package/dist/utils/logger.js.map +1 -0
  353. package/dist/utils/prismaErrorHandler.d.ts.map +1 -1
  354. package/dist/utils/prismaErrorHandler.js +7 -0
  355. package/dist/utils/prismaErrorHandler.js.map +1 -0
  356. package/dist/utils/prismaWrapper.d.ts +1 -0
  357. package/dist/utils/prismaWrapper.d.ts.map +1 -1
  358. package/dist/utils/prismaWrapper.js +8 -0
  359. package/dist/utils/prismaWrapper.js.map +1 -0
  360. package/docker-compose.yml +19 -0
  361. package/package.json +21 -4
  362. package/prisma/migrations/20251109122857_annuoncements_comments/migration.sql +30 -0
  363. package/prisma/migrations/20251109135555_reactions_announcements_comments/migration.sql +35 -0
  364. package/prisma/schema.prisma +180 -12
  365. package/scripts/test-pre-push.ts +14 -0
  366. package/src/index.ts +247 -52
  367. package/src/instrument.ts +15 -0
  368. package/src/lib/config/env.ts +132 -0
  369. package/src/lib/fileUpload.ts +81 -16
  370. package/src/lib/googleCloudStorage.ts +42 -6
  371. package/src/lib/jsonConversion.ts +12 -14
  372. package/src/lib/prisma.ts +23 -2
  373. package/src/lib/pusher.ts +11 -6
  374. package/src/lib/redis.ts +56 -0
  375. package/src/lib/thumbnailGenerator.ts +170 -168
  376. package/src/middleware/auth.ts +86 -137
  377. package/src/middleware/security.ts +80 -0
  378. package/src/models/agenda.ts +46 -0
  379. package/src/models/announcement.ts +134 -0
  380. package/src/models/assignment.ts +322 -0
  381. package/src/models/attendance.ts +208 -0
  382. package/src/models/auth.ts +247 -0
  383. package/src/models/class.ts +598 -0
  384. package/src/models/comment.ts +152 -0
  385. package/src/models/conversation.ts +200 -0
  386. package/src/models/event.ts +177 -0
  387. package/src/models/file.ts +129 -0
  388. package/src/models/folder.ts +225 -0
  389. package/src/models/labChat.ts +213 -0
  390. package/src/models/marketing.ts +45 -0
  391. package/src/models/message.ts +153 -0
  392. package/src/models/newtonChat.ts +70 -0
  393. package/src/models/notification.ts +54 -0
  394. package/src/models/section.ts +98 -0
  395. package/src/models/user.ts +47 -0
  396. package/src/models/worksheet.ts +294 -0
  397. package/src/pipelines/aiLabChat.ts +511 -0
  398. package/src/pipelines/aiNewtonChat.ts +347 -0
  399. package/src/pipelines/gradeWorksheet.ts +286 -0
  400. package/src/routers/_app.ts +6 -0
  401. package/src/routers/agenda.ts +3 -61
  402. package/src/routers/announcement.ts +622 -57
  403. package/src/routers/assignment.ts +157 -1688
  404. package/src/routers/attendance.ts +16 -277
  405. package/src/routers/auth.ts +79 -313
  406. package/src/routers/class.ts +265 -1038
  407. package/src/routers/comment.ts +76 -0
  408. package/src/routers/conversation.ts +53 -284
  409. package/src/routers/event.ts +50 -481
  410. package/src/routers/file.ts +45 -341
  411. package/src/routers/folder.ts +107 -836
  412. package/src/routers/labChat.ts +29 -960
  413. package/src/routers/marketing.ts +35 -77
  414. package/src/routers/message.ts +45 -571
  415. package/src/routers/newtonChat.ts +36 -0
  416. package/src/routers/notifications.ts +32 -82
  417. package/src/routers/section.ts +58 -200
  418. package/src/routers/user.ts +49 -226
  419. package/src/routers/worksheet.ts +252 -0
  420. package/src/seedDatabase.ts +330 -290
  421. package/src/services/agenda.ts +21 -0
  422. package/src/services/announcement.ts +290 -0
  423. package/src/services/assignment.ts +1198 -0
  424. package/src/services/attendance.ts +85 -0
  425. package/src/services/auth.ts +277 -0
  426. package/src/services/class.ts +622 -0
  427. package/src/services/comment.ts +106 -0
  428. package/src/services/conversation.ts +213 -0
  429. package/src/services/event.ts +231 -0
  430. package/src/services/file.ts +167 -0
  431. package/src/services/folder.ts +316 -0
  432. package/src/services/labChat.ts +352 -0
  433. package/src/services/marketing.ts +57 -0
  434. package/src/services/message.ts +461 -0
  435. package/src/services/newtonChat.ts +222 -0
  436. package/src/services/notification.ts +50 -0
  437. package/src/services/section.ts +283 -0
  438. package/src/services/user.ts +172 -0
  439. package/src/services/worksheet.ts +358 -0
  440. package/src/trpc.ts +4 -0
  441. package/src/utils/aiUser.ts +4 -3
  442. package/src/utils/email.ts +33 -4
  443. package/src/utils/generateInviteCode.ts +1 -3
  444. package/src/utils/inference.ts +89 -10
  445. package/src/utils/logger.ts +33 -3
  446. package/src/utils/prismaErrorHandler.ts +3 -0
  447. package/src/utils/prismaWrapper.ts +4 -0
  448. package/tests/globalSetup.ts +62 -0
  449. package/tests/helpers.ts +22 -0
  450. package/tests/middleware/security.test.ts +42 -0
  451. package/tests/routers/agenda.test.ts +138 -0
  452. package/tests/routers/announcement.test.ts +490 -0
  453. package/tests/routers/assignment.test.ts +837 -0
  454. package/tests/routers/attendance.test.ts +160 -0
  455. package/tests/routers/auth.test.ts +171 -0
  456. package/tests/{class.test.ts → routers/class.test.ts} +163 -92
  457. package/tests/routers/comment.test.ts +126 -0
  458. package/tests/routers/conversation.test.ts +145 -0
  459. package/tests/routers/event.test.ts +289 -0
  460. package/tests/routers/folder.test.ts +178 -0
  461. package/tests/routers/labChat.test.ts +115 -0
  462. package/tests/routers/marketing.test.ts +59 -0
  463. package/tests/routers/message.test.ts +123 -0
  464. package/tests/routers/notification.test.ts +69 -0
  465. package/tests/routers/section.test.ts +208 -0
  466. package/tests/server/rateLimit.test.ts +73 -0
  467. package/tests/setup.ts +39 -65
  468. package/tests/user.test.ts +136 -0
  469. package/tests/utils/aiUser.test.ts +22 -0
  470. package/tests/utils/generateInviteCode.test.ts +24 -0
  471. package/tests/utils/logger.test.ts +74 -0
  472. package/tests/utils/prismaErrorHandler.test.ts +101 -0
  473. package/tests/utils/prismaWrapper.test.ts +82 -0
  474. package/tests/worksheet.test.ts +181 -0
  475. package/tsconfig.json +9 -2
  476. package/vitest.config.ts +30 -1
  477. package/vitest.unit.config.ts +21 -0
  478. package/API_SPECIFICATION.md +0 -1597
  479. package/BASE64_REMOVAL_SUMMARY.md +0 -164
  480. package/CHAT_API_SPEC.md +0 -579
  481. package/LAB_CHAT_API_SPEC.md +0 -518
  482. package/dist/routers/school.d.ts +0 -208
  483. package/dist/routers/school.d.ts.map +0 -1
  484. package/dist/routers/school.js +0 -481
  485. package/tests/auth.test.ts +0 -25
@@ -1,316 +1,32 @@
1
+
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="f44142e2-ff83-5313-838b-11e431be8a3e")}catch(e){}}();
1
3
  import { z } from "zod";
2
- import { createTRPCRouter, protectedProcedure, protectedTeacherProcedure } from "../trpc.js";
3
- import { TRPCError } from "@trpc/server";
4
- import { getSignedUrl, deleteFile } from "../lib/googleCloudStorage.js";
5
- import { prisma } from "../lib/prisma.js";
6
- import { logger } from "../utils/logger.js";
4
+ import { createTRPCRouter, protectedProcedure, protectedTeacherProcedure, } from "../trpc.js";
5
+ import { getFileSignedUrl, moveFile, renameFile, deleteFileRecord, } from "../services/file.js";
7
6
  export const fileRouter = createTRPCRouter({
8
7
  getSignedUrl: protectedProcedure
9
- .input(z.object({
10
- fileId: z.string(),
11
- }))
12
- .mutation(async ({ ctx, input }) => {
13
- const { fileId } = input;
14
- const userId = ctx.user?.id;
15
- if (!userId) {
16
- throw new TRPCError({
17
- code: "UNAUTHORIZED",
18
- message: "You must be logged in to access files",
19
- });
20
- }
21
- // Get file metadata from database
22
- const file = await prisma.file.findUnique({
23
- where: { id: fileId },
24
- include: {
25
- assignment: {
26
- include: {
27
- class: {
28
- include: {
29
- students: true,
30
- teachers: true
31
- }
32
- }
33
- }
34
- },
35
- submission: {
36
- include: {
37
- student: true,
38
- assignment: {
39
- include: {
40
- class: {
41
- include: {
42
- teachers: true
43
- }
44
- }
45
- }
46
- }
47
- }
48
- },
49
- annotations: {
50
- include: {
51
- student: true,
52
- assignment: {
53
- include: {
54
- class: {
55
- include: {
56
- teachers: true,
57
- }
58
- }
59
- }
60
- }
61
- }
62
- },
63
- folder: {
64
- include: {
65
- class: {
66
- include: {
67
- students: true,
68
- teachers: true
69
- }
70
- }
71
- }
72
- }
73
- }
74
- });
75
- if (!file) {
76
- throw new TRPCError({
77
- code: "NOT_FOUND",
78
- message: "File does not exist",
79
- });
80
- }
81
- // Check if user has access to this file
82
- let hasAccess = false;
83
- let classId = null;
84
- // Check if user is a teacher of the class
85
- if (file.assignment?.class) {
86
- classId = file.assignment.class.id;
87
- hasAccess = file.assignment.class.teachers.some(teacher => teacher.id === userId) || false;
88
- }
89
- if (file.submission?.assignment?.classId) {
90
- classId = file.submission.assignment.classId;
91
- hasAccess = file.submission?.studentId === userId || false;
92
- if (!hasAccess)
93
- hasAccess = file.submission.assignment.class.teachers.some(teacher => teacher.id === userId) || false;
94
- }
95
- if (file.annotations?.assignment?.classId) {
96
- classId = file.annotations?.assignment.classId;
97
- hasAccess = file.annotations?.studentId === userId || false;
98
- if (!hasAccess)
99
- hasAccess = file.annotations.assignment.class.teachers.some(teacher => teacher.id === userId) || false;
100
- }
101
- // Check if user is the file owner
102
- if (file.userId === userId) {
103
- hasAccess = true;
104
- }
105
- // Check if file is in a folder and user has access to the class
106
- if (file.folder?.class) {
107
- hasAccess = hasAccess || file.folder.class.teachers.some(teacher => teacher.id === userId);
108
- hasAccess = hasAccess || file.folder.class.students.some(student => student.id === userId);
109
- }
110
- if (!hasAccess) {
111
- throw new TRPCError({
112
- code: "FORBIDDEN",
113
- message: "You do not have access to this file",
114
- });
115
- }
116
- try {
117
- const signedUrl = await getSignedUrl(file.path);
118
- return { url: signedUrl };
119
- }
120
- catch (error) {
121
- logger.error('Error generating signed URL:', error);
122
- throw new TRPCError({
123
- code: "INTERNAL_SERVER_ERROR",
124
- message: "Failed to generate download URL",
125
- });
126
- }
127
- }),
8
+ .input(z.object({ fileId: z.string() }))
9
+ .mutation(({ ctx, input }) => getFileSignedUrl(ctx.user.id, input.fileId)),
128
10
  move: protectedTeacherProcedure
129
11
  .input(z.object({
130
12
  fileId: z.string(),
131
13
  targetFolderId: z.string(),
132
14
  classId: z.string(),
133
15
  }))
134
- .mutation(async ({ ctx, input }) => {
135
- const { fileId, targetFolderId } = input;
136
- // Get the file
137
- const file = await prisma.file.findUnique({
138
- where: { id: fileId },
139
- include: {
140
- folder: {
141
- include: {
142
- class: true,
143
- },
144
- },
145
- },
146
- });
147
- if (!file) {
148
- throw new TRPCError({
149
- code: "NOT_FOUND",
150
- message: "File not found",
151
- });
152
- }
153
- // Get the target folder
154
- const targetFolder = await prisma.folder.findUnique({
155
- where: { id: targetFolderId },
156
- include: {
157
- class: true,
158
- },
159
- });
160
- if (!targetFolder) {
161
- throw new TRPCError({
162
- code: "NOT_FOUND",
163
- message: "Target folder not found",
164
- });
165
- }
166
- // Move the file
167
- const updatedFile = await prisma.file.update({
168
- where: { id: fileId },
169
- data: {
170
- folderId: targetFolderId,
171
- },
172
- include: {
173
- user: {
174
- select: {
175
- id: true,
176
- username: true,
177
- },
178
- },
179
- },
180
- });
181
- return updatedFile;
182
- }),
16
+ .mutation(({ input }) => moveFile(input.fileId, input.targetFolderId)),
183
17
  rename: protectedTeacherProcedure
184
18
  .input(z.object({
185
19
  fileId: z.string(),
186
20
  newName: z.string(),
187
21
  classId: z.string(),
188
22
  }))
189
- .mutation(async ({ ctx, input }) => {
190
- const { fileId, newName, classId } = input;
191
- // Verify user is a teacher of the class
192
- const classData = await prisma.class.findFirst({
193
- where: {
194
- id: classId,
195
- teachers: {
196
- some: {
197
- id: ctx.user.id,
198
- },
199
- },
200
- },
201
- });
202
- if (!classData) {
203
- throw new TRPCError({
204
- code: "FORBIDDEN",
205
- message: "You must be a teacher of this class to rename files",
206
- });
207
- }
208
- // Get the file
209
- const file = await prisma.file.findUnique({
210
- where: { id: fileId },
211
- include: {
212
- folder: {
213
- include: {
214
- class: true,
215
- },
216
- },
217
- },
218
- });
219
- if (!file) {
220
- throw new TRPCError({
221
- code: "NOT_FOUND",
222
- message: "File not found",
223
- });
224
- }
225
- // Validate new name
226
- if (!newName.trim()) {
227
- throw new TRPCError({
228
- code: "BAD_REQUEST",
229
- message: "File name cannot be empty",
230
- });
231
- }
232
- // Rename the file
233
- const updatedFile = await prisma.file.update({
234
- where: { id: fileId },
235
- data: {
236
- name: newName.trim(),
237
- },
238
- include: {
239
- user: {
240
- select: {
241
- id: true,
242
- username: true,
243
- },
244
- },
245
- },
246
- });
247
- return updatedFile;
248
- }),
23
+ .mutation(({ input }) => renameFile(input.fileId, input.newName)),
249
24
  delete: protectedTeacherProcedure
250
25
  .input(z.object({
251
26
  fileId: z.string(),
252
27
  classId: z.string(),
253
28
  }))
254
- .mutation(async ({ ctx, input }) => {
255
- const { fileId, classId } = input;
256
- // Verify user is a teacher of the class
257
- const classData = await prisma.class.findFirst({
258
- where: {
259
- id: classId,
260
- teachers: {
261
- some: {
262
- id: ctx.user.id,
263
- },
264
- },
265
- },
266
- });
267
- if (!classData) {
268
- throw new TRPCError({
269
- code: "FORBIDDEN",
270
- message: "You must be a teacher of this class to delete files",
271
- });
272
- }
273
- // Get the file
274
- const file = await prisma.file.findUnique({
275
- where: { id: fileId },
276
- include: {
277
- folder: {
278
- include: {
279
- class: true,
280
- },
281
- },
282
- thumbnail: true,
283
- },
284
- });
285
- if (!file) {
286
- throw new TRPCError({
287
- code: "NOT_FOUND",
288
- message: "File not found",
289
- });
290
- }
291
- // Verify the file belongs to this class
292
- if (file.folder?.classId !== classId) {
293
- throw new TRPCError({
294
- code: "FORBIDDEN",
295
- message: "File does not belong to this class",
296
- });
297
- }
298
- // Delete files from storage
299
- try {
300
- // Delete the main file
301
- await deleteFile(file.path);
302
- // Delete thumbnail if it exists
303
- if (file.thumbnail) {
304
- await deleteFile(file.thumbnail.path);
305
- }
306
- }
307
- catch (error) {
308
- logger.warn(`Failed to delete file ${file.path}:`, error);
309
- }
310
- // Delete the file record from database
311
- await prisma.file.delete({
312
- where: { id: fileId },
313
- });
314
- return { success: true };
315
- }),
29
+ .mutation(({ input }) => deleteFileRecord(input.fileId, input.classId)),
316
30
  });
31
+ //# sourceMappingURL=file.js.map
32
+ //# debugId=f44142e2-ff83-5313-838b-11e431be8a3e
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file.js","sources":["routers/file.ts"],"sourceRoot":"/","sourcesContent":["import { z } from \"zod\";\nimport {\n createTRPCRouter,\n protectedProcedure,\n protectedTeacherProcedure,\n} from \"../trpc.js\";\nimport {\n getFileSignedUrl,\n moveFile,\n renameFile,\n deleteFileRecord,\n} from \"../services/file.js\";\n\nexport const fileRouter = createTRPCRouter({\n getSignedUrl: protectedProcedure\n .input(z.object({ fileId: z.string() }))\n .mutation(({ ctx, input }) =>\n getFileSignedUrl(ctx.user!.id, input.fileId)\n ),\n\n move: protectedTeacherProcedure\n .input(\n z.object({\n fileId: z.string(),\n targetFolderId: z.string(),\n classId: z.string(),\n })\n )\n .mutation(({ input }) =>\n moveFile(input.fileId, input.targetFolderId)\n ),\n\n rename: protectedTeacherProcedure\n .input(\n z.object({\n fileId: z.string(),\n newName: z.string(),\n classId: z.string(),\n })\n )\n .mutation(({ input }) =>\n renameFile(input.fileId, input.newName)\n ),\n\n delete: protectedTeacherProcedure\n .input(\n z.object({\n fileId: z.string(),\n classId: z.string(),\n })\n )\n .mutation(({ input }) =>\n deleteFileRecord(input.fileId, input.classId)\n ),\n});\n"],"names":[],"mappings":";;AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,gBAAgB,EAChB,QAAQ,EACR,UAAU,EACV,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,CAAC,MAAM,UAAU,GAAG,gBAAgB,CAAC;IACzC,YAAY,EAAE,kBAAkB;SAC7B,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;SACvC,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,CAC3B,gBAAgB,CAAC,GAAG,CAAC,IAAK,CAAC,EAAE,EAAE,KAAK,CAAC,MAAM,CAAC,CAC7C;IAEH,IAAI,EAAE,yBAAyB;SAC5B,KAAK,CACJ,CAAC,CAAC,MAAM,CAAC;QACP,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;QAClB,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE;QAC1B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;KACpB,CAAC,CACH;SACA,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CACtB,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,cAAc,CAAC,CAC7C;IAEH,MAAM,EAAE,yBAAyB;SAC9B,KAAK,CACJ,CAAC,CAAC,MAAM,CAAC;QACP,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;QAClB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;QACnB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;KACpB,CAAC,CACH;SACA,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CACtB,UAAU,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CACxC;IAEH,MAAM,EAAE,yBAAyB;SAC9B,KAAK,CACJ,CAAC,CAAC,MAAM,CAAC;QACP,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;QAClB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;KACpB,CAAC,CACH;SACA,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CACtB,gBAAgB,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAC9C;CACJ,CAAC,CAAC","debug_id":"f44142e2-ff83-5313-838b-11e431be8a3e"}
@@ -1,5 +1,4 @@
1
1
  import { z } from "zod";
2
- import { type DirectUploadFile } from "../lib/fileUpload.js";
3
2
  export declare const folderRouter: import("@trpc/server").TRPCBuiltRouter<{
4
3
  ctx: import("../trpc.js").Context;
5
4
  meta: object;
@@ -44,12 +43,13 @@ export declare const folderRouter: import("@trpc/server").TRPCBuiltRouter<{
44
43
  childFolders: number;
45
44
  };
46
45
  name: string;
46
+ color: string | null;
47
47
  }[];
48
48
  } & {
49
49
  id: string;
50
+ classId: string | null;
50
51
  name: string;
51
52
  color: string | null;
52
- classId: string | null;
53
53
  parentFolderId: string | null;
54
54
  };
55
55
  meta: object;
@@ -87,9 +87,9 @@ export declare const folderRouter: import("@trpc/server").TRPCBuiltRouter<{
87
87
  } | null;
88
88
  } & {
89
89
  id: string;
90
+ classId: string | null;
90
91
  name: string;
91
92
  color: string | null;
92
- classId: string | null;
93
93
  parentFolderId: string | null;
94
94
  };
95
95
  meta: object;
@@ -118,12 +118,13 @@ export declare const folderRouter: import("@trpc/server").TRPCBuiltRouter<{
118
118
  childFolders: number;
119
119
  };
120
120
  name: string;
121
+ color: string | null;
121
122
  }[];
122
123
  } & {
123
124
  id: string;
125
+ classId: string | null;
124
126
  name: string;
125
127
  color: string | null;
126
- classId: string | null;
127
128
  parentFolderId: string | null;
128
129
  })[];
129
130
  meta: object;
@@ -157,9 +158,9 @@ export declare const folderRouter: import("@trpc/server").TRPCBuiltRouter<{
157
158
  }[];
158
159
  } & {
159
160
  id: string;
161
+ classId: string | null;
160
162
  name: string;
161
163
  color: string | null;
162
- classId: string | null;
163
164
  parentFolderId: string | null;
164
165
  })[];
165
166
  meta: object;
@@ -169,7 +170,7 @@ export declare const folderRouter: import("@trpc/server").TRPCBuiltRouter<{
169
170
  [x: string]: unknown;
170
171
  classId: string;
171
172
  };
172
- output: ({
173
+ output: {
173
174
  files: {
174
175
  type: string;
175
176
  user: {
@@ -186,6 +187,10 @@ export declare const folderRouter: import("@trpc/server").TRPCBuiltRouter<{
186
187
  files: {
187
188
  id: string;
188
189
  }[];
190
+ _count: {
191
+ files: number;
192
+ childFolders: number;
193
+ };
189
194
  name: string;
190
195
  color: string | null;
191
196
  childFolders: {
@@ -194,11 +199,11 @@ export declare const folderRouter: import("@trpc/server").TRPCBuiltRouter<{
194
199
  }[];
195
200
  } & {
196
201
  id: string;
202
+ classId: string | null;
197
203
  name: string;
198
204
  color: string | null;
199
- classId: string | null;
200
205
  parentFolderId: string | null;
201
- }) | null;
206
+ };
202
207
  meta: object;
203
208
  }>;
204
209
  uploadFiles: import("@trpc/server").TRPCMutationProcedure<{
@@ -257,12 +262,13 @@ export declare const folderRouter: import("@trpc/server").TRPCBuiltRouter<{
257
262
  childFolders: number;
258
263
  };
259
264
  name: string;
265
+ color: string | null;
260
266
  }[];
261
267
  } & {
262
268
  id: string;
269
+ classId: string | null;
263
270
  name: string;
264
271
  color: string | null;
265
- classId: string | null;
266
272
  parentFolderId: string | null;
267
273
  };
268
274
  meta: object;
@@ -294,12 +300,13 @@ export declare const folderRouter: import("@trpc/server").TRPCBuiltRouter<{
294
300
  childFolders: number;
295
301
  };
296
302
  name: string;
303
+ color: string | null;
297
304
  }[];
298
305
  } & {
299
306
  id: string;
307
+ classId: string | null;
300
308
  name: string;
301
309
  color: string | null;
302
- classId: string | null;
303
310
  parentFolderId: string | null;
304
311
  };
305
312
  meta: object;
@@ -308,13 +315,13 @@ export declare const folderRouter: import("@trpc/server").TRPCBuiltRouter<{
308
315
  input: {
309
316
  folderId: string;
310
317
  };
311
- output: {
318
+ output: ({
312
319
  id: string;
320
+ classId: string | null;
313
321
  name: string;
314
322
  color: string | null;
315
- classId: string | null;
316
323
  parentFolderId: string | null;
317
- }[];
324
+ } | null)[];
318
325
  meta: object;
319
326
  }>;
320
327
  getFolderUploadUrls: import("@trpc/server").TRPCMutationProcedure<{
@@ -328,7 +335,7 @@ export declare const folderRouter: import("@trpc/server").TRPCBuiltRouter<{
328
335
  }[];
329
336
  folderId: string;
330
337
  };
331
- output: DirectUploadFile[];
338
+ output: import("../lib/fileUpload.js").DirectUploadFile[];
332
339
  meta: object;
333
340
  }>;
334
341
  confirmFolderUpload: import("@trpc/server").TRPCMutationProcedure<{
@@ -1 +1 @@
1
- {"version":3,"file":"folder.d.ts","sourceRoot":"","sources":["../../src/routers/folder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,EAA2B,KAAK,gBAAgB,EAAqB,MAAM,sBAAsB,CAAC;AA6BzG,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAu2BvB,CAAC"}
1
+ {"version":3,"file":"folder.d.ts","sourceRoot":"/","sources":["routers/folder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA0CxB,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqIvB,CAAC"}