@goscribe/server 1.5.0 → 1.7.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 (321) hide show
  1. package/dist/context.d.ts +14 -1
  2. package/dist/context.js +23 -2
  3. package/dist/generated/prisma/client.d.ts +224 -0
  4. package/dist/generated/prisma/client.js +34 -0
  5. package/dist/generated/prisma/commonInputTypes.d.ts +941 -0
  6. package/dist/generated/prisma/commonInputTypes.js +10 -0
  7. package/dist/generated/prisma/enums.d.ts +67 -0
  8. package/dist/generated/prisma/enums.js +66 -0
  9. package/dist/generated/prisma/internal/class.d.ts +539 -0
  10. package/dist/generated/prisma/internal/class.js +49 -0
  11. package/dist/generated/prisma/internal/prismaNamespace.d.ts +3924 -0
  12. package/dist/generated/prisma/internal/prismaNamespace.js +557 -0
  13. package/dist/generated/prisma/models/ActivityLog.d.ts +1847 -0
  14. package/dist/generated/prisma/models/ActivityLog.js +1 -0
  15. package/dist/generated/prisma/models/Artifact.d.ts +2345 -0
  16. package/dist/generated/prisma/models/Artifact.js +1 -0
  17. package/dist/generated/prisma/models/ArtifactVersion.d.ts +1550 -0
  18. package/dist/generated/prisma/models/ArtifactVersion.js +1 -0
  19. package/dist/generated/prisma/models/Channel.d.ts +1257 -0
  20. package/dist/generated/prisma/models/Channel.js +1 -0
  21. package/dist/generated/prisma/models/Chat.d.ts +1339 -0
  22. package/dist/generated/prisma/models/Chat.js +1 -0
  23. package/dist/generated/prisma/models/CopilotConversation.d.ts +1450 -0
  24. package/dist/generated/prisma/models/CopilotConversation.js +1 -0
  25. package/dist/generated/prisma/models/CopilotMessage.d.ts +1179 -0
  26. package/dist/generated/prisma/models/CopilotMessage.js +1 -0
  27. package/dist/generated/prisma/models/FileAsset.d.ts +1832 -0
  28. package/dist/generated/prisma/models/FileAsset.js +1 -0
  29. package/dist/generated/prisma/models/Flashcard.d.ts +1460 -0
  30. package/dist/generated/prisma/models/Flashcard.js +1 -0
  31. package/dist/generated/prisma/models/FlashcardProgress.d.ts +1782 -0
  32. package/dist/generated/prisma/models/FlashcardProgress.js +1 -0
  33. package/dist/generated/prisma/models/Folder.d.ts +1685 -0
  34. package/dist/generated/prisma/models/Folder.js +1 -0
  35. package/dist/generated/prisma/models/IdempotencyRecord.d.ts +1319 -0
  36. package/dist/generated/prisma/models/IdempotencyRecord.js +1 -0
  37. package/dist/generated/prisma/models/Invoice.d.ts +1586 -0
  38. package/dist/generated/prisma/models/Invoice.js +1 -0
  39. package/dist/generated/prisma/models/KnowledgeBase.d.ts +1721 -0
  40. package/dist/generated/prisma/models/KnowledgeBase.js +1 -0
  41. package/dist/generated/prisma/models/KnowledgeBaseChunk.d.ts +1333 -0
  42. package/dist/generated/prisma/models/KnowledgeBaseChunk.js +1 -0
  43. package/dist/generated/prisma/models/KnowledgeBaseDocument.d.ts +1695 -0
  44. package/dist/generated/prisma/models/KnowledgeBaseDocument.js +1 -0
  45. package/dist/generated/prisma/models/Notification.d.ts +1992 -0
  46. package/dist/generated/prisma/models/Notification.js +1 -0
  47. package/dist/generated/prisma/models/PasswordResetToken.d.ts +1210 -0
  48. package/dist/generated/prisma/models/PasswordResetToken.js +1 -0
  49. package/dist/generated/prisma/models/Plan.d.ts +1431 -0
  50. package/dist/generated/prisma/models/Plan.js +1 -0
  51. package/dist/generated/prisma/models/PlanLimit.d.ts +1328 -0
  52. package/dist/generated/prisma/models/PlanLimit.js +1 -0
  53. package/dist/generated/prisma/models/PodcastSegment.d.ts +1564 -0
  54. package/dist/generated/prisma/models/PodcastSegment.js +1 -0
  55. package/dist/generated/prisma/models/ResourcePrice.d.ts +1008 -0
  56. package/dist/generated/prisma/models/ResourcePrice.js +1 -0
  57. package/dist/generated/prisma/models/Role.d.ts +1065 -0
  58. package/dist/generated/prisma/models/Role.js +1 -0
  59. package/dist/generated/prisma/models/Session.d.ts +1105 -0
  60. package/dist/generated/prisma/models/Session.js +1 -0
  61. package/dist/generated/prisma/models/StripeEvent.d.ts +1081 -0
  62. package/dist/generated/prisma/models/StripeEvent.js +1 -0
  63. package/dist/generated/prisma/models/StudyGuideComment.d.ts +1321 -0
  64. package/dist/generated/prisma/models/StudyGuideComment.js +1 -0
  65. package/dist/generated/prisma/models/StudyGuideHighlight.d.ts +1629 -0
  66. package/dist/generated/prisma/models/StudyGuideHighlight.js +1 -0
  67. package/dist/generated/prisma/models/Subscription.d.ts +1677 -0
  68. package/dist/generated/prisma/models/Subscription.js +1 -0
  69. package/dist/generated/prisma/models/User.d.ts +7559 -0
  70. package/dist/generated/prisma/models/User.js +1 -0
  71. package/dist/generated/prisma/models/UserCredit.d.ts +1249 -0
  72. package/dist/generated/prisma/models/UserCredit.js +1 -0
  73. package/dist/generated/prisma/models/VerificationToken.d.ts +946 -0
  74. package/dist/generated/prisma/models/VerificationToken.js +1 -0
  75. package/dist/generated/prisma/models/WorksheetPreset.d.ts +1433 -0
  76. package/dist/generated/prisma/models/WorksheetPreset.js +1 -0
  77. package/dist/generated/prisma/models/WorksheetQuestion.d.ts +1491 -0
  78. package/dist/generated/prisma/models/WorksheetQuestion.js +1 -0
  79. package/dist/generated/prisma/models/WorksheetQuestionProgress.d.ts +1620 -0
  80. package/dist/generated/prisma/models/WorksheetQuestionProgress.js +1 -0
  81. package/dist/generated/prisma/models/Workspace.d.ts +3620 -0
  82. package/dist/generated/prisma/models/Workspace.js +1 -0
  83. package/dist/generated/prisma/models/WorkspaceInvitation.d.ts +1490 -0
  84. package/dist/generated/prisma/models/WorkspaceInvitation.js +1 -0
  85. package/dist/generated/prisma/models/WorkspaceKnowledgeBase.d.ts +1410 -0
  86. package/dist/generated/prisma/models/WorkspaceKnowledgeBase.js +1 -0
  87. package/dist/generated/prisma/models/WorkspaceMember.d.ts +1326 -0
  88. package/dist/generated/prisma/models/WorkspaceMember.js +1 -0
  89. package/dist/generated/prisma/models.d.ts +39 -0
  90. package/dist/generated/prisma/models.js +1 -0
  91. package/dist/lib/ai/index.d.ts +3 -2
  92. package/dist/lib/ai/index.js +3 -2
  93. package/dist/lib/ai/llm-client.d.ts +1 -0
  94. package/dist/lib/ai/llm-client.js +17 -0
  95. package/dist/routers/_app.d.ts +40 -80
  96. package/dist/routers/auth.js +1 -1
  97. package/dist/routers/flashcards.d.ts +12 -1
  98. package/dist/routers/payment.d.ts +1 -12
  99. package/dist/routers/workspace.d.ts +27 -67
  100. package/dist/routers/workspace.js +1 -0
  101. package/dist/services/billing/payment.service.d.ts +1 -12
  102. package/dist/services/billing/payment.service.js +3 -6
  103. package/dist/services/billing/usage.service.d.ts +30 -10
  104. package/dist/services/billing/usage.service.js +87 -15
  105. package/dist/services/content/copilot.service.js +15 -29
  106. package/dist/services/content/flashcard-progress.service.js +9 -9
  107. package/dist/services/content/flashcard.service.d.ts +45 -1
  108. package/dist/services/content/flashcard.service.js +81 -68
  109. package/dist/services/content/media-analysis.service.js +27 -27
  110. package/dist/services/content/worksheet-generation.service.test.js +2 -2
  111. package/dist/services/workspace/workspace.service.d.ts +23 -67
  112. package/dist/services/workspace/workspace.service.js +69 -62
  113. package/dist/src/context.d.ts +27 -0
  114. package/dist/src/context.js +33 -0
  115. package/dist/src/index.d.ts +3 -0
  116. package/dist/src/index.js +1 -0
  117. package/dist/src/lib/ai/config.d.ts +20 -0
  118. package/dist/src/lib/ai/config.js +31 -0
  119. package/dist/src/lib/ai/embedding-client.d.ts +8 -0
  120. package/dist/src/lib/ai/embedding-client.js +30 -0
  121. package/dist/src/lib/ai/index.d.ts +48 -0
  122. package/dist/src/lib/ai/index.js +29 -0
  123. package/dist/src/lib/ai/inference-backend/client.d.ts +28 -0
  124. package/dist/src/lib/ai/inference-backend/client.js +301 -0
  125. package/dist/src/lib/ai/inference-backend/mocks.d.ts +12 -0
  126. package/dist/src/lib/ai/inference-backend/mocks.js +133 -0
  127. package/dist/src/lib/ai/inference-backend/types.d.ts +44 -0
  128. package/dist/src/lib/ai/inference-backend/types.js +1 -0
  129. package/dist/src/lib/ai/json-parse.d.ts +2 -0
  130. package/dist/src/lib/ai/json-parse.js +34 -0
  131. package/dist/src/lib/ai/llm-client.d.ts +7 -0
  132. package/dist/src/lib/ai/llm-client.js +36 -0
  133. package/dist/src/lib/ai/mock.d.ts +2 -0
  134. package/dist/src/lib/ai/mock.js +10 -0
  135. package/dist/src/lib/ai/types.d.ts +9 -0
  136. package/dist/src/lib/ai/types.js +1 -0
  137. package/dist/src/lib/auth.d.ts +36 -0
  138. package/dist/src/lib/auth.js +117 -0
  139. package/dist/src/lib/chunking.d.ts +19 -0
  140. package/dist/src/lib/chunking.js +47 -0
  141. package/dist/src/lib/constants.d.ts +13 -0
  142. package/dist/src/lib/constants.js +12 -0
  143. package/dist/src/lib/curated-kb-seed.d.ts +12 -0
  144. package/dist/src/lib/curated-kb-seed.js +155 -0
  145. package/dist/src/lib/email.d.ts +11 -0
  146. package/dist/src/lib/email.js +152 -0
  147. package/dist/src/lib/embeddings.d.ts +2 -0
  148. package/dist/src/lib/embeddings.js +1 -0
  149. package/dist/src/lib/ensure-curated-kb-catalog.d.ts +6 -0
  150. package/dist/src/lib/ensure-curated-kb-catalog.js +53 -0
  151. package/dist/src/lib/env.d.ts +41 -0
  152. package/dist/src/lib/env.js +57 -0
  153. package/dist/src/lib/errors.d.ts +33 -0
  154. package/dist/src/lib/errors.js +78 -0
  155. package/dist/src/lib/file.d.ts +0 -0
  156. package/dist/src/lib/file.js +1 -0
  157. package/dist/src/lib/inference.d.ts +1 -0
  158. package/dist/src/lib/inference.js +1 -0
  159. package/dist/src/lib/kb-meta.d.ts +8 -0
  160. package/dist/src/lib/kb-meta.js +77 -0
  161. package/dist/src/lib/logger.d.ts +62 -0
  162. package/dist/src/lib/logger.js +364 -0
  163. package/dist/src/lib/pdf.d.ts +11 -0
  164. package/dist/src/lib/pdf.js +11 -0
  165. package/dist/src/lib/prisma.d.ts +3 -0
  166. package/dist/src/lib/prisma.js +15 -0
  167. package/dist/src/lib/pusher.d.ts +38 -0
  168. package/dist/src/lib/pusher.js +170 -0
  169. package/dist/src/lib/retry.d.ts +15 -0
  170. package/dist/src/lib/retry.js +37 -0
  171. package/dist/src/lib/storage.d.ts +11 -0
  172. package/dist/src/lib/storage.js +71 -0
  173. package/dist/src/lib/stripe.d.ts +10 -0
  174. package/dist/src/lib/stripe.js +36 -0
  175. package/dist/src/lib/validation.d.ts +51 -0
  176. package/dist/src/lib/validation.js +64 -0
  177. package/dist/src/lib/workspace-kb.d.ts +5 -0
  178. package/dist/src/lib/workspace-kb.js +7 -0
  179. package/dist/src/repositories/artifact.repository.d.ts +64 -0
  180. package/dist/src/repositories/artifact.repository.js +40 -0
  181. package/dist/src/repositories/base.repository.d.ts +14 -0
  182. package/dist/src/repositories/base.repository.js +14 -0
  183. package/dist/src/repositories/invitation.repository.d.ts +104 -0
  184. package/dist/src/repositories/invitation.repository.js +44 -0
  185. package/dist/src/repositories/notification.repository.d.ts +76 -0
  186. package/dist/src/repositories/notification.repository.js +44 -0
  187. package/dist/src/repositories/user.repository.d.ts +84 -0
  188. package/dist/src/repositories/user.repository.js +37 -0
  189. package/dist/src/repositories/workspace-member.repository.d.ts +35 -0
  190. package/dist/src/repositories/workspace-member.repository.js +31 -0
  191. package/dist/src/repositories/workspace.repository.d.ts +101 -0
  192. package/dist/src/repositories/workspace.repository.js +79 -0
  193. package/dist/src/routers/_app.d.ts +3464 -0
  194. package/dist/src/routers/_app.js +36 -0
  195. package/dist/src/routers/admin.d.ts +358 -0
  196. package/dist/src/routers/admin.js +105 -0
  197. package/dist/src/routers/annotations.d.ts +219 -0
  198. package/dist/src/routers/annotations.js +29 -0
  199. package/dist/src/routers/artifactVersions.d.ts +65 -0
  200. package/dist/src/routers/artifactVersions.js +14 -0
  201. package/dist/src/routers/auth.d.ts +161 -0
  202. package/dist/src/routers/auth.js +97 -0
  203. package/dist/src/routers/chat.d.ts +170 -0
  204. package/dist/src/routers/chat.js +32 -0
  205. package/dist/src/routers/copilot.d.ts +200 -0
  206. package/dist/src/routers/copilot.js +52 -0
  207. package/dist/src/routers/flashcards.d.ts +336 -0
  208. package/dist/src/routers/flashcards.js +93 -0
  209. package/dist/src/routers/knowledgeBase.d.ts +421 -0
  210. package/dist/src/routers/knowledgeBase.js +118 -0
  211. package/dist/src/routers/members.d.ts +169 -0
  212. package/dist/src/routers/members.js +47 -0
  213. package/dist/src/routers/notifications.d.ts +99 -0
  214. package/dist/src/routers/notifications.js +25 -0
  215. package/dist/src/routers/payment.d.ts +80 -0
  216. package/dist/src/routers/payment.js +21 -0
  217. package/dist/src/routers/podcast.d.ts +287 -0
  218. package/dist/src/routers/podcast.js +34 -0
  219. package/dist/src/routers/studyguide.d.ts +36 -0
  220. package/dist/src/routers/studyguide.js +8 -0
  221. package/dist/src/routers/worksheets.d.ts +429 -0
  222. package/dist/src/routers/worksheets.js +139 -0
  223. package/dist/src/routers/workspace.d.ts +563 -0
  224. package/dist/src/routers/workspace.js +104 -0
  225. package/dist/src/scripts/purge-deleted-users.d.ts +1 -0
  226. package/dist/src/scripts/purge-deleted-users.js +148 -0
  227. package/dist/src/server.d.ts +1 -0
  228. package/dist/src/server.js +190 -0
  229. package/dist/src/services/activity/activity-human-description.service.d.ts +13 -0
  230. package/dist/src/services/activity/activity-human-description.service.js +221 -0
  231. package/dist/src/services/activity/activity-human-description.service.test.d.ts +1 -0
  232. package/dist/src/services/activity/activity-human-description.service.test.js +16 -0
  233. package/dist/src/services/activity/activity-log.service.d.ts +87 -0
  234. package/dist/src/services/activity/activity-log.service.js +276 -0
  235. package/dist/src/services/activity/activity-log.service.test.d.ts +1 -0
  236. package/dist/src/services/activity/activity-log.service.test.js +27 -0
  237. package/dist/src/services/admin/admin.service.d.ts +270 -0
  238. package/dist/src/services/admin/admin.service.js +476 -0
  239. package/dist/src/services/ai/ai-session.service.d.ts +5 -0
  240. package/dist/src/services/ai/ai-session.service.js +4 -0
  241. package/dist/src/services/artifacts/annotation.service.d.ts +177 -0
  242. package/dist/src/services/artifacts/annotation.service.js +154 -0
  243. package/dist/src/services/artifacts/artifact-version.service.d.ts +38 -0
  244. package/dist/src/services/artifacts/artifact-version.service.js +129 -0
  245. package/dist/src/services/artifacts/chat.service.d.ts +127 -0
  246. package/dist/src/services/artifacts/chat.service.js +182 -0
  247. package/dist/src/services/artifacts/study-guide.service.d.ts +18 -0
  248. package/dist/src/services/artifacts/study-guide.service.js +65 -0
  249. package/dist/src/services/auth/auth.service.d.ts +94 -0
  250. package/dist/src/services/auth/auth.service.js +368 -0
  251. package/dist/src/services/base.service.d.ts +14 -0
  252. package/dist/src/services/base.service.js +14 -0
  253. package/dist/src/services/billing/payment.service.d.ts +44 -0
  254. package/dist/src/services/billing/payment.service.js +365 -0
  255. package/dist/src/services/billing/subscription.service.d.ts +37 -0
  256. package/dist/src/services/billing/subscription.service.js +654 -0
  257. package/dist/src/services/billing/usage.service.d.ts +47 -0
  258. package/dist/src/services/billing/usage.service.js +149 -0
  259. package/dist/src/services/content/copilot.service.d.ts +113 -0
  260. package/dist/src/services/content/copilot.service.js +439 -0
  261. package/dist/src/services/content/flashcard-progress.service.d.ts +159 -0
  262. package/dist/src/services/content/flashcard-progress.service.js +432 -0
  263. package/dist/src/services/content/flashcard.service.d.ts +184 -0
  264. package/dist/src/services/content/flashcard.service.js +339 -0
  265. package/dist/src/services/content/media-analysis.service.d.ts +23 -0
  266. package/dist/src/services/content/media-analysis.service.js +404 -0
  267. package/dist/src/services/content/podcast.service.d.ts +267 -0
  268. package/dist/src/services/content/podcast.service.js +653 -0
  269. package/dist/src/services/content/worksheet-content.service.d.ts +37 -0
  270. package/dist/src/services/content/worksheet-content.service.js +84 -0
  271. package/dist/src/services/content/worksheet-content.service.test.d.ts +1 -0
  272. package/dist/src/services/content/worksheet-content.service.test.js +69 -0
  273. package/dist/src/services/content/worksheet-generation.service.d.ts +91 -0
  274. package/dist/src/services/content/worksheet-generation.service.js +95 -0
  275. package/dist/src/services/content/worksheet-generation.service.test.d.ts +1 -0
  276. package/dist/src/services/content/worksheet-generation.service.test.js +20 -0
  277. package/dist/src/services/content/worksheet.service.d.ts +347 -0
  278. package/dist/src/services/content/worksheet.service.js +599 -0
  279. package/dist/src/services/knowledge/knowledge-base.service.d.ts +316 -0
  280. package/dist/src/services/knowledge/knowledge-base.service.js +544 -0
  281. package/dist/src/services/members/invitation.service.d.ts +66 -0
  282. package/dist/src/services/members/invitation.service.js +348 -0
  283. package/dist/src/services/members/member.service.d.ts +36 -0
  284. package/dist/src/services/members/member.service.js +193 -0
  285. package/dist/src/services/notifications/notification.service.d.ts +214 -0
  286. package/dist/src/services/notifications/notification.service.js +550 -0
  287. package/dist/src/services/notifications/notification.service.test.d.ts +1 -0
  288. package/dist/src/services/notifications/notification.service.test.js +87 -0
  289. package/dist/src/services/workspace/workspace-analytics.service.d.ts +24 -0
  290. package/dist/src/services/workspace/workspace-analytics.service.js +95 -0
  291. package/dist/src/services/workspace/workspace-kb.service.d.ts +40 -0
  292. package/dist/src/services/workspace/workspace-kb.service.js +184 -0
  293. package/dist/src/services/workspace/workspace.service.d.ts +263 -0
  294. package/dist/src/services/workspace/workspace.service.js +401 -0
  295. package/dist/src/trpc.d.ts +60 -0
  296. package/dist/src/trpc.js +217 -0
  297. package/dist/src/types/index.d.ts +126 -0
  298. package/dist/src/types/index.js +1 -0
  299. package/dist/trpc.d.ts +12 -4
  300. package/dist/trpc.js +5 -11
  301. package/package.json +8 -9
  302. package/prisma/schema.prisma +3 -4
  303. package/prisma/seed.mjs +5 -2
  304. package/prisma.config.ts +16 -0
  305. package/src/context.ts +33 -3
  306. package/src/lib/ai/index.ts +3 -0
  307. package/src/lib/ai/llm-client.ts +23 -0
  308. package/src/lib/prisma.ts +18 -9
  309. package/src/routers/auth.ts +1 -1
  310. package/src/routers/workspace.ts +4 -0
  311. package/src/scripts/purge-deleted-users.ts +1 -3
  312. package/src/services/billing/payment.service.ts +3 -6
  313. package/src/services/billing/usage.service.ts +190 -77
  314. package/src/services/content/copilot.service.ts +23 -32
  315. package/src/services/content/flashcard-progress.service.ts +12 -9
  316. package/src/services/content/flashcard.service.ts +89 -66
  317. package/src/services/content/media-analysis.service.ts +34 -29
  318. package/src/services/content/worksheet-generation.service.test.ts +2 -2
  319. package/src/services/workspace/workspace.service.ts +73 -66
  320. package/src/trpc.ts +5 -13
  321. package/tsconfig.json +3 -0
