@anantagnanam/lms 0.1.0 → 0.2.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 (40) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +23 -2
  3. package/astro/client.ts +73 -151
  4. package/astro/components/CourseOverview.astro +25 -3
  5. package/astro/components/LearnerDashboard.astro +6 -0
  6. package/astro/components/LearningShell.astro +10 -1
  7. package/astro/components/LessonAvailabilityNote.astro +26 -0
  8. package/astro/components/LessonContent.astro +53 -0
  9. package/astro/components/LessonPlayer.astro +40 -61
  10. package/astro/components/MediaPlayer.astro +55 -4
  11. package/astro/media-player.ts +99 -0
  12. package/astro/pages/author-preview.astro +71 -0
  13. package/astro/pages/course.astro +10 -0
  14. package/astro/pages/lesson.astro +3 -0
  15. package/astro/pages/library.astro +173 -0
  16. package/astro/pages/paths.astro +73 -0
  17. package/astro/quiz-drafts.ts +87 -0
  18. package/astro/quiz-player.ts +362 -0
  19. package/astro/styles.css +12 -0
  20. package/dist/admin.d.ts +3 -1
  21. package/dist/admin.js +585 -116
  22. package/dist/admin.js.map +1 -1
  23. package/dist/astro.js +3 -0
  24. package/dist/astro.js.map +1 -1
  25. package/dist/{constants-C0kT94tc.js → constants-Cnx7k82Z.js} +25 -3
  26. package/dist/constants-Cnx7k82Z.js.map +1 -0
  27. package/dist/index.d.ts +1 -1
  28. package/dist/index.js +1 -1
  29. package/dist/runtime.d.ts +104 -12
  30. package/dist/runtime.js +896 -116
  31. package/dist/runtime.js.map +1 -1
  32. package/dist/schemas.d.ts +68 -1
  33. package/dist/schemas.js +34 -2
  34. package/dist/schemas.js.map +1 -1
  35. package/dist/server.d.ts +11 -1
  36. package/dist/server.js +13 -2
  37. package/dist/server.js.map +1 -1
  38. package/dist/types.d.ts +107 -4
  39. package/package.json +1 -1
  40. package/dist/constants-C0kT94tc.js.map +0 -1
package/dist/schemas.d.ts CHANGED
@@ -83,6 +83,12 @@ declare const lessonSchema: z.ZodObject<{
83
83
  field: z.ZodDefault<z.ZodString>;
84
84
  }, z.core.$strip>>>;
85
85
  mediaUrl: z.ZodDefault<z.ZodNullable<z.ZodString>>;
86
+ transcript: z.ZodDefault<z.ZodString>;
87
+ captions: z.ZodDefault<z.ZodArray<z.ZodObject<{
88
+ src: z.ZodString;
89
+ language: z.ZodString;
90
+ label: z.ZodString;
91
+ }, z.core.$strip>>>;
86
92
  liveStartsAt: z.ZodDefault<z.ZodNullable<z.ZodISODateTime>>;
87
93
  durationMinutes: z.ZodDefault<z.ZodNumber>;
88
94
  minStudySeconds: z.ZodDefault<z.ZodNumber>;
@@ -157,6 +163,12 @@ declare const moduleSchema: z.ZodObject<{
157
163
  field: z.ZodDefault<z.ZodString>;
158
164
  }, z.core.$strip>>>;
159
165
  mediaUrl: z.ZodDefault<z.ZodNullable<z.ZodString>>;
166
+ transcript: z.ZodDefault<z.ZodString>;
167
+ captions: z.ZodDefault<z.ZodArray<z.ZodObject<{
168
+ src: z.ZodString;
169
+ language: z.ZodString;
170
+ label: z.ZodString;
171
+ }, z.core.$strip>>>;
160
172
  liveStartsAt: z.ZodDefault<z.ZodNullable<z.ZodISODateTime>>;
161
173
  durationMinutes: z.ZodDefault<z.ZodNumber>;
162
174
  minStudySeconds: z.ZodDefault<z.ZodNumber>;
@@ -279,6 +291,12 @@ declare const courseDataSchema: z.ZodObject<{
279
291
  field: z.ZodDefault<z.ZodString>;
280
292
  }, z.core.$strip>>>;
281
293
  mediaUrl: z.ZodDefault<z.ZodNullable<z.ZodString>>;
294
+ transcript: z.ZodDefault<z.ZodString>;
295
+ captions: z.ZodDefault<z.ZodArray<z.ZodObject<{
296
+ src: z.ZodString;
297
+ language: z.ZodString;
298
+ label: z.ZodString;
299
+ }, z.core.$strip>>>;
282
300
  liveStartsAt: z.ZodDefault<z.ZodNullable<z.ZodISODateTime>>;
283
301
  durationMinutes: z.ZodDefault<z.ZodNumber>;
284
302
  minStudySeconds: z.ZodDefault<z.ZodNumber>;
@@ -405,6 +423,12 @@ declare const courseSaveSchema: z.ZodObject<{
405
423
  field: z.ZodDefault<z.ZodString>;
406
424
  }, z.core.$strip>>>;
407
425
  mediaUrl: z.ZodDefault<z.ZodNullable<z.ZodString>>;
426
+ transcript: z.ZodDefault<z.ZodString>;
427
+ captions: z.ZodDefault<z.ZodArray<z.ZodObject<{
428
+ src: z.ZodString;
429
+ language: z.ZodString;
430
+ label: z.ZodString;
431
+ }, z.core.$strip>>>;
408
432
  liveStartsAt: z.ZodDefault<z.ZodNullable<z.ZodISODateTime>>;
409
433
  durationMinutes: z.ZodDefault<z.ZodNumber>;
410
434
  minStudySeconds: z.ZodDefault<z.ZodNumber>;
@@ -486,6 +510,14 @@ declare const quizSubmitSchema: z.ZodObject<{
486
510
  lessonId: z.ZodString;
487
511
  attemptId: z.ZodString;
488
512
  answers: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
513
+ expectedDraftRevision: z.ZodOptional<z.ZodNumber>;
514
+ }, z.core.$strip>;
515
+ declare const quizDraftSchema: z.ZodObject<{
516
+ courseId: z.ZodString;
517
+ lessonId: z.ZodString;
518
+ attemptId: z.ZodString;
519
+ answers: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
520
+ expectedDraftRevision: z.ZodDefault<z.ZodNumber>;
489
521
  }, z.core.$strip>;
