import type { CourseRepository } from "@stc/course-format"; export type Role = "owner" | "author" | "learner"; export interface AuthUser { id: string; email: string; name: string; roles: Role[]; groups: string[] } export interface RequestContext { user: AuthUser | null; params: Record; url: URL } export type RouteHandler = (req: Request, rc: RequestContext) => Response | Promise; export interface Feature { name: string; // "learner" | "import" | "studio" | "ai" | "core" routes: Record>>; // paths start with /api/ jobs?: Record; // job kind -> handler } export interface JobContext { reportProgress(fraction: number, message?: string): Promise; signal: AbortSignal } export type JobHandler = (payload: unknown, jc: JobContext) => Promise; export type JobStatus = "queued" | "running" | "done" | "failed"; export interface JobRecord { id: string; kind: string; status: JobStatus; progress: number; message: string | null; payload: unknown; result: unknown; error: string | null; createdAt: string; updatedAt: string } export interface JobQueue { enqueue(kind: string, payload: unknown): Promise; get(id: string): Promise; list(kind?: string): Promise } export interface AuthService { userFromRequest(req: Request): Promise } import type { Db } from "@stc/db"; export interface ServerContext { config: AppConfig; db: Db; courses: CourseRepository; jobs: JobQueue; auth: AuthService; } export interface AppConfig { port: number; databaseUrl: string; dataDir: string; // dataDir holds cache/, media/, worktrees/ courseDirs: string[]; // folders scanned for stcourse.yaml (course repos and overlays/) secret: string; // STC_SECRET, used to encrypt BYOK keys publicUrl: string; }