npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@simpill/patterns.utils

v1.0.0

Published

Composable design patterns: Result/Either, strategySelector, pipeAsync (Node and Edge).

Readme

Features: Type-safe · Node & Edge · Tree-shakeable


When to use: You want Result/Either-style error handling. This package uses many small pattern files (adapter, facade, proxy, result, strategy-selector, etc.). If the count becomes hard to maintain, we may consolidate into fewer, more cohesive modules. (ok/err, unwrapOr, fromThrowable), async pipelines (pipeAsync), key-based strategy dispatch (strategySelector), or composable patterns (Observer, State, Builder, etc.) without pulling in a large library.


Installation

From npm

npm install @simpill/patterns.utils

From GitHub

To use this package from the monorepo source:

git clone https://github.com/SkinnnyJay/simpill.git
cd simpill/utils/@simpill-patterns.utils
npm install && npm run build

In your project you can then install from the local path: npm install /path/to/simpill/utils/@simpill-patterns.utils or use npm link from the package directory.


Quick Start

import {
  ok,
  err,
  isOk,
  isErr,
  unwrapOr,
  fromThrowable,
  pipeAsync,
  strategySelector,
} from "@simpill/patterns.utils";

const r = ok(42);
if (isOk(r)) console.log(r.value);
const e = err(new Error("fail"));
const value = unwrapOr(e, 0);

const pipeline = pipeAsync(addOne, double);
const compute = strategySelector({ add: ([a, b]) => a + b, mul: ([a, b]) => a * b });

High-value vs type helpers

| Kind | Exports | What you get | |------|---------|----------------| | High-value (real behavior) | Result, pipeAsync, strategySelector, chainOfResponsibility, createStateMachine, createObservable, createBuilder, createFlyweightFactory, createMediator, createComposite, createMethodProxy, runCommandWithUndo, raceOk, toResult, fromPromise | Implementations that save boilerplate and enforce structure. | | Type / pattern helpers | createAdapter, adapt, createFacade, createFacadeFrom, createFactory, createCommand | Typed names for patterns (e.g. “this function is an Adapter”). No extra runtime behavior—use when you want consistent vocabulary and types. |

Use high-value APIs when you need the behavior. Use type helpers when you want to label a pattern in types and docs (e.g. dependency injection, testing mocks, or team conventions).


Features

| Feature | Description | |---------|-------------| | Result | ok, err, isOk, isErr, unwrapOr, fromThrowable, toResult, fromPromise | | pipeAsync | Async function composition | | strategySelector | Dispatch by key with optional defaultKey | | chainOfResponsibility | handled, unhandled, ChainHandler | | Command | createCommand, runCommand, runCommandWithUndo | | Adapter | createAdapter, adapt | | Builder | createBuilder | | Decorator | decorate | | Factory | createFactory | | Facade | createFacade, createFacadeFrom | | Flyweight | createFlyweightFactory | | Mediator | createMediator | | Observer | createObservable | | Composite | createComposite, traverseComposite, mapComposite, reduceComposite | | Proxy | createMethodProxy | | State | createStateMachine | | raceOk | First Ok wins from many Result promises |

When to use which pattern

| Need | Use | |------|-----| | Return success/failure without throwing | Result (ok/err, unwrapOr, fromThrowable, toResult, fromPromise) | | Async pipeline (f1 then f2 then f3) | pipeAsync | | Dispatch by string key with fallback | strategySelector / strategySelectorOptional | | Handlers in sequence until one handles | chainOfResponsibility | | Typed “conversion function” (e.g. for DI) | Adapter (createAdapter, adapt) | | Stepwise construction with validation | Builder | | Single shared instance per key | Flyweight (createFlyweightFactory) | | Notify many listeners | Observer (createObservable) | | Typed event bus (event → payload) | Mediator (createMediator) | | State-dependent behavior | State (createStateMachine) | | Tree of nodes (e.g. UI, AST) | Composite | | Intercept method calls (logging, metrics) | Proxy (createMethodProxy) | | Execute + optional undo | Command (runCommandWithUndo) | | First success among many async Results | raceOk |

This package is minimal compared to fp-ts (no full type-class hierarchy) or neverthrow (no ResultAsync); use it when you want Result/strategy/pipe and small patterns without extra dependencies.

What we don't provide

  • ResultAsync / neverthrow-style — No lazy Result-from-Promise wrapper; use fromPromise or toResult with async functions and pipeAsync for pipelines.
  • fp-ts Option / Task / Either — No Option type or Task; Result covers success/failure. For full type-class style use fp-ts.
  • Full type-class hierarchy — No Functor, Monad, or Traversable; just Result, pipeAsync, and pattern helpers.

Documentation and use cases

Result (ok, err, isOk, isErr, unwrapOr, fromThrowable, toResult, fromPromise)

