import { reviewScalarUnitMapSchema } from "./reviewScalarUnitContracts.js"; import { z } from "zod"; import { NOMINAL_CASHFLOW_LIMITS, NOMINAL_CASHFLOW_CONTRACT_VERSION, NOMINAL_CASHFLOW_SOURCE_CONTRACT_VERSION, REVIEW_NOMINAL_CASHFLOW_CONTRACT_VERSION, aggregateNominalCashflows, calculateReviewNominalCashflows, createNominalCashflowSource, isDiagnosticToken, isRealIsoDate, replayNominalCashflows, replayReviewNominalCashflows, validateReviewNominalCashflowRequest, type AnalysisSnapshot, type NominalCashflowInput, type NominalCashflowResult, type NominalCashflowSourceDefinition, type ReviewNominalCashflowRequest, type ReviewNominalCashflowResult } from "@actuarial-ts/core"; import { analysisContextSchema, analysisReferenceSchema, analysisSnapshotSchema, quantitySemanticsSchema } from "./reviewContracts.js"; import { parseReviewBoundary } from "./reviewBoundary.js"; const token = z.string().refine(isDiagnosticToken), date = z.string().refine(isRealIsoDate); export const nominalNumericTokenSchema = z.union([z.number().finite(), z.object({ kind: z.literal("negative-zero"), value: z.literal(0) }).strict()]); const member = z.object({ id: token, origin: token, valuationDate: date.nullable() }).strict(); const cashflow = z.object({ fromMonths: nominalNumericTokenSchema, toMonths: nominalNumericTokenSchema, amount: nominalNumericTokenSchema.nullable(), reason: token.nullable() }).strict(); const schedule = z.object({ memberId: token, cashflows: z.array(cashflow).max(NOMINAL_CASHFLOW_LIMITS.maximumCashflows).nullable(), reason: token.nullable() }).strict(); const sourceMeaning = z.object({ quantity: quantitySemanticsSchema, financialBasis: analysisReferenceSchema, financialPopulation: analysisReferenceSchema }).strict(); export const nominalCashflowLimitsSchema = z.object({ maximumOrigins: z.number().int().positive().max(NOMINAL_CASHFLOW_LIMITS.maximumOrigins), maximumCashflows: z.number().int().positive().max(NOMINAL_CASHFLOW_LIMITS.maximumCashflows), maximumIntervals: z.number().int().positive().max(NOMINAL_CASHFLOW_LIMITS.maximumIntervals), maximumInputCharacters: z.number().int().positive().max(NOMINAL_CASHFLOW_LIMITS.maximumInputCharacters), maximumOutputCharacters: z.number().int().positive().max(NOMINAL_CASHFLOW_LIMITS.maximumOutputCharacters) }).strict(); const horizon = z.discriminatedUnion("kind", [z.object({ kind: z.literal("full-schedules") }).strict(), z.object({ kind: z.literal("through-months"), months: nominalNumericTokenSchema }).strict()]); const policies = z.object({ overlap: z.enum(["reject", "retain-qualified"]), unordered: z.enum(["reject", "retain-qualified"]), missing: z.enum(["reject", "retain-unavailable"]), overflow: z.enum(["reject", "retain-unavailable"]), crossingHorizon: z.enum(["reject", "retain-unavailable"]) }).strict(); export const nominalCashflowInputSchema = z.object({ members: z.array(member).min(1).max(NOMINAL_CASHFLOW_LIMITS.maximumOrigins), schedules: z.array(schedule).max(NOMINAL_CASHFLOW_LIMITS.maximumOrigins), horizon, policies, limits: nominalCashflowLimitsSchema }).strict(); const pattern = z.object({ rows: z.array(z.object({ origin: token, latestAge: nominalNumericTokenSchema, unpaid: nominalNumericTokenSchema, cashflows: z.array(cashflow.omit({ reason: true })) }).strict()).max(NOMINAL_CASHFLOW_LIMITS.maximumOrigins), warnings: z.array(token) }).strict(); const sourceBase = { id: token, version: token, asOfDate: date, members: z.array(member).min(1).max(NOMINAL_CASHFLOW_LIMITS.maximumOrigins), meaning: sourceMeaning, limits: nominalCashflowLimitsSchema }; export const nominalCashflowSourceDefinitionSchema = z.discriminatedUnion("kind", [z.object({ ...sourceBase, kind: z.literal("explicit"), schedules: z.array(schedule).max(NOMINAL_CASHFLOW_LIMITS.maximumOrigins) }).strict(), z.object({ ...sourceBase, kind: z.literal("payout-pattern"), pattern }).strict()]); export const nominalCashflowSourceContentSchema = z.object({ contractVersion: z.literal(NOMINAL_CASHFLOW_SOURCE_CONTRACT_VERSION), kind: z.enum(["explicit", "payout-pattern"]), asOfDate: date, members: z.array(member).min(1).max(NOMINAL_CASHFLOW_LIMITS.maximumOrigins), meaning: sourceMeaning, schedules: z.array(schedule).max(NOMINAL_CASHFLOW_LIMITS.maximumOrigins), pattern: pattern.nullable() }).strict(); export const nominalCashflowSourceSnapshotSchema = analysisSnapshotSchema.extend({ content: nominalCashflowSourceContentSchema }); export const reviewNominalCashflowRequestSchema = z.object({ id: token, version: token, intendedPurpose: token, source: nominalCashflowSourceSnapshotSchema, context: analysisContextSchema, quantity: quantitySemanticsSchema, scope: z.discriminatedUnion("kind", [z.object({ kind: z.literal("all-source-members") }).strict(), z.object({ kind: z.literal("source-member-indices"), indexes: z.array(z.number().int().nonnegative()).min(1).max(NOMINAL_CASHFLOW_LIMITS.maximumOrigins) }).strict()]), clock: z.discriminatedUnion("kind", [z.object({ kind: z.literal("common-valuation"), valuationDate: date }).strict(), z.object({ kind: z.literal("per-member-valuation-relative"), rationale: token }).strict()]), horizon, policies, declaration: z.object({ actorId: token, knownDate: date, rationale: token }).strict(), snapshots: z.array(analysisSnapshotSchema), limits: nominalCashflowLimitsSchema, }).strict(); const index=z.number().int().nonnegative(), finite=z.number().finite(); const finding=z.object({code:token,path:token,message:token,coordinates:z.record(z.union([z.string(),finite,z.boolean(),z.null()])).optional(),remedies:z.array(z.string())}).strict(); const aggregate={value:finite.nullable(),includedValue:finite.nullable(),availability:z.enum(["complete","partial","unavailable","overflow","outside-horizon"]),reason:token.nullable()}; const contribution=z.object({memberIndex:index,scheduleIndex:index,cashflowIndex:index,fromMonths:finite.nonnegative(),toMonths:finite.nonnegative(),amount:finite.nullable(),originalTokens:cashflow,disposition:z.enum(["included","after-horizon","crosses-horizon","unavailable-amount"]),reason:token.nullable()}).strict(); export const nominalCashflowResultSchema=z.object({contractVersion:z.literal(NOMINAL_CASHFLOW_CONTRACT_VERSION),request:nominalCashflowInputSchema,rows:z.array(z.object({...aggregate,memberIndex:index,member,scheduleIndex:index.nullable(),scheduleState:z.enum(["present","declared-empty","absent","unavailable"]),contributions:z.array(contribution)}).strict()),intervals:z.array(z.object({...aggregate,intervalIndex:index,fromMonths:finite.nonnegative(),toMonths:finite.nonnegative(),contributions:z.array(contribution),memberCoverage:z.enum(["complete-schedules","unknown-schedules"])}).strict()),totals:z.object(aggregate).strict(),unavailableMemberIndexes:z.array(index),findings:z.array(finding),assurance:z.object({calculation:z.literal("rate-free-original-order-ieee754-addition"),intervalGrouping:z.literal("exact-original-endpoints-first-use-order"),emptySchedules:z.literal("explicit-numeric-zero"),absentSchedules:z.literal("unavailable-never-zero"),inputSignedZero:z.literal("bound-explicit-numeric-tokens"),aggregateZero:z.literal("canonical-positive-zero"),sourceAuthenticity:z.literal("not-verified"),financialComparability:z.literal("not-established")}).strict()}).strict(); export const reviewNominalCashflowResultSchema=z.object({contractVersion:z.literal(REVIEW_NOMINAL_CASHFLOW_CONTRACT_VERSION),sdkVersion:token,request:reviewNominalCashflowRequestSchema,context:analysisContextSchema,quantity:quantitySemanticsSchema,unit:token,knownDate:date,evidenceUnits:reviewScalarUnitMapSchema,native:nominalCashflowResultSchema,sourceCoordinates:z.object({memberIndexes:z.array(index),scheduleIndexes:z.array(index)}).strict(),snapshots:z.array(analysisSnapshotSchema),findings:z.array(finding),assurance:z.object({calculation:z.literal("native-rate-free-nominal-aggregation"),source:z.literal("exact-retained-declared-source-snapshot"),sourceAuthenticity:z.literal("not-verified"),payoutModelReplay:z.literal("not-claimed"),financialComparability:z.literal("declared-context-bound-not-authenticated"),relativeIntervals:z.literal("calendar-equivalence-not-inferred"),uncertainty:z.literal("not-quantified")}).strict(),reference:analysisReferenceSchema}).strict(); export function aggregateNominalCashflowsFromInput(input: unknown): NominalCashflowResult { return aggregateNominalCashflows(parseReviewBoundary(nominalCashflowInputSchema, input) as NominalCashflowInput); } export function createNominalCashflowSourceFromInput(input: unknown): AnalysisSnapshot { return createNominalCashflowSource(parseReviewBoundary(nominalCashflowSourceDefinitionSchema, input) as NominalCashflowSourceDefinition); } export function parseReviewNominalCashflowRequest(input: unknown): ReviewNominalCashflowRequest { return validateReviewNominalCashflowRequest(parseReviewBoundary(reviewNominalCashflowRequestSchema, input) as ReviewNominalCashflowRequest).request; } export function calculateReviewNominalCashflowsFromInput(input: unknown): ReviewNominalCashflowResult { return calculateReviewNominalCashflows(parseReviewNominalCashflowRequest(input)); } export function replayNominalCashflowsFromInput(input:unknown):NominalCashflowResult{return replayNominalCashflows(parseReviewBoundary(nominalCashflowResultSchema,input) as NominalCashflowResult);} export function replayReviewNominalCashflowsFromInput(input: unknown): ReviewNominalCashflowResult { return replayReviewNominalCashflows(parseReviewBoundary(reviewNominalCashflowResultSchema,input) as ReviewNominalCashflowResult); }