UNPKG

run402-mcp

Version:

MCP server for Run402 — full-stack backend infrastructure for AI agents: Postgres, auth, storage, serverless functions and atomic deploys. Paid with x402/MPP. Includes $0.03 image generation.

62 lines • 2.28 kB
/** * `withRetry(fn, opts?)` — exponential-backoff retry helper for SDK * operations. Defaults to the canonical {@link isRetryableRun402Error} * policy. `safeToRetry` alone does not trigger a retry; it only says a * repeated mutation should not duplicate/corrupt state. * * Caller-controlled by design: the helper is policy-only. It does NOT own * idempotency keys. Pair with the SDK method's own `idempotencyKey` * option by baking it into the closure: * * @example * const release = await withRetry( * () => r.project(id).apply(spec, { idempotencyKey: "my-deploy-2026-05-01" }), * { attempts: 3, onRetry: (e, n, ms) => console.warn(`retry ${n} in ${ms}ms`) }, * ); * * The closure carries the same `idempotencyKey` on every retry, so the * gateway dedups duplicates server-side and a retried mutation does not * double-apply. */ import { isRetryableRun402Error } from "./errors.js"; /** * Run `fn` with exponential backoff, retrying retryable {@link Run402Error} * subclasses up to `attempts` times. After exhausting attempts (or when * `retryIf` returns false), throws the LAST observed error — preserving the * structured envelope so the caller's catch handler can branch on `kind`. */ export async function withRetry(fn, opts = {}) { const attempts = opts.attempts ?? 3; const base = opts.baseDelayMs ?? 250; const max = opts.maxDelayMs ?? 5_000; const retryIf = opts.retryIf ?? defaultRetryIf; let last; for (let attempt = 1; attempt <= attempts; attempt++) { try { return await fn(); } catch (e) { last = e; if (attempt === attempts) break; if (!retryIf(e, attempt)) break; const delay = Math.min(max, base * 2 ** (attempt - 1)); try { opts.onRetry?.(e, attempt, delay); } catch { /* swallow — buggy onRetry must not abort the retry chain */ } await sleep(delay); } } throw last; } function defaultRetryIf(error, _attempt) { return isRetryableRun402Error(error); } function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } //# sourceMappingURL=retry.js.map