What it is: A discriminated union { ok: true, value: T } | { ok: false, error: E } so you can return success or failure without throwing.

Use cases:

  • API / service layer: Return Result<User, AppError> instead of throwing; callers handle both branches.
  • Parsing / validation: fromThrowable(() => JSON.parse(str))Result<unknown, unknown>; then map error or use unwrapOr.
  • Async I/O: toResult(fetch(url)) or fromPromise(() => fetch(url))Promise<Result<T, AppError>> with normalized errors.
  • Default values: unwrapOr(result, defaultValue) when you don’t care about the error.

Examples:

// Sync: wrap throwing code
const parsed = fromThrowable(() => JSON.parse(input));
if (isErr(parsed)) return fallback;
console.log(parsed.value);

// Async: turn a promise into Result<T, AppError>
const res = await fromPromise(() => fetchUser(id));
if (isOk(res)) setUser(res.value);
else showError(res.error);

// Fallback
const value = unwrapOr(parseConfig(), defaultConfig);

pipeAsync

What it is: Composes async functions so the output of one is the input of the next. Short-circuits on first rejection.

Use cases:

  • Request pipelines: validate → enrich → persist (each step async).
  • ETL / data: fetch → parse → transform → write.
  • Middleware-like flows: auth → load → render.

Examples:

const loadUser = pipeAsync(
  (id: string) => api.getUser(id),
  (user) => enrichWithPermissions(user),
  (user) => cache.set(user.id, user)
);
await loadUser("u1");

strategySelector / strategySelectorOptional

What it is: Dispatch by a string key to a function; optional defaultKey or return undefined for unknown keys.

Use cases:

  • API version/action routing: strategySelector({ v1: handleV1, v2: handleV2 }, { defaultKey: "v2" }).
  • Calculators / ops: strategySelector({ add, mul, sub }) with (op, [a, b]).
  • File type handlers: key = extension, strategy = parser.
  • Feature flags / A/B: key = variant, strategy = handler (optional fallback with defaultKey).

Examples:

type Format = "json" | "xml" | "csv";
const serialize = strategySelector<Format, Data, string>({
  json: (d) => JSON.stringify(d),
  xml: (d) => toXML(d),
  csv: (d) => toCSV(d),
}, { defaultKey: "json" });
serialize("json", data);
serialize("unknown" as Format, data); // uses "json"

chainOfResponsibility (handled, unhandled, ChainHandler)

What it is: Run a list of handlers in order; the first that returns handled(value) wins; otherwise optional fallback or throw.

Use cases:

  • Request handling: try auth A, then auth B, then anonymous.
  • Validation: first handler that understands the input (e.g. by content-type) validates it.
  • Pricing / rules: first rule that applies returns a price.
  • Logging / pipelines: first handler that can process the event does so.

Examples:

const handle = chainOfResponsibility<Request, Response>([
  (req) => (req.headers.get("x-api-key") ? handled(handleWithKey(req)) : unhandled()),
  (req) => (req.headers.get("cookie") ? handled(handleWithSession(req)) : unhandled()),
], { fallback: (req) => respond401(req) });

createStateMachine

What it is: A finite state machine: states, events, transition map (literal next state or function), optional context and onTransition hook.

Use cases:

  • UI flows: idle → loading → success | error; buttons enable/disable by can(event).
  • Workflow / saga: draft → submitted → approved | rejected.
  • Connections: disconnected → connecting → connected → reconnecting.
  • Game / app lifecycle: boot → ready → running → paused.

Examples:

type State = "idle" | "loading" | "done" | "error";
type Event = "FETCH" | "RESOLVE" | "REJECT" | "RETRY";
const fsm = createStateMachine<State, Event, { userId: string }>(
  "idle",
  {
    idle:    { FETCH: "loading" },
    loading: { RESOLVE: "done", REJECT: "error" },
    done:    {},
    error:   { RETRY: "loading" },
  },
  { onTransition: (next, prev, event) => analytics.track(prev, event, next) }
);
fsm.transition("FETCH", { userId: "u1" });
console.log(fsm.getState()); // "loading"
console.log(fsm.can("RESOLVE")); // true

createObservable (Observer)

What it is: Simple pub/sub: subscribe(observer), next(event), clear(), getObserverCount().

Use cases:

  • Component events: one source, many UI subscribers.
  • Log streams / metrics: one emitter, many sinks.
  • Decoupled modules: producer calls next; consumers subscribe.
  • Testing: subscribe, trigger, assert on events.

Examples:

const events = createObservable<{ type: string; payload: unknown }>();
const unsub = events.subscribe((e) => console.log(e));
events.next({ type: "click", payload: { id: "btn1" } });
unsub();

createBuilder

What it is: Chainable set(key, value) and merge(partial) with immutable-style updates; build() returns a copy of the current object.

