@studious-lms/server 1.14.0 → 1.15.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 (196) hide show
  1. package/.github/workflows/deploy.yml +1 -1
  2. package/dist/instrument.js +3 -3
  3. package/dist/instrument.js.map +1 -1
  4. package/dist/lib/inferDocumentFormat.d.ts +10 -0
  5. package/dist/lib/inferDocumentFormat.d.ts.map +1 -0
  6. package/dist/lib/inferDocumentFormat.js +48 -0
  7. package/dist/lib/inferDocumentFormat.js.map +1 -0
  8. package/dist/lib/legal.d.ts +3 -0
  9. package/dist/lib/legal.d.ts.map +1 -0
  10. package/dist/lib/legal.js +6 -0
  11. package/dist/lib/legal.js.map +1 -0
  12. package/dist/models/announcement.d.ts +47 -45
  13. package/dist/models/announcement.d.ts.map +1 -1
  14. package/dist/models/assignment.d.ts +75 -75
  15. package/dist/models/auth.d.ts +8 -0
  16. package/dist/models/auth.d.ts.map +1 -1
  17. package/dist/models/auth.js +4 -2
  18. package/dist/models/auth.js.map +1 -1
  19. package/dist/models/class.d.ts +13 -13
  20. package/dist/models/comment.d.ts +6 -6
  21. package/dist/models/conversation.d.ts +23 -23
  22. package/dist/models/event.d.ts +20 -18
  23. package/dist/models/event.d.ts.map +1 -1
  24. package/dist/models/file.d.ts +33 -9
  25. package/dist/models/file.d.ts.map +1 -1
  26. package/dist/models/labChat.d.ts +4 -4
  27. package/dist/models/marketing.d.ts +2 -15
  28. package/dist/models/marketing.d.ts.map +1 -1
  29. package/dist/models/marketing.js +12 -4
  30. package/dist/models/marketing.js.map +1 -1
  31. package/dist/models/message.d.ts +13 -13
  32. package/dist/models/newtonChat.d.ts +3 -3
  33. package/dist/models/notification.d.ts +4 -4
  34. package/dist/models/worksheet.d.ts +121 -121
  35. package/dist/pipelines/aiLabChat.d.ts +7 -1
  36. package/dist/pipelines/aiLabChat.d.ts.map +1 -1
  37. package/dist/pipelines/aiLabChat.js +153 -67
  38. package/dist/pipelines/aiLabChat.js.map +1 -1
  39. package/dist/pipelines/aiLabChatContract.d.ts +34 -195
  40. package/dist/pipelines/aiLabChatContract.d.ts.map +1 -1
  41. package/dist/pipelines/aiLabChatContract.js +9 -19
  42. package/dist/pipelines/aiLabChatContract.js.map +1 -1
  43. package/dist/pipelines/gradeWorksheet.d.ts +6 -6
  44. package/dist/pipelines/labChatPrompt.d.ts +8 -3
  45. package/dist/pipelines/labChatPrompt.d.ts.map +1 -1
  46. package/dist/pipelines/labChatPrompt.js +52 -14
  47. package/dist/pipelines/labChatPrompt.js.map +1 -1
  48. package/dist/pipelines/pdfToWorksheet.d.ts +1 -1
  49. package/dist/routers/_app.d.ts +750 -546
  50. package/dist/routers/_app.d.ts.map +1 -1
  51. package/dist/routers/announcement.d.ts +40 -40
  52. package/dist/routers/assignment.d.ts +131 -121
  53. package/dist/routers/assignment.d.ts.map +1 -1
  54. package/dist/routers/auth.d.ts +2 -0
  55. package/dist/routers/auth.d.ts.map +1 -1
  56. package/dist/routers/auth.js +15 -2
  57. package/dist/routers/auth.js.map +1 -1
  58. package/dist/routers/class.d.ts +12 -8
  59. package/dist/routers/class.d.ts.map +1 -1
  60. package/dist/routers/comment.d.ts +5 -5
  61. package/dist/routers/conversation.d.ts +6 -6
  62. package/dist/routers/discussion.d.ts +3 -3
  63. package/dist/routers/embedding.d.ts +6 -2
  64. package/dist/routers/embedding.d.ts.map +1 -1
  65. package/dist/routers/embedding.js +44 -36
  66. package/dist/routers/embedding.js.map +1 -1
  67. package/dist/routers/event.d.ts +20 -18
  68. package/dist/routers/event.d.ts.map +1 -1
  69. package/dist/routers/file.d.ts +2 -2
  70. package/dist/routers/labChat.d.ts +3 -3
  71. package/dist/routers/marketing.d.ts +0 -13
  72. package/dist/routers/marketing.d.ts.map +1 -1
  73. package/dist/routers/notifications.d.ts +4 -4
  74. package/dist/routers/school.d.ts +4 -0
  75. package/dist/routers/school.d.ts.map +1 -1
  76. package/dist/routers/search.d.ts +2 -2
  77. package/dist/routers/user.d.ts +89 -0
  78. package/dist/routers/user.d.ts.map +1 -1
  79. package/dist/routers/user.js +19 -2
  80. package/dist/routers/user.js.map +1 -1
  81. package/dist/routers/worksheet.d.ts +44 -44
  82. package/dist/routes/aiStream.d.ts.map +1 -1
  83. package/dist/routes/aiStream.js +41 -11
  84. package/dist/routes/aiStream.js.map +1 -1
  85. package/dist/seedDatabase.d.ts +3 -1
  86. package/dist/seedDatabase.d.ts.map +1 -1
  87. package/dist/services/aiChatStream.d.ts +20 -0
  88. package/dist/services/aiChatStream.d.ts.map +1 -1
  89. package/dist/services/aiChatStream.js +110 -13
  90. package/dist/services/aiChatStream.js.map +1 -1
  91. package/dist/services/aiModifierTools.d.ts +0 -16
  92. package/dist/services/aiModifierTools.d.ts.map +1 -1
  93. package/dist/services/aiModifierTools.js +112 -3
  94. package/dist/services/aiModifierTools.js.map +1 -1
  95. package/dist/services/aiProposalTools.d.ts.map +1 -1
  96. package/dist/services/aiProposalTools.js +25 -25
  97. package/dist/services/aiProposalTools.js.map +1 -1
  98. package/dist/services/aiTools.js +3 -3
  99. package/dist/services/aiTools.js.map +1 -1
  100. package/dist/services/announcement.d.ts +34 -34
  101. package/dist/services/assignment.d.ts +121 -111
  102. package/dist/services/assignment.d.ts.map +1 -1
  103. package/dist/services/assignment.js +4 -2
  104. package/dist/services/assignment.js.map +1 -1
  105. package/dist/services/auth.d.ts +2 -0
  106. package/dist/services/auth.d.ts.map +1 -1
  107. package/dist/services/auth.js +13 -4
  108. package/dist/services/auth.js.map +1 -1
  109. package/dist/services/class.d.ts +10 -6
  110. package/dist/services/class.d.ts.map +1 -1
  111. package/dist/services/class.js +7 -3
  112. package/dist/services/class.js.map +1 -1
  113. package/dist/services/comment.d.ts +5 -5
  114. package/dist/services/conversation.d.ts +6 -6
  115. package/dist/services/discussion.d.ts.map +1 -1
  116. package/dist/services/discussion.js +9 -2
  117. package/dist/services/discussion.js.map +1 -1
  118. package/dist/services/embedding.d.ts +19 -1
  119. package/dist/services/embedding.d.ts.map +1 -1
  120. package/dist/services/embedding.js +154 -8
  121. package/dist/services/embedding.js.map +1 -1
  122. package/dist/services/event.d.ts +20 -18
  123. package/dist/services/event.d.ts.map +1 -1
  124. package/dist/services/file.d.ts +4 -4
  125. package/dist/services/folder.d.ts.map +1 -1
  126. package/dist/services/folder.js +32 -2
  127. package/dist/services/folder.js.map +1 -1
  128. package/dist/services/labChat.d.ts +3 -3
  129. package/dist/services/marketing.d.ts +1 -14
  130. package/dist/services/marketing.d.ts.map +1 -1
  131. package/dist/services/marketing.js +5 -5
  132. package/dist/services/marketing.js.map +1 -1
  133. package/dist/services/notification.d.ts +4 -4
  134. package/dist/services/openaiDocumentGen.d.ts +37 -0
  135. package/dist/services/openaiDocumentGen.d.ts.map +1 -0
  136. package/dist/services/openaiDocumentGen.js +262 -0
  137. package/dist/services/openaiDocumentGen.js.map +1 -0
  138. package/dist/services/school.d.ts +4 -0
  139. package/dist/services/school.d.ts.map +1 -1
  140. package/dist/services/search.d.ts +1 -1
  141. package/dist/services/search.d.ts.map +1 -1
  142. package/dist/services/search.js +27 -2
  143. package/dist/services/search.js.map +1 -1
  144. package/dist/services/userPrivacy.d.ts +86 -0
  145. package/dist/services/userPrivacy.d.ts.map +1 -0
  146. package/dist/services/userPrivacy.js +184 -0
  147. package/dist/services/userPrivacy.js.map +1 -0
  148. package/dist/services/worksheet.d.ts +66 -49
  149. package/dist/services/worksheet.d.ts.map +1 -1
  150. package/dist/services/worksheet.js +48 -3
  151. package/dist/services/worksheet.js.map +1 -1
  152. package/dist/utils/email.d.ts.map +1 -1
  153. package/dist/utils/email.js +7 -3
  154. package/dist/utils/email.js.map +1 -1
  155. package/dist/workers/processors/embedContent.d.ts.map +1 -1
  156. package/dist/workers/processors/embedContent.js +16 -25
  157. package/dist/workers/processors/embedContent.js.map +1 -1
  158. package/package.json +1 -1
  159. package/prisma/migrations/20260610140000_expand_embedding_source_types/migration.sql +4 -0
  160. package/prisma/migrations/20260611120000_add_user_terms_acceptance/migration.sql +3 -0
  161. package/prisma/schema.prisma +6 -0
  162. package/src/instrument.ts +1 -1
  163. package/src/lib/inferDocumentFormat.ts +58 -0
  164. package/src/lib/legal.ts +2 -0
  165. package/src/models/auth.ts +4 -0
  166. package/src/models/marketing.ts +10 -2
  167. package/src/pipelines/aiLabChat.ts +198 -74
  168. package/src/pipelines/aiLabChatContract.ts +7 -17
  169. package/src/pipelines/labChatPrompt.ts +64 -14
  170. package/src/routers/auth.ts +13 -0
  171. package/src/routers/embedding.ts +44 -39
  172. package/src/routers/user.ts +26 -0
  173. package/src/routes/aiStream.ts +47 -9
  174. package/src/services/aiChatStream.ts +133 -11
  175. package/src/services/aiModifierTools.ts +154 -1
  176. package/src/services/aiProposalTools.ts +27 -25
  177. package/src/services/aiTools.ts +1 -1
  178. package/src/services/assignment.ts +3 -0
  179. package/src/services/auth.ts +14 -2
  180. package/src/services/class.ts +7 -1
  181. package/src/services/discussion.ts +13 -0
  182. package/src/services/embedding.ts +167 -6
  183. package/src/services/folder.ts +31 -0
  184. package/src/services/marketing.ts +3 -3
  185. package/src/services/openaiDocumentGen.ts +319 -0
  186. package/src/services/search.ts +25 -1
  187. package/src/services/userPrivacy.ts +196 -0
  188. package/src/services/worksheet.ts +68 -1
  189. package/src/utils/email.ts +5 -1
  190. package/src/workers/processors/embedContent.ts +18 -22
  191. package/tests/helpers/registerInput.ts +20 -0
  192. package/tests/integration/globalSetup.ts +10 -18
  193. package/tests/integration/routers/auth.test.ts +13 -18
  194. package/tests/unit/lib/inferDocumentFormat.test.ts +33 -0
  195. package/tests/unit/services/aiChatStream.test.ts +92 -0
  196. package/tests/unit/services/openaiDocumentGen.test.ts +65 -0
