General

General questions

What is slowdep?
slowdep is a zero-dependency Node.js library that wraps async functions with realistic lognormal latency. Instead of adding a flat setTimeout delay, it models how real production dependencies like Postgres, Redis, Stripe, and OpenAI actually behave — fast most of the time, with occasional long-tail spikes.
Why not just use setTimeout?
setTimeout(fn, 100) makes every call take exactly 100 ms. Real services never behave that way. A Postgres query might take 4 ms 95% of the time and 300 ms 1% of the time. That long tail is where bugs live — timeout misconfiguration, missing retries, cascade failures. A flat delay hides all of that. slowdep uses a lognormal distribution to produce the same kind of variance you see in production.
Does slowdep work in production?
It is designed for development and testing environments. Using it in production would add artificial latency to real service calls, which is not the goal. The recommended pattern is to wrap conditionally: NODE_ENV !== 'production' ? withLatency(fn, preset) : fn. See the Production usage page for the full pattern including a reusable helper.
Is it safe to accidentally leave slowdep active in production?
It won't corrupt data or crash your service, but it will add latency to every wrapped call, which would degrade production performance. The conditional-wrap pattern ensures the package has no effect in production — and if you use dynamic import, production bundles won't include it at all. Always gate on an environment variable or NODE_ENV.
Does slowdep have any runtime dependencies?
No. slowdep is zero-dependency. It ships only source code and TypeScript type definitions. Nothing in your node_modules tree changes when you install it beyond the package itself.
Installation

Installation questions

How do I install slowdep?
Run npm install slowdep. That's it. No peer dependencies, no build step, no native bindings.
Does slowdep support ESM and CommonJS?
Yes. The package ships both an ESM build and a CommonJS build. Node.js will automatically pick the right one based on your project's "type" field in package.json. You can use either import { withLatency } from 'slowdep' or const { withLatency } = require('slowdep').
Do I need to configure TypeScript?
No configuration needed. TypeScript types are bundled with the package. Import the types you need directly: import type { LatencyOptions, LatencyPreset } from 'slowdep'. The only requirement is TypeScript 4.5 or later for full generic inference support.
Usage

Usage questions

How do I use withLatencyAll to wrap an entire client?
withLatencyAll takes a client object and a profile, and returns a new object where every async method is wrapped. For example: const slowRedis = withLatencyAll(redisClient, 'redis'). Only methods that return a Promise are wrapped; synchronous methods are passed through unchanged. The wrapped client has the same shape and TypeScript types as the original.
How do I disable slowdep in production?
The simplest pattern is a ternary at wrap time: const wrappedQuery = process.env.NODE_ENV !== 'production' ? withLatency(query, 'postgres') : query. For a reusable approach, create a maybeSlowDep helper that reads an environment variable and returns the function unwrapped when it's set to a falsy value. See Production usage for full examples.
How do I get deterministic latency in tests?
slowdep's lognormal sampling uses Math.random(). Mocking it makes output deterministic: jest.spyOn(Math, 'random').mockReturnValue(0.5). Restore it after each test with jest.restoreAllMocks(). Alternatively, mock withLatency itself to return the original function, or use an environment variable bypass pattern. See Deterministic testing for all three approaches.
What happens when the error rate fires?
The wrapped function rejects with a plain Error object. The error message is "Simulated transient error". The original function is not called — the rejection happens before the underlying function runs. This means your error handlers and retry logic need to handle it just like they would a real transient failure.
Can I pass custom latency numbers instead of a preset string?
Yes. Pass an options object instead of a preset string: withLatency(fn, { p50: 20, p99: 400, errorRate: 0.002 }). The p50 and p99 values are in milliseconds. errorRate is a fraction from 0 to 1 and defaults to 0 if omitted. Use this when your production metrics differ from the built-in presets.
Testing

Testing questions

Does slowdep work with Jest and Vitest?
Yes. slowdep works with any test runner that supports async/await. With Jest or Vitest, use fake timers (jest.useFakeTimers() / vi.useFakeTimers()) together with Math.random mocking to control both the sampled delay and the clock. Advancing the clock with jest.runAllTimers() lets tests complete instantly without changing any application logic.
How do I make tests deterministic when using slowdep?
The fastest approach is to mock Math.random to return a fixed value before each test. Since the lognormal sampling calls Math.random twice (Box-Muller transform), mocking it to a constant makes the sampled delay identical on every run. A value of 0.5 produces latency close to p50. Restore the mock in afterEach so other tests are not affected.
Can I assert that a specific latency was applied?
You can, but it's usually the wrong test. Instead of asserting "this call took 50 ms", assert that your code behaved correctly under that latency — the timeout fired, the retry was attempted, the fallback was used. That's the behavior slowdep exists to surface. If you must assert timing, mock Math.random to a fixed value, record Date.now() before and after, and compare. The exact ms will be consistent given a fixed random seed.
Comparisons

Comparison questions

How does slowdep compare to setTimeout for testing?
setTimeout adds a flat, identical delay to every call. slowdep adds variable, lognormally-distributed delays — fast most of the time, occasionally slow, with a configurable error rate. The practical difference: setTimeout will never reveal a timeout bug that only fires at p99; slowdep will. For any code that interacts with external dependencies, slowdep gives more confidence than a flat delay.
How does slowdep compare to Toxiproxy?
Toxiproxy is a network-level proxy that sits between your app and a real dependency. It requires running a separate process and configuring network routes. slowdep is a JavaScript-level wrapper — no extra process, no network config, and it works with mocked or real clients equally. Toxiproxy is better for integration tests that talk to real services. slowdep is better for unit and component tests that use mocked clients.
How does slowdep compare to MSW (Mock Service Worker)?
MSW intercepts HTTP requests at the network level and lets you define mock responses. It's excellent for mocking REST and GraphQL APIs. slowdep operates at the function level and adds latency to any async function — not just HTTP calls. The two tools are complementary: use MSW to define what a mocked API returns, and wrap the fetch/axios call with slowdep to make it respond with realistic timing.