@anantagnanam/lms 0.1.0 → 0.3.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 (51) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +40 -2
  3. package/astro/assignment-player.ts +231 -0
  4. package/astro/client.ts +97 -152
  5. package/astro/components/AssignmentFeedback.astro +32 -0
  6. package/astro/components/AssignmentRubric.astro +27 -0
  7. package/astro/components/CourseOverview.astro +25 -3
  8. package/astro/components/LearnerDashboard.astro +9 -0
  9. package/astro/components/LearningShell.astro +16 -1
  10. package/astro/components/LessonAvailabilityNote.astro +26 -0
  11. package/astro/components/LessonContent.astro +67 -0
  12. package/astro/components/LessonPlayer.astro +166 -99
  13. package/astro/components/MediaPlayer.astro +55 -4
  14. package/astro/draft-queue.ts +95 -0
  15. package/astro/media-player.ts +124 -0
  16. package/astro/pages/author-preview.astro +73 -0
  17. package/astro/pages/course.astro +10 -0
  18. package/astro/pages/lesson.astro +3 -0
  19. package/astro/pages/library.astro +173 -0
  20. package/astro/pages/paths.astro +73 -0
  21. package/astro/pages/study.astro +180 -0
  22. package/astro/quiz-drafts.ts +23 -0
  23. package/astro/quiz-player.ts +362 -0
  24. package/astro/study-tools.ts +386 -0
  25. package/astro/styles.css +30 -0
  26. package/astro/transcript-search.ts +56 -0
  27. package/dist/admin.d.ts +3 -1
  28. package/dist/admin.js +756 -127
  29. package/dist/admin.js.map +1 -1
  30. package/dist/assignments-CsuAjxGO.js +75 -0
  31. package/dist/assignments-CsuAjxGO.js.map +1 -0
  32. package/dist/astro.js +4 -0
  33. package/dist/astro.js.map +1 -1
  34. package/dist/{constants-C0kT94tc.js → constants-DyQbwNbP.js} +25 -3
  35. package/dist/constants-DyQbwNbP.js.map +1 -0
  36. package/dist/index.d.ts +1 -1
  37. package/dist/index.js +1 -1
  38. package/dist/runtime.d.ts +129 -16
  39. package/dist/runtime.js +1201 -130
  40. package/dist/runtime.js.map +1 -1
  41. package/dist/schemas.d.ts +136 -3
  42. package/dist/schemas.js +80 -7
  43. package/dist/schemas.js.map +1 -1
  44. package/dist/server.d.ts +18 -1
  45. package/dist/server.js +17 -3
  46. package/dist/server.js.map +1 -1
  47. package/dist/types.d.ts +172 -4
  48. package/package.json +1 -1
  49. package/dist/constants-C0kT94tc.js.map +0 -1
  50. package/dist/errors-ChzJS9al.js +0 -16
  51. package/dist/errors-ChzJS9al.js.map +0 -1
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { cohortDataSchema, courseDataSchema, lessonSchema, moduleSchema, questionSchema, quizSchema, settingsSchema } from "./schemas.js";
1
+ import { cohortDataSchema, courseDataSchema, lessonSchema, moduleSchema, pathDataSchema, preferencesSchema, questionSchema, quizSchema, rubricCriterionSchema, rubricScoreSchema, settingsSchema } from "./schemas.js";
2
2
  import { z } from "zod";
3
3
 
4
4
  //#region src/types.d.ts
@@ -7,6 +7,8 @@ type CourseModule = z.infer<typeof moduleSchema>;
7
7
  type Lesson = z.infer<typeof lessonSchema>;
8
8
  type QuizQuestion = z.infer<typeof questionSchema>;
9
9
  type Quiz = z.infer<typeof quizSchema>;
10
+ type RubricCriterion = z.infer<typeof rubricCriterionSchema>;
11
+ type RubricScore = z.infer<typeof rubricScoreSchema>;
10
12
  type LmsSettings = z.infer<typeof settingsSchema>;
