Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

billy

Package Overview
Dependencies
Maintainers
1
Versions
25
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

billy - npm Package Compare versions

Comparing version 1.7.3 to 2.0.0-beta-1

lib/Container.js

78

HISTORY.md
# Change History
## v1.7.3 (2015-11-30)
## v2.0.0 (???)
* Updated sack version to fix issue with nested functions
* Initial v2 release
## v1.7.2 (2015-11-29)
## v1 Change History
* Updated sack version to fix ES2015 class issue
## v1.7.1 (2015-11-29)
* Updated dependencies (fixes issues with ES2015 runtimes)
## v1.7.0 (2014-10-12)
* Added `Application#registerDefaultInstance` method
* Added `Application#registerInstance` convenience method
* Added `Application#registerSingleton` convenience method
## v1.6.0 (2014-10-01)
* Deprecate `Application#config` -- will be removing all `ConfigStore` stuff in v2
## v1.5.2 (2014-09-22)
* JSDoc updates / documention generation script
## v1.5.1 (2014-09-15)
* Expose `ConfigStore` class on the package
## v1.5.0 (2014-08-30)
* `container()` method deprecated
* Updated dependencies
* Updated documentation
## v1.4.1 (2014-06-10)
* Updated dependencies
* Using `bluebird` for promises now
## v1.4.0 (2014-05-31)
* Added `stop()` method to shutdown services in reverse order
* Removed nasty deferred-style `.started` property
* Expose the `Application` object directly off of module
## 1.3.1 (2014-05-29)
* Fixed issue with logging timing
## 1.3.0 (2014-05-28)
* `manifest()` is deprecated now -- semi pointless
* Added debugging statements
* Fixed bug with old IE
## 1.2.0 (2014-05-27)
* Added default implicit sets when getting a config key.
## 1.1.0 (2014-05-20)
* Fixed bug when accessing deep configs that are missing
* Added `manifest()` method to declaratively setup services and configs
## 1.0.2 (2014-05-17)
* Updated NPM dependencies.
## 1.0.1 (2014-05-16)
* Use `~` instead of `^` in `package.json` for installing with old versions of `npm`.
## 1.0.0 (2014-04-22)
* First release
* https://github.com/bvalosek/billy/blob/v1.7.3/HISTORY.md

@@ -1,7 +0,1 @@

var Application = module.exports = require('./lib/Application.js');
// in case we ref it the old way ( ... ಠ_ಠ )
Application.Application = Application;
// Expose config object for ref / testing
module.exports.ConfigStore = require('./lib/ConfigStore.js');
module.exports = require('./lib');

@@ -1,324 +0,130 @@

