import { z } from "zod"; import { AnalysisReviewError, DiagnosticValidationError, calculateAnalysisStatistic, canonicalJson, isRealIsoDate, type AnalysisStatisticResult } from "@actuarial-ts/core"; import { analysisMeasureDefinitionSchema, preparedHistoricalObservationSchema } from "./customizationContracts.js"; import { parseReviewBoundary } from "./reviewBoundary.js"; /** Limits of the standalone data boundary, not a claim about the legacy core kernel. */ export const ANALYSIS_STATISTIC_INPUT_LIMITS = Object.freeze({ maximumObservations: 50000, maximumInputCharacters: 20000000 }); export const analysisStatisticRequestSchema = z.object({ measure: analysisMeasureDefinitionSchema.refine((measure) => measure.condition === undefined, { path: ["condition"], message: "Apply the population and condition before supplying prepared observations, or use an analysis recipe" }), observations: z.array(preparedHistoricalObservationSchema).max(ANALYSIS_STATISTIC_INPUT_LIMITS.maximumObservations).superRefine((observations, context) => { const seen = new Set(); observations.forEach((observation, i) => { const key = canonicalJson([observation.sourceNamespace, observation.claimId, observation.evaluationDate]); if (seen.has(key)) context.addIssue({ code: z.ZodIssueCode.custom, path: [i], message: "Prepared observations must resolve revisions/components to one claim observation per evaluation date" }); seen.add(key); }); }), selection: z.discriminatedUnion("kind", [ z.object({ kind: z.literal("all-observations") }).strict(), z.object({ kind: z.literal("evaluation-date"), evaluationDate: z.string().refine(isRealIsoDate), observationSelection: z.enum(["exact", "latest-on-or-before"]) }).strict(), ]), }).strict(); export function parseAnalysisStatisticRequest(input: unknown): Parameters[0] { const request = parseReviewBoundary(analysisStatisticRequestSchema, input); if (canonicalJson(request).length > ANALYSIS_STATISTIC_INPUT_LIMITS.maximumInputCharacters) throw new AnalysisReviewError([{ code: "RESOURCE_LIMIT_EXCEEDED", path: "$", message: "Standalone statistic input exceeds its character ceiling", remedies: ["Use an explicitly selected bounded population or an appropriately bounded recipe execution"] }]); return request; } /** Executes the existing core kernel on a caller-selected, revision-resolved population. */ export function calculateAnalysisStatisticFromInput(input: unknown): AnalysisStatisticResult { const request = parseAnalysisStatisticRequest(input); try { return calculateAnalysisStatistic(request); } catch (error) { // Input is already safe and finite. A nonfinite JSON refusal here concerns native output arithmetic. if (!(error instanceof DiagnosticValidationError) || !error.issues.every((issue) => issue.code === "invalid-json-value")) throw error; throw new AnalysisReviewError(error.issues.map((issue) => ({ code: "STATISTIC_NUMERIC_FAILURE", path: "$", message: issue.message, coordinates: { nativeOutputPath: issue.path }, remedies: ["Inspect input magnitudes and declared units against the native estimator's numerical limits; no automatic rescaling or replacement value is applied"] }))); } }