import { z } from "zod"; import { HISTORICAL_PREPARATION_LIMITS, isDiagnosticToken, isRealIsoDate, resolveHistoricalInformationSet, prepareHistoricalReview, type HistoricalPreparationRequest, type HistoricalPreparation } from "@actuarial-ts/core"; import { historicalInformationSetRequestSchema } from "./historicalInformationContracts.js"; import { historicalDatasetInputSchema, customizationExposureObservationSchema, analysisDefinitionSchema, analysisRecipeSchema, exposureAllocationTargetSchema } from "./customizationContracts.js"; import { parseReviewBoundary } from "./reviewBoundary.js"; const token = z.string().refine(isDiagnosticToken), date = z.string().refine(isRealIsoDate); const bounded = (maximum: number) => z.number().int().positive().max(maximum); const historicalPreparationStructure = z.object({ id: token, version: token, information: historicalInformationSetRequestSchema, bindings: z.object({ definition: token, recipe: token, exposureTargets: token.nullable(), mappings: z.array(z.object({ sourceNamespace: token.nullable(), artifactId: token, mappingVersion: token, slot: token }).strict()), openingBalances: z.array(z.object({ index: z.number().int().nonnegative().safe(), slot: token }).strict()), }).strict(), history: historicalDatasetInputSchema, exposure: z.object({ observations: z.array(customizationExposureObservationSchema).max(HISTORICAL_PREPARATION_LIMITS.maximumExposures), revisionPrecedence: z.enum(["sequence", "corrected-at"]) }).strict().nullable(), evaluationDates: z.array(date).min(1).max(HISTORICAL_PREPARATION_LIMITS.maximumValuations), view: z.discriminatedUnion("kind", [z.object({ kind: z.literal("as-known") }).strict(), z.object({ kind: z.literal("restated"), knowledgeCutoff: token, rationale: token }).strict()]), unknownRecordAvailability: z.discriminatedUnion("kind", [z.object({ kind: z.literal("require-known") }).strict(), z.object({ kind: z.literal("allow-retrospective"), rationale: token }).strict()]), limits: z.object({ maximumRecords: bounded(HISTORICAL_PREPARATION_LIMITS.maximumRecords), maximumExposures: bounded(HISTORICAL_PREPARATION_LIMITS.maximumExposures), maximumValuations: bounded(HISTORICAL_PREPARATION_LIMITS.maximumValuations), maximumInputCharacters: bounded(HISTORICAL_PREPARATION_LIMITS.maximumInputCharacters), maximumOutputCharacters: bounded(HISTORICAL_PREPARATION_LIMITS.maximumOutputCharacters) }).strict(), }).strict(); export const historicalPreparationRequestSchema: z.ZodType = historicalPreparationStructure.superRefine((request, context) => { const information = resolveHistoricalInformationSet(request.information); for (const [slot, schema] of [ [request.bindings.definition, analysisDefinitionSchema], [request.bindings.recipe, analysisRecipeSchema], [request.bindings.exposureTargets, z.array(exposureAllocationTargetSchema)], ] as const) { const content = information.decisions.find((decision) => decision.slot === slot)?.selected?.snapshot.content; if (content !== undefined) { const parsed = (schema as z.ZodType).safeParse(content); if (!parsed.success) for (const issue of parsed.error.issues) context.addIssue({ code: z.ZodIssueCode.custom, path: ["information", "selected", slot!, "content", ...issue.path], message: issue.message }); } } }); export function parseHistoricalPreparationRequest(input: unknown): HistoricalPreparationRequest { return parseReviewBoundary(historicalPreparationRequestSchema, input); } export function prepareHistoricalReviewFromInput(input: unknown): HistoricalPreparation { return prepareHistoricalReview(parseHistoricalPreparationRequest(input)); }