@goscribe/server 1.3.3 → 1.5.0

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 (378) hide show
  1. package/.env.example +12 -0
  2. package/.vscode/settings.json +3 -0
  3. package/REFACTOR_NOTES.md +60 -0
  4. package/TESTING_PROMPT.md +225 -0
  5. package/dist/controllers/admin.controller.d.ts +715 -0
  6. package/dist/controllers/admin.controller.js +9 -0
  7. package/dist/controllers/annotations.controller.d.ts +439 -0
  8. package/dist/controllers/annotations.controller.js +9 -0
  9. package/dist/controllers/app-router.controller.d.ts +3011 -0
  10. package/dist/controllers/app-router.controller.js +38 -0
  11. package/dist/controllers/app-router.controller.test.d.ts +1 -0
  12. package/dist/controllers/app-router.controller.test.js +36 -0
  13. package/dist/controllers/auth.controller.d.ts +323 -0
  14. package/dist/controllers/auth.controller.js +9 -0
  15. package/dist/controllers/base.controller.d.ts +4 -0
  16. package/dist/controllers/base.controller.js +5 -0
  17. package/dist/controllers/chat.controller.d.ts +341 -0
  18. package/dist/controllers/chat.controller.js +9 -0
  19. package/dist/controllers/copilot.controller.d.ts +397 -0
  20. package/dist/controllers/copilot.controller.js +9 -0
  21. package/dist/controllers/flashcards.controller.d.ts +651 -0
  22. package/dist/controllers/flashcards.controller.js +9 -0
  23. package/dist/controllers/members.controller.d.ts +339 -0
  24. package/dist/controllers/members.controller.js +9 -0
  25. package/dist/controllers/notifications.controller.d.ts +199 -0
  26. package/dist/controllers/notifications.controller.js +9 -0
  27. package/dist/controllers/payment.controller.d.ts +181 -0
  28. package/dist/controllers/payment.controller.js +9 -0
  29. package/dist/controllers/podcast.controller.d.ts +575 -0
  30. package/dist/controllers/podcast.controller.js +9 -0
  31. package/dist/controllers/router-module.controller.d.ts +5 -0
  32. package/dist/controllers/router-module.controller.js +6 -0
  33. package/dist/controllers/studyguide.controller.d.ts +73 -0
  34. package/dist/controllers/studyguide.controller.js +9 -0
  35. package/dist/controllers/worksheets.controller.d.ts +829 -0
  36. package/dist/controllers/worksheets.controller.js +9 -0
  37. package/dist/controllers/workspace.controller.d.ts +1207 -0
  38. package/dist/controllers/workspace.controller.js +9 -0
  39. package/dist/lib/activity_human_description.test.js +16 -15
  40. package/dist/lib/activity_log_service.test.js +28 -23
  41. package/dist/lib/ai/config.d.ts +20 -0
  42. package/dist/lib/ai/config.js +31 -0
  43. package/dist/lib/ai/embedding-client.d.ts +8 -0
  44. package/dist/lib/ai/embedding-client.js +30 -0
  45. package/dist/lib/ai/index.d.ts +47 -0
  46. package/dist/lib/ai/index.js +28 -0
  47. package/dist/lib/ai/inference-backend/client.d.ts +28 -0
  48. package/dist/lib/ai/inference-backend/client.js +301 -0
  49. package/dist/lib/ai/inference-backend/mocks.d.ts +12 -0
  50. package/dist/lib/ai/inference-backend/mocks.js +133 -0
  51. package/dist/lib/ai/inference-backend/types.d.ts +44 -0
  52. package/dist/lib/ai/inference-backend/types.js +1 -0
  53. package/dist/lib/ai/json-parse.d.ts +2 -0
  54. package/dist/lib/ai/json-parse.js +34 -0
  55. package/dist/lib/ai/llm-client.d.ts +6 -0
  56. package/dist/lib/ai/llm-client.js +19 -0
  57. package/dist/lib/ai/mock.d.ts +2 -0
  58. package/dist/lib/ai/mock.js +10 -0
  59. package/dist/lib/ai/types.d.ts +9 -0
  60. package/dist/lib/ai/types.js +1 -0
  61. package/dist/lib/chunking.d.ts +19 -0
  62. package/dist/lib/chunking.js +47 -0
  63. package/dist/lib/curated-kb-seed.d.ts +12 -0
  64. package/dist/lib/curated-kb-seed.js +155 -0
  65. package/dist/lib/email.js +67 -108
  66. package/dist/lib/embeddings.d.ts +2 -0
  67. package/dist/lib/embeddings.js +1 -0
  68. package/dist/lib/ensure-curated-kb-catalog.d.ts +6 -0
  69. package/dist/lib/ensure-curated-kb-catalog.js +53 -0
  70. package/dist/lib/env.d.ts +1 -5
  71. package/dist/lib/env.js +2 -7
  72. package/dist/lib/inference.d.ts +1 -8
  73. package/dist/lib/inference.js +1 -19
  74. package/dist/lib/kb-meta.d.ts +8 -0
  75. package/dist/lib/kb-meta.js +77 -0
  76. package/dist/lib/note-text.d.ts +1 -0
  77. package/dist/lib/note-text.js +47 -0
  78. package/dist/lib/notification-service.test.js +37 -36
  79. package/dist/lib/pdf.d.ts +11 -0
  80. package/dist/lib/pdf.js +11 -0
  81. package/dist/lib/usage_service.d.ts +2 -1
  82. package/dist/lib/usage_service.js +30 -12
  83. package/dist/lib/worksheet-generation.js +4 -4
  84. package/dist/lib/worksheet-generation.test.js +32 -17
  85. package/dist/lib/workspace-kb.d.ts +5 -0
  86. package/dist/lib/workspace-kb.js +7 -0
  87. package/dist/models/controller-context.model.d.ts +8 -0
  88. package/dist/models/controller-context.model.js +1 -0
  89. package/dist/repositories/artifact.repository.d.ts +60 -0
  90. package/dist/repositories/artifact.repository.js +40 -0
  91. package/dist/repositories/base.repository.d.ts +14 -0
  92. package/dist/repositories/base.repository.js +14 -0
  93. package/dist/repositories/invitation.repository.d.ts +94 -0
  94. package/dist/repositories/invitation.repository.js +44 -0
  95. package/dist/repositories/notification.repository.d.ts +72 -0
  96. package/dist/repositories/notification.repository.js +44 -0
  97. package/dist/repositories/router-module.repository.d.ts +10 -0
  98. package/dist/repositories/router-module.repository.js +14 -0
  99. package/dist/repositories/user.repository.d.ts +74 -0
  100. package/dist/repositories/user.repository.js +37 -0
  101. package/dist/repositories/workspace-member.repository.d.ts +31 -0
  102. package/dist/repositories/workspace-member.repository.js +31 -0
  103. package/dist/repositories/workspace.repository.d.ts +97 -0
  104. package/dist/repositories/workspace.repository.js +79 -0
  105. package/dist/routers/_app.d.ts +528 -33
  106. package/dist/routers/_app.js +4 -0
  107. package/dist/routers/admin.d.ts +0 -4
  108. package/dist/routers/admin.js +21 -549
  109. package/dist/routers/annotations.js +12 -170
  110. package/dist/routers/artifactVersions.d.ts +65 -0
  111. package/dist/routers/artifactVersions.js +14 -0
  112. package/dist/routers/auth.d.ts +0 -6
  113. package/dist/routers/auth.js +36 -421
  114. package/dist/routers/chat.js +15 -229
  115. package/dist/routers/copilot.d.ts +14 -13
  116. package/dist/routers/copilot.js +13 -532
  117. package/dist/routers/flashcards.d.ts +5 -5
  118. package/dist/routers/flashcards.js +23 -349
  119. package/dist/routers/knowledgeBase.d.ts +421 -0
  120. package/dist/routers/knowledgeBase.js +118 -0
  121. package/dist/routers/members.d.ts +0 -41
  122. package/dist/routers/members.js +22 -710
  123. package/dist/routers/notes.d.ts +94 -0
  124. package/dist/routers/notes.js +37 -0
  125. package/dist/routers/notifications.js +7 -109
  126. package/dist/routers/payment.d.ts +3 -2
  127. package/dist/routers/payment.js +11 -393
  128. package/dist/routers/podcast.d.ts +1 -1
  129. package/dist/routers/podcast.js +11 -784
  130. package/dist/routers/studyguide.js +3 -129
  131. package/dist/routers/worksheets.d.ts +29 -14
  132. package/dist/routers/worksheets.js +49 -628
  133. package/dist/routers/workspace.d.ts +0 -4
  134. package/dist/routers/workspace.js +28 -922
  135. package/dist/scripts/purge-deleted-users.js +2 -2
  136. package/dist/server.js +10 -3
  137. package/dist/services/activity/activity-human-description.service.d.ts +13 -0
  138. package/dist/services/activity/activity-human-description.service.js +221 -0
  139. package/dist/services/activity/activity-human-description.service.test.d.ts +1 -0
  140. package/dist/services/activity/activity-human-description.service.test.js +16 -0
  141. package/dist/services/activity/activity-log.service.d.ts +87 -0
  142. package/dist/services/activity/activity-log.service.js +276 -0
  143. package/dist/services/activity/activity-log.service.test.d.ts +1 -0
  144. package/dist/services/activity/activity-log.service.test.js +27 -0
  145. package/dist/services/activity-human-description.service.d.ts +13 -0
  146. package/dist/services/activity-human-description.service.js +221 -0
  147. package/dist/services/activity-human-description.service.test.d.ts +1 -0
  148. package/dist/services/activity-human-description.service.test.js +16 -0
  149. package/dist/services/activity-log.service.d.ts +87 -0
  150. package/dist/services/activity-log.service.js +276 -0
  151. package/dist/services/activity-log.service.test.d.ts +1 -0
  152. package/dist/services/activity-log.service.test.js +27 -0
  153. package/dist/services/admin/admin.service.d.ts +270 -0
  154. package/dist/services/admin/admin.service.js +476 -0
  155. package/dist/services/admin.service.d.ts +270 -0
  156. package/dist/services/admin.service.js +476 -0
  157. package/dist/services/ai/ai-session.service.d.ts +5 -0
  158. package/dist/services/ai/ai-session.service.js +4 -0
  159. package/dist/services/ai-session.service.d.ts +60 -0
  160. package/dist/services/ai-session.service.js +561 -0
  161. package/dist/services/annotation.service.d.ts +177 -0
  162. package/dist/services/annotation.service.js +154 -0
  163. package/dist/services/artifact-notification.service.d.ts +14 -0
  164. package/dist/services/artifact-notification.service.js +20 -0
  165. package/dist/services/artifact-version.service.d.ts +38 -0
  166. package/dist/services/artifact-version.service.js +129 -0
  167. package/dist/services/artifacts/annotation.service.d.ts +177 -0
  168. package/dist/services/artifacts/annotation.service.js +154 -0
  169. package/dist/services/artifacts/artifact-version.service.d.ts +38 -0
  170. package/dist/services/artifacts/artifact-version.service.js +129 -0
  171. package/dist/services/artifacts/chat.service.d.ts +127 -0
  172. package/dist/services/artifacts/chat.service.js +182 -0
  173. package/dist/services/artifacts/study-guide.service.d.ts +18 -0
  174. package/dist/services/artifacts/study-guide.service.js +65 -0
  175. package/dist/services/auth/auth.service.d.ts +94 -0
  176. package/dist/services/auth/auth.service.js +368 -0
  177. package/dist/services/auth.service.d.ts +94 -0
  178. package/dist/services/auth.service.js +368 -0
  179. package/dist/services/base.service.d.ts +14 -0
  180. package/dist/services/base.service.js +14 -0
  181. package/dist/services/billing/payment.service.d.ts +55 -0
  182. package/dist/services/billing/payment.service.js +368 -0
  183. package/dist/services/billing/subscription.service.d.ts +37 -0
  184. package/dist/services/billing/subscription.service.js +654 -0
  185. package/dist/services/billing/usage.service.d.ts +27 -0
  186. package/dist/services/billing/usage.service.js +77 -0
  187. package/dist/services/chat.service.d.ts +127 -0
  188. package/dist/services/chat.service.js +182 -0
  189. package/dist/services/content/copilot.service.d.ts +113 -0
  190. package/dist/services/content/copilot.service.js +453 -0
  191. package/dist/services/content/flashcard-progress.service.d.ts +159 -0
  192. package/dist/services/content/flashcard-progress.service.js +432 -0
  193. package/dist/services/content/flashcard.service.d.ts +140 -0
  194. package/dist/services/content/flashcard.service.js +326 -0
  195. package/dist/services/content/media-analysis.service.d.ts +23 -0
  196. package/dist/services/content/media-analysis.service.js +404 -0
  197. package/dist/services/content/podcast.service.d.ts +267 -0
  198. package/dist/services/content/podcast.service.js +653 -0
  199. package/dist/services/content/worksheet-content.service.d.ts +37 -0
  200. package/dist/services/content/worksheet-content.service.js +84 -0
  201. package/dist/services/content/worksheet-content.service.test.d.ts +1 -0
  202. package/dist/services/content/worksheet-content.service.test.js +69 -0
  203. package/dist/services/content/worksheet-generation.service.d.ts +91 -0
  204. package/dist/services/content/worksheet-generation.service.js +95 -0
  205. package/dist/services/content/worksheet-generation.service.test.d.ts +1 -0
  206. package/dist/services/content/worksheet-generation.service.test.js +20 -0
  207. package/dist/services/content/worksheet.service.d.ts +347 -0
  208. package/dist/services/content/worksheet.service.js +599 -0
  209. package/dist/services/copilot.service.d.ts +116 -0
  210. package/dist/services/copilot.service.js +447 -0
  211. package/dist/services/flashcard-progress.service.d.ts +2 -2
  212. package/dist/services/flashcard-progress.service.js +3 -2
  213. package/dist/services/flashcard.service.d.ts +140 -0
  214. package/dist/services/flashcard.service.js +325 -0
  215. package/dist/services/invitation.service.d.ts +66 -0
  216. package/dist/services/invitation.service.js +348 -0
  217. package/dist/services/knowledge/knowledge-base.service.d.ts +316 -0
  218. package/dist/services/knowledge/knowledge-base.service.js +544 -0
  219. package/dist/services/knowledge-base.service.d.ts +316 -0
  220. package/dist/services/knowledge-base.service.js +536 -0
  221. package/dist/services/media-analysis.service.d.ts +23 -0
  222. package/dist/services/media-analysis.service.js +384 -0
  223. package/dist/services/member.service.d.ts +36 -0
  224. package/dist/services/member.service.js +193 -0
  225. package/dist/services/members/invitation.service.d.ts +66 -0
  226. package/dist/services/members/invitation.service.js +348 -0
  227. package/dist/services/members/member.service.d.ts +36 -0
  228. package/dist/services/members/member.service.js +193 -0
  229. package/dist/services/note.service.d.ts +55 -0
  230. package/dist/services/note.service.js +111 -0
  231. package/dist/services/notification.service.d.ts +214 -0
  232. package/dist/services/notification.service.js +550 -0
  233. package/dist/services/notification.service.test.d.ts +1 -0
  234. package/dist/services/notification.service.test.js +87 -0
  235. package/dist/services/notifications/notification.service.d.ts +214 -0
  236. package/dist/services/notifications/notification.service.js +550 -0
  237. package/dist/services/notifications/notification.service.test.d.ts +1 -0
  238. package/dist/services/notifications/notification.service.test.js +87 -0
  239. package/dist/services/payment.service.d.ts +55 -0
  240. package/dist/services/payment.service.js +368 -0
  241. package/dist/services/podcast.service.d.ts +267 -0
  242. package/dist/services/podcast.service.js +654 -0
  243. package/dist/services/router-module.service.d.ts +7 -0
  244. package/dist/services/router-module.service.js +10 -0
  245. package/dist/services/study-guide.service.d.ts +18 -0
  246. package/dist/services/study-guide.service.js +65 -0
  247. package/dist/services/subscription.service.d.ts +37 -0
  248. package/dist/services/subscription.service.js +654 -0
  249. package/dist/services/usage-limit-policy.service.d.ts +12 -0
  250. package/dist/services/usage-limit-policy.service.js +22 -0
  251. package/dist/services/usage-limit-policy.service.test.d.ts +1 -0
  252. package/dist/services/usage-limit-policy.service.test.js +46 -0
  253. package/dist/services/usage.service.d.ts +27 -0
  254. package/dist/services/usage.service.js +77 -0
  255. package/dist/services/worksheet-content.service.d.ts +42 -0
  256. package/dist/services/worksheet-content.service.js +84 -0
  257. package/dist/services/worksheet-content.service.test.d.ts +1 -0
  258. package/dist/services/worksheet-content.service.test.js +69 -0
  259. package/dist/services/worksheet-generation.service.d.ts +91 -0
  260. package/dist/services/worksheet-generation.service.js +95 -0
  261. package/dist/services/worksheet-generation.service.test.d.ts +1 -0
  262. package/dist/services/worksheet-generation.service.test.js +20 -0
  263. package/dist/services/worksheet.service.d.ts +385 -0
  264. package/dist/services/worksheet.service.js +596 -0
  265. package/dist/services/workspace/workspace-analytics.service.d.ts +24 -0
  266. package/dist/services/workspace/workspace-analytics.service.js +95 -0
  267. package/dist/services/workspace/workspace-kb.service.d.ts +40 -0
  268. package/dist/services/workspace/workspace-kb.service.js +184 -0
  269. package/dist/services/workspace/workspace.service.d.ts +307 -0
  270. package/dist/services/workspace/workspace.service.js +394 -0
  271. package/dist/services/workspace-analytics.service.d.ts +24 -0
  272. package/dist/services/workspace-analytics.service.js +95 -0
  273. package/dist/services/workspace-kb.service.d.ts +40 -0
  274. package/dist/services/workspace-kb.service.js +184 -0
  275. package/dist/services/workspace-progress.service.d.ts +27 -0
  276. package/dist/services/workspace-progress.service.js +56 -0
  277. package/dist/services/workspace-progress.service.test.d.ts +1 -0
  278. package/dist/services/workspace-progress.service.test.js +49 -0
  279. package/dist/services/workspace.service.d.ts +307 -0
  280. package/dist/services/workspace.service.js +390 -0
  281. package/dist/trpc.js +2 -2
  282. package/package.json +5 -6
  283. package/prisma/migrations/20260509000001_add_knowledge_base/migration.sql +99 -0
  284. package/prisma/migrations/20260509000002_curate_knowledge_base/migration.sql +52 -0
  285. package/prisma/migrations/20260522000000_add_notes/migration.sql +27 -0
  286. package/prisma/migrations/20260524000000_remove_notes/migration.sql +3 -0
  287. package/prisma/schema.prisma +150 -48
  288. package/prisma/seed.mjs +67 -0
  289. package/scripts/debug/README.md +4 -0
  290. package/src/README.md +63 -0
  291. package/src/lib/ai/config.ts +34 -0
  292. package/src/lib/ai/embedding-client.ts +47 -0
  293. package/src/lib/ai/index.ts +62 -0
  294. package/src/lib/ai/inference-backend/client.ts +479 -0
  295. package/src/lib/ai/inference-backend/mocks.ts +171 -0
  296. package/src/lib/ai/inference-backend/types.ts +50 -0
  297. package/src/lib/ai/json-parse.ts +35 -0
  298. package/src/lib/ai/llm-client.ts +31 -0
  299. package/src/lib/ai/mock.ts +12 -0
  300. package/src/lib/ai/types.ts +11 -0
  301. package/src/lib/chunking.ts +81 -0
  302. package/src/lib/curated-kb-seed.ts +164 -0
  303. package/src/lib/email.ts +77 -115
  304. package/src/lib/embeddings.ts +9 -0
  305. package/src/lib/ensure-curated-kb-catalog.ts +60 -0
  306. package/src/lib/env.ts +2 -7
  307. package/src/lib/inference.ts +1 -21
  308. package/src/lib/kb-meta.ts +81 -0
  309. package/src/lib/pdf.ts +23 -0
  310. package/src/lib/workspace-kb.ts +7 -0
  311. package/src/repositories/artifact.repository.ts +55 -0
  312. package/src/repositories/base.repository.ts +19 -0
  313. package/src/repositories/invitation.repository.ts +53 -0
  314. package/src/repositories/notification.repository.ts +53 -0
  315. package/src/repositories/user.repository.ts +44 -0
  316. package/src/repositories/workspace-member.repository.ts +38 -0
  317. package/src/repositories/workspace.repository.ts +89 -0
  318. package/src/routers/_app.ts +4 -0
  319. package/src/routers/admin.ts +124 -692
  320. package/src/routers/annotations.ts +25 -203
  321. package/src/routers/artifactVersions.ts +32 -0
  322. package/src/routers/auth.ts +81 -519
  323. package/src/routers/chat.ts +42 -245
  324. package/src/routers/copilot.ts +41 -666
  325. package/src/routers/flashcards.ts +108 -404
  326. package/src/routers/knowledgeBase.ts +216 -0
  327. package/src/routers/members.ts +60 -782
  328. package/src/routers/notifications.ts +15 -117
  329. package/src/routers/payment.ts +37 -446
  330. package/src/routers/podcast.ts +36 -898
  331. package/src/routers/studyguide.ts +5 -144
  332. package/src/routers/worksheets.ts +171 -735
  333. package/src/routers/workspace.ts +138 -1109
  334. package/src/scripts/purge-deleted-users.ts +2 -2
  335. package/src/server.ts +10 -3
  336. package/src/{lib/activity_human_description.test.ts → services/activity/activity-human-description.service.test.ts} +1 -1
  337. package/src/{lib/activity_log_service.test.ts → services/activity/activity-log.service.test.ts} +1 -1
  338. package/src/{lib/activity_log_service.ts → services/activity/activity-log.service.ts} +2 -2
  339. package/src/services/admin/admin.service.ts +612 -0
  340. package/src/services/ai/ai-session.service.ts +5 -0
  341. package/src/services/artifacts/annotation.service.ts +189 -0
  342. package/src/services/artifacts/artifact-version.service.ts +151 -0
  343. package/src/services/artifacts/chat.service.ts +197 -0
  344. package/src/services/artifacts/study-guide.service.ts +72 -0
  345. package/src/services/auth/auth.service.ts +473 -0
  346. package/src/services/base.service.ts +19 -0
  347. package/src/services/billing/payment.service.ts +436 -0
  348. package/src/{lib/subscription_service.ts → services/billing/subscription.service.ts} +5 -5
  349. package/src/{lib/usage_service.ts → services/billing/usage.service.ts} +32 -12
  350. package/src/services/content/copilot.service.ts +596 -0
  351. package/src/services/{flashcard-progress.service.ts → content/flashcard-progress.service.ts} +6 -3
  352. package/src/services/content/flashcard.service.ts +394 -0
  353. package/src/services/content/media-analysis.service.ts +556 -0
  354. package/src/services/content/podcast.service.ts +777 -0
  355. package/src/services/content/worksheet-content.service.test.ts +83 -0
  356. package/src/services/content/worksheet-content.service.ts +117 -0
  357. package/src/{lib/worksheet-generation.test.ts → services/content/worksheet-generation.service.test.ts} +1 -1
  358. package/src/services/content/worksheet.service.ts +751 -0
  359. package/src/services/knowledge/knowledge-base.service.ts +705 -0
  360. package/src/services/members/invitation.service.ts +427 -0
  361. package/src/services/members/member.service.ts +241 -0
  362. package/src/{lib/notification-service.test.ts → services/notifications/notification.service.test.ts} +2 -2
  363. package/src/{lib/notification-service.ts → services/notifications/notification.service.ts} +102 -1
  364. package/src/services/workspace/workspace-analytics.service.ts +107 -0
  365. package/src/services/workspace/workspace-kb.service.ts +273 -0
  366. package/src/services/workspace/workspace.service.ts +481 -0
  367. package/src/trpc.ts +2 -2
  368. package/src/lib/ai-session.ts +0 -704
  369. package/src/lib/workspace-access.ts +0 -13
  370. /package/{check-difficulty.cjs → scripts/debug/check-difficulty.cjs} +0 -0
  371. /package/{check-questions.cjs → scripts/debug/check-questions.cjs} +0 -0
  372. /package/{db-summary.cjs → scripts/debug/db-summary.cjs} +0 -0
  373. /package/{mcq-test.cjs → scripts/debug/mcq-test.cjs} +0 -0
  374. /package/{test-generate.js → scripts/debug/test-generate.js} +0 -0
  375. /package/{test-ratio.cjs → scripts/debug/test-ratio.cjs} +0 -0
  376. /package/{zod-test.cjs → scripts/debug/zod-test.cjs} +0 -0
  377. /package/src/{lib/activity_human_description.ts → services/activity/activity-human-description.service.ts} +0 -0
  378. /package/src/{lib/worksheet-generation.ts → services/content/worksheet-generation.service.ts} +0 -0
