import { z } from "zod"; import type { DeclaredBirthWindowConsultation } from "./consultation-route-service.ts"; const clockSchema = z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/); export const declaredWindowChartPacketSchema = z.object({ declared_range: z.object({ start: clockSchema, end: clockSchema, wraps_midnight: z.boolean(), }), probe_count: z.number().int().min(2).max(4), probes: z.array(z.object({ clock: clockSchema, role: z.enum(["range_start", "interior", "range_end"]), }).strict()).min(2).max(4), stable_layers: z.object({ planet_signs: z.record(z.string()), moon_nakshatra: z.string().optional(), ascendant_sign: z.string().optional(), house_cusp_signs: z.record(z.string()).optional(), }).passthrough(), varying_layers: z.object({ planet_signs: z.record(z.array(z.string())).optional(), moon_nakshatra: z.array(z.string()).optional(), ascendant_signs: z.array(z.string()).optional(), house_cusp_signs: z.record(z.array(z.string())).optional(), }).passthrough(), blocked_layers: z.array(z.string()), answer_policy: z.object({ can_answer_direction: z.boolean(), can_answer_precise_timing: z.literal(false), birth_time_confidence: z.literal("declared_window"), candidate_is_confirmed: z.literal(false), }).passthrough(), result_hash: z.string().min(1), }).passthrough(); export type DeclaredWindowChartPacket = z.infer; type LoadDeclaredWindowChartInput = Readonly<{ window: DeclaredBirthWindowConsultation; fetchImpl?: typeof fetch; apiBase?: string; signal?: AbortSignal; }>; export async function fetchDeclaredWindowChart( input: LoadDeclaredWindowChartInput, ): Promise { const fetchImpl = input.fetchImpl ?? fetch; const apiBase = input.apiBase ?? process.env.JYOTISH_API_BASE ?? "http://127.0.0.1:5200"; const { toolInput } = input.window; const response = await fetchImpl(`${apiBase}/api/declared_window_chart`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ year: toolInput.year, month: toolInput.month, day: toolInput.day, lat: toolInput.lat, lon: toolInput.lon, tz: toolInput.tz, range_start: toolInput.rangeStart, range_end: toolInput.rangeEnd, }), cache: "no-store", signal: input.signal, }); if (!response.ok) { throw new Error("declared_window_chart_unavailable"); } const payload = await response.json().catch(() => null); if (!payload || typeof payload !== "object" || Array.isArray(payload)) { throw new Error("declared_window_chart_unavailable"); } const record = payload as Record; if (record.success !== true || record.endpoint !== "declared_window_chart") { throw new Error("declared_window_chart_unavailable"); } if ("hour" in record || "minute" in record || "birth_time" in record) { throw new Error("declared_window_chart_unavailable"); } return declaredWindowChartPacketSchema.parse(record.packet); }