import { PatternDef } from '../pattern.js'; // Retry pattern: blind mechanical retry for read-only operations. // Valid because a read-only action is idempotent by nature: clicking a query // button any number of times never changes the result, so retrying the same // call is always safe. No idempotency key, no query-and-resume, no compensate. export const retryPattern: PatternDef = { name: 'retry', params: { max: { type: 'int', min: 1, default: 3 }, backoffMs: { type: 'int', min: 0, default: 0 }, }, }; export interface RetryAction { /** Function to call, e.g. 'queryOrderList' */ call: string; /** Type name of the single argument passed through, e.g. 'OrderQueryParams' */ params?: string; } export interface RetryRefArgs { max?: number; backoffMs?: number; action: RetryAction; /** Generated function name; defaults to 'WithRetry' */ fnName?: string; } export function renderRetry(args: RetryRefArgs): string { if (args.max !== undefined && args.max < 1) throw new Error('retry: max must be >= 1'); if (!args.action.call) throw new Error('retry: action.call is required'); const max = args.max ?? 3; const backoffMs = args.backoffMs ?? 0; const call = args.action.call; const paramType = args.action.params ?? 'unknown'; const fnName = args.fnName ?? `${call}WithRetry`; const retryLine = backoffMs > 0 ? ` await sleep(${backoffMs});` : ''; return [ `export async function ${fnName}(params: ${paramType}) {`, ` for (let attempt = 1; ; attempt++) {`, ` try {`, ` return await ${call}(params);`, ` } catch (err) {`, ` if (attempt >= ${max}) throw err;`, retryLine, ` }`, ` }`, `}`, '', ].join('\n'); }