Use cases:

  • Config objects: optional steps (set timeout, set retries, set headers) then build.
  • Test fixtures: set only the fields you need, merge defaults.
  • DTOs / requests: stepwise construction with a final validated build (e.g. call a validator in tests before build).

Examples:

const config = createBuilder({ host: "localhost", port: 3000 })
  .set("port", 4000)
  .merge({ timeout: 5000 })
  .build();

createFlyweightFactory

What it is: Cache instances by key; same key returns the same instance. get(key), clear(), size().

Use cases:

  • Shared instances per ID: one “connection” or “loader” per resource ID.
  • Parsers / formatters per locale or options: key = locale, value = formatter instance.
  • Expensive objects: reuse by a canonical key (e.g. normalized config string).

Examples:

const formatters = createFlyweightFactory(
  (locale: string) => locale,
  (locale) => new Intl.NumberFormat(locale)
);
formatters.get("en-US") === formatters.get("en-US"); // true

createMediator

What it is: Typed event hub: on(event, handler), off, emit(event, payload), clear, listenerCount(event).

Use cases:

  • App-level events: emit("userLoggedIn", user); nav, analytics, and cache all subscribe.
  • Decoupled features: component emits; other modules react without direct imports.
  • Testing: subscribe to events and assert payloads.

Examples:

type AppEvents = { userLoggedIn: User; orderPlaced: Order };
const bus = createMediator<AppEvents>();
bus.on("userLoggedIn", (user) => analytics.identify(user.id));
bus.emit("userLoggedIn", user);

createComposite, traverseComposite, mapComposite, reduceComposite

What it is: Tree of nodes with value and children; helpers to traverse, map, and reduce.

Use cases:

  • UI trees: sections with nested sections or components.
  • AST / config: hierarchical structure; map or aggregate over nodes.
  • Categories / taxonomies: parent/child; count or collect by predicate.

Examples:

const root = createComposite("root", [
  createComposite("a", [createComposite("a1")]),
  createComposite("b"),
]);
traverseComposite(root, (n) => console.log(n.value));
const sum = reduceComposite(root, (acc, n) => acc + (typeof n.value === "number" ? n.value : 0), 0);

createMethodProxy (Proxy)

What it is: Wraps an object’s methods with before, after, and error hooks (logging, metrics, error handling).

Use cases:

  • Logging: log method name and args before, result after.
  • Metrics: time methods or count calls.
  • Error handling: normalize errors or retry in error hook.

Examples:

const api = createMethodProxy(realApi, {
  before: (method, args) => console.log(method, args),
  after: (method, _args, result) => console.log(method, "->", result),
  error: (method, args, err) => logger.error({ method, args, err }),
});

Command (createCommand, runCommand, runCommandWithUndo)

What it is: Typed “command” object with execute and optional undo. runCommandWithUndo runs and returns { result, undo } so you can call undo() later.

Use cases:

  • Undo/redo: store undo from runCommandWithUndo in a stack.
  • Transactional steps: execute several commands; on failure, run undos in reverse.
  • Audit: record command + input + result for replay or audit log.

Examples:

const setVolume = createCommand({
  execute: (v: number) => { const prev = volume; volume = v; return prev; },
  undo: (_input, prev) => { volume = prev; },
});
const { result: previousVolume, undo } = runCommandWithUndo(setVolume, 80);
// later
undo();

raceOk

What it is: Takes an array of promises that resolve to Result<T, E>; resolves with the first Ok, or with Err(mapped) if all fail.

Use cases:

  • Redundancy: try multiple endpoints or caches; use first success.
  • Fallback sources: primary + backup; first Ok wins.
  • Timeouts / retries: combine with fromPromise and short timeouts.

Examples:

const res = await raceOk([
  fetchUserFromPrimary(id),
  fetchUserFromReplica(id),
]);
if (isOk(res)) setUser(res.value);
else showError(res.error);

Adapter (createAdapter, adapt) — type helper

What it is: A typed name for “function from A to B”. createAdapter(fn) returns fn; adapt(source, adapter) returns adapter(source).

Use cases: Document that a function “adapts” one interface to another; use in DI or tests (e.g. “this service takes an Adapter<ExternalUser, OurUser>”).


Facade (createFacade, createFacadeFrom) — type helper

What it is: A typed name for “simplified surface”. createFacade(obj) returns obj; createFacadeFrom(deps, factory) returns factory(deps).

Use cases: Document that an object is a “facade” over a complex subsystem; createFacadeFrom is useful when building that facade from dependencies (e.g. in composition root).


Factory (createFactory) — type helper

What it is: A typed name for “function that creates T”. createFactory(fn) returns fn.

Use cases: Type factories in DI or tests (e.g. Factory<Logger, [Config]>).


Decorator (decorate)

What it is: Composes one base function with several “decorator” functions (each wraps the previous). Applied left to right.

