Signature
TypeScript signature
function withLatency< T extends (...args: any[]) => Promise<any> >( fn: T, profile: LatencyPreset | LatencyOptions ): T
The wrapped function has exactly the same type as the original. All parameter types, return type, and overloads are preserved.
Parameters
Function parameters
| param | type | default | description |
|---|---|---|---|
| fn | (...args: any[]) => Promise<any> | — | The async function to wrap. Must return a Promise. The wrapped function will have the same signature. |
| profile | LatencyPreset | LatencyOptions | — | Either a preset string name ('postgres', 'redis', etc.) or a custom LatencyOptions object with explicit p50/p99 values. |
Options
LatencyOptions
When passing a custom options object instead of a preset string, these fields are available:
| field | type | default | description |
|---|---|---|---|
| p50 | number | — | Median latency in milliseconds. Half of sampled delays will be below this value. Required. |
| p99 | number | — | 99th-percentile latency in milliseconds. Used alongside p50 to fit the lognormal distribution. Must be greater than p50. Required. |
| errorRate | number | 0 | Fraction of calls that reject with a transient error before the underlying function is called. Range: 0 to 1. A value of 0.01 means 1% of calls fail. Optional. |
Return value
What withLatency returns
withLatency returns a new function with the same TypeScript type as fn. Calling the returned function:
- Samples a delay from the lognormal distribution parameterized by the profile.
- If
errorRatefires, immediately rejects withnew Error('Simulated transient error'). - Otherwise, waits the sampled delay, then calls
fnwith the original arguments. - Returns
fn's resolved value, or rethrows any error thrown byfn.
The returned function is a plain async function — not a Proxy. It can be called, passed around, bound to objects, and destructured exactly like a regular function.
Examples
Code examples
import { withLatency } from 'slowdep' async function getUser(id: number) { return db.query('SELECT * FROM users WHERE id = $1', [id]) } // Wrap with Postgres preset (p50: 5ms, p99: 200ms, errorRate: 0.001) const slowGetUser = withLatency(getUser, 'postgres') // Call exactly like the original const user = await slowGetUser(42) console.log(user.name)
import { withLatency } from 'slowdep' // Custom profile matching your actual production metrics const slowFetch = withLatency(fetchProduct, { p50: 45, // your measured median p99: 600, // your measured 99th percentile }) const product = await slowFetch('sku-123')
import { withLatency } from 'slowdep' // 2% of calls will reject — test your retry logic const slowCharge = withLatency(createCharge, { p50: 200, p99: 2000, errorRate: 0.02 }) try { const charge = await slowCharge({ amount: 1000 }) fulfill(charge) } catch (err) { // err.message === 'Simulated transient error' await scheduleRetry() }
import { withLatency } from 'slowdep' import type { LatencyOptions, LatencyPreset } from 'slowdep' // Type-annotated options object const opts: LatencyOptions = { p50: 5, p99: 200, errorRate: 0.001 } // Explicit type parameter when inference needs help const slowGetUser = withLatency<typeof getUser>(getUser, opts) // Preset as typed string literal const preset: LatencyPreset = 'redis' const slowGet = withLatency(cacheGet, preset) // Return type is preserved — same as original function const result: Awaited<ReturnType<typeof getUser>> = await slowGetUser(1)
Behavior
Detailed call behavior
On each invocation of the wrapped function, slowdep follows this sequence:
1
Sample latency
Two uniform random numbers are generated via
Math.random(). The Box-Muller transform converts them to a standard normal sample, which is then scaled by the lognormal parameters (mu = ln(p50), sigma derived from p99) and exponentiated. The result is capped at p99 × 3.2
Check error rate
A third random number is generated and compared to
errorRate. If it falls below errorRate, the call rejects immediately with new Error('Simulated transient error'). The original function is not called.3
Wait the sampled delay
A
setTimeout for the sampled duration is awaited. This is the artificial latency added to the call.4
Call the original function
The original
fn is called with all the arguments passed to the wrapper. Its resolved value is returned, or any error it throws is rethrown transparently.