module.exports = Application;
const debug = require('debug')('billy:Application');
const Container = require('./Container.js');
var Promise = require('bluebird');
var Container = require('sack').Container;
var IoCBinding = require('sack').IoCBinding;
var debug = require('debug')('billy:Application');
var getName = require('typedef').getName;
var ConfigStore = require('./ConfigStore.js');
/*
/**
* @class
* Centralized application harness used to register and boot up IoC-injected
* services.
*
* ```
* var Application = require('billy');
*
* var app = new Application();
*
* app.service(function() {
* ...
* });
*
* app.service(SomeService);
* app.service(SomeOtherService);
* app.config('http', {
* ...
* });
*
* app.start();
* ```
*/
function Application()
Centralized application harness used to register and boot up IoC-injected
services.
*/
module.exports = class Application
{
if (!(this instanceof Application))
return new Application();
constructor({ containerTag = 'container' } = { })
{
this._started = false;
this._services = [ ];
this._running = [ ];
this._services = [];
this._running = [];
this._container = new Container();
this._container = new Container();
/**
* The convenience function from {@link ConfigStore#getStore}.
*
* Also exposed in the app as the `config` dependency.
* @readonly
* @example
* var password = app.config('http.auth.password');
*
* app.service(function(config) {
*
* // set a new config
* config('new config', 123);
*
* });
* @type {function}
* @deprecated
*/
this.config = new ConfigStore().getStore();
}
if (containerTag) {
this._container.registerValue(containerTag, this._container);
}
}
/**
* Add a service to the application.
*
* Your application entry point will register a series of services that will
* power your app. Services can either be a simple closure or a class
* constructor, and can optionally use promises to signal an asynchronous
* startup.
* @param {function} T Class constructor or closure
* @example
* // Use a closure as a service
* app.service(function main() {
* console.log('service created');
* });
* @example
* // Return a Promise to signal an asynchronous startup
* app.service(function main() {
* console.log('service created');
*
* return someAsyncTask()
* .then(function() {
* console.log('service started');
* });
* });
* @example
* // Use a class constructor
* var HttpService = require('billy-http-express');
*
* app.service(HttpService);
*/
Application.prototype.service = function(T)
{
this._services.push(T);
debug('registered service %s', getName(T));
};
get container()
{
return this._container;
}
/**
* Delegate registering things to the IoC container.
*
* See {@link Container#register}
* @param {string} tag Tag to register a dependency under
* @param {function|object} thing A class constructor or object instance
* @return {IoCBinding}
*/
Application.prototype.register = function(tag, thing)
{
if (typeof tag !== 'string')
throw new TypeError('tag');
/*
debug('registering dependency with tag %s', tag);
return this._container.register(tag, thing);
};
Add a new service class to the application stack
/**
* Register a thing as a singleton dependency.
*
* This is a convenience method that is equivalent to:
*
* ```
* app.register('thing', Thing).asSingleton();
* ```
*
* See {@link Container#register}
* @param {string} tag Tag to register a depedency under
* @param {function|object} thing A class constructor or object instance
* @return {IoCBinding}
*/
Application.prototype.registerSingleton = function(tag, thing)
{
return this.register(tag, thing).asSingleton();
};
*/
service(T)
{
if (this._started) {
throw new Error('Cannot add service after application has been started');
}
/**
* Register an existing object instance as a dependency.
*
* This is a convenience method that is equivalent to:
*
* ```
* app.register('thing', thing).asInstance();
* ```
*
* See {@link Container#register}
* @param {string} tag Tag to register a depedency under
* @param {object} thing An object instance
* @return {IoCBinding}
*/
Application.prototype.registerInstance = function(tag, thing)
{
return this.register(tag, thing).asInstance();
};
if (!T) {
throw new Error('Missing parameter T');
}
/**
* Register a weak instance dependency if there isn't already one registered.
*
* This effectively checks first if a dependency exists (via
* {@link Container#tagExists}), and if it doesn't, will register a dependency
* via the {@link IoCBinding#asWeak} and {@link IoCBinding#asInstance}
* @param {string} tag
* @param {object} thing
*/
Application.prototype.registerDefaultInstance = function(tag, thing)
{
if (this._container.tagExists(tag))
return;
const type = typeof T;
this._container.register(tag, thing)
.asWeak()
.asInstance();
};
if (type !== 'function') {
throw new Error(`Invalid typeof T: ${type}, must be 'function'`);
}
/**
* Delegate object creation to the IoC container.
*
* See {@link Container#make}
* @param {Function|string} thing
* @return {object}
*/
Application.prototype.make = function(thing)
{
return this._container.make(thing);
};
const name = T.name;
/**
* Start all of the services in the ordered they were registered.
*
* Any service closure that returns a `Promise`, or service class that implements
* a `start` method that returns a `Promise`, will cause the startup to wait
* until it has resolved before moving onto the next service.
* @return {Promise} This promise is resolved when all services have finished
* starting, and is rejected if there was an error during service start.
* @example
* app.start().then(function() {
* console.log('app started');
* });
*/
Application.prototype.start = function()
{
debug('starting app');
if (this._services.includes(T)) {
throw new Error(`Service already registered: ${name}`);
}
// Default stuff registered as weak so another service could override it if
// needed
this.register('config', this.config).asInstance().asWeak();
this.register('app', this).asInstance().asWeak();
this._services.push(T);
// Instantiate all services in order -- this works equally well if a class
// constructor was passed to service() or just a normal function.
for (var n = 0; n < this._services.length; n++) {
var T = this._services[n];
debug('creating service %s', getName(T));
var service = this.make(T);
this._running.push(service);
debug(`registered ${name}`);
}
// Start in order and async
var sequence = Promise.resolve();
for (var m = 0; m < this._running.length; m++) {
var s = this._running[m];
sequence = sequence.then(start(s));
}
/*
// Resolve the final started promise
return sequence.then(function() {
debug('app started successfully');
},
function(err) {
debug('error starting app: %s', err);
throw err;
});
};
Instantiate and start each registered service
/**
* Stop all registered services in the reverse order they were registered.
*
* This allows for graceful shutdown of any services that are running.
*
* If a service class implements a `stop` method, it can return a `Promise` to
* allow for asynchronous shutdown.
* @return {Promise} This promise is resolved when all services have finished
* stopping, and is rejected if there was an error during service shutdown.
* @example
* app.stop().then(function() {
* console.log('app gracefully shutdown');
* });
*/
Application.prototype.stop = function()
{
debug('stopping app');
*/
async start()
{
debug('starting application');
// Loop through all services in reverse, allowing them to customize stop
// behavior if needed
var sequence = Promise.resolve();
for (var n = this._running.length - 1; n >= 0; n--) {
var service = this._running[n];
sequence = sequence.then(stop(service));
this._started = true;
for (const T of this._services) {
debug(`creating ${T.name}`);
const instance = this._container.new(T);
this._running.push(instance);
}
for (const instance of this._running) {
debug(`starting ${instance.constructor.name}`);
if (typeof instance.start === 'function') {
await instance.start();
}
}
debug('application started');
}
return sequence.then(function() {
debug('app stopped successfully');
},
function(err) {
debug('error stopping app: %s', err);
});
};
/*
/**
* Change the IoC container instance.
* @deprecated
* @param {{register:function}} container
* @return {object} Old container.
*/
Application.prototype.container = function(container)
{
if (typeof container.make !== 'function')
throw new TypeError('container.make');
Give each started service a chance to stop in the reverse order they were
started
if (typeof container.register !== 'function')
throw new TypeError('container.register');
*/
async stop()
{
debug('stopping application');
var old = this._container;
this._container = container;
return old;
};
const errs = [ ];
/**
* Load up application information from a manifest hash.
* @deprecated
* @param {{config:object, services:array<any>}} hash
* @return {Application} This object.
*/
Application.prototype.manifest = function(hash)
{
// Add services one by one
if (hash.services) {
for (var n = 0; n < hash.services.length; n++) {
this.service(hash.services[n]);
for (const instance of this._running.reverse()) {
const name = instance.constructor.name;
try {
debug(`stopping ${name}`);
if (typeof instance.stop === 'function') {
await instance.stop();
}
}
catch (err) {
// Want to let all services get a chance to shut down, even if some
// error out, so just swallow the error and continue
errs.push(err);
}
}
}
// Iterate over keys
if (hash.config) {
for (var key in hash.config) {
var value = hash.config[key];
this.config(key, value);
debug('added config %s', key);
// Ensure stop function throws if theres any errors while stopping
if (errs.length) {
throw errs[0];
}
debug('application stopped');
}
return this;
};
function start(s) {
return function() {
debug('starting service %s', getName(s.constructor));
return s.start ? s.start() : s;
};
}
function stop(s) {
return function() {
debug('stopping service %s', getName(s.constructor));
return s.stop ? s.stop() : s;
};
}
{
"name": "billy",
"version": "1.7.3",
"version": "2.0.0-beta-1",
"description": "A minimal application harness that stays out of your way and out of your code.",
"author": {
"name": "Brandon Valosek",
"email": "bvalosek@gmail.com",
"url": "http://bvalosek.com"
},
"author": "Brandon Valosek <bvalosek@gmail.com>",
"repository": "git@github.com:bvalosek/billy.git",
"license": "MIT",
"scripts": {
"test": "tape test/*.js",
"doc": "jsdoc -c ./.jsdoc.json"
"test": "tape test/*.js"
},
"main": "index.js",
"license": "MIT",
"dependencies": {
"babylon": "6.17.0",
"debug": "2.6.6"
},
"devDependencies": {
"jsdoc": "^3.3.0-alpha9",
"jsdoc3-bootstrap": "^0.7.1",
"tape": "^3.0.0"
},
"repository": {
"type": "git",
"url": "git@github.com:bvalosek/billy.git"
},
"testling": {
"files": "test/*.js",
"browsers": [
"ie/6..latest",
"chrome/28..canary",
"firefox/23..nightly",
"safari/5.1..latest",
"opera/latest",
"opera/next",
"iphone/latest",
"ipad/latest",
"android-browser/latest"
]
},
"dependencies": {
"bluebird": "^3.0.5",
"debug": "^2.0.0",
"sack": "^2.2.3",
"typedef": "^1.1.0"
"tape": "4.6.3"
}
}
# billy
[![Build Status](https://travis-ci.org/bvalosek/billy.png?branch=master)](https://travis-ci.org/bvalosek/billy)
[![NPM version](https://badge.fury.io/js/billy.png)](http://badge.fury.io/js/billy)
> v2 is currently in progress and is NOT yet shipped / final.
> To install it, you need to run `npm install billy@v2-beta`
[![CircleCI](https://circleci.com/gh/bvalosek/billy/tree/master.svg?style=svg)](https://circleci.com/gh/bvalosek/billy/tree/master)
A minimal application harness that stays out of your way and out of your code.
> This is the v2 branch, which has back-compat breaking changes from v1, see
> [billy v1.7.3](https://github.com/bvalosek/billy/tree/v1.7.3) for the old
> version.
## Installation

@@ -14,150 +21,153 @@

## Usage
## Overview
```javascript
import Application from 'billy';
The primary goal and driving philosophy of Billy is to provide a cohesive and
useful set of patterns for building an application that doesn't creep its way
into your business logic and domain code.
let app = new Application();
It is flexible and generic enough to work great for building server apps,
browser apps, Javascript games, or even CLI utilities.
app.service(function main() {
console.log('Hello, World!');
});
Much like [express](https://github.com/visionmedia/express), Billy strives not
to be a framework that permeates all parts of your codebase, but rather the
scaffolding that allows you to roll your own application architecture stack.
app.start();
### The `Application` instance and the Service Stack
The root of your application is a single instance of the `Application` class:
```javascript
const Application = require('billy');
const app = new Application();
```
## Links
An application is composed of several **services**. A service is a class that
sets up and starts the various dependencies in your application. Services
should be free of all business logic, and should be the only parts of the
aplication that are aware of Billy.
* [Billy services on npm](https://www.npmjs.org/browse/keyword/billy-service)
* [API documentation](http://docs.billy.technology)
### The `Container` instance and Dependency Injection
## Features
> Philosophy behind the IoC container
* [Dependency injection / inversion-of-control container](https://github.com/bvalosek/sack)
* Asynchronous promise-based service stack
* Extremely minimal
* Service-oriented design
* Compatible in all browsers and NodeJS
## Usage
## Overview
> Code Examples
The primary goal and driving philosophy of Billy is to provide a cohesive and
useful set of patterns for building an application that doesn't creep its way
into your business logic and domain code.
### Environments
It is flexible and generic enough to work great for building server apps,
browser apps, javascript games, or even CLI utilities.
Billy is written to run in modern Javascript environments (ES2017) that support
the CommonJS module system (e.g, Node 7).
Much like [express](https://github.com/visionmedia/express), Billy strives not
to be a framework that permeates all parts of your codebase, but rather the
scaffolding that allows you to roll your own application architecture stack.
#### Older JS Runtimes
## Services
> Examples of requiring the transpiled versions of the lib
Billy views your application as the composition of several dependency-injected
Services. When the application is started via `app.start()`, all registered
services will be instantiated in turn and be given a chance to startup.
## API
A service should be used to create various run-time objects and register them
as dependencies with the IoC container via the `app` dependency for other parts
of the application to use.
### `Application()`
Services are effectively the place where all the various pieces of your
application are booted, configured, and wired together.
Root application class.
### Registering a service
```javascript
const app = new Application();
```
Your application entry point will register a series of services that will power
your app. Services can either be a simple closure or a class constructor, and
can optionally use promises to signal an asynchronous startup.
#### `Application#service(T)`
### Using closures as a Service
Register a service class with the application.
The simplest example of a service is a function:
```javascript
app.service(PostgresDatabaseService);
```
#### `Application#start()`
Instantiate and start all services in the order they were registered.
```javascript
app.service(() => {
console.log('service created');
});
await app.start();
```
If our service took some time to startup, we could return a `Promise` to ensure
during the service start phase, the application would wait.
#### `Application#stop()`
Give each service a chance to shut down in reverse order they were started.
```javascript
app.service(async () => {
console.log('service created');
await someAsyncTask();
console.log('service started');
});
await app.stop();
```
Note that all services are first *created* all at once (by calling the provided
function), synchronously. Then, all of the services are *started* (by waiting
on any promises returned in the service function).
#### `Application#container`
#### Using Class Constructors as a Service
Reference to the dependency injection container for the application.
A simple class constructor can be passed to the `app.service()` method as well.
### `Container`
The dependency injection container. There is no need to instantiate this
directly as a reference to the application's container is exposed as a property
on the `Application` instance:
```javascript
// MyService.js
const container = app.container;
```
export default class MyService
{
constructor()
{
console.log('service created');
}
}
#### `Container#registerValue(tag, thing)`
Store a simple value in the container. Every time the `tag` dependency is
resolved, the same value is returned.
```javascript
app.container.registerValue('config', require('./config.json'));
```
In our startup file:
#### `Container#registerFactory(tag, factory)`
Store a factory function in the container. Every time the `tag` dependency is
resolved, the factory function will be called with its parameters injected.
```javascript
// main.js
import Application from 'billy';
import MyService from './MyService.js';
app.container.registerFactory('currentTime', () => new Date());
```
let app = new Application();
#### `Container#registerClass(tag, T)`
app.service(MyService);
app.start();
Store a class in the container. Every time the `tag` dependency is resolved, a
fresh instance of the class is instantiated, with its constructor parameters
injected.
```javascript
app.container.registerClass('logger', ElasticSearchLogger);
```
If this service requires some additional setup after all services have been
created, or requires an asynchronous startup, we can implement a `start`
method:
#### `Container#registerSingleton(tag, T)`
Store a **singleton** class in the container. The first time `tag` dependency
is resolved, the class will be instantiated and cached. Each subsequent
resolution of `tag` will return the original instance after that.
```javascript
export default class MyService
{
async start()
{
await someAsyncTask();
console.log('service started');
}
}
app.container.registerSingleton('db', PostgresDatabaseDriver);
```
Any promise return is waited on until it resolves before attempting to start
any subsequent services.
#### `Container#resolve(tag)`
This is useful for things like downloading external data, verifying
credentials, bootstrapping external connections, etc. The application startup
process will block until the service resolves, guaranteeing a deterministic
boot up.
Resolve a dependency from the container via its string tag. Typically this
method shouldn't be used directly, but rather rely on automatic injection to
get a hold of registered dependencies.
## Testing
```javascript
const db = app.container.resolve('db');
```
$ npm test
```
## Documentation
## Contributors
This will generate the HTML documentation under `./doc`:
* [Brandon Valosek](https://github.com/bvalosek)
* [Zak Angelle](https://github.com/zakangelle)
* [Dillon Shook](https://github.com/dshook)
## Testing
```
$ npm run doc
$ npm test
```

@@ -167,2 +177,3 @@

MIT
[MIT](https://github.com/bvalosek/billy/blob/master/LICENSE)

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc