Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
56 lines
2.5 KiB
TypeScript
56 lines
2.5 KiB
TypeScript
import { resolveServerOwnedChartBirth } from "./chart-birth-truth.ts";
|
|
|
|
/**
|
|
* 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}$/;
|
|
|
|
function record(value: unknown): Record<string, unknown> | null {
|
|
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
? value as Record<string, unknown>
|
|
: null;
|
|
}
|
|
|
|
/** 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;
|
|
}
|