@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
@@ -0,0 +1,358 @@
1
+ /**
2
+ * Worksheet service – CRUD for worksheets, questions, submissions, and AI grading.
3
+ * Integrates with gradeWorksheet pipeline for auto-grading.
4
+ */
5
+ import { TRPCError } from "@trpc/server";
6
+ import { GenerationStatus, type WorksheetQuestionType } from "@prisma/client";
7
+ import { prisma } from "../lib/prisma.js";
8
+ import { cancelGradePipeline, regradeWorksheetPipeline } from "../pipelines/gradeWorksheet.js";
9
+ import {
10
+ findWorksheetById,
11
+ findWorksheetByIdMinimal,
12
+ findWorksheetsByClassId,
13
+ createWorksheet,
14
+ updateWorksheet,
15
+ deleteWorksheet,
16
+ findQuestionsByWorksheetId,
17
+ createWorksheetQuestion,
18
+ updateWorksheetQuestion,
19
+ deleteWorksheetQuestion,
20
+ findSubmissionById,
21
+ findOrCreateWorksheetResponse,
22
+ findWorksheetResponseWithResponses,
23
+ findWorksheetQuestionById,
24
+ updateStudentQuestionProgress,
25
+ createStudentQuestionProgress,
26
+ findStudentQuestionProgress,
27
+ updateStudentQuestionProgressForGrading,
28
+ createStudentQuestionProgressForGrading,
29
+ createComment,
30
+ } from "../models/worksheet.js";
31
+
32
+ type MCQOptions = { id: string; text: string; isCorrect: boolean }[];
33
+
34
+ /** Get worksheet with questions and mark schemes. */
35
+ export async function getWorksheet(worksheetId: string) {
36
+ const worksheet = await findWorksheetById(worksheetId);
37
+ if (!worksheet) {
38
+ throw new TRPCError({
39
+ code: "NOT_FOUND",
40
+ message: "Worksheet not found",
41
+ });
42
+ }
43
+ return worksheet;
44
+ }
45
+
46
+ /** Check if worksheet exists. */
47
+ export async function worksheetExists(id: string) {
48
+ const worksheet = await findWorksheetByIdMinimal(id);
49
+ return !!worksheet;
50
+ }
51
+
52
+ export async function listWorksheets(classId: string) {
53
+ const worksheets = await findWorksheetsByClassId(classId);
54
+ return worksheets.map((w) => ({
55
+ ...w,
56
+ questionCount: w.questions.length,
57
+ }));
58
+ }
59
+
60
+ export async function updateWorksheetRecord(
61
+ worksheetId: string,
62
+ data: { name?: string }
63
+ ) {
64
+ return updateWorksheet(worksheetId, data);
65
+ }
66
+
67
+ export async function deleteWorksheetRecord(worksheetId: string) {
68
+ return deleteWorksheet(worksheetId);
69
+ }
70
+
71
+ export async function createWorksheetRecord(classId: string, name: string) {
72
+ return createWorksheet({ classId, name });
73
+ }
74
+
75
+ export async function addQuestionToWorksheet(
76
+ worksheetId: string,
77
+ data: {
78
+ question: string;
79
+ answer: string;
80
+ points?: number;
81
+ options?: object;
82
+ markScheme?: object;
83
+ type: WorksheetQuestionType;
84
+ }
85
+ ) {
86
+ const worksheet = await findWorksheetByIdMinimal(worksheetId);
87
+ if (!worksheet) {
88
+ throw new TRPCError({
89
+ code: "NOT_FOUND",
90
+ message: "Worksheet not found",
91
+ });
92
+ }
93
+ return createWorksheetQuestion({
94
+ worksheetId,
95
+ ...data,
96
+ });
97
+ }
98
+
99
+ export async function reorderWorksheetQuestions(
100
+ worksheetId: string,
101
+ movedId: string,
102
+ position: "before" | "after",
103
+ targetId: string
104
+ ) {
105
+ const worksheet = await findWorksheetByIdMinimal(worksheetId);
106
+ if (!worksheet) {
107
+ throw new TRPCError({
108
+ code: "NOT_FOUND",
109
+ message: "Worksheet not found",
110
+ });
111
+ }
112
+
113
+ const questions = await findQuestionsByWorksheetId(worksheetId);
114
+ const movedIdx = questions.findIndex((q) => q.id === movedId);
115
+ if (movedIdx === -1) {
116
+ throw new TRPCError({
117
+ code: "NOT_FOUND",
118
+ message: "Moved question not found",
119
+ });
120
+ }
121
+ const targetIdx = questions.findIndex((q) => q.id === targetId);
122
+ if (targetIdx === -1) {
123
+ throw new TRPCError({
124
+ code: "NOT_FOUND",
125
+ message: "Target question not found",
126
+ });
127
+ }
128
+
129
+ const withoutMoved = questions.filter((q) => q.id !== movedId);
130
+ const next =
131
+ position === "before"
132
+ ? [
133
+ ...withoutMoved.slice(0, targetIdx).map((item) => ({ id: item.id })),
134
+ { id: movedId },
135
+ ...withoutMoved.slice(targetIdx).map((item) => ({ id: item.id })),
136
+ ]
137
+ : [
138
+ ...withoutMoved
139
+ .slice(0, targetIdx + 1)
140
+ .map((item) => ({ id: item.id })),
141
+ { id: movedId },
142
+ ...withoutMoved
143
+ .slice(targetIdx + 1)
144
+ .map((item) => ({ id: item.id })),
145
+ ];
146
+
147
+ await prisma.$transaction(
148
+ next.map((item, index) =>
149
+ prisma.worksheetQuestion.update({
150
+ where: { id: item.id },
151
+ data: { order: index },
152
+ })
153
+ )
154
+ );
155
+ return next;
156
+ }
157
+
158
+ export async function updateWorksheetQuestionRecord(
159
+ worksheetId: string,
160
+ questionId: string,
161
+ data: {
162
+ question?: string;
163
+ answer?: string;
164
+ points?: number;
165
+ options?: object;
166
+ markScheme?: object;
167
+ type?: WorksheetQuestionType;
168
+ }
169
+ ) {
170
+ const worksheet = await findWorksheetByIdMinimal(worksheetId);
171
+ if (!worksheet) {
172
+ throw new TRPCError({
173
+ code: "NOT_FOUND",
174
+ message: "Worksheet not found",
175
+ });
176
+ }
177
+ return updateWorksheetQuestion(questionId, data);
178
+ }
179
+
180
+ export async function deleteWorksheetQuestionRecord(
181
+ worksheetId: string,
182
+ questionId: string
183
+ ) {
184
+ const worksheet = await findWorksheetByIdMinimal(worksheetId);
185
+ if (!worksheet) {
186
+ throw new TRPCError({
187
+ code: "NOT_FOUND",
188
+ message: "Worksheet not found",
189
+ });
190
+ }
191
+ return deleteWorksheetQuestion(questionId);
192
+ }
193
+
194
+ export async function getWorksheetSubmission(
195
+ worksheetId: string,
196
+ submissionId: string
197
+ ) {
198
+ const submission = await findSubmissionById(submissionId);
199
+ if (!submission) {
200
+ throw new TRPCError({
201
+ code: "NOT_FOUND",
202
+ message: "Submission not found",
203
+ });
204
+ }
205
+ return findOrCreateWorksheetResponse(
206
+ submissionId,
207
+ worksheetId,
208
+ submission.studentId
209
+ );
210
+ }
211
+
212
+ export async function answerWorksheetQuestion(
213
+ worksheetResponseId: string,
214
+ questionId: string,
215
+ response: string,
216
+ userId: string
217
+ ) {
218
+ const worksheetResponse = await findWorksheetResponseWithResponses(
219
+ worksheetResponseId,
220
+ questionId
221
+ );
222
+ if (!worksheetResponse) {
223
+ throw new TRPCError({
224
+ code: "NOT_FOUND",
225
+ message: "Worksheet response not found",
226
+ });
227
+ }
228
+
229
+ const question = await findWorksheetQuestionById(questionId);
230
+ const isMarkableByAlgo =
231
+ question?.type === "MULTIPLE_CHOICE" || question?.type === "TRUE_FALSE";
232
+ const marksAwardedIfCorrect = question?.points || 0;
233
+ const correctAnswer = isMarkableByAlgo
234
+ ? question?.type === "MULTIPLE_CHOICE"
235
+ ? (question?.options as MCQOptions)?.find((o) => o.isCorrect)?.id
236
+ : question?.answer?.toString()
237
+ : null;
238
+
239
+ const existingResponse = worksheetResponse.responses[0];
240
+
241
+ if (existingResponse) {
242
+ await updateStudentQuestionProgress(existingResponse.id, {
243
+ response,
244
+ ...(isMarkableByAlgo && {
245
+ isCorrect: response === correctAnswer,
246
+ }),
247
+ ...(isMarkableByAlgo && {
248
+ points: response === correctAnswer ? marksAwardedIfCorrect : 0,
249
+ }),
250
+ status: GenerationStatus.NOT_STARTED,
251
+ });
252
+ } else {
253
+ await createStudentQuestionProgress({
254
+ studentId: worksheetResponse.studentId,
255
+ questionId,
256
+ response,
257
+ studentWorksheetResponseId: worksheetResponseId,
258
+ ...(isMarkableByAlgo && {
259
+ isCorrect: response === correctAnswer,
260
+ }),
261
+ ...(isMarkableByAlgo && {
262
+ points: response === correctAnswer ? marksAwardedIfCorrect : 0,
263
+ }),
264
+ });
265
+ }
266
+
267
+ const updated = await prisma.studentWorksheetResponse.findUnique({
268
+ where: { id: worksheetResponseId },
269
+ include: { responses: true },
270
+ });
271
+ return updated!;
272
+ }
273
+
274
+ export async function cancelGrading(
275
+ worksheetResponseId: string,
276
+ progressId: string
277
+ ) {
278
+ return cancelGradePipeline(worksheetResponseId, progressId);
279
+ }
280
+
281
+ export async function regradeQuestion(
282
+ worksheetResponseId: string,
283
+ progressId: string
284
+ ) {
285
+ return regradeWorksheetPipeline(worksheetResponseId, progressId);
286
+ }
287
+
288
+ export async function gradeAnswer(
289
+ questionId: string,
290
+ studentWorksheetResponseId: string,
291
+ data: {
292
+ responseId?: string;
293
+ response?: string;
294
+ isCorrect: boolean;
295
+ feedback?: string;
296
+ markschemeState?: object;
297
+ points?: number;
298
+ }
299
+ ) {
300
+ if (data.responseId) {
301
+ return updateStudentQuestionProgressForGrading(data.responseId, {
302
+ isCorrect: data.isCorrect,
303
+ feedback: data.feedback,
304
+ markschemeState: data.markschemeState,
305
+ points: data.points,
306
+ });
307
+ }
308
+
309
+ const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({
310
+ where: { id: studentWorksheetResponseId },
311
+ select: { studentId: true },
312
+ });
313
+ if (!worksheetResponse) {
314
+ throw new TRPCError({
315
+ code: "NOT_FOUND",
316
+ message: "Student worksheet response not found",
317
+ });
318
+ }
319
+
320
+ const existing = await findStudentQuestionProgress(
321
+ worksheetResponse.studentId,
322
+ questionId,
323
+ studentWorksheetResponseId
324
+ );
325
+
326
+ if (existing) {
327
+ return updateStudentQuestionProgressForGrading(existing.id, {
328
+ isCorrect: data.isCorrect,
329
+ response: data.response,
330
+ feedback: data.feedback,
331
+ markschemeState: data.markschemeState,
332
+ points: data.points,
333
+ });
334
+ }
335
+
336
+ return createStudentQuestionProgressForGrading({
337
+ studentId: worksheetResponse.studentId,
338
+ questionId,
339
+ studentWorksheetResponseId,
340
+ response: data.response || "",
341
+ isCorrect: data.isCorrect,
342
+ feedback: data.feedback,
343
+ markschemeState: data.markschemeState,
344
+ points: data.points || 0,
345
+ });
346
+ }
347
+
348
+ export async function addCommentToResponse(
349
+ responseId: string,
350
+ comment: string,
351
+ authorId: string
352
+ ) {
353
+ return createComment({
354
+ studentQuestionProgressId: responseId,
355
+ content: comment,
356
+ authorId,
357
+ });
358
+ }
package/src/trpc.ts CHANGED
@@ -7,6 +7,7 @@ import { createAuthMiddleware } from './middleware/auth.js';
7
7
  import { Request, Response } from 'express';
