@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,289 @@
1
+ import { test, expect, describe, beforeEach } from 'vitest';
2
+ import { user1Caller, user2Caller } from '../setup';
3
+ import { expectRejects, expectUnauth } from '../helpers';
4
+ import { prisma } from '../../src/lib/prisma';
5
+ import { startOfWeek } from 'date-fns';
6
+
7
+ describe('Event Router', () => {
8
+ let testClass: any;
9
+ let testEvent: any;
10
+ let testAssignment: any;
11
+
12
+ beforeEach(async () => {
13
+ // Create a test class for user1
14
+ testClass = await user1Caller.class.create({
15
+ name: 'Test Class for Events',
16
+ subject: 'Mathematics',
17
+ section: '10th Grade',
18
+ });
19
+
20
+ // Create a test event
21
+ const startTime = new Date();
22
+ startTime.setHours(10, 0, 0, 0);
23
+ const endTime = new Date(startTime);
24
+ endTime.setHours(11, 0, 0, 0);
25
+
26
+ testEvent = await user1Caller.event.create({
27
+ name: 'Test Event',
28
+ location: 'Room 101',
29
+ remarks: 'Test remarks',
30
+ startTime: startTime.toISOString(),
31
+ endTime: endTime.toISOString(),
32
+ classId: testClass.id,
33
+ color: '#3B82F6',
34
+ });
35
+
36
+ const dueDate = new Date();
37
+ dueDate.setDate(dueDate.getDate() + 7);
38
+ testAssignment = await user1Caller.assignment.create({
39
+ classId: testClass.id,
40
+ title: 'Test Assignment for Event',
41
+ instructions: 'Test',
42
+ dueDate: dueDate.toISOString(),
43
+ });
44
+ });
45
+
46
+ describe('create', () => {
47
+ test('should create event successfully', async () => {
48
+ expect(testEvent).toBeDefined();
49
+ expect(testEvent.event).toBeDefined();
50
+ expect(testEvent.event.name).toBe('Test Event');
51
+ expect(testEvent.event.location).toBe('Room 101');
52
+ expect(testEvent.event.remarks).toBe('Test remarks');
53
+ expect(testEvent.event.classId).toBe(testClass.id);
54
+ expect(testEvent.event.color).toBe('#3B82F6');
55
+ });
56
+
57
+ test('should create event without class', async () => {
58
+ const startTime = new Date();
59
+ startTime.setHours(14, 0, 0, 0);
60
+ const endTime = new Date(startTime);
61
+ endTime.setHours(15, 0, 0, 0);
62
+
63
+ const event = await user1Caller.event.create({
64
+ name: 'Personal Event',
65
+ location: 'Home',
66
+ startTime: startTime.toISOString(),
67
+ endTime: endTime.toISOString(),
68
+ });
69
+
70
+ expect(event.event).toBeDefined();
71
+ expect(event.event.name).toBe('Personal Event');
72
+ expect(event.event.classId).toBeNull();
73
+ });
74
+
75
+ test('should fail to create event for class user is not teacher of', () =>
76
+ expectRejects(() => {
77
+ const startTime = new Date();
78
+ startTime.setHours(10, 0, 0, 0);
79
+ const endTime = new Date(startTime);
80
+ endTime.setHours(11, 0, 0, 0);
81
+ return user2Caller.event.create({
82
+ name: 'Unauthorized Event',
83
+ startTime: startTime.toISOString(),
84
+ endTime: endTime.toISOString(),
85
+ classId: testClass.id,
86
+ });
87
+ }));
88
+
89
+ test('should fail without authentication', () =>
90
+ expectUnauth((c) => {
91
+ const startTime = new Date();
92
+ return c.event.create({
93
+ name: 'Test',
94
+ startTime: startTime.toISOString(),
95
+ endTime: startTime.toISOString(),
96
+ });
97
+ }));
98
+ });
99
+
100
+ describe('get', () => {
101
+ test('should get event successfully', async () => {
102
+ const event = await user1Caller.event.get({
103
+ id: testEvent.event.id,
104
+ });
105
+
106
+ expect(event).toBeDefined();
107
+ expect(event.event.id).toBe(testEvent.event.id);
108
+ expect(event.event.name).toBe('Test Event');
109
+ expect(event.event.class).toBeDefined();
110
+ });
111
+
112
+ test('should fail to get event user does not own', async () => {
113
+ await expect(user2Caller.event.get({
114
+ id: testEvent.event.id,
115
+ })).rejects.toThrow();
116
+ });
117
+
118
+ test('should fail to get non-existent event', async () => {
119
+ await expect(user1Caller.event.get({
120
+ id: 'non-existent-id',
121
+ })).rejects.toThrow();
122
+ });
123
+ });
124
+
125
+ describe('update', () => {
126
+ test('should update event successfully', async () => {
127
+ const startTime = new Date();
128
+ startTime.setHours(12, 0, 0, 0);
129
+ const endTime = new Date(startTime);
130
+ endTime.setHours(13, 0, 0, 0);
131
+
132
+ const updated = await user1Caller.event.update({
133
+ id: testEvent.event.id,
134
+ data: {
135
+ name: 'Updated Event Name',
136
+ location: 'Room 202',
137
+ remarks: 'Updated remarks',
138
+ startTime: startTime.toISOString(),
139
+ endTime: endTime.toISOString(),
140
+ color: '#10B981',
141
+ },
142
+ });
143
+
144
+ expect(updated.event.name).toBe('Updated Event Name');
145
+ expect(updated.event.location).toBe('Room 202');
146
+ expect(updated.event.remarks).toBe('Updated remarks');
147
+ expect(updated.event.color).toBe('#10B981');
148
+ });
149
+
150
+ // @todo: not implemented partial updates
151
+ // test('should update event with partial data', async () => {
152
+ // const updated = await user1Caller.event.update({
153
+ // id: testEvent.event.id,
154
+ // data: {
155
+ // name: 'Partially Updated Event',
156
+ // },
157
+ // });
158
+
159
+ // expect(updated.event.name).toBe('Partially Updated Event');
160
+ // expect(updated.event.location).toBe('Room 101'); // Should remain unchanged
161
+ // });
162
+
163
+ test('should fail to update event user does not own', async () => {
164
+ await expect(user2Caller.event.update({
165
+ id: testEvent.event.id,
166
+ data: {
167
+ name: 'Unauthorized Update',
168
+ location: 'Room 101',
169
+ remarks: 'Updated remarks',
170
+ startTime: testEvent.event.startTime,
171
+ endTime: testEvent.event.endTime,
172
+ color: '#10B981',
173
+ },
174
+ })).rejects.toThrow();
175
+ });
176
+ });
177
+
178
+ describe('delete', () => {
179
+ test('should delete event successfully', async () => {
180
+ const result = await user1Caller.event.delete({
181
+ id: testEvent.event.id,
182
+ });
183
+
184
+ expect(result.success).toBe(true);
185
+
186
+ // Verify event is deleted
187
+ await expect(user1Caller.event.get({
188
+ id: testEvent.event.id,
189
+ })).rejects.toThrow();
190
+ });
191
+
192
+ test('should fail to delete event user does not own', async () => {
193
+ await expect(user2Caller.event.delete({
194
+ id: testEvent.event.id,
195
+ })).rejects.toThrow();
196
+ });
197
+ });
198
+
199
+ describe('attachAssignment', () => {
200
+ test('should attach assignment to event', async () => {
201
+ const result = await user1Caller.event.attachAssignment({
202
+ eventId: testEvent.event.id,
203
+ assignmentId: testAssignment.id,
204
+ });
205
+ expect(result).toBeDefined();
206
+ });
207
+
208
+ test('should fail when user is not teacher of class', () =>
209
+ expectRejects(() =>
210
+ user2Caller.event.attachAssignment({
211
+ eventId: testEvent.event.id,
212
+ assignmentId: testAssignment.id,
213
+ })
214
+ ));
215
+ });
216
+
217
+ describe('detachAssignment', () => {
218
+ test('should detach assignment from event', async () => {
219
+ await user1Caller.event.attachAssignment({
220
+ eventId: testEvent.event.id,
221
+ assignmentId: testAssignment.id,
222
+ });
223
+ const result = await user1Caller.event.detachAssignment({
224
+ eventId: testEvent.event.id,
225
+ assignmentId: testAssignment.id,
226
+ });
227
+ expect(result).toBeDefined();
228
+ });
229
+
230
+ test('should fail when user is not teacher of class', () =>
231
+ expectRejects(() =>
232
+ user2Caller.event.detachAssignment({
233
+ eventId: testEvent.event.id,
234
+ assignmentId: testAssignment.id,
235
+ })
236
+ ));
237
+ });
238
+
239
+ describe('getAvailableAssignments', () => {
240
+ test('should return available assignments for class event', async () => {
241
+ const result = await user1Caller.event.getAvailableAssignments({
242
+ eventId: testEvent.event.id,
243
+ });
244
+ expect(result).toBeDefined();
245
+ expect(result.assignments).toBeDefined();
246
+ expect(Array.isArray(result.assignments)).toBe(true);
247
+ expect(result.assignments.some((a: any) => a.id === testAssignment.id)).toBe(true);
248
+ });
249
+
250
+ test('should fail without authentication', () =>
251
+ expectUnauth((c) =>
252
+ c.event.getAvailableAssignments({ eventId: testEvent.event.id })
253
+ ));
254
+ });
255
+
256
+ describe('getAll', () => {
257
+ test('should get all events for user', async () => {
258
+ // Create another event
259
+ const startTime = new Date();
260
+ startTime.setHours(15, 0, 0, 0);
261
+ const endTime = new Date(startTime);
262
+ endTime.setHours(16, 0, 0, 0);
263
+
264
+ const secondEvent = await user1Caller.event.create({
265
+ name: 'Second Event',
266
+ startTime: startTime.toISOString(),
267
+ endTime: endTime.toISOString(),
268
+ });
269
+
270
+ const events = await user1Caller.agenda.get({
271
+ weekStart: startOfWeek(startTime).toISOString(),
272
+ });
273
+
274
+ expect(events.events).toBeDefined();
275
+ expect(events.events.personal.length).toBeGreaterThanOrEqual(2);
276
+ expect(events.events.personal.some((e) => e.id === secondEvent.event.id)).toBe(true);
277
+ });
278
+
279
+ test('should return empty array for user with no events', async () => {
280
+ const events = await user2Caller.agenda.get({
281
+ weekStart: new Date().toISOString(),
282
+ });
283
+ expect(events.events.personal).toBeDefined();
284
+ expect(Array.isArray(events.events.personal)).toBe(true);
285
+ expect(events.events.personal.length).toBe(0);
286
+ });
287
+ });
288
+ });
289
+
@@ -0,0 +1,178 @@
1
+ import { test, expect, describe, beforeAll } from 'vitest';
2
+ import { user1Caller, user2Caller } from '../setup';
3
+
4
+ describe('Folder Router', () => {
5
+ let testClass: any;
6
+ let rootFolder: any;
7
+ let childFolder: any;
8
+
9
+ beforeAll(async () => {
10
+ testClass = await user1Caller.class.create({
11
+ name: 'Folder Test Class',
12
+ subject: 'Art',
13
+ section: '10th Grade',
14
+ });
15
+ });
16
+
17
+ describe('getRootFolder', () => {
18
+ test('should get or create root folder for class', async () => {
19
+ rootFolder = await user1Caller.folder.getRootFolder({
20
+ classId: testClass.id,
21
+ });
22
+
23
+ expect(rootFolder).toBeDefined();
24
+ expect(rootFolder.name).toBe('Class Files');
25
+ });
26
+ });
27
+
28
+ describe('create', () => {
29
+ test('should create a subfolder', async () => {
30
+ childFolder = await user1Caller.folder.create({
31
+ classId: testClass.id,
32
+ name: 'Homework',
33
+ color: '#3B82F6',
34
+ });
35
+
36
+ expect(childFolder).toBeDefined();
37
+ expect(childFolder.name).toBe('Homework');
38
+ });
39
+
40
+ test('should create folder under specific parent', async () => {
41
+ const nested = await user1Caller.folder.create({
42
+ classId: testClass.id,
43
+ name: 'Week 1',
44
+ parentFolderId: childFolder.id,
45
+ });
46
+
47
+ expect(nested).toBeDefined();
48
+ expect(nested.name).toBe('Week 1');
49
+ });
50
+
51
+ test('should fail for non-teacher', async () => {
52
+ await expect(
53
+ user2Caller.folder.create({
54
+ classId: testClass.id,
55
+ name: 'Unauthorized Folder',
56
+ }),
57
+ ).rejects.toThrow();
58
+ });
59
+ });
60
+
61
+ describe('get', () => {
62
+ test('should get folder by ID', async () => {
63
+ const folder = await user1Caller.folder.get({
64
+ folderId: childFolder.id,
65
+ classId: testClass.id,
66
+ });
67
+
68
+ expect(folder).toBeDefined();
69
+ expect(folder.name).toBe('Homework');
70
+ expect(folder.childFolders.length).toBe(1);
71
+ });
72
+
73
+ test('should fail for non-existent folder', async () => {
74
+ await expect(
75
+ user1Caller.folder.get({
76
+ folderId: 'nonexistent',
77
+ classId: testClass.id,
78
+ }),
79
+ ).rejects.toThrow();
80
+ });
81
+ });
82
+
83
+ describe('getChildFolders', () => {
84
+ test('should list child folders of root', async () => {
85
+ const children = await user1Caller.folder.getChildFolders({
86
+ classId: testClass.id,
87
+ });
88
+
89
+ expect(Array.isArray(children)).toBe(true);
90
+ expect(children.some((f: any) => f.name === 'Homework')).toBe(true);
91
+ });
92
+ });
93
+
94
+ describe('update', () => {
95
+ test('should rename a folder', async () => {
96
+ const updated = await user1Caller.folder.update({
97
+ classId: testClass.id,
98
+ folderId: childFolder.id,
99
+ name: 'Classwork',
100
+ color: '#EF4444',
101
+ });
102
+
103
+ expect(updated.name).toBe('Classwork');
104
+ });
105
+
106
+ test('should fail for non-teacher', async () => {
107
+ await expect(
108
+ user2Caller.folder.update({
109
+ classId: testClass.id,
110
+ folderId: childFolder.id,
111
+ name: 'Unauthorized Rename',
112
+ }),
113
+ ).rejects.toThrow();
114
+ });
115
+ });
116
+
117
+ describe('move', () => {
118
+ test('should move folder to a different parent', async () => {
119
+ const newParent = await user1Caller.folder.create({
120
+ classId: testClass.id,
121
+ name: 'Archive',
122
+ });
123
+
124
+ const childToMove = await user1Caller.folder.create({
125
+ classId: testClass.id,
126
+ name: 'Movable Folder',
127
+ });
128
+
129
+ const moved = await user1Caller.folder.move({
130
+ classId: testClass.id,
131
+ folderId: childToMove.id,
132
+ targetParentFolderId: newParent.id,
133
+ });
134
+
135
+ expect(moved).toBeDefined();
136
+ });
137
+
138
+ test('should fail to move folder into itself', async () => {
139
+ await expect(
140
+ user1Caller.folder.move({
141
+ classId: testClass.id,
142
+ folderId: childFolder.id,
143
+ targetParentFolderId: childFolder.id,
144
+ }),
145
+ ).rejects.toThrow();
146
+ });
147
+ });
148
+
149
+ describe('delete', () => {
150
+ test('should delete a folder', async () => {
151
+ const tempClass = await user1Caller.class.create({
152
+ name: 'Temp Delete Class',
153
+ subject: 'Temp',
154
+ section: 'Temp',
155
+ });
156
+
157
+ const root = await user1Caller.folder.getRootFolder({
158
+ classId: tempClass.id,
159
+ });
160
+
161
+ const result = await user1Caller.folder.delete({
162
+ classId: tempClass.id,
163
+ folderId: root!.id,
164
+ });
165
+
166
+ expect(result.success).toBe(true);
167
+ });
168
+
169
+ test('should fail for non-teacher', async () => {
170
+ await expect(
171
+ user2Caller.folder.delete({
172
+ classId: testClass.id,
173
+ folderId: childFolder.id,
174
+ }),
175
+ ).rejects.toThrow();
176
+ });
177
+ });
178
+ });
@@ -0,0 +1,115 @@
1
+ import { test, expect, describe, beforeAll } from 'vitest';
2
+ import { user1Caller, user2Caller } from '../setup';
3
+
4
+ describe('Lab Chat Router', () => {
5
+ let testClass: any;
6
+ let labChatId: string;
7
+
8
+ beforeAll(async () => {
9
+ testClass = await user1Caller.class.create({
10
+ name: 'Lab Chat Test Class',
11
+ subject: 'Chemistry',
12
+ section: '11th Grade',
13
+ });
14
+ });
15
+
16
+ describe('create', () => {
17
+ test('should create a lab chat', async () => {
18
+ const context = JSON.stringify({
19
+ topic: 'Chemical Reactions',
20
+ objectives: ['Understand reaction types'],
21
+ });
22
+
23
+ const labChat = await user1Caller.labChat.create({
24
+ classId: testClass.id,
25
+ title: 'Chemical Reactions Lab',
26
+ context,
27
+ });
28
+
29
+ expect(labChat).toBeDefined();
30
+ expect(labChat.title).toBe('Chemical Reactions Lab');
31
+ expect(labChat.classId).toBe(testClass.id);
32
+ expect(labChat.conversationId).toBeDefined();
33
+ labChatId = labChat.id;
34
+ });
35
+
36
+ test('should fail with invalid JSON context', async () => {
37
+ await expect(
38
+ user1Caller.labChat.create({
39
+ classId: testClass.id,
40
+ title: 'Bad Context Lab',
41
+ context: 'not valid json',
42
+ }),
43
+ ).rejects.toThrow();
44
+ });
45
+
46
+ test('should fail for non-teacher', async () => {
47
+ await expect(
48
+ user2Caller.labChat.create({
49
+ classId: testClass.id,
50
+ title: 'Unauthorized Lab',
51
+ context: '{}',
52
+ }),
53
+ ).rejects.toThrow();
54
+ });
55
+ });
56
+
57
+ describe('list', () => {
58
+ test('should list lab chats for a class', async () => {
59
+ const labChats = await user1Caller.labChat.list({
60
+ classId: testClass.id,
61
+ });
62
+
63
+ expect(Array.isArray(labChats)).toBe(true);
64
+ expect(labChats.some((lc) => lc.id === labChatId)).toBe(true);
65
+ });
66
+
67
+ test('should fail for non-class-member', async () => {
68
+ await expect(
69
+ user2Caller.labChat.list({ classId: testClass.id }),
70
+ ).rejects.toThrow();
71
+ });
72
+ });
73
+
74
+ describe('get', () => {
75
+ test('should get lab chat by ID', async () => {
76
+ const labChat = await user1Caller.labChat.get({
77
+ labChatId,
78
+ });
79
+
80
+ expect(labChat).toBeDefined();
81
+ expect(labChat.id).toBe(labChatId);
82
+ expect(labChat.title).toBe('Chemical Reactions Lab');
83
+ expect(labChat.conversation).toBeDefined();
84
+ });
85
+
86
+ test('should fail for non-member', async () => {
87
+ await expect(
88
+ user2Caller.labChat.get({ labChatId }),
89
+ ).rejects.toThrow();
90
+ });
91
+ });
92
+
93
+ describe('delete', () => {
94
+ test('should fail for non-creator', async () => {
95
+ const user2Profile = await user2Caller.user.getProfile();
96
+ await user1Caller.class.addStudent({
97
+ classId: testClass.id,
98
+ studentId: user2Profile.id,
99
+ });
100
+
101
+ await expect(
102
+ user2Caller.labChat.delete({ labChatId }),
103
+ ).rejects.toThrow();
104
+ });
105
+
106
+ test('should delete lab chat', async () => {
107
+ const result = await user1Caller.labChat.delete({ labChatId });
108
+ expect(result.success).toBe(true);
109
+
110
+ await expect(
111
+ user1Caller.labChat.get({ labChatId }),
112
+ ).rejects.toThrow();
113
+ });
114
+ });
115
+ });
@@ -0,0 +1,59 @@
1
+ import { test, expect, describe } from 'vitest';
2
+ import { caller } from '../setup';
3
+
4
+ describe('Marketing Router', () => {
5
+ let programId: string;
6
+
7
+ describe('createSchoolDevelopementProgram', () => {
8
+ test('should create a school development program', async () => {
9
+ const result = await caller.marketing.createSchoolDevelopementProgram({
10
+ name: 'Test School',
11
+ type: 'Public',
12
+ address: '123 Test St',
13
+ city: 'Test City',
14
+ country: 'US',
15
+ numberOfStudents: 500,
16
+ numberOfTeachers: 30,
17
+ contactName: 'John Doe',
18
+ contactEmail: 'john@test.com',
19
+ });
20
+
21
+ expect(result).toBeDefined();
22
+ expect(result.id).toBeDefined();
23
+ expect(result.id).toContain('TES-');
24
+ programId = result.id;
25
+ });
26
+ });
27
+
28
+ describe('searchSchoolDevelopementPrograms', () => {
29
+ test('should find program by ID', async () => {
30
+ const result = await caller.marketing.searchSchoolDevelopementPrograms({
31
+ id: programId,
32
+ });
33
+
34
+ expect(result).toBeDefined();
35
+ expect(result!.name).toBe('Test School');
36
+ expect(result!.city).toBe('Test City');
37
+ });
38
+
39
+ test('should return null for non-existent ID', async () => {
40
+ const result = await caller.marketing.searchSchoolDevelopementPrograms({
41
+ id: 'NONEXISTENT',
42
+ });
43
+
44
+ expect(result).toBeNull();
45
+ });
46
+ });
47
+
48
+ describe('earlyAccessRequest', () => {
49
+ test('should create an early access request', async () => {
50
+ const result = await caller.marketing.earlyAccessRequest({
51
+ email: 'earlyaccess@test.com',
52
+ institutionSize: '100-500',
53
+ });
54
+
55
+ expect(result).toBeDefined();
56
+ expect(result.id).toBeDefined();
57
+ });
58
+ });
59
+ });