45 lines
2.7 KiB
TypeScript
45 lines
2.7 KiB
TypeScript
import { z } from "zod";
|
|
import type { AnswerViewState } from "../answer-ui/answer-view-state";
|
|
|
|
const index = z.number().int().nonnegative();
|
|
const chartTable = z.object({ query: z.string(), sort: z.object({ column: index, descending: z.boolean() }).strict().nullable(), selection: z.array(index) }).strict();
|
|
const node = z.discriminatedUnion("type", [
|
|
z.object({ type: z.literal("Chart"), view: z.enum(["line", "bar", "table"]), hidden: z.array(index), range: z.tuple([index, index]), point: index, inspecting: z.boolean(), table: chartTable.optional() }).strict(),
|
|
z.object({ type: z.literal("Table"), query: z.string(), sort: z.object({ column: index, descending: z.boolean() }).strict().nullable(), selection: z.array(index) }).strict(),
|
|
z.object({ type: z.literal("FollowUpForm"), values: z.record(z.string()) }).strict(),
|
|
z.object({ type: z.literal("Evidence"), open: z.boolean() }).strict(),
|
|
]);
|
|
const record = z.object({
|
|
version: z.literal("answer-view-state@1"), message_id: z.string(), block_id: z.string(),
|
|
revision: index, schema_digest: z.string().min(1), nodes: z.record(node),
|
|
block: z.object({ evidenceFilter: z.string() }).strict(), updated_at: z.string().nullable(),
|
|
}).strict();
|
|
|
|
export type AnswerViewStateRecord = AnswerViewState & {
|
|
version: "answer-view-state@1"; message_id: string; block_id: string;
|
|
revision: number; schema_digest: string; updated_at: string | null;
|
|
};
|
|
export type AnswerViewStateWrite = AnswerViewState & {
|
|
message_id: string; block_id: string; schema_digest: string;
|
|
expected_revision: number; request_id: string;
|
|
};
|
|
export class AnswerViewStateApiError extends Error {
|
|
constructor(message: string, readonly status: number, readonly code?: string, readonly current?: AnswerViewStateRecord) {
|
|
super(message);
|
|
this.name = "AnswerViewStateApiError";
|
|
}
|
|
}
|
|
export function parseAnswerViewStateRecord(value: unknown): AnswerViewStateRecord {
|
|
return record.parse(value) as AnswerViewStateRecord;
|
|
}
|
|
export async function readAnswerViewStateResponse(response: Response): Promise<AnswerViewStateRecord> {
|
|
const data: unknown = await response.json();
|
|
if (!response.ok) {
|
|
const error = z.object({ code: z.string().optional(), detail: z.string().optional(), current: z.unknown().optional() }).passthrough().safeParse(data);
|
|
const current = error.success && error.data.current ? record.safeParse(error.data.current) : null;
|
|
throw new AnswerViewStateApiError(error.success ? error.data.detail ?? "交互状态未保存,请重试" : "交互状态暂不可用,请重试", response.status,
|
|
error.success ? error.data.code : undefined, current?.success ? current.data as AnswerViewStateRecord : undefined);
|
|
}
|
|
return parseAnswerViewStateRecord(data);
|
|
}
|