58 lines
2.1 KiB
TypeScript
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 };
|
|
}
|