Comparing version 3.0.0 to 4.0.1
{ | ||
"name": "pdi", | ||
"version": "3.0.0", | ||
"version": "4.0.1", | ||
"description": "Minimal Promise based dependency injection framework", | ||
"repository": "https://github.com/davidgtonge/pdi", | ||
"main": "src/index.js", | ||
@@ -14,12 +15,12 @@ "scripts": { | ||
"dependencies": { | ||
"bluebird": "^3.5.1", | ||
"debug": "^3.1.0", | ||
"ramda": "^0.25.0" | ||
"bluebird": "^3.7.2", | ||
"debug": "^4.2.0", | ||
"ramda": "^0.27.1" | ||
}, | ||
"devDependencies": { | ||
"@mft/eslint-config-momentumft": "^3.1.5", | ||
"eslint": "^4.18.1", | ||
"mocha": "^5.0.1", | ||
"sinon": "^4.4.2" | ||
"@mft/eslint-config-momentumft": "^4.0.0", | ||
"eslint": "^7.9.0", | ||
"mocha": "^8.1.3", | ||
"sinon": "^9.0.3" | ||
} | ||
} |
@@ -74,5 +74,14 @@ # PDI - Minimal Promise based Dependency Injection framework | ||
### `strict` | ||
If this function is called before activation, then there are checks made when calling the factory fucntion, the following will cause an error to be thrown | ||
* If the factory tries to access a property that hasn't been depended on | ||
* If a property is not accessed even though it has been depended on | ||
### `create` | ||
This function is useful for where the DI container will be used to perform a particular operation, rather then for system start-up. It returns a DI container with the `add`, `start` and `clear` methods. | ||
This function is useful for where the DI container will be used to perform a particular operation, rather then for system start-up. It returns a DI container with the `add`, `start`, `strict` and `clear` methods. | ||
@@ -79,0 +88,0 @@ While being small this library is powerful enough to be used for async flow |
@@ -42,3 +42,3 @@ const Promise = require("bluebird") | ||
function move(subset, from, to) { | ||
forEach(item => { | ||
forEach((item) => { | ||
const fromPos = from.indexOf(item) | ||
@@ -67,13 +67,37 @@ from.splice(fromPos, 1) | ||
function activationReducer(memo, items) { | ||
const activationReducer = (strictMode) => (memo, items) => { | ||
const names = pluck("name", items) | ||
debug(`Initialising ${names.join(", ")}`) | ||
return Promise.map(items, item => { | ||
const opts = pick(item.deps, memo) | ||
return Promise.resolve(item.fn(opts)) | ||
return Promise.map(items, (item) => { | ||
const _opts = pick(item.deps, memo) | ||
if (!strictMode) { | ||
return Promise.resolve(item.fn(_opts)) | ||
} | ||
const accessed = [] | ||
const opts = new Proxy(_opts, { | ||
get(target, prop) { | ||
if (target[prop]) { | ||
accessed.push(prop) | ||
return target[prop] | ||
} | ||
throw new Error(`Invalid property access: ${prop}`) | ||
}, | ||
}) | ||
const result = item.fn(opts) | ||
const notAccessed = difference(item.deps, accessed) | ||
if (notAccessed.length) { | ||
throw new Error( | ||
`Depended on property not accessed: ${notAccessed.join(",")}`, | ||
) | ||
} | ||
return Promise.resolve(result) | ||
}).then(compose(merge(memo), zipObj(names))) | ||
} | ||
function startActivation(array) { | ||
return Promise.reduce(array, activationReducer, {}) | ||
function startActivation(array, strictMode) { | ||
return Promise.reduce(array, activationReducer(strictMode), {}) | ||
} | ||
@@ -88,3 +112,3 @@ | ||
named arugments using destructing. It therefore doesn't accept functions with | ||
a length of more than 1` | ||
a length of more than 1`, | ||
) | ||
@@ -100,9 +124,17 @@ } | ||
let nameIdx = 0 | ||
let strictMode = false | ||
const startTime = Date.now() | ||
let firstAdd | ||
const strict = () => { | ||
if (activated) { | ||
throw new Error("Can't set strict mode after activation") | ||
} | ||
strictMode = true | ||
} | ||
function add(name, deps, fn) { | ||
if (!firstAdd) firstAdd = Date.now() | ||
if (activated) { | ||
throw new Error(`DI already activated - can't register: ${name} `) | ||
throw new Error(`DI already activated - can't register: ${name}`) | ||
} | ||
@@ -132,3 +164,3 @@ // (array, fn) = side effect function | ||
const sorted = checkAndSortDependencies(registry) | ||
return startActivation(sorted).then(_modules => { | ||
return startActivation(sorted, strictMode).then((_modules) => { | ||
debug("Activation complete", Date.now() - startTime) | ||
@@ -142,2 +174,3 @@ modules = _modules | ||
activated = false | ||
strictMode = false | ||
registry = {} | ||
@@ -159,3 +192,3 @@ modules = {} | ||
return {add, start, clear, __test} | ||
return {add, start, clear, strict, __test} | ||
} | ||
@@ -162,0 +195,0 @@ |
/* eslint max-nested-callbacks: 0 */ | ||
const {ok, equal, deepEqual, throws} = require("assert") | ||
const {ok, strictEqual, deepStrictEqual, throws, rejects} = require("assert") | ||
const pdi = require("../src/index") | ||
@@ -19,5 +19,5 @@ const sinon = require("sinon") | ||
const registry = pdi.__test.getRegistry() | ||
equal(registry[name].name, name) | ||
equal(registry[name].fn, fn) | ||
equal(registry[name].deps, deps) | ||
strictEqual(registry[name].name, name) | ||
strictEqual(registry[name].fn, fn) | ||
strictEqual(registry[name].deps, deps) | ||
}) | ||
@@ -30,5 +30,5 @@ | ||
const registry = pdi.__test.getRegistry() | ||
equal(registry[name].name, name) | ||
equal(registry[name].fn, fn) | ||
deepEqual(registry[name].deps, []) | ||
strictEqual(registry[name].name, name) | ||
strictEqual(registry[name].fn, fn) | ||
deepStrictEqual(registry[name].deps, []) | ||
}) | ||
@@ -40,5 +40,8 @@ | ||
pdi.add(name, fn) | ||
throws(() => { | ||
pdi.add(name, fn) | ||
}) | ||
throws( | ||
() => { | ||
pdi.add(name, fn) | ||
}, | ||
{message: "Attempted to register module: NAME multiple times"}, | ||
) | ||
}) | ||
@@ -51,5 +54,8 @@ | ||
return pdi.start().then(() => { | ||
throws(() => { | ||
pdi.add("something", fn) | ||
}) | ||
throws( | ||
() => { | ||
pdi.add("something", fn) | ||
}, | ||
{message: "DI already activated - can't register: something"}, | ||
) | ||
}) | ||
@@ -65,4 +71,4 @@ }) | ||
const sorted = pdi.__test.checkAndSortDependencies(registry) | ||
equal(sorted[0][0].name, "2") | ||
equal(sorted[1][0].name, "1") | ||
strictEqual(sorted[0][0].name, "2") | ||
strictEqual(sorted[1][0].name, "1") | ||
}) | ||
@@ -97,4 +103,4 @@ | ||
const names = compose(pluck("name"), flatten)(sorted) | ||
equal(sorted.length, 4) | ||
deepEqual(names, ["1", "2", "4", "5", "3"]) | ||
strictEqual(sorted.length, 4) | ||
deepStrictEqual(names, ["1", "2", "4", "5", "3"]) | ||
}) | ||
@@ -124,4 +130,4 @@ | ||
return pdi.start().then(() => { | ||
equal(fn1.callCount, 1) | ||
equal(fn2.callCount, 1) | ||
strictEqual(fn1.callCount, 1) | ||
strictEqual(fn2.callCount, 1) | ||
}) | ||
@@ -135,5 +141,5 @@ }) | ||
pdi.add("2", fn2) | ||
return pdi.start().then(result => { | ||
equal(result["1"], "foo") | ||
equal(result["2"], "bar") | ||
return pdi.start().then((result) => { | ||
strictEqual(result["1"], "foo") | ||
strictEqual(result["2"], "bar") | ||
}) | ||
@@ -149,6 +155,3 @@ }) | ||
pdi.add("2", fn2) | ||
return pdi.start().catch(err => { | ||
ok(err) | ||
equal(err.message, "error in dep") | ||
}) | ||
return rejects(() => pdi.start(), {message: "error in dep"}) | ||
}) | ||
@@ -161,6 +164,3 @@ | ||
pdi.add("2", fn2) | ||
return pdi.start().catch(err => { | ||
ok(err) | ||
equal(err.message, "error in dep") | ||
}) | ||
return rejects(() => pdi.start(), {message: "error in dep"}) | ||
}) | ||
@@ -175,3 +175,3 @@ | ||
return pdi.start().then(() => { | ||
ok(fn1.calledWith({"2": foo})) | ||
ok(fn1.calledWith({2: foo})) | ||
}) | ||
@@ -190,6 +190,61 @@ }) | ||
return pdi.start().then(() => { | ||
ok(fn1.calledWith({"2": foo, "3": foo2})) | ||
ok(fn1.calledWith({2: foo, 3: foo2})) | ||
}) | ||
}) | ||
}) | ||
describe("strict mode", () => { | ||
it("throws if a property is accessed that has not been depended on", () => { | ||
pdi.strict() | ||
const foo = Math.random() | ||
const foo2 = Math.random() | ||
const fn1 = ({b, c, d}) => [b, c, d] | ||
const fn2 = sinon.stub().returns(foo) | ||
const fn3 = sinon.stub().returns(foo2) | ||
pdi.add("a", ["b", "c"], fn1) | ||
pdi.add("b", fn2) | ||
pdi.add("c", fn3) | ||
return rejects(() => pdi.start(), {message: "Invalid property access: d"}) | ||
}) | ||
it("throws if a property is depended on but not accessed", () => { | ||
pdi.strict() | ||
const foo = Math.random() | ||
const foo2 = Math.random() | ||
const fn1 = ({b}) => [b] | ||
const fn2 = sinon.stub().returns(foo) | ||
const fn3 = sinon.stub().returns(foo2) | ||
pdi.add("a", ["b", "c"], fn1) | ||
pdi.add("b", fn2) | ||
pdi.add("c", fn3) | ||
return rejects(() => pdi.start(), { | ||
message: "Depended on property not accessed: c", | ||
}) | ||
}) | ||
it("throws if a property is depended on but not accessed in an async fn", () => { | ||
pdi.strict() | ||
const foo = Math.random() | ||
const foo2 = Math.random() | ||
const fn1 = async ({b}) => Promise.resolve([b]) | ||
const fn2 = sinon.stub().returns(foo) | ||
const fn3 = sinon.stub().returns(foo2) | ||
pdi.add("a", ["b", "c"], fn1) | ||
pdi.add("b", fn2) | ||
pdi.add("c", fn3) | ||
return rejects(() => pdi.start(), { | ||
message: "Depended on property not accessed: c", | ||
}) | ||
}) | ||
}) | ||
}) | ||
/* | ||
Create a proxy and throw if invalid property is accessed | ||
Log if property is not accessed. | ||
Also consider how it can handle things like ids | ||
e.g. whenever log gets injected | ||
*/ |
Sorry, the diff of this file is not supported yet
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
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
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
387
107
16274
7
+ Addeddebug@4.3.7(transitive)
+ Addedramda@0.27.2(transitive)
- Removeddebug@3.2.7(transitive)
- Removedramda@0.25.0(transitive)
Updatedbluebird@^3.7.2
Updateddebug@^4.2.0
Updatedramda@^0.27.1