@@ -0,0 +1,86 @@
1
+ /** Export a portable snapshot of the authenticated user's personal data. */
2
+ export declare function exportUserData(userId: string): Promise<{
3
+ exportedAt: string;
4
+ user: {
5
+ id: string;
6
+ username: string;
7
+ email: string;
8
+ verified: boolean;
9
+ termsAcceptedAt: Date | null;
10
+ termsVersion: string | null;
11
+ profile: {
12
+ id: string;
13
+ userId: string;
14
+ displayName: string | null;
15
+ bio: string | null;
16
+ location: string | null;
17
+ website: string | null;
18
+ createdAt: Date;
19
+ updatedAt: Date;
20
+ profilePicture: string | null;
21
+ profilePictureThumbnail: string | null;
22
+ } | null;
23
+ teacherIn: {
24
+ section: string;
25
+ id: string;
26
+ name: string;
27
+ subject: string;
28
+ }[];
29
+ studentIn: {
30
+ section: string;
31
+ id: string;
32
+ name: string;
33
+ subject: string;
34
+ }[];
35
+ submissions: {
36
+ assignment: {
37
+ id: string;
38
+ classId: string;
39
+ title: string;
40
+ };
41
+ id: string;
42
+ createdAt: Date;
43
+ extendedResponse: string | null;
44
+ gradeReceived: number | null;
45
+ submittedAt: Date | null;
46
+ }[];
47
+ events: {
48
+ id: string;
49
+ location: string | null;
50
+ name: string | null;
51
+ startTime: Date;
52
+ endTime: Date;
53
+ remarks: string | null;
54
+ }[];
55
+ notificationsReceived: {
56
+ id: string;
57
+ createdAt: Date;
58
+ title: string;
59
+ content: string;
60
+ read: boolean;
61
+ }[];
62
+ schoolMemberships: {
63
+ status: import(".prisma/client").$Enums.SchoolMembershipStatus;
64
+ school: {
65
+ id: string;
66
+ name: string;
67
+ };
68
+ role: import(".prisma/client").$Enums.SchoolMembershipRole;
69
+ }[];
70
+ sentMessages: {
71
+ id: string;
72
+ createdAt: Date;
73
+ content: string;
74
+ conversationId: string;
75
+ }[];
76
+ };
77
+ }>;
78
+ /**
79
+ * Erase personal account data by anonymizing the user record and removing
80
+ * sessions and memberships. Educational records tied to classes (e.g.
81
+ * submissions) remain with an anonymized identity where required.
82
+ */
83
+ export declare function deleteUserAccount(userId: string): Promise<{
84
+ success: boolean;
85
+ }>;
86
+ //# sourceMappingURL=userPrivacy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"userPrivacy.d.ts","sourceRoot":"/","sources":["services/userPrivacy.ts"],"names":[],"mappings":"AA6DA,4EAA4E;AAC5E,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4ElD;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM;;GAkDrD"}
@@ -0,0 +1,184 @@
1
+ /**
2
+ * User privacy service — data export and account erasure (anonymization).
3
+ */
4
+
5
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="7184e3ea-1921-5a91-936e-6fa9a92f8a03")}catch(e){}}();
6
+ import { TRPCError } from "@trpc/server";
7
+ import { randomBytes } from "node:crypto";
8
+ import { hash } from "bcryptjs";
9
+ import { prisma } from "../lib/prisma.js";
10
+ import { deleteSessionsByUserId } from "../models/auth.js";
11
+ async function assertAccountCanBeErased(userId) {
12
+ const user = await prisma.user.findUnique({
13
+ where: { id: userId },
14
+ select: {
15
+ id: true,
16
+ teacherIn: {
17
+ select: {
18
+ id: true,
19
+ name: true,
20
+ _count: { select: { students: true } },
21
+ },
22
+ },
23
+ _count: {
24
+ select: {
25
+ assignments: true,
26
+ createdLabChats: true,
27
+ },
28
+ },
29
+ },
30
+ });
31
+ if (!user) {
32
+ throw new TRPCError({ code: "NOT_FOUND", message: "User not found" });
33
+ }
34
+ const classesWithStudents = user.teacherIn.filter((cls) => cls._count.students > 0);
35
+ if (classesWithStudents.length > 0) {
36
+ throw new TRPCError({
37
+ code: "PRECONDITION_FAILED",
38
+ message: "Delete or transfer your classes before deleting your account.",
39
+ });
40
+ }
41
+ if (user._count.assignments > 0) {
42
+ throw new TRPCError({
43
+ code: "PRECONDITION_FAILED",
44
+ message: "Remove or reassign your assignments before deleting your account.",
45
+ });
46
+ }
47
+ if (user._count.createdLabChats > 0) {
48
+ throw new TRPCError({
49
+ code: "PRECONDITION_FAILED",
50
+ message: "Remove your AI lab chats before deleting your account.",
51
+ });
52
+ }
53
+ }
54
+ /** Export a portable snapshot of the authenticated user's personal data. */
55
+ export async function exportUserData(userId) {
56
+ const user = await prisma.user.findUnique({
57
+ where: { id: userId },
58
+ select: {
59
+ id: true,
60
+ username: true,
61
+ email: true,
62
+ verified: true,
63
+ termsAcceptedAt: true,
64
+ termsVersion: true,
65
+ profile: true,
66
+ teacherIn: {
67
+ select: { id: true, name: true, subject: true, section: true },
68
+ },
69
+ studentIn: {
70
+ select: { id: true, name: true, subject: true, section: true },
71
+ },
72
+ schoolMemberships: {
73
+ select: {
74
+ role: true,
75
+ status: true,
76
+ school: { select: { id: true, name: true } },
77
+ },
78
+ },
79
+ submissions: {
80
+ select: {
81
+ id: true,
82
+ createdAt: true,
83
+ submittedAt: true,
84
+ gradeReceived: true,
85
+ extendedResponse: true,
86
+ assignment: { select: { id: true, title: true, classId: true } },
87
+ },
88
+ },
89
+ events: {
90
+ select: {
91
+ id: true,
92
+ name: true,
93
+ startTime: true,
94
+ endTime: true,
95
+ location: true,
96
+ remarks: true,
97
+ },
98
+ },
99
+ sentMessages: {
100
+ select: {
101
+ id: true,
102
+ content: true,
103
+ createdAt: true,
104
+ conversationId: true,
105
+ },
106
+ take: 500,
107
+ orderBy: { createdAt: "desc" },
108
+ },
109
+ notificationsReceived: {
110
+ select: {
111
+ id: true,
112
+ title: true,
113
+ content: true,
114
+ read: true,
115
+ createdAt: true,
116
+ },
117
+ take: 200,
118
+ orderBy: { createdAt: "desc" },
119
+ },
120
+ },
121
+ });
122
+ if (!user) {
123
+ throw new TRPCError({ code: "NOT_FOUND", message: "User not found" });
124
+ }
125
+ return {
126
+ exportedAt: new Date().toISOString(),
127
+ user,
128
+ };
129
+ }
130
+ /**
131
+ * Erase personal account data by anonymizing the user record and removing
132
+ * sessions and memberships. Educational records tied to classes (e.g.
133
+ * submissions) remain with an anonymized identity where required.
134
+ */
135
+ export async function deleteUserAccount(userId) {
136
+ await assertAccountCanBeErased(userId);
137
+ await deleteSessionsByUserId(userId);
138
+ const anonUsername = `deleted_${userId.slice(0, 8)}`;
139
+ const anonEmail = `deleted+${userId}@studious.invalid`;
140
+ const randomPassword = await hash(randomBytes(32).toString("hex"), 10);
141
+ await prisma.$transaction([
142
+ prisma.conversationMember.deleteMany({ where: { userId } }),
143
+ prisma.notification.deleteMany({
144
+ where: { OR: [{ senderId: userId }, { receiverId: userId }] },
145
+ }),
146
+ prisma.schoolMembership.deleteMany({ where: { userId } }),
147
+ prisma.permission.deleteMany({
148
+ where: { OR: [{ userId }, { grantedById: userId }] },
149
+ }),
150
+ prisma.user.update({
151
+ where: { id: userId },
152
+ data: {
153
+ teacherIn: { set: [] },
154
+ studentIn: { set: [] },
155
+ departments: { set: [] },
156
+ tags: { set: [] },
157
+ specificAssignments: { set: [] },
158
+ presentAttendance: { set: [] },
159
+ lateAttendance: { set: [] },
160
+ absentAttendance: { set: [] },
161
+ schoolId: null,
162
+ username: anonUsername,
163
+ email: anonEmail,
164
+ password: randomPassword,
165
+ verified: false,
166
+ termsAcceptedAt: null,
167
+ termsVersion: null,
168
+ profile: {
169
+ update: {
170
+ displayName: null,
171
+ bio: null,
172
+ location: null,
173
+ website: null,
174
+ profilePicture: null,
175
+ profilePictureThumbnail: null,
176
+ },
177
+ },
178
+ },
179
+ }),
180
+ ]);
181
+ return { success: true };
182
+ }
183
+ //# sourceMappingURL=userPrivacy.js.map
184
+ //# debugId=7184e3ea-1921-5a91-936e-6fa9a92f8a03
@@ -0,0 +1 @@
1
+ {"version":3,"file":"userPrivacy.js","sources":["services/userPrivacy.ts"],"sourceRoot":"/","sourcesContent":["/**\n * User privacy service — data export and account erasure (anonymization).\n */\nimport { TRPCError } from \"@trpc/server\";\nimport { randomBytes } from \"node:crypto\";\nimport { hash } from \"bcryptjs\";\nimport { prisma } from \"../lib/prisma.js\";\nimport { deleteSessionsByUserId } from \"../models/auth.js\";\n\nasync function assertAccountCanBeErased(userId: string) {\n const user = await prisma.user.findUnique({\n where: { id: userId },\n select: {\n id: true,\n teacherIn: {\n select: {\n id: true,\n name: true,\n _count: { select: { students: true } },\n },\n },\n _count: {\n select: {\n assignments: true,\n createdLabChats: true,\n },\n },\n },\n });\n\n if (!user) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"User not found\" });\n }\n\n const classesWithStudents = user.teacherIn.filter(\n (cls) => cls._count.students > 0,\n );\n if (classesWithStudents.length > 0) {\n throw new TRPCError({\n code: \"PRECONDITION_FAILED\",\n message:\n \"Delete or transfer your classes before deleting your account.\",\n });\n }\n\n if (user._count.assignments > 0) {\n throw new TRPCError({\n code: \"PRECONDITION_FAILED\",\n message:\n \"Remove or reassign your assignments before deleting your account.\",\n });\n }\n\n if (user._count.createdLabChats > 0) {\n throw new TRPCError({\n code: \"PRECONDITION_FAILED\",\n message: \"Remove your AI lab chats before deleting your account.\",\n });\n }\n}\n\n/** Export a portable snapshot of the authenticated user's personal data. */\nexport async function exportUserData(userId: string) {\n const user = await prisma.user.findUnique({\n where: { id: userId },\n select: {\n id: true,\n username: true,\n email: true,\n verified: true,\n termsAcceptedAt: true,\n termsVersion: true,\n profile: true,\n teacherIn: {\n select: { id: true, name: true, subject: true, section: true },\n },\n studentIn: {\n select: { id: true, name: true, subject: true, section: true },\n },\n schoolMemberships: {\n select: {\n role: true,\n status: true,\n school: { select: { id: true, name: true } },\n },\n },\n submissions: {\n select: {\n id: true,\n createdAt: true,\n submittedAt: true,\n gradeReceived: true,\n extendedResponse: true,\n assignment: { select: { id: true, title: true, classId: true } },\n },\n },\n events: {\n select: {\n id: true,\n name: true,\n startTime: true,\n endTime: true,\n location: true,\n remarks: true,\n },\n },\n sentMessages: {\n select: {\n id: true,\n content: true,\n createdAt: true,\n conversationId: true,\n },\n take: 500,\n orderBy: { createdAt: \"desc\" },\n },\n notificationsReceived: {\n select: {\n id: true,\n title: true,\n content: true,\n read: true,\n createdAt: true,\n },\n take: 200,\n orderBy: { createdAt: \"desc\" },\n },\n },\n });\n\n if (!user) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"User not found\" });\n }\n\n return {\n exportedAt: new Date().toISOString(),\n user,\n };\n}\n\n/**\n * Erase personal account data by anonymizing the user record and removing\n * sessions and memberships. Educational records tied to classes (e.g.\n * submissions) remain with an anonymized identity where required.\n */\nexport async function deleteUserAccount(userId: string) {\n await assertAccountCanBeErased(userId);\n await deleteSessionsByUserId(userId);\n\n const anonUsername = `deleted_${userId.slice(0, 8)}`;\n const anonEmail = `deleted+${userId}@studious.invalid`;\n const randomPassword = await hash(randomBytes(32).toString(\"hex\"), 10);\n\n await prisma.$transaction([\n prisma.conversationMember.deleteMany({ where: { userId } }),\n prisma.notification.deleteMany({\n where: { OR: [{ senderId: userId }, { receiverId: userId }] },\n }),\n prisma.schoolMembership.deleteMany({ where: { userId } }),\n prisma.permission.deleteMany({\n where: { OR: [{ userId }, { grantedById: userId }] },\n }),\n prisma.user.update({\n where: { id: userId },\n data: {\n teacherIn: { set: [] },\n studentIn: { set: [] },\n departments: { set: [] },\n tags: { set: [] },\n specificAssignments: { set: [] },\n presentAttendance: { set: [] },\n lateAttendance: { set: [] },\n absentAttendance: { set: [] },\n schoolId: null,\n username: anonUsername,\n email: anonEmail,\n password: randomPassword,\n verified: false,\n termsAcceptedAt: null,\n termsVersion: null,\n profile: {\n update: {\n displayName: null,\n bio: null,\n location: null,\n website: null,\n profilePicture: null,\n profilePictureThumbnail: null,\n },\n },\n },\n }),\n ]);\n\n return { success: true };\n}\n"],"names":[],"mappings":"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","debug_id":"7184e3ea-1921-5a91-936e-6fa9a92f8a03"}
@@ -1,4 +1,4 @@
1
- import { type WorksheetQuestionType } from "@prisma/client";
1
+ import { Prisma, type WorksheetQuestionType } from "@prisma/client";
2
2
  /** Get worksheet with questions and mark schemes. */