@@ -1,94 +1,207 @@
1
+ import type { Prisma } from '@prisma/client';
1
2
  import { prisma } from '../../lib/prisma.js';
3
+ import { workspaceAccessWhere } from '../../repositories/workspace.repository.js';
2
4
 
3
5
  export interface UserUsage {
4
- flashcards: number;
5
- worksheets: number;
6
- studyGuides: number;
7
- podcasts: number;
8
- storageBytes: number;
6
+ flashcards: number;
7
+ worksheets: number;
8
+ studyGuides: number;
9
+ podcasts: number;
10
+ storageBytes: number;
11
+ }
12
+
13
+ export interface AccountStats {
14
+ workspaces: number;
15
+ folders: number;
16
+ lastUpdated: Date | null;
17
+ spaceUsed: number;
18
+ spaceTotal: number;
19
+ }
20
+
21
+ export interface UserPlanLimits {
22
+ id: string;
23
+ planId: string;
24
+ maxStorageBytes: bigint;
25
+ maxWorksheets: number;
26
+ maxFlashcards: number;
27
+ maxPodcasts: number;
28
+ maxStudyGuides: number;
29
+ createdAt: Date;
30
+ updatedAt: Date;
31
+ isFallbackPlan: boolean;
32
+ }
33
+
34
+ export interface AccountSummary {
35
+ stats: AccountStats;
36
+ usage: UserUsage;
37
+ limits: UserPlanLimits;
38
+ hasActivePlan: boolean;
9
39
  }
