ProvenPin Install

opossum circuit breaker with fallback and recovery: working versions (tested 2026-10-15)

Wrap a flaky async call in an opossum circuit breaker that opens after failures, serves a fallback while open, half-opens after resetTimeout and closes on success, with event and stats checks.

passed2 passed · 0 failed2026-10-15 17:11 UTC
Status
passed
Last passed
2026-10-15 17:11 UTC
Tests
2 / 2
Language
TypeScript
Runtime
node v22.23.3
pinned versions4
opossum@10.0.0@types/node@26.6.5tsx@4.23.15@types/opossum@8.1.9
run it
npm install --no-audit --no-fund --ignore-scripts=false
npm test
test environment
node v22.23.3
linux 6.17.0-1022-azure
x64
runner: github-actions 38775053802

The code

Exactly the files that passed. Your agent can pull them with get_recipe("opossum-circuit-breaker") (install), or see them on GitHub.

package.json
{
  "name": "opossum-circuit-breaker",
  "private": true,
  "type": "module",
  "scripts": {
    "test": "node --import tsx --test test/*.test.ts"
  },
  "dependencies": {
    "opossum": "10.0.0"
  },
  "devDependencies": {
    "@types/node": "26.6.5",
    "tsx": "4.23.15",
    "@types/opossum": "8.1.9"
  }
}
src/breaker.ts
import CircuitBreaker from "opossum";

export type Fetcher = (id: string) => Promise<{ id: string; price: number }>;

export function createPriceBreaker(fetcher: Fetcher, opts: { resetTimeout?: number; timeout?: number } = {}) {
  const breaker = new CircuitBreaker(fetcher, {
    timeout: opts.timeout ?? 100,
    errorThresholdPercentage: 50,
    volumeThreshold: 2,
    resetTimeout: opts.resetTimeout ?? 50,
  });
  const events: string[] = [];
  for (const ev of ["open", "halfOpen", "close"] as const) breaker.on(ev, () => events.push(ev));
  breaker.fallback((id: string) => ({ id, price: -1, cached: true }));
  return { breaker, events };
}
test/breaker.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
import { createPriceBreaker } from "../src/breaker.ts";

const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));

test("opens after failures, serves fallback, then recovers", async () => {
  let healthy = false;
  let calls = 0;
  const { breaker, events } = createPriceBreaker(async (id) => {
    calls++;
    if (!healthy) throw new Error("upstream down");
    return { id, price: 42 };
  });

  assert.deepEqual(await breaker.fire("a"), { id: "a", price: -1, cached: true });
  assert.deepEqual(await breaker.fire("a"), { id: "a", price: -1, cached: true });
  assert.equal(breaker.opened, true);
  assert.deepEqual(events, ["open"]);

  const before = calls;
  await breaker.fire("a");
  assert.equal(calls, before, "open circuit must not call upstream");

  healthy = true;
  await sleep(80);
  assert.equal(breaker.halfOpen, true);
  assert.deepEqual(await breaker.fire("b"), { id: "b", price: 42 });
  assert.equal(breaker.closed, true);
  assert.deepEqual(events, ["open", "halfOpen", "close"]);
  assert.ok(breaker.stats.fallbacks >= 3);
  breaker.shutdown();
});

test("slow calls count as timeouts", async () => {
  const { breaker } = createPriceBreaker(async (id) => {
    await sleep(200);
    return { id, price: 1 };
  }, { timeout: 20 });
  const r = await breaker.fire("slow");
  assert.equal((r as { cached?: boolean }).cached, true);
  assert.equal(breaker.stats.timeouts, 1);
  breaker.shutdown();
});

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.