@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
@@ -0,0 +1,75 @@
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
+ //#region src/assignments.ts
16
+ function emptyAssignmentDraft() {
17
+ return {
18
+ body: "",
19
+ attachmentUrl: null,
20
+ revision: 0,
21
+ updatedAt: null
22
+ };
23
+ }
24
+ function assignmentBlock(assignment, progress, now) {
25
+ if (progress.completedAt) return {
26
+ code: "ASSIGNMENT_PASSED",
27
+ message: "This assignment has already been passed.",
28
+ status: 409
29
+ };
30
+ if (assignment.dueAt && now.getTime() > Date.parse(assignment.dueAt)) return {
31
+ code: "ASSIGNMENT_CLOSED",
32
+ message: "The assignment deadline has passed.",
33
+ status: 403
34
+ };
35
+ const last = progress.assignments.at(-1);
36
+ if (last && (last.status !== "graded" || !assignment.allowResubmission)) return {
37
+ code: "SUBMISSION_PENDING",
38
+ message: last.status === "submitted" ? "Wait for grading before submitting again." : "Resubmission is not enabled for this assignment.",
39
+ status: 409
40
+ };
41
+ if (progress.assignments.length >= 10) return {
42
+ code: "SUBMISSION_LIMIT",
43
+ message: "The assignment submission limit has been reached.",
44
+ status: 403
45
+ };
46
+ return null;
47
+ }
48
+ /** Only the submission's snapshot can determine the score, never the latest course draft. */
49
+ function assignmentGrade(submission, points, scores = []) {
50
+ const rubric = submission.rubric ?? [];
51
+ if (!rubric.length) {
52
+ invariant(!scores.length, "INVALID_GRADE", "This submission has no grading rubric.");
53
+ invariant(points !== void 0 && points <= submission.maxPoints, "INVALID_GRADE", "Supply a score within this submission's maximum points.");
54
+ return {
55
+ points,
56
+ rubricScores: []
57
+ };
58
+ }
59
+ invariant(scores.length === rubric.length && new Set(scores.map((item) => item.criterionId)).size === rubric.length, "INVALID_GRADE", "Score each rubric criterion exactly once.");
60
+ const rubricScores = rubric.map((criterion) => {
61
+ const score = scores.find((item) => item.criterionId === criterion.id);
62
+ invariant(score && score.points <= criterion.maxPoints, "INVALID_GRADE", "Each score must match a rubric criterion and stay within its maximum.");
63
+ return score;
64
+ });
65
+ const total = rubricScores.reduce((sum, score) => sum + score.points, 0);
66
+ invariant(points === void 0 || points === total, "INVALID_GRADE", "Total points must match the rubric scores.");
67
+ return {
68
+ points: total,
69
+ rubricScores
70
+ };
71
+ }
72
+
73
+ //#endregion
74
+ export { invariant as a, LmsError as i, assignmentGrade as n, emptyAssignmentDraft as r, assignmentBlock as t };
75
+ //# sourceMappingURL=assignments-CsuAjxGO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assignments-CsuAjxGO.js","names":[],"sources":["../src/errors.ts","../src/assignments.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","import type {\n AssignmentDraft,\n AssignmentSubmission,\n Lesson,\n LessonProgress,\n RubricScore,\n} from \"./types.js\";\nimport { invariant } from \"./errors.js\";\n\nexport function emptyAssignmentDraft(): AssignmentDraft {\n return { body: \"\", attachmentUrl: null, revision: 0, updatedAt: null };\n}\n\nexport function assignmentBlock(\n assignment: NonNullable<Lesson[\"assignment\"]>,\n progress: Pick<LessonProgress, \"completedAt\" | \"assignments\">,\n now: Date,\n) {\n if (progress.completedAt)\n return {\n code: \"ASSIGNMENT_PASSED\",\n message: \"This assignment has already been passed.\",\n status: 409,\n };\n if (assignment.dueAt && now.getTime() > Date.parse(assignment.dueAt))\n return {\n code: \"ASSIGNMENT_CLOSED\",\n message: \"The assignment deadline has passed.\",\n status: 403,\n };\n const last = progress.assignments.at(-1);\n if (last && (last.status !== \"graded\" || !assignment.allowResubmission))\n return {\n code: \"SUBMISSION_PENDING\",\n message:\n last.status === \"submitted\"\n ? \"Wait for grading before submitting again.\"\n : \"Resubmission is not enabled for this assignment.\",\n status: 409,\n };\n if (progress.assignments.length >= 10)\n return {\n code: \"SUBMISSION_LIMIT\",\n message: \"The assignment submission limit has been reached.\",\n status: 403,\n };\n return null;\n}\n\n/** Only the submission's snapshot can determine the score, never the latest course draft. */\nexport function assignmentGrade(\n submission: AssignmentSubmission,\n points: number | undefined,\n scores: RubricScore[] = [],\n) {\n const rubric = submission.rubric ?? [];\n if (!rubric.length) {\n invariant(!scores.length, \"INVALID_GRADE\", \"This submission has no grading rubric.\");\n invariant(\n points !== undefined && points <= submission.maxPoints,\n \"INVALID_GRADE\",\n \"Supply a score within this submission's maximum points.\",\n );\n return { points, rubricScores: [] };\n }\n invariant(\n scores.length === rubric.length &&\n new Set(scores.map((item) => item.criterionId)).size === rubric.length,\n \"INVALID_GRADE\",\n \"Score each rubric criterion exactly once.\",\n );\n const rubricScores = rubric.map((criterion) => {\n const score = scores.find((item) => item.criterionId === criterion.id);\n invariant(\n score && score.points <= criterion.maxPoints,\n \"INVALID_GRADE\",\n \"Each score must match a rubric criterion and stay within its maximum.\",\n );\n return score;\n });\n const total = rubricScores.reduce((sum, score) => sum + score.points, 0);\n invariant(\n points === undefined || points === total,\n \"INVALID_GRADE\",\n \"Total points must match the rubric scores.\",\n );\n return { points: total, rubricScores };\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;;;;;ACR3D,SAAgB,uBAAwC;AACtD,QAAO;EAAE,MAAM;EAAI,eAAe;EAAM,UAAU;EAAG,WAAW;EAAM;;AAGxE,SAAgB,gBACd,YACA,UACA,KACA;AACA,KAAI,SAAS,YACX,QAAO;EACL,MAAM;EACN,SAAS;EACT,QAAQ;EACT;AACH,KAAI,WAAW,SAAS,IAAI,SAAS,GAAG,KAAK,MAAM,WAAW,MAAM,CAClE,QAAO;EACL,MAAM;EACN,SAAS;EACT,QAAQ;EACT;CACH,MAAM,OAAO,SAAS,YAAY,GAAG,GAAG;AACxC,KAAI,SAAS,KAAK,WAAW,YAAY,CAAC,WAAW,mBACnD,QAAO;EACL,MAAM;EACN,SACE,KAAK,WAAW,cACZ,8CACA;EACN,QAAQ;EACT;AACH,KAAI,SAAS,YAAY,UAAU,GACjC,QAAO;EACL,MAAM;EACN,SAAS;EACT,QAAQ;EACT;AACH,QAAO;;;AAIT,SAAgB,gBACd,YACA,QACA,SAAwB,EAAE,EAC1B;CACA,MAAM,SAAS,WAAW,UAAU,EAAE;AACtC,KAAI,CAAC,OAAO,QAAQ;AAClB,YAAU,CAAC,OAAO,QAAQ,iBAAiB,yCAAyC;AACpF,YACE,WAAW,UAAa,UAAU,WAAW,WAC7C,iBACA,0DACD;AACD,SAAO;GAAE;GAAQ,cAAc,EAAE;GAAE;;AAErC,WACE,OAAO,WAAW,OAAO,UACvB,IAAI,IAAI,OAAO,KAAK,SAAS,KAAK,YAAY,CAAC,CAAC,SAAS,OAAO,QAClE,iBACA,4CACD;CACD,MAAM,eAAe,OAAO,KAAK,cAAc;EAC7C,MAAM,QAAQ,OAAO,MAAM,SAAS,KAAK,gBAAgB,UAAU,GAAG;AACtE,YACE,SAAS,MAAM,UAAU,UAAU,WACnC,iBACA,wEACD;AACD,SAAO;GACP;CACF,MAAM,QAAQ,aAAa,QAAQ,KAAK,UAAU,MAAM,MAAM,QAAQ,EAAE;AACxE,WACE,WAAW,UAAa,WAAW,OACnC,iBACA,6CACD;AACD,QAAO;EAAE,QAAQ;EAAO;EAAc"}
package/dist/astro.js CHANGED
@@ -24,8 +24,12 @@ function lmsAstro(options = {}) {
24
24
  [`${basePath}/courses/[slug]`, "course.astro"],
25
25
  [`${basePath}/courses/[slug]/lessons/[lessonId]`, "lesson.astro"],
26
26
  [`${basePath}/my-learning`, "dashboard.astro"],
27
+ [`${basePath}/my-learning/library`, "library.astro"],
28
+ [`${basePath}/my-learning/study`, "study.astro"],
29
+ [`${basePath}/paths`, "paths.astro"],
27
30
  [`${basePath}/certificates/[certificateId]`, "certificate.astro"],
28
31
  [`${basePath}/teach`, "teach.astro"],
32
+ [`${basePath}/teach/courses/[courseId]/lessons/[lessonId]`, "author-preview.astro"],
29
33
  [`${basePath}/cohorts/[cohortId]`, "cohort.astro"]
30
34
  ];
