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鈥檝e always been into building performant and accessible sites, but lately I鈥檝e been taking it extremely seriously. So much so that I鈥檝e been building a tool to help me optimize and monitor the sites that I build to make sure that I鈥檓 making an attempt to offer the best experience to those who visit them. If you鈥檙e into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 馃憢, I鈥檓 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鈥檙e interested in other things I鈥檓 working on, follow me on Twitter or check out the open source projects I鈥檝e 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鈥搃sh.

Open Software & Tools

This site wouldn鈥檛 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 馃檹

漏 2024 鈥撀燩kg Stats / Ryan Hefner

@coderspirit/lambda-ioc

v2.2.1

Published

Super type safe dependency injection 馃拤 for TypeScript

Downloads

663

Readme

@coderspirit/lambda-ioc

NPM version TypeScript License npm downloads Known Vulnerabilities Security Score

Super type safe dependency injection 馃拤 for TypeScript

Install instructions

Node

# With PNPM
pnpm add @coderspirit/lambda-ioc

# With NPM
npm install @coderspirit/lambda-ioc

# With Yarn:
yarn add @coderspirit/lambda-ioc

Example

import { createContainer } from '@coderspirit/lambda-ioc'
import { pino } from 'pino'

import { getDb } from './infra/db.ts'
import {
  buildService,
  buildServiceA,
  buildServiceB
} from './services.ts'
import { buildServer } from './server.ts'

const container = createContainer()
  // We can register already instantiated values
  .registerValue('logger', pino())
  // We can register factories as well
  .registerFactory('logger2', pino)
  // Factories don't guarantee returning the same instance every time,
  // which can be necessary sometimes, so we provide a solution:
  .registerSingleton('logger3', pino)
  .registerSingleton('db', getDb)
  // We can also pass dependencies to factories and singleton factories.
  // What follows is "equivalent" to:
  // .registerValue('service', buildService(pino(), getDb()))
  .registerSingleton('service', buildService, 'logger', 'db')
  // The type checker will raise an error if we try to pass dependencies
  // that we didn't specify before, protecting us from having errors at
  // runtime:
  .registerSingleton('brokenService', buildService, 'logger', 'db2')
  // We might want to register some things within "groups". This is done
  // by specifying a prefix. We'll see later how this can be useful:
  .registerSingleton('svc:a', buildServiceA, 'logger', 'db')
  .registerSingleton('svc:b', buildServiceB, 'logger', 'db')
  // Sometimes our factories are asynchronous
  .registerAsyncFactory('asyncStuff', async () => Promise.resolve(42))
  .registerAsyncSingleton(
    'aSingleton',
    async () => Promise.resolve({ v: 42 })
  )
  // We can inject groups into other registered dependencies by using
  // the `:*` suffix
  .registerAsync('server', buildServer, 'svc:*')
  // The next call is not strictly necessary, but it helps to "clean up"
  // the container's type for faster type checking.
  // Although we don't do it in this example, we can parametrize the
  // type parameters of `close` if we want to expose less dependencies
  // than the ones we registered.
  .close()

// Once we have the container, we can start resolving its registered
// values in a type-safe way:

// The type checker will know that `logger` is an instance of `Logger`
const logger = container.resolve('logger')

// The type checker will raise an error because it knows we didn't
// register anything under the key 'wrong'.
const wrong = container.resolve('wrong')

// To resolve what we registered asynchronously, we have to use the
// asynchronous resolver
const asyncStuff = await container.resolveAsync('asyncStuff')

// We can't resolve synchronously what was registered asynchronously,
// what follows will raise a type checking error:
const asyncAsSync = container.resolve('asyncStuff')

// To resolve "groups", we also have to do it asynchronously, even if
// they were registered synchronously. This is because groups can have
// synchronous and asynchronous dependencies.
// `svcGroup` will be an array (with arbitrary order) containing the
// dependencies registered under the 'svc:` prefix.
const svcGroup = await container.resolveGroup('svc')

// Having a specific method to resolve groups is fine, but it does not
// fit well in dependency resolution pipelines. For this reason, we also
// provide a way to asynchronously resolve groups by relying on the `:*`
// suffix, so we can pass whole groups as dependencies.
const svcGroup = await container.resolveAsync('svc:*')

// In case we wanted to keep the labels of our resolved group
// dependencies, we can rely on the `:#` suffix. This will return us a
// list where its values are [label, dependency] pairs.
const svcLabelledGroup = await container.resolveAsync('svc:#')

// We can also resolve the labels in a group without having to resolve
// their associated dependencies (using the `:@` suffix).
const svcLabels = container.resolve('svc:@')

Other considerations

While this library is intended to provide compile-time safety, it also provides runtime safety to ensure that there are no surprises when it's used in pure JS projects.

Benefits

  • 100% type safe:
    • The type checker will complain if we try to resolve unregistered dependencies.
    • The type checker will complain if we try to register new dependencies that depend on unregistered dependencies, or if there is any kind of type mismatch.
  • Purely functional
  • Immutable
  • Circular dependencies are impossible

Drawbacks

  • All dependencies must be declared "in order".
    • This implies that this IoC container cannot be used in combination with some auto-wiring solutions, such as IoC decorators.
  • The involved types are a bit convoluted:
    • They might cause the type checker to be slow.
    • In some situations, the type checker might be unable to infer the involved types due to excessive "nested types" depth.