Comparing version 3.3.7 to 3.3.9
# Change Log | ||
## [3.3.9] | ||
- Exclude various files (including the 12M cc-test-reporter binary) from the npm package. | ||
## [3.3.8] | ||
- Remove lodash | ||
- Replace mocha with zunit | ||
- Update nyc | ||
- Replace travis with github actions | ||
- Replace eslint imperative with ESNext and updated syntax | ||
- Bump dependencies | ||
## [3.3.7] | ||
@@ -4,0 +15,0 @@ ### Changed |
447
index.js
@@ -1,256 +0,261 @@ | ||
var async = require('async') | ||
var debug = require('debug')('systemic:index') | ||
var format = require('util').format | ||
var Toposort = require('toposort-class') | ||
var getProp = require('lodash.get') | ||
var setProp = require('lodash.set') | ||
var hasProp = require('lodash.has') | ||
var map = require('lodash.map') | ||
var find = require('lodash.find') | ||
var isFunction = require('lodash.isfunction') | ||
var toArray = require('lodash.toarray') | ||
var defaults = require('lodash.defaults') | ||
var assign = require('lodash.assign') | ||
var intersection = require('lodash.intersection') | ||
var requireAll = require('require-all') | ||
var Chance = require('chance') | ||
const async = require('async'); | ||
const debug = require('debug')('systemic:index'); | ||
const format = require('util').format; | ||
const Toposort = require('toposort-class'); | ||
const requireAll = require('require-all'); | ||
const { | ||
randomName, | ||
isFunction, | ||
arraysIntersection, | ||
hasProp, | ||
getProp, | ||
setProp | ||
} = require('./utils'); | ||
module.exports = function(_params) { | ||
var params = assign({}, { name: new Chance().first() }, _params) | ||
var definitions = {} | ||
var currentDefinition | ||
var running = false | ||
var started | ||
var defaultComponent = { | ||
start: function(dependencies, cb) { | ||
cb(null, dependencies) | ||
} | ||
const api = {}; | ||
const params = Object.assign({}, {name: randomName()}, _params); | ||
let definitions = {}; | ||
let currentDefinition; | ||
let running = false; | ||
let started; | ||
const defaultComponent = { | ||
start(dependencies, cb) { | ||
cb(null, dependencies); | ||
} | ||
}; | ||
function bootstrap(path) { | ||
requireAll({ | ||
dirname: path, | ||
filter: /^(index.js)$/, | ||
resolve: function(exported) { | ||
var component = exported.default || exported | ||
api.include(isFunction(component) ? component() : component) | ||
} | ||
}) | ||
return api | ||
} | ||
function bootstrap(path) { | ||
requireAll({ | ||
dirname: path, | ||
filter: /^(index.js)$/, | ||
resolve(exported) { | ||
const component = exported.default || exported; | ||
api.include(isFunction(component) ? component() : component); | ||
} | ||
}); | ||
return api; | ||
} | ||
function configure(component) { | ||
return add('config', component, { scoped: true }) | ||
} | ||
function configure(component) { | ||
return add('config', component, { scoped: true }); | ||
} | ||
function add(name, component, options) { | ||
debug('Adding component %s to system %s', name, params.name) | ||
if (definitions.hasOwnProperty(name)) throw new Error(format('Duplicate component: %s', name)) | ||
if (arguments.length === 1) return add(name, defaultComponent) | ||
return _set(name, component, options) | ||
} | ||
function add(...args) { | ||
const [name, component, options] = args; | ||
debug('Adding component %s to system %s', name, params.name); | ||
if (definitions.hasOwnProperty(name)) throw new Error(format('Duplicate component: %s', name)); | ||
if (args.length === 1) return add(name, defaultComponent); | ||
return _set(name, component, options); | ||
} | ||
function set(name, component, options) { | ||
debug('Setting component %s on system %s', name, params.name) | ||
return _set(name, component, options) | ||
} | ||
function set(name, component, options) { | ||
debug('Setting component %s on system %s', name, params.name); | ||
return _set(name, component, options); | ||
} | ||
function remove(name) { | ||
debug('Removing component %s from system %s', name, params.name) | ||
delete definitions[name] | ||
return api | ||
} | ||
function remove(name) { | ||
debug('Removing component %s from system %s', name, params.name); | ||
delete definitions[name]; | ||
return api; | ||
} | ||
function _set(name, component, options) { | ||
if (!component) throw new Error(format('Component %s is null or undefined', name)) | ||
definitions[name] = assign({}, options, { name: name, component: component.start ? component : wrap(component), dependencies: [] }) | ||
currentDefinition = definitions[name] | ||
return api | ||
} | ||
function _set(name, component, options) { | ||
if (!component) throw new Error(format('Component %s is null or undefined', name)); | ||
definitions[name] = Object.assign({}, options, { | ||
name, | ||
component: component.start ? component : wrap(component), | ||
dependencies: [] | ||
}); | ||
currentDefinition = definitions[name]; | ||
return api; | ||
} | ||
function include(subSystem) { | ||
debug('Including definitions from sub system %s into system %s', subSystem.name, params.name) | ||
definitions = assign({}, definitions, subSystem._definitions) | ||
return api | ||
} | ||
function include(subSystem) { | ||
debug('Including definitions from sub system %s into system %s', subSystem.name, params.name); | ||
definitions = Object.assign({}, definitions, subSystem._definitions); | ||
return api; | ||
} | ||
function dependsOn() { | ||
if (!currentDefinition) throw new Error('You must add a component before calling dependsOn') | ||
currentDefinition.dependencies = toArray(arguments).reduce(toDependencyDefinitions, currentDefinition.dependencies) | ||
return api | ||
} | ||
function dependsOn(...args) { | ||
if (!currentDefinition) throw new Error('You must add a component before calling dependsOn'); | ||
currentDefinition.dependencies = args.reduce(toDependencyDefinitions, currentDefinition.dependencies); | ||
return api; | ||
} | ||
function toDependencyDefinitions(accumulator, arg) { | ||
var record = typeof arg === 'string' ? { component: arg, destination: arg } : defaults({}, arg, { destination: arg.component }) | ||
if (!record.component) throw new Error(format('Component %s has an invalid dependency %s', currentDefinition.name, JSON.stringify(arg))) | ||
if (find(currentDefinition.dependencies, { destination: record.destination })) throw new Error(format('Component %s has a duplicate dependency %s', currentDefinition.name, record.destination)) | ||
return accumulator.concat(record) | ||
function toDependencyDefinitions(accumulator, arg) { | ||
const record = typeof arg === 'string' ? { | ||
component: arg, | ||
destination: arg | ||
} : Object.assign({}, {destination: arg.component}, arg); | ||
if (!record.component) throw new Error(format('Component %s has an invalid dependency %s', currentDefinition.name, JSON.stringify(arg))); | ||
if (currentDefinition.dependencies.find((dep) => dep.destination === record.destination)) { | ||
throw new Error(format('Component %s has a duplicate dependency %s', currentDefinition.name, record.destination)); | ||
} | ||
return accumulator.concat(record); | ||
} | ||
function start(cb) { | ||
debug('Starting system %s', params.name) | ||
started = [] | ||
var p = new Promise(function(resolve, reject) { | ||
async.seq(sortComponents, ensureComponents, function(components, cb) { | ||
debug('System %s started', params.name) | ||
running = components | ||
cb(null, components) | ||
})(function(err, components) { | ||
if (err) return reject(err, {}) | ||
resolve(components) | ||
}) | ||
}) | ||
return cb ? p.then(immediateCallback(cb)).catch(immediateError(cb, {})) : p | ||
} | ||
function start(cb) { | ||
debug('Starting system %s', params.name); | ||
started = []; | ||
const p = new Promise((resolve, reject) => { | ||
async.seq(sortComponents, ensureComponents, (components, cb) => { | ||
debug('System %s started', params.name); | ||
running = components; | ||
cb(null, components); | ||
})((err, components) => { | ||
if (err) return reject(err, {}); | ||
resolve(components); | ||
}); | ||
}); | ||
return cb ? p.then(immediateCallback(cb)).catch(immediateError(cb, {})) : p; | ||
} | ||
function ensureComponents(components, cb) { | ||
if (running) return cb(null, running) | ||
async.reduce(components.reverse(), {}, toSystem, cb) | ||
} | ||
function ensureComponents(components, cb) { | ||
if (running) return cb(null, running); | ||
async.reduce(components.reverse(), {}, toSystem, cb); | ||
} | ||
function toSystem(system, name, cb) { | ||
debug('Inspecting compontent %s', name) | ||
getDependencies(name, system, function(err, dependencies) { | ||
if (err) return cb(err) | ||
startComponent(dependencies, name, system, cb) | ||
}) | ||
} | ||
function toSystem(system, name, cb) { | ||
debug('Inspecting compontent %s', name); | ||
getDependencies(name, system, (err, dependencies) => { | ||
if (err) return cb(err); | ||
startComponent(dependencies, name, system, cb); | ||
}); | ||
} | ||
function startComponent(dependencies, name, system, cb) { | ||
debug('Starting component %s', name) | ||
started.push(name) | ||
var component = definitions[name].component | ||
var onStarted = function(err, started) { | ||
if (err) return cb(err) | ||
setProp(system, name, started) | ||
debug('Component %s started', name) | ||
setImmediate(function() { | ||
cb(null, system) | ||
}) | ||
} | ||
const p = component.start(dependencies, onStarted) | ||
if (p && p.then) { | ||
p.then(immediateCallback(onStarted)).catch(immediateError(cb)) | ||
} | ||
function startComponent(dependencies, name, system, cb) { | ||
debug('Starting component %s', name); | ||
started.push(name); | ||
const component = definitions[name].component; | ||
const onStarted = function(err, started) { | ||
if (err) return cb(err); | ||
setProp(system, name, started); | ||
debug('Component %s started', name); | ||
setImmediate(() => { | ||
cb(null, system); | ||
}); | ||
}; | ||
const p = component.start(dependencies, onStarted); | ||
if (p && p.then) { | ||
p.then(immediateCallback(onStarted)).catch(immediateError(cb)); | ||
} | ||
} | ||
function stop(cb) { | ||
debug('Stopping system %s', params.name) | ||
var p = new Promise(function(resolve, reject) { | ||
async.seq(sortComponents, removeUnstarted, stopComponents, function(cb) { | ||
debug('System %s stopped', params.name) | ||
running = false | ||
cb() | ||
})(function(err) { | ||
if (err) return reject(err) | ||
resolve(); | ||
}) | ||
}) | ||
return cb ? p.then(immediateCallback(cb)).catch(immediateError(cb)) : p | ||
} | ||
function stop(cb) { | ||
debug('Stopping system %s', params.name); | ||
const p = new Promise((resolve, reject) => { | ||
async.seq(sortComponents, removeUnstarted, stopComponents, (cb) => { | ||
debug('System %s stopped', params.name); | ||
running = false; | ||
cb(); | ||
})((err) => { | ||
if (err) return reject(err); | ||
resolve(); | ||
}); | ||
}); | ||
return cb ? p.then(immediateCallback(cb)).catch(immediateError(cb)) : p; | ||
} | ||
function stopComponents(components, cb) { | ||
async.eachSeries(components, stopComponent, cb) | ||
} | ||
function stopComponents(components, cb) { | ||
async.eachSeries(components, stopComponent, cb); | ||
} | ||
function stopComponent(name, cb) { | ||
debug('Stopping component %s', name) | ||
var stop = definitions[name].component.stop || noop | ||
var onStopped = function(err) { | ||
if (err) return cb(err) | ||
debug('Component %s stopped', name) | ||
setImmediate(cb) | ||
} | ||
var p = stop(onStopped) | ||
if (p && p.then) { | ||
p.then(immediateCallback(onStopped)).catch(immediateError(cb)) | ||
} | ||
function stopComponent(name, cb) { | ||
debug('Stopping component %s', name); | ||
const stop = definitions[name].component.stop || noop; | ||
const onStopped = function(err) { | ||
if (err) return cb(err); | ||
debug('Component %s stopped', name); | ||
setImmediate(cb); | ||
}; | ||
const p = stop(onStopped); | ||
if (p && p.then) { | ||
p.then(immediateCallback(onStopped)).catch(immediateError(cb)); | ||
} | ||
} | ||
function sortComponents(cb) { | ||
var result = [] | ||
try { | ||
var graph = new Toposort() | ||
Object.keys(definitions).forEach(function(name) { | ||
graph.add(name, map(definitions[name].dependencies, 'component')) | ||
}) | ||
result = intersection(graph.sort(), Object.keys(definitions)) | ||
} catch (err) { | ||
return cb(err) | ||
} | ||
return cb(null, result) | ||
function sortComponents(cb) { | ||
let result = []; | ||
try { | ||
const graph = new Toposort(); | ||
Object.keys(definitions).forEach((name) => { | ||
graph.add(name, definitions[name].dependencies.map((dep) => dep.component)); | ||
}); | ||
result = arraysIntersection(graph.sort(), Object.keys(definitions)); | ||
} catch (err) { | ||
return cb(err); | ||
} | ||
return cb(null, result); | ||
} | ||
function removeUnstarted(components, cb) { | ||
cb(null, intersection(components, started)) | ||
} | ||
function removeUnstarted(components, cb) { | ||
cb(null, arraysIntersection(components, started)); | ||
} | ||
function getDependencies(name, system, cb) { | ||
async.reduce(definitions[name].dependencies, {}, function(accumulator, dependency, cb) { | ||
if (!hasProp(definitions, dependency.component)) return cb(new Error(format('Component %s has an unsatisfied dependency on %s', name, dependency.component))) | ||
if (!dependency.hasOwnProperty('source') && definitions[dependency.component].scoped) dependency.source = name | ||
dependency.source ? debug('Injecting dependency %s.%s as %s into %s', dependency.component, dependency.source, dependency.destination, name) | ||
: debug('Injecting dependency %s as %s into %s', dependency.component, dependency.destination, name) | ||
var component = getProp(system, dependency.component) | ||
setProp(accumulator, dependency.destination, dependency.source ? getProp(component, dependency.source) : component) | ||
cb(null, accumulator) | ||
}, cb) | ||
} | ||
function getDependencies(name, system, cb) { | ||
async.reduce(definitions[name].dependencies, {}, (accumulator, dependency, cb) => { | ||
if (!hasProp(definitions, dependency.component)) return cb(new Error(format('Component %s has an unsatisfied dependency on %s', name, dependency.component))); | ||
if (!dependency.hasOwnProperty('source') && definitions[dependency.component].scoped) dependency.source = name; | ||
dependency.source ? debug('Injecting dependency %s.%s as %s into %s', dependency.component, dependency.source, dependency.destination, name) | ||
: debug('Injecting dependency %s as %s into %s', dependency.component, dependency.destination, name); | ||
const component = getProp(system, dependency.component); | ||
setProp(accumulator, dependency.destination, dependency.source ? getProp(component, dependency.source) : component); | ||
cb(null, accumulator); | ||
}, cb); | ||
} | ||
function noop() { | ||
var args = toArray(arguments) | ||
var cb = args.pop() | ||
cb && cb.apply(null, [null].concat(args)) | ||
} | ||
function noop(...args) { | ||
const cb = args.pop(); | ||
cb && cb(...[null].concat(args)); | ||
} | ||
function wrap(component) { | ||
return { | ||
start: function(dependencies, cb) { | ||
return cb(null, component) | ||
} | ||
} | ||
} | ||
function wrap(component) { | ||
return { | ||
start(dependencies, cb) { | ||
return cb(null, component); | ||
} | ||
}; | ||
} | ||
function restart(cb) { | ||
var p = api.stop().then(function() { | ||
return api.start() | ||
}) | ||
function restart(cb) { | ||
const p = api.stop().then(() => api.start()); | ||
return cb ? p.then(immediateCallback(cb)).catch(immediateError(cb)) : p | ||
} | ||
return cb ? p.then(immediateCallback(cb)).catch(immediateError(cb)) : p; | ||
} | ||
function immediateCallback(cb) { | ||
return function(resolved) { | ||
setImmediate(function() { | ||
cb(null, resolved); | ||
}) | ||
} | ||
} | ||
function immediateCallback(cb) { | ||
return function (resolved) { | ||
setImmediate(() => { | ||
cb(null, resolved); | ||
}); | ||
}; | ||
} | ||
function immediateError(cb, resolved) { | ||
return function(err) { | ||
setImmediate(function() { | ||
resolved ? cb(err, resolved) : cb(err); | ||
}) | ||
} | ||
} | ||
function immediateError(cb, resolved) { | ||
return function (err) { | ||
setImmediate(() => { | ||
resolved ? cb(err, resolved) : cb(err); | ||
}); | ||
}; | ||
} | ||
var api = { | ||
name: params.name, | ||
bootstrap: bootstrap, | ||
configure: configure, | ||
add: add, | ||
set: set, | ||
remove: remove, | ||
merge: include, | ||
include: include, | ||
dependsOn: dependsOn, | ||
start: start, | ||
stop: stop, | ||
restart: restart, | ||
_definitions: definitions | ||
} | ||
Object.assign(api, { | ||
name: params.name, | ||
bootstrap, | ||
configure, | ||
add, | ||
set, | ||
remove, | ||
merge: include, | ||
include, | ||
dependsOn, | ||
start, | ||
stop, | ||
restart, | ||
_definitions: definitions | ||
}); | ||
return api | ||
} | ||
return api; | ||
}; |
{ | ||
"name": "systemic", | ||
"version": "3.3.7", | ||
"version": "3.3.9", | ||
"description": "A minimal dependency injection library for node", | ||
"main": "index.js", | ||
"scripts": { | ||
"qa": "npm run lint && npm run test", | ||
"qa": "npm run lint && npm test", | ||
"lint": "eslint .", | ||
"test": "nyc --report html --reporter lcov --reporter text-summary mocha test" | ||
"test": "node test", | ||
"coverage": "nyc --report html --reporter lcov --reporter text-summary node test" | ||
}, | ||
@@ -27,22 +28,11 @@ "keywords": [ | ||
"eslint": "^6.8.0", | ||
"eslint-config-imperative": "^3.0.0", | ||
"eslint-plugin-imperative": "^3.0.0", | ||
"husky": "^4.2.3", | ||
"mocha": "^7.1.1", | ||
"nyc": "^15.0.0" | ||
"eslint-config-esnext": "^4.1.0", | ||
"husky": "^4.3.6", | ||
"nyc": "^15.1.0", | ||
"zunit": "^3.0.0" | ||
}, | ||
"dependencies": { | ||
"async": "^3.2.0", | ||
"chance": "^1.1.4", | ||
"debug": "^4.1.1", | ||
"lodash.assign": "^4.2.0", | ||
"lodash.defaults": "^4.2.0", | ||
"lodash.find": "^4.6.0", | ||
"lodash.get": "^4.4.2", | ||
"lodash.has": "^4.5.2", | ||
"lodash.intersection": "^4.4.0", | ||
"lodash.isfunction": "^3.0.9", | ||
"lodash.map": "^4.6.0", | ||
"lodash.set": "^4.3.2", | ||
"lodash.toarray": "^4.4.0", | ||
"chance": "^1.1.7", | ||
"debug": "^4.3.1", | ||
"require-all": "^3.0.0", | ||
@@ -52,7 +42,6 @@ "toposort-class": "^1.0.1" | ||
"directories": { | ||
"example": "examples", | ||
"test": "tests" | ||
"example": "examples" | ||
}, | ||
"engines": { | ||
"node": ">6.0.0" | ||
"node": ">=10.0.0" | ||
}, | ||
@@ -59,0 +48,0 @@ "repository": { |
@@ -5,9 +5,7 @@ # Systemic | ||
[![Greenkeeper badge](https://badges.greenkeeper.io/guidesmiths/systemic.svg)](https://greenkeeper.io/) | ||
[![NPM version](https://img.shields.io/npm/v/systemic.svg?style=flat-square)](https://www.npmjs.com/package/systemic) | ||
[![NPM downloads](https://img.shields.io/npm/dm/systemic.svg?style=flat-square)](https://www.npmjs.com/package/systemic) | ||
[![Build Status](https://img.shields.io/travis/guidesmiths/systemic/master.svg)](https://travis-ci.org/guidesmiths/systemic) | ||
[![Node.js CI](https://github.com/guidesmiths/systemic/workflows/Node.js%20CI/badge.svg)](https://github.com/guidesmiths/systemic/actions?query=workflow%3A%22Node.js+CI%22) | ||
[![Code Climate](https://codeclimate.com/github/guidesmiths/systemic/badges/gpa.svg)](https://codeclimate.com/github/guidesmiths/systemic) | ||
[![Test Coverage](https://codeclimate.com/github/guidesmiths/systemic/badges/coverage.svg)](https://codeclimate.com/github/guidesmiths/systemic/coverage) | ||
[![Code Style](https://img.shields.io/badge/code%20style-imperative-brightgreen.svg)](https://github.com/guidesmiths/eslint-config-imperative) | ||
[![Dependency Status](https://david-dm.org/guidesmiths/systemic.svg)](https://david-dm.org/guidesmiths/systemic) | ||
@@ -106,3 +104,3 @@ [![devDependencies Status](https://david-dm.org/guidesmiths/systemic/dev-status.svg)](https://david-dm.org/guidesmiths/systemic?type=dev) | ||
### Runners | ||
While not shown in the above examples we usually separate the system definition from system start. This is important for testing since you ofen want to make changes to the system definition (e.g. replacing components with stubs), before starting the system. By wrapping the system definition in a function you create a new system in each of your tests. | ||
While not shown in the above examples we usually separate the system definition from system start. This is important for testing since you often want to make changes to the system definition (e.g. replacing components with stubs), before starting the system. By wrapping the system definition in a function you create a new system in each of your tests. | ||
@@ -252,3 +250,3 @@ ```js | ||
#### Including components from another system | ||
You can simplfiy large systems by breaking them up into smaller ones, then including their component definitions into the main system. | ||
You can simplify large systems by breaking them up into smaller ones, then including their component definitions into the main system. | ||
@@ -299,3 +297,3 @@ ```js | ||
#### Bootstraping components | ||
#### Bootstrapping components | ||
The dependency graph for a medium size project can grow quickly leading to a large system definition. To simplify this you can bootstrap components from a specified directory, where each folder in the directory includes an index.js which defines a sub system. e.g. | ||
@@ -372,9 +370,9 @@ | ||
systemic:index Starting system server +0ms | ||
systemic:index Inspecting compontent routes.admin +0ms | ||
systemic:index Inspecting component routes.admin +0ms | ||
systemic:index Starting component routes.admin +0ms | ||
systemic:index Component routes.admin started +15ms | ||
systemic:index Inspecting compontent routes.api +0ms | ||
systemic:index Inspecting component routes.api +0ms | ||
systemic:index Starting component routes.api +0ms | ||
systemic:index Component routes.api started +15ms | ||
systemic:index Inspecting compontent routes +0ms | ||
systemic:index Inspecting component routes +0ms | ||
systemic:index Injecting dependency routes.admin as routes.admin into routes +0ms | ||
@@ -381,0 +379,0 @@ systemic:index Injecting dependency routes.api as routes.api into routes +0ms |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
5
6
31811
20
437
380
1
- Removedlodash.assign@^4.2.0
- Removedlodash.defaults@^4.2.0
- Removedlodash.find@^4.6.0
- Removedlodash.get@^4.4.2
- Removedlodash.has@^4.5.2
- Removedlodash.intersection@^4.4.0
- Removedlodash.isfunction@^3.0.9
- Removedlodash.map@^4.6.0
- Removedlodash.set@^4.3.2
- Removedlodash.toarray@^4.4.0
- Removedlodash.assign@4.2.0(transitive)
- Removedlodash.defaults@4.2.0(transitive)
- Removedlodash.find@4.6.0(transitive)
- Removedlodash.get@4.4.2(transitive)
- Removedlodash.has@4.5.2(transitive)
- Removedlodash.intersection@4.4.0(transitive)
- Removedlodash.isfunction@3.0.9(transitive)
- Removedlodash.map@4.6.0(transitive)
- Removedlodash.set@4.3.2(transitive)
- Removedlodash.toarray@4.4.0(transitive)
Updatedchance@^1.1.7
Updateddebug@^4.3.1