11
13
  interface Course extends CourseData {
12
14
  id: string;
@@ -14,7 +16,39 @@ interface Course extends CourseData {
14
16
  createdBy: string;
15
17
  createdAt: string;
16
18
  updatedAt: string;
17
- schemaVersion: 1;
19
+ schemaVersion: 1 | 2;
20
+ draftRevisionId?: string;
21
+ publishedEditionId?: string;
22
+ publishedDraftRevisionId?: string;
23
+ legacyEditionId?: string;
24
+ }
25
+ interface CourseRevision {
26
+ id: string;
27
+ courseId: string;
28
+ parentId: string | null;
29
+ data: CourseData;
30
+ kind: "migration" | "draft" | "publish" | "restore" | "archive";
31
+ createdBy: string;
32
+ createdAt: string;
33
+ editionId?: string;
34
+ restoredFrom?: string;
35
+ }
36
+ type RevisionSummary = Omit<CourseRevision, "data"> & {
37
+ title: string;
38
+ };
39
+ interface CourseEdition {
40
+ id: string;
41
+ courseId: string;
42
+ data: CourseData;
43
+ createdAt: string;
44
+ }
45
+ interface MigrationState {
46
+ phase: "courses" | "enrollments" | "complete";
47
+ cursor?: string;
48
+ coursesProcessed: number;
49
+ enrollmentsProcessed: number;
50
+ updatedAt: string;
51
+ lastError: string | null;
18
52
  }
19
53
  interface Actor {
20
54
  id: string;
@@ -49,8 +83,12 @@ interface QuizAttempt {
49
83
  passed: boolean;
50
84
  feedback: QuestionResult[];
51
85
  answers: Answers;
86
+ /** Records created before draft fencing have revision zero. */
87
+ draftRevision?: number;
52
88
  }
53
- type PublicQuizAttempt = Omit<QuizAttempt, "quiz">;
89
+ type PublicQuizAttempt = Omit<QuizAttempt, "quiz" | "draftRevision"> & {
90
+ draftRevision: number;
91
+ };
54
92
  interface AssignmentSubmission {
55
93
  id: string;
56
94
  body: string;
@@ -63,13 +101,132 @@ interface AssignmentSubmission {
63
101
  feedback: string;
64
102
  gradedAt: string | null;
65
103
  gradedBy: string | null;
104
+ /** Criteria are frozen from the learner's course edition at submission. */
105
+ rubric?: RubricCriterion[];
106
+ rubricScores?: RubricScore[];
107
+ }
108
+ interface AssignmentDraft {
109
+ body: string;
110
+ attachmentUrl: string | null;
111
+ revision: number;
112
+ updatedAt: string | null;
113
+ }
114
+ interface AssignmentState {
115
+ draft: AssignmentDraft;
116
+ submission: AssignmentSubmission | null;
117
+ canSubmit: boolean;
118
+ closedReason: string | null;
66
119
  }
67
120
  interface LessonProgress {
68
121
  startedAt: string | null;
69
122
  completedAt: string | null;
70
123
  note: string;
124
+ noteRevision?: number;
71
125
  quizAttempts: QuizAttempt[];
72
126
  assignments: AssignmentSubmission[];
127
+ assignmentDraft?: AssignmentDraft;
128
+ playback?: Playback;
129
+ }
130
+ interface Playback {
131
+ positionSeconds: number;
132
+ playbackRate: number;
133
+ revision: number;
134
+ updatedAt: string;
135
+ }
136
+ interface StudyNote {
137
+ id: string;
138
+ lessonId: string;
139
+ lessonTitle: string;
140
+ body: string;
141
+ positionSeconds: number | null;
142
+ starred: boolean;
143
+ archived: boolean;
144
+ revision: number;
145
+ createdAt: string;
146
+ updatedAt: string;
147
+ }
148
+ interface Notebook {
149
+ course: {
150
+ id: string;
151
+ title: string;
152
+ slug: string;
153
+ };
154
+ notes: StudyNote[];
155
+ summaries: {
156
+ lessonId: string;
157
+ lessonTitle: string;
158
+ body: string;
159
+ }[];
160
+ availableLessonIds: string[];
161
+ }
162
+ interface CourseAgenda {
163
+ course: {
164
+ id: string;
165
+ title: string;
166
+ slug: string;
167
+ };
168
+ generatedAt: string;
169
+ events: {
170
+ id: string;
171
+ kind: "assignment" | "live" | "release";
172
+ lessonId: string;
173
+ title: string;
174
+ startsAt: string;
175
+ endsAt: string | null;
176
+ available: boolean;
177
+ reason: string | null;
178
+ status: "upcoming" | "overdue" | "submitted" | "completed" | "past";
179
+ }[];
180
+ }
181
+ type LearnerPreferences = z.infer<typeof preferencesSchema>;
182
+ interface SavedCourse {
183
+ userId: string;
184
+ courseId: string;
185
+ saved: boolean;
186
+ updatedAt: string;
187
+ }
188
+ interface LearningPath extends z.infer<typeof pathDataSchema> {
189
+ id: string;
190
+ createdAt: string;
191
+ updatedAt: string;
192
+ }
193
+ interface PathEnrollment {
194
+ pathId: string;
195
+ userId: string;
196
+ title: string;
197
+ courseIds: string[];
198
+ joinedAt: string;
199
+ }
200
+ interface PublicPath {
201
+ id: string;
202
+ title: string;
203
+ description: string;
204
+ courses: (CourseCard | null)[];
205
+ }
206
+ interface LearnerPath {
207
+ pathId: string;
208
+ title: string;
209
+ joinedAt: string;
210
+ completed: number;
211
+ total: number;
212
+ courses: {
213
+ course: CourseCard | null;
214
+ completed: boolean;
215
+ }[];
216
+ }
217
+ interface LmsHealth {
218
+ version: string;
219
+ maintenance: {
220
+ startedAt: string;
221
+ completedAt: string | null;
222
+ failed: boolean;
223
+ } | null;
224
+ maintenanceStale: boolean;
225
+ pendingEmails: number;
226
+ failedEmails: number;
227
+ oldestPendingAt: string | null;
228
+ emailEnabled: boolean;
229
+ migration: MigrationState | null;
73
230
  }
74
231
  interface Certificate {
75
232
  id: string;
@@ -86,6 +243,8 @@ interface Certificate {
86
243
  interface Enrollment {
87
244
  id: string;
88
245
  courseId: string;
246
+ /** Immutable curriculum selected at enrollment; v1 records use the course's legacy edition. */
247
+ courseEditionId?: string;
89
248
  userId: string;
90
249
  learnerName: string;
91
250
  learnerEmail: string;
@@ -105,6 +264,7 @@ interface Enrollment {
105
264
  accessExpiresAt: string | null;
106
265
  cohortId: string | null;
107
266
  progress: Record<string, LessonProgress>;
267
+ studyNotes?: StudyNote[];
108
268
  certificate: Certificate | null;
109
269
  completionLessonIds: string[];
110
270
  lastActivityAt: string;
@@ -265,6 +425,14 @@ interface ProgressSummary {
265
425
  total: number;
266
426
  percent: number;
267
427
  nextLessonId: string | null;
428
+ blockedLessonId: string | null;
429
+ availability: Record<string, LessonAvailability>;
430
+ }
431
+ interface LessonAvailability {
432
+ available: boolean;
433
+ code: string | null;
434
+ reason: string | null;
435
+ releaseAt: string | null;
268
436
  }
269
437
  interface LmsOptions {
270
438
  basePath?: string;
@@ -280,5 +448,5 @@ interface RouteEnvelope<T> {
280
448
  };
281
449
  }
282
450
  //#endregion
283
- export { AccessGrant, Acquisition, Actor, Answers, AssignmentSubmission, AuditRecord, Certificate, CertificateIndex, Cohort, CohortMembership, Course, CourseCard, CourseData, CourseDetail, CourseModule, CurriculumLesson, CurriculumModule, Discussion, Enrollment, Lesson, LessonProgress, LmsOptions, LmsSettings, Notification, Page, ProgressSummary, PublicEnrollment, PublicLesson, PublicQuestion, PublicQuizAttempt, QuestionResult, Quiz, QuizAttempt, QuizQuestion, Review, RouteEnvelope, Versioned };
451
+ export { AccessGrant, Acquisition, Actor, Answers, AssignmentDraft, AssignmentState, AssignmentSubmission, AuditRecord, Certificate, CertificateIndex, Cohort, CohortMembership, Course, CourseAgenda, CourseCard, CourseData, CourseDetail, CourseEdition, CourseModule, CourseRevision, CurriculumLesson, CurriculumModule, Discussion, Enrollment, LearnerPath, LearnerPreferences, LearningPath, Lesson, LessonAvailability, LessonProgress, LmsHealth, LmsOptions, LmsSettings, MigrationState, Notebook, Notification, Page, PathEnrollment, Playback, ProgressSummary, PublicEnrollment, PublicLesson, PublicPath, PublicQuestion, PublicQuizAttempt, QuestionResult, Quiz, QuizAttempt, QuizQuestion, Review, RevisionSummary, RouteEnvelope, RubricCriterion, RubricScore, SavedCourse, StudyNote, Versioned };
284
452
  //# sourceMappingURL=types.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anantagnanam/lms",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Courses, assessments, enrollment, progress, certificates and learning communities for EmDash CMS.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1 +0,0 @@
1
- {"version":3,"file":"constants-C0kT94tc.js","names":[],"sources":["../src/constants.ts"],"sourcesContent":["export const PLUGIN_ID = \"anantagnanam-lms\";\nexport const PLUGIN_VERSION = \"0.1.0\";\nexport const API_BASE = `/_emdash/api/plugins/${PLUGIN_ID}`;\nexport const MAX_DOCUMENT_BYTES = 950_000;\n\nimport type { PluginStorageConfig } from \"emdash\";\n\nexport const storage = {\n courses: {\n indexes: [\n \"status\",\n \"title\",\n \"searchTitle\",\n \"category\",\n \"level\",\n \"language\",\n \"updatedAt\",\n \"createdBy\",\n ],\n uniqueIndexes: [\"slug\"],\n },\n enrollments: {\n indexes: [\n \"userId\",\n \"courseId\",\n \"status\",\n \"accessStatus\",\n \"accessSource\",\n \"enrolledAt\",\n \"completedAt\",\n \"lastActivityAt\",\n \"cohortId\",\n [\"courseId\", \"status\"],\n [\"userId\", \"status\"],\n ],\n },\n certificates: { indexes: [\"userId\", \"courseId\"] },\n discussions: {\n indexes: [\n \"courseId\",\n \"lessonId\",\n \"authorId\",\n \"status\",\n \"createdAt\",\n \"parentId\",\n [\"courseId\", \"createdAt\"],\n ],\n },\n reviews: { indexes: [\"courseId\", \"userId\", \"status\", \"createdAt\"] },\n cohorts: { indexes: [\"title\", \"createdAt\", \"createdBy\"] },\n cohort_enrollments: { indexes: [\"cohortId\", \"userId\", \"status\", \"requestedAt\"] },\n notifications: { indexes: [\"userId\", \"read\", \"createdAt\", \"emailStatus\", \"claimedAt\"] },\n audit: { indexes: [\"actorId\", \"action\", \"resourceId\", \"createdAt\"] },\n} satisfies PluginStorageConfig;\n\nexport const adminPages = [\n { path: \"/\", label: \"Learning overview\", icon: \"graduation-cap\", group: \"Learning\" },\n { path: \"/courses\", label: \"Courses\", icon: \"books\", group: \"Learning\" },\n { path: \"/learners\", label: \"Enrollments\", icon: \"users\", group: \"Learning\" },\n { path: \"/assignments\", label: \"Assignments\", icon: \"exam\", group: \"Learning\" },\n { path: \"/community\", label: \"Community\", icon: \"chats\", group: \"Learning\" },\n { path: \"/cohorts\", label: \"Cohorts\", icon: \"users-three\", group: \"Learning\" },\n { path: \"/reports\", label: \"Reports\", icon: \"chart-bar\", group: \"Learning\" },\n { path: \"/notifications\", label: \"Notifications\", icon: \"bell\", group: \"Learning\" },\n { path: \"/audit\", label: \"Audit history\", icon: \"clock-counter-clockwise\", group: \"Learning\" },\n { path: \"/settings\", label: \"LMS settings\", icon: \"gear\", group: \"Learning\" },\n];\n"],"mappings":";AAAA,MAAa,YAAY;AACzB,MAAa,iBAAiB;AAC9B,MAAa,WAAW,wBAAwB;AAChD,MAAa,qBAAqB;AAIlC,MAAa,UAAU;CACrB,SAAS;EACP,SAAS;GACP;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD;EACD,eAAe,CAAC,OAAO;EACxB;CACD,aAAa,EACX,SAAS;EACP;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,CAAC,YAAY,SAAS;EACtB,CAAC,UAAU,SAAS;EACrB,EACF;CACD,cAAc,EAAE,SAAS,CAAC,UAAU,WAAW,EAAE;CACjD,aAAa,EACX,SAAS;EACP;EACA;EACA;EACA;EACA;EACA;EACA,CAAC,YAAY,YAAY;EAC1B,EACF;CACD,SAAS,EAAE,SAAS;EAAC;EAAY;EAAU;EAAU;EAAY,EAAE;CACnE,SAAS,EAAE,SAAS;EAAC;EAAS;EAAa;EAAY,EAAE;CACzD,oBAAoB,EAAE,SAAS;EAAC;EAAY;EAAU;EAAU;EAAc,EAAE;CAChF,eAAe,EAAE,SAAS;EAAC;EAAU;EAAQ;EAAa;EAAe;EAAY,EAAE;CACvF,OAAO,EAAE,SAAS;EAAC;EAAW;EAAU;EAAc;EAAY,EAAE;CACrE;AAED,MAAa,aAAa;CACxB;EAAE,MAAM;EAAK,OAAO;EAAqB,MAAM;EAAkB,OAAO;EAAY;CACpF;EAAE,MAAM;EAAY,OAAO;EAAW,MAAM;EAAS,OAAO;EAAY;CACxE;EAAE,MAAM;EAAa,OAAO;EAAe,MAAM;EAAS,OAAO;EAAY;CAC7E;EAAE,MAAM;EAAgB,OAAO;EAAe,MAAM;EAAQ,OAAO;EAAY;CAC/E;EAAE,MAAM;EAAc,OAAO;EAAa,MAAM;EAAS,OAAO;EAAY;CAC5E;EAAE,MAAM;EAAY,OAAO;EAAW,MAAM;EAAe,OAAO;EAAY;CAC9E;EAAE,MAAM;EAAY,OAAO;EAAW,MAAM;EAAa,OAAO;EAAY;CAC5E;EAAE,MAAM;EAAkB,OAAO;EAAiB,MAAM;EAAQ,OAAO;EAAY;CACnF;EAAE,MAAM;EAAU,OAAO;EAAiB,MAAM;EAA2B,OAAO;EAAY;CAC9F;EAAE,MAAM;EAAa,OAAO;EAAgB,MAAM;EAAQ,OAAO;EAAY;CAC9E"}
@@ -1,16 +0,0 @@
1
- //#region src/errors.ts
2
- var LmsError = class extends Error {
3
- constructor(code, message, status = 400) {
4
- super(message);
5
- this.code = code;
6
- this.status = status;
7
- this.name = "PluginRouteError";
8
- }
9
- };
10
- function invariant(condition, code, message, status = 400) {
11
- if (!condition) throw new LmsError(code, message, status);
12
- }
13
-
14
- //#endregion
15
- export { invariant as n, LmsError as t };
16
- //# sourceMappingURL=errors-ChzJS9al.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors-ChzJS9al.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["export class LmsError extends Error {\n constructor(\n readonly code: string,\n message: string,\n readonly status = 400,\n ) {\n super(message);\n this.name = \"PluginRouteError\";\n }\n}\n\nexport function invariant(\n condition: unknown,\n code: string,\n message: string,\n status = 400,\n): asserts condition {\n if (!condition) throw new LmsError(code, message, status);\n}\n"],"mappings":";AAAA,IAAa,WAAb,cAA8B,MAAM;CAClC,YACE,AAAS,MACT,SACA,AAAS,SAAS,KAClB;AACA,QAAM,QAAQ;EAJL;EAEA;AAGT,OAAK,OAAO;;;AAIhB,SAAgB,UACd,WACA,MACA,SACA,SAAS,KACU;AACnB,KAAI,CAAC,UAAW,OAAM,IAAI,SAAS,MAAM,SAAS,OAAO"}