31
35
  for (const [pattern, file] of pages) injectRoute({
package/dist/astro.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"astro.js","names":[],"sources":["../src/astro.ts"],"sourcesContent":["import type { AstroIntegration } from \"astro\";\nimport { fileURLToPath } from \"node:url\";\nimport type { LmsOptions } from \"./types.js\";\nimport { normalizeLmsOptions } from \"./config.js\";\n\nexport interface LmsAstroOptions extends LmsOptions {\n layout?: string;\n}\n\nexport function lmsAstro(options: LmsAstroOptions = {}): AstroIntegration {\n const { basePath, loginPath } = normalizeLmsOptions(options);\n return {\n name: \"@anantagnanam/lms\",\n hooks: {\n \"astro:config:setup\": ({ config, injectRoute, updateConfig }) => {\n if (config.output !== \"server\") throw new Error(\"The LMS requires Astro output: server.\");\n const layouts = options.layout\n ? fileURLToPath(new URL(options.layout, config.root))\n : fileURLToPath(new URL(\"../astro/layouts/AcademyLayout.astro\", import.meta.url));\n const virtual = {\n \"virtual:anantagnanam-lms/config\": `export const basePath = ${JSON.stringify(basePath)}; export const loginPath = ${JSON.stringify(loginPath)};`,\n \"virtual:anantagnanam-lms/layout\": `export { default } from ${JSON.stringify(layouts.replaceAll(\"\\\\\", \"/\"))};`,\n };\n updateConfig({\n vite: {\n plugins: [\n {\n name: \"anantagnanam-lms-pages\",\n resolveId: (id) => (id in virtual ? `\\0${id}` : undefined),\n load: (id) => virtual[id.replace(/^\\0/, \"\") as keyof typeof virtual],\n },\n ],\n },\n });\n const pages = [\n [basePath, \"index.astro\"],\n [`${basePath}/courses/[slug]`, \"course.astro\"],\n [`${basePath}/courses/[slug]/lessons/[lessonId]`, \"lesson.astro\"],\n [`${basePath}/my-learning`, \"dashboard.astro\"],\n [`${basePath}/certificates/[certificateId]`, \"certificate.astro\"],\n [`${basePath}/teach`, \"teach.astro\"],\n [`${basePath}/cohorts/[cohortId]`, \"cohort.astro\"],\n ];\n for (const [pattern, file] of pages)\n injectRoute({\n pattern: pattern!,\n entrypoint: fileURLToPath(new URL(`../astro/pages/${file}`, import.meta.url)),\n prerender: false,\n });\n },\n \"astro:config:done\": ({ injectTypes }) => {\n injectTypes({\n filename: \"anantagnanam-lms.d.ts\",\n content:\n 'declare module \"virtual:anantagnanam-lms/config\" { export const basePath: string; export const loginPath: string; }\\ndeclare module \"virtual:anantagnanam-lms/layout\" { const Layout: import(\"astro/runtime/server/index.js\").AstroComponentFactory; export default Layout; }',\n });\n },\n },\n };\n}\nexport default lmsAstro;\n"],"mappings":";;;;AASA,SAAgB,SAAS,UAA2B,EAAE,EAAoB;CACxE,MAAM,EAAE,UAAU,cAAc,oBAAoB,QAAQ;AAC5D,QAAO;EACL,MAAM;EACN,OAAO;GACL,uBAAuB,EAAE,QAAQ,aAAa,mBAAmB;AAC/D,QAAI,OAAO,WAAW,SAAU,OAAM,IAAI,MAAM,yCAAyC;IACzF,MAAM,UAAU,QAAQ,SACpB,cAAc,IAAI,IAAI,QAAQ,QAAQ,OAAO,KAAK,CAAC,GACnD,cAAc,IAAI,IAAI,wCAAwC,OAAO,KAAK,IAAI,CAAC;IACnF,MAAM,UAAU;KACd,mCAAmC,2BAA2B,KAAK,UAAU,SAAS,CAAC,6BAA6B,KAAK,UAAU,UAAU,CAAC;KAC9I,mCAAmC,2BAA2B,KAAK,UAAU,QAAQ,WAAW,MAAM,IAAI,CAAC,CAAC;KAC7G;AACD,iBAAa,EACX,MAAM,EACJ,SAAS,CACP;KACE,MAAM;KACN,YAAY,OAAQ,MAAM,UAAU,KAAK,OAAO;KAChD,OAAO,OAAO,QAAQ,GAAG,QAAQ,OAAO,GAAG;KAC5C,CACF,EACF,EACF,CAAC;IACF,MAAM,QAAQ;KACZ,CAAC,UAAU,cAAc;KACzB,CAAC,GAAG,SAAS,kBAAkB,eAAe;KAC9C,CAAC,GAAG,SAAS,qCAAqC,eAAe;KACjE,CAAC,GAAG,SAAS,eAAe,kBAAkB;KAC9C,CAAC,GAAG,SAAS,gCAAgC,oBAAoB;KACjE,CAAC,GAAG,SAAS,SAAS,cAAc;KACpC,CAAC,GAAG,SAAS,sBAAsB,eAAe;KACnD;AACD,SAAK,MAAM,CAAC,SAAS,SAAS,MAC5B,aAAY;KACD;KACT,YAAY,cAAc,IAAI,IAAI,kBAAkB,QAAQ,OAAO,KAAK,IAAI,CAAC;KAC7E,WAAW;KACZ,CAAC;;GAEN,sBAAsB,EAAE,kBAAkB;AACxC,gBAAY;KACV,UAAU;KACV,SACE;KACH,CAAC;;GAEL;EACF"}
1
+ {"version":3,"file":"astro.js","names":[],"sources":["../src/astro.ts"],"sourcesContent":["import type { AstroIntegration } from \"astro\";\nimport { fileURLToPath } from \"node:url\";\nimport type { LmsOptions } from \"./types.js\";\nimport { normalizeLmsOptions } from \"./config.js\";\n\nexport interface LmsAstroOptions extends LmsOptions {\n layout?: string;\n}\n\nexport function lmsAstro(options: LmsAstroOptions = {}): AstroIntegration {\n const { basePath, loginPath } = normalizeLmsOptions(options);\n return {\n name: \"@anantagnanam/lms\",\n hooks: {\n \"astro:config:setup\": ({ config, injectRoute, updateConfig }) => {\n if (config.output !== \"server\") throw new Error(\"The LMS requires Astro output: server.\");\n const layouts = options.layout\n ? fileURLToPath(new URL(options.layout, config.root))\n : fileURLToPath(new URL(\"../astro/layouts/AcademyLayout.astro\", import.meta.url));\n const virtual = {\n \"virtual:anantagnanam-lms/config\": `export const basePath = ${JSON.stringify(basePath)}; export const loginPath = ${JSON.stringify(loginPath)};`,\n \"virtual:anantagnanam-lms/layout\": `export { default } from ${JSON.stringify(layouts.replaceAll(\"\\\\\", \"/\"))};`,\n };\n updateConfig({\n vite: {\n plugins: [\n {\n name: \"anantagnanam-lms-pages\",\n resolveId: (id) => (id in virtual ? `\\0${id}` : undefined),\n load: (id) => virtual[id.replace(/^\\0/, \"\") as keyof typeof virtual],\n },\n ],\n },\n });\n const pages = [\n [basePath, \"index.astro\"],\n [`${basePath}/courses/[slug]`, \"course.astro\"],\n [`${basePath}/courses/[slug]/lessons/[lessonId]`, \"lesson.astro\"],\n [`${basePath}/my-learning`, \"dashboard.astro\"],\n [`${basePath}/my-learning/library`, \"library.astro\"],\n [`${basePath}/my-learning/study`, \"study.astro\"],\n [`${basePath}/paths`, \"paths.astro\"],\n [`${basePath}/certificates/[certificateId]`, \"certificate.astro\"],\n [`${basePath}/teach`, \"teach.astro\"],\n [`${basePath}/teach/courses/[courseId]/lessons/[lessonId]`, \"author-preview.astro\"],\n [`${basePath}/cohorts/[cohortId]`, \"cohort.astro\"],\n ];\n for (const [pattern, file] of pages)\n injectRoute({\n pattern: pattern!,\n entrypoint: fileURLToPath(new URL(`../astro/pages/${file}`, import.meta.url)),\n prerender: false,\n });\n },\n \"astro:config:done\": ({ injectTypes }) => {\n injectTypes({\n filename: \"anantagnanam-lms.d.ts\",\n content:\n 'declare module \"virtual:anantagnanam-lms/config\" { export const basePath: string; export const loginPath: string; }\\ndeclare module \"virtual:anantagnanam-lms/layout\" { const Layout: import(\"astro/runtime/server/index.js\").AstroComponentFactory; export default Layout; }',\n });\n },\n },\n };\n}\nexport default lmsAstro;\n"],"mappings":";;;;AASA,SAAgB,SAAS,UAA2B,EAAE,EAAoB;CACxE,MAAM,EAAE,UAAU,cAAc,oBAAoB,QAAQ;AAC5D,QAAO;EACL,MAAM;EACN,OAAO;GACL,uBAAuB,EAAE,QAAQ,aAAa,mBAAmB;AAC/D,QAAI,OAAO,WAAW,SAAU,OAAM,IAAI,MAAM,yCAAyC;IACzF,MAAM,UAAU,QAAQ,SACpB,cAAc,IAAI,IAAI,QAAQ,QAAQ,OAAO,KAAK,CAAC,GACnD,cAAc,IAAI,IAAI,wCAAwC,OAAO,KAAK,IAAI,CAAC;IACnF,MAAM,UAAU;KACd,mCAAmC,2BAA2B,KAAK,UAAU,SAAS,CAAC,6BAA6B,KAAK,UAAU,UAAU,CAAC;KAC9I,mCAAmC,2BAA2B,KAAK,UAAU,QAAQ,WAAW,MAAM,IAAI,CAAC,CAAC;KAC7G;AACD,iBAAa,EACX,MAAM,EACJ,SAAS,CACP;KACE,MAAM;KACN,YAAY,OAAQ,MAAM,UAAU,KAAK,OAAO;KAChD,OAAO,OAAO,QAAQ,GAAG,QAAQ,OAAO,GAAG;KAC5C,CACF,EACF,EACF,CAAC;IACF,MAAM,QAAQ;KACZ,CAAC,UAAU,cAAc;KACzB,CAAC,GAAG,SAAS,kBAAkB,eAAe;KAC9C,CAAC,GAAG,SAAS,qCAAqC,eAAe;KACjE,CAAC,GAAG,SAAS,eAAe,kBAAkB;KAC9C,CAAC,GAAG,SAAS,uBAAuB,gBAAgB;KACpD,CAAC,GAAG,SAAS,qBAAqB,cAAc;KAChD,CAAC,GAAG,SAAS,SAAS,cAAc;KACpC,CAAC,GAAG,SAAS,gCAAgC,oBAAoB;KACjE,CAAC,GAAG,SAAS,SAAS,cAAc;KACpC,CAAC,GAAG,SAAS,+CAA+C,uBAAuB;KACnF,CAAC,GAAG,SAAS,sBAAsB,eAAe;KACnD;AACD,SAAK,MAAM,CAAC,SAAS,SAAS,MAC5B,aAAY;KACD;KACT,YAAY,cAAc,IAAI,IAAI,kBAAkB,QAAQ,OAAO,KAAK,IAAI,CAAC;KAC7E,WAAW;KACZ,CAAC;;GAEN,sBAAsB,EAAE,kBAAkB;AACxC,gBAAY;KACV,UAAU;KACV,SACE;KACH,CAAC;;GAEL;EACF"}
@@ -1,6 +1,6 @@
1
1
  //#region src/constants.ts
2
2
  const PLUGIN_ID = "anantagnanam-lms";
3
- const PLUGIN_VERSION = "0.1.0";
3
+ const PLUGIN_VERSION = "0.3.0";
4
4
  const API_BASE = `/_emdash/api/plugins/${PLUGIN_ID}`;
5
5
  const MAX_DOCUMENT_BYTES = 95e4;
6
6
  const storage = {
@@ -13,10 +13,26 @@ const storage = {
13
13
  "level",
14
14
  "language",
15
15
  "updatedAt",
16
- "createdBy"
16
+ "createdBy",
17
+ "createdAt"
17
18
  ],
18
19
  uniqueIndexes: ["slug"]
19
20
  },
21
+ course_revisions: { indexes: ["courseId", "createdAt"] },
22
+ course_editions: { indexes: ["courseId", "createdAt"] },
23
+ migrations: { indexes: ["phase"] },
24
+ preferences: { indexes: [] },
25
+ saved_courses: { indexes: [
26
+ "userId",
27
+ "saved",
28
+ "updatedAt"
29
+ ] },
30
+ learning_paths: { indexes: ["status", "updatedAt"] },
31
+ path_enrollments: { indexes: [
32
+ "userId",
33
+ "pathId",
34
+ "joinedAt"
35
+ ] },
20
36
  enrollments: { indexes: [
21
37
  "userId",
22
38
  "courseId",
@@ -108,6 +124,12 @@ const adminPages = [
108
124
  icon: "users-three",
109
125
  group: "Learning"
110
126
  },
127
+ {
128
+ path: "/paths",
129
+ label: "Learning paths",
130
+ icon: "books",
131
+ group: "Learning"
132
+ },
111
133
  {
112
134
  path: "/reports",
113
135
  label: "Reports",
@@ -136,4 +158,4 @@ const adminPages = [
136
158
 
137
159
  //#endregion
138
160
  export { adminPages as a, PLUGIN_VERSION as i, MAX_DOCUMENT_BYTES as n, storage as o, PLUGIN_ID as r, API_BASE as t };
139
- //# sourceMappingURL=constants-C0kT94tc.js.map
161
+ //# sourceMappingURL=constants-DyQbwNbP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants-DyQbwNbP.js","names":[],"sources":["../src/constants.ts"],"sourcesContent":["export const PLUGIN_ID = \"anantagnanam-lms\";\nexport const PLUGIN_VERSION = \"0.3.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 \"createdAt\",\n ],\n uniqueIndexes: [\"slug\"],\n },\n course_revisions: { indexes: [\"courseId\", \"createdAt\"] },\n course_editions: { indexes: [\"courseId\", \"createdAt\"] },\n migrations: { indexes: [\"phase\"] },\n preferences: { indexes: [] },\n saved_courses: { indexes: [\"userId\", \"saved\", \"updatedAt\"] },\n learning_paths: { indexes: [\"status\", \"updatedAt\"] },\n path_enrollments: { indexes: [\"userId\", \"pathId\", \"joinedAt\"] },\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: \"/paths\", label: \"Learning paths\", icon: \"books\", 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;GACA;GACD;EACD,eAAe,CAAC,OAAO;EACxB;CACD,kBAAkB,EAAE,SAAS,CAAC,YAAY,YAAY,EAAE;CACxD,iBAAiB,EAAE,SAAS,CAAC,YAAY,YAAY,EAAE;CACvD,YAAY,EAAE,SAAS,CAAC,QAAQ,EAAE;CAClC,aAAa,EAAE,SAAS,EAAE,EAAE;CAC5B,eAAe,EAAE,SAAS;EAAC;EAAU;EAAS;EAAY,EAAE;CAC5D,gBAAgB,EAAE,SAAS,CAAC,UAAU,YAAY,EAAE;CACpD,kBAAkB,EAAE,SAAS;EAAC;EAAU;EAAU;EAAW,EAAE;CAC/D,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;EAAU,OAAO;EAAkB,MAAM;EAAS,OAAO;EAAY;CAC7E;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"}
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ import { PluginDescriptor } from "emdash";
3
3
 
4
4
  //#region src/constants.d.ts
5
5
  declare const PLUGIN_ID = "anantagnanam-lms";
6
- declare const PLUGIN_VERSION = "0.1.0";
6
+ declare const PLUGIN_VERSION = "0.3.0";
7
7
  declare const API_BASE = "/_emdash/api/plugins/anantagnanam-lms";
8
8
  //#endregion
9
9
  //#region src/index.d.ts
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as adminPages, i as PLUGIN_VERSION, r as PLUGIN_ID, t as API_BASE } from "./constants-C0kT94tc.js";
1
+ import { a as adminPages, i as PLUGIN_VERSION, r as PLUGIN_ID, t as API_BASE } from "./constants-DyQbwNbP.js";
2
2
  import { t as normalizeLmsOptions } from "./config-BX3beTpM.js";
3
3
 
4
4
  //#region src/index.ts
package/dist/runtime.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AccessGrant, Acquisition, Actor, AuditRecord, Certificate, CertificateIndex, Cohort, CohortMembership, Course, CourseCard, CourseData, CourseDetail, CurriculumModule, Discussion, Enrollment, Lesson, LessonProgress, LmsOptions, LmsSettings, Notification, Page, ProgressSummary, PublicEnrollment, PublicLesson, PublicQuizAttempt, Review } from "./types.js";
1
+ import { AccessGrant, Acquisition, Actor, AssignmentState, AuditRecord, Certificate, CertificateIndex, Cohort, CohortMembership, Course, CourseCard, CourseData, CourseDetail, CourseEdition, CourseRevision, CurriculumModule, Discussion, Enrollment, LearnerPreferences, LearningPath, Lesson, LessonProgress, LmsOptions, LmsSettings, MigrationState, Notification, Page, PathEnrollment, ProgressSummary, PublicEnrollment, PublicLesson, PublicQuizAttempt, Review, RevisionSummary, RubricCriterion, RubricScore, SavedCourse, StudyNote, Versioned } from "./types.js";
2
2
  import * as emdash from "emdash";
3
3
  import { PluginContext, StorageCollection } from "emdash";
4
4
  import { z } from "zod";
@@ -6,6 +6,13 @@ import { z } from "zod";
6
6
  //#region src/store.d.ts
7
7
  interface LmsCollections {
8
8
  courses: StorageCollection<Course>;
9
+ course_revisions: StorageCollection<CourseRevision>;
10
+ course_editions: StorageCollection<CourseEdition>;
11
+ migrations: StorageCollection<MigrationState>;
12
+ preferences: StorageCollection<LearnerPreferences>;
13
+ saved_courses: StorageCollection<SavedCourse>;
14
+ learning_paths: StorageCollection<LearningPath>;
15
+ path_enrollments: StorageCollection<PathEnrollment>;
9
16
  enrollments: StorageCollection<Enrollment>;
10
17
  certificates: StorageCollection<CertificateIndex>;
11
18
  discussions: StorageCollection<Discussion>;
@@ -16,6 +23,28 @@ interface LmsCollections {
16
23
  audit: StorageCollection<AuditRecord>;
17
24
  }
18
25
  //#endregion
26
+ //#region src/editions.d.ts
27
+ /** Child snapshots are immutable. Only the root CAS makes a revision visible. */
28
+ declare class CourseEditions {
29
+ readonly stores: LmsCollections;
30
+ readonly time: () => string;
31
+ readonly resolveContent: (lesson: Lesson) => Promise<Lesson>;
32
+ constructor(stores: LmsCollections, time: () => string, resolveContent: (lesson: Lesson) => Promise<Lesson>);
33
+ snapshot(courseId: string, data: CourseData): Promise<string>;
34
+ revision(course: Course, data: CourseData, actorId: string, kind: CourseRevision["kind"], extra?: Partial<Pick<CourseRevision, "editionId" | "restoredFrom">>): Promise<CourseRevision>;
35
+ commit(current: Versioned<Course> | null, course: Course): Promise<Versioned<Course>>;
36
+ ensure(id: string, expectedRevision?: string): Promise<Versioned<Course>>;
37
+ draft(course: Course): Promise<Course>;
38
+ learning(course: Course, enrollment: Enrollment | null): Promise<Course>;
39
+ history(course: Course, cursor?: string): Promise<{
40
+ items: RevisionSummary[];
41
+ cursor?: string;
42
+ hasMore: boolean;
43
+ }>;
44
+ migrationStatus(): Promise<MigrationState>;
45
+ migrateBatch(limit: number): Promise<MigrationState>;
46
+ }
47
+ //#endregion
19
48
  //#region src/curriculum.d.ts
20
49
  declare function findLesson(course: Course, lessonId: string): Lesson;
21
50
  //#endregion
@@ -24,6 +53,7 @@ declare class LmsService {
24
53
  readonly ctx: PluginContext;
25
54
  readonly now: () => Date;
26
55
  readonly stores: LmsCollections;
56
+ readonly editions: CourseEditions;
27
57
  constructor(ctx: PluginContext, now?: () => Date);
28
58
  time(): string;
29
59
  settings(): Promise<LmsSettings>;
@@ -32,6 +62,10 @@ declare class LmsService {
32
62
  course(id: string): Promise<Course>;
33
63
  accessEnrollment(enrollment: Enrollment | null): Promise<Enrollment | null>;
34
64
  enrollmentView(enrollment: Enrollment): Promise<PublicEnrollment>;
65
+ learningState(course: Course, enrollment: Enrollment | null): Promise<{
66
+ enrollment: PublicEnrollment | null;
67
+ progress: ProgressSummary;
68
+ }>;
35
69
  lessonAccess(course: Course, lessonId: string, enrollment: Enrollment | null): Promise<{
36
70
  id: string;
37
71
  title: string;
@@ -45,6 +79,12 @@ declare class LmsService {
45
79
  field: string;
46
80
  } | null;
47
81
  mediaUrl: string | null;
82
+ transcript: string;
83
+ captions: {
84
+ src: string;
85
+ language: string;
86
+ label: string;
87
+ }[];
48
88
  liveStartsAt: string | null;
49
89
  durationMinutes: number;
50
90
  minStudySeconds: number;
@@ -82,6 +122,12 @@ declare class LmsService {
82
122
  passingPoints: number;
83
123
  dueAt: string | null;
84
124
  allowResubmission: boolean;
125
+ rubric?: {
126
+ id: string;
127
+ title: string;
128
+ description: string;
129
+ maxPoints: number;
130
+ }[] | undefined;
85
131
  } | null;
86
132
  }>;
87
133
  instructor(course: Course, actor: Actor | undefined): Actor;
@@ -89,6 +135,25 @@ declare class LmsService {
89
135
  course: Course;
90
136
  revision: string;
91
137
  }>;
138
+ publishCourse(actor: Actor | undefined, id: string, expectedRevision: string): Promise<{
139
+ course: Course;
140
+ revision: string;
141
+ }>;
142
+ courseHistory(actor: Actor | undefined, id: string, cursor?: string): Promise<{
143
+ items: RevisionSummary[];
144
+ cursor?: string;
145
+ hasMore: boolean;
146
+ }>;
147
+ restoreCourse(actor: Actor | undefined, id: string, expectedRevision: string, revisionId: string): Promise<{
148
+ course: Course;
149
+ revision: string;
150
+ }>;
151
+ archiveCourse(actor: Actor | undefined, id: string, expectedRevision: string): Promise<{
152
+ course: Course;
153
+ revision: string;
154
+ }>;
155
+ migrationStatus(actor: Actor | undefined): Promise<MigrationState>;
156
+ migrateBatch(actor: Actor | undefined, limit: number): Promise<MigrationState>;
92
157
  assertPrerequisiteGraph(courseId: string, prerequisites: string[]): Promise<void>;
93
158
  editableCourse(actor: Actor | undefined, id: string): Promise<{
94
159
  course: Course;
@@ -163,6 +228,10 @@ declare class LmsService {
163
228
  lesson: PublicLesson;
164
229
  }>;
165
230
  resolveContent(lesson: ReturnType<typeof findLesson>): Promise<ReturnType<typeof findLesson>>;
231
+ authorLessonPreview(actor: Actor | undefined, courseId: string, lessonId: string): Promise<{
232
+ course: CourseDetail;
233
+ lesson: PublicLesson;
234
+ }>;
166
235
  assertPrerequisites(course: Course, actorId: string): Promise<void>;
167
236
  newEnrollment(course: Course, actor: Actor, status: Enrollment["status"]): Enrollment;
168
237
  enroll(actor: Actor | undefined, courseId: string, options?: {
@@ -170,6 +239,8 @@ declare class LmsService {
170
239
  }): Promise<PublicEnrollment>;
171
240
  adminEnrollment(actor: Actor | undefined, input: unknown): Promise<PublicEnrollment>;
172
241
  learnerCourse(actor: Actor | undefined, courseId: string): Promise<{
242
+ enrollment: PublicEnrollment | null;
243
+ progress: ProgressSummary;
173
244
  course: {
174
245
  prerequisites: {
175
246
  id: string;
@@ -199,10 +270,10 @@ declare class LmsService {
199
270
  lessonCount: number;
200
271
  durationMinutes: number;
201
272
  };
202
- enrollment: PublicEnrollment | null;
203
- progress: ProgressSummary;
204
273
  }>;
205
274
  learnerCourseBySlug(actor: Actor | undefined, slug: string): Promise<{
275
+ enrollment: PublicEnrollment | null;
276
+ progress: ProgressSummary;
206
277
  course: {
207
278
  prerequisites: {
208
279
  id: string;
@@ -232,47 +303,52 @@ declare class LmsService {
232
303
  lessonCount: number;
233
304
  durationMinutes: number;
234
305
  };
235
- enrollment: PublicEnrollment | null;
236
- progress: ProgressSummary;
237
306
  }>;
238
307
  dashboard(actor: Actor | undefined, options?: {
239
308
  cursor?: string;
240
309
  limit?: number;
241
310
  }): Promise<{
242
311
  items: {
243
- course: CourseCard;
244
- enrollment: PublicEnrollment;
312
+ enrollment: PublicEnrollment | null;
245
313
  progress: ProgressSummary;
314
+ course: CourseCard;
246
315
  }[];
247
316
  cursor?: string;
248
317
  hasMore: boolean;
249
318
  }>;
250
319
  learnerLesson(actor: Actor | undefined, courseId: string, lessonId: string): Promise<{
320
+ enrollment: PublicEnrollment | null;
321
+ progress: ProgressSummary;
251
322
  course: CourseDetail;
252
323
  lesson: PublicLesson;
253
- enrollment: PublicEnrollment;
254
- progress: ProgressSummary;
255
324
  }>;
256
325
  startLesson(actor: Actor | undefined, courseId: string, lessonId: string): Promise<PublicEnrollment>;
257
326
  completeLesson(actor: Actor | undefined, courseId: string, lessonId: string): Promise<{
258
- enrollment: PublicEnrollment;
327
+ enrollment: PublicEnrollment | null;
259
328
  progress: ProgressSummary;
260
329
  }>;
261
330
  finish(course: Course, enrollment: Enrollment, settings: LmsSettings): void;
262
331
  projectCompletion(enrollment: Enrollment): Promise<void>;
263
332
  startQuiz(actor: Actor | undefined, courseId: string, lessonId: string): Promise<PublicQuizAttempt>;
264
333
  submitQuiz(actor: Actor | undefined, input: unknown): Promise<{
265
- attempt: PublicQuizAttempt;
266
- enrollment: PublicEnrollment;
334
+ enrollment: PublicEnrollment | null;
267
335
  progress: ProgressSummary;
336
+ attempt: PublicQuizAttempt;
268
337
  }>;
269
338
  saveQuizDraft(actor: Actor | undefined, input: unknown): Promise<{
270
339
  saved: boolean;
340
+ draftRevision: number;
341
+ }>;
342
+ assignmentState(actor: Actor | undefined, courseId: string, lessonId: string): Promise<AssignmentState>;
343
+ saveAssignmentDraft(actor: Actor | undefined, input: unknown): Promise<{
344
+ saved: boolean;
345
+ draftRevision: number;
271
346
  }>;
272
347
  submitAssignment(actor: Actor | undefined, input: unknown): Promise<PublicEnrollment>;
273
348
  gradeAssignment(actor: Actor | undefined, input: unknown): Promise<PublicEnrollment>;
274
349
  saveNote(actor: Actor | undefined, input: unknown): Promise<{
275
350
  note: string;
351
+ revision: number;
276
352
  }>;
277
353
  certificate(actor: Actor | undefined, courseId: string): Promise<Certificate>;
278
354
  ownedCertificate(actor: Actor | undefined, id: string): Promise<Certificate>;
@@ -395,18 +471,17 @@ declare class LmsService {
395
471
  progress: Record<string, Omit<LessonProgress, "quizAttempts"> & {
396
472
  quizAttempts: PublicQuizAttempt[];
397
473
  }>;
398
- id: string;
399
474
  status: "pending" | "active" | "completed" | "rejected" | "suspended" | "cancelled";
400
475
  courseId: string;
401
- cohortId: string | null;
402
476
  userId: string;
403
- accessStartsAt: string | null;
404
- accessExpiresAt: string | null;
405
477
  accessStatus: "pending" | "granted" | "suspended" | "revoked";
406
478
  accessSource: "free" | "manual" | "cohort" | "purchase";
407
479
  enrolledAt: string;
408
480
  completedAt: string | null;
409
481
  lastActivityAt: string;
482
+ cohortId: string | null;
483
+ id: string;
484
+ courseEditionId?: string | undefined;
410
485
  learnerName: string;
411
486
  grants: Record<string, AccessGrant>;
412
487
  cohortIds: string[];
@@ -415,6 +490,9 @@ declare class LmsService {
415
490
  decisionAt: string | null;
416
491
  decisionReason: string | null;
417
492
  approvedAt: string | null;
493
+ accessStartsAt: string | null;
494
+ accessExpiresAt: string | null;
495
+ studyNotes?: StudyNote[] | undefined;
418
496
  certificate: Certificate | null;
419
497
  completionLessonIds: string[];
420
498
  schemaVersion: 1;
@@ -524,6 +602,8 @@ declare class LmsService {
524
602
  feedback: string;
525
603
  gradedAt: string | null;
526
604
  gradedBy: string | null;
605
+ rubric?: RubricCriterion[];
606
+ rubricScores?: RubricScore[];
527
607
  }[];
528
608
  cursor?: string;
529
609
  hasMore: boolean;
@@ -581,6 +661,12 @@ declare class LmsService {
581
661
  field: string;
582
662
  } | null;
583
663
  mediaUrl: string | null;
664
+ transcript: string;
665
+ captions: {
666
+ src: string;
667
+ language: string;
668
+ label: string;
669
+ }[];
584
670
  liveStartsAt: string | null;
585
671
  durationMinutes: number;
586
672
  minStudySeconds: number;
@@ -618,6 +704,12 @@ declare class LmsService {
618
704
  passingPoints: number;
619
705
  dueAt: string | null;
620
706
  allowResubmission: boolean;
707
+ rubric?: {
708
+ id: string;
709
+ title: string;
710
+ description: string;
711
+ maxPoints: number;
712
+ }[] | undefined;
621
713
  } | null;
622
714
  }[];
623
715
  }[];
@@ -651,6 +743,27 @@ declare function createPlugin(_options?: LmsOptions): emdash.ResolvedPlugin<{
651
743
  indexes: string[];
652
744
  uniqueIndexes: string[];
653
745
  };
746
+ course_revisions: {
747
+ indexes: string[];
748
+ };
749
+ course_editions: {
750
+ indexes: string[];
751
+ };
752
+ migrations: {
753
+ indexes: string[];
754
+ };
755
+ preferences: {
756
+ indexes: never[];
757
+ };
758
+ saved_courses: {
759
+ indexes: string[];
760
+ };
761
+ learning_paths: {
762
+ indexes: string[];
763
+ };
764
+ path_enrollments: {
765
+ indexes: string[];
766
+ };
654
767
  enrollments: {
655
768
  indexes: (string | string[])[];
656
769
  };