fix(chart): keep account and chart-view birth truth consistent

This commit is contained in:
jesse-ux
2026-09-22 12:14:50 +08:00
parent dd4141a030
commit cc7ef8a339
21 changed files with 1243 additions and 90 deletions
+30 -10
View File
@@ -1,4 +1,7 @@
import { ZodError } from "zod";
import { resolveAyanamsa } from "./ayanamsa.ts";
import type { ChartBirthFailureReason } from "./chart-birth-truth.ts";
import {
chartViewMessageSchema,
type ChartViewResponse,
@@ -9,6 +12,7 @@ import {
type ChartViewLayer,
type EngineCallResult,
} from "./chart-view-engine.ts";
import { chartViewFailureResponse } from "./chart-view-failure.ts";
import { CHART_VIEW_COPY, VARGA_CHIP_ORDER } from "./chart-view-labels.ts";
import {
buildChartView,
@@ -28,13 +32,31 @@ export type ChartViewLoadInput = {
asOf: string;
rateLimited?: boolean;
layers?: readonly ChartViewLayer[];
accountId?: string | null;
profileFingerprint?: string | null;
};
function message(
status: "unauthenticated" | "birth_profile_incomplete" | "chart_unavailable" | "rate_limited",
copy: string,
reason?: ChartBirthFailureReason,
): ChartViewResponse {
return chartViewMessageSchema.parse({ status, billed: false, message: copy });
return chartViewMessageSchema.parse({ status, billed: false, message: copy, reason });
}
function stamp(body: ChartViewResponse, input: ChartViewLoadInput): ChartViewResponse {
return {
...body,
...(input.accountId ? { accountId: input.accountId } : {}),
...(input.profileFingerprint ? { profileFingerprint: input.profileFingerprint } : {}),
};
}
function engineFailureReason(status: Exclude<EngineCallResult["status"], "ok">): ChartBirthFailureReason {
if (status === "busy") return "engine_busy";
if (status === "timeout") return "engine_timeout";
if (status === "bad_payload") return "engine_bad_payload";
return "engine_http_error";
}
function birthPayload(profile: ChartViewProfileInput): Record<string, unknown> {
@@ -87,14 +109,14 @@ export async function assembleChartView(input: ChartViewLoadInput): Promise<{
if (input.rateLimited) {
return {
httpStatus: 429,
body: message("rate_limited", CHART_VIEW_COPY.rateLimited),
body: stamp(message("rate_limited", CHART_VIEW_COPY.rateLimited), input),
};
}
const profile = input.profile;
if (!profile) {
return {
httpStatus: 200,
body: message("birth_profile_incomplete", CHART_VIEW_COPY.incomplete),
body: stamp(chartViewFailureResponse("profile_incomplete"), input),
};
}
@@ -103,10 +125,7 @@ export async function assembleChartView(input: ChartViewLoadInput): Promise<{
if (chartResult.status !== "ok") {
return {
httpStatus: 200,
body: message(
"chart_unavailable",
chartResult.status === "busy" ? CHART_VIEW_COPY.busy : CHART_VIEW_COPY.unavailable,
),
body: stamp(chartViewFailureResponse(engineFailureReason(chartResult.status)), input),
};
}
const chart = chartResult.payload;
@@ -119,7 +138,7 @@ export async function assembleChartView(input: ChartViewLoadInput): Promise<{
});
return {
httpStatus: 200,
body: message("chart_unavailable", CHART_VIEW_COPY.unavailable),
body: stamp(chartViewFailureResponse("engine_bad_payload"), input),
};
}
@@ -165,14 +184,15 @@ export async function assembleChartView(input: ChartViewLoadInput): Promise<{
profile,
asOf: input.asOf,
});
return { httpStatus: 200, body };
return { httpStatus: 200, body: stamp(body, input) };
} catch (error) {
const errorName = error instanceof Error ? error.name : "Error";
const errorMessage = error instanceof Error ? error.message : "unknown";
console.warn("chart_view_assemble_failed", { errorName, message: errorMessage });
const reason = error instanceof ZodError ? "response_schema_failure" : "engine_bad_payload";
return {
httpStatus: 200,
body: message("chart_unavailable", CHART_VIEW_COPY.unavailable),
body: stamp(chartViewFailureResponse(reason), input),
};
}
}