New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

@stringstack/core

Package Overview
Dependencies
Maintainers
1
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@stringstack/core - npm Package Compare versions

Comparing version
0.3.2
to
0.4.0
.editorconfig

Sorry, the diff of this file is too big to display

+61
-24

@@ -12,27 +12,35 @@ {

},
"globals": {
"localRequire": true
},
"rules": {
"camelcase": "error",
"comma-dangle": [
"error",
"never"
],
"comma-style": [
"error",
"last"
],
"curly": "error",
"strict": [
"default-case": "error",
"dot-location": [
"error",
"global"
"property"
],
"dot-notation": [
"error",
{
"allowKeywords": true
}
],
"eqeqeq": "error",
"no-undefined": "error",
"vars-on-top": "error",
"no-useless-return": "error",
"no-unused-expressions": "error",
"no-throw-literal": "error",
"no-return-assign": "error",
"no-new": "error",
"no-console": "off",
"no-multi-spaces": "error",
"no-loop-func": "error",
"no-implicit-globals": "error",
"no-floating-decimal": "error",
"no-extend-native": "error",
"no-eq-null": "error",
"wrap-iife": [ "error", "inside" ],
"getter-return": [
"error",
{
"allowImplicit": false
}
],
"guard-for-in": "error",
"default-case": "error",
"dot-location": [ "error", "property" ],
"indent": [

@@ -45,3 +53,2 @@ "error",

],
"dot-notation": [ "error", { "allowKeywords": true } ],
"linebreak-style": [

@@ -51,2 +58,25 @@ "error",

],
"new-cap": [
"error",
{
"newIsCap": true
}
],
"no-eq-null": "error",
"no-extend-native": "error",
"no-floating-decimal": "error",
"no-implicit-globals": "error",
"no-loop-func": "error",
"no-multi-spaces": "error",
"no-new": "error",
"no-return-assign": "error",
"no-throw-literal": "error",
"no-undefined": "error",
"no-unused-expressions": "error",
"no-useless-return": "error",
"no-var": "error",
"one-var": [
"error",
"never"
],
"quotes": [

@@ -60,9 +90,16 @@ "error",

],
"getter-return": [
"space-in-parens": [
"error",
{
"allowImplicit": false
}
"always"
],
"strict": [
"error",
"global"
],
"vars-on-top": "error",
"wrap-iife": [
"error",
"inside"
]
}
}

@@ -13,2 +13,4 @@ 'use strict';

// logger has to be passed with the container because logging is scoped to the component and so is the container.
// so the container is the first time we can inject the path of the component doing the logging
this._logger = logger;

@@ -45,8 +47,4 @@

// set( path, value ) {
// return this._loader.set( this._path, path, value );
// }
}
module.exports = Container;

@@ -37,23 +37,2 @@ 'use strict';

