Files
Jyotisha/frontend/src/lib/report-birth-binding.ts
T
jesse-uxandGrok (agent loop) d2296c2654 把前后端共用的请求契约和小工具收成一份,避免再各抄一份。
报告创建请求、校正入口和同名类型改从 contracts 推导;同义的对象判断与 sha256 改走共享函数。调用处用 import 别名保留原名,使 diff 低于 1500 行。行为、哈希和状态字符串不变。

Co-Authored-By: Grok (agent loop)
2026-10-15 22:22:44 +08:00

51 lines
2.3 KiB
TypeScript

import { resolveServerOwnedChartBirth } from "./chart-birth-truth.ts";
import { asRecord as record } from "@/lib/guards";
/**
* Report ↔ birth-data binding for the report cover (TASK-birth-sky-followup-20260928,
* product decision A).
*
* A report records the fingerprint of the birth data it was generated from, in
* `subject.birthFingerprint`. The fingerprint is the chart page's
* `profileFingerprint`: `resolveServerOwnedChartBirth` over the **stored** row,
* before any in-memory timezone fill. Reports generated before this field
* existed never get one. Nothing reads it back yet: the report sky cover that
* compared it was removed (TASK-mobile-chart-and-confirmed-edit-20260929 M5,
* product 2026-09-29); the field is kept so a later binding can use it. It is
* never sent to a client or to the model.
*/
/** Server-only key stamped on the loaded profile row; never persisted, never serialized to a client. */
export const REPORT_BIRTH_FINGERPRINT_KEY = "report_birth_fingerprint";
const FINGERPRINT_PATTERN = /^[0-9a-f]{32}$/;
/** Same algorithm and same input (the stored row) as the chart page's `profileFingerprint`. */
export function subjectBirthFingerprint(storedRow: unknown): string {
return resolveServerOwnedChartBirth(storedRow).fingerprint;
}
/**
* Carries the stored row's fingerprint on the timezone-filled row the
* generators read. Filling a missing offset changes the fingerprint, so it
* must be taken before the fill.
*/
export function stampReportBirthFingerprint(storedRow: unknown, filledRow: unknown): unknown {
const filled = record(filledRow);
if (!filled) return filledRow;
return { ...filled, [REPORT_BIRTH_FINGERPRINT_KEY]: subjectBirthFingerprint(storedRow) };
}
export function readReportBirthFingerprint(profile: unknown): string | undefined {
const value = record(profile)?.[REPORT_BIRTH_FINGERPRINT_KEY];
return typeof value === "string" && FINGERPRINT_PATTERN.test(value) ? value : undefined;
}
/** Writes `subject.birthFingerprint` on a report document; a missing fingerprint leaves it untouched. */
export function withReportBirthFingerprint<T>(document: T, fingerprint: string | undefined): T {
const source = record(document);
const subject = record(source?.subject);
if (!source || !subject || !fingerprint) return document;
return { ...source, subject: { ...subject, birthFingerprint: fingerprint } } as T;
}