Files
Jyotisha/frontend/src/lib/personal-report-contract.server-core.ts
T
2026-08-15 05:10:37 +08:00

53 lines
1.8 KiB
TypeScript

import { createHash } from "node:crypto";
import {
canonicalEvidence,
safeParseReportDocument,
ReportDocumentValidationError,
type EvidenceAppendix,
type ReportDocument,
type ReportDocumentParseResult,
} from "./personal-report-contract.ts";
/**
* Server hash core for ReportDocument v1/v2.
*
* Pure Node implementation (node:crypto) without the server-only marker so
* tests can import it directly; the production entry
* personal-report-contract.server.ts adds `import "server-only"` and
* re-exports this module. Client bundles must never import this file.
*
* The canonical isomorphic parse runs first, then provenance.evidenceHash is
* recomputed over the evidence appendix. Stored documents are never trusted to
* self-report their evidence hash. The Python validator performs the same
* recomputation for both schema versions.
*/
export function computeEvidenceHash(appendix: EvidenceAppendix): string {
const canonical = canonicalEvidence(appendix);
return createHash("sha256").update(JSON.stringify(canonical), "utf8").digest("hex");
}
export function safeParseServerReportDocument(input: unknown): ReportDocumentParseResult {
const parsed = safeParseReportDocument(input);
if (!parsed.ok) return parsed;
const recomputed = computeEvidenceHash(parsed.document.evidenceAppendix);
if (parsed.document.provenance.evidenceHash !== recomputed) {
return {
ok: false,
errors: [
{
path: "provenance.evidenceHash",
message: `does not match recomputed evidence hash ${recomputed}`,
},
],
};
}
return parsed;
}
export function parseServerReportDocument(input: unknown): ReportDocument {
const result = safeParseServerReportDocument(input);
if (!result.ok) throw new ReportDocumentValidationError(result.errors);
return result.document;
}