10
40
 
11
41
  const FALLBACK_PLAN_LIMITS = {
12
- id: 'fallback-free',
13
- planId: 'fallback-free',
14
- maxStorageBytes: BigInt(1024 * 1024 * 1024), // 1GB
15
- maxWorksheets: 3,
16
- maxFlashcards: 20,
17
- maxPodcasts: 0,
18
- maxStudyGuides: 1,
42
+ id: 'fallback-free',
43
+ planId: 'fallback-free',
44
+ maxStorageBytes: BigInt(1024 * 1024 * 1024), // 1GB
45
+ maxWorksheets: 3,
46
+ maxFlashcards: 20,
47
+ maxPodcasts: 0,
48
+ maxStudyGuides: 1,
19
49
  } as const;
20
50
 
51
+ const CACHE_TTL_MS = 30_000;
52
+
53
+ type CacheEntry<T> = { value: T; expiresAt: number };
54
+
55
+ const usageCache = new Map<string, CacheEntry<UserUsage>>();
56
+ const limitsCache = new Map<string, CacheEntry<UserPlanLimits>>();
57
+ const accountSummaryCache = new Map<string, CacheEntry<AccountSummary>>();
58
+
59
+ function readCache<T>(map: Map<string, CacheEntry<T>>, userId: string): T | null {
60
+ const entry = map.get(userId);
61
+ if (!entry || entry.expiresAt <= Date.now()) {
62
+ map.delete(userId);
63
+ return null;
64
+ }
65
+ return entry.value;
66
+ }
67
+
68
+ function writeCache<T>(map: Map<string, CacheEntry<T>>, userId: string, value: T) {
69
+ map.set(userId, { value, expiresAt: Date.now() + CACHE_TTL_MS });
70
+ }
71
+
72
+ /** Bust cached usage/limit reads after creates, deletes, or billing changes. */
73
+ export function invalidateUserBillingCache(userId: string) {
74
+ usageCache.delete(userId);
75
+ limitsCache.delete(userId);
76
+ accountSummaryCache.delete(userId);
77
+ }
78
+
79
+ function workspaceAccessFilter(userId: string): Prisma.WorkspaceWhereInput {
80
+ return workspaceAccessWhere(userId);
81
+ }
82
+
21
83
  /**
22
84
  * Counts all resources consumed by a user across the platform.
23
85
  */