Use cases: Add logging, timing, or validation around a core function without changing its signature.

Examples:

const withLog = (fn: typeof add) => (...args: Parameters<typeof add>) => {
  const result = fn(...args);
  console.log("result", result);
  return result;
};
const addLogged = decorate(add, withLog);

Import Paths

import { ... } from "@simpill/patterns.utils";         // Everything
import { ... } from "@simpill/patterns.utils/client";  // Client
import { ... } from "@simpill/patterns.utils/server";  // Server
import { ... } from "@simpill/patterns.utils/shared"; // Shared only

API Reference

  • Result<T, E>, ok, err, isOk, isErr, unwrapOr, fromThrowable, toResult, fromPromise
  • pipeAsync(...fns) → composed async function
  • strategySelector(strategies, options?) → (key, input) => O
  • strategySelectorOptional — returns undefined for unknown key
  • chainOfResponsibility, handled, unhandled, ChainHandler, ChainResult, ChainOptions
  • createCommand, runCommand, runCommandWithUndo, Command, CommandExecution
  • createAdapter, adapt, Adapter
  • createBuilder, Builder
  • decorate, Decorator
  • createFacade, createFacadeFrom, Facade
  • createFactory, Factory
  • createFlyweightFactory, FlyweightFactory
  • createMediator, Mediator, MediatorHandler
  • createObservable, Observable, Observer, Unsubscribe
  • createComposite, CompositeNode, addChild, removeChild, traverseComposite, mapComposite, reduceComposite
  • createMethodProxy, MethodProxyHooks
  • createStateMachine, StateMachine, StateTransitions, StateTransition, StateMachineOptions
  • raceOk, RaceOkOptions

Result combinators

This package does not provide map, flatMap, mapError, or andThen on Result. Use isOk / isErr and branch, or unwrapOr(r, default). To transform: isOk(r) ? ok(f(r.value)) : r; to chain async: use fromPromise / toResult in sequence and check isOk between steps. For a full combinator set use fp-ts or neverthrow.

Async Result

toResult(promise, mapError?) and fromPromise(fn, mapError?) turn a Promise into Promise<Result<T, AppError>> (default mapError wraps to AppError). Use them for async I/O; combine with raceOk for “first success.” For retry that returns Result use @simpill/resilience.utils retryResult.

All exports

The API Reference above lists every public export. From shared: Result (ok, err, isOk, isErr, unwrapOr, fromThrowable, toResult, fromPromise), pipeAsync, strategySelector, strategySelectorOptional, chainOfResponsibility (handled, unhandled), Command, Adapter, Builder, Decorator, Facade, Factory, Flyweight, Mediator, Observable, Proxy, StateMachine, Composite helpers, raceOk. Client/server re-export the same; see package index and shared/index for the full list.

Unwrap (value or throw)

There is no unwrap(r) that returns value or throws. Use unwrapOr(r, fallback) for a default, or if (isOk(r)) return r.value; throw r.error (or return a typed error response). This avoids hidden throws and keeps handling explicit.

pipeAsync and cancellation

pipeAsync composes async functions with .then(); it does not accept AbortSignal. To cancel, pass an AbortSignal into your pipeline (e.g. as first argument) and have each step check signal.aborted or pass it to fetch. When a step rejects, the pipeline short-circuits and the returned promise rejects; there is no built-in “cancel pipeline” API.

Result and AppError

toResult and fromPromise use @simpill/errors.utils AppError by default (via mapError). Use Result<T, AppError> so errors have code, cause, and meta. Example: const res = await fromPromise(() => fetchUser(id)); if (isErr(res)) logger.error({ code: res.error.code, cause: res.error.cause }); else setUser(res.value);. For custom codes pass mapError: fromPromise(fn, (e) => new AppError("Fetch failed", { code: ERROR_CODES.NOT_FOUND, cause: e })).

Chaining Results

There is no flatMap/chain helper. Chain by sequencing fromPromise and branching:

const a = await fromPromise(() => loadUser(id));
if (isErr(a)) return a;
const b = await fromPromise(() => loadOrders(a.value.id));
if (isErr(b)) return b;
return ok({ user: a.value, orders: b.value });

Or use pipeAsync with steps that return Result and a final unwrapOr or single isOk check at the end.


Examples

npx ts-node examples/01-basic-usage.ts

| Example | Description | |---------|-------------| | 01-basic-usage.ts | Result, pipeAsync, strategySelector | | 02-core-patterns.ts | Adapter, Decorator, Observer, State, Mediator | | 03-structure-proxy.ts | Composite, Proxy | | 04-creational.ts | Builder, Facade, Flyweight | | 05-result-chain-command.ts | toResult, fromPromise, raceOk, chainOfResponsibility, runCommandWithUndo |


Development

npm install
npm test
npm run build
npm run verify

Documentation


License

ISC