Skip to content
mycl is in early development. The API may change before a stable release; it is not yet recommended for production use.

Augmentation

.augment(cap, wrapper) wraps whatever a capability resolves to: the layered value if a .layer() bound one, otherwise the base. Unlike .layer(), an augment does not replace anything: it composes around the resolved implementation, which still runs inside it unless the wrapper deliberately skips it. Reach for augmentation to log, time, transform a result, or recover from a failure without touching the implementation or its call sites.

A wrapper receives next (the callable it wraps) and returns a new callable with the same signature. Call next(...args) to run the wrapped implementation; do anything you like before, after, or instead.

const wrapper = (next) => (...args) => {
// before...
const result = next(...args);
// after...
return result;
};

You rarely write that by hand. Four helpers cover the common shapes. Import them from @mycl/core/helpers:

  • before(fn) runs fn(...args) before each call; its return value is ignored. Good for logging inputs, metrics, assertions.
  • after(fn) runs fn(result, ...args) after each call; the result is passed through unchanged.
  • pipe(...transforms) chains transforms left-to-right over the return value; each transform receives the previous one’s output and returns the next.
  • handleError(handler, shouldHandle?, { rethrow }?) catches a throw (and an async rejection); handler(error, ...args) returns the fallback value. Optional shouldHandle(error) filters which errors are caught: the rest re-throw. { rethrow: true } runs the handler for side effects only and re-throws the original error.

Augments nest like an onion. The first augment added sits closest to the implementation (innermost); each later augment wraps the previous ones. So the last augment added is outermost: its logic runs first on the way in and last on the way out.

outer was added last, so it wraps inner and runs first. If a .layer() binding is present too, the augments wrap the layered value, not the base default.

Async: after and pipe see the Promise, not the value

Section titled “Async: after and pipe see the Promise, not the value”

Augmentation is the tool for a cautious rollout. Layer a new implementation and augment it with handleError to fall back to the stable one on failure. If the new code misbehaves, the old path runs automatically: no redeploy to roll back. The fallback lives in the registry, not the implementation: drop the .layer() binding and the safety net is gone.

pay(50) runs the canary. pay(200) throws inside the canary; handleError catches it (it is an Error, so shouldHandle passes) and returns stable(200). The fallback calls stable directly rather than re-dispatching charge, which would loop straight back into the canary.