switch ( level ) {
case 'emergency':
level = 'emerg';
break;
case 'critical':
level = 'crit';
break;
case 'err':
level = 'error';
break;
case 'warn':
level = 'warning';
break;
case 'information':
level = 'info';
break;
default:
// NO-OP
break;
}
try {

@@ -72,11 +51,18 @@ config.log( level, component, message, meta );

// Create the loader and pass in the logger.
//
// The logger is passed in like this because it is wrapped by a scope for each component, which injects the
// component's name space into each log entry. Env and config are the same for all components so we can just set
// them globally on the next lines after instantiating the loader.
this._loader = new Loader( this._logger );
this._loader._set( 'app', 'env', env );
// env set globally because the env name is the same for all components
this._loader.set( 'app', 'env', env );
// config set globally because the config is the same for all components
let nconf = new nconfProvider();
this._loader._set( 'app', 'config', nconf );
this._loader.set( 'app', 'config', nconf );
// clamp to array
let rootComponents = config.rootComponents;
if ( !Array.isArray( rootComponents ) ) {

@@ -123,2 +109,18 @@ rootComponents = [];

testComponent( path ) {
const env = this._loader.get( 'app', 'env' );
if ( typeof env !== 'string' || !env.match( /^test/ ) ) {
const message = 'testComponent() is only indented for testing environment, ensure you instantiate App with an environment name beginning with the word \'test\' in order to access this method.';
throw new Error( message )
}
return this._loader.get( 'app', path );
}
}

@@ -125,0 +127,0 @@

@@ -37,4 +37,4 @@ 'use strict';

const component = this._components[ path ];
const instance = this._componentInstances[ path ];
const component = this._components[path];
const instance = this._componentInstances[path];

@@ -111,4 +111,4 @@ // bad component entry or already initialized, there is a bug someplace in this code!

const component = this._components[ path ];
const instance = this._componentInstances[ path ];
const component = this._components[path];
const instance = this._componentInstances[path];

@@ -187,2 +187,10 @@ // bad component entry or already initialized, there is a bug someplace in this code!

isLoaded( targetPath ) {
targetPath = this._normalizePath( targetPath );
return this._componentInstances.hasOwnProperty( targetPath ) && !!this._componentInstances[targetPath];
}
_load( sourcePath, targetPath, inject = false ) {

@@ -210,4 +218,4 @@

if ( this._componentInstances.hasOwnProperty( targetPath ) && this._componentInstances[ targetPath ] ) {
return this._componentInstances[ targetPath ];
if ( this.isLoaded( targetPath ) ) {
return this._componentInstances[targetPath];
}

@@ -253,3 +261,3 @@

return this._instantiateStack[ this._instantiateStack.length - 1 ];
return this._instantiateStack[this._instantiateStack.length - 1];
}

@@ -269,3 +277,3 @@

this._components[ path ] = {
this._components[path] = {
path: path,

@@ -276,11 +284,11 @@ initialized: false,

this._componentInstances[ path ] = instance || null;
this._componentInstances[path] = instance || null;
} else if ( !this._componentInstances[ path ] ) {
} else if ( !this._componentInstances[path] ) {
// replace stub
this._componentInstances[ path ] = instance || null;
this._componentInstances[path] = instance || null;
}
return this._components[ path ].instance;
return this._components[path].instance;

@@ -293,3 +301,3 @@ }

this._components[ sourcePath ] = {
this._components[sourcePath] = {
path: sourcePath,

@@ -303,3 +311,3 @@ instance: null,

this._components[ sourcePath ].dependencies.push( targetPath );
this._components[sourcePath].dependencies.push( targetPath );

@@ -314,3 +322,3 @@ if ( this._createsCycle( sourcePath, targetPath ) ) {

_set( sourcePath, targetPath, targetInstance ) {
set( sourcePath, targetPath, targetInstance ) {

@@ -360,3 +368,3 @@ this._checkPath( sourcePath );

let component = this._components[ targetPath ];
let component = this._components[targetPath];

@@ -400,3 +408,3 @@ if ( !component ) {

let path = outputList[ index ];
let path = outputList[index];

@@ -407,3 +415,3 @@ outputList.splice( index, 1 ); // remove path from middle

// put dependencies on the end now, and their dependencies, etc.
this._components[ path ].dependencies.forEach( ( dependency ) => {
this._components[path].dependencies.forEach( ( dependency ) => {

@@ -434,3 +442,3 @@ let index = outputList.indexOf( dependency );

this._components[ path ].dependencies.forEach( ( dependency ) => {
this._components[path].dependencies.forEach( ( dependency ) => {

@@ -437,0 +445,0 @@ processList.push( dependency );

{
"name": "@stringstack/core",
"description": "StringStack Core",
"version": "0.3.2",
"version": "0.4.0",
"repository": {

@@ -13,3 +13,3 @@ "type": "git",

"dependencies": {
"async": "2.6.1",
"async": "2.6.3",
"nconf": "0.10.0"

@@ -26,5 +26,2 @@ },

],
"engines": {
"node": ">=6.0.0"
},
"license": "MIT",

@@ -41,3 +38,6 @@ "private": false,

},
"author": "Anthony Hildoer <anthony@bluerival.com>"
"author": "Anthony Hildoer <anthony@bluerival.com>",
"engines": {
"node": ">=8.0.0"
}
}
+456
-88

@@ -6,5 +6,46 @@ # StringStack Core

This document will explain the details of implementing component interfaces as well as the semantics for how
dependencies are managed.
StringStack has a very short list of very important objectives in the world of Node.js, from most to least important:
1. Be as vanilla Node.js as possible
1. Graceful startup and shutdown
1. Avoid dependency cycles (dependency spaghetti, I know that doesn't rhyme)
1. Handy utility for accessing contents of package.json (yes, this is basically require(process.cwd() + '/package.json'))
1. Handy utility for accessing name of the current environment (no this isn't just a wrapper for process.env.NODE_ENV)
That is it. You organize your code into components, which are just ECMAScript 6 classes with a certain interface and
then load your code using the StringStack code App class. That is it. StringStack will take care of instantiating your
components in the correct order, then initializing them, and then d-initializing them.
This document is mainly written to be in the order you should probably learn things, but feel free to use the
[Table of Contents](#table-of-contents) to skip around.
StringStack is maintained by [BlueRival Software](https://bluerival.com) and is deployed on APIs for multiple Internet
scale systems for multiple Fortune 500 and Fortune 100 companies.
## Table of Contents
* [Component Interfaces](#component-interfaces)
* [Choosing a Form](#choosing-a-form)
* [Form 1 - ES6 Class](#form-1---es6-class)
* [Form 2 - Object Literal](#form-2---object-literal)
* [Form 3 - JSON](#form-3---json)
* [Interface Methods](#interface-methods)
* [Dependency Container](#dependency-container)
* [get() Method](#get-method)
* [inject() Method](#inject-method)
* [Bootstrap Yo' App](#bootstrap-yo-app)
* [Create App and App Class Interfaces](#create-app-and-app-class-interfaces)
* [Core.createApp()](#corecreateapp)
* [App Class](#app-class)
* [StringStack Life Cycle](#stringstack-life-cycle)
* [Cheat Sheet](#cheat-sheet)
* [Path Resolution](#path-resolution)
* [Configuration](#configuration)
* [Configuration for 3rd Party Components](#configuration-for-3rd-party-components)
* [Logging](#logging)
* [Logging from Custom Components](#logging-from-custom-components)
* [Testing](#testing)
* [Daemonix for Creating Proper Linux Services](#daemonix-for-creating-proper-linux-services)
## Component Interfaces

@@ -20,4 +61,15 @@

### Form 1 - ES6 Class
### Choosing a Form
Should you use form 1 or form 2? The question is really about testing. If you want truly isolated tests, then you
should use form 1. With form 1 you can have multiple tests that each pass in different dependency variations to your
component. You can then test your component under each scenario. With form 2, although StringStack will call `load()`
it is up to your code to ensure consistency between tests, which means your tests now need to also test for consistency.
Internal StringStack engineers only use form 1 for StringStack components and for projects that utilize StringStack.
Note: Form 2 is now deprecated entirely.
#### Form 1 - ES6 Class
```javascript

@@ -32,7 +84,19 @@

init(done) {
done();
done(); // yay the component initialized!
// OR
done( new Error('boo!') ); // no, the component failed to initialize!
}
dinit(done) {
done();
done(); // yay the component d-initialized!
// OR
done( new Error('boo!') ); // no, the component failed to d-initialize!
}

@@ -57,4 +121,8 @@

### Form 2 - Object Literal
#### Form 2 - Object Literal
(Deprecated) This interface leads to issues when users try to do testing, because testing typically involves creating
multiple instances of App class and initializing over and over to test different things. We are going to get rid of this
interface in a future release soon. Don't use it!
```javascript

@@ -69,7 +137,19 @@

init: (done) => {
done();
done(); // yay the component initialized!
// OR
done( new Error('boo!') ); // no, the component failed to initialize!
},
dinit: (done) => {
done();
done(); // yay the component d-initialized!
// OR
done( new Error('boo!') ); // no, the component failed to d-initialize!
}

@@ -86,39 +166,50 @@

component (global singleton) since StringStack will not instantiate this object with the `new Class()` syntax.
Otherwise the semantics of loading components of either form are identical.
Otherwise the semantics of loading components of either form are identical.
### Form 3 - JSON
Note: If you create multiple instances of the App class, they will all load and init the same instance of this object.
This is because it is an object literal and only one exists in the entire Node.js process.
#### Form 3 - JSON
The final form is completely different than the other two forms. It is not instantiated, initialized or d-initialized.
This form is for including JSON files. The files are parsed and returned as native javascript data structures. For
example, in any component you could call `deps.get('./package.json')` and this would return the parsed package.json file
for your application, assuming your current working directory is where your package.json file is located. This is a
great way to load config or other meta data.
example, in any component you could call `deps.get( './meta/some-data.json' )` and this would return the parsed
contents of the meta/some-data.json file for your application, assuming your current working directory is where your the
meta directory lives. This is a great way to load config or other meta data.
### Choosing a Form
Note: This will throw an exception if the contents of the target file does not parse with JSON.parse().
Should you use form 1 or form 2? The question is really about testing. If you want truly isolated tests, then you
should use form 1. With form 1 you can have multiple tests that each pass in different dependency variations to your
component. You can then test your component under each scenario. With form 2, although StringStack will call `load()`
it is up to your code to ensure consistency between tests, which means your tests now need to also test for consistency.
Internal StringStack engineers only use form 1 for StringStack components and for projects that utilize StringStack.
### Interface Methods
The methods of each form are constructor, init, dinit; and load, init, dinit; respectively. The constructor and load
methods both accept a dependency container. The dependency container has two methods `get( path )` and `inject( path )`.
Path is a string containing the path of the component to be retrieved. See the Path Resolution section in this document
to know how paths are resolved. The difference between the two methods is whether the calling component depends on the
target, or if the calling component is injecting itself as a dependency of the target path.
The methods of each of the first 2 forms are constructor, init, dinit; and load, init, dinit; respectively. The
constructor and load methods both accept a dependency container. The dependency container has two methods `get( path )`
and `inject( path )`. Path is a string containing the path of the component to be retrieved. See the
[Path Resolution](#path-resolution) section in this document to know how paths are resolved. The difference between the
two methods is whether the calling component depends on the target, or if the calling component is injecting itself as a
dependency of the target path.
get( path ): This instructs the dependency management system that the calling component depends on the component identified by path.
inject( path ): This instructs the dependency management system that the calling component must be injected as a dependency of the component identified by path. See the section on configuration for an example of why this might be useful.
get( path ): This instructs the dependency management system that the calling component depends on the component
identified by path. That is, StringStack will ensure that the component identified by path is initialized BEFORE the
component calling get is initialized. Similarly, it will ensure that the component that called get is d-initialized
BEFORE the component identified by path is d-initialized.
inject( path ): This instructs the dependency management system that the calling component must be injected as a
dependency of the component identified by path. See the section on configuration for an example of why this might be
useful. StringStack will ensure that the component identified by path is initialized AFTER the component calling get is
initialized. Similarly, it will ensure that the component that called get is d-initialized AFTER the component
identified by path is d-initialized.
Each component MUST get all of its dependencies in its constructor or load method. If you attempt to get a dependency
outside of one of these methods an exception will be through by the container.
outside of one of these methods an exception will be thrown by the container.
Each of the `init()` and `dinit()` methods are optional. But, if your component does define either method your component
MUST call the done method passed once your component is ready for all dependent components to start using it.
MUST call the done method passed once your component is ready for all dependent components to start using it. If you
omit one of the methods, StringStack simply considers the component immediately initialized or d-initialized.
For an imaginary database component, it might look something like this.
Learn more about when things are instantiated, initialized, d-initialized, etc in the section
[StringStack Life Cycle](#stringstack-life-cycle).
For an imaginary database component you might want to create, it could look something like this.
```javascript

@@ -150,24 +241,87 @@

### Dependency Container
## Semantics
Every component that has a constructor or load method gets a dependency container passed in as the only parameter. This
section will describe how to use that container.
StringStack will instantiate, initialize and d-initialize each of your components, and all 3rd party components you load
in a very specific manner. The goal of this semantic is to ensure a few things:
The dependency container is how any component loads any string stack resource. This includes:
1. Your dependencies are 100% ready to be used anytime your component is initialized. That is, it ensures graceful
propagation of start and stop signals of your application.
2. Prevents cycles in your dependency graph. Cycles in dependencies create unmanageable code. (Spaghetti code)
3. Promotes strong modularization of code and DRY patterns.
4. Enables considerably easier testing of your code since dependencies are injected via constructor or load methods.
* Built-in String Stack Resources
* env: A string containing the environment name passed to App class during instantiation.
* logger: Method that accepts level (string), message (string) and meta (optional:object || Error instance) to log
to
* config: An empty instance of nconf. It is up to your components to setup this object. Typically you would
bootstrap a configSetup component, and configSetup would call deps.inject( 'config' ) to populate config with values
before any other component calls deps.get( 'config' ) to access config values.
* 3rd party components are loaded through NPM and are identified by their package name from your package.json file.
Before we go on, a note on ES6 classes vs object literals. When we use the term 'instantiation', this refers to calling
`new SomeComponent(deps)` on a component of the ES6 class variety, or to calling `load(deps)` on the object literal
variety.
* Your custom components. You access all your components in your code base through the dependency container.
When StringStack core is instantiated, you pass in the root components. These are the top of your dependency graph. You
would do so like this.
You MUST access the dependency container in your constructor. Extract all dependencies via the .get() or .inject()
methods in your constructor and store them on your object. Do not use any of the dependencies until init() is called
on your component.
Side note: you can still have traditional require() methods at the global level of your component, but those resources
will load outside of StringStack. It is up to you to handle those resources.
Learn more about when things are instantiated, initialized, d-initialized, etc in the section
[StringStack Life Cycle](#stringstack-life-cycle).
#### get() Method
This is how your component accesses a component it needs and at the same time tells StringStack, "The component I am
asking for needs to be initialized before I am initialized". It also tells StringStack, "I need to be d-initialized
before the component I am accessing here".
The method will return an instantiated instance of the component synchronously. If you load a json file, the file is
already parsed and you can access contents immediately. This is the ONLY resource type you can access immediately inside
your constructor and before init() is called on your component.
#### inject() Method
This method is nearly identical to get(). The only difference is what your are telling StringStack about what is
dependent on what. With .get(), you are telling StringStack that code calling .get() is dependent on the target path
passed to get(). With inject, you are telling StringStack that the code calling inject() is depended on by the target
path.
##### Example Populating Config
Why would you need this? This is for configuring built-in and 3rd party components. For example, the config component
is built in to StringStack. You can't access the constructor in the config component and tell it that you want the
config component to init after your custom configSetup component. Lets say you are using StringStack/mongo, a 3rd party
component. StringStack/mongo will check for its config in a special place by in deps.get( 'config' ). This means that
StringStack will not initialize StringStack/mongo until config is initialized. If you call deps.inject( 'config' ) in
your custom configSetup component, then StringStack will initialize all three components in this order:
configSetup, config, StringStack/mongo
That guarantees the config values are in place before StringStack/mongo looks for them.
##### Example Setting Up Express Routes
Similarly, with a 3rd party component, such as StringStack/express, you would want to setup all your express routes
before StringStack/express initializes and opens up a port to accept HTTP traffic. You would ensure your expressSetup
component initializes first by having expressSetup call deps.inject( 'StringStack/express' ). Then, expressSetup
can initialize all the HTTP routes before any HTTP ports are opened.
## Bootstrap Yo' App
(Finally, something that does rhyme!)
Ok, so you build a bunch of components, now what? This... This is what....
1. Create an instance of StringStack/core.
1. call core.createApp() to create an App class that starts and stops your application.
1. Instantiate App class.
1. Call app.init().
1. When its time to shutdown, call app.dinit().
Aside from the completely made up names for root components, this is all the code you need to bootstrap a production
system.
```javascript
const Core = require('@stringstack/core');
const Core = require( '@stringstack/core' );

@@ -178,13 +332,35 @@ let core = new Core();

rootComponents: [
'./lib/some-component-a',
'./lib/some-component-b',
'./lib/some-component-a',
'./lib/some-component-b',
]
} );
let app = new App('production');
// you can just pass process.env.NODE_ENV, or any other thing to identify env name
let app = new App( 'production' );
function dinit() {
app.dinit( (err) => {
if (err) {
console.error('something went wrong, the app may not have shutdown correctly', e);
} else {
console.log('the node process should exit after this statement prints!');
}
process.exit();
} );
}
app.init( (err) => {
if (err) {
// initialization bails on first thrown exception or callback that returns an Error. Handle error and shutdown
console.error('something went wrong', e);
dinit(); // its ok to d-init even though init failed. Only the initialized components will get d-initialized.
} else {

@@ -198,16 +374,99 @@ console.log('app is up and running!');

// handle shutdown signals, as well as some other nifty process management features.
onSomeProcessShutdownSignal( () => {
app.dinit( (err) => {
if (err) {
console.error('something went wrong, the app may not have shutdown correctly', e);
} else {
console.log('the node process should exit after this statement prints!');
}
});
});
onSomeProcessShutdownSignal( dinit );
```
### Create App and App Class Interfaces
Here we describe the interfaces for the Core.createApp() method and the App class returned from createApp().
#### Core.createApp()
The job of createApp() is to create an App class that controls your code. The method accepts a single object parameter
of the form:
```javascript
let params = {
"log": function ( level, component, message, meta ) {
// wire this up to Winston, or whatever you log with.
},
"rootComponents": [
// A list of component paths to load.
]
};
```
##### Log Method
The log method handles logging calls routed from all components. It is up to each component to determine how it logs.
Including log level semantics, what to put in the message, and if it wants to pass meta data.
For the most part the log values passed from the component are the same values that arrive at this handler. See the
details below to see exactly how each value works.
The log method accepts the following four parameters:
level: This can be any string. StringStack/core will force it to lowercase, so it is a little opinionated about that.
Otherwise it will pass through directly to your log handler from each component.
component: This is a string value of the component path. This is the same string value you would use to load the
component via deps.get() in your constructor. It is provided by StringStack/core automatically.
message: This should be a string. It is up to your components to use message correctly.
meta [optional]: Should be a serializable object or an instance of Error. It is up to your component to use this field
correctly.
Note: 3rd party components will write log entries to this same log handler. It is up to your handler to handle them
correctly. A well written component will document it's log levels, message types, etc. Your handler could look at the
component value to determine if the incoming log entry is from a 3rd party component and needs modification for your
logging facility.
#### rootComponents
This is an array of component paths for the components that will bootstrap your entire application stack. Typically you
would specify at least a config setup component that populates the config object, and an app setup component that starts
loading your actual application code. Each string in this array is a path to a component. See the section
[Path Resolution](#path-resolution) for details on how to craft those strings.
Learn more about when things are instantiated, initialized, d-initialized, etc in the section
[StringStack Life Cycle](#stringstack-life-cycle).
#### App Class
The App class returned by createApp() is a very simple interface. The constructor accepts a single string for the name
of the environment where the app is running. Traditionally node processes are provided the name of their environment
via the environment variable NODE_ENV. So, you could just instantiate app with:
```javascript
new App( process.env.NODE_ENV )
```
For the production load of your app this is probably fine. For testing you will likely just hard code 'test' for your
environment name when you instantiate App.
The instance of App will have two methods you will use for normal production: init() and dinit(). These methods will
initialize and d-initialize the instance of App respectively.
Learn more about when things are instantiated, initialized, d-initialized, etc in the section
[StringStack Life Cycle](#stringstack-life-cycle).
## StringStack Life Cycle
StringStack will instantiate, initialize and d-initialize each of your components, and all 3rd party components you load
in a very specific manner. The goal of this semantic is to ensure a few things:
1. Your dependencies are 100% ready to be used anytime your component is initialized. That is, it ensures graceful
propagation of start and stop signals of your application.
2. Prevents cycles in your dependency graph. Cycles in dependencies create unmanageable code. (Spaghetti code)
3. Promotes strong modularization of code and DRY patterns.
4. Enables considerably easier testing of your code since dependencies are injected via constructor or load methods.
Before we go on, a note on ES6 classes vs object literals. When we use the term 'instantiation', this refers to calling
`new SomeComponent(deps)` on a component of the ES6 class variety, or to calling `load(deps)` on the object literal
variety. Note that object literal component format is now deprecated. See the section
[Form 2 - Object Literal](#form-2---object-literal) for explanation on the rational for removing that form.
When StringStack core is instantiated, you pass in the root components. These are the top of your dependency graph.
Here we are passing in two root components. The order matters. StringStack instantiates components in depth-first order.

@@ -298,3 +557,43 @@

### Cheat Sheet
This is a short hand reminder of everything you need to know for the order things occur in StringStack.
1. Your code calls: ```let App = core.createApp();```
1. Your code instantiates App: ```let app = new App( env );```
1. StringStack: sets up the global log handler and puts it in the global dependency container as ```logger```.
1. StringStack: App creates an empty instance of nconf and puts it in the global dependency container as
```config```.
1. StringStack: App sets the ```env``` value in the global dependency container.
1. StringStack: Instantiates each ```root component``` in order
1. Each root component may call ```deps.get()``` or ```deps.inject()```. If any component, including a root
components, calls ```deps.get()``` or ```deps.inject()```, the target path component of that call will get
instantiated immediately if it hasn't already been instantiated by another component. So the loading of
components is a depth first, recursive instantiation of dependencies. If a dependency cycle is detected,
StringStack will throw an Error.
1. Once all root components have been instantiated, and thus all their upstream dependency trees have also been
instantiated, the constructor for App returns.
1. If any component tries to call ```deps.get()``` or ```deps.inject()``` after this point an Error will be thrown.
1. Your code calls: ``` app.init( (err) => { } );```
1. If an err is returned, your app did not finish initializing. You could attempt a dinit() then process.exit(), or
just process.exit().
1. StringStack: Asynchronously initializes all components by calling ```init( done )``` on each component. The order
depends on what components called ```deps.get()``` and which called ```deps.inject()```. But you can be assured that
if your component called deps.get( target ), then target was initialized before your component was initialized. If
your component called deps.inject( target ), then target won't be initialized until your component finishes
initialization.
1. Your app is now running. StringStack is no longer involved except for routing logging calls to your handler. The
performance of your application is 100% based on the quality of your code.
After some time, its time for your code to shutdown. Maybe it is a Ctrl+C signal or the server is shutting down, or
an uncaught exception handler is causing the system to shutdown. This is the d-initialization life cycle.
1. Your code responds to the shutdown request and calls: ``` app.dinit( (err) => { } );```
1. StringStack: Asynchronously d-initializes all components. The order is exactly the reverse of initialization
order. This ensures that, for example, you stop accepting new web requests, and let existing web requests finish
before you close your connection to the database.
1. If you get an error, you could log it, or not, but either way call process.exit() once the callback to dinit() is
fired.
## Path Resolution

@@ -316,4 +615,4 @@

StringStack/core has a built in configuration place. It is implemented with nconf (https://www.npmjs.com/package/nconf).
The current version of nconf being used is v0.10.0.
StringStack/core has a built in configuration place. It is implemented with [nconf](https://www.npmjs.com/package/nconf)
. The current version of nconf being used is v0.10.0.

@@ -333,3 +632,3 @@ You can access the nconf instance with the dependency container in the constructor of your component.

That is all that is done. It is up to you to initialize the instance. Keep in mind that nconf is is geared more toward
That is all that is done. It is up to you to initialize the instance. Keep in mind that nconf is geared more toward
synchronous loading of config, so you will need to trigger the loading and parsing of config resources in a constructor

@@ -351,5 +650,5 @@ of one of your custom components. It is recommended that you create a config setup component that is loaded as one of

// using deps.get( 'config' ). For example, all StringStack/* components will load config with get(), this means
// that the SetupConfig.init() method will run before say StringStack/express. That allows you to pull down config
// asynchronously from some remote location, such as we do here inside the init() method. Config will be available
// before StringStack/express.init() method is called.
// that the SetupConfig.init() method will run before say StringStack/express.init(). That allows you to pull down
// config asynchronously from some remote location, such as we do here inside the example init() method. Config
// will then be available before StringStack/express.init() method is called.
this._config = deps.inject( 'config' );

@@ -397,5 +696,13 @@

# Logging
### Configuration for 3rd Party Components
StringStack/core provides a logging facility that you can use to tap into your favorite logging tool. Simply pass a
One of the values of StringStack is the ability to include 3rd party libraries into your stack. Many of these 3rd party
libraries, such as StringStack/express, will require config. Each of these components will specify where they will look
for config within the nconf component.
See the documentation of each 3rd party component to know how to configure them.
## Logging
StringStack/core provides a logging router that you can use to tap into your favorite logging tool. Simply pass a
logger function to the config for createApp() and get all the log writes from all components. You could wire up Winston

@@ -406,7 +713,13 @@ like this.

level: This is a string. Your custom components can pass anything your logger understands. All @StringStack/* community components will use log levels as prescribed by https://www.npmjs.com/package/winston#logging-levels
component: This is the string name of the component that triggered the log event. The dependency injector will provide this field for you. The logging function passed into your component will only accept level, message and meta.
level: This is a string. Your custom components can pass anything your logger understands. All @StringStack/* community
components will use log levels as prescribed by https://www.npmjs.com/package/winston#logging-levels
component: This is the string name of the component that triggered the log event. The dependency injector will provide
this field for you. The logging function passed into your component will only accept level, message and meta.
message: This is a string containing a message describing the event.
meta: This is any value you want to associate with your message. @StringStack/* components may pass instances of Error as meta.
meta: This is any value you want to associate with your message. @StringStack/* components may pass instances of Error
as meta or an object that can be serialized with JSON.stringify().
```javascript

@@ -462,7 +775,7 @@

The handler function will receive a log level, the full path to the component triggering the log event, a string message
and a meta object with relevant data about the log message. Meta might be an instance of Error, a random object literal,
or some other piece of data to describe the log event beyond the message. However,
and an optional meta object with relevant data about the log message. Meta might be an instance of Error, a random
object literal, or some other piece of data to describe the log event beyond the message.
The component loader and the generated app, both parts of StringStack/core, will generate some log entries, as well as
all StringStack/* components built by the StringStack team. The logs events generated will conform to the following
all StringStack/* components built by the StringStack team. The log events generated will conform to the following
practices as it pertains to log level. We use the same log level semantics recommended by RFC5424,

@@ -519,5 +832,6 @@ https://www.npmjs.com/package/winston, and Linux' syslog.

## Logging from Custom Components
### Logging from Custom Components
Accessing the logging function from within your custom component is accomplished like this.
Accessing the logging function from within your custom component is accomplished like this. Most components will tell
you when they are ready to run. Logger is available immediately in your constructor.

@@ -528,3 +842,3 @@ ```javascript

constructor(deps) {
constructor( deps ) {

@@ -544,4 +858,6 @@ this._log = deps.get( 'logger' );

if ( err ) {
this._log('err', 'Error doing something', err);
}
this._log( 'err', 'Error doing somethingElse', err );
} else {
this._log( 'debug', 'somethingElse returned fine' );
}

@@ -561,4 +877,65 @@ done( err || null );

# Daemonix for Linux Signal Management
## Testing
StringStack provides a way to access components for easy testing. In order to access the testing method to extract
components your environment name string must begin with the word 'test'. Any of these environment names works:
'test'
'test-4'
'testing'
'testing-3'
'tester'
'tester-1'
'test '
'test this'
If you have a valid environment name set for testing, then you can access testComponent() method. Do so like this.
```javascript
let core = new Core();
let App = core.createApp( {
rootComponents: [
'./lib/configSetup', // you may or may not need this, depending on how you want to test componentToTest
'./lib/componentToTest'
]
} );
let app = new App( 'test' );
let componentToTest = app.testComponent( './lib/componentToTest' );
// Now do testing on componentToTest pre-initialization
app.init( ( err ) => {
// test if componentToTest initializes correctly
// You can also test componentToTest initialized
} );
```
### Coming Soon
Currently when you access a component for testing it will pull in its dependencies without a way for you to override
dependencies. So there is no way to mock the dependencies of the component you want to test. We are working on a way
to override dependencies. We are currently deciding on what the interface should look like. Some of the questions we are
still trying to settle are:
* Should we provide dependency injection overrides as part of the call to createApp() or on the instantiation of App?
(We think, createApp())
* Should the dependency injection overrides impact all components that request that dependency, or should we be able to
isolate the override for specific dependents? (We think, all)
* When an override is specified, should it accept an alternate path name string only, a Class reference only, or both?
(We think, both)
* Should we allow overriding the built-in components, such as logger, env and config? (We think, yes.)
## Daemonix for Creating Proper Linux Services
If you are running your application on a Linux/Mac/BSD/Unix/etc based system, including containers or app engines, we

@@ -592,10 +969,1 @@ recommend using Daemonix for handling OS process signals and properly daemonizing your NodeJS application. Daemonix also

```
### Config for 3rd Party Components
One of the values of StringStack is the ability to include 3rd party libraries into your stack. Many of these 3rd party
libraries, such as StringStack/express, will require config. Each of these components will specify where they will look
for config within the nconf component.
See the configuration section above for an example pattern on how to load config (even asychronously) into the config
instance before 3rd part components need the config.

@@ -23,5 +23,5 @@ /* eslint-disable no-undefined */

assert.equal(
app._loader._components[ path ].initialized,
app._loader._components[path].initialized,
initialized,
'module path ' + path + ' should be ' + (initialized ? 'initialized' : 'dinitialized')
'module path ' + path + ' should be ' + ( initialized ? 'initialized' : 'dinitialized' )
);

@@ -52,3 +52,3 @@

if ( app._loader._componentInstances.hasOwnProperty( targetPath ) ) {
return app._loader._componentInstances[ targetPath ];
return app._loader._componentInstances[targetPath];
}

@@ -365,2 +365,260 @@

it( 'should allow test access via testComponent()', function ( done ) {
let core = new Core();
let App = core.createApp( {
rootComponents: [
'./test/lib/general/class.config.setup',
'./test/lib/general/class.a'
]
} );
let app = new App( 'test' );
// init/dinit over and over. It is up to the actual modules to ensure they reset their internal state
// correctly on subsequent init/dinit cycles.
async.series( [
( done ) => {
try {
const component = app.testComponent( './test/lib/general/class.d' );
assert.strictEqual( component.isInitialized(), false, 'component should not be initialized' );
} catch ( e ) {
return done( e );
}
done();
},
checkInitialized( app, false ),
( done ) => {
app.init( done );
},
( done ) => {
try {
const component = app.testComponent( './test/lib/general/class.d' );
assert.strictEqual( component.isInitialized(), true, 'component should be initialized' );
} catch ( e ) {
return done( e );
}
done();
},
checkInitialized( app, true ),
( done ) => {
app.dinit( done );
},
( done ) => {
try {
const component = app.testComponent( './test/lib/general/class.d' );
assert.strictEqual( component.isInitialized(), false, 'component should not be initialized' );
} catch ( e ) {
return done( e );
}
done();
},
checkInitialized( app, false ),
( done ) => {
let actualEvents = getComponentManually( app, './test/lib/general/class.a' )._getEvents();
let expectedEvents = [
'TestConfigSetup:instantiate',
'TestA:instantiate',
'TestB:instantiate',
'TestDatabase:instantiate',
'TestC:instantiate',
'TestD:instantiate',
'TestE:instantiate',
'StaticH:instantiate',
'TestF:instantiate',
'TestG:instantiate',
'TestConfigSetup:init',
'TestDatabase:init',
'TestG:init',
'TestF:init',
'StaticH:init',
'TestE:init',
'TestD:init',
'TestC:init',
'TestB:init',
'TestA:init',
'TestA:dinit',
'TestB:dinit',
'TestC:dinit',
'TestD:dinit',
'TestE:dinit',
'StaticH:dinit',
'TestF:dinit',
'TestG:dinit',
'TestDatabase:dinit',
'TestConfigSetup:dinit'
];
try {
assert.deepStrictEqual( actualEvents,
expectedEvents,
'log of instantiation, initialization and d-initialization is not correct' );
} catch ( e ) {
return done( e );
}
done();
}
], done );
} );
it( 'should allow test access via testComponent() for other compatible environment names', function ( done ) {
let core = new Core();
let App = core.createApp( {
rootComponents: [
'./test/lib/general/class.config.setup',
'./test/lib/general/class.a'
]
} );
let app = new App( 'testing' );
// init/dinit over and over. It is up to the actual modules to ensure they reset their internal state
// correctly on subsequent init/dinit cycles.
async.series( [
( done ) => {
try {
const component = app.testComponent( './test/lib/general/class.d' );
assert.strictEqual( component.isInitialized(), false, 'component should not be initialized' );
} catch ( e ) {
return done( e );
}
done();
},
checkInitialized( app, false ),
( done ) => {
app.init( done );
},
( done ) => {
try {
const component = app.testComponent( './test/lib/general/class.d' );
assert.strictEqual( component.isInitialized(), true, 'component should be initialized' );
} catch ( e ) {
return done( e );
}
done();
},
checkInitialized( app, true ),
( done ) => {
app.dinit( done );
},
( done ) => {
try {
const component = app.testComponent( './test/lib/general/class.d' );
assert.strictEqual( component.isInitialized(), false, 'component should not be initialized' );
} catch ( e ) {
return done( e );
}
done();
},
checkInitialized( app, false ),
( done ) => {
let actualEvents = getComponentManually( app, './test/lib/general/class.a' )._getEvents();
let expectedEvents = [
'TestConfigSetup:instantiate',
'TestA:instantiate',
'TestB:instantiate',
'TestDatabase:instantiate',
'TestC:instantiate',
'TestD:instantiate',
'TestE:instantiate',
'StaticH:instantiate',
'TestF:instantiate',
'TestG:instantiate',
'TestConfigSetup:init',
'TestDatabase:init',
'TestG:init',
'TestF:init',
'StaticH:init',
'TestE:init',
'TestD:init',
'TestC:init',
'TestB:init',
'TestA:init',
'TestA:dinit',
'TestB:dinit',
'TestC:dinit',
'TestD:dinit',
'TestE:dinit',
'StaticH:dinit',
'TestF:dinit',
'TestG:dinit',
'TestDatabase:dinit',
'TestConfigSetup:dinit'
];
try {
assert.deepStrictEqual( actualEvents,
expectedEvents,
'log of instantiation, initialization and d-initialization is not correct' );
} catch ( e ) {
return done( e );
}
done();
}
], done );
} );
it( 'should not allow test access via testComponent() when environment name doesn\'t start with test', function ( done ) {
let core = new Core();
let App = core.createApp( {
rootComponents: [
'./test/lib/general/class.config.setup',
'./test/lib/general/class.a'
]
} );
let app = new App( 'production' );
// correctly on subsequent init/dinit cycles.
async.series( [
( done ) => {
try {
assert.throws(
() => {
app.testComponent( './test/lib/general/class.d' );
},
{
message: 'testComponent() is only indented for testing environment, ensure you instantiate App with an environment name beginning with the word \'test\' in order to access this method.'
},
'error message does not match'
);
} catch ( e ) {
return done( e );
}
done();
}
], done );
} );
it( 'should load dependencies correctly on injection', function ( done ) {

@@ -989,3 +1247,3 @@

[
'warning',
'warn',
dir + '/test/lib/log/class.a',

@@ -1078,3 +1336,3 @@ 'TestLogA init warn',

[
'info',
'information',
dir + '/test/lib/log/class.b',

@@ -1081,0 +1339,0 @@ 'TestLogB information',

@@ -12,2 +12,3 @@ 'use strict';

this._dinitCalled = 0;
this._initialized = false;
this._testEvent( 'instantiate' );

@@ -18,2 +19,3 @@ }

this._testEvent( 'init' );
this._initialized = true;
this._initCalled++;

@@ -25,2 +27,3 @@ done();

this._testEvent( 'dinit' );
this._initialized = false;
this._dinitCalled++;

@@ -27,0 +30,0 @@ done();

@@ -11,4 +11,8 @@ 'use strict';

isInitialized() {
return this._initialized;
}
}
module.exports = TestD;

@@ -8,2 +8,3 @@ 'use strict';

constructor( deps ) {
super( deps, 'TestLogA' );

@@ -10,0 +11,0 @@