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

@dflowng/di

v0.1.1

Published

DI container and service locator for Data Flow eNGine

Downloads

4

Readme

@dflowng/di - DI container and service locator for the Data Flow eNGine

This package is a part of the Data Flow eNGine project.

Usage

Getting root container

To get the root container just import @dflowng/di package:

const DI = require('@dflowng/di');

It also would be useful to call #configure(Module) method. This method will look for package.json of the current module and load all the dependencies (using Module#require(id) call):

DI.configure(module);

If any of packages current package depends on will define any services then they will be available after call to #configure(Module). There also is a version of #configure(Module) method called #configureDev(Module). This method will load dependencies listed in "devDependencies" section of package.json too:

DI.configureDev(module);

Object lifecycle management

There is a class (Referenced) of objects that have a reference counter. This class is defined in @dflowng/di package as there is a dependency loop between DI container and this class.

Methods Referenced#retain() and Referenced#release() increment and decrement a internal reference counter of the object. When after call of Referenced#release() method the reference counter reaches zero (or negative value) the Referenced#destructor() method (destructor) is called and if after destructor execution the reference counter remains non-positive number then the object is recognized as not-alive (the property Referenced#isAlive tells if object is alive).

Methods Referenced#referTo(obj) and Referenced#noReferTo(obj) register and destroy a references from the object to another. When a Referenced#referTo(obj) method is called with object that is instance of Referenced then the reference counter of that object is incremented and reference to that object is saved. When destructor of a object is called all of the references (and probably referred objects) are destroyed.

Method Referenced#do(callback) performs (possibly asynchronous) operation keeping the object alive.

Creating child containers

Child (local) container of a DI container is a container that (at any moment of time) has defined all services defined in it's parent container. Services defined in child container will override the services defined in it's parent.

There are two ways to get a child container. The first one is to use #for(obj) method of parent container:

const x = {};
const di = DI.for(x);

Every call to #for(obj) method will return the same object for the same argument if the argument is a object (even if called on different containers).

If the object passed as argument to #for(obj) is instance of Referenced then the child container becomes able to store services that are instances of Referenced until the object is destroyed.

The second way is to use #withLocal(callback) method:

DI.withLocal(di => {
    // . . .
});

The created container becomes able to store services that are instances of Referenced until the (possibly asynchronous) operation represented by callback is completed.

Defining and resolving dependencies (services)

There are different methods to access different kinds of services -- variables, classes, functions and arrays. Services of any kind have represented by sequence of one or more string identifiers.

Defining and resolving classes

To access class service #class(className[, implementationName1[, ...]]) method should be used:

// Use #get() method to get a raw class (for inheritance).
// There also is #getClass(className[, implName1[, ...]])
// method that is a shorthand for #class(...).get().
class A extends DI.class('Referenced').get() {
    constructor(n) {
        // . . .
    }
    // . . .
};

// Use this if no instances of the class should be created
//A[DI.abstract] = true;

// Use #define(clazz) method to register class
DI.class('A').define(A);

// Method #new(...args) creates new instance and performs
// dependency injection (if necessarry)
const a = DI.class('A').new(42);

Defining and resolving functions

To access function services #func(functionName[, implementationName1[, ...]]) method should be used:

// Use #define(func) to define function:
DI.func('foo').define(function foo(x) {return x*x;});

// The service accessor function will delegate call to the function if it is defined:
DI.func('foo')(2); // Will call foo(2)

// Use #get() method to get the function itself:
DI.func('foo').get() === foo // true

Defining and resolving variables

Class and function services may be defined only as es6-classes and functions. To define services that are not functions or classes there are variable services.

Method #var(varName[, implName[, ...]]) is used to access variable services:

// Set a variable using #set(value) method:
DI.var('bar').set('baz');

// Get a variable using #get() method:
DI.var('bar').get() // 'baz'

Defining and resolving arrays

A special case of service is a array of objects items of which may be distributed between multiple containers (the container and chain of it's parents).

Method #var(varName[, implName[, ...]]) is used to access variable services:

// Define array items in root container:
DI.array('myArray').add('foo','bar');

DI.withLocal(di => {
    // Add items in local container:
    di.array('myArray').add('baz');
    
    // Get array from root and from local containers:`
    di.array('myArray').get() // ['foo', 'bar', 'baz']
    DI.array('myArray').get() // ['foo', 'bar']
});

Dependency injection

Dependency injection is implemented interface-injection-like way. A class instances of which require dependency injection should implement #[DI.injector](di[, ...args]) method. That method will be called by method #new([... args]) of DI container with container the #new([... args]) method was called on as first argument and arguments passed to the #new([... args]) method as the following arguments.

If more than one class in inheritance chain defines #[DI.injector](di[, ...args]) method then all the methods will be called in the same order as constructors of that classes -- that is not necessarry (and even dangerous) to call #[DI.injector](di[, ...args]) of superclass explicitly.

Here is an example of dependency injection:

class A {
    [DI.injector](di) {
        this._n = di.var('n').get();
    }
    
    get n() {
        return this._n;
    }
}

A[DI.abstract] = true;

DI.class('A').define(A);

class A1 extends DI.getClass('A') {
    [DI.injector](di) {
        // [DI.injector] of A is already executed so it's safe to access #n
        this._m = this.n * 2;
    }
    
    get m() {
        return this._m;
    }
}

DI.class('A', '1').define(A1);

DI.withLocal(di => {
    // Will cause error - class A is abstract
    // di.class('A').new()
    
    // Define cariable in local container
    di.var('n').define(42);
    // Create instance of A1 injecting services from local container (di)
    const a = di.class('A', '1').new();
    
    a.m; // 84
});