@anantagnanam/lms 0.2.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 (40) hide show
  1. package/README.md +19 -2
  2. package/astro/assignment-player.ts +231 -0
  3. package/astro/client.ts +24 -1
  4. package/astro/components/AssignmentFeedback.astro +32 -0
  5. package/astro/components/AssignmentRubric.astro +27 -0
  6. package/astro/components/LearnerDashboard.astro +3 -0
  7. package/astro/components/LearningShell.astro +6 -0
  8. package/astro/components/LessonContent.astro +16 -2
  9. package/astro/components/LessonPlayer.astro +126 -38
  10. package/astro/draft-queue.ts +95 -0
  11. package/astro/media-player.ts +26 -1
  12. package/astro/pages/author-preview.astro +2 -0
  13. package/astro/pages/study.astro +180 -0
  14. package/astro/quiz-drafts.ts +12 -76
  15. package/astro/study-tools.ts +386 -0
  16. package/astro/styles.css +18 -0
  17. package/astro/transcript-search.ts +56 -0
  18. package/dist/admin.js +172 -12
  19. package/dist/admin.js.map +1 -1
  20. package/dist/assignments-CsuAjxGO.js +75 -0
  21. package/dist/assignments-CsuAjxGO.js.map +1 -0
  22. package/dist/astro.js +1 -0
  23. package/dist/astro.js.map +1 -1
  24. package/dist/{constants-Cnx7k82Z.js → constants-DyQbwNbP.js} +2 -2
  25. package/dist/{constants-Cnx7k82Z.js.map → constants-DyQbwNbP.js.map} +1 -1
  26. package/dist/index.d.ts +1 -1
  27. package/dist/index.js +1 -1
  28. package/dist/runtime.d.ts +26 -5
  29. package/dist/runtime.js +307 -16
  30. package/dist/runtime.js.map +1 -1
  31. package/dist/schemas.d.ts +69 -3
  32. package/dist/schemas.js +47 -6
  33. package/dist/schemas.js.map +1 -1
  34. package/dist/server.d.ts +9 -2
  35. package/dist/server.js +6 -3
  36. package/dist/server.js.map +1 -1
  37. package/dist/types.d.ts +67 -2
  38. package/package.json +1 -1
  39. package/dist/errors-ChzJS9al.js +0 -16
  40. 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
@@ -25,6 +25,7 @@ function lmsAstro(options = {}) {
25
25
  [`${basePath}/courses/[slug]/lessons/[lessonId]`, "lesson.astro"],
26
26
  [`${basePath}/my-learning`, "dashboard.astro"],
27
27
  [`${basePath}/my-learning/library`, "library.astro"],
28
+ [`${basePath}/my-learning/study`, "study.astro"],
28
29
  [`${basePath}/paths`, "paths.astro"],
29
30
  [`${basePath}/certificates/[certificateId]`, "certificate.astro"],
30
31
  [`${basePath}/teach`, "teach.astro"],
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}/my-learning/library`, \"library.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,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
+ {"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.2.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 = {
@@ -158,4 +158,4 @@ const adminPages = [
158
158
 
159
159
  //#endregion
160
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 };
161
- //# sourceMappingURL=constants-Cnx7k82Z.js.map
161
+ //# sourceMappingURL=constants-DyQbwNbP.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants-Cnx7k82Z.js","names":[],"sources":["../src/constants.ts"],"sourcesContent":["export const PLUGIN_ID = \"anantagnanam-lms\";\nexport const PLUGIN_VERSION = \"0.2.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"}
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.2.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-Cnx7k82Z.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, CourseEdition, CourseRevision, CurriculumModule, Discussion, Enrollment, LearnerPreferences, LearningPath, Lesson, LessonProgress, LmsOptions, LmsSettings, MigrationState, Notification, Page, PathEnrollment, ProgressSummary, PublicEnrollment, PublicLesson, PublicQuizAttempt, Review, RevisionSummary, SavedCourse, Versioned } 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";
@@ -122,6 +122,12 @@ declare class LmsService {
122
122
  passingPoints: number;
123
123
  dueAt: string | null;
124
124
  allowResubmission: boolean;
125
+ rubric?: {
126
+ id: string;
127
+ title: string;
128
+ description: string;
129
+ maxPoints: number;
130
+ }[] | undefined;
125
131
  } | null;
126
132
  }>;
127
133
  instructor(course: Course, actor: Actor | undefined): Actor;
@@ -333,10 +339,16 @@ declare class LmsService {
333
339
  saved: boolean;
334
340
  draftRevision: number;
335
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;
346
+ }>;
336
347
  submitAssignment(actor: Actor | undefined, input: unknown): Promise<PublicEnrollment>;
337
348
  gradeAssignment(actor: Actor | undefined, input: unknown): Promise<PublicEnrollment>;
338
349
  saveNote(actor: Actor | undefined, input: unknown): Promise<{
339
350
  note: string;
351
+ revision: number;
340
352
  }>;
341
353
  certificate(actor: Actor | undefined, courseId: string): Promise<Certificate>;
342
354
  ownedCertificate(actor: Actor | undefined, id: string): Promise<Certificate>;
@@ -459,18 +471,16 @@ declare class LmsService {
459
471
  progress: Record<string, Omit<LessonProgress, "quizAttempts"> & {
460
472
  quizAttempts: PublicQuizAttempt[];
461
473
  }>;
462
- id: string;
463
474
  status: "pending" | "active" | "completed" | "rejected" | "suspended" | "cancelled";
464
475
  courseId: string;
465
- cohortId: string | null;
466
476
  userId: string;
467
- accessStartsAt: string | null;
468
- accessExpiresAt: string | null;
469
477
  accessStatus: "pending" | "granted" | "suspended" | "revoked";
470
478
  accessSource: "free" | "manual" | "cohort" | "purchase";
471
479
  enrolledAt: string;
472
480
  completedAt: string | null;
473
481
  lastActivityAt: string;
482
+ cohortId: string | null;
483
+ id: string;
474
484
  courseEditionId?: string | undefined;
475
485
  learnerName: string;
476
486
  grants: Record<string, AccessGrant>;
@@ -480,6 +490,9 @@ declare class LmsService {
480
490
  decisionAt: string | null;
481
491
  decisionReason: string | null;
482
492
  approvedAt: string | null;
493
+ accessStartsAt: string | null;
494
+ accessExpiresAt: string | null;
495
+ studyNotes?: StudyNote[] | undefined;
483
496
  certificate: Certificate | null;
484
497
  completionLessonIds: string[];
485
498
  schemaVersion: 1;
@@ -589,6 +602,8 @@ declare class LmsService {
589
602
  feedback: string;
590
603
  gradedAt: string | null;
591
604
  gradedBy: string | null;
605
+ rubric?: RubricCriterion[];
606
+ rubricScores?: RubricScore[];
592
607
  }[];
593
608
  cursor?: string;
594
609
  hasMore: boolean;
@@ -689,6 +704,12 @@ declare class LmsService {
689
704
  passingPoints: number;
690
705
  dueAt: string | null;
691
706
  allowResubmission: boolean;
707
+ rubric?: {
708
+ id: string;
709
+ title: string;
710
+ description: string;
711
+ maxPoints: number;
712
+ }[] | undefined;
692
713
  } | null;
693
714
  }[];
694
715
  }[];