Keep askable cards after exhaustion, explain each probe, read the adopted credible range in reports and chat, and compare declared periods before the minute grid when the clock is unknown. Co-authored-by: Cursor <cursoragent@cursor.com>
582 lines
21 KiB
TypeScript
582 lines
21 KiB
TypeScript
import { chinaLocations } from "../data/china-locations.ts";
|
|
import { isBirthClockTime, parseBirthDate } from "./birth-time-intake-model.ts";
|
|
import { resolveMissingBirthTimezoneOffset } from "./birth-profile-timezone.ts";
|
|
import {
|
|
declaredClockRangeFromProfile,
|
|
declaredRangeWrapsMidnight,
|
|
} from "./declared-birth-window.ts";
|
|
import {
|
|
isNatalMinuteConsultationMode,
|
|
type ConsultationBirthTimeMode,
|
|
} from "./consultation-birth-time-mode.ts";
|
|
import type { GeneralDailyReference } from "./general-daily-panchanga.ts";
|
|
import { resolveAyanamsa, type AyanamsaName } from "./ayanamsa.ts";
|
|
|
|
export type ConsultationProfileTruthErrorCode =
|
|
| "profile_unavailable"
|
|
| "profile_incomplete"
|
|
| "profile_inconsistent"
|
|
| "mode_changed";
|
|
|
|
export class ConsultationProfileTruthError extends Error {
|
|
readonly code: ConsultationProfileTruthErrorCode;
|
|
|
|
constructor(code: ConsultationProfileTruthErrorCode) {
|
|
super(`Consultation profile truth rejected: ${code}`);
|
|
this.name = "ConsultationProfileTruthError";
|
|
this.code = code;
|
|
}
|
|
}
|
|
|
|
export type DeclaredBirthAccuracy = Readonly<{
|
|
declaredAccuracy: "rectified" | "minute" | "15min";
|
|
timeSource: string;
|
|
}>;
|
|
|
|
type ServerChartToolInput = Readonly<{
|
|
year: number;
|
|
month: number;
|
|
day: number;
|
|
hour: number;
|
|
minute: number;
|
|
city: string;
|
|
lat: number;
|
|
lon: number;
|
|
tz: number;
|
|
ayanamsa: AyanamsaName;
|
|
declared_accuracy: DeclaredBirthAccuracy["declaredAccuracy"];
|
|
time_source: string;
|
|
birth_time_accuracy?: "provisional";
|
|
candidate_range?: Readonly<{ start_time: string; end_time: string }>;
|
|
}>;
|
|
|
|
export type ServerChartConsultation = Readonly<{
|
|
name: string;
|
|
toolInput: ServerChartToolInput;
|
|
truth: Readonly<{
|
|
birthDate: string;
|
|
reportedBirthTime: string | null;
|
|
activeBirthTime: string | null;
|
|
selectedTimeKind: "reported" | "active";
|
|
birthTimeSource: string;
|
|
birthTimeStatus: string;
|
|
placeLabel: string;
|
|
placeCodes: Readonly<{
|
|
countryCode: string | null;
|
|
provinceCode: string | null;
|
|
cityCode: string | null;
|
|
districtCode: string | null;
|
|
}>;
|
|
placeId: string | null;
|
|
placeType: string | null;
|
|
placeProvider: string | null;
|
|
timezoneId: string | null;
|
|
timezoneSource: string | null;
|
|
latitude: number;
|
|
longitude: number;
|
|
timezoneOffset: number;
|
|
}>;
|
|
}>;
|
|
|
|
export type DeclaredBirthWindowConsultation = Readonly<{
|
|
name: string;
|
|
toolInput: Readonly<{
|
|
year: number;
|
|
month: number;
|
|
day: number;
|
|
city: string;
|
|
lat: number;
|
|
lon: number;
|
|
tz: number;
|
|
ayanamsa: AyanamsaName;
|
|
rangeStart: string;
|
|
rangeEnd: string;
|
|
}>;
|
|
truth: Readonly<{
|
|
birthDate: string;
|
|
birthTimeSource: string;
|
|
birthTimePeriod: string | null;
|
|
birthTimeStatus: string;
|
|
wrapsMidnight: boolean;
|
|
placeLabel: string;
|
|
placeCodes: Readonly<{
|
|
countryCode: string | null;
|
|
provinceCode: string | null;
|
|
cityCode: string | null;
|
|
districtCode: string | null;
|
|
}>;
|
|
placeId: string | null;
|
|
placeType: string | null;
|
|
placeProvider: string | null;
|
|
timezoneId: string | null;
|
|
timezoneSource: string | null;
|
|
latitude: number;
|
|
longitude: number;
|
|
timezoneOffset: number;
|
|
}>;
|
|
}>;
|
|
|
|
type ConsultationPreReserveContext = Readonly<{
|
|
consultationMode: ConsultationBirthTimeMode;
|
|
serverChart: ServerChartConsultation | null;
|
|
declaredWindow: DeclaredBirthWindowConsultation | null;
|
|
generalDailyReference: GeneralDailyReference | null;
|
|
}>;
|
|
|
|
type PrepareConsultationRouteInput<Reservation> = Readonly<{
|
|
userId: string;
|
|
mode: ConsultationBirthTimeMode;
|
|
loadProfile: (userId: string) => Promise<unknown>;
|
|
loadCandidateRange?: (userId: string) => Promise<{ startTime: string; endTime: string } | null>;
|
|
resolveTimezoneOffset?: (profile: unknown, selectedTime?: string) => Promise<unknown>;
|
|
beforeReserve?: (context: ConsultationPreReserveContext) => unknown | Promise<unknown>;
|
|
reserve: () => Promise<Reservation>;
|
|
}>;
|
|
|
|
type PrepareConsultationRouteWithGuard<Reservation, GuardResult> = Omit<
|
|
PrepareConsultationRouteInput<Reservation>,
|
|
"beforeReserve"
|
|
> & Readonly<{
|
|
beforeReserve: (context: ConsultationPreReserveContext) => GuardResult | Promise<GuardResult>;
|
|
}>;
|
|
|
|
export type PreparedConsultationRoute<Reservation, GuardResult = undefined> = Readonly<{
|
|
consultationMode: ConsultationBirthTimeMode;
|
|
serverChart: ServerChartConsultation | null;
|
|
declaredWindow: DeclaredBirthWindowConsultation | null;
|
|
generalDailyReference: GeneralDailyReference | null;
|
|
reservation: Reservation;
|
|
preReserveResult: GuardResult;
|
|
}>;
|
|
|
|
type RecordValue = Record<string, unknown>;
|
|
|
|
const allowedBirthTimeSources = new Set([
|
|
"hospital_record", "family_exact", "approximate", "period_only", "unknown", "legacy_import",
|
|
]);
|
|
const allowedBirthTimeStatuses = new Set([
|
|
"reported", "assessing", "rectifying", "candidate", "accepted", "confirmed",
|
|
]);
|
|
const concreteReportedSources = new Set([
|
|
"hospital_record", "family_exact", "approximate",
|
|
]);
|
|
|
|
/**
|
|
* Map persisted profile truth onto the Python rectification-gate vocabulary.
|
|
* `rectified` is reserved for an accepted or confirmed active birth time.
|
|
*/
|
|
export function declaredBirthAccuracyFromProfile(input: {
|
|
birthTimeStatus: string;
|
|
birthTimeSource: string;
|
|
hasActiveBirthTime: boolean;
|
|
}): DeclaredBirthAccuracy {
|
|
if (
|
|
(input.birthTimeStatus === "accepted" || input.birthTimeStatus === "confirmed")
|
|
&& input.hasActiveBirthTime
|
|
) {
|
|
return { declaredAccuracy: "rectified", timeSource: "rectified" };
|
|
}
|
|
if (input.birthTimeSource === "hospital_record") {
|
|
return { declaredAccuracy: "minute", timeSource: "hospital" };
|
|
}
|
|
if (input.birthTimeSource === "family_exact") {
|
|
return { declaredAccuracy: "minute", timeSource: "family_clear" };
|
|
}
|
|
return { declaredAccuracy: "15min", timeSource: "family_vague" };
|
|
}
|
|
|
|
function record(value: unknown): RecordValue | null {
|
|
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
? value as RecordValue
|
|
: null;
|
|
}
|
|
|
|
function requiredText(profile: RecordValue, key: string): string {
|
|
const value = profile[key];
|
|
if (typeof value !== "string" || !value.trim()) {
|
|
throw new ConsultationProfileTruthError("profile_incomplete");
|
|
}
|
|
return value.trim();
|
|
}
|
|
|
|
function nullableClock(profile: RecordValue, key: string): string | null {
|
|
const value = profile[key];
|
|
if (value === null || value === undefined) return null;
|
|
if (typeof value !== "string") {
|
|
throw new ConsultationProfileTruthError("profile_inconsistent");
|
|
}
|
|
const clock = value.slice(0, 5);
|
|
if (!isBirthClockTime(clock)) {
|
|
throw new ConsultationProfileTruthError("profile_inconsistent");
|
|
}
|
|
return clock;
|
|
}
|
|
|
|
function requiredFiniteNumber(profile: RecordValue, key: string, minimum: number, maximum: number) {
|
|
const value = profile[key];
|
|
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
throw new ConsultationProfileTruthError("profile_incomplete");
|
|
}
|
|
if (value < minimum || value > maximum) {
|
|
throw new ConsultationProfileTruthError("profile_inconsistent");
|
|
}
|
|
return value;
|
|
}
|
|
|
|
function optionalText(profile: RecordValue, key: string): string | null {
|
|
const value = profile[key];
|
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
}
|
|
|
|
function generalDailyReferenceFromProfile(value: unknown): GeneralDailyReference | null {
|
|
const profile = record(value);
|
|
if (!profile) return null;
|
|
const latitude = profile.latitude;
|
|
const longitude = profile.longitude;
|
|
const timezoneOffset = profile.timezone_offset;
|
|
if (typeof latitude !== "number" || !Number.isFinite(latitude) || latitude < -90 || latitude > 90
|
|
|| typeof longitude !== "number" || !Number.isFinite(longitude) || longitude < -180 || longitude > 180
|
|
|| typeof timezoneOffset !== "number" || !Number.isFinite(timezoneOffset) || timezoneOffset < -12 || timezoneOffset > 14) {
|
|
return null;
|
|
}
|
|
return Object.freeze({
|
|
latitude,
|
|
longitude,
|
|
timezoneOffset,
|
|
placeLabel: optionalText(profile, "birth_place_label") ?? "已保存地点",
|
|
});
|
|
}
|
|
|
|
function persistedConsultationMode(value: unknown): Exclude<ConsultationBirthTimeMode, "general_no_birth_time"> | null {
|
|
const profile = record(value);
|
|
if (!profile) return null;
|
|
const status = optionalText(profile, "birth_time_status");
|
|
const source = optionalText(profile, "birth_time_source");
|
|
const activeTime = optionalText(profile, "active_birth_time")?.slice(0, 5) ?? "";
|
|
const reportedTime = optionalText(profile, "reported_birth_time")?.slice(0, 5) ?? "";
|
|
if ((status === "accepted" || status === "confirmed") && isBirthClockTime(activeTime)) return "verified_chart";
|
|
if (status && allowedBirthTimeStatuses.has(status) && status !== "accepted" && status !== "confirmed"
|
|
&& source && concreteReportedSources.has(source)
|
|
&& isBirthClockTime(reportedTime)) return "unverified_birth_time";
|
|
if (status && allowedBirthTimeStatuses.has(status) && status !== "accepted" && status !== "confirmed"
|
|
&& source && (source === "period_only" || source === "unknown" || source === "legacy_import")
|
|
&& !isBirthClockTime(reportedTime)) return "declared_birth_window";
|
|
return null;
|
|
}
|
|
|
|
function legacyChinaPlaceLabel(profile: RecordValue): string | null {
|
|
const countryCode = optionalText(profile, "country_code");
|
|
const provinceCode = optionalText(profile, "province_code");
|
|
const cityCode = optionalText(profile, "city_code");
|
|
const districtCode = optionalText(profile, "district_code");
|
|
const country = chinaLocations.country;
|
|
if (countryCode !== country.code || !provinceCode || !cityCode) return null;
|
|
const province = country.provinces.find((candidate) => candidate.code === provinceCode);
|
|
const city = province?.cities.find((candidate) => candidate.code === cityCode);
|
|
const district = districtCode
|
|
? city?.districts.find((candidate) => candidate.code === districtCode)
|
|
: undefined;
|
|
if (!province || !city || (districtCode && !district)) return null;
|
|
return [country.name, province.name, city.name, district?.name]
|
|
.filter((label, index, labels) => Boolean(label) && labels.indexOf(label) === index)
|
|
.join(" · ");
|
|
}
|
|
|
|
function serverChartFromProfile(
|
|
value: unknown,
|
|
mode: Extract<ConsultationBirthTimeMode, "verified_chart" | "unverified_birth_time">,
|
|
): ServerChartConsultation {
|
|
const profile = record(value);
|
|
if (!profile) throw new ConsultationProfileTruthError("profile_incomplete");
|
|
|
|
const name = requiredText(profile, "name");
|
|
if (name.length > 80) throw new ConsultationProfileTruthError("profile_inconsistent");
|
|
const birthDate = requiredText(profile, "birth_date");
|
|
if (!parseBirthDate(birthDate)) {
|
|
throw new ConsultationProfileTruthError("profile_inconsistent");
|
|
}
|
|
const [year, month, day] = birthDate.split("-").map(Number);
|
|
const reportedBirthTime = nullableClock(profile, "reported_birth_time");
|
|
const activeBirthTime = nullableClock(profile, "active_birth_time");
|
|
const birthTimeSource = requiredText(profile, "birth_time_source");
|
|
const birthTimeStatus = requiredText(profile, "birth_time_status");
|
|
if (!allowedBirthTimeSources.has(birthTimeSource)
|
|
|| !allowedBirthTimeStatuses.has(birthTimeStatus)) {
|
|
throw new ConsultationProfileTruthError("profile_inconsistent");
|
|
}
|
|
|
|
const countryCode = optionalText(profile, "country_code");
|
|
const provinceCode = optionalText(profile, "province_code");
|
|
const cityCode = optionalText(profile, "city_code");
|
|
const districtCode = optionalText(profile, "district_code");
|
|
const placeId = optionalText(profile, "birth_place_provider_id");
|
|
const placeType = optionalText(profile, "birth_place_type");
|
|
const placeProvider = optionalText(profile, "birth_place_provider");
|
|
const timezoneId = optionalText(profile, "timezone_id");
|
|
const timezoneSource = optionalText(profile, "timezone_source");
|
|
const latitude = requiredFiniteNumber(profile, "latitude", -90, 90);
|
|
const longitude = requiredFiniteNumber(profile, "longitude", -180, 180);
|
|
const timezoneOffset = requiredFiniteNumber(profile, "timezone_offset", -12, 14);
|
|
const placeLabel = optionalText(profile, "birth_place_label")
|
|
?? legacyChinaPlaceLabel(profile)
|
|
?? placeId;
|
|
if (!placeLabel) throw new ConsultationProfileTruthError("profile_incomplete");
|
|
|
|
let selectedTime: string;
|
|
let selectedTimeKind: "reported" | "active";
|
|
if (mode === "verified_chart") {
|
|
if (birthTimeStatus !== "accepted" && birthTimeStatus !== "confirmed") {
|
|
throw new ConsultationProfileTruthError("mode_changed");
|
|
}
|
|
if (!activeBirthTime) throw new ConsultationProfileTruthError("profile_incomplete");
|
|
selectedTime = activeBirthTime;
|
|
selectedTimeKind = "active";
|
|
} else {
|
|
if (birthTimeStatus === "accepted" || birthTimeStatus === "confirmed" || !concreteReportedSources.has(birthTimeSource)) {
|
|
throw new ConsultationProfileTruthError("mode_changed");
|
|
}
|
|
if (!reportedBirthTime) throw new ConsultationProfileTruthError("profile_incomplete");
|
|
selectedTime = reportedBirthTime;
|
|
selectedTimeKind = "reported";
|
|
}
|
|
const [hour, minute] = selectedTime.split(":").map(Number);
|
|
const accuracy = declaredBirthAccuracyFromProfile({
|
|
birthTimeStatus,
|
|
birthTimeSource,
|
|
hasActiveBirthTime: Boolean(activeBirthTime),
|
|
});
|
|
|
|
return Object.freeze({
|
|
name,
|
|
toolInput: Object.freeze({
|
|
year,
|
|
month,
|
|
day,
|
|
hour,
|
|
minute,
|
|
city: placeLabel,
|
|
lat: latitude,
|
|
lon: longitude,
|
|
tz: timezoneOffset,
|
|
ayanamsa: resolveAyanamsa(profile),
|
|
declared_accuracy: accuracy.declaredAccuracy,
|
|
time_source: accuracy.timeSource,
|
|
}),
|
|
truth: Object.freeze({
|
|
birthDate,
|
|
reportedBirthTime,
|
|
activeBirthTime,
|
|
selectedTimeKind,
|
|
birthTimeSource,
|
|
birthTimeStatus,
|
|
placeLabel,
|
|
placeCodes: Object.freeze({
|
|
countryCode,
|
|
provinceCode,
|
|
cityCode,
|
|
districtCode,
|
|
}),
|
|
placeId,
|
|
placeType,
|
|
placeProvider,
|
|
timezoneId,
|
|
timezoneSource,
|
|
latitude,
|
|
longitude,
|
|
timezoneOffset,
|
|
}),
|
|
});
|
|
}
|
|
|
|
function declaredWindowFromProfile(value: unknown): DeclaredBirthWindowConsultation {
|
|
const profile = record(value);
|
|
if (!profile) throw new ConsultationProfileTruthError("profile_incomplete");
|
|
|
|
const name = requiredText(profile, "name");
|
|
if (name.length > 80) throw new ConsultationProfileTruthError("profile_inconsistent");
|
|
const birthDate = requiredText(profile, "birth_date");
|
|
if (!parseBirthDate(birthDate)) {
|
|
throw new ConsultationProfileTruthError("profile_inconsistent");
|
|
}
|
|
const [year, month, day] = birthDate.split("-").map(Number);
|
|
const birthTimeSource = requiredText(profile, "birth_time_source");
|
|
const birthTimeStatus = requiredText(profile, "birth_time_status");
|
|
if (!allowedBirthTimeSources.has(birthTimeSource)
|
|
|| !allowedBirthTimeStatuses.has(birthTimeStatus)) {
|
|
throw new ConsultationProfileTruthError("profile_inconsistent");
|
|
}
|
|
if (birthTimeStatus === "accepted" || birthTimeStatus === "confirmed") {
|
|
throw new ConsultationProfileTruthError("mode_changed");
|
|
}
|
|
if (concreteReportedSources.has(birthTimeSource) && nullableClock(profile, "reported_birth_time")) {
|
|
throw new ConsultationProfileTruthError("mode_changed");
|
|
}
|
|
const birthTimePeriod = optionalText(profile, "birth_time_period");
|
|
const range = declaredClockRangeFromProfile(profile);
|
|
if (!range) throw new ConsultationProfileTruthError("profile_incomplete");
|
|
|
|
const countryCode = optionalText(profile, "country_code");
|
|
const provinceCode = optionalText(profile, "province_code");
|
|
const cityCode = optionalText(profile, "city_code");
|
|
const districtCode = optionalText(profile, "district_code");
|
|
const placeId = optionalText(profile, "birth_place_provider_id");
|
|
const placeType = optionalText(profile, "birth_place_type");
|
|
const placeProvider = optionalText(profile, "birth_place_provider");
|
|
const timezoneId = optionalText(profile, "timezone_id");
|
|
const timezoneSource = optionalText(profile, "timezone_source");
|
|
const latitude = requiredFiniteNumber(profile, "latitude", -90, 90);
|
|
const longitude = requiredFiniteNumber(profile, "longitude", -180, 180);
|
|
const timezoneOffset = requiredFiniteNumber(profile, "timezone_offset", -12, 14);
|
|
const placeLabel = optionalText(profile, "birth_place_label")
|
|
?? legacyChinaPlaceLabel(profile)
|
|
?? placeId;
|
|
if (!placeLabel) throw new ConsultationProfileTruthError("profile_incomplete");
|
|
|
|
return Object.freeze({
|
|
name,
|
|
toolInput: Object.freeze({
|
|
year,
|
|
month,
|
|
day,
|
|
city: placeLabel,
|
|
lat: latitude,
|
|
lon: longitude,
|
|
tz: timezoneOffset,
|
|
ayanamsa: resolveAyanamsa(profile),
|
|
rangeStart: range.startTime,
|
|
rangeEnd: range.endTime,
|
|
}),
|
|
truth: Object.freeze({
|
|
birthDate,
|
|
birthTimeSource,
|
|
birthTimePeriod,
|
|
birthTimeStatus,
|
|
wrapsMidnight: declaredRangeWrapsMidnight(range),
|
|
placeLabel,
|
|
placeCodes: Object.freeze({
|
|
countryCode,
|
|
provinceCode,
|
|
cityCode,
|
|
districtCode,
|
|
}),
|
|
placeId,
|
|
placeType,
|
|
placeProvider,
|
|
timezoneId,
|
|
timezoneSource,
|
|
latitude,
|
|
longitude,
|
|
timezoneOffset,
|
|
}),
|
|
});
|
|
}
|
|
|
|
function reconcileConsultationMode(
|
|
requested: ConsultationBirthTimeMode,
|
|
profile: unknown,
|
|
): ConsultationBirthTimeMode {
|
|
const persisted = persistedConsultationMode(profile);
|
|
if (requested === "general_no_birth_time") return persisted ?? requested;
|
|
if (requested === "declared_birth_window") {
|
|
if (persisted === "verified_chart" || persisted === "unverified_birth_time") return persisted;
|
|
return "declared_birth_window";
|
|
}
|
|
return requested;
|
|
}
|
|
|
|
/**
|
|
* The route's pre-billing service boundary. Chart modes must load and resolve
|
|
* account truth successfully before the reservation callback can run.
|
|
*/
|
|
export function prepareConsultationRoute<Reservation, GuardResult>(
|
|
input: PrepareConsultationRouteWithGuard<Reservation, GuardResult>,
|
|
): Promise<PreparedConsultationRoute<Reservation, Awaited<GuardResult>>>;
|
|
export function prepareConsultationRoute<Reservation>(
|
|
input: PrepareConsultationRouteInput<Reservation>,
|
|
): Promise<PreparedConsultationRoute<Reservation>>;
|
|
export async function prepareConsultationRoute<Reservation, GuardResult>(
|
|
input: PrepareConsultationRouteInput<Reservation> | PrepareConsultationRouteWithGuard<Reservation, GuardResult>,
|
|
): Promise<PreparedConsultationRoute<Reservation, Awaited<GuardResult> | undefined>> {
|
|
let profile: unknown;
|
|
try {
|
|
profile = await input.loadProfile(input.userId);
|
|
} catch (error) {
|
|
if (input.mode === "general_no_birth_time") profile = null;
|
|
else if (error instanceof ConsultationProfileTruthError) throw error;
|
|
else throw new ConsultationProfileTruthError("profile_unavailable");
|
|
}
|
|
|
|
const consultationMode = reconcileConsultationMode(input.mode, profile);
|
|
let serverChart: ServerChartConsultation | null = null;
|
|
let declaredWindow: DeclaredBirthWindowConsultation | null = null;
|
|
const generalDailyReference = consultationMode === "general_no_birth_time"
|
|
|| consultationMode === "declared_birth_window"
|
|
? generalDailyReferenceFromProfile(profile)
|
|
: null;
|
|
if (isNatalMinuteConsultationMode(consultationMode)) {
|
|
const profileValue = record(profile);
|
|
const selectedTime = consultationMode === "verified_chart"
|
|
? nullableClock(profileValue ?? {}, "active_birth_time")
|
|
: nullableClock(profileValue ?? {}, "reported_birth_time");
|
|
try {
|
|
profile = await (input.resolveTimezoneOffset ?? ((value, time) => (
|
|
resolveMissingBirthTimezoneOffset(value, { preferredTime: time })
|
|
)))(profile, selectedTime ?? undefined);
|
|
} catch {
|
|
throw new ConsultationProfileTruthError("profile_unavailable");
|
|
}
|
|
serverChart = serverChartFromProfile(profile, consultationMode);
|
|
if (
|
|
consultationMode === "verified_chart"
|
|
&& serverChart.truth.birthTimeStatus === "accepted"
|
|
&& input.loadCandidateRange
|
|
) {
|
|
try {
|
|
const range = await input.loadCandidateRange(input.userId);
|
|
if (range) {
|
|
serverChart = Object.freeze({
|
|
...serverChart,
|
|
toolInput: Object.freeze({
|
|
...serverChart.toolInput,
|
|
birth_time_accuracy: "provisional" as const,
|
|
candidate_range: Object.freeze({
|
|
start_time: range.startTime,
|
|
end_time: range.endTime,
|
|
}),
|
|
}),
|
|
});
|
|
}
|
|
} catch {
|
|
// Fail closed: accepted charts still consult the representative minute.
|
|
}
|
|
}
|
|
} else if (consultationMode === "declared_birth_window") {
|
|
const range = declaredClockRangeFromProfile(record(profile) ?? {});
|
|
try {
|
|
profile = await (input.resolveTimezoneOffset ?? ((value, time) => (
|
|
resolveMissingBirthTimezoneOffset(value, { preferredTime: time })
|
|
)))(profile, range?.startTime);
|
|
} catch {
|
|
throw new ConsultationProfileTruthError("profile_unavailable");
|
|
}
|
|
declaredWindow = declaredWindowFromProfile(profile);
|
|
}
|
|
const preReserveResult = input.beforeReserve
|
|
? await input.beforeReserve({
|
|
consultationMode,
|
|
serverChart,
|
|
declaredWindow,
|
|
generalDailyReference,
|
|
}) as Awaited<GuardResult>
|
|
: undefined;
|
|
const reservation = await input.reserve();
|
|
return Object.freeze({
|
|
consultationMode,
|
|
serverChart,
|
|
declaredWindow,
|
|
generalDailyReference,
|
|
reservation,
|
|
preReserveResult,
|
|
});
|
|
}
|