24
86
  export async function getUserUsage(userId: string): Promise<UserUsage> {
25
- const [flashcards, worksheets, studyGuides, podcasts, storageResult] = await Promise.all([
26
- prisma.artifact.count({ where: { createdById: userId, type: 'FLASHCARD_SET' } }),
27
- prisma.artifact.count({ where: { createdById: userId, type: 'WORKSHEET' } }),
28
- prisma.artifact.count({ where: { createdById: userId, type: 'STUDY_GUIDE' } }),
29
- prisma.artifact.count({ where: { createdById: userId, type: 'PODCAST_EPISODE' } }),
30
- prisma.fileAsset.aggregate({
31
- _sum: { size: true },
32
- where: { userId }
33
- })
34
- ]);
35
-
36
- return {
37
- flashcards,
38
- worksheets,
39
- studyGuides,
40
- podcasts,
41
- storageBytes: Number(storageResult._sum.size || 0)
42
- };
87
+ const cached = readCache(usageCache, userId);
88
+ if (cached) return cached;
89
+
90
+ const [flashcards, worksheets, studyGuides, podcasts, storageResult] = await Promise.all([
91
+ prisma.artifact.count({ where: { createdById: userId, type: 'FLASHCARD_SET' } }),
92
+ prisma.artifact.count({ where: { createdById: userId, type: 'WORKSHEET' } }),
93
+ prisma.artifact.count({ where: { createdById: userId, type: 'STUDY_GUIDE' } }),
94
+ prisma.artifact.count({ where: { createdById: userId, type: 'PODCAST_EPISODE' } }),
95
+ prisma.fileAsset.aggregate({
96
+ _sum: { size: true },
97
+ where: { userId },
98
+ }),
99
+ ]);
100
+
101
+ const usage: UserUsage = {
102
+ flashcards,
103
+ worksheets,
104
+ studyGuides,
105
+ podcasts,
106
+ storageBytes: Number(storageResult._sum.size || 0),
107
+ };
108
+ writeCache(usageCache, userId, usage);
109
+ return usage;
43
110
  }