490
522
  declare const assignmentSubmitSchema: z.ZodObject<{
491
523
  courseId: z.ZodString;
@@ -579,6 +611,41 @@ declare const noteSchema: z.ZodObject<{
579
611
  lessonId: z.ZodString;
580
612
  note: z.ZodString;
581
613
  }, z.core.$strip>;
614
+ declare const preferencesSchema: z.ZodObject<{
615
+ emailNotifications: z.ZodDefault<z.ZodBoolean>;
616
+ weeklyGoalLessons: z.ZodDefault<z.ZodNumber>;
617
+ }, z.core.$strip>;
618
+ declare const playbackSchema: z.ZodObject<{
619
+ courseId: z.ZodString;
620
+ lessonId: z.ZodString;
621
+ positionSeconds: z.ZodNumber;
622
+ playbackRate: z.ZodNumber;
623
+ expectedRevision: z.ZodNumber;
624
+ }, z.core.$strip>;
625
+ declare const pathDataSchema: z.ZodObject<{
626
+ title: z.ZodString;
627
+ description: z.ZodDefault<z.ZodString>;
628
+ courseIds: z.ZodArray<z.ZodString>;
629
+ status: z.ZodDefault<z.ZodEnum<{
630
+ draft: "draft";
631
+ published: "published";
632
+ archived: "archived";
633
+ }>>;
634
+ }, z.core.$strip>;
635
+ declare const pathSaveSchema: z.ZodObject<{
636
+ id: z.ZodOptional<z.ZodString>;
637
+ expectedRevision: z.ZodDefault<z.ZodNullable<z.ZodString>>;
638
+ data: z.ZodObject<{
639
+ title: z.ZodString;
640
+ description: z.ZodDefault<z.ZodString>;
641
+ courseIds: z.ZodArray<z.ZodString>;
642
+ status: z.ZodDefault<z.ZodEnum<{
643
+ draft: "draft";
644
+ published: "published";
645
+ archived: "archived";
646
+ }>>;
647
+ }, z.core.$strip>;
648
+ }, z.core.$strip>;
582
649
  //#endregion
583
- export { answersSchema, assignmentSchema, assignmentSubmitSchema, catalogSchema, certificateActionSchema, certificateRevokeSchema, cohortDataSchema, cohortSaveSchema, courseDataSchema, courseIdSchema, courseSaveSchema, discussionSchema, enrollmentAdminSchema, enrollmentRequestSchema, gradeSchema, idSchema, keySchema, lessonIdSchema, lessonSchema, moderateSchema, moduleSchema, noteSchema, pageSchema, questionSchema, quizSchema, quizSubmitSchema, resourceSchema, reviewSchema, safeUrlSchema, settingsSchema };
650
+ export { answersSchema, assignmentSchema, assignmentSubmitSchema, catalogSchema, certificateActionSchema, certificateRevokeSchema, cohortDataSchema, cohortSaveSchema, courseDataSchema, courseIdSchema, courseSaveSchema, discussionSchema, enrollmentAdminSchema, enrollmentRequestSchema, gradeSchema, idSchema, keySchema, lessonIdSchema, lessonSchema, moderateSchema, moduleSchema, noteSchema, pageSchema, pathDataSchema, pathSaveSchema, playbackSchema, preferencesSchema, questionSchema, quizDraftSchema, quizSchema, quizSubmitSchema, resourceSchema, reviewSchema, safeUrlSchema, settingsSchema };
584
651
  //# sourceMappingURL=schemas.d.ts.map
package/dist/schemas.js CHANGED
@@ -111,6 +111,12 @@ const lessonSchema = z.object({
111
111
  field: idSchema.default("content")
112
112
  }).nullable().default(null),
113
113
  mediaUrl: safeUrlSchema.nullable().default(null),
114
+ transcript: text.default(""),
115
+ captions: z.array(z.object({
116
+ src: safeUrlSchema,
117
+ language: z.string().regex(/^[a-zA-Z]{2,3}(?:-[a-zA-Z0-9]{2,8})*$/).max(35),
118
+ label: title
119
+ })).max(20).default([]),
114
120
  liveStartsAt: timestamp.nullable().default(null),
115
121
  durationMinutes: z.number().int().min(0).max(1440).default(10),
116
122
  minStudySeconds: z.number().int().min(0).max(86400).default(0),
@@ -243,8 +249,10 @@ const lessonIdSchema = courseIdSchema.extend({ lessonId: idSchema });
243
249
  const answersSchema = z.record(idSchema, z.union([z.string().max(2e3), z.array(z.string().max(500)).max(10)]));
244
250
  const quizSubmitSchema = lessonIdSchema.extend({
245
251
  attemptId: idSchema,
246
- answers: answersSchema
252
+ answers: answersSchema,
253
+ expectedDraftRevision: z.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional()
247
254
  });
255
+ const quizDraftSchema = quizSubmitSchema.extend({ expectedDraftRevision: z.number().int().min(0).max(Number.MAX_SAFE_INTEGER).default(0) });
248
256
  const assignmentSubmitSchema = lessonIdSchema.extend({
249
257
  body: z.string().trim().min(1).max(2e4),
250
258
  attachmentUrl: safeUrlSchema.nullable().default(null)
@@ -309,7 +317,31 @@ const certificateRevokeSchema = courseIdSchema.extend({
309
317
  reason: z.string().trim().min(1).max(1e3)
310
318
  });
311
319
  const noteSchema = lessonIdSchema.extend({ note: z.string().max(1e4) });
320
+ const preferencesSchema = z.object({
321
+ emailNotifications: z.boolean().default(true),
322
+ weeklyGoalLessons: z.number().int().min(0).max(100).default(3)
323
+ });
324
+ const playbackSchema = lessonIdSchema.extend({
325
+ positionSeconds: z.number().finite().min(0).max(86400),
326
+ playbackRate: z.number().min(.25).max(3),
327
+ expectedRevision: z.number().int().min(0).max(Number.MAX_SAFE_INTEGER)
328
+ });
329
+ const pathDataSchema = z.object({
330
+ title,
331
+ description: z.string().max(5e3).default(""),
332
+ courseIds: z.array(idSchema).min(1).max(30).refine((ids) => new Set(ids).size === ids.length, "Courses must be unique."),
333
+ status: z.enum([
334
+ "draft",
335
+ "published",
336
+ "archived"
337
+ ]).default("draft")
338
+ });
339
+ const pathSaveSchema = z.object({
340
+ id: idSchema.optional(),
341
+ expectedRevision: z.string().max(128).nullable().default(null),
342
+ data: pathDataSchema
343
+ });
312
344
 
313
345
  //#endregion
314
- export { answersSchema, assignmentSchema, assignmentSubmitSchema, catalogSchema, certificateActionSchema, certificateRevokeSchema, cohortDataSchema, cohortSaveSchema, courseDataSchema, courseIdSchema, courseSaveSchema, discussionSchema, enrollmentAdminSchema, enrollmentRequestSchema, gradeSchema, idSchema, keySchema, lessonIdSchema, lessonSchema, moderateSchema, moduleSchema, noteSchema, pageSchema, questionSchema, quizSchema, quizSubmitSchema, resourceSchema, reviewSchema, safeUrlSchema, settingsSchema };
346
+ export { answersSchema, assignmentSchema, assignmentSubmitSchema, catalogSchema, certificateActionSchema, certificateRevokeSchema, cohortDataSchema, cohortSaveSchema, courseDataSchema, courseIdSchema, courseSaveSchema, discussionSchema, enrollmentAdminSchema, enrollmentRequestSchema, gradeSchema, idSchema, keySchema, lessonIdSchema, lessonSchema, moderateSchema, moduleSchema, noteSchema, pageSchema, pathDataSchema, pathSaveSchema, playbackSchema, preferencesSchema, questionSchema, quizDraftSchema, quizSchema, quizSubmitSchema, resourceSchema, reviewSchema, safeUrlSchema, settingsSchema };
315
347
  //# sourceMappingURL=schemas.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"schemas.js","names":[],"sources":["../src/schemas.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const idSchema = z\n .string()\n .min(1)\n .max(128)\n .regex(/^[a-zA-Z0-9_-]+$/, \"Use letters, numbers, underscores or hyphens.\")\n .refine(\n (value) => ![\"__proto__\", \"constructor\", \"prototype\"].includes(value),\n \"This identifier is reserved. Choose another ID.\",\n );\nexport const keySchema = z\n .string()\n .min(1)\n .max(1024)\n .regex(/^[a-zA-Z0-9_:.-]+$/);\nconst title = z.string().trim().min(1).max(200);\nconst text = z.string().max(50_000);\nconst timestamp = z.iso.datetime({ offset: true });\nexport const safeUrlSchema = z\n .string()\n .max(2048)\n .refine((value) => {\n if (/^\\/(?!\\/)[^\\s\\\\]*$/.test(value)) return true;\n try {\n const url = new URL(value);\n return url.protocol === \"https:\" && !url.username && !url.password;\n } catch {\n return false;\n }\n }, \"Use an HTTPS URL or a path on this site.\");\n\nexport const resourceSchema = z.object({\n id: idSchema,\n title,\n url: safeUrlSchema,\n description: z.string().max(1000).default(\"\"),\n});\nconst optionSchema = z.object({ id: idSchema, text: z.string().trim().min(1).max(2000) });\nexport const questionSchema = z\n .object({\n id: idSchema,\n type: z.enum([\"single\", \"multiple\", \"true_false\", \"short\"]),\n prompt: z.string().trim().min(1).max(5000),\n options: z.array(optionSchema).max(10).default([]),\n correctAnswers: z.array(z.string().trim().min(1).max(500)).min(1).max(10),\n points: z.number().int().min(1).max(100).default(1),\n explanation: z.string().max(5000).default(\"\"),\n })\n .superRefine((question, ctx) => {\n const optionIds = question.options.map((option) => option.id);\n if (new Set(optionIds).size !== optionIds.length)\n ctx.addIssue({ code: \"custom\", message: \"Option IDs must be unique.\" });\n if (new Set(question.correctAnswers).size !== question.correctAnswers.length)\n ctx.addIssue({ code: \"custom\", message: \"Correct answers must be unique.\" });\n if (question.type !== \"short\") {\n if (question.options.length < 2)\n ctx.addIssue({ code: \"custom\", message: \"Choice questions need at least two options.\" });\n if (question.correctAnswers.some((answer) => !optionIds.includes(answer)))\n ctx.addIssue({ code: \"custom\", message: \"Correct answers must refer to option IDs.\" });\n if (question.type !== \"multiple\" && question.correctAnswers.length !== 1)\n ctx.addIssue({ code: \"custom\", message: \"This question requires one correct answer.\" });\n if (\n question.type === \"true_false\" &&\n (question.options.length !== 2 ||\n !optionIds.includes(\"true\") ||\n !optionIds.includes(\"false\"))\n )\n ctx.addIssue({\n code: \"custom\",\n message: \"True/false options must have the IDs true and false.\",\n });\n }\n });\nexport const quizSchema = z\n .object({\n questions: z.array(questionSchema).min(1).max(50),\n passingPercent: z.number().int().min(1).max(100).default(70),\n maxAttempts: z.number().int().min(1).max(20).default(3),\n timeLimitMinutes: z.number().int().min(0).max(180).default(30),\n randomizeQuestions: z.boolean().default(true),\n randomizeOptions: z.boolean().default(false),\n showFeedback: z.enum([\"after_pass\", \"after_submit\", \"never\"]).default(\"after_pass\"),\n })\n .refine(\n (quiz) => new Set(quiz.questions.map((question) => question.id)).size === quiz.questions.length,\n \"Question IDs must be unique.\",\n );\n\nexport const assignmentSchema = z\n .object({\n instructions: z.string().trim().min(1).max(20_000),\n maxPoints: z.number().int().min(1).max(1000).default(100),\n passingPoints: z.number().int().min(1).max(1000).default(60),\n dueAt: timestamp.nullable().default(null),\n allowResubmission: z.boolean().default(true),\n })\n .refine(\n (assignment) => assignment.passingPoints <= assignment.maxPoints,\n \"Passing points cannot exceed maximum points.\",\n );\n\nexport const lessonSchema = z\n .object({\n id: idSchema,\n title,\n summary: z.string().max(2000).default(\"\"),\n kind: z.enum([\"text\", \"video\", \"audio\", \"quiz\", \"assignment\", \"live\"]).default(\"text\"),\n body: text.default(\"\"),\n portableText: z.array(z.record(z.string(), z.unknown())).max(1000).default([]),\n contentSource: z\n .object({ collection: idSchema, id: idSchema, field: idSchema.default(\"content\") })\n .nullable()\n .default(null),\n mediaUrl: safeUrlSchema.nullable().default(null),\n liveStartsAt: timestamp.nullable().default(null),\n durationMinutes: z.number().int().min(0).max(1440).default(10),\n minStudySeconds: z.number().int().min(0).max(86400).default(0),\n preview: z.boolean().default(false),\n required: z.boolean().default(true),\n resources: z.array(resourceSchema).max(30).default([]),\n quiz: quizSchema.nullable().default(null),\n assignment: assignmentSchema.nullable().default(null),\n })\n .superRefine((lesson, ctx) => {\n if (lesson.kind === \"quiz\" && !lesson.quiz)\n ctx.addIssue({ code: \"custom\", message: \"Quiz lessons need a quiz.\" });\n if (lesson.kind === \"assignment\" && !lesson.assignment)\n ctx.addIssue({ code: \"custom\", message: \"Assignment lessons need an assignment.\" });\n if ([\"video\", \"audio\", \"live\"].includes(lesson.kind) && !lesson.mediaUrl)\n ctx.addIssue({ code: \"custom\", message: \"Media and live lessons need a URL.\" });\n if (lesson.kind === \"live\" && !lesson.liveStartsAt)\n ctx.addIssue({ code: \"custom\", message: \"Live lessons need a start time.\" });\n if (new Set(lesson.resources.map((resource) => resource.id)).size !== lesson.resources.length)\n ctx.addIssue({ code: \"custom\", message: \"Resource IDs must be unique.\" });\n });\n\nexport const moduleSchema = z.object({\n id: idSchema,\n title,\n description: z.string().max(2000).default(\"\"),\n releaseDays: z.number().int().min(0).max(3650).default(0),\n unlockAt: timestamp.nullable().default(null),\n lessons: z.array(lessonSchema).max(100).default([]),\n});\n\nexport const courseDataSchema = z\n .object({\n slug: z\n .string()\n .trim()\n .min(1)\n .max(100)\n .regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, \"Use lowercase words separated by hyphens.\"),\n title,\n summary: z.string().trim().max(1000).default(\"\"),\n description: text.default(\"\"),\n outcomes: z.array(z.string().trim().min(1).max(500)).max(30).default([]),\n requirements: z.array(z.string().trim().min(1).max(500)).max(30).default([]),\n category: z.string().trim().max(100).default(\"\"),\n tags: z.array(z.string().trim().min(1).max(60)).max(20).default([]),\n language: z.string().trim().min(2).max(35).default(\"en\"),\n level: z.enum([\"beginner\", \"intermediate\", \"advanced\", \"all\"]).default(\"all\"),\n featuredImage: z\n .object({\n id: z.string().max(128).default(\"\"),\n src: safeUrlSchema,\n alt: z.string().max(500).default(\"\"),\n })\n .nullable()\n .default(null),\n instructorName: z.string().trim().max(200).default(\"\"),\n instructorIds: z.array(idSchema).max(20).default([]),\n status: z.enum([\"draft\", \"published\", \"archived\"]).default(\"draft\"),\n enrollmentMode: z.enum([\"open\", \"approval\", \"invite\"]).default(\"open\"),\n commerce: z\n .object({\n accessModel: z.enum([\"free\", \"paid\"]).default(\"free\"),\n offerId: idSchema.nullable().default(null),\n priceMinor: z.number().int().min(0).max(Number.MAX_SAFE_INTEGER).nullable().default(null),\n currency: z\n .string()\n .regex(/^[A-Z]{3}$/)\n .default(\"INR\"),\n })\n .default({ accessModel: \"free\", offerId: null, priceMinor: null, currency: \"INR\" }),\n prerequisiteCourseIds: z.array(idSchema).max(20).default([]),\n sequential: z.boolean().default(true),\n enrollmentClosesAt: timestamp.nullable().default(null),\n accessDays: z.number().int().min(0).max(3650).default(0),\n certificateEnabled: z.boolean().default(true),\n modules: z.array(moduleSchema).max(50).default([]),\n })\n .superRefine((course, ctx) => {\n const allIds = course.modules.flatMap((module) => [\n module.id,\n ...module.lessons.map((lesson) => lesson.id),\n ]);\n if (new Set(allIds).size !== allIds.length)\n ctx.addIssue({\n code: \"custom\",\n message: \"Module and lesson IDs must be unique across the course.\",\n });\n if (course.modules.flatMap((module) => module.lessons).length > 150)\n ctx.addIssue({ code: \"custom\", message: \"A course supports up to 150 lessons.\" });\n if (\n course.status === \"published\" &&\n !course.modules.some((module) => module.lessons.some((lesson) => lesson.required))\n )\n ctx.addIssue({\n code: \"custom\",\n message: \"Publish a course with at least one required lesson.\",\n });\n });\n\nexport const courseSaveSchema = z.object({\n id: idSchema.optional(),\n expectedRevision: z.string().max(128).nullable().default(null),\n data: courseDataSchema,\n});\nexport const pageSchema = z.object({\n cursor: z.string().max(4000).optional(),\n limit: z.coerce.number().int().min(1).max(100).default(24),\n});\nexport const catalogSchema = pageSchema.extend({\n q: z.string().max(100).default(\"\"),\n category: z.string().max(100).default(\"\"),\n level: z.enum([\"beginner\", \"intermediate\", \"advanced\", \"all\"]).optional(),\n language: z.string().max(35).optional(),\n});\nexport const courseIdSchema = z.object({ courseId: idSchema });\nexport const lessonIdSchema = courseIdSchema.extend({ lessonId: idSchema });\nexport const answersSchema = z.record(\n idSchema,\n z.union([z.string().max(2000), z.array(z.string().max(500)).max(10)]),\n);\nexport const quizSubmitSchema = lessonIdSchema.extend({\n attemptId: idSchema,\n answers: answersSchema,\n});\nexport const assignmentSubmitSchema = lessonIdSchema.extend({\n body: z.string().trim().min(1).max(20_000),\n attachmentUrl: safeUrlSchema.nullable().default(null),\n});\nexport const enrollmentRequestSchema = courseIdSchema.extend({ cohortId: idSchema.optional() });\nexport const enrollmentAdminSchema = courseIdSchema.extend({\n userId: idSchema,\n status: z.enum([\"active\", \"rejected\", \"suspended\", \"cancelled\"]),\n reason: z.string().max(1000).default(\"\"),\n cohortId: idSchema.nullable().optional(),\n accessStartsAt: timestamp.nullable().optional(),\n accessExpiresAt: timestamp.nullable().optional(),\n});\nexport const gradeSchema = lessonIdSchema.extend({\n userId: idSchema,\n submissionId: idSchema,\n points: z.number().int().min(0).max(1000),\n feedback: z.string().max(10_000).default(\"\"),\n});\nexport const discussionSchema = lessonIdSchema.extend({\n body: z.string().trim().min(1).max(5000),\n parentId: idSchema.nullable().default(null),\n});\nexport const reviewSchema = courseIdSchema.extend({\n rating: z.number().int().min(1).max(5),\n body: z.string().trim().min(1).max(3000),\n});\nexport const moderateSchema = z.object({\n id: keySchema,\n status: z.enum([\"published\", \"hidden\"]),\n expectedRevision: z.string().min(1).max(128),\n});\nexport const cohortDataSchema = z\n .object({\n title,\n courseIds: z.array(idSchema).min(1).max(20),\n memberIds: z.array(idSchema).max(1000).default([]),\n startsAt: timestamp.nullable().default(null),\n endsAt: timestamp.nullable().default(null),\n })\n .refine(\n (cohort) =>\n !cohort.startsAt || !cohort.endsAt || Date.parse(cohort.endsAt) > Date.parse(cohort.startsAt),\n \"Cohort end must follow its start.\",\n )\n .refine(\n (cohort) => new Set(cohort.memberIds).size === cohort.memberIds.length,\n \"Cohort members must be unique.\",\n )\n .refine(\n (cohort) => new Set(cohort.courseIds).size === cohort.courseIds.length,\n \"Cohort courses must be unique.\",\n );\nexport const cohortSaveSchema = z.object({\n id: idSchema.optional(),\n expectedRevision: z.string().max(128).nullable().default(null),\n data: cohortDataSchema,\n});\nexport const settingsSchema = z.object({\n academyTitle: title.default(\"Ananta Gnanam Academy\"),\n academyDescription: z.string().max(2000).default(\"Build knowledge, one lesson at a time.\"),\n issuerName: title.default(\"Ananta Gnanam\"),\n moderateDiscussions: z.boolean().default(true),\n moderateReviews: z.boolean().default(true),\n emailNotifications: z.boolean().default(false),\n reminderDays: z.number().int().min(0).max(90).default(7),\n});\nexport const certificateActionSchema = courseIdSchema.extend({ shared: z.boolean() });\nexport const certificateRevokeSchema = courseIdSchema.extend({\n userId: idSchema,\n reason: z.string().trim().min(1).max(1000),\n});\nexport const noteSchema = lessonIdSchema.extend({ note: z.string().max(10_000) });\n"],"mappings":"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"}
1
+ {"version":3,"file":"schemas.js","names":[],"sources":["../src/schemas.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const idSchema = z\n .string()\n .min(1)\n .max(128)\n .regex(/^[a-zA-Z0-9_-]+$/, \"Use letters, numbers, underscores or hyphens.\")\n .refine(\n (value) => ![\"__proto__\", \"constructor\", \"prototype\"].includes(value),\n \"This identifier is reserved. Choose another ID.\",\n );\nexport const keySchema = z\n .string()\n .min(1)\n .max(1024)\n .regex(/^[a-zA-Z0-9_:.-]+$/);\nconst title = z.string().trim().min(1).max(200);\nconst text = z.string().max(50_000);\nconst timestamp = z.iso.datetime({ offset: true });\nexport const safeUrlSchema = z\n .string()\n .max(2048)\n .refine((value) => {\n if (/^\\/(?!\\/)[^\\s\\\\]*$/.test(value)) return true;\n try {\n const url = new URL(value);\n return url.protocol === \"https:\" && !url.username && !url.password;\n } catch {\n return false;\n }\n }, \"Use an HTTPS URL or a path on this site.\");\n\nexport const resourceSchema = z.object({\n id: idSchema,\n title,\n url: safeUrlSchema,\n description: z.string().max(1000).default(\"\"),\n});\nconst optionSchema = z.object({ id: idSchema, text: z.string().trim().min(1).max(2000) });\nexport const questionSchema = z\n .object({\n id: idSchema,\n type: z.enum([\"single\", \"multiple\", \"true_false\", \"short\"]),\n prompt: z.string().trim().min(1).max(5000),\n options: z.array(optionSchema).max(10).default([]),\n correctAnswers: z.array(z.string().trim().min(1).max(500)).min(1).max(10),\n points: z.number().int().min(1).max(100).default(1),\n explanation: z.string().max(5000).default(\"\"),\n })\n .superRefine((question, ctx) => {\n const optionIds = question.options.map((option) => option.id);\n if (new Set(optionIds).size !== optionIds.length)\n ctx.addIssue({ code: \"custom\", message: \"Option IDs must be unique.\" });\n if (new Set(question.correctAnswers).size !== question.correctAnswers.length)\n ctx.addIssue({ code: \"custom\", message: \"Correct answers must be unique.\" });\n if (question.type !== \"short\") {\n if (question.options.length < 2)\n ctx.addIssue({ code: \"custom\", message: \"Choice questions need at least two options.\" });\n if (question.correctAnswers.some((answer) => !optionIds.includes(answer)))\n ctx.addIssue({ code: \"custom\", message: \"Correct answers must refer to option IDs.\" });\n if (question.type !== \"multiple\" && question.correctAnswers.length !== 1)\n ctx.addIssue({ code: \"custom\", message: \"This question requires one correct answer.\" });\n if (\n question.type === \"true_false\" &&\n (question.options.length !== 2 ||\n !optionIds.includes(\"true\") ||\n !optionIds.includes(\"false\"))\n )\n ctx.addIssue({\n code: \"custom\",\n message: \"True/false options must have the IDs true and false.\",\n });\n }\n });\nexport const quizSchema = z\n .object({\n questions: z.array(questionSchema).min(1).max(50),\n passingPercent: z.number().int().min(1).max(100).default(70),\n maxAttempts: z.number().int().min(1).max(20).default(3),\n timeLimitMinutes: z.number().int().min(0).max(180).default(30),\n randomizeQuestions: z.boolean().default(true),\n randomizeOptions: z.boolean().default(false),\n showFeedback: z.enum([\"after_pass\", \"after_submit\", \"never\"]).default(\"after_pass\"),\n })\n .refine(\n (quiz) => new Set(quiz.questions.map((question) => question.id)).size === quiz.questions.length,\n \"Question IDs must be unique.\",\n );\n\nexport const assignmentSchema = z\n .object({\n instructions: z.string().trim().min(1).max(20_000),\n maxPoints: z.number().int().min(1).max(1000).default(100),\n passingPoints: z.number().int().min(1).max(1000).default(60),\n dueAt: timestamp.nullable().default(null),\n allowResubmission: z.boolean().default(true),\n })\n .refine(\n (assignment) => assignment.passingPoints <= assignment.maxPoints,\n \"Passing points cannot exceed maximum points.\",\n );\n\nexport const lessonSchema = z\n .object({\n id: idSchema,\n title,\n summary: z.string().max(2000).default(\"\"),\n kind: z.enum([\"text\", \"video\", \"audio\", \"quiz\", \"assignment\", \"live\"]).default(\"text\"),\n body: text.default(\"\"),\n portableText: z.array(z.record(z.string(), z.unknown())).max(1000).default([]),\n contentSource: z\n .object({ collection: idSchema, id: idSchema, field: idSchema.default(\"content\") })\n .nullable()\n .default(null),\n mediaUrl: safeUrlSchema.nullable().default(null),\n transcript: text.default(\"\"),\n captions: z\n .array(\n z.object({\n src: safeUrlSchema,\n language: z\n .string()\n .regex(/^[a-zA-Z]{2,3}(?:-[a-zA-Z0-9]{2,8})*$/)\n .max(35),\n label: title,\n }),\n )\n .max(20)\n .default([]),\n liveStartsAt: timestamp.nullable().default(null),\n durationMinutes: z.number().int().min(0).max(1440).default(10),\n minStudySeconds: z.number().int().min(0).max(86400).default(0),\n preview: z.boolean().default(false),\n required: z.boolean().default(true),\n resources: z.array(resourceSchema).max(30).default([]),\n quiz: quizSchema.nullable().default(null),\n assignment: assignmentSchema.nullable().default(null),\n })\n .superRefine((lesson, ctx) => {\n if (lesson.kind === \"quiz\" && !lesson.quiz)\n ctx.addIssue({ code: \"custom\", message: \"Quiz lessons need a quiz.\" });\n if (lesson.kind === \"assignment\" && !lesson.assignment)\n ctx.addIssue({ code: \"custom\", message: \"Assignment lessons need an assignment.\" });\n if ([\"video\", \"audio\", \"live\"].includes(lesson.kind) && !lesson.mediaUrl)\n ctx.addIssue({ code: \"custom\", message: \"Media and live lessons need a URL.\" });\n if (lesson.kind === \"live\" && !lesson.liveStartsAt)\n ctx.addIssue({ code: \"custom\", message: \"Live lessons need a start time.\" });\n if (new Set(lesson.resources.map((resource) => resource.id)).size !== lesson.resources.length)\n ctx.addIssue({ code: \"custom\", message: \"Resource IDs must be unique.\" });\n });\n\nexport const moduleSchema = z.object({\n id: idSchema,\n title,\n description: z.string().max(2000).default(\"\"),\n releaseDays: z.number().int().min(0).max(3650).default(0),\n unlockAt: timestamp.nullable().default(null),\n lessons: z.array(lessonSchema).max(100).default([]),\n});\n\nexport const courseDataSchema = z\n .object({\n slug: z\n .string()\n .trim()\n .min(1)\n .max(100)\n .regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, \"Use lowercase words separated by hyphens.\"),\n title,\n summary: z.string().trim().max(1000).default(\"\"),\n description: text.default(\"\"),\n outcomes: z.array(z.string().trim().min(1).max(500)).max(30).default([]),\n requirements: z.array(z.string().trim().min(1).max(500)).max(30).default([]),\n category: z.string().trim().max(100).default(\"\"),\n tags: z.array(z.string().trim().min(1).max(60)).max(20).default([]),\n language: z.string().trim().min(2).max(35).default(\"en\"),\n level: z.enum([\"beginner\", \"intermediate\", \"advanced\", \"all\"]).default(\"all\"),\n featuredImage: z\n .object({\n id: z.string().max(128).default(\"\"),\n src: safeUrlSchema,\n alt: z.string().max(500).default(\"\"),\n })\n .nullable()\n .default(null),\n instructorName: z.string().trim().max(200).default(\"\"),\n instructorIds: z.array(idSchema).max(20).default([]),\n status: z.enum([\"draft\", \"published\", \"archived\"]).default(\"draft\"),\n enrollmentMode: z.enum([\"open\", \"approval\", \"invite\"]).default(\"open\"),\n commerce: z\n .object({\n accessModel: z.enum([\"free\", \"paid\"]).default(\"free\"),\n offerId: idSchema.nullable().default(null),\n priceMinor: z.number().int().min(0).max(Number.MAX_SAFE_INTEGER).nullable().default(null),\n currency: z\n .string()\n .regex(/^[A-Z]{3}$/)\n .default(\"INR\"),\n })\n .default({ accessModel: \"free\", offerId: null, priceMinor: null, currency: \"INR\" }),\n prerequisiteCourseIds: z.array(idSchema).max(20).default([]),\n sequential: z.boolean().default(true),\n enrollmentClosesAt: timestamp.nullable().default(null),\n accessDays: z.number().int().min(0).max(3650).default(0),\n certificateEnabled: z.boolean().default(true),\n modules: z.array(moduleSchema).max(50).default([]),\n })\n .superRefine((course, ctx) => {\n const allIds = course.modules.flatMap((module) => [\n module.id,\n ...module.lessons.map((lesson) => lesson.id),\n ]);\n if (new Set(allIds).size !== allIds.length)\n ctx.addIssue({\n code: \"custom\",\n message: \"Module and lesson IDs must be unique across the course.\",\n });\n if (course.modules.flatMap((module) => module.lessons).length > 150)\n ctx.addIssue({ code: \"custom\", message: \"A course supports up to 150 lessons.\" });\n if (\n course.status === \"published\" &&\n !course.modules.some((module) => module.lessons.some((lesson) => lesson.required))\n )\n ctx.addIssue({\n code: \"custom\",\n message: \"Publish a course with at least one required lesson.\",\n });\n });\n\nexport const courseSaveSchema = z.object({\n id: idSchema.optional(),\n expectedRevision: z.string().max(128).nullable().default(null),\n data: courseDataSchema,\n});\nexport const pageSchema = z.object({\n cursor: z.string().max(4000).optional(),\n limit: z.coerce.number().int().min(1).max(100).default(24),\n});\nexport const catalogSchema = pageSchema.extend({\n q: z.string().max(100).default(\"\"),\n category: z.string().max(100).default(\"\"),\n level: z.enum([\"beginner\", \"intermediate\", \"advanced\", \"all\"]).optional(),\n language: z.string().max(35).optional(),\n});\nexport const courseIdSchema = z.object({ courseId: idSchema });\nexport const lessonIdSchema = courseIdSchema.extend({ lessonId: idSchema });\nexport const answersSchema = z.record(\n idSchema,\n z.union([z.string().max(2000), z.array(z.string().max(500)).max(10)]),\n);\nexport const quizSubmitSchema = lessonIdSchema.extend({\n attemptId: idSchema,\n answers: answersSchema,\n expectedDraftRevision: z.number().int().min(0).max(Number.MAX_SAFE_INTEGER).optional(),\n});\nexport const quizDraftSchema = quizSubmitSchema.extend({\n expectedDraftRevision: z.number().int().min(0).max(Number.MAX_SAFE_INTEGER).default(0),\n});\nexport const assignmentSubmitSchema = lessonIdSchema.extend({\n body: z.string().trim().min(1).max(20_000),\n attachmentUrl: safeUrlSchema.nullable().default(null),\n});\nexport const enrollmentRequestSchema = courseIdSchema.extend({ cohortId: idSchema.optional() });\nexport const enrollmentAdminSchema = courseIdSchema.extend({\n userId: idSchema,\n status: z.enum([\"active\", \"rejected\", \"suspended\", \"cancelled\"]),\n reason: z.string().max(1000).default(\"\"),\n cohortId: idSchema.nullable().optional(),\n accessStartsAt: timestamp.nullable().optional(),\n accessExpiresAt: timestamp.nullable().optional(),\n});\nexport const gradeSchema = lessonIdSchema.extend({\n userId: idSchema,\n submissionId: idSchema,\n points: z.number().int().min(0).max(1000),\n feedback: z.string().max(10_000).default(\"\"),\n});\nexport const discussionSchema = lessonIdSchema.extend({\n body: z.string().trim().min(1).max(5000),\n parentId: idSchema.nullable().default(null),\n});\nexport const reviewSchema = courseIdSchema.extend({\n rating: z.number().int().min(1).max(5),\n body: z.string().trim().min(1).max(3000),\n});\nexport const moderateSchema = z.object({\n id: keySchema,\n status: z.enum([\"published\", \"hidden\"]),\n expectedRevision: z.string().min(1).max(128),\n});\nexport const cohortDataSchema = z\n .object({\n title,\n courseIds: z.array(idSchema).min(1).max(20),\n memberIds: z.array(idSchema).max(1000).default([]),\n startsAt: timestamp.nullable().default(null),\n endsAt: timestamp.nullable().default(null),\n })\n .refine(\n (cohort) =>\n !cohort.startsAt || !cohort.endsAt || Date.parse(cohort.endsAt) > Date.parse(cohort.startsAt),\n \"Cohort end must follow its start.\",\n )\n .refine(\n (cohort) => new Set(cohort.memberIds).size === cohort.memberIds.length,\n \"Cohort members must be unique.\",\n )\n .refine(\n (cohort) => new Set(cohort.courseIds).size === cohort.courseIds.length,\n \"Cohort courses must be unique.\",\n );\nexport const cohortSaveSchema = z.object({\n id: idSchema.optional(),\n expectedRevision: z.string().max(128).nullable().default(null),\n data: cohortDataSchema,\n});\nexport const settingsSchema = z.object({\n academyTitle: title.default(\"Ananta Gnanam Academy\"),\n academyDescription: z.string().max(2000).default(\"Build knowledge, one lesson at a time.\"),\n issuerName: title.default(\"Ananta Gnanam\"),\n moderateDiscussions: z.boolean().default(true),\n moderateReviews: z.boolean().default(true),\n emailNotifications: z.boolean().default(false),\n reminderDays: z.number().int().min(0).max(90).default(7),\n});\nexport const certificateActionSchema = courseIdSchema.extend({ shared: z.boolean() });\nexport const certificateRevokeSchema = courseIdSchema.extend({\n userId: idSchema,\n reason: z.string().trim().min(1).max(1000),\n});\nexport const noteSchema = lessonIdSchema.extend({ note: z.string().max(10_000) });\n\nexport const preferencesSchema = z.object({\n emailNotifications: z.boolean().default(true),\n weeklyGoalLessons: z.number().int().min(0).max(100).default(3),\n});\nexport const playbackSchema = lessonIdSchema.extend({\n positionSeconds: z.number().finite().min(0).max(86_400),\n playbackRate: z.number().min(0.25).max(3),\n expectedRevision: z.number().int().min(0).max(Number.MAX_SAFE_INTEGER),\n});\nexport const pathDataSchema = z.object({\n title,\n description: z.string().max(5000).default(\"\"),\n courseIds: z\n .array(idSchema)\n .min(1)\n .max(30)\n .refine((ids) => new Set(ids).size === ids.length, \"Courses must be unique.\"),\n status: z.enum([\"draft\", \"published\", \"archived\"]).default(\"draft\"),\n});\nexport const pathSaveSchema = z.object({\n id: idSchema.optional(),\n expectedRevision: z.string().max(128).nullable().default(null),\n data: pathDataSchema,\n});\n"],"mappings":"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"}
package/dist/server.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { CourseDetail, PublicLesson } from "./types.js";
2
+
1
3
  //#region src/errors.d.ts
2
4
  declare class LmsError extends Error {
3
5
  readonly code: string;
@@ -22,9 +24,17 @@ interface RuntimeLocals {
22
24
  declare function lmsPublic<T>(locals: RuntimeLocals, route: string, input: Record<string, unknown>, origin: string): Promise<T>;
23
25
  declare function lmsLearner<T>(locals: RuntimeLocals, route: string, input: Record<string, unknown>, origin: string): Promise<T>;
24
26
  declare function loginUrl(loginPath: string, returnTo: string): string;
27
+ /** A fixed private read route; authorization remains at the EmDash runtime boundary. */
28
+ declare function lmsAuthorLessonPreview(locals: RuntimeLocals, input: {
29
+ courseId: string;
30
+ lessonId: string;
31
+ }, origin: string): Promise<{
32
+ course: CourseDetail;
33
+ lesson: PublicLesson;
34
+ }>;
25
35
  declare function courseUrl(basePath: string, slug: string): string;
26
36
  declare function lessonUrl(basePath: string, slug: string, lessonId: string): string;
27
37
  declare function certificateUrl(basePath: string, id: string): string;
28
38
  //#endregion
29
- export { LmsError, certificateUrl, courseUrl, lessonUrl, lmsLearner, lmsPublic, loginUrl };
39
+ export { LmsError, certificateUrl, courseUrl, lessonUrl, lmsAuthorLessonPreview, lmsLearner, lmsPublic, loginUrl };
30
40
  //# sourceMappingURL=server.d.ts.map
package/dist/server.js CHANGED
@@ -1,4 +1,4 @@
1
- import { r as PLUGIN_ID } from "./constants-C0kT94tc.js";
1
+ import { r as PLUGIN_ID } from "./constants-Cnx7k82Z.js";
2
2
  import { t as LmsError } from "./errors-ChzJS9al.js";
3
3
 
4
4
  //#region src/server.ts
@@ -18,6 +18,9 @@ async function lmsPublic(locals, route, input, origin) {
18
18
  return unwrap(await dispatch(PLUGIN_ID, "GET", route, new Request(requestUrl(route, input, origin))));
19
19
  }
20
20
  const READ_ROUTES = new Set([
21
+ "learner/paths",
22
+ "learner/saved",
23
+ "learner/preferences",
21
24
  "learner/dashboard",
22
25
  "learner/course",
23
26
  "learner/course-by-slug",
@@ -39,6 +42,14 @@ function loginUrl(loginPath, returnTo) {
39
42
  url.searchParams.set("redirect", returnTo);
40
43
  return `${url.pathname}${url.search}${url.hash}`;
41
44
  }
45
+ /** A fixed private read route; authorization remains at the EmDash runtime boundary. */
46
+ async function lmsAuthorLessonPreview(locals, input, origin) {
47
+ if (!locals.user) throw new LmsError("UNAUTHORIZED", "Sign in to preview this lesson.", 401);
48
+ const dispatch = locals.emdash?.handlePluginApiRoute;
49
+ if (!dispatch) throw new LmsError("LMS_UNAVAILABLE", "The learning service is unavailable.", 503);
50
+ const route = "admin/lesson/preview";
51
+ return unwrap(await dispatch(PLUGIN_ID, "GET", route, new Request(requestUrl(route, input, origin)), locals.user));
52
+ }
42
53
  function courseUrl(basePath, slug) {
43
54
  return `${basePath}/courses/${encodeURIComponent(slug)}`;
44
55
  }
@@ -50,5 +61,5 @@ function certificateUrl(basePath, id) {
50
61
  }
51
62
 
52
63
  //#endregion
53
- export { LmsError, certificateUrl, courseUrl, lessonUrl, lmsLearner, lmsPublic, loginUrl };
64
+ export { LmsError, certificateUrl, courseUrl, lessonUrl, lmsAuthorLessonPreview, lmsLearner, lmsPublic, loginUrl };
54
65
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","names":[],"sources":["../src/server.ts"],"sourcesContent":["import { PLUGIN_ID } from \"./constants.js\";\nimport { LmsError } from \"./errors.js\";\nimport type { RouteEnvelope } from \"./types.js\";\n\ninterface RuntimeLocals {\n user?: { id: string; email: string; name: string | null; role: number; createdAt: Date | string };\n emdash?: {\n handlePublicPluginApiRoute?: (\n pluginId: string,\n method: string,\n path: string,\n request: Request,\n ) => Promise<unknown>;\n handlePluginApiRoute?: (\n pluginId: string,\n method: string,\n path: string,\n request: Request,\n user?: RuntimeLocals[\"user\"] | null,\n ) => Promise<unknown>;\n };\n}\n\nfunction requestUrl(route: string, input: Record<string, unknown>, origin: string): URL {\n const url = new URL(`/_emdash/api/plugins/${PLUGIN_ID}/${route}`, origin);\n for (const [key, value] of Object.entries(input))\n if (value !== undefined && value !== null && value !== \"\")\n url.searchParams.set(key, String(value));\n return url;\n}\nfunction unwrap<T>(input: unknown): T {\n const result = input as RouteEnvelope<T>;\n if (!result?.success)\n throw new LmsError(\n result?.error?.code ?? \"LMS_UNAVAILABLE\",\n result?.error?.message ?? \"The learning service is unavailable.\",\n result?.status ?? 503,\n );\n return result.data as T;\n}\nexport async function lmsPublic<T>(\n locals: RuntimeLocals,\n route: string,\n input: Record<string, unknown>,\n origin: string,\n): Promise<T> {\n const dispatch = locals.emdash?.handlePublicPluginApiRoute;\n if (!dispatch) throw new LmsError(\"LMS_UNAVAILABLE\", \"The learning service is unavailable.\", 503);\n return unwrap<T>(\n await dispatch(PLUGIN_ID, \"GET\", route, new Request(requestUrl(route, input, origin))),\n );\n}\nconst READ_ROUTES = new Set([\n \"learner/dashboard\",\n \"learner/course\",\n \"learner/course-by-slug\",\n \"learner/lesson\",\n \"learner/certificate\",\n \"learner/certificate-by-id\",\n \"learner/cohort\",\n \"learner/notifications\",\n]);\nexport async function lmsLearner<T>(\n locals: RuntimeLocals,\n route: string,\n input: Record<string, unknown>,\n origin: string,\n): Promise<T> {\n if (!locals.user || locals.user.role < 10)\n throw new LmsError(\"UNAUTHORIZED\", \"Sign in to continue.\", 401);\n if (!READ_ROUTES.has(route))\n throw new LmsError(\"FORBIDDEN\", \"This route is not available to the learner renderer.\", 403);\n const dispatch = locals.emdash?.handlePluginApiRoute;\n if (!dispatch) throw new LmsError(\"LMS_UNAVAILABLE\", \"The learning service is unavailable.\", 503);\n return unwrap<T>(\n await dispatch(\n PLUGIN_ID,\n \"GET\",\n route,\n new Request(requestUrl(route, input, origin)),\n locals.user,\n ),\n );\n}\nexport function loginUrl(loginPath: string, returnTo: string): string {\n const url = new URL(loginPath, \"https://lms.invalid\");\n url.searchParams.set(\"redirect\", returnTo);\n return `${url.pathname}${url.search}${url.hash}`;\n}\nexport function courseUrl(basePath: string, slug: string): string {\n return `${basePath}/courses/${encodeURIComponent(slug)}`;\n}\nexport function lessonUrl(basePath: string, slug: string, lessonId: string): string {\n return `${courseUrl(basePath, slug)}/lessons/${encodeURIComponent(lessonId)}`;\n}\nexport function certificateUrl(basePath: string, id: string): string {\n return `${basePath}/certificates/${encodeURIComponent(id)}`;\n}\nexport { LmsError } from \"./errors.js\";\n"],"mappings":";;;;AAuBA,SAAS,WAAW,OAAe,OAAgC,QAAqB;CACtF,MAAM,MAAM,IAAI,IAAI,wBAAwB,UAAU,GAAG,SAAS,OAAO;AACzE,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,CAC9C,KAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,GACrD,KAAI,aAAa,IAAI,KAAK,OAAO,MAAM,CAAC;AAC5C,QAAO;;AAET,SAAS,OAAU,OAAmB;CACpC,MAAM,SAAS;AACf,KAAI,CAAC,QAAQ,QACX,OAAM,IAAI,SACR,QAAQ,OAAO,QAAQ,mBACvB,QAAQ,OAAO,WAAW,wCAC1B,QAAQ,UAAU,IACnB;AACH,QAAO,OAAO;;AAEhB,eAAsB,UACpB,QACA,OACA,OACA,QACY;CACZ,MAAM,WAAW,OAAO,QAAQ;AAChC,KAAI,CAAC,SAAU,OAAM,IAAI,SAAS,mBAAmB,wCAAwC,IAAI;AACjG,QAAO,OACL,MAAM,SAAS,WAAW,OAAO,OAAO,IAAI,QAAQ,WAAW,OAAO,OAAO,OAAO,CAAC,CAAC,CACvF;;AAEH,MAAM,cAAc,IAAI,IAAI;CAC1B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AACF,eAAsB,WACpB,QACA,OACA,OACA,QACY;AACZ,KAAI,CAAC,OAAO,QAAQ,OAAO,KAAK,OAAO,GACrC,OAAM,IAAI,SAAS,gBAAgB,wBAAwB,IAAI;AACjE,KAAI,CAAC,YAAY,IAAI,MAAM,CACzB,OAAM,IAAI,SAAS,aAAa,wDAAwD,IAAI;CAC9F,MAAM,WAAW,OAAO,QAAQ;AAChC,KAAI,CAAC,SAAU,OAAM,IAAI,SAAS,mBAAmB,wCAAwC,IAAI;AACjG,QAAO,OACL,MAAM,SACJ,WACA,OACA,OACA,IAAI,QAAQ,WAAW,OAAO,OAAO,OAAO,CAAC,EAC7C,OAAO,KACR,CACF;;AAEH,SAAgB,SAAS,WAAmB,UAA0B;CACpE,MAAM,MAAM,IAAI,IAAI,WAAW,sBAAsB;AACrD,KAAI,aAAa,IAAI,YAAY,SAAS;AAC1C,QAAO,GAAG,IAAI,WAAW,IAAI,SAAS,IAAI;;AAE5C,SAAgB,UAAU,UAAkB,MAAsB;AAChE,QAAO,GAAG,SAAS,WAAW,mBAAmB,KAAK;;AAExD,SAAgB,UAAU,UAAkB,MAAc,UAA0B;AAClF,QAAO,GAAG,UAAU,UAAU,KAAK,CAAC,WAAW,mBAAmB,SAAS;;AAE7E,SAAgB,eAAe,UAAkB,IAAoB;AACnE,QAAO,GAAG,SAAS,gBAAgB,mBAAmB,GAAG"}
1
+ {"version":3,"file":"server.js","names":[],"sources":["../src/server.ts"],"sourcesContent":["import { PLUGIN_ID } from \"./constants.js\";\nimport { LmsError } from \"./errors.js\";\nimport type { CourseDetail, PublicLesson, RouteEnvelope } from \"./types.js\";\n\ninterface RuntimeLocals {\n user?: { id: string; email: string; name: string | null; role: number; createdAt: Date | string };\n emdash?: {\n handlePublicPluginApiRoute?: (\n pluginId: string,\n method: string,\n path: string,\n request: Request,\n ) => Promise<unknown>;\n handlePluginApiRoute?: (\n pluginId: string,\n method: string,\n path: string,\n request: Request,\n user?: RuntimeLocals[\"user\"] | null,\n ) => Promise<unknown>;\n };\n}\n\nfunction requestUrl(route: string, input: Record<string, unknown>, origin: string): URL {\n const url = new URL(`/_emdash/api/plugins/${PLUGIN_ID}/${route}`, origin);\n for (const [key, value] of Object.entries(input))\n if (value !== undefined && value !== null && value !== \"\")\n url.searchParams.set(key, String(value));\n return url;\n}\nfunction unwrap<T>(input: unknown): T {\n const result = input as RouteEnvelope<T>;\n if (!result?.success)\n throw new LmsError(\n result?.error?.code ?? \"LMS_UNAVAILABLE\",\n result?.error?.message ?? \"The learning service is unavailable.\",\n result?.status ?? 503,\n );\n return result.data as T;\n}\nexport async function lmsPublic<T>(\n locals: RuntimeLocals,\n route: string,\n input: Record<string, unknown>,\n origin: string,\n): Promise<T> {\n const dispatch = locals.emdash?.handlePublicPluginApiRoute;\n if (!dispatch) throw new LmsError(\"LMS_UNAVAILABLE\", \"The learning service is unavailable.\", 503);\n return unwrap<T>(\n await dispatch(PLUGIN_ID, \"GET\", route, new Request(requestUrl(route, input, origin))),\n );\n}\nconst READ_ROUTES = new Set([\n \"learner/paths\",\n \"learner/saved\",\n \"learner/preferences\",\n \"learner/dashboard\",\n \"learner/course\",\n \"learner/course-by-slug\",\n \"learner/lesson\",\n \"learner/certificate\",\n \"learner/certificate-by-id\",\n \"learner/cohort\",\n \"learner/notifications\",\n]);\nexport async function lmsLearner<T>(\n locals: RuntimeLocals,\n route: string,\n input: Record<string, unknown>,\n origin: string,\n): Promise<T> {\n if (!locals.user || locals.user.role < 10)\n throw new LmsError(\"UNAUTHORIZED\", \"Sign in to continue.\", 401);\n if (!READ_ROUTES.has(route))\n throw new LmsError(\"FORBIDDEN\", \"This route is not available to the learner renderer.\", 403);\n const dispatch = locals.emdash?.handlePluginApiRoute;\n if (!dispatch) throw new LmsError(\"LMS_UNAVAILABLE\", \"The learning service is unavailable.\", 503);\n return unwrap<T>(\n await dispatch(\n PLUGIN_ID,\n \"GET\",\n route,\n new Request(requestUrl(route, input, origin)),\n locals.user,\n ),\n );\n}\nexport function loginUrl(loginPath: string, returnTo: string): string {\n const url = new URL(loginPath, \"https://lms.invalid\");\n url.searchParams.set(\"redirect\", returnTo);\n return `${url.pathname}${url.search}${url.hash}`;\n}\n/** A fixed private read route; authorization remains at the EmDash runtime boundary. */\nexport async function lmsAuthorLessonPreview(\n locals: RuntimeLocals,\n input: { courseId: string; lessonId: string },\n origin: string,\n): Promise<{ course: CourseDetail; lesson: PublicLesson }> {\n if (!locals.user) throw new LmsError(\"UNAUTHORIZED\", \"Sign in to preview this lesson.\", 401);\n const dispatch = locals.emdash?.handlePluginApiRoute;\n if (!dispatch) throw new LmsError(\"LMS_UNAVAILABLE\", \"The learning service is unavailable.\", 503);\n const route = \"admin/lesson/preview\";\n return unwrap(\n await dispatch(\n PLUGIN_ID,\n \"GET\",\n route,\n new Request(requestUrl(route, input, origin)),\n locals.user,\n ),\n );\n}\nexport function courseUrl(basePath: string, slug: string): string {\n return `${basePath}/courses/${encodeURIComponent(slug)}`;\n}\nexport function lessonUrl(basePath: string, slug: string, lessonId: string): string {\n return `${courseUrl(basePath, slug)}/lessons/${encodeURIComponent(lessonId)}`;\n}\nexport function certificateUrl(basePath: string, id: string): string {\n return `${basePath}/certificates/${encodeURIComponent(id)}`;\n}\nexport { LmsError } from \"./errors.js\";\n"],"mappings":";;;;AAuBA,SAAS,WAAW,OAAe,OAAgC,QAAqB;CACtF,MAAM,MAAM,IAAI,IAAI,wBAAwB,UAAU,GAAG,SAAS,OAAO;AACzE,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,CAC9C,KAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,GACrD,KAAI,aAAa,IAAI,KAAK,OAAO,MAAM,CAAC;AAC5C,QAAO;;AAET,SAAS,OAAU,OAAmB;CACpC,MAAM,SAAS;AACf,KAAI,CAAC,QAAQ,QACX,OAAM,IAAI,SACR,QAAQ,OAAO,QAAQ,mBACvB,QAAQ,OAAO,WAAW,wCAC1B,QAAQ,UAAU,IACnB;AACH,QAAO,OAAO;;AAEhB,eAAsB,UACpB,QACA,OACA,OACA,QACY;CACZ,MAAM,WAAW,OAAO,QAAQ;AAChC,KAAI,CAAC,SAAU,OAAM,IAAI,SAAS,mBAAmB,wCAAwC,IAAI;AACjG,QAAO,OACL,MAAM,SAAS,WAAW,OAAO,OAAO,IAAI,QAAQ,WAAW,OAAO,OAAO,OAAO,CAAC,CAAC,CACvF;;AAEH,MAAM,cAAc,IAAI,IAAI;CAC1B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AACF,eAAsB,WACpB,QACA,OACA,OACA,QACY;AACZ,KAAI,CAAC,OAAO,QAAQ,OAAO,KAAK,OAAO,GACrC,OAAM,IAAI,SAAS,gBAAgB,wBAAwB,IAAI;AACjE,KAAI,CAAC,YAAY,IAAI,MAAM,CACzB,OAAM,IAAI,SAAS,aAAa,wDAAwD,IAAI;CAC9F,MAAM,WAAW,OAAO,QAAQ;AAChC,KAAI,CAAC,SAAU,OAAM,IAAI,SAAS,mBAAmB,wCAAwC,IAAI;AACjG,QAAO,OACL,MAAM,SACJ,WACA,OACA,OACA,IAAI,QAAQ,WAAW,OAAO,OAAO,OAAO,CAAC,EAC7C,OAAO,KACR,CACF;;AAEH,SAAgB,SAAS,WAAmB,UAA0B;CACpE,MAAM,MAAM,IAAI,IAAI,WAAW,sBAAsB;AACrD,KAAI,aAAa,IAAI,YAAY,SAAS;AAC1C,QAAO,GAAG,IAAI,WAAW,IAAI,SAAS,IAAI;;;AAG5C,eAAsB,uBACpB,QACA,OACA,QACyD;AACzD,KAAI,CAAC,OAAO,KAAM,OAAM,IAAI,SAAS,gBAAgB,mCAAmC,IAAI;CAC5F,MAAM,WAAW,OAAO,QAAQ;AAChC,KAAI,CAAC,SAAU,OAAM,IAAI,SAAS,mBAAmB,wCAAwC,IAAI;CACjG,MAAM,QAAQ;AACd,QAAO,OACL,MAAM,SACJ,WACA,OACA,OACA,IAAI,QAAQ,WAAW,OAAO,OAAO,OAAO,CAAC,EAC7C,OAAO,KACR,CACF;;AAEH,SAAgB,UAAU,UAAkB,MAAsB;AAChE,QAAO,GAAG,SAAS,WAAW,mBAAmB,KAAK;;AAExD,SAAgB,UAAU,UAAkB,MAAc,UAA0B;AAClF,QAAO,GAAG,UAAU,UAAU,KAAK,CAAC,WAAW,mBAAmB,SAAS;;AAE7E,SAAgB,eAAe,UAAkB,IAAoB;AACnE,QAAO,GAAG,SAAS,gBAAgB,mBAAmB,GAAG"}
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, settingsSchema } from "./schemas.js";
2
2
  import { z } from "zod";
3
3
 
4
4
  //#region src/types.d.ts
@@ -14,7 +14,39 @@ interface Course extends CourseData {
14
14
  createdBy: string;
15
15
  createdAt: string;
16
16
  updatedAt: string;
17
- schemaVersion: 1;
17
+ schemaVersion: 1 | 2;
18
+ draftRevisionId?: string;
19
+ publishedEditionId?: string;
20
+ publishedDraftRevisionId?: string;
21
+ legacyEditionId?: string;
22
+ }
23
+ interface CourseRevision {
24
+ id: string;
25
+ courseId: string;
26
+ parentId: string | null;
27
+ data: CourseData;
28
+ kind: "migration" | "draft" | "publish" | "restore" | "archive";
29
+ createdBy: string;
30
+ createdAt: string;
31
+ editionId?: string;
32
+ restoredFrom?: string;
33
+ }
34
+ type RevisionSummary = Omit<CourseRevision, "data"> & {
35
+ title: string;
36
+ };
37
+ interface CourseEdition {
38
+ id: string;
39
+ courseId: string;
40
+ data: CourseData;
41
+ createdAt: string;
42
+ }
43
+ interface MigrationState {
44
+ phase: "courses" | "enrollments" | "complete";
45
+ cursor?: string;
46
+ coursesProcessed: number;
47
+ enrollmentsProcessed: number;
48
+ updatedAt: string;
49
+ lastError: string | null;
18
50
  }
19
51
  interface Actor {
20
52
  id: string;
@@ -49,8 +81,12 @@ interface QuizAttempt {
49
81
  passed: boolean;
50
82
  feedback: QuestionResult[];
51
83
  answers: Answers;
84
+ /** Records created before draft fencing have revision zero. */
85
+ draftRevision?: number;
52
86
  }
53
- type PublicQuizAttempt = Omit<QuizAttempt, "quiz">;
87
+ type PublicQuizAttempt = Omit<QuizAttempt, "quiz" | "draftRevision"> & {
88
+ draftRevision: number;
89
+ };
54
90
  interface AssignmentSubmission {
55
91
  id: string;
56
92
  body: string;
@@ -70,6 +106,63 @@ interface LessonProgress {
70
106
  note: string;
71
107
  quizAttempts: QuizAttempt[];
72
108
  assignments: AssignmentSubmission[];
109
+ playback?: Playback;
110
+ }
111
+ interface Playback {
112
+ positionSeconds: number;
113
+ playbackRate: number;
114
+ revision: number;
115
+ updatedAt: string;
116
+ }
117
+ type LearnerPreferences = z.infer<typeof preferencesSchema>;
118
+ interface SavedCourse {
119
+ userId: string;
120
+ courseId: string;
121
+ saved: boolean;
122
+ updatedAt: string;
123
+ }
124
+ interface LearningPath extends z.infer<typeof pathDataSchema> {
125
+ id: string;
126
+ createdAt: string;
127
+ updatedAt: string;
128
+ }
129
+ interface PathEnrollment {
130
+ pathId: string;
131
+ userId: string;
132
+ title: string;
133
+ courseIds: string[];
134
+ joinedAt: string;
135
+ }
136
+ interface PublicPath {
137
+ id: string;
138
+ title: string;
139
+ description: string;
140
+ courses: (CourseCard | null)[];
141
+ }
142
+ interface LearnerPath {
143
+ pathId: string;
144
+ title: string;
145
+ joinedAt: string;
146
+ completed: number;
147
+ total: number;
148
+ courses: {
149
+ course: CourseCard | null;
150
+ completed: boolean;
151
+ }[];
152
+ }
153
+ interface LmsHealth {
154
+ version: string;
155
+ maintenance: {
156
+ startedAt: string;
157
+ completedAt: string | null;
158
+ failed: boolean;
159
+ } | null;
160
+ maintenanceStale: boolean;
161
+ pendingEmails: number;
162
+ failedEmails: number;
163
+ oldestPendingAt: string | null;
164
+ emailEnabled: boolean;
165
+ migration: MigrationState | null;
73
166
  }
74
167
  interface Certificate {
75
168
  id: string;
@@ -86,6 +179,8 @@ interface Certificate {
86
179
  interface Enrollment {
87
180
  id: string;
88
181
  courseId: string;
182
+ /** Immutable curriculum selected at enrollment; v1 records use the course's legacy edition. */
183
+ courseEditionId?: string;
89
184
  userId: string;
90
185
  learnerName: string;
91
186
  learnerEmail: string;
@@ -265,6 +360,14 @@ interface ProgressSummary {
265
360
  total: number;
266
361
  percent: number;
267
362
  nextLessonId: string | null;
363
+ blockedLessonId: string | null;
364
+ availability: Record<string, LessonAvailability>;
365
+ }
366
+ interface LessonAvailability {
367
+ available: boolean;
368
+ code: string | null;
369
+ reason: string | null;
370
+ releaseAt: string | null;
268
371
  }
269
372
  interface LmsOptions {
270
373
  basePath?: string;
@@ -280,5 +383,5 @@ interface RouteEnvelope<T> {
280
383
  };
281
384
  }
282
385
  //#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 };
386
+ export { AccessGrant, Acquisition, Actor, Answers, AssignmentSubmission, AuditRecord, Certificate, CertificateIndex, Cohort, CohortMembership, Course, CourseCard, CourseData, CourseDetail, CourseEdition, CourseModule, CourseRevision, CurriculumLesson, CurriculumModule, Discussion, Enrollment, LearnerPath, LearnerPreferences, LearningPath, Lesson, LessonAvailability, LessonProgress, LmsHealth, LmsOptions, LmsSettings, MigrationState, Notification, Page, PathEnrollment, Playback, ProgressSummary, PublicEnrollment, PublicLesson, PublicPath, PublicQuestion, PublicQuizAttempt, QuestionResult, Quiz, QuizAttempt, QuizQuestion, Review, RevisionSummary, RouteEnvelope, SavedCourse, Versioned };
284
387
  //# 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.2.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"}