Skip to content

Repository files navigation

@favy/di

codecov npm version npm bundle size GitHub

Dependency graphs, just typed functions.

@favy/di v3 turns ordinary TypeScript functions into named, composable dependency providers. Dependency objects stay explicit, results stay inferred, and concrete implementations are selected at the composition root—without decorators or container configuration.

Features

  • Build named modules from ordinary functions
  • Carry transitive requirements through Live<T>
  • Replace dependencies at the application boundary
  • Bind known values incrementally with .provide()
  • Resolve lazily and cache within each run by default
  • Customize input and output types when a graph needs them

Installation

npm install @favy/di

Requires TypeScript 5+.

Quick Start

import { Module, type Live } from '@favy/di';

const Clock = Module()('Clock', () => ({
  now: () => new Date(),
}));
type ClockLive = Live<typeof Clock>;

const Greeting = Module<ClockLive>()('Greeting', ({ Clock }) => {
  const hour = Clock.now().getUTCHours();
  return hour < 12 ? 'Good morning!' : 'Good evening!';
});

Mental model

  • Module creates a named callable provider from a typed dependency object and an ordinary function.
  • Live<T> carries a module's transitive requirements plus its result under the module's declared name.
  • The composition root is the top-level call where the application assembles concrete values and providers.

Replace at the boundary

import { Module, type Live } from '@favy/di';

const Clock = Module()('Clock', () => ({
  now: () => new Date(),
}));
type ClockLive = Live<typeof Clock>;

const Greeting = Module<ClockLive>()('Greeting', ({ Clock }) => {
  const hour = Clock.now().getUTCHours();
  return hour < 12 ? 'Good morning!' : 'Good evening!';
});

console.log(
  Greeting({
    Clock: { now: () => new Date('2025-01-01T09:00:00.000Z') },
  }),
); // "Good morning!"

The replacement is a plain value with the same contract as the value produced by Clock; no container mutation or special test API is required.

Partial application

Use .provide() to bind part of a dependency object and get back a module that asks only for the remaining fields.

import { Module } from '@favy/di';

const Add = Module<{ left: number; right: number }>()(
  'Add',
  ({ left, right }) => left + right,
);

const AddTen = Add.provide({ right: 10 });
console.log(AddTen({ left: 5 })); // 15

Default lifecycle

Default Behavior
lazy: true A supplied provider runs only when its key is first read.
cache: 'run' Its resolved value is reused for the current top-level call; if its key is read in a later run, the provider resolves again.

Documentation

Contributing

See the documentation contributor guide for local commands. Bug reports and pull requests are welcome in the GitHub repository.

License

@favy/di is distributed under the MIT license. See the LICENSE file.

Releases

Packages

Used by

Contributors

Languages