44
111
 
45
112
  /**
46
- * Retrieves the specific plan limits for a user based on their active subscription
113
+ * Retrieves the specific plan limits for a user based on their active subscription
47
114
  * PLUS any extra credits purchased.
48
115
  */
49
- export async function getUserPlanLimits(userId: string) {
50
- const [activeSub, freePlan, userCredits] = await Promise.all([
51
- prisma.subscription.findFirst({
52
- where: { userId, status: 'active' },
53
- include: { plan: { include: { limit: true } } },
54
- orderBy: { createdAt: 'desc' }
55
- }),
56
- prisma.plan.findFirst({
57
- where: {
58
- active: true,
59
- price: 0,
60
- limit: { isNot: null }
61
- },
62
- include: { limit: true },
63
- orderBy: { createdAt: 'asc' }
64
- }),
65
- prisma.userCredit.groupBy({
66
- by: ['resourceType'],
67
- where: { userId },
68
- _sum: { amount: true }
69
- })
70
- ]);
71
-
72
- const baseLimit = activeSub?.plan?.limit ?? freePlan?.limit;
73
-
74
- // Prefer subscription limits, then DB free-plan limits, then hardcoded safety fallback.
75
- const base = baseLimit ?? FALLBACK_PLAN_LIMITS;
76
-
77
- // Fast lookup for credits
78
- const getCreditSum = (type: string) =>
79
- userCredits.find(c => c.resourceType === type)?._sum.amount || 0;
80
-
81
- // Return the merged total limit
82
- return {
83
- id: base.id,
84
- planId: base.planId,
85
- maxStorageBytes: BigInt(Number(base.maxStorageBytes) + (getCreditSum('STORAGE') * 1024 * 1024)),
86
- maxWorksheets: base.maxWorksheets + getCreditSum('WORKSHEET'),
87
- maxFlashcards: base.maxFlashcards + getCreditSum('FLASHCARD_SET'),
88
- maxPodcasts: base.maxPodcasts + getCreditSum('PODCAST_EPISODE'),
89
- maxStudyGuides: base.maxStudyGuides + getCreditSum('STUDY_GUIDE'),
90
- createdAt: baseLimit?.createdAt || new Date(),
91
- updatedAt: baseLimit?.updatedAt || new Date(),
92
- isFallbackPlan: !baseLimit,
93
- };
94
- }
116
+ export async function getUserPlanLimits(userId: string): Promise<UserPlanLimits> {
117
+ const cached = readCache(limitsCache, userId);
118
+ if (cached) return cached;
119
+
120
+ const [activeSub, freePlan, userCredits] = await Promise.all([
121
+ prisma.subscription.findFirst({
122
+ where: { userId, status: 'active' },
123
+ include: { plan: { include: { limit: true } } },
124
+ orderBy: { createdAt: 'desc' },
125
+ }),
126
+ prisma.plan.findFirst({
127
+ where: {
128
+ active: true,
129
+ price: 0,
130
+ limit: { isNot: null },
131
+ },
132
+ include: { limit: true },
133
+ orderBy: { createdAt: 'asc' },
134
+ }),
135
+ prisma.userCredit.groupBy({
136
+ by: ['resourceType'],
137
+ where: { userId },
138
+ _sum: { amount: true },
139
+ }),
140
+ ]);
141
+
142
+ const baseLimit = activeSub?.plan?.limit ?? freePlan?.limit;
143
+ const base = baseLimit ?? FALLBACK_PLAN_LIMITS;
144
+
145
+ const getCreditSum = (type: string) =>
146
+ userCredits.find((c) => c.resourceType === type)?._sum.amount || 0;
147
+
148
+ const limits: UserPlanLimits = {
149
+ id: base.id,
150
+ planId: base.planId,
151
+ maxStorageBytes: BigInt(Number(base.maxStorageBytes) + getCreditSum('STORAGE') * 1024 * 1024),
152
+ maxWorksheets: base.maxWorksheets + getCreditSum('WORKSHEET'),
153
+ maxFlashcards: base.maxFlashcards + getCreditSum('FLASHCARD_SET'),
154
+ maxPodcasts: base.maxPodcasts + getCreditSum('PODCAST_EPISODE'),
155
+ maxStudyGuides: base.maxStudyGuides + getCreditSum('STUDY_GUIDE'),
156
+ createdAt: baseLimit?.createdAt || new Date(),
157
+ updatedAt: baseLimit?.updatedAt || new Date(),
158
+ isFallbackPlan: !baseLimit,
159
+ };
160
+
161
+ writeCache(limitsCache, userId, limits);
162
+ return limits;
163
+ }
164
+
165
+ /**
166
+ * Sidebar/dashboard summary: stats + usage + limits in one parallel DB round-trip batch.
167
+ */
168
+ export async function getAccountSummary(userId: string): Promise<AccountSummary> {
169
+ const cached = readCache(accountSummaryCache, userId);
170
+ if (cached) return cached;
171
+
172
+ const workspaceWhere = workspaceAccessFilter(userId);
173
+
174
+ const [workspaceMeta, folderCount, usage, limits, storageUsed] = await Promise.all([
175
+ prisma.workspace.aggregate({
176
+ where: workspaceWhere,
177
+ _count: { _all: true },
178
+ _max: { updatedAt: true },
179
+ }),
180
+ prisma.folder.count({ where: { ownerId: userId } }),
181
+ getUserUsage(userId),
182
+ getUserPlanLimits(userId),
183
+ prisma.fileAsset.aggregate({
184
+ where: {
185
+ userId,
186
+ workspace: workspaceWhere,
187
+ },
188
+ _sum: { size: true },
189
+ }),
190
+ ]);
191
+
192
+ const summary: AccountSummary = {
193
+ stats: {
194
+ workspaces: workspaceMeta._count._all,
195
+ folders: folderCount,
196
+ lastUpdated: workspaceMeta._max.updatedAt,
197
+ spaceUsed: Number(storageUsed._sum.size ?? 0),
198
+ spaceTotal: Number(limits.maxStorageBytes),
199
+ },
200
+ usage,
201
+ limits,
202
+ hasActivePlan: !limits.isFallbackPlan,
203
+ };
204
+
205
+ writeCache(accountSummaryCache, userId, summary);
206
+ return summary;
207
+ }
@@ -8,6 +8,7 @@ import { sanitizeString } from '../../lib/validation.js';
8
8
  import { workspaceAccessWhere } from '../../repositories/workspace.repository.js';
