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

@miracledevs/paradigm-web-di

v1.0.3

Published

A minimal dependency injection library for the web

Downloads

66

Readme

Paradigm Web DI Build Status

A minimal dependency injection framework for the web.

Installing

$ npm i @miracledevs/paradigm-web-di

How to use

The library was written to be easy to use. You can decorate your classes, or manually register them for later use.

If you want to register your classes, just decorate your class with the @Injectable decorator. You'll need to provide the life time of your class:

import { Injectable, DependencyLifeTime } from "@miracledevs/paradigm-web-di";

@Injectable({ lifeTime: DependencyLifeTime.Singleton })
export class ExampleService
{
    getValues(): number[]
    {
        return [1, 2, 3, 4];
    }
}

@Injectable({ lifeTime: DependencyLifeTime.Singleton })
export class AnotherService
{
    constructor(private readonly example: ExampleService)
    {
    }

    getValues(): number[]
    {
        return this.example.getValues().map(x => x + 1);
    }
}

Or, if you prefer to have all the dependencies registered at the same time onto a single point, you can also use the procedural approach:

import { DependencyCollection } from "@miracledevs/paradigm-web-di";

DependencyCollection.globalCollection.registerSingleton(ExampleService);
DependencyCollection.globalCollection.registerSingleton(AnotherService, [ ExampleService ]);

We recommend the first approach because we can automatically extract metadata when executing the decorator, and you don't need to manually describe the class dependencies. But, if you need to manually register a class, you can do it.

To resolve your services, you need a reference to a service container. The easiest way to obtain a container is to build one from the global collection DependencyCollection.globalCollection.buildContainer():

import { DependencyCollection } from "@miracledevs/paradigm-web-di";

const container = DependencyCollection.globalCollection.buildContainer();
const service = container.resolve(AnotherService);
console.log(service.getValues());

Lifetimes

The framework works with 3 dependency types:

| Lifetime | Meaning | | --- | --- | | Transient | Every time a class request a transient service, the framework will create a new instance. So, each class will have their own instance of the given transient service. | | Singleton | The framework will allow only one instance of a given type per application life cycle. Every time a class request a singleton reference, the framework will return the same reference. | | Scoped | The global dependency container allows you to create scoped containers. Scoped containers are containers isolated from the global container. When you mark a service a scoped, that service will exist as a singleton inside the scope asking to resolve the class. This may be difficult to understand at first, but let's present an example: On a web server, each request should have their own instances, and they shouldn't know about each other. In this case, you can create a new scope per request, and scope your singleton services if you want the same reference along the request, but not globally accessible to every request.|

Important: Scoped classes can not be resolved in the global container.

Scoped Containers

A Scoped container can be created from a parent container, and they isolate scoped services from other containers. When resolving dependencies, they can look on their parental hierarchy form already instantiated classes if required.

const scopedContainer = container.createScopedInjector("my scope");

Once you have your scoped container, you can used it the same way you would use the global container:

const service = scopedContainer.resolve(AnotherService);
console.log(service.getValues());

Building and Validating the Dependency Tree

The framework can validate if your dependency tree is consistent, to prevent runtime errors:

import { DependencyCollection } from "@miracledevs/paradigm-web-di";

const container = DependencyCollection.globalCollection.buildContainer(true);

The dependency collection is where you register your services, either by calling the DependencyCollection directly, or by using the decorator @Injectable({ ... }). The dependency collection contains the dependency tree you registered, plus some reflection data extracted at declaration time.

When validating the tree, the framework can validate:

  • Circular References: When two classes reference each other, this can derivate into a stack overflow. The dependency can validate this and throw an error informing about this circular reference.

  • Missing Registration: If one of your classes is expecting a service on its constructor, but the latter is not registered, the validation will throw an error telling which classes are failing.

  • Scoped Dependencies on Singletons: If one of your global singletons depends on a scoped service, the framework will fail. Remember, scoped services can only be resolved inside a scoped containers, and singleton live in the global context.

Custom Collections

You can opt out from using the global collection when writing your program. If for some reason you want to use your own collection, or have multiple trees, you can specify the collection when declaring classes:

import { DependencyCollection } from "@miracledevs/paradigm-web-di";

const customCollection = new DependencyCollection();

@Injectable({ collection: customCollection })
class ClassA
{
}

@Injectable({ collection: customCollection })
class ClassB
{
    constructor(private readonly a: ClassA)
    {
    }
}

const container = customContainer.buildContainer(true);
const b = container.resolve(ClassB);

Building and Testing

To build the library:

$ npm run build

To watch-build the library:

$ npm run watch

To watch for changes and build after every change:

$ npm run watch

To test the solution:

$ npm run test

To watch-test the solution:

$ npm run watch-test

To see the test coverage:

$ npm run coverage