feat(course-format): markdown extraction, discovery, loading, repository
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7a68800f6f
commit
c78c7ec8d8
7 files changed
+558
-2
No files matched your search
@@ -0,0 +1,42 @@
|
||||
import { extname } from "node:path";
|
||||
import { loadAll, resolveInside, type LoadedCourse } from "./load";
|
||||
import { extractSection } from "./markdown";
|
||||
|
||||
export interface CourseRepository {
|
||||
list(): LoadedCourse[];
|
||||
get(id: string): LoadedCourse | null;
|
||||
/** Re-scans the course dirs. `id` is accepted for API symmetry; a rescan is cheap and keeps overlay shadowing correct. */
|
||||
reload(id?: string): Promise<void>;
|
||||
readLessonMarkdown(courseId: string, lessonId: string): Promise<string | null>;
|
||||
/** Absolute real path if inside the course root (symlinks followed), else null. */
|
||||
resolvePath(courseId: string, relPath: string): string | null;
|
||||
}
|
||||
|
||||
const MD = new Set([".md", ".markdown"]);
|
||||
|
||||
export async function createCourseRepository(courseDirs: string[]): Promise<CourseRepository> {
|
||||
let courses = await loadAll(courseDirs);
|
||||
const repo: CourseRepository = {
|
||||
list: () => courses,
|
||||
get: (id) => courses.find((c) => c.id === id) ?? null,
|
||||
async reload() {
|
||||
courses = await loadAll(courseDirs);
|
||||
},
|
||||
async readLessonMarkdown(courseId, lessonId) {
|
||||
const entry = repo.get(courseId)?.lessonIndex.get(lessonId);
|
||||
if (!entry) return null;
|
||||
const { lesson } = entry;
|
||||
const rel = lesson.type === "video" ? lesson.transcript : lesson.file;
|
||||
if (!rel || !MD.has(extname(rel).toLowerCase())) return null;
|
||||
const abs = repo.resolvePath(courseId, rel);
|
||||
if (!abs) return null;
|
||||
const text = await Bun.file(abs).text();
|
||||
return lesson.anchor ? extractSection(text, lesson.anchor) : text;
|
||||
},
|
||||
resolvePath(courseId, relPath) {
|
||||
const c = repo.get(courseId);
|
||||
return c ? resolveInside(c.root, relPath) : null;
|
||||
},
|
||||
};
|
||||
return repo;
|
||||
}
|
||||
Reference in new issue
Block a user