Files
Jyotisha/frontend/src/lib/chart-view-profile-read.ts
T

97 lines
3.6 KiB
TypeScript

import { BirthProfileTimezoneError, resolveMissingBirthTimezoneOffset } from "./birth-profile-timezone.ts";
import {
resolveServerOwnedChartBirth,
unresolvedChartBirth,
type ChartBirthFailureReason,
type ServerOwnedChartBirth,
} from "./chart-birth-truth.ts";
import type { ChartViewProfileInput } from "./chart-view-mapper.ts";
export type ChartViewProfileRead =
| { ok: true; profile: ChartViewProfileInput; birth: ServerOwnedChartBirth }
| { ok: false; failure: ChartBirthFailureReason; birth: ServerOwnedChartBirth };
function text(value: unknown): string | undefined {
if (typeof value !== "string") return undefined;
const trimmed = value.trim();
return trimmed.length > 0 ? trimmed : undefined;
}
function isTimezoneFailure(error: unknown): boolean {
return error instanceof BirthProfileTimezoneError
|| (error instanceof Error && error.name === "BirthProfileTimezoneError");
}
function chartInput(row: unknown, birth: ServerOwnedChartBirth): ChartViewProfileInput | null {
if (!birth.chartable || birth.date == null || birth.time == null || birth.timezoneOffset == null) return null;
const source = row !== null && typeof row === "object" && !Array.isArray(row)
? row as Record<string, unknown>
: {};
const latitude = typeof source.latitude === "number" ? source.latitude : Number(source.latitude);
const longitude = typeof source.longitude === "number" ? source.longitude : Number(source.longitude);
if (!Number.isFinite(latitude) || !Number.isFinite(longitude)) return null;
return {
name: text(source.name),
date: birth.date,
time: birth.time,
placeLabel: text(source.birth_place_label),
latitude,
longitude,
timezoneOffset: birth.timezoneOffset,
timezoneId: birth.timezoneId ?? undefined,
ayanamsa: typeof source.ayanamsa === "string" ? source.ayanamsa : undefined,
birthTimeStatus: birth.status ?? undefined,
};
}
export async function prepareChartViewProfile(input: {
row: unknown;
queryError?: unknown;
resolveTimezone?: typeof resolveMissingBirthTimezoneOffset;
}): Promise<ChartViewProfileRead> {
if (input.queryError) {
return {
ok: false,
failure: "profile_query_error",
birth: unresolvedChartBirth("profile_query_error"),
};
}
const stored = resolveServerOwnedChartBirth(input.row);
if (stored.failure === "adopted_calculation_incomplete") {
return { ok: false, failure: stored.failure, birth: stored };
}
const resolveTimezone = input.resolveTimezone ?? resolveMissingBirthTimezoneOffset;
let resolved: unknown;
try {
resolved = await resolveTimezone(input.row, { useActiveDate: true });
} catch (error) {
if (!isTimezoneFailure(error)) throw error;
return {
ok: false,
failure: "timezone_resolver_failure",
birth: {
...stored,
failure: "timezone_resolver_failure",
chartable: false,
timezoneOffset: null,
},
};
}
const filled = resolveServerOwnedChartBirth(resolved);
// Keep the stored-row fingerprint so account GET and chart-view name the
// same profile even when a missing declared offset was filled in memory.
const birth = { ...filled, fingerprint: stored.fingerprint };
if (birth.failure === "adopted_calculation_incomplete") {
return { ok: false, failure: birth.failure, birth };
}
const profile = chartInput(resolved, birth);
if (!profile || birth.failure) {
return {
ok: false,
failure: birth.failure ?? "profile_incomplete",
birth: { ...birth, failure: birth.failure ?? "profile_incomplete", chartable: false },
};
}
return { ok: true, profile, birth };
}