@hile/reloader
Stabilize config-driven runtime reloads with debounce, singleflight, latest-wins, safe disposal, and multi-source config aggregation.Choose This Package When
Use When
Use@hile/reloader when runtime config can change frequently and service code needs debounce, singleflight reload, latest-wins behavior, and safe old-runtime preservation.
Do Not Use When
- Do not use it as a durable queue.
- Do not use it to restart an HTTP server on the same port; keep the listener stable and reload handlers or runtime state.
- Do not pass
Map,Set, class instances, functions, symbols, circular objects, or sparse arrays to the default comparator; usesignatureornormalize. - Do not rely on timeouts to kill non-cooperative work; timeouts reject the reload path and abort the signal, and late-created runtimes are disposed best-effort.
Install
Imports
Copy-Paste Example
Stable HTTP shell with reloadable runtime:More Examples
Aggregate multiple micro config topics before reloading:Runtime And Lifecycle Notes
RuntimeReloader.update(input)schedules reload work and settles when the scheduler becomes idle.RuntimeReloader.onStateChange(state)fires when the observable state changes, includingstatus,hasCurrent, andhasPendingtransitions.- During an active reload, a newer update flips
hasPendingtotrue; when that pending input is consumed,hasPendingflips back tofalse. - If that input starts
create()andcreate()fails, thatupdate()rejects, the old runtime remains current, and the failed input is not marked applied. - If an update is superseded before its input starts
create(), it settles with the newer pending input because latest-wins coalesced it into that reload. - Only one reload runs at a time.
- Updates during an active reload collapse to the latest pending input.
- Debounce timers reset while updates arrive before reload starts.
create()must succeed beforecurrent()switches.- After a successful switch,
dispose(oldRuntime)runs; dispose failures are reported but do not roll back the new runtime. stop()rejects future updates, aborts an in-flightcreate()throughAbortSignal, clears queued input, and disposes the current runtime.- If
create()resolves after timeout or abort, that late runtime is not made current and is disposed best-effort. - Default compare priority is
equals, thensignature, thennormalize, then an internal stable signature. - The internal stable signature is not exported and only supports JSON-like plain config values plus
Dateandbigint. - Default timeouts are 30 seconds for create and 10 seconds for dispose. Override with
createTimeoutMsanddisposeTimeoutMs. ConfigAggregator.requiredkeys must be explicitly received;defaultsdo not satisfy required keys.undefinedis an explicit received value. Useunset(key)to remove a value and mark the key as not received.ConfigAggregatorserializes emits. If values change while listeners are running, the next emit uses the latest snapshot after the current emit finishes.ConfigAggregatorrecords the last emitted signature only after all listeners finish successfully.ConfigAggregator.flush()rejects when a listener fails. Timer-driven emits callonErrorand leave the signature uncommitted so the same config can retry later.
Anti-Patterns
- Recreating an HTTP server on the same port for every config change.
- Letting multiple subscribe callbacks each reload the same runtime independently.
- Comparing whole runtime objects instead of normalized config.
- Forgetting to call
stop()during application shutdown.
Verification Checklist
- Burst config updates produce at most one reload per debounce window.
- A reload failure leaves the previous runtime serving traffic.
- Multiple required config keys are present before the first reload.
- HTTP handlers read
reloader.current()instead of rebinding the port. onErrorandonCompareErrorare connected to logs or metrics.
Related Recipes
Stable Runtime Reload
Complete Example
Runtime Dynamic Config
Complete Example
Package-Local AI Guide
This package also shipsAI.md in npm so agents can read accurate examples after installation.