import { z } from "zod"; import { AnalysisReviewError, canonicalJson, expandAnalysisScenarioGrid, runAnalysisScenarios, runAnalysisScenarioGrid, runAnalysisScenarioAttribution, runAnalysisScenarioBridge } from "@actuarial-ts/core"; import { analysisScenarioSchema, analysisScenarioGridSchema } from "./customizationContracts.js"; import { recipeExecutionRequestSchema } from "./recipeDevelopmentContracts.js"; import { parseReviewBoundary } from "./reviewBoundary.js"; /** Standalone boundary ceilings; expanded input is a work proxy, not a peak-memory or latency guarantee. */ export const ANALYSIS_SCENARIO_INPUT_LIMITS = Object.freeze({ maximumActiveScenarios: 256, maximumRecipeRuns: 257, maximumInputCharacters: 20000000, maximumExpandedInputCharacters: 80000000, maximumOutputCharacters: 80000000 }); type ScenarioRequest = Parameters[0]; type GridRequest = Parameters[0]; const limits = z.object({ maximumActiveScenarios: z.number().int().safe().positive().max(ANALYSIS_SCENARIO_INPUT_LIMITS.maximumActiveScenarios) }).strict(); const scenarios = z.array(analysisScenarioSchema).max(ANALYSIS_SCENARIO_INPUT_LIMITS.maximumActiveScenarios); export const analysisScenariosRequestSchema: z.ZodType = z.lazy(() => recipeExecutionRequestSchema().extend({ scenarios, limits })); export const analysisScenarioGridRequestSchema: z.ZodType = z.lazy(() => recipeExecutionRequestSchema().extend({ grid: analysisScenarioGridSchema, limits })); export const analysisScenarioAttributionRequestSchema: z.ZodType[0]> = analysisScenariosRequestSchema; export const analysisScenarioBridgeRequestSchema: z.ZodType[0]> = analysisScenariosRequestSchema; function refuse(path: string, message: string): never { throw new AnalysisReviewError([{ code: "RESOURCE_LIMIT_EXCEEDED", path, message, remedies: ["Reduce the declared population or scenario/coalition count, or use an explicitly bounded host execution plan"] }]); } function bounded(schema: z.ZodType, input: unknown, kind: "scenarios" | "grid" | "attribution" | "bridge"): T { const request = parseReviewBoundary(schema, input), characters = canonicalJson(request).length; if (characters > ANALYSIS_SCENARIO_INPUT_LIMITS.maximumInputCharacters) refuse("$", "Scenario input exceeds the standalone boundary character ceiling"); const runs = "grid" in request ? 1 + expandAnalysisScenarioGrid(request.grid, request.limits).length : kind === "attribution" ? 2 ** request.scenarios.length : 1 + request.scenarios.length; if (runs > ANALYSIS_SCENARIO_INPUT_LIMITS.maximumRecipeRuns) refuse(kind === "grid" ? "$.grid" : "$.scenarios", "Scenario execution exceeds the standalone recipe-run ceiling"); if (runs * characters > ANALYSIS_SCENARIO_INPUT_LIMITS.maximumExpandedInputCharacters) refuse("$", "Scenario input multiplied by recipe runs exceeds the standalone expansion ceiling"); return request; } function output(result: T): T { if (canonicalJson(result).length > ANALYSIS_SCENARIO_INPUT_LIMITS.maximumOutputCharacters) refuse("$", "Native scenario output exceeds the standalone serialized-output ceiling"); return result; } export function parseAnalysisScenariosRequest(input: unknown): ScenarioRequest { return bounded(analysisScenariosRequestSchema, input, "scenarios"); } export function parseAnalysisScenarioGridRequest(input: unknown): GridRequest { return bounded(analysisScenarioGridRequestSchema, input, "grid"); } export function parseAnalysisScenarioAttributionRequest(input: unknown): Parameters[0] { return bounded(analysisScenarioAttributionRequestSchema, input, "attribution"); } export function parseAnalysisScenarioBridgeRequest(input: unknown): Parameters[0] { return bounded(analysisScenarioBridgeRequestSchema, input, "bridge"); } export function runAnalysisScenariosFromInput(input: unknown): ReturnType { return output(runAnalysisScenarios(parseAnalysisScenariosRequest(input))); } export function runAnalysisScenarioGridFromInput(input: unknown): ReturnType { return output(runAnalysisScenarioGrid(parseAnalysisScenarioGridRequest(input))); } export function runAnalysisScenarioAttributionFromInput(input: unknown): ReturnType { return output(runAnalysisScenarioAttribution(parseAnalysisScenarioAttributionRequest(input))); } export function runAnalysisScenarioBridgeFromInput(input: unknown): ReturnType { return output(runAnalysisScenarioBridge(parseAnalysisScenarioBridgeRequest(input))); }