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

nilo

v4.0.11

Published

A dependency injection toolset for building applications

Downloads

57

Readme

CI nlm-version nlm-node nlm-github

nilo ("ex nihilo")

Ex nihilo is a Latin phrase meaning "out of nothing". It often appears in conjunction with the concept of creation, as in creatio ex nihilo, meaning "creation out of nothing"—chiefly in philosophical or theological contexts, but also occurs in other fields.

Wikipedia

A dependency injection toolset for building applications. In most cases this wouldn't be used directly but via a framework.

const { App, main } = require('nilo');

const app = new App();
main(app);

Concepts

  • Scope: A set of dependency definitions for objects that share a lifecycle. E.g. the singleton scope is for objects that are created once while objects in the request scope are created for each request. Scopes can be nested which means that objects in the request scope may depend on objects in the singleton scope but not vice versa.
  • Injector: An instance of a scope, holding the individual objects. For each request there will be exactly one injector for the request scope.
  • Key: An id that is used to request a dependency.

Usage

The rest of the docs will use singleton->xyz as a shorthand for "the dependency with key xyz provided in the singleton scope".

const {
  Project, // the project on disk, allows loading files etc.
  Registry, // DI dependency registration
  App, // running application, includes instances of Project and Registry
  main, // run CLI command for an app
} = require('nilo');

App

The App class brings all the other pieces together. It is the primary interface of nilo.

  • app.appDirectory: The root directory of this app.
  • app.project: The Project for this app.
  • app.registry: The Registry for this app.

new App(appDirectory, frameworkDirectory)

app.initialize(): Promise<void>

Loads object-graph interface files which are used to declare dependencies. The default export of each file is expected to be a function that will be invoked with the registry. Example:

module.exports = registry => {
  registry.singleton.setFactory('answer', null, () => 42);
};

app.configure(): Promise<void>

Runs the following steps:

  1. Run app.initialize() if it hasn't happened yet.
  2. Run singleton->configure[] hooks. Their job is to make sure that the config is available.
  3. Run singleton->afterConfigure[] hooks. These are basic bootstrapping hooks that may require configuration to be available. None of them should be specific to a particular kind of app.

Standard Dependencies

singleton->app

The app instance that is currently running.

singleton->project

The project used to load modules.

singleton->registry

The registry used to register dependencies.

Project

A collection of helpers that can be used to load additional files relative to the app's root directory.

new Project(appDirectory, frameworkDirectory)

  • appDirectory: The app's root directory.
  • frameworkDirectory: The directory of the framework. When methods refer to "bundled" dependencies, they're talking about dependencies loaded from here. This can be convenient to handle npm link correctly.

project.loadInterfaceFiles(basename: string)

