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

opium-ioc

v2.5.2

Published

> Opium - DI for the masses.

Downloads

18

Readme

Opium

Opium - DI for the masses.

Opium is a dependency injection framework for javascript. The aim of opium is to provide the simplest possible and yet functionally complete dependency injection solution. Opium's main feature is its lack of assumptions around dependency declaration techniques such as XML, JSON or a DSL. Instead, opium relies on a clean and non opinionated api, that leaves the door open to use your own style/technique for declaring dependencies.

BREAKING CHANGE: As of version 2.0.0, all methods are async to enable async initialization. Don't forget to call await dep.inject(), or bad things will happen.

Getting started

npm install --save opium-ioc

Usage

(async () => {
  const { Opium, PROTOTYPE, SINGLETON } = require('opium-ioc')

  const opium = new Opium()

  // register instance dependencies
  opium.registerInstance('dep1Name', 'param 1')
  opium.registerInstance('dep2Name', 'param 2')

  let count = 0
  // make a factory function that will get
  const factory = async (dep1Name, dep2Name) => {
    return [
      dep1Name, // receives `param 1` as value
      dep2Name, // receives `param 2` as value
      ++count
    ]
  }

  // register the factory
  opium.registerFactory('factory', factory, ['dep1Name', 'dep2Name'])

  const dep = opium.getDep('factory') // top level dep who's graph will be resolved
  const injected = await dep.inject()

  console.log(injected[0], injected[1], injected[2])
  // prints: param 1 param 2 1
})()

The Dependency object

Any dependency registered with opium is wrapped in a Dependency object that provides a very basic set of metadata, and methods to manipulate it.

The most important method is inject. Whenever inject is called on a Dependency, its dependency graph is immediately resolved and all its dependencies are properly injected.

Depending on its life cycle, a dependency might be resolved once and cached for each subsequent call, or resolved every time inject is called. In the case on SINGLETON the result of calling inject is cached, in the case of PROTOTYPE no caching is done, and the resolution happens on every invocation.

It's important to understand, that the result of the invocation is cached, not the type/factory/instance. For example calling inject on a type with PROTOTYPE life cycle will create a new instance every time, however calling a factory with SINGLETON life cycle will cache the result of invoking the factory function and return the same result over and over again.

In most cases interacting with the Dependency object will only happen once - when a top level dependency is resolved from the container and it's inject() method is invoked. This will suffice to trigger the dependency graph resolution and no subsequent interactions with it are required after the fact. It is however useful to expose it as it allows building more sophisticated or specialized dependency resolvers. In other words, it is most useful for framework creators who want to extend opium IoC with their own wiring conventions, for example those that want to have the dependency declarations in a json based config or, use it to power some decorator (@inject) syntax based approach, in languages that support it such as typescript and such.

Types of Dependencies

Opium is built around the assumption of three common types of dependencies. These dependency types are:

  • type - a newable class or function, it receives other dependencies through the constructor - known as constructor injection.
  • factory - a factory method (can be async) that receives other dependencies as arguments and can perform more sophisticated instantiation - known as argument injection.
  • instance - an object that is registered as is, no instantiation is performed, it receives its dependencies as properties on the registered object - known as property injection.

There are three corresponding methods for registering each dependency type:

  • registerType - registers a type dependency.
  • registerFactory - registers a factory dependency.
  • registerInstance - registers an instance dependency.

In addition to dependency types, opium also assumes two types of dependency life cycle:

  • SINGLETON - instantiated and injected only once on each inject() method invocation on the dependency.
  • PROTOTYPE - instantiated and injected on each inject() method invocation on the dependency.

API

Opium class

opium.getDep()

Get a dependency from the IoC context. Returns a Dependency object, who's inject method can be called. Usually, this is the entry point to resolve a top level dependency graph.

async opium.inject()

Triggers injection of all dependencies registered with that IoC context, by default resolution happens on a top level dependency, but in some cases it might be useful to resolve all dependencies in the the context. This could happen when there are more than one top level dependency.

opium.deRegister()

Remote a dependency from the IoC context.

opium.registerType(name, Type, [dependencies], SINGLETON|PROTOTYPE)

A type is either a constructor function, or an ES6 class declaration. When a type dependency is registered, opium will create an instance and an pass all listed dependencies as constructor parameters, in effect performing constructor injection. Constructor parameters will be passed in the order of their declaration in the dependencies array.

opium.registerFactory(name, async ()=>{}, [dependencies], SINGLETON|PROTOTYPE)

When a factory dependency is registered, opium will invoke the factory and an pass all listed dependencies as function arguments, in effect performing argument injection. Dependencies will be passed in the order of declaration in the dependencies array.

opium.registerInstance(name, new MyClass(), [dependencies], SINGLETON|PROTOTYPE)

When an instance dependency is registered, opium will look for, and set properties that match the name of dependencies listed in the dependencies array, in effect performing property injection. If the property is not defined, it will be defined by opium. If debug logging is enabled a warning will be printed.

opium.register(name, dep, [], injector, SINGLETON|PROTOTYPE)

This is the core method that all the register* methods call. In addition to the params that those methods expect, it also expects an instance of an injector type. There are three default injector types that are used by each register* method respectively - ArgumentInjector, ConstructorInjector and PropertyInjector. Each of this injectors will treat the dependency in a well defined manner and would most likely not work correctly if mixed up. For example, although it will work, there are very few cases for setting properties on a bare types, if registered with a PropertyInjector.

Use this method, only if you know what you're doing, for all common cases the predefined injector/register pair should suffice.

Dependency

dependency.resolve()

Return an array of Dependencies that this Dependency expects. The returned Dependencies might or might not be injected.

dependency.inject()

Triggers the Dependency graph resolution for this Dependency and all its Dependencies. Call this if you want to wire a single Dependency. This method returns a Promise that should be awaited, otherwise the behavior is undefined.