3
3
  export declare function getWorksheet(worksheetId: string, userId: string): Promise<{
4
4
  class: {
@@ -22,16 +22,16 @@ export declare function getWorksheet(worksheetId: string, userId: string): Promi
22
22
  }[];
23
23
  questions: {
24
24
  type: import(".prisma/client").$Enums.WorksheetQuestionType;
25
- options: import("@prisma/client/runtime/library.js").JsonValue | null;
26
- markScheme: import("@prisma/client/runtime/library.js").JsonValue | null;
25
+ options: Prisma.JsonValue | null;
26
+ markScheme: Prisma.JsonValue | null;
27
27
  id: string;
28
28
  createdAt: Date;
29
29
  updatedAt: Date;
30
- order: number | null;
31
- worksheetId: string;
30
+ points: number;
32
31
  question: string;
32
+ worksheetId: string;
33
+ order: number | null;
33
34
  answer: string;
34
- points: number;
35
35
  }[];
36
36
  } & {
37
37
  status: import(".prisma/client").$Enums.JobStatus | null;
@@ -40,8 +40,8 @@ export declare function getWorksheet(worksheetId: string, userId: string): Promi
40
40
  updatedAt: Date;
41
41
  name: string;
42
42
  classId: string;
43
- sourceUrl: string | null;
44
43
  runJobId: string | null;
44
+ sourceUrl: string | null;
45
45
  }>;
46
46
  /** Check if worksheet exists. */
47
47
  export declare function worksheetExists(id: string): Promise<boolean>;
@@ -62,8 +62,8 @@ export declare function listWorksheets(classId: string): Promise<{
62
62
  updatedAt: Date;
63
63
  name: string;
64
64
  classId: string;
65
- sourceUrl: string | null;
66
65
  runJobId: string | null;
66
+ sourceUrl: string | null;
67
67
  }[]>;
68
68
  export declare function updateWorksheetRecord(worksheetId: string, userId: string, data: {
69
69
  name?: string;
@@ -74,8 +74,8 @@ export declare function updateWorksheetRecord(worksheetId: string, userId: strin
74
74
  updatedAt: Date;
75
75
  name: string;
76
76
  classId: string;
77
- sourceUrl: string | null;
78
77
  runJobId: string | null;
78
+ sourceUrl: string | null;
79
79
  }>;
80
80
  export declare function deleteWorksheetRecord(worksheetId: string, userId: string): Promise<{
81
81
  status: import(".prisma/client").$Enums.JobStatus | null;
@@ -84,8 +84,8 @@ export declare function deleteWorksheetRecord(worksheetId: string, userId: strin
84
84
  updatedAt: Date;
85
85
  name: string;
86
86
  classId: string;
87
- sourceUrl: string | null;
88
87
  runJobId: string | null;
88
+ sourceUrl: string | null;
89
89
  }>;
90
90
  export declare function createWorksheetRecord(classId: string, name: string): Promise<{
91
91
  status: import(".prisma/client").$Enums.JobStatus | null;
@@ -94,8 +94,8 @@ export declare function createWorksheetRecord(classId: string, name: string): Pr
94
94
  updatedAt: Date;
95
95
  name: string;
96
96
  classId: string;
97
- sourceUrl: string | null;
98
97
  runJobId: string | null;
98
+ sourceUrl: string | null;
99
99
  }>;
100
100
  /**
101
101
  * Duplicate a worksheet into another class. Caller must teach both the source worksheet's class
@@ -108,8 +108,8 @@ export declare function duplicateWorksheetRecord(worksheetId: string, classId: s
108
108
  updatedAt: Date;
109
109
  name: string;
110
110
  classId: string;
111
- sourceUrl: string | null;
112
111
  runJobId: string | null;
112
+ sourceUrl: string | null;
113
113
  }>;
114
114
  /**
115
115
  * Kick off PDF-to-worksheet conversion asynchronously.
@@ -157,17 +157,34 @@ export declare function addQuestionToWorksheet(worksheetId: string, userId: stri
157
157
  order?: number;
158
158
  }): Promise<{
159
159
  type: import(".prisma/client").$Enums.WorksheetQuestionType;
160
- options: import("@prisma/client/runtime/library.js").JsonValue | null;
161
- markScheme: import("@prisma/client/runtime/library.js").JsonValue | null;
160
+ options: Prisma.JsonValue | null;
161
+ markScheme: Prisma.JsonValue | null;
162
162
  id: string;
163
163
  createdAt: Date;
164
164
  updatedAt: Date;
165
- order: number | null;
165
+ points: number;
166
+ question: string;
166
167
  worksheetId: string;
168
+ order: number | null;
169
+ answer: string;
170
+ }>;
171
+ /** A question's full editable state as captured before an AI modifier run. */
172
+ export interface WorksheetQuestionSnapshot {
173
+ id: string;
174
+ type: WorksheetQuestionType;
167
175
  question: string;
168
176
  answer: string;
177
+ options: unknown;
178
+ markScheme: unknown;
169
179
  points: number;
170
- }>;
180
+ order: number | null;
181
+ }
182
+ /**
183
+ * Restore a worksheet's questions to a snapshot: deletes questions not in the
184
+ * snapshot, re-creates deleted ones (with their original ids), and reverts
185
+ * edited ones. Used by AI modifier undo.
186
+ */
187
+ export declare function restoreWorksheetQuestions(worksheetId: string, userId: string, questions: WorksheetQuestionSnapshot[]): Promise<void>;
171
188
  export declare function reorderWorksheetQuestions(worksheetId: string, userId: string, movedId: string, position: "before" | "after", targetId: string): Promise<{
172
189
  id: string;
173
190
  }[]>;
@@ -180,29 +197,29 @@ export declare function updateWorksheetQuestionRecord(worksheetId: string, quest
180
197
  type?: WorksheetQuestionType;
181
198
  }): Promise<{
182
199
  type: import(".prisma/client").$Enums.WorksheetQuestionType;
183
- options: import("@prisma/client/runtime/library.js").JsonValue | null;
184
- markScheme: import("@prisma/client/runtime/library.js").JsonValue | null;
200
+ options: Prisma.JsonValue | null;
201
+ markScheme: Prisma.JsonValue | null;
185
202
  id: string;
186
203
  createdAt: Date;
187
204
  updatedAt: Date;
188
- order: number | null;
189
- worksheetId: string;
205
+ points: number;
190
206
  question: string;
207
+ worksheetId: string;
208
+ order: number | null;
191
209
  answer: string;
192
- points: number;
193
210
  }>;
194
211
  export declare function deleteWorksheetQuestionRecord(worksheetId: string, questionId: string, userId: string): Promise<{
195
212
  type: import(".prisma/client").$Enums.WorksheetQuestionType;
196
- options: import("@prisma/client/runtime/library.js").JsonValue | null;
197
- markScheme: import("@prisma/client/runtime/library.js").JsonValue | null;
213
+ options: Prisma.JsonValue | null;
214
+ markScheme: Prisma.JsonValue | null;
198
215
  id: string;
199
216
  createdAt: Date;
200
217
  updatedAt: Date;
201
- order: number | null;
202
- worksheetId: string;
218
+ points: number;
203
219
  question: string;
220
+ worksheetId: string;
221
+ order: number | null;
204
222
  answer: string;
205
- points: number;
206
223
  }>;
207
224
  export declare function getWorksheetSubmission(worksheetId: string, submissionId: string, userId: string): Promise<{
208
225
  responses: ({
@@ -216,14 +233,14 @@ export declare function getWorksheetSubmission(worksheetId: string, submissionId
216
233
  createdAt: Date;
217
234
  updatedAt: Date | null;
218
235
  feedback: string | null;
219
- studentId: string;
220
236
  runJobId: string | null;
221
- points: number;
222
237
  response: string;
223
238
  isCorrect: boolean;
224
- markschemeState: import("@prisma/client/runtime/library.js").JsonValue | null;
225
- studentWorksheetResponseId: string | null;
239
+ markschemeState: Prisma.JsonValue | null;
240
+ points: number;
241
+ studentId: string;
226
242
  questionId: string;
243
+ studentWorksheetResponseId: string | null;
227
244
  })[];
228
245
  } & {
229
246
  id: string;
@@ -231,9 +248,9 @@ export declare function getWorksheetSubmission(worksheetId: string, submissionId
231
248
  updatedAt: Date;
232
249
  submissionId: string | null;
233
250
  studentId: string;
251
+ worksheetId: string;
234
252
  submittedAt: Date | null;
235
253
  submitted: boolean;
236
- worksheetId: string;
237
254
  }>;
238
255
  export declare function answerWorksheetQuestion(worksheetResponseId: string, questionId: string, response: string, userId: string): Promise<{
239
256
  responses: {
@@ -242,14 +259,14 @@ export declare function answerWorksheetQuestion(worksheetResponseId: string, que
242
259
  createdAt: Date;
243
260
  updatedAt: Date | null;
244
261
  feedback: string | null;
245
- studentId: string;
246
262
  runJobId: string | null;
247
- points: number;
248
263
  response: string;
249
264
  isCorrect: boolean;
250
- markschemeState: import("@prisma/client/runtime/library.js").JsonValue | null;
251
- studentWorksheetResponseId: string | null;
265
+ markschemeState: Prisma.JsonValue | null;
266
+ points: number;
267
+ studentId: string;
252
268
  questionId: string;
269
+ studentWorksheetResponseId: string | null;
253
270
  }[];
254
271
  } & {
255
272
  id: string;
@@ -257,9 +274,9 @@ export declare function answerWorksheetQuestion(worksheetResponseId: string, que
257
274
  updatedAt: Date;
258
275
  submissionId: string | null;
259
276
  studentId: string;
277
+ worksheetId: string;
260
278
  submittedAt: Date | null;
261
279
  submitted: boolean;
262
- worksheetId: string;
263
280
  }>;
264
281
  export declare function cancelGrading(worksheetResponseId: string, progressId: string, userId: string): Promise<{
265
282
  status: import(".prisma/client").$Enums.JobStatus | null;
@@ -267,14 +284,14 @@ export declare function cancelGrading(worksheetResponseId: string, progressId: s
267
284
  createdAt: Date;
268
285
  updatedAt: Date | null;
269
286
  feedback: string | null;
270
- studentId: string;
271
287
  runJobId: string | null;
272
- points: number;
273
288
  response: string;
274
289
  isCorrect: boolean;
275
- markschemeState: import("@prisma/client/runtime/library.js").JsonValue | null;
276
- studentWorksheetResponseId: string | null;
290
+ markschemeState: Prisma.JsonValue | null;
291
+ points: number;
292
+ studentId: string;
277
293
  questionId: string;
294
+ studentWorksheetResponseId: string | null;
278
295
  }>;
279
296
  export declare function regradeQuestion(worksheetResponseId: string, progressId: string, userId: string): Promise<{
280
297
  status: import(".prisma/client").$Enums.JobStatus | null;
@@ -282,14 +299,14 @@ export declare function regradeQuestion(worksheetResponseId: string, progressId:
282
299
  createdAt: Date;
283
300
  updatedAt: Date | null;
284
301
  feedback: string | null;
285
- studentId: string;
286
302
  runJobId: string | null;
287
- points: number;
288
303
  response: string;
289
304
  isCorrect: boolean;
290
- markschemeState: import("@prisma/client/runtime/library.js").JsonValue | null;
291
- studentWorksheetResponseId: string | null;
305
+ markschemeState: Prisma.JsonValue | null;
306
+ points: number;
307
+ studentId: string;
292
308
  questionId: string;
309
+ studentWorksheetResponseId: string | null;
293
310
  } | null>;
294
311
  export declare function gradeAnswer(questionId: string, studentWorksheetResponseId: string, userId: string, data: {
295
312
  responseId?: string;
@@ -304,14 +321,14 @@ export declare function gradeAnswer(questionId: string, studentWorksheetResponse
304
321
  createdAt: Date;
305
322
  updatedAt: Date | null;
306
323
  feedback: string | null;
307
- studentId: string;
308
324
  runJobId: string | null;
309
- points: number;
310
325
  response: string;
311
326
  isCorrect: boolean;
312
- markschemeState: import("@prisma/client/runtime/library.js").JsonValue | null;
313
- studentWorksheetResponseId: string | null;
327
+ markschemeState: Prisma.JsonValue | null;
328
+ points: number;
329
+ studentId: string;
314
330
  questionId: string;
331
+ studentWorksheetResponseId: string | null;
315
332
  }>;
316
333
  export declare function addCommentToResponse(responseId: string, comment: string, authorId: string): Promise<{
317
334
  id: string;
@@ -319,8 +336,8 @@ export declare function addCommentToResponse(responseId: string, comment: string
319
336
  content: string;
320
337
  assignmentId: string | null;
321
338
  submissionId: string | null;
322
- modifiedAt: Date | null;
323
339
  announcementId: string | null;
340
+ modifiedAt: Date | null;
324
341
  authorId: string;
325
342
  isPinned: boolean;
326
343
  parentCommentId: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"worksheet.d.ts","sourceRoot":"/","sources":["services/worksheet.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"worksheet.d.ts","sourceRoot":"/","sources":["services/worksheet.ts"],"names":[],"mappings":"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"}
@@ -3,9 +3,9 @@
3
3
  * Integrates with gradeWorksheet pipeline for auto-grading.
4
4
  */
5
5
 
6
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="d0f14b4e-0601-5f64-8c75-5790d8da194e")}catch(e){}}();
6
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="ecf79b03-756f-53ee-81b5-52a53a17b0e8")}catch(e){}}();
7
7
  import { TRPCError } from "@trpc/server";
8
- import { JobStatus } from "@prisma/client";
8
+ import { JobStatus, Prisma } from "@prisma/client";
9
9
  import { prisma } from "../lib/prisma.js";
10
10
  import { getFileUrl } from "../lib/fileUpload.js";
11
11
  import { cancelGradePipeline, regradeWorksheetPipeline } from "../pipelines/gradeWorksheet.js";
@@ -226,6 +226,51 @@ export async function addQuestionToWorksheet(worksheetId, userId, data) {
226
226
  ...data,
227
227
  });
228
228
  }
229
+ const toJsonInput = (value) => value === null || value === undefined
230
+ ? Prisma.JsonNull
231
+ : value;
232
+ /**
233
+ * Restore a worksheet's questions to a snapshot: deletes questions not in the
234
+ * snapshot, re-creates deleted ones (with their original ids), and reverts
235
+ * edited ones. Used by AI modifier undo.
236
+ */
237
+ export async function restoreWorksheetQuestions(worksheetId, userId, questions) {
238
+ const worksheet = await findWorksheetByIdForTeacher(worksheetId, userId);
239
+ if (!worksheet) {
240
+ throw new TRPCError({
241
+ code: "FORBIDDEN",
242
+ message: "Only class teachers can modify worksheet questions",
243
+ });
244
+ }
245
+ await prisma.$transaction([
246
+ prisma.worksheetQuestion.deleteMany({
247
+ where: { worksheetId, id: { notIn: questions.map((q) => q.id) } },
248
+ }),
249
+ ...questions.map((q) => prisma.worksheetQuestion.upsert({
250
+ where: { id: q.id },
251
+ create: {
252
+ id: q.id,
253
+ worksheetId,
254
+ type: q.type,
255
+ question: q.question,
256
+ answer: q.answer,
257
+ options: toJsonInput(q.options),
258
+ markScheme: toJsonInput(q.markScheme),
259
+ points: q.points,
260
+ order: q.order,
261
+ },
262
+ update: {
263
+ type: q.type,
264
+ question: q.question,
265
+ answer: q.answer,
266
+ options: toJsonInput(q.options),
267
+ markScheme: toJsonInput(q.markScheme),
268
+ points: q.points,
269
+ order: q.order,
270
+ },
271
+ })),
272
+ ]);
273
+ }
229
274
  export async function reorderWorksheetQuestions(worksheetId, userId, movedId, position, targetId) {
230
275
  const worksheet = await findWorksheetByIdForTeacher(worksheetId, userId);
231
276
  if (!worksheet) {
@@ -449,4 +494,4 @@ export async function addCommentToResponse(responseId, comment, authorId) {
449
494
  });
450
495
  }
451
496
  //# sourceMappingURL=worksheet.js.map
452
- //# debugId=d0f14b4e-0601-5f64-8c75-5790d8da194e
497
+ //# debugId=ecf79b03-756f-53ee-81b5-52a53a17b0e8