Interface files are files that follow a specific naming convention and are found in well-known locations:

  • {lib,modules}/*/$basename.{js,mjs}
  • For each dependencies $key in package.json, from $key/$basename.
  • Outside of NODE_ENV=production also for each devDependencies key.

This function returns an array with one entry for each interface file:

  • moduleNamespace: The namespace record for the file.
  • defaultExport: Convenience property for moduleNamespace.default.
  • specifier: The specifier the file was loaded from, relative to the app's root directory.
  • group: The directory the file was found in.

Note that .mjs file support requires you to be using a version of node with builtin support for ES Modules. Currently this is Node 10+ with the --experimental-modules flag. Node 10.x seems to experience segfaults under certain conditions, so we recommend 12+.

Registry

A set of three scopes, in order of nesting:

  • singleton: Scope for objects that are created once and then reused.
  • request: Scope for objects that are created for each request.
  • action: Scope for ephemeral objects that are created for one aspect of handling a request.

DependencyQuery

A dependency query is either a string or a DependencyDescriptor object. They are used both when asking for a dependency (ask for) and when providing one (provide). The following kinds of dependency queries are recognized, each assuming that the resulting dependency is called x:

  • x: A required dependency (provide or ask for). It is expected to be provided exactly once. Descriptor: { key: 'x' }.
  • x?: An optional dependency (ask for only). If it is provided, it is expected to be provided exactly once. Descriptor: { key: 'x', optional: true }.
  • x[]: Getting all values of a multi-valued dependency (ask for only). It may be provided multiple times and all will be injected as an array. Descriptor: { key: 'x', multiValued: true }.
  • x[y]: A specific element of a multi-valued dependency with a unique index (provide or ask for). When providing a multi-valued dependency, this form has to be used. Descriptor: { key: 'x', multiValued: true, index: 'y' }.

registry.getProviderGraph()

Returns a structured object with information about all registered providers, where they have been registered, and what their dependencies are. This data can be used to provide inspection and other developer tooling.

registry.getSingletonInjector()

Creates an Injector for the singleton scope. The injector returned will always be the same instance.

registry.getRequestInjector(request, response)

Creates an Injector for the request scope. Both 'request' and 'response' will be available as dependencies. For the same request, it will return the same injector instance.

registry.getActionInjector(request, response, action)

Creates an Injector for the action scope, based on the request injector for the given request object. In addition to 'request' and 'response', 'action' will be as a dependency. It will always return a new injector.

For testing or trivial uses, you may omit all of the arguments to be given empty default values.

Registry.from(([scope, name, value] | (registry) => void)[]): Registry

Sometimes you might wish to create a registry from scratch, all-at-once, particularly when testing. In this case, you may call the static method Registry.from(), passing it an array which contains items, each of which is either:

  • an tuple specifying a static entry on the specified scope
  • a function which accepts the registry being constructed and makes whatever calls on it it likes

This lets you do something like this:

const deps = Registry.from([
  ['singleton', 'x', 42],
  ['request', 'y', 88],
  require('../one/object-graph'),
  require('../another/object-graph'),
]).getActionInjector().getProvider();
// ^ this is a slightly neater way of doing something like:
const reg = new Registry();
reg.singleton.setValue('x', 42);
reg.request.setValue('y', 42);
require('../one/object-graph')(reg);
require('../another/object-graph')(reg);
const deps = reg.getActionInjector().getProvider();

injector.get(key)

Resolve the dependency specified the DependencyQuery in key and return the resulting object.

registry.singleton.setFactory('x', null, () => 'x-value');
const injector = registry.getSingletonInjector();
const x = injector.get('x');
x === 'x-value';
const y = injector.get('y?');
y === null;
// throws because `y` hasn't been provided:
injector.get('y');

For multi-valued dependencies, the result will be an array with named properties for each index. Example:

registry.singleton.setFactory('x[a]', null, () => 'a-value');
registry.singleton.setFactory('x[b]', null, () => 'b-value');
const x = registry.getSingletonInjector().get('x[]');
x[0] === x.a && x.a === 'a-value';
x[1] === x.b && x.b === 'b-value';

injector.keys()

Returns an array of all registered dependency keys that could be created using this injector.

injector.getProvider()

Get a magical proxy object that can be used to read dependencies. While very convenient, it should be used sparingly. Roughly speaking, reading properties from the provider is equivalent of passing the key to injector.get.

scope.setFactory(key, deps, factory)

  • key: A DependencyQuery that this factory can fulfil.
  • deps: An array of DependencyQuerys that this factory depends on. If there are no dependencies, it may be null.
  • factory: A function that takes an object with the fulfilled dependencies.
registry.singleton.setFactory(
  'projectRootLength',
  ['project'],
  ({ project }) => project.root.length
);

registry.singleton.setFactory('pid', null, () => process.pid);

scope.setValue(key, value)

A convenience method for when a factory would always return the same value, especially handy for things in the singleton scope.

main(app, defaultCommand = 'start', argv = process.argv)

  1. Run app.initialize().
  2. Discover all available commands from singleton->commands[].
  3. Parse CLI options and run selected command, defaulting to defaultCommand.
  4. Exit once the command resolves.