@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.
- package/README.md +19 -2
- package/astro/assignment-player.ts +231 -0
- package/astro/client.ts +24 -1
- package/astro/components/AssignmentFeedback.astro +32 -0
- package/astro/components/AssignmentRubric.astro +27 -0
- package/astro/components/LearnerDashboard.astro +3 -0
- package/astro/components/LearningShell.astro +6 -0
- package/astro/components/LessonContent.astro +16 -2
- package/astro/components/LessonPlayer.astro +126 -38
- package/astro/draft-queue.ts +95 -0
- package/astro/media-player.ts +26 -1
- package/astro/pages/author-preview.astro +2 -0
- package/astro/pages/study.astro +180 -0
- package/astro/quiz-drafts.ts +12 -76
- package/astro/study-tools.ts +386 -0
- package/astro/styles.css +18 -0
- package/astro/transcript-search.ts +56 -0
- package/dist/admin.js +172 -12
- package/dist/admin.js.map +1 -1
- package/dist/assignments-CsuAjxGO.js +75 -0
- package/dist/assignments-CsuAjxGO.js.map +1 -0
- package/dist/astro.js +1 -0
- package/dist/astro.js.map +1 -1
- package/dist/{constants-Cnx7k82Z.js → constants-DyQbwNbP.js} +2 -2
- package/dist/{constants-Cnx7k82Z.js.map → constants-DyQbwNbP.js.map} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/runtime.d.ts +26 -5
- package/dist/runtime.js +307 -16
- package/dist/runtime.js.map +1 -1
- package/dist/schemas.d.ts +69 -3
- package/dist/schemas.js +47 -6
- package/dist/schemas.js.map +1 -1
- package/dist/server.d.ts +9 -2
- package/dist/server.js +6 -3
- package/dist/server.js.map +1 -1
- package/dist/types.d.ts +67 -2
- package/package.json +1 -1
- package/dist/errors-ChzJS9al.js +0 -16
- package/dist/errors-ChzJS9al.js.map +0 -1
package/dist/runtime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.js","names":["user"],"sources":["../src/store.ts","../src/editions.ts","../src/curriculum.ts","../src/service.ts","../src/learning.ts","../src/runtime.ts"],"sourcesContent":["import type { PluginContext, StorageCollection } from \"emdash\";\nimport type {\n Course,\n CourseRevision,\n CourseEdition,\n MigrationState,\n Enrollment,\n CertificateIndex,\n Discussion,\n Review,\n Cohort,\n CohortMembership,\n Notification,\n AuditRecord,\n Page,\n LearnerPreferences,\n SavedCourse,\n LearningPath,\n PathEnrollment,\n} from \"./types.js\";\nimport { MAX_DOCUMENT_BYTES } from \"./constants.js\";\nimport { LmsError, invariant } from \"./errors.js\";\n\nexport type StorageQueryOptions = NonNullable<Parameters<StorageCollection<unknown>[\"query\"]>[0]>;\n\nexport interface LmsCollections {\n courses: StorageCollection<Course>;\n course_revisions: StorageCollection<CourseRevision>;\n course_editions: StorageCollection<CourseEdition>;\n migrations: StorageCollection<MigrationState>;\n preferences: StorageCollection<LearnerPreferences>;\n saved_courses: StorageCollection<SavedCourse>;\n learning_paths: StorageCollection<LearningPath>;\n path_enrollments: StorageCollection<PathEnrollment>;\n enrollments: StorageCollection<Enrollment>;\n certificates: StorageCollection<CertificateIndex>;\n discussions: StorageCollection<Discussion>;\n reviews: StorageCollection<Review>;\n cohorts: StorageCollection<Cohort>;\n cohort_enrollments: StorageCollection<CohortMembership>;\n notifications: StorageCollection<Notification>;\n audit: StorageCollection<AuditRecord>;\n}\n\nexport function collections(ctx: PluginContext): LmsCollections {\n return ctx.storage as unknown as LmsCollections;\n}\nexport function enrollmentKey(courseId: string, userId: string): string {\n return `${courseId}:${userId}`;\n}\nexport function assertSize(data: unknown): void {\n invariant(\n new TextEncoder().encode(JSON.stringify(data)).byteLength <= MAX_DOCUMENT_BYTES,\n \"DOCUMENT_TOO_LARGE\",\n \"This record exceeds the LMS storage limit. Split the course into smaller courses.\",\n 413,\n );\n}\n\n// Enrollment progress, attempts and certificate eligibility share one CAS record.\nexport async function change<T, R>(\n store: StorageCollection<T>,\n id: string,\n update: (value: T | null) => Promise<{ value: T; result: R }> | { value: T; result: R },\n): Promise<R> {\n for (let attempt = 0; attempt < 8; attempt++) {\n const current = await store.getVersioned(id);\n const { value, result } = await update(current?.value ?? null);\n assertSize(value);\n const saved = await store.compareAndSet(id, current?.revision ?? null, value);\n if (saved.applied) return result;\n }\n throw new LmsError(\"CONFLICT\", \"Another request changed this record. Please try again.\", 409);\n}\n\nexport async function queryPage<T>(\n store: StorageCollection<T>,\n options: StorageQueryOptions = {},\n): Promise<Page<T>> {\n const page = await store.query(options);\n return { ...page, items: page.items.map((item) => item.data) };\n}\n\nexport async function putOnce<T>(store: StorageCollection<T>, id: string, value: T): Promise<void> {\n assertSize(value);\n await store.compareAndSet(id, null, value);\n}\n","import { courseDataSchema } from \"./schemas.js\";\nimport { invariant, LmsError } from \"./errors.js\";\nimport { assertSize, change, putOnce, type LmsCollections } from \"./store.js\";\nimport type {\n Course,\n CourseData,\n CourseRevision,\n Enrollment,\n Lesson,\n MigrationState,\n RevisionSummary,\n Versioned,\n} from \"./types.js\";\n\n/** Child snapshots are immutable. Only the root CAS makes a revision visible. */\nexport class CourseEditions {\n constructor(\n readonly stores: LmsCollections,\n readonly time: () => string,\n readonly resolveContent: (lesson: Lesson) => Promise<Lesson>,\n ) {}\n\n async snapshot(courseId: string, data: CourseData): Promise<string> {\n const frozen = structuredClone(data);\n for (const module of frozen.modules)\n for (let i = 0; i < module.lessons.length; i++)\n module.lessons[i] = {\n ...(await this.resolveContent(module.lessons[i]!)),\n contentSource: null,\n };\n const id = crypto.randomUUID();\n await putOnce(this.stores.course_editions, id, {\n id,\n courseId,\n data: frozen,\n createdAt: this.time(),\n });\n return id;\n }\n\n async revision(\n course: Course,\n data: CourseData,\n actorId: string,\n kind: CourseRevision[\"kind\"],\n extra: Partial<Pick<CourseRevision, \"editionId\" | \"restoredFrom\">> = {},\n ) {\n const revision: CourseRevision = {\n id: crypto.randomUUID(),\n courseId: course.id,\n parentId: course.draftRevisionId ?? null,\n data: courseDataSchema.parse({ ...data, status: \"draft\" }),\n kind,\n createdBy: actorId,\n createdAt: this.time(),\n ...extra,\n };\n await putOnce(this.stores.course_revisions, revision.id, revision);\n return revision;\n }\n\n async commit(current: Versioned<Course> | null, course: Course): Promise<Versioned<Course>> {\n assertSize(course);\n const saved = await this.stores.courses.compareAndSet(\n course.id,\n current?.revision ?? null,\n course,\n );\n invariant(saved.applied, \"CONFLICT\", \"The course changed. Reload before continuing.\", 409);\n return { value: course, revision: saved.revision };\n }\n\n async ensure(id: string, expectedRevision?: string): Promise<Versioned<Course>> {\n for (let attempt = 0; attempt < 8; attempt++) {\n const current = await this.stores.courses.getVersioned(id);\n invariant(current, \"NOT_FOUND\", \"Course not found.\", 404);\n invariant(\n expectedRevision === undefined || current.revision === expectedRevision,\n \"CONFLICT\",\n \"The course changed. Reload before continuing.\",\n 409,\n );\n if (current.value.schemaVersion === 2) return current;\n const course = current.value;\n invariant(\n course.schemaVersion === 1,\n \"UNSUPPORTED_SCHEMA\",\n \"This course needs a different LMS version. No migration was applied.\",\n 409,\n );\n const data = courseDataSchema.parse(course);\n // Even archived/legacy draft enrollments keep their original curriculum.\n const editionId = await this.snapshot(id, data);\n const revision = await this.revision(course, data, course.createdBy, \"migration\", {\n editionId,\n });\n const edition = (await this.stores.course_editions.get(editionId))!;\n const next: Course = {\n ...course,\n ...edition.data,\n schemaVersion: 2,\n draftRevisionId: revision.id,\n legacyEditionId: editionId,\n ...(course.status !== \"draft\"\n ? { publishedEditionId: editionId, publishedDraftRevisionId: revision.id }\n : {}),\n };\n try {\n return await this.commit(current, next);\n } catch (error) {\n if (\n !(error instanceof LmsError) ||\n error.code !== \"CONFLICT\" ||\n expectedRevision !== undefined\n )\n throw error;\n }\n }\n throw new LmsError(\"CONFLICT\", \"Course migration conflicted. Please retry.\", 409);\n }\n\n async draft(course: Course): Promise<Course> {\n if (!course.draftRevisionId) return course;\n const revision = await this.stores.course_revisions.get(course.draftRevisionId);\n invariant(\n revision?.courseId === course.id,\n \"EDITION_UNAVAILABLE\",\n \"The saved course draft is unavailable.\",\n 503,\n );\n return { ...course, ...revision.data, status: course.status };\n }\n\n async learning(course: Course, enrollment: Enrollment | null): Promise<Course> {\n if (!enrollment) return course;\n const id = enrollment.courseEditionId ?? course.legacyEditionId ?? course.publishedEditionId;\n if (!id) return course;\n const edition = await this.stores.course_editions.get(id);\n invariant(\n edition?.courseId === course.id,\n \"EDITION_UNAVAILABLE\",\n \"The enrolled course edition is unavailable.\",\n 503,\n );\n return {\n ...course,\n ...edition.data,\n // Current visibility, URL and staff authority never come from historical snapshots.\n status: course.status,\n slug: course.slug,\n instructorIds: course.instructorIds,\n };\n }\n\n async history(\n course: Course,\n cursor?: string,\n ): Promise<{ items: RevisionSummary[]; cursor?: string; hasMore: boolean }> {\n let id = course.draftRevisionId;\n let found = !cursor;\n const items: RevisionSummary[] = [];\n // Bounded traversal validates cursors against the committed chain, excluding failed CAS writes.\n for (let i = 0; id && i < 1000; i++) {\n const revision = await this.stores.course_revisions.get(id);\n invariant(\n revision?.courseId === course.id,\n \"EDITION_UNAVAILABLE\",\n \"Course history is unavailable.\",\n 503,\n );\n if (id === cursor) found = true;\n if (found) {\n const { data, ...summary } = revision;\n items.push({ ...summary, title: data.title });\n }\n id = revision.parentId ?? undefined;\n if (items.length === 20) return { items, cursor: id, hasMore: !!id };\n }\n invariant(\n found && !id,\n \"INVALID_CURSOR\",\n \"History cursor is invalid or exceeds the 1,000-revision browsing limit.\",\n 400,\n );\n return { items, hasMore: false };\n }\n\n async migrationStatus(): Promise<MigrationState> {\n return (\n (await this.stores.migrations.get(\"course-editions-v2\")) ?? {\n phase: \"courses\",\n coursesProcessed: 0,\n enrollmentsProcessed: 0,\n updatedAt: this.time(),\n lastError: null,\n }\n );\n }\n\n async migrateBatch(limit: number): Promise<MigrationState> {\n const id = \"course-editions-v2\";\n const current = await this.stores.migrations.getVersioned(id);\n const state = current?.value ?? (await this.migrationStatus());\n if (state.phase === \"complete\") return state;\n const next = { ...state, lastError: null, updatedAt: this.time() };\n try {\n if (state.phase === \"courses\") {\n const page = await this.stores.courses.query({\n orderBy: { createdAt: \"asc\" },\n cursor: state.cursor,\n limit,\n });\n for (const item of page.items) await this.ensure(item.id);\n next.coursesProcessed += page.items.length;\n next.cursor = page.cursor;\n if (!page.hasMore) {\n next.phase = \"enrollments\";\n next.cursor = undefined;\n }\n } else {\n const page = await this.stores.enrollments.query({\n orderBy: { enrolledAt: \"asc\" },\n cursor: state.cursor,\n limit,\n });\n for (const item of page.items) {\n if (item.data.courseEditionId) continue;\n const course = (await this.ensure(item.data.courseId)).value;\n await change(this.stores.enrollments, item.id, async (enrollment) => {\n invariant(enrollment, \"NOT_FOUND\", \"Enrollment disappeared during migration.\", 409);\n return {\n value: {\n ...enrollment,\n courseEditionId:\n enrollment.courseEditionId ?? course.legacyEditionId ?? course.publishedEditionId,\n },\n result: null,\n };\n });\n }\n next.enrollmentsProcessed += page.items.length;\n next.cursor = page.cursor;\n if (!page.hasMore) {\n next.phase = \"complete\";\n next.cursor = undefined;\n }\n }\n } catch (error) {\n // Partial child writes are idempotent; leave the batch cursor at its starting point.\n await this.stores.migrations.compareAndSet(id, current?.revision ?? null, {\n ...state,\n updatedAt: this.time(),\n lastError:\n error instanceof LmsError\n ? error.message\n : \"Migration failed. Retry the batch after checking storage availability.\",\n });\n throw error;\n }\n const saved = await this.stores.migrations.compareAndSet(id, current?.revision ?? null, next);\n invariant(\n saved.applied,\n \"CONFLICT\",\n \"Another migration batch advanced the checkpoint. Refresh and continue.\",\n 409,\n );\n return next;\n }\n}\n","import { invariant, LmsError } from \"./errors.js\";\nimport type {\n Course,\n CourseCard,\n CourseDetail,\n Enrollment,\n Lesson,\n LessonProgress,\n ProgressSummary,\n PublicEnrollment,\n PublicLesson,\n PublicQuizAttempt,\n QuizAttempt,\n QuizQuestion,\n Answers,\n QuestionResult,\n LessonAvailability,\n} from \"./types.js\";\n\nexport function lessons(course: Course): Lesson[] {\n return course.modules.flatMap((module) => module.lessons);\n}\nexport function emptyProgress(): LessonProgress {\n return { startedAt: null, completedAt: null, note: \"\", quizAttempts: [], assignments: [] };\n}\nexport function courseCard(course: Course): CourseCard {\n const all = lessons(course);\n return {\n id: course.id,\n slug: course.slug,\n title: course.title,\n summary: course.summary,\n category: course.category,\n tags: course.tags,\n level: course.level,\n language: course.language,\n featuredImage: course.featuredImage,\n instructorName: course.instructorName,\n enrollmentMode: course.enrollmentMode,\n lessonCount: all.length,\n durationMinutes: all.reduce((sum, lesson) => sum + lesson.durationMinutes, 0),\n };\n}\nexport function courseDetail(course: Course): CourseDetail {\n return {\n prerequisites: [],\n ...courseCard(course),\n description: course.description,\n outcomes: course.outcomes,\n requirements: course.requirements,\n sequential: course.sequential,\n prerequisiteCourseIds: course.prerequisiteCourseIds,\n enrollmentClosesAt: course.enrollmentClosesAt,\n accessDays: course.accessDays,\n certificateEnabled: course.certificateEnabled,\n modules: course.modules.map((module) => ({\n id: module.id,\n title: module.title,\n description: module.description,\n releaseDays: module.releaseDays,\n unlockAt: module.unlockAt,\n lessons: module.lessons.map((lesson) => ({\n id: lesson.id,\n title: lesson.title,\n summary: lesson.summary,\n kind: lesson.kind,\n durationMinutes: lesson.durationMinutes,\n preview: lesson.preview,\n required: lesson.required,\n })),\n })),\n };\n}\nexport function publicLesson(lesson: Lesson): PublicLesson {\n const { quiz, contentSource: _source, ...safe } = lesson;\n return {\n ...safe,\n transcript: lesson.transcript ?? \"\",\n captions: lesson.captions ?? [],\n quiz: quiz\n ? {\n passingPercent: quiz.passingPercent,\n maxAttempts: quiz.maxAttempts,\n timeLimitMinutes: quiz.timeLimitMinutes,\n randomizeQuestions: quiz.randomizeQuestions,\n randomizeOptions: quiz.randomizeOptions,\n showFeedback: quiz.showFeedback,\n questionCount: quiz.questions.length,\n }\n : null,\n };\n}\nexport function publicAttempt(attempt: QuizAttempt): PublicQuizAttempt {\n const { quiz: _quiz, ...safe } = attempt;\n return { ...safe, draftRevision: attempt.draftRevision ?? 0 };\n}\nexport function effectiveAccess(\n enrollment: Enrollment,\n now = new Date(),\n): PublicEnrollment[\"effectiveAccess\"] {\n if (enrollment.status === \"suspended\" || enrollment.accessStatus === \"suspended\")\n return \"suspended\";\n if (\n [\"rejected\", \"cancelled\"].includes(enrollment.status) ||\n enrollment.accessStatus === \"revoked\"\n )\n return \"revoked\";\n if (enrollment.status === \"pending\" || enrollment.accessStatus === \"pending\") return \"pending\";\n if (enrollment.accessExpiresAt && now.getTime() >= Date.parse(enrollment.accessExpiresAt))\n return \"expired\";\n if (enrollment.accessStartsAt && now.getTime() < Date.parse(enrollment.accessStartsAt))\n return \"scheduled\";\n const grants = Object.values(enrollment.grants);\n if (\n grants.some(\n (grant) =>\n grant.status === \"granted\" &&\n (!grant.startsAt || now.getTime() >= Date.parse(grant.startsAt)) &&\n (!grant.expiresAt || now.getTime() < Date.parse(grant.expiresAt)),\n )\n )\n return \"granted\";\n if (\n grants.some(\n (grant) =>\n grant.status === \"granted\" &&\n grant.startsAt &&\n now.getTime() < Date.parse(grant.startsAt) &&\n (!grant.expiresAt || now.getTime() < Date.parse(grant.expiresAt)),\n )\n )\n return \"scheduled\";\n if (grants.some((grant) => grant.status === \"suspended\")) return \"suspended\";\n if (\n grants.some(\n (grant) =>\n grant.status === \"granted\" &&\n grant.expiresAt &&\n now.getTime() >= Date.parse(grant.expiresAt),\n )\n )\n return \"expired\";\n return \"revoked\";\n}\n\nexport function publicEnrollment(enrollment: Enrollment, now = new Date()): PublicEnrollment {\n const { learnerEmail: _email, progress, ...safe } = enrollment;\n return {\n ...safe,\n effectiveAccess: effectiveAccess(enrollment, now),\n progress: Object.fromEntries(\n Object.entries(progress).map(([id, item]) => [\n id,\n { ...item, quizAttempts: item.quizAttempts.map(publicAttempt) },\n ]),\n ),\n };\n}\n/** Staff need assessment and completion records, not a learner's private study notes. */\nexport function staffEnrollment(enrollment: Enrollment, now = new Date()): PublicEnrollment {\n const view = publicEnrollment(enrollment, now);\n for (const progress of Object.values(view.progress)) {\n progress.note = \"\";\n delete progress.playback;\n }\n return view;\n}\nexport function progressSummary(\n course: Course,\n enrollment: Enrollment | null,\n now = new Date(),\n): ProgressSummary {\n const required = enrollment?.completedAt\n ? enrollment.completionLessonIds\n : lessons(course)\n .filter((lesson) => lesson.required)\n .map((lesson) => lesson.id);\n const completed = required.filter((id) => enrollment?.progress[id]?.completedAt).length;\n const all = lessons(course);\n const availability = Object.fromEntries(\n all.map((lesson) => [lesson.id, lessonAvailability(course, lesson.id, enrollment, now)]),\n );\n const incomplete = all.filter((lesson) => !enrollment?.progress[lesson.id]?.completedAt);\n const nextLessonId = incomplete.find((lesson) => availability[lesson.id]!.available)?.id ?? null;\n return {\n completed,\n total: required.length,\n percent: required.length ? Math.floor((completed / required.length) * 100) : 0,\n nextLessonId,\n blockedLessonId: nextLessonId ? null : (incomplete[0]?.id ?? null),\n availability,\n };\n}\n\n/** Use the same access boundary for navigation and private lesson reads. Enrollment\n * must already have its cohort grants resolved against authoritative membership. */\nexport function lessonAvailability(\n course: Course,\n lessonId: string,\n enrollment: Enrollment | null,\n now: Date,\n): LessonAvailability {\n try {\n assertLessonAccess(course, lessonId, enrollment, now);\n return { available: true, code: null, reason: null, releaseAt: null };\n } catch (error) {\n if (!(error instanceof LmsError)) throw error;\n let releaseAt: string | null = null;\n if (enrollment && error.code === \"LESSON_LOCKED\")\n releaseAt = new Date(moduleReleaseAt(course, lessonId, enrollment)).toISOString();\n else if (enrollment && effectiveAccess(enrollment, now) === \"scheduled\") {\n const starts = Object.values(enrollment.grants)\n .filter(\n (grant) =>\n grant.status === \"granted\" &&\n (!grant.expiresAt || Date.parse(grant.expiresAt) > now.getTime()),\n )\n .flatMap((grant) => {\n const date = Math.max(\n grant.startsAt ? Date.parse(grant.startsAt) : 0,\n enrollment.accessStartsAt ? Date.parse(enrollment.accessStartsAt) : 0,\n );\n return date > now.getTime() &&\n (!grant.expiresAt || date < Date.parse(grant.expiresAt)) &&\n (!enrollment.accessExpiresAt || date < Date.parse(enrollment.accessExpiresAt))\n ? [date]\n : [];\n });\n if (starts.length) releaseAt = new Date(Math.min(...starts)).toISOString();\n }\n return { available: false, code: error.code, reason: error.message, releaseAt };\n }\n}\n\nfunction moduleReleaseAt(course: Course, lessonId: string, enrollment: Enrollment): number {\n const module = course.modules.find((item) =>\n item.lessons.some((entry) => entry.id === lessonId),\n )!;\n return Math.max(\n module.unlockAt ? Date.parse(module.unlockAt) : 0,\n Date.parse(enrollment.approvedAt ?? enrollment.enrolledAt) + module.releaseDays * 86_400_000,\n );\n}\n\nexport function assertEnrollmentAccess(\n enrollment: Enrollment | null,\n now: Date,\n): asserts enrollment is Enrollment {\n invariant(\n enrollment && (enrollment.status === \"active\" || enrollment.status === \"completed\"),\n \"ENROLLMENT_REQUIRED\",\n \"An active enrollment is required for this lesson.\",\n 403,\n );\n invariant(\n enrollment.accessStatus === \"granted\",\n \"ACCESS_RESTRICTED\",\n \"Your course access is not currently granted.\",\n 403,\n );\n invariant(\n Object.values(enrollment.grants).some(\n (grant) =>\n grant.status === \"granted\" &&\n (!grant.startsAt || now.getTime() >= Date.parse(grant.startsAt)) &&\n (!grant.expiresAt || now.getTime() < Date.parse(grant.expiresAt)),\n ),\n \"GRANT_REQUIRED\",\n \"Your course access grant has not started, expired, or been revoked.\",\n 403,\n );\n invariant(\n !enrollment.accessStartsAt || now.getTime() >= Date.parse(enrollment.accessStartsAt),\n \"COURSE_NOT_STARTED\",\n \"Your course access has not started yet.\",\n 403,\n );\n invariant(\n !enrollment.accessExpiresAt || now.getTime() < Date.parse(enrollment.accessExpiresAt),\n \"ACCESS_EXPIRED\",\n \"Your course access has expired.\",\n 403,\n );\n}\n\nexport function findLesson(course: Course, lessonId: string): Lesson {\n const lesson = lessons(course).find((item) => item.id === lessonId);\n invariant(lesson, \"NOT_FOUND\", \"Lesson not found.\", 404);\n return lesson;\n}\nexport function assertLessonAccess(\n course: Course,\n lessonId: string,\n enrollment: Enrollment | null,\n now: Date,\n allowPreview = false,\n): Lesson {\n const lesson = findLesson(course, lessonId);\n if (allowPreview && lesson.preview && ![\"quiz\", \"assignment\", \"live\"].includes(lesson.kind))\n return lesson;\n assertEnrollmentAccess(enrollment, now);\n invariant(course.status !== \"draft\", \"NOT_FOUND\", \"Course not found.\", 404);\n const releaseAt = moduleReleaseAt(course, lessonId, enrollment);\n invariant(\n now.getTime() >= releaseAt,\n \"LESSON_LOCKED\",\n \"This module has not been released yet.\",\n 403,\n );\n if (course.sequential) {\n const all = lessons(course);\n const index = all.findIndex((item) => item.id === lessonId);\n invariant(\n all\n .slice(0, index)\n .filter((item) => item.required)\n .every((item) => enrollment.progress[item.id]?.completedAt),\n \"PREREQUISITE_REQUIRED\",\n \"Complete the earlier required lessons first.\",\n 403,\n );\n }\n return lesson;\n}\n\nexport function shuffle<T>(\n items: T[],\n random: () => number = () => {\n const value = new Uint32Array(1);\n crypto.getRandomValues(value);\n return value[0]! / 4_294_967_296;\n },\n): T[] {\n const output = [...items];\n for (let index = output.length - 1; index > 0; index--) {\n const pick = Math.floor(random() * (index + 1));\n [output[index], output[pick]] = [output[pick]!, output[index]!];\n }\n return output;\n}\nfunction normalized(value: string): string {\n return value.normalize(\"NFKC\").trim().toLocaleLowerCase(\"en\").replace(/\\s+/g, \" \");\n}\nexport function gradeAnswers(\n questions: QuizQuestion[],\n answers: Answers,\n): { score: number; maxScore: number; percent: number; feedback: QuestionResult[] } {\n const ids = new Set(questions.map((question) => question.id));\n invariant(\n Object.keys(answers).every((id) => ids.has(id)),\n \"INVALID_ANSWERS\",\n \"Answers contain an unknown question.\",\n );\n const feedback = questions.map((question) => {\n const response = answers[question.id];\n const selected = Array.isArray(response) ? response : response === undefined ? [] : [response];\n invariant(\n new Set(selected).size === selected.length,\n \"INVALID_ANSWERS\",\n \"Duplicate answer choices are not accepted.\",\n );\n if (question.type !== \"multiple\")\n invariant(selected.length <= 1, \"INVALID_ANSWERS\", \"This question accepts one answer.\");\n if (question.type !== \"short\")\n invariant(\n selected.every((answer) => question.options.some((option) => option.id === answer)),\n \"INVALID_ANSWERS\",\n \"An answer contains an invalid option.\",\n );\n const correct =\n question.type === \"short\"\n ? selected.length === 1 &&\n question.correctAnswers.some((answer) => normalized(answer) === normalized(selected[0]!))\n : selected.length === question.correctAnswers.length &&\n selected.every((answer) => question.correctAnswers.includes(answer));\n return {\n questionId: question.id,\n correct,\n points: correct ? question.points : 0,\n maxPoints: question.points,\n explanation: question.explanation,\n };\n });\n const score = feedback.reduce((sum, item) => sum + item.points, 0);\n const maxScore = questions.reduce((sum, question) => sum + question.points, 0);\n return { score, maxScore, percent: Math.round((score / maxScore) * 10_000) / 100, feedback };\n}\n","import type { PluginContext } from \"emdash\";\nimport { z } from \"zod\";\nimport {\n catalogSchema,\n courseSaveSchema,\n courseDataSchema,\n settingsSchema,\n enrollmentAdminSchema,\n quizSubmitSchema,\n quizDraftSchema,\n assignmentSubmitSchema,\n gradeSchema,\n discussionSchema,\n reviewSchema,\n moderateSchema,\n cohortSaveSchema,\n noteSchema,\n} from \"./schemas.js\";\nimport type {\n Actor,\n Course,\n Enrollment,\n QuizAttempt,\n LmsSettings,\n PublicQuizAttempt,\n Discussion,\n Review,\n Cohort,\n CohortMembership,\n Notification,\n Page,\n CourseCard,\n} from \"./types.js\";\nimport {\n assertSize,\n change,\n collections,\n enrollmentKey,\n putOnce,\n queryPage,\n type StorageQueryOptions,\n} from \"./store.js\";\nimport { invariant, LmsError } from \"./errors.js\";\nimport { CourseEditions } from \"./editions.js\";\nimport { enrollmentCsvHeader, enrollmentCsvLines } from \"./csv.js\";\nimport {\n assertEnrollmentAccess,\n assertLessonAccess,\n courseCard,\n courseDetail,\n emptyProgress,\n effectiveAccess,\n findLesson,\n gradeAnswers,\n lessons,\n progressSummary,\n publicAttempt,\n publicEnrollment,\n staffEnrollment,\n publicLesson,\n shuffle,\n} from \"./curriculum.js\";\n\nexport function parse<T>(schema: z.ZodType<T>, input: unknown): T {\n const result = schema.safeParse(input);\n if (!result.success)\n throw new LmsError(\n \"VALIDATION_ERROR\",\n result.error.issues\n .map((issue) => `${issue.path.join(\".\") || \"Input\"}: ${issue.message}`)\n .slice(0, 5)\n .join(\" \"),\n 400,\n );\n return result.data;\n}\nfunction user(actor: Actor | undefined): Actor {\n invariant(actor, \"UNAUTHORIZED\", \"Sign in to continue.\", 401);\n return actor;\n}\n\nexport class LmsService {\n readonly stores;\n readonly editions;\n constructor(\n readonly ctx: PluginContext,\n readonly now: () => Date = () => new Date(),\n ) {\n this.stores = collections(ctx);\n this.editions = new CourseEditions(\n this.stores,\n () => this.time(),\n (lesson) => this.resolveContent(lesson),\n );\n }\n time(): string {\n return this.now().toISOString();\n }\n async settings(): Promise<LmsSettings> {\n return settingsSchema.parse((await this.ctx.settings.get(\"configuration\")) ?? {});\n }\n async saveSettings(actor: Actor | undefined, input: unknown): Promise<LmsSettings> {\n invariant(\n user(actor).role >= 50,\n \"FORBIDDEN\",\n \"Only administrators can change LMS settings.\",\n 403,\n );\n const settings = parse(settingsSchema, input);\n invariant(\n !settings.emailNotifications || this.ctx.email,\n \"EMAIL_UNAVAILABLE\",\n \"Configure an email transport in EmDash before enabling LMS emails.\",\n );\n await this.ctx.settings.set(\"configuration\", settings);\n await this.audit(actor!, \"settings.save\", \"configuration\");\n return settings;\n }\n async audit(\n actor: Actor,\n action: string,\n resourceId: string,\n details: Record<string, unknown> = {},\n ): Promise<void> {\n const id = crypto.randomUUID();\n await this.stores.audit.put(id, {\n id,\n actorId: actor.id,\n action,\n resourceId,\n createdAt: this.time(),\n details,\n });\n }\n async course(id: string): Promise<Course> {\n const course = await this.stores.courses.get(id);\n invariant(course, \"NOT_FOUND\", \"Course not found.\", 404);\n return course;\n }\n // Membership is authoritative while course grant projections are being reconciled.\n async accessEnrollment(enrollment: Enrollment | null): Promise<Enrollment | null> {\n if (!enrollment) return null;\n const grants = Object.values(enrollment.grants).filter((grant) => grant.source === \"cohort\");\n if (!grants.length) return enrollment;\n const [memberships, cohorts] = await Promise.all([\n this.stores.cohort_enrollments.getMany(\n grants.map((grant) => enrollmentKey(grant.referenceId ?? \"\", enrollment.userId)),\n ),\n this.stores.cohorts.getMany(grants.map((grant) => grant.referenceId ?? \"\")),\n ]);\n const resolved = structuredClone(enrollment);\n for (const grant of grants) {\n const membership = memberships.get(enrollmentKey(grant.referenceId ?? \"\", enrollment.userId));\n const cohort = cohorts.get(grant.referenceId ?? \"\");\n if (membership?.status !== \"active\" || !cohort?.courseIds.includes(enrollment.courseId))\n resolved.grants[grant.id]!.status =\n membership?.status === \"suspended\" ? \"suspended\" : \"revoked\";\n else {\n resolved.grants[grant.id]!.status = \"granted\";\n resolved.grants[grant.id]!.startsAt = cohort.startsAt;\n resolved.grants[grant.id]!.expiresAt = cohort.endsAt;\n }\n }\n return resolved;\n }\n async enrollmentView(enrollment: Enrollment) {\n return publicEnrollment((await this.accessEnrollment(enrollment))!, this.now());\n }\n async learningState(course: Course, enrollment: Enrollment | null) {\n course = await this.editions.learning(course, enrollment);\n const resolved = await this.accessEnrollment(enrollment);\n const now = this.now();\n return {\n enrollment: resolved ? publicEnrollment(resolved, now) : null,\n progress: progressSummary(course, resolved, now),\n };\n }\n async lessonAccess(course: Course, lessonId: string, enrollment: Enrollment | null) {\n return assertLessonAccess(\n await this.editions.learning(course, enrollment),\n lessonId,\n await this.accessEnrollment(enrollment),\n this.now(),\n );\n }\n instructor(course: Course, actor: Actor | undefined): Actor {\n const current = user(actor);\n invariant(\n current.role >= 20 &&\n (current.role >= 50 ||\n course.createdBy === current.id ||\n course.instructorIds.includes(current.id)),\n \"FORBIDDEN\",\n \"You are not an instructor for this course.\",\n 403,\n );\n return current;\n }\n async saveCourse(\n actor: Actor | undefined,\n input: unknown,\n ): Promise<{ course: Course; revision: string }> {\n const currentActor = user(actor);\n const parsed = parse(courseSaveSchema, input);\n const id = parsed.id ?? crypto.randomUUID();\n let current = await this.stores.courses.getVersioned(id);\n if (current) this.instructor(current.value, currentActor);\n else\n invariant(\n currentActor.role >= 20,\n \"FORBIDDEN\",\n \"Course authoring requires a contributor account or higher.\",\n 403,\n );\n invariant(\n (current?.revision ?? null) === parsed.expectedRevision,\n \"CONFLICT\",\n \"The course changed since you opened it. Reload before saving.\",\n 409,\n );\n if (!current && parsed.data.status === \"published\")\n invariant(\n currentActor.role >= 50 || currentActor.role >= 30,\n \"FORBIDDEN\",\n \"Publishing requires an author account or higher.\",\n 403,\n );\n invariant(\n current || parsed.data.status !== \"published\" || parsed.data.commerce.accessModel === \"free\",\n \"PAID_COURSES_DISABLED\",\n \"Payment processing is not enabled in this release. Publish free courses only.\",\n 409,\n );\n const duplicate = await this.stores.courses.query({\n where: { slug: parsed.data.slug },\n limit: 1,\n });\n invariant(\n !duplicate.items.some((item) => item.id !== id),\n \"SLUG_TAKEN\",\n \"A course already uses this URL.\",\n 409,\n );\n await this.assertPrerequisiteGraph(id, parsed.data.prerequisiteCourseIds);\n if (current) current = await this.editions.ensure(id, current.revision);\n let course: Course = {\n ...parsed.data,\n id,\n searchTitle: parsed.data.title.toLocaleLowerCase(\"en\"),\n createdBy: current?.value.createdBy ?? currentActor.id,\n createdAt: current?.value.createdAt ?? this.time(),\n updatedAt: this.time(),\n schemaVersion: 2,\n };\n const draft = await this.editions.revision(\n current?.value ?? course,\n parsed.data,\n currentActor.id,\n \"draft\",\n );\n if (current) {\n // Once published, the live discovery projection stays intact until explicit publication.\n course = {\n ...current.value,\n ...(current.value.publishedEditionId ? {} : parsed.data),\n status: current.value.status,\n draftRevisionId: draft.id,\n updatedAt: this.time(),\n };\n // Staff assignments apply when saved; historical editions cannot restore old authority.\n course.instructorIds = parsed.data.instructorIds;\n } else {\n course.draftRevisionId = draft.id;\n if (course.status === \"published\") {\n const editionId = await this.editions.snapshot(id, parsed.data);\n const edition = (await this.stores.course_editions.get(editionId))!;\n course = {\n ...course,\n ...edition.data,\n publishedEditionId: editionId,\n publishedDraftRevisionId: draft.id,\n };\n }\n }\n course.searchTitle = course.title.toLocaleLowerCase(\"en\");\n const saved = await this.editions.commit(current, course);\n await this.audit(currentActor, \"course.save\", id, { draftRevisionId: draft.id });\n return { course: await this.editions.draft(course), revision: saved.revision };\n }\n\n async publishCourse(actor: Actor | undefined, id: string, expectedRevision: string) {\n const owner = this.instructor(await this.course(id), actor);\n invariant(\n owner.role >= 30,\n \"FORBIDDEN\",\n \"Publishing requires an author account or higher.\",\n 403,\n );\n const current = await this.editions.ensure(id, expectedRevision);\n const draft = await this.editions.draft(current.value);\n const data = parse(courseDataSchema, { ...draft, status: \"published\" });\n invariant(\n data.commerce.accessModel === \"free\",\n \"PAID_COURSES_DISABLED\",\n \"Publish free courses only.\",\n 409,\n );\n await this.assertPrerequisiteGraph(id, data.prerequisiteCourseIds);\n const duplicate = await this.stores.courses.query({ where: { slug: data.slug }, limit: 1 });\n invariant(\n !duplicate.items.some((row) => row.id !== id),\n \"SLUG_TAKEN\",\n \"A course already uses this URL.\",\n 409,\n );\n const editionId = await this.editions.snapshot(id, data);\n const revision = await this.editions.revision(current.value, data, owner.id, \"publish\", {\n editionId,\n });\n const edition = (await this.stores.course_editions.get(editionId))!;\n const next = {\n ...current.value,\n ...edition.data,\n searchTitle: data.title.toLocaleLowerCase(\"en\"),\n draftRevisionId: revision.id,\n publishedDraftRevisionId: revision.id,\n publishedEditionId: editionId,\n updatedAt: this.time(),\n };\n const saved = await this.editions.commit(current, next);\n await this.audit(owner, \"course.publish\", id, { editionId });\n return { course: await this.editions.draft(next), revision: saved.revision };\n }\n\n async courseHistory(actor: Actor | undefined, id: string, cursor?: string) {\n const course = await this.course(id);\n this.instructor(course, actor);\n return this.editions.history(course, cursor);\n }\n\n async restoreCourse(\n actor: Actor | undefined,\n id: string,\n expectedRevision: string,\n revisionId: string,\n ) {\n const owner = this.instructor(await this.course(id), actor);\n const current = await this.editions.ensure(id, expectedRevision);\n const page = await this.editions.history(current.value, revisionId);\n invariant(page.items[0]?.id === revisionId, \"NOT_FOUND\", \"Saved revision not found.\", 404);\n const previous = (await this.stores.course_revisions.get(revisionId))!;\n // Restore content, not past access to the instructor console.\n const data = { ...previous.data, instructorIds: current.value.instructorIds };\n const revision = await this.editions.revision(current.value, data, owner.id, \"restore\", {\n restoredFrom: revisionId,\n });\n const next = { ...current.value, draftRevisionId: revision.id, updatedAt: this.time() };\n const saved = await this.editions.commit(current, next);\n await this.audit(owner, \"course.restore\", id, { revisionId });\n return { course: await this.editions.draft(next), revision: saved.revision };\n }\n\n async archiveCourse(actor: Actor | undefined, id: string, expectedRevision: string) {\n const owner = this.instructor(await this.course(id), actor);\n invariant(\n owner.role >= 30,\n \"FORBIDDEN\",\n \"Archiving requires an author account or higher.\",\n 403,\n );\n const legacy = await this.stores.courses.getVersioned(id);\n if (legacy?.value.schemaVersion === 1) {\n invariant(\n legacy.revision === expectedRevision,\n \"CONFLICT\",\n \"The course changed. Reload before continuing.\",\n 409,\n );\n // Visibility controls must work even when an unavailable source blocks snapshot migration.\n const next: Course = { ...legacy.value, status: \"archived\", updatedAt: this.time() };\n const saved = await this.editions.commit(legacy, next);\n await this.audit(owner, \"course.archive\", id);\n return { course: next, revision: saved.revision };\n }\n const current = await this.editions.ensure(id, expectedRevision);\n const draft = await this.editions.draft(current.value);\n const revision = await this.editions.revision(current.value, draft, owner.id, \"archive\");\n const next: Course = {\n ...current.value,\n status: \"archived\",\n draftRevisionId: revision.id,\n updatedAt: this.time(),\n };\n const saved = await this.editions.commit(current, next);\n await this.audit(owner, \"course.archive\", id);\n return { course: await this.editions.draft(next), revision: saved.revision };\n }\n\n async migrationStatus(actor: Actor | undefined) {\n invariant(\n user(actor).role >= 50,\n \"FORBIDDEN\",\n \"Only administrators can manage migrations.\",\n 403,\n );\n return this.editions.migrationStatus();\n }\n async migrateBatch(actor: Actor | undefined, limit: number) {\n await this.migrationStatus(actor);\n const state = await this.editions.migrateBatch(limit);\n if (state.phase === \"complete\") await this.ctx.kv.set(\"schemaVersion\", 2);\n return state;\n }\n async assertPrerequisiteGraph(courseId: string, prerequisites: string[]): Promise<void> {\n const visited = new Set<string>();\n const visit = async (id: string): Promise<void> => {\n invariant(id !== courseId, \"PREREQUISITE_CYCLE\", \"Course prerequisites cannot form a cycle.\");\n if (visited.has(id)) return;\n invariant(\n visited.size < 100,\n \"PREREQUISITE_GRAPH_TOO_LARGE\",\n \"The prerequisite chain is too long.\",\n );\n visited.add(id);\n const course = await this.course(id);\n for (const next of course.prerequisiteCourseIds) await visit(next);\n };\n for (const id of prerequisites) await visit(id);\n }\n async editableCourse(\n actor: Actor | undefined,\n id: string,\n ): Promise<{ course: Course; revision: string }> {\n const current = await this.stores.courses.getVersioned(id);\n invariant(current, \"NOT_FOUND\", \"Course not found.\", 404);\n this.instructor(current.value, actor);\n return { course: await this.editions.draft(current.value), revision: current.revision };\n }\n async adminCourses(\n actor: Actor | undefined,\n options: { cursor?: string; limit?: number; status?: string } = {},\n ): Promise<Page<Course>> {\n const current = user(actor);\n const page = await queryPage(this.stores.courses, {\n where: options.status ? { status: options.status } : {},\n orderBy: { updatedAt: \"desc\" },\n limit: options.limit ?? 24,\n cursor: options.cursor,\n });\n return {\n ...page,\n items: await Promise.all(\n page.items\n .filter(\n (course) =>\n current.role >= 20 &&\n (current.role >= 50 ||\n course.createdBy === current.id ||\n course.instructorIds.includes(current.id)),\n )\n .map((course) => this.editions.draft(course)),\n ),\n };\n }\n async catalog(input: unknown): Promise<Page<CourseCard>> {\n const options = parse(catalogSchema, input);\n const where: NonNullable<StorageQueryOptions[\"where\"]> = { status: \"published\" };\n if (options.q.trim())\n where.searchTitle = { startsWith: options.q.trim().toLocaleLowerCase(\"en\") };\n if (options.category) where.category = options.category;\n if (options.language) where.language = options.language;\n if (options.level && options.level !== \"all\") where.level = { in: [options.level, \"all\"] };\n const page = await queryPage(this.stores.courses, {\n where,\n orderBy: { title: \"asc\" },\n limit: options.limit,\n cursor: options.cursor,\n });\n return { ...page, items: page.items.map(courseCard) };\n }\n async publicCourse(slug: string) {\n const page = await this.stores.courses.query({\n where: { slug, status: \"published\" },\n limit: 1,\n });\n invariant(page.items[0], \"NOT_FOUND\", \"Course not found.\", 404);\n return this.detail(page.items[0].data);\n }\n async detail(course: Course) {\n const prerequisites = await this.stores.courses.getMany(course.prerequisiteCourseIds);\n return {\n ...courseDetail(course),\n prerequisites: course.prerequisiteCourseIds.map((id) => {\n const value = prerequisites.get(id);\n return {\n id,\n title: value?.title ?? \"Prerequisite course\",\n slug: value?.status === \"published\" ? value.slug : null,\n };\n }),\n };\n }\n async preview(courseId: string, lessonId: string) {\n const course = await this.course(courseId);\n invariant(course.status === \"published\", \"NOT_FOUND\", \"Course not found.\", 404);\n const lesson = findLesson(course, lessonId);\n invariant(\n lesson.preview && ![\"quiz\", \"assignment\", \"live\"].includes(lesson.kind),\n \"PREVIEW_UNAVAILABLE\",\n \"This lesson is available to enrolled learners.\",\n 403,\n );\n return {\n course: courseDetail(course),\n lesson: publicLesson(await this.resolveContent(lesson)),\n };\n }\n async resolveContent(\n lesson: ReturnType<typeof findLesson>,\n ): Promise<ReturnType<typeof findLesson>> {\n if (!lesson.contentSource) return lesson;\n invariant(this.ctx.content, \"CONTENT_UNAVAILABLE\", \"Content integration is unavailable.\", 503);\n const entry = await this.ctx.content.get(\n lesson.contentSource.collection,\n lesson.contentSource.id,\n );\n invariant(\n entry?.status === \"published\",\n \"CONTENT_UNAVAILABLE\",\n \"The linked lesson content is not published.\",\n 404,\n );\n const body = entry.data[lesson.contentSource.field];\n invariant(\n Array.isArray(body) || typeof body === \"string\",\n \"CONTENT_UNAVAILABLE\",\n \"The linked lesson field is unavailable.\",\n 404,\n );\n if (Array.isArray(body))\n return { ...lesson, body: \"\", portableText: body as Record<string, unknown>[] };\n return { ...lesson, body, portableText: [] };\n }\n async authorLessonPreview(actor: Actor | undefined, courseId: string, lessonId: string) {\n const { course } = await this.editableCourse(actor, courseId);\n return {\n course: courseDetail(course),\n lesson: publicLesson(await this.resolveContent(findLesson(course, lessonId))),\n };\n }\n async assertPrerequisites(course: Course, actorId: string): Promise<void> {\n for (const id of course.prerequisiteCourseIds) {\n const enrollment = await this.stores.enrollments.get(enrollmentKey(id, actorId));\n invariant(\n enrollment?.completedAt,\n \"COURSE_PREREQUISITE_REQUIRED\",\n \"Complete the prerequisite courses before enrolling.\",\n 403,\n );\n }\n }\n newEnrollment(course: Course, actor: Actor, status: Enrollment[\"status\"]): Enrollment {\n const now = this.time();\n const active = status === \"active\";\n return {\n id: enrollmentKey(course.id, actor.id),\n courseId: course.id,\n courseEditionId: course.publishedEditionId ?? course.legacyEditionId,\n userId: actor.id,\n learnerName: actor.name || actor.email.split(\"@\")[0] || \"Learner\",\n learnerEmail: actor.email,\n status,\n accessStatus: active ? \"granted\" : \"pending\",\n accessSource: \"free\",\n grants: {\n free: {\n id: \"free\",\n source: \"free\",\n referenceId: null,\n status: active ? \"granted\" : \"pending\",\n startsAt: null,\n expiresAt:\n active && course.accessDays\n ? new Date(this.now().getTime() + course.accessDays * 86_400_000).toISOString()\n : null,\n grantedAt: active ? now : null,\n grantedBy: null,\n },\n },\n cohortIds: [],\n acquisition: {\n orderId: null,\n offerId: course.commerce.offerId,\n paymentStatus: \"not_required\",\n amountMinor: null,\n currency: null,\n providerId: null,\n providerPaymentId: null,\n },\n approvedBy: null,\n decisionAt: active ? now : null,\n decisionReason: null,\n enrolledAt: now,\n approvedAt: active ? now : null,\n completedAt: null,\n accessStartsAt: null,\n accessExpiresAt: null,\n cohortId: null,\n progress: {},\n certificate: null,\n completionLessonIds: [],\n lastActivityAt: now,\n schemaVersion: 1,\n };\n }\n async enroll(actor: Actor | undefined, courseId: string, options: { cohortId?: string } = {}) {\n const current = user(actor);\n let course = await this.course(courseId);\n invariant(course.status === \"published\", \"NOT_FOUND\", \"Course not found.\", 404);\n invariant(\n course.commerce.accessModel === \"free\",\n \"PAID_COURSES_DISABLED\",\n \"Payment processing is not enabled for this course.\",\n 403,\n );\n course = (await this.editions.ensure(courseId)).value;\n invariant(course.status === \"published\", \"NOT_FOUND\", \"Course not found.\", 404);\n const cohort = options.cohortId ? await this.stores.cohorts.get(options.cohortId) : null;\n if (options.cohortId)\n invariant(\n cohort && cohort.courseIds.includes(courseId),\n \"NOT_FOUND\",\n \"This course is not part of that cohort.\",\n 404,\n );\n if (cohort) {\n const membership = await this.stores.cohort_enrollments.get(\n enrollmentKey(cohort.id, current.id),\n );\n invariant(\n membership?.status === \"active\",\n \"COHORT_APPROVAL_REQUIRED\",\n \"Request cohort membership and wait for administrator approval.\",\n 403,\n );\n }\n const id = enrollmentKey(courseId, current.id);\n const existing = await this.stores.enrollments.get(id);\n if (!cohort && existing && [\"active\", \"completed\", \"pending\"].includes(existing.status))\n return await this.enrollmentView(existing);\n invariant(\n course.enrollmentMode !== \"invite\" || cohort,\n \"INVITATION_REQUIRED\",\n \"An administrator must enroll you in this course.\",\n 403,\n );\n invariant(\n !course.enrollmentClosesAt || this.now().getTime() < Date.parse(course.enrollmentClosesAt),\n \"ENROLLMENT_CLOSED\",\n \"Enrollment for this course is closed.\",\n 403,\n );\n await this.assertPrerequisites(course, current.id);\n const enrollment = await change(this.stores.enrollments, id, async (stored) => {\n invariant(\n !stored || ![\"suspended\", \"cancelled\", \"rejected\"].includes(stored.status),\n \"ENROLLMENT_RESTRICTED\",\n \"Contact an administrator to restore your enrollment.\",\n 403,\n );\n const next = structuredClone(\n stored ??\n this.newEnrollment(\n course,\n current,\n !cohort && course.enrollmentMode === \"approval\" ? \"pending\" : \"active\",\n ),\n );\n if (cohort) {\n next.cohortId = cohort.id;\n next.cohortIds = [...new Set([...next.cohortIds, cohort.id])];\n if (!stored || stored.accessStatus !== \"granted\") next.accessSource = \"cohort\";\n if (!stored) delete next.grants.free;\n next.status = next.completedAt ? \"completed\" : \"active\";\n next.accessStatus = \"granted\";\n next.approvedAt ??= this.time();\n next.grants[`cohort:${cohort.id}`] = {\n id: `cohort:${cohort.id}`,\n source: \"cohort\",\n referenceId: cohort.id,\n status: \"granted\",\n startsAt: cohort.startsAt,\n expiresAt: cohort.endsAt,\n grantedAt: this.time(),\n grantedBy: null,\n };\n }\n return { value: next, result: next };\n });\n await this.notify(\n enrollment,\n \"enrolled\",\n enrollment.status === \"pending\" ? \"Enrollment requested\" : \"You are enrolled\",\n `${course.title}: ${enrollment.status === \"pending\" ? \"an administrator will review your request.\" : \"your learning journey is ready.\"}`,\n );\n return await this.enrollmentView(enrollment);\n }\n async adminEnrollment(actor: Actor | undefined, input: unknown) {\n const parsed = parse(enrollmentAdminSchema, input);\n let course = await this.course(parsed.courseId);\n const currentActor = this.instructor(course, actor);\n invariant(\n currentActor.role >= 50,\n \"FORBIDDEN\",\n \"Only administrators can make enrollment decisions.\",\n 403,\n );\n invariant(this.ctx.users, \"USERS_UNAVAILABLE\", \"The user directory is unavailable.\", 503);\n if (\n parsed.status === \"active\" &&\n !(await this.stores.enrollments.get(enrollmentKey(course.id, parsed.userId)))\n )\n course = (await this.editions.ensure(parsed.courseId)).value;\n const target = await this.ctx.users.get(parsed.userId);\n invariant(target, \"NOT_FOUND\", \"Learner account not found.\", 404);\n invariant(\n !parsed.accessStartsAt ||\n !parsed.accessExpiresAt ||\n Date.parse(parsed.accessExpiresAt) > Date.parse(parsed.accessStartsAt),\n \"INVALID_ACCESS_WINDOW\",\n \"Access expiry must follow its start.\",\n );\n if (parsed.cohortId) {\n const cohort = await this.stores.cohorts.get(parsed.cohortId);\n invariant(\n cohort && cohort.courseIds.includes(course.id),\n \"INVALID_COHORT\",\n \"This course must belong to the selected cohort.\",\n );\n }\n if (parsed.status === \"active\") {\n invariant(\n course.status !== \"draft\",\n \"COURSE_NOT_PUBLISHED\",\n \"Publish the course before enrolling learners.\",\n 409,\n );\n invariant(\n course.commerce.accessModel === \"free\",\n \"PAID_COURSES_DISABLED\",\n \"Paid access is not enabled in this release.\",\n 403,\n );\n await this.assertPrerequisites(course, target.id);\n }\n const cohort = parsed.cohortId ? await this.stores.cohorts.get(parsed.cohortId) : null;\n if (cohort && parsed.status === \"active\") {\n const membership = await this.stores.cohort_enrollments.get(\n enrollmentKey(cohort.id, target.id),\n );\n invariant(\n membership?.status === \"active\",\n \"COHORT_APPROVAL_REQUIRED\",\n \"Approve cohort membership before granting cohort access.\",\n 403,\n );\n }\n const settings = await this.settings();\n const enrollment = await change(\n this.stores.enrollments,\n enrollmentKey(course.id, target.id),\n async (stored) => {\n const next = structuredClone(stored ?? this.newEnrollment(course, target, parsed.status));\n const firstApproval = parsed.status === \"active\" && !next.approvedAt;\n next.courseEditionId ??= course.legacyEditionId ?? course.publishedEditionId;\n next.status = parsed.status === \"active\" && next.completedAt ? \"completed\" : parsed.status;\n next.accessStatus =\n parsed.status === \"active\"\n ? \"granted\"\n : parsed.status === \"suspended\"\n ? \"suspended\"\n : \"revoked\";\n next.approvedBy = parsed.status === \"active\" ? currentActor.id : next.approvedBy;\n next.decisionAt = this.time();\n next.decisionReason = parsed.reason || null;\n next.accessSource = stored?.approvedAt ? stored.accessSource : cohort ? \"cohort\" : \"manual\";\n if (firstApproval) {\n next.approvedAt = this.time();\n }\n if (parsed.cohortId !== undefined) next.cohortId = parsed.cohortId;\n if (!cohort && parsed.accessStartsAt !== undefined)\n next.accessStartsAt = parsed.accessStartsAt;\n if (!cohort && parsed.accessExpiresAt !== undefined)\n next.accessExpiresAt = parsed.accessExpiresAt;\n if (parsed.status === \"active\") {\n if (!stored) delete next.grants.free;\n const grantId = cohort ? `cohort:${cohort.id}` : \"manual\";\n const previous = next.grants[grantId];\n next.grants[grantId] = {\n id: grantId,\n source: cohort ? \"cohort\" : \"manual\",\n referenceId: cohort?.id ?? null,\n status: \"granted\",\n startsAt: cohort?.startsAt ?? null,\n expiresAt: cohort\n ? cohort.endsAt\n : parsed.accessExpiresAt !== undefined\n ? parsed.accessExpiresAt\n : previous &&\n (!previous.expiresAt || Date.parse(previous.expiresAt) > this.now().getTime())\n ? previous.expiresAt\n : course.accessDays\n ? new Date(this.now().getTime() + course.accessDays * 86_400_000).toISOString()\n : null,\n grantedAt: previous?.grantedAt ?? this.time(),\n grantedBy: currentActor.id,\n };\n if (cohort) next.cohortIds = [...new Set([...next.cohortIds, cohort.id])];\n }\n invariant(\n !next.accessStartsAt ||\n !next.accessExpiresAt ||\n Date.parse(next.accessExpiresAt) > Date.parse(next.accessStartsAt),\n \"INVALID_ACCESS_WINDOW\",\n \"Access expiry must follow its start.\",\n );\n next.lastActivityAt = this.time();\n if (parsed.status === \"active\")\n this.finish(await this.editions.learning(course, next), next, settings);\n return { value: next, result: next };\n },\n );\n await this.audit(currentActor, \"enrollment.update\", enrollment.id, {\n status: enrollment.status,\n });\n await this.notify(\n enrollment,\n `enrollment-${enrollment.status}-${enrollment.decisionAt}`,\n \"Enrollment updated\",\n `${course.title}: your enrollment is ${enrollment.status}.`,\n );\n await this.projectCompletion(enrollment);\n return staffEnrollment((await this.accessEnrollment(enrollment))!, this.now());\n }\n async learnerCourse(actor: Actor | undefined, courseId: string) {\n const current = user(actor);\n const course = await this.course(courseId);\n const enrollment = await this.stores.enrollments.get(enrollmentKey(courseId, current.id));\n invariant(\n course.status === \"published\" || (course.status === \"archived\" && enrollment),\n \"NOT_FOUND\",\n \"Course not found.\",\n 404,\n );\n if (enrollment?.completedAt) await this.projectCompletion(enrollment);\n return {\n course: await this.detail(await this.editions.learning(course, enrollment)),\n ...(await this.learningState(course, enrollment)),\n };\n }\n async learnerCourseBySlug(actor: Actor | undefined, slug: string) {\n user(actor);\n const page = await this.stores.courses.query({ where: { slug }, limit: 1 });\n invariant(page.items[0], \"NOT_FOUND\", \"Course not found.\", 404);\n return this.learnerCourse(actor, page.items[0].id);\n }\n async dashboard(actor: Actor | undefined, options: { cursor?: string; limit?: number } = {}) {\n const current = user(actor);\n const page = await queryPage(this.stores.enrollments, {\n where: { userId: current.id },\n orderBy: { enrolledAt: \"desc\" },\n limit: options.limit ?? 24,\n cursor: options.cursor,\n });\n const courses = await this.stores.courses.getMany(page.items.map((entry) => entry.courseId));\n const rows = await Promise.all(\n page.items.map(async (enrollment) => {\n if (enrollment.completedAt) await this.projectCompletion(enrollment);\n const course = courses.get(enrollment.courseId);\n return course\n ? {\n course: courseCard(await this.editions.learning(course, enrollment)),\n ...(await this.learningState(course, enrollment)),\n }\n : null;\n }),\n );\n const items = rows.filter((row) => row !== null);\n return { ...page, items };\n }\n async learnerLesson(actor: Actor | undefined, courseId: string, lessonId: string) {\n const current = user(actor);\n const course = await this.course(courseId);\n const enrollment = await this.stores.enrollments.get(enrollmentKey(courseId, current.id));\n const lesson = await this.lessonAccess(course, lessonId, enrollment);\n return {\n course: courseDetail(await this.editions.learning(course, enrollment)),\n lesson: publicLesson(await this.resolveContent(lesson)),\n ...(await this.learningState(course, enrollment)),\n };\n }\n async startLesson(actor: Actor | undefined, courseId: string, lessonId: string) {\n const current = user(actor);\n const course = await this.course(courseId);\n const enrollment = await change(\n this.stores.enrollments,\n enrollmentKey(courseId, current.id),\n async (stored) => {\n await this.lessonAccess(course, lessonId, stored);\n const next = structuredClone(stored!);\n const progress = next.progress[lessonId] ?? emptyProgress();\n progress.startedAt ??= this.time();\n next.progress[lessonId] = progress;\n next.lastActivityAt = this.time();\n return { value: next, result: next };\n },\n );\n return await this.enrollmentView(enrollment);\n }\n async completeLesson(actor: Actor | undefined, courseId: string, lessonId: string) {\n const current = user(actor);\n const course = await this.course(courseId);\n const settings = await this.settings();\n const enrollment = await change(\n this.stores.enrollments,\n enrollmentKey(courseId, current.id),\n async (stored) => {\n const lesson = await this.lessonAccess(course, lessonId, stored);\n invariant(\n ![\"quiz\", \"assignment\"].includes(lesson.kind),\n \"ASSESSMENT_REQUIRED\",\n \"Pass the quiz or assignment to complete this lesson.\",\n 403,\n );\n const next = structuredClone(stored!);\n const progress = next.progress[lessonId] ?? emptyProgress();\n invariant(\n progress.startedAt,\n \"LESSON_NOT_STARTED\",\n \"Start the lesson before marking it complete.\",\n );\n if (lesson.kind === \"live\")\n invariant(\n lesson.liveStartsAt && this.now().getTime() >= Date.parse(lesson.liveStartsAt),\n \"LIVE_NOT_STARTED\",\n \"The live session has not started yet.\",\n 403,\n );\n invariant(\n this.now().getTime() - Date.parse(progress.startedAt) >= lesson.minStudySeconds * 1000,\n \"STUDY_TIME_REQUIRED\",\n \"Spend the minimum study time on this lesson before completing it.\",\n 403,\n );\n progress.completedAt ??= this.time();\n next.progress[lessonId] = progress;\n this.finish(await this.editions.learning(course, next), next, settings);\n return { value: next, result: next };\n },\n );\n await this.projectCompletion(enrollment);\n return {\n ...(await this.learningState(course, enrollment)),\n };\n }\n finish(course: Course, enrollment: Enrollment, settings: LmsSettings): void {\n enrollment.lastActivityAt = this.time();\n const required = lessons(course).filter((lesson) => lesson.required);\n if (\n enrollment.completedAt ||\n !required.length ||\n !required.every((lesson) => enrollment.progress[lesson.id]?.completedAt)\n )\n return;\n enrollment.status = \"completed\";\n enrollment.completedAt = this.time();\n enrollment.completionLessonIds = required.map((lesson) => lesson.id);\n if (course.certificateEnabled)\n enrollment.certificate = {\n id: crypto.randomUUID(),\n courseId: course.id,\n courseTitle: course.title,\n userId: enrollment.userId,\n learnerName: enrollment.learnerName,\n issuerName: settings.issuerName,\n issuedAt: this.time(),\n shared: false,\n revokedAt: null,\n revocationReason: null,\n };\n }\n async projectCompletion(enrollment: Enrollment): Promise<void> {\n if (enrollment.certificate)\n await putOnce(this.stores.certificates, enrollment.certificate.id, {\n enrollmentId: enrollment.id,\n courseId: enrollment.courseId,\n userId: enrollment.userId,\n });\n if (enrollment.completedAt)\n await this.notify(\n enrollment,\n \"completed\",\n \"Course complete\",\n \"You have completed your course. Your achievement is saved in My Learning.\",\n );\n }\n async startQuiz(\n actor: Actor | undefined,\n courseId: string,\n lessonId: string,\n ): Promise<PublicQuizAttempt> {\n const current = user(actor);\n const course = await this.course(courseId);\n const result = await change<Enrollment, { attempt: QuizAttempt | null; blocked: boolean }>(\n this.stores.enrollments,\n enrollmentKey(courseId, current.id),\n async (stored) => {\n const lesson = await this.lessonAccess(course, lessonId, stored);\n invariant(lesson.kind === \"quiz\" && lesson.quiz, \"NOT_A_QUIZ\", \"This lesson has no quiz.\");\n const next = structuredClone(stored!);\n const progress = next.progress[lessonId] ?? emptyProgress();\n progress.startedAt ??= this.time();\n const existing = progress.quizAttempts.find((attempt) => attempt.status === \"in_progress\");\n if (\n existing &&\n (!existing.expiresAt || this.now().getTime() < Date.parse(existing.expiresAt))\n )\n return { value: next, result: { attempt: existing, blocked: false } };\n if (existing) {\n existing.status = \"expired\";\n existing.quiz = null;\n existing.submittedAt = this.time();\n }\n next.progress[lessonId] = progress;\n if (progress.quizAttempts.length >= lesson.quiz.maxAttempts || progress.completedAt)\n return { value: next, result: { attempt: null, blocked: true } };\n const quiz = structuredClone(lesson.quiz);\n const order = quiz.randomizeQuestions ? shuffle(quiz.questions) : quiz.questions;\n const questions = order.map(\n ({ correctAnswers: _correct, explanation: _explanation, ...question }) => ({\n ...question,\n options: quiz.randomizeOptions ? shuffle(question.options) : question.options,\n }),\n );\n const attempt = {\n id: crypto.randomUUID(),\n startedAt: this.time(),\n expiresAt: quiz.timeLimitMinutes\n ? new Date(this.now().getTime() + quiz.timeLimitMinutes * 60_000).toISOString()\n : null,\n status: \"in_progress\" as const,\n quiz,\n questions,\n submittedAt: null,\n score: null,\n maxScore: null,\n percent: null,\n passed: false,\n feedback: [],\n answers: {},\n draftRevision: 0,\n };\n progress.quizAttempts.push(attempt);\n next.lastActivityAt = this.time();\n return { value: next, result: { attempt, blocked: false } };\n },\n );\n invariant(\n result.attempt && !result.blocked,\n \"ATTEMPT_LIMIT\",\n \"No further quiz attempts are available. Contact an instructor.\",\n 403,\n );\n return publicAttempt(result.attempt);\n }\n async submitQuiz(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const parsed = parse(quizSubmitSchema, input);\n const course = await this.course(parsed.courseId);\n const settings = await this.settings();\n const result = await change(\n this.stores.enrollments,\n enrollmentKey(course.id, current.id),\n async (stored) => {\n await this.lessonAccess(course, parsed.lessonId, stored);\n const next = structuredClone(stored!);\n const progress = next.progress[parsed.lessonId];\n const attempt = progress?.quizAttempts.find((item) => item.id === parsed.attemptId);\n invariant(attempt && progress, \"NOT_FOUND\", \"Quiz attempt not found.\", 404);\n if (attempt.status !== \"in_progress\")\n return {\n value: next,\n result: { enrollment: next, attempt, expired: attempt.status === \"expired\" },\n };\n if (attempt.expiresAt && this.now().getTime() >= Date.parse(attempt.expiresAt)) {\n attempt.status = \"expired\";\n attempt.quiz = null;\n attempt.submittedAt = this.time();\n return { value: next, result: { enrollment: next, attempt, expired: true } };\n }\n invariant(attempt.quiz, \"INVALID_ATTEMPT\", \"This attempt cannot be graded.\", 409);\n invariant(\n parsed.expectedDraftRevision === undefined ||\n parsed.expectedDraftRevision === (attempt.draftRevision ?? 0),\n \"DRAFT_CONFLICT\",\n \"Saved answers changed in another request. Review them before submitting.\",\n 409,\n );\n const graded = gradeAnswers(attempt.quiz.questions, parsed.answers);\n attempt.passed = graded.score * 100 >= graded.maxScore * attempt.quiz.passingPercent;\n attempt.feedback =\n attempt.quiz.showFeedback === \"after_submit\" ||\n (attempt.quiz.showFeedback === \"after_pass\" && attempt.passed)\n ? graded.feedback\n : [];\n attempt.score = graded.score;\n attempt.maxScore = graded.maxScore;\n attempt.percent = graded.percent;\n attempt.answers = parsed.answers;\n attempt.status = \"submitted\";\n attempt.submittedAt = this.time();\n attempt.quiz = null;\n if (attempt.passed) progress.completedAt ??= this.time();\n this.finish(await this.editions.learning(course, next), next, settings);\n return { value: next, result: { enrollment: next, attempt, expired: false } };\n },\n );\n invariant(\n !result.expired,\n \"QUIZ_EXPIRED\",\n \"The quiz time limit expired. Start a new attempt if one is available.\",\n 409,\n );\n await this.projectCompletion(result.enrollment);\n return {\n attempt: publicAttempt(result.attempt),\n ...(await this.learningState(course, result.enrollment)),\n };\n }\n async saveQuizDraft(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const parsed = parse(quizDraftSchema, input);\n const course = await this.course(parsed.courseId);\n const draftRevision = await change(\n this.stores.enrollments,\n enrollmentKey(course.id, current.id),\n async (stored) => {\n await this.lessonAccess(course, parsed.lessonId, stored);\n const next = structuredClone(stored!);\n const attempt = next.progress[parsed.lessonId]?.quizAttempts.find(\n (item) => item.id === parsed.attemptId,\n );\n invariant(\n attempt?.status === \"in_progress\" && attempt.quiz,\n \"ATTEMPT_CLOSED\",\n \"This quiz attempt is closed.\",\n 409,\n );\n invariant(\n !attempt.expiresAt || this.now().getTime() < Date.parse(attempt.expiresAt),\n \"QUIZ_EXPIRED\",\n \"This quiz attempt has expired.\",\n 409,\n );\n gradeAnswers(attempt.quiz.questions, parsed.answers);\n const revision = attempt.draftRevision ?? 0;\n const canonical = (answers: typeof parsed.answers) =>\n JSON.stringify(Object.entries(answers).sort(([a], [b]) => a.localeCompare(b)));\n // Retrying a successfully saved request whose response was lost is harmless.\n if (\n revision === parsed.expectedDraftRevision + 1 &&\n canonical(attempt.answers) === canonical(parsed.answers)\n )\n return { value: next, result: revision };\n invariant(\n revision === parsed.expectedDraftRevision,\n \"DRAFT_CONFLICT\",\n \"Saved answers changed in another request. Review them before saving.\",\n 409,\n );\n attempt.answers = parsed.answers;\n attempt.draftRevision = revision + 1;\n return { value: next, result: attempt.draftRevision };\n },\n );\n return { saved: true, draftRevision };\n }\n async submitAssignment(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const parsed = parse(assignmentSubmitSchema, input);\n const course = await this.course(parsed.courseId);\n const enrollment = await change(\n this.stores.enrollments,\n enrollmentKey(course.id, current.id),\n async (stored) => {\n const lesson = await this.lessonAccess(course, parsed.lessonId, stored);\n invariant(\n lesson.kind === \"assignment\" && lesson.assignment,\n \"NOT_AN_ASSIGNMENT\",\n \"This lesson has no assignment.\",\n );\n invariant(\n !lesson.assignment.dueAt || this.now().getTime() <= Date.parse(lesson.assignment.dueAt),\n \"ASSIGNMENT_CLOSED\",\n \"The assignment deadline has passed.\",\n 403,\n );\n const next = structuredClone(stored!);\n const progress = next.progress[parsed.lessonId] ?? emptyProgress();\n const last = progress.assignments.at(-1);\n invariant(\n !progress.completedAt,\n \"ASSIGNMENT_PASSED\",\n \"This assignment has already been passed.\",\n 409,\n );\n invariant(\n !last || (last.status === \"graded\" && lesson.assignment.allowResubmission),\n \"SUBMISSION_PENDING\",\n \"Wait for grading before submitting again.\",\n 409,\n );\n invariant(\n progress.assignments.length < 10,\n \"SUBMISSION_LIMIT\",\n \"The assignment submission limit has been reached.\",\n 403,\n );\n progress.startedAt ??= this.time();\n progress.assignments.push({\n id: crypto.randomUUID(),\n body: parsed.body,\n attachmentUrl: parsed.attachmentUrl,\n submittedAt: this.time(),\n status: \"submitted\",\n maxPoints: lesson.assignment.maxPoints,\n passingPoints: lesson.assignment.passingPoints,\n points: null,\n feedback: \"\",\n gradedAt: null,\n gradedBy: null,\n });\n next.progress[parsed.lessonId] = progress;\n next.lastActivityAt = this.time();\n return { value: next, result: next };\n },\n );\n return await this.enrollmentView(enrollment);\n }\n async gradeAssignment(actor: Actor | undefined, input: unknown) {\n const parsed = parse(gradeSchema, input);\n const course = await this.course(parsed.courseId);\n const current = this.instructor(course, actor);\n const settings = await this.settings();\n const enrollment = await change(\n this.stores.enrollments,\n enrollmentKey(course.id, parsed.userId),\n async (stored) => {\n invariant(stored, \"NOT_FOUND\", \"Enrollment not found.\", 404);\n const next = structuredClone(stored);\n const progress = next.progress[parsed.lessonId];\n const submission = progress?.assignments.find((item) => item.id === parsed.submissionId);\n invariant(submission && progress, \"NOT_FOUND\", \"Assignment submission not found.\", 404);\n invariant(\n submission.status === \"submitted\",\n \"ALREADY_GRADED\",\n \"This submission has already been graded.\",\n 409,\n );\n invariant(\n parsed.points <= submission.maxPoints,\n \"INVALID_GRADE\",\n \"The grade cannot exceed the assignment's maximum points.\",\n );\n submission.points = parsed.points;\n submission.feedback = parsed.feedback;\n submission.status = \"graded\";\n submission.gradedAt = this.time();\n submission.gradedBy = current.id;\n if (parsed.points >= submission.passingPoints) progress.completedAt ??= this.time();\n const access = effectiveAccess((await this.accessEnrollment(next))!, this.now());\n if (\n [\"active\", \"completed\"].includes(next.status) &&\n ![\"suspended\", \"revoked\", \"pending\"].includes(access)\n )\n this.finish(await this.editions.learning(course, next), next, settings);\n return { value: next, result: next };\n },\n );\n await this.projectCompletion(enrollment);\n await this.notify(\n enrollment,\n `graded-${parsed.submissionId}`,\n \"Assignment graded\",\n \"Your assignment feedback is ready in My Learning.\",\n );\n await this.audit(current, \"assignment.grade\", enrollment.id, {\n lessonId: parsed.lessonId,\n points: parsed.points,\n });\n return staffEnrollment((await this.accessEnrollment(enrollment))!, this.now());\n }\n async saveNote(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const parsed = parse(noteSchema, input);\n const course = await this.course(parsed.courseId);\n const enrollment = await change(\n this.stores.enrollments,\n enrollmentKey(course.id, current.id),\n async (stored) => {\n await this.lessonAccess(course, parsed.lessonId, stored);\n const next = structuredClone(stored!);\n const progress = next.progress[parsed.lessonId] ?? emptyProgress();\n progress.note = parsed.note;\n next.progress[parsed.lessonId] = progress;\n return { value: next, result: next };\n },\n );\n return { note: enrollment.progress[parsed.lessonId]!.note };\n }\n async certificate(actor: Actor | undefined, courseId: string) {\n const current = user(actor);\n const enrollment = await this.stores.enrollments.get(enrollmentKey(courseId, current.id));\n invariant(enrollment?.certificate, \"NOT_FOUND\", \"Certificate not found.\", 404);\n await this.projectCompletion(enrollment);\n return enrollment.certificate;\n }\n async ownedCertificate(actor: Actor | undefined, id: string) {\n const current = user(actor);\n const index = await this.stores.certificates.get(id);\n invariant(index?.userId === current.id, \"NOT_FOUND\", \"Certificate not found.\", 404);\n const enrollment = await this.stores.enrollments.get(index.enrollmentId);\n invariant(enrollment?.certificate?.id === id, \"NOT_FOUND\", \"Certificate not found.\", 404);\n return enrollment.certificate;\n }\n async verifyCertificate(id: string) {\n const index = await this.stores.certificates.get(id);\n invariant(index, \"NOT_FOUND\", \"Certificate not found.\", 404);\n const enrollment = await this.stores.enrollments.get(index.enrollmentId);\n const certificate = enrollment?.certificate;\n invariant(certificate?.id === id, \"NOT_FOUND\", \"Certificate not found.\", 404);\n return {\n id,\n courseTitle: certificate.courseTitle,\n issuerName: certificate.issuerName,\n issuedAt: certificate.issuedAt,\n valid: !certificate.revokedAt,\n shared: certificate.shared,\n learnerName: certificate.shared ? certificate.learnerName : null,\n };\n }\n async shareCertificate(actor: Actor | undefined, courseId: string, shared: boolean) {\n const current = user(actor);\n const certificate = await change(\n this.stores.enrollments,\n enrollmentKey(courseId, current.id),\n async (stored) => {\n invariant(stored?.certificate, \"NOT_FOUND\", \"Certificate not found.\", 404);\n const next = structuredClone(stored);\n next.certificate!.shared = shared;\n return { value: next, result: next.certificate! };\n },\n );\n return certificate;\n }\n async revokeCertificate(\n actor: Actor | undefined,\n courseId: string,\n userId: string,\n reason: string,\n ) {\n const course = await this.course(courseId);\n const current = this.instructor(course, actor);\n const certificate = await change(\n this.stores.enrollments,\n enrollmentKey(courseId, userId),\n async (stored) => {\n invariant(stored?.certificate, \"NOT_FOUND\", \"Certificate not found.\", 404);\n const next = structuredClone(stored);\n next.certificate!.revokedAt = this.time();\n next.certificate!.revocationReason = reason;\n return { value: next, result: next.certificate! };\n },\n );\n await this.audit(current, \"certificate.revoke\", certificate.id);\n return certificate;\n }\n async cancelEnrollment(actor: Actor | undefined, courseId: string) {\n const current = user(actor);\n const enrollment = await change(\n this.stores.enrollments,\n enrollmentKey(courseId, current.id),\n async (stored) => {\n invariant(stored, \"NOT_FOUND\", \"Enrollment not found.\", 404);\n const next = { ...stored, status: \"cancelled\" as const, accessStatus: \"revoked\" as const };\n return { value: next, result: next };\n },\n );\n return await this.enrollmentView(enrollment);\n }\n async discussions(actor: Actor | undefined, courseId: string, lessonId: string, cursor?: string) {\n const current = user(actor);\n const course = await this.course(courseId);\n const enrollment = await this.stores.enrollments.get(enrollmentKey(courseId, current.id));\n await this.lessonAccess(course, lessonId, enrollment);\n const page = await queryPage(this.stores.discussions, {\n where: { courseId, lessonId, status: \"published\" },\n orderBy: { createdAt: \"asc\" },\n cursor,\n limit: 30,\n });\n const own = await queryPage(this.stores.discussions, {\n where: { courseId, lessonId, authorId: current.id, status: \"pending\" },\n limit: 30,\n });\n return { ...page, pending: own.items };\n }\n async postDiscussion(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const parsed = parse(discussionSchema, input);\n const course = await this.course(parsed.courseId);\n const enrollment = await this.stores.enrollments.get(enrollmentKey(course.id, current.id));\n await this.lessonAccess(course, parsed.lessonId, enrollment);\n if (parsed.parentId) {\n const parent = await this.stores.discussions.get(parsed.parentId);\n invariant(\n parent &&\n parent.courseId === course.id &&\n parent.lessonId === parsed.lessonId &&\n parent.status === \"published\" &&\n !parent.parentId,\n \"INVALID_PARENT\",\n \"Reply to a published discussion in this lesson.\",\n );\n }\n const settings = await this.settings();\n const id = crypto.randomUUID();\n const discussion: Discussion = {\n ...parsed,\n id,\n authorId: current.id,\n authorName: current.name || \"Learner\",\n status: settings.moderateDiscussions ? \"pending\" : \"published\",\n createdAt: this.time(),\n updatedAt: this.time(),\n };\n await this.stores.discussions.put(id, discussion);\n return discussion;\n }\n async publicReviews(courseId: string, cursor?: string) {\n const course = await this.course(courseId);\n invariant(course.status === \"published\", \"NOT_FOUND\", \"Course not found.\", 404);\n return queryPage(this.stores.reviews, {\n where: { courseId, status: \"published\" },\n orderBy: { createdAt: \"desc\" },\n limit: 24,\n cursor,\n });\n }\n async postReview(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const parsed = parse(reviewSchema, input);\n const course = await this.course(parsed.courseId);\n const enrollment = await this.accessEnrollment(\n await this.stores.enrollments.get(enrollmentKey(course.id, current.id)),\n );\n assertEnrollmentAccess(enrollment, this.now());\n invariant(\n Object.values(enrollment.progress).some((progress) => progress.completedAt),\n \"REVIEW_TOO_EARLY\",\n \"Complete at least one lesson before reviewing this course.\",\n 403,\n );\n const settings = await this.settings();\n const id = enrollmentKey(course.id, current.id);\n const review = await change(this.stores.reviews, id, async (stored) => {\n const next: Review = {\n ...parsed,\n id,\n userId: current.id,\n authorName: current.name || \"Learner\",\n status: settings.moderateReviews ? \"pending\" : \"published\",\n createdAt: stored?.createdAt ?? this.time(),\n updatedAt: this.time(),\n };\n return { value: next, result: next };\n });\n return review;\n }\n async moderate(actor: Actor | undefined, collection: \"reviews\" | \"discussions\", input: unknown) {\n const parsed = parse(moderateSchema, input);\n const store = this.stores[collection];\n const stored = await store.getVersioned(parsed.id);\n invariant(stored, \"NOT_FOUND\", \"Community item not found.\", 404);\n const course = await this.course(stored.value.courseId);\n const current = this.instructor(course, actor);\n invariant(\n stored.revision === parsed.expectedRevision,\n \"CONFLICT\",\n \"This item changed. Reload before moderating.\",\n 409,\n );\n const next = { ...stored.value, status: parsed.status, updatedAt: this.time() };\n const saved = await (store as typeof this.stores.discussions).compareAndSet(\n parsed.id,\n stored.revision,\n next as Discussion,\n );\n invariant(saved.applied, \"CONFLICT\", \"This item changed while moderating.\", 409);\n await this.audit(current, `${collection}.moderate`, parsed.id, { status: parsed.status });\n return next;\n }\n async communityQueue(\n actor: Actor | undefined,\n collection: \"reviews\" | \"discussions\",\n options: { courseId?: string; cursor?: string; status?: string } = {},\n ) {\n const current = user(actor);\n if (options.courseId) this.instructor(await this.course(options.courseId), current);\n else invariant(current.role >= 50, \"FORBIDDEN\", \"Select a course you teach.\", 403);\n const store = this.stores[collection];\n const page = await store.query({\n where: {\n ...(options.courseId ? { courseId: options.courseId } : {}),\n ...(options.status ? { status: options.status } : {}),\n },\n orderBy: { createdAt: \"desc\" },\n limit: 30,\n cursor: options.cursor,\n });\n const versions = await Promise.all(page.items.map((item) => store.getVersioned(item.id)));\n const items = versions.flatMap((item) =>\n item ? [{ ...item.value, revision: item.revision }] : [],\n );\n return { ...page, items };\n }\n async enrollments(\n actor: Actor | undefined,\n options: { courseId?: string; status?: string; cursor?: string; limit?: number } = {},\n ) {\n const current = user(actor);\n if (options.courseId) this.instructor(await this.course(options.courseId), current);\n else invariant(current.role >= 50, \"FORBIDDEN\", \"Select a course you teach.\", 403);\n const page = await queryPage(this.stores.enrollments, {\n where: {\n ...(options.courseId ? { courseId: options.courseId } : {}),\n ...(options.status ? { status: options.status } : {}),\n },\n orderBy: { enrolledAt: \"desc\" },\n limit: options.limit ?? 30,\n cursor: options.cursor,\n });\n const courses = await this.stores.courses.getMany(page.items.map((item) => item.courseId));\n return {\n ...page,\n items: await Promise.all(\n page.items.map(async (enrollment) => {\n const resolved = await this.accessEnrollment(enrollment);\n const root = courses.get(enrollment.courseId);\n const course = root ? await this.editions.learning(root, enrollment) : null;\n const now = this.now();\n return {\n ...staffEnrollment(resolved!, now),\n learnerEmail: enrollment.learnerEmail,\n courseTitle: course?.title ?? \"Archived course\",\n lessonTitles: Object.fromEntries(\n (course ? lessons(course) : []).map((lesson) => [lesson.id, lesson.title]),\n ),\n progressSummary: course ? progressSummary(course, resolved, now) : null,\n };\n }),\n ),\n };\n }\n async report(actor: Actor | undefined, courseId?: string) {\n const current = user(actor);\n if (courseId) this.instructor(await this.course(courseId), current);\n else invariant(current.role >= 50, \"FORBIDDEN\", \"Select a course you teach.\", 403);\n const filter: NonNullable<StorageQueryOptions[\"where\"]> = courseId ? { courseId } : {};\n const statuses = [\"pending\", \"active\", \"completed\", \"rejected\", \"suspended\", \"cancelled\"];\n const counts = await Promise.all(\n statuses.map((status) => this.stores.enrollments.count({ ...filter, status })),\n );\n const statusCounts = Object.fromEntries(\n statuses.map((status, index) => [status, counts[index]!]),\n );\n const total = counts.reduce((sum, count) => sum + count, 0);\n const completedEver = await this.stores.enrollments.count({\n ...filter,\n completedAt: { gte: \"0000-01-01T00:00:00.000Z\" },\n });\n const sourceCounts = Object.fromEntries(\n await Promise.all(\n [\"free\", \"manual\", \"cohort\", \"purchase\"].map(async (accessSource) => [\n accessSource,\n await this.stores.enrollments.count({ ...filter, accessSource }),\n ]),\n ),\n );\n const accessDecisions = Object.fromEntries(\n await Promise.all(\n [\"pending\", \"granted\", \"suspended\", \"revoked\"].map(async (accessStatus) => [\n accessStatus,\n await this.stores.enrollments.count({ ...filter, accessStatus }),\n ]),\n ),\n );\n return {\n total,\n completedEver,\n ...statusCounts,\n accessSources: sourceCounts,\n accessDecisions,\n completionRate: total ? Math.round((completedEver / total) * 100) : 0,\n courseCount: courseId ? 1 : await this.stores.courses.count(),\n generatedAt: this.time(),\n };\n }\n async enrollmentCsv(\n actor: Actor | undefined,\n options: { courseId?: string; status?: string } = {},\n ) {\n const lines = [enrollmentCsvHeader];\n let cursor: string | undefined;\n let rows = 0;\n let bytes = new TextEncoder().encode(enrollmentCsvHeader).byteLength + 3;\n do {\n const page = await this.enrollments(actor, { ...options, cursor, limit: 100 });\n const next = enrollmentCsvLines(page.items);\n rows += next.length;\n bytes += new TextEncoder().encode(next.join(\"\\r\\n\")).byteLength + next.length * 2;\n invariant(\n rows <= 10_000 && bytes <= 6_000_000,\n \"EXPORT_TOO_LARGE\",\n \"Filter by course or enrollment status to export fewer records.\",\n 413,\n );\n lines.push(...next);\n cursor = page.hasMore ? page.cursor : undefined;\n } while (cursor);\n return \"\\uFEFF\" + lines.join(\"\\r\\n\");\n }\n async cohortList(actor: Actor | undefined, cursor?: string) {\n const current = user(actor);\n invariant(current.role >= 50, \"FORBIDDEN\", \"Only administrators can manage cohorts.\", 403);\n const page = await this.stores.cohorts.query({\n orderBy: { createdAt: \"desc\" },\n limit: 24,\n cursor,\n });\n const versions = await Promise.all(\n page.items.map((item) => this.stores.cohorts.getVersioned(item.id)),\n );\n const items = versions.flatMap((item) =>\n item ? [{ ...item.value, revision: item.revision }] : [],\n );\n return { ...page, items };\n }\n async publicCohort(id: string) {\n const cohort = await this.stores.cohorts.get(id);\n invariant(cohort, \"NOT_FOUND\", \"Cohort not found.\", 404);\n const courses = await this.stores.courses.getMany(cohort.courseIds);\n return {\n id: cohort.id,\n title: cohort.title,\n startsAt: cohort.startsAt,\n endsAt: cohort.endsAt,\n courses: [...courses.values()]\n .filter((course) => course.status === \"published\" && course.commerce.accessModel === \"free\")\n .map(courseCard),\n };\n }\n async requestCohort(actor: Actor | undefined, cohortId: string) {\n const current = user(actor);\n const cohort = await this.stores.cohorts.get(cohortId);\n invariant(cohort, \"NOT_FOUND\", \"Cohort not found.\", 404);\n invariant(\n !cohort.endsAt || this.now().getTime() < Date.parse(cohort.endsAt),\n \"COHORT_CLOSED\",\n \"This cohort has ended.\",\n 403,\n );\n const id = enrollmentKey(cohortId, current.id);\n return change(this.stores.cohort_enrollments, id, async (stored) => {\n invariant(\n !stored || ![\"rejected\", \"suspended\", \"cancelled\"].includes(stored.status),\n \"COHORT_RESTRICTED\",\n \"Contact an administrator to restore your cohort membership.\",\n 403,\n );\n const next: CohortMembership = stored ?? {\n id,\n cohortId,\n userId: current.id,\n learnerName: current.name || \"Learner\",\n learnerEmail: current.email,\n status: \"pending\",\n requestedAt: this.time(),\n decidedAt: null,\n decidedBy: null,\n reason: null,\n };\n return { value: next, result: next };\n });\n }\n async cohortMemberships(\n actor: Actor | undefined,\n options: { cohortId?: string; status?: string; cursor?: string } = {},\n ) {\n invariant(\n user(actor).role >= 50,\n \"FORBIDDEN\",\n \"Only administrators can review cohort membership.\",\n 403,\n );\n return queryPage(this.stores.cohort_enrollments, {\n where: {\n ...(options.cohortId ? { cohortId: options.cohortId } : {}),\n ...(options.status ? { status: options.status } : {}),\n },\n orderBy: { requestedAt: \"desc\" },\n limit: 30,\n cursor: options.cursor,\n });\n }\n async decideCohort(\n actor: Actor | undefined,\n cohortId: string,\n userId: string,\n status: \"active\" | \"rejected\" | \"suspended\" | \"cancelled\",\n reason = \"\",\n ) {\n const current = user(actor);\n invariant(\n current.role >= 50,\n \"FORBIDDEN\",\n \"Only administrators can make cohort decisions.\",\n 403,\n );\n const cohort = await this.stores.cohorts.get(cohortId);\n invariant(cohort && this.ctx.users, \"NOT_FOUND\", \"Cohort not found.\", 404);\n const target = await this.ctx.users.get(userId);\n invariant(target, \"NOT_FOUND\", \"Learner account not found.\", 404);\n const membership = await change(\n this.stores.cohort_enrollments,\n enrollmentKey(cohortId, userId),\n async (stored) => {\n const next: CohortMembership = {\n ...(stored ?? {\n id: enrollmentKey(cohortId, userId),\n cohortId,\n userId,\n learnerName: target.name || \"Learner\",\n learnerEmail: target.email,\n requestedAt: this.time(),\n }),\n status,\n decidedAt: this.time(),\n decidedBy: current.id,\n reason: reason || null,\n };\n return { value: next, result: next };\n },\n );\n const results: { courseId: string; success: boolean; error?: string }[] = [];\n for (const courseId of cohort.courseIds) {\n try {\n if (status === \"active\") {\n const existing = await this.stores.enrollments.get(enrollmentKey(courseId, userId));\n invariant(\n !existing || ![\"suspended\", \"cancelled\", \"rejected\"].includes(existing.status),\n \"ENROLLMENT_RESTRICTED\",\n \"Restore this course enrollment separately before granting cohort access.\",\n 403,\n );\n await this.adminEnrollment(current, { courseId, userId, cohortId, status: \"active\" });\n } else {\n const id = enrollmentKey(courseId, userId);\n const exists = await this.stores.enrollments.exists(id);\n if (exists)\n await change(this.stores.enrollments, id, async (stored) => {\n invariant(stored, \"NOT_FOUND\", \"Enrollment not found.\", 404);\n const next = structuredClone(stored);\n const grant = next.grants[`cohort:${cohortId}`];\n if (grant) grant.status = status === \"suspended\" ? \"suspended\" : \"revoked\";\n return { value: next, result: true };\n });\n }\n results.push({ courseId, success: true });\n } catch (error) {\n results.push({\n courseId,\n success: false,\n error: error instanceof LmsError ? error.message : \"Access update failed.\",\n });\n }\n }\n await this.audit(current, \"cohort.decision\", membership.id, { status });\n return { membership, results };\n }\n async saveCohort(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n invariant(current.role >= 50, \"FORBIDDEN\", \"Only administrators can manage cohorts.\", 403);\n const parsed = parse(cohortSaveSchema, input);\n for (const id of parsed.data.courseIds) await this.course(id);\n const id = parsed.id ?? crypto.randomUUID();\n const existing = await this.stores.cohorts.getVersioned(id);\n invariant(\n (existing?.revision ?? null) === parsed.expectedRevision,\n \"CONFLICT\",\n \"The cohort changed. Reload before saving.\",\n 409,\n );\n if (existing && (await this.stores.cohort_enrollments.count({ cohortId: id }))) {\n invariant(\n existing.value.courseIds.every((courseId) => parsed.data.courseIds.includes(courseId)),\n \"COHORT_IN_USE\",\n \"Cohorts with membership records must retain existing courses. Create a new cohort to remove courses.\",\n 409,\n );\n }\n const cohort: Cohort = {\n ...parsed.data,\n id,\n createdBy: existing?.value.createdBy ?? current.id,\n createdAt: existing?.value.createdAt ?? this.time(),\n updatedAt: this.time(),\n };\n const saved = await this.stores.cohorts.compareAndSet(id, existing?.revision ?? null, cohort);\n invariant(saved.applied, \"CONFLICT\", \"The cohort changed while saving.\", 409);\n await this.audit(current, \"cohort.save\", id);\n return { cohort, revision: saved.revision };\n }\n async applyCohort(\n actor: Actor | undefined,\n id: string,\n expectedRevision: string,\n offset: number,\n ) {\n const current = user(actor);\n invariant(current.role >= 50, \"FORBIDDEN\", \"Only administrators can enroll cohorts.\", 403);\n const stored = await this.stores.cohorts.getVersioned(id);\n invariant(stored, \"NOT_FOUND\", \"Cohort not found.\", 404);\n invariant(\n stored.revision === expectedRevision,\n \"CONFLICT\",\n \"The cohort changed. Reload before continuing.\",\n 409,\n );\n const cohort = stored.value;\n const members = cohort.memberIds.slice(\n offset,\n offset + Math.max(1, Math.floor(10 / cohort.courseIds.length)),\n );\n const results: { userId: string; courseId: string; success: boolean; error?: string }[] = [];\n for (const userId of members) {\n try {\n const decided = await this.decideCohort(current, id, userId, \"active\");\n results.push(...decided.results.map((result) => ({ userId, ...result })));\n } catch (error) {\n results.push({\n userId,\n courseId: \"\",\n success: false,\n error: error instanceof LmsError ? error.message : \"Enrollment failed.\",\n });\n }\n }\n const nextOffset = offset + members.length;\n return { results, nextOffset, hasMore: nextOffset < cohort.memberIds.length };\n }\n async notify(enrollment: Enrollment, key: string, title: string, message: string): Promise<void> {\n const settings = await this.settings();\n const preferences = await this.stores.preferences.get(enrollment.userId);\n const id = `${enrollment.id}:${key}`;\n const notification: Notification = {\n id,\n userId: enrollment.userId,\n email: enrollment.learnerEmail,\n title,\n message,\n courseId: enrollment.courseId,\n read: false,\n createdAt: this.time(),\n emailStatus:\n settings.emailNotifications && preferences?.emailNotifications !== false\n ? \"pending\"\n : \"disabled\",\n emailAttempts: 0,\n claimedAt: null,\n };\n await putOnce(this.stores.notifications, id, notification);\n }\n async notifications(actor: Actor | undefined, cursor?: string) {\n const current = user(actor);\n const page = await queryPage(this.stores.notifications, {\n where: { userId: current.id },\n orderBy: { createdAt: \"desc\" },\n limit: 30,\n cursor,\n });\n return {\n ...page,\n items: page.items.map(\n ({\n email: _email,\n emailStatus: _status,\n emailAttempts: _attempts,\n claimedAt: _claimed,\n ...item\n }) => item,\n ),\n };\n }\n async readNotification(actor: Actor | undefined, id: string) {\n const current = user(actor);\n await change(this.stores.notifications, id, async (stored) => {\n invariant(\n stored && stored.userId === current.id,\n \"NOT_FOUND\",\n \"Notification not found.\",\n 404,\n );\n return { value: { ...stored, read: true }, result: true };\n });\n return { read: true };\n }\n async deliverNotifications(): Promise<{ sent: number; failed: number }> {\n const settings = await this.settings();\n if (!settings.emailNotifications || !this.ctx.email) return { sent: 0, failed: 0 };\n const cutoff = new Date(this.now().getTime() - 15 * 60_000).toISOString();\n const abandoned = await this.stores.notifications.query({\n where: { emailStatus: \"sending\", claimedAt: { lte: cutoff } },\n limit: 20,\n });\n let failed = 0;\n for (const item of abandoned.items) {\n const recovered = await this.stores.notifications.updateIf(item.id, {\n where: { emailStatus: \"sending\", claimedAt: { lte: cutoff } },\n set: { emailStatus: \"failed\" },\n });\n if (recovered.applied) failed++;\n }\n const page = await this.stores.notifications.query({\n where: { emailStatus: \"pending\" },\n limit: 20,\n });\n let sent = 0;\n for (const item of page.items) {\n if (item.data.emailAttempts >= 3) continue;\n const claimedAt = this.time();\n const emailAttempts = item.data.emailAttempts + 1;\n const claim = await this.stores.notifications.updateIf(item.id, {\n where: { emailStatus: \"pending\", emailAttempts: item.data.emailAttempts },\n set: { emailStatus: \"sending\", claimedAt },\n delta: { emailAttempts: { inc: 1 } },\n });\n if (!claim.applied) continue;\n try {\n if ((await this.stores.preferences.get(item.data.userId))?.emailNotifications === false) {\n await this.stores.notifications.updateIf(item.id, {\n where: { emailStatus: \"sending\", emailAttempts, claimedAt },\n set: { emailStatus: \"disabled\" },\n });\n continue;\n }\n await this.ctx.email.send({\n to: item.data.email,\n subject: item.data.title,\n text: item.data.message,\n });\n await this.stores.notifications.updateIf(item.id, {\n where: { emailStatus: \"sending\", emailAttempts, claimedAt },\n set: { emailStatus: \"sent\" },\n });\n sent++;\n } catch {\n await this.stores.notifications.updateIf(item.id, {\n where: { emailStatus: \"sending\", emailAttempts, claimedAt },\n set: { emailStatus: \"failed\" },\n });\n failed++;\n }\n }\n return { sent, failed };\n }\n async assignments(actor: Actor | undefined, courseId: string, cursor?: string) {\n const page = await this.enrollments(actor, { courseId, cursor, limit: 30 });\n return {\n ...page,\n items: page.items.flatMap((enrollment) =>\n Object.entries(enrollment.progress).flatMap(([lessonId, progress]) =>\n progress.assignments.map((submission) => ({\n ...submission,\n userId: enrollment.userId,\n learnerName: enrollment.learnerName,\n courseId,\n lessonId,\n })),\n ),\n ),\n };\n }\n async exportCourse(actor: Actor | undefined, id: string) {\n const { course } = await this.editableCourse(actor, id);\n const data = courseDataSchema.parse({ ...course, status: \"draft\" });\n return { format: \"anantagnanam-lms-course\", version: 1, course: data };\n }\n async importCourse(actor: Actor | undefined, input: unknown) {\n const parsed = parse(\n z.object({\n format: z.literal(\"anantagnanam-lms-course\"),\n version: z.literal(1),\n course: courseSaveSchema.shape.data,\n }),\n input,\n );\n return this.saveCourse(actor, {\n data: { ...parsed.course, status: \"draft\" },\n expectedRevision: null,\n });\n }\n async deleteCourse(actor: Actor | undefined, id: string, expectedRevision: string) {\n const stored = await this.stores.courses.getVersioned(id);\n invariant(stored, \"NOT_FOUND\", \"Course not found.\", 404);\n const current = this.instructor(stored.value, actor);\n invariant(\n stored.value.status === \"draft\" &&\n (await this.stores.enrollments.count({ courseId: id })) === 0,\n \"COURSE_IN_USE\",\n \"Only drafts without enrollments can be deleted. Archive other courses.\",\n 409,\n );\n const deleted = await this.stores.courses.compareAndDelete(id, expectedRevision);\n invariant(deleted.applied, \"CONFLICT\", \"The course changed. Reload before deleting.\", 409);\n await this.audit(current, \"course.delete\", id);\n return { deleted: true };\n }\n async retryNotification(actor: Actor | undefined, id: string) {\n invariant(\n user(actor).role >= 50,\n \"FORBIDDEN\",\n \"Only administrators can retry email delivery.\",\n 403,\n );\n const result = await this.stores.notifications.updateIf(id, {\n where: { emailStatus: \"failed\", emailAttempts: { lt: 3 } },\n set: { emailStatus: \"pending\", claimedAt: null },\n });\n invariant(result.applied, \"RETRY_UNAVAILABLE\", \"This notification cannot be retried.\", 409);\n await this.audit(actor!, \"notification.retry\", id);\n return { queued: true };\n }\n async notificationQueue(\n actor: Actor | undefined,\n options: { status?: Notification[\"emailStatus\"]; cursor?: string; limit?: number } = {},\n ) {\n invariant(user(actor).role >= 50, \"FORBIDDEN\", \"Administrator access required.\", 403);\n return queryPage(this.stores.notifications, {\n where: options.status ? { emailStatus: options.status } : {},\n orderBy: { createdAt: \"desc\" },\n limit: options.limit ?? 30,\n cursor: options.cursor,\n });\n }\n async auditList(actor: Actor | undefined, options: { cursor?: string; limit?: number } = {}) {\n invariant(user(actor).role >= 50, \"FORBIDDEN\", \"Administrator access required.\", 403);\n return queryPage(this.stores.audit, {\n orderBy: { createdAt: \"desc\" },\n limit: options.limit ?? 30,\n cursor: options.cursor,\n });\n }\n async reminders(): Promise<number> {\n const settings = await this.settings();\n if (!settings.reminderDays) return 0;\n const cutoff = new Date(\n this.now().getTime() - settings.reminderDays * 86_400_000,\n ).toISOString();\n const cursor = await this.ctx.kv.get<string>(\"reminders:cursor\");\n const page = await this.stores.enrollments.query({\n where: { status: \"active\" },\n limit: 30,\n cursor: cursor ?? undefined,\n });\n const week = Math.floor(this.now().getTime() / (7 * 86_400_000));\n let queued = 0;\n for (const item of page.items) {\n if (\n item.data.lastActivityAt > cutoff ||\n effectiveAccess((await this.accessEnrollment(item.data))!, this.now()) !== \"granted\"\n )\n continue;\n await this.notify(\n item.data,\n `reminder-${week}`,\n \"Continue your learning\",\n \"Your next lesson is waiting in My Learning.\",\n );\n queued++;\n }\n if (page.hasMore && page.cursor) await this.ctx.kv.set(\"reminders:cursor\", page.cursor);\n else await this.ctx.kv.delete(\"reminders:cursor\");\n return queued;\n }\n}\n","import { z } from \"zod\";\nimport { PLUGIN_VERSION } from \"./constants.js\";\nimport { courseCard, emptyProgress } from \"./curriculum.js\";\nimport { invariant } from \"./errors.js\";\nimport { parse, type LmsService } from \"./service.js\";\nimport { assertSize, change, enrollmentKey, putOnce, queryPage } from \"./store.js\";\nimport {\n courseIdSchema,\n idSchema,\n pageSchema,\n pathSaveSchema,\n playbackSchema,\n preferencesSchema,\n} from \"./schemas.js\";\nimport type { Actor, LearningPath, LearnerPath, LmsHealth, PublicPath } from \"./types.js\";\n\nexport const exportSchema = pageSchema.extend({\n section: z.enum([\n \"enrollments\",\n \"discussions\",\n \"reviews\",\n \"notifications\",\n \"saved_courses\",\n \"path_enrollments\",\n \"cohort_enrollments\",\n ]),\n});\n\nfunction user(actor: Actor | undefined): Actor {\n invariant(actor, \"UNAUTHORIZED\", \"Sign in to continue.\", 401);\n return actor;\n}\nfunction admin(actor: Actor | undefined): Actor {\n const current = user(actor);\n invariant(current.role >= 50, \"FORBIDDEN\", \"Administrator access required.\", 403);\n return current;\n}\nfunction pathPage(input: unknown) {\n const options = parse(pageSchema, input);\n // Each path expands up to 30 courses and their immutable enrollment editions.\n return { ...options, limit: Math.min(options.limit, 8) };\n}\n\n/** Optional learning features share the existing access and immutable-edition service. */\nexport class LearningFeatures {\n constructor(readonly service: LmsService) {}\n get stores() {\n return this.service.stores;\n }\n\n async preferences(actor: Actor | undefined) {\n const stored = await this.stores.preferences.getVersioned(user(actor).id);\n return {\n value: preferencesSchema.parse(stored?.value ?? {}),\n revision: stored?.revision ?? null,\n };\n }\n async savePreferences(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const parsed = parse(\n z.object({ data: preferencesSchema, expectedRevision: z.string().max(128).nullable() }),\n input,\n );\n const saved = await this.stores.preferences.compareAndSet(\n current.id,\n parsed.expectedRevision,\n parsed.data,\n );\n invariant(\n saved.applied,\n \"CONFLICT\",\n \"Preferences changed in another tab. Reload before saving.\",\n 409,\n );\n return { value: parsed.data, revision: saved.revision };\n }\n async saveCourse(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const parsed = parse(courseIdSchema.extend({ saved: z.boolean() }), input);\n if (parsed.saved) {\n const course = await this.service.course(parsed.courseId);\n invariant(course.status === \"published\", \"NOT_FOUND\", \"Course not found.\", 404);\n }\n await change(this.stores.saved_courses, enrollmentKey(parsed.courseId, current.id), () => ({\n value: {\n userId: current.id,\n courseId: parsed.courseId,\n saved: parsed.saved,\n updatedAt: this.service.time(),\n },\n result: true,\n }));\n return { saved: parsed.saved };\n }\n async savedCourses(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const options = parse(pageSchema, input);\n const page = await queryPage(this.stores.saved_courses, {\n ...options,\n where: { userId: current.id, saved: true },\n orderBy: { updatedAt: \"desc\" },\n });\n return {\n ...page,\n items: await Promise.all(\n page.items.map(async (item) => ({\n courseId: item.courseId,\n course: await this.card(item.courseId),\n })),\n ),\n };\n }\n private async card(courseId: string) {\n const course = await this.stores.courses.get(courseId);\n return course?.status === \"published\" ? courseCard(course) : null;\n }\n async savePath(actor: Actor | undefined, input: unknown) {\n const current = admin(actor);\n const parsed = parse(pathSaveSchema, input);\n const id = parsed.id ?? crypto.randomUUID();\n const existing = await this.stores.learning_paths.getVersioned(id);\n invariant(!parsed.id || existing, \"NOT_FOUND\", \"Learning path not found.\", 404);\n invariant(\n (existing?.revision ?? null) === parsed.expectedRevision,\n \"CONFLICT\",\n \"This path changed. Reload before saving.\",\n 409,\n );\n for (const courseId of parsed.data.courseIds) {\n const course = await this.service.course(courseId);\n invariant(\n parsed.data.status !== \"published\" || course.status === \"published\",\n \"VALIDATION_ERROR\",\n \"Publish all courses before publishing the path.\",\n );\n }\n const path: LearningPath = {\n ...parsed.data,\n id,\n createdAt: existing?.value.createdAt ?? this.service.time(),\n updatedAt: this.service.time(),\n };\n assertSize(path);\n const saved = await this.stores.learning_paths.compareAndSet(id, parsed.expectedRevision, path);\n invariant(saved.applied, \"CONFLICT\", \"This path changed. Reload before saving.\", 409);\n await this.service.audit(current, \"path.save\", id);\n return { path, revision: saved.revision };\n }\n async adminPaths(actor: Actor | undefined, input: unknown) {\n admin(actor);\n const page = await this.stores.learning_paths.query({\n ...parse(pageSchema, input),\n orderBy: { updatedAt: \"desc\" },\n });\n const versions = await Promise.all(\n page.items.map((item) => this.stores.learning_paths.getVersioned(item.id)),\n );\n return {\n ...page,\n items: versions.flatMap((item) => (item ? [{ ...item.value, revision: item.revision }] : [])),\n };\n }\n async paths(input: unknown) {\n const page = await queryPage(this.stores.learning_paths, {\n ...pathPage(input),\n where: { status: \"published\" },\n orderBy: { updatedAt: \"desc\" },\n });\n const items: PublicPath[] = await Promise.all(\n page.items.map(async (path) => ({\n id: path.id,\n title: path.title,\n description: path.description,\n courses: await Promise.all(path.courseIds.map((id) => this.card(id))),\n })),\n );\n return { ...page, items };\n }\n async joinPath(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const { pathId } = parse(z.object({ pathId: idSchema }), input);\n const id = enrollmentKey(pathId, current.id);\n const existing = await this.stores.path_enrollments.get(id);\n if (existing) return { joined: true };\n const path = await this.stores.learning_paths.get(pathId);\n invariant(path?.status === \"published\", \"NOT_FOUND\", \"Learning path not found.\", 404);\n // A path records intent. Only the normal course enrollment flow grants access.\n await putOnce(this.stores.path_enrollments, id, {\n pathId,\n userId: current.id,\n title: path.title,\n courseIds: path.courseIds,\n joinedAt: this.service.time(),\n });\n return { joined: true };\n }\n async learnerPaths(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const page = await queryPage(this.stores.path_enrollments, {\n ...pathPage(input),\n where: { userId: current.id },\n orderBy: { joinedAt: \"desc\" },\n });\n const items: LearnerPath[] = await Promise.all(\n page.items.map(async (path) => {\n const courses = await Promise.all(\n path.courseIds.map(async (courseId) => {\n const enrollment = await this.stores.enrollments.get(\n enrollmentKey(courseId, current.id),\n );\n const course = await this.stores.courses.get(courseId);\n return {\n course:\n course && enrollment\n ? courseCard(await this.service.editions.learning(course, enrollment))\n : await this.card(courseId),\n completed: Boolean(enrollment?.completedAt),\n };\n }),\n );\n return {\n pathId: path.pathId,\n title: path.title,\n joinedAt: path.joinedAt,\n total: courses.length,\n completed: courses.filter((c) => c.completed).length,\n courses,\n };\n }),\n );\n return { ...page, items };\n }\n async playback(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const parsed = parse(playbackSchema, input);\n const course = await this.service.course(parsed.courseId);\n return change(this.stores.enrollments, enrollmentKey(course.id, current.id), async (stored) => {\n const lesson = await this.service.lessonAccess(course, parsed.lessonId, stored);\n invariant(\n [\"audio\", \"video\"].includes(lesson.kind),\n \"VALIDATION_ERROR\",\n \"Only audio and video lessons store playback positions.\",\n );\n const next = structuredClone(stored!);\n const progress = next.progress[lesson.id] ?? emptyProgress();\n invariant(\n (progress.playback?.revision ?? 0) === parsed.expectedRevision,\n \"CONFLICT\",\n \"Playback changed in another tab. Reload to resume saving.\",\n 409,\n );\n const playback = {\n positionSeconds: parsed.positionSeconds,\n playbackRate: parsed.playbackRate,\n revision: parsed.expectedRevision + 1,\n updatedAt: this.service.time(),\n };\n progress.playback = playback;\n next.progress[lesson.id] = progress;\n next.lastActivityAt = this.service.time();\n // A player report never proves attendance or completes a lesson.\n return { value: next, result: playback };\n });\n }\n async exportData(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const parsed = parse(exportSchema, input);\n const { section, cursor } = parsed;\n const where: Record<string, string> =\n section === \"discussions\" ? { authorId: current.id } : { userId: current.id };\n // Small pages bound the worst case when an enrollment contains many attempts.\n const options = { where, cursor, limit: Math.min(parsed.limit, 5) };\n let page: { items: unknown[]; cursor?: string; hasMore: boolean };\n if (section === \"enrollments\") {\n const raw = await queryPage(this.stores.enrollments, options);\n page = {\n ...raw,\n items: await Promise.all(\n raw.items.map((enrollment) => this.service.enrollmentView(enrollment)),\n ),\n };\n } else {\n // Keep the union narrowed: every collection has an explicit owner predicate.\n const raw = await this.stores[section].query(options);\n page = { ...raw, items: raw.items.map((item) => item.data) };\n }\n return {\n format: \"anantagnanam-lms-learner\",\n version: 1,\n exportedAt: this.service.time(),\n section,\n profile: { id: current.id, name: current.name, email: current.email },\n preferences: (await this.preferences(current)).value,\n ...page,\n };\n }\n async health(actor: Actor | undefined): Promise<LmsHealth> {\n admin(actor);\n const [maintenance, pendingEmails, failedEmails, pending, settings, migration] =\n await Promise.all([\n this.service.ctx.kv.get<LmsHealth[\"maintenance\"]>(\"maintenanceHealth\"),\n this.stores.notifications.count({ emailStatus: \"pending\" }),\n this.stores.notifications.count({ emailStatus: \"failed\" }),\n this.stores.notifications.query({\n where: { emailStatus: \"pending\" },\n orderBy: { createdAt: \"asc\" },\n limit: 1,\n }),\n this.service.settings(),\n this.service.migrationStatus(actor),\n ]);\n return {\n version: PLUGIN_VERSION,\n maintenance,\n maintenanceStale:\n !maintenance?.completedAt ||\n this.service.now().getTime() - Date.parse(maintenance.completedAt) > 45 * 60_000,\n pendingEmails,\n failedEmails,\n oldestPendingAt: pending.items[0]?.data.createdAt ?? null,\n emailEnabled: settings.emailNotifications,\n migration,\n };\n }\n}\n","import {\n definePlugin,\n definePluginRoute,\n pluginResponse,\n type PluginRoute,\n type RouteContext,\n} from \"emdash\";\nimport { z } from \"zod\";\nimport { PLUGIN_ID, PLUGIN_VERSION, storage, adminPages } from \"./constants.js\";\nimport { LmsService, parse } from \"./service.js\";\nimport { invariant } from \"./errors.js\";\nimport {\n pageSchema,\n courseIdSchema,\n lessonIdSchema,\n courseSaveSchema,\n idSchema,\n keySchema,\n enrollmentRequestSchema,\n enrollmentAdminSchema,\n gradeSchema,\n certificateActionSchema,\n certificateRevokeSchema,\n} from \"./schemas.js\";\nimport type { LmsOptions } from \"./types.js\";\nimport { normalizeLmsOptions } from \"./config.js\";\nimport { LearningFeatures } from \"./learning.js\";\n\nconst listSchema = pageSchema.extend({\n courseId: idSchema.optional(),\n status: z\n .enum([\"pending\", \"active\", \"completed\", \"rejected\", \"suspended\", \"cancelled\"])\n .optional(),\n});\nconst communityListSchema = pageSchema.extend({\n courseId: idSchema.optional(),\n status: z.enum([\"pending\", \"published\", \"hidden\"]).optional(),\n});\nconst slugSchema = z.object({ slug: z.string().min(1).max(100) });\nconst keyInput = z.object({ id: keySchema });\nconst courseRevisionInput = z.object({\n id: idSchema,\n expectedRevision: z.string().min(1).max(128),\n});\n\nfunction get(\n handler: (ctx: RouteContext, service: LmsService) => Promise<unknown>,\n publicRoute = false,\n permission: PluginRoute[\"permission\"] = \"content:read\",\n): PluginRoute {\n return definePluginRoute({\n methods: [\"GET\"],\n public: publicRoute,\n permission,\n request: { body: \"none\" },\n handler: async (ctx) => handler(ctx, new LmsService(ctx)),\n });\n}\nfunction post(\n handler: (ctx: RouteContext, service: LmsService) => Promise<unknown>,\n permission: PluginRoute[\"permission\"] = \"content:read\",\n): PluginRoute {\n return definePluginRoute({\n methods: [\"POST\"],\n permission,\n request: { body: \"json\", maxBytes: 1_000_000 },\n handler: async (ctx) => handler(ctx, new LmsService(ctx)),\n });\n}\n\nexport function createPlugin(_options: LmsOptions = {}) {\n const options = normalizeLmsOptions(_options);\n return definePlugin({\n id: PLUGIN_ID,\n version: PLUGIN_VERSION,\n capabilities: [\"schema:read\", \"content:read\", \"users:read\", \"email:send\"],\n storage,\n admin: {\n entry: \"@anantagnanam/lms/admin\",\n pages: adminPages,\n widgets: [{ id: \"learning\", title: \"Learning\", size: \"half\" }],\n },\n hooks: {\n \"plugin:install\": async (_event, ctx) => {\n await ctx.kv.compareAndSet(\"schemaVersion\", null, 1);\n },\n \"plugin:activate\": async (_event, ctx) => {\n await ctx.cron?.schedule(\"maintenance\", { schedule: \"*/15 * * * *\" });\n },\n \"plugin:deactivate\": async (_event, ctx) => {\n await ctx.cron?.cancel(\"maintenance\");\n },\n cron: async (event, ctx) => {\n if (event.name === \"maintenance\") {\n const service = new LmsService(ctx);\n const startedAt = service.time();\n try {\n await service.reminders();\n await service.deliverNotifications();\n await ctx.kv.set(\"maintenanceHealth\", {\n startedAt,\n completedAt: service.time(),\n failed: false,\n });\n } catch (error) {\n await ctx.kv.set(\"maintenanceHealth\", { startedAt, completedAt: null, failed: true });\n throw error;\n }\n }\n },\n },\n routes: {\n paths: get(async (ctx, service) => new LearningFeatures(service).paths(ctx.input), true),\n \"learner/paths\": get(async (ctx, service) =>\n new LearningFeatures(service).learnerPaths(ctx.user, ctx.input),\n ),\n \"learner/path/join\": post(async (ctx, service) =>\n new LearningFeatures(service).joinPath(ctx.user, ctx.input),\n ),\n \"learner/saved\": get(async (ctx, service) =>\n new LearningFeatures(service).savedCourses(ctx.user, ctx.input),\n ),\n \"learner/saved/save\": post(async (ctx, service) =>\n new LearningFeatures(service).saveCourse(ctx.user, ctx.input),\n ),\n \"learner/preferences\": get(async (ctx, service) =>\n new LearningFeatures(service).preferences(ctx.user),\n ),\n \"learner/preferences/save\": post(async (ctx, service) =>\n new LearningFeatures(service).savePreferences(ctx.user, ctx.input),\n ),\n \"learner/playback\": post(async (ctx, service) =>\n new LearningFeatures(service).playback(ctx.user, ctx.input),\n ),\n \"learner/export\": get(async (ctx, service) =>\n new LearningFeatures(service).exportData(ctx.user, ctx.input),\n ),\n \"admin/paths\": get(\n async (ctx, service) => new LearningFeatures(service).adminPaths(ctx.user, ctx.input),\n false,\n \"plugins:manage\",\n ),\n \"admin/path/save\": post(\n async (ctx, service) => new LearningFeatures(service).savePath(ctx.user, ctx.input),\n \"plugins:manage\",\n ),\n \"admin/health\": get(\n async (ctx, service) => new LearningFeatures(service).health(ctx.user),\n false,\n \"plugins:manage\",\n ),\n academy: get(async (_ctx, service) => {\n const settings = await service.settings();\n return {\n title: settings.academyTitle,\n description: settings.academyDescription,\n basePath: options.basePath,\n };\n }, true),\n catalog: get(async (ctx, service) => service.catalog(ctx.input), true),\n course: get(\n async (ctx, service) => service.publicCourse(parse(slugSchema, ctx.input).slug),\n true,\n ),\n cohort: get(\n async (ctx, service) =>\n service.publicCohort(parse(z.object({ id: idSchema }), ctx.input).id),\n true,\n ),\n preview: get(async (ctx, service) => {\n const input = parse(lessonIdSchema, ctx.input);\n return service.preview(input.courseId, input.lessonId);\n }, true),\n reviews: get(async (ctx, service) => {\n const input = parse(courseIdSchema.extend({ cursor: z.string().optional() }), ctx.input);\n return service.publicReviews(input.courseId, input.cursor);\n }, true),\n \"certificates/verify\": get(\n async (ctx, service) =>\n service.verifyCertificate(parse(z.object({ id: idSchema }), ctx.input).id),\n true,\n ),\n \"learner/dashboard\": get(async (ctx, service) =>\n service.dashboard(ctx.user, parse(pageSchema, ctx.input)),\n ),\n \"learner/course\": get(async (ctx, service) =>\n service.learnerCourse(ctx.user, parse(courseIdSchema, ctx.input).courseId),\n ),\n \"learner/course-by-slug\": get(async (ctx, service) =>\n service.learnerCourseBySlug(ctx.user, parse(slugSchema, ctx.input).slug),\n ),\n \"learner/lesson\": get(async (ctx, service) => {\n const input = parse(lessonIdSchema, ctx.input);\n return service.learnerLesson(ctx.user, input.courseId, input.lessonId);\n }),\n \"learner/enroll\": post(async (ctx, service) => {\n const input = parse(enrollmentRequestSchema, ctx.input);\n return service.enroll(ctx.user, input.courseId, { cohortId: input.cohortId });\n }),\n \"learner/cohort/request\": post(async (ctx, service) =>\n service.requestCohort(\n ctx.user,\n parse(z.object({ cohortId: idSchema }), ctx.input).cohortId,\n ),\n ),\n \"learner/cancel\": post(async (ctx, service) =>\n service.cancelEnrollment(ctx.user, parse(courseIdSchema, ctx.input).courseId),\n ),\n \"learner/start\": post(async (ctx, service) => {\n const input = parse(lessonIdSchema, ctx.input);\n return service.startLesson(ctx.user, input.courseId, input.lessonId);\n }),\n \"learner/complete\": post(async (ctx, service) => {\n const input = parse(lessonIdSchema, ctx.input);\n return service.completeLesson(ctx.user, input.courseId, input.lessonId);\n }),\n \"learner/quiz/start\": post(async (ctx, service) => {\n const input = parse(lessonIdSchema, ctx.input);\n return service.startQuiz(ctx.user, input.courseId, input.lessonId);\n }),\n \"learner/quiz/submit\": post(async (ctx, service) => service.submitQuiz(ctx.user, ctx.input)),\n \"learner/quiz/draft\": post(async (ctx, service) =>\n service.saveQuizDraft(ctx.user, ctx.input),\n ),\n \"learner/assignment\": post(async (ctx, service) =>\n service.submitAssignment(ctx.user, ctx.input),\n ),\n \"learner/note\": post(async (ctx, service) => service.saveNote(ctx.user, ctx.input)),\n \"learner/discussions\": get(async (ctx, service) => {\n const input = parse(lessonIdSchema.extend({ cursor: z.string().optional() }), ctx.input);\n return service.discussions(ctx.user, input.courseId, input.lessonId, input.cursor);\n }),\n \"learner/discussion\": post(async (ctx, service) =>\n service.postDiscussion(ctx.user, ctx.input),\n ),\n \"learner/review\": post(async (ctx, service) => service.postReview(ctx.user, ctx.input)),\n \"learner/certificate\": get(async (ctx, service) =>\n service.certificate(ctx.user, parse(courseIdSchema, ctx.input).courseId),\n ),\n \"learner/certificate-by-id\": get(async (ctx, service) =>\n service.ownedCertificate(ctx.user, parse(z.object({ id: idSchema }), ctx.input).id),\n ),\n \"learner/cohort\": get(async (ctx, service) => {\n const input = parse(z.object({ cohortId: idSchema }), ctx.input);\n invariant(ctx.user, \"UNAUTHORIZED\", \"Sign in to continue.\", 401);\n return service.stores.cohort_enrollments.get(`${input.cohortId}:${ctx.user.id}`);\n }),\n \"learner/certificate/share\": post(async (ctx, service) => {\n const input = parse(certificateActionSchema, ctx.input);\n return service.shareCertificate(ctx.user, input.courseId, input.shared);\n }),\n \"learner/notifications\": get(async (ctx, service) =>\n service.notifications(ctx.user, parse(pageSchema, ctx.input).cursor),\n ),\n \"learner/notification/read\": post(async (ctx, service) =>\n service.readNotification(ctx.user, parse(keyInput, ctx.input).id),\n ),\n \"admin/courses\": get(async (ctx, service) =>\n service.adminCourses(\n ctx.user,\n parse(\n pageSchema.extend({ status: z.enum([\"draft\", \"published\", \"archived\"]).optional() }),\n ctx.input,\n ),\n ),\n ),\n \"admin/course\": get(async (ctx, service) =>\n service.editableCourse(ctx.user, parse(z.object({ id: idSchema }), ctx.input).id),\n ),\n \"admin/course/save\": post(async (ctx, service) => service.saveCourse(ctx.user, ctx.input)),\n \"admin/course/publish\": post(async (ctx, service) => {\n const input = parse(courseRevisionInput, ctx.input);\n return service.publishCourse(ctx.user, input.id, input.expectedRevision);\n }),\n \"admin/course/archive\": post(async (ctx, service) => {\n const input = parse(courseRevisionInput, ctx.input);\n return service.archiveCourse(ctx.user, input.id, input.expectedRevision);\n }),\n \"admin/course/history\": get(async (ctx, service) => {\n const input = parse(z.object({ id: idSchema, cursor: idSchema.optional() }), ctx.input);\n return service.courseHistory(ctx.user, input.id, input.cursor);\n }),\n \"admin/course/restore\": post(async (ctx, service) => {\n const input = parse(courseRevisionInput.extend({ revisionId: idSchema }), ctx.input);\n return service.restoreCourse(ctx.user, input.id, input.expectedRevision, input.revisionId);\n }),\n \"admin/migrations\": get(\n async (ctx, service) => service.migrationStatus(ctx.user),\n false,\n \"plugins:manage\",\n ),\n \"admin/migrations/run\": post(async (ctx, service) => {\n const input = parse(\n z.object({ limit: z.number().int().min(1).max(20).default(20) }),\n ctx.input,\n );\n return service.migrateBatch(ctx.user, input.limit);\n }, \"plugins:manage\"),\n \"admin/lesson/preview\": get(async (ctx, service) => {\n const input = parse(lessonIdSchema, ctx.input);\n return service.authorLessonPreview(ctx.user, input.courseId, input.lessonId);\n }),\n \"admin/course/delete\": post(async (ctx, service) => {\n const input = parse(\n z.object({ id: idSchema, expectedRevision: z.string().min(1).max(128) }),\n ctx.input,\n );\n return service.deleteCourse(ctx.user, input.id, input.expectedRevision);\n }),\n \"admin/course/export\": get(async (ctx, service) =>\n service.exportCourse(ctx.user, parse(z.object({ id: idSchema }), ctx.input).id),\n ),\n \"admin/course/import\": post(async (ctx, service) =>\n service.importCourse(ctx.user, ctx.input),\n ),\n \"admin/enrollments\": get(async (ctx, service) =>\n service.enrollments(ctx.user, parse(listSchema, ctx.input)),\n ),\n \"admin/enrollment/save\": post(\n async (ctx, service) => service.adminEnrollment(ctx.user, ctx.input),\n \"plugins:manage\",\n ),\n \"admin/assignments\": get(async (ctx, service) => {\n const input = parse(courseIdSchema.extend({ cursor: z.string().optional() }), ctx.input);\n return service.assignments(ctx.user, input.courseId, input.cursor);\n }),\n \"admin/assignment/grade\": post(async (ctx, service) =>\n service.gradeAssignment(ctx.user, ctx.input),\n ),\n \"admin/discussions\": get(async (ctx, service) =>\n service.communityQueue(ctx.user, \"discussions\", parse(communityListSchema, ctx.input)),\n ),\n \"admin/reviews\": get(async (ctx, service) =>\n service.communityQueue(ctx.user, \"reviews\", parse(communityListSchema, ctx.input)),\n ),\n \"admin/discussion/moderate\": post(async (ctx, service) =>\n service.moderate(ctx.user, \"discussions\", ctx.input),\n ),\n \"admin/review/moderate\": post(async (ctx, service) =>\n service.moderate(ctx.user, \"reviews\", ctx.input),\n ),\n \"admin/certificate/revoke\": post(async (ctx, service) => {\n const input = parse(certificateRevokeSchema, ctx.input);\n return service.revokeCertificate(ctx.user, input.courseId, input.userId, input.reason);\n }),\n \"admin/report\": get(async (ctx, service) =>\n service.report(\n ctx.user,\n parse(z.object({ courseId: idSchema.optional() }), ctx.input).courseId,\n ),\n ),\n \"admin/cohorts\": get(\n async (ctx, service) => service.cohortList(ctx.user, parse(pageSchema, ctx.input).cursor),\n false,\n \"plugins:manage\",\n ),\n \"admin/cohort/save\": post(\n async (ctx, service) => service.saveCohort(ctx.user, ctx.input),\n \"plugins:manage\",\n ),\n \"admin/cohort/enroll\": post(async (ctx, service) => {\n const input = parse(\n z.object({\n id: idSchema,\n expectedRevision: z.string().min(1).max(128),\n offset: z.number().int().min(0).max(1000).default(0),\n }),\n ctx.input,\n );\n return service.applyCohort(ctx.user, input.id, input.expectedRevision, input.offset);\n }, \"plugins:manage\"),\n \"admin/cohort/memberships\": get(\n async (ctx, service) =>\n service.cohortMemberships(\n ctx.user,\n parse(\n pageSchema.extend({\n cohortId: idSchema.optional(),\n status: z\n .enum([\"pending\", \"active\", \"rejected\", \"suspended\", \"cancelled\"])\n .optional(),\n }),\n ctx.input,\n ),\n ),\n false,\n \"plugins:manage\",\n ),\n \"admin/cohort/decision\": post(async (ctx, service) => {\n const input = parse(\n z.object({\n cohortId: idSchema,\n userId: idSchema,\n status: z.enum([\"active\", \"rejected\", \"suspended\", \"cancelled\"]),\n reason: z.string().max(1000).default(\"\"),\n }),\n ctx.input,\n );\n return service.decideCohort(\n ctx.user,\n input.cohortId,\n input.userId,\n input.status,\n input.reason,\n );\n }, \"plugins:manage\"),\n \"admin/settings\": get(async (_ctx, service) => service.settings(), false, \"plugins:manage\"),\n \"admin/settings/save\": post(\n async (ctx, service) => service.saveSettings(ctx.user, ctx.input),\n \"plugins:manage\",\n ),\n \"admin/notification/retry\": post(\n async (ctx, service) => service.retryNotification(ctx.user, parse(keyInput, ctx.input).id),\n \"plugins:manage\",\n ),\n \"admin/notifications\": get(\n async (ctx, service) =>\n service.notificationQueue(\n ctx.user,\n parse(\n pageSchema.extend({\n status: z.enum([\"pending\", \"sending\", \"sent\", \"disabled\", \"failed\"]).optional(),\n }),\n ctx.input,\n ),\n ),\n false,\n \"plugins:manage\",\n ),\n \"admin/audit\": get(\n async (ctx, service) => service.auditList(ctx.user, parse(pageSchema, ctx.input)),\n false,\n \"plugins:manage\",\n ),\n \"admin/users\": get(\n async (ctx) => {\n invariant(ctx.users, \"USERS_UNAVAILABLE\", \"The user directory is unavailable.\", 503);\n const input = parse(pageSchema, ctx.input);\n const page = await ctx.users.list({ limit: input.limit, cursor: input.cursor });\n return {\n ...page,\n items: page.items.map((entry) => ({\n id: entry.id,\n name: entry.name,\n email: entry.email,\n role: entry.role,\n })),\n };\n },\n false,\n \"users:read\",\n ),\n \"admin/instructors\": get(async (ctx) => {\n invariant(\n ctx.user && ctx.user.role >= 20,\n \"FORBIDDEN\",\n \"Course authoring access required.\",\n 403,\n );\n invariant(ctx.users, \"USERS_UNAVAILABLE\", \"The user directory is unavailable.\", 503);\n const input = parse(pageSchema, ctx.input);\n const page = await ctx.users.list({ limit: input.limit, cursor: input.cursor });\n return {\n ...page,\n items: page.items\n .filter((entry) => entry.role >= 20)\n .map((entry) => ({ id: entry.id, name: entry.name, email: entry.email })),\n };\n }),\n \"admin/content/collections\": get(async (ctx) => {\n invariant(\n ctx.user && ctx.user.role >= 20,\n \"FORBIDDEN\",\n \"Course authoring access required.\",\n 403,\n );\n invariant(ctx.schema, \"SCHEMA_UNAVAILABLE\", \"The content schema is unavailable.\", 503);\n return (await ctx.schema.listCollections())\n .map((collection) => ({\n slug: collection.slug,\n label: collection.label,\n fields: collection.fields\n .filter((field) => [\"portableText\", \"text\", \"string\"].includes(field.type))\n .map((field) => ({ slug: field.slug, label: field.label, type: field.type })),\n }))\n .filter((collection) => collection.fields.length);\n }),\n \"admin/content/entries\": get(async (ctx) => {\n invariant(\n ctx.user && ctx.user.role >= 20,\n \"FORBIDDEN\",\n \"Course authoring access required.\",\n 403,\n );\n invariant(\n ctx.schema && ctx.content,\n \"CONTENT_UNAVAILABLE\",\n \"Content integration is unavailable.\",\n 503,\n );\n const input = parse(pageSchema.extend({ collection: idSchema }), ctx.input);\n const collection = await ctx.schema.getCollection(input.collection);\n invariant(collection, \"NOT_FOUND\", \"Content collection not found.\", 404);\n const page = await ctx.content.list(input.collection, {\n limit: input.limit,\n cursor: input.cursor,\n where: { status: \"published\" },\n });\n return {\n ...page,\n items: page.items.map((entry) => ({\n id: entry.id,\n title: String(\n entry.data[collection.titleField ?? \"title\"] ?? entry.slug ?? \"Published entry\",\n ),\n })),\n };\n }),\n \"admin/learner/find\": post(async (ctx, service) => {\n const input = parse(courseIdSchema.extend({ email: z.email().max(254) }), ctx.input);\n service.instructor(await service.course(input.courseId), ctx.user);\n invariant(ctx.users, \"USERS_UNAVAILABLE\", \"The user directory is unavailable.\", 503);\n const target = await ctx.users.getByEmail(input.email);\n invariant(\n target,\n \"NOT_FOUND\",\n \"No account uses this email address. Invite the learner through EmDash first.\",\n 404,\n );\n return { id: target.id, name: target.name, email: target.email };\n }, \"plugins:manage\"),\n \"admin/report/csv\": definePluginRoute({\n methods: [\"GET\"],\n permission: \"content:read\",\n request: { body: \"none\" },\n response: \"raw\",\n handler: async (ctx) => {\n const input = parse(listSchema, ctx.input);\n const csv = await new LmsService(ctx).enrollmentCsv(ctx.user, input);\n return pluginResponse({\n body: { kind: \"text\", value: csv },\n headers: {\n \"Content-Type\": \"text/csv; charset=utf-8\",\n \"Content-Disposition\": 'attachment; filename=\"lms-enrollments.csv\"',\n },\n });\n },\n }),\n \"mcp/courses\": post(async (ctx, service) =>\n service.adminCourses(ctx.user, parse(pageSchema, ctx.input)),\n ),\n },\n mcp: {\n tools: {\n list_courses: {\n route: \"mcp/courses\",\n description: \"List courses available to this instructor or administrator.\",\n input: pageSchema,\n },\n save_course: {\n route: \"admin/course/save\",\n description:\n \"Create a course or save a working draft using its current storage revision. Existing courses require publish_course to publish edits; initial creation with published status publishes immediately.\",\n input: courseSaveSchema,\n destructive: true,\n },\n publish_course: {\n route: \"admin/course/publish\",\n description:\n \"Publish the saved draft as a new course edition using its current storage revision. Existing learners retain their enrolled edition.\",\n input: courseRevisionInput,\n destructive: true,\n },\n enroll_learner: {\n route: \"admin/enrollment/save\",\n description:\n \"Approve, reject, suspend, or manually enroll a learner. Requires an administrator account.\",\n input: enrollmentAdminSchema,\n destructive: true,\n },\n grade_assignment: {\n route: \"admin/assignment/grade\",\n description: \"Grade a pending assignment submission in a course you teach.\",\n input: gradeSchema,\n destructive: true,\n },\n },\n },\n });\n}\n\nexport { LmsService } from \"./service.js\";\n"],"mappings":";;;;;;;;;AA4CA,SAAgB,YAAY,KAAoC;AAC9D,QAAO,IAAI;;AAEb,SAAgB,cAAc,UAAkB,QAAwB;AACtE,QAAO,GAAG,SAAS,GAAG;;AAExB,SAAgB,WAAW,MAAqB;AAC9C,WACE,IAAI,aAAa,CAAC,OAAO,KAAK,UAAU,KAAK,CAAC,CAAC,cAAc,oBAC7D,sBACA,qFACA,IACD;;AAIH,eAAsB,OACpB,OACA,IACA,QACY;AACZ,MAAK,IAAI,UAAU,GAAG,UAAU,GAAG,WAAW;EAC5C,MAAM,UAAU,MAAM,MAAM,aAAa,GAAG;EAC5C,MAAM,EAAE,OAAO,WAAW,MAAM,OAAO,SAAS,SAAS,KAAK;AAC9D,aAAW,MAAM;AAEjB,OADc,MAAM,MAAM,cAAc,IAAI,SAAS,YAAY,MAAM,MAAM,EACnE,QAAS,QAAO;;AAE5B,OAAM,IAAI,SAAS,YAAY,0DAA0D,IAAI;;AAG/F,eAAsB,UACpB,OACA,UAA+B,EAAE,EACf;CAClB,MAAM,OAAO,MAAM,MAAM,MAAM,QAAQ;AACvC,QAAO;EAAE,GAAG;EAAM,OAAO,KAAK,MAAM,KAAK,SAAS,KAAK,KAAK;EAAE;;AAGhE,eAAsB,QAAW,OAA6B,IAAY,OAAyB;AACjG,YAAW,MAAM;AACjB,OAAM,MAAM,cAAc,IAAI,MAAM,MAAM;;;;;;ACtE5C,IAAa,iBAAb,MAA4B;CAC1B,YACE,AAAS,QACT,AAAS,MACT,AAAS,gBACT;EAHS;EACA;EACA;;CAGX,MAAM,SAAS,UAAkB,MAAmC;EAClE,MAAM,SAAS,gBAAgB,KAAK;AACpC,OAAK,MAAM,UAAU,OAAO,QAC1B,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,QAAQ,IACzC,QAAO,QAAQ,KAAK;GAClB,GAAI,MAAM,KAAK,eAAe,OAAO,QAAQ,GAAI;GACjD,eAAe;GAChB;EACL,MAAM,KAAK,OAAO,YAAY;AAC9B,QAAM,QAAQ,KAAK,OAAO,iBAAiB,IAAI;GAC7C;GACA;GACA,MAAM;GACN,WAAW,KAAK,MAAM;GACvB,CAAC;AACF,SAAO;;CAGT,MAAM,SACJ,QACA,MACA,SACA,MACA,QAAqE,EAAE,EACvE;EACA,MAAM,WAA2B;GAC/B,IAAI,OAAO,YAAY;GACvB,UAAU,OAAO;GACjB,UAAU,OAAO,mBAAmB;GACpC,MAAM,iBAAiB,MAAM;IAAE,GAAG;IAAM,QAAQ;IAAS,CAAC;GAC1D;GACA,WAAW;GACX,WAAW,KAAK,MAAM;GACtB,GAAG;GACJ;AACD,QAAM,QAAQ,KAAK,OAAO,kBAAkB,SAAS,IAAI,SAAS;AAClE,SAAO;;CAGT,MAAM,OAAO,SAAmC,QAA4C;AAC1F,aAAW,OAAO;EAClB,MAAM,QAAQ,MAAM,KAAK,OAAO,QAAQ,cACtC,OAAO,IACP,SAAS,YAAY,MACrB,OACD;AACD,YAAU,MAAM,SAAS,YAAY,iDAAiD,IAAI;AAC1F,SAAO;GAAE,OAAO;GAAQ,UAAU,MAAM;GAAU;;CAGpD,MAAM,OAAO,IAAY,kBAAuD;AAC9E,OAAK,IAAI,UAAU,GAAG,UAAU,GAAG,WAAW;GAC5C,MAAM,UAAU,MAAM,KAAK,OAAO,QAAQ,aAAa,GAAG;AAC1D,aAAU,SAAS,aAAa,qBAAqB,IAAI;AACzD,aACE,qBAAqB,UAAa,QAAQ,aAAa,kBACvD,YACA,iDACA,IACD;AACD,OAAI,QAAQ,MAAM,kBAAkB,EAAG,QAAO;GAC9C,MAAM,SAAS,QAAQ;AACvB,aACE,OAAO,kBAAkB,GACzB,sBACA,wEACA,IACD;GACD,MAAM,OAAO,iBAAiB,MAAM,OAAO;GAE3C,MAAM,YAAY,MAAM,KAAK,SAAS,IAAI,KAAK;GAC/C,MAAM,WAAW,MAAM,KAAK,SAAS,QAAQ,MAAM,OAAO,WAAW,aAAa,EAChF,WACD,CAAC;GACF,MAAM,UAAW,MAAM,KAAK,OAAO,gBAAgB,IAAI,UAAU;GACjE,MAAM,OAAe;IACnB,GAAG;IACH,GAAG,QAAQ;IACX,eAAe;IACf,iBAAiB,SAAS;IAC1B,iBAAiB;IACjB,GAAI,OAAO,WAAW,UAClB;KAAE,oBAAoB;KAAW,0BAA0B,SAAS;KAAI,GACxE,EAAE;IACP;AACD,OAAI;AACF,WAAO,MAAM,KAAK,OAAO,SAAS,KAAK;YAChC,OAAO;AACd,QACE,EAAE,iBAAiB,aACnB,MAAM,SAAS,cACf,qBAAqB,OAErB,OAAM;;;AAGZ,QAAM,IAAI,SAAS,YAAY,8CAA8C,IAAI;;CAGnF,MAAM,MAAM,QAAiC;AAC3C,MAAI,CAAC,OAAO,gBAAiB,QAAO;EACpC,MAAM,WAAW,MAAM,KAAK,OAAO,iBAAiB,IAAI,OAAO,gBAAgB;AAC/E,YACE,UAAU,aAAa,OAAO,IAC9B,uBACA,0CACA,IACD;AACD,SAAO;GAAE,GAAG;GAAQ,GAAG,SAAS;GAAM,QAAQ,OAAO;GAAQ;;CAG/D,MAAM,SAAS,QAAgB,YAAgD;AAC7E,MAAI,CAAC,WAAY,QAAO;EACxB,MAAM,KAAK,WAAW,mBAAmB,OAAO,mBAAmB,OAAO;AAC1E,MAAI,CAAC,GAAI,QAAO;EAChB,MAAM,UAAU,MAAM,KAAK,OAAO,gBAAgB,IAAI,GAAG;AACzD,YACE,SAAS,aAAa,OAAO,IAC7B,uBACA,+CACA,IACD;AACD,SAAO;GACL,GAAG;GACH,GAAG,QAAQ;GAEX,QAAQ,OAAO;GACf,MAAM,OAAO;GACb,eAAe,OAAO;GACvB;;CAGH,MAAM,QACJ,QACA,QAC0E;EAC1E,IAAI,KAAK,OAAO;EAChB,IAAI,QAAQ,CAAC;EACb,MAAM,QAA2B,EAAE;AAEnC,OAAK,IAAI,IAAI,GAAG,MAAM,IAAI,KAAM,KAAK;GACnC,MAAM,WAAW,MAAM,KAAK,OAAO,iBAAiB,IAAI,GAAG;AAC3D,aACE,UAAU,aAAa,OAAO,IAC9B,uBACA,kCACA,IACD;AACD,OAAI,OAAO,OAAQ,SAAQ;AAC3B,OAAI,OAAO;IACT,MAAM,EAAE,MAAM,GAAG,YAAY;AAC7B,UAAM,KAAK;KAAE,GAAG;KAAS,OAAO,KAAK;KAAO,CAAC;;AAE/C,QAAK,SAAS,YAAY;AAC1B,OAAI,MAAM,WAAW,GAAI,QAAO;IAAE;IAAO,QAAQ;IAAI,SAAS,CAAC,CAAC;IAAI;;AAEtE,YACE,SAAS,CAAC,IACV,kBACA,2EACA,IACD;AACD,SAAO;GAAE;GAAO,SAAS;GAAO;;CAGlC,MAAM,kBAA2C;AAC/C,SACG,MAAM,KAAK,OAAO,WAAW,IAAI,qBAAqB,IAAK;GAC1D,OAAO;GACP,kBAAkB;GAClB,sBAAsB;GACtB,WAAW,KAAK,MAAM;GACtB,WAAW;GACZ;;CAIL,MAAM,aAAa,OAAwC;EACzD,MAAM,KAAK;EACX,MAAM,UAAU,MAAM,KAAK,OAAO,WAAW,aAAa,GAAG;EAC7D,MAAM,QAAQ,SAAS,SAAU,MAAM,KAAK,iBAAiB;AAC7D,MAAI,MAAM,UAAU,WAAY,QAAO;EACvC,MAAM,OAAO;GAAE,GAAG;GAAO,WAAW;GAAM,WAAW,KAAK,MAAM;GAAE;AAClE,MAAI;AACF,OAAI,MAAM,UAAU,WAAW;IAC7B,MAAM,OAAO,MAAM,KAAK,OAAO,QAAQ,MAAM;KAC3C,SAAS,EAAE,WAAW,OAAO;KAC7B,QAAQ,MAAM;KACd;KACD,CAAC;AACF,SAAK,MAAM,QAAQ,KAAK,MAAO,OAAM,KAAK,OAAO,KAAK,GAAG;AACzD,SAAK,oBAAoB,KAAK,MAAM;AACpC,SAAK,SAAS,KAAK;AACnB,QAAI,CAAC,KAAK,SAAS;AACjB,UAAK,QAAQ;AACb,UAAK,SAAS;;UAEX;IACL,MAAM,OAAO,MAAM,KAAK,OAAO,YAAY,MAAM;KAC/C,SAAS,EAAE,YAAY,OAAO;KAC9B,QAAQ,MAAM;KACd;KACD,CAAC;AACF,SAAK,MAAM,QAAQ,KAAK,OAAO;AAC7B,SAAI,KAAK,KAAK,gBAAiB;KAC/B,MAAM,UAAU,MAAM,KAAK,OAAO,KAAK,KAAK,SAAS,EAAE;AACvD,WAAM,OAAO,KAAK,OAAO,aAAa,KAAK,IAAI,OAAO,eAAe;AACnE,gBAAU,YAAY,aAAa,4CAA4C,IAAI;AACnF,aAAO;OACL,OAAO;QACL,GAAG;QACH,iBACE,WAAW,mBAAmB,OAAO,mBAAmB,OAAO;QAClE;OACD,QAAQ;OACT;OACD;;AAEJ,SAAK,wBAAwB,KAAK,MAAM;AACxC,SAAK,SAAS,KAAK;AACnB,QAAI,CAAC,KAAK,SAAS;AACjB,UAAK,QAAQ;AACb,UAAK,SAAS;;;WAGX,OAAO;AAEd,SAAM,KAAK,OAAO,WAAW,cAAc,IAAI,SAAS,YAAY,MAAM;IACxE,GAAG;IACH,WAAW,KAAK,MAAM;IACtB,WACE,iBAAiB,WACb,MAAM,UACN;IACP,CAAC;AACF,SAAM;;AAGR,aADc,MAAM,KAAK,OAAO,WAAW,cAAc,IAAI,SAAS,YAAY,MAAM,KAAK,EAErF,SACN,YACA,0EACA,IACD;AACD,SAAO;;;;;;ACvPX,SAAgB,QAAQ,QAA0B;AAChD,QAAO,OAAO,QAAQ,SAAS,WAAW,OAAO,QAAQ;;AAE3D,SAAgB,gBAAgC;AAC9C,QAAO;EAAE,WAAW;EAAM,aAAa;EAAM,MAAM;EAAI,cAAc,EAAE;EAAE,aAAa,EAAE;EAAE;;AAE5F,SAAgB,WAAW,QAA4B;CACrD,MAAM,MAAM,QAAQ,OAAO;AAC3B,QAAO;EACL,IAAI,OAAO;EACX,MAAM,OAAO;EACb,OAAO,OAAO;EACd,SAAS,OAAO;EAChB,UAAU,OAAO;EACjB,MAAM,OAAO;EACb,OAAO,OAAO;EACd,UAAU,OAAO;EACjB,eAAe,OAAO;EACtB,gBAAgB,OAAO;EACvB,gBAAgB,OAAO;EACvB,aAAa,IAAI;EACjB,iBAAiB,IAAI,QAAQ,KAAK,WAAW,MAAM,OAAO,iBAAiB,EAAE;EAC9E;;AAEH,SAAgB,aAAa,QAA8B;AACzD,QAAO;EACL,eAAe,EAAE;EACjB,GAAG,WAAW,OAAO;EACrB,aAAa,OAAO;EACpB,UAAU,OAAO;EACjB,cAAc,OAAO;EACrB,YAAY,OAAO;EACnB,uBAAuB,OAAO;EAC9B,oBAAoB,OAAO;EAC3B,YAAY,OAAO;EACnB,oBAAoB,OAAO;EAC3B,SAAS,OAAO,QAAQ,KAAK,YAAY;GACvC,IAAI,OAAO;GACX,OAAO,OAAO;GACd,aAAa,OAAO;GACpB,aAAa,OAAO;GACpB,UAAU,OAAO;GACjB,SAAS,OAAO,QAAQ,KAAK,YAAY;IACvC,IAAI,OAAO;IACX,OAAO,OAAO;IACd,SAAS,OAAO;IAChB,MAAM,OAAO;IACb,iBAAiB,OAAO;IACxB,SAAS,OAAO;IAChB,UAAU,OAAO;IAClB,EAAE;GACJ,EAAE;EACJ;;AAEH,SAAgB,aAAa,QAA8B;CACzD,MAAM,EAAE,MAAM,eAAe,SAAS,GAAG,SAAS;AAClD,QAAO;EACL,GAAG;EACH,YAAY,OAAO,cAAc;EACjC,UAAU,OAAO,YAAY,EAAE;EAC/B,MAAM,OACF;GACE,gBAAgB,KAAK;GACrB,aAAa,KAAK;GAClB,kBAAkB,KAAK;GACvB,oBAAoB,KAAK;GACzB,kBAAkB,KAAK;GACvB,cAAc,KAAK;GACnB,eAAe,KAAK,UAAU;GAC/B,GACD;EACL;;AAEH,SAAgB,cAAc,SAAyC;CACrE,MAAM,EAAE,MAAM,OAAO,GAAG,SAAS;AACjC,QAAO;EAAE,GAAG;EAAM,eAAe,QAAQ,iBAAiB;EAAG;;AAE/D,SAAgB,gBACd,YACA,sBAAM,IAAI,MAAM,EACqB;AACrC,KAAI,WAAW,WAAW,eAAe,WAAW,iBAAiB,YACnE,QAAO;AACT,KACE,CAAC,YAAY,YAAY,CAAC,SAAS,WAAW,OAAO,IACrD,WAAW,iBAAiB,UAE5B,QAAO;AACT,KAAI,WAAW,WAAW,aAAa,WAAW,iBAAiB,UAAW,QAAO;AACrF,KAAI,WAAW,mBAAmB,IAAI,SAAS,IAAI,KAAK,MAAM,WAAW,gBAAgB,CACvF,QAAO;AACT,KAAI,WAAW,kBAAkB,IAAI,SAAS,GAAG,KAAK,MAAM,WAAW,eAAe,CACpF,QAAO;CACT,MAAM,SAAS,OAAO,OAAO,WAAW,OAAO;AAC/C,KACE,OAAO,MACJ,UACC,MAAM,WAAW,cAChB,CAAC,MAAM,YAAY,IAAI,SAAS,IAAI,KAAK,MAAM,MAAM,SAAS,MAC9D,CAAC,MAAM,aAAa,IAAI,SAAS,GAAG,KAAK,MAAM,MAAM,UAAU,EACnE,CAED,QAAO;AACT,KACE,OAAO,MACJ,UACC,MAAM,WAAW,aACjB,MAAM,YACN,IAAI,SAAS,GAAG,KAAK,MAAM,MAAM,SAAS,KACzC,CAAC,MAAM,aAAa,IAAI,SAAS,GAAG,KAAK,MAAM,MAAM,UAAU,EACnE,CAED,QAAO;AACT,KAAI,OAAO,MAAM,UAAU,MAAM,WAAW,YAAY,CAAE,QAAO;AACjE,KACE,OAAO,MACJ,UACC,MAAM,WAAW,aACjB,MAAM,aACN,IAAI,SAAS,IAAI,KAAK,MAAM,MAAM,UAAU,CAC/C,CAED,QAAO;AACT,QAAO;;AAGT,SAAgB,iBAAiB,YAAwB,sBAAM,IAAI,MAAM,EAAoB;CAC3F,MAAM,EAAE,cAAc,QAAQ,UAAU,GAAG,SAAS;AACpD,QAAO;EACL,GAAG;EACH,iBAAiB,gBAAgB,YAAY,IAAI;EACjD,UAAU,OAAO,YACf,OAAO,QAAQ,SAAS,CAAC,KAAK,CAAC,IAAI,UAAU,CAC3C,IACA;GAAE,GAAG;GAAM,cAAc,KAAK,aAAa,IAAI,cAAc;GAAE,CAChE,CAAC,CACH;EACF;;;AAGH,SAAgB,gBAAgB,YAAwB,sBAAM,IAAI,MAAM,EAAoB;CAC1F,MAAM,OAAO,iBAAiB,YAAY,IAAI;AAC9C,MAAK,MAAM,YAAY,OAAO,OAAO,KAAK,SAAS,EAAE;AACnD,WAAS,OAAO;AAChB,SAAO,SAAS;;AAElB,QAAO;;AAET,SAAgB,gBACd,QACA,YACA,sBAAM,IAAI,MAAM,EACC;CACjB,MAAM,WAAW,YAAY,cACzB,WAAW,sBACX,QAAQ,OAAO,CACZ,QAAQ,WAAW,OAAO,SAAS,CACnC,KAAK,WAAW,OAAO,GAAG;CACjC,MAAM,YAAY,SAAS,QAAQ,OAAO,YAAY,SAAS,KAAK,YAAY,CAAC;CACjF,MAAM,MAAM,QAAQ,OAAO;CAC3B,MAAM,eAAe,OAAO,YAC1B,IAAI,KAAK,WAAW,CAAC,OAAO,IAAI,mBAAmB,QAAQ,OAAO,IAAI,YAAY,IAAI,CAAC,CAAC,CACzF;CACD,MAAM,aAAa,IAAI,QAAQ,WAAW,CAAC,YAAY,SAAS,OAAO,KAAK,YAAY;CACxF,MAAM,eAAe,WAAW,MAAM,WAAW,aAAa,OAAO,IAAK,UAAU,EAAE,MAAM;AAC5F,QAAO;EACL;EACA,OAAO,SAAS;EAChB,SAAS,SAAS,SAAS,KAAK,MAAO,YAAY,SAAS,SAAU,IAAI,GAAG;EAC7E;EACA,iBAAiB,eAAe,OAAQ,WAAW,IAAI,MAAM;EAC7D;EACD;;;;AAKH,SAAgB,mBACd,QACA,UACA,YACA,KACoB;AACpB,KAAI;AACF,qBAAmB,QAAQ,UAAU,YAAY,IAAI;AACrD,SAAO;GAAE,WAAW;GAAM,MAAM;GAAM,QAAQ;GAAM,WAAW;GAAM;UAC9D,OAAO;AACd,MAAI,EAAE,iBAAiB,UAAW,OAAM;EACxC,IAAI,YAA2B;AAC/B,MAAI,cAAc,MAAM,SAAS,gBAC/B,aAAY,IAAI,KAAK,gBAAgB,QAAQ,UAAU,WAAW,CAAC,CAAC,aAAa;WAC1E,cAAc,gBAAgB,YAAY,IAAI,KAAK,aAAa;GACvE,MAAM,SAAS,OAAO,OAAO,WAAW,OAAO,CAC5C,QACE,UACC,MAAM,WAAW,cAChB,CAAC,MAAM,aAAa,KAAK,MAAM,MAAM,UAAU,GAAG,IAAI,SAAS,EACnE,CACA,SAAS,UAAU;IAClB,MAAM,OAAO,KAAK,IAChB,MAAM,WAAW,KAAK,MAAM,MAAM,SAAS,GAAG,GAC9C,WAAW,iBAAiB,KAAK,MAAM,WAAW,eAAe,GAAG,EACrE;AACD,WAAO,OAAO,IAAI,SAAS,KACxB,CAAC,MAAM,aAAa,OAAO,KAAK,MAAM,MAAM,UAAU,MACtD,CAAC,WAAW,mBAAmB,OAAO,KAAK,MAAM,WAAW,gBAAgB,IAC3E,CAAC,KAAK,GACN,EAAE;KACN;AACJ,OAAI,OAAO,OAAQ,aAAY,IAAI,KAAK,KAAK,IAAI,GAAG,OAAO,CAAC,CAAC,aAAa;;AAE5E,SAAO;GAAE,WAAW;GAAO,MAAM,MAAM;GAAM,QAAQ,MAAM;GAAS;GAAW;;;AAInF,SAAS,gBAAgB,QAAgB,UAAkB,YAAgC;CACzF,MAAM,SAAS,OAAO,QAAQ,MAAM,SAClC,KAAK,QAAQ,MAAM,UAAU,MAAM,OAAO,SAAS,CACpD;AACD,QAAO,KAAK,IACV,OAAO,WAAW,KAAK,MAAM,OAAO,SAAS,GAAG,GAChD,KAAK,MAAM,WAAW,cAAc,WAAW,WAAW,GAAG,OAAO,cAAc,MACnF;;AAGH,SAAgB,uBACd,YACA,KACkC;AAClC,WACE,eAAe,WAAW,WAAW,YAAY,WAAW,WAAW,cACvE,uBACA,qDACA,IACD;AACD,WACE,WAAW,iBAAiB,WAC5B,qBACA,gDACA,IACD;AACD,WACE,OAAO,OAAO,WAAW,OAAO,CAAC,MAC9B,UACC,MAAM,WAAW,cAChB,CAAC,MAAM,YAAY,IAAI,SAAS,IAAI,KAAK,MAAM,MAAM,SAAS,MAC9D,CAAC,MAAM,aAAa,IAAI,SAAS,GAAG,KAAK,MAAM,MAAM,UAAU,EACnE,EACD,kBACA,uEACA,IACD;AACD,WACE,CAAC,WAAW,kBAAkB,IAAI,SAAS,IAAI,KAAK,MAAM,WAAW,eAAe,EACpF,sBACA,2CACA,IACD;AACD,WACE,CAAC,WAAW,mBAAmB,IAAI,SAAS,GAAG,KAAK,MAAM,WAAW,gBAAgB,EACrF,kBACA,mCACA,IACD;;AAGH,SAAgB,WAAW,QAAgB,UAA0B;CACnE,MAAM,SAAS,QAAQ,OAAO,CAAC,MAAM,SAAS,KAAK,OAAO,SAAS;AACnE,WAAU,QAAQ,aAAa,qBAAqB,IAAI;AACxD,QAAO;;AAET,SAAgB,mBACd,QACA,UACA,YACA,KACA,eAAe,OACP;CACR,MAAM,SAAS,WAAW,QAAQ,SAAS;AAC3C,KAAI,gBAAgB,OAAO,WAAW,CAAC;EAAC;EAAQ;EAAc;EAAO,CAAC,SAAS,OAAO,KAAK,CACzF,QAAO;AACT,wBAAuB,YAAY,IAAI;AACvC,WAAU,OAAO,WAAW,SAAS,aAAa,qBAAqB,IAAI;CAC3E,MAAM,YAAY,gBAAgB,QAAQ,UAAU,WAAW;AAC/D,WACE,IAAI,SAAS,IAAI,WACjB,iBACA,0CACA,IACD;AACD,KAAI,OAAO,YAAY;EACrB,MAAM,MAAM,QAAQ,OAAO;EAC3B,MAAM,QAAQ,IAAI,WAAW,SAAS,KAAK,OAAO,SAAS;AAC3D,YACE,IACG,MAAM,GAAG,MAAM,CACf,QAAQ,SAAS,KAAK,SAAS,CAC/B,OAAO,SAAS,WAAW,SAAS,KAAK,KAAK,YAAY,EAC7D,yBACA,gDACA,IACD;;AAEH,QAAO;;AAGT,SAAgB,QACd,OACA,eAA6B;CAC3B,MAAM,QAAQ,IAAI,YAAY,EAAE;AAChC,QAAO,gBAAgB,MAAM;AAC7B,QAAO,MAAM,KAAM;GAEhB;CACL,MAAM,SAAS,CAAC,GAAG,MAAM;AACzB,MAAK,IAAI,QAAQ,OAAO,SAAS,GAAG,QAAQ,GAAG,SAAS;EACtD,MAAM,OAAO,KAAK,MAAM,QAAQ,IAAI,QAAQ,GAAG;AAC/C,GAAC,OAAO,QAAQ,OAAO,SAAS,CAAC,OAAO,OAAQ,OAAO,OAAQ;;AAEjE,QAAO;;AAET,SAAS,WAAW,OAAuB;AACzC,QAAO,MAAM,UAAU,OAAO,CAAC,MAAM,CAAC,kBAAkB,KAAK,CAAC,QAAQ,QAAQ,IAAI;;AAEpF,SAAgB,aACd,WACA,SACkF;CAClF,MAAM,MAAM,IAAI,IAAI,UAAU,KAAK,aAAa,SAAS,GAAG,CAAC;AAC7D,WACE,OAAO,KAAK,QAAQ,CAAC,OAAO,OAAO,IAAI,IAAI,GAAG,CAAC,EAC/C,mBACA,uCACD;CACD,MAAM,WAAW,UAAU,KAAK,aAAa;EAC3C,MAAM,WAAW,QAAQ,SAAS;EAClC,MAAM,WAAW,MAAM,QAAQ,SAAS,GAAG,WAAW,aAAa,SAAY,EAAE,GAAG,CAAC,SAAS;AAC9F,YACE,IAAI,IAAI,SAAS,CAAC,SAAS,SAAS,QACpC,mBACA,6CACD;AACD,MAAI,SAAS,SAAS,WACpB,WAAU,SAAS,UAAU,GAAG,mBAAmB,oCAAoC;AACzF,MAAI,SAAS,SAAS,QACpB,WACE,SAAS,OAAO,WAAW,SAAS,QAAQ,MAAM,WAAW,OAAO,OAAO,OAAO,CAAC,EACnF,mBACA,wCACD;EACH,MAAM,UACJ,SAAS,SAAS,UACd,SAAS,WAAW,KACpB,SAAS,eAAe,MAAM,WAAW,WAAW,OAAO,KAAK,WAAW,SAAS,GAAI,CAAC,GACzF,SAAS,WAAW,SAAS,eAAe,UAC5C,SAAS,OAAO,WAAW,SAAS,eAAe,SAAS,OAAO,CAAC;AAC1E,SAAO;GACL,YAAY,SAAS;GACrB;GACA,QAAQ,UAAU,SAAS,SAAS;GACpC,WAAW,SAAS;GACpB,aAAa,SAAS;GACvB;GACD;CACF,MAAM,QAAQ,SAAS,QAAQ,KAAK,SAAS,MAAM,KAAK,QAAQ,EAAE;CAClE,MAAM,WAAW,UAAU,QAAQ,KAAK,aAAa,MAAM,SAAS,QAAQ,EAAE;AAC9E,QAAO;EAAE;EAAO;EAAU,SAAS,KAAK,MAAO,QAAQ,WAAY,IAAO,GAAG;EAAK;EAAU;;;;;AClU9F,SAAgB,MAAS,QAAsB,OAAmB;CAChE,MAAM,SAAS,OAAO,UAAU,MAAM;AACtC,KAAI,CAAC,OAAO,QACV,OAAM,IAAI,SACR,oBACA,OAAO,MAAM,OACV,KAAK,UAAU,GAAG,MAAM,KAAK,KAAK,IAAI,IAAI,QAAQ,IAAI,MAAM,UAAU,CACtE,MAAM,GAAG,EAAE,CACX,KAAK,IAAI,EACZ,IACD;AACH,QAAO,OAAO;;AAEhB,SAASA,OAAK,OAAiC;AAC7C,WAAU,OAAO,gBAAgB,wBAAwB,IAAI;AAC7D,QAAO;;AAGT,IAAa,aAAb,MAAwB;CACtB,AAAS;CACT,AAAS;CACT,YACE,AAAS,KACT,AAAS,4BAAwB,IAAI,MAAM,EAC3C;EAFS;EACA;AAET,OAAK,SAAS,YAAY,IAAI;AAC9B,OAAK,WAAW,IAAI,eAClB,KAAK,cACC,KAAK,MAAM,GAChB,WAAW,KAAK,eAAe,OAAO,CACxC;;CAEH,OAAe;AACb,SAAO,KAAK,KAAK,CAAC,aAAa;;CAEjC,MAAM,WAAiC;AACrC,SAAO,eAAe,MAAO,MAAM,KAAK,IAAI,SAAS,IAAI,gBAAgB,IAAK,EAAE,CAAC;;CAEnF,MAAM,aAAa,OAA0B,OAAsC;AACjF,YACEA,OAAK,MAAM,CAAC,QAAQ,IACpB,aACA,gDACA,IACD;EACD,MAAM,WAAW,MAAM,gBAAgB,MAAM;AAC7C,YACE,CAAC,SAAS,sBAAsB,KAAK,IAAI,OACzC,qBACA,qEACD;AACD,QAAM,KAAK,IAAI,SAAS,IAAI,iBAAiB,SAAS;AACtD,QAAM,KAAK,MAAM,OAAQ,iBAAiB,gBAAgB;AAC1D,SAAO;;CAET,MAAM,MACJ,OACA,QACA,YACA,UAAmC,EAAE,EACtB;EACf,MAAM,KAAK,OAAO,YAAY;AAC9B,QAAM,KAAK,OAAO,MAAM,IAAI,IAAI;GAC9B;GACA,SAAS,MAAM;GACf;GACA;GACA,WAAW,KAAK,MAAM;GACtB;GACD,CAAC;;CAEJ,MAAM,OAAO,IAA6B;EACxC,MAAM,SAAS,MAAM,KAAK,OAAO,QAAQ,IAAI,GAAG;AAChD,YAAU,QAAQ,aAAa,qBAAqB,IAAI;AACxD,SAAO;;CAGT,MAAM,iBAAiB,YAA2D;AAChF,MAAI,CAAC,WAAY,QAAO;EACxB,MAAM,SAAS,OAAO,OAAO,WAAW,OAAO,CAAC,QAAQ,UAAU,MAAM,WAAW,SAAS;AAC5F,MAAI,CAAC,OAAO,OAAQ,QAAO;EAC3B,MAAM,CAAC,aAAa,WAAW,MAAM,QAAQ,IAAI,CAC/C,KAAK,OAAO,mBAAmB,QAC7B,OAAO,KAAK,UAAU,cAAc,MAAM,eAAe,IAAI,WAAW,OAAO,CAAC,CACjF,EACD,KAAK,OAAO,QAAQ,QAAQ,OAAO,KAAK,UAAU,MAAM,eAAe,GAAG,CAAC,CAC5E,CAAC;EACF,MAAM,WAAW,gBAAgB,WAAW;AAC5C,OAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,aAAa,YAAY,IAAI,cAAc,MAAM,eAAe,IAAI,WAAW,OAAO,CAAC;GAC7F,MAAM,SAAS,QAAQ,IAAI,MAAM,eAAe,GAAG;AACnD,OAAI,YAAY,WAAW,YAAY,CAAC,QAAQ,UAAU,SAAS,WAAW,SAAS,CACrF,UAAS,OAAO,MAAM,IAAK,SACzB,YAAY,WAAW,cAAc,cAAc;QAClD;AACH,aAAS,OAAO,MAAM,IAAK,SAAS;AACpC,aAAS,OAAO,MAAM,IAAK,WAAW,OAAO;AAC7C,aAAS,OAAO,MAAM,IAAK,YAAY,OAAO;;;AAGlD,SAAO;;CAET,MAAM,eAAe,YAAwB;AAC3C,SAAO,iBAAkB,MAAM,KAAK,iBAAiB,WAAW,EAAI,KAAK,KAAK,CAAC;;CAEjF,MAAM,cAAc,QAAgB,YAA+B;AACjE,WAAS,MAAM,KAAK,SAAS,SAAS,QAAQ,WAAW;EACzD,MAAM,WAAW,MAAM,KAAK,iBAAiB,WAAW;EACxD,MAAM,MAAM,KAAK,KAAK;AACtB,SAAO;GACL,YAAY,WAAW,iBAAiB,UAAU,IAAI,GAAG;GACzD,UAAU,gBAAgB,QAAQ,UAAU,IAAI;GACjD;;CAEH,MAAM,aAAa,QAAgB,UAAkB,YAA+B;AAClF,SAAO,mBACL,MAAM,KAAK,SAAS,SAAS,QAAQ,WAAW,EAChD,UACA,MAAM,KAAK,iBAAiB,WAAW,EACvC,KAAK,KAAK,CACX;;CAEH,WAAW,QAAgB,OAAiC;EAC1D,MAAM,UAAUA,OAAK,MAAM;AAC3B,YACE,QAAQ,QAAQ,OACb,QAAQ,QAAQ,MACf,OAAO,cAAc,QAAQ,MAC7B,OAAO,cAAc,SAAS,QAAQ,GAAG,GAC7C,aACA,8CACA,IACD;AACD,SAAO;;CAET,MAAM,WACJ,OACA,OAC+C;EAC/C,MAAM,eAAeA,OAAK,MAAM;EAChC,MAAM,SAAS,MAAM,kBAAkB,MAAM;EAC7C,MAAM,KAAK,OAAO,MAAM,OAAO,YAAY;EAC3C,IAAI,UAAU,MAAM,KAAK,OAAO,QAAQ,aAAa,GAAG;AACxD,MAAI,QAAS,MAAK,WAAW,QAAQ,OAAO,aAAa;MAEvD,WACE,aAAa,QAAQ,IACrB,aACA,8DACA,IACD;AACH,aACG,SAAS,YAAY,UAAU,OAAO,kBACvC,YACA,iEACA,IACD;AACD,MAAI,CAAC,WAAW,OAAO,KAAK,WAAW,YACrC,WACE,aAAa,QAAQ,MAAM,aAAa,QAAQ,IAChD,aACA,oDACA,IACD;AACH,YACE,WAAW,OAAO,KAAK,WAAW,eAAe,OAAO,KAAK,SAAS,gBAAgB,QACtF,yBACA,iFACA,IACD;AAKD,YACE,EALgB,MAAM,KAAK,OAAO,QAAQ,MAAM;GAChD,OAAO,EAAE,MAAM,OAAO,KAAK,MAAM;GACjC,OAAO;GACR,CAAC,EAEW,MAAM,MAAM,SAAS,KAAK,OAAO,GAAG,EAC/C,cACA,mCACA,IACD;AACD,QAAM,KAAK,wBAAwB,IAAI,OAAO,KAAK,sBAAsB;AACzE,MAAI,QAAS,WAAU,MAAM,KAAK,SAAS,OAAO,IAAI,QAAQ,SAAS;EACvE,IAAI,SAAiB;GACnB,GAAG,OAAO;GACV;GACA,aAAa,OAAO,KAAK,MAAM,kBAAkB,KAAK;GACtD,WAAW,SAAS,MAAM,aAAa,aAAa;GACpD,WAAW,SAAS,MAAM,aAAa,KAAK,MAAM;GAClD,WAAW,KAAK,MAAM;GACtB,eAAe;GAChB;EACD,MAAM,QAAQ,MAAM,KAAK,SAAS,SAChC,SAAS,SAAS,QAClB,OAAO,MACP,aAAa,IACb,QACD;AACD,MAAI,SAAS;AAEX,YAAS;IACP,GAAG,QAAQ;IACX,GAAI,QAAQ,MAAM,qBAAqB,EAAE,GAAG,OAAO;IACnD,QAAQ,QAAQ,MAAM;IACtB,iBAAiB,MAAM;IACvB,WAAW,KAAK,MAAM;IACvB;AAED,UAAO,gBAAgB,OAAO,KAAK;SAC9B;AACL,UAAO,kBAAkB,MAAM;AAC/B,OAAI,OAAO,WAAW,aAAa;IACjC,MAAM,YAAY,MAAM,KAAK,SAAS,SAAS,IAAI,OAAO,KAAK;IAC/D,MAAM,UAAW,MAAM,KAAK,OAAO,gBAAgB,IAAI,UAAU;AACjE,aAAS;KACP,GAAG;KACH,GAAG,QAAQ;KACX,oBAAoB;KACpB,0BAA0B,MAAM;KACjC;;;AAGL,SAAO,cAAc,OAAO,MAAM,kBAAkB,KAAK;EACzD,MAAM,QAAQ,MAAM,KAAK,SAAS,OAAO,SAAS,OAAO;AACzD,QAAM,KAAK,MAAM,cAAc,eAAe,IAAI,EAAE,iBAAiB,MAAM,IAAI,CAAC;AAChF,SAAO;GAAE,QAAQ,MAAM,KAAK,SAAS,MAAM,OAAO;GAAE,UAAU,MAAM;GAAU;;CAGhF,MAAM,cAAc,OAA0B,IAAY,kBAA0B;EAClF,MAAM,QAAQ,KAAK,WAAW,MAAM,KAAK,OAAO,GAAG,EAAE,MAAM;AAC3D,YACE,MAAM,QAAQ,IACd,aACA,oDACA,IACD;EACD,MAAM,UAAU,MAAM,KAAK,SAAS,OAAO,IAAI,iBAAiB;EAEhE,MAAM,OAAO,MAAM,kBAAkB;GAAE,GADzB,MAAM,KAAK,SAAS,MAAM,QAAQ,MAAM;GACL,QAAQ;GAAa,CAAC;AACvE,YACE,KAAK,SAAS,gBAAgB,QAC9B,yBACA,8BACA,IACD;AACD,QAAM,KAAK,wBAAwB,IAAI,KAAK,sBAAsB;AAElE,YACE,EAFgB,MAAM,KAAK,OAAO,QAAQ,MAAM;GAAE,OAAO,EAAE,MAAM,KAAK,MAAM;GAAE,OAAO;GAAG,CAAC,EAE9E,MAAM,MAAM,QAAQ,IAAI,OAAO,GAAG,EAC7C,cACA,mCACA,IACD;EACD,MAAM,YAAY,MAAM,KAAK,SAAS,SAAS,IAAI,KAAK;EACxD,MAAM,WAAW,MAAM,KAAK,SAAS,SAAS,QAAQ,OAAO,MAAM,MAAM,IAAI,WAAW,EACtF,WACD,CAAC;EACF,MAAM,UAAW,MAAM,KAAK,OAAO,gBAAgB,IAAI,UAAU;EACjE,MAAM,OAAO;GACX,GAAG,QAAQ;GACX,GAAG,QAAQ;GACX,aAAa,KAAK,MAAM,kBAAkB,KAAK;GAC/C,iBAAiB,SAAS;GAC1B,0BAA0B,SAAS;GACnC,oBAAoB;GACpB,WAAW,KAAK,MAAM;GACvB;EACD,MAAM,QAAQ,MAAM,KAAK,SAAS,OAAO,SAAS,KAAK;AACvD,QAAM,KAAK,MAAM,OAAO,kBAAkB,IAAI,EAAE,WAAW,CAAC;AAC5D,SAAO;GAAE,QAAQ,MAAM,KAAK,SAAS,MAAM,KAAK;GAAE,UAAU,MAAM;GAAU;;CAG9E,MAAM,cAAc,OAA0B,IAAY,QAAiB;EACzE,MAAM,SAAS,MAAM,KAAK,OAAO,GAAG;AACpC,OAAK,WAAW,QAAQ,MAAM;AAC9B,SAAO,KAAK,SAAS,QAAQ,QAAQ,OAAO;;CAG9C,MAAM,cACJ,OACA,IACA,kBACA,YACA;EACA,MAAM,QAAQ,KAAK,WAAW,MAAM,KAAK,OAAO,GAAG,EAAE,MAAM;EAC3D,MAAM,UAAU,MAAM,KAAK,SAAS,OAAO,IAAI,iBAAiB;AAEhE,aADa,MAAM,KAAK,SAAS,QAAQ,QAAQ,OAAO,WAAW,EACpD,MAAM,IAAI,OAAO,YAAY,aAAa,6BAA6B,IAAI;EAG1F,MAAM,OAAO;GAAE,IAFG,MAAM,KAAK,OAAO,iBAAiB,IAAI,WAAW,EAEzC;GAAM,eAAe,QAAQ,MAAM;GAAe;EAC7E,MAAM,WAAW,MAAM,KAAK,SAAS,SAAS,QAAQ,OAAO,MAAM,MAAM,IAAI,WAAW,EACtF,cAAc,YACf,CAAC;EACF,MAAM,OAAO;GAAE,GAAG,QAAQ;GAAO,iBAAiB,SAAS;GAAI,WAAW,KAAK,MAAM;GAAE;EACvF,MAAM,QAAQ,MAAM,KAAK,SAAS,OAAO,SAAS,KAAK;AACvD,QAAM,KAAK,MAAM,OAAO,kBAAkB,IAAI,EAAE,YAAY,CAAC;AAC7D,SAAO;GAAE,QAAQ,MAAM,KAAK,SAAS,MAAM,KAAK;GAAE,UAAU,MAAM;GAAU;;CAG9E,MAAM,cAAc,OAA0B,IAAY,kBAA0B;EAClF,MAAM,QAAQ,KAAK,WAAW,MAAM,KAAK,OAAO,GAAG,EAAE,MAAM;AAC3D,YACE,MAAM,QAAQ,IACd,aACA,mDACA,IACD;EACD,MAAM,SAAS,MAAM,KAAK,OAAO,QAAQ,aAAa,GAAG;AACzD,MAAI,QAAQ,MAAM,kBAAkB,GAAG;AACrC,aACE,OAAO,aAAa,kBACpB,YACA,iDACA,IACD;GAED,MAAM,OAAe;IAAE,GAAG,OAAO;IAAO,QAAQ;IAAY,WAAW,KAAK,MAAM;IAAE;GACpF,MAAM,QAAQ,MAAM,KAAK,SAAS,OAAO,QAAQ,KAAK;AACtD,SAAM,KAAK,MAAM,OAAO,kBAAkB,GAAG;AAC7C,UAAO;IAAE,QAAQ;IAAM,UAAU,MAAM;IAAU;;EAEnD,MAAM,UAAU,MAAM,KAAK,SAAS,OAAO,IAAI,iBAAiB;EAChE,MAAM,QAAQ,MAAM,KAAK,SAAS,MAAM,QAAQ,MAAM;EACtD,MAAM,WAAW,MAAM,KAAK,SAAS,SAAS,QAAQ,OAAO,OAAO,MAAM,IAAI,UAAU;EACxF,MAAM,OAAe;GACnB,GAAG,QAAQ;GACX,QAAQ;GACR,iBAAiB,SAAS;GAC1B,WAAW,KAAK,MAAM;GACvB;EACD,MAAM,QAAQ,MAAM,KAAK,SAAS,OAAO,SAAS,KAAK;AACvD,QAAM,KAAK,MAAM,OAAO,kBAAkB,GAAG;AAC7C,SAAO;GAAE,QAAQ,MAAM,KAAK,SAAS,MAAM,KAAK;GAAE,UAAU,MAAM;GAAU;;CAG9E,MAAM,gBAAgB,OAA0B;AAC9C,YACEA,OAAK,MAAM,CAAC,QAAQ,IACpB,aACA,8CACA,IACD;AACD,SAAO,KAAK,SAAS,iBAAiB;;CAExC,MAAM,aAAa,OAA0B,OAAe;AAC1D,QAAM,KAAK,gBAAgB,MAAM;EACjC,MAAM,QAAQ,MAAM,KAAK,SAAS,aAAa,MAAM;AACrD,MAAI,MAAM,UAAU,WAAY,OAAM,KAAK,IAAI,GAAG,IAAI,iBAAiB,EAAE;AACzE,SAAO;;CAET,MAAM,wBAAwB,UAAkB,eAAwC;EACtF,MAAM,0BAAU,IAAI,KAAa;EACjC,MAAM,QAAQ,OAAO,OAA8B;AACjD,aAAU,OAAO,UAAU,sBAAsB,4CAA4C;AAC7F,OAAI,QAAQ,IAAI,GAAG,CAAE;AACrB,aACE,QAAQ,OAAO,KACf,gCACA,sCACD;AACD,WAAQ,IAAI,GAAG;GACf,MAAM,SAAS,MAAM,KAAK,OAAO,GAAG;AACpC,QAAK,MAAM,QAAQ,OAAO,sBAAuB,OAAM,MAAM,KAAK;;AAEpE,OAAK,MAAM,MAAM,cAAe,OAAM,MAAM,GAAG;;CAEjD,MAAM,eACJ,OACA,IAC+C;EAC/C,MAAM,UAAU,MAAM,KAAK,OAAO,QAAQ,aAAa,GAAG;AAC1D,YAAU,SAAS,aAAa,qBAAqB,IAAI;AACzD,OAAK,WAAW,QAAQ,OAAO,MAAM;AACrC,SAAO;GAAE,QAAQ,MAAM,KAAK,SAAS,MAAM,QAAQ,MAAM;GAAE,UAAU,QAAQ;GAAU;;CAEzF,MAAM,aACJ,OACA,UAAgE,EAAE,EAC3C;EACvB,MAAM,UAAUA,OAAK,MAAM;EAC3B,MAAM,OAAO,MAAM,UAAU,KAAK,OAAO,SAAS;GAChD,OAAO,QAAQ,SAAS,EAAE,QAAQ,QAAQ,QAAQ,GAAG,EAAE;GACvD,SAAS,EAAE,WAAW,QAAQ;GAC9B,OAAO,QAAQ,SAAS;GACxB,QAAQ,QAAQ;GACjB,CAAC;AACF,SAAO;GACL,GAAG;GACH,OAAO,MAAM,QAAQ,IACnB,KAAK,MACF,QACE,WACC,QAAQ,QAAQ,OACf,QAAQ,QAAQ,MACf,OAAO,cAAc,QAAQ,MAC7B,OAAO,cAAc,SAAS,QAAQ,GAAG,EAC9C,CACA,KAAK,WAAW,KAAK,SAAS,MAAM,OAAO,CAAC,CAChD;GACF;;CAEH,MAAM,QAAQ,OAA2C;EACvD,MAAM,UAAU,MAAM,eAAe,MAAM;EAC3C,MAAM,QAAmD,EAAE,QAAQ,aAAa;AAChF,MAAI,QAAQ,EAAE,MAAM,CAClB,OAAM,cAAc,EAAE,YAAY,QAAQ,EAAE,MAAM,CAAC,kBAAkB,KAAK,EAAE;AAC9E,MAAI,QAAQ,SAAU,OAAM,WAAW,QAAQ;AAC/C,MAAI,QAAQ,SAAU,OAAM,WAAW,QAAQ;AAC/C,MAAI,QAAQ,SAAS,QAAQ,UAAU,MAAO,OAAM,QAAQ,EAAE,IAAI,CAAC,QAAQ,OAAO,MAAM,EAAE;EAC1F,MAAM,OAAO,MAAM,UAAU,KAAK,OAAO,SAAS;GAChD;GACA,SAAS,EAAE,OAAO,OAAO;GACzB,OAAO,QAAQ;GACf,QAAQ,QAAQ;GACjB,CAAC;AACF,SAAO;GAAE,GAAG;GAAM,OAAO,KAAK,MAAM,IAAI,WAAW;GAAE;;CAEvD,MAAM,aAAa,MAAc;EAC/B,MAAM,OAAO,MAAM,KAAK,OAAO,QAAQ,MAAM;GAC3C,OAAO;IAAE;IAAM,QAAQ;IAAa;GACpC,OAAO;GACR,CAAC;AACF,YAAU,KAAK,MAAM,IAAI,aAAa,qBAAqB,IAAI;AAC/D,SAAO,KAAK,OAAO,KAAK,MAAM,GAAG,KAAK;;CAExC,MAAM,OAAO,QAAgB;EAC3B,MAAM,gBAAgB,MAAM,KAAK,OAAO,QAAQ,QAAQ,OAAO,sBAAsB;AACrF,SAAO;GACL,GAAG,aAAa,OAAO;GACvB,eAAe,OAAO,sBAAsB,KAAK,OAAO;IACtD,MAAM,QAAQ,cAAc,IAAI,GAAG;AACnC,WAAO;KACL;KACA,OAAO,OAAO,SAAS;KACvB,MAAM,OAAO,WAAW,cAAc,MAAM,OAAO;KACpD;KACD;GACH;;CAEH,MAAM,QAAQ,UAAkB,UAAkB;EAChD,MAAM,SAAS,MAAM,KAAK,OAAO,SAAS;AAC1C,YAAU,OAAO,WAAW,aAAa,aAAa,qBAAqB,IAAI;EAC/E,MAAM,SAAS,WAAW,QAAQ,SAAS;AAC3C,YACE,OAAO,WAAW,CAAC;GAAC;GAAQ;GAAc;GAAO,CAAC,SAAS,OAAO,KAAK,EACvE,uBACA,kDACA,IACD;AACD,SAAO;GACL,QAAQ,aAAa,OAAO;GAC5B,QAAQ,aAAa,MAAM,KAAK,eAAe,OAAO,CAAC;GACxD;;CAEH,MAAM,eACJ,QACwC;AACxC,MAAI,CAAC,OAAO,cAAe,QAAO;AAClC,YAAU,KAAK,IAAI,SAAS,uBAAuB,uCAAuC,IAAI;EAC9F,MAAM,QAAQ,MAAM,KAAK,IAAI,QAAQ,IACnC,OAAO,cAAc,YACrB,OAAO,cAAc,GACtB;AACD,YACE,OAAO,WAAW,aAClB,uBACA,+CACA,IACD;EACD,MAAM,OAAO,MAAM,KAAK,OAAO,cAAc;AAC7C,YACE,MAAM,QAAQ,KAAK,IAAI,OAAO,SAAS,UACvC,uBACA,2CACA,IACD;AACD,MAAI,MAAM,QAAQ,KAAK,CACrB,QAAO;GAAE,GAAG;GAAQ,MAAM;GAAI,cAAc;GAAmC;AACjF,SAAO;GAAE,GAAG;GAAQ;GAAM,cAAc,EAAE;GAAE;;CAE9C,MAAM,oBAAoB,OAA0B,UAAkB,UAAkB;EACtF,MAAM,EAAE,WAAW,MAAM,KAAK,eAAe,OAAO,SAAS;AAC7D,SAAO;GACL,QAAQ,aAAa,OAAO;GAC5B,QAAQ,aAAa,MAAM,KAAK,eAAe,WAAW,QAAQ,SAAS,CAAC,CAAC;GAC9E;;CAEH,MAAM,oBAAoB,QAAgB,SAAgC;AACxE,OAAK,MAAM,MAAM,OAAO,sBAEtB,YADmB,MAAM,KAAK,OAAO,YAAY,IAAI,cAAc,IAAI,QAAQ,CAAC,GAElE,aACZ,gCACA,uDACA,IACD;;CAGL,cAAc,QAAgB,OAAc,QAA0C;EACpF,MAAM,MAAM,KAAK,MAAM;EACvB,MAAM,SAAS,WAAW;AAC1B,SAAO;GACL,IAAI,cAAc,OAAO,IAAI,MAAM,GAAG;GACtC,UAAU,OAAO;GACjB,iBAAiB,OAAO,sBAAsB,OAAO;GACrD,QAAQ,MAAM;GACd,aAAa,MAAM,QAAQ,MAAM,MAAM,MAAM,IAAI,CAAC,MAAM;GACxD,cAAc,MAAM;GACpB;GACA,cAAc,SAAS,YAAY;GACnC,cAAc;GACd,QAAQ,EACN,MAAM;IACJ,IAAI;IACJ,QAAQ;IACR,aAAa;IACb,QAAQ,SAAS,YAAY;IAC7B,UAAU;IACV,WACE,UAAU,OAAO,aACb,IAAI,KAAK,KAAK,KAAK,CAAC,SAAS,GAAG,OAAO,aAAa,MAAW,CAAC,aAAa,GAC7E;IACN,WAAW,SAAS,MAAM;IAC1B,WAAW;IACZ,EACF;GACD,WAAW,EAAE;GACb,aAAa;IACX,SAAS;IACT,SAAS,OAAO,SAAS;IACzB,eAAe;IACf,aAAa;IACb,UAAU;IACV,YAAY;IACZ,mBAAmB;IACpB;GACD,YAAY;GACZ,YAAY,SAAS,MAAM;GAC3B,gBAAgB;GAChB,YAAY;GACZ,YAAY,SAAS,MAAM;GAC3B,aAAa;GACb,gBAAgB;GAChB,iBAAiB;GACjB,UAAU;GACV,UAAU,EAAE;GACZ,aAAa;GACb,qBAAqB,EAAE;GACvB,gBAAgB;GAChB,eAAe;GAChB;;CAEH,MAAM,OAAO,OAA0B,UAAkB,UAAiC,EAAE,EAAE;EAC5F,MAAM,UAAUA,OAAK,MAAM;EAC3B,IAAI,SAAS,MAAM,KAAK,OAAO,SAAS;AACxC,YAAU,OAAO,WAAW,aAAa,aAAa,qBAAqB,IAAI;AAC/E,YACE,OAAO,SAAS,gBAAgB,QAChC,yBACA,sDACA,IACD;AACD,YAAU,MAAM,KAAK,SAAS,OAAO,SAAS,EAAE;AAChD,YAAU,OAAO,WAAW,aAAa,aAAa,qBAAqB,IAAI;EAC/E,MAAM,SAAS,QAAQ,WAAW,MAAM,KAAK,OAAO,QAAQ,IAAI,QAAQ,SAAS,GAAG;AACpF,MAAI,QAAQ,SACV,WACE,UAAU,OAAO,UAAU,SAAS,SAAS,EAC7C,aACA,2CACA,IACD;AACH,MAAI,OAIF,YAHmB,MAAM,KAAK,OAAO,mBAAmB,IACtD,cAAc,OAAO,IAAI,QAAQ,GAAG,CACrC,GAEa,WAAW,UACvB,4BACA,kEACA,IACD;EAEH,MAAM,KAAK,cAAc,UAAU,QAAQ,GAAG;EAC9C,MAAM,WAAW,MAAM,KAAK,OAAO,YAAY,IAAI,GAAG;AACtD,MAAI,CAAC,UAAU,YAAY;GAAC;GAAU;GAAa;GAAU,CAAC,SAAS,SAAS,OAAO,CACrF,QAAO,MAAM,KAAK,eAAe,SAAS;AAC5C,YACE,OAAO,mBAAmB,YAAY,QACtC,uBACA,oDACA,IACD;AACD,YACE,CAAC,OAAO,sBAAsB,KAAK,KAAK,CAAC,SAAS,GAAG,KAAK,MAAM,OAAO,mBAAmB,EAC1F,qBACA,yCACA,IACD;AACD,QAAM,KAAK,oBAAoB,QAAQ,QAAQ,GAAG;EAClD,MAAM,aAAa,MAAM,OAAO,KAAK,OAAO,aAAa,IAAI,OAAO,WAAW;AAC7E,aACE,CAAC,UAAU,CAAC;IAAC;IAAa;IAAa;IAAW,CAAC,SAAS,OAAO,OAAO,EAC1E,yBACA,wDACA,IACD;GACD,MAAM,OAAO,gBACX,UACE,KAAK,cACH,QACA,SACA,CAAC,UAAU,OAAO,mBAAmB,aAAa,YAAY,SAC/D,CACJ;AACD,OAAI,QAAQ;AACV,SAAK,WAAW,OAAO;AACvB,SAAK,YAAY,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,KAAK,WAAW,OAAO,GAAG,CAAC,CAAC;AAC7D,QAAI,CAAC,UAAU,OAAO,iBAAiB,UAAW,MAAK,eAAe;AACtE,QAAI,CAAC,OAAQ,QAAO,KAAK,OAAO;AAChC,SAAK,SAAS,KAAK,cAAc,cAAc;AAC/C,SAAK,eAAe;AACpB,SAAK,eAAe,KAAK,MAAM;AAC/B,SAAK,OAAO,UAAU,OAAO,QAAQ;KACnC,IAAI,UAAU,OAAO;KACrB,QAAQ;KACR,aAAa,OAAO;KACpB,QAAQ;KACR,UAAU,OAAO;KACjB,WAAW,OAAO;KAClB,WAAW,KAAK,MAAM;KACtB,WAAW;KACZ;;AAEH,UAAO;IAAE,OAAO;IAAM,QAAQ;IAAM;IACpC;AACF,QAAM,KAAK,OACT,YACA,YACA,WAAW,WAAW,YAAY,yBAAyB,oBAC3D,GAAG,OAAO,MAAM,IAAI,WAAW,WAAW,YAAY,+CAA+C,oCACtG;AACD,SAAO,MAAM,KAAK,eAAe,WAAW;;CAE9C,MAAM,gBAAgB,OAA0B,OAAgB;EAC9D,MAAM,SAAS,MAAM,uBAAuB,MAAM;EAClD,IAAI,SAAS,MAAM,KAAK,OAAO,OAAO,SAAS;EAC/C,MAAM,eAAe,KAAK,WAAW,QAAQ,MAAM;AACnD,YACE,aAAa,QAAQ,IACrB,aACA,sDACA,IACD;AACD,YAAU,KAAK,IAAI,OAAO,qBAAqB,sCAAsC,IAAI;AACzF,MACE,OAAO,WAAW,YAClB,CAAE,MAAM,KAAK,OAAO,YAAY,IAAI,cAAc,OAAO,IAAI,OAAO,OAAO,CAAC,CAE5E,WAAU,MAAM,KAAK,SAAS,OAAO,OAAO,SAAS,EAAE;EACzD,MAAM,SAAS,MAAM,KAAK,IAAI,MAAM,IAAI,OAAO,OAAO;AACtD,YAAU,QAAQ,aAAa,8BAA8B,IAAI;AACjE,YACE,CAAC,OAAO,kBACN,CAAC,OAAO,mBACR,KAAK,MAAM,OAAO,gBAAgB,GAAG,KAAK,MAAM,OAAO,eAAe,EACxE,yBACA,uCACD;AACD,MAAI,OAAO,UAAU;GACnB,MAAM,SAAS,MAAM,KAAK,OAAO,QAAQ,IAAI,OAAO,SAAS;AAC7D,aACE,UAAU,OAAO,UAAU,SAAS,OAAO,GAAG,EAC9C,kBACA,kDACD;;AAEH,MAAI,OAAO,WAAW,UAAU;AAC9B,aACE,OAAO,WAAW,SAClB,wBACA,iDACA,IACD;AACD,aACE,OAAO,SAAS,gBAAgB,QAChC,yBACA,+CACA,IACD;AACD,SAAM,KAAK,oBAAoB,QAAQ,OAAO,GAAG;;EAEnD,MAAM,SAAS,OAAO,WAAW,MAAM,KAAK,OAAO,QAAQ,IAAI,OAAO,SAAS,GAAG;AAClF,MAAI,UAAU,OAAO,WAAW,SAI9B,YAHmB,MAAM,KAAK,OAAO,mBAAmB,IACtD,cAAc,OAAO,IAAI,OAAO,GAAG,CACpC,GAEa,WAAW,UACvB,4BACA,4DACA,IACD;EAEH,MAAM,WAAW,MAAM,KAAK,UAAU;EACtC,MAAM,aAAa,MAAM,OACvB,KAAK,OAAO,aACZ,cAAc,OAAO,IAAI,OAAO,GAAG,EACnC,OAAO,WAAW;GAChB,MAAM,OAAO,gBAAgB,UAAU,KAAK,cAAc,QAAQ,QAAQ,OAAO,OAAO,CAAC;GACzF,MAAM,gBAAgB,OAAO,WAAW,YAAY,CAAC,KAAK;AAC1D,QAAK,oBAAoB,OAAO,mBAAmB,OAAO;AAC1D,QAAK,SAAS,OAAO,WAAW,YAAY,KAAK,cAAc,cAAc,OAAO;AACpF,QAAK,eACH,OAAO,WAAW,WACd,YACA,OAAO,WAAW,cAChB,cACA;AACR,QAAK,aAAa,OAAO,WAAW,WAAW,aAAa,KAAK,KAAK;AACtE,QAAK,aAAa,KAAK,MAAM;AAC7B,QAAK,iBAAiB,OAAO,UAAU;AACvC,QAAK,eAAe,QAAQ,aAAa,OAAO,eAAe,SAAS,WAAW;AACnF,OAAI,cACF,MAAK,aAAa,KAAK,MAAM;AAE/B,OAAI,OAAO,aAAa,OAAW,MAAK,WAAW,OAAO;AAC1D,OAAI,CAAC,UAAU,OAAO,mBAAmB,OACvC,MAAK,iBAAiB,OAAO;AAC/B,OAAI,CAAC,UAAU,OAAO,oBAAoB,OACxC,MAAK,kBAAkB,OAAO;AAChC,OAAI,OAAO,WAAW,UAAU;AAC9B,QAAI,CAAC,OAAQ,QAAO,KAAK,OAAO;IAChC,MAAM,UAAU,SAAS,UAAU,OAAO,OAAO;IACjD,MAAM,WAAW,KAAK,OAAO;AAC7B,SAAK,OAAO,WAAW;KACrB,IAAI;KACJ,QAAQ,SAAS,WAAW;KAC5B,aAAa,QAAQ,MAAM;KAC3B,QAAQ;KACR,UAAU,QAAQ,YAAY;KAC9B,WAAW,SACP,OAAO,SACP,OAAO,oBAAoB,SACzB,OAAO,kBACP,aACG,CAAC,SAAS,aAAa,KAAK,MAAM,SAAS,UAAU,GAAG,KAAK,KAAK,CAAC,SAAS,IAC7E,SAAS,YACT,OAAO,aACL,IAAI,KAAK,KAAK,KAAK,CAAC,SAAS,GAAG,OAAO,aAAa,MAAW,CAAC,aAAa,GAC7E;KACV,WAAW,UAAU,aAAa,KAAK,MAAM;KAC7C,WAAW,aAAa;KACzB;AACD,QAAI,OAAQ,MAAK,YAAY,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,KAAK,WAAW,OAAO,GAAG,CAAC,CAAC;;AAE3E,aACE,CAAC,KAAK,kBACJ,CAAC,KAAK,mBACN,KAAK,MAAM,KAAK,gBAAgB,GAAG,KAAK,MAAM,KAAK,eAAe,EACpE,yBACA,uCACD;AACD,QAAK,iBAAiB,KAAK,MAAM;AACjC,OAAI,OAAO,WAAW,SACpB,MAAK,OAAO,MAAM,KAAK,SAAS,SAAS,QAAQ,KAAK,EAAE,MAAM,SAAS;AACzE,UAAO;IAAE,OAAO;IAAM,QAAQ;IAAM;IAEvC;AACD,QAAM,KAAK,MAAM,cAAc,qBAAqB,WAAW,IAAI,EACjE,QAAQ,WAAW,QACpB,CAAC;AACF,QAAM,KAAK,OACT,YACA,cAAc,WAAW,OAAO,GAAG,WAAW,cAC9C,sBACA,GAAG,OAAO,MAAM,uBAAuB,WAAW,OAAO,GAC1D;AACD,QAAM,KAAK,kBAAkB,WAAW;AACxC,SAAO,gBAAiB,MAAM,KAAK,iBAAiB,WAAW,EAAI,KAAK,KAAK,CAAC;;CAEhF,MAAM,cAAc,OAA0B,UAAkB;EAC9D,MAAM,UAAUA,OAAK,MAAM;EAC3B,MAAM,SAAS,MAAM,KAAK,OAAO,SAAS;EAC1C,MAAM,aAAa,MAAM,KAAK,OAAO,YAAY,IAAI,cAAc,UAAU,QAAQ,GAAG,CAAC;AACzF,YACE,OAAO,WAAW,eAAgB,OAAO,WAAW,cAAc,YAClE,aACA,qBACA,IACD;AACD,MAAI,YAAY,YAAa,OAAM,KAAK,kBAAkB,WAAW;AACrE,SAAO;GACL,QAAQ,MAAM,KAAK,OAAO,MAAM,KAAK,SAAS,SAAS,QAAQ,WAAW,CAAC;GAC3E,GAAI,MAAM,KAAK,cAAc,QAAQ,WAAW;GACjD;;CAEH,MAAM,oBAAoB,OAA0B,MAAc;AAChE,SAAK,MAAM;EACX,MAAM,OAAO,MAAM,KAAK,OAAO,QAAQ,MAAM;GAAE,OAAO,EAAE,MAAM;GAAE,OAAO;GAAG,CAAC;AAC3E,YAAU,KAAK,MAAM,IAAI,aAAa,qBAAqB,IAAI;AAC/D,SAAO,KAAK,cAAc,OAAO,KAAK,MAAM,GAAG,GAAG;;CAEpD,MAAM,UAAU,OAA0B,UAA+C,EAAE,EAAE;EAC3F,MAAM,UAAUA,OAAK,MAAM;EAC3B,MAAM,OAAO,MAAM,UAAU,KAAK,OAAO,aAAa;GACpD,OAAO,EAAE,QAAQ,QAAQ,IAAI;GAC7B,SAAS,EAAE,YAAY,QAAQ;GAC/B,OAAO,QAAQ,SAAS;GACxB,QAAQ,QAAQ;GACjB,CAAC;EACF,MAAM,UAAU,MAAM,KAAK,OAAO,QAAQ,QAAQ,KAAK,MAAM,KAAK,UAAU,MAAM,SAAS,CAAC;EAa5F,MAAM,SAZO,MAAM,QAAQ,IACzB,KAAK,MAAM,IAAI,OAAO,eAAe;AACnC,OAAI,WAAW,YAAa,OAAM,KAAK,kBAAkB,WAAW;GACpE,MAAM,SAAS,QAAQ,IAAI,WAAW,SAAS;AAC/C,UAAO,SACH;IACE,QAAQ,WAAW,MAAM,KAAK,SAAS,SAAS,QAAQ,WAAW,CAAC;IACpE,GAAI,MAAM,KAAK,cAAc,QAAQ,WAAW;IACjD,GACD;IACJ,CACH,EACkB,QAAQ,QAAQ,QAAQ,KAAK;AAChD,SAAO;GAAE,GAAG;GAAM;GAAO;;CAE3B,MAAM,cAAc,OAA0B,UAAkB,UAAkB;EAChF,MAAM,UAAUA,OAAK,MAAM;EAC3B,MAAM,SAAS,MAAM,KAAK,OAAO,SAAS;EAC1C,MAAM,aAAa,MAAM,KAAK,OAAO,YAAY,IAAI,cAAc,UAAU,QAAQ,GAAG,CAAC;EACzF,MAAM,SAAS,MAAM,KAAK,aAAa,QAAQ,UAAU,WAAW;AACpE,SAAO;GACL,QAAQ,aAAa,MAAM,KAAK,SAAS,SAAS,QAAQ,WAAW,CAAC;GACtE,QAAQ,aAAa,MAAM,KAAK,eAAe,OAAO,CAAC;GACvD,GAAI,MAAM,KAAK,cAAc,QAAQ,WAAW;GACjD;;CAEH,MAAM,YAAY,OAA0B,UAAkB,UAAkB;EAC9E,MAAM,UAAUA,OAAK,MAAM;EAC3B,MAAM,SAAS,MAAM,KAAK,OAAO,SAAS;EAC1C,MAAM,aAAa,MAAM,OACvB,KAAK,OAAO,aACZ,cAAc,UAAU,QAAQ,GAAG,EACnC,OAAO,WAAW;AAChB,SAAM,KAAK,aAAa,QAAQ,UAAU,OAAO;GACjD,MAAM,OAAO,gBAAgB,OAAQ;GACrC,MAAM,WAAW,KAAK,SAAS,aAAa,eAAe;AAC3D,YAAS,cAAc,KAAK,MAAM;AAClC,QAAK,SAAS,YAAY;AAC1B,QAAK,iBAAiB,KAAK,MAAM;AACjC,UAAO;IAAE,OAAO;IAAM,QAAQ;IAAM;IAEvC;AACD,SAAO,MAAM,KAAK,eAAe,WAAW;;CAE9C,MAAM,eAAe,OAA0B,UAAkB,UAAkB;EACjF,MAAM,UAAUA,OAAK,MAAM;EAC3B,MAAM,SAAS,MAAM,KAAK,OAAO,SAAS;EAC1C,MAAM,WAAW,MAAM,KAAK,UAAU;EACtC,MAAM,aAAa,MAAM,OACvB,KAAK,OAAO,aACZ,cAAc,UAAU,QAAQ,GAAG,EACnC,OAAO,WAAW;GAChB,MAAM,SAAS,MAAM,KAAK,aAAa,QAAQ,UAAU,OAAO;AAChE,aACE,CAAC,CAAC,QAAQ,aAAa,CAAC,SAAS,OAAO,KAAK,EAC7C,uBACA,wDACA,IACD;GACD,MAAM,OAAO,gBAAgB,OAAQ;GACrC,MAAM,WAAW,KAAK,SAAS,aAAa,eAAe;AAC3D,aACE,SAAS,WACT,sBACA,+CACD;AACD,OAAI,OAAO,SAAS,OAClB,WACE,OAAO,gBAAgB,KAAK,KAAK,CAAC,SAAS,IAAI,KAAK,MAAM,OAAO,aAAa,EAC9E,oBACA,yCACA,IACD;AACH,aACE,KAAK,KAAK,CAAC,SAAS,GAAG,KAAK,MAAM,SAAS,UAAU,IAAI,OAAO,kBAAkB,KAClF,uBACA,qEACA,IACD;AACD,YAAS,gBAAgB,KAAK,MAAM;AACpC,QAAK,SAAS,YAAY;AAC1B,QAAK,OAAO,MAAM,KAAK,SAAS,SAAS,QAAQ,KAAK,EAAE,MAAM,SAAS;AACvE,UAAO;IAAE,OAAO;IAAM,QAAQ;IAAM;IAEvC;AACD,QAAM,KAAK,kBAAkB,WAAW;AACxC,SAAO,EACL,GAAI,MAAM,KAAK,cAAc,QAAQ,WAAW,EACjD;;CAEH,OAAO,QAAgB,YAAwB,UAA6B;AAC1E,aAAW,iBAAiB,KAAK,MAAM;EACvC,MAAM,WAAW,QAAQ,OAAO,CAAC,QAAQ,WAAW,OAAO,SAAS;AACpE,MACE,WAAW,eACX,CAAC,SAAS,UACV,CAAC,SAAS,OAAO,WAAW,WAAW,SAAS,OAAO,KAAK,YAAY,CAExE;AACF,aAAW,SAAS;AACpB,aAAW,cAAc,KAAK,MAAM;AACpC,aAAW,sBAAsB,SAAS,KAAK,WAAW,OAAO,GAAG;AACpE,MAAI,OAAO,mBACT,YAAW,cAAc;GACvB,IAAI,OAAO,YAAY;GACvB,UAAU,OAAO;GACjB,aAAa,OAAO;GACpB,QAAQ,WAAW;GACnB,aAAa,WAAW;GACxB,YAAY,SAAS;GACrB,UAAU,KAAK,MAAM;GACrB,QAAQ;GACR,WAAW;GACX,kBAAkB;GACnB;;CAEL,MAAM,kBAAkB,YAAuC;AAC7D,MAAI,WAAW,YACb,OAAM,QAAQ,KAAK,OAAO,cAAc,WAAW,YAAY,IAAI;GACjE,cAAc,WAAW;GACzB,UAAU,WAAW;GACrB,QAAQ,WAAW;GACpB,CAAC;AACJ,MAAI,WAAW,YACb,OAAM,KAAK,OACT,YACA,aACA,mBACA,4EACD;;CAEL,MAAM,UACJ,OACA,UACA,UAC4B;EAC5B,MAAM,UAAUA,OAAK,MAAM;EAC3B,MAAM,SAAS,MAAM,KAAK,OAAO,SAAS;EAC1C,MAAM,SAAS,MAAM,OACnB,KAAK,OAAO,aACZ,cAAc,UAAU,QAAQ,GAAG,EACnC,OAAO,WAAW;GAChB,MAAM,SAAS,MAAM,KAAK,aAAa,QAAQ,UAAU,OAAO;AAChE,aAAU,OAAO,SAAS,UAAU,OAAO,MAAM,cAAc,2BAA2B;GAC1F,MAAM,OAAO,gBAAgB,OAAQ;GACrC,MAAM,WAAW,KAAK,SAAS,aAAa,eAAe;AAC3D,YAAS,cAAc,KAAK,MAAM;GAClC,MAAM,WAAW,SAAS,aAAa,MAAM,YAAY,QAAQ,WAAW,cAAc;AAC1F,OACE,aACC,CAAC,SAAS,aAAa,KAAK,KAAK,CAAC,SAAS,GAAG,KAAK,MAAM,SAAS,UAAU,EAE7E,QAAO;IAAE,OAAO;IAAM,QAAQ;KAAE,SAAS;KAAU,SAAS;KAAO;IAAE;AACvE,OAAI,UAAU;AACZ,aAAS,SAAS;AAClB,aAAS,OAAO;AAChB,aAAS,cAAc,KAAK,MAAM;;AAEpC,QAAK,SAAS,YAAY;AAC1B,OAAI,SAAS,aAAa,UAAU,OAAO,KAAK,eAAe,SAAS,YACtE,QAAO;IAAE,OAAO;IAAM,QAAQ;KAAE,SAAS;KAAM,SAAS;KAAM;IAAE;GAClE,MAAM,OAAO,gBAAgB,OAAO,KAAK;GAEzC,MAAM,aADQ,KAAK,qBAAqB,QAAQ,KAAK,UAAU,GAAG,KAAK,WAC/C,KACrB,EAAE,gBAAgB,UAAU,aAAa,cAAc,GAAG,gBAAgB;IACzE,GAAG;IACH,SAAS,KAAK,mBAAmB,QAAQ,SAAS,QAAQ,GAAG,SAAS;IACvE,EACF;GACD,MAAM,UAAU;IACd,IAAI,OAAO,YAAY;IACvB,WAAW,KAAK,MAAM;IACtB,WAAW,KAAK,mBACZ,IAAI,KAAK,KAAK,KAAK,CAAC,SAAS,GAAG,KAAK,mBAAmB,IAAO,CAAC,aAAa,GAC7E;IACJ,QAAQ;IACR;IACA;IACA,aAAa;IACb,OAAO;IACP,UAAU;IACV,SAAS;IACT,QAAQ;IACR,UAAU,EAAE;IACZ,SAAS,EAAE;IACX,eAAe;IAChB;AACD,YAAS,aAAa,KAAK,QAAQ;AACnC,QAAK,iBAAiB,KAAK,MAAM;AACjC,UAAO;IAAE,OAAO;IAAM,QAAQ;KAAE;KAAS,SAAS;KAAO;IAAE;IAE9D;AACD,YACE,OAAO,WAAW,CAAC,OAAO,SAC1B,iBACA,kEACA,IACD;AACD,SAAO,cAAc,OAAO,QAAQ;;CAEtC,MAAM,WAAW,OAA0B,OAAgB;EACzD,MAAM,UAAUA,OAAK,MAAM;EAC3B,MAAM,SAAS,MAAM,kBAAkB,MAAM;EAC7C,MAAM,SAAS,MAAM,KAAK,OAAO,OAAO,SAAS;EACjD,MAAM,WAAW,MAAM,KAAK,UAAU;EACtC,MAAM,SAAS,MAAM,OACnB,KAAK,OAAO,aACZ,cAAc,OAAO,IAAI,QAAQ,GAAG,EACpC,OAAO,WAAW;AAChB,SAAM,KAAK,aAAa,QAAQ,OAAO,UAAU,OAAO;GACxD,MAAM,OAAO,gBAAgB,OAAQ;GACrC,MAAM,WAAW,KAAK,SAAS,OAAO;GACtC,MAAM,UAAU,UAAU,aAAa,MAAM,SAAS,KAAK,OAAO,OAAO,UAAU;AACnF,aAAU,WAAW,UAAU,aAAa,2BAA2B,IAAI;AAC3E,OAAI,QAAQ,WAAW,cACrB,QAAO;IACL,OAAO;IACP,QAAQ;KAAE,YAAY;KAAM;KAAS,SAAS,QAAQ,WAAW;KAAW;IAC7E;AACH,OAAI,QAAQ,aAAa,KAAK,KAAK,CAAC,SAAS,IAAI,KAAK,MAAM,QAAQ,UAAU,EAAE;AAC9E,YAAQ,SAAS;AACjB,YAAQ,OAAO;AACf,YAAQ,cAAc,KAAK,MAAM;AACjC,WAAO;KAAE,OAAO;KAAM,QAAQ;MAAE,YAAY;MAAM;MAAS,SAAS;MAAM;KAAE;;AAE9E,aAAU,QAAQ,MAAM,mBAAmB,kCAAkC,IAAI;AACjF,aACE,OAAO,0BAA0B,UAC/B,OAAO,2BAA2B,QAAQ,iBAAiB,IAC7D,kBACA,4EACA,IACD;GACD,MAAM,SAAS,aAAa,QAAQ,KAAK,WAAW,OAAO,QAAQ;AACnE,WAAQ,SAAS,OAAO,QAAQ,OAAO,OAAO,WAAW,QAAQ,KAAK;AACtE,WAAQ,WACN,QAAQ,KAAK,iBAAiB,kBAC7B,QAAQ,KAAK,iBAAiB,gBAAgB,QAAQ,SACnD,OAAO,WACP,EAAE;AACR,WAAQ,QAAQ,OAAO;AACvB,WAAQ,WAAW,OAAO;AAC1B,WAAQ,UAAU,OAAO;AACzB,WAAQ,UAAU,OAAO;AACzB,WAAQ,SAAS;AACjB,WAAQ,cAAc,KAAK,MAAM;AACjC,WAAQ,OAAO;AACf,OAAI,QAAQ,OAAQ,UAAS,gBAAgB,KAAK,MAAM;AACxD,QAAK,OAAO,MAAM,KAAK,SAAS,SAAS,QAAQ,KAAK,EAAE,MAAM,SAAS;AACvE,UAAO;IAAE,OAAO;IAAM,QAAQ;KAAE,YAAY;KAAM;KAAS,SAAS;KAAO;IAAE;IAEhF;AACD,YACE,CAAC,OAAO,SACR,gBACA,yEACA,IACD;AACD,QAAM,KAAK,kBAAkB,OAAO,WAAW;AAC/C,SAAO;GACL,SAAS,cAAc,OAAO,QAAQ;GACtC,GAAI,MAAM,KAAK,cAAc,QAAQ,OAAO,WAAW;GACxD;;CAEH,MAAM,cAAc,OAA0B,OAAgB;EAC5D,MAAM,UAAUA,OAAK,MAAM;EAC3B,MAAM,SAAS,MAAM,iBAAiB,MAAM;EAC5C,MAAM,SAAS,MAAM,KAAK,OAAO,OAAO,SAAS;AA2CjD,SAAO;GAAE,OAAO;GAAM,eA1CA,MAAM,OAC1B,KAAK,OAAO,aACZ,cAAc,OAAO,IAAI,QAAQ,GAAG,EACpC,OAAO,WAAW;AAChB,UAAM,KAAK,aAAa,QAAQ,OAAO,UAAU,OAAO;IACxD,MAAM,OAAO,gBAAgB,OAAQ;IACrC,MAAM,UAAU,KAAK,SAAS,OAAO,WAAW,aAAa,MAC1D,SAAS,KAAK,OAAO,OAAO,UAC9B;AACD,cACE,SAAS,WAAW,iBAAiB,QAAQ,MAC7C,kBACA,gCACA,IACD;AACD,cACE,CAAC,QAAQ,aAAa,KAAK,KAAK,CAAC,SAAS,GAAG,KAAK,MAAM,QAAQ,UAAU,EAC1E,gBACA,kCACA,IACD;AACD,iBAAa,QAAQ,KAAK,WAAW,OAAO,QAAQ;IACpD,MAAM,WAAW,QAAQ,iBAAiB;IAC1C,MAAM,aAAa,YACjB,KAAK,UAAU,OAAO,QAAQ,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC,CAAC;AAEhF,QACE,aAAa,OAAO,wBAAwB,KAC5C,UAAU,QAAQ,QAAQ,KAAK,UAAU,OAAO,QAAQ,CAExD,QAAO;KAAE,OAAO;KAAM,QAAQ;KAAU;AAC1C,cACE,aAAa,OAAO,uBACpB,kBACA,wEACA,IACD;AACD,YAAQ,UAAU,OAAO;AACzB,YAAQ,gBAAgB,WAAW;AACnC,WAAO;KAAE,OAAO;KAAM,QAAQ,QAAQ;KAAe;KAExD;GACoC;;CAEvC,MAAM,iBAAiB,OAA0B,OAAgB;EAC/D,MAAM,UAAUA,OAAK,MAAM;EAC3B,MAAM,SAAS,MAAM,wBAAwB,MAAM;EACnD,MAAM,SAAS,MAAM,KAAK,OAAO,OAAO,SAAS;EACjD,MAAM,aAAa,MAAM,OACvB,KAAK,OAAO,aACZ,cAAc,OAAO,IAAI,QAAQ,GAAG,EACpC,OAAO,WAAW;GAChB,MAAM,SAAS,MAAM,KAAK,aAAa,QAAQ,OAAO,UAAU,OAAO;AACvE,aACE,OAAO,SAAS,gBAAgB,OAAO,YACvC,qBACA,iCACD;AACD,aACE,CAAC,OAAO,WAAW,SAAS,KAAK,KAAK,CAAC,SAAS,IAAI,KAAK,MAAM,OAAO,WAAW,MAAM,EACvF,qBACA,uCACA,IACD;GACD,MAAM,OAAO,gBAAgB,OAAQ;GACrC,MAAM,WAAW,KAAK,SAAS,OAAO,aAAa,eAAe;GAClE,MAAM,OAAO,SAAS,YAAY,GAAG,GAAG;AACxC,aACE,CAAC,SAAS,aACV,qBACA,4CACA,IACD;AACD,aACE,CAAC,QAAS,KAAK,WAAW,YAAY,OAAO,WAAW,mBACxD,sBACA,6CACA,IACD;AACD,aACE,SAAS,YAAY,SAAS,IAC9B,oBACA,qDACA,IACD;AACD,YAAS,cAAc,KAAK,MAAM;AAClC,YAAS,YAAY,KAAK;IACxB,IAAI,OAAO,YAAY;IACvB,MAAM,OAAO;IACb,eAAe,OAAO;IACtB,aAAa,KAAK,MAAM;IACxB,QAAQ;IACR,WAAW,OAAO,WAAW;IAC7B,eAAe,OAAO,WAAW;IACjC,QAAQ;IACR,UAAU;IACV,UAAU;IACV,UAAU;IACX,CAAC;AACF,QAAK,SAAS,OAAO,YAAY;AACjC,QAAK,iBAAiB,KAAK,MAAM;AACjC,UAAO;IAAE,OAAO;IAAM,QAAQ;IAAM;IAEvC;AACD,SAAO,MAAM,KAAK,eAAe,WAAW;;CAE9C,MAAM,gBAAgB,OAA0B,OAAgB;EAC9D,MAAM,SAAS,MAAM,aAAa,MAAM;EACxC,MAAM,SAAS,MAAM,KAAK,OAAO,OAAO,SAAS;EACjD,MAAM,UAAU,KAAK,WAAW,QAAQ,MAAM;EAC9C,MAAM,WAAW,MAAM,KAAK,UAAU;EACtC,MAAM,aAAa,MAAM,OACvB,KAAK,OAAO,aACZ,cAAc,OAAO,IAAI,OAAO,OAAO,EACvC,OAAO,WAAW;AAChB,aAAU,QAAQ,aAAa,yBAAyB,IAAI;GAC5D,MAAM,OAAO,gBAAgB,OAAO;GACpC,MAAM,WAAW,KAAK,SAAS,OAAO;GACtC,MAAM,aAAa,UAAU,YAAY,MAAM,SAAS,KAAK,OAAO,OAAO,aAAa;AACxF,aAAU,cAAc,UAAU,aAAa,oCAAoC,IAAI;AACvF,aACE,WAAW,WAAW,aACtB,kBACA,4CACA,IACD;AACD,aACE,OAAO,UAAU,WAAW,WAC5B,iBACA,2DACD;AACD,cAAW,SAAS,OAAO;AAC3B,cAAW,WAAW,OAAO;AAC7B,cAAW,SAAS;AACpB,cAAW,WAAW,KAAK,MAAM;AACjC,cAAW,WAAW,QAAQ;AAC9B,OAAI,OAAO,UAAU,WAAW,cAAe,UAAS,gBAAgB,KAAK,MAAM;GACnF,MAAM,SAAS,gBAAiB,MAAM,KAAK,iBAAiB,KAAK,EAAI,KAAK,KAAK,CAAC;AAChF,OACE,CAAC,UAAU,YAAY,CAAC,SAAS,KAAK,OAAO,IAC7C,CAAC;IAAC;IAAa;IAAW;IAAU,CAAC,SAAS,OAAO,CAErD,MAAK,OAAO,MAAM,KAAK,SAAS,SAAS,QAAQ,KAAK,EAAE,MAAM,SAAS;AACzE,UAAO;IAAE,OAAO;IAAM,QAAQ;IAAM;IAEvC;AACD,QAAM,KAAK,kBAAkB,WAAW;AACxC,QAAM,KAAK,OACT,YACA,UAAU,OAAO,gBACjB,qBACA,oDACD;AACD,QAAM,KAAK,MAAM,SAAS,oBAAoB,WAAW,IAAI;GAC3D,UAAU,OAAO;GACjB,QAAQ,OAAO;GAChB,CAAC;AACF,SAAO,gBAAiB,MAAM,KAAK,iBAAiB,WAAW,EAAI,KAAK,KAAK,CAAC;;CAEhF,MAAM,SAAS,OAA0B,OAAgB;EACvD,MAAM,UAAUA,OAAK,MAAM;EAC3B,MAAM,SAAS,MAAM,YAAY,MAAM;EACvC,MAAM,SAAS,MAAM,KAAK,OAAO,OAAO,SAAS;AAajD,SAAO,EAAE,OAZU,MAAM,OACvB,KAAK,OAAO,aACZ,cAAc,OAAO,IAAI,QAAQ,GAAG,EACpC,OAAO,WAAW;AAChB,SAAM,KAAK,aAAa,QAAQ,OAAO,UAAU,OAAO;GACxD,MAAM,OAAO,gBAAgB,OAAQ;GACrC,MAAM,WAAW,KAAK,SAAS,OAAO,aAAa,eAAe;AAClE,YAAS,OAAO,OAAO;AACvB,QAAK,SAAS,OAAO,YAAY;AACjC,UAAO;IAAE,OAAO;IAAM,QAAQ;IAAM;IAEvC,EACyB,SAAS,OAAO,UAAW,MAAM;;CAE7D,MAAM,YAAY,OAA0B,UAAkB;EAC5D,MAAM,UAAUA,OAAK,MAAM;EAC3B,MAAM,aAAa,MAAM,KAAK,OAAO,YAAY,IAAI,cAAc,UAAU,QAAQ,GAAG,CAAC;AACzF,YAAU,YAAY,aAAa,aAAa,0BAA0B,IAAI;AAC9E,QAAM,KAAK,kBAAkB,WAAW;AACxC,SAAO,WAAW;;CAEpB,MAAM,iBAAiB,OAA0B,IAAY;EAC3D,MAAM,UAAUA,OAAK,MAAM;EAC3B,MAAM,QAAQ,MAAM,KAAK,OAAO,aAAa,IAAI,GAAG;AACpD,YAAU,OAAO,WAAW,QAAQ,IAAI,aAAa,0BAA0B,IAAI;EACnF,MAAM,aAAa,MAAM,KAAK,OAAO,YAAY,IAAI,MAAM,aAAa;AACxE,YAAU,YAAY,aAAa,OAAO,IAAI,aAAa,0BAA0B,IAAI;AACzF,SAAO,WAAW;;CAEpB,MAAM,kBAAkB,IAAY;EAClC,MAAM,QAAQ,MAAM,KAAK,OAAO,aAAa,IAAI,GAAG;AACpD,YAAU,OAAO,aAAa,0BAA0B,IAAI;EAE5D,MAAM,eADa,MAAM,KAAK,OAAO,YAAY,IAAI,MAAM,aAAa,GACxC;AAChC,YAAU,aAAa,OAAO,IAAI,aAAa,0BAA0B,IAAI;AAC7E,SAAO;GACL;GACA,aAAa,YAAY;GACzB,YAAY,YAAY;GACxB,UAAU,YAAY;GACtB,OAAO,CAAC,YAAY;GACpB,QAAQ,YAAY;GACpB,aAAa,YAAY,SAAS,YAAY,cAAc;GAC7D;;CAEH,MAAM,iBAAiB,OAA0B,UAAkB,QAAiB;EAClF,MAAM,UAAUA,OAAK,MAAM;AAW3B,SAVoB,MAAM,OACxB,KAAK,OAAO,aACZ,cAAc,UAAU,QAAQ,GAAG,EACnC,OAAO,WAAW;AAChB,aAAU,QAAQ,aAAa,aAAa,0BAA0B,IAAI;GAC1E,MAAM,OAAO,gBAAgB,OAAO;AACpC,QAAK,YAAa,SAAS;AAC3B,UAAO;IAAE,OAAO;IAAM,QAAQ,KAAK;IAAc;IAEpD;;CAGH,MAAM,kBACJ,OACA,UACA,QACA,QACA;EACA,MAAM,SAAS,MAAM,KAAK,OAAO,SAAS;EAC1C,MAAM,UAAU,KAAK,WAAW,QAAQ,MAAM;EAC9C,MAAM,cAAc,MAAM,OACxB,KAAK,OAAO,aACZ,cAAc,UAAU,OAAO,EAC/B,OAAO,WAAW;AAChB,aAAU,QAAQ,aAAa,aAAa,0BAA0B,IAAI;GAC1E,MAAM,OAAO,gBAAgB,OAAO;AACpC,QAAK,YAAa,YAAY,KAAK,MAAM;AACzC,QAAK,YAAa,mBAAmB;AACrC,UAAO;IAAE,OAAO;IAAM,QAAQ,KAAK;IAAc;IAEpD;AACD,QAAM,KAAK,MAAM,SAAS,sBAAsB,YAAY,GAAG;AAC/D,SAAO;;CAET,MAAM,iBAAiB,OAA0B,UAAkB;EACjE,MAAM,UAAUA,OAAK,MAAM;EAC3B,MAAM,aAAa,MAAM,OACvB,KAAK,OAAO,aACZ,cAAc,UAAU,QAAQ,GAAG,EACnC,OAAO,WAAW;AAChB,aAAU,QAAQ,aAAa,yBAAyB,IAAI;GAC5D,MAAM,OAAO;IAAE,GAAG;IAAQ,QAAQ;IAAsB,cAAc;IAAoB;AAC1F,UAAO;IAAE,OAAO;IAAM,QAAQ;IAAM;IAEvC;AACD,SAAO,MAAM,KAAK,eAAe,WAAW;;CAE9C,MAAM,YAAY,OAA0B,UAAkB,UAAkB,QAAiB;EAC/F,MAAM,UAAUA,OAAK,MAAM;EAC3B,MAAM,SAAS,MAAM,KAAK,OAAO,SAAS;EAC1C,MAAM,aAAa,MAAM,KAAK,OAAO,YAAY,IAAI,cAAc,UAAU,QAAQ,GAAG,CAAC;AACzF,QAAM,KAAK,aAAa,QAAQ,UAAU,WAAW;EACrD,MAAM,OAAO,MAAM,UAAU,KAAK,OAAO,aAAa;GACpD,OAAO;IAAE;IAAU;IAAU,QAAQ;IAAa;GAClD,SAAS,EAAE,WAAW,OAAO;GAC7B;GACA,OAAO;GACR,CAAC;EACF,MAAM,MAAM,MAAM,UAAU,KAAK,OAAO,aAAa;GACnD,OAAO;IAAE;IAAU;IAAU,UAAU,QAAQ;IAAI,QAAQ;IAAW;GACtE,OAAO;GACR,CAAC;AACF,SAAO;GAAE,GAAG;GAAM,SAAS,IAAI;GAAO;;CAExC,MAAM,eAAe,OAA0B,OAAgB;EAC7D,MAAM,UAAUA,OAAK,MAAM;EAC3B,MAAM,SAAS,MAAM,kBAAkB,MAAM;EAC7C,MAAM,SAAS,MAAM,KAAK,OAAO,OAAO,SAAS;EACjD,MAAM,aAAa,MAAM,KAAK,OAAO,YAAY,IAAI,cAAc,OAAO,IAAI,QAAQ,GAAG,CAAC;AAC1F,QAAM,KAAK,aAAa,QAAQ,OAAO,UAAU,WAAW;AAC5D,MAAI,OAAO,UAAU;GACnB,MAAM,SAAS,MAAM,KAAK,OAAO,YAAY,IAAI,OAAO,SAAS;AACjE,aACE,UACE,OAAO,aAAa,OAAO,MAC3B,OAAO,aAAa,OAAO,YAC3B,OAAO,WAAW,eAClB,CAAC,OAAO,UACV,kBACA,kDACD;;EAEH,MAAM,WAAW,MAAM,KAAK,UAAU;EACtC,MAAM,KAAK,OAAO,YAAY;EAC9B,MAAM,aAAyB;GAC7B,GAAG;GACH;GACA,UAAU,QAAQ;GAClB,YAAY,QAAQ,QAAQ;GAC5B,QAAQ,SAAS,sBAAsB,YAAY;GACnD,WAAW,KAAK,MAAM;GACtB,WAAW,KAAK,MAAM;GACvB;AACD,QAAM,KAAK,OAAO,YAAY,IAAI,IAAI,WAAW;AACjD,SAAO;;CAET,MAAM,cAAc,UAAkB,QAAiB;AAErD,aADe,MAAM,KAAK,OAAO,SAAS,EACzB,WAAW,aAAa,aAAa,qBAAqB,IAAI;AAC/E,SAAO,UAAU,KAAK,OAAO,SAAS;GACpC,OAAO;IAAE;IAAU,QAAQ;IAAa;GACxC,SAAS,EAAE,WAAW,QAAQ;GAC9B,OAAO;GACP;GACD,CAAC;;CAEJ,MAAM,WAAW,OAA0B,OAAgB;EACzD,MAAM,UAAUA,OAAK,MAAM;EAC3B,MAAM,SAAS,MAAM,cAAc,MAAM;EACzC,MAAM,SAAS,MAAM,KAAK,OAAO,OAAO,SAAS;EACjD,MAAM,aAAa,MAAM,KAAK,iBAC5B,MAAM,KAAK,OAAO,YAAY,IAAI,cAAc,OAAO,IAAI,QAAQ,GAAG,CAAC,CACxE;AACD,yBAAuB,YAAY,KAAK,KAAK,CAAC;AAC9C,YACE,OAAO,OAAO,WAAW,SAAS,CAAC,MAAM,aAAa,SAAS,YAAY,EAC3E,oBACA,8DACA,IACD;EACD,MAAM,WAAW,MAAM,KAAK,UAAU;EACtC,MAAM,KAAK,cAAc,OAAO,IAAI,QAAQ,GAAG;AAa/C,SAZe,MAAM,OAAO,KAAK,OAAO,SAAS,IAAI,OAAO,WAAW;GACrE,MAAM,OAAe;IACnB,GAAG;IACH;IACA,QAAQ,QAAQ;IAChB,YAAY,QAAQ,QAAQ;IAC5B,QAAQ,SAAS,kBAAkB,YAAY;IAC/C,WAAW,QAAQ,aAAa,KAAK,MAAM;IAC3C,WAAW,KAAK,MAAM;IACvB;AACD,UAAO;IAAE,OAAO;IAAM,QAAQ;IAAM;IACpC;;CAGJ,MAAM,SAAS,OAA0B,YAAuC,OAAgB;EAC9F,MAAM,SAAS,MAAM,gBAAgB,MAAM;EAC3C,MAAM,QAAQ,KAAK,OAAO;EAC1B,MAAM,SAAS,MAAM,MAAM,aAAa,OAAO,GAAG;AAClD,YAAU,QAAQ,aAAa,6BAA6B,IAAI;EAChE,MAAM,SAAS,MAAM,KAAK,OAAO,OAAO,MAAM,SAAS;EACvD,MAAM,UAAU,KAAK,WAAW,QAAQ,MAAM;AAC9C,YACE,OAAO,aAAa,OAAO,kBAC3B,YACA,gDACA,IACD;EACD,MAAM,OAAO;GAAE,GAAG,OAAO;GAAO,QAAQ,OAAO;GAAQ,WAAW,KAAK,MAAM;GAAE;AAM/E,aALc,MAAO,MAAyC,cAC5D,OAAO,IACP,OAAO,UACP,KACD,EACe,SAAS,YAAY,uCAAuC,IAAI;AAChF,QAAM,KAAK,MAAM,SAAS,GAAG,WAAW,YAAY,OAAO,IAAI,EAAE,QAAQ,OAAO,QAAQ,CAAC;AACzF,SAAO;;CAET,MAAM,eACJ,OACA,YACA,UAAmE,EAAE,EACrE;EACA,MAAM,UAAUA,OAAK,MAAM;AAC3B,MAAI,QAAQ,SAAU,MAAK,WAAW,MAAM,KAAK,OAAO,QAAQ,SAAS,EAAE,QAAQ;MAC9E,WAAU,QAAQ,QAAQ,IAAI,aAAa,8BAA8B,IAAI;EAClF,MAAM,QAAQ,KAAK,OAAO;EAC1B,MAAM,OAAO,MAAM,MAAM,MAAM;GAC7B,OAAO;IACL,GAAI,QAAQ,WAAW,EAAE,UAAU,QAAQ,UAAU,GAAG,EAAE;IAC1D,GAAI,QAAQ,SAAS,EAAE,QAAQ,QAAQ,QAAQ,GAAG,EAAE;IACrD;GACD,SAAS,EAAE,WAAW,QAAQ;GAC9B,OAAO;GACP,QAAQ,QAAQ;GACjB,CAAC;EAEF,MAAM,SADW,MAAM,QAAQ,IAAI,KAAK,MAAM,KAAK,SAAS,MAAM,aAAa,KAAK,GAAG,CAAC,CAAC,EAClE,SAAS,SAC9B,OAAO,CAAC;GAAE,GAAG,KAAK;GAAO,UAAU,KAAK;GAAU,CAAC,GAAG,EAAE,CACzD;AACD,SAAO;GAAE,GAAG;GAAM;GAAO;;CAE3B,MAAM,YACJ,OACA,UAAmF,EAAE,EACrF;EACA,MAAM,UAAUA,OAAK,MAAM;AAC3B,MAAI,QAAQ,SAAU,MAAK,WAAW,MAAM,KAAK,OAAO,QAAQ,SAAS,EAAE,QAAQ;MAC9E,WAAU,QAAQ,QAAQ,IAAI,aAAa,8BAA8B,IAAI;EAClF,MAAM,OAAO,MAAM,UAAU,KAAK,OAAO,aAAa;GACpD,OAAO;IACL,GAAI,QAAQ,WAAW,EAAE,UAAU,QAAQ,UAAU,GAAG,EAAE;IAC1D,GAAI,QAAQ,SAAS,EAAE,QAAQ,QAAQ,QAAQ,GAAG,EAAE;IACrD;GACD,SAAS,EAAE,YAAY,QAAQ;GAC/B,OAAO,QAAQ,SAAS;GACxB,QAAQ,QAAQ;GACjB,CAAC;EACF,MAAM,UAAU,MAAM,KAAK,OAAO,QAAQ,QAAQ,KAAK,MAAM,KAAK,SAAS,KAAK,SAAS,CAAC;AAC1F,SAAO;GACL,GAAG;GACH,OAAO,MAAM,QAAQ,IACnB,KAAK,MAAM,IAAI,OAAO,eAAe;IACnC,MAAM,WAAW,MAAM,KAAK,iBAAiB,WAAW;IACxD,MAAM,OAAO,QAAQ,IAAI,WAAW,SAAS;IAC7C,MAAM,SAAS,OAAO,MAAM,KAAK,SAAS,SAAS,MAAM,WAAW,GAAG;IACvE,MAAM,MAAM,KAAK,KAAK;AACtB,WAAO;KACL,GAAG,gBAAgB,UAAW,IAAI;KAClC,cAAc,WAAW;KACzB,aAAa,QAAQ,SAAS;KAC9B,cAAc,OAAO,aAClB,SAAS,QAAQ,OAAO,GAAG,EAAE,EAAE,KAAK,WAAW,CAAC,OAAO,IAAI,OAAO,MAAM,CAAC,CAC3E;KACD,iBAAiB,SAAS,gBAAgB,QAAQ,UAAU,IAAI,GAAG;KACpE;KACD,CACH;GACF;;CAEH,MAAM,OAAO,OAA0B,UAAmB;EACxD,MAAM,UAAUA,OAAK,MAAM;AAC3B,MAAI,SAAU,MAAK,WAAW,MAAM,KAAK,OAAO,SAAS,EAAE,QAAQ;MAC9D,WAAU,QAAQ,QAAQ,IAAI,aAAa,8BAA8B,IAAI;EAClF,MAAM,SAAoD,WAAW,EAAE,UAAU,GAAG,EAAE;EACtF,MAAM,WAAW;GAAC;GAAW;GAAU;GAAa;GAAY;GAAa;GAAY;EACzF,MAAM,SAAS,MAAM,QAAQ,IAC3B,SAAS,KAAK,WAAW,KAAK,OAAO,YAAY,MAAM;GAAE,GAAG;GAAQ;GAAQ,CAAC,CAAC,CAC/E;EACD,MAAM,eAAe,OAAO,YAC1B,SAAS,KAAK,QAAQ,UAAU,CAAC,QAAQ,OAAO,OAAQ,CAAC,CAC1D;EACD,MAAM,QAAQ,OAAO,QAAQ,KAAK,UAAU,MAAM,OAAO,EAAE;EAC3D,MAAM,gBAAgB,MAAM,KAAK,OAAO,YAAY,MAAM;GACxD,GAAG;GACH,aAAa,EAAE,KAAK,4BAA4B;GACjD,CAAC;EACF,MAAM,eAAe,OAAO,YAC1B,MAAM,QAAQ,IACZ;GAAC;GAAQ;GAAU;GAAU;GAAW,CAAC,IAAI,OAAO,iBAAiB,CACnE,cACA,MAAM,KAAK,OAAO,YAAY,MAAM;GAAE,GAAG;GAAQ;GAAc,CAAC,CACjE,CAAC,CACH,CACF;EACD,MAAM,kBAAkB,OAAO,YAC7B,MAAM,QAAQ,IACZ;GAAC;GAAW;GAAW;GAAa;GAAU,CAAC,IAAI,OAAO,iBAAiB,CACzE,cACA,MAAM,KAAK,OAAO,YAAY,MAAM;GAAE,GAAG;GAAQ;GAAc,CAAC,CACjE,CAAC,CACH,CACF;AACD,SAAO;GACL;GACA;GACA,GAAG;GACH,eAAe;GACf;GACA,gBAAgB,QAAQ,KAAK,MAAO,gBAAgB,QAAS,IAAI,GAAG;GACpE,aAAa,WAAW,IAAI,MAAM,KAAK,OAAO,QAAQ,OAAO;GAC7D,aAAa,KAAK,MAAM;GACzB;;CAEH,MAAM,cACJ,OACA,UAAkD,EAAE,EACpD;EACA,MAAM,QAAQ,CAAC,oBAAoB;EACnC,IAAI;EACJ,IAAI,OAAO;EACX,IAAI,QAAQ,IAAI,aAAa,CAAC,OAAO,oBAAoB,CAAC,aAAa;AACvE,KAAG;GACD,MAAM,OAAO,MAAM,KAAK,YAAY,OAAO;IAAE,GAAG;IAAS;IAAQ,OAAO;IAAK,CAAC;GAC9E,MAAM,OAAO,mBAAmB,KAAK,MAAM;AAC3C,WAAQ,KAAK;AACb,YAAS,IAAI,aAAa,CAAC,OAAO,KAAK,KAAK,OAAO,CAAC,CAAC,aAAa,KAAK,SAAS;AAChF,aACE,QAAQ,OAAU,SAAS,KAC3B,oBACA,kEACA,IACD;AACD,SAAM,KAAK,GAAG,KAAK;AACnB,YAAS,KAAK,UAAU,KAAK,SAAS;WAC/B;AACT,SAAO,MAAW,MAAM,KAAK,OAAO;;CAEtC,MAAM,WAAW,OAA0B,QAAiB;AAE1D,YADgBA,OAAK,MAAM,CACT,QAAQ,IAAI,aAAa,2CAA2C,IAAI;EAC1F,MAAM,OAAO,MAAM,KAAK,OAAO,QAAQ,MAAM;GAC3C,SAAS,EAAE,WAAW,QAAQ;GAC9B,OAAO;GACP;GACD,CAAC;EAIF,MAAM,SAHW,MAAM,QAAQ,IAC7B,KAAK,MAAM,KAAK,SAAS,KAAK,OAAO,QAAQ,aAAa,KAAK,GAAG,CAAC,CACpE,EACsB,SAAS,SAC9B,OAAO,CAAC;GAAE,GAAG,KAAK;GAAO,UAAU,KAAK;GAAU,CAAC,GAAG,EAAE,CACzD;AACD,SAAO;GAAE,GAAG;GAAM;GAAO;;CAE3B,MAAM,aAAa,IAAY;EAC7B,MAAM,SAAS,MAAM,KAAK,OAAO,QAAQ,IAAI,GAAG;AAChD,YAAU,QAAQ,aAAa,qBAAqB,IAAI;EACxD,MAAM,UAAU,MAAM,KAAK,OAAO,QAAQ,QAAQ,OAAO,UAAU;AACnE,SAAO;GACL,IAAI,OAAO;GACX,OAAO,OAAO;GACd,UAAU,OAAO;GACjB,QAAQ,OAAO;GACf,SAAS,CAAC,GAAG,QAAQ,QAAQ,CAAC,CAC3B,QAAQ,WAAW,OAAO,WAAW,eAAe,OAAO,SAAS,gBAAgB,OAAO,CAC3F,IAAI,WAAW;GACnB;;CAEH,MAAM,cAAc,OAA0B,UAAkB;EAC9D,MAAM,UAAUA,OAAK,MAAM;EAC3B,MAAM,SAAS,MAAM,KAAK,OAAO,QAAQ,IAAI,SAAS;AACtD,YAAU,QAAQ,aAAa,qBAAqB,IAAI;AACxD,YACE,CAAC,OAAO,UAAU,KAAK,KAAK,CAAC,SAAS,GAAG,KAAK,MAAM,OAAO,OAAO,EAClE,iBACA,0BACA,IACD;EACD,MAAM,KAAK,cAAc,UAAU,QAAQ,GAAG;AAC9C,SAAO,OAAO,KAAK,OAAO,oBAAoB,IAAI,OAAO,WAAW;AAClE,aACE,CAAC,UAAU,CAAC;IAAC;IAAY;IAAa;IAAY,CAAC,SAAS,OAAO,OAAO,EAC1E,qBACA,+DACA,IACD;GACD,MAAM,OAAyB,UAAU;IACvC;IACA;IACA,QAAQ,QAAQ;IAChB,aAAa,QAAQ,QAAQ;IAC7B,cAAc,QAAQ;IACtB,QAAQ;IACR,aAAa,KAAK,MAAM;IACxB,WAAW;IACX,WAAW;IACX,QAAQ;IACT;AACD,UAAO;IAAE,OAAO;IAAM,QAAQ;IAAM;IACpC;;CAEJ,MAAM,kBACJ,OACA,UAAmE,EAAE,EACrE;AACA,YACEA,OAAK,MAAM,CAAC,QAAQ,IACpB,aACA,qDACA,IACD;AACD,SAAO,UAAU,KAAK,OAAO,oBAAoB;GAC/C,OAAO;IACL,GAAI,QAAQ,WAAW,EAAE,UAAU,QAAQ,UAAU,GAAG,EAAE;IAC1D,GAAI,QAAQ,SAAS,EAAE,QAAQ,QAAQ,QAAQ,GAAG,EAAE;IACrD;GACD,SAAS,EAAE,aAAa,QAAQ;GAChC,OAAO;GACP,QAAQ,QAAQ;GACjB,CAAC;;CAEJ,MAAM,aACJ,OACA,UACA,QACA,QACA,SAAS,IACT;EACA,MAAM,UAAUA,OAAK,MAAM;AAC3B,YACE,QAAQ,QAAQ,IAChB,aACA,kDACA,IACD;EACD,MAAM,SAAS,MAAM,KAAK,OAAO,QAAQ,IAAI,SAAS;AACtD,YAAU,UAAU,KAAK,IAAI,OAAO,aAAa,qBAAqB,IAAI;EAC1E,MAAM,SAAS,MAAM,KAAK,IAAI,MAAM,IAAI,OAAO;AAC/C,YAAU,QAAQ,aAAa,8BAA8B,IAAI;EACjE,MAAM,aAAa,MAAM,OACvB,KAAK,OAAO,oBACZ,cAAc,UAAU,OAAO,EAC/B,OAAO,WAAW;GAChB,MAAM,OAAyB;IAC7B,GAAI,UAAU;KACZ,IAAI,cAAc,UAAU,OAAO;KACnC;KACA;KACA,aAAa,OAAO,QAAQ;KAC5B,cAAc,OAAO;KACrB,aAAa,KAAK,MAAM;KACzB;IACD;IACA,WAAW,KAAK,MAAM;IACtB,WAAW,QAAQ;IACnB,QAAQ,UAAU;IACnB;AACD,UAAO;IAAE,OAAO;IAAM,QAAQ;IAAM;IAEvC;EACD,MAAM,UAAoE,EAAE;AAC5E,OAAK,MAAM,YAAY,OAAO,UAC5B,KAAI;AACF,OAAI,WAAW,UAAU;IACvB,MAAM,WAAW,MAAM,KAAK,OAAO,YAAY,IAAI,cAAc,UAAU,OAAO,CAAC;AACnF,cACE,CAAC,YAAY,CAAC;KAAC;KAAa;KAAa;KAAW,CAAC,SAAS,SAAS,OAAO,EAC9E,yBACA,4EACA,IACD;AACD,UAAM,KAAK,gBAAgB,SAAS;KAAE;KAAU;KAAQ;KAAU,QAAQ;KAAU,CAAC;UAChF;IACL,MAAM,KAAK,cAAc,UAAU,OAAO;AAE1C,QADe,MAAM,KAAK,OAAO,YAAY,OAAO,GAAG,CAErD,OAAM,OAAO,KAAK,OAAO,aAAa,IAAI,OAAO,WAAW;AAC1D,eAAU,QAAQ,aAAa,yBAAyB,IAAI;KAC5D,MAAM,OAAO,gBAAgB,OAAO;KACpC,MAAM,QAAQ,KAAK,OAAO,UAAU;AACpC,SAAI,MAAO,OAAM,SAAS,WAAW,cAAc,cAAc;AACjE,YAAO;MAAE,OAAO;MAAM,QAAQ;MAAM;MACpC;;AAEN,WAAQ,KAAK;IAAE;IAAU,SAAS;IAAM,CAAC;WAClC,OAAO;AACd,WAAQ,KAAK;IACX;IACA,SAAS;IACT,OAAO,iBAAiB,WAAW,MAAM,UAAU;IACpD,CAAC;;AAGN,QAAM,KAAK,MAAM,SAAS,mBAAmB,WAAW,IAAI,EAAE,QAAQ,CAAC;AACvE,SAAO;GAAE;GAAY;GAAS;;CAEhC,MAAM,WAAW,OAA0B,OAAgB;EACzD,MAAM,UAAUA,OAAK,MAAM;AAC3B,YAAU,QAAQ,QAAQ,IAAI,aAAa,2CAA2C,IAAI;EAC1F,MAAM,SAAS,MAAM,kBAAkB,MAAM;AAC7C,OAAK,MAAM,MAAM,OAAO,KAAK,UAAW,OAAM,KAAK,OAAO,GAAG;EAC7D,MAAM,KAAK,OAAO,MAAM,OAAO,YAAY;EAC3C,MAAM,WAAW,MAAM,KAAK,OAAO,QAAQ,aAAa,GAAG;AAC3D,aACG,UAAU,YAAY,UAAU,OAAO,kBACxC,YACA,6CACA,IACD;AACD,MAAI,YAAa,MAAM,KAAK,OAAO,mBAAmB,MAAM,EAAE,UAAU,IAAI,CAAC,CAC3E,WACE,SAAS,MAAM,UAAU,OAAO,aAAa,OAAO,KAAK,UAAU,SAAS,SAAS,CAAC,EACtF,iBACA,wGACA,IACD;EAEH,MAAM,SAAiB;GACrB,GAAG,OAAO;GACV;GACA,WAAW,UAAU,MAAM,aAAa,QAAQ;GAChD,WAAW,UAAU,MAAM,aAAa,KAAK,MAAM;GACnD,WAAW,KAAK,MAAM;GACvB;EACD,MAAM,QAAQ,MAAM,KAAK,OAAO,QAAQ,cAAc,IAAI,UAAU,YAAY,MAAM,OAAO;AAC7F,YAAU,MAAM,SAAS,YAAY,oCAAoC,IAAI;AAC7E,QAAM,KAAK,MAAM,SAAS,eAAe,GAAG;AAC5C,SAAO;GAAE;GAAQ,UAAU,MAAM;GAAU;;CAE7C,MAAM,YACJ,OACA,IACA,kBACA,QACA;EACA,MAAM,UAAUA,OAAK,MAAM;AAC3B,YAAU,QAAQ,QAAQ,IAAI,aAAa,2CAA2C,IAAI;EAC1F,MAAM,SAAS,MAAM,KAAK,OAAO,QAAQ,aAAa,GAAG;AACzD,YAAU,QAAQ,aAAa,qBAAqB,IAAI;AACxD,YACE,OAAO,aAAa,kBACpB,YACA,iDACA,IACD;EACD,MAAM,SAAS,OAAO;EACtB,MAAM,UAAU,OAAO,UAAU,MAC/B,QACA,SAAS,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,OAAO,UAAU,OAAO,CAAC,CAC/D;EACD,MAAM,UAAoF,EAAE;AAC5F,OAAK,MAAM,UAAU,QACnB,KAAI;GACF,MAAM,UAAU,MAAM,KAAK,aAAa,SAAS,IAAI,QAAQ,SAAS;AACtE,WAAQ,KAAK,GAAG,QAAQ,QAAQ,KAAK,YAAY;IAAE;IAAQ,GAAG;IAAQ,EAAE,CAAC;WAClE,OAAO;AACd,WAAQ,KAAK;IACX;IACA,UAAU;IACV,SAAS;IACT,OAAO,iBAAiB,WAAW,MAAM,UAAU;IACpD,CAAC;;EAGN,MAAM,aAAa,SAAS,QAAQ;AACpC,SAAO;GAAE;GAAS;GAAY,SAAS,aAAa,OAAO,UAAU;GAAQ;;CAE/E,MAAM,OAAO,YAAwB,KAAa,OAAe,SAAgC;EAC/F,MAAM,WAAW,MAAM,KAAK,UAAU;EACtC,MAAM,cAAc,MAAM,KAAK,OAAO,YAAY,IAAI,WAAW,OAAO;EACxE,MAAM,KAAK,GAAG,WAAW,GAAG,GAAG;EAC/B,MAAM,eAA6B;GACjC;GACA,QAAQ,WAAW;GACnB,OAAO,WAAW;GAClB;GACA;GACA,UAAU,WAAW;GACrB,MAAM;GACN,WAAW,KAAK,MAAM;GACtB,aACE,SAAS,sBAAsB,aAAa,uBAAuB,QAC/D,YACA;GACN,eAAe;GACf,WAAW;GACZ;AACD,QAAM,QAAQ,KAAK,OAAO,eAAe,IAAI,aAAa;;CAE5D,MAAM,cAAc,OAA0B,QAAiB;EAC7D,MAAM,UAAUA,OAAK,MAAM;EAC3B,MAAM,OAAO,MAAM,UAAU,KAAK,OAAO,eAAe;GACtD,OAAO,EAAE,QAAQ,QAAQ,IAAI;GAC7B,SAAS,EAAE,WAAW,QAAQ;GAC9B,OAAO;GACP;GACD,CAAC;AACF,SAAO;GACL,GAAG;GACH,OAAO,KAAK,MAAM,KACf,EACC,OAAO,QACP,aAAa,SACb,eAAe,WACf,WAAW,UACX,GAAG,WACC,KACP;GACF;;CAEH,MAAM,iBAAiB,OAA0B,IAAY;EAC3D,MAAM,UAAUA,OAAK,MAAM;AAC3B,QAAM,OAAO,KAAK,OAAO,eAAe,IAAI,OAAO,WAAW;AAC5D,aACE,UAAU,OAAO,WAAW,QAAQ,IACpC,aACA,2BACA,IACD;AACD,UAAO;IAAE,OAAO;KAAE,GAAG;KAAQ,MAAM;KAAM;IAAE,QAAQ;IAAM;IACzD;AACF,SAAO,EAAE,MAAM,MAAM;;CAEvB,MAAM,uBAAkE;AAEtE,MAAI,EADa,MAAM,KAAK,UAAU,EACxB,sBAAsB,CAAC,KAAK,IAAI,MAAO,QAAO;GAAE,MAAM;GAAG,QAAQ;GAAG;EAClF,MAAM,0BAAS,IAAI,KAAK,KAAK,KAAK,CAAC,SAAS,GAAG,KAAK,IAAO,EAAC,aAAa;EACzE,MAAM,YAAY,MAAM,KAAK,OAAO,cAAc,MAAM;GACtD,OAAO;IAAE,aAAa;IAAW,WAAW,EAAE,KAAK,QAAQ;IAAE;GAC7D,OAAO;GACR,CAAC;EACF,IAAI,SAAS;AACb,OAAK,MAAM,QAAQ,UAAU,MAK3B,MAJkB,MAAM,KAAK,OAAO,cAAc,SAAS,KAAK,IAAI;GAClE,OAAO;IAAE,aAAa;IAAW,WAAW,EAAE,KAAK,QAAQ;IAAE;GAC7D,KAAK,EAAE,aAAa,UAAU;GAC/B,CAAC,EACY,QAAS;EAEzB,MAAM,OAAO,MAAM,KAAK,OAAO,cAAc,MAAM;GACjD,OAAO,EAAE,aAAa,WAAW;GACjC,OAAO;GACR,CAAC;EACF,IAAI,OAAO;AACX,OAAK,MAAM,QAAQ,KAAK,OAAO;AAC7B,OAAI,KAAK,KAAK,iBAAiB,EAAG;GAClC,MAAM,YAAY,KAAK,MAAM;GAC7B,MAAM,gBAAgB,KAAK,KAAK,gBAAgB;AAMhD,OAAI,EALU,MAAM,KAAK,OAAO,cAAc,SAAS,KAAK,IAAI;IAC9D,OAAO;KAAE,aAAa;KAAW,eAAe,KAAK,KAAK;KAAe;IACzE,KAAK;KAAE,aAAa;KAAW;KAAW;IAC1C,OAAO,EAAE,eAAe,EAAE,KAAK,GAAG,EAAE;IACrC,CAAC,EACS,QAAS;AACpB,OAAI;AACF,SAAK,MAAM,KAAK,OAAO,YAAY,IAAI,KAAK,KAAK,OAAO,GAAG,uBAAuB,OAAO;AACvF,WAAM,KAAK,OAAO,cAAc,SAAS,KAAK,IAAI;MAChD,OAAO;OAAE,aAAa;OAAW;OAAe;OAAW;MAC3D,KAAK,EAAE,aAAa,YAAY;MACjC,CAAC;AACF;;AAEF,UAAM,KAAK,IAAI,MAAM,KAAK;KACxB,IAAI,KAAK,KAAK;KACd,SAAS,KAAK,KAAK;KACnB,MAAM,KAAK,KAAK;KACjB,CAAC;AACF,UAAM,KAAK,OAAO,cAAc,SAAS,KAAK,IAAI;KAChD,OAAO;MAAE,aAAa;MAAW;MAAe;MAAW;KAC3D,KAAK,EAAE,aAAa,QAAQ;KAC7B,CAAC;AACF;WACM;AACN,UAAM,KAAK,OAAO,cAAc,SAAS,KAAK,IAAI;KAChD,OAAO;MAAE,aAAa;MAAW;MAAe;MAAW;KAC3D,KAAK,EAAE,aAAa,UAAU;KAC/B,CAAC;AACF;;;AAGJ,SAAO;GAAE;GAAM;GAAQ;;CAEzB,MAAM,YAAY,OAA0B,UAAkB,QAAiB;EAC7E,MAAM,OAAO,MAAM,KAAK,YAAY,OAAO;GAAE;GAAU;GAAQ,OAAO;GAAI,CAAC;AAC3E,SAAO;GACL,GAAG;GACH,OAAO,KAAK,MAAM,SAAS,eACzB,OAAO,QAAQ,WAAW,SAAS,CAAC,SAAS,CAAC,UAAU,cACtD,SAAS,YAAY,KAAK,gBAAgB;IACxC,GAAG;IACH,QAAQ,WAAW;IACnB,aAAa,WAAW;IACxB;IACA;IACD,EAAE,CACJ,CACF;GACF;;CAEH,MAAM,aAAa,OAA0B,IAAY;EACvD,MAAM,EAAE,WAAW,MAAM,KAAK,eAAe,OAAO,GAAG;AAEvD,SAAO;GAAE,QAAQ;GAA2B,SAAS;GAAG,QAD3C,iBAAiB,MAAM;IAAE,GAAG;IAAQ,QAAQ;IAAS,CAAC;GACG;;CAExE,MAAM,aAAa,OAA0B,OAAgB;EAC3D,MAAM,SAAS,MACb,EAAE,OAAO;GACP,QAAQ,EAAE,QAAQ,0BAA0B;GAC5C,SAAS,EAAE,QAAQ,EAAE;GACrB,QAAQ,iBAAiB,MAAM;GAChC,CAAC,EACF,MACD;AACD,SAAO,KAAK,WAAW,OAAO;GAC5B,MAAM;IAAE,GAAG,OAAO;IAAQ,QAAQ;IAAS;GAC3C,kBAAkB;GACnB,CAAC;;CAEJ,MAAM,aAAa,OAA0B,IAAY,kBAA0B;EACjF,MAAM,SAAS,MAAM,KAAK,OAAO,QAAQ,aAAa,GAAG;AACzD,YAAU,QAAQ,aAAa,qBAAqB,IAAI;EACxD,MAAM,UAAU,KAAK,WAAW,OAAO,OAAO,MAAM;AACpD,YACE,OAAO,MAAM,WAAW,WACrB,MAAM,KAAK,OAAO,YAAY,MAAM,EAAE,UAAU,IAAI,CAAC,KAAM,GAC9D,iBACA,0EACA,IACD;AAED,aADgB,MAAM,KAAK,OAAO,QAAQ,iBAAiB,IAAI,iBAAiB,EAC9D,SAAS,YAAY,+CAA+C,IAAI;AAC1F,QAAM,KAAK,MAAM,SAAS,iBAAiB,GAAG;AAC9C,SAAO,EAAE,SAAS,MAAM;;CAE1B,MAAM,kBAAkB,OAA0B,IAAY;AAC5D,YACEA,OAAK,MAAM,CAAC,QAAQ,IACpB,aACA,iDACA,IACD;AAKD,aAJe,MAAM,KAAK,OAAO,cAAc,SAAS,IAAI;GAC1D,OAAO;IAAE,aAAa;IAAU,eAAe,EAAE,IAAI,GAAG;IAAE;GAC1D,KAAK;IAAE,aAAa;IAAW,WAAW;IAAM;GACjD,CAAC,EACe,SAAS,qBAAqB,wCAAwC,IAAI;AAC3F,QAAM,KAAK,MAAM,OAAQ,sBAAsB,GAAG;AAClD,SAAO,EAAE,QAAQ,MAAM;;CAEzB,MAAM,kBACJ,OACA,UAAqF,EAAE,EACvF;AACA,YAAUA,OAAK,MAAM,CAAC,QAAQ,IAAI,aAAa,kCAAkC,IAAI;AACrF,SAAO,UAAU,KAAK,OAAO,eAAe;GAC1C,OAAO,QAAQ,SAAS,EAAE,aAAa,QAAQ,QAAQ,GAAG,EAAE;GAC5D,SAAS,EAAE,WAAW,QAAQ;GAC9B,OAAO,QAAQ,SAAS;GACxB,QAAQ,QAAQ;GACjB,CAAC;;CAEJ,MAAM,UAAU,OAA0B,UAA+C,EAAE,EAAE;AAC3F,YAAUA,OAAK,MAAM,CAAC,QAAQ,IAAI,aAAa,kCAAkC,IAAI;AACrF,SAAO,UAAU,KAAK,OAAO,OAAO;GAClC,SAAS,EAAE,WAAW,QAAQ;GAC9B,OAAO,QAAQ,SAAS;GACxB,QAAQ,QAAQ;GACjB,CAAC;;CAEJ,MAAM,YAA6B;EACjC,MAAM,WAAW,MAAM,KAAK,UAAU;AACtC,MAAI,CAAC,SAAS,aAAc,QAAO;EACnC,MAAM,0BAAS,IAAI,KACjB,KAAK,KAAK,CAAC,SAAS,GAAG,SAAS,eAAe,MAChD,EAAC,aAAa;EACf,MAAM,SAAS,MAAM,KAAK,IAAI,GAAG,IAAY,mBAAmB;EAChE,MAAM,OAAO,MAAM,KAAK,OAAO,YAAY,MAAM;GAC/C,OAAO,EAAE,QAAQ,UAAU;GAC3B,OAAO;GACP,QAAQ,UAAU;GACnB,CAAC;EACF,MAAM,OAAO,KAAK,MAAM,KAAK,KAAK,CAAC,SAAS,IAAI,IAAI,OAAY;EAChE,IAAI,SAAS;AACb,OAAK,MAAM,QAAQ,KAAK,OAAO;AAC7B,OACE,KAAK,KAAK,iBAAiB,UAC3B,gBAAiB,MAAM,KAAK,iBAAiB,KAAK,KAAK,EAAI,KAAK,KAAK,CAAC,KAAK,UAE3E;AACF,SAAM,KAAK,OACT,KAAK,MACL,YAAY,QACZ,0BACA,8CACD;AACD;;AAEF,MAAI,KAAK,WAAW,KAAK,OAAQ,OAAM,KAAK,IAAI,GAAG,IAAI,oBAAoB,KAAK,OAAO;MAClF,OAAM,KAAK,IAAI,GAAG,OAAO,mBAAmB;AACjD,SAAO;;;;;;ACviEX,MAAa,eAAe,WAAW,OAAO,EAC5C,SAAS,EAAE,KAAK;CACd;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC,EACH,CAAC;AAEF,SAAS,KAAK,OAAiC;AAC7C,WAAU,OAAO,gBAAgB,wBAAwB,IAAI;AAC7D,QAAO;;AAET,SAAS,MAAM,OAAiC;CAC9C,MAAM,UAAU,KAAK,MAAM;AAC3B,WAAU,QAAQ,QAAQ,IAAI,aAAa,kCAAkC,IAAI;AACjF,QAAO;;AAET,SAAS,SAAS,OAAgB;CAChC,MAAM,UAAU,MAAM,YAAY,MAAM;AAExC,QAAO;EAAE,GAAG;EAAS,OAAO,KAAK,IAAI,QAAQ,OAAO,EAAE;EAAE;;;AAI1D,IAAa,mBAAb,MAA8B;CAC5B,YAAY,AAAS,SAAqB;EAArB;;CACrB,IAAI,SAAS;AACX,SAAO,KAAK,QAAQ;;CAGtB,MAAM,YAAY,OAA0B;EAC1C,MAAM,SAAS,MAAM,KAAK,OAAO,YAAY,aAAa,KAAK,MAAM,CAAC,GAAG;AACzE,SAAO;GACL,OAAO,kBAAkB,MAAM,QAAQ,SAAS,EAAE,CAAC;GACnD,UAAU,QAAQ,YAAY;GAC/B;;CAEH,MAAM,gBAAgB,OAA0B,OAAgB;EAC9D,MAAM,UAAU,KAAK,MAAM;EAC3B,MAAM,SAAS,MACb,EAAE,OAAO;GAAE,MAAM;GAAmB,kBAAkB,EAAE,QAAQ,CAAC,IAAI,IAAI,CAAC,UAAU;GAAE,CAAC,EACvF,MACD;EACD,MAAM,QAAQ,MAAM,KAAK,OAAO,YAAY,cAC1C,QAAQ,IACR,OAAO,kBACP,OAAO,KACR;AACD,YACE,MAAM,SACN,YACA,6DACA,IACD;AACD,SAAO;GAAE,OAAO,OAAO;GAAM,UAAU,MAAM;GAAU;;CAEzD,MAAM,WAAW,OAA0B,OAAgB;EACzD,MAAM,UAAU,KAAK,MAAM;EAC3B,MAAM,SAAS,MAAM,eAAe,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM;AAC1E,MAAI,OAAO,MAET,YADe,MAAM,KAAK,QAAQ,OAAO,OAAO,SAAS,EACxC,WAAW,aAAa,aAAa,qBAAqB,IAAI;AAEjF,QAAM,OAAO,KAAK,OAAO,eAAe,cAAc,OAAO,UAAU,QAAQ,GAAG,SAAS;GACzF,OAAO;IACL,QAAQ,QAAQ;IAChB,UAAU,OAAO;IACjB,OAAO,OAAO;IACd,WAAW,KAAK,QAAQ,MAAM;IAC/B;GACD,QAAQ;GACT,EAAE;AACH,SAAO,EAAE,OAAO,OAAO,OAAO;;CAEhC,MAAM,aAAa,OAA0B,OAAgB;EAC3D,MAAM,UAAU,KAAK,MAAM;EAC3B,MAAM,UAAU,MAAM,YAAY,MAAM;EACxC,MAAM,OAAO,MAAM,UAAU,KAAK,OAAO,eAAe;GACtD,GAAG;GACH,OAAO;IAAE,QAAQ,QAAQ;IAAI,OAAO;IAAM;GAC1C,SAAS,EAAE,WAAW,QAAQ;GAC/B,CAAC;AACF,SAAO;GACL,GAAG;GACH,OAAO,MAAM,QAAQ,IACnB,KAAK,MAAM,IAAI,OAAO,UAAU;IAC9B,UAAU,KAAK;IACf,QAAQ,MAAM,KAAK,KAAK,KAAK,SAAS;IACvC,EAAE,CACJ;GACF;;CAEH,MAAc,KAAK,UAAkB;EACnC,MAAM,SAAS,MAAM,KAAK,OAAO,QAAQ,IAAI,SAAS;AACtD,SAAO,QAAQ,WAAW,cAAc,WAAW,OAAO,GAAG;;CAE/D,MAAM,SAAS,OAA0B,OAAgB;EACvD,MAAM,UAAU,MAAM,MAAM;EAC5B,MAAM,SAAS,MAAM,gBAAgB,MAAM;EAC3C,MAAM,KAAK,OAAO,MAAM,OAAO,YAAY;EAC3C,MAAM,WAAW,MAAM,KAAK,OAAO,eAAe,aAAa,GAAG;AAClE,YAAU,CAAC,OAAO,MAAM,UAAU,aAAa,4BAA4B,IAAI;AAC/E,aACG,UAAU,YAAY,UAAU,OAAO,kBACxC,YACA,4CACA,IACD;AACD,OAAK,MAAM,YAAY,OAAO,KAAK,WAAW;GAC5C,MAAM,SAAS,MAAM,KAAK,QAAQ,OAAO,SAAS;AAClD,aACE,OAAO,KAAK,WAAW,eAAe,OAAO,WAAW,aACxD,oBACA,kDACD;;EAEH,MAAM,OAAqB;GACzB,GAAG,OAAO;GACV;GACA,WAAW,UAAU,MAAM,aAAa,KAAK,QAAQ,MAAM;GAC3D,WAAW,KAAK,QAAQ,MAAM;GAC/B;AACD,aAAW,KAAK;EAChB,MAAM,QAAQ,MAAM,KAAK,OAAO,eAAe,cAAc,IAAI,OAAO,kBAAkB,KAAK;AAC/F,YAAU,MAAM,SAAS,YAAY,4CAA4C,IAAI;AACrF,QAAM,KAAK,QAAQ,MAAM,SAAS,aAAa,GAAG;AAClD,SAAO;GAAE;GAAM,UAAU,MAAM;GAAU;;CAE3C,MAAM,WAAW,OAA0B,OAAgB;AACzD,QAAM,MAAM;EACZ,MAAM,OAAO,MAAM,KAAK,OAAO,eAAe,MAAM;GAClD,GAAG,MAAM,YAAY,MAAM;GAC3B,SAAS,EAAE,WAAW,QAAQ;GAC/B,CAAC;EACF,MAAM,WAAW,MAAM,QAAQ,IAC7B,KAAK,MAAM,KAAK,SAAS,KAAK,OAAO,eAAe,aAAa,KAAK,GAAG,CAAC,CAC3E;AACD,SAAO;GACL,GAAG;GACH,OAAO,SAAS,SAAS,SAAU,OAAO,CAAC;IAAE,GAAG,KAAK;IAAO,UAAU,KAAK;IAAU,CAAC,GAAG,EAAE,CAAE;GAC9F;;CAEH,MAAM,MAAM,OAAgB;EAC1B,MAAM,OAAO,MAAM,UAAU,KAAK,OAAO,gBAAgB;GACvD,GAAG,SAAS,MAAM;GAClB,OAAO,EAAE,QAAQ,aAAa;GAC9B,SAAS,EAAE,WAAW,QAAQ;GAC/B,CAAC;EACF,MAAM,QAAsB,MAAM,QAAQ,IACxC,KAAK,MAAM,IAAI,OAAO,UAAU;GAC9B,IAAI,KAAK;GACT,OAAO,KAAK;GACZ,aAAa,KAAK;GAClB,SAAS,MAAM,QAAQ,IAAI,KAAK,UAAU,KAAK,OAAO,KAAK,KAAK,GAAG,CAAC,CAAC;GACtE,EAAE,CACJ;AACD,SAAO;GAAE,GAAG;GAAM;GAAO;;CAE3B,MAAM,SAAS,OAA0B,OAAgB;EACvD,MAAM,UAAU,KAAK,MAAM;EAC3B,MAAM,EAAE,WAAW,MAAM,EAAE,OAAO,EAAE,QAAQ,UAAU,CAAC,EAAE,MAAM;EAC/D,MAAM,KAAK,cAAc,QAAQ,QAAQ,GAAG;AAE5C,MADiB,MAAM,KAAK,OAAO,iBAAiB,IAAI,GAAG,CAC7C,QAAO,EAAE,QAAQ,MAAM;EACrC,MAAM,OAAO,MAAM,KAAK,OAAO,eAAe,IAAI,OAAO;AACzD,YAAU,MAAM,WAAW,aAAa,aAAa,4BAA4B,IAAI;AAErF,QAAM,QAAQ,KAAK,OAAO,kBAAkB,IAAI;GAC9C;GACA,QAAQ,QAAQ;GAChB,OAAO,KAAK;GACZ,WAAW,KAAK;GAChB,UAAU,KAAK,QAAQ,MAAM;GAC9B,CAAC;AACF,SAAO,EAAE,QAAQ,MAAM;;CAEzB,MAAM,aAAa,OAA0B,OAAgB;EAC3D,MAAM,UAAU,KAAK,MAAM;EAC3B,MAAM,OAAO,MAAM,UAAU,KAAK,OAAO,kBAAkB;GACzD,GAAG,SAAS,MAAM;GAClB,OAAO,EAAE,QAAQ,QAAQ,IAAI;GAC7B,SAAS,EAAE,UAAU,QAAQ;GAC9B,CAAC;EACF,MAAM,QAAuB,MAAM,QAAQ,IACzC,KAAK,MAAM,IAAI,OAAO,SAAS;GAC7B,MAAM,UAAU,MAAM,QAAQ,IAC5B,KAAK,UAAU,IAAI,OAAO,aAAa;IACrC,MAAM,aAAa,MAAM,KAAK,OAAO,YAAY,IAC/C,cAAc,UAAU,QAAQ,GAAG,CACpC;IACD,MAAM,SAAS,MAAM,KAAK,OAAO,QAAQ,IAAI,SAAS;AACtD,WAAO;KACL,QACE,UAAU,aACN,WAAW,MAAM,KAAK,QAAQ,SAAS,SAAS,QAAQ,WAAW,CAAC,GACpE,MAAM,KAAK,KAAK,SAAS;KAC/B,WAAW,QAAQ,YAAY,YAAY;KAC5C;KACD,CACH;AACD,UAAO;IACL,QAAQ,KAAK;IACb,OAAO,KAAK;IACZ,UAAU,KAAK;IACf,OAAO,QAAQ;IACf,WAAW,QAAQ,QAAQ,MAAM,EAAE,UAAU,CAAC;IAC9C;IACD;IACD,CACH;AACD,SAAO;GAAE,GAAG;GAAM;GAAO;;CAE3B,MAAM,SAAS,OAA0B,OAAgB;EACvD,MAAM,UAAU,KAAK,MAAM;EAC3B,MAAM,SAAS,MAAM,gBAAgB,MAAM;EAC3C,MAAM,SAAS,MAAM,KAAK,QAAQ,OAAO,OAAO,SAAS;AACzD,SAAO,OAAO,KAAK,OAAO,aAAa,cAAc,OAAO,IAAI,QAAQ,GAAG,EAAE,OAAO,WAAW;GAC7F,MAAM,SAAS,MAAM,KAAK,QAAQ,aAAa,QAAQ,OAAO,UAAU,OAAO;AAC/E,aACE,CAAC,SAAS,QAAQ,CAAC,SAAS,OAAO,KAAK,EACxC,oBACA,yDACD;GACD,MAAM,OAAO,gBAAgB,OAAQ;GACrC,MAAM,WAAW,KAAK,SAAS,OAAO,OAAO,eAAe;AAC5D,cACG,SAAS,UAAU,YAAY,OAAO,OAAO,kBAC9C,YACA,6DACA,IACD;GACD,MAAM,WAAW;IACf,iBAAiB,OAAO;IACxB,cAAc,OAAO;IACrB,UAAU,OAAO,mBAAmB;IACpC,WAAW,KAAK,QAAQ,MAAM;IAC/B;AACD,YAAS,WAAW;AACpB,QAAK,SAAS,OAAO,MAAM;AAC3B,QAAK,iBAAiB,KAAK,QAAQ,MAAM;AAEzC,UAAO;IAAE,OAAO;IAAM,QAAQ;IAAU;IACxC;;CAEJ,MAAM,WAAW,OAA0B,OAAgB;EACzD,MAAM,UAAU,KAAK,MAAM;EAC3B,MAAM,SAAS,MAAM,cAAc,MAAM;EACzC,MAAM,EAAE,SAAS,WAAW;EAI5B,MAAM,UAAU;GAAE,OAFhB,YAAY,gBAAgB,EAAE,UAAU,QAAQ,IAAI,GAAG,EAAE,QAAQ,QAAQ,IAAI;GAEtD;GAAQ,OAAO,KAAK,IAAI,OAAO,OAAO,EAAE;GAAE;EACnE,IAAI;AACJ,MAAI,YAAY,eAAe;GAC7B,MAAM,MAAM,MAAM,UAAU,KAAK,OAAO,aAAa,QAAQ;AAC7D,UAAO;IACL,GAAG;IACH,OAAO,MAAM,QAAQ,IACnB,IAAI,MAAM,KAAK,eAAe,KAAK,QAAQ,eAAe,WAAW,CAAC,CACvE;IACF;SACI;GAEL,MAAM,MAAM,MAAM,KAAK,OAAO,SAAS,MAAM,QAAQ;AACrD,UAAO;IAAE,GAAG;IAAK,OAAO,IAAI,MAAM,KAAK,SAAS,KAAK,KAAK;IAAE;;AAE9D,SAAO;GACL,QAAQ;GACR,SAAS;GACT,YAAY,KAAK,QAAQ,MAAM;GAC/B;GACA,SAAS;IAAE,IAAI,QAAQ;IAAI,MAAM,QAAQ;IAAM,OAAO,QAAQ;IAAO;GACrE,cAAc,MAAM,KAAK,YAAY,QAAQ,EAAE;GAC/C,GAAG;GACJ;;CAEH,MAAM,OAAO,OAA8C;AACzD,QAAM,MAAM;EACZ,MAAM,CAAC,aAAa,eAAe,cAAc,SAAS,UAAU,aAClE,MAAM,QAAQ,IAAI;GAChB,KAAK,QAAQ,IAAI,GAAG,IAA8B,oBAAoB;GACtE,KAAK,OAAO,cAAc,MAAM,EAAE,aAAa,WAAW,CAAC;GAC3D,KAAK,OAAO,cAAc,MAAM,EAAE,aAAa,UAAU,CAAC;GAC1D,KAAK,OAAO,cAAc,MAAM;IAC9B,OAAO,EAAE,aAAa,WAAW;IACjC,SAAS,EAAE,WAAW,OAAO;IAC7B,OAAO;IACR,CAAC;GACF,KAAK,QAAQ,UAAU;GACvB,KAAK,QAAQ,gBAAgB,MAAM;GACpC,CAAC;AACJ,SAAO;GACL,SAAS;GACT;GACA,kBACE,CAAC,aAAa,eACd,KAAK,QAAQ,KAAK,CAAC,SAAS,GAAG,KAAK,MAAM,YAAY,YAAY,GAAG,KAAK;GAC5E;GACA;GACA,iBAAiB,QAAQ,MAAM,IAAI,KAAK,aAAa;GACrD,cAAc,SAAS;GACvB;GACD;;;;;;ACtSL,MAAM,aAAa,WAAW,OAAO;CACnC,UAAU,SAAS,UAAU;CAC7B,QAAQ,EACL,KAAK;EAAC;EAAW;EAAU;EAAa;EAAY;EAAa;EAAY,CAAC,CAC9E,UAAU;CACd,CAAC;AACF,MAAM,sBAAsB,WAAW,OAAO;CAC5C,UAAU,SAAS,UAAU;CAC7B,QAAQ,EAAE,KAAK;EAAC;EAAW;EAAa;EAAS,CAAC,CAAC,UAAU;CAC9D,CAAC;AACF,MAAM,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC;AACjE,MAAM,WAAW,EAAE,OAAO,EAAE,IAAI,WAAW,CAAC;AAC5C,MAAM,sBAAsB,EAAE,OAAO;CACnC,IAAI;CACJ,kBAAkB,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,IAAI;CAC7C,CAAC;AAEF,SAAS,IACP,SACA,cAAc,OACd,aAAwC,gBAC3B;AACb,QAAO,kBAAkB;EACvB,SAAS,CAAC,MAAM;EAChB,QAAQ;EACR;EACA,SAAS,EAAE,MAAM,QAAQ;EACzB,SAAS,OAAO,QAAQ,QAAQ,KAAK,IAAI,WAAW,IAAI,CAAC;EAC1D,CAAC;;AAEJ,SAAS,KACP,SACA,aAAwC,gBAC3B;AACb,QAAO,kBAAkB;EACvB,SAAS,CAAC,OAAO;EACjB;EACA,SAAS;GAAE,MAAM;GAAQ,UAAU;GAAW;EAC9C,SAAS,OAAO,QAAQ,QAAQ,KAAK,IAAI,WAAW,IAAI,CAAC;EAC1D,CAAC;;AAGJ,SAAgB,aAAa,WAAuB,EAAE,EAAE;CACtD,MAAM,UAAU,oBAAoB,SAAS;AAC7C,QAAO,aAAa;EAClB,IAAI;EACJ,SAAS;EACT,cAAc;GAAC;GAAe;GAAgB;GAAc;GAAa;EACzE;EACA,OAAO;GACL,OAAO;GACP,OAAO;GACP,SAAS,CAAC;IAAE,IAAI;IAAY,OAAO;IAAY,MAAM;IAAQ,CAAC;GAC/D;EACD,OAAO;GACL,kBAAkB,OAAO,QAAQ,QAAQ;AACvC,UAAM,IAAI,GAAG,cAAc,iBAAiB,MAAM,EAAE;;GAEtD,mBAAmB,OAAO,QAAQ,QAAQ;AACxC,UAAM,IAAI,MAAM,SAAS,eAAe,EAAE,UAAU,gBAAgB,CAAC;;GAEvE,qBAAqB,OAAO,QAAQ,QAAQ;AAC1C,UAAM,IAAI,MAAM,OAAO,cAAc;;GAEvC,MAAM,OAAO,OAAO,QAAQ;AAC1B,QAAI,MAAM,SAAS,eAAe;KAChC,MAAM,UAAU,IAAI,WAAW,IAAI;KACnC,MAAM,YAAY,QAAQ,MAAM;AAChC,SAAI;AACF,YAAM,QAAQ,WAAW;AACzB,YAAM,QAAQ,sBAAsB;AACpC,YAAM,IAAI,GAAG,IAAI,qBAAqB;OACpC;OACA,aAAa,QAAQ,MAAM;OAC3B,QAAQ;OACT,CAAC;cACK,OAAO;AACd,YAAM,IAAI,GAAG,IAAI,qBAAqB;OAAE;OAAW,aAAa;OAAM,QAAQ;OAAM,CAAC;AACrF,YAAM;;;;GAIb;EACD,QAAQ;GACN,OAAO,IAAI,OAAO,KAAK,YAAY,IAAI,iBAAiB,QAAQ,CAAC,MAAM,IAAI,MAAM,EAAE,KAAK;GACxF,iBAAiB,IAAI,OAAO,KAAK,YAC/B,IAAI,iBAAiB,QAAQ,CAAC,aAAa,IAAI,MAAM,IAAI,MAAM,CAChE;GACD,qBAAqB,KAAK,OAAO,KAAK,YACpC,IAAI,iBAAiB,QAAQ,CAAC,SAAS,IAAI,MAAM,IAAI,MAAM,CAC5D;GACD,iBAAiB,IAAI,OAAO,KAAK,YAC/B,IAAI,iBAAiB,QAAQ,CAAC,aAAa,IAAI,MAAM,IAAI,MAAM,CAChE;GACD,sBAAsB,KAAK,OAAO,KAAK,YACrC,IAAI,iBAAiB,QAAQ,CAAC,WAAW,IAAI,MAAM,IAAI,MAAM,CAC9D;GACD,uBAAuB,IAAI,OAAO,KAAK,YACrC,IAAI,iBAAiB,QAAQ,CAAC,YAAY,IAAI,KAAK,CACpD;GACD,4BAA4B,KAAK,OAAO,KAAK,YAC3C,IAAI,iBAAiB,QAAQ,CAAC,gBAAgB,IAAI,MAAM,IAAI,MAAM,CACnE;GACD,oBAAoB,KAAK,OAAO,KAAK,YACnC,IAAI,iBAAiB,QAAQ,CAAC,SAAS,IAAI,MAAM,IAAI,MAAM,CAC5D;GACD,kBAAkB,IAAI,OAAO,KAAK,YAChC,IAAI,iBAAiB,QAAQ,CAAC,WAAW,IAAI,MAAM,IAAI,MAAM,CAC9D;GACD,eAAe,IACb,OAAO,KAAK,YAAY,IAAI,iBAAiB,QAAQ,CAAC,WAAW,IAAI,MAAM,IAAI,MAAM,EACrF,OACA,iBACD;GACD,mBAAmB,KACjB,OAAO,KAAK,YAAY,IAAI,iBAAiB,QAAQ,CAAC,SAAS,IAAI,MAAM,IAAI,MAAM,EACnF,iBACD;GACD,gBAAgB,IACd,OAAO,KAAK,YAAY,IAAI,iBAAiB,QAAQ,CAAC,OAAO,IAAI,KAAK,EACtE,OACA,iBACD;GACD,SAAS,IAAI,OAAO,MAAM,YAAY;IACpC,MAAM,WAAW,MAAM,QAAQ,UAAU;AACzC,WAAO;KACL,OAAO,SAAS;KAChB,aAAa,SAAS;KACtB,UAAU,QAAQ;KACnB;MACA,KAAK;GACR,SAAS,IAAI,OAAO,KAAK,YAAY,QAAQ,QAAQ,IAAI,MAAM,EAAE,KAAK;GACtE,QAAQ,IACN,OAAO,KAAK,YAAY,QAAQ,aAAa,MAAM,YAAY,IAAI,MAAM,CAAC,KAAK,EAC/E,KACD;GACD,QAAQ,IACN,OAAO,KAAK,YACV,QAAQ,aAAa,MAAM,EAAE,OAAO,EAAE,IAAI,UAAU,CAAC,EAAE,IAAI,MAAM,CAAC,GAAG,EACvE,KACD;GACD,SAAS,IAAI,OAAO,KAAK,YAAY;IACnC,MAAM,QAAQ,MAAM,gBAAgB,IAAI,MAAM;AAC9C,WAAO,QAAQ,QAAQ,MAAM,UAAU,MAAM,SAAS;MACrD,KAAK;GACR,SAAS,IAAI,OAAO,KAAK,YAAY;IACnC,MAAM,QAAQ,MAAM,eAAe,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,UAAU,EAAE,CAAC,EAAE,IAAI,MAAM;AACxF,WAAO,QAAQ,cAAc,MAAM,UAAU,MAAM,OAAO;MACzD,KAAK;GACR,uBAAuB,IACrB,OAAO,KAAK,YACV,QAAQ,kBAAkB,MAAM,EAAE,OAAO,EAAE,IAAI,UAAU,CAAC,EAAE,IAAI,MAAM,CAAC,GAAG,EAC5E,KACD;GACD,qBAAqB,IAAI,OAAO,KAAK,YACnC,QAAQ,UAAU,IAAI,MAAM,MAAM,YAAY,IAAI,MAAM,CAAC,CAC1D;GACD,kBAAkB,IAAI,OAAO,KAAK,YAChC,QAAQ,cAAc,IAAI,MAAM,MAAM,gBAAgB,IAAI,MAAM,CAAC,SAAS,CAC3E;GACD,0BAA0B,IAAI,OAAO,KAAK,YACxC,QAAQ,oBAAoB,IAAI,MAAM,MAAM,YAAY,IAAI,MAAM,CAAC,KAAK,CACzE;GACD,kBAAkB,IAAI,OAAO,KAAK,YAAY;IAC5C,MAAM,QAAQ,MAAM,gBAAgB,IAAI,MAAM;AAC9C,WAAO,QAAQ,cAAc,IAAI,MAAM,MAAM,UAAU,MAAM,SAAS;KACtE;GACF,kBAAkB,KAAK,OAAO,KAAK,YAAY;IAC7C,MAAM,QAAQ,MAAM,yBAAyB,IAAI,MAAM;AACvD,WAAO,QAAQ,OAAO,IAAI,MAAM,MAAM,UAAU,EAAE,UAAU,MAAM,UAAU,CAAC;KAC7E;GACF,0BAA0B,KAAK,OAAO,KAAK,YACzC,QAAQ,cACN,IAAI,MACJ,MAAM,EAAE,OAAO,EAAE,UAAU,UAAU,CAAC,EAAE,IAAI,MAAM,CAAC,SACpD,CACF;GACD,kBAAkB,KAAK,OAAO,KAAK,YACjC,QAAQ,iBAAiB,IAAI,MAAM,MAAM,gBAAgB,IAAI,MAAM,CAAC,SAAS,CAC9E;GACD,iBAAiB,KAAK,OAAO,KAAK,YAAY;IAC5C,MAAM,QAAQ,MAAM,gBAAgB,IAAI,MAAM;AAC9C,WAAO,QAAQ,YAAY,IAAI,MAAM,MAAM,UAAU,MAAM,SAAS;KACpE;GACF,oBAAoB,KAAK,OAAO,KAAK,YAAY;IAC/C,MAAM,QAAQ,MAAM,gBAAgB,IAAI,MAAM;AAC9C,WAAO,QAAQ,eAAe,IAAI,MAAM,MAAM,UAAU,MAAM,SAAS;KACvE;GACF,sBAAsB,KAAK,OAAO,KAAK,YAAY;IACjD,MAAM,QAAQ,MAAM,gBAAgB,IAAI,MAAM;AAC9C,WAAO,QAAQ,UAAU,IAAI,MAAM,MAAM,UAAU,MAAM,SAAS;KAClE;GACF,uBAAuB,KAAK,OAAO,KAAK,YAAY,QAAQ,WAAW,IAAI,MAAM,IAAI,MAAM,CAAC;GAC5F,sBAAsB,KAAK,OAAO,KAAK,YACrC,QAAQ,cAAc,IAAI,MAAM,IAAI,MAAM,CAC3C;GACD,sBAAsB,KAAK,OAAO,KAAK,YACrC,QAAQ,iBAAiB,IAAI,MAAM,IAAI,MAAM,CAC9C;GACD,gBAAgB,KAAK,OAAO,KAAK,YAAY,QAAQ,SAAS,IAAI,MAAM,IAAI,MAAM,CAAC;GACnF,uBAAuB,IAAI,OAAO,KAAK,YAAY;IACjD,MAAM,QAAQ,MAAM,eAAe,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,UAAU,EAAE,CAAC,EAAE,IAAI,MAAM;AACxF,WAAO,QAAQ,YAAY,IAAI,MAAM,MAAM,UAAU,MAAM,UAAU,MAAM,OAAO;KAClF;GACF,sBAAsB,KAAK,OAAO,KAAK,YACrC,QAAQ,eAAe,IAAI,MAAM,IAAI,MAAM,CAC5C;GACD,kBAAkB,KAAK,OAAO,KAAK,YAAY,QAAQ,WAAW,IAAI,MAAM,IAAI,MAAM,CAAC;GACvF,uBAAuB,IAAI,OAAO,KAAK,YACrC,QAAQ,YAAY,IAAI,MAAM,MAAM,gBAAgB,IAAI,MAAM,CAAC,SAAS,CACzE;GACD,6BAA6B,IAAI,OAAO,KAAK,YAC3C,QAAQ,iBAAiB,IAAI,MAAM,MAAM,EAAE,OAAO,EAAE,IAAI,UAAU,CAAC,EAAE,IAAI,MAAM,CAAC,GAAG,CACpF;GACD,kBAAkB,IAAI,OAAO,KAAK,YAAY;IAC5C,MAAM,QAAQ,MAAM,EAAE,OAAO,EAAE,UAAU,UAAU,CAAC,EAAE,IAAI,MAAM;AAChE,cAAU,IAAI,MAAM,gBAAgB,wBAAwB,IAAI;AAChE,WAAO,QAAQ,OAAO,mBAAmB,IAAI,GAAG,MAAM,SAAS,GAAG,IAAI,KAAK,KAAK;KAChF;GACF,6BAA6B,KAAK,OAAO,KAAK,YAAY;IACxD,MAAM,QAAQ,MAAM,yBAAyB,IAAI,MAAM;AACvD,WAAO,QAAQ,iBAAiB,IAAI,MAAM,MAAM,UAAU,MAAM,OAAO;KACvE;GACF,yBAAyB,IAAI,OAAO,KAAK,YACvC,QAAQ,cAAc,IAAI,MAAM,MAAM,YAAY,IAAI,MAAM,CAAC,OAAO,CACrE;GACD,6BAA6B,KAAK,OAAO,KAAK,YAC5C,QAAQ,iBAAiB,IAAI,MAAM,MAAM,UAAU,IAAI,MAAM,CAAC,GAAG,CAClE;GACD,iBAAiB,IAAI,OAAO,KAAK,YAC/B,QAAQ,aACN,IAAI,MACJ,MACE,WAAW,OAAO,EAAE,QAAQ,EAAE,KAAK;IAAC;IAAS;IAAa;IAAW,CAAC,CAAC,UAAU,EAAE,CAAC,EACpF,IAAI,MACL,CACF,CACF;GACD,gBAAgB,IAAI,OAAO,KAAK,YAC9B,QAAQ,eAAe,IAAI,MAAM,MAAM,EAAE,OAAO,EAAE,IAAI,UAAU,CAAC,EAAE,IAAI,MAAM,CAAC,GAAG,CAClF;GACD,qBAAqB,KAAK,OAAO,KAAK,YAAY,QAAQ,WAAW,IAAI,MAAM,IAAI,MAAM,CAAC;GAC1F,wBAAwB,KAAK,OAAO,KAAK,YAAY;IACnD,MAAM,QAAQ,MAAM,qBAAqB,IAAI,MAAM;AACnD,WAAO,QAAQ,cAAc,IAAI,MAAM,MAAM,IAAI,MAAM,iBAAiB;KACxE;GACF,wBAAwB,KAAK,OAAO,KAAK,YAAY;IACnD,MAAM,QAAQ,MAAM,qBAAqB,IAAI,MAAM;AACnD,WAAO,QAAQ,cAAc,IAAI,MAAM,MAAM,IAAI,MAAM,iBAAiB;KACxE;GACF,wBAAwB,IAAI,OAAO,KAAK,YAAY;IAClD,MAAM,QAAQ,MAAM,EAAE,OAAO;KAAE,IAAI;KAAU,QAAQ,SAAS,UAAU;KAAE,CAAC,EAAE,IAAI,MAAM;AACvF,WAAO,QAAQ,cAAc,IAAI,MAAM,MAAM,IAAI,MAAM,OAAO;KAC9D;GACF,wBAAwB,KAAK,OAAO,KAAK,YAAY;IACnD,MAAM,QAAQ,MAAM,oBAAoB,OAAO,EAAE,YAAY,UAAU,CAAC,EAAE,IAAI,MAAM;AACpF,WAAO,QAAQ,cAAc,IAAI,MAAM,MAAM,IAAI,MAAM,kBAAkB,MAAM,WAAW;KAC1F;GACF,oBAAoB,IAClB,OAAO,KAAK,YAAY,QAAQ,gBAAgB,IAAI,KAAK,EACzD,OACA,iBACD;GACD,wBAAwB,KAAK,OAAO,KAAK,YAAY;IACnD,MAAM,QAAQ,MACZ,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,GAAG,CAAC,QAAQ,GAAG,EAAE,CAAC,EAChE,IAAI,MACL;AACD,WAAO,QAAQ,aAAa,IAAI,MAAM,MAAM,MAAM;MACjD,iBAAiB;GACpB,wBAAwB,IAAI,OAAO,KAAK,YAAY;IAClD,MAAM,QAAQ,MAAM,gBAAgB,IAAI,MAAM;AAC9C,WAAO,QAAQ,oBAAoB,IAAI,MAAM,MAAM,UAAU,MAAM,SAAS;KAC5E;GACF,uBAAuB,KAAK,OAAO,KAAK,YAAY;IAClD,MAAM,QAAQ,MACZ,EAAE,OAAO;KAAE,IAAI;KAAU,kBAAkB,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,IAAI;KAAE,CAAC,EACxE,IAAI,MACL;AACD,WAAO,QAAQ,aAAa,IAAI,MAAM,MAAM,IAAI,MAAM,iBAAiB;KACvE;GACF,uBAAuB,IAAI,OAAO,KAAK,YACrC,QAAQ,aAAa,IAAI,MAAM,MAAM,EAAE,OAAO,EAAE,IAAI,UAAU,CAAC,EAAE,IAAI,MAAM,CAAC,GAAG,CAChF;GACD,uBAAuB,KAAK,OAAO,KAAK,YACtC,QAAQ,aAAa,IAAI,MAAM,IAAI,MAAM,CAC1C;GACD,qBAAqB,IAAI,OAAO,KAAK,YACnC,QAAQ,YAAY,IAAI,MAAM,MAAM,YAAY,IAAI,MAAM,CAAC,CAC5D;GACD,yBAAyB,KACvB,OAAO,KAAK,YAAY,QAAQ,gBAAgB,IAAI,MAAM,IAAI,MAAM,EACpE,iBACD;GACD,qBAAqB,IAAI,OAAO,KAAK,YAAY;IAC/C,MAAM,QAAQ,MAAM,eAAe,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,UAAU,EAAE,CAAC,EAAE,IAAI,MAAM;AACxF,WAAO,QAAQ,YAAY,IAAI,MAAM,MAAM,UAAU,MAAM,OAAO;KAClE;GACF,0BAA0B,KAAK,OAAO,KAAK,YACzC,QAAQ,gBAAgB,IAAI,MAAM,IAAI,MAAM,CAC7C;GACD,qBAAqB,IAAI,OAAO,KAAK,YACnC,QAAQ,eAAe,IAAI,MAAM,eAAe,MAAM,qBAAqB,IAAI,MAAM,CAAC,CACvF;GACD,iBAAiB,IAAI,OAAO,KAAK,YAC/B,QAAQ,eAAe,IAAI,MAAM,WAAW,MAAM,qBAAqB,IAAI,MAAM,CAAC,CACnF;GACD,6BAA6B,KAAK,OAAO,KAAK,YAC5C,QAAQ,SAAS,IAAI,MAAM,eAAe,IAAI,MAAM,CACrD;GACD,yBAAyB,KAAK,OAAO,KAAK,YACxC,QAAQ,SAAS,IAAI,MAAM,WAAW,IAAI,MAAM,CACjD;GACD,4BAA4B,KAAK,OAAO,KAAK,YAAY;IACvD,MAAM,QAAQ,MAAM,yBAAyB,IAAI,MAAM;AACvD,WAAO,QAAQ,kBAAkB,IAAI,MAAM,MAAM,UAAU,MAAM,QAAQ,MAAM,OAAO;KACtF;GACF,gBAAgB,IAAI,OAAO,KAAK,YAC9B,QAAQ,OACN,IAAI,MACJ,MAAM,EAAE,OAAO,EAAE,UAAU,SAAS,UAAU,EAAE,CAAC,EAAE,IAAI,MAAM,CAAC,SAC/D,CACF;GACD,iBAAiB,IACf,OAAO,KAAK,YAAY,QAAQ,WAAW,IAAI,MAAM,MAAM,YAAY,IAAI,MAAM,CAAC,OAAO,EACzF,OACA,iBACD;GACD,qBAAqB,KACnB,OAAO,KAAK,YAAY,QAAQ,WAAW,IAAI,MAAM,IAAI,MAAM,EAC/D,iBACD;GACD,uBAAuB,KAAK,OAAO,KAAK,YAAY;IAClD,MAAM,QAAQ,MACZ,EAAE,OAAO;KACP,IAAI;KACJ,kBAAkB,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,IAAI;KAC5C,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,IAAK,CAAC,QAAQ,EAAE;KACrD,CAAC,EACF,IAAI,MACL;AACD,WAAO,QAAQ,YAAY,IAAI,MAAM,MAAM,IAAI,MAAM,kBAAkB,MAAM,OAAO;MACnF,iBAAiB;GACpB,4BAA4B,IAC1B,OAAO,KAAK,YACV,QAAQ,kBACN,IAAI,MACJ,MACE,WAAW,OAAO;IAChB,UAAU,SAAS,UAAU;IAC7B,QAAQ,EACL,KAAK;KAAC;KAAW;KAAU;KAAY;KAAa;KAAY,CAAC,CACjE,UAAU;IACd,CAAC,EACF,IAAI,MACL,CACF,EACH,OACA,iBACD;GACD,yBAAyB,KAAK,OAAO,KAAK,YAAY;IACpD,MAAM,QAAQ,MACZ,EAAE,OAAO;KACP,UAAU;KACV,QAAQ;KACR,QAAQ,EAAE,KAAK;MAAC;MAAU;MAAY;MAAa;MAAY,CAAC;KAChE,QAAQ,EAAE,QAAQ,CAAC,IAAI,IAAK,CAAC,QAAQ,GAAG;KACzC,CAAC,EACF,IAAI,MACL;AACD,WAAO,QAAQ,aACb,IAAI,MACJ,MAAM,UACN,MAAM,QACN,MAAM,QACN,MAAM,OACP;MACA,iBAAiB;GACpB,kBAAkB,IAAI,OAAO,MAAM,YAAY,QAAQ,UAAU,EAAE,OAAO,iBAAiB;GAC3F,uBAAuB,KACrB,OAAO,KAAK,YAAY,QAAQ,aAAa,IAAI,MAAM,IAAI,MAAM,EACjE,iBACD;GACD,4BAA4B,KAC1B,OAAO,KAAK,YAAY,QAAQ,kBAAkB,IAAI,MAAM,MAAM,UAAU,IAAI,MAAM,CAAC,GAAG,EAC1F,iBACD;GACD,uBAAuB,IACrB,OAAO,KAAK,YACV,QAAQ,kBACN,IAAI,MACJ,MACE,WAAW,OAAO,EAChB,QAAQ,EAAE,KAAK;IAAC;IAAW;IAAW;IAAQ;IAAY;IAAS,CAAC,CAAC,UAAU,EAChF,CAAC,EACF,IAAI,MACL,CACF,EACH,OACA,iBACD;GACD,eAAe,IACb,OAAO,KAAK,YAAY,QAAQ,UAAU,IAAI,MAAM,MAAM,YAAY,IAAI,MAAM,CAAC,EACjF,OACA,iBACD;GACD,eAAe,IACb,OAAO,QAAQ;AACb,cAAU,IAAI,OAAO,qBAAqB,sCAAsC,IAAI;IACpF,MAAM,QAAQ,MAAM,YAAY,IAAI,MAAM;IAC1C,MAAM,OAAO,MAAM,IAAI,MAAM,KAAK;KAAE,OAAO,MAAM;KAAO,QAAQ,MAAM;KAAQ,CAAC;AAC/E,WAAO;KACL,GAAG;KACH,OAAO,KAAK,MAAM,KAAK,WAAW;MAChC,IAAI,MAAM;MACV,MAAM,MAAM;MACZ,OAAO,MAAM;MACb,MAAM,MAAM;MACb,EAAE;KACJ;MAEH,OACA,aACD;GACD,qBAAqB,IAAI,OAAO,QAAQ;AACtC,cACE,IAAI,QAAQ,IAAI,KAAK,QAAQ,IAC7B,aACA,qCACA,IACD;AACD,cAAU,IAAI,OAAO,qBAAqB,sCAAsC,IAAI;IACpF,MAAM,QAAQ,MAAM,YAAY,IAAI,MAAM;IAC1C,MAAM,OAAO,MAAM,IAAI,MAAM,KAAK;KAAE,OAAO,MAAM;KAAO,QAAQ,MAAM;KAAQ,CAAC;AAC/E,WAAO;KACL,GAAG;KACH,OAAO,KAAK,MACT,QAAQ,UAAU,MAAM,QAAQ,GAAG,CACnC,KAAK,WAAW;MAAE,IAAI,MAAM;MAAI,MAAM,MAAM;MAAM,OAAO,MAAM;MAAO,EAAE;KAC5E;KACD;GACF,6BAA6B,IAAI,OAAO,QAAQ;AAC9C,cACE,IAAI,QAAQ,IAAI,KAAK,QAAQ,IAC7B,aACA,qCACA,IACD;AACD,cAAU,IAAI,QAAQ,sBAAsB,sCAAsC,IAAI;AACtF,YAAQ,MAAM,IAAI,OAAO,iBAAiB,EACvC,KAAK,gBAAgB;KACpB,MAAM,WAAW;KACjB,OAAO,WAAW;KAClB,QAAQ,WAAW,OAChB,QAAQ,UAAU;MAAC;MAAgB;MAAQ;MAAS,CAAC,SAAS,MAAM,KAAK,CAAC,CAC1E,KAAK,WAAW;MAAE,MAAM,MAAM;MAAM,OAAO,MAAM;MAAO,MAAM,MAAM;MAAM,EAAE;KAChF,EAAE,CACF,QAAQ,eAAe,WAAW,OAAO,OAAO;KACnD;GACF,yBAAyB,IAAI,OAAO,QAAQ;AAC1C,cACE,IAAI,QAAQ,IAAI,KAAK,QAAQ,IAC7B,aACA,qCACA,IACD;AACD,cACE,IAAI,UAAU,IAAI,SAClB,uBACA,uCACA,IACD;IACD,MAAM,QAAQ,MAAM,WAAW,OAAO,EAAE,YAAY,UAAU,CAAC,EAAE,IAAI,MAAM;IAC3E,MAAM,aAAa,MAAM,IAAI,OAAO,cAAc,MAAM,WAAW;AACnE,cAAU,YAAY,aAAa,iCAAiC,IAAI;IACxE,MAAM,OAAO,MAAM,IAAI,QAAQ,KAAK,MAAM,YAAY;KACpD,OAAO,MAAM;KACb,QAAQ,MAAM;KACd,OAAO,EAAE,QAAQ,aAAa;KAC/B,CAAC;AACF,WAAO;KACL,GAAG;KACH,OAAO,KAAK,MAAM,KAAK,WAAW;MAChC,IAAI,MAAM;MACV,OAAO,OACL,MAAM,KAAK,WAAW,cAAc,YAAY,MAAM,QAAQ,kBAC/D;MACF,EAAE;KACJ;KACD;GACF,sBAAsB,KAAK,OAAO,KAAK,YAAY;IACjD,MAAM,QAAQ,MAAM,eAAe,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,IAAI,MAAM;AACpF,YAAQ,WAAW,MAAM,QAAQ,OAAO,MAAM,SAAS,EAAE,IAAI,KAAK;AAClE,cAAU,IAAI,OAAO,qBAAqB,sCAAsC,IAAI;IACpF,MAAM,SAAS,MAAM,IAAI,MAAM,WAAW,MAAM,MAAM;AACtD,cACE,QACA,aACA,gFACA,IACD;AACD,WAAO;KAAE,IAAI,OAAO;KAAI,MAAM,OAAO;KAAM,OAAO,OAAO;KAAO;MAC/D,iBAAiB;GACpB,oBAAoB,kBAAkB;IACpC,SAAS,CAAC,MAAM;IAChB,YAAY;IACZ,SAAS,EAAE,MAAM,QAAQ;IACzB,UAAU;IACV,SAAS,OAAO,QAAQ;KACtB,MAAM,QAAQ,MAAM,YAAY,IAAI,MAAM;AAE1C,YAAO,eAAe;MACpB,MAAM;OAAE,MAAM;OAAQ,OAFZ,MAAM,IAAI,WAAW,IAAI,CAAC,cAAc,IAAI,MAAM,MAAM;OAEhC;MAClC,SAAS;OACP,gBAAgB;OAChB,uBAAuB;OACxB;MACF,CAAC;;IAEL,CAAC;GACF,eAAe,KAAK,OAAO,KAAK,YAC9B,QAAQ,aAAa,IAAI,MAAM,MAAM,YAAY,IAAI,MAAM,CAAC,CAC7D;GACF;EACD,KAAK,EACH,OAAO;GACL,cAAc;IACZ,OAAO;IACP,aAAa;IACb,OAAO;IACR;GACD,aAAa;IACX,OAAO;IACP,aACE;IACF,OAAO;IACP,aAAa;IACd;GACD,gBAAgB;IACd,OAAO;IACP,aACE;IACF,OAAO;IACP,aAAa;IACd;GACD,gBAAgB;IACd,OAAO;IACP,aACE;IACF,OAAO;IACP,aAAa;IACd;GACD,kBAAkB;IAChB,OAAO;IACP,aAAa;IACb,OAAO;IACP,aAAa;IACd;GACF,EACF;EACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"runtime.js","names":["user"],"sources":["../src/store.ts","../src/editions.ts","../src/curriculum.ts","../src/service.ts","../src/learning.ts","../src/study.ts","../src/calendar.ts","../src/runtime.ts"],"sourcesContent":["import type { PluginContext, StorageCollection } from \"emdash\";\nimport type {\n Course,\n CourseRevision,\n CourseEdition,\n MigrationState,\n Enrollment,\n CertificateIndex,\n Discussion,\n Review,\n Cohort,\n CohortMembership,\n Notification,\n AuditRecord,\n Page,\n LearnerPreferences,\n SavedCourse,\n LearningPath,\n PathEnrollment,\n} from \"./types.js\";\nimport { MAX_DOCUMENT_BYTES } from \"./constants.js\";\nimport { LmsError, invariant } from \"./errors.js\";\n\nexport type StorageQueryOptions = NonNullable<Parameters<StorageCollection<unknown>[\"query\"]>[0]>;\n\nexport interface LmsCollections {\n courses: StorageCollection<Course>;\n course_revisions: StorageCollection<CourseRevision>;\n course_editions: StorageCollection<CourseEdition>;\n migrations: StorageCollection<MigrationState>;\n preferences: StorageCollection<LearnerPreferences>;\n saved_courses: StorageCollection<SavedCourse>;\n learning_paths: StorageCollection<LearningPath>;\n path_enrollments: StorageCollection<PathEnrollment>;\n enrollments: StorageCollection<Enrollment>;\n certificates: StorageCollection<CertificateIndex>;\n discussions: StorageCollection<Discussion>;\n reviews: StorageCollection<Review>;\n cohorts: StorageCollection<Cohort>;\n cohort_enrollments: StorageCollection<CohortMembership>;\n notifications: StorageCollection<Notification>;\n audit: StorageCollection<AuditRecord>;\n}\n\nexport function collections(ctx: PluginContext): LmsCollections {\n return ctx.storage as unknown as LmsCollections;\n}\nexport function enrollmentKey(courseId: string, userId: string): string {\n return `${courseId}:${userId}`;\n}\nexport function assertSize(data: unknown): void {\n invariant(\n new TextEncoder().encode(JSON.stringify(data)).byteLength <= MAX_DOCUMENT_BYTES,\n \"DOCUMENT_TOO_LARGE\",\n \"This record exceeds the LMS storage limit. Split the course into smaller courses.\",\n 413,\n );\n}\n\n// Enrollment progress, attempts and certificate eligibility share one CAS record.\nexport async function change<T, R>(\n store: StorageCollection<T>,\n id: string,\n update: (value: T | null) => Promise<{ value: T; result: R }> | { value: T; result: R },\n): Promise<R> {\n for (let attempt = 0; attempt < 8; attempt++) {\n const current = await store.getVersioned(id);\n const { value, result } = await update(current?.value ?? null);\n assertSize(value);\n const saved = await store.compareAndSet(id, current?.revision ?? null, value);\n if (saved.applied) return result;\n }\n throw new LmsError(\"CONFLICT\", \"Another request changed this record. Please try again.\", 409);\n}\n\nexport async function queryPage<T>(\n store: StorageCollection<T>,\n options: StorageQueryOptions = {},\n): Promise<Page<T>> {\n const page = await store.query(options);\n return { ...page, items: page.items.map((item) => item.data) };\n}\n\nexport async function putOnce<T>(store: StorageCollection<T>, id: string, value: T): Promise<void> {\n assertSize(value);\n await store.compareAndSet(id, null, value);\n}\n","import { courseDataSchema } from \"./schemas.js\";\nimport { invariant, LmsError } from \"./errors.js\";\nimport { assertSize, change, putOnce, type LmsCollections } from \"./store.js\";\nimport type {\n Course,\n CourseData,\n CourseRevision,\n Enrollment,\n Lesson,\n MigrationState,\n RevisionSummary,\n Versioned,\n} from \"./types.js\";\n\n/** Child snapshots are immutable. Only the root CAS makes a revision visible. */\nexport class CourseEditions {\n constructor(\n readonly stores: LmsCollections,\n readonly time: () => string,\n readonly resolveContent: (lesson: Lesson) => Promise<Lesson>,\n ) {}\n\n async snapshot(courseId: string, data: CourseData): Promise<string> {\n const frozen = structuredClone(data);\n for (const module of frozen.modules)\n for (let i = 0; i < module.lessons.length; i++)\n module.lessons[i] = {\n ...(await this.resolveContent(module.lessons[i]!)),\n contentSource: null,\n };\n const id = crypto.randomUUID();\n await putOnce(this.stores.course_editions, id, {\n id,\n courseId,\n data: frozen,\n createdAt: this.time(),\n });\n return id;\n }\n\n async revision(\n course: Course,\n data: CourseData,\n actorId: string,\n kind: CourseRevision[\"kind\"],\n extra: Partial<Pick<CourseRevision, \"editionId\" | \"restoredFrom\">> = {},\n ) {\n const revision: CourseRevision = {\n id: crypto.randomUUID(),\n courseId: course.id,\n parentId: course.draftRevisionId ?? null,\n data: courseDataSchema.parse({ ...data, status: \"draft\" }),\n kind,\n createdBy: actorId,\n createdAt: this.time(),\n ...extra,\n };\n await putOnce(this.stores.course_revisions, revision.id, revision);\n return revision;\n }\n\n async commit(current: Versioned<Course> | null, course: Course): Promise<Versioned<Course>> {\n assertSize(course);\n const saved = await this.stores.courses.compareAndSet(\n course.id,\n current?.revision ?? null,\n course,\n );\n invariant(saved.applied, \"CONFLICT\", \"The course changed. Reload before continuing.\", 409);\n return { value: course, revision: saved.revision };\n }\n\n async ensure(id: string, expectedRevision?: string): Promise<Versioned<Course>> {\n for (let attempt = 0; attempt < 8; attempt++) {\n const current = await this.stores.courses.getVersioned(id);\n invariant(current, \"NOT_FOUND\", \"Course not found.\", 404);\n invariant(\n expectedRevision === undefined || current.revision === expectedRevision,\n \"CONFLICT\",\n \"The course changed. Reload before continuing.\",\n 409,\n );\n if (current.value.schemaVersion === 2) return current;\n const course = current.value;\n invariant(\n course.schemaVersion === 1,\n \"UNSUPPORTED_SCHEMA\",\n \"This course needs a different LMS version. No migration was applied.\",\n 409,\n );\n const data = courseDataSchema.parse(course);\n // Even archived/legacy draft enrollments keep their original curriculum.\n const editionId = await this.snapshot(id, data);\n const revision = await this.revision(course, data, course.createdBy, \"migration\", {\n editionId,\n });\n const edition = (await this.stores.course_editions.get(editionId))!;\n const next: Course = {\n ...course,\n ...edition.data,\n schemaVersion: 2,\n draftRevisionId: revision.id,\n legacyEditionId: editionId,\n ...(course.status !== \"draft\"\n ? { publishedEditionId: editionId, publishedDraftRevisionId: revision.id }\n : {}),\n };\n try {\n return await this.commit(current, next);\n } catch (error) {\n if (\n !(error instanceof LmsError) ||\n error.code !== \"CONFLICT\" ||\n expectedRevision !== undefined\n )\n throw error;\n }\n }\n throw new LmsError(\"CONFLICT\", \"Course migration conflicted. Please retry.\", 409);\n }\n\n async draft(course: Course): Promise<Course> {\n if (!course.draftRevisionId) return course;\n const revision = await this.stores.course_revisions.get(course.draftRevisionId);\n invariant(\n revision?.courseId === course.id,\n \"EDITION_UNAVAILABLE\",\n \"The saved course draft is unavailable.\",\n 503,\n );\n return { ...course, ...revision.data, status: course.status };\n }\n\n async learning(course: Course, enrollment: Enrollment | null): Promise<Course> {\n if (!enrollment) return course;\n const id = enrollment.courseEditionId ?? course.legacyEditionId ?? course.publishedEditionId;\n if (!id) return course;\n const edition = await this.stores.course_editions.get(id);\n invariant(\n edition?.courseId === course.id,\n \"EDITION_UNAVAILABLE\",\n \"The enrolled course edition is unavailable.\",\n 503,\n );\n return {\n ...course,\n ...edition.data,\n // Current visibility, URL and staff authority never come from historical snapshots.\n status: course.status,\n slug: course.slug,\n instructorIds: course.instructorIds,\n };\n }\n\n async history(\n course: Course,\n cursor?: string,\n ): Promise<{ items: RevisionSummary[]; cursor?: string; hasMore: boolean }> {\n let id = course.draftRevisionId;\n let found = !cursor;\n const items: RevisionSummary[] = [];\n // Bounded traversal validates cursors against the committed chain, excluding failed CAS writes.\n for (let i = 0; id && i < 1000; i++) {\n const revision = await this.stores.course_revisions.get(id);\n invariant(\n revision?.courseId === course.id,\n \"EDITION_UNAVAILABLE\",\n \"Course history is unavailable.\",\n 503,\n );\n if (id === cursor) found = true;\n if (found) {\n const { data, ...summary } = revision;\n items.push({ ...summary, title: data.title });\n }\n id = revision.parentId ?? undefined;\n if (items.length === 20) return { items, cursor: id, hasMore: !!id };\n }\n invariant(\n found && !id,\n \"INVALID_CURSOR\",\n \"History cursor is invalid or exceeds the 1,000-revision browsing limit.\",\n 400,\n );\n return { items, hasMore: false };\n }\n\n async migrationStatus(): Promise<MigrationState> {\n return (\n (await this.stores.migrations.get(\"course-editions-v2\")) ?? {\n phase: \"courses\",\n coursesProcessed: 0,\n enrollmentsProcessed: 0,\n updatedAt: this.time(),\n lastError: null,\n }\n );\n }\n\n async migrateBatch(limit: number): Promise<MigrationState> {\n const id = \"course-editions-v2\";\n const current = await this.stores.migrations.getVersioned(id);\n const state = current?.value ?? (await this.migrationStatus());\n if (state.phase === \"complete\") return state;\n const next = { ...state, lastError: null, updatedAt: this.time() };\n try {\n if (state.phase === \"courses\") {\n const page = await this.stores.courses.query({\n orderBy: { createdAt: \"asc\" },\n cursor: state.cursor,\n limit,\n });\n for (const item of page.items) await this.ensure(item.id);\n next.coursesProcessed += page.items.length;\n next.cursor = page.cursor;\n if (!page.hasMore) {\n next.phase = \"enrollments\";\n next.cursor = undefined;\n }\n } else {\n const page = await this.stores.enrollments.query({\n orderBy: { enrolledAt: \"asc\" },\n cursor: state.cursor,\n limit,\n });\n for (const item of page.items) {\n if (item.data.courseEditionId) continue;\n const course = (await this.ensure(item.data.courseId)).value;\n await change(this.stores.enrollments, item.id, async (enrollment) => {\n invariant(enrollment, \"NOT_FOUND\", \"Enrollment disappeared during migration.\", 409);\n return {\n value: {\n ...enrollment,\n courseEditionId:\n enrollment.courseEditionId ?? course.legacyEditionId ?? course.publishedEditionId,\n },\n result: null,\n };\n });\n }\n next.enrollmentsProcessed += page.items.length;\n next.cursor = page.cursor;\n if (!page.hasMore) {\n next.phase = \"complete\";\n next.cursor = undefined;\n }\n }\n } catch (error) {\n // Partial child writes are idempotent; leave the batch cursor at its starting point.\n await this.stores.migrations.compareAndSet(id, current?.revision ?? null, {\n ...state,\n updatedAt: this.time(),\n lastError:\n error instanceof LmsError\n ? error.message\n : \"Migration failed. Retry the batch after checking storage availability.\",\n });\n throw error;\n }\n const saved = await this.stores.migrations.compareAndSet(id, current?.revision ?? null, next);\n invariant(\n saved.applied,\n \"CONFLICT\",\n \"Another migration batch advanced the checkpoint. Refresh and continue.\",\n 409,\n );\n return next;\n }\n}\n","import { invariant, LmsError } from \"./errors.js\";\nimport type {\n Course,\n CourseCard,\n CourseDetail,\n Enrollment,\n Lesson,\n LessonProgress,\n ProgressSummary,\n PublicEnrollment,\n PublicLesson,\n PublicQuizAttempt,\n QuizAttempt,\n QuizQuestion,\n Answers,\n QuestionResult,\n LessonAvailability,\n} from \"./types.js\";\n\nexport function lessons(course: Course): Lesson[] {\n return course.modules.flatMap((module) => module.lessons);\n}\nexport function emptyProgress(): LessonProgress {\n return { startedAt: null, completedAt: null, note: \"\", quizAttempts: [], assignments: [] };\n}\nexport function courseCard(course: Course): CourseCard {\n const all = lessons(course);\n return {\n id: course.id,\n slug: course.slug,\n title: course.title,\n summary: course.summary,\n category: course.category,\n tags: course.tags,\n level: course.level,\n language: course.language,\n featuredImage: course.featuredImage,\n instructorName: course.instructorName,\n enrollmentMode: course.enrollmentMode,\n lessonCount: all.length,\n durationMinutes: all.reduce((sum, lesson) => sum + lesson.durationMinutes, 0),\n };\n}\nexport function courseDetail(course: Course): CourseDetail {\n return {\n prerequisites: [],\n ...courseCard(course),\n description: course.description,\n outcomes: course.outcomes,\n requirements: course.requirements,\n sequential: course.sequential,\n prerequisiteCourseIds: course.prerequisiteCourseIds,\n enrollmentClosesAt: course.enrollmentClosesAt,\n accessDays: course.accessDays,\n certificateEnabled: course.certificateEnabled,\n modules: course.modules.map((module) => ({\n id: module.id,\n title: module.title,\n description: module.description,\n releaseDays: module.releaseDays,\n unlockAt: module.unlockAt,\n lessons: module.lessons.map((lesson) => ({\n id: lesson.id,\n title: lesson.title,\n summary: lesson.summary,\n kind: lesson.kind,\n durationMinutes: lesson.durationMinutes,\n preview: lesson.preview,\n required: lesson.required,\n })),\n })),\n };\n}\nexport function publicLesson(lesson: Lesson): PublicLesson {\n const { quiz, contentSource: _source, ...safe } = lesson;\n return {\n ...safe,\n transcript: lesson.transcript ?? \"\",\n captions: lesson.captions ?? [],\n quiz: quiz\n ? {\n passingPercent: quiz.passingPercent,\n maxAttempts: quiz.maxAttempts,\n timeLimitMinutes: quiz.timeLimitMinutes,\n randomizeQuestions: quiz.randomizeQuestions,\n randomizeOptions: quiz.randomizeOptions,\n showFeedback: quiz.showFeedback,\n questionCount: quiz.questions.length,\n }\n : null,\n };\n}\nexport function publicAttempt(attempt: QuizAttempt): PublicQuizAttempt {\n const { quiz: _quiz, ...safe } = attempt;\n return { ...safe, draftRevision: attempt.draftRevision ?? 0 };\n}\nexport function effectiveAccess(\n enrollment: Enrollment,\n now = new Date(),\n): PublicEnrollment[\"effectiveAccess\"] {\n if (enrollment.status === \"suspended\" || enrollment.accessStatus === \"suspended\")\n return \"suspended\";\n if (\n [\"rejected\", \"cancelled\"].includes(enrollment.status) ||\n enrollment.accessStatus === \"revoked\"\n )\n return \"revoked\";\n if (enrollment.status === \"pending\" || enrollment.accessStatus === \"pending\") return \"pending\";\n if (enrollment.accessExpiresAt && now.getTime() >= Date.parse(enrollment.accessExpiresAt))\n return \"expired\";\n if (enrollment.accessStartsAt && now.getTime() < Date.parse(enrollment.accessStartsAt))\n return \"scheduled\";\n const grants = Object.values(enrollment.grants);\n if (\n grants.some(\n (grant) =>\n grant.status === \"granted\" &&\n (!grant.startsAt || now.getTime() >= Date.parse(grant.startsAt)) &&\n (!grant.expiresAt || now.getTime() < Date.parse(grant.expiresAt)),\n )\n )\n return \"granted\";\n if (\n grants.some(\n (grant) =>\n grant.status === \"granted\" &&\n grant.startsAt &&\n now.getTime() < Date.parse(grant.startsAt) &&\n (!grant.expiresAt || now.getTime() < Date.parse(grant.expiresAt)),\n )\n )\n return \"scheduled\";\n if (grants.some((grant) => grant.status === \"suspended\")) return \"suspended\";\n if (\n grants.some(\n (grant) =>\n grant.status === \"granted\" &&\n grant.expiresAt &&\n now.getTime() >= Date.parse(grant.expiresAt),\n )\n )\n return \"expired\";\n return \"revoked\";\n}\n\nexport function publicEnrollment(enrollment: Enrollment, now = new Date()): PublicEnrollment {\n const { learnerEmail: _email, progress, ...safe } = enrollment;\n return {\n ...safe,\n effectiveAccess: effectiveAccess(enrollment, now),\n progress: Object.fromEntries(\n Object.entries(progress).map(([id, item]) => [\n id,\n { ...item, quizAttempts: item.quizAttempts.map(publicAttempt) },\n ]),\n ),\n };\n}\n/** Staff need assessment and completion records, not a learner's private study notes. */\nexport function staffEnrollment(enrollment: Enrollment, now = new Date()): PublicEnrollment {\n const view = publicEnrollment(enrollment, now);\n delete view.studyNotes;\n for (const progress of Object.values(view.progress)) {\n progress.note = \"\";\n delete progress.noteRevision;\n delete progress.playback;\n delete progress.assignmentDraft;\n }\n return view;\n}\nexport function progressSummary(\n course: Course,\n enrollment: Enrollment | null,\n now = new Date(),\n): ProgressSummary {\n const required = enrollment?.completedAt\n ? enrollment.completionLessonIds\n : lessons(course)\n .filter((lesson) => lesson.required)\n .map((lesson) => lesson.id);\n const completed = required.filter((id) => enrollment?.progress[id]?.completedAt).length;\n const all = lessons(course);\n const availability = Object.fromEntries(\n all.map((lesson) => [lesson.id, lessonAvailability(course, lesson.id, enrollment, now)]),\n );\n const incomplete = all.filter((lesson) => !enrollment?.progress[lesson.id]?.completedAt);\n const nextLessonId = incomplete.find((lesson) => availability[lesson.id]!.available)?.id ?? null;\n return {\n completed,\n total: required.length,\n percent: required.length ? Math.floor((completed / required.length) * 100) : 0,\n nextLessonId,\n blockedLessonId: nextLessonId ? null : (incomplete[0]?.id ?? null),\n availability,\n };\n}\n\n/** Use the same access boundary for navigation and private lesson reads. Enrollment\n * must already have its cohort grants resolved against authoritative membership. */\nexport function lessonAvailability(\n course: Course,\n lessonId: string,\n enrollment: Enrollment | null,\n now: Date,\n): LessonAvailability {\n try {\n assertLessonAccess(course, lessonId, enrollment, now);\n return { available: true, code: null, reason: null, releaseAt: null };\n } catch (error) {\n if (!(error instanceof LmsError)) throw error;\n let releaseAt: string | null = null;\n if (enrollment && error.code === \"LESSON_LOCKED\")\n releaseAt = new Date(moduleReleaseAt(course, lessonId, enrollment)).toISOString();\n else if (enrollment && effectiveAccess(enrollment, now) === \"scheduled\") {\n const starts = Object.values(enrollment.grants)\n .filter(\n (grant) =>\n grant.status === \"granted\" &&\n (!grant.expiresAt || Date.parse(grant.expiresAt) > now.getTime()),\n )\n .flatMap((grant) => {\n const date = Math.max(\n grant.startsAt ? Date.parse(grant.startsAt) : 0,\n enrollment.accessStartsAt ? Date.parse(enrollment.accessStartsAt) : 0,\n );\n return date > now.getTime() &&\n (!grant.expiresAt || date < Date.parse(grant.expiresAt)) &&\n (!enrollment.accessExpiresAt || date < Date.parse(enrollment.accessExpiresAt))\n ? [date]\n : [];\n });\n if (starts.length) releaseAt = new Date(Math.min(...starts)).toISOString();\n }\n return { available: false, code: error.code, reason: error.message, releaseAt };\n }\n}\n\nfunction moduleReleaseAt(course: Course, lessonId: string, enrollment: Enrollment): number {\n const module = course.modules.find((item) =>\n item.lessons.some((entry) => entry.id === lessonId),\n )!;\n return Math.max(\n module.unlockAt ? Date.parse(module.unlockAt) : 0,\n Date.parse(enrollment.approvedAt ?? enrollment.enrolledAt) + module.releaseDays * 86_400_000,\n );\n}\n\nexport function assertEnrollmentAccess(\n enrollment: Enrollment | null,\n now: Date,\n): asserts enrollment is Enrollment {\n invariant(\n enrollment && (enrollment.status === \"active\" || enrollment.status === \"completed\"),\n \"ENROLLMENT_REQUIRED\",\n \"An active enrollment is required for this lesson.\",\n 403,\n );\n invariant(\n enrollment.accessStatus === \"granted\",\n \"ACCESS_RESTRICTED\",\n \"Your course access is not currently granted.\",\n 403,\n );\n invariant(\n Object.values(enrollment.grants).some(\n (grant) =>\n grant.status === \"granted\" &&\n (!grant.startsAt || now.getTime() >= Date.parse(grant.startsAt)) &&\n (!grant.expiresAt || now.getTime() < Date.parse(grant.expiresAt)),\n ),\n \"GRANT_REQUIRED\",\n \"Your course access grant has not started, expired, or been revoked.\",\n 403,\n );\n invariant(\n !enrollment.accessStartsAt || now.getTime() >= Date.parse(enrollment.accessStartsAt),\n \"COURSE_NOT_STARTED\",\n \"Your course access has not started yet.\",\n 403,\n );\n invariant(\n !enrollment.accessExpiresAt || now.getTime() < Date.parse(enrollment.accessExpiresAt),\n \"ACCESS_EXPIRED\",\n \"Your course access has expired.\",\n 403,\n );\n}\n\nexport function findLesson(course: Course, lessonId: string): Lesson {\n const lesson = lessons(course).find((item) => item.id === lessonId);\n invariant(lesson, \"NOT_FOUND\", \"Lesson not found.\", 404);\n return lesson;\n}\nexport function assertLessonAccess(\n course: Course,\n lessonId: string,\n enrollment: Enrollment | null,\n now: Date,\n allowPreview = false,\n): Lesson {\n const lesson = findLesson(course, lessonId);\n if (allowPreview && lesson.preview && ![\"quiz\", \"assignment\", \"live\"].includes(lesson.kind))\n return lesson;\n assertEnrollmentAccess(enrollment, now);\n invariant(course.status !== \"draft\", \"NOT_FOUND\", \"Course not found.\", 404);\n const releaseAt = moduleReleaseAt(course, lessonId, enrollment);\n invariant(\n now.getTime() >= releaseAt,\n \"LESSON_LOCKED\",\n \"This module has not been released yet.\",\n 403,\n );\n if (course.sequential) {\n const all = lessons(course);\n const index = all.findIndex((item) => item.id === lessonId);\n invariant(\n all\n .slice(0, index)\n .filter((item) => item.required)\n .every((item) => enrollment.progress[item.id]?.completedAt),\n \"PREREQUISITE_REQUIRED\",\n \"Complete the earlier required lessons first.\",\n 403,\n );\n }\n return lesson;\n}\n\nexport function shuffle<T>(\n items: T[],\n random: () => number = () => {\n const value = new Uint32Array(1);\n crypto.getRandomValues(value);\n return value[0]! / 4_294_967_296;\n },\n): T[] {\n const output = [...items];\n for (let index = output.length - 1; index > 0; index--) {\n const pick = Math.floor(random() * (index + 1));\n [output[index], output[pick]] = [output[pick]!, output[index]!];\n }\n return output;\n}\nfunction normalized(value: string): string {\n return value.normalize(\"NFKC\").trim().toLocaleLowerCase(\"en\").replace(/\\s+/g, \" \");\n}\nexport function gradeAnswers(\n questions: QuizQuestion[],\n answers: Answers,\n): { score: number; maxScore: number; percent: number; feedback: QuestionResult[] } {\n const ids = new Set(questions.map((question) => question.id));\n invariant(\n Object.keys(answers).every((id) => ids.has(id)),\n \"INVALID_ANSWERS\",\n \"Answers contain an unknown question.\",\n );\n const feedback = questions.map((question) => {\n const response = answers[question.id];\n const selected = Array.isArray(response) ? response : response === undefined ? [] : [response];\n invariant(\n new Set(selected).size === selected.length,\n \"INVALID_ANSWERS\",\n \"Duplicate answer choices are not accepted.\",\n );\n if (question.type !== \"multiple\")\n invariant(selected.length <= 1, \"INVALID_ANSWERS\", \"This question accepts one answer.\");\n if (question.type !== \"short\")\n invariant(\n selected.every((answer) => question.options.some((option) => option.id === answer)),\n \"INVALID_ANSWERS\",\n \"An answer contains an invalid option.\",\n );\n const correct =\n question.type === \"short\"\n ? selected.length === 1 &&\n question.correctAnswers.some((answer) => normalized(answer) === normalized(selected[0]!))\n : selected.length === question.correctAnswers.length &&\n selected.every((answer) => question.correctAnswers.includes(answer));\n return {\n questionId: question.id,\n correct,\n points: correct ? question.points : 0,\n maxPoints: question.points,\n explanation: question.explanation,\n };\n });\n const score = feedback.reduce((sum, item) => sum + item.points, 0);\n const maxScore = questions.reduce((sum, question) => sum + question.points, 0);\n return { score, maxScore, percent: Math.round((score / maxScore) * 10_000) / 100, feedback };\n}\n","import type { PluginContext } from \"emdash\";\nimport { z } from \"zod\";\nimport {\n catalogSchema,\n courseSaveSchema,\n courseDataSchema,\n settingsSchema,\n enrollmentAdminSchema,\n quizSubmitSchema,\n quizDraftSchema,\n assignmentSubmitSchema,\n assignmentDraftSchema,\n gradeSchema,\n discussionSchema,\n reviewSchema,\n moderateSchema,\n cohortSaveSchema,\n noteSchema,\n} from \"./schemas.js\";\nimport type {\n Actor,\n Course,\n Enrollment,\n QuizAttempt,\n LmsSettings,\n PublicQuizAttempt,\n Discussion,\n Review,\n Cohort,\n CohortMembership,\n Notification,\n Page,\n CourseCard,\n AssignmentState,\n} from \"./types.js\";\nimport {\n assertSize,\n change,\n collections,\n enrollmentKey,\n putOnce,\n queryPage,\n type StorageQueryOptions,\n} from \"./store.js\";\nimport { invariant, LmsError } from \"./errors.js\";\nimport { CourseEditions } from \"./editions.js\";\nimport { assignmentBlock, assignmentGrade, emptyAssignmentDraft } from \"./assignments.js\";\nimport { enrollmentCsvHeader, enrollmentCsvLines } from \"./csv.js\";\nimport {\n assertEnrollmentAccess,\n assertLessonAccess,\n courseCard,\n courseDetail,\n emptyProgress,\n effectiveAccess,\n findLesson,\n gradeAnswers,\n lessons,\n progressSummary,\n publicAttempt,\n publicEnrollment,\n staffEnrollment,\n publicLesson,\n shuffle,\n} from \"./curriculum.js\";\n\nexport function parse<T>(schema: z.ZodType<T>, input: unknown): T {\n const result = schema.safeParse(input);\n if (!result.success)\n throw new LmsError(\n \"VALIDATION_ERROR\",\n result.error.issues\n .map((issue) => `${issue.path.join(\".\") || \"Input\"}: ${issue.message}`)\n .slice(0, 5)\n .join(\" \"),\n 400,\n );\n return result.data;\n}\nfunction user(actor: Actor | undefined): Actor {\n invariant(actor, \"UNAUTHORIZED\", \"Sign in to continue.\", 401);\n return actor;\n}\n\nexport class LmsService {\n readonly stores;\n readonly editions;\n constructor(\n readonly ctx: PluginContext,\n readonly now: () => Date = () => new Date(),\n ) {\n this.stores = collections(ctx);\n this.editions = new CourseEditions(\n this.stores,\n () => this.time(),\n (lesson) => this.resolveContent(lesson),\n );\n }\n time(): string {\n return this.now().toISOString();\n }\n async settings(): Promise<LmsSettings> {\n return settingsSchema.parse((await this.ctx.settings.get(\"configuration\")) ?? {});\n }\n async saveSettings(actor: Actor | undefined, input: unknown): Promise<LmsSettings> {\n invariant(\n user(actor).role >= 50,\n \"FORBIDDEN\",\n \"Only administrators can change LMS settings.\",\n 403,\n );\n const settings = parse(settingsSchema, input);\n invariant(\n !settings.emailNotifications || this.ctx.email,\n \"EMAIL_UNAVAILABLE\",\n \"Configure an email transport in EmDash before enabling LMS emails.\",\n );\n await this.ctx.settings.set(\"configuration\", settings);\n await this.audit(actor!, \"settings.save\", \"configuration\");\n return settings;\n }\n async audit(\n actor: Actor,\n action: string,\n resourceId: string,\n details: Record<string, unknown> = {},\n ): Promise<void> {\n const id = crypto.randomUUID();\n await this.stores.audit.put(id, {\n id,\n actorId: actor.id,\n action,\n resourceId,\n createdAt: this.time(),\n details,\n });\n }\n async course(id: string): Promise<Course> {\n const course = await this.stores.courses.get(id);\n invariant(course, \"NOT_FOUND\", \"Course not found.\", 404);\n return course;\n }\n // Membership is authoritative while course grant projections are being reconciled.\n async accessEnrollment(enrollment: Enrollment | null): Promise<Enrollment | null> {\n if (!enrollment) return null;\n const grants = Object.values(enrollment.grants).filter((grant) => grant.source === \"cohort\");\n if (!grants.length) return enrollment;\n const [memberships, cohorts] = await Promise.all([\n this.stores.cohort_enrollments.getMany(\n grants.map((grant) => enrollmentKey(grant.referenceId ?? \"\", enrollment.userId)),\n ),\n this.stores.cohorts.getMany(grants.map((grant) => grant.referenceId ?? \"\")),\n ]);\n const resolved = structuredClone(enrollment);\n for (const grant of grants) {\n const membership = memberships.get(enrollmentKey(grant.referenceId ?? \"\", enrollment.userId));\n const cohort = cohorts.get(grant.referenceId ?? \"\");\n if (membership?.status !== \"active\" || !cohort?.courseIds.includes(enrollment.courseId))\n resolved.grants[grant.id]!.status =\n membership?.status === \"suspended\" ? \"suspended\" : \"revoked\";\n else {\n resolved.grants[grant.id]!.status = \"granted\";\n resolved.grants[grant.id]!.startsAt = cohort.startsAt;\n resolved.grants[grant.id]!.expiresAt = cohort.endsAt;\n }\n }\n return resolved;\n }\n async enrollmentView(enrollment: Enrollment) {\n return publicEnrollment((await this.accessEnrollment(enrollment))!, this.now());\n }\n async learningState(course: Course, enrollment: Enrollment | null) {\n course = await this.editions.learning(course, enrollment);\n const resolved = await this.accessEnrollment(enrollment);\n const now = this.now();\n return {\n enrollment: resolved ? publicEnrollment(resolved, now) : null,\n progress: progressSummary(course, resolved, now),\n };\n }\n async lessonAccess(course: Course, lessonId: string, enrollment: Enrollment | null) {\n return assertLessonAccess(\n await this.editions.learning(course, enrollment),\n lessonId,\n await this.accessEnrollment(enrollment),\n this.now(),\n );\n }\n instructor(course: Course, actor: Actor | undefined): Actor {\n const current = user(actor);\n invariant(\n current.role >= 20 &&\n (current.role >= 50 ||\n course.createdBy === current.id ||\n course.instructorIds.includes(current.id)),\n \"FORBIDDEN\",\n \"You are not an instructor for this course.\",\n 403,\n );\n return current;\n }\n async saveCourse(\n actor: Actor | undefined,\n input: unknown,\n ): Promise<{ course: Course; revision: string }> {\n const currentActor = user(actor);\n const parsed = parse(courseSaveSchema, input);\n const id = parsed.id ?? crypto.randomUUID();\n let current = await this.stores.courses.getVersioned(id);\n if (current) this.instructor(current.value, currentActor);\n else\n invariant(\n currentActor.role >= 20,\n \"FORBIDDEN\",\n \"Course authoring requires a contributor account or higher.\",\n 403,\n );\n invariant(\n (current?.revision ?? null) === parsed.expectedRevision,\n \"CONFLICT\",\n \"The course changed since you opened it. Reload before saving.\",\n 409,\n );\n if (!current && parsed.data.status === \"published\")\n invariant(\n currentActor.role >= 50 || currentActor.role >= 30,\n \"FORBIDDEN\",\n \"Publishing requires an author account or higher.\",\n 403,\n );\n invariant(\n current || parsed.data.status !== \"published\" || parsed.data.commerce.accessModel === \"free\",\n \"PAID_COURSES_DISABLED\",\n \"Payment processing is not enabled in this release. Publish free courses only.\",\n 409,\n );\n const duplicate = await this.stores.courses.query({\n where: { slug: parsed.data.slug },\n limit: 1,\n });\n invariant(\n !duplicate.items.some((item) => item.id !== id),\n \"SLUG_TAKEN\",\n \"A course already uses this URL.\",\n 409,\n );\n await this.assertPrerequisiteGraph(id, parsed.data.prerequisiteCourseIds);\n if (current) current = await this.editions.ensure(id, current.revision);\n let course: Course = {\n ...parsed.data,\n id,\n searchTitle: parsed.data.title.toLocaleLowerCase(\"en\"),\n createdBy: current?.value.createdBy ?? currentActor.id,\n createdAt: current?.value.createdAt ?? this.time(),\n updatedAt: this.time(),\n schemaVersion: 2,\n };\n const draft = await this.editions.revision(\n current?.value ?? course,\n parsed.data,\n currentActor.id,\n \"draft\",\n );\n if (current) {\n // Once published, the live discovery projection stays intact until explicit publication.\n course = {\n ...current.value,\n ...(current.value.publishedEditionId ? {} : parsed.data),\n status: current.value.status,\n draftRevisionId: draft.id,\n updatedAt: this.time(),\n };\n // Staff assignments apply when saved; historical editions cannot restore old authority.\n course.instructorIds = parsed.data.instructorIds;\n } else {\n course.draftRevisionId = draft.id;\n if (course.status === \"published\") {\n const editionId = await this.editions.snapshot(id, parsed.data);\n const edition = (await this.stores.course_editions.get(editionId))!;\n course = {\n ...course,\n ...edition.data,\n publishedEditionId: editionId,\n publishedDraftRevisionId: draft.id,\n };\n }\n }\n course.searchTitle = course.title.toLocaleLowerCase(\"en\");\n const saved = await this.editions.commit(current, course);\n await this.audit(currentActor, \"course.save\", id, { draftRevisionId: draft.id });\n return { course: await this.editions.draft(course), revision: saved.revision };\n }\n\n async publishCourse(actor: Actor | undefined, id: string, expectedRevision: string) {\n const owner = this.instructor(await this.course(id), actor);\n invariant(\n owner.role >= 30,\n \"FORBIDDEN\",\n \"Publishing requires an author account or higher.\",\n 403,\n );\n const current = await this.editions.ensure(id, expectedRevision);\n const draft = await this.editions.draft(current.value);\n const data = parse(courseDataSchema, { ...draft, status: \"published\" });\n invariant(\n data.commerce.accessModel === \"free\",\n \"PAID_COURSES_DISABLED\",\n \"Publish free courses only.\",\n 409,\n );\n await this.assertPrerequisiteGraph(id, data.prerequisiteCourseIds);\n const duplicate = await this.stores.courses.query({ where: { slug: data.slug }, limit: 1 });\n invariant(\n !duplicate.items.some((row) => row.id !== id),\n \"SLUG_TAKEN\",\n \"A course already uses this URL.\",\n 409,\n );\n const editionId = await this.editions.snapshot(id, data);\n const revision = await this.editions.revision(current.value, data, owner.id, \"publish\", {\n editionId,\n });\n const edition = (await this.stores.course_editions.get(editionId))!;\n const next = {\n ...current.value,\n ...edition.data,\n searchTitle: data.title.toLocaleLowerCase(\"en\"),\n draftRevisionId: revision.id,\n publishedDraftRevisionId: revision.id,\n publishedEditionId: editionId,\n updatedAt: this.time(),\n };\n const saved = await this.editions.commit(current, next);\n await this.audit(owner, \"course.publish\", id, { editionId });\n return { course: await this.editions.draft(next), revision: saved.revision };\n }\n\n async courseHistory(actor: Actor | undefined, id: string, cursor?: string) {\n const course = await this.course(id);\n this.instructor(course, actor);\n return this.editions.history(course, cursor);\n }\n\n async restoreCourse(\n actor: Actor | undefined,\n id: string,\n expectedRevision: string,\n revisionId: string,\n ) {\n const owner = this.instructor(await this.course(id), actor);\n const current = await this.editions.ensure(id, expectedRevision);\n const page = await this.editions.history(current.value, revisionId);\n invariant(page.items[0]?.id === revisionId, \"NOT_FOUND\", \"Saved revision not found.\", 404);\n const previous = (await this.stores.course_revisions.get(revisionId))!;\n // Restore content, not past access to the instructor console.\n const data = { ...previous.data, instructorIds: current.value.instructorIds };\n const revision = await this.editions.revision(current.value, data, owner.id, \"restore\", {\n restoredFrom: revisionId,\n });\n const next = { ...current.value, draftRevisionId: revision.id, updatedAt: this.time() };\n const saved = await this.editions.commit(current, next);\n await this.audit(owner, \"course.restore\", id, { revisionId });\n return { course: await this.editions.draft(next), revision: saved.revision };\n }\n\n async archiveCourse(actor: Actor | undefined, id: string, expectedRevision: string) {\n const owner = this.instructor(await this.course(id), actor);\n invariant(\n owner.role >= 30,\n \"FORBIDDEN\",\n \"Archiving requires an author account or higher.\",\n 403,\n );\n const legacy = await this.stores.courses.getVersioned(id);\n if (legacy?.value.schemaVersion === 1) {\n invariant(\n legacy.revision === expectedRevision,\n \"CONFLICT\",\n \"The course changed. Reload before continuing.\",\n 409,\n );\n // Visibility controls must work even when an unavailable source blocks snapshot migration.\n const next: Course = { ...legacy.value, status: \"archived\", updatedAt: this.time() };\n const saved = await this.editions.commit(legacy, next);\n await this.audit(owner, \"course.archive\", id);\n return { course: next, revision: saved.revision };\n }\n const current = await this.editions.ensure(id, expectedRevision);\n const draft = await this.editions.draft(current.value);\n const revision = await this.editions.revision(current.value, draft, owner.id, \"archive\");\n const next: Course = {\n ...current.value,\n status: \"archived\",\n draftRevisionId: revision.id,\n updatedAt: this.time(),\n };\n const saved = await this.editions.commit(current, next);\n await this.audit(owner, \"course.archive\", id);\n return { course: await this.editions.draft(next), revision: saved.revision };\n }\n\n async migrationStatus(actor: Actor | undefined) {\n invariant(\n user(actor).role >= 50,\n \"FORBIDDEN\",\n \"Only administrators can manage migrations.\",\n 403,\n );\n return this.editions.migrationStatus();\n }\n async migrateBatch(actor: Actor | undefined, limit: number) {\n await this.migrationStatus(actor);\n const state = await this.editions.migrateBatch(limit);\n if (state.phase === \"complete\") await this.ctx.kv.set(\"schemaVersion\", 2);\n return state;\n }\n async assertPrerequisiteGraph(courseId: string, prerequisites: string[]): Promise<void> {\n const visited = new Set<string>();\n const visit = async (id: string): Promise<void> => {\n invariant(id !== courseId, \"PREREQUISITE_CYCLE\", \"Course prerequisites cannot form a cycle.\");\n if (visited.has(id)) return;\n invariant(\n visited.size < 100,\n \"PREREQUISITE_GRAPH_TOO_LARGE\",\n \"The prerequisite chain is too long.\",\n );\n visited.add(id);\n const course = await this.course(id);\n for (const next of course.prerequisiteCourseIds) await visit(next);\n };\n for (const id of prerequisites) await visit(id);\n }\n async editableCourse(\n actor: Actor | undefined,\n id: string,\n ): Promise<{ course: Course; revision: string }> {\n const current = await this.stores.courses.getVersioned(id);\n invariant(current, \"NOT_FOUND\", \"Course not found.\", 404);\n this.instructor(current.value, actor);\n return { course: await this.editions.draft(current.value), revision: current.revision };\n }\n async adminCourses(\n actor: Actor | undefined,\n options: { cursor?: string; limit?: number; status?: string } = {},\n ): Promise<Page<Course>> {\n const current = user(actor);\n const page = await queryPage(this.stores.courses, {\n where: options.status ? { status: options.status } : {},\n orderBy: { updatedAt: \"desc\" },\n limit: options.limit ?? 24,\n cursor: options.cursor,\n });\n return {\n ...page,\n items: await Promise.all(\n page.items\n .filter(\n (course) =>\n current.role >= 20 &&\n (current.role >= 50 ||\n course.createdBy === current.id ||\n course.instructorIds.includes(current.id)),\n )\n .map((course) => this.editions.draft(course)),\n ),\n };\n }\n async catalog(input: unknown): Promise<Page<CourseCard>> {\n const options = parse(catalogSchema, input);\n const where: NonNullable<StorageQueryOptions[\"where\"]> = { status: \"published\" };\n if (options.q.trim())\n where.searchTitle = { startsWith: options.q.trim().toLocaleLowerCase(\"en\") };\n if (options.category) where.category = options.category;\n if (options.language) where.language = options.language;\n if (options.level && options.level !== \"all\") where.level = { in: [options.level, \"all\"] };\n const page = await queryPage(this.stores.courses, {\n where,\n orderBy: { title: \"asc\" },\n limit: options.limit,\n cursor: options.cursor,\n });\n return { ...page, items: page.items.map(courseCard) };\n }\n async publicCourse(slug: string) {\n const page = await this.stores.courses.query({\n where: { slug, status: \"published\" },\n limit: 1,\n });\n invariant(page.items[0], \"NOT_FOUND\", \"Course not found.\", 404);\n return this.detail(page.items[0].data);\n }\n async detail(course: Course) {\n const prerequisites = await this.stores.courses.getMany(course.prerequisiteCourseIds);\n return {\n ...courseDetail(course),\n prerequisites: course.prerequisiteCourseIds.map((id) => {\n const value = prerequisites.get(id);\n return {\n id,\n title: value?.title ?? \"Prerequisite course\",\n slug: value?.status === \"published\" ? value.slug : null,\n };\n }),\n };\n }\n async preview(courseId: string, lessonId: string) {\n const course = await this.course(courseId);\n invariant(course.status === \"published\", \"NOT_FOUND\", \"Course not found.\", 404);\n const lesson = findLesson(course, lessonId);\n invariant(\n lesson.preview && ![\"quiz\", \"assignment\", \"live\"].includes(lesson.kind),\n \"PREVIEW_UNAVAILABLE\",\n \"This lesson is available to enrolled learners.\",\n 403,\n );\n return {\n course: courseDetail(course),\n lesson: publicLesson(await this.resolveContent(lesson)),\n };\n }\n async resolveContent(\n lesson: ReturnType<typeof findLesson>,\n ): Promise<ReturnType<typeof findLesson>> {\n if (!lesson.contentSource) return lesson;\n invariant(this.ctx.content, \"CONTENT_UNAVAILABLE\", \"Content integration is unavailable.\", 503);\n const entry = await this.ctx.content.get(\n lesson.contentSource.collection,\n lesson.contentSource.id,\n );\n invariant(\n entry?.status === \"published\",\n \"CONTENT_UNAVAILABLE\",\n \"The linked lesson content is not published.\",\n 404,\n );\n const body = entry.data[lesson.contentSource.field];\n invariant(\n Array.isArray(body) || typeof body === \"string\",\n \"CONTENT_UNAVAILABLE\",\n \"The linked lesson field is unavailable.\",\n 404,\n );\n if (Array.isArray(body))\n return { ...lesson, body: \"\", portableText: body as Record<string, unknown>[] };\n return { ...lesson, body, portableText: [] };\n }\n async authorLessonPreview(actor: Actor | undefined, courseId: string, lessonId: string) {\n const { course } = await this.editableCourse(actor, courseId);\n return {\n course: courseDetail(course),\n lesson: publicLesson(await this.resolveContent(findLesson(course, lessonId))),\n };\n }\n async assertPrerequisites(course: Course, actorId: string): Promise<void> {\n for (const id of course.prerequisiteCourseIds) {\n const enrollment = await this.stores.enrollments.get(enrollmentKey(id, actorId));\n invariant(\n enrollment?.completedAt,\n \"COURSE_PREREQUISITE_REQUIRED\",\n \"Complete the prerequisite courses before enrolling.\",\n 403,\n );\n }\n }\n newEnrollment(course: Course, actor: Actor, status: Enrollment[\"status\"]): Enrollment {\n const now = this.time();\n const active = status === \"active\";\n return {\n id: enrollmentKey(course.id, actor.id),\n courseId: course.id,\n courseEditionId: course.publishedEditionId ?? course.legacyEditionId,\n userId: actor.id,\n learnerName: actor.name || actor.email.split(\"@\")[0] || \"Learner\",\n learnerEmail: actor.email,\n status,\n accessStatus: active ? \"granted\" : \"pending\",\n accessSource: \"free\",\n grants: {\n free: {\n id: \"free\",\n source: \"free\",\n referenceId: null,\n status: active ? \"granted\" : \"pending\",\n startsAt: null,\n expiresAt:\n active && course.accessDays\n ? new Date(this.now().getTime() + course.accessDays * 86_400_000).toISOString()\n : null,\n grantedAt: active ? now : null,\n grantedBy: null,\n },\n },\n cohortIds: [],\n acquisition: {\n orderId: null,\n offerId: course.commerce.offerId,\n paymentStatus: \"not_required\",\n amountMinor: null,\n currency: null,\n providerId: null,\n providerPaymentId: null,\n },\n approvedBy: null,\n decisionAt: active ? now : null,\n decisionReason: null,\n enrolledAt: now,\n approvedAt: active ? now : null,\n completedAt: null,\n accessStartsAt: null,\n accessExpiresAt: null,\n cohortId: null,\n progress: {},\n certificate: null,\n completionLessonIds: [],\n lastActivityAt: now,\n schemaVersion: 1,\n };\n }\n async enroll(actor: Actor | undefined, courseId: string, options: { cohortId?: string } = {}) {\n const current = user(actor);\n let course = await this.course(courseId);\n invariant(course.status === \"published\", \"NOT_FOUND\", \"Course not found.\", 404);\n invariant(\n course.commerce.accessModel === \"free\",\n \"PAID_COURSES_DISABLED\",\n \"Payment processing is not enabled for this course.\",\n 403,\n );\n course = (await this.editions.ensure(courseId)).value;\n invariant(course.status === \"published\", \"NOT_FOUND\", \"Course not found.\", 404);\n const cohort = options.cohortId ? await this.stores.cohorts.get(options.cohortId) : null;\n if (options.cohortId)\n invariant(\n cohort && cohort.courseIds.includes(courseId),\n \"NOT_FOUND\",\n \"This course is not part of that cohort.\",\n 404,\n );\n if (cohort) {\n const membership = await this.stores.cohort_enrollments.get(\n enrollmentKey(cohort.id, current.id),\n );\n invariant(\n membership?.status === \"active\",\n \"COHORT_APPROVAL_REQUIRED\",\n \"Request cohort membership and wait for administrator approval.\",\n 403,\n );\n }\n const id = enrollmentKey(courseId, current.id);\n const existing = await this.stores.enrollments.get(id);\n if (!cohort && existing && [\"active\", \"completed\", \"pending\"].includes(existing.status))\n return await this.enrollmentView(existing);\n invariant(\n course.enrollmentMode !== \"invite\" || cohort,\n \"INVITATION_REQUIRED\",\n \"An administrator must enroll you in this course.\",\n 403,\n );\n invariant(\n !course.enrollmentClosesAt || this.now().getTime() < Date.parse(course.enrollmentClosesAt),\n \"ENROLLMENT_CLOSED\",\n \"Enrollment for this course is closed.\",\n 403,\n );\n await this.assertPrerequisites(course, current.id);\n const enrollment = await change(this.stores.enrollments, id, async (stored) => {\n invariant(\n !stored || ![\"suspended\", \"cancelled\", \"rejected\"].includes(stored.status),\n \"ENROLLMENT_RESTRICTED\",\n \"Contact an administrator to restore your enrollment.\",\n 403,\n );\n const next = structuredClone(\n stored ??\n this.newEnrollment(\n course,\n current,\n !cohort && course.enrollmentMode === \"approval\" ? \"pending\" : \"active\",\n ),\n );\n if (cohort) {\n next.cohortId = cohort.id;\n next.cohortIds = [...new Set([...next.cohortIds, cohort.id])];\n if (!stored || stored.accessStatus !== \"granted\") next.accessSource = \"cohort\";\n if (!stored) delete next.grants.free;\n next.status = next.completedAt ? \"completed\" : \"active\";\n next.accessStatus = \"granted\";\n next.approvedAt ??= this.time();\n next.grants[`cohort:${cohort.id}`] = {\n id: `cohort:${cohort.id}`,\n source: \"cohort\",\n referenceId: cohort.id,\n status: \"granted\",\n startsAt: cohort.startsAt,\n expiresAt: cohort.endsAt,\n grantedAt: this.time(),\n grantedBy: null,\n };\n }\n return { value: next, result: next };\n });\n await this.notify(\n enrollment,\n \"enrolled\",\n enrollment.status === \"pending\" ? \"Enrollment requested\" : \"You are enrolled\",\n `${course.title}: ${enrollment.status === \"pending\" ? \"an administrator will review your request.\" : \"your learning journey is ready.\"}`,\n );\n return await this.enrollmentView(enrollment);\n }\n async adminEnrollment(actor: Actor | undefined, input: unknown) {\n const parsed = parse(enrollmentAdminSchema, input);\n let course = await this.course(parsed.courseId);\n const currentActor = this.instructor(course, actor);\n invariant(\n currentActor.role >= 50,\n \"FORBIDDEN\",\n \"Only administrators can make enrollment decisions.\",\n 403,\n );\n invariant(this.ctx.users, \"USERS_UNAVAILABLE\", \"The user directory is unavailable.\", 503);\n if (\n parsed.status === \"active\" &&\n !(await this.stores.enrollments.get(enrollmentKey(course.id, parsed.userId)))\n )\n course = (await this.editions.ensure(parsed.courseId)).value;\n const target = await this.ctx.users.get(parsed.userId);\n invariant(target, \"NOT_FOUND\", \"Learner account not found.\", 404);\n invariant(\n !parsed.accessStartsAt ||\n !parsed.accessExpiresAt ||\n Date.parse(parsed.accessExpiresAt) > Date.parse(parsed.accessStartsAt),\n \"INVALID_ACCESS_WINDOW\",\n \"Access expiry must follow its start.\",\n );\n if (parsed.cohortId) {\n const cohort = await this.stores.cohorts.get(parsed.cohortId);\n invariant(\n cohort && cohort.courseIds.includes(course.id),\n \"INVALID_COHORT\",\n \"This course must belong to the selected cohort.\",\n );\n }\n if (parsed.status === \"active\") {\n invariant(\n course.status !== \"draft\",\n \"COURSE_NOT_PUBLISHED\",\n \"Publish the course before enrolling learners.\",\n 409,\n );\n invariant(\n course.commerce.accessModel === \"free\",\n \"PAID_COURSES_DISABLED\",\n \"Paid access is not enabled in this release.\",\n 403,\n );\n await this.assertPrerequisites(course, target.id);\n }\n const cohort = parsed.cohortId ? await this.stores.cohorts.get(parsed.cohortId) : null;\n if (cohort && parsed.status === \"active\") {\n const membership = await this.stores.cohort_enrollments.get(\n enrollmentKey(cohort.id, target.id),\n );\n invariant(\n membership?.status === \"active\",\n \"COHORT_APPROVAL_REQUIRED\",\n \"Approve cohort membership before granting cohort access.\",\n 403,\n );\n }\n const settings = await this.settings();\n const enrollment = await change(\n this.stores.enrollments,\n enrollmentKey(course.id, target.id),\n async (stored) => {\n const next = structuredClone(stored ?? this.newEnrollment(course, target, parsed.status));\n const firstApproval = parsed.status === \"active\" && !next.approvedAt;\n next.courseEditionId ??= course.legacyEditionId ?? course.publishedEditionId;\n next.status = parsed.status === \"active\" && next.completedAt ? \"completed\" : parsed.status;\n next.accessStatus =\n parsed.status === \"active\"\n ? \"granted\"\n : parsed.status === \"suspended\"\n ? \"suspended\"\n : \"revoked\";\n next.approvedBy = parsed.status === \"active\" ? currentActor.id : next.approvedBy;\n next.decisionAt = this.time();\n next.decisionReason = parsed.reason || null;\n next.accessSource = stored?.approvedAt ? stored.accessSource : cohort ? \"cohort\" : \"manual\";\n if (firstApproval) {\n next.approvedAt = this.time();\n }\n if (parsed.cohortId !== undefined) next.cohortId = parsed.cohortId;\n if (!cohort && parsed.accessStartsAt !== undefined)\n next.accessStartsAt = parsed.accessStartsAt;\n if (!cohort && parsed.accessExpiresAt !== undefined)\n next.accessExpiresAt = parsed.accessExpiresAt;\n if (parsed.status === \"active\") {\n if (!stored) delete next.grants.free;\n const grantId = cohort ? `cohort:${cohort.id}` : \"manual\";\n const previous = next.grants[grantId];\n next.grants[grantId] = {\n id: grantId,\n source: cohort ? \"cohort\" : \"manual\",\n referenceId: cohort?.id ?? null,\n status: \"granted\",\n startsAt: cohort?.startsAt ?? null,\n expiresAt: cohort\n ? cohort.endsAt\n : parsed.accessExpiresAt !== undefined\n ? parsed.accessExpiresAt\n : previous &&\n (!previous.expiresAt || Date.parse(previous.expiresAt) > this.now().getTime())\n ? previous.expiresAt\n : course.accessDays\n ? new Date(this.now().getTime() + course.accessDays * 86_400_000).toISOString()\n : null,\n grantedAt: previous?.grantedAt ?? this.time(),\n grantedBy: currentActor.id,\n };\n if (cohort) next.cohortIds = [...new Set([...next.cohortIds, cohort.id])];\n }\n invariant(\n !next.accessStartsAt ||\n !next.accessExpiresAt ||\n Date.parse(next.accessExpiresAt) > Date.parse(next.accessStartsAt),\n \"INVALID_ACCESS_WINDOW\",\n \"Access expiry must follow its start.\",\n );\n next.lastActivityAt = this.time();\n if (parsed.status === \"active\")\n this.finish(await this.editions.learning(course, next), next, settings);\n return { value: next, result: next };\n },\n );\n await this.audit(currentActor, \"enrollment.update\", enrollment.id, {\n status: enrollment.status,\n });\n await this.notify(\n enrollment,\n `enrollment-${enrollment.status}-${enrollment.decisionAt}`,\n \"Enrollment updated\",\n `${course.title}: your enrollment is ${enrollment.status}.`,\n );\n await this.projectCompletion(enrollment);\n return staffEnrollment((await this.accessEnrollment(enrollment))!, this.now());\n }\n async learnerCourse(actor: Actor | undefined, courseId: string) {\n const current = user(actor);\n const course = await this.course(courseId);\n const enrollment = await this.stores.enrollments.get(enrollmentKey(courseId, current.id));\n invariant(\n course.status === \"published\" || (course.status === \"archived\" && enrollment),\n \"NOT_FOUND\",\n \"Course not found.\",\n 404,\n );\n if (enrollment?.completedAt) await this.projectCompletion(enrollment);\n return {\n course: await this.detail(await this.editions.learning(course, enrollment)),\n ...(await this.learningState(course, enrollment)),\n };\n }\n async learnerCourseBySlug(actor: Actor | undefined, slug: string) {\n user(actor);\n const page = await this.stores.courses.query({ where: { slug }, limit: 1 });\n invariant(page.items[0], \"NOT_FOUND\", \"Course not found.\", 404);\n return this.learnerCourse(actor, page.items[0].id);\n }\n async dashboard(actor: Actor | undefined, options: { cursor?: string; limit?: number } = {}) {\n const current = user(actor);\n const page = await queryPage(this.stores.enrollments, {\n where: { userId: current.id },\n orderBy: { enrolledAt: \"desc\" },\n limit: options.limit ?? 24,\n cursor: options.cursor,\n });\n const courses = await this.stores.courses.getMany(page.items.map((entry) => entry.courseId));\n const rows = await Promise.all(\n page.items.map(async (enrollment) => {\n if (enrollment.completedAt) await this.projectCompletion(enrollment);\n const course = courses.get(enrollment.courseId);\n return course\n ? {\n course: courseCard(await this.editions.learning(course, enrollment)),\n ...(await this.learningState(course, enrollment)),\n }\n : null;\n }),\n );\n const items = rows.filter((row) => row !== null);\n return { ...page, items };\n }\n async learnerLesson(actor: Actor | undefined, courseId: string, lessonId: string) {\n const current = user(actor);\n const course = await this.course(courseId);\n const enrollment = await this.stores.enrollments.get(enrollmentKey(courseId, current.id));\n const lesson = await this.lessonAccess(course, lessonId, enrollment);\n return {\n course: courseDetail(await this.editions.learning(course, enrollment)),\n lesson: publicLesson(await this.resolveContent(lesson)),\n ...(await this.learningState(course, enrollment)),\n };\n }\n async startLesson(actor: Actor | undefined, courseId: string, lessonId: string) {\n const current = user(actor);\n const course = await this.course(courseId);\n const enrollment = await change(\n this.stores.enrollments,\n enrollmentKey(courseId, current.id),\n async (stored) => {\n await this.lessonAccess(course, lessonId, stored);\n const next = structuredClone(stored!);\n const progress = next.progress[lessonId] ?? emptyProgress();\n progress.startedAt ??= this.time();\n next.progress[lessonId] = progress;\n next.lastActivityAt = this.time();\n return { value: next, result: next };\n },\n );\n return await this.enrollmentView(enrollment);\n }\n async completeLesson(actor: Actor | undefined, courseId: string, lessonId: string) {\n const current = user(actor);\n const course = await this.course(courseId);\n const settings = await this.settings();\n const enrollment = await change(\n this.stores.enrollments,\n enrollmentKey(courseId, current.id),\n async (stored) => {\n const lesson = await this.lessonAccess(course, lessonId, stored);\n invariant(\n ![\"quiz\", \"assignment\"].includes(lesson.kind),\n \"ASSESSMENT_REQUIRED\",\n \"Pass the quiz or assignment to complete this lesson.\",\n 403,\n );\n const next = structuredClone(stored!);\n const progress = next.progress[lessonId] ?? emptyProgress();\n invariant(\n progress.startedAt,\n \"LESSON_NOT_STARTED\",\n \"Start the lesson before marking it complete.\",\n );\n if (lesson.kind === \"live\")\n invariant(\n lesson.liveStartsAt && this.now().getTime() >= Date.parse(lesson.liveStartsAt),\n \"LIVE_NOT_STARTED\",\n \"The live session has not started yet.\",\n 403,\n );\n invariant(\n this.now().getTime() - Date.parse(progress.startedAt) >= lesson.minStudySeconds * 1000,\n \"STUDY_TIME_REQUIRED\",\n \"Spend the minimum study time on this lesson before completing it.\",\n 403,\n );\n progress.completedAt ??= this.time();\n next.progress[lessonId] = progress;\n this.finish(await this.editions.learning(course, next), next, settings);\n return { value: next, result: next };\n },\n );\n await this.projectCompletion(enrollment);\n return {\n ...(await this.learningState(course, enrollment)),\n };\n }\n finish(course: Course, enrollment: Enrollment, settings: LmsSettings): void {\n enrollment.lastActivityAt = this.time();\n const required = lessons(course).filter((lesson) => lesson.required);\n if (\n enrollment.completedAt ||\n !required.length ||\n !required.every((lesson) => enrollment.progress[lesson.id]?.completedAt)\n )\n return;\n enrollment.status = \"completed\";\n enrollment.completedAt = this.time();\n enrollment.completionLessonIds = required.map((lesson) => lesson.id);\n if (course.certificateEnabled)\n enrollment.certificate = {\n id: crypto.randomUUID(),\n courseId: course.id,\n courseTitle: course.title,\n userId: enrollment.userId,\n learnerName: enrollment.learnerName,\n issuerName: settings.issuerName,\n issuedAt: this.time(),\n shared: false,\n revokedAt: null,\n revocationReason: null,\n };\n }\n async projectCompletion(enrollment: Enrollment): Promise<void> {\n if (enrollment.certificate)\n await putOnce(this.stores.certificates, enrollment.certificate.id, {\n enrollmentId: enrollment.id,\n courseId: enrollment.courseId,\n userId: enrollment.userId,\n });\n if (enrollment.completedAt)\n await this.notify(\n enrollment,\n \"completed\",\n \"Course complete\",\n \"You have completed your course. Your achievement is saved in My Learning.\",\n );\n }\n async startQuiz(\n actor: Actor | undefined,\n courseId: string,\n lessonId: string,\n ): Promise<PublicQuizAttempt> {\n const current = user(actor);\n const course = await this.course(courseId);\n const result = await change<Enrollment, { attempt: QuizAttempt | null; blocked: boolean }>(\n this.stores.enrollments,\n enrollmentKey(courseId, current.id),\n async (stored) => {\n const lesson = await this.lessonAccess(course, lessonId, stored);\n invariant(lesson.kind === \"quiz\" && lesson.quiz, \"NOT_A_QUIZ\", \"This lesson has no quiz.\");\n const next = structuredClone(stored!);\n const progress = next.progress[lessonId] ?? emptyProgress();\n progress.startedAt ??= this.time();\n const existing = progress.quizAttempts.find((attempt) => attempt.status === \"in_progress\");\n if (\n existing &&\n (!existing.expiresAt || this.now().getTime() < Date.parse(existing.expiresAt))\n )\n return { value: next, result: { attempt: existing, blocked: false } };\n if (existing) {\n existing.status = \"expired\";\n existing.quiz = null;\n existing.submittedAt = this.time();\n }\n next.progress[lessonId] = progress;\n if (progress.quizAttempts.length >= lesson.quiz.maxAttempts || progress.completedAt)\n return { value: next, result: { attempt: null, blocked: true } };\n const quiz = structuredClone(lesson.quiz);\n const order = quiz.randomizeQuestions ? shuffle(quiz.questions) : quiz.questions;\n const questions = order.map(\n ({ correctAnswers: _correct, explanation: _explanation, ...question }) => ({\n ...question,\n options: quiz.randomizeOptions ? shuffle(question.options) : question.options,\n }),\n );\n const attempt = {\n id: crypto.randomUUID(),\n startedAt: this.time(),\n expiresAt: quiz.timeLimitMinutes\n ? new Date(this.now().getTime() + quiz.timeLimitMinutes * 60_000).toISOString()\n : null,\n status: \"in_progress\" as const,\n quiz,\n questions,\n submittedAt: null,\n score: null,\n maxScore: null,\n percent: null,\n passed: false,\n feedback: [],\n answers: {},\n draftRevision: 0,\n };\n progress.quizAttempts.push(attempt);\n next.lastActivityAt = this.time();\n return { value: next, result: { attempt, blocked: false } };\n },\n );\n invariant(\n result.attempt && !result.blocked,\n \"ATTEMPT_LIMIT\",\n \"No further quiz attempts are available. Contact an instructor.\",\n 403,\n );\n return publicAttempt(result.attempt);\n }\n async submitQuiz(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const parsed = parse(quizSubmitSchema, input);\n const course = await this.course(parsed.courseId);\n const settings = await this.settings();\n const result = await change(\n this.stores.enrollments,\n enrollmentKey(course.id, current.id),\n async (stored) => {\n await this.lessonAccess(course, parsed.lessonId, stored);\n const next = structuredClone(stored!);\n const progress = next.progress[parsed.lessonId];\n const attempt = progress?.quizAttempts.find((item) => item.id === parsed.attemptId);\n invariant(attempt && progress, \"NOT_FOUND\", \"Quiz attempt not found.\", 404);\n if (attempt.status !== \"in_progress\")\n return {\n value: next,\n result: { enrollment: next, attempt, expired: attempt.status === \"expired\" },\n };\n if (attempt.expiresAt && this.now().getTime() >= Date.parse(attempt.expiresAt)) {\n attempt.status = \"expired\";\n attempt.quiz = null;\n attempt.submittedAt = this.time();\n return { value: next, result: { enrollment: next, attempt, expired: true } };\n }\n invariant(attempt.quiz, \"INVALID_ATTEMPT\", \"This attempt cannot be graded.\", 409);\n invariant(\n parsed.expectedDraftRevision === undefined ||\n parsed.expectedDraftRevision === (attempt.draftRevision ?? 0),\n \"DRAFT_CONFLICT\",\n \"Saved answers changed in another request. Review them before submitting.\",\n 409,\n );\n const graded = gradeAnswers(attempt.quiz.questions, parsed.answers);\n attempt.passed = graded.score * 100 >= graded.maxScore * attempt.quiz.passingPercent;\n attempt.feedback =\n attempt.quiz.showFeedback === \"after_submit\" ||\n (attempt.quiz.showFeedback === \"after_pass\" && attempt.passed)\n ? graded.feedback\n : [];\n attempt.score = graded.score;\n attempt.maxScore = graded.maxScore;\n attempt.percent = graded.percent;\n attempt.answers = parsed.answers;\n attempt.status = \"submitted\";\n attempt.submittedAt = this.time();\n attempt.quiz = null;\n if (attempt.passed) progress.completedAt ??= this.time();\n this.finish(await this.editions.learning(course, next), next, settings);\n return { value: next, result: { enrollment: next, attempt, expired: false } };\n },\n );\n invariant(\n !result.expired,\n \"QUIZ_EXPIRED\",\n \"The quiz time limit expired. Start a new attempt if one is available.\",\n 409,\n );\n await this.projectCompletion(result.enrollment);\n return {\n attempt: publicAttempt(result.attempt),\n ...(await this.learningState(course, result.enrollment)),\n };\n }\n async saveQuizDraft(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const parsed = parse(quizDraftSchema, input);\n const course = await this.course(parsed.courseId);\n const draftRevision = await change(\n this.stores.enrollments,\n enrollmentKey(course.id, current.id),\n async (stored) => {\n await this.lessonAccess(course, parsed.lessonId, stored);\n const next = structuredClone(stored!);\n const attempt = next.progress[parsed.lessonId]?.quizAttempts.find(\n (item) => item.id === parsed.attemptId,\n );\n invariant(\n attempt?.status === \"in_progress\" && attempt.quiz,\n \"ATTEMPT_CLOSED\",\n \"This quiz attempt is closed.\",\n 409,\n );\n invariant(\n !attempt.expiresAt || this.now().getTime() < Date.parse(attempt.expiresAt),\n \"QUIZ_EXPIRED\",\n \"This quiz attempt has expired.\",\n 409,\n );\n gradeAnswers(attempt.quiz.questions, parsed.answers);\n const revision = attempt.draftRevision ?? 0;\n const canonical = (answers: typeof parsed.answers) =>\n JSON.stringify(Object.entries(answers).sort(([a], [b]) => a.localeCompare(b)));\n // Retrying a successfully saved request whose response was lost is harmless.\n if (\n revision === parsed.expectedDraftRevision + 1 &&\n canonical(attempt.answers) === canonical(parsed.answers)\n )\n return { value: next, result: revision };\n invariant(\n revision === parsed.expectedDraftRevision,\n \"DRAFT_CONFLICT\",\n \"Saved answers changed in another request. Review them before saving.\",\n 409,\n );\n attempt.answers = parsed.answers;\n attempt.draftRevision = revision + 1;\n return { value: next, result: attempt.draftRevision };\n },\n );\n return { saved: true, draftRevision };\n }\n async assignmentState(\n actor: Actor | undefined,\n courseId: string,\n lessonId: string,\n ): Promise<AssignmentState> {\n const current = user(actor);\n const course = await this.course(courseId);\n const enrollment = await this.stores.enrollments.get(enrollmentKey(courseId, current.id));\n const lesson = await this.lessonAccess(course, lessonId, enrollment);\n invariant(\n lesson.kind === \"assignment\" && lesson.assignment,\n \"NOT_AN_ASSIGNMENT\",\n \"This lesson has no assignment.\",\n );\n const progress = enrollment!.progress[lessonId] ?? emptyProgress();\n const blocked = assignmentBlock(lesson.assignment, progress, this.now());\n return {\n draft: progress.assignmentDraft ?? emptyAssignmentDraft(),\n submission: progress.assignments.at(-1) ?? null,\n canSubmit: !blocked,\n closedReason: blocked?.message ?? null,\n };\n }\n async saveAssignmentDraft(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const parsed = parse(assignmentDraftSchema, input);\n const course = await this.course(parsed.courseId);\n const draftRevision = await change(\n this.stores.enrollments,\n enrollmentKey(course.id, current.id),\n async (stored) => {\n const lesson = await this.lessonAccess(course, parsed.lessonId, stored);\n invariant(\n lesson.kind === \"assignment\" && lesson.assignment,\n \"NOT_AN_ASSIGNMENT\",\n \"This lesson has no assignment.\",\n );\n const next = structuredClone(stored!);\n const progress = next.progress[parsed.lessonId] ?? emptyProgress();\n const draft = progress.assignmentDraft ?? emptyAssignmentDraft();\n const blocked = assignmentBlock(lesson.assignment, progress, this.now());\n if (blocked) throw new LmsError(blocked.code, blocked.message, blocked.status);\n if (\n draft.revision === parsed.expectedDraftRevision + 1 &&\n draft.body === parsed.body &&\n draft.attachmentUrl === parsed.attachmentUrl\n )\n return { value: next, result: draft.revision };\n invariant(\n draft.revision === parsed.expectedDraftRevision,\n \"DRAFT_CONFLICT\",\n \"Your assignment draft changed in another request. Review it before saving.\",\n 409,\n );\n progress.assignmentDraft = {\n body: parsed.body,\n attachmentUrl: parsed.attachmentUrl,\n revision: draft.revision + 1,\n updatedAt: this.time(),\n };\n next.progress[parsed.lessonId] = progress;\n return { value: next, result: progress.assignmentDraft.revision };\n },\n );\n return { saved: true, draftRevision };\n }\n async submitAssignment(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const parsed = parse(assignmentSubmitSchema, input);\n const course = await this.course(parsed.courseId);\n const enrollment = await change(\n this.stores.enrollments,\n enrollmentKey(course.id, current.id),\n async (stored) => {\n const lesson = await this.lessonAccess(course, parsed.lessonId, stored);\n invariant(\n lesson.kind === \"assignment\" && lesson.assignment,\n \"NOT_AN_ASSIGNMENT\",\n \"This lesson has no assignment.\",\n );\n const next = structuredClone(stored!);\n const progress = next.progress[parsed.lessonId] ?? emptyProgress();\n const previous =\n parsed.submissionId &&\n progress.assignments.find((item) => item.id === parsed.submissionId);\n if (previous) {\n invariant(\n previous.body === parsed.body && previous.attachmentUrl === parsed.attachmentUrl,\n \"SUBMISSION_CONFLICT\",\n \"This submission ID already belongs to different work.\",\n 409,\n );\n return { value: next, result: next };\n }\n const blocked = assignmentBlock(lesson.assignment, progress, this.now());\n if (blocked) throw new LmsError(blocked.code, blocked.message, blocked.status);\n const draft = progress.assignmentDraft ?? emptyAssignmentDraft();\n invariant(\n parsed.expectedDraftRevision === undefined\n ? !draft.body && !draft.attachmentUrl\n : draft.revision === parsed.expectedDraftRevision,\n \"DRAFT_CONFLICT\",\n \"Your assignment draft changed. Review it before submitting.\",\n 409,\n );\n progress.startedAt ??= this.time();\n progress.assignments.push({\n id: parsed.submissionId ?? crypto.randomUUID(),\n body: parsed.body,\n attachmentUrl: parsed.attachmentUrl,\n submittedAt: this.time(),\n status: \"submitted\",\n maxPoints: lesson.assignment.maxPoints,\n passingPoints: lesson.assignment.passingPoints,\n points: null,\n feedback: \"\",\n gradedAt: null,\n gradedBy: null,\n rubric: structuredClone(lesson.assignment.rubric ?? []),\n rubricScores: [],\n });\n // Retain the revision after consuming a draft so a stale tab cannot recreate it.\n progress.assignmentDraft = {\n body: \"\",\n attachmentUrl: null,\n revision: draft.revision + 1,\n updatedAt: this.time(),\n };\n next.progress[parsed.lessonId] = progress;\n next.lastActivityAt = this.time();\n return { value: next, result: next };\n },\n );\n return await this.enrollmentView(enrollment);\n }\n async gradeAssignment(actor: Actor | undefined, input: unknown) {\n const parsed = parse(gradeSchema, input);\n const course = await this.course(parsed.courseId);\n const current = this.instructor(course, actor);\n const settings = await this.settings();\n const enrollment = await change(\n this.stores.enrollments,\n enrollmentKey(course.id, parsed.userId),\n async (stored) => {\n invariant(stored, \"NOT_FOUND\", \"Enrollment not found.\", 404);\n const next = structuredClone(stored);\n const progress = next.progress[parsed.lessonId];\n const submission = progress?.assignments.find((item) => item.id === parsed.submissionId);\n invariant(submission && progress, \"NOT_FOUND\", \"Assignment submission not found.\", 404);\n invariant(\n submission.status === \"submitted\",\n \"ALREADY_GRADED\",\n \"This submission has already been graded.\",\n 409,\n );\n const grade = assignmentGrade(submission, parsed.points, parsed.rubricScores);\n submission.points = grade.points;\n submission.rubricScores = grade.rubricScores;\n submission.feedback = parsed.feedback;\n submission.status = \"graded\";\n submission.gradedAt = this.time();\n submission.gradedBy = current.id;\n if (grade.points >= submission.passingPoints) progress.completedAt ??= this.time();\n const access = effectiveAccess((await this.accessEnrollment(next))!, this.now());\n if (\n [\"active\", \"completed\"].includes(next.status) &&\n ![\"suspended\", \"revoked\", \"pending\"].includes(access)\n )\n this.finish(await this.editions.learning(course, next), next, settings);\n return { value: next, result: next };\n },\n );\n await this.projectCompletion(enrollment);\n await this.notify(\n enrollment,\n `graded-${parsed.submissionId}`,\n \"Assignment graded\",\n \"Your assignment feedback is ready in My Learning.\",\n );\n await this.audit(current, \"assignment.grade\", enrollment.id, {\n lessonId: parsed.lessonId,\n submissionId: parsed.submissionId,\n points: enrollment.progress[parsed.lessonId]!.assignments.find(\n (item) => item.id === parsed.submissionId,\n )!.points,\n });\n return staffEnrollment((await this.accessEnrollment(enrollment))!, this.now());\n }\n async saveNote(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const parsed = parse(noteSchema, input);\n const course = await this.course(parsed.courseId);\n const enrollment = await change(\n this.stores.enrollments,\n enrollmentKey(course.id, current.id),\n async (stored) => {\n await this.lessonAccess(course, parsed.lessonId, stored);\n const next = structuredClone(stored!);\n const progress = next.progress[parsed.lessonId] ?? emptyProgress();\n const revision = progress.noteRevision ?? 0;\n if (revision === parsed.expectedRevision + 1 && progress.note === parsed.note)\n return { value: next, result: next };\n invariant(\n revision === parsed.expectedRevision,\n \"NOTE_CONFLICT\",\n \"Your lesson notes changed in another tab. Copy your work before reloading to compare it with the saved notes.\",\n 409,\n );\n progress.note = parsed.note;\n progress.noteRevision = revision + 1;\n next.progress[parsed.lessonId] = progress;\n return { value: next, result: next };\n },\n );\n return {\n note: enrollment.progress[parsed.lessonId]!.note,\n revision: enrollment.progress[parsed.lessonId]!.noteRevision ?? 0,\n };\n }\n async certificate(actor: Actor | undefined, courseId: string) {\n const current = user(actor);\n const enrollment = await this.stores.enrollments.get(enrollmentKey(courseId, current.id));\n invariant(enrollment?.certificate, \"NOT_FOUND\", \"Certificate not found.\", 404);\n await this.projectCompletion(enrollment);\n return enrollment.certificate;\n }\n async ownedCertificate(actor: Actor | undefined, id: string) {\n const current = user(actor);\n const index = await this.stores.certificates.get(id);\n invariant(index?.userId === current.id, \"NOT_FOUND\", \"Certificate not found.\", 404);\n const enrollment = await this.stores.enrollments.get(index.enrollmentId);\n invariant(enrollment?.certificate?.id === id, \"NOT_FOUND\", \"Certificate not found.\", 404);\n return enrollment.certificate;\n }\n async verifyCertificate(id: string) {\n const index = await this.stores.certificates.get(id);\n invariant(index, \"NOT_FOUND\", \"Certificate not found.\", 404);\n const enrollment = await this.stores.enrollments.get(index.enrollmentId);\n const certificate = enrollment?.certificate;\n invariant(certificate?.id === id, \"NOT_FOUND\", \"Certificate not found.\", 404);\n return {\n id,\n courseTitle: certificate.courseTitle,\n issuerName: certificate.issuerName,\n issuedAt: certificate.issuedAt,\n valid: !certificate.revokedAt,\n shared: certificate.shared,\n learnerName: certificate.shared ? certificate.learnerName : null,\n };\n }\n async shareCertificate(actor: Actor | undefined, courseId: string, shared: boolean) {\n const current = user(actor);\n const certificate = await change(\n this.stores.enrollments,\n enrollmentKey(courseId, current.id),\n async (stored) => {\n invariant(stored?.certificate, \"NOT_FOUND\", \"Certificate not found.\", 404);\n const next = structuredClone(stored);\n next.certificate!.shared = shared;\n return { value: next, result: next.certificate! };\n },\n );\n return certificate;\n }\n async revokeCertificate(\n actor: Actor | undefined,\n courseId: string,\n userId: string,\n reason: string,\n ) {\n const course = await this.course(courseId);\n const current = this.instructor(course, actor);\n const certificate = await change(\n this.stores.enrollments,\n enrollmentKey(courseId, userId),\n async (stored) => {\n invariant(stored?.certificate, \"NOT_FOUND\", \"Certificate not found.\", 404);\n const next = structuredClone(stored);\n next.certificate!.revokedAt = this.time();\n next.certificate!.revocationReason = reason;\n return { value: next, result: next.certificate! };\n },\n );\n await this.audit(current, \"certificate.revoke\", certificate.id);\n return certificate;\n }\n async cancelEnrollment(actor: Actor | undefined, courseId: string) {\n const current = user(actor);\n const enrollment = await change(\n this.stores.enrollments,\n enrollmentKey(courseId, current.id),\n async (stored) => {\n invariant(stored, \"NOT_FOUND\", \"Enrollment not found.\", 404);\n const next = { ...stored, status: \"cancelled\" as const, accessStatus: \"revoked\" as const };\n return { value: next, result: next };\n },\n );\n return await this.enrollmentView(enrollment);\n }\n async discussions(actor: Actor | undefined, courseId: string, lessonId: string, cursor?: string) {\n const current = user(actor);\n const course = await this.course(courseId);\n const enrollment = await this.stores.enrollments.get(enrollmentKey(courseId, current.id));\n await this.lessonAccess(course, lessonId, enrollment);\n const page = await queryPage(this.stores.discussions, {\n where: { courseId, lessonId, status: \"published\" },\n orderBy: { createdAt: \"asc\" },\n cursor,\n limit: 30,\n });\n const own = await queryPage(this.stores.discussions, {\n where: { courseId, lessonId, authorId: current.id, status: \"pending\" },\n limit: 30,\n });\n return { ...page, pending: own.items };\n }\n async postDiscussion(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const parsed = parse(discussionSchema, input);\n const course = await this.course(parsed.courseId);\n const enrollment = await this.stores.enrollments.get(enrollmentKey(course.id, current.id));\n await this.lessonAccess(course, parsed.lessonId, enrollment);\n if (parsed.parentId) {\n const parent = await this.stores.discussions.get(parsed.parentId);\n invariant(\n parent &&\n parent.courseId === course.id &&\n parent.lessonId === parsed.lessonId &&\n parent.status === \"published\" &&\n !parent.parentId,\n \"INVALID_PARENT\",\n \"Reply to a published discussion in this lesson.\",\n );\n }\n const settings = await this.settings();\n const id = crypto.randomUUID();\n const discussion: Discussion = {\n ...parsed,\n id,\n authorId: current.id,\n authorName: current.name || \"Learner\",\n status: settings.moderateDiscussions ? \"pending\" : \"published\",\n createdAt: this.time(),\n updatedAt: this.time(),\n };\n await this.stores.discussions.put(id, discussion);\n return discussion;\n }\n async publicReviews(courseId: string, cursor?: string) {\n const course = await this.course(courseId);\n invariant(course.status === \"published\", \"NOT_FOUND\", \"Course not found.\", 404);\n return queryPage(this.stores.reviews, {\n where: { courseId, status: \"published\" },\n orderBy: { createdAt: \"desc\" },\n limit: 24,\n cursor,\n });\n }\n async postReview(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const parsed = parse(reviewSchema, input);\n const course = await this.course(parsed.courseId);\n const enrollment = await this.accessEnrollment(\n await this.stores.enrollments.get(enrollmentKey(course.id, current.id)),\n );\n assertEnrollmentAccess(enrollment, this.now());\n invariant(\n Object.values(enrollment.progress).some((progress) => progress.completedAt),\n \"REVIEW_TOO_EARLY\",\n \"Complete at least one lesson before reviewing this course.\",\n 403,\n );\n const settings = await this.settings();\n const id = enrollmentKey(course.id, current.id);\n const review = await change(this.stores.reviews, id, async (stored) => {\n const next: Review = {\n ...parsed,\n id,\n userId: current.id,\n authorName: current.name || \"Learner\",\n status: settings.moderateReviews ? \"pending\" : \"published\",\n createdAt: stored?.createdAt ?? this.time(),\n updatedAt: this.time(),\n };\n return { value: next, result: next };\n });\n return review;\n }\n async moderate(actor: Actor | undefined, collection: \"reviews\" | \"discussions\", input: unknown) {\n const parsed = parse(moderateSchema, input);\n const store = this.stores[collection];\n const stored = await store.getVersioned(parsed.id);\n invariant(stored, \"NOT_FOUND\", \"Community item not found.\", 404);\n const course = await this.course(stored.value.courseId);\n const current = this.instructor(course, actor);\n invariant(\n stored.revision === parsed.expectedRevision,\n \"CONFLICT\",\n \"This item changed. Reload before moderating.\",\n 409,\n );\n const next = { ...stored.value, status: parsed.status, updatedAt: this.time() };\n const saved = await (store as typeof this.stores.discussions).compareAndSet(\n parsed.id,\n stored.revision,\n next as Discussion,\n );\n invariant(saved.applied, \"CONFLICT\", \"This item changed while moderating.\", 409);\n await this.audit(current, `${collection}.moderate`, parsed.id, { status: parsed.status });\n return next;\n }\n async communityQueue(\n actor: Actor | undefined,\n collection: \"reviews\" | \"discussions\",\n options: { courseId?: string; cursor?: string; status?: string } = {},\n ) {\n const current = user(actor);\n if (options.courseId) this.instructor(await this.course(options.courseId), current);\n else invariant(current.role >= 50, \"FORBIDDEN\", \"Select a course you teach.\", 403);\n const store = this.stores[collection];\n const page = await store.query({\n where: {\n ...(options.courseId ? { courseId: options.courseId } : {}),\n ...(options.status ? { status: options.status } : {}),\n },\n orderBy: { createdAt: \"desc\" },\n limit: 30,\n cursor: options.cursor,\n });\n const versions = await Promise.all(page.items.map((item) => store.getVersioned(item.id)));\n const items = versions.flatMap((item) =>\n item ? [{ ...item.value, revision: item.revision }] : [],\n );\n return { ...page, items };\n }\n async enrollments(\n actor: Actor | undefined,\n options: { courseId?: string; status?: string; cursor?: string; limit?: number } = {},\n ) {\n const current = user(actor);\n if (options.courseId) this.instructor(await this.course(options.courseId), current);\n else invariant(current.role >= 50, \"FORBIDDEN\", \"Select a course you teach.\", 403);\n const page = await queryPage(this.stores.enrollments, {\n where: {\n ...(options.courseId ? { courseId: options.courseId } : {}),\n ...(options.status ? { status: options.status } : {}),\n },\n orderBy: { enrolledAt: \"desc\" },\n limit: options.limit ?? 30,\n cursor: options.cursor,\n });\n const courses = await this.stores.courses.getMany(page.items.map((item) => item.courseId));\n return {\n ...page,\n items: await Promise.all(\n page.items.map(async (enrollment) => {\n const resolved = await this.accessEnrollment(enrollment);\n const root = courses.get(enrollment.courseId);\n const course = root ? await this.editions.learning(root, enrollment) : null;\n const now = this.now();\n return {\n ...staffEnrollment(resolved!, now),\n learnerEmail: enrollment.learnerEmail,\n courseTitle: course?.title ?? \"Archived course\",\n lessonTitles: Object.fromEntries(\n (course ? lessons(course) : []).map((lesson) => [lesson.id, lesson.title]),\n ),\n progressSummary: course ? progressSummary(course, resolved, now) : null,\n };\n }),\n ),\n };\n }\n async report(actor: Actor | undefined, courseId?: string) {\n const current = user(actor);\n if (courseId) this.instructor(await this.course(courseId), current);\n else invariant(current.role >= 50, \"FORBIDDEN\", \"Select a course you teach.\", 403);\n const filter: NonNullable<StorageQueryOptions[\"where\"]> = courseId ? { courseId } : {};\n const statuses = [\"pending\", \"active\", \"completed\", \"rejected\", \"suspended\", \"cancelled\"];\n const counts = await Promise.all(\n statuses.map((status) => this.stores.enrollments.count({ ...filter, status })),\n );\n const statusCounts = Object.fromEntries(\n statuses.map((status, index) => [status, counts[index]!]),\n );\n const total = counts.reduce((sum, count) => sum + count, 0);\n const completedEver = await this.stores.enrollments.count({\n ...filter,\n completedAt: { gte: \"0000-01-01T00:00:00.000Z\" },\n });\n const sourceCounts = Object.fromEntries(\n await Promise.all(\n [\"free\", \"manual\", \"cohort\", \"purchase\"].map(async (accessSource) => [\n accessSource,\n await this.stores.enrollments.count({ ...filter, accessSource }),\n ]),\n ),\n );\n const accessDecisions = Object.fromEntries(\n await Promise.all(\n [\"pending\", \"granted\", \"suspended\", \"revoked\"].map(async (accessStatus) => [\n accessStatus,\n await this.stores.enrollments.count({ ...filter, accessStatus }),\n ]),\n ),\n );\n return {\n total,\n completedEver,\n ...statusCounts,\n accessSources: sourceCounts,\n accessDecisions,\n completionRate: total ? Math.round((completedEver / total) * 100) : 0,\n courseCount: courseId ? 1 : await this.stores.courses.count(),\n generatedAt: this.time(),\n };\n }\n async enrollmentCsv(\n actor: Actor | undefined,\n options: { courseId?: string; status?: string } = {},\n ) {\n const lines = [enrollmentCsvHeader];\n let cursor: string | undefined;\n let rows = 0;\n let bytes = new TextEncoder().encode(enrollmentCsvHeader).byteLength + 3;\n do {\n const page = await this.enrollments(actor, { ...options, cursor, limit: 100 });\n const next = enrollmentCsvLines(page.items);\n rows += next.length;\n bytes += new TextEncoder().encode(next.join(\"\\r\\n\")).byteLength + next.length * 2;\n invariant(\n rows <= 10_000 && bytes <= 6_000_000,\n \"EXPORT_TOO_LARGE\",\n \"Filter by course or enrollment status to export fewer records.\",\n 413,\n );\n lines.push(...next);\n cursor = page.hasMore ? page.cursor : undefined;\n } while (cursor);\n return \"\\uFEFF\" + lines.join(\"\\r\\n\");\n }\n async cohortList(actor: Actor | undefined, cursor?: string) {\n const current = user(actor);\n invariant(current.role >= 50, \"FORBIDDEN\", \"Only administrators can manage cohorts.\", 403);\n const page = await this.stores.cohorts.query({\n orderBy: { createdAt: \"desc\" },\n limit: 24,\n cursor,\n });\n const versions = await Promise.all(\n page.items.map((item) => this.stores.cohorts.getVersioned(item.id)),\n );\n const items = versions.flatMap((item) =>\n item ? [{ ...item.value, revision: item.revision }] : [],\n );\n return { ...page, items };\n }\n async publicCohort(id: string) {\n const cohort = await this.stores.cohorts.get(id);\n invariant(cohort, \"NOT_FOUND\", \"Cohort not found.\", 404);\n const courses = await this.stores.courses.getMany(cohort.courseIds);\n return {\n id: cohort.id,\n title: cohort.title,\n startsAt: cohort.startsAt,\n endsAt: cohort.endsAt,\n courses: [...courses.values()]\n .filter((course) => course.status === \"published\" && course.commerce.accessModel === \"free\")\n .map(courseCard),\n };\n }\n async requestCohort(actor: Actor | undefined, cohortId: string) {\n const current = user(actor);\n const cohort = await this.stores.cohorts.get(cohortId);\n invariant(cohort, \"NOT_FOUND\", \"Cohort not found.\", 404);\n invariant(\n !cohort.endsAt || this.now().getTime() < Date.parse(cohort.endsAt),\n \"COHORT_CLOSED\",\n \"This cohort has ended.\",\n 403,\n );\n const id = enrollmentKey(cohortId, current.id);\n return change(this.stores.cohort_enrollments, id, async (stored) => {\n invariant(\n !stored || ![\"rejected\", \"suspended\", \"cancelled\"].includes(stored.status),\n \"COHORT_RESTRICTED\",\n \"Contact an administrator to restore your cohort membership.\",\n 403,\n );\n const next: CohortMembership = stored ?? {\n id,\n cohortId,\n userId: current.id,\n learnerName: current.name || \"Learner\",\n learnerEmail: current.email,\n status: \"pending\",\n requestedAt: this.time(),\n decidedAt: null,\n decidedBy: null,\n reason: null,\n };\n return { value: next, result: next };\n });\n }\n async cohortMemberships(\n actor: Actor | undefined,\n options: { cohortId?: string; status?: string; cursor?: string } = {},\n ) {\n invariant(\n user(actor).role >= 50,\n \"FORBIDDEN\",\n \"Only administrators can review cohort membership.\",\n 403,\n );\n return queryPage(this.stores.cohort_enrollments, {\n where: {\n ...(options.cohortId ? { cohortId: options.cohortId } : {}),\n ...(options.status ? { status: options.status } : {}),\n },\n orderBy: { requestedAt: \"desc\" },\n limit: 30,\n cursor: options.cursor,\n });\n }\n async decideCohort(\n actor: Actor | undefined,\n cohortId: string,\n userId: string,\n status: \"active\" | \"rejected\" | \"suspended\" | \"cancelled\",\n reason = \"\",\n ) {\n const current = user(actor);\n invariant(\n current.role >= 50,\n \"FORBIDDEN\",\n \"Only administrators can make cohort decisions.\",\n 403,\n );\n const cohort = await this.stores.cohorts.get(cohortId);\n invariant(cohort && this.ctx.users, \"NOT_FOUND\", \"Cohort not found.\", 404);\n const target = await this.ctx.users.get(userId);\n invariant(target, \"NOT_FOUND\", \"Learner account not found.\", 404);\n const membership = await change(\n this.stores.cohort_enrollments,\n enrollmentKey(cohortId, userId),\n async (stored) => {\n const next: CohortMembership = {\n ...(stored ?? {\n id: enrollmentKey(cohortId, userId),\n cohortId,\n userId,\n learnerName: target.name || \"Learner\",\n learnerEmail: target.email,\n requestedAt: this.time(),\n }),\n status,\n decidedAt: this.time(),\n decidedBy: current.id,\n reason: reason || null,\n };\n return { value: next, result: next };\n },\n );\n const results: { courseId: string; success: boolean; error?: string }[] = [];\n for (const courseId of cohort.courseIds) {\n try {\n if (status === \"active\") {\n const existing = await this.stores.enrollments.get(enrollmentKey(courseId, userId));\n invariant(\n !existing || ![\"suspended\", \"cancelled\", \"rejected\"].includes(existing.status),\n \"ENROLLMENT_RESTRICTED\",\n \"Restore this course enrollment separately before granting cohort access.\",\n 403,\n );\n await this.adminEnrollment(current, { courseId, userId, cohortId, status: \"active\" });\n } else {\n const id = enrollmentKey(courseId, userId);\n const exists = await this.stores.enrollments.exists(id);\n if (exists)\n await change(this.stores.enrollments, id, async (stored) => {\n invariant(stored, \"NOT_FOUND\", \"Enrollment not found.\", 404);\n const next = structuredClone(stored);\n const grant = next.grants[`cohort:${cohortId}`];\n if (grant) grant.status = status === \"suspended\" ? \"suspended\" : \"revoked\";\n return { value: next, result: true };\n });\n }\n results.push({ courseId, success: true });\n } catch (error) {\n results.push({\n courseId,\n success: false,\n error: error instanceof LmsError ? error.message : \"Access update failed.\",\n });\n }\n }\n await this.audit(current, \"cohort.decision\", membership.id, { status });\n return { membership, results };\n }\n async saveCohort(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n invariant(current.role >= 50, \"FORBIDDEN\", \"Only administrators can manage cohorts.\", 403);\n const parsed = parse(cohortSaveSchema, input);\n for (const id of parsed.data.courseIds) await this.course(id);\n const id = parsed.id ?? crypto.randomUUID();\n const existing = await this.stores.cohorts.getVersioned(id);\n invariant(\n (existing?.revision ?? null) === parsed.expectedRevision,\n \"CONFLICT\",\n \"The cohort changed. Reload before saving.\",\n 409,\n );\n if (existing && (await this.stores.cohort_enrollments.count({ cohortId: id }))) {\n invariant(\n existing.value.courseIds.every((courseId) => parsed.data.courseIds.includes(courseId)),\n \"COHORT_IN_USE\",\n \"Cohorts with membership records must retain existing courses. Create a new cohort to remove courses.\",\n 409,\n );\n }\n const cohort: Cohort = {\n ...parsed.data,\n id,\n createdBy: existing?.value.createdBy ?? current.id,\n createdAt: existing?.value.createdAt ?? this.time(),\n updatedAt: this.time(),\n };\n const saved = await this.stores.cohorts.compareAndSet(id, existing?.revision ?? null, cohort);\n invariant(saved.applied, \"CONFLICT\", \"The cohort changed while saving.\", 409);\n await this.audit(current, \"cohort.save\", id);\n return { cohort, revision: saved.revision };\n }\n async applyCohort(\n actor: Actor | undefined,\n id: string,\n expectedRevision: string,\n offset: number,\n ) {\n const current = user(actor);\n invariant(current.role >= 50, \"FORBIDDEN\", \"Only administrators can enroll cohorts.\", 403);\n const stored = await this.stores.cohorts.getVersioned(id);\n invariant(stored, \"NOT_FOUND\", \"Cohort not found.\", 404);\n invariant(\n stored.revision === expectedRevision,\n \"CONFLICT\",\n \"The cohort changed. Reload before continuing.\",\n 409,\n );\n const cohort = stored.value;\n const members = cohort.memberIds.slice(\n offset,\n offset + Math.max(1, Math.floor(10 / cohort.courseIds.length)),\n );\n const results: { userId: string; courseId: string; success: boolean; error?: string }[] = [];\n for (const userId of members) {\n try {\n const decided = await this.decideCohort(current, id, userId, \"active\");\n results.push(...decided.results.map((result) => ({ userId, ...result })));\n } catch (error) {\n results.push({\n userId,\n courseId: \"\",\n success: false,\n error: error instanceof LmsError ? error.message : \"Enrollment failed.\",\n });\n }\n }\n const nextOffset = offset + members.length;\n return { results, nextOffset, hasMore: nextOffset < cohort.memberIds.length };\n }\n async notify(enrollment: Enrollment, key: string, title: string, message: string): Promise<void> {\n const settings = await this.settings();\n const preferences = await this.stores.preferences.get(enrollment.userId);\n const id = `${enrollment.id}:${key}`;\n const notification: Notification = {\n id,\n userId: enrollment.userId,\n email: enrollment.learnerEmail,\n title,\n message,\n courseId: enrollment.courseId,\n read: false,\n createdAt: this.time(),\n emailStatus:\n settings.emailNotifications && preferences?.emailNotifications !== false\n ? \"pending\"\n : \"disabled\",\n emailAttempts: 0,\n claimedAt: null,\n };\n await putOnce(this.stores.notifications, id, notification);\n }\n async notifications(actor: Actor | undefined, cursor?: string) {\n const current = user(actor);\n const page = await queryPage(this.stores.notifications, {\n where: { userId: current.id },\n orderBy: { createdAt: \"desc\" },\n limit: 30,\n cursor,\n });\n return {\n ...page,\n items: page.items.map(\n ({\n email: _email,\n emailStatus: _status,\n emailAttempts: _attempts,\n claimedAt: _claimed,\n ...item\n }) => item,\n ),\n };\n }\n async readNotification(actor: Actor | undefined, id: string) {\n const current = user(actor);\n await change(this.stores.notifications, id, async (stored) => {\n invariant(\n stored && stored.userId === current.id,\n \"NOT_FOUND\",\n \"Notification not found.\",\n 404,\n );\n return { value: { ...stored, read: true }, result: true };\n });\n return { read: true };\n }\n async deliverNotifications(): Promise<{ sent: number; failed: number }> {\n const settings = await this.settings();\n if (!settings.emailNotifications || !this.ctx.email) return { sent: 0, failed: 0 };\n const cutoff = new Date(this.now().getTime() - 15 * 60_000).toISOString();\n const abandoned = await this.stores.notifications.query({\n where: { emailStatus: \"sending\", claimedAt: { lte: cutoff } },\n limit: 20,\n });\n let failed = 0;\n for (const item of abandoned.items) {\n const recovered = await this.stores.notifications.updateIf(item.id, {\n where: { emailStatus: \"sending\", claimedAt: { lte: cutoff } },\n set: { emailStatus: \"failed\" },\n });\n if (recovered.applied) failed++;\n }\n const page = await this.stores.notifications.query({\n where: { emailStatus: \"pending\" },\n limit: 20,\n });\n let sent = 0;\n for (const item of page.items) {\n if (item.data.emailAttempts >= 3) continue;\n const claimedAt = this.time();\n const emailAttempts = item.data.emailAttempts + 1;\n const claim = await this.stores.notifications.updateIf(item.id, {\n where: { emailStatus: \"pending\", emailAttempts: item.data.emailAttempts },\n set: { emailStatus: \"sending\", claimedAt },\n delta: { emailAttempts: { inc: 1 } },\n });\n if (!claim.applied) continue;\n try {\n if ((await this.stores.preferences.get(item.data.userId))?.emailNotifications === false) {\n await this.stores.notifications.updateIf(item.id, {\n where: { emailStatus: \"sending\", emailAttempts, claimedAt },\n set: { emailStatus: \"disabled\" },\n });\n continue;\n }\n await this.ctx.email.send({\n to: item.data.email,\n subject: item.data.title,\n text: item.data.message,\n });\n await this.stores.notifications.updateIf(item.id, {\n where: { emailStatus: \"sending\", emailAttempts, claimedAt },\n set: { emailStatus: \"sent\" },\n });\n sent++;\n } catch {\n await this.stores.notifications.updateIf(item.id, {\n where: { emailStatus: \"sending\", emailAttempts, claimedAt },\n set: { emailStatus: \"failed\" },\n });\n failed++;\n }\n }\n return { sent, failed };\n }\n async assignments(actor: Actor | undefined, courseId: string, cursor?: string) {\n const page = await this.enrollments(actor, { courseId, cursor, limit: 30 });\n return {\n ...page,\n items: page.items.flatMap((enrollment) =>\n Object.entries(enrollment.progress).flatMap(([lessonId, progress]) =>\n progress.assignments.map((submission) => ({\n ...submission,\n userId: enrollment.userId,\n learnerName: enrollment.learnerName,\n courseId,\n lessonId,\n })),\n ),\n ),\n };\n }\n async exportCourse(actor: Actor | undefined, id: string) {\n const { course } = await this.editableCourse(actor, id);\n const data = courseDataSchema.parse({ ...course, status: \"draft\" });\n return { format: \"anantagnanam-lms-course\", version: 1, course: data };\n }\n async importCourse(actor: Actor | undefined, input: unknown) {\n const parsed = parse(\n z.object({\n format: z.literal(\"anantagnanam-lms-course\"),\n version: z.literal(1),\n course: courseSaveSchema.shape.data,\n }),\n input,\n );\n return this.saveCourse(actor, {\n data: { ...parsed.course, status: \"draft\" },\n expectedRevision: null,\n });\n }\n async deleteCourse(actor: Actor | undefined, id: string, expectedRevision: string) {\n const stored = await this.stores.courses.getVersioned(id);\n invariant(stored, \"NOT_FOUND\", \"Course not found.\", 404);\n const current = this.instructor(stored.value, actor);\n invariant(\n stored.value.status === \"draft\" &&\n (await this.stores.enrollments.count({ courseId: id })) === 0,\n \"COURSE_IN_USE\",\n \"Only drafts without enrollments can be deleted. Archive other courses.\",\n 409,\n );\n const deleted = await this.stores.courses.compareAndDelete(id, expectedRevision);\n invariant(deleted.applied, \"CONFLICT\", \"The course changed. Reload before deleting.\", 409);\n await this.audit(current, \"course.delete\", id);\n return { deleted: true };\n }\n async retryNotification(actor: Actor | undefined, id: string) {\n invariant(\n user(actor).role >= 50,\n \"FORBIDDEN\",\n \"Only administrators can retry email delivery.\",\n 403,\n );\n const result = await this.stores.notifications.updateIf(id, {\n where: { emailStatus: \"failed\", emailAttempts: { lt: 3 } },\n set: { emailStatus: \"pending\", claimedAt: null },\n });\n invariant(result.applied, \"RETRY_UNAVAILABLE\", \"This notification cannot be retried.\", 409);\n await this.audit(actor!, \"notification.retry\", id);\n return { queued: true };\n }\n async notificationQueue(\n actor: Actor | undefined,\n options: { status?: Notification[\"emailStatus\"]; cursor?: string; limit?: number } = {},\n ) {\n invariant(user(actor).role >= 50, \"FORBIDDEN\", \"Administrator access required.\", 403);\n return queryPage(this.stores.notifications, {\n where: options.status ? { emailStatus: options.status } : {},\n orderBy: { createdAt: \"desc\" },\n limit: options.limit ?? 30,\n cursor: options.cursor,\n });\n }\n async auditList(actor: Actor | undefined, options: { cursor?: string; limit?: number } = {}) {\n invariant(user(actor).role >= 50, \"FORBIDDEN\", \"Administrator access required.\", 403);\n return queryPage(this.stores.audit, {\n orderBy: { createdAt: \"desc\" },\n limit: options.limit ?? 30,\n cursor: options.cursor,\n });\n }\n async reminders(): Promise<number> {\n const settings = await this.settings();\n if (!settings.reminderDays) return 0;\n const cutoff = new Date(\n this.now().getTime() - settings.reminderDays * 86_400_000,\n ).toISOString();\n const cursor = await this.ctx.kv.get<string>(\"reminders:cursor\");\n const page = await this.stores.enrollments.query({\n where: { status: \"active\" },\n limit: 30,\n cursor: cursor ?? undefined,\n });\n const week = Math.floor(this.now().getTime() / (7 * 86_400_000));\n let queued = 0;\n for (const item of page.items) {\n if (\n item.data.lastActivityAt > cutoff ||\n effectiveAccess((await this.accessEnrollment(item.data))!, this.now()) !== \"granted\"\n )\n continue;\n await this.notify(\n item.data,\n `reminder-${week}`,\n \"Continue your learning\",\n \"Your next lesson is waiting in My Learning.\",\n );\n queued++;\n }\n if (page.hasMore && page.cursor) await this.ctx.kv.set(\"reminders:cursor\", page.cursor);\n else await this.ctx.kv.delete(\"reminders:cursor\");\n return queued;\n }\n}\n","import { z } from \"zod\";\nimport { PLUGIN_VERSION } from \"./constants.js\";\nimport { courseCard, emptyProgress } from \"./curriculum.js\";\nimport { invariant } from \"./errors.js\";\nimport { parse, type LmsService } from \"./service.js\";\nimport { assertSize, change, enrollmentKey, putOnce, queryPage } from \"./store.js\";\nimport {\n courseIdSchema,\n idSchema,\n pageSchema,\n pathSaveSchema,\n playbackSchema,\n preferencesSchema,\n} from \"./schemas.js\";\nimport type { Actor, LearningPath, LearnerPath, LmsHealth, PublicPath } from \"./types.js\";\n\nexport const exportSchema = pageSchema.extend({\n section: z.enum([\n \"enrollments\",\n \"discussions\",\n \"reviews\",\n \"notifications\",\n \"saved_courses\",\n \"path_enrollments\",\n \"cohort_enrollments\",\n ]),\n});\n\nfunction user(actor: Actor | undefined): Actor {\n invariant(actor, \"UNAUTHORIZED\", \"Sign in to continue.\", 401);\n return actor;\n}\nfunction admin(actor: Actor | undefined): Actor {\n const current = user(actor);\n invariant(current.role >= 50, \"FORBIDDEN\", \"Administrator access required.\", 403);\n return current;\n}\nfunction pathPage(input: unknown) {\n const options = parse(pageSchema, input);\n // Each path expands up to 30 courses and their immutable enrollment editions.\n return { ...options, limit: Math.min(options.limit, 8) };\n}\n\n/** Optional learning features share the existing access and immutable-edition service. */\nexport class LearningFeatures {\n constructor(readonly service: LmsService) {}\n get stores() {\n return this.service.stores;\n }\n\n async preferences(actor: Actor | undefined) {\n const stored = await this.stores.preferences.getVersioned(user(actor).id);\n return {\n value: preferencesSchema.parse(stored?.value ?? {}),\n revision: stored?.revision ?? null,\n };\n }\n async savePreferences(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const parsed = parse(\n z.object({ data: preferencesSchema, expectedRevision: z.string().max(128).nullable() }),\n input,\n );\n const saved = await this.stores.preferences.compareAndSet(\n current.id,\n parsed.expectedRevision,\n parsed.data,\n );\n invariant(\n saved.applied,\n \"CONFLICT\",\n \"Preferences changed in another tab. Reload before saving.\",\n 409,\n );\n return { value: parsed.data, revision: saved.revision };\n }\n async saveCourse(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const parsed = parse(courseIdSchema.extend({ saved: z.boolean() }), input);\n if (parsed.saved) {\n const course = await this.service.course(parsed.courseId);\n invariant(course.status === \"published\", \"NOT_FOUND\", \"Course not found.\", 404);\n }\n await change(this.stores.saved_courses, enrollmentKey(parsed.courseId, current.id), () => ({\n value: {\n userId: current.id,\n courseId: parsed.courseId,\n saved: parsed.saved,\n updatedAt: this.service.time(),\n },\n result: true,\n }));\n return { saved: parsed.saved };\n }\n async savedCourses(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const options = parse(pageSchema, input);\n const page = await queryPage(this.stores.saved_courses, {\n ...options,\n where: { userId: current.id, saved: true },\n orderBy: { updatedAt: \"desc\" },\n });\n return {\n ...page,\n items: await Promise.all(\n page.items.map(async (item) => ({\n courseId: item.courseId,\n course: await this.card(item.courseId),\n })),\n ),\n };\n }\n private async card(courseId: string) {\n const course = await this.stores.courses.get(courseId);\n return course?.status === \"published\" ? courseCard(course) : null;\n }\n async savePath(actor: Actor | undefined, input: unknown) {\n const current = admin(actor);\n const parsed = parse(pathSaveSchema, input);\n const id = parsed.id ?? crypto.randomUUID();\n const existing = await this.stores.learning_paths.getVersioned(id);\n invariant(!parsed.id || existing, \"NOT_FOUND\", \"Learning path not found.\", 404);\n invariant(\n (existing?.revision ?? null) === parsed.expectedRevision,\n \"CONFLICT\",\n \"This path changed. Reload before saving.\",\n 409,\n );\n for (const courseId of parsed.data.courseIds) {\n const course = await this.service.course(courseId);\n invariant(\n parsed.data.status !== \"published\" || course.status === \"published\",\n \"VALIDATION_ERROR\",\n \"Publish all courses before publishing the path.\",\n );\n }\n const path: LearningPath = {\n ...parsed.data,\n id,\n createdAt: existing?.value.createdAt ?? this.service.time(),\n updatedAt: this.service.time(),\n };\n assertSize(path);\n const saved = await this.stores.learning_paths.compareAndSet(id, parsed.expectedRevision, path);\n invariant(saved.applied, \"CONFLICT\", \"This path changed. Reload before saving.\", 409);\n await this.service.audit(current, \"path.save\", id);\n return { path, revision: saved.revision };\n }\n async adminPaths(actor: Actor | undefined, input: unknown) {\n admin(actor);\n const page = await this.stores.learning_paths.query({\n ...parse(pageSchema, input),\n orderBy: { updatedAt: \"desc\" },\n });\n const versions = await Promise.all(\n page.items.map((item) => this.stores.learning_paths.getVersioned(item.id)),\n );\n return {\n ...page,\n items: versions.flatMap((item) => (item ? [{ ...item.value, revision: item.revision }] : [])),\n };\n }\n async paths(input: unknown) {\n const page = await queryPage(this.stores.learning_paths, {\n ...pathPage(input),\n where: { status: \"published\" },\n orderBy: { updatedAt: \"desc\" },\n });\n const items: PublicPath[] = await Promise.all(\n page.items.map(async (path) => ({\n id: path.id,\n title: path.title,\n description: path.description,\n courses: await Promise.all(path.courseIds.map((id) => this.card(id))),\n })),\n );\n return { ...page, items };\n }\n async joinPath(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const { pathId } = parse(z.object({ pathId: idSchema }), input);\n const id = enrollmentKey(pathId, current.id);\n const existing = await this.stores.path_enrollments.get(id);\n if (existing) return { joined: true };\n const path = await this.stores.learning_paths.get(pathId);\n invariant(path?.status === \"published\", \"NOT_FOUND\", \"Learning path not found.\", 404);\n // A path records intent. Only the normal course enrollment flow grants access.\n await putOnce(this.stores.path_enrollments, id, {\n pathId,\n userId: current.id,\n title: path.title,\n courseIds: path.courseIds,\n joinedAt: this.service.time(),\n });\n return { joined: true };\n }\n async learnerPaths(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const page = await queryPage(this.stores.path_enrollments, {\n ...pathPage(input),\n where: { userId: current.id },\n orderBy: { joinedAt: \"desc\" },\n });\n const items: LearnerPath[] = await Promise.all(\n page.items.map(async (path) => {\n const courses = await Promise.all(\n path.courseIds.map(async (courseId) => {\n const enrollment = await this.stores.enrollments.get(\n enrollmentKey(courseId, current.id),\n );\n const course = await this.stores.courses.get(courseId);\n return {\n course:\n course && enrollment\n ? courseCard(await this.service.editions.learning(course, enrollment))\n : await this.card(courseId),\n completed: Boolean(enrollment?.completedAt),\n };\n }),\n );\n return {\n pathId: path.pathId,\n title: path.title,\n joinedAt: path.joinedAt,\n total: courses.length,\n completed: courses.filter((c) => c.completed).length,\n courses,\n };\n }),\n );\n return { ...page, items };\n }\n async playback(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const parsed = parse(playbackSchema, input);\n const course = await this.service.course(parsed.courseId);\n return change(this.stores.enrollments, enrollmentKey(course.id, current.id), async (stored) => {\n const lesson = await this.service.lessonAccess(course, parsed.lessonId, stored);\n invariant(\n [\"audio\", \"video\"].includes(lesson.kind),\n \"VALIDATION_ERROR\",\n \"Only audio and video lessons store playback positions.\",\n );\n const next = structuredClone(stored!);\n const progress = next.progress[lesson.id] ?? emptyProgress();\n invariant(\n (progress.playback?.revision ?? 0) === parsed.expectedRevision,\n \"CONFLICT\",\n \"Playback changed in another tab. Reload to resume saving.\",\n 409,\n );\n const playback = {\n positionSeconds: parsed.positionSeconds,\n playbackRate: parsed.playbackRate,\n revision: parsed.expectedRevision + 1,\n updatedAt: this.service.time(),\n };\n progress.playback = playback;\n next.progress[lesson.id] = progress;\n next.lastActivityAt = this.service.time();\n // A player report never proves attendance or completes a lesson.\n return { value: next, result: playback };\n });\n }\n async exportData(actor: Actor | undefined, input: unknown) {\n const current = user(actor);\n const parsed = parse(exportSchema, input);\n const { section, cursor } = parsed;\n const where: Record<string, string> =\n section === \"discussions\" ? { authorId: current.id } : { userId: current.id };\n // Small pages bound the worst case when an enrollment contains many attempts.\n const options = { where, cursor, limit: Math.min(parsed.limit, 5) };\n let page: { items: unknown[]; cursor?: string; hasMore: boolean };\n if (section === \"enrollments\") {\n const raw = await queryPage(this.stores.enrollments, options);\n page = {\n ...raw,\n items: await Promise.all(\n raw.items.map((enrollment) => this.service.enrollmentView(enrollment)),\n ),\n };\n } else {\n // Keep the union narrowed: every collection has an explicit owner predicate.\n const raw = await this.stores[section].query(options);\n page = { ...raw, items: raw.items.map((item) => item.data) };\n }\n return {\n format: \"anantagnanam-lms-learner\",\n version: 1,\n exportedAt: this.service.time(),\n section,\n profile: { id: current.id, name: current.name, email: current.email },\n preferences: (await this.preferences(current)).value,\n ...page,\n };\n }\n async health(actor: Actor | undefined): Promise<LmsHealth> {\n admin(actor);\n const [maintenance, pendingEmails, failedEmails, pending, settings, migration] =\n await Promise.all([\n this.service.ctx.kv.get<LmsHealth[\"maintenance\"]>(\"maintenanceHealth\"),\n this.stores.notifications.count({ emailStatus: \"pending\" }),\n this.stores.notifications.count({ emailStatus: \"failed\" }),\n this.stores.notifications.query({\n where: { emailStatus: \"pending\" },\n orderBy: { createdAt: \"asc\" },\n limit: 1,\n }),\n this.service.settings(),\n this.service.migrationStatus(actor),\n ]);\n return {\n version: PLUGIN_VERSION,\n maintenance,\n maintenanceStale:\n !maintenance?.completedAt ||\n this.service.now().getTime() - Date.parse(maintenance.completedAt) > 45 * 60_000,\n pendingEmails,\n failedEmails,\n oldestPendingAt: pending.items[0]?.data.createdAt ?? null,\n emailEnabled: settings.emailNotifications,\n migration,\n };\n }\n}\n","import type { Actor, CourseAgenda, Notebook, StudyNote } from \"./types.js\";\nimport { assertEnrollmentAccess, lessonAvailability, lessons } from \"./curriculum.js\";\nimport { invariant } from \"./errors.js\";\nimport { parse, type LmsService } from \"./service.js\";\nimport { change, enrollmentKey, queryPage } from \"./store.js\";\nimport { courseIdSchema, pageSchema, studyNoteSchema } from \"./schemas.js\";\n\nfunction owner(actor: Actor | undefined) {\n invariant(actor, \"UNAUTHORIZED\", \"Sign in to continue.\", 401);\n return actor;\n}\n\n/** Personal study data is retained separately from access and completion decisions. */\nexport class StudyTools {\n constructor(readonly service: LmsService) {}\n async courses(actor: Actor | undefined, input: unknown) {\n const current = owner(actor);\n const page = await queryPage(this.service.stores.enrollments, {\n ...parse(pageSchema, input),\n where: { userId: current.id },\n orderBy: { enrolledAt: \"desc\" },\n });\n return {\n ...page,\n items: await Promise.all(\n page.items.map(async (enrollment) => {\n const source = await this.service.stores.courses.get(enrollment.courseId);\n const course = source ? await this.service.editions.learning(source, enrollment) : null;\n return {\n courseId: enrollment.courseId,\n title: course?.title ?? \"Unavailable course\",\n slug: course?.slug ?? null,\n };\n }),\n ),\n };\n }\n async notebook(actor: Actor | undefined, input: unknown): Promise<Notebook> {\n const current = owner(actor);\n const { courseId } = parse(courseIdSchema, input);\n const stored = await this.service.stores.enrollments.get(enrollmentKey(courseId, current.id));\n invariant(stored, \"NOT_FOUND\", \"Your course notebook was not found.\", 404);\n const source = await this.service.stores.courses.get(courseId);\n const course = source ? await this.service.editions.learning(source, stored) : null;\n const resolved = await this.service.accessEnrollment(stored);\n const all = course ? lessons(course) : [];\n return {\n course: {\n id: courseId,\n title: course?.title ?? \"Unavailable course\",\n slug: course?.slug ?? \"\",\n },\n notes: stored.studyNotes ?? [],\n summaries: Object.entries(stored.progress).flatMap(([lessonId, progress]) =>\n progress.note\n ? [\n {\n lessonId,\n lessonTitle: all.find((lesson) => lesson.id === lessonId)?.title ?? \"Lesson\",\n body: progress.note,\n },\n ]\n : [],\n ),\n availableLessonIds: course\n ? all\n .filter(\n (lesson) =>\n lessonAvailability(course, lesson.id, resolved, this.service.now()).available,\n )\n .map((lesson) => lesson.id)\n : [],\n };\n }\n async saveNote(actor: Actor | undefined, input: unknown): Promise<StudyNote> {\n const current = owner(actor);\n const parsed = parse(studyNoteSchema, input);\n const course = await this.service.course(parsed.courseId);\n return change(\n this.service.stores.enrollments,\n enrollmentKey(course.id, current.id),\n async (stored) => {\n const lesson = await this.service.lessonAccess(course, parsed.lessonId, stored);\n invariant(\n parsed.positionSeconds === null || [\"video\", \"audio\"].includes(lesson.kind),\n \"INVALID_TIMESTAMP\",\n \"Timestamps belong to audio or video lessons.\",\n );\n const next = structuredClone(stored!);\n const notes = (next.studyNotes ??= []);\n const existing = notes.find((note) => note.id === parsed.id);\n invariant(\n !existing || existing.lessonId === lesson.id,\n \"NOTE_CONFLICT\",\n \"This note belongs to another lesson.\",\n 409,\n );\n const same =\n existing &&\n existing.body === parsed.body &&\n existing.positionSeconds === parsed.positionSeconds &&\n existing.starred === parsed.starred &&\n existing.archived === parsed.archived;\n if (existing && same && existing.revision === parsed.expectedRevision + 1)\n return { value: next, result: existing };\n invariant(\n (existing?.revision ?? 0) === parsed.expectedRevision,\n \"NOTE_CONFLICT\",\n \"This note changed in another tab. Your edits are still here; reload the saved note to compare.\",\n 409,\n );\n invariant(\n existing || notes.length < 100,\n \"NOTE_LIMIT\",\n \"This course notebook has reached its limit of 100 notes, including archived notes. Edit an existing note instead.\",\n 409,\n );\n const note: StudyNote = {\n id: parsed.id,\n lessonId: lesson.id,\n lessonTitle: existing?.lessonTitle ?? lesson.title,\n body: parsed.body,\n positionSeconds: parsed.positionSeconds,\n starred: parsed.starred,\n archived: parsed.archived,\n revision: (existing?.revision ?? 0) + 1,\n createdAt: existing?.createdAt ?? this.service.time(),\n updatedAt: this.service.time(),\n };\n if (existing) notes[notes.indexOf(existing)] = note;\n else notes.push(note);\n invariant(\n new TextEncoder().encode(JSON.stringify(notes)).byteLength <= 150_000,\n \"NOTE_LIMIT\",\n \"This course notebook has reached its storage limit. Shorten an existing note to make room; archived notes still count.\",\n 409,\n );\n return { value: next, result: note };\n },\n );\n }\n async agenda(actor: Actor | undefined, input: unknown): Promise<CourseAgenda> {\n const current = owner(actor);\n const { courseId } = parse(courseIdSchema, input);\n const source = await this.service.course(courseId);\n const stored = await this.service.stores.enrollments.get(enrollmentKey(courseId, current.id));\n const enrollment = await this.service.accessEnrollment(stored);\n assertEnrollmentAccess(enrollment, this.service.now());\n const course = await this.service.editions.learning(source, enrollment);\n const now = this.service.now();\n const events: CourseAgenda[\"events\"] = [];\n for (const lesson of lessons(course)) {\n const access = lessonAvailability(course, lesson.id, enrollment, now);\n const progress = enrollment.progress[lesson.id];\n const append = (\n kind: \"assignment\" | \"live\" | \"release\",\n startsAt: string,\n endsAt: string | null,\n ) => {\n const past = Date.parse(startsAt) < now.getTime();\n events.push({\n id: `${course.id}-${lesson.id}-${kind}`,\n kind,\n lessonId: lesson.id,\n title: lesson.title,\n startsAt,\n endsAt,\n available: access.available,\n reason: access.reason,\n status: progress?.completedAt\n ? \"completed\"\n : kind === \"assignment\" && progress?.assignments.at(-1)?.status === \"submitted\"\n ? \"submitted\"\n : past\n ? kind === \"assignment\"\n ? \"overdue\"\n : \"past\"\n : \"upcoming\",\n });\n };\n if (lesson.assignment?.dueAt) append(\"assignment\", lesson.assignment.dueAt, null);\n if (lesson.kind === \"live\" && lesson.liveStartsAt)\n append(\n \"live\",\n lesson.liveStartsAt,\n new Date(\n Date.parse(lesson.liveStartsAt) + Math.max(1, lesson.durationMinutes) * 60_000,\n ).toISOString(),\n );\n if (access.code === \"LESSON_LOCKED\" && access.releaseAt)\n append(\"release\", access.releaseAt, null);\n }\n events.sort(\n (a, b) => Date.parse(a.startsAt) - Date.parse(b.startsAt) || a.id.localeCompare(b.id),\n );\n return {\n course: { id: course.id, title: course.title, slug: course.slug },\n generatedAt: now.toISOString(),\n events,\n };\n }\n}\n","import type { CourseAgenda } from \"./types.js\";\n\nconst text = (value: string) =>\n value\n .replace(/\\r\\n|\\r/g, \"\\n\")\n .replace(/[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f]/g, \"\")\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/\\n/g, \"\\\\n\")\n .replace(/;/g, \"\\\\;\")\n .replace(/,/g, \"\\\\,\");\nconst date = (value: string) =>\n new Date(value)\n .toISOString()\n .replace(/[-:]/g, \"\")\n .replace(/\\.\\d{3}Z$/, \"Z\");\n/** RFC 5545 folding counts UTF-8 octets, including the continuation space. */\nexport function foldCalendarLine(value: string): string {\n const encoder = new TextEncoder();\n let line = \"\";\n let bytes = 0;\n const lines: string[] = [];\n for (const char of value) {\n const size = encoder.encode(char).byteLength;\n if (bytes + size > 75) {\n lines.push(line);\n line = \" \";\n bytes = 1;\n }\n line += char;\n bytes += size;\n }\n lines.push(line);\n return lines.join(\"\\r\\n\");\n}\n/** A private downloadable snapshot: no subscription token, meeting URL or alarm. */\nexport function courseCalendar(agenda: CourseAgenda, origin: string, basePath: string): string {\n const lines = [\n \"BEGIN:VCALENDAR\",\n \"VERSION:2.0\",\n \"PRODID:-//Ananta Gnanam//LMS//EN\",\n \"CALSCALE:GREGORIAN\",\n \"METHOD:PUBLISH\",\n ];\n for (const event of agenda.events.filter((item) => item.status === \"upcoming\")) {\n const kind = { assignment: \"Assignment due\", live: \"Live session\", release: \"Lesson release\" }[\n event.kind\n ];\n const url = new URL(\n `${basePath}/courses/${encodeURIComponent(agenda.course.slug)}/lessons/${encodeURIComponent(event.lessonId)}`,\n origin,\n ).href;\n lines.push(\n \"BEGIN:VEVENT\",\n `UID:${event.id}@anantagnanam-lms`,\n `DTSTAMP:${date(agenda.generatedAt)}`,\n `DTSTART:${date(event.startsAt)}`,\n );\n if (event.endsAt) lines.push(`DTEND:${date(event.endsAt)}`);\n lines.push(\n `SUMMARY:${text(`${kind}: ${event.title}`)}`,\n `DESCRIPTION:${text(`${agenda.course.title}. Check the course for current access and requirements. This downloaded calendar does not update automatically.`)}`,\n `URL:${url}`,\n \"CLASS:PRIVATE\",\n `STATUS:${event.available ? \"CONFIRMED\" : \"TENTATIVE\"}`,\n \"END:VEVENT\",\n );\n }\n lines.push(\"END:VCALENDAR\");\n return lines.map(foldCalendarLine).join(\"\\r\\n\") + \"\\r\\n\";\n}\n","import {\n definePlugin,\n definePluginRoute,\n pluginResponse,\n type PluginRoute,\n type RouteContext,\n} from \"emdash\";\nimport { z } from \"zod\";\nimport { PLUGIN_ID, PLUGIN_VERSION, storage, adminPages } from \"./constants.js\";\nimport { LmsService, parse } from \"./service.js\";\nimport { invariant } from \"./errors.js\";\nimport {\n pageSchema,\n courseIdSchema,\n lessonIdSchema,\n courseSaveSchema,\n idSchema,\n keySchema,\n enrollmentRequestSchema,\n enrollmentAdminSchema,\n gradeSchema,\n certificateActionSchema,\n certificateRevokeSchema,\n} from \"./schemas.js\";\nimport type { LmsOptions } from \"./types.js\";\nimport { normalizeLmsOptions } from \"./config.js\";\nimport { LearningFeatures } from \"./learning.js\";\nimport { StudyTools } from \"./study.js\";\nimport { courseCalendar } from \"./calendar.js\";\n\nconst listSchema = pageSchema.extend({\n courseId: idSchema.optional(),\n status: z\n .enum([\"pending\", \"active\", \"completed\", \"rejected\", \"suspended\", \"cancelled\"])\n .optional(),\n});\nconst communityListSchema = pageSchema.extend({\n courseId: idSchema.optional(),\n status: z.enum([\"pending\", \"published\", \"hidden\"]).optional(),\n});\nconst slugSchema = z.object({ slug: z.string().min(1).max(100) });\nconst keyInput = z.object({ id: keySchema });\nconst courseRevisionInput = z.object({\n id: idSchema,\n expectedRevision: z.string().min(1).max(128),\n});\n\nfunction get(\n handler: (ctx: RouteContext, service: LmsService) => Promise<unknown>,\n publicRoute = false,\n permission: PluginRoute[\"permission\"] = \"content:read\",\n): PluginRoute {\n return definePluginRoute({\n methods: [\"GET\"],\n public: publicRoute,\n permission,\n request: { body: \"none\" },\n handler: async (ctx) => handler(ctx, new LmsService(ctx)),\n });\n}\nfunction post(\n handler: (ctx: RouteContext, service: LmsService) => Promise<unknown>,\n permission: PluginRoute[\"permission\"] = \"content:read\",\n): PluginRoute {\n return definePluginRoute({\n methods: [\"POST\"],\n permission,\n request: { body: \"json\", maxBytes: 1_000_000 },\n handler: async (ctx) => handler(ctx, new LmsService(ctx)),\n });\n}\n\nexport function createPlugin(_options: LmsOptions = {}) {\n const options = normalizeLmsOptions(_options);\n return definePlugin({\n id: PLUGIN_ID,\n version: PLUGIN_VERSION,\n capabilities: [\"schema:read\", \"content:read\", \"users:read\", \"email:send\"],\n storage,\n admin: {\n entry: \"@anantagnanam/lms/admin\",\n pages: adminPages,\n widgets: [{ id: \"learning\", title: \"Learning\", size: \"half\" }],\n },\n hooks: {\n \"plugin:install\": async (_event, ctx) => {\n await ctx.kv.compareAndSet(\"schemaVersion\", null, 1);\n },\n \"plugin:activate\": async (_event, ctx) => {\n await ctx.cron?.schedule(\"maintenance\", { schedule: \"*/15 * * * *\" });\n },\n \"plugin:deactivate\": async (_event, ctx) => {\n await ctx.cron?.cancel(\"maintenance\");\n },\n cron: async (event, ctx) => {\n if (event.name === \"maintenance\") {\n const service = new LmsService(ctx);\n const startedAt = service.time();\n try {\n await service.reminders();\n await service.deliverNotifications();\n await ctx.kv.set(\"maintenanceHealth\", {\n startedAt,\n completedAt: service.time(),\n failed: false,\n });\n } catch (error) {\n await ctx.kv.set(\"maintenanceHealth\", { startedAt, completedAt: null, failed: true });\n throw error;\n }\n }\n },\n },\n routes: {\n \"learner/study/courses\": get((ctx, service) =>\n new StudyTools(service).courses(ctx.user, ctx.input),\n ),\n \"learner/notebook\": get((ctx, service) =>\n new StudyTools(service).notebook(ctx.user, ctx.input),\n ),\n \"learner/notebook/save\": post((ctx, service) =>\n new StudyTools(service).saveNote(ctx.user, ctx.input),\n ),\n \"learner/agenda\": get((ctx, service) => new StudyTools(service).agenda(ctx.user, ctx.input)),\n \"learner/agenda/calendar\": definePluginRoute({\n methods: [\"GET\"],\n permission: \"content:read\",\n request: { body: \"none\" },\n response: \"raw\",\n handler: async (ctx) => {\n const agenda = await new StudyTools(new LmsService(ctx)).agenda(ctx.user, ctx.input);\n return pluginResponse({\n body: {\n kind: \"text\",\n value: courseCalendar(agenda, new URL(ctx.request.url).origin, options.basePath),\n },\n headers: {\n \"Content-Type\": \"text/calendar; charset=utf-8\",\n \"Content-Disposition\": 'attachment; filename=\"lms-course-agenda.ics\"',\n },\n });\n },\n }),\n paths: get(async (ctx, service) => new LearningFeatures(service).paths(ctx.input), true),\n \"learner/paths\": get(async (ctx, service) =>\n new LearningFeatures(service).learnerPaths(ctx.user, ctx.input),\n ),\n \"learner/path/join\": post(async (ctx, service) =>\n new LearningFeatures(service).joinPath(ctx.user, ctx.input),\n ),\n \"learner/saved\": get(async (ctx, service) =>\n new LearningFeatures(service).savedCourses(ctx.user, ctx.input),\n ),\n \"learner/saved/save\": post(async (ctx, service) =>\n new LearningFeatures(service).saveCourse(ctx.user, ctx.input),\n ),\n \"learner/preferences\": get(async (ctx, service) =>\n new LearningFeatures(service).preferences(ctx.user),\n ),\n \"learner/preferences/save\": post(async (ctx, service) =>\n new LearningFeatures(service).savePreferences(ctx.user, ctx.input),\n ),\n \"learner/playback\": post(async (ctx, service) =>\n new LearningFeatures(service).playback(ctx.user, ctx.input),\n ),\n \"learner/export\": get(async (ctx, service) =>\n new LearningFeatures(service).exportData(ctx.user, ctx.input),\n ),\n \"admin/paths\": get(\n async (ctx, service) => new LearningFeatures(service).adminPaths(ctx.user, ctx.input),\n false,\n \"plugins:manage\",\n ),\n \"admin/path/save\": post(\n async (ctx, service) => new LearningFeatures(service).savePath(ctx.user, ctx.input),\n \"plugins:manage\",\n ),\n \"admin/health\": get(\n async (ctx, service) => new LearningFeatures(service).health(ctx.user),\n false,\n \"plugins:manage\",\n ),\n academy: get(async (_ctx, service) => {\n const settings = await service.settings();\n return {\n title: settings.academyTitle,\n description: settings.academyDescription,\n basePath: options.basePath,\n };\n }, true),\n catalog: get(async (ctx, service) => service.catalog(ctx.input), true),\n course: get(\n async (ctx, service) => service.publicCourse(parse(slugSchema, ctx.input).slug),\n true,\n ),\n cohort: get(\n async (ctx, service) =>\n service.publicCohort(parse(z.object({ id: idSchema }), ctx.input).id),\n true,\n ),\n preview: get(async (ctx, service) => {\n const input = parse(lessonIdSchema, ctx.input);\n return service.preview(input.courseId, input.lessonId);\n }, true),\n reviews: get(async (ctx, service) => {\n const input = parse(courseIdSchema.extend({ cursor: z.string().optional() }), ctx.input);\n return service.publicReviews(input.courseId, input.cursor);\n }, true),\n \"certificates/verify\": get(\n async (ctx, service) =>\n service.verifyCertificate(parse(z.object({ id: idSchema }), ctx.input).id),\n true,\n ),\n \"learner/dashboard\": get(async (ctx, service) =>\n service.dashboard(ctx.user, parse(pageSchema, ctx.input)),\n ),\n \"learner/course\": get(async (ctx, service) =>\n service.learnerCourse(ctx.user, parse(courseIdSchema, ctx.input).courseId),\n ),\n \"learner/course-by-slug\": get(async (ctx, service) =>\n service.learnerCourseBySlug(ctx.user, parse(slugSchema, ctx.input).slug),\n ),\n \"learner/lesson\": get(async (ctx, service) => {\n const input = parse(lessonIdSchema, ctx.input);\n return service.learnerLesson(ctx.user, input.courseId, input.lessonId);\n }),\n \"learner/enroll\": post(async (ctx, service) => {\n const input = parse(enrollmentRequestSchema, ctx.input);\n return service.enroll(ctx.user, input.courseId, { cohortId: input.cohortId });\n }),\n \"learner/cohort/request\": post(async (ctx, service) =>\n service.requestCohort(\n ctx.user,\n parse(z.object({ cohortId: idSchema }), ctx.input).cohortId,\n ),\n ),\n \"learner/cancel\": post(async (ctx, service) =>\n service.cancelEnrollment(ctx.user, parse(courseIdSchema, ctx.input).courseId),\n ),\n \"learner/start\": post(async (ctx, service) => {\n const input = parse(lessonIdSchema, ctx.input);\n return service.startLesson(ctx.user, input.courseId, input.lessonId);\n }),\n \"learner/complete\": post(async (ctx, service) => {\n const input = parse(lessonIdSchema, ctx.input);\n return service.completeLesson(ctx.user, input.courseId, input.lessonId);\n }),\n \"learner/quiz/start\": post(async (ctx, service) => {\n const input = parse(lessonIdSchema, ctx.input);\n return service.startQuiz(ctx.user, input.courseId, input.lessonId);\n }),\n \"learner/quiz/submit\": post(async (ctx, service) => service.submitQuiz(ctx.user, ctx.input)),\n \"learner/quiz/draft\": post(async (ctx, service) =>\n service.saveQuizDraft(ctx.user, ctx.input),\n ),\n \"learner/assignment\": post(async (ctx, service) =>\n service.submitAssignment(ctx.user, ctx.input),\n ),\n \"learner/assignment/state\": get(async (ctx, service) => {\n const input = parse(lessonIdSchema, ctx.input);\n return service.assignmentState(ctx.user, input.courseId, input.lessonId);\n }),\n \"learner/assignment/draft\": post(async (ctx, service) =>\n service.saveAssignmentDraft(ctx.user, ctx.input),\n ),\n \"learner/note\": post(async (ctx, service) => service.saveNote(ctx.user, ctx.input)),\n \"learner/discussions\": get(async (ctx, service) => {\n const input = parse(lessonIdSchema.extend({ cursor: z.string().optional() }), ctx.input);\n return service.discussions(ctx.user, input.courseId, input.lessonId, input.cursor);\n }),\n \"learner/discussion\": post(async (ctx, service) =>\n service.postDiscussion(ctx.user, ctx.input),\n ),\n \"learner/review\": post(async (ctx, service) => service.postReview(ctx.user, ctx.input)),\n \"learner/certificate\": get(async (ctx, service) =>\n service.certificate(ctx.user, parse(courseIdSchema, ctx.input).courseId),\n ),\n \"learner/certificate-by-id\": get(async (ctx, service) =>\n service.ownedCertificate(ctx.user, parse(z.object({ id: idSchema }), ctx.input).id),\n ),\n \"learner/cohort\": get(async (ctx, service) => {\n const input = parse(z.object({ cohortId: idSchema }), ctx.input);\n invariant(ctx.user, \"UNAUTHORIZED\", \"Sign in to continue.\", 401);\n return service.stores.cohort_enrollments.get(`${input.cohortId}:${ctx.user.id}`);\n }),\n \"learner/certificate/share\": post(async (ctx, service) => {\n const input = parse(certificateActionSchema, ctx.input);\n return service.shareCertificate(ctx.user, input.courseId, input.shared);\n }),\n \"learner/notifications\": get(async (ctx, service) =>\n service.notifications(ctx.user, parse(pageSchema, ctx.input).cursor),\n ),\n \"learner/notification/read\": post(async (ctx, service) =>\n service.readNotification(ctx.user, parse(keyInput, ctx.input).id),\n ),\n \"admin/courses\": get(async (ctx, service) =>\n service.adminCourses(\n ctx.user,\n parse(\n pageSchema.extend({ status: z.enum([\"draft\", \"published\", \"archived\"]).optional() }),\n ctx.input,\n ),\n ),\n ),\n \"admin/course\": get(async (ctx, service) =>\n service.editableCourse(ctx.user, parse(z.object({ id: idSchema }), ctx.input).id),\n ),\n \"admin/course/save\": post(async (ctx, service) => service.saveCourse(ctx.user, ctx.input)),\n \"admin/course/publish\": post(async (ctx, service) => {\n const input = parse(courseRevisionInput, ctx.input);\n return service.publishCourse(ctx.user, input.id, input.expectedRevision);\n }),\n \"admin/course/archive\": post(async (ctx, service) => {\n const input = parse(courseRevisionInput, ctx.input);\n return service.archiveCourse(ctx.user, input.id, input.expectedRevision);\n }),\n \"admin/course/history\": get(async (ctx, service) => {\n const input = parse(z.object({ id: idSchema, cursor: idSchema.optional() }), ctx.input);\n return service.courseHistory(ctx.user, input.id, input.cursor);\n }),\n \"admin/course/restore\": post(async (ctx, service) => {\n const input = parse(courseRevisionInput.extend({ revisionId: idSchema }), ctx.input);\n return service.restoreCourse(ctx.user, input.id, input.expectedRevision, input.revisionId);\n }),\n \"admin/migrations\": get(\n async (ctx, service) => service.migrationStatus(ctx.user),\n false,\n \"plugins:manage\",\n ),\n \"admin/migrations/run\": post(async (ctx, service) => {\n const input = parse(\n z.object({ limit: z.number().int().min(1).max(20).default(20) }),\n ctx.input,\n );\n return service.migrateBatch(ctx.user, input.limit);\n }, \"plugins:manage\"),\n \"admin/lesson/preview\": get(async (ctx, service) => {\n const input = parse(lessonIdSchema, ctx.input);\n return service.authorLessonPreview(ctx.user, input.courseId, input.lessonId);\n }),\n \"admin/course/delete\": post(async (ctx, service) => {\n const input = parse(\n z.object({ id: idSchema, expectedRevision: z.string().min(1).max(128) }),\n ctx.input,\n );\n return service.deleteCourse(ctx.user, input.id, input.expectedRevision);\n }),\n \"admin/course/export\": get(async (ctx, service) =>\n service.exportCourse(ctx.user, parse(z.object({ id: idSchema }), ctx.input).id),\n ),\n \"admin/course/import\": post(async (ctx, service) =>\n service.importCourse(ctx.user, ctx.input),\n ),\n \"admin/enrollments\": get(async (ctx, service) =>\n service.enrollments(ctx.user, parse(listSchema, ctx.input)),\n ),\n \"admin/enrollment/save\": post(\n async (ctx, service) => service.adminEnrollment(ctx.user, ctx.input),\n \"plugins:manage\",\n ),\n \"admin/assignments\": get(async (ctx, service) => {\n const input = parse(courseIdSchema.extend({ cursor: z.string().optional() }), ctx.input);\n return service.assignments(ctx.user, input.courseId, input.cursor);\n }),\n \"admin/assignment/grade\": post(async (ctx, service) =>\n service.gradeAssignment(ctx.user, ctx.input),\n ),\n \"admin/discussions\": get(async (ctx, service) =>\n service.communityQueue(ctx.user, \"discussions\", parse(communityListSchema, ctx.input)),\n ),\n \"admin/reviews\": get(async (ctx, service) =>\n service.communityQueue(ctx.user, \"reviews\", parse(communityListSchema, ctx.input)),\n ),\n \"admin/discussion/moderate\": post(async (ctx, service) =>\n service.moderate(ctx.user, \"discussions\", ctx.input),\n ),\n \"admin/review/moderate\": post(async (ctx, service) =>\n service.moderate(ctx.user, \"reviews\", ctx.input),\n ),\n \"admin/certificate/revoke\": post(async (ctx, service) => {\n const input = parse(certificateRevokeSchema, ctx.input);\n return service.revokeCertificate(ctx.user, input.courseId, input.userId, input.reason);\n }),\n \"admin/report\": get(async (ctx, service) =>\n service.report(\n ctx.user,\n parse(z.object({ courseId: idSchema.optional() }), ctx.input).courseId,\n ),\n ),\n \"admin/cohorts\": get(\n async (ctx, service) => service.cohortList(ctx.user, parse(pageSchema, ctx.input).cursor),\n false,\n \"plugins:manage\",\n ),\n \"admin/cohort/save\": post(\n async (ctx, service) => service.saveCohort(ctx.user, ctx.input),\n \"plugins:manage\",\n ),\n \"admin/cohort/enroll\": post(async (ctx, service) => {\n const input = parse(\n z.object({\n id: idSchema,\n expectedRevision: z.string().min(1).max(128),\n offset: z.number().int().min(0).max(1000).default(0),\n }),\n ctx.input,\n );\n return service.applyCohort(ctx.user, input.id, input.expectedRevision, input.offset);\n }, \"plugins:manage\"),\n \"admin/cohort/memberships\": get(\n async (ctx, service) =>\n service.cohortMemberships(\n ctx.user,\n parse(\n pageSchema.extend({\n cohortId: idSchema.optional(),\n status: z\n .enum([\"pending\", \"active\", \"rejected\", \"suspended\", \"cancelled\"])\n .optional(),\n }),\n ctx.input,\n ),\n ),\n false,\n \"plugins:manage\",\n ),\n \"admin/cohort/decision\": post(async (ctx, service) => {\n const input = parse(\n z.object({\n cohortId: idSchema,\n userId: idSchema,\n status: z.enum([\"active\", \"rejected\", \"suspended\", \"cancelled\"]),\n reason: z.string().max(1000).default(\"\"),\n }),\n ctx.input,\n );\n return service.decideCohort(\n ctx.user,\n input.cohortId,\n input.userId,\n input.status,\n input.reason,\n );\n }, \"plugins:manage\"),\n \"admin/settings\": get(async (_ctx, service) => service.settings(), false, \"plugins:manage\"),\n \"admin/settings/save\": post(\n async (ctx, service) => service.saveSettings(ctx.user, ctx.input),\n \"plugins:manage\",\n ),\n \"admin/notification/retry\": post(\n async (ctx, service) => service.retryNotification(ctx.user, parse(keyInput, ctx.input).id),\n \"plugins:manage\",\n ),\n \"admin/notifications\": get(\n async (ctx, service) =>\n service.notificationQueue(\n ctx.user,\n parse(\n pageSchema.extend({\n status: z.enum([\"pending\", \"sending\", \"sent\", \"disabled\", \"failed\"]).optional(),\n }),\n ctx.input,\n ),\n ),\n false,\n \"plugins:manage\",\n ),\n \"admin/audit\": get(\n async (ctx, service) => service.auditList(ctx.user, parse(pageSchema, ctx.input)),\n false,\n \"plugins:manage\",\n ),\n \"admin/users\": get(\n async (ctx) => {\n invariant(ctx.users, \"USERS_UNAVAILABLE\", \"The user directory is unavailable.\", 503);\n const input = parse(pageSchema, ctx.input);\n const page = await ctx.users.list({ limit: input.limit, cursor: input.cursor });\n return {\n ...page,\n items: page.items.map((entry) => ({\n id: entry.id,\n name: entry.name,\n email: entry.email,\n role: entry.role,\n })),\n };\n },\n false,\n \"users:read\",\n ),\n \"admin/instructors\": get(async (ctx) => {\n invariant(\n ctx.user && ctx.user.role >= 20,\n \"FORBIDDEN\",\n \"Course authoring access required.\",\n 403,\n );\n invariant(ctx.users, \"USERS_UNAVAILABLE\", \"The user directory is unavailable.\", 503);\n const input = parse(pageSchema, ctx.input);\n const page = await ctx.users.list({ limit: input.limit, cursor: input.cursor });\n return {\n ...page,\n items: page.items\n .filter((entry) => entry.role >= 20)\n .map((entry) => ({ id: entry.id, name: entry.name, email: entry.email })),\n };\n }),\n \"admin/content/collections\": get(async (ctx) => {\n invariant(\n ctx.user && ctx.user.role >= 20,\n \"FORBIDDEN\",\n \"Course authoring access required.\",\n 403,\n );\n invariant(ctx.schema, \"SCHEMA_UNAVAILABLE\", \"The content schema is unavailable.\", 503);\n return (await ctx.schema.listCollections())\n .map((collection) => ({\n slug: collection.slug,\n label: collection.label,\n fields: collection.fields\n .filter((field) => [\"portableText\", \"text\", \"string\"].includes(field.type))\n .map((field) => ({ slug: field.slug, label: field.label, type: field.type })),\n }))\n .filter((collection) => collection.fields.length);\n }),\n \"admin/content/entries\": get(async (ctx) => {\n invariant(\n ctx.user && ctx.user.role >= 20,\n \"FORBIDDEN\",\n \"Course authoring access required.\",\n 403,\n );\n invariant(\n ctx.schema && ctx.content,\n \"CONTENT_UNAVAILABLE\",\n \"Content integration is unavailable.\",\n 503,\n );\n const input = parse(pageSchema.extend({ collection: idSchema }), ctx.input);\n const collection = await ctx.schema.getCollection(input.collection);\n invariant(collection, \"NOT_FOUND\", \"Content collection not found.\", 404);\n const page = await ctx.content.list(input.collection, {\n limit: input.limit,\n cursor: input.cursor,\n where: { status: \"published\" },\n });\n return {\n ...page,\n items: page.items.map((entry) => ({\n id: entry.id,\n title: String(\n entry.data[collection.titleField ?? \"title\"] ?? entry.slug ?? \"Published entry\",\n ),\n })),\n };\n }),\n \"admin/learner/find\": post(async (ctx, service) => {\n const input = parse(courseIdSchema.extend({ email: z.email().max(254) }), ctx.input);\n service.instructor(await service.course(input.courseId), ctx.user);\n invariant(ctx.users, \"USERS_UNAVAILABLE\", \"The user directory is unavailable.\", 503);\n const target = await ctx.users.getByEmail(input.email);\n invariant(\n target,\n \"NOT_FOUND\",\n \"No account uses this email address. Invite the learner through EmDash first.\",\n 404,\n );\n return { id: target.id, name: target.name, email: target.email };\n }, \"plugins:manage\"),\n \"admin/report/csv\": definePluginRoute({\n methods: [\"GET\"],\n permission: \"content:read\",\n request: { body: \"none\" },\n response: \"raw\",\n handler: async (ctx) => {\n const input = parse(listSchema, ctx.input);\n const csv = await new LmsService(ctx).enrollmentCsv(ctx.user, input);\n return pluginResponse({\n body: { kind: \"text\", value: csv },\n headers: {\n \"Content-Type\": \"text/csv; charset=utf-8\",\n \"Content-Disposition\": 'attachment; filename=\"lms-enrollments.csv\"',\n },\n });\n },\n }),\n \"mcp/courses\": post(async (ctx, service) =>\n service.adminCourses(ctx.user, parse(pageSchema, ctx.input)),\n ),\n },\n mcp: {\n tools: {\n list_courses: {\n route: \"mcp/courses\",\n description: \"List courses available to this instructor or administrator.\",\n input: pageSchema,\n },\n save_course: {\n route: \"admin/course/save\",\n description:\n \"Create a course or save a working draft using its current storage revision. Existing courses require publish_course to publish edits; initial creation with published status publishes immediately.\",\n input: courseSaveSchema,\n destructive: true,\n },\n publish_course: {\n route: \"admin/course/publish\",\n description:\n \"Publish the saved draft as a new course edition using its current storage revision. Existing learners retain their enrolled edition.\",\n input: courseRevisionInput,\n destructive: true,\n },\n enroll_learner: {\n route: \"admin/enrollment/save\",\n description:\n \"Approve, reject, suspend, or manually enroll a learner. Requires an administrator account.\",\n input: enrollmentAdminSchema,\n destructive: true,\n },\n grade_assignment: {\n route: \"admin/assignment/grade\",\n description: \"Grade a pending assignment submission in a course you teach.\",\n input: gradeSchema,\n destructive: true,\n },\n },\n },\n });\n}\n\nexport { LmsService } from \"./service.js\";\n"],"mappings":"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"}
|