import type { Carte } from "./carte.js"; import { CarteExecutionError, type CarteExecutionErrorCategory, type CarteExecutionLogger, defaultExecutionLogger, } from "./errors.js"; import type { ValidatedPlan } from "./validate.js"; /** * Optional execution-time configuration. The `logger` is the *only* place the * underlying `cause` (driver error, ZodError) is delivered — wire it to * Sentry, Datadog, or structured logs for server-side diagnostics. The thrown * `CarteExecutionError`'s `toModelSafeJSON()` projection deliberately excludes * `cause` to preserve Invariant 2. */ export interface ExecutePlanOptions { logger?: CarteExecutionLogger; } /** * Executes every panel's query in `plan` against `carte`, in parallel. * The plan is assumed to have already passed `validatePlan`, so panel shape, * query existence, and access have been verified — but params are still * re-parsed here to apply Zod transforms and produce the typed value the query * slot expects. Returns rows indexed by panel index. * * Each panel's result is validated against the entry's `returns` schema; * mismatches surface as `CarteExecutionError` with category `"returns_mismatch"`. * The original `ZodError` is delivered to `options.logger` (server-side only) * and attached to `Error.prototype.cause` for stack traces — but * `toModelSafeJSON()` excludes both. * * v1 has no cross-panel data dependencies — every panel runs concurrently. */ export async function executePlan( plan: ValidatedPlan, carte: Carte, options?: ExecutePlanOptions, ): Promise { const logger = options?.logger ?? defaultExecutionLogger; return Promise.all( plan.panels.map(async (panel, panelIndex) => { const queryId = panel.query.id; const entry = carte[queryId]; if (!entry) { throw raise({ category: "unknown_query", panelIndex, queryId, cause: undefined, logger, }); } let raw: unknown; try { raw = await entry.query(panel.query.params); } catch (cause) { throw raise({ category: "query_failed", panelIndex, queryId, cause, logger, }); } const returnsResult = entry.returns.safeParse(raw); if (!returnsResult.success) { throw raise({ category: "returns_mismatch", panelIndex, queryId, cause: returnsResult.error, logger, }); } return returnsResult.data; }), ); } function raise(args: { category: CarteExecutionErrorCategory; panelIndex: number; queryId: string; cause: unknown; logger: CarteExecutionLogger; }): CarteExecutionError { const err = new CarteExecutionError({ category: args.category, panelIndex: args.panelIndex, queryId: args.queryId, cause: args.cause, }); args.logger({ category: args.category, panelIndex: args.panelIndex, queryId: args.queryId, correlationId: err.correlationId, cause: args.cause, }); return err; }