fix(learner): narrow lesson target result for the type checker

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Lucas Winther
2026-10-30 03:24:28 +01:00
co-authored by Claude Opus 5.5
parent 96c147e3a2
commit 478ebeb255
@@ -45,7 +45,7 @@ export function userDataRoutes(ctx: ServerContext): Feature["routes"] {
if (!b || !STATES.includes(b.state as ProgressState)) return badRequest("state must be not-started, in-progress or done");
if (b.positionSeconds !== undefined && (typeof b.positionSeconds !== "number" || !Number.isFinite(b.positionSeconds) || b.positionSeconds < 0)) return badRequest("positionSeconds must be a non-negative number");
const t = await target(user.id, user, courseId, lessonId);
if ("error" in t) return t.error;
if (t.error) return t.error;
const prior = t.progress[lessonId];
// A position report ("in-progress") never undoes "done"; only an explicit not-started does.
const state = prior?.state === "done" && b.state === "in-progress" ? "done" : b.state!;
@@ -103,7 +103,7 @@ export function userDataRoutes(ctx: ServerContext): Feature["routes"] {
if (!b || typeof b.code !== "string") return badRequest("code is required");
if (b.code.length > MAX_CODE) return badRequest("code is too long");
const t = await target(user.id, user, courseId, lessonId);
if ("error" in t) return t.error;
if (t.error) return t.error;
if (t.entry.lesson.type !== "exercise") return badRequest("not an exercise");
const passed = b.passed === true;
await attempts.save({ userId: user.id, courseId, lessonId, track: b.track || "default", code: b.code, result: b.result ?? null, passed, updatedAt: new Date().toISOString() });