8
8
  import { z } from 'zod';
9
9
  import { handlePrismaError, PrismaErrorInfo } from './utils/prismaErrorHandler.js';
10
+ import { generalLimiter } from './middleware/security.js';
10
11
 
11
12
  interface CreateContextOptions {
12
13
  req: Request;
@@ -89,11 +90,14 @@ const { isAuthed, isMemberInClass, isTeacherInClass } = createAuthMiddleware(t);
89
90
  export const createTRPCRouter = t.router;
90
91
  export const publicProcedure = t.procedure.use(loggingMiddleware);
91
92
 
93
+
92
94
  // Protected procedures
93
95
  export const protectedProcedure = publicProcedure.use(isAuthed);
96
+
94
97
  export const protectedClassMemberProcedure = protectedProcedure
95
98
  .input(z.object({ classId: z.string() }).passthrough())
96
99
  .use(isMemberInClass);
100
+
97
101
  export const protectedTeacherProcedure = protectedProcedure
98
102
  .input(z.object({ classId: z.string() }).passthrough())
99
103
  .use(isTeacherInClass);
@@ -1,11 +1,12 @@
1
+ /**
2
+ * AI user util – ensures AI assistant user exists, provides ID and isAIUser check.
3
+ */
1
4
  import { prisma } from '../lib/prisma.js';
2
5
  import { logger } from './logger.js';
3
6
 
4
7
  const AI_USER_ID = 'AI_ASSISTANT';
5
8
 
6
- /**
7
- * Ensure AI assistant user exists in the database
8
- */
9
+ /** Ensure AI assistant user exists in the database. */
9
10
  export async function ensureAIUserExists(): Promise<void> {
10
11
  try {
11
12
  // Check if AI user already exists
@@ -1,11 +1,40 @@
1
+ /**
2
+ * Email util – nodemailer transport and sendMail wrapper. Supports EMAIL_DRY_RUN.
3
+ */
1
4
  import nodemailer from 'nodemailer';
5
+ import { env } from '../lib/config/env.js';
6
+ import { logger } from './logger.js';
7
+
8
+ type sendMailProps = {
9
+ from: string;
10
+ to: string;
11
+ subject: string;
12
+ text: string;
13
+ };
14
+
2
15
 
3
16
  export const transport = nodemailer.createTransport({
4
- host: process.env.EMAIL_HOST,
5
- port: 587,
17
+ host: env.EMAIL_HOST,
18
+ port: env.EMAIL_PORT,
6
19
  secure: false,
7
20
  auth: {
8
- user: process.env.EMAIL_USER,
9
- pass: process.env.EMAIL_PASS,
21
+ user: env.EMAIL_USER,
22
+ pass: env.EMAIL_PASS,
10
23
  },
11
24
  });
25
+
26
+
27
+ export const sendMail = async ({ from, to, subject, text }: sendMailProps) => {
28
+ // Wrapper function for sending emails
29
+ if (env.EMAIL_DRY_RUN == "true") {
30
+ logger.info(`Email dry run enabled. Would have sent email to ${to} from ${from} with subject ${subject} and text ${text}`);
31
+ return;
32
+ }
33
+
34
+ await transport.sendMail({
35
+ from: `"Studious" <${from}>`,
36
+ to,
37
+ subject,
38
+ text,
39
+ });
40
+ };
@@ -1,8 +1,6 @@
1
1
  /**
2
- * Generates a random invite code
3
- * @returns {string} The invite code with length 5
2
+ * Generate invite code util – random 5-char alphanumeric code for class invites.
4
3
  */
5
-
6
4
  export const generateInviteCode = () => {
7
5
  return (Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)).slice(0, 5);
8
6
  }
@@ -1,19 +1,24 @@
1
+ /**
2
+ * Inference util – OpenAI client, inference() for structured output, sendAIMessage for chat.
3
+ */
1
4
  import OpenAI from 'openai';
2
5
  import { logger } from './logger.js';
3
6
  import { prisma } from '../lib/prisma.js';
4
- import { pusher } from '../lib/pusher.js';
7
+ import { chatChannel, pusher } from '../lib/pusher.js';
5
8
  import { ensureAIUserExists, getAIUserId } from './aiUser.js';
9
+ import { env } from '../lib/config/env.js';
10
+ import { ZodSchema } from 'zod';
11
+ import { zodTextFormat } from "openai/helpers/zod";
6
12
 
7
- // Initialize inference client (Cohere via OpenAI SDK)
8
13
 
9
- logger.info('Inference API Key', { apiKey: process.env.INFERENCE_API_KEY });
10
- logger.info('Inference API Base URL', { baseURL: process.env.INFERENCE_API_BASE_URL });
11
14
 
12
15
  export const inferenceClient = new OpenAI({
13
- apiKey: process.env.INFERENCE_API_KEY,
14
- baseURL: process.env.INFERENCE_API_BASE_URL,
16
+ apiKey: env.INFERENCE_API_KEY,
17
+ baseURL: env.INFERENCE_API_BASE_URL,
15
18
  });
16
19
 
20
+ export const openAIClient = new OpenAI();
21
+
17
22
  // Types for lab chat context
18
23
  export interface LabChatContext {
19
24
  subject: string;
@@ -46,6 +51,7 @@ export async function sendAIMessage(
46
51
  attachments?: {
47
52
  connect: { id: string }[];
48
53
  };
54
+ meta?: Record<string, any>;
49
55
  customSender?: {
50
56
  displayName: string;
51
57
  profilePicture?: string | null;
@@ -57,6 +63,7 @@ export async function sendAIMessage(
57
63
  senderId: string;
58
64
  conversationId: string;
59
65
  createdAt: Date;
66
+ meta?: Record<string, any>;
60
67
  }> {
61
68
  // Ensure AI user exists
62
69
  await ensureAIUserExists();
@@ -72,6 +79,9 @@ export async function sendAIMessage(
72
79
  connect: options.attachments.connect,
73
80
  },
74
81
  }),
82
+ ...(options.meta && {
83
+ meta: options.meta,
84
+ }),
75
85
  },
76
86
  include: {
77
87
  attachments: true,
@@ -96,7 +106,7 @@ export async function sendAIMessage(
96
106
 
97
107
  // Broadcast via Pusher
98
108
  try {
99
- await pusher.trigger(`conversation-${conversationId}`, 'new-message', {
109
+ await pusher.trigger(chatChannel(conversationId), 'new-message', {
100
110
  id: aiMessage.id,
101
111
  content: aiMessage.content,
102
112
  senderId: getAIUserId(),
@@ -104,6 +114,7 @@ export async function sendAIMessage(
104
114
  createdAt: aiMessage.createdAt,
105
115
  sender: senderInfo,
106
116
  mentionedUserIds: [],
117
+ meta: aiMessage.meta,
107
118
  attachments: aiMessage.attachments.map(attachment => ({
108
119
  id: attachment.id,
109
120
  attachmentId: attachment.id,
@@ -123,9 +134,77 @@ export async function sendAIMessage(
123
134
  senderId: getAIUserId(),
124
135
  conversationId: aiMessage.conversationId,
125
136
  createdAt: aiMessage.createdAt,
137
+ meta: aiMessage.meta as Record<string, any>,
126
138
  };
127
139
  }
128
140
 
141
+ export async function inference<T>(
142
+ content: string | OpenAI.Chat.Completions.ChatCompletionMessageParam[],
143
+ format?: ZodSchema
144
+ ): Promise<T> {
145
+ try {
146
+
147
+ if (!format) {
148
+ const completion = await openAIClient.chat.completions.create({
149
+ model: 'gpt-5-nano',
150
+ messages: typeof content === 'string' ? [
151
+ {
152
+ role: 'user',
153
+ content: content,
154
+ },
155
+ ] : content as Array<{ role: 'user' | 'assistant' | 'system'; content: string }>,
156
+ });
157
+
158
+ return completion.choices[0]?.message?.content as T;
159
+ }
160
+
161
+ const completion = await openAIClient.responses.parse({
162
+ model: 'gpt-5-nano',
163
+ input: typeof content === 'string' ? [
164
+ {
165
+ role: 'user',
166
+ content: content,
167
+ },
168
+ ] : content as Array<{ role: 'user' | 'assistant' | 'system'; content: string }>,
169
+ ...(format ? { text: {
170
+ format: zodTextFormat(format, "newton_response_format"),
171
+ },
172
+ } : {}),
173
+ });
174
+
175
+ console.log({
176
+ model: 'gpt-5-nano',
177
+ input: typeof content === 'string' ? [
178
+ {
179
+ role: 'user',
180
+ content: content,
181
+ },
182
+ ] : content as Array<{ role: 'user' | 'assistant' | 'system'; content: string }>,
183
+ ...(format ? { text: {
184
+ format: zodTextFormat(format, "newton_response_format"),
185
+ },
186
+ } : {}),
187
+ });
188
+
189
+
190
+ if (!completion) {
191
+ throw new Error('No response generated from inference API');
192
+ }
193
+
194
+ // if (format) {
195
+ // if (typeof completion.output === 'string') {
196
+ // return JSON.parse(completion.output);
197
+ // }
198
+ // return JSON.parse(completion.output);
199
+ // }
200
+
201
+ return completion.output_parsed;
202
+ } catch (error) {
203
+ logger.error('Failed to generate inference response', { error });
204
+ throw error;
205
+ }
206
+ }
207
+
129
208
  /**
130
209
  * Simple inference function for general use
131
210
  */
@@ -137,10 +216,10 @@ export async function generateInferenceResponse(
137
216
  maxTokens?: number;
138
217
  } = {}
139
218
  ): Promise<InferenceResponse> {
140
- const { model = 'command-r-plus', maxTokens = 500 } = options;
219
+ const { model = 'gpt-5-nano', maxTokens = 500 } = options;
141
220
 
142
221
  try {
143
- const completion = await inferenceClient.chat.completions.create({
222
+ const completion = await openAIClient.chat.completions.create({
144
223
  model,
145
224
  messages: [
146
225
  {
@@ -180,7 +259,7 @@ export async function generateInferenceResponse(
180
259
  * Validate inference configuration
181
260
  */
182
261
  export function validateInferenceConfig(): boolean {
183
- if (!process.env.INFERENCE_API_KEY) {
262
+ if (!env.INFERENCE_API_KEY) {
184
263
  logger.error('Inference API key not configured for Cohere');
185
264
  return false;
186
265
  }
@@ -1,3 +1,6 @@
1
+ /**
2
+ * Logger util – structured logging with levels, modes, and colored output.
3
+ */
1
4
  export enum LogLevel {
2
5
  INFO = 'info',
3
6
  WARN = 'warn',
@@ -26,7 +29,24 @@ const colors = {
26
29
  magenta: '\x1b[35m',
27
30
  cyan: '\x1b[36m',
28
31
  white: '\x1b[37m',
29
- gray: '\x1b[90m'
32
+ gray: '\x1b[90m',
33
+ // Background colors
34
+ bgRed: '\x1b[41m',
35
+ bgGreen: '\x1b[42m',
36
+ bgYellow: '\x1b[43m',
37
+ bgBlue: '\x1b[44m',
38
+ bgMagenta: '\x1b[45m',
39
+ bgCyan: '\x1b[46m',
40
+ bgWhite: '\x1b[47m',
41
+ bgGray: '\x1b[100m',
42
+ // Bright background colors
43
+ bgBrightRed: '\x1b[101m',
44
+ bgBrightGreen: '\x1b[102m',
45
+ bgBrightYellow: '\x1b[103m',
46
+ bgBrightBlue: '\x1b[104m',
47
+ bgBrightMagenta: '\x1b[105m',
48
+ bgBrightCyan: '\x1b[106m',
49
+ bgBrightWhite: '\x1b[107m'
30
50
  };
31
51
 
32
52
  class Logger {
@@ -34,6 +54,7 @@ class Logger {
34
54
  private isDevelopment: boolean;
35
55
  private mode: LogMode;
36
56
  private levelColors: Record<LogLevel, string>;
57
+ private levelBgColors: Record<LogLevel, string>;
37
58
  private levelEmojis: Record<LogLevel, string>;
38
59
 
39
60
  private constructor() {
@@ -41,7 +62,7 @@ class Logger {
41
62
  // this.isDevelopment = process.env.NODE_ENV === 'development';
42
63
  this.isDevelopment = true;
43
64
 
44
- this.mode = (process.env.LOG_MODE as LogMode) || 'normal';
65
+ this.mode = (process.env.NODE_ENV === 'test' ? 'silent' : ((process.env.LOG_MODE as LogMode) || 'normal'));
45
66
 
46
67
 
47
68
  this.levelColors = {
@@ -51,6 +72,13 @@ class Logger {
51
72
  [LogLevel.DEBUG]: colors.magenta
52
73
  };
53
74
 
75
+ this.levelBgColors = {
76
+ [LogLevel.INFO]: colors.bgBlue,
77
+ [LogLevel.WARN]: colors.bgYellow,
78
+ [LogLevel.ERROR]: colors.bgRed,
79
+ [LogLevel.DEBUG]: colors.bgMagenta
80
+ };
81
+
54
82
  this.levelEmojis = {
55
83
  [LogLevel.INFO]: 'ℹ️',
56
84
  [LogLevel.WARN]: '⚠️',
@@ -95,10 +123,12 @@ class Logger {
95
123
  private formatMessage(logMessage: LogMessage): string {
96
124
  const { level, message, timestamp, context } = logMessage;
97
125
  const color = this.levelColors[level];
126
+ const bgColor = this.levelBgColors[level];
98
127
  const emoji = this.levelEmojis[level];
99
128
 
100
129
  const timestampStr = colors.gray + `[${timestamp}]` + colors.reset;
101
- const levelStr = color + `[${level.toUpperCase()}]` + colors.reset;
130
+ // Use background color for level badge like Vitest
131
+ const levelStr = colors.white + bgColor + ` ${level.toUpperCase()} ` + colors.reset;
102
132
  const emojiStr = emoji + ' ';
103
133
  const messageStr = colors.bright + message + colors.reset;
104
134
 
@@ -1,3 +1,6 @@
1
+ /**
2
+ * Prisma error handler – maps Prisma errors to user-friendly messages and tRPC codes.
3
+ */
1
4
  import {
2
5
  PrismaClientKnownRequestError,
3
6
  PrismaClientUnknownRequestError,