9
9
  import PusherService from '../../lib/pusher.js';
10
10
  import { ArtifactType } from '../../lib/constants.js';
11
+ import { FlashcardService } from './flashcard.service.js';
11
12
 
12
13
  export const copilotArtifactType = z.enum([
13
14
  'study-guide',
@@ -382,10 +383,6 @@ export class CopilotService extends BaseService {
382
383
  enforceRateLimit(userId, input.context.workspaceId);
383
384
  await this.assertWorkspaceAccess(userId, input.context.workspaceId);
384
385
 
385
- await PusherService.emitTaskComplete(input.context.workspaceId, 'copilot:thinking', {
386
- status: 'started',
387
- });
388
-
389
386
  const history = await this.loadHistory(input.conversationId);
390
387
  const model = await callCopilotModel({
391
388
  mode: 'ask',
@@ -404,10 +401,6 @@ export class CopilotService extends BaseService {
404
401
  input.context.documentPlainText ?? input.context.documentContent,
405
402
  );
406
403
 
407
- await PusherService.emitTaskComplete(input.context.workspaceId, 'copilot:response', {
408
- status: 'completed',
409
- });
410
-
411
404
  await this.persistConversationExchange({
412
405
  userId,
413
406
  workspaceId: input.context.workspaceId,
@@ -426,10 +419,6 @@ export class CopilotService extends BaseService {
426
419
  enforceRateLimit(userId, input.context.workspaceId);
427
420
  await this.assertWorkspaceAccess(userId, input.context.workspaceId);
428
421
 
429
- await PusherService.emitTaskComplete(input.context.workspaceId, 'copilot:thinking', {
430
- status: 'started',
431
- });
432
-
433
422
  const question =
434
423
  input.message && input.message.trim().length > 0
435
424
  ? input.message
@@ -448,10 +437,6 @@ export class CopilotService extends BaseService {
448
437
  model.rawContent ||
449
438
  'I could not explain this selection.';
450
439
 
451
- await PusherService.emitTaskComplete(input.context.workspaceId, 'copilot:response', {
452
- status: 'completed',
453
- });
454
-
455
440
  await this.persistConversationExchange({
456
441
  userId,
457
442
  workspaceId: input.context.workspaceId,
@@ -551,22 +536,28 @@ export class CopilotService extends BaseService {
551
536
  });
552
537
  }
553
538
 
554
- const artifact = await this.db.artifact.create({
555
- data: {
556
- workspaceId: input.context.workspaceId,
557
- type: ArtifactType.FLASHCARD_SET,
558
- title: `Copilot Flashcards - ${new Date().toLocaleString()}`,
559
- createdById: userId,
560
- generating: false,
561
- flashcards: {
562
- create: cards.map((card, index) => ({
563
- front: sanitizeString(card.front, 200),
564
- back: sanitizeString(card.back, 500),
565
- order: index,
566
- tags: ['copilot-generated'],
567
- })),
568
- },
569
- },
539
+ const flashcardService = new FlashcardService(this.db);
540
+ const primarySet = await flashcardService.ensurePrimarySet(
541
+ userId,
542
+ input.context.workspaceId,
543
+ );
544
+ const orderOffset = primarySet.flashcards.reduce(
545
+ (max, card) => Math.max(max, card.order),
546
+ -1,
547
+ );
548
+
549
+ await this.db.flashcard.createMany({
550
+ data: cards.map((card, index) => ({
551
+ artifactId: primarySet.id,
552
+ front: sanitizeString(card.front, 200),
553
+ back: sanitizeString(card.back, 500),
554
+ order: orderOffset + 1 + index,
555
+ tags: ['copilot-generated'],
556
+ })),
557
+ });
558
+
559
+ const artifact = await this.db.artifact.findUniqueOrThrow({
560
+ where: { id: primarySet.id },
570
561
  include: { flashcards: true },
571
562
  });
572
563
 
@@ -320,25 +320,28 @@ export class FlashcardProgressService extends BaseService {
320
320
  const now = new Date();
321
321
  const LOW_MASTERY_THRESHOLD = 50; // Consider mastery < 50 as low
322
322
 
323
- // Get the latest artifact in the workspace
324
- const latestArtifact = await this.db.artifact.findFirst({
323
+ const flashcardSets = await this.db.artifact.findMany({
325
324
  where: {
326
325
  workspaceId,
327
326
  type: 'FLASHCARD_SET',
328
327
  },
329
- orderBy: {
330
- updatedAt: 'desc',
331
- },
328
+ include: { _count: { select: { flashcards: true } } },
329
+ orderBy: { createdAt: 'asc' },
332
330
  });
333
331
 
334
- if (!latestArtifact) {
332
+ if (flashcardSets.length === 0) {
335
333
  return [];
336
334
  }
337
335
 
338
- // Get all flashcards from the latest artifact
336
+ const withCards = flashcardSets.filter((set) => set._count.flashcards > 0);
337
+ const candidates = withCards.length > 0 ? withCards : flashcardSets;
338
+ const primaryArtifact = candidates.reduce((best, current) =>
339
+ current._count.flashcards > best._count.flashcards ? current : best,
340
+ );
341
+
339
342
  const allFlashcards = await this.db.flashcard.findMany({
340
343
  where: {
341
- artifactId: latestArtifact.id,
344
+ artifactId: primaryArtifact.id,
342
345
  },
343
346
  include: {
344
347
  artifact: true,
@@ -376,7 +379,7 @@ export class FlashcardProgressService extends BaseService {
376
379
  }
377
380
  ],
378
381
  flashcard: {
379
- artifactId: latestArtifact.id,
382
+ artifactId: primaryArtifact.id,
380
383
  },
381
384
  },
382
385
  include: {
@@ -8,6 +8,7 @@ import { ai } from '../../lib/ai/index.js';
8
8
  import { workspaceKbService } from '../workspace/workspace-kb.service.js';
9
9
  import PusherService from '../../lib/pusher.js';
10
10
  import { notifyArtifactFailed, notifyArtifactReady } from '../notifications/notification.service.js';
11
+ import { invalidateUserBillingCache } from '../billing/usage.service.js';
11
12
 
12
13
  export const typedAnswerGradeSchema = z.object({
13
14
  isCorrect: z.boolean(),
@@ -46,6 +47,55 @@ export class FlashcardService extends BaseService {
46
47
  super(db);
47
48
  }
48
49
 
50
+ private async findPrimarySet(userId: string, workspaceId: string) {
51
+ const sets = await this.db.artifact.findMany({
52
+ where: {
53
+ workspaceId,
54
+ type: ArtifactType.FLASHCARD_SET,
55
+ workspace: workspaceAccessWhere(userId),
56
+ },
57
+ include: {
58
+ flashcards: { orderBy: { order: 'asc' } },
59
+ _count: { select: { flashcards: true } },
60
+ },
61
+ orderBy: { createdAt: 'asc' },
62
+ });
63
+ if (sets.length === 0) return null;
64
+
65
+ const withCards = sets.filter((set) => set._count.flashcards > 0);
66
+ const candidates = withCards.length > 0 ? withCards : sets;
67
+ return candidates.reduce((best, current) =>
68
+ current._count.flashcards > best._count.flashcards ? current : best,
69
+ );
70
+ }
71
+
72
+ /** Returns the primary set, creating one only when flashcard content is first added. */
73
+ async ensurePrimarySet(userId: string, workspaceId: string) {
74
+ const existing = await this.findPrimarySet(userId, workspaceId);
75
+ if (existing) return existing;
76
+
77
+ const workspace = await this.db.workspace.findFirst({
78
+ where: { id: workspaceId, ...workspaceAccessWhere(userId) },
79
+ select: { id: true, title: true },
80
+ });
81
+ if (!workspace) {
82
+ throw new TRPCError({ code: 'NOT_FOUND', message: 'Workspace not found' });
83
+ }
84
+
85
+ return this.db.artifact.create({
86
+ data: {
87
+ workspaceId,
88
+ type: ArtifactType.FLASHCARD_SET,
89
+ title: 'Flashcards',
90
+ createdById: userId,
91
+ },
92
+ include: {
93
+ flashcards: { orderBy: { order: 'asc' } },
94
+ _count: { select: { flashcards: true } },
95
+ },
96
+ });
97
+ }
98
+
49
99
  async listSets(userId: string, workspaceId: string) {
50
100
  const workspace = await this.db.workspace.findFirst({
51
101
  where: { id: workspaceId, ownerId: userId },
@@ -61,35 +111,20 @@ export class FlashcardService extends BaseService {
61
111
  }
62
112
 
63
113
  async listCards(userId: string, workspaceId: string) {
64
- const set = await this.db.artifact.findFirst({
65
- where: {
66
- workspaceId,
67
- type: ArtifactType.FLASHCARD_SET,
68
- workspace: workspaceAccessWhere(userId),
69
- },
114
+ const set = await this.findPrimarySet(userId, workspaceId);
115
+ if (!set) return [];
116
+ return this.db.flashcard.findMany({
117
+ where: { artifactId: set.id },
70
118
  include: {
71
- flashcards: {
72
- include: {
73
- progress: { where: { userId } },
74
- },
75
- },
119
+ progress: { where: { userId } },
76
120
  },
77
- orderBy: { createdAt: 'desc' },
121
+ orderBy: { order: 'asc' },
78
122
  });
79
- if (!set) throw new TRPCError({ code: 'NOT_FOUND' });
80
- return set.flashcards;
81
123
  }
82
124
 
83
125
  async isGenerating(userId: string, workspaceId: string) {
84
- const artifact = await this.db.artifact.findFirst({
85
- where: {
86
- workspaceId,
87
- type: ArtifactType.FLASHCARD_SET,
88
- workspace: workspaceAccessWhere(userId),
89
- },
90
- orderBy: { createdAt: 'desc' },
91
- });
92
- return artifact?.generating;
126
+ const set = await this.findPrimarySet(userId, workspaceId);
127
+ return set?.generating ?? false;
93
128
  }
94
129
 
95
130
  async createCard(
@@ -103,25 +138,22 @@ export class FlashcardService extends BaseService {
103
138
  order?: number;
104
139
  },
105
140
  ) {
106
- const set = await this.db.artifact.findFirst({
107
- where: {
108
- type: ArtifactType.FLASHCARD_SET,
109
- workspace: { id: input.workspaceId },
110
- },
111
- include: { flashcards: true },
112
- orderBy: { updatedAt: 'desc' },
113
- });
114
- if (!set) throw new TRPCError({ code: 'NOT_FOUND' });
115
- return this.db.flashcard.create({
141
+ const set = await this.ensurePrimarySet(userId, input.workspaceId);
142
+ const nextOrder =
143
+ input.order ??
144
+ (set.flashcards.reduce((max, card) => Math.max(max, card.order), -1) + 1);
145
+ const card = await this.db.flashcard.create({
116
146
  data: {
117
147
  artifactId: set.id,
118
148
  front: input.front,
119
149
  back: input.back,
120
150
  acceptedAnswers: normalizeAcceptedAnswers(input.acceptedAnswers),
121
151
  tags: input.tags ?? [],
122
- order: input.order ?? 0,
152
+ order: nextOrder,
123
153
  },
124
154
  });
155
+ invalidateUserBillingCache(userId);
156
+ return card;
125
157
  }
126
158
 
127
159
  async updateCard(
@@ -262,14 +294,11 @@ export class FlashcardService extends BaseService {
262
294
  });
263
295
  if (!workspace) throw new TRPCError({ code: 'NOT_FOUND' });
264
296
 
265
- const flashcardCurrent = await this.db.artifact.findFirst({
266
- where: {
267
- workspaceId: input.workspaceId,
268
- type: ArtifactType.FLASHCARD_SET,
269
- },
270
- select: { id: true, flashcards: true },
271
- orderBy: { updatedAt: 'desc' },
272
- });
297
+ const primarySet = await this.ensurePrimarySet(userId, input.workspaceId);
298
+ const orderOffset = primarySet.flashcards.reduce(
299
+ (max, card) => Math.max(max, card.order),
300
+ -1,
301
+ );
273
302
 
274
303
  try {
275
304
  await PusherService.emitTaskComplete(input.workspaceId, 'flash_card_info', {
@@ -278,23 +307,15 @@ export class FlashcardService extends BaseService {
278
307
  difficulty: input.difficulty,
279
308
  });
280
309
 
281
- const artifact = await this.db.artifact.create({
310
+ await this.db.artifact.update({
311
+ where: { id: primarySet.id },
282
312
  data: {
283
- workspaceId: input.workspaceId,
284
- type: ArtifactType.FLASHCARD_SET,
285
- title: input.title || `Flashcards - ${new Date().toLocaleString()}`,
286
- createdById: userId,
287
313
  generating: true,
288
314
  generatingMetadata: {
289
315
  quantity: input.numCards,
290
316
  difficulty: input.difficulty.toLowerCase(),
291
317
  },
292
- flashcards: {
293
- create: flashcardCurrent?.flashcards.map((card) => ({
294
- front: card.front,
295
- back: card.back,
296
- })),
297
- },
318
+ ...(input.title ? { title: input.title } : {}),
298
319
  },
299
320
  });
300
321
 
@@ -324,10 +345,10 @@ export class FlashcardService extends BaseService {
324
345
  const back = card.definition || card.back || card.answer || card.solution || `Answer ${i + 1}`;
325
346
  await this.db.flashcard.create({
326
347
  data: {
327
- artifactId: artifact.id,
348
+ artifactId: primarySet.id,
328
349
  front,
329
350
  back,
330
- order: i,
351
+ order: orderOffset + 1 + i,
331
352
  tags: input.tags ?? ['ai-generated', input.difficulty],
332
353
  },
333
354
  });
@@ -342,10 +363,10 @@ export class FlashcardService extends BaseService {
342
363
  const [front, back] = line.split(' - ');
343
364
  await this.db.flashcard.create({
344
365
  data: {
345
- artifactId: artifact.id,
366
+ artifactId: primarySet.id,
346
367
  front: front.trim(),
347
368
  back: back.trim(),
348
- order: i,
369
+ order: orderOffset + 1 + i,
349
370
  tags: input.tags ?? ['ai-generated', input.difficulty],
350
371
  },
351
372
  });
@@ -354,13 +375,14 @@ export class FlashcardService extends BaseService {
354
375
  }
355
376
  }
356
377
 
357
- await PusherService.emitFlashcardComplete(input.workspaceId, artifact);
358
-
359
- await this.db.artifact.update({
360
- where: { id: artifact.id },
378
+ const artifact = await this.db.artifact.update({
379
+ where: { id: primarySet.id },
361
380
  data: { generating: false },
381
+ include: { flashcards: true },
362
382
  });
363
383
 
384
+ await PusherService.emitFlashcardComplete(input.workspaceId, artifact);
385
+
364
386
  await notifyArtifactReady(this.db, {
365
387
  userId,
366
388
  workspaceId: input.workspaceId,
@@ -369,14 +391,15 @@ export class FlashcardService extends BaseService {
369
391
  title: artifact.title,
370
392
  }).catch(() => {});
371
393
 
394
+ invalidateUserBillingCache(userId);
372
395
  return { artifact, createdCards };
373
396
  } catch (error) {
374
- if (flashcardCurrent?.id) {
375
- await this.db.artifact.update({
376
- where: { id: flashcardCurrent.id },
397
+ await this.db.artifact
398
+ .update({
399
+ where: { id: primarySet.id },
377
400
  data: { generating: false },
378
- });
379
- }
401
+ })
402
+ .catch(() => {});
380
403
  await PusherService.emitError(
381
404
  input.workspaceId,
382
405
  `Failed to generate flashcards: ${error}`,