core api
the base cruel api for fetch, services, and async functions
quick start
import { cruel } from "cruel"
const api = cruel(fetch, {
fail: 0.1,
delay: [100, 500],
timeout: 0.05,
})
const res = await api("https://api.example.com")cruel(fn, options) injects chaos into async functions.
try without api keys
cd packages/examples
bun run run.ts corethis runs local core examples only (core/basic.ts, core/resilience.ts, core/control.ts).
chaos options
type ChaosOptions = {
fail?: number
delay?: number | [number, number]
timeout?: number
jitter?: number
corrupt?: number
spike?: number | [number, number]
enabled?: boolean
}fail: chance to throwCruelErrordelay: fixed or random latencytimeout: chance to never resolvejitter: extra random latency between0andjittercorrupt: chance to corrupt string outputspike: occasional added latencyenabled: force-disable on a wrapper
shortcut wrappers
cruel.fail(fn, 0.1)
cruel.slow(fn, [100, 500])
cruel.timeout(fn, 0.05)
cruel.flaky(fn)
cruel.unreliable(fn)
cruel.nightmare(fn)domains
network
cruel.network.latency(fn, [100, 500])
cruel.network.packetLoss(fn, 0.1)
cruel.network.disconnect(fn, 0.05)
cruel.network.dns(fn, 0.02)
cruel.network.bandwidth(fn, 256)
cruel.network.slow(fn)
cruel.network.unstable(fn)
cruel.network.offline(fn)http
cruel.http.status(fn, 500, 0.1)
cruel.http.status(fn, [500, 502, 503])
cruel.http.rateLimit(fn, { rate: 0.1, retryAfter: 60 })
cruel.http.serverError(fn, 0.1)
cruel.http.clientError(fn, 0.1)
cruel.http.badGateway(fn)
cruel.http.serviceUnavailable(fn)
cruel.http.gatewayTimeout(fn)stream
cruel.stream.cut(fn, 0.1)
cruel.stream.pause(fn, [200, 1000])
cruel.stream.corrupt(fn, 0.1)
cruel.stream.truncate(fn, 0.1)
cruel.stream.reorder(fn, 0.1)
cruel.stream.duplicate(fn, 0.1)
cruel.stream.dropChunks(fn, 0.1)
cruel.stream.corruptChunks(fn, 0.1)
cruel.stream.slow(fn)
cruel.stream.flaky(fn)ai
cruel.ai.rateLimit(fn, 0.1)
cruel.ai.overloaded(fn, 0.05)
cruel.ai.contextLength(fn, 0.02)
cruel.ai.contentFilter(fn, 0.01)
cruel.ai.modelUnavailable(fn, 0.02)
cruel.ai.slowTokens(fn, [50, 200])
cruel.ai.streamCut(fn, 0.1)
cruel.ai.partialResponse(fn, 0.1)
cruel.ai.invalidJson(fn, 0.05)
cruel.ai.realistic(fn)
cruel.ai.nightmare(fn)state and control
cruel.enable({ fail: 0.1, delay: [100, 500] })
cruel.disable()
cruel.toggle()
cruel.isEnabled()
await cruel.scope(async () => {
await api("https://api.example.com")
}, { fail: 0.2 })resilience wrappers
cruel.circuitBreaker(fn, { threshold: 5, timeout: 30000 })
cruel.retry(fn, { attempts: 3, delay: 1000, backoff: "exponential" })
cruel.bulkhead(fn, { maxConcurrent: 10, maxQueue: 100 })
cruel.withTimeout(fn, { ms: 5000 })
cruel.fallback(fn, { fallback: backup })
cruel.hedge(fn, { count: 2, delay: 100 })
cruel.rateLimiter(fn, { requests: 100, interval: 60000 })
cruel.cache(fn, { ttl: 30000 })
cruel.abort(fn, { signal })composition
const wrapped = cruel.compose(fetcher, {
retry: { attempts: 3, delay: 1000, backoff: "exponential" },
circuitBreaker: { threshold: 5, timeout: 30000 },
timeoutMs: 5000,
cache: { ttl: 10000 },
timeout: 0.05,
corrupt: 0.02,
fail: 0.05,
delay: [100, 300],
})timeoutMs applies wrapper timeout in milliseconds. timeout remains chaos probability.
cruel.wrap(fetcher).fail(0.1)
cruel.wrap(fetcher).slow([100, 500])
cruel.wrap(fetcher).timeout(0.05)presets and scenarios
cruel.enable(cruel.presets.development)
cruel.enable(cruel.presets.nightmare)
cruel.enable(cruel.presets.apocalypse)
await cruel.play("networkPartition")
await cruel.play("highLatency")
await cruel.play("degraded")
await cruel.play("recovery")
cruel.stop()diagnostics
const off = cruel.on((event) => {
console.log(event.type, event.target)
})
const stats = cruel.stats()
cruel.resetStats()
off()fetch interception
cruel.patchFetch()
cruel.intercept("api.openai.com", {
rateLimit: { rate: 0.1, retryAfter: 60 },
delay: [100, 500],
slowBody: [100, 300],
headers: { "x-chaos": "true" },
truncate: 0.05,
malformed: 0.02,
})
cruel.unpatchFetch()
cruel.clearIntercepts()utilities
cruel.seed(12345)
cruel.coin(0.5)
cruel.pick([1, 2, 3])
cruel.between(10, 20)
cruel.maybe("value", 0.5)
await cruel.delay([100, 300])