@@ -0,0 +1,394 @@
1
+ import { TRPCError } from '@trpc/server';
2
+ import { BaseService } from '../base.service.js';
3
+ import { ArtifactType } from '../../lib/constants.js';
4
+ import { supabaseClient } from '../../lib/storage.js';
5
+ import PusherService from '../../lib/pusher.js';
6
+ import { ai } from '../../lib/ai/index.js';
7
+ import { workspaceKbService } from './workspace-kb.service.js';
8
+ import { getUserStorageLimit } from '../billing/subscription.service.js';
9
+ import { notifyWorkspaceDeleted } from '../notifications/notification.service.js';
10
+ export class WorkspaceService extends BaseService {
11
+ constructor(db) {
12
+ super(db);
13
+ }
14
+ async list(userId, parentId) {
15
+ const workspaces = await this.db.workspace.findMany({
16
+ where: { ownerId: userId, folderId: parentId },
17
+ orderBy: { updatedAt: 'desc' },
18
+ });
19
+ const folders = await this.db.folder.findMany({
20
+ where: { ownerId: userId, parentId },
21
+ });
22
+ return { workspaces, folders };
23
+ }
24
+ async getTree(userId) {
25
+ const allFolders = await this.db.folder.findMany({
26
+ where: { ownerId: userId },
27
+ orderBy: { updatedAt: 'desc' },
28
+ });
29
+ const allWorkspaces = await this.db.workspace.findMany({
30
+ where: { ownerId: userId },
31
+ include: {
32
+ uploads: {
33
+ select: {
34
+ id: true,
35
+ name: true,
36
+ mimeType: true,
37
+ createdAt: true,
38
+ },
39
+ },
40
+ },
41
+ orderBy: { updatedAt: 'desc' },
42
+ });
43
+ return { folders: allFolders, workspaces: allWorkspaces };
44
+ }
45
+ async create(userId, input) {
46
+ const ws = await this.db.workspace.create({
47
+ data: {
48
+ title: input.name,
49
+ description: input.description,
50
+ ownerId: userId,
51
+ folderId: input.parentId ?? null,
52
+ ...(input.markerColor !== undefined ? { markerColor: input.markerColor } : {}),
53
+ artifacts: {
54
+ create: {
55
+ type: ArtifactType.FLASHCARD_SET,
56
+ title: 'New Flashcard Set',
57
+ },
58
+ createMany: {
59
+ data: [
60
+ { type: ArtifactType.WORKSHEET, title: 'Worksheet 1' },
61
+ { type: ArtifactType.WORKSHEET, title: 'Worksheet 2' },
62
+ ],
63
+ },
64
+ },
65
+ },
66
+ });
67
+ await ai.backend.initSession(ws.id, userId).catch((err) => {
68
+ this.logger.error('Failed to init AI session on workspace creation:', err);
69
+ });
70
+ await workspaceKbService.ensureWorkspaceKb(ws.id, userId, input.name).catch((err) => {
71
+ this.logger.error('Failed to create workspace knowledge base:', err);
72
+ });
73
+ await PusherService.emitLibraryUpdate(userId);
74
+ return ws;
75
+ }
76
+ async createFolder(userId, input) {
77
+ const folder = await this.db.folder.create({
78
+ data: {
79
+ name: input.name,
80
+ ownerId: userId,
81
+ color: input.color ?? '#9D00FF',
82
+ parentId: input.parentId ?? null,
83
+ },
84
+ });
85
+ await PusherService.emitLibraryUpdate(userId);
86
+ return folder;
87
+ }
88
+ async updateFolder(userId, input) {
89
+ const folder = await this.db.folder.update({
90
+ where: { id: input.id },
91
+ data: { name: input.name, markerColor: input.markerColor },
92
+ });
93
+ await PusherService.emitLibraryUpdate(userId);
94
+ return folder;
95
+ }
96
+ async deleteFolder(userId, id) {
97
+ const folder = await this.db.folder.delete({ where: { id } });
98
+ await PusherService.emitLibraryUpdate(userId);
99
+ return folder;
100
+ }
101
+ async get(userId, id) {
102
+ const ws = await this.db.workspace.findFirst({
103
+ where: { id, ownerId: userId },
104
+ include: {
105
+ artifacts: true,
106
+ folder: true,
107
+ uploads: true,
108
+ },
109
+ });
110
+ if (!ws)
111
+ throw new TRPCError({ code: 'NOT_FOUND' });
112
+ return ws;
113
+ }
114
+ async getStats(userId) {
115
+ const workspaces = await this.db.workspace.findMany({
116
+ where: {
117
+ OR: [{ ownerId: userId }, { sharedWith: { some: { id: userId } } }],
118
+ },
119
+ });
120
+ const folders = await this.db.folder.findMany({
121
+ where: { OR: [{ ownerId: userId }] },
122
+ });
123
+ const lastUpdated = await this.db.workspace.findFirst({
124
+ where: {
125
+ OR: [{ ownerId: userId }, { sharedWith: { some: { id: userId } } }],
126
+ },
127
+ orderBy: { updatedAt: 'desc' },
128
+ });
129
+ const spaceLeft = await this.db.fileAsset.aggregate({
130
+ where: { workspaceId: { in: workspaces.map((ws) => ws.id) }, userId },
131
+ _sum: { size: true },
132
+ });
133
+ const storageLimit = await getUserStorageLimit(userId);
134
+ return {
135
+ workspaces: workspaces.length,
136
+ folders: folders.length,
137
+ lastUpdated: lastUpdated?.updatedAt,
138
+ spaceUsed: spaceLeft._sum?.size ?? 0,
139
+ spaceTotal: storageLimit,
140
+ };
141
+ }
142
+ async update(userId, input) {
143
+ const existed = await this.db.workspace.findFirst({
144
+ where: { id: input.id, ownerId: userId },
145
+ });
146
+ if (!existed)
147
+ throw new TRPCError({ code: 'NOT_FOUND' });
148
+ const updated = await this.db.workspace.update({
149
+ where: { id: input.id },
150
+ data: {
151
+ title: input.name ?? existed.title,
152
+ description: input.description,
153
+ markerColor: input.markerColor !== undefined ? input.markerColor : existed.markerColor,
154
+ icon: input.icon ?? existed.icon,
155
+ },
156
+ });
157
+ await PusherService.emitLibraryUpdate(userId);
158
+ return updated;
159
+ }
160
+ async delete(userId, id) {
161
+ const workspaceToDelete = await this.db.workspace.findFirst({
162
+ where: { id, ownerId: userId },
163
+ select: {
164
+ id: true,
165
+ title: true,
166
+ ownerId: true,
167
+ members: { select: { userId: true } },
168
+ },
169
+ });
170
+ if (!workspaceToDelete)
171
+ throw new TRPCError({ code: 'NOT_FOUND' });
172
+ const actor = await this.db.user.findUnique({
173
+ where: { id: userId },
174
+ select: { name: true, email: true },
175
+ });
176
+ const actorName = actor?.name || actor?.email || 'A user';
177
+ await notifyWorkspaceDeleted(this.db, {
178
+ recipientUserIds: workspaceToDelete.members.map((m) => m.userId),
179
+ actorUserId: userId,
180
+ actorName,
181
+ workspaceId: workspaceToDelete.id,
182
+ workspaceTitle: workspaceToDelete.title,
183
+ });
184
+ const deleted = await this.db.workspace.deleteMany({
185
+ where: { id, ownerId: userId },
186
+ });
187
+ if (deleted.count === 0)
188
+ throw new TRPCError({ code: 'NOT_FOUND' });
189
+ await PusherService.emitLibraryUpdate(userId);
190
+ return true;
191
+ }
192
+ async getFolderInformation(userId, id) {
193
+ const folder = await this.db.folder.findFirst({
194
+ where: { id, ownerId: userId },
195
+ });
196
+ if (!folder)
197
+ throw new TRPCError({ code: 'NOT_FOUND' });
198
+ const parents = [];
199
+ let current = folder;
200
+ while (current.parentId) {
201
+ const parent = await this.db.folder.findFirst({
202
+ where: { id: current.parentId, ownerId: userId },
203
+ });
204
+ if (!parent)
205
+ break;
206
+ parents.push(parent);
207
+ current = parent;
208
+ }
209
+ return { folder, parents };
210
+ }
211
+ async getSharedWith(userId, id) {
212
+ const user = await this.db.user.findFirst({ where: { id: userId } });
213
+ if (!user || !user.email)
214
+ throw new TRPCError({ code: 'NOT_FOUND' });
215
+ const sharedWith = await this.db.workspace.findMany({
216
+ where: { members: { some: { userId } } },
217
+ });
218
+ const invitations = await this.db.workspaceInvitation.findMany({
219
+ where: { email: user.email, acceptedAt: null },
220
+ include: { workspace: true },
221
+ });
222
+ return { shared: sharedWith, invitations };
223
+ }
224
+ async uploadFiles(userId, input) {
225
+ const ws = await this.db.workspace.findFirst({
226
+ where: { id: input.id, ownerId: userId },
227
+ });
228
+ if (!ws)
229
+ throw new TRPCError({ code: 'NOT_FOUND' });
230
+ const workspaces = await this.db.workspace.findMany({
231
+ where: {
232
+ OR: [{ ownerId: userId }, { sharedWith: { some: { id: userId } } }],
233
+ },
234
+ });
235
+ const spaceUsed = await this.db.fileAsset.aggregate({
236
+ where: { workspaceId: { in: workspaces.map((w) => w.id) }, userId },
237
+ _sum: { size: true },
238
+ });
239
+ const storageLimit = await getUserStorageLimit(userId);
240
+ const totalSize = input.files.reduce((acc, file) => acc + file.size, 0);
241
+ if ((spaceUsed._sum?.size ?? 0) + totalSize > storageLimit) {
242
+ this.logger.warn(`Storage limit exceeded for user ${userId}. Used: ${spaceUsed._sum?.size}, Tried to upload: ${totalSize}, Limit: ${storageLimit}`);
243
+ throw new TRPCError({
244
+ code: 'FORBIDDEN',
245
+ message: `Storage limit exceeded. Maximum allowed storage is ${(storageLimit / (1024 * 1024 * 1024)).toFixed(1)}GB.`,
246
+ });
247
+ }
248
+ const results = [];
249
+ for (const file of input.files) {
250
+ const record = await this.db.fileAsset.create({
251
+ data: {
252
+ userId,
253
+ name: file.filename,
254
+ mimeType: file.contentType,
255
+ size: file.size,
256
+ workspaceId: input.id,
257
+ },
258
+ });
259
+ const objectKey = `${userId}/${record.id}-${file.filename}`;
260
+ const { data: signedUrlData, error: signedUrlError } = await supabaseClient.storage
261
+ .from('media')
262
+ .createSignedUploadUrl(objectKey);
263
+ if (signedUrlError) {
264
+ throw new TRPCError({
265
+ code: 'INTERNAL_SERVER_ERROR',
266
+ message: `Failed to upload file`,
267
+ });
268
+ }
269
+ await this.db.fileAsset.update({
270
+ where: { id: record.id },
271
+ data: {
272
+ bucket: 'media',
273
+ objectKey,
274
+ },
275
+ });
276
+ results.push({
277
+ fileId: record.id,
278
+ uploadUrl: signedUrlData.signedUrl,
279
+ });
280
+ }
281
+ return results;
282
+ }
283
+ async deleteFiles(userId, input) {
284
+ const files = await this.db.fileAsset.findMany({
285
+ where: {
286
+ id: { in: input.fileId },
287
+ workspaceId: input.id,
288
+ userId,
289
+ },
290
+ });
291
+ for (const file of files) {
292
+ if (file.bucket && file.objectKey) {
293
+ supabaseClient.storage
294
+ .from(file.bucket)
295
+ .remove([file.objectKey])
296
+ .catch((err) => {
297
+ this.logger.error(`Error deleting file ${file.objectKey} from bucket ${file.bucket}:`, err);
298
+ });
299
+ }
300
+ }
301
+ await this.db.fileAsset.deleteMany({
302
+ where: {
303
+ id: { in: input.fileId },
304
+ workspaceId: input.id,
305
+ userId,
306
+ },
307
+ });
308
+ return true;
309
+ }
310
+ async getFileUploadUrl(userId, input) {
311
+ const workspaces = await this.db.workspace.findMany({
312
+ where: {
313
+ OR: [{ ownerId: userId }, { sharedWith: { some: { id: userId } } }],
314
+ },
315
+ });
316
+ const spaceUsed = await this.db.fileAsset.aggregate({
317
+ where: { workspaceId: { in: workspaces.map((w) => w.id) }, userId },
318
+ _sum: { size: true },
319
+ });
320
+ const storageLimit = await getUserStorageLimit(userId);
321
+ if ((spaceUsed._sum?.size ?? 0) + input.size > storageLimit) {
322
+ this.logger.warn(`Storage limit exceeded for user ${userId}. Used: ${spaceUsed._sum?.size}, Tried to upload: ${input.size}, Limit: ${storageLimit}`);
323
+ throw new TRPCError({
324
+ code: 'FORBIDDEN',
325
+ message: `Storage limit exceeded. Maximum allowed storage is ${(storageLimit / (1024 * 1024 * 1024)).toFixed(1)}GB.`,
326
+ });
327
+ }
328
+ const objectKey = `workspace_${userId}/${input.workspaceId}-file_${input.filename}`;
329
+ const fileAsset = await this.db.fileAsset.create({
330
+ data: {
331
+ workspaceId: input.workspaceId,
332
+ name: input.filename,
333
+ mimeType: input.contentType,
334
+ size: input.size,
335
+ userId,
336
+ bucket: 'media',
337
+ objectKey,
338
+ },
339
+ });
340
+ const { data: signedUrlData, error: signedUrlError } = await supabaseClient.storage
341
+ .from('media')
342
+ .createSignedUploadUrl(objectKey, { upsert: true });
343
+ if (signedUrlError) {
344
+ this.logger.error('Signed upload URL error:', signedUrlError);
345
+ throw new TRPCError({
346
+ code: 'INTERNAL_SERVER_ERROR',
347
+ message: `Failed to create upload URL: ${signedUrlError.message}`,
348
+ });
349
+ }
350
+ await this.db.workspace.update({
351
+ where: { id: input.workspaceId },
352
+ data: { needsAnalysis: true },
353
+ });
354
+ return {
355
+ fileId: fileAsset.id,
356
+ uploadUrl: signedUrlData.signedUrl,
357
+ };
358
+ }
359
+ async search(userId, input) {
360
+ const { query, color } = input;
361
+ const workspaces = await this.db.workspace.findMany({
362
+ where: {
363
+ ownerId: userId,
364
+ markerColor: color || undefined,
365
+ ...(query
366
+ ? {
367
+ OR: [
368
+ { title: { contains: query, mode: 'insensitive' } },
369
+ { description: { contains: query, mode: 'insensitive' } },
370
+ ],
371
+ }
372
+ : {}),
373
+ },
374
+ orderBy: { updatedAt: 'desc' },
375
+ take: input.limit,
376
+ });
377
+ const folders = await this.db.folder.findMany({
378
+ where: {
379
+ ownerId: userId,
380
+ markerColor: color || undefined,
381
+ ...(query ? { name: { contains: query, mode: 'insensitive' } } : {}),
382
+ },
383
+ orderBy: { updatedAt: 'desc' },
384
+ take: input.limit,
385
+ });
386
+ const results = [
387
+ ...workspaces.map((w) => ({ ...w, type: 'workspace' })),
388
+ ...folders.map((f) => ({ ...f, type: 'folder', title: f.name })),
389
+ ]
390
+ .sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime())
391
+ .slice(0, input.limit);
392
+ return results;
393
+ }
394
+ }
@@ -0,0 +1,24 @@
1
+ import type { PrismaClient } from '@prisma/client';
2
+ import { BaseService } from './base.service.js';
3
+ /**
4
+ * Study analytics: streaks, weekly activity, flashcard mastery, worksheet
5
+ * accuracy. Behavior-preserving extraction from `workspace.getStudyAnalytics`.
6
+ */
7
+ export declare class WorkspaceAnalyticsService extends BaseService {
8
+ constructor(db: PrismaClient);
9
+ getStudyAnalytics(userId: string): Promise<{
10
+ streak: number;
11
+ totalStudyDays: number;
12
+ weeklyActivity: boolean[];
13
+ flashcards: {
14
+ total: number;
15
+ mastered: number;
16
+ dueForReview: number;
17
+ };
18
+ worksheets: {
19
+ completed: number;
20
+ correct: number;
21
+ accuracy: number;
22
+ };
23
+ }>;
24
+ }
@@ -0,0 +1,95 @@
1
+ import { BaseService } from './base.service.js';
2
+ /**
3
+ * Study analytics: streaks, weekly activity, flashcard mastery, worksheet
4
+ * accuracy. Behavior-preserving extraction from `workspace.getStudyAnalytics`.
5
+ */
6
+ export class WorkspaceAnalyticsService extends BaseService {
7
+ constructor(db) {
8
+ super(db);
9
+ }
10
+ async getStudyAnalytics(userId) {
11
+ const flashcardProgress = await this.db.flashcardProgress.findMany({
12
+ where: { userId },
13
+ select: { lastStudiedAt: true },
14
+ });
15
+ const worksheetProgress = await this.db.worksheetQuestionProgress.findMany({
16
+ where: { userId },
17
+ select: { updatedAt: true, completedAt: true },
18
+ });
19
+ const studyDays = new Set();
20
+ for (const fp of flashcardProgress) {
21
+ if (fp.lastStudiedAt) {
22
+ studyDays.add(fp.lastStudiedAt.toISOString().split('T')[0]);
23
+ }
24
+ }
25
+ for (const wp of worksheetProgress) {
26
+ if (wp.completedAt) {
27
+ studyDays.add(wp.completedAt.toISOString().split('T')[0]);
28
+ }
29
+ else {
30
+ studyDays.add(wp.updatedAt.toISOString().split('T')[0]);
31
+ }
32
+ }
33
+ const sortedDays = [...studyDays].sort().reverse();
34
+ let streak = 0;
35
+ if (sortedDays.length > 0) {
36
+ const today = new Date();
37
+ today.setHours(0, 0, 0, 0);
38
+ const yesterday = new Date(today);
39
+ yesterday.setDate(yesterday.getDate() - 1);
40
+ const todayStr = today.toISOString().split('T')[0];
41
+ const yesterdayStr = yesterday.toISOString().split('T')[0];
42
+ if (sortedDays[0] === todayStr || sortedDays[0] === yesterdayStr) {
43
+ streak = 1;
44
+ for (let i = 1; i < sortedDays.length; i++) {
45
+ const current = new Date(sortedDays[i - 1]);
46
+ const prev = new Date(sortedDays[i]);
47
+ const diffDays = (current.getTime() - prev.getTime()) / (1000 * 60 * 60 * 24);
48
+ if (diffDays === 1) {
49
+ streak++;
50
+ }
51
+ else {
52
+ break;
53
+ }
54
+ }
55
+ }
56
+ }
57
+ const weeklyActivity = [];
58
+ const today = new Date();
59
+ today.setHours(0, 0, 0, 0);
60
+ for (let i = 6; i >= 0; i--) {
61
+ const d = new Date(today);
62
+ d.setDate(d.getDate() - i);
63
+ const dayStr = d.toISOString().split('T')[0];
64
+ weeklyActivity.push(studyDays.has(dayStr));
65
+ }
66
+ const totalCards = await this.db.flashcardProgress.count({ where: { userId } });
67
+ const masteredCards = await this.db.flashcardProgress.count({
68
+ where: { userId, masteryLevel: { gte: 80 } },
69
+ });
70
+ const dueCards = await this.db.flashcardProgress.count({
71
+ where: { userId, nextReviewAt: { lte: new Date() } },
72
+ });
73
+ const completedQuestions = await this.db.worksheetQuestionProgress.count({
74
+ where: { userId, completedAt: { not: null } },
75
+ });
76
+ const correctQuestions = await this.db.worksheetQuestionProgress.count({
77
+ where: { userId, correct: true },
78
+ });
79
+ return {
80
+ streak,
81
+ totalStudyDays: studyDays.size,
82
+ weeklyActivity,
83
+ flashcards: {
84
+ total: totalCards,
85
+ mastered: masteredCards,
86
+ dueForReview: dueCards,
87
+ },
88
+ worksheets: {
89
+ completed: completedQuestions,
90
+ correct: correctQuestions,
91
+ accuracy: completedQuestions > 0 ? Math.round((correctQuestions / completedQuestions) * 100) : 0,
92
+ },
93
+ };
94
+ }
95
+ }
@@ -0,0 +1,40 @@
1
+ import type { PrismaClient } from '@prisma/client';
2
+ import { BaseService } from './base.service.js';
3
+ export interface WorkspaceKbMatch {
4
+ chunkId: string;
5
+ documentId: string;
6
+ documentName: string;
7
+ chunkIndex: number;
8
+ content: string;
9
+ score: number;
10
+ }
11
+ export declare class WorkspaceKbService extends BaseService {
12
+ constructor(db: PrismaClient);
13
+ /** Returns the single auto-managed KB for a workspace, creating it if needed. */
14
+ ensureWorkspaceKb(workspaceId: string, userId: string, title?: string): Promise<{
15
+ meta: import("@prisma/client/runtime/library").JsonValue | null;
16
+ name: string;
17
+ id: string;
18
+ createdAt: Date;
19
+ updatedAt: Date;
20
+ createdById: string | null;
21
+ description: string | null;
22
+ status: import("@prisma/client").$Enums.KnowledgeBaseStatus;
23
+ isCurated: boolean;
24
+ embeddingModel: string;
25
+ embeddingDim: number;
26
+ }>;
27
+ getWorkspaceKbId(workspaceId: string): Promise<string | null>;
28
+ indexText(workspaceId: string, userId: string, input: {
29
+ sourceType: string;
30
+ sourceId: string;
31
+ name: string;
32
+ text: string;
33
+ }): Promise<void>;
34
+ /** Fire-and-forget indexing — never blocks the caller. */
35
+ indexTextAsync(workspaceId: string, userId: string, input: Parameters<WorkspaceKbService['indexText']>[2]): void;
36
+ search(workspaceId: string, query: string, topK?: number): Promise<WorkspaceKbMatch[]>;
37
+ formatContext(matches: WorkspaceKbMatch[]): string;
38
+ retrieveContext(workspaceId: string, query: string, topK?: number): Promise<string>;
39
+ }
40
+ export declare const workspaceKbService: WorkspaceKbService;