Skip to content

Dependency graphs, just typed functions.

v3 · TypeScript-first DI

Explicit dependencies. Replaceable boundaries. No decorators, reflection, or container ceremony.

npm install @favy/di
greeting.ts
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 }) => ({
    message: () => 'Hello at ' + Clock.now().toISOString(),
  }),
);

Greeting({ Clock }).message();

Clock is resolved when Greeting uses it. Types verify the connection before the graph runs.

  • 0 decorators
  • Typed end to end
  • Lazy by default
  • Replaceable boundaries

The graph is the configuration.

Each edge is an ordinary TypeScript contract. Follow it in the code, replace it in a test, or preconfigure it for the next caller.

  1. 01

    Define

    A module is a named function with a typed input and output.

  2. 02

    Compose

    ClockLive names Clock's output for reuse as a typed dependency.

  3. 03

    Run

    Supply implementations at the boundary and call the resulting graph.

Only the machinery your graph needs.

Start with typed functions. Add lifetime and transformation rules at the boundary where they matter.

Graph

Dependencies stay explicit

Read the contract at the function boundary. TypeScript verifies every connection.

Lifetime

Cache on your terms

Choose run, module, or no caching without changing the consumer.

Resolution

Initialize only when used

Dependencies resolve lazily by default, including deep inside the graph.

Composition

Preconfigure a boundary

Provide part of a graph once, then expose only what the caller should supply.

Testing

Replace values, not machinery

Pass a small fake at the same typed boundary—there is no container to reset.

Advanced

Shape the edges

Transform input and output while preserving inference through the graph.

Go straight to the work at hand.

The core stays small. Pick a path when you need the next layer of control.

Start with one module. Grow when the graph does.