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 🙏

© 2024 – Pkg Stats / Ryan Hefner

di1

v0.4.3

Published

Simple but yet powerful, DI container for JavaScript or TypeScript

Downloads

24

Readme

di1 Dependency Injection Container

Build Status codecov.io

Dependency Injection Container for JavaScript, with following goals:

  • minimalistic, and relatively simple to use
  • possibility to create separate Container instance for specific context (user session for example)
  • possibility to inject Container instance - useful when dealing with circular dependencies
  • good TypeScript support, allowing inferring for most of the things

Installation

npm install di1

API

Service tokens

Services inside DI container, can be referenced using tokens.

Token for registering service or specifying dependency, can be created using createToken function:

function createToken<ServiceType>(name?: string): Token;

name argument here, is to be used for debug messages in cases like when having error about cyclic dependency.

There is also a special kind of which allows accessing DI container itself from a service. Such a token can be created using containerToken function:

function containerToken(...deps: Injectable[]): Token

Which as an argument optionally has a list of dependencies which should be available for the container, this affects in which specific container in the hierarchy service using it is to be created.

This is useful for more dynamic cases when requesting dependencies known only at runtime time.

Also, this makes it possible to have cyclic dependencies, in limited cases(which btw, better to be avoided whenever possible).

Service declaration

Typically service should be defined using declareService funtion:

function declareService(depsObject, factory): Declaration;

which creates a service declaration using object specifying dependencies(list of service tokens or other declarations), and a factory function to be called with object of dependency instances(having same shape as depsObject)

There is also a bit more low level version of this:

function declareServiceRaw(factory, ...deps): Declaration

the only difference is that dependencies would be injected as separate function arguments into factory function. It is slightly closer to how things work internally, but most of the difference is in how it looks.

Supposedly declareService should be more convenient in most of the cases, and suggested as preferred option.

DI Container

class Container - Dependency injection container. Represents a registry of service declarations, and cache of already created instances.

Registering a service

To register declaration for given token, or to replace/override previously declared service register method can be used:

container.register(tokenOrDeclaration, declaration)

Because it is allowed to have service declaration as a dependency - it might be useful to register it to be created on specific layer in the container hierarchy. (by default it would be created in upper possible layer having all the dependencies)

For this case it is possible to register the declaration in the specific container (effectively limiting it to be created in it or its decedents)

container.register(declaration)

To create a service instance or to use previously created one - get method is to be used:

container.get(tokenOrDeclaration)

There are cases when there is a need for separate context for services to be created, while allowing to reuse some service instances from existing context, this can be done createChild method:

container.createChild()

Usage example

import {
  declareService,
  declareServiceRaw,
  Container,
  createToken,
  containerToken,
} from 'di1';

// declare service without dependencies
const svc1 = declareServiceRaw(() => 1);

// declare service with dependency on other declaration
const svc2 = declareServiceRaw(one => 1 + one, svc1);

// declaring a service specifying dependencies as object
const svc3 = declareService(
  // dependencies to be inject
  { 
    one: svc1,
    two: svc2
  },
  ({ one, two }) => {
    return one + two;
  }
);
// by default declaration are unnamed
// but for debug purposes name can be assigned to it
svc3.name = 'svc3';
// or specified as name of factory function
const svc4 = declareServiceRaw(function svc4(){ return 1 });

// create container instance
const rootContainer = new Container();

// get instance of specific service
// if not previously registered - declaration would be automatically registered at root container
rootContainer.get(svc3); // will return 3

// creating token for service to be registered later
// token name is optional, but might be helpful for debug purposes
const token1 = createToken('one');

// get instance of specific service using declaration as token
rootContainer.register(token1, svc1); // will return 3
rootContainer.get(token1); // will return 1 by creating new service instance using declaration svc1

// creating child container
const childContainer = rootContainer.createChild();
childContainer.get(svc3); // will return 3 by reusing service instance created previously in parent container

// overriding existing implementation
childContainer.register(svc2, declareServiceRaw(() => 0));

// now when requesting service with overridden dependency, new instance would be created in child container
// for svc1 dependency instance from parent container would be used
// for svc2 new instance would be created in child container using new declaration
childContainer.get(svc3); // will return 1
rootContainer.get(svc3);  // will still use originally created instance (will return 3)

// dealing with circular dependencies
const t1 = createToken('t1');
const t2 = createToken('t2');
const s1 = declareServiceRaw(s2 => {
  return { s2 };
}, t2);

const s2 = declareServiceRaw(container => {
  return () => {
    const s1 = container.get(t1);
  };
}, containerToken(t1));

rootContainer.register(t1, s1);
rootContainer.register(t2, s2);

const s1instance = rootContainer.get(t1);

Example use case for child container

This is cases when there is a need to create a child container providing context specific implementations while reusing not specific whenever possible.

For example, imagine simple shopping app, having following services registered in root container:

  • products - provides access to products db
  • user (depends on: session)
  • cart (depends on: user, products)

But session in request specific, and so implementation is to be registered in a child container(created per request),

the following will happen:

  1. products service would be created just once and will be reused across all requests
  2. cart and user services will be created for each session separately