Files
kandev/apps/web/lib/plan-comment-refs.ts
T
2026-09-10 22:33:56 +01:00

58 lines
2.1 KiB
TypeScript

import type { PlanComment } from "@/lib/state/slices/comments";
import type {
TaskPlanCommentRef,
TaskPlanCommentSnapshot,
TaskSessionState,
} from "@/lib/types/http";
import { WebSocketRequestError } from "@/lib/ws/request-error";
/** Freeze the task-plan comment versions visible when a user submits. */
export function toTaskPlanCommentRefs(comments: PlanComment[]): TaskPlanCommentRef[] {
return comments.map((comment) => {
if (!comment.version || comment.version < 1) {
// i18n-exempt: internal invariant; legacy rows are migrated before delivery.
throw new Error("Task plan comment is missing a persisted version");
}
return { id: comment.id, version: comment.version };
});
}
export type PlanCommentAdmissionConflict = {
code: "plan_comments_changed" | "primary_session_changed";
snapshot?: TaskPlanCommentSnapshot;
primarySessionId?: string | null;
primarySessionState?: TaskSessionState;
};
function parsePrimarySessionId(value: unknown): string | null | undefined {
if (value === null) return null;
if (typeof value !== "string") return undefined;
return value || null;
}
/** Read the stable plan-comment conflict fields from a rejected WS request. */
export function planCommentAdmissionConflict(
error: unknown,
): PlanCommentAdmissionConflict | undefined {
if (!(error instanceof WebSocketRequestError)) return undefined;
if (error.code !== "plan_comments_changed" && error.code !== "primary_session_changed") {
return undefined;
}
const candidate = error.details?.snapshot;
const snapshot =
typeof candidate === "object" &&
candidate !== null &&
"task_id" in candidate &&
"plan_id" in candidate &&
"revision" in candidate &&
"comments" in candidate
? (candidate as TaskPlanCommentSnapshot)
: undefined;
const primarySessionId = parsePrimarySessionId(error.details?.primary_session_id);
const primarySessionState =
typeof error.details?.primary_session_state === "string"
? (error.details.primary_session_state as TaskSessionState)
: undefined;
return { code: error.code, snapshot, primarySessionId, primarySessionState };
}